Поиск по карте

API предоставляет сервис геокодирования, который позволяет определять географические координаты объекта по его названию (прямое геокодирование) и, наоборот, название объекта по его координатам (обратное геокодирование).

Оба вида геокодирования (прямое и обратное) производятся с помощью функции geocode, в которую можно передать название объекта в виде строки или координаты в виде массива.

var myGeocoder = ymaps.geocode("Петрозаводск");
var myReverseGeocoder = ymaps.geocode([61.79,34.36]);

Функция geocode является асинхронной, то есть во время ее выполнения производится обмен данными с сервером.

Асинхронное взаимодействие реализовано с помощью обещаний (promises). Вызов функции geocode приводит к (немедленному) созданию объекта типа util.Promise, который выполнит заданные функции в тот момент, когда от сервера вернутся результаты геокодирования или сообщение об ошибке.

Результат функции geocode передается в функцию-обработчик в виде коллекции GeoObjectCollection. Этот объект реализует интерфейс IGeoObject, то есть может быть размещен на карте.

var myGeocoder = ymaps.geocode("Петрозаводск");
myGeocoder.then(
    function (res) {
        alert('Координаты объекта :' + res.geoObjects.get(0).geometry.getCoordinates());
    },
    function (err) {
        alert('Ошибка');
    }
);

Результаты геокодирования передаются в функцию-обработчик либо в виде коллекции GeoObjectArray (по умолчанию) либо в виде JSON. Формат возвращаемых данных задается опцией json (true/false). Подробнее о формате возвращаемых данных можно посмотреть Поиск по карте.

Поиск можно производить как по всей карте мира, так и по заданной прямоугольной области. Однако прямоугольная область может и не накладывать жесткие ограничения на поиск. Это означает, что поиск будет производиться по всей карте, но чем ближе будет находиться найденный объект к центру области, тем более высоко он будет ранжироваться.

При обратном геокодировании можно уточнить тип объектов, которые необходимо найти (дом, улица, район города, населенный пункт, станция метро).

Найденные объекты ранжируются по степени удаленности от заданной точки.

var myCoords = [55.754952,37.615319];
myMap.geoObjects.add(
    new ymaps.Placemark(myCoords,
        {iconContent: 'Где метро?'},
        {preset: 'twirl#greenStretchyIcon'}
    )
);
var myGeocoder = ymaps.geocode(myCoords, {kind: 'metro'});
myGeocoder.then(
    function (res) {
        var nearest = res.geoObjects.get(0);
        var name = nearest.properties.get('name');
        nearest.properties.set('iconContent', name);
        nearest.options.set('preset', 'twirl#redStretchyIcon');
        myMap.geoObjects.add(res.geoObjects);
    },
    function (err) {
        alert('Ошибка');
    }
);

При поиске улиц, районов и населенных пунктов не учитываются отнесенные к ним здания, каких бы размеров они ни были.

var myGeocoder = ymaps.geocode('Новый Арбат, 10');
myGeocoder.then(
    function (res) {
        var coords = res.geoObjects.get(0).geometry.getCoordinates();
        var myGeocoder = ymaps.geocode(coords, {kind: 'street'});
        myGeocoder.then(
            function (res) {
                var street = res.geoObjects.get(0);
                var name = street.properties.get('name');
                // Будет выведено «улица Большая Молчановка»,
                // несмотря на то, что обратно геокодируются
                // координаты дома 10 на ул. Новый Арбат.
                alert(name);
            }
        );
});

Множественное геокодирование

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

Чаще всего геокодирование производится на стороне клиента (то есть запросы геокодеру отправляет браузер). При множественном геокодировании такой подход не всегда является оптимальным. Дело в том, что геокодер за один запрос позволяет получать данные только для одного объекта. То есть чем больше адресов необходимо геокодировать, тем больше HTTP-запросов надо отправить.

Отправка большого числа HTTP-запросов приводит к увеличению трафика, а дальнейшая обработка ответов — к уменьшению скорости отображения данных на карте. К тому же высока вероятность превысить суточный лимит в 25000 запросов к геокодеру (см. Пользовательское соглашение). При поиске на карте, например, ста объектов бразуер отправляет серверу сто запросов. Соответственно, при посещении сайта уже 300 пользователей число запросов к серверу достигнет 30000.

Решением данной проблемы служит геокодирование данных на стороне сервера. Если на карте необходимо искать всегда одни и те же географические объекты, то нет смысла производить геокодирование на каждом клиенте отдельно. В этом случае целесообразно выполнить его один раз на стороне сервера и кэшировать полученный результат. А клиентам передавать уже данные, подготовленные для добавления на карту.

Геокодирование на стороне сервера

Для реализации геокодирования на серверной стороне разработан Node.js-модуль (multi-geocode), который использует HTTP-сервис геокодирования. Данный модуль позволяет осуществлять поиск сразу множества объектов по их географическим названиям. Полученный результат может быть добавлен на карту с помощью встроенных функций API.

Примечание. Модуль multi-geocode позволяет производить только прямое геокодирование.

Установить модуль можно с помощью менеджера пакетов npm:

npm install multi-geocoder

После установки модуля Node.js-приложению будет доступен класс MultiGeocoder, инициализируемый объектом со следующими полями (все поля являются необязательными):

  • provider – провайдер геокодирования. Поддерживаются провайдеры Яндекса и Google (по умолчанию принимает значение 'yandex');
  • coordorder – предпочитаемый порядок координат в ответе геокодера. По умолчанию координаты возвращаются в последовательности: долгота, широта ('longlat');
  • встроенные параметры геокодирования, допустимые для указанного провайдера.
// Подключаем модуль multi-geocoder
var MultiGeocoder = require('multi-geocoder'),
    // Получаем доступ к сервису геокодирования.
    geocoder = new MultiGeocoder({
        coordorder: 'latlong',
        lang: 'ru-RU'
    });

Для формирования и отправки запроса геокодеру используется функция geocode. Она принимает на вход следующие параметры:

  • массив строк с адресами, которые требуется геокодировать ('Москва, ул Крылатские холмы, 28'), либо массив объектов, содержащих адрес и любые другие свойства ({'address': 'Москва, ул. Крылатские холмы, 28', 'organization': 'школа'});
    Примечание. Перед формированием запроса модуль извлекает адреса из переданного массива с помощью встроенного метода getText. По умолчанию предполагается, что адреса заданы в виде строк, поэтому getText возвращает содержимое элементов массива целиком. Если же адреса заданы в виде объектов с полями, то необходимо переопределить метод getText так, чтобы он возвращал значение полей, содержащих нужные адреса. Подробнее см. ниже.
  • встроенные параметры геокодирования, которые будут применены только для текущего запроса.

Ниже приведен пример геокодирования адресов, заданных в виде строк:

var MultiGeocoder = require('multi-geocoder'),
    geocoder = new MultiGeocoder({ coordorder: 'latlong', lang: 'ru-RU' });

geocoder.geocode([
    'Москва, 1905 года ул., д.19',
    'Москва, 1-ая Квесисская ул., д 18',
    'Москва, 1-й Тверской-Ямской пер, д.16'
    ], {
    // Описание объектов в ответе будет на алглийском языке,
    // несмотря на то что параметр lang задан также в конструкторе MultiGeocoder.
    lang: 'en-US'
})
    .then(function (res) {
        console.log(res);
    });
Примечание. Параметры геокодирования, заданные через функцию geocode, имеют более высокий приоритет по сравнению с параметрами, заданными в конструкторе MultiGeocoder.

В следующем примере в функцию geocode передается массив объектов с полями address, name и phone. Чтобы извлечь из заданных объектов адреса для геокодирования, необходимо переопределить метод getText:

var MultiGeocoder = require('multi-geocoder'),
    geocoder = new MultiGeocoder({ provider: 'yandex', coordorder: 'latlong' }),
    // Получаем экземпляр провайдера.
    provider = geocoder.getProvider();

// Переопределяем метод getText(), извлекающий из переданного массива адреса,
// которые требуется геокодировать.
provider.getText = function (point) {
    var text = 'Москва, ' + point.address;
    console.log(text);
    return text;
};
geocoder.geocode([
    {
        "address": "1905 года ул., д.19",
        "name": "Старый лекарь",
        "phone": "8-499-253-52-61"
    }, {
        "address": "1-ая Квесисская ул., д 18",
        "name": "А5 №2",
        "phone": "8-495-614-04-67"
    }, {
        "address": "1-й Тверской-Ямской пер, д.16",
        "name": "Самсон Фарма №9",
        "phone": "8-495-251-22-27"
    }
])
    .then(function (res) {
        console.log(res);
    }); 

Посмотреть более развернутый пример можно в следующих файлах: index.js, source.json (исходные данные), gecoded.json (результат геокодирования).

Функция geocode возвращает объект-обещание (util.Promise), который выполнит заданную функцию-обработчик в тот момент, когда сервер вернет результаты геокодирования.

Результат геокодирования передается функции-обработчику в формате GeoJSON. Данный формат предназначен для описания различных структур географических данных. Объекты, представленные в GeoJSON-формате, могут быть добавлены на карту.

Далее представлен результат множественного геокодирования с использованием модуля multi-geocode:

/* Объект GeoJSON представляет собой коллекцию объектов типа Feature,
 содержащих информацию о каждом из  запрашиваемых адресов. */
{ type: 'FeatureCollection',
  features: [{
    // Результаты поиска 'Москва, 1905 года ул., д.19'
    type: 'Feature',
    bbox: [ [ 37.556586, 55.765554 ], [ 37.560682, 55.767863 ] ],
    geometry: { /* hide:geometry */
      type: 'Point',
      coordinates: [ 37.558634, 55.766709 ]
    },
    properties: {
      name: 'улица 1905 года, 19',
      description: 'Москва, Россия',
      metaDataProperty: {
          GeocoderMetaData: {
            kind: 'house',
            text: 'Россия, Москва, улица 1905 года, 19',
            precision: 'exact',
            AddressDetails: {
              Country: {
                AddressLine: 'Москва, улица 1905 года, 19',
                CountryNameCode: 'RU',
                CountryName: 'Россия',
                AdministrativeArea: {
                  AdministrativeAreaName: 'Московская область',
                  SubAdministrativeArea: {
                    SubAdministrativeAreaName: 'Москва',
                    Locality: {
                      LocalityName: 'Москва',
                      Thoroughfare: {
                        ThoroughfareName: 'улица 1905 года',
                        Premise: {
                          PremiseNumber: '19'
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }

    // Результаты поиска 'Москва, 1-ая Квесисская ул., д 18'
    { type: 'Feature',
      ...
    }

    // Результаты поиска '1-й Тверской-Ямской пер, д.16'
    { type: 'Feature',
      ...
    }
  ]
}
Описание параметров
Объекты properties Описание
bbox Координаты ограничивающей области.
kind Вид найденного топонима. Список доступных значений.
text Полный адрес топонима одной строкой текста.
name Название объекта либо его неполный адрес. Данная информация используется, как правило, для отображения в метке или балуне.
precision Точность соответствия между номером найденного дома и номером дома из запроса.
description Подробный адрес объекта.
AddressLine Запрашиваемая строка в поиске.
CountryNameCode Код страны в соответствии со стандартом ISO 3166-1.
CountryName Название страны.
AdministrativeAreaName Область, провинция, край, республика, города — субъекты федерации.
SubAdministrativeAreaName Район области, муниципальный район, городское поселение, сельское поселение.
LocalityName Населенный пункт: город, поселок, станица и т. д.
ThoroughfareName Общепризнанная именованная территория согласно спецификации формата xAL.
PremiseNumber Конечная единица в иерархии административно-территориального деления. Номер дома, парк, название моста и пр. Подробнее см. в описании спецификации формата xAL
Объекты feature Описание
type Тип объекта GeoJSON.
  • FeatureCollection – описание коллекции объектов;
  • Feature – описание конечного объекта.
Объекты, вложенные в geometry Описание
type Тип геометрии.
coordinates Координаты найденного топонима.
Объекты properties Описание
bbox Координаты ограничивающей области.
kind Вид найденного топонима. Список доступных значений.
text Полный адрес топонима одной строкой текста.
name Название объекта либо его неполный адрес. Данная информация используется, как правило, для отображения в метке или балуне.
precision Точность соответствия между номером найденного дома и номером дома из запроса.
description Подробный адрес объекта.
AddressLine Запрашиваемая строка в поиске.
CountryNameCode Код страны в соответствии со стандартом ISO 3166-1.
CountryName Название страны.
AdministrativeAreaName Область, провинция, край, республика, города — субъекты федерации.
SubAdministrativeAreaName Район области, муниципальный район, городское поселение, сельское поселение.
LocalityName Населенный пункт: город, поселок, станица и т. д.
ThoroughfareName Общепризнанная именованная территория согласно спецификации формата xAL.
PremiseNumber Конечная единица в иерархии административно-территориального деления. Номер дома, парк, название моста и пр. Подробнее см. в описании спецификации формата xAL
Объекты feature Описание
type Тип объекта GeoJSON.
  • FeatureCollection – описание коллекции объектов;
  • Feature – описание конечного объекта.
Объекты, вложенные в geometry Описание
type Тип геометрии.
coordinates Координаты найденного топонима.

Ниже приведен фрагмент кода, демонстрирующий загрузку сохраненных GeoJSON-данных и их добавление на карту:

// Загружаем данные с сервера с помощью jQuery.
jQuery.ajax({
    url: 'http://api.yandex.ru/maps/doc/jsapi/2.0/examples/geocoded.json',
    dataType: 'json'
}).then(function(res) {
      // Формируем выборку на основе загруженных данных.
      var objects = ymaps.geoQuery(res);
      objects.addToMap(myMap);
  });

На данный момент API позволяет манипулировать GeoJSON-данными только через класс GeoQueryResult. Это означает, что на основе загруженных данных необходимо сформировать выборку, после чего уже производить над ней различные действия (добавлять на карту, кластеризовать и т. д.).