При работе с векторными данными в веб-картах основная вычислительная нагрузка возникает при операциях поиска и фильтрации геометрий: пересечение с текущим экстентом карты, попадание в область видимости, выбор объектов по клику, кластеризация и перерисовка при изменении масштаба.
Пространственная индексация решает задачу сокращения количества проверяемых объектов за счёт структур, позволяющих быстро исключать геометрии, заведомо не попадающие в интересующую область.
Ключевая идея заключается в переходе от линейного перебора:
R-tree — базовая структура, используемая для индексирования прямоугольных областей (bounding box).
Каждый объект хранится не как геометрия, а как ограничивающий прямоугольник:
[minX, minY, maxX, maxY]
Дерево группирует близкие прямоугольники в узлы, что позволяет:
В OpenLayers применяется реализация R-tree на базе библиотеки RBush.
RBush оптимизирован под:
Внутри OpenLayers RBush используется для индексации геометрий векторных источников.
Хотя основная реализация базируется на R-tree, концептуально иногда используется разбиение пространства на квадранты:
QuadTree чаще применяется в пользовательских оптимизациях или кастомных источниках.
Векторный источник OpenLayers использует пространственный индекс для хранения feature-объектов.
Ключевая структура:
При добавлении объекта:
extent геометрииУпрощённая схема добавления объекта:
const extent = geometry.getExtent();
index.insert({
minX: extent[0],
minY: extent[1],
maxX: extent[2],
maxY: extent[3],
feature: feature
});
При каждом рендере карты происходит выборка объектов, попадающих в текущий экстент видимой области.
Процесс включает:
const extent = map.getView().calculateExtent(map.getSize());
const features = vectorSource.getFeaturesInExtent(extent);
Внутри метода происходит обращение к RBush:
Без пространственного индекса сложность поиска составляет:
O(n)
С использованием R-tree:
O(log n + k)
где:
На практике это критично при:
Для точечных объектов bounding box вырождается в точку:
[minX, minY, maxX, maxY] = [x, y, x, y]
Индекс работает максимально эффективно.
Для линий используется общий bounding box всех вершин.
Особенность:
Для полигонов используется внешний контур:
При использовании кластеризации (Cluster Source) индекс применяется дважды:
Схема:
При изменении геометрии объекта индекс требует синхронизации:
Типичный сценарий:
feature.setGeometry(newGeometry);
vectorSource.changed();
Внутри происходит:
OpenLayers применяет отложенную индексацию:
Это снижает стоимость операций при загрузке больших GeoJSON.
Пространственный индекс взаимодействует с тайловой логикой:
TileGrid ограничивает область поиска, уменьшая нагрузку на индекс:
При обработке событий pointer используется индекс:
Пример логики:
const pixel = map.getEventPixel(event);
const coord = map.getCoordinateFromPixel(pixel);
const extent = [
coord[0], coord[1],
coord[0], coord[1]
];
const features = vectorSource.getFeaturesInExtent(extent);
Несмотря на высокую эффективность, индекс имеет особенности:
В Canvas-рендерере индекс используется для:
В WebGL:
При работе с потоковыми данными (например, real-time геоданные):
После первичного отбора через R-tree применяются точные операции:
intersectscontainswithindistanceИндекс выполняет роль фильтра первого уровня, снижая количество дорогих вычислений.
При работе с сотнями тысяч объектов эффективность индекса проявляется особенно сильно:
Структура R-tree обеспечивает логарифмическую деградацию производительности, а не линейную.