How to calculate 3D distance (including altitude) between two points in GeoDjango

后端 未结 3 827
别那么骄傲
别那么骄傲 2020-12-17 03:28

Prologue:

This is a question arising often in SO:

  • 3d distance calculations with GeoDjango
  • Calculating distance between two po
相关标签:
3条回答
  • 2020-12-17 04:14

    Using geopy, this is the easiest and perfect solution.

    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
    
    0 讨论(0)
  • 2020-12-17 04:26

    Once converted into Cartesian coordinates, you can compute the norm with numpy:

    np.linalg.norm(point_1 - point_2)
    
    0 讨论(0)
  • 2020-12-17 04:29

    Reading from the documentation on the GEOSGeometry.distance method:

    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.

    Therefore we need to implement a method to calculate a more accurate 2D distance between 2 points and then we can try to apply the altitude (Z) difference between those points.

    1. Great-Circle 2D distance calculation:

    The most common way to calculate the distance between 2 points on the surface of a sphere (as the Earth is simplistically but usually modeled) is the Haversine formula:

    The haversine formula determines the great-circle distance between two points on a sphere given their longitudes and latitudes.

    Although from the great-circle distance wiki page we read:

    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.

    We can create our own implementation of the Haversine or the Vincenty formula (as shown here for 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 (Haversine):

          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 (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. Adding altitude in the mix:

    As mentioned, each of the above calculations yields the great-circle distance between 2 points. That distance is also called "as the crow flies", assuming that the "crow" flies without changing altitude and as straight as possible from point A to point B.

    We can have a better estimation of the "walking/driving" ("as the crow walks"??) distance by combining the result of one of the previous methods with the difference (delta) in altitude between point A and point B, inside the Euclidean Formula for distance calculation:

    acw_dist = sqrt(great_circle(p1, p2).m**2, (p1.z - p2.z)**2)
    

    The previous solution is prone to errors especially the longer the real distance between the points is.
    I leave it here for comment continuation reasons.

    GeoDjango Distance calculates the 2D distance between two points and doesn't take into consideration the altitude differences.
    In order to get the 3D calculation, we need to create a distance function that will consider altitude differences in the calculation:

    Theory:

    The latitude, longitude and altitude are Polar coordinates and we need to translate them to Cartesian coordinates (x, y, z) in order to apply the Euclidean Formula on them and calculate their 3D distance.

    • Assume:
      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)
提交回复
热议问题