如何在GeoDjango中计算两点之间的3D距离(包括海拔高度)

Joh*_*fis 5 python gis django postgis geodjango

序幕:

这是在SO中经常出现的一个问题:

我想撰写一个关于SO文档的示例,但是geodjango本章从未停止过,并且自从文档于2017年8月8日关闭以来,我将遵循这个广受赞誉并讨论了元答案的建议,并将我的示例写为自我回答发布。

当然,我也很高兴看到任何其他方法!!


题:

假设模型:

class MyModel(models.Model):
    name = models.CharField()
    coordinates = models.PointField()
Run Code Online (Sandbox Code Playgroud)

我将点存储在coordinate变量中作为lan, lng, alt点的位置:

MyModel.objects.create(
    name='point_name', 
    coordinates='SRID=3857;POINT Z (100.00 10.00 150)')
Run Code Online (Sandbox Code Playgroud)

我正在尝试计算两个这样的点之间的3D距离:

p1 = MyModel.objects.get(name='point_1').coordinates
p2 = MyModel.objects.get(name='point_2').coordinates

d = Distance(m=p1.distance(p2))
Run Code Online (Sandbox Code Playgroud)

现在d=X以米为单位。

如果仅更改其中一个点的高度,则:

例如:

p1.coordinates = 'SRID=3857;POINT Z (100.00 10.00 200)'
Run Code Online (Sandbox Code Playgroud)

从150以前,计算:

d = Distance(m=p1.distance(p2))
Run Code Online (Sandbox Code Playgroud)

d=X再次返回,就像忽略高程一样。
如何计算两点之间的3D距离?

Joh*_*fis 6

从该GEOSGeometry.distance方法的文档中阅读:

返回此几何体上最近点与给定几何体(另一个 GEOSGeometry 对象)之间的距离。

笔记

GEOS 距离计算是线性的——换句话说,即使 SRID 指定了地理坐标系,GEOS 也不执行球面计算。

因此,我们需要实现一种方法来计算 2 点之间更准确的 2D 距离,然后我们可以尝试应用这些点之间的高度 (Z) 差。

1.大圆二维距离计算

计算球体表面两点之间距离的最常用方法(因为地球很简单但通常建模)是Haversine公式

正弦公式确定球面上两点之间的大圆距离,给定它们的经度和纬度。

虽然从大圆距离维基页面我们读到:

尽管此公式对于球体上的大多数距离都是准确的,但对于对映点(在球体的相对两端)的特殊(且有些不寻常)情况,它也存在舍入误差。对于所有距离都准确的公式是以下Vincenty 公式的特例,用于长轴和短轴相等的椭球。

我们可以创建自己的Haversine 或Vincenty 公式实现(如Haversine 所示:Python 中的Haversine 公式(两个GPS 点之间的轴承和距离)),或者我们可以使用geopy 中已实现的方法之一:

2. 在混合中添加高度:

如上所述,上述每个计算都会产生 2 点之间的大圆距离。该距离也称为“乌鸦飞翔”,假设“乌鸦”从 A 点飞到 B 点时高度不变,并且尽可能笔直地飞行。

通过将前面一种方法的结果与 A 点和 B 点之间的高度差(delta)相结合,我们可以更好地估计“步行/驾驶”(“乌鸦走路”??)距离距离计算的欧几里得公式

acw_dist = sqrt(great_circle(p1, p2).m**2, (p1.z - p2.z)**2)
Run Code Online (Sandbox Code Playgroud)

以前的解决方案容易出错,尤其是点之间的实际距离越长。
我把它留在这里是为了评论继续的原因。

GeoDjangoDistance计算两点之间的二维距离并且不考虑高度差异。
为了获得 3D 计算,我们需要创建一个距离函数,该函数将在计算中考虑高度差异:

理论:

latitudelongitude并且altitude极坐标,我们需要把它们翻译成直角坐标系(xyz以应用欧氏公式对他们并计算它们的3D距离。

  • 假设:
    polar_point_1 = (long_1, lat_1, alt_1)

    polar_point_2 = (long_2, lat_2, alt_2)

  • 使用以下公式将每个点转换为笛卡尔等价物:

    x = alt * cos(lat) * sin(long)
    y = alt * sin(lat)
    z = alt * cos(lat) * cos(long)
    
    Run Code Online (Sandbox Code Playgroud)

    你将分别拥有p_1 = (x_1, y_1, z_1)p_2 = (x_2, y_2, z_2)积分。

  • 最后使用欧几里得公式:

    dist = sqrt((x_2-x_1)**2 + (y_2-y_1)**2 + (z_2-z_1)**2)
    
    Run Code Online (Sandbox Code Playgroud)