Создание простого маркера

MapLibre GL JS предоставляет API для добавления интерактивных объектов поверх векторной карты. Маркеры в этой библиотеке реализуются как DOM-элементы, привязанные к координатам долготы и широты, и управляются через класс Marker.


Перед созданием маркеров требуется инициализировать карту и задать стиль отображения. Основной объект создаётся через Map, который управляет рендерингом WebGL-сцены и слоями.

import maplibregl from 'maplibre-gl';

const map = new maplibregl.Map({
  container: 'map',
  style: 'https://demotiles.maplibre.org/style.json',
  center: [69.2401, 41.2995],
  zoom: 10
});

Параметр container указывает DOM-элемент, внутри которого будет размещена карта. style определяет визуальную модель данных. center задаёт начальную позицию камеры, а zoom — масштаб.


Создание простого маркера

Маркеры создаются через конструктор maplibregl.Marker. Минимальная конфигурация предполагает только указание координат.

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

Метод setLngLat принимает массив [longitude, latitude]. Важно учитывать порядок координат: сначала долгота, затем широта, что соответствует географическому стандарту GeoJSON.

Метод addTo(map) привязывает маркер к конкретному экземпляру карты.


Поведение DOM-маркера

Каждый маркер в MapLibre GL JS представляет собой HTML-элемент, который позиционируется через трансформации CSS. По умолчанию используется элемент div с классом maplibregl-marker.

Это означает:

  • маркеры участвуют в DOM-дереве страницы;
  • их можно стилизовать через CSS;
  • их производительность зависит от количества элементов;
  • они не являются частью WebGL-слоя.

Настройка внешнего вида маркера

Для изменения внешнего вида можно передать HTML-элемент в конструктор Marker.

const el = document.createElement('div');
el.className = 'custom-marker';
el.style.width = '20px';
el.style.height = '20px';
el.style.backgroundColor = 'red';
el.style.borderRadius = '50%';

const marker = new maplibregl.Marker({ element: el })
  .setLngLat([69.2401, 41.2995])
  .addTo(map);

CSS-класс позволяет централизованно управлять стилями:

.custom-marker {
  border: 2px solid white;
  box-shadow: 0 0 6px rgba(0,0,0,0.5);
}

Использование кастомного элемента даёт полный контроль над визуальным представлением, включая SVG, Canvas или сложные HTML-компоненты.


Добавление всплывающего окна (Popup)

Маркеры часто используются совместно с всплывающими окнами. Класс Popup привязывается к маркеру через метод setPopup.

const popup = new maplibregl.Popup({ offset: 25 })
  .setHTML('<strong>Точка интереса</strong><p>Описание объекта</p>');

const marker = new maplibregl.Marker()
  .setLngLat([69.2401, 41.2995])
  .setPopup(popup)
  .addTo(map);

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


Перетаскиваемые маркеры

MapLibre GL JS поддерживает интерактивное перемещение маркеров с помощью опции draggable.

const marker = new maplibregl.Marker({ draggable: true })
  .setLngLat([69.2401, 41.2995])
  .addTo(map);

Обработка события изменения позиции:

marker.on('dragend', () => {
  const lngLat = marker.getLngLat();
  console.log(lngLat.lng, lngLat.lat);
});

Метод getLngLat() возвращает текущие координаты маркера после завершения перетаскивания.


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

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

const points = [
  [69.2401, 41.2995],
  [69.2500, 41.3100],
  [69.2600, 41.3200]
];

points.forEach(coords => {
  new maplibregl.Marker()
    .setLngLat(coords)
    .addTo(map);
});

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


Оптимизация большого количества маркеров

DOM-маркеры плохо масштабируются при тысячах объектов. В таких случаях используется подход:

  • группировка точек (clustering на уровне GeoJSON);
  • переход на GeoJSONSource и символические слои (symbol layer);
  • минимизация кастомных DOM-элементов.

Пример перехода к источнику данных:

map.on('load', () => {
  map.addSource('points', {
    type: 'geojson',
    data: {
      type: 'FeatureCollection',
      features: [
        {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: [69.2401, 41.2995]
          }
        }
      ]
    }
  });

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

Этот подход исключает использование DOM и переносит рендеринг в WebGL.


Изменение позиции маркера после создания

Маркер можно динамически перемещать без пересоздания:

marker.setLngLat([69.3000, 41.3300]);

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


Связь маркера с состоянием приложения

В прикладных сценариях маркеры часто используются как представление состояния данных:

  • координаты приходят из API;
  • маркер обновляется при изменении данных;
  • события карты синхронизируются с бизнес-логикой.

Пример обновления из внешнего источника:

function updateMarkerPosition(data) {
  marker.setLngLat([data.lng, data.lat]);
}

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


Работа с якорем маркера

По умолчанию точка привязки маркера находится в центре элемента. Её можно изменить через anchor.

new maplibregl.Marker({ anchor: 'bottom' })
  .setLngLat([69.2401, 41.2995])
  .addTo(map);

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

  • center
  • top
  • bottom
  • left
  • right
  • комбинации вроде top-left

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


Взаимодействие с событиями карты

Маркер может участвовать в событийной модели:

marker.getElement().addEventListener('click', () => {
  console.log('Маркер активирован');
});

Такой подход даёт возможность реализовать кастомное поведение без привязки к popup-механике.


Практические ограничения DOM-маркеров

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

  • небольшом количестве объектов;
  • необходимости сложной HTML-вёрстки;
  • интерактивных UI-компонентах на карте.

При увеличении количества объектов растёт нагрузка на:

  • layout и reflow DOM;
  • память браузера;
  • обработку событий.

Поэтому архитектурно важно разделять:

  • визуальные маркеры (DOM);
  • геоданные (GeoJSON и слои WebGL).