Reputation: 1545
Say I have an arbitrary set of latitude and longitude pairs representing points on some simple, closed curve. In Cartesian space I could easily calculate the area enclosed by such a curve using Green's Theorem. What is the analogous approach to calculating the area on the surface of a sphere? I guess what I am after is (even some approximation of) the algorithm behind Matlab's areaint
function.
Upvotes: 27
Views: 31210
Reputation: 119
As of now google maps utils library provide a method to calculate the Area.
You have to add below dependency
implementation("com.google.maps.android:android-maps-utils:3.8.2")
And then call below method and give it yours LatLng list. It will return area in square meters.
fun calculateSurfaceArea(latLngList: List<LatLng>): Double {
if (latLngList.size < 3) {
return 0.0
}
return SphericalUtil.computeArea(latLngList)
}
Upvotes: 0
Reputation: 1720
If your polygon fits within some hemisphere then you can sum up the triangular areas swept out by each of the polygon's edges from its first vertex. The following formula gets the area of a spherical triangle with unit vector vertices A, B and C:
area = 2 * atan( (AxB.C) / (1 + A.B + B.C + C.A) )
The above formula uses dot products, and (AxB.C) is the triple product. The calculated area is signed, so this method will handle concave polygons automatically.
If your polygon does not fit onto a hemisphere then some vertices could be >180 degrees apart in the polygon, and the above formula will measure the triangle with the wrong part of the great circle. So it may fail.
Note: An earlier answer suggested using Girard's Theorem to get the area of a spherical triangle. This will be OK with simple convex polygons, but it is difficult to use Girard's Theorem with concave polygons as cutting the polygon into triangles that lie inside the polygon is difficult. Also, Girard's Theorem tends to blow up if there are repeated vertices, and/or get inaccurate answers when vertices are very close together. The area formula in this answer behaves better with repeated and close vertices.
Upvotes: 1
Reputation: 1015
You could also have a look at this code of the spherical_geometry
package: Here and here. It does provide two different methods for calculating the area of a spherical polygon.
Upvotes: 0
Reputation: 723
Here is a Python 3 implementation, loosely inspired by the above answers:
def polygon_area(lats, lons, algorithm = 0, radius = 6378137):
"""
Computes area of spherical polygon, assuming spherical Earth.
Returns result in ratio of the sphere's area if the radius is specified.
Otherwise, in the units of provided radius.
lats and lons are in degrees.
"""
from numpy import arctan2, cos, sin, sqrt, pi, power, append, diff, deg2rad
lats = np.deg2rad(lats)
lons = np.deg2rad(lons)
# Line integral based on Green's Theorem, assumes spherical Earth
#close polygon
if lats[0]!=lats[-1]:
lats = append(lats, lats[0])
lons = append(lons, lons[0])
#colatitudes relative to (0,0)
a = sin(lats/2)**2 + cos(lats)* sin(lons/2)**2
colat = 2*arctan2( sqrt(a), sqrt(1-a) )
#azimuths relative to (0,0)
az = arctan2(cos(lats) * sin(lons), sin(lats)) % (2*pi)
# Calculate diffs
# daz = diff(az) % (2*pi)
daz = diff(az)
daz = (daz + pi) % (2 * pi) - pi
deltas=diff(colat)/2
colat=colat[0:-1]+deltas
# Perform integral
integrands = (1-cos(colat)) * daz
# Integrate
area = abs(sum(integrands))/(4*pi)
area = min(area,1-area)
if radius is not None: #return in units of radius
return area * 4*pi*radius**2
else: #return in ratio of sphere total area
return area
Please find a somewhat more explicit version (and with many more references and TODOs...) here.
Upvotes: 1
Reputation: 69252
There several ways to do this.
1) Integrate the contributions from latitudinal strips. Here the area of each strip will be (Rcos(A)(B1-B0))(RdA), where A is the latitude, B1 and B0 are the starting and ending longitudes, and all angles are in radians.
2) Break the surface into spherical triangles, and calculate the area using Girard's Theorem, and add these up.
3) As suggested here by James Schek, in GIS work they use an area preserving projection onto a flat space and calculate the area in there.
From the description of your data, in sounds like the first method might be the easiest. (Of course, there may be other easier methods I don't know of.)
Edit – comparing these two methods:
On first inspection, it may seem that the spherical triangle approach is easiest, but, in general, this is not the case. The problem is that one not only needs to break the region up into triangles, but into spherical triangles, that is, triangles whose sides are great circle arcs. For example, latitudinal boundaries don't qualify, so these boundaries need to be broken up into edges that better approximate great circle arcs. And this becomes more difficult to do for arbitrary edges where the great circles require specific combinations of spherical angles. Consider, for example, how one would break up a middle band around a sphere, say all the area between lat 0 and 45deg into spherical triangles.
In the end, if one is to do this properly with similar errors for each method, method 2 will give fewer triangles, but they will be harder to determine. Method 1 gives more strips, but they are trivial to determine. Therefore, I suggest method 1 as the better approach.
Upvotes: 13
Reputation: 919
I rewrote the MATLAB's "areaint" function in java, which has exactly the same result. "areaint" calculates the "suface per unit", so I multiplied the answer by Earth's Surface Area (5.10072e14 sq m).
private double area(ArrayList<Double> lats,ArrayList<Double> lons)
{
double sum=0;
double prevcolat=0;
double prevaz=0;
double colat0=0;
double az0=0;
for (int i=0;i<lats.size();i++)
{
double colat=2*Math.atan2(Math.sqrt(Math.pow(Math.sin(lats.get(i)*Math.PI/180/2), 2)+ Math.cos(lats.get(i)*Math.PI/180)*Math.pow(Math.sin(lons.get(i)*Math.PI/180/2), 2)),Math.sqrt(1- Math.pow(Math.sin(lats.get(i)*Math.PI/180/2), 2)- Math.cos(lats.get(i)*Math.PI/180)*Math.pow(Math.sin(lons.get(i)*Math.PI/180/2), 2)));
double az=0;
if (lats.get(i)>=90)
{
az=0;
}
else if (lats.get(i)<=-90)
{
az=Math.PI;
}
else
{
az=Math.atan2(Math.cos(lats.get(i)*Math.PI/180) * Math.sin(lons.get(i)*Math.PI/180),Math.sin(lats.get(i)*Math.PI/180))% (2*Math.PI);
}
if(i==0)
{
colat0=colat;
az0=az;
}
if(i>0 && i<lats.size())
{
sum=sum+(1-Math.cos(prevcolat + (colat-prevcolat)/2))*Math.PI*((Math.abs(az-prevaz)/Math.PI)-2*Math.ceil(((Math.abs(az-prevaz)/Math.PI)-1)/2))* Math.signum(az-prevaz);
}
prevcolat=colat;
prevaz=az;
}
sum=sum+(1-Math.cos(prevcolat + (colat0-prevcolat)/2))*(az0-prevaz);
return 5.10072E14* Math.min(Math.abs(sum)/4/Math.PI,1-Math.abs(sum)/4/Math.PI);
}
Upvotes: 12
Reputation: 17970
You mention "geography" in one of your tags so I can only assume you are after the area of a polygon on the surface of a geoid. Normally, this is done using a projected coordinate system rather than a geographic coordinate system (i.e. lon/lat). If you were to do it in lon/lat, then I would assume the unit-of-measure returned would be percent of sphere surface.
If you want to do this with a more "GIS" flavor, then you need to select an unit-of-measure for your area and find an appropriate projection that preserves area (not all do). Since you are talking about calculating an arbitrary polygon, I would use something like a Lambert Azimuthal Equal Area projection. Set the origin/center of the projection to be the center of your polygon, project the polygon to the new coordinate system, then calculate the area using standard planar techniques.
If you needed to do many polygons in a geographic area, there are likely other projections that will work (or will be close enough). UTM, for example, is an excellent approximation if all of your polygons are clustered around a single meridian.
I am not sure if any of this has anything to do with how Matlab's areaint function works.
Upvotes: 9
Reputation: 497752
I don't know anything about Matlab's function, but here we go. Consider splitting your spherical polygon into spherical triangles, say by drawing diagonals from a vertex. The surface area of a spherical triangle is given by
R^2 * ( A + B + C - \pi)
where R
is the radius of the sphere, and A
, B
, and C
are the interior angles of the triangle (in radians). The quantity in the parentheses is known as the "spherical excess".
Your n
-sided polygon will be split into n-2
triangles. Summing over all the triangles, extracting the common factor of R^2
, and bringing all of the \pi
together, the area of your polygon is
R^2 * ( S - (n-2)\pi )
where S
is the angle sum of your polygon. The quantity in parentheses is again the spherical excess of the polygon.
[edit] This is true whether or not the polygon is convex. All that matters is that it can be dissected into triangles.
You can determine the angles from a bit of vector math. Suppose you have three vertices A
,B
,C
and are interested in the angle at B
. We must therefore find two tangent vectors (their magnitudes are irrelevant) to the sphere from point B
along the great circle segments (the polygon edges). Let's work it out for BA
. The great circle lies in the plane defined by OA
and OB
, where O
is the center of the sphere, so it should be perpendicular to the normal vector OA x OB
. It should also be perpendicular to OB
since it's tangent there. Such a vector is therefore given by OB x (OA x OB)
. You can use the right-hand rule to verify that this is in the appropriate direction. Note also that this simplifies to OA * (OB.OB) - OB * (OB.OA) = OA * |OB| - OB * (OB.OA)
.
You can then use the good ol' dot product to find the angle between sides: BA'.BC' = |BA'|*|BC'|*cos(B)
, where BA'
and BC'
are the tangent vectors from B
along sides to A
and C
.
[edited to be clear that these are tangent vectors, not literal between the points]
Upvotes: 5