/** 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))));
 }
 public static S2LatLng fromE7(long latE7, long lngE7) {
   return new S2LatLng(S1Angle.e7(latE7), S1Angle.e7(lngE7));
 }
 public static S2LatLng fromE6(long latE6, long lngE6) {
   return new S2LatLng(S1Angle.e6(latE6), S1Angle.e6(lngE6));
 }
 public static S2LatLng fromE5(long latE5, long lngE5) {
   return new S2LatLng(S1Angle.e5(latE5), S1Angle.e5(lngE5));
 }
 public static S2LatLng fromDegrees(double latDegrees, double lngDegrees) {
   return new S2LatLng(S1Angle.degrees(latDegrees), S1Angle.degrees(lngDegrees));
 }
 /** 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);
 }