В MapLibre GL JS источники (sources) являются фундаментальным уровнем архитектуры визуализации. Они определяют, откуда карта получает данные: векторные тайлы, GeoJSON, растровые изображения или видео. Динамическое добавление источников позволяет изменять структуру данных карты во время выполнения приложения без пересоздания стиля и без перезагрузки карты.
Ключевой особенностью является то, что источник не существует сам по себе — он всегда связан с определённым стилем и может быть использован слоями (layers). Это означает, что добавление источника динамически обычно сопровождается последующим добавлением или обновлением слоёв.
В MapLibre GL JS источники добавляются через метод
addSource объекта карты.
map.addSource('cities', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: []
}
});
Основные параметры:
id — уникальный идентификатор источникаtype — тип данных (geojson,
vector, raster, image,
video)data — объект данных или URLПосле добавления источник становится доступен для слоёв:
map.addLayer({
id: 'cities-layer',
type: 'circle',
source: 'cities',
paint: {
'circle-radius': 6,
'circle-color': '#ff0000'
}
});
Динамическое добавление подразумевает, что источник создаётся после инициализации карты, например в ответ на пользовательское действие или загрузку данных с сервера.
function addCitySource(geojson) {
if (map.getSource('dynamic-cities')) {
map.getSource('dynamic-cities').setData(geojson);
return;
}
map.addSource('dynamic-cities', {
type: 'geojson',
data: geojson
});
map.addLayer({
id: 'dynamic-cities-layer',
type: 'circle',
source: 'dynamic-cities',
paint: {
'circle-radius': 5,
'circle-color': '#1e90ff'
}
});
}
Ключевой паттерн:
getSourcesetData, если источник уже естьGeoJSON-источники поддерживают динамическое обновление через метод
setData.
const source = map.getSource('dynamic-cities');
source.setData({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [73.3499, 49.8061]
},
properties: {
name: 'Karaganda'
}
}
]
});
Особенность поведения:
На практике данные почти всегда приходят с сервера. В этом случае
добавление источника происходит после завершения fetch.
async function loadCities() {
const response = await fetch('/api/cities');
const geojson = await response.json();
if (!map.getSource('cities')) {
map.addSource('cities', {
type: 'geojson',
data: geojson
});
map.addLayer({
id: 'cities-layer',
type: 'circle',
source: 'cities'
});
} else {
map.getSource('cities').setData(geojson);
}
}
Источники нельзя безопасно удалять в любой момент, так как они могут
быть связаны со слоями. Удаление выполняется через
removeSource, но только после удаления всех зависимых
слоёв.
if (map.getLayer('cities-layer')) {
map.removeLayer('cities-layer');
}
if (map.getSource('cities')) {
map.removeSource('cities');
}
Порядок имеет критическое значение:
В некоторых сценариях требуется заменить источник данных, не трогая
слои. Это достигается через setData или замену URL (для
тайловых источников через setTiles или обновление
стиля).
function switchDataset(url) {
fetch(url)
.then(res => res.json())
.then(data => {
const source = map.getSource('points');
if (source) {
source.setData(data);
}
});
}
При работе с большими наборами данных часто создаются несколько источников, разделённых по категориям.
const categories = ['restaurants', 'schools', 'hospitals'];
categories.forEach(category => {
map.addSource(category, {
type: 'geojson',
data: `/api/${category}.geojson`
});
map.addLayer({
id: `${category}-layer`,
type: 'circle',
source: category,
paint: {
'circle-radius': 4,
'circle-color': category === 'restaurants'
? '#e74c3c'
: category === 'schools'
? '#2ecc71'
: '#3498db'
}
});
});
Такой подход обеспечивает:
Динамические данные могут обновляться регулярно, например при отображении трекинга объектов.
setInterval(async () => {
const res = await fetch('/api/live-positions');
const data = await res.json();
const source = map.getSource('vehicles');
if (source) {
source.setData(data);
}
}, 5000);
Особенность: MapLibre оптимизирует перерисовку и обновляет только изменённые геометрии.
В сложных приложениях источник добавляется только при выполнении условий, например при активации слоя.
function enableTrafficLayer() {
if (!map.getSource('traffic')) {
map.addSource('traffic', {
type: 'vector',
url: 'mapbox://traffic.source'
});
map.addLayer({
id: 'traffic-layer',
type: 'line',
source: 'traffic',
'source-layer': 'traffic_flow'
});
}
}
Частые проблемы связаны с неправильным порядком операций и повторным добавлением источников.
Типичные ошибки:
Динамическое добавление источников должно выполняться только после
события load.
map.on('load', () => {
map.addSource('initial-data', {
type: 'geojson',
data: '/data/init.geojson'
});
});
Без этой проверки возможно возникновение ошибок из-за незагруженного стиля.
Временные источники используются для интерактивных операций, таких как рисование, выделение или измерение.
map.addSource('draw-temp', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: []
}
});
map.on('mousemove', (e) => {
const source = map.getSource('draw-temp');
source.setData({
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [e.lngLat.lng, e.lngLat.lat]
}
}]
});
});
При частых обновлениях важно минимизировать нагрузку на рендеринг.
Основные подходы:
requestAnimationFramesetDatalet pendingData = null;
function scheduleUpdate(data) {
pendingData = data;
requestAnimationFrame(() => {
if (pendingData) {
map.getSource('dynamic').setData(pendingData);
pendingData = null;
}
});
}
В крупных приложениях источники управляются централизованно.
Подход через registry:
const sourceRegistry = new Map();
function registerSource(id, definition) {
if (map.getSource(id)) return;
map.addSource(id, definition);
sourceRegistry.set(id, definition);
}
function updateSource(id, data) {
const source = map.getSource(id);
if (source) {
source.setData(data);
}
}
Такой подход обеспечивает:
Слои в MapLibre GL JS полностью зависят от источников. При динамическом добавлении источника важно учитывать момент создания слоя.
map.addSource('points', {
type: 'geojson',
data: emptyGeoJSON
});
map.addLayer({
id: 'points-layer',
type: 'circle',
source: 'points'
});
После этого любые обновления источника автоматически отражаются в слое без дополнительных действий.