Мне в работе с одним из сайтов недвижимости нужно было сделать функционал который для заданной точке на карте города ищет ближайшие станции метро и рассчитывает расстояние до них. Все это должно отрабатывать на PHP скрипте которому передается через аякс адрес, который он обрабатывает и возвращает только 3 станции метро и расстояние до них (как к примеру в Яндекс картах), также рассчитывает время пешком до метро.
Список станций метрополитена
Начнем с того что нужно сделать список станций метро и проставить им координаты широты и долготы. Список станций Московского метрополитена можно получить несколькими способами. Первый способ самый простой - ручной. Находим список станций в интернете, например на официальном сайте Московского метро или в статье на википедии, и вручную перенести их в таблицу БД. Этот вариант немного однообразный и не очень интересный, поэтому есть и второй вариант.
Второй вариант использовать API сайтов которые отдают список станций метро с координатами, распарсить их и автоматически добавить в базу. Этот вариант был немного быстрее и интересней. Нужные нам данные по API из тех сайтов что я просмотрел, отдает только HeadHunter, остальные Superjob, Cian, Avito - отдают только список станций метро без координат.
Чтобы получить список всех станций для метрополитена, необходимо отправить GET запрос на указанный адрес https://api.hh.ru/metro/1, где 1 - это ID города в базе HeadHunter. Если вам нужен будет другой город смотрите в документации HH.
API возвращает список станций с группировкой по веткам метро в формате JSON, также он отдает и цвет линии, что очень удобно.
Хранить данные мы будем в MySQL в двух таблицах, станции и линии метро. Примерная структура таблиц такая:
CREATE TABLE `metro_lines` ( `id` int(11) NOT NULL auto_increment, `name` varchar(250) NOT NULL, `color` varchar(6) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; CREATE TABLE `metro` ( `id` int(11) NOT NULL auto_increment, `name` varchar(250) NOT NULL, `latitude` decimal(9,6) NOT NULL, `longitude` decimal(9,6) NOT NULL, `line_id` int(11) NOT NULL, `order` tinyint(4) NOT NULL default '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
Скрипт для импорта станций метро выглядит примерно так:
/** * Импортируем станции метро в нашу базу */ public function importFromApi(){ //Ссылка на json выгрузку станций метро для города Москвы (ID:1) $url = "https://api.hh.ru/metro/1"; // Метод getUrl делает запрос с помощью cURL $file = $this->getUrl($url); $file = json_decode($file, true); // Очищаем таблицы с линиями и станциями метро $this->pdo->prepare("TRUNCATE TABLE `metro`")->execute(); $this->pdo->prepare("TRUNCATE TABLE `metro_lines`")->execute(); foreach($file['lines'] as $lines){ // Сохраняем линию метро $query = $this->pdo->prepare("INSERT INTO `metro_lines` SET `color` = :color, `name` = :name"); $query->execute( array(':color'=>$lines['hex_color'], ':name'=>$lines['name'])); $line_id = $this->pdo->lastInsertId(); if($line_id){ foreach($lines['stations'] as $station){ // Сохраняем станции и привязываем их к линиям метро $query = $this->pdo->prepare("INSERT INTO `metro` SET `latitude` = :latitude, `longitude` = :longitude, `line_id` = :line_id, `name` = :name, `order` = :order"); $query->execute( array(':longitude'=>$station['lng'], ':latitude'=>$station['lat'], ':line_id'=>$line_id, ':name'=>$station['name'], ':order'=>$station['order'])); } } } }
Находим координаты нужного объекта
Для того чтобы определить ближайшие станции метро нам нужно определить сначала координаты для нашего объекта, зная его адрес. Для это отправим запрос к геокодеру Яндекса.
/** * Находим координаты объекта через API Yandex * * @param string $street Адрес объекта */ public function getLngLatStreet($street){ $url = 'https://geocode-maps.yandex.ru/1.x/?format=json&geocode='.urlencode('Москва, '.$street); $yandex_street_response = $this->getUrl($url); $geocodeInfo = json_decode($yandex_street_response, true); $lngLat = $geocodeInfo['response']['GeoObjectCollection']['featureMember'][0]['GeoObject']['Point']['pos']; // Если улица не найдено то precision == other и скорей всего координаты будут центра указанного города, в нашем случае это Москва. if($lngLat && $geocodeInfo['response']['GeoObjectCollection']['featureMember'][0]['GeoObject']['metaDataProperty']['GeocoderMetaData']['precision'] != 'other'){ list($longitude, $latitude) = explode(" ", $lngLat); return array('longitude'=>$longitude, 'latitude'=>$latitude); } return false; }
Находим ближайшие станции метро
Дальнейшим нашим действием будет рассчитать какие станции метро находятся в круге с нужным нам радиусом точнее, для простоты расчетов, квадрате см. рис 1.
Рис. 1
Точка О - здесь находится нужный нам объект, зеленым обозначена область в которой мы будем искать станции метро. Для этого нам нужно найти координаты точек A, B, C, D зная при этом радиус AO и координаты точки О. Код нахождения координат нужных нам точек будет такой:
/** * Нахождение координат точки по координатам другой точки и известным длине и дирекционному углу данного направления, соединяющей эти точки * * @param float $longitude1 Долгота первой точки * @param float $latitude1 Широта первой точки * @param int $distance Максимальное расстояние до метро в км * @param int $angle Дирекционный угол * @param string $type Если нужно только одна координата широта или высота, то указываем ее */ public function getPoint($longitude, $latitude, $distance=2, $angle=0, $type=null){ // Переводим км в метры $distance = $distance * 1000; // Длина дуги параллели в 1° на экваторе в метрах $latMeter = 111321; $angle = deg2rad($angle); $dx = $distance / ($latMeter * cos(deg2rad($latitude))) * cos($angle); $dy = $distance / $latMeter * sin($angle); $result = array( 'longitude' => $longitude + $dx, 'latitude' => $latitude + $dy ); // Если нам нужна только одна координата (широта или высота), то возвращаем только ее if($type){ return $result[$type]; } return $result; }
Для точек A и B нам нужно найти долготу, для C и D - широту. После нахождения координат можно выбирать станции из базы:
А как изменить путь для выгрузки станций метро СПБ
url = "https://api.hh.ru/metro/2";
В каком файле прописан этот путь?
Или придется Менять построчно Ваш файл dump?
это нужно изменить в файле metro.class.php в строке 176
Уважаемый админ, а подскажите еще, в каком файле находится ссылка url = "https://api.hh.ru/metro/1";
Т.е я сейчас пытаюсь сделать то же самое, но для Спб, нашел, что его ID=2 и как теперь правильно прописать путь, для выгрузки станций метро?
Неужели построчно менять ваш файл dump
это нужно изменить в файле metro.class.php в строке 176
Да, увидел, поменял
Мои знания в этой области минимальны, поэтому прошу простить заранее за излишнюю тупость, но файл дамп же не изменился, а соответственно и результатом обработки пока являются станции Москвы..
Как заменить теперь данные таблиц в phpmyadmin?
include("./metro.class.php");
// Укажите доступы к БД
$setting = array(
'username' => 'root',
'password' => '',
'database' => 'test',
'host' => 'localhost',
);
$metroClass = new Metro($setting);
$metroClass->importFromApi();
Да, таблички обновились, но при любой улице теперь выдает "Нет ближайших станций метро".
Менял параметр максимального расстояния, но это не помогло.(
Доброго времени суток!
Скажите пожалуйста, как нужно импортировать данные в таблицу?
Т.е. тот файл, который предназначен для импорта данных, как его внедрить, как сделать на него ссылку?
Файл в архиве это sql дамп. Его можно импортировать в mysql через phpmyadmin или adminer
Все, нашел это в своем Денвере, в phpmyadmin, большое спасибо за помощь!
А почему просто не использовать геокодер Яндекса?
Он из коробки умеет искать ближайшие метро к точке, заданной координатами: https://tech.yandex.ru/maps/doc/geocoder/desc/concepts/input_params-docpage/
Просто нужно указать kind=metro
Это одно из применений и понятное для всех, на практике мне нужно было искать ближайшие объекты, которые забитые в БД с координатами, от местоположения пользователя