假设我有一组任意的纬度和经度对,表示某个简单封闭曲线上的点。在笛卡尔空间中,我可以使用格林定理轻松计算由这样的曲线所包围的面积。那么在球面上计算面积的类比方法是什么?我猜想我想知道的是(即使是一些近似)Matlab的areaint
函数背后的算法。
假设我有一组任意的纬度和经度对,表示某个简单封闭曲线上的点。在笛卡尔空间中,我可以使用格林定理轻松计算由这样的曲线所包围的面积。那么在球面上计算面积的类比方法是什么?我猜想我想知道的是(即使是一些近似)Matlab的areaint
函数背后的算法。
有几种方法可以做到这一点。
1) 整合纬度带的贡献。 这里每个带的面积将为(Rcos(A)(B1-B0))(RdA),其中A是纬度,B1和B0是起始和结束经度,所有角度都以弧度表示。
2) 将表面分成球面三角形,并使用Girard定理计算面积,并将其相加。
3) 正如James Schek在此处建议的那样,在GIS工作中,他们使用一个面积保持投影到平面空间中,并在其中计算面积。
从您的数据描述来看,第一种方法可能是最简单的。 (当然,可能还有其他我不知道的更简单的方法。)
编辑 - 比较这两种方法:
初看起来,球面三角形方法似乎最容易,但通常情况下并非如此。问题在于不仅需要将区域分割成三角形,还需要分割成球面三角形,即边是大圆弧的三角形。例如,纬度边界不符合要求,因此需要将这些边界分解为更好地近似大圆弧的边缘。当大圆需要特定的球面角的组合时,任意边缘变得更加难以处理。例如,考虑如何将围绕球体的中间带子进行分解,例如介于0度和45度之间的所有区域。
最终,如果要按照每种方法的相似误差正确完成此操作,则方法2将给出更少的三角形,但它们更难确定。方法1会产生更多条带,但它们很容易确定。因此,我建议使用方法1作为更好的方法。
我用Java重新编写了MATLAB的"areaint"函数,其结果完全相同。 "areaint"计算的是每个单位的表面积,所以我将答案乘以地球的表面积(5.10072e14平方米)。
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);
}
我不了解Matlab的函数,但是我们可以来看看。考虑通过从一个顶点画对角线将你的球面多边形分成球面三角形。球面三角形的表面积由以下公式给出:
R^2 * ( A + B + C - \pi)
其中 R
是球体的半径,A
、B
和 C
分别为三角形的内角(以弧度表示)。括号中的量被称为“球面过剩量”。
你的 n
边形将被分成 n-2
个三角形。对所有三角形求和,提取公共因数 R^2
,并将所有的 \pi
放在一起,你的多边形面积为
R^2 * ( S - (n-2)\pi )
其中S
是你的多边形的角度和。括号中的量再次是该多边形的球面超额。
[编辑]这适用于多边形是否是凸的。重要的是它可以被分解成三角形。
您可以通过一些向量数学来确定角度。假设您有三个顶点A
,B
,C
,并且对B
处的角度感兴趣。因此,我们必须找到两个切向量(它们的大小不相关)沿着大圆线段(多边形边缘)从点B
到球体上。让我们为BA
计算出来。大圆线段位于由OA
和OB
定义的平面中,其中O
是球体的中心,因此它应该垂直于法向量OA x OB
。它也应该垂直于OB
,因为它在那里是切线。因此,这样的向量由OB x (OA x OB)
给出。您可以使用右手定则验证其方向是否正确。还要注意,这简化为OA * |OB| - OB * (OB.OA)
。
然后,您可以使用点积来找到边之间的角度:BA'.BC' = |BA'|*|BC'|*cos(B)
,其中BA'
和BC'
是从B
沿着边到A
和C
的切向量。
[编辑以明确这些是切向量,而不是字面上的两点之间]
这里是一个 Python 3 实现,受上面答案的启发而来:
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
请在这里找到一个更加明确的版本(并且有更多的参考和待办事项...)这里。