Выборка геообъектов

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

Однако довольно часто перед разработчиком возникают более сложные задачи. Например, необходимо найти такие геообъекты, которые располагаются севернее какой-нибудь географической области (дома, улицы и т. д.). Или проверить, попадает ли один геообъект в геометрическую область другого. Реализация таких задач с использованием стандартных функций API может привести к значительному разрастанию программного кода.

Для упрощения реализации подобных задач в API встроен набор вспомогательных методов. Данные методы являются обертками над стандартными функциями API. При использовании вспомогательных методов не нужно вручную реализовывать такие операции, как поиск по геообъектам, определение их положения относительно друг друга и многие другие.

В качестве примера в приведенной ниже таблице продемонстрировано два варианта реализации одной и той же задачи.

Задача: геообъекты, которые попадают в перетаскиваемый круг, сделать красными. А все остальные геообъекты окрасить в синий (посмотреть пример в песочнице).

myCircle.events.add('drag', function () {
    // Геобъекты, попадающие в круг, будут красными.
    var objectsInsideCircle = objects.searchInside(myCircle);
    objectsInsideCircle.setOptions({
        preset: 'islands#redIcon',
        fillColor: '#ff001a',
        strokeColor: '#ff001a'
    });

    // Оставшиеся объекты - синими.
    objects.remove(objectsInsideCircle).setOptions({
        preset: 'islands#blueIcon',
        fillColor: '#0081ff',
        strokeColor: '#0081ff'
    });
});

// В примере рассматриваются геообъекты всех типов: метки, линии, полигоны и др.
myCircle.events.add('drag', function () {
  for (var i = 0, l = myObjects.length; i < l; i++) {
    var contains = false;
    // Для каждого типа геообъекта реализуется отдельная проверка на попадание в круг.
    switch (myObjects[i].geometry.getType()) {
      case "Point":
        contains = myCircle.contains(myObjects[i].geometry.getCoordinates());
        break;
      case "Polygon":
        contains = true;
        var coordinates = myObjects[i].geometry.getCoordinates();
        for (var i = 0, l = coordinates.length; i < l && contains; i++) {
          for (var j = 0, k = coordinates[i].length; j < k && contains; j++) {
            if (!myCircle.contains(coordinates[i][j])) {
              contains = false;
            }
          }
        }
        break;
      case "Polyline":
      case "Rectangle":
        contains = true;
        var coordinates = myObjects[i].geometry.getCoordinates();
        for (var i = 0, l = coordinates.length; i < l && contains; i++) {
          if (!myCircle.contains(coordinates[i])) {
            contains = false;
          }
        }
        break;
        case "Circle":
          if (!myCircle.contains(myObjects[i].geometry.getCoordinates())) {
            contains = false;
            break;
          }
          var distance = myMap.options.get('projection').getCoordSystem().getDistance(
            myObjects[i].geometry.getCoordinates(),
                              myCircle.geometry.getCoordinates()
          );
          contains = distance + myObjects[i].geometry.getRadius() <= myCircle.geometry.getRadius();
        break;
    }
    if (contains) {
      myObjects[i].options.set({
        preset: 'islands#redIcon',
        fillColor: '#ff001a',
        strokeColor: '#ff001a'
      });
    } else {
      myObjects[i].options.set({
        preset: 'islands#blueIcon',
        fillColor: '#0081ff',
        strokeColor: '#0081ff'
      });
    }
  }
});

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

Вспомогательные методы определены в классе GeoQueryResult. Объект GeoQueryResult называется выборкой геообъектов и представляет собой хранилище данных со специальной структурой. В него предварительно нужно добавить геообъекты, с которыми будут производиться какие-либо действия. Это могут быть геообъекты любого типа: метки, круги, прямоугольники, их коллекции и т.д. При этом их размещение на карте необязательно.

Для создания выборки GeoQueryResult используется функция geoQuery. В нее передается источник данных — геообъекты, на основе которых будет сформирована выборка.

var circle = new ymaps.Circle([[30, 42 ], 5000]);
//Добавление круга на карту.
myMap.geoObjects.add(circle);
// Формирование источника данных.
var objects = [
        new ymaps.Placemark([34, 56]),
        new ymaps.Rectangle([[34, 56], [36, 57]]),
        circle
    ],
    // Формирование выборки.
    storage = ymaps.geoQuery(objects);

// В качестве источника данных в выборку передаются все геообъекты карты
var storage = ymaps.geoQuery(myMap.geoObjects);

Функция geoQuery также может формировать выборку геообъектов на основе асинхронного источника данных. Например, в качестве источника можно указать данные, которые будут получены в результате обращения к серверу. Так как обработка запроса и ответа занимает какое-то время, то перед формированием выборки geoQuery будет ожидать готовности источника:

// Когда сервер вернет результат, geoQuery обработает данные и построит на их основе выборку.
ymaps.geoQuery(ymaps.geocode('Санкт-Петербург'));

Функция geoQuery возвращает сформированную выборку GeoQueryResult. Теперь с ее геообъектами при помощи вспомогательных методов можно производить различные действия: добавлять на карту, задавать свойства или опции и т.д. Также, применяя к данной выборке различные фильтры, на ее основе можно формировать новые выборки.

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

1. Методы, производящие операции над выборкой

Выборка представляет собой упорядоченное множество геообъектов, отобранных по какому-либо критерию. Как и с обычными массивами, с выборкой можно производить различные действия, например, добавлять или удалять объекты, сортировать, представлять ее элементы в обратном порядке и т.д. Для работы с множеством отобранных объектов предназначены следующие методы:

add

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

var placemark = new ymaps.Placemark([34, 56]);
myGeoQueryResult.add(placemark);

Подробнее о методе см. в справочнике.

getLength

Возвращает количество элементов выборки.

var result = ymaps.geoQuery(myMap.geoObject).searchIntersect(myPolygon);
alert('Количество геообъектов, пересекающих многоугольник: ' + result.getLength());

Подробнее о методе см. в справочнике.

remove

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

var objects = [
        new ymaps.Placemark([34, 56]),
        new ymaps.Rectangle([[34, 56], [36, 57]])
    ],
    result = ymaps.geoQuery(objects);
// Обратите внимание, что в результате будет получен другой объет GeoQueryResult, а старый останется без изменений.
var newResult = result.remove(objects[1]);

Подробнее о методе см. в справочнике.

reverse

Переставляет элементы выборки в обратном порядке. Возвращает новую выборку.

var result = ymaps.geoQuery(myMap.geoObjects).sort('x'),
    reversedResult = result.reverse();

Подробнее о методе см. в справочнике.

slice

Возвращает срез выборки.

var result = ymaps.geoQuery(map.geoObjects).slice(0, 10);
alert('Количество элементов в новой выборке:' + result.getLength());

Подробнее о методе см. в справочнике.

sort

Сортирует выборку по заданному параметру.

var result = ymaps.geoQuery(myMap.geoObjects);
result.sort('lat').sort('x');

Подробнее о методе см. в справочнике.

sortByDistance

Создает новую выборку, элементы которой отсортированы по расстоянию от указанного объекта.

var result = ymaps.geoQuery(objects).addToMap(myMap),
    polyline = new ymaps.Polyline([[35, 65], [35, 66], [34, 62], [34, 63]]);
myMap.geoObjects.add(polyline);
var sortedByPolyline = result.sortByDistance(polyline);

Подробнее о методе см. в справочнике.

2. Методы для получения доступа к элементу выборки

Для получения доступа к нужному элементу выборки предназначены методы:

each

Проход по элементам выборки.

ymaps.geoQuery(placemarks).searchIntersect(myMap).each(function(pm) {
    if (pm.options.get('preset') == 'islands#redIcon') {
        myMap.geoObjects.remove(pm);
    }
});

Подробнее о методе см. в справочнике.

get

Возвращает элемент выборки по индексу.

var result = ymaps.geoQuery(placemarks).sort('lat'),
    // Самый южный объект.
    southObject = result.get(0),
    // Самый северный объект.
    northObject = result.get(result.getLength() - 1);

Подробнее о методе см. в справочнике.

getIterator

Возвращает итератор по элементам выборки.

// Поиск элементов, на которые попадают координаты клика.
myMap.events.add('click', function (event) {
    var iterator = ymaps.geoQuery(myMap.geoObjects)
            .searchContaining(event.getCoordinates())
            .getIterator(),
        obj;
    while ((obj = iterator.getNext()) != iterator.STOP_ITERATION) {
        // Совершаем необходимые действия над геообъектом.
    }
});

Подробнее о методе см. в справочнике.

indexOf

Возвращает индекс элемента в выборке. Если элемент не найден, возвращает -1.

// Отсортируем выборку по полю name.
var result = ymaps.geoQuery(polygons).sort('properties.name');
alert('Новая позиция первого элемента: ' + result.indexOf(polygons[0]));

Подробнее о методе см. в справочнике.

map

Вызывает callback для всех элементов выборки и формирует новую выборку на основе полученных результатов.

// Добавим на карту только объекты-окружности.
var circlesResult = ymaps.geoQuery(objects).search('geometry.type="Circle"').addToMap(myMap),
// Также добавим на карту метки, обозначающие центры окружностей.
    centers = circlesResult.map(function (object) {
        return new ymaps.Placemark(object.geometry.getCenter());
    }).addToMap(myMap);

Подробнее о методе см. в справочнике.

3. Методы для обработки асинхронных операций

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

Асинхронное взаимодействие реализовано с помощью обещаний (объектов-promise). Принцип такого взаимодействия проиллюстрирован на рисунке.

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

На рисунке функция geocode, ожидающая ответа от сервера, создает объект-обещание (promise1), который выполнится тогда, когда сервер вернет данные. Следующая функция geoQuery подписывается на promise1 и начнет заполнять выборку только тогда, когда этот объект-обещание будет выполнен. Аналогично создается новый объект promise3, на который подписывается уже следующая функция add.

Внимание

Функции, не возвращающие объект GeoQueryResult (например, getLength()), выполняются синхронно, то есть перед началом работы не дожидаются готовности данных:

var result = ymaps.geoQuery(ymaps.geocode('река Лена')).getLength();
alert(result); // result = 0

Для того чтобы такие функции выполнялись асинхронно, необходимо передать их вызов функции then в качестве callback:

var result = ymaps.geoQuery(ymaps.geocode('река Лена'));
result.then(function () {
     alert('Количество найденных объектов: ' + result.getLength());
}, function () {
     alert('Произошла ошибка.');
});

Подробнее о методе см. в справочнике.

Для отладки асинхронной операции предназначен метод isReady(), возвращающий признак готовности результата:

var result = ymaps.geoQuery(ymaps.geocode('Иваново'));
if (!result.isReady()) {
    result.then(function () {
        // Обработка данных.
    });
} else {
    // Обработка данных.
}

Подробнее о методе см. в справочнике.

4. Методы для групповой обработки элементов выборки

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

addEvents

Назначает обработчики событий на все элементы выборки.

ymaps.geoQuery(map.geoObjects).search('geometry.type="Circle"').addEvents('click', function () {
    alert('Вы кликнули по кругу!');
});

Подробнее о методе см. в справочнике.

addTo

Добавляет элементы выборки в заданную коллекцию геообъектов.

// Покажем на карте объекты северного полушария.
var result1 = ymaps.geoQuery(placemarks).search('lat > 0').addTo(myMap.geoObjects);

Подробнее о методе см. в справочнике.

addToMap

Добавляет объекты выборки на карту.

// Покажем на карте объекты северного полушария.
var result1 = ymaps.geoQuery(placemarks).search('lat > 0').addToMap(myMap);

Подробнее о методе см. в справочнике.

clusterize

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

// Выберем только точечные объекты и добавим их в кластеризатор.
var clusterer = ymaps.geoQuery(objects).search('geometry.type="Point"').clusterize();
myMap.geoObjects.add(clusterer);

Подробнее о методе см. в справочнике.

removeEvents

Удаляет подписку на событие с объектов.

Внимание

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

var callback = function () {
        alert('Вы кликнули по кругу!');
    };
ymaps.geoQuery(map.geoObjects).search('geometry.type="Circle"').addEvents('click', callback);
// ...
ymaps.geoQuery(map.geoObjects).search('geometry.type="Circle"').removeEvents('click', callback);

Подробнее о методе см. в справочнике.

removeFrom

Удаляет объекты выборки из заданной коллекции геообъектов.

// Покажем на карте все объекты.
var result1 = ymaps.geoQuery(placemarks).addTo(myMap.geoObjects),
// А затем скроем объекты из северного полушария.
    result2 = result1.search('lat > 0').removeFrom(myMap.geoObjects);

Подробнее о методе см. в справочнике.

removeFromMap

Удаляет объекты выборки с карты.

// Покажем на карте все объекты.
var result1 = ymaps.geoQuery(placemarks).addToMap(myMap),
// А затем скроем объекты из северного полушария.
    result2 = result1.search('lat > 0').removeFromMap(myMap);

Подробнее о методе см. в справочнике.

setOptions

Задает значение опций всем объектам выборки.

var result = ymaps.geoQuery(placemarks);
// Сделаем видимыми элементы, попадающие в прямоугольную область.
result.searchIntersect(myBounds).setOptions('visible', true);.

Подробнее о методе см. в справочнике.

setProperties

Задает значение поля properties всем объектам выборки.

var result = ymaps.geoQuery(objects);
// Пометим элементы, попадающие в области.
result.searchIntersect(myBounds1).setProperties('intersectBounds', true);
result.searchIntersect(myBounds2).setProperties('intersectBounds', true);
// ...
result.search('properties.intersectBounds = true').addToMap(myMap);

Подробнее о методе см. в справочнике.

unsetOptions

Обнуляет значение опций всем объектам выборки.

result.unsetOptions('visible');

Подробнее о методе см. в справочнике.

unsetProperties

Обнуляет значение поля properties у всех элементов выборки.

var result = ymaps.geoQuery(objects);
// Пометим элементы, попадающие в первую область, но не попадающие во вторую.
result.searchIntersect(myBounds1).setProperties('intersectBounds', true);
result.searchIntersect(myBounds2).unsetProperties('intersectBounds', true);
// ...
result.search('properties.intersectBounds = true').addToMap(myMap);

Подробнее о методе см. в справочнике.

5. Методы, осуществляющие поиск по выборке

intersect

Создает новую выборку, содержащую общие элементы для двух других выборок.

var result = ymaps.geoQuery(placemarks),
    greenObjects = result.search('properties.color=green'),
    roundObjects = result.search('properties.shape=round'),
    greenRoundObjects = greenObjects.intersect(roundObjects);
alert('Количество круглых зеленых объектов: ' + greenRoundObjects.getLength());

Подробнее о методе см. в справочнике.

search

Поиск объектов выборки, удовлетворяющих заданным условиям.

var result = ymaps.geoQuery(myMap.geoObjects);

// Поиск объектов с определенным типом геометрии. Значение поля указано в кавычках, так как это строка.
result.search('geometry.type = "Point"')
     // Поиск по координате.
    .search('geometry.coordinates.0 > 100')

Подробнее о методе см. в справочнике.

searchContaining

Cоздает новую выборку из объектов, содержащих указанный геообъект.

Внимание

Для корректных расчетов все геообъекты необходимо добавить на карту. Если геообъекты не нужно отображать, то необходимо выставить значение их опции visible: false.

var result = ymaps.geoQuery(objects).addToMap(myMap),
    polygon = new ymaps.Polygon([[[35, 65], [35, 66], [34, 62], [34, 63], [35, 65]]]);
myMap.geoObjects.add(polygon);
var objectsContainingPolygon = result.searchContaining(polygon);

Подробнее о методе см. в справочнике.

searchInside

Создает новую выборку из объектов, целиком входящих в указанный объект.

Внимание

Для корректных расчетов геообъекты необходимо добавить на карту. Если геообъекты не нужно отображать, то необходимо выставить значение их опции visible: false.

var result = ymaps.geoQuery(objects).addToMap(myMap),
    polygon = new ymaps.Polygon([[[35, 65], [35, 66], [34, 62], [34, 63], [35, 65]]]);
myMap.geoObjects.add(polygon);
var objectsInsidePolygon = result.searchInside(polygon);

Подробнее о методе см. в справочнике.

searchIntersect

Создает новую выборку из объектов выборки, пересекающих указанный геобъект.

Внимание

Для корректных расчетов геообъекты необходимо добавить на карту. Если геообъекты не нужно отображать, то необходимо выставить значение их опции visible: false.

var result = ymaps.geoQuery(objects).addToMap(myMap),
    polygon = new ymaps.Polygon([[[35, 65], [35, 66], [34, 62], [34, 63], [35, 65]]]);
myMap.geoObjects.add(polygon);
var objectsIntersectPolygon = result.searchIntersect(polygon);

Подробнее о методе см. в справочнике.

6. Методы для получения геометрических параметров выборки

applyBoundsToMap

Позволяет установить видимую область карты так, чтобы видны были все объекты из выборки.

var result = ymaps.geoQuery(objects).applyBoundsToMap(myMap);
alert('Видимая область карты изменена.');

Подробнее о методе см. в справочнике.

getBounds

Возвращает географические координаты области, охватывающей объекты результата.

// Устанавливаем центр и масштаб карты так, чтобы отобразить весь результат целиком.
myMap.setBounds(myResult.getBounds());
getCenter

Возвращает координаты центра области, охватывающей объекты.

// Сместим центр карты в центр области, охватывающей объекты.
myMap.setCenter(ymaps.geoQuery(objects).getCenter());
getCentralObject

Возвращает геообъект, который наиболее близко расположен к центру видимой области карты.

// Откроем балун у геообъекта, ближайшего к центру видимой области карты.
ymaps.geoQuery(objects).getCentralObject(myMap).balloon.open();
getClosestTo

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

Внимание

Для корректных расчетов геообъекты необходимо добавить на карту. Если геообъекты не нужно отображать, то необходимо выставить значение их опции visible: false.

var result = ymaps.geoQuery(objects).addToMap(myMap),
    polyline = new ymaps.Polyline([[35, 65], [35, 66], [34, 62], [34, 63]]);
myMap.geoObjects.add(polyline);
var closestObject = result.getClosestTo(polyline);

Подробнее о методе см. в справочнике.

getExtreme

Возвращает максимальные и минимальные значения координат среди координат объектов выборки.

alert('Самая северная координата: ', ymaps.geoQuery(myMap.geoObjects).getExtreme('top'));

Подробнее о методе см. в справочнике.

getExtremeObject

Возвращает объект с минимальной или максимальной координатой среди координат объектов выборки.

// Откроем балун на самом северном объекте.
var topObject = ymaps.geoQuery(myMap.geoObjects).getExtremeObject('top');
topObject.balloon.open();

Подробнее о методе см. в справочнике.

getGlobalPixelBounds

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

var result = ymaps.geoQuery(placemarks).search('properties.type="shop"').getGlobalPixelBounds(myMap);
if (Math.abs(result[0][0] - result[1][0]) > myMap.container.getSize()[0]) {
   alert('Объекты не поместятся на карту по ширине!');
}

Подробнее о методе см. в справочнике.

getGlobalPixelCenter

Для текущего коэффициента масштабирования возвращает глобальные пиксельные координаты центра области, охватывающей объекты выборки.

// Посчитаем номер тайла, на который приходится центр области, охватывающей результат.
var globalPixelCenter = ymaps.geoQuery(objects).getGlobalPixelCenter(myMap),
    tileNumber = [
        Math.floor(globalPixelCenter[0] / 256),
        Math.floor(globalPixelCenter[1] / 256)
    ];
alert('Номер центрального тайла: ' + tileNumber[0] + ' ' + tileNumber[1]);

Подробнее о методе см. в справочнике.

Примечание

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