Как правильно рассчитать расстояние и азимут из двух данных GPS долготы и широты? - PullRequest
0 голосов
/ 29 октября 2019

У меня есть набор данных, состоящий из долготы и широты, и мне нужно преобразовать эти значения в значения расстояния и азимута. По сути, моя цель - как рассчитать расстояние и азимут между двумя точками широты и долготы.

Я знаю, что есть много подобных вопросов, но почему-то ни один из них не дал мне того, что мне точно нужно. Есть много ресурсов о том, как рассчитать расстояние, но сложно вычислить азимут. Я реализовал то, что нашел в интернете, и также скопировал некоторые решения людей, которые ответили на подобные вопросы.

Вот мой окончательный код:

from math import *
from geopy.distance import geodesic, distance
from geographiclib.geodesic import Geodesic
from haversine import haversine
from pygc import great_distance
import pyproj

lat1, lon1, lat2, lon2 = 48.863846, 8.863657, 48.846757, 8.927386     # Stuttgart: 48.1351, 11.5820
print("distance using the haversine lib => ", haversine(point1=(lat1, lon1), point2=(lat2, lon2)))


geod = pyproj.Geod(ellps='WGS84')
azimuth1, azimuth2, dist = geod.inv(lon1, lat1, lon2, lat2)

print('pyproj distance: ', dist/1000)  # pyproj distance:  5.048040324129042
print('pyproj azimuth: ', azimuth1, azimuth2)  # pyproj azimuth:  112.09094775492177 -67.86106109849165

d = great_distance(start_latitude=lat1, start_longitude=lon1, end_latitude=lat2, end_longitude=lon2)
print("distance using pygc lib => ", d)  # distance using pygc lib => 'distance': 50.48 'azimuth': 112.09094775472433, 'reverse_azimuth': 292.1389389013081}


dis = geodesic((lat1, lon1), (lat2, lon2)).kilometers
print("distance using geopy = ", dis) # distance using geopy =  5.048040324129042
dis1 = distance((lat1, lon1), (lat2, lon2))
print("distance using distance geopy = ", dis1)  # distance using distance geopy =  5.048040324129042 km

res = Geodesic.WGS84.Inverse(lat1, lon1, lat2, lon2)
print("distance and azi using geographiclib => ", res['s12']/1000, res['azi1'])  # distance and azi using geographiclib =>  5.048040324129042 112.09094775492177
print("bearing using geographiclib => ", res['azi1'])  # bearing =>  112.09094775492177


def get_bearing(lat1, lon1, lat2, lon2, method='fab'):
    rad_lat1 = lat1 * pi / 180
    rad_lon1 = lon1 * pi / 180
    rad_lat2 = lat1 * pi / 180
    rad_lon2 = lat1 * pi / 180

    if method.lower() == 'fab':
        rad_theta = rad_lon2 - rad_lon1
        y = sin(rad_theta) * cos(rad_lat2)
        x = cos(rad_lat1) * sin(rad_lat2) - sin(rad_lat1) * cos(rad_lat2) * cos(rad_theta)
        brng = atan2(y, x) * 180 / pi
        return (brng + 360) % 360

    elif method.lower() == 'python':
        import numpy as np
        dLon = rad_lon2 - rad_lon1
        y = sin(dLon) * cos(rad_lat2)
        x = cos(rad_lat1) * sin(rad_lat2) - sin(rad_lat1) * cos(rad_lat2) * cos(dLon)
        brng = np.rad2deg(atan2(y, x))
        if brng < 0:
            brng += 360
        return brng

    elif method.lower() == 'so':
        bearing = atan2(sin(rad_lon2 - rad_lon1) * cos(rad_lat2),
                        cos(rad_lat1) * sin(rad_lat2) - sin(rad_lat1) * cos(rad_lat2) * cos(rad_lon2 - rad_lon1))
        bearing = degrees(bearing)
        bearing = (bearing + 360) % 360
        return bearing

    elif method.lower() == 'initial':

        def initial_bearing(pointA, pointB):

            if (type(pointA) != tuple) or (type(pointB) != tuple):
                raise TypeError("Only tuples are supported as arguments")

            lat1 = radians(pointA[0])
            lat2 = radians(pointB[0])

            diffLong = radians(pointB[1] - pointA[1])

            x = sin(diffLong) * cos(lat2)
            y = cos(lat1) * sin(lat2) - (sin(lat1)
                                         * cos(lat2) * cos(diffLong))
            initial_bearing = atan2(x, y)
            initial_bearing = degrees(initial_bearing)
            compass_bearing = (initial_bearing + 360) % 360
            return compass_bearing
        return initial_bearing(pointA=(lat1, lon1), pointB=(lat2, lon2))
    else:
        dLon = rad_lon2 - rad_lon1
        y = sin(dLon) * cos(rad_lat2)
        x = cos(rad_lat1)*sin(rad_lat2) - sin(rad_lat1)*cos(rad_lat2)*cos(dLon)
        brng = atan2(y, x) * 180 / pi
        return brng


def get_distance_in_km(lat1, lon1, lat2, lon2, method='fab'):
    earth_diameter = 6371 * 2
    if method.lower() == 'fab':
        fi1 = lat1 * pi / 180
        fi2 = lat2 * pi / 180
        deltafi = (lat2 - lat1) * pi / 180
        deltasigma = (lon2 - lon1) * pi / 180

        a = sin(deltafi/2) * sin(deltafi/2) + cos(fi1) * cos(fi2) * sin(deltasigma/2) * sin(deltasigma/2)
        return earth_diameter * atan2(sqrt(a), sqrt(1-a))

    elif method.lower() == 'haversian':
        # convert decimal degrees to radians
        lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

        # haversine formula
        dlon = lon2 - lon1
        dlat = lat2 - lat1
        a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2
        c = 2 * asin(sqrt(a))
        r = 6371  # Radius of earth in kilometers. Use 3956 for miles
        return c * r

    else:

        def haversin(x):
            return sin(x / 2) ** 2
        return 2 * asin(
            sqrt(haversin(lat2 - lat1) + cos(lat1) * cos(lat2) * haversin(lon2 - lon1))
        ) * (earth_diameter/2)


res = get_distance_in_km(lat1, lon1, lat2, lon2, method='fab')
res1 = get_distance_in_km(lat1, lon1, lat2, lon2, method='haversian')
print("distance using fab method => ", res)   # distance using fab method =>  5.034895107102602
print("distance using haversian => ", res1)  # distance using haversian =>  5.034895107102733

br = get_bearing(lat1, lat2, lon1, lon2)
br1 = get_bearing(lat1, lat2, lon1, lon2, method='other')
br2 = get_bearing(lat1, lat2, lon1, lon2, method='python')
br3 = get_bearing(lat1, lat2, lon1, lon2, method='so')
br4 = get_bearing(lat1, lat2, lon1, lon2, method='initial')

print("bearing using the fab method : ", br) # bearing using the fab method :  89.99356472317174
print("bearing using other: ", br1) # bearing using other:  89.99356472317177
print("bearing using python: ", br2) # bearing using python:  89.99356472317177
print("bearing using method found on stackoverflow: ", br3)# bearing using method found on stackoverflow:  89.99356472317174
print("bearing using initial method: ", br4)  # bearing using initial method:  233.48863432180315

Как вы можете видеть вкод, я использую разные методы для расчета расстояния и пеленга. Я нашел эти методы в Интернете на разных форумах и в научных статьях. Проблема в том, что они не дают одинаковый выход.

Разница между выходами расстояния очень мала и может быть проигнорирована, но выход азимута сбивает с толку. 3 метода выводят одно и то же значение для азимута, а другие методы выводят другие значения.

Я предоставил выходные данные в комментариях вместе с кодом, но вы можете запустить код и посмотреть, что я имею в виду. Проект, над которым я работаю, требует высокой точности, и поэтому я пробую разные методы, чтобы увидеть, какой из них точнее других, а также библиотеки, которые предоставляют более точные значения, чем другие, как здесь, я пытаюсь geopy, geographiclib, pygc и pyproj.

Вы можете видеть, что эти библиотеки предоставляют разные значения для обратного азимута или обратного азимута, а исходный метод также предоставляет совершенно другое значение. Я использовал этот калькулятор расстояний веб-сайт, чтобы проверить свои результаты. Согласно веб-сайту, значения библиотек более точны, чем реализация.

Это сбивает с толку, так как я реализовал эти функции в соответствии с научными статьями, а некоторые другие детали реализации были найдены на stackoverflow и других форумах. Я очень запутался в этих результатах, и, как я уже сказал, мой Проект требует высокой точности выбора значений, поэтому я должен быть осторожен.

Я надеюсь, что кто-то может подсказать мне об этом и сказать, какой метод или библиотекая должен использовать для этого проекта или какой метод является более точным для расчета значений расстояния и азимута.

Я использую Python 3.7. Вот ссылки на библиотеки, которые я использовал: geopy geographiclib pygc pyproj

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...