Центроид — это геометрический центр объекта, вычисляемый на основе координат его вершин. В картографических приложениях центроиды используются для размещения подписей, отображения маркеров объектов, выполнения пространственного анализа, расчета расстояний между объектами и построения различных визуализаций.
В контексте MapLibre GL JS центроид представляет собой координату в
формате [longitude, latitude], которую можно использовать
как обычную точку на карте.
Основные области применения:
Важно различать несколько понятий центра объекта.
Самый простой способ определения центра — вычисление середины ограничивающего прямоугольника (Bounding Box).
Для полигона строится минимальный прямоугольник:
minX, minY
maxX, maxY
Центр вычисляется как:
centerX = (minX + maxX) / 2;
centerY = (minY + maxY) / 2;
Такой подход работает быстро, но может давать неточные результаты для сложных фигур.
Другой вариант — вычислить среднее значение всех вершин.
const centroid = [
coordinates.reduce((sum, p) => sum + p[0], 0) / coordinates.length,
coordinates.reduce((sum, p) => sum + p[1], 0) / coordinates.length
];
Подход подходит для простых наборов точек, но для полигонов с неравномерным распределением вершин может давать смещенный результат.
Для полигонов применяется формула, учитывающая площадь фигуры и положение каждой вершины.
Такой центроид отражает реальный центр геометрической массы объекта.
Рассмотрим простейший случай.
Имеется набор координат:
const points = [
[37.6176, 55.7558],
[37.6200, 55.7580],
[37.6220, 55.7560]
];
Центроид определяется как среднее значение координат.
function calculateCentroid(points) {
let sumLng = 0;
let sumLat = 0;
for (const point of points) {
sumLng += point[0];
sumLat += point[1];
}
return [
sumLng / points.length,
sumLat / points.length
];
}
Использование:
const centroid = calculateCentroid(points);
console.log(centroid);
Результат:
[37.619866, 55.7566]
После вычисления координаты можно добавить маркер.
const centroid = calculateCentroid(points);
new maplibregl.Marker()
.setLngLat(centroid)
.addTo(map);
Полный пример:
const map = new maplibregl.Map({
container: 'map',
style: 'https://demotiles.maplibre.org/style.json',
center: [37.6176, 55.7558],
zoom: 12
});
const points = [
[37.6176, 55.7558],
[37.6200, 55.7580],
[37.6220, 55.7560]
];
const centroid = calculateCentroid(points);
new maplibregl.Marker({
color: 'red'
})
.setLngLat(centroid)
.addTo(map);
Часто данные поступают в формате GeoJSON.
Пример объекта:
const feature = {
type: "Feature",
geometry: {
type: "MultiPoint",
coordinates: [
[37.6176, 55.7558],
[37.6200, 55.7580],
[37.6220, 55.7560]
]
}
};
Функция вычисления:
function centroidFromGeoJSON(feature) {
const coords = feature.geometry.coordinates;
let lng = 0;
let lat = 0;
coords.forEach(coord => {
lng += coord[0];
lat += coord[1];
});
return [
lng / coords.length,
lat / coords.length
];
}
Для полигона вычисление усложняется.
Исходный объект:
const polygon = {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [[
[37.61, 55.75],
[37.63, 55.75],
[37.63, 55.77],
[37.61, 55.77],
[37.61, 55.75]
]]
}
};
Для простых прямоугольных фигур можно использовать усреднение координат.
function simplePolygonCentroid(coords) {
let x = 0;
let y = 0;
for (const point of coords) {
x += point[0];
y += point[1];
}
return [
x / coords.length,
y / coords.length
];
}
Однако такой метод не всегда корректен для сложных контуров.
Для произвольного полигона используется формула через ориентированную площадь.
Площадь:
A = 1/2 Σ(xi * yi+1 − xi+1 * yi)
Координаты центроида:
Cx = (1 / 6A) Σ(xi + xi+1)(xi * yi+1 − xi+1 * yi)
Cy = (1 / 6A) Σ(yi + yi+1)(xi * yi+1 − xi+1 * yi)
Данный алгоритм обеспечивает корректный результат для большинства полигональных объектов.
function polygonCentroid(vertices) {
let area = 0;
let centroidX = 0;
let centroidY = 0;
for (let i = 0; i < vertices.length - 1; i++) {
const x1 = vertices[i][0];
const y1 = vertices[i][1];
const x2 = vertices[i + 1][0];
const y2 = vertices[i + 1][1];
const factor = x1 * y2 - x2 * y1;
area += factor;
centroidX += (x1 + x2) * factor;
centroidY += (y1 + y2) * factor;
}
area *= 0.5;
centroidX /= (6 * area);
centroidY /= (6 * area);
return [centroidX, centroidY];
}
Использование:
const coords = polygon.geometry.coordinates[0];
const centroid = polygonCentroid(coords);
Добавление полигона:
map.addSource('district', {
type: 'geojson',
data: polygon
});
map.addLayer({
id: 'district-fill',
type: 'fill',
source: 'district',
paint: {
'fill-color': '#4CAF50',
'fill-opacity': 0.4
}
});
Отображение центроида:
new maplibregl.Marker({
color: 'blue'
})
.setLngLat(centroid)
.addTo(map);
В результате центр фигуры будет обозначен отдельным маркером.
На практике центроиды чаще вычисляются при помощи библиотеки Turf.js.
Установка:
<script src="https://unpkg.com/@turf/turf@latest/turf.min.js"></script>
Расчет центроида:
const centroid = turf.centroid(polygon);
Получение координат:
const coordinates =
centroid.geometry.coordinates;
Отображение:
new maplibregl.Marker()
.setLngLat(coordinates)
.addTo(map);
Преимущества Turf.js:
Объекты административного деления часто представлены несколькими полигонами.
Пример:
{
type: "MultiPolygon",
coordinates: [
[...],
[...],
[...]
]
}
Ручной расчет становится сложным из-за необходимости учитывать площадь каждого полигона.
Наиболее удобный вариант:
const centroid = turf.centroid(feature);
или
const centerOfMass =
turf.centerOfMass(feature);
Метод centerOfMass() обычно дает более реалистичный
результат для сложных территорий.
Полигоны могут содержать внутренние кольца.
Пример:
{
type: "Polygon",
coordinates: [
outerRing,
hole1,
hole2
]
}
Отверстия уменьшают эффективную площадь объекта.
При самостоятельной реализации необходимо:
Библиотека Turf.js выполняет эти операции автоматически.
Одна из наиболее распространенных задач — размещение названия территории.
const centroid = turf.centroid(feature);
new maplibregl.Popup({
closeButton: false
})
.setLngLat(
centroid.geometry.coordinates
)
.setHTML("Район №1")
.addTo(map);
Такой подход позволяет выводить подписи в центральной части объекта.
Для сложных вогнутых фигур геометрический центроид может оказаться за пределами объекта.
Например:
В таких случаях центр может визуально выглядеть неправильно.
Пример:
const centroid = turf.centroid(feature);
Полученная точка может лежать вне полигона.
Для корректного размещения подписей часто используется точка, гарантированно находящаяся внутри объекта.
const point =
turf.pointOnFeature(feature);
или
const point =
turf.centerOfMass(feature);
Отображение:
new maplibregl.Marker()
.setLngLat(point.geometry.coordinates)
.addTo(map);
Такой подход особенно полезен для тематических карт.
После вычисления центра можно переместить карту.
map.flyTo({
center: centroid,
zoom: 14
});
Либо:
map.easeTo({
center: centroid,
duration: 2000
});
Это часто используется при выборе объекта из списка.
Допустим, имеется набор полигонов.
const features = geojson.features;
Вычисление центроидов:
const centroids = features.map(feature =>
turf.centroid(feature)
);
Создание нового GeoJSON:
const centroidCollection = {
type: "FeatureCollection",
features: centroids
};
Добавление источника:
map.addSource('centroids', {
type: 'geojson',
data: centroidCollection
});
Создание слоя точек:
map.addLayer({
id: 'centroids-layer',
type: 'circle',
source: 'centroids',
paint: {
'circle-radius': 5,
'circle-color': '#ff0000'
}
});
При работе с тысячами объектов расчет центроидов может стать затратной операцией.
Основные методы оптимизации:
Центроиды вычисляются на сервере:
{
"name": "District A",
"centroid": [37.62, 55.76]
}
Клиент получает уже готовые координаты.
const centroidCache = new Map();
После первого вычисления результат сохраняется.
Тяжелые геометрические вычисления переносятся в отдельный поток.
worker.postMessage(featureCollection);
Это предотвращает зависание интерфейса карты.
turf.pointOnFeature(feature)
turf.centerOfMass(feature)
turf.centroid(feature)
calculateCentroid(points)
map.flyTo({
center: centroid
});
const centers =
features.map(f => turf.centroid(f));
Расчет центроидов является одной из базовых операций пространственного анализа в MapLibre GL JS. Правильно выбранный алгоритм позволяет получать точные геометрические центры объектов, корректно размещать подписи, выполнять аналитические вычисления и создавать более информативные картографические интерфейсы.