/** * Convert lat/lon coordinates to projection coordinates. * * @param from array of lat/lon coordinates: from[2][n], where from[0][i], from[1][i] is the * (lat,lon) coordinate of the ith point * @param to resulting array of projection coordinates, where to[0][i], to[1][i] is the (x,y) * coordinate of the ith point * @param latIndex index of latitude in "from" * @param lonIndex index of longitude in "from" * @return the "to" array. */ public double[][] latLonToProj(double[][] from, double[][] to, int latIndex, int lonIndex) { int cnt = from[0].length; double[] fromLatA = from[latIndex]; double[] fromLonA = from[lonIndex]; double[] resultXA = to[INDEX_X]; double[] resultYA = to[INDEX_Y]; double toX, toY; for (int i = 0; i < cnt; i++) { double fromLat = fromLatA[i]; double fromLon = fromLonA[i]; fromLat = Math.toRadians(fromLat); double lonDiff = Math.toRadians(LatLonPointImpl.lonNormal(fromLon - lon0Degrees)); double cosc = sinLat0 * Math.sin(fromLat) + cosLat0 * Math.cos(fromLat) * Math.cos(lonDiff); if (cosc >= 0) { toX = R * Math.cos(fromLat) * Math.sin(lonDiff); toY = R * (cosLat0 * Math.sin(fromLat) - sinLat0 * Math.cos(fromLat) * Math.cos(lonDiff)); } else { toX = Double.POSITIVE_INFINITY; toY = Double.POSITIVE_INFINITY; } resultXA[i] = toX; resultYA[i] = toY; } return to; }
/** * Convert projection coordinates to a LatLonPoint Note: a new object is not created on each call * for the return value. * * @param world convert from these projection coordinates * @param result the object to write to * @return LatLonPoint convert to these lat/lon coordinates */ public LatLonPoint projToLatLon(ProjectionPoint world, LatLonPointImpl result) { double fromX = world.getX() - falseEasting; double fromY = world.getY() - falseNorthing; double toLon = Math.toDegrees(fromX / A) + lon0; double e = Math.exp(-fromY / A); double toLat = Math.toDegrees(Math.PI / 2 - 2 * Math.atan(e)); // Snyder p 44 result.setLatitude(toLat); result.setLongitude(toLon); return result; }
/** * Construct a Orthographic Projection, specify earth radius * * @param lat0 lat origin of the coord. system on the projection plane * @param lon0 lon origin of the coord. system on the projection plane * @param earthRadius radius of the earth * @throws IllegalArgumentException if lat0, par1, par2 = +/-90 deg */ public Orthographic(double lat0, double lon0, double earthRadius) { super("Orthographic", false); this.lat0 = Math.toRadians(lat0); this.lon0 = Math.toRadians(lon0); R = earthRadius; precalculate(); addParameter(CF.GRID_MAPPING_NAME, CF.ORTHOGRAPHIC); addParameter(CF.LATITUDE_OF_PROJECTION_ORIGIN, lat0); addParameter(CF.LONGITUDE_OF_PROJECTION_ORIGIN, lon0); addParameter(CF.EARTH_RADIUS, earthRadius * 1000); }
/** * This returns true when the line between pt1 and pt2 crosses the seam. When the cone is * flattened, the "seam" is lon0 +- 180. * * @param pt1 point 1 * @param pt2 point 2 * @return true when the line between pt1 and pt2 crosses the seam. */ public boolean crossSeam(ProjectionPoint pt1, ProjectionPoint pt2) { // either point is infinite if (ProjectionPointImpl.isInfinite(pt1) || ProjectionPointImpl.isInfinite(pt2)) return true; // opposite signed X values, larger then 5000 km return (pt1.getX() * pt2.getX() < 0) && (Math.abs(pt1.getX() - pt2.getX()) > 5000.0); }
/** * Convert a LatLonPoint to projection coordinates * * @param latLon convert from these lat, lon coordinates * @param result the object to write to * @return the given result */ public ProjectionPoint latLonToProj(LatLonPoint latLon, ProjectionPointImpl result) { double toX, toY; double fromLat = latLon.getLatitude(); double fromLon = latLon.getLongitude(); double fromLat_r = Math.toRadians(fromLat); // infinite projection if ((Math.abs(90.0 - Math.abs(fromLat))) < TOLERANCE) { toX = Double.POSITIVE_INFINITY; toY = Double.POSITIVE_INFINITY; } else { toX = A * Math.toRadians(LatLonPointImpl.range180(fromLon - this.lon0)); toY = A * SpecialMathFunction.atanh(Math.sin(fromLat_r)); // p 41 Snyder } result.setLocation(toX + falseEasting, toY + falseNorthing); return result; }
/** * Convert a LatLonPoint to projection coordinates * * @param latLon convert from these lat, lon coordinates * @param result the object to write to * @return the given result */ public ProjectionPoint latLonToProj(LatLonPoint latLon, ProjectionPointImpl result) { double toX, toY; double fromLat = latLon.getLatitude(); double fromLon = latLon.getLongitude(); fromLat = Math.toRadians(fromLat); double lonDiff = Math.toRadians(LatLonPointImpl.lonNormal(fromLon - lon0Degrees)); double cosc = sinLat0 * Math.sin(fromLat) + cosLat0 * Math.cos(fromLat) * Math.cos(lonDiff); if (cosc >= 0) { toX = R * Math.cos(fromLat) * Math.sin(lonDiff); toY = R * (cosLat0 * Math.sin(fromLat) - sinLat0 * Math.cos(fromLat) * Math.cos(lonDiff)); } else { toX = Double.POSITIVE_INFINITY; toY = Double.POSITIVE_INFINITY; } result.setLocation(toX, toY); return result; }
/** * Construct a Mercator Projection. * * @param lon0 longitude of origin (degrees) * @param par standard parallel (degrees). cylinder cuts earth at this latitude. * @param false_easting false_easting in km * @param false_northing false_northing in km * @param radius earth radius in km */ public Mercator( double lon0, double par, double false_easting, double false_northing, double radius) { super("Mercator", false); this.lon0 = lon0; this.par = par; this.falseEasting = false_easting; this.falseNorthing = false_northing; this.earthRadius = radius; this.par_r = Math.toRadians(par); precalculate(); addParameter(CF.GRID_MAPPING_NAME, CF.MERCATOR); addParameter(CF.LONGITUDE_OF_PROJECTION_ORIGIN, lon0); addParameter(CF.STANDARD_PARALLEL, par); addParameter(CF.EARTH_RADIUS, earthRadius * 1000); if ((false_easting != 0.0) || (false_northing != 0.0)) { addParameter(CF.FALSE_EASTING, false_easting); addParameter(CF.FALSE_NORTHING, false_northing); addParameter(CDM.UNITS, "km"); } }
/** Precalculate some params */ private void precalculate() { A = earthRadius * Math.cos(par_r); // incorporates the scale factor at par }
boolean close(double d1, double d2) { return Math.abs(d1 - d2) < TOLERENCE; }
/** * Convert lat/lon coordinates to projection coordinates. * * @param from array of lat/lon coordinates: from[2][n], where (from[0][i], from[1][i]) is the * (lat,lon) coordinate of the ith point * @param to resulting array of projection coordinates: to[2][n] where (to[0][i], to[1][i]) is the * (x,y) coordinate of the ith point * @return the "to" array */ public double[][] projToLatLon(double[][] from, double[][] to) { int cnt = from[0].length; double[] fromXA = from[INDEX_X]; double[] fromYA = from[INDEX_Y]; double[] toLatA = to[INDEX_LAT]; double[] toLonA = to[INDEX_LON]; double toLat, toLon; for (int i = 0; i < cnt; i++) { double fromX = fromXA[i]; double fromY = fromYA[i]; double rho = Math.sqrt(fromX * fromX + fromY * fromY); double c = Math.asin(rho / R); toLon = lon0; double temp = 0; if (Math.abs(rho) > TOLERANCE) { toLat = Math.asin(Math.cos(c) * sinLat0 + (fromY * Math.sin(c) * cosLat0 / rho)); if (Math.abs(lat0 - PI_OVER_4) > TOLERANCE) { // not 90 or -90 temp = rho * cosLat0 * Math.cos(c) - fromY * sinLat0 * Math.sin(c); toLon = lon0 + Math.atan(fromX * Math.sin(c) / temp); } else if (lat0 == PI_OVER_4) { toLon = lon0 + Math.atan(fromX / -fromY); temp = -fromY; } else { toLon = lon0 + Math.atan(fromX / fromY); temp = fromY; } } else { toLat = lat0; } toLat = Math.toDegrees(toLat); toLon = Math.toDegrees(toLon); if (temp < 0) { toLon += 180; } toLon = LatLonPointImpl.lonNormal(toLon); toLatA[i] = toLat; toLonA[i] = toLon; } return to; }
/** * Get the origin longitude in degrees * * @return the origin longitude. */ public double getOriginLon() { return Math.toDegrees(lon0); }
/** * Convert projection coordinates to a LatLonPoint Note: a new object is not created on each call * for the return value. * * @param world convert from these projection coordinates * @param result the object to write to * @return LatLonPoint convert to these lat/lon coordinates */ public LatLonPoint projToLatLon(ProjectionPoint world, LatLonPointImpl result) { double toLat, toLon; double fromX = world.getX(); double fromY = world.getY(); double rho = Math.sqrt(fromX * fromX + fromY * fromY); double c = Math.asin(rho / R); toLon = lon0; double temp = 0; if (Math.abs(rho) > TOLERANCE) { toLat = Math.asin(Math.cos(c) * sinLat0 + (fromY * Math.sin(c) * cosLat0 / rho)); if (Math.abs(lat0 - PI_OVER_4) > TOLERANCE) { // not 90 or -90 temp = rho * cosLat0 * Math.cos(c) - fromY * sinLat0 * Math.sin(c); toLon = lon0 + Math.atan(fromX * Math.sin(c) / temp); } else if (lat0 == PI_OVER_4) { toLon = lon0 + Math.atan(fromX / -fromY); temp = -fromY; } else { toLon = lon0 + Math.atan(fromX / fromY); temp = fromY; } } else { toLat = lat0; } toLat = Math.toDegrees(toLat); toLon = Math.toDegrees(toLon); if (temp < 0) { toLon += 180; } toLon = LatLonPointImpl.lonNormal(toLon); result.setLatitude(toLat); result.setLongitude(toLon); return result; }
/** * Set the origin latitude. * * @param lat the origin latitude. */ public void setOriginLat(double lat) { lat0 = Math.toRadians(lat); precalculate(); }
/** * Set the origin longitude. * * @param lon the origin longitude. */ public void setOriginLon(double lon) { lon0 = Math.toRadians(lon); precalculate(); }
/** * Get the origin latitude in degrees * * @return the origin latitude. */ public double getOriginLat() { return Math.toDegrees(lat0); }
/** * Convert "scale at standard parellel" to "standard parellel" * * @param scale scale at standard parallel * @return standard parellel in degrees */ public static double convertScaleToStandardParallel(double scale) { // k = 1 / cos (par); snyder p 44 // par = arccos(1/k); double par = Math.acos(1.0 / scale); return Math.toDegrees(par); }
/** Precalculate some stuff */ private void precalculate() { sinLat0 = Math.sin(lat0); cosLat0 = Math.cos(lat0); lon0Degrees = Math.toDegrees(lon0); }