В MapLibre GL JS точечные объекты представлены через источники данных
GeoJSON и визуализируются с помощью слоёв типа circle или
symbol. Такой подход отделяет данные от их отображения:
источник описывает геометрию, слой — правила рендеринга.
Точка в GeoJSON задаётся как:
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [37.6173, 55.7558]
},
"properties": {
"title": "Москва"
}
}
Координаты всегда задаются в порядке [долгота, широта],
что критично для корректного отображения.
Перед тем как отобразить точки на карте, необходимо зарегистрировать источник данных:
map.on('load', () => {
map.addSource('cities', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [37.6173, 55.7558]
},
properties: {
name: 'Москва',
population: 11920000
}
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [30.3351, 59.9343]
},
properties: {
name: 'Санкт-Петербург',
population: 5384000
}
}
]
}
});
});
Источник может быть как статическим объектом, так и URL, возвращающим GeoJSON.
Самый прямой способ визуализации точек — использование слоя
circle. Он рисует геометрические круги в местах
координат.
map.addLayer({
id: 'cities-circles',
type: 'circle',
source: 'cities',
paint: {
'circle-radius': 6,
'circle-color': '#3b82f6',
'circle-stroke-width': 1,
'circle-stroke-color': '#ffffff'
}
});
Параметры слоя позволяют управлять внешним видом:
circle-radius — радиус в пикселяхcircle-color — цвет заливкиcircle-stroke-width — толщина обводкиcircle-stroke-color — цвет обводкиСтили можно привязывать к свойствам объектов. Например, размер точки может зависеть от числового атрибута:
map.addLayer({
id: 'cities-scaled',
type: 'circle',
source: 'cities',
paint: {
'circle-radius': [
'interpolate',
['linear'],
['get', 'population'],
1000000, 4,
5000000, 8,
10000000, 14
],
'circle-color': [
'step',
['get', 'population'],
'#60a5fa',
5000000,
'#2563eb',
10000000,
'#1e3a8a'
]
}
});
Здесь используются выражения MapLibre:
interpolate — плавное изменение значенияstep — дискретное изменение по порогамget — доступ к свойствам объектаКогда требуется отображать подписи или иконки, применяется слой
symbol.
map.addLayer({
id: 'cities-labels',
type: 'symbol',
source: 'cities',
layout: {
'text-field': ['get', 'name'],
'text-font': ['Open Sans Regular', 'Arial Unicode MS Regular'],
'text-size': 12,
'text-offset': [0, 1.2],
'text-anchor': 'top'
},
paint: {
'text-color': '#111827'
}
});
В этом случае точка становится якорем для текста или изображения.
На практике часто используется двойной слой: один отвечает за геометрию, второй — за подписи.
map.addLayer({
id: 'cities-points',
type: 'circle',
source: 'cities',
paint: {
'circle-radius': 6,
'circle-color': '#ef4444'
}
});
map.addLayer({
id: 'cities-text',
type: 'symbol',
source: 'cities',
layout: {
'text-field': ['get', 'name'],
'text-size': 11,
'text-offset': [0, 1.5]
},
paint: {
'text-color': '#000000'
}
});
Разделение позволяет независимо настраивать визуальные аспекты.
MapLibre позволяет динамически изменять данные источника:
const source = map.getSource('cities');
source.setData({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [39.7015, 47.2357]
},
properties: {
name: 'Ростов-на-Дону'
}
}
]
});
Такой подход используется при работе с потоковыми данными или пользовательскими действиями.
Интерактивное добавление новых точек реализуется через обработчики событий карты:
map.on('click', (e) => {
const newPoint = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [e.lngLat.lng, e.lngLat.lat]
},
properties: {
name: 'Новая точка'
}
};
const source = map.getSource('cities');
const data = source._data;
data.features.push(newPoint);
source.setData(data);
});
Координаты берутся из объекта события lngLat.
Точки можно сделать интерактивными, добавив обработчик на слой:
map.on('click', 'cities-circles', (e) => {
const feature = e.features[0];
const name = feature.properties.name;
console.log('Выбрана точка:', name);
});
Для корректной работы необходимо включить
queryRenderedFeatures:
map.on('load', () => {
map.on('click', 'cities-circles', () => {});
});
Для улучшения UX часто добавляют изменение стиля при наведении:
map.on('mouseenter', 'cities-circles', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'cities-circles', () => {
map.getCanvas().style.cursor = '';
});
Также можно менять цвет через состояние feature-state:
map.setFeatureState(
{ source: 'cities', id: 1 },
{ hover: true }
);
И использовать его в стиле:
'circle-color': [
'case',
['boolean', ['feature-state', 'hover'], false],
'#f59e0b',
'#3b82f6'
]
При большом количестве объектов используется кластеризация:
map.addSource('cities', {
type: 'geojson',
data: 'cities.json',
cluster: true,
clusterRadius: 40
});
Отдельный слой для кластеров:
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'cities',
filter: ['has', 'point_count'],
paint: {
'circle-radius': 15,
'circle-color': '#6366f1'
}
});
И слой для отдельных точек:
map.addLayer({
id: 'unclustered-point',
type: 'circle',
source: 'cities',
filter: ['!', ['has', 'point_count']],
paint: {
'circle-radius': 5,
'circle-color': '#10b981'
}
});
Точки могут отображаться как изображения:
map.loadImage('marker.png', (error, image) => {
map.addImage('custom-marker', image);
map.addLayer({
id: 'points-icons',
type: 'symbol',
source: 'cities',
layout: {
'icon-image': 'custom-marker',
'icon-size': 0.5,
'icon-anchor': 'bottom'
}
});
});
Такой подход используется для кастомных маркеров и брендированных слоёв.
При десятках тысяч объектов ключевое значение имеет:
clusterpaintsymbol только при необходимости
текстаВекторный рендер WebGL в MapLibre позволяет обрабатывать большие наборы данных, но узким местом остаётся CPU при обновлении источников и расчёте выражений стилей.