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

В основе архитектуры библиотеки лежит модель слоёв (layers), где каждый визуальный или функциональный элемент карты представлен отдельным объектом, который можно добавлять на карту или удалять с неё. Карта выступает контейнером, управляющим набором слоёв, а сами слои реализуют единый интерфейс с методами добавления и удаления.

Любой слой в Leaflet реализует базовый контракт: он может быть подключён к карте через addTo(map) и удалён через remove() либо через управление картой map.removeLayer(layer).


Базовые операции добавления и удаления слоёв

Добавление слоя на карту

Слой добавляется через метод карты:

const map = L.map('map').setView([51.505, -0.09], 13);

const marker = L.marker([51.5, -0.09]);

marker.addTo(map);

Эквивалентная запись через карту:

map.addLayer(marker);

Оба варианта идентичны по результату. Метод addTo(map) является синтаксическим сахаром, внутри он вызывает map.addLayer(this).


Удаление слоя с карты

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

map.removeLayer(marker);

или через сам слой:

marker.remove();

Обе операции приводят к удалению слоя из внутреннего списка карты и его деактивации на экране.


Внутренняя модель хранения слоёв

Карта хранит слои в структуре, разделённой на группы:

  • базовые слои (base layers)
  • оверлеи (overlays)

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

  • onAdd(map) — вызывается при добавлении
  • onRemove(map) — вызывается при удалении

Пример внутренней логики:

map.addLayer = function (layer) {
    this._layers[L.stamp(layer)] = layer;
    layer.onAdd(this);
};

Удаление:

map.removeLayer = function (layer) {
    delete this._layers[L.stamp(layer)];
    layer.onRemove(this);
};

Типы слоёв и их поведение при добавлении

Маркеры

Маркеры — самый простой тип слоёв:

const marker = L.marker([55.75, 37.61]);
marker.addTo(map);

При добавлении создаётся DOM-элемент, который вставляется в overlayPane карты.

Удаление:

marker.remove();

Тайловые слои

Тайловые слои представляют собой подложку карты:

const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png');

tiles.addTo(map);

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

Удаление:

map.removeLayer(tiles);

После удаления тайлы перестают загружаться, DOM очищается.


GeoJSON слои

GeoJSON слой представляет коллекцию геометрий:

const geojson = L.geoJSON(data);

geojson.addTo(map);

При добавлении происходит:

  • парсинг GeoJSON
  • создание соответствующих векторных слоёв (Polyline, Polygon, Marker)
  • добавление их в FeatureGroup

Удаление:

geojson.remove();

Группы слоёв

LayerGroup

Группа слоёв позволяет управлять набором объектов как единым целым:

const group = L.layerGroup();

const marker1 = L.marker([51.5, -0.1]);
const marker2 = L.marker([51.51, -0.12]);

group.addLayer(marker1);
group.addLayer(marker2);

group.addTo(map);

Удаление всей группы:

map.removeLayer(group);

Или:

group.remove();

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


FeatureGroup

FeatureGroup расширяет LayerGroup и добавляет событийную модель:

const featureGroup = L.featureGroup([marker1, marker2]).addTo(map);

Дополнительные возможности:

  • обработка событий на уровне всей группы
  • вычисление bounds всех элементов

Удаление:

featureGroup.remove();

Контроль слоёв через Layer Control

Для динамического включения и выключения слоёв используется контрол:

const baseMaps = {
    "OSM": osmLayer,
    "Satellite": satelliteLayer
};

const overlayMaps = {
    "Markers": markerGroup,
    "Routes": routeLayer
};

L.control.layers(baseMaps, overlayMaps).addTo(map);

Этот механизм автоматически вызывает:

  • addLayer при включении чекбокса
  • removeLayer при выключении

Контрол работает поверх той же системы map.addLayer / removeLayer.


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

Карта позволяет проверить, добавлен ли слой:

map.hasLayer(marker);

Возвращает:

  • true — слой активен
  • false — слой отсутствует

Это используется при динамическом управлении состоянием интерфейса.


Управление слоями через события

Слои и карта генерируют события при добавлении и удалении.

События карты

map.on('layeradd', function (e) {
    console.log('Добавлен слой:', e.layer);
});

map.on('layerremove', function (e) {
    console.log('Удалён слой:', e.layer);
});

События слоя

Каждый слой реагирует на жизненный цикл:

const customLayer = L.layerGroup();

customLayer.on('add', function (e) {
    console.log('Группа добавлена на карту');
});

customLayer.on('remove', function (e) {
    console.log('Группа удалена с карты');
});

Динамическое переключение слоёв

Частый сценарий — переключение между наборами данных:

let currentLayer = L.geoJSON(data1).addTo(map);

function switchData(data) {
    map.removeLayer(currentLayer);

    currentLayer = L.geoJSON(data);
    map.addLayer(currentLayer);
}

Здесь важен порядок операций:

  1. удалить старый слой
  2. создать новый
  3. добавить новый слой

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

При работе с большим количеством объектов используется массив слоёв:

const layers = [];

for (let i = 0; i < 10; i++) {
    const marker = L.marker([51.5 + i * 0.01, -0.09]);
    layers.push(marker);
    marker.addTo(map);
}

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

layers.forEach(layer => map.removeLayer(layer));
layers.length = 0;

Очистка карты

Полная очистка пользовательских слоёв:

map.eachLayer(function (layer) {
    if (!layer._url) {
        map.removeLayer(layer);
    }
});

Здесь используется фильтрация базового тайлового слоя.


Временные слои и их жизненный цикл

Временные объекты часто создаются и удаляются динамически:

function showTemporaryMarker(latlng) {
    const marker = L.marker(latlng).addTo(map);

    setTimeout(() => {
        map.removeLayer(marker);
    }, 3000);
}

Такой подход используется для:

  • подсветки событий
  • уведомлений на карте
  • визуализации временных данных

Особенности удаления слоёв с вложенными элементами

Некоторые слои содержат дочерние элементы:

  • LayerGroup
  • FeatureGroup
  • GeoJSON

Удаление родителя автоматически вызывает onRemove у всех потомков:

const group = L.layerGroup([
    L.marker([51.5, -0.09]),
    L.marker([51.51, -0.1])
]);

group.addTo(map);
group.remove();

Результат: все маркеры также удаляются.


Перерисовка и влияние удаления слоёв

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

  • перерасчёт тайлов
  • обновление bounds
  • пересчёт z-index

Пример принудительного обновления:

map.invalidateSize();

Используется при изменении контейнера после удаления крупных слоёв.


Z-index и порядок слоёв при добавлении

Порядок добавления влияет на визуальное перекрытие:

map.addLayer(layer1);
map.addLayer(layer2);

layer2 отображается выше.

Можно управлять порядком через panes:

const customPane = map.createPane('customPane');
customPane.style.zIndex = 650;

const marker = L.marker([51.5, -0.09], {
    pane: 'customPane'
}).addTo(map);

Удаление слоя также очищает его из соответствующего pane.


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

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

if (!map.hasLayer(marker)) {
    map.addLayer(marker);
}

Или безопасное удаление:

if (map.hasLayer(marker)) {
    map.removeLayer(marker);
}

Поведение при повторном добавлении

Если слой уже добавлен и вызывается addLayer повторно:

  • Leaflet игнорирует повторное добавление
  • состояние карты не дублируется
  • события не повторяются

Связь слоёв с UI-контролами

Контрол слоёв синхронизируется автоматически:

const control = L.control.layers(null, {
    "Markers": markerLayer
}).addTo(map);

При удалении слоя вручную:

map.removeLayer(markerLayer);

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


Архитектурная модель добавления/удаления

Система можно представить как поток:

  1. создание слоя
  2. регистрация через addLayer
  3. вызов onAdd
  4. вставка в нужный pane
  5. отображение

Удаление:

  1. вызов removeLayer
  2. удаление из registry карты
  3. вызов onRemove
  4. удаление DOM-элементов
  5. очистка памяти ссылок

Управление слоями в сложных приложениях

При масштабных проектах используется централизованный менеджер слоёв:

const layerManager = {
    activeLayers: [],

    add(layer) {
        this.activeLayers.push(layer);
        layer.addTo(map);
    },

    remove(layer) {
        map.removeLayer(layer);
        this.activeLayers = this.activeLayers.filter(l => l !== layer);
    }
};

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