Control интерфейс

Интерфейс управления картой в MapLibre GL JS построен вокруг концепции контролов (controls) — модульных UI-компонентов, которые подключаются к экземпляру карты и расширяют его функциональность без изменения базовой логики рендеринга. Контролы добавляются поверх canvas и взаимодействуют с экземпляром карты через стандартный программный интерфейс, обеспечивая единообразие поведения и внешнего вида.

Контрол в MapLibre GL JS представляет собой объект, реализующий минимальный контракт:

  • метод onAdd(map) — вызывается при добавлении на карту
  • метод onRemove() — вызывается при удалении
  • опционально getDefaultPosition() — определяет позицию по умолчанию

Контролы не зависят от конкретной реализации карты и взаимодействуют с ней через публичное API. Это позволяет создавать переиспользуемые компоненты, которые можно подключать к разным проектам без модификации ядра.

Каждый контрол добавляется через:

map.addControl(control, position);

Параметр position определяет размещение интерфейса:

  • top-left
  • top-right
  • bottom-left
  • bottom-right

Если позиция не указана, используется значение, возвращаемое getDefaultPosition().

NavigationControl объединяет основные инструменты навигации: зум, поворот и наклон.

const nav = new maplibregl.NavigationControl({
    visualizePitch: true,
    showCompass: true
});

map.addControl(nav, 'top-right');

Функциональность:

  • кнопки увеличения и уменьшения масштаба
  • сброс направления (bearing reset)
  • визуализация наклона (pitch control)

NavigationControl особенно полезен в интерактивных сценах, где требуется пространственная навигация, а не только 2D-панорама.

ScaleControl: масштабная линейка

ScaleControl отображает линейку масштаба, автоматически пересчитываемую при изменении зума и широты.

const scale = new maplibregl.ScaleControl({
    maxWidth: 120,
    unit: 'metric'
});

map.addControl(scale, 'bottom-left');

Параметры:

  • maxWidth — максимальная ширина линейки в пикселях

  • unit — единицы измерения:

    • metric
    • imperial
    • nautical

ScaleControl опирается на проекцию карты и геодезические расчёты, обеспечивая корректность отображения расстояний при разных уровнях масштабирования.

AttributionControl: управление атрибуцией источников

AttributionControl отвечает за отображение юридической информации о данных карты.

const attribution = new maplibregl.AttributionControl({
    compact: true,
    customAttribution: 'Данные: OpenStreetMap contributors'
});

map.addControl(attribution);

Особенности:

  • автоматическое отображение атрибуции слоёв
  • поддержка компактного режима (compact)
  • возможность добавления пользовательских источников

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

GeolocateControl: определение местоположения

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

const geolocate = new maplibregl.GeolocateControl({
    positionOptions: {
        enableHighAccuracy: true
    },
    trackUserLocation: true,
    showAccuracyCircle: true
});

map.addControl(geolocate, 'top-right');

Режимы работы:

  • одиночное определение позиции
  • отслеживание перемещения пользователя
  • отображение радиуса точности

Контрол взаимодействует с устройствами GPS, Wi-Fi позиционированием и другими источниками браузера, возвращая объект GeolocationPosition.

FullscreenControl: полноэкранный режим

FullScreenControl переключает карту в полноэкранный режим с использованием Fullscreen API браузера.

const fullscreen = new maplibregl.FullscreenControl();

map.addControl(fullscreen, 'top-right');

Поведение:

  • переключение DOM-элемента карты в fullscreen
  • автоматическое восстановление состояния при выходе
  • поддержка событий изменения режима

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

Позиционирование контролов и порядок отображения

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

map.addControl(new maplibregl.NavigationControl(), 'top-left');
map.addControl(new maplibregl.ScaleControl(), 'bottom-left');
map.addControl(new maplibregl.GeolocateControl(), 'top-left');

Порядок важен при комбинировании нескольких контролов в одной зоне интерфейса: последний добавленный отображается выше в DOM-иерархии.

Жизненный цикл контролов

Каждый контрол проходит стандартные стадии:

  1. Создание экземпляра
  2. Вызов onAdd(map)
  3. Привязка DOM-элемента к контейнеру карты
  4. Работа в активном состоянии
  5. Вызов onRemove() при удалении

Удаление осуществляется через:

map.removeControl(control);

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

Создание пользовательских контролов

Интерфейс IControl позволяет создавать собственные элементы управления.

Базовая структура:

class CustomControl {
    onAdd(map) {
        this._map = map;

        this._container = document.createElement('div');
        this._container.className = 'maplibregl-ctrl custom-control';

        const button = document.createElement('button');
        button.textContent = 'Центр';

        button.oncl ick = () => {
            map.setCenter([0, 0]);
        };

        this._container.appendChild(button);

        return this._container;
    }

    onRemove() {
        this._container.parentNode.removeChild(this._container);
        this._map = undefined;
    }

    getDefaultPosition() {
        return 'top-right';
    }
}

map.addControl(new CustomControl());

Ключевые аспекты:

  • контрол управляет собственным DOM
  • взаимодействие с картой происходит через сохранённую ссылку map
  • обязательное удаление DOM при onRemove

Интеграция с событиями карты

Контролы часто подписываются на события карты:

onAdd(map) {
    this._map = map;

    this._map.on('move', this._update.bind(this));
}

Типичные события:

  • move
  • zoom
  • rotate
  • load

При использовании событий важно обеспечивать отписку:

onRemove() {
    this._map.off('move', this._update);
}

Стилизация контролов

Все стандартные контролы используют CSS-классы:

  • maplibregl-ctrl
  • maplibregl-ctrl-group
  • maplibregl-ctrl-icon

Переопределение внешнего вида выполняется через CSS:

.maplibregl-ctrl button {
    width: 36px;
    height: 36px;
    background-color: #1e1e1e;
    color: #fff;
}

Контейнер контролов не изолирован, поэтому стили наследуются из глобального DOM.

Условное отображение контролов

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

if (userHasPermission) {
    map.addControl(new maplibregl.NavigationControl());
}

И удаляться при изменении состояния приложения:

map.removeControl(navControl);

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

Взаимодействие контролов между собой

Контролы не имеют встроенного механизма координации, но могут взаимодействовать через общий экземпляр карты:

  • изменение центра
  • управление слоями
  • переключение режимов отображения

Пример синхронизации:

geolocate.on('geolocate', (e) => {
    map.flyTo({
        center: [e.coords.longitude, e.coords.latitude],
        zoom: 14
    });
});

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