Поиск расстояния до точки на карте по координатам

В проекте, для которого мы использовали базу GeoIP, появилась новая задача: в базе у нас уже хранится много записей о салонах и теперь необходима возможность искать по этой базе ближайшие по расстоянию объекты или, например, салоны, которые находятся на расстоянии менее 10км. Тут на помощь пришла редко используемая в современной тригонометрии функция гаверсинус. По сути, она и является правильной формулой для сферического приближения, которая выглядит так:

76427d2d1b89e9ad68fc1d90e067f79b

Данная формула подвержена проблеме точек-антиподов, чтобы ее решить используется следующая ее модификация.

great-cirlcles-09

Но тут в нашем классе, который и отвечал за вычисления пришлось ввести константу, задающую радиус земли в километрах:

const EARTH_RADIUS_KM = 6373;

Осталось дело за малым: наиболее рационально пользоваться этой формулой. Так как, я повторюсь, данные с координатами у нас лежат в таблице, то и вычислять нужные нам расстояния мы будем при помощи SQL.

Вот сама функция формирующая запрос для вычисления расстояния:

private function _getHaversineWithAntipodDotsDistanceCondition($latitude,$longitude)
{
$numerator = 'POW(COS(RADIANS(latitude)) * SIN(ABS(RADIANS('.$longitude.')-RADIANS(longitude))),2)';

$numerator .= ' + POW(
COS(RADIANS('.$latitude.')) * SIN(RADIANS(latitude)) - SIN(RADIANS('.$latitude.'))
* COS(RADIANS(latitude))*COS(ABS(RADIANS('.$longitude.')-RADIANS(longitude)))
,2)';
$numerator = 'SQRT('.$numerator.')';

$denominator = 'SIN(RADIANS(latitude))*SIN(RADIANS('.$latitude.')) +
COS(RADIANS(latitude))*COS(RADIANS('.$latitude.'))*
COS(ABS(RADIANS('.$longitude.')-RADIANS(longitude)))';

$condition = 'ATAN('.$numerator.'/('.$denominator.')) * '.self::EARTH_RADIUS_KM;

return $condition;
}

В данную функцию мы передаем две координаты точки, от которой нам нужно считать наше расстояние: широту и долготу.

В таблице, по которой мы ищем наши объекты, также должны быть поля latitude и longitude. Как мы можем применить нашу функцию?! Например, нам нужно найти все записи таблицы `table`, описывающие объекты, которые находятся от данной точки с известными нам координатами на определенном расстоянии. Делается так:

public function getNearestsObjectsByDistance( $latitude_start, $longitude_start, $distance )
{
$condition = $this->_getHaversineWithAntipodDotsDistanceCondition($latitude_start, $longitude_start);
$sqlQuery = 'SELECT
*,
'.$condition.' as distance
FROM
table
WHERE distance <'.intval($distance);
}

В эту функцию мы передаем следующие параметры: широта нашей точки, долгота, максимальное удаление,  на котором могут находиться объекты. Задав, например координаты, полученные по IP пользователя в предыдущем посте и дальность 100 км, мы получим следующий запрос:

SELECT
*,
ATAN(SQRT(POW(COS(RADIANS(latitude)) * SIN(ABS(RADIANS(-111.8147)-RADIANS(longitude))),2) + POW(
COS(RADIANS(41.692)) * SIN(RADIANS(latitude)) - SIN(RADIANS(41.692))
* COS(RADIANS(latitude))*COS(ABS(RADIANS(-111.8147)-RADIANS(longitude)))
,2))/(SIN(RADIANS(latitude))*SIN(RADIANS(41.692)) +
COS(RADIANS(latitude))*COS(RADIANS(41.692))*
COS(ABS(RADIANS(-111.8147)-RADIANS(longitude))))) * 6373 as distance
FROM
table
WHERE distance <100

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

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

Поиск расстояния до точки на карте по координатам: 30 комментариев

  1. Великолепная подача! Все, у кого есть дополнительная инфа — пожалуйста в асю пять672404пять2.

    • Как только появится больше свободного времени обещаю написать еще парочку статей по данному вопросу

  2. Как ни крути, а это ваш самый лучший пост. Добавил в закладки.

    • Спасибо. Посты на самом деле еще далеки от совершенства, но я работаю над своим стилем, сочинения в школе писал лучше девчонок. Буду стараться преподносить материал более интерестно 🙂

  3. Только сегодня наткнулся на ваш сайт 🙂 Прочёл от корки до корки. Но именно этот пост однозначно один из самых лучших!

    • 🙂 не первый комментарий говорят о качестве этого поста, наверное стоит взять его за образец 🙂

  4. Мда… Примерно тоже самое прочел на 7дей.ру. Уже много раз замечал. Просто сам сталкивался с этим. Впрочем… может меня и подглючивает .

    • Честно признаться формулу я не выводил 🙂 а вот всю программную реализацию делал сам

    • Спасибо. Пока этот я считаю самым удачным 🙂 и интерестным

  5. К стати, диз в опере немного коряв. Проверьте style.css.

  6. Супер, но сегодня дочитать не успею, добавил в закладки.

  7. Эмм.. а можно ли у вас брать посты с рсс канала? Ссылку на вас обязательно поставлю.

    • Целиком посты нет, можно брать заголовок или начало поста, а дальше ставить ссылку на меня — блог молодой и дублирование контента ему очень не нужно, простите

  8. Уже не первую неделю читаю. Но более всего нравится стиль изложения. Даже чем-то на Гришковца похоже. Буквально на раз все прочитываю.

    • Спасибо :)) Я не специально, я так и в жизни рассказываю

  9. Хотел спросить, а может вы напишете что-нибудь про кризис? Что делать и что будет потом… А то меня уволили с работы, не знаю что делать дальше. Думаю многим будет интересно!

    • В принципе, кризис затронул и меня и тема довольно-таки интересная, о нем писано-переписано уйма, но можно изложить и свою точку зрения, если будет что сказать конечно-же

  10. Очень долго писал, а потом подумал, а почему надрываться? В общем, нужно писать более полезные посты. Вот буквально прошлый — замечательный. А вот этот — уже не так. Держите планку!

  11. Как вижу тут многие выскащываются, что вы хорошо пишите. Так вот, как копирайтер заявляю — уровень у вас как у хорошего писателя!

  12. Все хорошо, только, как я думаю, стоит обновляться побольше! 🙂

  13. Здравствуйте. Можно сказать, что совсем недавно попал на ваш блог, теперь каждое утро захожу глянуть, а не написали ли чего нового. 🙂 Но, к сожалению, вы не каждый день свой блог обновляете 🙁

    • Да, к сожалению не всегда хватает времени писать о том что хочется, да и хорошо подать материал тоже требует времени, но я буду стараться 🙂

  14. Если не секрет, откуда вы родом? Уж очень на украинца похожи 🙂

    • :))) почти угадали — родился и вырос в Западной Беларуси

  15. Спасибо за статью, у меня как раз проект, работающий с Яндекс.Картами. И поиск ближайшей точки на карте является актуальной задачей.

Обсуждение закрыто.