使用 GeoDjango 进行 3d 距离计算

3d distance calculations with GeoDjango

我正在使用

第一题

我需要使用 GeoDjango 来计算两点之间的距离。当我检查 documentation 时,它说 GeoQuerySet.distance() 已弃用,而是使用 Distance() 来自 django.contrib.gis.db.models.functions.

以下代码工作正常:

from django.contrib.gis.db.models.functions import Distance

p1 = Instrument.objects.get(pk=151071000).coordinates
p2 = Instrument.objects.filter(pk=151071008)

for i in p2.annotate(distance=Distance('coordinates', p1)):
    print i.distance
    print i.distance.__class__

输出:

461.10913945 m
<class 'django.contrib.gis.measure.Distance'>

我的模特:

class Instrument(models.Model):
    ...
    coordinates = gis_models.PointField(null=True, blank=True, dim=3)

但我只有两点,所以当我尝试使用不带 annotate() 的 Distance() 时,它 returns class [=42] 的实例=]django.contrib.gis.db.models.functions.Distance() 而不是 django.contrib.gis.measure.Distance():

p1 = Instrument.objects.get(pk=151071000).coordinates
p2 = Instrument.objects.get(pk=151071008).coordinates
print Distance(p1, p2)

输出:

Distance(Value(SRID=4326;POINT Z (-76.48623600000001 44.260223 0)), GeomValue(SRID=4326;POINT Z (-76.490923 44.262658 0)))

如何获得与使用 annotate() 相同的结果?

第二题

我必须计算考虑到 depth/elevation 的 3d 距离。但是当我尝试这样做时,我收到与 2d 相同的结果。下面我在第一个对象中将海拔更改为 200:

p1 = Instrument.objects.get(pk=151071000)
p1.coordinates = 'SRID=4326;POINT Z (-76.48623600000001 44.260223 200)'
p2 = Instrument.objects.filter(pk=151071008)

for i in p2.annotate(distance=Distance('coordinates', p1.coordinates)):
    print i.distance

输出:

461.10913945 m

让我们分解问题:

  1. Distanceclass文档中,我们可以阅读到以下内容:

    Accepts two geographic fields or expressions and returns the distance between them, as a Distance object.

    所以 Distance(p1, p2) returns 一个 Distance object.
    如果你这样做:

    p1 = Instrument.objects.get(pk=151071000).coordinates
    p2 = Instrument.objects.get(pk=151071008).coordinates
    d = Distance(m=p1.distance(p2))
    print d.m
    

    您将获得以米为单位的测量值。

    我会坚持使用 annotate 解决方案,它看起来更可靠! (自以为是的回应)


  1. Distance 计算两点之间的二维距离。为了获得 3D 计算,您需要自己创建一个。
    你可以从这个问题看看我的方法:Calculating distance between two points using latitude longitude and altitude (elevation)

    2019 年编辑: 自最初的回答以来,我在这里编写了一个问答式示例: 使用了更好(并且计算错误更少)的距离2点之间的高度计算。

    排序:

    我们需要计算它们之间的二维great-circle distance between 2 points using either the Haversine formula or the Vicenty formula and then we can combine it with the difference (delta) in altitude between the 2 points to calculate the Euclidean distance如下:

    dist = sqrt(great_circle((lat_1, lon_1), (lat-2, lon_2).m**2, (alt_1 - alt_2)**2)
    

    该解决方案假定海拔高度以米为单位,因此也将 great_circle 的结果转换为米。


留在这里是为了继续评论。

2. Distance计算两点之间的二维距离。为了获得 3D 计算,您需要自己创建一个。
你可以从这个问题看看我的方法:Calculating distance between two points using latitude longitude and altitude (elevation)

  • Let polar_point_1 = (long_1, lat_1, alt_1) and polar_point_2 = (long_2, lat_2, alt_2)

  • Translate each point to it's Cartesian equivalent by utilizing this formula:

    x = alt * cos(lat) * sin(long)
    y = alt * sin(lat)
    z = alt * cos(lat) * cos(long)
    

    and you will have p_1 = (x_1, y_1, z_1) and p_2 = (x_2, y_2, z_2) points respectively.

  • Finally use the Euclidean formula:

    dist = sqrt((x_2-x_1)**2 + (y_2-y_1)**2 + (z_2-z_1)**2)
    

** python 3.7,Django 2.2.10

我编写了一些有用的函数来帮助我处理应用程序中的 lat/lon 坐标

from django.contrib.gis.geos import Point
from django.contrib.gis.measure import Distance


def get_point(lat, lon):
    try:
        lat = float(lat)
        lon = float(lon)
        if lat and lon:
            return Point(x=float(round(lon, 6)), y=float(round(lat, 6)), srid=4326)
        else:
            return None
    except Exception:
        return None


def get_point_to_point_distance(point_one, point_two, measurement_unit="mile"):
    return Distance(**{measurement_unit: point_one.distance(point_two)})

其中 measurement_unit 可以是以下任何一项:

    STANDARD_UNIT = "m"
    UNITS = {
        'chain': 20.1168,
        'chain_benoit': 20.116782,
        'chain_sears': 20.1167645,
        'british_chain_benoit': 20.1167824944,
        'british_chain_sears': 20.1167651216,
        'british_chain_sears_truncated': 20.116756,
        'cm': 0.01,
        'british_ft': 0.304799471539,
        'british_yd': 0.914398414616,
        'clarke_ft': 0.3047972654,
        'clarke_link': 0.201166195164,
        'fathom': 1.8288,
        'ft': 0.3048,
        'german_m': 1.0000135965,
        'gold_coast_ft': 0.304799710181508,
        'indian_yd': 0.914398530744,
        'inch': 0.0254,
        'km': 1000.0,
        'link': 0.201168,
        'link_benoit': 0.20116782,
        'link_sears': 0.20116765,
        'm': 1.0,
        'mi': 1609.344,
        'mm': 0.001,
        'nm': 1852.0,
        'nm_uk': 1853.184,
        'rod': 5.0292,
        'sears_yd': 0.91439841,
        'survey_ft': 0.304800609601,
        'um': 0.000001,
        'yd': 0.9144,
    }

    # Unit aliases for `UNIT` terms encountered in Spatial Reference WKT.
    ALIAS = {
        'centimeter': 'cm',
        'foot': 'ft',
        'inches': 'inch',
        'kilometer': 'km',
        'kilometre': 'km',
        'meter': 'm',
        'metre': 'm',
        'micrometer': 'um',
        'micrometre': 'um',
        'millimeter': 'mm',
        'millimetre': 'mm',
        'mile': 'mi',
        'yard': 'yd',
        'British chain (Benoit 1895 B)': 'british_chain_benoit',
        'British chain (Sears 1922)': 'british_chain_sears',
        'British chain (Sears 1922 truncated)': 'british_chain_sears_truncated',
        'British foot (Sears 1922)': 'british_ft',
        'British foot': 'british_ft',
        'British yard (Sears 1922)': 'british_yd',
        'British yard': 'british_yd',
        "Clarke's Foot": 'clarke_ft',
        "Clarke's link": 'clarke_link',
        'Chain (Benoit)': 'chain_benoit',
        'Chain (Sears)': 'chain_sears',
        'Foot (International)': 'ft',
        'German legal metre': 'german_m',
        'Gold Coast foot': 'gold_coast_ft',
        'Indian yard': 'indian_yd',
        'Link (Benoit)': 'link_benoit',
        'Link (Sears)': 'link_sears',
        'Nautical Mile': 'nm',
        'Nautical Mile (UK)': 'nm_uk',
        'US survey foot': 'survey_ft',
        'U.S. Foot': 'survey_ft',
        'Yard (Indian)': 'indian_yd',
        'Yard (Sears)': 'sears_yd'
    }