Отключение невидимых слоев

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

  • отключение слоя через пользовательский интерфейс;
  • отображение слоя только на определенных масштабах;
  • скрытие слоя в зависимости от состояния приложения;
  • переключение между различными тематическими наборами данных;
  • реализация фильтрации объектов.

Несмотря на отсутствие визуального отображения, слой продолжает существовать внутри стиля карты. Если управление видимостью реализовано неэффективно, скрытые слои могут участвовать в обработке данных, занимать память и усложнять поддержку проекта.

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


Свойство visibility

Основным механизмом управления отображением слоя является свойство visibility, расположенное в секции layout.

Допустимые значения:

visible
none

Пример изменения видимости слоя:

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

Для повторного отображения:

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

Внутри стиля Mapbox слой будет выглядеть следующим образом:

{
    id: 'roads-layer',
    type: 'line',
    source: 'roads',
    layout: {
        visibility: 'visible'
    }
}

После отключения:

{
    id: 'roads-layer',
    type: 'line',
    source: 'roads',
    layout: {
        visibility: 'none'
    }
}

Когда значение равно none, слой не участвует в отрисовке карты.


Проверка текущего состояния слоя

Перед изменением настроек полезно определить текущее состояние слоя.

Получение свойства:

const visibility = map.getLayoutProperty(
    'roads-layer',
    'visibility'
);

console.log(visibility);

Результат:

visible

или

none

На основе полученного значения можно реализовать переключатель:

const visibility = map.getLayoutProperty(
    'roads-layer',
    'visibility'
);

if (visibility === 'visible') {
    map.setLayoutProperty(
        'roads-layer',
        'visibility',
        'none'
    );
} else {
    map.setLayoutProperty(
        'roads-layer',
        'visibility',
        'visible'
    );
}

Создание кнопки для отключения слоя

Типичный сценарий — предоставление пользователю возможности скрывать и показывать слои.

HTML:

<button id="toggle-roads">
    Переключить дороги
</button>

Jav * aScript:

document
    .getElementById('toggle-roads')
    .addEventListener('click', () => {

        const visibility =
            map.getLayoutProperty(
                'roads-layer',
                'visibility'
            );

        map.setLayoutProperty(
            'roads-layer',
            'visibility',
            visibility === 'visible'
                ? 'none'
                : 'visible'
        );
    });

Такой подход широко используется при создании панелей управления картой.


Отключение нескольких слоев одновременно

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

Например:

buildings-fill
buildings-outline
buildings-labels

Для скрытия всего блока объектов можно использовать цикл:

const buildingLayers = [
    'buildings-fill',
    'buildings-outline',
    'buildings-labels'
];

buildingLayers.forEach(layerId => {
    map.setLayoutProperty(
        layerId,
        'visibility',
        'none'
    );
});

Обратное включение:

buildingLayers.forEach(layerId => {
    map.setLayoutProperty(
        layerId,
        'visibility',
        'visible'
    );
});

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


Управление слоями через конфигурационный объект

В крупных приложениях количество слоев может исчисляться десятками и сотнями.

Для удобства создается объект конфигурации:

const layers = {
    roads: [
        'roads-main',
        'roads-secondary'
    ],
    buildings: [
        'building-fill',
        'building-outline'
    ],
    water: [
        'water-fill',
        'water-outline'
    ]
};

Универсальная функция отключения:

function hideGroup(groupName) {

    layers[groupName].forEach(layerId => {

        map.setLayoutProperty(
            layerId,
            'visibility',
            'none'
        );

    });

}

Использование:

hideGroup('buildings');

Подобная организация кода значительно упрощает сопровождение проекта.


Использование zoom-уровней вместо ручного отключения

Иногда слой должен отображаться только на определенных масштабах.

Для этого используются свойства:

minzoom
maxzoom

Пример:

map.addLayer({
    id: 'city-labels',
    type: 'symbol',
    source: 'cities',
    minzoom: 8,
    maxzoom: 14
});

Поведение:

Масштаб Отображение
0–7 Скрыт
8–13 Видим
14+ Скрыт

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


Скрытие слоя через фильтрацию

Иногда необходимо не отключить слой полностью, а временно убрать все объекты.

Для этого можно использовать фильтр, который не соответствует ни одному объекту.

Пример:

map.setFilter(
    'roads-layer',
    ['==', 'id', -1]
);

Поскольку объекта с таким значением нет, слой фактически становится пустым.

Возврат фильтра:

map.setFilter(
    'roads-layer',
    null
);

Однако данный подход отличается от установки visibility: none.

Слой продолжает существовать и участвовать в обработке данных.


Разница между visibility и opacity

Распространенная ошибка — использование прозрачности вместо отключения слоя.

Например:

map.setPaintProperty(
    'roads-layer',
    'line-opacity',
    0
);

Визуально слой исчезнет.

Но движок продолжит:

  • вычислять стиль;
  • обрабатывать объекты;
  • подготавливать данные к отрисовке.

Поэтому прозрачность не является полноценным отключением.

Для скрытия слоя рекомендуется:

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

Удаление слоя вместо отключения

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

Тогда его можно удалить.

Сначала удаляется слой:

map.removeLayer('roads-layer');

После этого при необходимости удаляется источник данных:

map.removeSource('roads');

Сравнение подходов:

Метод Можно быстро вернуть Освобождает ресурсы
visibility: none Да Частично
removeLayer() Нет Да
removeLayer() + removeSource() Нет Максимально

Удаление подходит для редко используемых данных большого объема.


Проверка существования слоя перед отключением

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

Без проверки возникнет ошибка:

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

Безопасный вариант:

if (map.getLayer('roads-layer')) {

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

}

Подобная проверка особенно важна при переключении стилей карты.


Отключение всех пользовательских слоев

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

Пример:

const style = map.getStyle();

style.layers.forEach(layer => {

    if (
        layer.id.startsWith('custom-')
    ) {

        map.setLayoutProperty(
            layer.id,
            'visibility',
            'none'
        );

    }

});

Такой подход эффективен при работе с большим количеством динамически создаваемых слоев.


Массовое переключение через события приложения

Часто состояние слоя зависит от глобального состояния интерфейса.

Например:

window.addEventListener(
    'hideAnalysis',
    () => {

        analysisLayers.forEach(id => {

            map.setLayoutProperty(
                id,
                'visibility',
                'none'
            );

        });

    }
);

Для повторного отображения:

window.addEventListener(
    'showAnalysis',
    () => {

        analysisLayers.forEach(id => {

            map.setLayoutProperty(
                id,
                'visibility',
                'visible'
            );

        });

    }
);

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


Производительность при большом количестве слоев

При работе со сложными картографическими приложениями необходимо учитывать следующие рекомендации:

Отключать неиспользуемые слои через visibility: none.

map.setLayoutProperty(
    layerId,
    'visibility',
    'none'
);

Не заменять отключение прозрачностью.

Плохо:

line-opacity: 0

Хорошо:

visibility: none

Группировать слои по функциональным категориям.

Пример:

transportLayers
analysisLayers
buildingLayers
weatherLayers

Удалять тяжелые временные слои после завершения работы.

map.removeLayer(layerId);
map.removeSource(sourceId);

Проверять существование слоя перед изменением его свойств.

if (map.getLayer(layerId)) {
    // изменение настроек
}

Использовать minzoom и maxzoom для автоматического скрытия слоев на неподходящих масштабах.

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