У меня есть таблица MySQL с пространственными точками, и мне нужно рассчитать расстояния. Я нашел много материалов о том, как сделать это с помощью формулы Хаверсина, однако все они предполагают большое расстояние между точками. В моем случае меня интересуют только короткие расстояния (‹ 1 мили), поэтому мне не нужно делать поправку на кривизну земли. Моя интуиция подсказывает, что использование формулы Хаверсина будет неточным на таких малых расстояниях. Какие-либо предложения?
Вычисление коротких расстояний между точками широты/долготы
Ответы (2)
Ваша интуиция неверна. Рассмотрим формулу гаверсинуса и определение гаверсинуса согласно Википедии (φ — это широта, а ψ — долгота):
Есть еще один важный факт: для малых значений θ sin θ приблизительно равен θ; что более важно, оно примерно линейно в θ. Таким образом, гаверсин θ будет приблизительно равен (θ/2)². Это приближение улучшается по мере приближения θ к нулю.
Если широта и долгота близки друг к другу, то φ₂ - φ₁ и ψ₂ - ψ₁, к которым здесь применяется гаверсинус, будут близки к нулю, а это означает, что формула приблизительно
(d/2r)² = ((φ₂ - φ₁) / 2)² + cos(φ₁) cos(φ₂) ((ψ₂ - ψ₁) / 2)²
Теперь обратите внимание, что эта формула имеет ту же форму, что и евклидово расстояние в двух измерениях с некоторыми произвольными коэффициентами масштабирования (не забывайте, что (kx)² = k² x em>², чтобы мы могли перемещать константы в квадраты и из них):
k₁ d² = k₂ ∆φ² + k₃ ∆ψ²
Наконец, я утверждаю без доказательства, что эти произвольные коэффициенты масштабирования оказываются теми же самыми, которые преобразуют изменения широты/долготы в линейное расстояние.
Следовательно, формула гаверсинуса не становится неточной для малых расстояний; это точно так же, как обычное вычисление евклидова расстояния в пределе малых расстояний.
Создайте свои точки, используя значения Point типов данных Geometry в таблице MyISAM.
Создайте ПРОСТРАНСТВЕННЫЙ индекс для этих точек
Используйте MBRContains(), чтобы найти значения:
SELECT *
FROM table
WHERE MBRContains(LineFromText(CONCAT(
'('
, @lon + 10 / ( 111.1 / cos(RADIANS(@lon)))
, ' '
, @lat + 10 / 111.1
, ','
, @lon - 10 / ( 111.1 / cos(RADIANS(@lat)))
, ' '
, @lat - 10 / 111.1
, ')' )
,mypoint)
, или в MySQL 5.1 и выше:
SELECT *
FROM table
WHERE MBRContains
(
LineString
(
Point
(
@lon + 10 / ( 111.1 / COS(RADIANS(@lat))),
@lat + 10 / 111.1
)
Point
(
@lon - 10 / ( 111.1 / COS(RADIANS(@lat))),
@lat - 10 / 111.1
)
),
mypoint
)
При этом будут выбраны все точки примерно в пределах поля (@широта +/- 10 км, @долгота +/- 10 км).
На самом деле это не прямоугольник, а сферический прямоугольник: широта и долгота ограничивают сегмент сферы. Это может отличаться от простого прямоугольника на Земле Франца-Иосифа, но довольно близко к нему в большинстве населенных мест.
Примените дополнительную фильтрацию, чтобы выбрать все, что находится внутри круга (не квадрата).
Возможно применение дополнительной тонкой фильтрации для учета расстояния по большому кругу (для больших расстояний)
здесь следующее решение для нажатия