GeoJSON представляет собой текстовый формат для описания геометрии и
свойств объектов на карте: точек, линий и полигонов. В контексте Mapbox
GL JS GeoJSON используется как один из ключевых типов источников данных
(source), обеспечивающий динамическое отображение
пространственных объектов.
Источник типа geojson в Mapbox GL JS хранит структуру
FeatureCollection и может быть изменён в любой момент через API, без
необходимости пересоздания слоя или всей карты.
Базовая структура источника:
map.addSource('points-source', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: []
}
});
Источник становится реактивной точкой данных: любое обновление
содержимого data может быть отражено на карте.
После создания источника он связывается со слоем отображения:
map.addLayer({
id: 'points-layer',
type: 'circle',
source: 'points-source',
paint: {
'circle-radius': 6,
'circle-color': '#3b82f6'
}
});
Слой не содержит собственных данных — он лишь визуализирует содержимое GeoJSON-источника. Это ключевой принцип архитектуры Mapbox GL JS: разделение данных и представления.
setDataДля обновления GeoJSON используется метод:
source.setData(newGeojson);
Где source — объект источника, полученный через:
const source = map.getSource('points-source');
Пример полного обновления данных:
const source = map.getSource('points-source');
source.setData({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [69.589, 42.315]
},
properties: {
name: 'Object A'
}
}
]
});
При вызове setData происходит полная перерисовка всех
слоёв, использующих данный источник.
Наиболее простой подход — полная замена данных. Он используется, когда структура данных формируется заново:
source.setData(geojson);
Преимущества:
Недостатки:
GeoJSON не поддерживает частичное обновление отдельных features напрямую. Однако возможна имитация через локальное изменение структуры:
const data = source._data; // внутреннее состояние (не рекомендуется напрямую использовать)
data.features[0].properties.status = 'upd ated';
source.setData(data);
Корректный подход предполагает хранение копии данных вне Mapbox и
обновление её перед передачей в setData.
При работе с динамическими данными ключевым становится внешний стор состояния:
let geojsonState = {
type: 'FeatureCollection',
features: []
};
function updateFeature(id, newCoords) {
geojsonState.features = geojsonState.features.map(f => {
if (f.properties.id === id) {
return {
...f,
geometry: {
type: 'Point',
coordinates: newCoords
}
};
}
return f;
});
map.getSource('points-source').setData(geojsonState);
}
Такой подход исключает мутации внутренних структур Mapbox GL JS и обеспечивает предсказуемость обновлений.
Добавление новых элементов осуществляется через модификацию массива
features:
function addPoint(coords, props) {
geojsonState.features.push({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: coords
},
properties: props
});
map.getSource('points-source').setData(geojsonState);
}
Каждое добавление приводит к перерасчёту слоя.
Удаление выполняется через фильтрацию:
function removePoint(id) {
geojsonState.features = geojsonState.features.filter(
f => f.properties.id !== id
);
map.getSource('points-source').setData(geojsonState);
}
При поступлении данных из WebSocket или аналогичных потоков обновление GeoJSON становится непрерывным процессом.
const socket = new WebSocket('wss://example.com/stream');
socket.onmess age = (event) => {
const message = JSON.parse(event.data);
geojsonState.features = message.features;
map.getSource('points-source').setData(geojsonState);
};
В подобных сценариях критично контролировать частоту обновлений, так
как каждый setData вызывает перерисовку слоя.
Частые обновления могут перегружать рендеринг:
let pending = false;
function scheduleUpdate(data) {
geojsonState = data;
if (!pending) {
pending = true;
requestAnimationFrame(() => {
map.getSource('points-source').setData(geojsonState);
pending = false;
});
}
}
Каждое обновление передаёт полный объект, поэтому уменьшение количества свойств напрямую влияет на производительность.
Для больших наборов данных используется кластеризация:
map.addSource('points', {
type: 'geojson',
data: geojsonState,
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
Это снижает количество рендеримых объектов при масштабировании.
Одним из частых сценариев является перемещение объектов по карте:
function animatePoint() {
const feature = geojsonState.features[0];
feature.geometry.coordinates[0] += 0.001;
map.getSource('points-source').setData(geojsonState);
requestAnimationFrame(animatePoint);
}
animatePoint();
Каждый кадр обновляет GeoJSON, создавая эффект движения.
| Метод | Производительность | Сложность | Применение |
|---|---|---|---|
| Полный setData | Средняя | Низкая | Небольшие наборы данных |
| Внешнее состояние | Высокая | Средняя | Динамические приложения |
| Частые обновления | Низкая | Низкая | Потоки реального времени |
При вызове setData Mapbox GL JS:
Это объясняет, почему обновление GeoJSON является относительно дорогой операцией.
const data = geojsonState;
data.features.push(newFeature);
map.getSource('points').setData(data);
Проблема возникает при неявных ссылках на один и тот же объект.
map.getSource('points').setData({
type: 'Feature'
});
Структура должна строго соответствовать спецификации GeoJSON.
Высокочастотные вызовы setData приводят к:
Наиболее устойчивый подход строится на разделении слоёв ответственности:
class GeojsonController {
constructor(map, sourceId) {
this.map = map;
this.sourceId = sourceId;
this.state = { type: 'FeatureCollection', features: [] };
}
se t(features) {
this.state.features = features;
this.sync();
}
sync() {
this.map.getSource(this.sourceId).setData(this.state);
}
}
В Mapbox GL JS обновление GeoJSON строится вокруг одного механизма —
полной перезаписи источника данных через setData. Все
оптимизации и архитектурные решения направлены не на частичное
обновление, а на минимизацию количества полных пересчётов и контроль
частоты вызовов этого метода.