如何计算 GeoDjango 中两点之间的 3D 距离(包括高度)
How to calculate 3D distance (including altitude) between two points in GeoDjango
序言:
这是SO中经常出现的问题:
- Calculating distance between two points using latitude longitude and altitude (elevation)
- Distance between two 3D point in geodjango (postgis)
我想编写一个关于 SO 文档的示例,但是 geodjango
章节从未起飞,并且由于文档于 2017 年 8 月 8 日关闭,我将遵循 this widely upvoted and discussed meta answer 的建议并且将我的例子写成自我回答 post.
当然,我也很乐意看到任何不同的方法!!
问题:
假设模型:
class MyModel(models.Model):
name = models.CharField()
coordinates = models.PointField()
我将 coordinate
变量中的点存储为 lan, lng, alt
点:
MyModel.objects.create(
name='point_name',
coordinates='SRID=3857;POINT Z (100.00 10.00 150)')
我正在尝试计算两个这样的点之间的 3D 距离:
p1 = MyModel.objects.get(name='point_1').coordinates
p2 = MyModel.objects.get(name='point_2').coordinates
d = Distance(m=p1.distance(p2))
现在 d=X
以米为单位。
如果我只改变其中一个点的高度:
例如:
p1.coordinates = 'SRID=3857;POINT Z (100.00 10.00 200)'
由之前的150计算:
d = Distance(m=p1.distance(p2))
又是returnsd=X
,好像忽略了高程。
如何计算点之间的 3D 距离?
阅读有关 GEOSGeometry.distance
方法的文档:
Returns the distance between the closest points on this geometry and the given geom (another GEOSGeometry object).
Note
GEOS distance calculations are linear – in other words, GEOS does not perform a spherical calculation even if the SRID specifies a geographic coordinate system.
因此我们需要实现一种方法来计算两点之间更准确的二维距离,然后我们可以尝试应用这些点之间的高度 (Z) 差异。
1.大圆二维距离计算:
计算球体表面两点之间距离的最常用方法(因为地球是简单但通常建模的)是 Haversine formula:
The haversine formula determines the great-circle distance between two points on a sphere given their longitudes and latitudes.
尽管从 great-circle distance wiki page 我们读到:
Although this formula is accurate for most distances on a sphere, it too suffers from rounding errors for the special (and somewhat unusual) case of antipodal points (on opposite ends of the sphere). A formula that is accurate for all distances is the following special case of the Vincenty formula for an ellipsoid with equal major and minor axes.
我们可以创建我们自己的 Haversine 或 Vincenty 公式的实现(如此处所示的 Haversine:Haversine Formula in Python (Bearing and Distance between two GPS points)) or we can use one of the already implemented methods contained in geopy:
geopy.distance.great_circle
(半正矢):
from geopy.distance import great_circle
newport_ri = (41.49008, -71.312796)
cleveland_oh = (41.499498, -81.695391)
# This call will result in 536.997990696 miles
great_circle(newport_ri, cleveland_oh).miles)
geopy.distance.vincenty
(文森蒂):
from geopy.distance import vincenty
newport_ri = (41.49008, -71.312796)
cleveland_oh = (41.499498, -81.695391)
# This call will result in 536.997990696 miles
vincenty(newport_ri, cleveland_oh).miles
2。在混合中添加高度:
如前所述,上述每个计算都会产生两点之间的大圆距离。该距离也称为“乌鸦飞行”,假设“乌鸦”在不改变高度的情况下飞行,并且尽可能笔直地从点 A 飞到点 B。
我们可以更好地估计“walking/driving”(“as the crow walks”??)距离,方法是将之前方法之一的结果与两者之间的高度差(delta)相结合A点和B点,在Euclidean Formula内进行距离计算:
acw_dist = sqrt(great_circle(p1, p2).m**2 + (p1.z - p2.z)**2)
以前的解决方案容易出错,尤其是点之间的实际距离越长。
我把它留在这里是为了继续评论。
GeoDjango Distance
计算两点之间的二维距离,不考虑高度差。
为了获得 3D 计算,我们需要创建一个距离函数,在计算中考虑高度差异:
理论:
latitude
、longitude
和altitude
是极坐标,我们需要将它们转换为直角坐标(x
, y
, z
) 以便对它们应用 Euclidean Formula 并计算它们的 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)
你将分别获得 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)
转换为笛卡尔坐标后,您可以使用 numpy 计算范数:
np.linalg.norm(point_1 - point_2)
使用geopy
,这是最简单完美的解决方案。
https://geopy.readthedocs.io/en/stable/#geopy.distance.lonlat
>>> from geopy.distance import distance
>>> from geopy.point import Point
>>> a = Point(-71.312796, 41.49008, 0)
>>> b = Point(-81.695391, 41.499498, 0)
>>> print(distance(a, b).miles)
538.3904453677203
序言:
这是SO中经常出现的问题:
- Calculating distance between two points using latitude longitude and altitude (elevation)
- Distance between two 3D point in geodjango (postgis)
我想编写一个关于 SO 文档的示例,但是 geodjango
章节从未起飞,并且由于文档于 2017 年 8 月 8 日关闭,我将遵循 this widely upvoted and discussed meta answer 的建议并且将我的例子写成自我回答 post.
当然,我也很乐意看到任何不同的方法!!
问题:
假设模型:
class MyModel(models.Model):
name = models.CharField()
coordinates = models.PointField()
我将 coordinate
变量中的点存储为 lan, lng, alt
点:
MyModel.objects.create(
name='point_name',
coordinates='SRID=3857;POINT Z (100.00 10.00 150)')
我正在尝试计算两个这样的点之间的 3D 距离:
p1 = MyModel.objects.get(name='point_1').coordinates
p2 = MyModel.objects.get(name='point_2').coordinates
d = Distance(m=p1.distance(p2))
现在 d=X
以米为单位。
如果我只改变其中一个点的高度:
例如:
p1.coordinates = 'SRID=3857;POINT Z (100.00 10.00 200)'
由之前的150计算:
d = Distance(m=p1.distance(p2))
又是returnsd=X
,好像忽略了高程。
如何计算点之间的 3D 距离?
阅读有关 GEOSGeometry.distance
方法的文档:
Returns the distance between the closest points on this geometry and the given geom (another GEOSGeometry object).
Note
GEOS distance calculations are linear – in other words, GEOS does not perform a spherical calculation even if the SRID specifies a geographic coordinate system.
因此我们需要实现一种方法来计算两点之间更准确的二维距离,然后我们可以尝试应用这些点之间的高度 (Z) 差异。
1.大圆二维距离计算:
计算球体表面两点之间距离的最常用方法(因为地球是简单但通常建模的)是 Haversine formula:
The haversine formula determines the great-circle distance between two points on a sphere given their longitudes and latitudes.
尽管从 great-circle distance wiki page 我们读到:
Although this formula is accurate for most distances on a sphere, it too suffers from rounding errors for the special (and somewhat unusual) case of antipodal points (on opposite ends of the sphere). A formula that is accurate for all distances is the following special case of the Vincenty formula for an ellipsoid with equal major and minor axes.
我们可以创建我们自己的 Haversine 或 Vincenty 公式的实现(如此处所示的 Haversine:Haversine Formula in Python (Bearing and Distance between two GPS points)) or we can use one of the already implemented methods contained in geopy:
geopy.distance.great_circle
(半正矢):from geopy.distance import great_circle newport_ri = (41.49008, -71.312796) cleveland_oh = (41.499498, -81.695391) # This call will result in 536.997990696 miles great_circle(newport_ri, cleveland_oh).miles)
geopy.distance.vincenty
(文森蒂):from geopy.distance import vincenty newport_ri = (41.49008, -71.312796) cleveland_oh = (41.499498, -81.695391) # This call will result in 536.997990696 miles vincenty(newport_ri, cleveland_oh).miles
2。在混合中添加高度:
如前所述,上述每个计算都会产生两点之间的大圆距离。该距离也称为“乌鸦飞行”,假设“乌鸦”在不改变高度的情况下飞行,并且尽可能笔直地从点 A 飞到点 B。
我们可以更好地估计“walking/driving”(“as the crow walks”??)距离,方法是将之前方法之一的结果与两者之间的高度差(delta)相结合A点和B点,在Euclidean Formula内进行距离计算:
acw_dist = sqrt(great_circle(p1, p2).m**2 + (p1.z - p2.z)**2)
以前的解决方案容易出错,尤其是点之间的实际距离越长。
我把它留在这里是为了继续评论。
GeoDjango Distance
计算两点之间的二维距离,不考虑高度差。
为了获得 3D 计算,我们需要创建一个距离函数,在计算中考虑高度差异:
理论:
latitude
、longitude
和altitude
是极坐标,我们需要将它们转换为直角坐标(x
, y
, z
) 以便对它们应用 Euclidean Formula 并计算它们的 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)
你将分别获得 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)
转换为笛卡尔坐标后,您可以使用 numpy 计算范数:
np.linalg.norm(point_1 - point_2)
使用geopy
,这是最简单完美的解决方案。
https://geopy.readthedocs.io/en/stable/#geopy.distance.lonlat
>>> from geopy.distance import distance
>>> from geopy.point import Point
>>> a = Point(-71.312796, 41.49008, 0)
>>> b = Point(-81.695391, 41.499498, 0)
>>> print(distance(a, b).miles)
538.3904453677203