Category: город

Category was added automatically. Read all entries about "город".

Расстояние и протяженность маршрута между двумя точками. Ближайшие метро. Через API Yandex Map

Узнаем расстояние между двумя точками через API Yandex MAP 2.0
API Yandex Map позволяет найти расстояние через свой интерфе ICoordSystem
C помощью методов:

  • solveInverseProblem(startPoint, endPoint, reverseDirection) — построение кратчайшего маршрута между двумя точками, определение расстояния этого маршрута и направление движения

  • getDistance(point1, point2) — возвращает кратчайшее расстояние между двумя заданными точками (в метрах).

Пример от Yandex Ma маршрутизация и расчет стоимости поездки. (Определение маршрута между двумя точками, расчет расстояния по этому маршруту)


Поиск ближайших станций метро через API Yandex Map 1.0
Модуль "Метро" позволяет осуществлять поиск ближайших станций метрополитена в заданной области карты.
Класс YMaps.Metro.Closest позволяет производить поиск ближайших станций метро и получать набор меток в качестве результата.

Находим ближайшие станции метро и расстояния до них, используя Yandex Map

Вступление:
Есть большой список организаций. Список постоянно пополняется сразу множеством организаций. Каждой нужно присвоить множество ближайших станций метро и расстояние по прямой до нее.
В этой статье я рассмотрю, как это можно сделать на php, не прибегая к js.

Задача:
Известна координата организации (можно узнать через http запрос). Также известны координаты всех станций метро (они заранее лежат в БД). Написать скрипт на php, который будет присваивать каждой организации несколько ближайших метро, а также можно расстояние до него.

Решение:
Это полезно для понимания! Почитайте базовые знания в географических координатах: https://leonid.shevtsov.me/post/chto-ty-dolzhen-znat-pro-geograficheskie-koordinaty/

1. Структура БД
Есть 3 таблицы:  1 - организация, 2 - метро, 3 - вспомогательная таблица (где я указываю id_org, id_metro, distance). Другими словами: связь многие-ко-многим через вспомогательную таблицу.
В таблице метро находятся все метро со своими координатами (долгота, широта). Да, надо заранее занести все координаты. Зато в будущем брать координаты из своей БД, и сопоставлять со своим id метро.
Координаты станций метро Москвы, правда, не всех, можно взять отсюда.
2. Выбираем из БД только ближайшие метро в определенном радиусе
Допустим у нас есть координаты организации. Также имеются координаты в БД для каждого метро, которые хранятся в таблице metro. Как выбрать с минимальными производительными затратами ближайшие метро, да еще узнать расстояния по прямой?
Для начала, рассмотрим и поймем логику, по которой будем действовать.
Возьмем самую простую систему координат и пусть точка О, с координатами (3,2)  - это организация. (Рис. 1)
Точки М - это станции метро.
Зеленый круг - это радиус, в котором будем искать ближайшие станции метро.
metror
Рис. 1

В первую очередь необходимо выбрать только те метро, которые попадают в радиус. (Для того, чтобы вычислять расстояния только до тех метро, которые попали в этот радиус, а не до всех существующих - ибо на реальной карте Москвы - их больше 180)

Применительно к рис.1:
Откладываем от точки О отрезки (радиус) по оси X (влево и вправо) и по оси Y (вверх и вниз) и получаем координаты квадрата, в котором будем искать метро.
Т.е., грубо и округленно координаты которых лежат в интервале:
по оси Х: от 0 до 6
по оси Y: от 0 до 6
Запрос к MySQL будет выглядеть примерно так:
SELECT * FROM `metro` WHERE `x` BETWEEN '0' AND '6' AND `y` BETWEEN '0' AND '6'
В результате получим список метро, которые окажутся в нужном нам квадрате
А далее - выбрать нужное количество ближайших и записать их во вспомогательную таблицу.


3. Находим расстояние между двумя точками (организацией и метро)
На данном этапе у нас есть координаты каждого метро, и собственно координаты нашей организации.
Теперь надо поочередно для каждого метро рассчитать расстояние и выбрать нужное количество ближайших.
С последним все понятно, остановлюсь на нахождении расстояния между двумя точками координат (спутниковых, они же Yandex Map, они же Google Map)

Находим расстояние между 2мя точками через формулу:
cos(d) = sin(φА)·sin(φB) + cos(φА)·cos(φB)·cos(λА − λB),
Прочитать статью про эту формулу для наилучшего понимания =)

На php это будет выглядеть так (возвращает дистанцию в м.):
function distance($longitude1, $latitude1, $longitude2, $latitude2)
{
 $earth_radius = 6372797; //средний радиус Земли в м
 
 $dLat = deg2rad($latitude2 - $latitude1);
 $dLon = deg2rad($longitude2 - $longitude1);
 
 $a = sin($dLat/2) * sin($dLat/2) + cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * sin($dLon/2) * sin($dLon/2);
 $c = 2 * asin(sqrt($a));
 $d = $earth_radius * $c;
 
 return $d;
}







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


Используем http запрос для получения ближайших метро
Также можно использовать get запрос для получения текстового списка метро в формате XML или JSON.
Подробнее в разделе офиц. документации
Например, запрос получения ближайших метро для Марсова Поля в СПб, будет выглядеть так:
http://geocode-maps.yandex.ru/1.x/?geocode=30.331393,59.943419&kind=metro
Недостаток: мы получим названия метро, которые будем вынуждены искать в своей БД.

Полезные ссылки:
Хабр. Как работает Yandex MAP API 2.0
Формула вычисления расстояния между двух точек на Земле. Все языки


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

2ой способ нахождения расстояния между двумя точками спутниковых координат (или между двумя точками Yandex Map / Google Map)
Когда я рассматривал этот способ, я еще не знал формулы и кода, которые написал в первой части. Эти 2 способа работают почти одинаково. Разница вычислений составляет всего 2 метра. И для данного случая это совершенно не существенно.
Найдем расстояние от точки О до М3. (Рис. 2)
metro
Рис. 2

Вспоминаем теорему Пифагора (квадрат гипотенузы равен сумме квадратов катетов). Т.к. координаты всех точек нам известны - то легко находим длину С (расстояние от метро до организации).

Гладко было на бумаге, да забыли про овраги
А если точнее,  то координаты в Yandex Map (да и в Google Map) у нас указаны по долготе и широте в градусах, а следовательно, необходимо переводить градусы широты и долготы в метры, используя коэффициенты. Коэффициенты отличаются в зависимости от широты
Необходимые коэффициенты можно посмотреть в таблице "Длина градуса широты и долготы".

Таблица: длина градуса широты и долготы
Эту таблицу я не нашел в интернете  - поэтому нашел ее в в книге: Министерство Обороны Союза ССР. Главное управление навигации и океанографии. Мореходные таблицы. 1976 г. =)
таблица


Рассмотрим код для окончательного понимания как этим пользоваться:
Функция возвращает расстояние в метрах между объектами
public function distance2($lng1, $lat1, $lng2, $lat2)
{
 define('LNG_SPB', 55801); //градус долготы в метрах для СПБ
 define('LAT_SPB', 111414); //градус широты в метрах для СПБ
 define('LNG_MOSCOW', 63995); //градус долготы в метрах для Москвы
 define('LAT_MOSCOW', 111325); //градус широты в метрах для Москвы
 
 $diffLng = abs($lng2 - $lng1); // разница долготы по модулю
 $diffLat = abs($lat2 - $lat1); // разница широты по модулю
 
 $diffLngM = $diffLng * LNG_SPB; //расстояние в метрах по долготе
 $diffLatM = $diffLat * LAT_SPB; //расстояние в метрах по широте
 
 return $distance = sqrt($diffLngM*$diffLngM + $diffLatM*$diffLatM);
} 






Проверяем первый и второй способ
Для проверки, измеряем расстояние первым и вторым способом между м. "Невский проспект": 30.327144,59.935387 и м. "Пл. Восстания": 30.360704,59.931639. Координаты написаны в порядке (долгота, широта).
function distance() возвратит: 1916.0274249762 (м.)
function distance2() возвратит: 1918.6737626891 (м.)
А если на карте вручную кликнуть на эти станции метро (берем погрешность клика) - то расстояние будет: 1916 м.
distance

Пишите комментарии и задавайте вопросы, если они есть =)


UPDATE 27.07.2017:
КОД НА MySQL:
Ищем организации в радиусе  = 7 км.
SELECT *,
111.414 * DEGREES(ACOS(COS(RADIANS(59.901346)) * COS(RADIANS(X(coordinates))) *
COS(RADIANS(30.355807 - Y(coordinates))) + SIN(RADIANS(59.901346)) * SIN(RADIANS(X(coordinates))))) AS distance_in_km
FROM cemeteries
WHERE MBRContains( LINESTRING(POINT(59.901346 - 0.009 * 3 , 30.355807 - 0.01792 * 6371),
POINT(59.901346 + 0.009 * 3 , 30.355807 + 0.01792 * 3 )), coordinates )
AND ACOS(COS(RADIANS(59.901346)) * COS(RADIANS(X(coordinates))) * COS(RADIANS(Y(coordinates)) - RADIANS(30.355807))
+ SIN(RADIANS(59.901346)) * SIN(radians(X(coordinates)))) <= 3 / 6371




http://stackoverflow.com/questions/6919661/select-within-20-kilometers-based-on-latitude-longitude - Рабочий пример c POINT на mysql!!!!!
http://stackoverflow.com/questions/24370975/find-distance-between-two-points-using-latitude-and-longitude-in-mysql - расстояние к км между двумя точками/ Рабочий пример с POINT
https://www.frameworks.su/article/rasstoyanie_do_bligayshih_stantsii_metro - хорошая статья с применением php и без POINT