Расчет центроидов

Центроид — это геометрический центр объекта, вычисляемый на основе координат его вершин. В картографических приложениях центроиды используются для размещения подписей, отображения маркеров объектов, выполнения пространственного анализа, расчета расстояний между объектами и построения различных визуализаций.

В контексте 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

Часто данные поступают в формате 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)

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


Реализация центроида полигона на JavaScript

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

На практике центроиды чаще вычисляются при помощи библиотеки 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:

  • готовые геометрические алгоритмы;
  • высокая надежность;
  • поддержка сложных фигур;
  • работа с MultiPolygon;
  • совместимость с GeoJSON.

Центроид MultiPolygon

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

Пример:

{
    type: "MultiPolygon",
    coordinates: [
        [...],
        [...],
        [...]
    ]
}

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

Наиболее удобный вариант:

const centroid = turf.centroid(feature);

или

const centerOfMass =
    turf.centerOfMass(feature);

Метод centerOfMass() обычно дает более реалистичный результат для сложных территорий.


Центроид и внутренние отверстия

Полигоны могут содержать внутренние кольца.

Пример:

{
    type: "Polygon",
    coordinates: [
        outerRing,
        hole1,
        hole2
    ]
}

Отверстия уменьшают эффективную площадь объекта.

При самостоятельной реализации необходимо:

  1. вычислить площадь внешнего контура;
  2. вычислить площади отверстий;
  3. вычесть площади внутренних колец;
  4. скорректировать координаты центра.

Библиотека 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();

После первого вычисления результат сохраняется.

Использование Web Worker

Тяжелые геометрические вычисления переносятся в отдельный поток.

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. Правильно выбранный алгоритм позволяет получать точные геометрические центры объектов, корректно размещать подписи, выполнять аналитические вычисления и создавать более информативные картографические интерфейсы.