/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android;
import com.google.android.gms.maps.model.LatLng;
import java.util.List;
import static com.google.maps.android.MathUtil.arcHav;
import static com.google.maps.android.MathUtil.havDistance;
import static java.lang.Math.PI;
import static java.lang.Math.abs;
import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static java.lang.Math.tan;
import static java.lang.Math.toRadians;
public class SphericalUtil {
private final static double EARTH_RADIUS = 6371000;
private SphericalUtil() {
}
/**
* Returns distance on the unit sphere; the arguments are in radians.
*/
private static double distanceRadians(double lat1, double lng1, double lat2, double lng2) {
return arcHav(havDistance(lat1, lat2, lng1 - lng2));
}
/**
* Returns the angle between two LatLngs, in radians. This is the same as the distance
* on the unit sphere.
*/
private static double computeAngleBetween(LatLng from, LatLng to) {
return distanceRadians(toRadians(from.latitude), toRadians(from.longitude),
toRadians(to.latitude), toRadians(to.longitude));
}
/**
* Returns the distance between two LatLngs, in meters.
*/
public static double computeDistanceBetween(LatLng from, LatLng to) {
return computeAngleBetween(from, to) * EARTH_RADIUS;
}
/**
* Returns the area of a closed path on Earth.
*
* @param path A closed path.
* @return The path's area in square meters.
*/
public static double computeArea(List<LatLng> path) {
return abs(computeSignedArea(path));
}
/**
* Returns the signed area of a closed path on a sphere of given radius.
* The computed area uses the same units as the radius squared.
* Used by SphericalUtilTest.
*/
private static double computeSignedArea(List<LatLng> path) {
int size = path.size();
if (size < 3) {
return 0;
}
double total = 0;
LatLng prev = path.get(size - 1);
double prevTanLat = tan((PI / 2 - toRadians(prev.latitude)) / 2);
double prevLng = toRadians(prev.longitude);
// For each edge, accumulate the signed area of the triangle formed by the North Pole
// and that edge ("polar triangle").
for (LatLng point : path) {
double tanLat = tan((PI / 2 - toRadians(point.latitude)) / 2);
double lng = toRadians(point.longitude);
total += polarTriangleArea(tanLat, lng, prevTanLat, prevLng);
prevTanLat = tanLat;
prevLng = lng;
}
return total * (EARTH_RADIUS * EARTH_RADIUS);
}
/**
* Returns the signed area of a triangle which has North Pole as a vertex.
* Formula derived from "Area of a spherical triangle given two edges and the included angle"
* as per "Spherical Trigonometry" by Todhunter, page 71, section 103, point 2.
* See http://books.google.com/books?id=3uBHAAAAIAAJ&pg=PA71
* The arguments named "tan" are tan((pi/2 - latitude)/2).
*/
private static double polarTriangleArea(double tan1, double lng1, double tan2, double lng2) {
double deltaLng = lng1 - lng2;
double t = tan1 * tan2;
return 2 * atan2(t * sin(deltaLng), 1 + t * cos(deltaLng));
}
}