/** Return the distance (measured along the surface of the sphere) to the given point. */ public S1Angle getDistance(final S2LatLng o) { // This implements the Haversine formula, which is numerically stable for // small distances but only gets about 8 digits of precision for very large // distances (e.g. antipodal points). Note that 8 digits is still accurate // to within about 10cm for a sphere the size of the Earth. // // This could be fixed with another sin() and cos() below, but at that point // you might as well just convert both arguments to S2Points and compute the // distance that way (which gives about 15 digits of accuracy for all // distances). double lat1 = lat().radians(); double lat2 = o.lat().radians(); double lng1 = lng().radians(); double lng2 = o.lng().radians(); double dlat = Math.sin(0.5 * (lat2 - lat1)); double dlng = Math.sin(0.5 * (lng2 - lng1)); double x = dlat * dlat + dlng * dlng * Math.cos(lat1) * Math.cos(lat2); return S1Angle.radians(2 * Math.atan2(Math.sqrt(x), Math.sqrt(Math.max(0.0, 1.0 - x)))); // Return the distance (measured along the surface of the sphere) to the // given S2LatLng. This is mathematically equivalent to: // // S1Angle::FromRadians(ToPoint().Angle(o.ToPoint()) // // but this implementation is slightly more efficient. }
/** * Basic constructor. The latitude and longitude must be within the ranges allowed by is_valid() * below. * * <p>TODO(dbeaumont): Make this a static factory method (fromLatLng() ?). */ public S2LatLng(S1Angle lat, S1Angle lng) { this(lat.radians(), lng.radians()); }
public static S1Angle longitude(S2Point p) { // Note that atan2(0, 0) is defined to be zero. return S1Angle.radians(Math.atan2(p.get(1), p.get(0))); }
public static S1Angle latitude(S2Point p) { // We use atan2 rather than asin because the input vector is not necessarily // unit length, and atan2 is much more accurate than asin near the poles. return S1Angle.radians( Math.atan2(p.get(2), Math.sqrt(p.get(0) * p.get(0) + p.get(1) * p.get(1)))); }
/** Returns the longitude of this point as a new S1Angle. */ public S1Angle lng() { return S1Angle.radians(this.lngRadians); }
/** Returns the latitude of this point as a new S1Angle. */ public S1Angle lat() { return S1Angle.radians(this.latRadians); }