@Override public GeodeticCoordinate backwardConvert(final EarthCenteredEarthFixedCoordinate coordinate) { final double semiMinor = Math.sqrt(WorldGeodeticStandardConstants.SEMI_MAJOR_AXIS_SQUARED * (1 - WorldGeodeticStandardConstants.ECCENTRICITY_SQUARED)); final double semiMinorSquared = Math.pow(semiMinor, 2); final double secondEccentricity = Math .sqrt((WorldGeodeticStandardConstants.SEMI_MAJOR_AXIS_SQUARED - semiMinorSquared) / semiMinorSquared); final double auxiliaryP = Math .sqrt(Math.pow(coordinate.getX(), 2) + Math.pow(coordinate.getY(), 2)); final double theta = Math.atan2( coordinate.getZ() * WorldGeodeticStandardConstants.SEMI_MAJOR_AXIS.asMeters(), auxiliaryP * semiMinor); final double longitude = Math.atan2(coordinate.getY(), coordinate.getX()); final double latitude = Math.atan2( coordinate.getZ() + Math.pow(secondEccentricity, 2) * semiMinor * Math.pow(Math.sin(theta), 3), auxiliaryP - WorldGeodeticStandardConstants.ECCENTRICITY_SQUARED * WorldGeodeticStandardConstants.SEMI_MAJOR_AXIS.asMeters() * Math.pow(Math.cos(theta), 3)); final double radiusOfCurviture = WorldGeodeticStandardConstants.SEMI_MAJOR_AXIS.asMeters() / Math.sqrt(1 - WorldGeodeticStandardConstants.ECCENTRICITY_SQUARED * Math.pow(Math.sin(latitude), 2)); final double altitude = auxiliaryP / Math.cos(latitude) - radiusOfCurviture; return new GeodeticCoordinate(Latitude.radians(latitude), Longitude.radians(longitude), Altitude.meters(altitude)); }
/** * The half-way point along a great-circle path between this and that point * * @param that * The other point to compute the midpoint between * @return The {@link Location} of the midpoint * @see "http://www.movable-type.co.uk/scripts/latlong.html" */ public Location midPoint(final Location that) { // Convert to Radians final double lat1 = this.getLatitude().asRadians(); final double lon1 = this.getLongitude().asRadians(); final double lat2 = that.getLatitude().asRadians(); final double lon2 = that.getLongitude().asRadians(); final double longitudeDelta = lon2 - lon1; final double xBearing = Math.cos(lat2) * Math.cos(longitudeDelta); final double yBearing = Math.cos(lat2) * Math.sin(longitudeDelta); final double pheta = Math.atan2(Math.sin(lat1) + Math.sin(lat2), Math.sqrt( (Math.cos(lat1) + xBearing) * (Math.cos(lat1) + xBearing) + yBearing * yBearing)); double lambda = lon1 + Math.atan2(yBearing, Math.cos(lat1) + xBearing); // Normalize to -180/180 lambda = (lambda + FACTOR_OF_3 * Math.PI) % (2 * Math.PI) - Math.PI; return new Location(Latitude.radians(pheta), Longitude.radians(lambda)); }
/** * Midpoint along a Rhumb line between this point and that point * * @param that * The other point to compute the midpoint between * @return The {@link Location} of the loxodromic midpoint * @see "http://www.movable-type.co.uk/scripts/latlong.html" */ public Location loxodromicMidPoint(final Location that) { // Convert to Radians final double lat1 = this.getLatitude().asRadians(); double lon1 = this.getLongitude().asRadians(); final double lat2 = that.getLatitude().asRadians(); final double lon2 = that.getLongitude().asRadians(); // Crossing anti-meridian if (Math.abs(lon2 - lon1) > Math.PI) { lon1 += 2 * Math.PI; } final double pheta = (lat1 + lat2) / 2; final double phi1 = Math.tan(Math.PI / 4 + lat1 / 2); final double phi2 = Math.tan(Math.PI / 4 + lat2 / 2); final double phi3 = Math.tan(Math.PI / 4 + pheta / 2); double lambda = ((lon2 - lon1) * Math.log(phi3) + lon1 * Math.log(phi2) - lon2 * Math.log(phi1)) / Math.log(phi2 / phi1); // Normalize to -180/180 lambda = (lambda + FACTOR_OF_3 * Math.PI) % (2 * Math.PI) - Math.PI; return new Location(Latitude.radians(pheta), Longitude.radians(lambda)); }