Выборка геообъектов
При работе с интерактивной картой с геообъектами можно производить различные действия. Например, добавлять на карту, изменять свойства или опции, назначать обработчик события и т. д. Такие операции являются базовыми, и для выполнения каждой из них в 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]);
Подробнее о методе см. в справочнике.
Примечание
В песочнице можно ознакомиться с примерами работы с выборкой геообъектов.