В проекте, для которого мы использовали базу GeoIP, появилась новая задача: в базе у нас уже хранится много записей о салонах и теперь необходима возможность искать по этой базе ближайшие по расстоянию объекты или, например, салоны, которые находятся на расстоянии менее 10км. Тут на помощь пришла редко используемая в современной тригонометрии функция гаверсинус. По сути, она и является правильной формулой для сферического приближения, которая выглядит так:
Данная формула подвержена проблеме точек-антиподов, чтобы ее решить используется следующая ее модификация.
Но тут в нашем классе, который и отвечал за вычисления пришлось ввести константу, задающую радиус земли в километрах:
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
Пожалуй, правильней было бы использовать тут хранимые процедуры, что, возможно, в скором времени и придется сделать.
Иногда написав кучу кода, а потом случайно что-то удалив, жалеешь, что не установил на компьютер А чтобы не забывать что и куда вы пишете можете установить на компьютер клавиатурный шпион. Всем хорошего дня и креативного настроения в работе 🙂
Великолепная подача! Все, у кого есть дополнительная инфа — пожалуйста в асю пять672404пять2.
Как только появится больше свободного времени обещаю написать еще парочку статей по данному вопросу
Как ни крути, а это ваш самый лучший пост. Добавил в закладки.
Спасибо. Посты на самом деле еще далеки от совершенства, но я работаю над своим стилем, сочинения в школе писал лучше девчонок. Буду стараться преподносить материал более интерестно 🙂
Только сегодня наткнулся на ваш сайт 🙂 Прочёл от корки до корки. Но именно этот пост однозначно один из самых лучших!
🙂 не первый комментарий говорят о качестве этого поста, наверное стоит взять его за образец 🙂
Мда… Примерно тоже самое прочел на 7дей.ру. Уже много раз замечал. Просто сам сталкивался с этим. Впрочем… может меня и подглючивает .
Честно признаться формулу я не выводил 🙂 а вот всю программную реализацию делал сам
Великолепный пост. Добавил в бобрдобр.
Спасибо. Пока этот я считаю самым удачным 🙂 и интерестным
К стати, диз в опере немного коряв. Проверьте style.css.
Спасибо, проверю!
Супер, но сегодня дочитать не успею, добавил в закладки.
Эмм.. а можно ли у вас брать посты с рсс канала? Ссылку на вас обязательно поставлю.
Целиком посты нет, можно брать заголовок или начало поста, а дальше ставить ссылку на меня — блог молодой и дублирование контента ему очень не нужно, простите
Уже не первую неделю читаю. Но более всего нравится стиль изложения. Даже чем-то на Гришковца похоже. Буквально на раз все прочитываю.
Спасибо :)) Я не специально, я так и в жизни рассказываю
Хотел спросить, а может вы напишете что-нибудь про кризис? Что делать и что будет потом… А то меня уволили с работы, не знаю что делать дальше. Думаю многим будет интересно!
В принципе, кризис затронул и меня и тема довольно-таки интересная, о нем писано-переписано уйма, но можно изложить и свою точку зрения, если будет что сказать конечно-же
Очень долго писал, а потом подумал, а почему надрываться? В общем, нужно писать более полезные посты. Вот буквально прошлый — замечательный. А вот этот — уже не так. Держите планку!
Спасибо за критику, буду стараться
Как вижу тут многие выскащываются, что вы хорошо пишите. Так вот, как копирайтер заявляю — уровень у вас как у хорошего писателя!
Все хорошо, только, как я думаю, стоит обновляться побольше! 🙂
Здравствуйте. Можно сказать, что совсем недавно попал на ваш блог, теперь каждое утро захожу глянуть, а не написали ли чего нового. 🙂 Но, к сожалению, вы не каждый день свой блог обновляете 🙁
Да, к сожалению не всегда хватает времени писать о том что хочется, да и хорошо подать материал тоже требует времени, но я буду стараться 🙂
Если не секрет, откуда вы родом? Уж очень на украинца похожи 🙂
:))) почти угадали — родился и вырос в Западной Беларуси
Подписался на РСС канал, буду следить =)
Спасибо, еще и друзей приводите 🙂
Спасибо за статью, у меня как раз проект, работающий с Яндекс.Картами. И поиск ближайшей точки на карте является актуальной задачей.