Добавление и удаление

Работа с динамическими данными в Mapbox GL JS строится вокруг управления слоями (layers), источниками данных (sources) и объектами интерфейса карты. Архитектура библиотеки предполагает строгую зависимость: сначала добавляется источник данных, затем на его основе создаются визуальные слои. Нарушение этого порядка приводит к ошибкам рендеринга и исключениям во время выполнения.

Источник (source) в Mapbox GL JS представляет собой абстракцию данных, которые будут визуализированы на карте. Чаще всего используется формат GeoJSON, хотя поддерживаются и тайловые источники.

Добавление источника выполняется через метод addSource:

map.on('load', () => {
  map.addSource('cities-source', {
    type: 'geojson',
    data: {
      type: 'FeatureCollection',
      features: [
        {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: [69.2401, 53.2145]
          },
          properties: {
            name: 'City A'
          }
        }
      ]
    }
  });
});

Ключевой момент заключается в том, что источник становится доступным для использования только после события load. Попытка добавления до загрузки стиля приводит к ошибкам, так как внутренняя модель карты ещё не инициализирована.

Добавление визуальных слоёв

Слой определяет способ отображения данных из источника: точки, линии, полигоны, символы, заливки и другие типы.

Простейший пример слоя типа circle:

map.addLayer({
  id: 'cities-layer',
  type: 'circle',
  source: 'cities-source',
  paint: {
    'circle-radius': 6,
    'circle-color': '#ff5200'
  }
});

Слой напрямую связан с источником через поле source. Без существующего источника добавление слоя невозможно.

Слои символов (symbol layer)

Слои типа symbol используются для отображения текста и иконок:

map.addLayer({
  id: 'cities-labels',
  type: 'symbol',
  source: 'cities-source',
  layout: {
    'text-field': ['get', 'name'],
    'text-size': 14
  },
  paint: {
    'text-color': '#000'
  }
});

Управление порядком слоёв

Порядок добавления слоёв определяет их визуальное наложение. Позднее добавленные слои отображаются выше предыдущих.

Контроль позиции осуществляется через параметр beforeId:

map.addLayer({
  id: 'roads-layer',
  type: 'line',
  source: 'roads-source',
  paint: {
    'line-color': '#333',
    'line-width': 2
  }
}, 'cities-layer');

В данном случае слой дорог будет добавлен ниже слоя cities-layer.

Удаление слоёв

Удаление слоя выполняется методом removeLayer:

if (map.getLayer('cities-layer')) {
  map.removeLayer('cities-layer');
}

Перед удалением важно проверить существование слоя, так как повторный вызов удаления приводит к ошибке.

Удаление слоя не затрагивает источник данных. Это разделение позволяет переиспользовать один источник в нескольких слоях.

Удаление источников данных

Удаление источника выполняется через removeSource:

if (map.getSource('cities-source')) {
  map.removeSource('cities-source');
}

Критическое правило архитектуры Mapbox GL JS: источник нельзя удалить, пока он используется хотя бы одним слоем. Поэтому порядок всегда обратный:

  1. Удалить все слои, использующие источник
  2. Удалить сам источник

Нарушение приводит к исключениям в консоли и нестабильному состоянию карты.

Обновление данных источника

Для динамических приложений ключевым механизмом является метод setData, доступный у GeoJSON-источников.

const source = map.getSource('cities-source');

source.setData({
  type: 'FeatureCollection',
  features: [
    {
      type: 'Feature',
      geometry: {
        type: 'Point',
        coordinates: [69.2401, 53.2145]
      },
      properties: {
        name: 'Updated City'
      }
    }
  ]
});

Обновление данных автоматически вызывает перерасчёт слоёв без необходимости их пересоздания.

Динамическое добавление и удаление объектов карты

Маркеры

Маркер создаётся независимо от слоя и источников:

const marker = new mapboxgl.Marker()
  .setLngLat([69.2401, 53.2145])
  .addTo(map);

Удаление маркера:

marker.remove();

Маркеры не связаны с системой слоёв и управляются отдельным DOM-подобным API.

Программное управление группами маркеров

Для масштабных наборов маркеров требуется хранение ссылок:

const markers = [];

for (const coord of coordinates) {
  const m = new mapboxgl.Marker()
    .setLngLat(coord)
    .addTo(map);

  markers.push(m);
}

Удаление всех маркеров:

markers.forEach(m => m.remove());
markers.length = 0;

Управление контролами интерфейса

Контролы добавляются через addControl:

const nav = new mapboxgl.NavigationControl();
map.addControl(nav, 'top-right');

Удаление контролов осуществляется методом removeControl:

map.removeControl(nav);

Контролы являются отдельными компонентами UI и не зависят от источников и слоёв.

Очистка и управление жизненным циклом

При динамической смене представлений карты требуется аккуратное управление всеми сущностями:

  • источники данных
  • слои отображения
  • маркеры
  • контролы

Типичный порядок очистки:

if (map.getLayer('cities-layer')) {
  map.removeLayer('cities-layer');
}

if (map.getSource('cities-source')) {
  map.removeSource('cities-source');
}

Дополнительно учитывается, что слои могут зависеть друг от друга (например, beforeId), поэтому удаление выполняется в обратном порядке добавления.

Работа с условной видимостью слоёв

Удаление не всегда требуется; часто достаточно скрыть слой:

map.setLayoutProperty('cities-layer', 'visibility', 'none');

Возврат отображения:

map.setLayoutProperty('cities-layer', 'visibility', 'visible');

Такой подход снижает накладные расходы на повторное создание слоёв и источников.

Переключение источников у слоя

Прямое изменение источника у слоя невозможно. Вместо этого используется пересоздание слоя:

map.removeLayer('cities-layer');

map.addLayer({
  id: 'cities-layer',
  type: 'circle',
  source: 'new-source',
  paint: {
    'circle-radius': 5,
    'circle-color': '#00aaff'
  }
});

Этот механизм часто применяется при переключении тематических наборов данных.

Обновление стиля слоя без удаления

Слой можно изменять «на лету» через setPaintProperty и setLayoutProperty:

map.setPaintProperty('cities-layer', 'circle-color', '#00ff00');
map.setPaintProperty('cities-layer', 'circle-radius', 10);

Изменение применяется мгновенно, без пересоздания слоя и источника.

Зависимости и ошибки удаления

Частая ошибка возникает при попытке удалить источник, который используется:

Error: Source "cities-source" cannot be removed while layer "cities-layer" is using it.

Такая модель предотвращает нарушение целостности графа рендеринга. Поэтому структура зависимостей всегда должна анализироваться перед удалением.

Переиспользование источников

Один источник может быть связан с несколькими слоями одновременно:

map.addLayer({
  id: 'cities-points',
  type: 'circle',
  source: 'cities-source'
});

map.addLayer({
  id: 'cities-labels',
  type: 'symbol',
  source: 'cities-source'
});

Удаление одного слоя не влияет на остальные, что позволяет строить сложные визуальные композиции без дублирования данных.

Динамическое переключение наборов данных

Распространённый паттерн — замена данных внутри одного источника:

map.getSource('cities-source').setData(newData);

Это предпочтительнее полного удаления и пересоздания слоёв, так как сохраняет состояние карты, анимации и порядок рендеринга.

Контроль состояния карты при масштабных изменениях

При массовом обновлении слоёв используется временная блокировка перерисовки через логическую группировку операций:

map.once('idle', () => {
  // операции добавления/удаления завершены
});

Такой подход позволяет избегать промежуточных состояний отображения при каскадных изменениях структуры карты.