GeoJSON тип MultiPoint представляет собой коллекцию
точек, объединённых в единый геометрический объект. В контексте
веб-картографии он используется для эффективной передачи и отрисовки
множества координат, которые логически принадлежат одной сущности или
одному слою данных.
В библиотеке MapLibre GL JS поддержка MultiPoint
реализуется через стандарт GeoJSON-источники, что позволяет использовать
его наравне с Point, LineString и
Polygon. Несмотря на то что визуально каждая точка
обрабатывается рендеринг-пайплайном как отдельный элемент, логически они
остаются частью единой геометрии.
Базовая структура MultiPoint строго соответствует
спецификации GeoJSON:
{
"type": "MultiPoint",
"coordinates": [
[30.31413, 59.93863],
[30.31510, 59.93920],
[30.31600, 59.94010]
]
}
Каждый элемент массива coordinates представляет собой
отдельную точку в формате [долгота, широта]. Важно
учитывать порядок координат: GeoJSON всегда использует именно
[lng, lat], а не наоборот.
В MapLibre GL JS MultiPoint добавляется через источник типа
geojson, где геометрия передаётся напрямую в объект
Feature.
map.addSource('multipoint-source', {
type: 'geojson',
data: {
type: 'Feature',
geometry: {
type: 'MultiPoint',
coordinates: [
[37.6173, 55.7558],
[37.6250, 55.7600],
[37.6300, 55.7620]
]
},
properties: {
group: 'demo-points'
}
}
});
Несмотря на то что используется Feature, ключевым
моментом является именно geometry.type = "MultiPoint".
Наиболее прямой способ визуализации — использование слоя
circle. В этом случае каждая координата внутри
MultiPoint будет отрисована как отдельный круг.
map.addLayer({
id: 'multipoint-layer',
type: 'circle',
source: 'multipoint-source',
paint: {
'circle-radius': 6,
'circle-color': '#3b82f6',
'circle-stroke-width': 1,
'circle-stroke-color': '#1e3a8a'
}
});
Внутренне MapLibre GL JS автоматически “разворачивает”
MultiPoint в набор отдельных feature-подобных элементов для
рендеринга в WebGL.
При использовании symbol слоя каждая точка MultiPoint
может быть представлена как отдельный текстовый или иконный маркер.
map.addLayer({
id: 'multipoint-symbols',
type: 'symbol',
source: 'multipoint-source',
layout: {
'icon-image': 'marker-15',
'icon-size': 1,
'text-field': ['get', 'label'],
'text-offset': [0, 1.2]
}
});
Если у Feature присутствуют свойства, они применяются ко
всем точкам внутри MultiPoint, поскольку объект общий.
MultiPoint является единым Feature,
поэтому:
properties общие для всех точекMultiPointЭто важное ограничение, которое часто приводит к необходимости
перехода от MultiPoint к набору отдельных
Point-feature.
Фильтры применяются ко всему объекту целиком:
map.addLayer({
id: 'filtered-multipoint',
type: 'circle',
source: 'multipoint-source',
filter: ['==', ['get', 'group'], 'demo-points'],
paint: {
'circle-radius': 5,
'circle-color': '#ef4444'
}
});
Если хотя бы один элемент MultiPoint проходит фильтр,
отрисовывается весь объект, включая все его координаты.
Использование MultiPoint может быть как оптимизацией,
так и источником ограничений:
Преимущества:
Ограничения:
В WebGL-конвейере MapLibre каждый элемент MultiPoint всё равно превращается в отдельный draw call на уровне вершинного буфера, поэтому выигрыш наблюдается в основном на уровне CPU и JSON-парсинга.
События click, mouseenter,
mouseleave работают на уровне отдельных отрисованных
точек:
map.on('click', 'multipoint-layer', (e) => {
const features = map.queryRenderedFeatures(e.point, {
layers: ['multipoint-layer']
});
if (features.length) {
const feature = features[0];
console.log(feature.geometry.type);
}
});
При этом важно учитывать, что возвращаемый feature будет
ссылаться на исходный MultiPoint, а не на отдельную
координату внутри него. Это усложняет определение конкретной точки
внутри набора.
Обновление данных выполняется через setData, поскольку
геометрия является частью единого Feature:
const source = map.getSource('multipoint-source');
source.setData({
type: 'Feature',
geometry: {
type: 'MultiPoint',
coordinates: [
[37.6200, 55.7550],
[37.6210, 55.7560],
[37.6220, 55.7570],
[37.6230, 55.7580]
]
}
});
При каждом обновлении происходит полная замена геометрии, а не дифференциальное изменение отдельных точек.
Хотя MultiPoint сам по себе не поддерживает
индивидуальные свойства точек, можно применять стили на уровне слоя:
paint: {
'circle-radius': [
'interpolate',
['linear'],
['zoom'],
5, 2,
10, 6,
15, 10
],
'circle-color': [
'match',
['get', 'type'],
'A', '#22c55e',
'B', '#f59e0b',
'#3b82f6'
]
}
Такой подход эффективен, когда различия между группами точек
определяются на уровне всего Feature, а не отдельных
координат.
Использование MultiPoint и набора отдельных
Point имеет принципиальные различия:
MultiPoint — единая сущность, компактная структураPoint features — максимальная гибкость, индивидуальные
свойстваВ сценариях с высокой интерактивностью (кластеризация, уникальные
tooltip-данные, динамические состояния) отдельные
Point-объекты оказываются более практичными. MultiPoint же
лучше подходит для статических или слабо дифференцированных наборов
координат.
MultiPoint часто используется в следующих сценариях:
При работе с потоковыми данными MultiPoint может выступать промежуточным форматом перед разбиением на полноценные feature-объекты.
GeoJSON не накладывает ограничений на количество точек внутри
MultiPoint, однако производительность рендеринга зависит
от:
При больших объёмах данных возможна деградация интерактивности,
особенно при частых вызовах setData.
Кластеризация в MapLibre GL JS применяется на уровне
Point-features. Поэтому MultiPoint сначала
должен быть преобразован в отдельные точки, иначе кластеризация не будет
работать корректно.
map.addSource('points', {
type: 'geojson',
data: expandedPoints,
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
В этом контексте MultiPoint выступает скорее как исходный формат хранения, но не как финальный формат для кластеризации.
При передаче через сеть MultiPoint имеет меньший overhead по сравнению с массивом Feature-объектов:
// компактный вариант
{
"type": "MultiPoint",
"coordinates": [...]
}
против:
{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "geometry": { "type": "Point", ... } },
...
]
}
Однако выигрыш уменьшается при необходимости сложных свойств для каждой точки.