Работа с полигонами в Leaflet часто выходит за рамки простого отображения контуров на карте. Одной из типичных задач является вычисление площади области, ограниченной полигоном. В контексте географических данных это нетривиальная операция, поскольку координаты заданы в системе широта/долгота, а поверхность Земли является сфероидом, а не плоскостью.
Полигон в Leaflet задаётся массивом координат LatLng,
образующих замкнутый контур.
const polygon = L.polygon([
[51.505, -0.09],
[51.51, -0.1],
[51.51, -0.08]
]).addTo(map);
Каждая вершина описывает географическую точку. Последняя точка автоматически соединяется с первой, формируя замкнутую область.
Для вычисления площади важно понимать, что Leaflet хранит координаты в географической системе (WGS84), где прямое применение классических формул планиметрии приводит к погрешностям.
Существует два основных подхода к вычислению площади:
Планарный метод предполагает проекцию координат на плоскость. Он проще, но подходит только для небольших участков, где искажения минимальны.
Геодезический метод учитывает эллипсоидальную модель Земли и даёт более точный результат на больших расстояниях.
Наиболее распространённый способ вычисления площади полигонов в экосистеме Leaflet — использование Turf.js.
import * as turf from '@turf/turf';
const polygon = turf.polygon([[
[ -0.09, 51.505 ],
[ -0.1, 51.51 ],
[ -0.08, 51.51 ],
[ -0.09, 51.505 ]
]]);
const area = turf.area(polygon);
console.log(area);
Функция turf.area возвращает площадь в квадратных
метрах. Важно учитывать порядок координат: в GeoJSON используется формат
[longitude, latitude], в отличие от Leaflet.
Leaflet и Turf.js часто используются совместно. Полигон создаётся в Leaflet, затем его координаты извлекаются и передаются в Turf.
const latlngs = polygon.getLatLngs()[0];
const coords = latlngs.map(point => [point.lng, point.lat]);
coords.push(coords[0]);
const turfPolygon = turf.polygon([coords]);
const area = turf.area(turfPolygon);
Такой подход обеспечивает точность и совместимость с геопространственными вычислениями.
При работе с полигонами часто возникают типичные ошибки:
Если первая и последняя точки не совпадают, некоторые алгоритмы дают некорректный результат.
Leaflet использует [lat, lng], тогда как GeoJSON и Turf
— [lng, lat].
Простейшие формулы площади (например, через “шнуровку”) работают только в проекции и дают значительные ошибки на больших полигонах.
Существует геодезический алгоритм вычисления площади на сфере, основанный на суммировании углов между вершинами. Один из вариантов реализуется через формулу сферического многоугольника.
function polygonArea(latlngs) {
const R = 6378137; // радиус Земли в метрах
let area = 0;
for (let i = 0; i < latlngs.length; i++) {
const j = (i + 1) % latlngs.length;
const lat1 = latlngs[i].lat * Math.PI / 180;
const lat2 = latlngs[j].lat * Math.PI / 180;
const lng1 = latlngs[i].lng * Math.PI / 180;
const lng2 = latlngs[j].lng * Math.PI / 180;
area += (lng2 - lng1) * (2 + Math.sin(lat1) + Math.sin(lat2));
}
return Math.abs(area * R * R / 2);
}
Этот метод приближённый, но подходит для большинства прикладных задач.
Leaflet поддерживает полигоны с внутренними кольцами (holes). Структура данных усложняется:
const polygon = L.polygon([
[
[51.51, -0.1],
[51.52, -0.12],
[51.52, -0.08]
],
[
[51.51, -0.11],
[51.51, -0.095],
[51.515, -0.095]
]
]);
При вычислении площади необходимо учитывать вычитание внутренних областей.
В Turf.js это реализовано автоматически:
const polygon = turf.polygon([
outerRing,
holeRing
]);
const area = turf.area(polygon);
При переходе от глобальных к локальным картам часто используется проекция Web Mercator (EPSG:3857). Она искажает площади, особенно ближе к полюсам.
Искажение выражается в:
Поэтому вычисления, основанные на координатах Leaflet без геодезической коррекции, требуют осторожности.
Результат вычисления часто используется для визуализации:
const area = turf.area(turfPolygon);
L.popup()
.setLatLng(polygon.getBounds().getCenter())
.setContent(`Площадь: ${area.toFixed(2)} м²`)
.openOn(map);
Такой подход связывает геометрические вычисления с интерактивной картой.
Перед передачей данных в алгоритмы вычисления площади необходимо привести координаты к единому виду:
function normalize(latlngs) {
const cleaned = latlngs.filter((p, i, arr) =>
i === 0 || p.lat !== arr[i - 1].lat || p.lng !== arr[i - 1].lng
);
const first = cleaned[0];
const last = cleaned[cleaned.length - 1];
if (first.lat !== last.lat || first.lng !== last.lng) {
cleaned.push(first);
}
return cleaned;
}
Выбор подхода зависит от характеристик данных:
При интеграции с Leaflet наиболее устойчивой практикой остаётся использование специализированных геопространственных библиотек, поскольку сам Leaflet не предназначен для математической обработки геометрии.
При работе с большим количеством объектов вычисление площади становится затратной операцией.
Оптимизации включают:
self.onmess age = function (e) {
const polygon = turf.polygon(e.data);
const area = turf.area(polygon);
postMessage(area);
};
Разделение вычислений позволяет избежать блокировки основного потока интерфейса карты.
При использовании Leaflet.draw площадь может
пересчитываться динамически:
map.on('draw:edited', function (e) {
e.layers.eachLayer(function (layer) {
const coords = layer.getLatLngs()[0];
const turfPolygon = turf.polygon([coords.map(p => [p.lng, p.lat])]);
const area = turf.area(turfPolygon);
});
});
Это обеспечивает актуальность данных при интерактивном изменении формы полигона.