Множественное геокодирование с использованием API Яндекс.Карт

При работе над  проектом с использованием API Яндекс.Карт возникает задача определения географических координат для множества точек.

Можно использовать для этого два подхода.

  1. Воспользоваться функцией геокодирования и каждый раз при выводе определять координаты объекта по его почтовому адресу.

У этого подхода есть два недостатка: геокодеру необходимо некоторое время на обработку запроса и ответ, еще количество обращений к сервису геокодирования ограничено 25000 запросов для одного API-Ключа в сутки.

2. Предварительно использовать сервис определения координат по адресу http://api.yandex.ru/maps/tools/getlonglat/

Если точек много, то вручную это очень утомительно.

Я предлагаю автоматизировать процесс определения координат.

Для этого мы будем использовать возможность формирования HTTP-запроса, подробнее смотри http://api.yandex.ru/maps/geocoder/doc/desc/concepts/geocode.xml

С начала мы в базе данных MySQL создадим таблицу для хранения информации об объектах.

Это можно сделать, используя утилиту PhpMyAdmin или SQL-запросом:

CREATE  TABLE IF NOT EXISTS `markers_geocod` (
 
  `id` int(11) NOT NULL auto_increment,
 
  `name` varchar(255) NOT NULL,
 
  `address` varchar(100) NOT NULL,
 
  `lat` varchar(255) NOT NULL,
 
  `lng` varchar(255) NOT NULL,
 
  `telephon` varchar(30) NOT NULL,
 
  PRIMARY KEY   (`id`)
 
  )  ENGINE=MyISAM  DEFAULT CHARSET=utf8;

После этого заполняем таблицу тестовыми данными вида:

Наименование, адрес, координаты – долгота, широта (0.0, 0.0), номер телефона.

Для обработки ответа геокодера нам понадобиться работающее расширение php – библиотека curl (входит в состав php5).

Если Вы работаете на локальном компьютере необходимо убрать точку с запятой возле строки extension=php_curl.dll в файле php.ini и перезапустить сервер.

На хостинге данная библиотека, как правило, уже бывает задействовано.

Полный код примера:

<?php
 
set_time_limit(360);
 
require("config.php");
 
 
define("MAPS_HOST", "geocode-maps.yandex.ru");
define("KEY", "AIW0o0kBAAAAkwyUMwMAEkDz8ddCkU0UnlyLEu4ddFBXhQQAAAAAAAAAAAA1Md4ULCo5QbciEo5T26eV1lhuEQ==");
 
// Select all the rows in the markers table
$query = "SELECT * FROM markers_geocod";
$result = mysql_query($query);
if (!$result) {
  die("Invalid query: " . mysql_error());
}
 
// Initialize delay in geocode speed
$delay = 0;
$base_url = "http://" . MAPS_HOST . "/1.x/?geocode=";
 
// Iterate through the rows, geocoding each address
while ($row = @mysql_fetch_array($result)) {
 
$exp_str1 = explode(",", $row["address"]);
$address = implode($exp_str1, ",+");
$id = $row["id"];
$request_url = $base_url . urlencode($address)."&key=" . KEY;
 
$ch = curl_init($request_url);
$fp = fopen("example_homepage.txt", "w+");
 
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
 
curl_exec($ch);
curl_close($ch);
fclose($fp);
 
	$lines = file("example_homepage.txt");
 
	if($lines[5] == '<ygeo:request>несуществующая улица</ygeo:request>'){
	echo 'адрес: ', $row["address"], ' ', 'не найден', '<br><br>';
	}
	else
	{
	foreach ($lines as $line_num => $line)
	{
 
if(strpos($line, "<pos>")!= FALSE)
{
    echo "Строка #<b>{$line_num}</b> : " . htmlspecialchars($line) . "<br>";
 
	$line = trim ($line);
 
	$coord = substr($line, 5, -6);
 
	echo $coord;
 
	$newcoord = explode(" ", $coord);
 
	$lat1 = $newcoord[1];
	$lng1 = $newcoord[0];
	echo '<br><br>';
 
	$query = sprintf("UPDATE markers_geocod " .
             " SET lat = '%s', lng = '%s' " .
             " WHERE id = '%s' LIMIT 1;",
             mysql_real_escape_string($lat1),
             mysql_real_escape_string($lng1),
             mysql_real_escape_string($id));
      $update_result = mysql_query($query);
      if (!$update_result) {
        die("Invalid query: " . mysql_error());
		}
 
	}	
} 
 
echo '<br><br>';
 
}	
 
}
?>

С начала мы последовательно читаем из таблицы адреса объектов и формируем ссылку для запроса в формате http:// geocode-maps.yandex.ru /1.x/?geocode= строка с адресом, который требуется геокодировать в координаты &key= API-ключ Яндекс.Карт

После этого ответ геокодера мы записываем в файл example_homepage.txt и обрабатываем его, находим строку с координатами и заносим их в базу данных.

После этого мы можем сформировать из данных таблицы YMapsML-файл и вывести его на карте.

Еще одно замечание.

Если геокодер не нашел данный адрес в своей базе, он ищет ближайший по адресу.

Например, мы ищем дом по адресу Нижний Новгород, Казанское шоссе, 10/6, а геокодер определяет координаты дома по адресу Россия, Нижегородская область, Нижний Новгород, Казанское шоссе, 10.

Поэтому,  после автоматического геокодирования необходимо провести проверку.

  • Гость: Огромное спасибо автору этого сайта за отличный материал по Картам. Я мучился с Яндекс.картами очень долгое время и много было непонятного. На оф.сайте карт настолько запутанная документация что разобраться в ней для меня составило многих недель изучения, а вот у вас на сайте Описано и даны примеры. Я в восторге. Еще раз спасибо!
  • Гость: Здравствуйте, я начал делать по вашему примеру на Денвере. Нашел в файле php.ini нужную строку, убрал точку с запятой, перезапустил сервер, но вылетает ошибка: Fatal error: Call to undefined function curl_init() in Z:homelocalhostwwwmyphpconfig_2.php. Как видно не найдена функция curl_init() ...Подскажите, что делать?
  • Гость: Я извиняюсь, что забыл написать следующее. Поддержка функции curl_init() отсутствует в Денвере. Чтобы все нормально работало не достаточно убрать точку с запятой, нужно скачать полную версию PHP с сайта http://www.php.net/downloads.php - файл PHP 5.2.12 zip package [10,268Kb] - 17 December 2009 Разархивировать его и все содержимое папки php-5.2.12-Win32 скопировать в папку Имя диска:WebServersusrlocalphp5 Денвера, после чего перезапустить сервер.
  • Гость: Хорошее дополнение. Надо на локальном сделать. Спасибо всем! Особенно admin!
  • Гость: Здравствуйте У меня такая ошибка вылезла: Warning: mysql_query() [function.mysql-query]: Access denied for user 'ODBC'@'localhost' (using password: NO) in Z:homegeo.locwwwindex.php on line 13 Warning: mysql_query() [function.mysql-query]: A link to the server could not be established in Z:homegeo.locwwwindex.php on line 13 Invalid query: Access denied for user 'ODBC'@'localhost' (using password: NO)
  • Гость: Вы в файле config.php правильно прописали параметры для соединения с базой данных: имя бд, имя пользователя и пароль?
  • Гость: Здравствуйте! Ваш вариант множественного геокодирования возвращает несколько вариантов координат, по какому принципу выбирается единственный, который заносится в таблицу? Как можно задать зону поиска, например Москву? Спасибо заранее.
  • Гость: Нужно ограничить область поиска, для этого у геокодера существует параметр boundedBy, подробнее см. статью <a href="http://ymapsapi.ya.ru/replies.xml?item_no=5" rel="nofollow">Как ограничить область поиска?</a>. У html-геокодера, который используется в скрипте заметки, задать область поиска можно с помощью параметров ll и spn, а с помощью параметра rspn ограничить поиск только заданной областью. Подробнее о параметрах <a href="http://api.yandex.ru/maps/geocoder/doc/desc/concepts/input_params.xml" rel="nofollow">здесь</a>
  • Гость: Добрый день! Еще один вопрос по геокодеру. Если взять для примера адрес: Ленинский 53 и геокодировать его непосредственно через сайт maps.yandex.ru, то в результате выдается Россия, Москва, Ленинский проспект, 53с1 или http://maps.yandex.ru/-/CBQnuLZj . В случае использования представленного геокодера или использования сервиса - http://api.yandex.ru/maps/tools/getlonglat/ выдается: Ленинский проезд, Россия, Московская область, Юбилейный. И так по многим адресам... С чем это может быть связано?
  • Гость: Подскажите новичку пожалуйста. Какие строчки в коде и на что поменять, если адрес в базе состоит из нескольких полей? В примере одно поле "address", а у меня в базе из нескольких полей - "country", "admn-area", "street", "house", "house", "build".