3d distance calculations with GeoDjango

后端 未结 2 1907
眼角桃花
眼角桃花 2020-12-19 21:10

I am using

  • python 2.7.12
  • django 1.10.6
  • postgreSQL 9.5.6
  • postGIS 2.2.2

First question

I ne

相关标签:
2条回答
  • 2020-12-19 21:39

    ** python 3.7, Django 2.2.10

    some useful functions ive written to help me with lat/lon coordinates in applications

    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)})
    
    

    where measurement_unit can be any of the following:

        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'
        }
    
    0 讨论(0)
  • 2020-12-19 22:01

    Let's break the problem down:

    1. In the Distance class documentation, we can read the following:

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

      So the Distance(p1, p2) returns a Distance object.
      If you do:

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

      You will get the measurement in meters.

      I would stick with the annotate solution, which seems more solid! (opinionated response)


    1. Distance calculates the 2D distance between two points. In order to get a 3D calculation, you need to create one yourself.
      You can have a look at my method from this question: Calculating distance between two points using latitude longitude and altitude (elevation)

      EDIT 2019: Since the initial answer I have composed a Q&A style example here: How to calculate 3D distance (including altitude) between two points in GeoDjango that uses a far better (and less calculation error-prone) distance calculation between 2 points with altitude.

      In sort:

      We need to calculate the 2D 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 between them as follows:

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

      The solution assumes that the altitude is in meters and thus converts the great_circle's result into meters as well.


    Leaving this here for comment continuation purposes.

    2. Distance calculates the 2D distance between two points. In order to get a 3D calculation, you need to create one yourself.
    You can have a look at my method from this question: 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)
      

    0 讨论(0)
提交回复
热议问题