Добавление и удаление маркеров

MapLibre GL JS предоставляет механизм маркеров как надслой над картой, позволяющий отображать произвольные DOM-элементы или стандартные визуальные указатели в координатах географической сцены. В отличие от источников данных и слоёв, маркеры существуют в DOM-дереве и не участвуют в WebGL-рендеринге, что делает их гибкими, но менее производительными при большом количестве объектов.

Базовое добавление маркера

Основной класс для работы с маркерами — maplibregl.Marker. Он принимает HTML-элемент или создаёт стандартный маркер по умолчанию.

import maplibregl from "maplibre-gl";

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

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

Маркер создаётся как независимый объект и привязывается к карте методом addTo. Координаты задаются в формате [longitude, latitude].

Маркер с пользовательским HTML

Маркер может быть полностью переопределён через DOM-элемент, что позволяет реализовать сложную визуализацию.

const el = document.createElement("div");
el.className = "custom-marker";
el.innerHTML = `<div class="pin"></div>`;

const marker = new maplibregl.Marker({
  element: el,
  anchor: "bottom"
})
  .setLngLat([37.6173, 55.7558])
  .addTo(map);

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

Управление позиционированием

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

marker.setLngLat([37.62, 55.76]);

Обновление координат происходит мгновенно, карта автоматически пересчитывает позицию элемента в экранных координатах.

Привязка всплывающих окон

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

const popup = new maplibregl.Popup({ offset: 25 })
  .setText("Точка интереса");

marker.setPopup(popup);

Popup активируется по клику на маркер и автоматически привязывается к его позиции. Это создаёт связку «объект — описание».

Добавление нескольких маркеров

Несколько маркеров создаются как независимые экземпляры:

const points = [
  [37.61, 55.75],
  [37.62, 55.76],
  [37.63, 55.74]
];

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

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

Хранение ссылок на маркеры

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

const markers = [];

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

markers.push(marker);

Такой подход позволяет централизованно удалять или обновлять объекты.

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

Удаление выполняется методом remove, который отсоединяет маркер от карты и DOM.

marker.remove();

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

При управлении коллекцией маркеров применяется массовое удаление:

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

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

Вместо удаления можно управлять видимостью через DOM-стили:

marker.getElement().style.display = "none";

или

marker.getElement().style.visibility = "hidden";

Такой подход сохраняет объект в памяти, но исключает его визуализацию.

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

Маркер может быть сделан интерактивным с помощью опции draggable.

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

marker.on("dragend", () => {
  const lngLat = marker.getLngLat();
});

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

Жизненный цикл маркера

Маркер проходит несколько этапов:

  1. Создание экземпляра Marker
  2. Привязка координат через setLngLat
  3. Добавление на карту через addTo
  4. Отображение через DOM-синхронизацию
  5. Обновление позиции при изменении карты
  6. Удаление через remove

Каждый этап влияет на состояние DOM и внутренние слушатели событий.

Обновление маркеров при изменении данных

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

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

или пересоздаются полностью при изменении набора данных.

Отличие маркеров от слоёв

Маркер не является частью WebGL-слоя. В отличие от GeoJSON source + layer, маркеры:

  • не используют GPU-рендеринг
  • создаются как HTML-элементы
  • имеют отдельный жизненный цикл
  • легче реализуют интерактивность
  • хуже масштабируются при больших объёмах данных

Для сотен и тысяч объектов предпочтительнее слои, а не маркеры.

Очистка ресурсов

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

map.on("remove", () => {
  markers.forEach(m => m.remove());
});

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

Кастомные события маркеров

Маркер поддерживает стандартные DOM-события:

marker.getElement().addEventListener("click", () => {
  console.log("Маркер нажат");
});

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

Группировка логики управления

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

class MarkerManager {
  constructor(map) {
    this.map = map;
    this.markers = [];
  }

  add(coord) {
    const marker = new maplibregl.Marker()
      .setLngLat(coord)
      .addTo(this.map);

    this.markers.push(marker);
    return marker;
  }

  clear() {
    this.markers.forEach(m => m.remove());
    this.markers = [];
  }
}

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