/** * Forward projection, from geographic to gnomonic. * <p> * @param lat0 latitude of center point of projection (degrees). * @param lon0 longitude of center point of projection (degrees). * @param lat latitude of point (degrees). * @param lon longitude of point (degrees). * @return {@link GnomonicData} object with the following fields: * <i>lat0</i>, <i>lon0</i>, <i>lat</i>, <i>lon</i>, <i>x</i>, <i>y</i>, * <i>azi</i>, <i>rk</i>. * <p> * <i>lat0</i> and <i>lat</i> should be in the range [−90°, * 90°] and <i>lon0</i> and <i>lon</i> should be in the range * [−540°, 540°). The scale of the projection is * 1/<i>rk<sup>2</sup></i> in the "radial" direction, <i>azi</i> clockwise * from true north, and is 1/<i>rk</i> in the direction perpendicular to * this. If the point lies "over the horizon", i.e., if <i>rk</i> ≤ 0, * then NaNs are returned for <i>x</i> and <i>y</i> (the correct values are * returned for <i>azi</i> and <i>rk</i>). A call to Forward followed by a * call to Reverse will return the original (<i>lat</i>, <i>lon</i>) (to * within roundoff) provided the point in not over the horizon. */ public GnomonicData Forward(double lat0, double lon0, double lat, double lon) { GeodesicData inv = _earth.Inverse(lat0, lon0, lat, lon, GeodesicMask.AZIMUTH | GeodesicMask.GEODESICSCALE | GeodesicMask.REDUCEDLENGTH); GnomonicData fwd = new GnomonicData(lat0, lon0, lat, lon, Double.NaN, Double.NaN, inv.FinalAzimuth, inv.GeodesicScale12); if (inv.GeodesicScale12 > 0) { double rho = inv.ReducedLength / inv.GeodesicScale12; Pair p = GeoMath.SinCosD(inv.InitialAzimuth); fwd.x = rho * p.First; fwd.y = rho * p.Second; } return(fwd); }
/** * Reverse projection, from gnomonic to geographic. * <p> * @param lat0 latitude of center point of projection (degrees). * @param lon0 longitude of center point of projection (degrees). * @param x easting of point (meters). * @param y northing of point (meters). * @return {@link GnomonicData} object with the following fields: * <i>lat0</i>, <i>lon0</i>, <i>lat</i>, <i>lon</i>, <i>x</i>, <i>y</i>, * <i>azi</i>, <i>rk</i>. * <p> * <i>lat0</i> should be in the range [−90°, 90°] and * <i>lon0</i> should be in the range [−540°, 540°). * <i>lat</i> will be in the range [−90°, 90°] and <i>lon</i> * will be in the range [−180°, 180°]. The scale of the * projection is 1/<i>rk<sup>2</sup></i> in the "radial" direction, * <i>azi</i> clockwise from true north, and is 1/<i>rk</i> in the direction * perpendicular to this. Even though all inputs should return a valid * <i>lat</i> and <i>lon</i>, it's possible that the procedure fails to * converge for very large <i>x</i> or <i>y</i>; in this case NaNs are * returned for all the output arguments. A call to Reverse followed by a * call to Forward will return the original (<i>x</i>, <i>y</i>) (to * roundoff). */ public GnomonicData Reverse(double lat0, double lon0, double x, double y) { GnomonicData rev = new GnomonicData(lat0, lon0, Double.NaN, Double.NaN, x, y, Double.NaN, Double.NaN); double azi0 = GeoMath.Atan2d(x, y); double rho = GeoMath.Hypot(x, y); double s = _a * Math.Atan(rho / _a); bool little = rho <= _a; if (!little) { rho = 1 / rho; } GeodesicLine line = _earth.Line(lat0, lon0, azi0, GeodesicMask.LATITUDE | GeodesicMask.LONGITUDE | GeodesicMask.AZIMUTH | GeodesicMask.DISTANCE_IN | GeodesicMask.REDUCEDLENGTH | GeodesicMask.GEODESICSCALE); int count = numit_, trip = 0; GeodesicData pos = null; while (count-- > 0) { pos = line.Position(s, GeodesicMask.LONGITUDE | GeodesicMask.LATITUDE | GeodesicMask.AZIMUTH | GeodesicMask.DISTANCE_IN | GeodesicMask.REDUCEDLENGTH | GeodesicMask.GEODESICSCALE); if (trip > 0) { break; } double ds = little ? ((pos.ReducedLength / pos.GeodesicScale12) - rho) * pos.GeodesicScale12 * pos.GeodesicScale12 : (rho - (pos.GeodesicScale12 / pos.ReducedLength)) * pos.ReducedLength * pos.ReducedLength; s -= ds; if (Math.Abs(ds) <= eps_ * _a) { trip++; } } if (trip == 0) { return(rev); } rev.PointLatitude = pos.Latitude2; rev.PointLongitude = pos.Longitude2; rev.azi = pos.FinalAzimuth; rev.rk = pos.GeodesicScale12; return(rev); }