Аварийный менеджер команд разработки
3,2
рейтинг
29 ноября 2013 в 22:45

Разработка → Простая публикация геоданных на собственной карте на базе 2ГИС

Недавно передо мной встала интересная задача — отображать на сайте карту, с различными объектами, причем этих объектов могут быть десятки и сотни, а управлять ими должен уметь любой менеджер ниже среднего звена.

Более формально задача сводилась к тому, чтобы дать возможность пользователям задавать некий список гео-объектов, дать им возможность легко и быстро этими объектами управлять и автоматически отображать эти объекты на некоей карте.

Для реализации задумки была выбрана связка Google Docs и API картографического сервиса 2ГИС. Решение получилось действительно простым, в духе знаменитых «30 строк» :)

Шаг 1. Заводим таблицу для данных.

Открываем Google Docs и создаем таблицу. В моей таблице было достаточно много ненужных мне, но очень нужных для прочих служб полей, поэтому я пойду на небольшую хитрость и для тестирования заведу отдельную.

Вот ссылка на нее: docs.google.com/spreadsheet/ccc?key=0AvdXWIJtCGnndG0tODY5MzRsVFhnbkJseFk0aWJkaUE&usp=sharing

В качестве тестовых данных я взял адреса домов, относящихся к двум небольшим избирательным участкам в городе Костроме. Номер участка, улица, номер дома и число квартир.

Использование в качестве источника данных Google Drive снимает множество проблем с совместной работой специалистов разной степени подготовки и с оперативным обновлением данных.

Теперь таблицу нужно опубликовать. Для этого идем в меню «Файл — Опубликовать в интернете». В появившемся окне выбираем «Начать публикацию», в нижней части — выбираем «CSV (значения, разделенные запятыми)» и получаем ссылку на наш лист таблицы в виде CSV.

Получается вот такая ссылка: docs.google.com/spreadsheet/pub?key=0AvdXWIJtCGnndG0tODY5MzRsVFhnbkJseFk0aWJkaUE&single=true&gid=0&output=csv

Шаг 2. Готовим данные для карты.

Напишем несложный скрипт на PHP и назовем его getMapData.php:
define('CSV_DATA_URL', 'https://docs.google.com/spreadsheet/pub?key=0AvdXWIJtCGnndG0tODY5MzRsVFhnbkJseFk0aWJkaUE&single=true&gid=0&output=csv');
define('DEFAULT_CITY', 'Кострома');

$points = array();
$csvDataHandle = fopen(CSV_DATA_URL, 'r');
$i = 0;
while ( false !== ($line = fgetcsv($csvDataHandle, null, ',', '"')) ) {
    // Пропускаем первую строку в файле
    if ( !$i++ ) continue;
    // Считываем номер участка
    if ( !empty($line[0]) )
        $zone = $line[0];
    $points[] = array(
        'zone' => $zone,
        'address' => DEFAULT_CITY . ', ' . $line[1] . ', ' . $line[2],
        'data' => $line[3],
    );
}

Тут нужно заметить, что скорее всего при первом запуске вы получите ошибку «Warning: fopen(): Unable to find the wrapper „https“». Это связано с тем, что в дефолтной установке PHP не включен модуль OpenSSL. Найдите в файле php.ini строчку
;extension=php_openssl.so и раскомментируйте ее, убрав точку с запятой. Обычного этого достаточно.

Разумеется, массив $points нужно куда-то закешировать, чтобы не «дергать» Google Docs при каждой отрисовке карты.

Теперь перед нами стоит следующая задача. Нужно адреса геообъектов каким-то образом перевести в координаты на карте. Для этого у 2ГИС есть соответствующее API: api.2gis.ru/doc/geo/search

Вот простейший код для работы с API геокодера:
define('DGIS_API_KEY', 'XXXXXX'); // Ваш ключ
define('DGIS_API_URL', 'http://catalog.api.2gis.ru/geo/search?version=1.3&key=' . DGIS_API_KEY . '&q=%s');

function getGeoObjectInfo($address) {
    $url = sprintf(DGIS_API_URL, $address);
    $response = json_decode(file_get_contents($url));
    if ( $response->response_code == 200 ) {
        // Координаты центра геообъекта
        $coords = $response->result[0]->centroid;
        // Немного магии, чтобы привести их в нужный вид
        $coords = explode(',', preg_replace('/^POINT\(([\S]+)([\s]+)([\S]+)\)$/', '$1,$3', $coords));
    } else
        $coords = array();
    return $coords;
}

Ну и наконец пробежимся по массиву $points нашим «геокодером» и выведем результат в формате JSON:
foreach ( $points as &$address ) {
    $address['coords'] = getGeoObjectInfo($address['address']);
    sleep(1);
}

echo json_encode($points);

Обратите внимание на странную строчку sleep(1); в коде. Дело в том, что я пользуюсь тестовым доступом к API 2GIS, а при нем частота запросов ограничена одним в секунду. После получения полного доступа эту строчку нужно, разумеется, убрать.

Ну и не забываем опять же кэшировать полученные данные, чтобы не создавать 2ГИС лишнюю нагрузку, а себе — лишнее время работы скрипта.

Шаг 3. Показываем карту.

Тут я приведу полный код страницы. Ничего сложного в нем нет, тем более что у 2ГИС есть неплохая документация, ее можно прочитать по адресу api.2gis.ru/doc/maps/info

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script type="text/javascript" src="http://maps.api.2gis.ru/1.0?loadByRequire=1"></script>
    <title>Тестовая карта с объектами</title>
</head>
<body>
    <div id="DGMap" style="width:100%; height:600px"></div>

    <script type="text/javascript">

        $(DG.load(function() {

            // Создаем карту
            var map = new DG.Map('DGMap');
            map.setCenter(new DG.GeoPoint(40.95,57.76),12);
            map.controls.add(new DG.Controls.Zoom());

            // Получаем данные
            $.get('getMapData.php', function (data) {
                var objects = JSON.parse(data);
                for ( i in objects ) {
                    var marker = new DG.Markers.MarkerWithBalloon({
                        geoPoint: new DG.GeoPoint(objects[i].coords[0], objects[i].coords[1]),
                        balloonOptions: {
                            headerContentHtml: '<b>Участок №'+objects[i].zone+'</b>',
                            contentHtml: 'Адрес:'+objects[i].address+'<br />Число квартир:'+objects[i].data
                        }
                    });
                    map.markers.add(marker);
                }
                // Получаем границы добавленных на карту маркеров:
                var markersBounds = map.markers.getBounds();
                // Устанавливаем карте новые границы по маркерам:
                map.setBounds(markersBounds);
            });

        }));

    </script>

</body>
</html>


На этом, собственно, всё. Мы получили простейшую, но вполне функциональную систему, которая показывает на карте объекты согласно списку, ведущемуся в Google Docs. Вот такой mash-up :)

P.S.

Ссылку на получившуюся карту не даю, потому что опасаюсь за сервер, который легко может лечь под хабраэффектом. Но картинку не могу не вставить:


P.P.S.

Собственно, ничто не мешает сделать такой же «трюк» с картами, например, от Яндекса. Получится даже красивее, за счет множества встроенных стилей и технологии кластеризации маркеров на карте. Изначально я пошел именно этим путем, но тесты показали, что геокодер 2ГИС все-таки гораздо точнее. Яндекс справляется с получением координат примерно от 95% адресов при условии их тщательной подготовки, 2ГИС — почти все 100%.
Альберт Степанцев @AlexLeonov
карма
151,0
рейтинг 3,2
Аварийный менеджер команд разработки
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (5)

  • 0
    Можно ли подобным путем нанести на карту схемы трасс для различных технических служб?
    • +1
      Думаю, что можно. Продумайте только формат хранения данных, удобный для редактирования.

      Вот у Яндекса: api.yandex.ru/maps/doc/jsapi/2.x/dg/concepts/router.xml
    • +4
      Схемы трасс чего? Если речь о коммуникациях, то путь получится не совсем аналогичным в любом случае, потому что для этого нужны точные координаты а не координаты центроидов зданий, полученных геокодированием.
      Начертить что-то на карте и сохранить затем в GeoJSON или WKT, поддерживаемый 2GIS API напрямую, можно, например, тут share.mapbbcode.org
      • 0
        Да, речь шла о коммуникациях.
  • +5
    Для нанесения трасс на карту можно воспользоваться геометриями, в частности ломаными. В качестве формата хранения данных можно использовать WKT. В API карт 2GIS есть компонент, который умеет принимать на вход описание геометрий в WKT-формате и потом показывать эту геометрию на карте, вот пример: api.2gis.ru/doc/maps/manual/wkt/#toc-dgwktparser-m-5

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.