All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.unidata.geoloc.projection.Stereographic Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package ucar.unidata.geoloc.projection;

import ucar.nc2.constants.CDM;
import ucar.nc2.constants.CF;
import ucar.unidata.geoloc.*;

/**
 * Stereographic projection, spherical earth.
 * Projection plane is a plane tangent to the earth at latt, lont.
 * see John Snyder, Map Projections used by the USGS, Bulletin 1532, 2nd edition (1983), p 153
 *
 * @author John Caron
 */

public class Stereographic extends ProjectionImpl {

  /**
   * Construct a Stereographic Projection using latitude of true scale and calculating scale factor.
   * 

Since the scale factor at lat = k = 2*k0/(1+sin(lat)) [Snyder,Working Manual p157] * then to make scale = 1 at lat, set k0 = (1+sin(lat))/2 * * @param latt tangent point of projection, also origin of projection coord system * @param lont tangent point of projection, also origin of projection coord system * @param latTrue latitude of true scale in degrees north; latitude where scale factor = 1.0 * @return Stereographic projection */ static public Stereographic factory(double latt, double lont, double latTrue) { double scale = (1.0 + Math.sin(Math.toRadians(latTrue))) / 2.0; return new Stereographic(latt, lont, scale); } //////////////////////////////////////////////////////////////////////////////////////////////// private double falseEasting, falseNorthing; private double scale, earthRadius; private double latt, lont; private double sinlatt, coslatt, latts; private boolean isNorth = false; private boolean isPolar = false; @Override public ProjectionImpl constructCopy() { ProjectionImpl result = new Stereographic(getTangentLat(), getTangentLon(), getScale(), getFalseEasting(), getFalseNorthing(), getEarthRadius()); result.setDefaultMapArea(defaultMapArea); result.setName(name); return result; } /** * Constructor with default parameters = North Polar */ public Stereographic() { this(90.0, -105.0, 1.0); } /** * Construct a Stereographic Projection. * * @param latt tangent point of projection, also origin of projection coord system * @param lont tangent point of projection, also origin of projection coord system * @param scale scale factor at tangent point, "normally 1.0 but may be reduced" */ public Stereographic(double latt, double lont, double scale) { this(latt, lont, scale, 0, 0, EARTH_RADIUS); } /** * Construct a Stereographic Projection. * * @param latt tangent point of projection, also origin of projection coord system * @param lont tangent point of projection, also origin of projection coord system * @param scale scale factor at tangent point, "normally 1.0 but may be reduced" */ public Stereographic(double latt, double lont, double scale, double false_easting, double false_northing) { this(latt, lont, scale, false_easting, false_northing, EARTH_RADIUS); } /** * Construct a Stereographic Projection. * * @param latt tangent point of projection, also origin of projection coord system * @param lont tangent point of projection, also origin of projection coord system * @param scale scale factor at tangent point, "normally 1.0 but may be reduced" * @param false_easting false easting in units of x coords * @param false_northing false northing in units of y coords * @param radius earth radius in km */ public Stereographic(double latt, double lont, double scale, double false_easting, double false_northing, double radius) { super("Stereographic", false); this.latt = Math.toRadians(latt); this.lont = Math.toRadians(lont); this.earthRadius = radius; this.scale = scale * earthRadius; this.falseEasting = false_easting; this.falseNorthing = false_northing; precalculate(); addParameter(CF.GRID_MAPPING_NAME, CF.STEREOGRAPHIC); addParameter(CF.LONGITUDE_OF_PROJECTION_ORIGIN, lont); addParameter(CF.LATITUDE_OF_PROJECTION_ORIGIN, latt); addParameter(CF.SCALE_FACTOR_AT_PROJECTION_ORIGIN, scale); 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"); } } /** * Construct a polar Stereographic Projection, from the "natural origin" and the tangent point, * calculating the scale factor. * * @param lat_ts_deg Latitude at natural origin (degrees_north) * @param latt_deg tangent point of projection (degrees_north) * @param lont_deg tangent point of projection, also origin of projection coord system ((degrees_east) * @param north true if north pole, false is south pole */ public Stereographic(double lat_ts_deg, double latt_deg, double lont_deg, boolean north) { super("PolarStereographic", false); this.latts = Math.toRadians(lat_ts_deg); this.latt = Math.toRadians(latt_deg); this.lont = Math.toRadians(lont_deg); this.isPolar = true; this.isNorth = north; this.earthRadius = EARTH_RADIUS; this.falseEasting = 0; this.falseNorthing = 0; precalculate(); double scaleFactor = (lat_ts_deg == 90 || lat_ts_deg == -90) ? 1.0 : getScaleFactor(latts, north); this.scale = scaleFactor * earthRadius; addParameter(CF.GRID_MAPPING_NAME, "polar_stereographic"); addParameter("longitude_of_projection_origin", lont_deg); addParameter("latitude_of_projection_origin", latt_deg); addParameter("scale_factor_at_projection_origin", scaleFactor); } /** * Calculate polar stereographic scale factor based on the natural latitude and * longitude of the original * Ref: OGP Surveying and Positioning Guidance Note number 7, part 2 April 2009 * http://www.epsg.org * added by Qun He * * @param lat_ts Latitude at natural origin * @param north Is it north polar? * @return scale factor */ private double getScaleFactor(double lat_ts, boolean north) { double e = 0.081819191; double tf = 1.0, mf = 1.0, k0 = 1.0; double root = (1 + e * Math.sin(lat_ts)) / (1 - e * Math.sin(lat_ts)); double power = e / 2; if (north) tf = Math.tan(Math.PI / 4 - lat_ts / 2) * (Math.pow(root, power)); else tf = Math.tan(Math.PI / 4 + lat_ts / 2) / (Math.pow(root, power)); mf = Math.cos(lat_ts) / Math.sqrt(1 - e * e * Math.pow(Math.sin(lat_ts), 2)); k0 = mf * Math.sqrt(Math.pow(1 + e, 1 + e) * Math.pow(1 - e, 1 - e)) / (2 * tf); return Double.isNaN(k0) ? 1.0 : k0; } /** * precalculate some stuff */ private void precalculate() { sinlatt = Math.sin(latt); coslatt = Math.cos(latt); } // bean properties /** * Get the scale * * @return the scale */ public double getScale() { return scale / earthRadius; } /** * Get the latitude at natural origin * * @return latitude at natural origin */ public double getNaturalOriginLat() { return Math.toDegrees(latts); } /** * Get the tangent longitude in degrees * * @return the origin longitude. */ public double getTangentLon() { return Math.toDegrees(lont); } /** * Get the tangent latitude in degrees * * @return the origin latitude. */ public double getTangentLat() { return Math.toDegrees(latt); } public double getEarthRadius() { return earthRadius; } public boolean isNorth() { return isNorth; } public boolean isPolar() { return isPolar; } ////////////////////////////////////////////// // setters for IDV serialization - do not use except for object creating /** * @deprecated */ public void setScale(double scale) { this.scale = earthRadius * scale; } /** * @deprecated */ public void setTangentLat(double latt) { this.latt = Math.toRadians(latt); precalculate(); } /** * @deprecated */ public void setTangentLon(double lont) { this.lont = Math.toRadians(lont); precalculate(); } // cruft from IDV bundle /** * @deprecated */ public void setCentralMeridian(double lont) { setTangentLon(lont); } /** * Set the false_easting, in km. * natural_x_coordinate + false_easting = x coordinate * * @param falseEasting x offset */ public void setFalseEasting(double falseEasting) { this.falseEasting = falseEasting; } /** * Set the false northing, in km. * natural_y_coordinate + false_northing = y coordinate * * @param falseNorthing y offset */ public void setFalseNorthing(double falseNorthing) { this.falseNorthing = falseNorthing; } /** * Get the parameters as a String * * @return the parameters as a String */ public String paramsToString() { return toString(); } ////////////////////////////////////////////// @Override public String toString() { return "Stereographic{" + "falseEasting=" + falseEasting + ", falseNorthing=" + falseNorthing + ", scale=" + scale + ", earthRadius=" + earthRadius + ", latt=" + latt + ", lont=" + lont + '}'; } /** * Does the line between these two points cross the projection "seam". * * @param pt1 the line goes between these two points * @param pt2 the line goes between these two points * @return false if there is no seam */ public boolean crossSeam(ProjectionPoint pt1, ProjectionPoint pt2) { return false; // ProjectionPointImpl.isInfinite(pt1) || ProjectionPointImpl.isInfinite(pt2); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Stereographic that = (Stereographic) o; if (Double.compare(that.earthRadius, earthRadius) != 0) return false; if (Double.compare(that.falseEasting, falseEasting) != 0) return false; if (Double.compare(that.falseNorthing, falseNorthing) != 0) return false; if (Double.compare(that.latt, latt) != 0) return false; if (Double.compare(that.lont, lont) != 0) return false; if (Double.compare(that.scale, scale) != 0) return false; if ((defaultMapArea == null) != (that.defaultMapArea == null)) return false; // common case is that these are null if (defaultMapArea != null && !that.defaultMapArea.equals(defaultMapArea)) return false; return true; } @Override public int hashCode() { int result; long temp; temp = falseEasting != +0.0d ? Double.doubleToLongBits(falseEasting) : 0L; result = (int) (temp ^ (temp >>> 32)); temp = falseNorthing != +0.0d ? Double.doubleToLongBits(falseNorthing) : 0L; result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = scale != +0.0d ? Double.doubleToLongBits(scale) : 0L; result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = earthRadius != +0.0d ? Double.doubleToLongBits(earthRadius) : 0L; result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = latt != +0.0d ? Double.doubleToLongBits(latt) : 0L; result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = lont != +0.0d ? Double.doubleToLongBits(lont) : 0L; result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } /** * Get the false easting, in km. * * @return the false easting. */ public double getFalseEasting() { return falseEasting; } /** * Get the false northing, in km. * * @return the false northing. */ public double getFalseNorthing() { return falseNorthing; } /*MACROBODY projToLatLon {double phi, lam;} { double rho = Math.sqrt( fromX*fromX + fromY*fromY); double c = 2.0 * Math.atan2( rho, 2.0*scale); double sinc = Math.sin(c); double cosc = Math.cos(c); if (Math.abs(rho) < TOLERANCE) phi = latt; else phi = Math.asin( cosc * sinlatt + fromY * sinc * coslatt / rho); toLat = Math.toDegrees(phi); if ((Math.abs(fromX) < TOLERANCE) && (Math.abs(fromY) < TOLERANCE)) lam = lont; else if (Math.abs(coslatt) < TOLERANCE) lam = lont + Math.atan2( fromX, ((latt > 0) ? -fromY : fromY) ); else lam = lont + Math.atan2( fromX*sinc, rho*coslatt*cosc - fromY*sinc*sinlatt); toLon = Math.toDegrees(lam); } latLonToProj {} { double lat = Math.toRadians (fromLat); double lon = Math.toRadians(fromLon); // keep away from the singular point if ((Math.abs(lat + latt) <= TOLERANCE)) { lat = -latt * (1.0 - TOLERANCE); } double sdlon = Math.sin(lon - lont); double cdlon = Math.cos(lon - lont); double sinlat = Math.sin(lat); double coslat = Math.cos(lat); double k = 2.0 * scale / (1.0 + sinlatt * sinlat + coslatt * coslat * cdlon); toX = k * coslat * sdlon; toY = k * ( coslatt * sinlat - sinlatt * coslat * cdlon); } MACROBODY*/ /*BEGINGENERATED*/ /* Note this section has been generated using the convert.tcl script. This script, run as: tcl convert.tcl Stereographic.java takes the actual projection conversion code defined in the MACROBODY section above and generates the following 6 methods */ /** * 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 lat = Math.toRadians(fromLat); double lon = Math.toRadians(fromLon); // keep away from the singular point if ((Math.abs(lat + latt) <= TOLERANCE)) { lat = -latt * (1.0 - TOLERANCE); } double sdlon = Math.sin(lon - lont); double cdlon = Math.cos(lon - lont); double sinlat = Math.sin(lat); double coslat = Math.cos(lat); double k = 2.0 * scale / (1.0 + sinlatt * sinlat + coslatt * coslat * cdlon); toX = k * coslat * sdlon; toY = k * (coslatt * sinlat - sinlatt * coslat * cdlon); result.setLocation(toX + falseEasting, toY + falseNorthing); return result; } /** * 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() - falseEasting; double fromY = world.getY() - falseNorthing; double phi, lam; double rho = Math.sqrt(fromX * fromX + fromY * fromY); double c = 2.0 * Math.atan2(rho, 2.0 * scale); double sinc = Math.sin(c); double cosc = Math.cos(c); if (Math.abs(rho) < TOLERANCE) { phi = latt; } else { phi = Math.asin(cosc * sinlatt + fromY * sinc * coslatt / rho); } toLat = Math.toDegrees(phi); if ((Math.abs(fromX) < TOLERANCE) && (Math.abs(fromY) < TOLERANCE)) { lam = lont; } else if (Math.abs(coslatt) < TOLERANCE) { lam = lont + Math.atan2(fromX, ((latt > 0) ? -fromY : fromY)); } else { lam = lont + Math.atan2(fromX * sinc, rho * coslatt * cosc - fromY * sinc * sinlatt); } toLon = Math.toDegrees(lam); result.setLatitude(toLat); result.setLongitude(toLon); return result; } /** * Convert lat/lon coordinates to projection coordinates. * * @param from array of lat/lon coordinates: from[2][n], where * (from[latIndex][i], from[lonIndex][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 * @param latIndex index of lat coordinate; must be 0 or 1 * @param lonIndex index of lon coordinate; must be 0 or 1 * @return the "to" array */ public float[][] latLonToProj(float[][] from, float[][] to, int latIndex, int lonIndex) { int cnt = from[0].length; float[] fromLatA = from[latIndex]; float[] fromLonA = from[lonIndex]; float[] resultXA = to[INDEX_X]; float[] resultYA = to[INDEX_Y]; double toX, toY; for (int i = 0; i < cnt; i++) { double fromLat = fromLatA[i]; double fromLon = fromLonA[i]; double lat = Math.toRadians(fromLat); double lon = Math.toRadians(fromLon); // keep away from the singular point if ((Math.abs(lat + latt) <= TOLERANCE)) { lat = -latt * (1.0 - TOLERANCE); } double sdlon = Math.sin(lon - lont); double cdlon = Math.cos(lon - lont); double sinlat = Math.sin(lat); double coslat = Math.cos(lat); double k = 2.0 * scale / (1.0 + sinlatt * sinlat + coslatt * coslat * cdlon); toX = k * coslat * sdlon; toY = k * (coslatt * sinlat - sinlatt * coslat * cdlon); resultXA[i] = (float) (toX + falseEasting); resultYA[i] = (float) (toY + falseNorthing); } return to; } /** * Convert projection coordinates to lat/lon coordinate. * * @param from array of projection coordinates: from[2][n], where * (from[0][i], from[1][i]) is the (x, y) coordinate * of the ith point * @param to resulting array of lat/lon coordinates: to[2][n] where * (to[0][i], to[1][i]) is the (lat, lon) coordinate of * the ith point * @return the "to" array */ public float[][] projToLatLon(float[][] from, float[][] to) { int cnt = from[0].length; float[] fromXA = from[INDEX_X]; float[] fromYA = from[INDEX_Y]; float[] toLatA = to[INDEX_LAT]; float[] toLonA = to[INDEX_LON]; double phi, lam; double toLat, toLon; for (int i = 0; i < cnt; i++) { double fromX = fromXA[i] - falseEasting; double fromY = fromYA[i] - falseNorthing; double rho = Math.sqrt(fromX * fromX + fromY * fromY); double c = 2.0 * Math.atan2(rho, 2.0 * scale); double sinc = Math.sin(c); double cosc = Math.cos(c); if (Math.abs(rho) < TOLERANCE) { phi = latt; } else { phi = Math.asin(cosc * sinlatt + fromY * sinc * coslatt / rho); } toLat = Math.toDegrees(phi); if ((Math.abs(fromX) < TOLERANCE) && (Math.abs(fromY) < TOLERANCE)) { lam = lont; } else if (Math.abs(coslatt) < TOLERANCE) { lam = lont + Math.atan2(fromX, ((latt > 0) ? -fromY : fromY)); } else { lam = lont + Math.atan2(fromX * sinc, rho * coslatt * cosc - fromY * sinc * sinlatt); } toLon = Math.toDegrees(lam); toLatA[i] = (float) toLat; toLonA[i] = (float) toLon; } return to; } /** * Convert lat/lon coordinates to projection coordinates. * * @param from array of lat/lon coordinates: from[2][n], where * (from[latIndex][i], from[lonIndex][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 * @param latIndex index of lat coordinate; must be 0 or 1 * @param lonIndex index of lon coordinate; must be 0 or 1 * @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]; double lat = Math.toRadians(fromLat); double lon = Math.toRadians(fromLon); // keep away from the singular point if ((Math.abs(lat + latt) <= TOLERANCE)) { lat = -latt * (1.0 - TOLERANCE); } double sdlon = Math.sin(lon - lont); double cdlon = Math.cos(lon - lont); double sinlat = Math.sin(lat); double coslat = Math.cos(lat); double k = 2.0 * scale / (1.0 + sinlatt * sinlat + coslatt * coslat * cdlon); toX = k * coslat * sdlon; toY = k * (coslatt * sinlat - sinlatt * coslat * cdlon); resultXA[i] = toX + falseEasting; resultYA[i] = toY + falseNorthing; } return to; } /** * Convert projection coordinates to lat/lon coordinate. * * @param from array of projection coordinates: from[2][n], where * (from[0][i], from[1][i]) is the (x, y) coordinate * of the ith point * @param to resulting array of lat/lon coordinates: to[2][n] where * (to[0][i], to[1][i]) is the (lat, lon) 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 phi, lam; double toLat, toLon; for (int i = 0; i < cnt; i++) { double fromX = fromXA[i] - falseEasting; double fromY = fromYA[i] - falseNorthing; double rho = Math.sqrt(fromX * fromX + fromY * fromY); double c = 2.0 * Math.atan2(rho, 2.0 * scale); double sinc = Math.sin(c); double cosc = Math.cos(c); if (Math.abs(rho) < TOLERANCE) { phi = latt; } else { phi = Math.asin(cosc * sinlatt + fromY * sinc * coslatt / rho); } toLat = Math.toDegrees(phi); if ((Math.abs(fromX) < TOLERANCE) && (Math.abs(fromY) < TOLERANCE)) { lam = lont; } else if (Math.abs(coslatt) < TOLERANCE) { lam = lont + Math.atan2(fromX, ((latt > 0) ? -fromY : fromY)); } else { lam = lont + Math.atan2(fromX * sinc, rho * coslatt * cosc - fromY * sinc * sinlatt); } toLon = Math.toDegrees(lam); toLatA[i] = toLat; toLonA[i] = toLon; } return to; } /*ENDGENERATED*/ }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy