Позиционирование маркеров в MapLibre GL JS основано на привязке географических координат к экранным пикселям через WebGL-трансформацию карты. Каждый маркер представляет собой DOM-элемент или WebGL-слойный объект, который синхронизируется с состоянием карты: центром, масштабом, вращением и наклоном.
Основой позиционирования выступает пара координат longitude/latitude.
При создании маркера используется объект LngLat, который
фиксирует точку на географической плоскости:
import maplibregl from 'maplibre-gl';
const marker = new maplibregl.Marker()
.setLngLat([37.6173, 55.7558])
.addTo(map);
Внутри MapLibre GL JS происходит преобразование координат в координаты canvas с учётом текущего состояния камеры. Любое изменение центра карты или масштаба автоматически инициирует перерасчёт позиции.
Позиционирование маркеров опирается на матрицы преобразования:
При каждом кадре рендеринга выполняется пересчёт:
map.transform.coordinateToPoint(lngLat)Это обеспечивает синхронное движение маркеров вместе с картой без ручного обновления.
Маркер может быть смещён относительно географической точки. Это используется для визуального поднятия маркера над точкой или корректировки якоря:
const marker = new maplibregl.Marker({
offset: [0, -20]
})
.setLngLat([37.6173, 55.7558])
.addTo(map);
Смещение применяется уже после проекции координат в экранное пространство. Значения задаются в пикселях и не зависят от масштаба карты.
Параметр anchor определяет, какая часть маркера
совпадает с координатой:
centertopbottomleftrightПример:
const marker = new maplibregl.Marker({
anchor: 'bottom'
})
.setLngLat([37.6173, 55.7558])
.addTo(map);
При использовании anchor система автоматически
корректирует смещение DOM-элемента, чтобы заданная точка элемента
совпадала с географической координатой.
Маркер может быть основан на произвольном HTML-элементе. В этом случае позиционирование осуществляется через CSS transform:
const el = document.createElement('div');
el.className = 'custom-marker';
const marker = new maplibregl.Marker({
element: el,
anchor: 'center'
})
.setLngLat([37.6173, 55.7558])
.addTo(map);
MapLibre GL JS управляет стилем:
transform: translate(...) translate(-50%, -50%)position: absoluterender событии картыDOM-маркеры дороже по производительности, так как требуют синхронизации с layout браузера.
Позиция может изменяться динамически без пересоздания объекта:
marker.setLngLat([30.3141, 59.9386]);
При этом система не создаёт новый DOM-узел, а пересчитывает только трансформацию. Это критично для анимаций и трекинга объектов.
При изменении состояния карты выполняется перерасчёт всех маркеров:
movezoomrotatepitchМаркер остаётся географически закреплённым и визуально перемещается за счёт трансформации сцены.
Пример логики обновления:
map.on('move', () => {
// внутренний вызов render pipeline
});
При изменении масштаба координаты остаются фиксированными, однако расстояния между маркерами в пикселях изменяются нелинейно. Это связано с экспоненциальной природой WebMercator-проекции:
При вращении карты маркеры сохраняют ориентацию относительно экрана,
если не задан параметр rotationAlignment.
Доступные режимы:
map — маркер поворачивается вместе с картойviewport — маркер всегда ориентирован вверхconst marker = new maplibregl.Marker({
rotationAlignment: 'viewport'
});
Это влияет на UX в навигационных сценариях, где важна стабильная ориентация иконок.
При использовании наклона карты возникает перспективное искажение. Позиция маркера вычисляется в 3D-пространстве:
Маркер визуально смещается в зависимости от высоты точки и угла обзора.
В случаях, когда используется рельеф или 3D-terrain, маркеры могут учитывать высоту:
Также применяется порядок отрисовки:
При анимации карты (flyTo, easeTo) позиционирование маркеров обновляется на каждом кадре интерполяции:
map.flyTo({
center: [37.6173, 55.7558],
zoom: 10
});
Маркер не анимируется отдельно, а следует за изменением матрицы камеры.
При работе с большим количеством точек важны следующие ограничения:
Альтернативой является использование layer-based rendering (symbol layer), где точки рендерятся через WebGL без DOM.
Для высокой плотности данных используется источник GeoJSON и слой:
map.addSource('points', {
type: 'geojson',
data: geojsonData
});
map.addLayer({
id: 'points-layer',
type: 'symbol',
source: 'points',
layout: {
'icon-image': 'marker-15',
'icon-size': 1
}
});
В этом случае позиционирование происходит полностью на GPU, без DOM-узлов, с использованием тех же матричных преобразований камеры.