Алгоритм, чтобы найти все местоположения долготы широты на определенном расстоянии от данного местоположения Lat Lng

Учитывая базу данных мест с местоположениями с широтой + долготы, такими как 40.8120390, -73.4889650, как мне найти все местоположения на заданном расстоянии в определенном месте?

Кажется, не очень эффективно выбирать все местоположения из БД, а затем проходить через них один за другим, получая расстояние от начального местоположения, чтобы увидеть, находятся ли они на указанном расстоянии. Есть ли хороший способ сузить первоначально выбранные местоположения из БД? Как только у меня есть (или нет?) Суженный набор мест, я все еще просматриваю их один за другим, чтобы проверить расстояние, или есть лучший способ?

Язык, в котором я делаю это, не имеет большого значения. Спасибо!

Ответ 1

Начнем с сравнения расстояния между широтами. Каждая градус широты составляет приблизительно 69 миль (111 километров) друг от друга. Диапазон варьируется (из-за формы с небольшим эллипсоидом земли) от 68,703 миль (110,567 км) на экваторе до 69,407 (111,699 км) на полюсах. Расстояние между двумя точками будет равно или больше расстояния между их широтами.

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


Продолжить будет расчет с низкой точностью и быстрым расстоянием, который предполагает сферическую землю:

Большое расстояние круга d между двумя точками с координатами {lat1, lon1} и {lat2, lon2} определяется следующим образом:

d = acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon1-lon2))

Математически эквивалентная формула, которая меньше подвержена ошибкам округления на короткие расстояния:

d = 2*asin(sqrt((sin((lat1-lat2)/2))^2 +
    cos(lat1)*cos(lat2)*(sin((lon1-lon2)/2))^2))

d - расстояние в радианах

distance_km ≈ radius_km * distance_radians ≈ 6371 * d

(6371 км - средний радиус земли)

Этот метод вычислительных требований является минимальным. Однако результат очень точный для малых расстояний.


Затем, если он находится на заданном расстоянии, более или менее, используйте более точный метод.

GeographicLib - самая точная реализация, которую я знаю, хотя Также может быть использована обратная формула Vincenty.


Если вы используете РСУБД, установите широту как первичный ключ и долготу в качестве вторичного ключа. Запросить диапазон широты или диапазон широты/долготы, как описано выше, затем рассчитать точные расстояния для набора результатов.

Обратите внимание, что современные версии всех основных РСУБД поддерживают географические типы данных и запросы изначально.

Ответ 2

Основываясь на текущей пользовательской широте, долготе и расстоянии, которое вы хотите найти, запрос sql приведен ниже.

SELECT * FROM(
    SELECT *,(((acos(sin((@latitude*pi()/180)) * sin((Latitude*pi()/180))+cos((@latitude*pi()/180)) * cos((Latitude*pi()/180)) * cos(((@longitude - Longitude)*pi()/180))))*180/pi())*60*1.1515*1.609344) as distance FROM Distances) t
WHERE distance <= @distance

@latitude и @longitude - широта и долгота точки. Широта и долгота являются столбцами таблицы расстояний. Значение pi равно 22/7

Ответ 3

Расширения PostgreSQL GIS могут быть полезны - как и в случае, он уже может реализовать большую часть функций, которые вы планируете реализовать.

Ответ 5

Йогиостинг танков

У меня есть в моей базе один goups таблиц из Open Streep Maps, и я успешно прошел тестирование.

Расстояние работает отлично в метрах.

SET @orig_lat=-8.116137;
SET @orig_lon=-34.897488;
SET @dist=1000;

SELECT *,(((acos(sin((@orig_lat*pi()/180)) * sin((dest.latitude*pi()/180))+cos((@orig_lat*pi()/180))*cos((dest.latitude*pi()/180))*cos(((@orig_lon-dest.longitude)*pi()/180))))*180/pi())*60*1.1515*1609.344) as distance FROM nodes AS dest HAVING distance < @dist ORDER BY distance ASC LIMIT 100;

Ответ 7

Как упоминалось в biziclop, скорее всего, вашим лучшим вариантом будет какое-то метрическое пространство. У меня есть опыт использования kd-деревьев и квадроциклов для выполнения подобных запросов диапазона, и они удивительно быстры; их тоже не так сложно написать. Я бы предложил заглянуть в одну из этих структур, так как они также позволяют вам отвечать на другие интересные вопросы, такие как "какая ближайшая точка в моих данных установлена ​​в эту другую точку?"

Ответ 8

Вам нужен пространственный поиск. Вы можете использовать Solr Spatial search. Он также имеет встроенный lat/long тип данных, здесь.

Ответ 9

Вы можете преобразовать долготу широты в формат UTM, который является метрическим форматом, который может помочь вам рассчитать расстояния. Тогда вы можете легко решить, попадает ли точка в определенное место.

Ответ 10

Поскольку вы говорите, что любой язык является приемлемым, естественным выбором является PostGIS:

SELECT * FROM places
WHERE ST_DistanceSpheroid(geom, $location, $spheroid) < $max_metres;

Если вы хотите использовать базу данных WGS, вы должны установить $spheroid в 'SPHEROID["WGS 84",6378137,298.257223563]'

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

Ответ 11

вы можете проверить это уравнение я думаю, что это поможет

SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;