Плотность в контексте геоаналитики представляет собой отношение количества объектов к площади или объёму пространства, в котором они распределены. В большинстве задач Turf.js речь идёт о двумерных данных, поэтому базовая формула сводится к нормализации числа точек относительно площади:
[ = ]
где (N) — количество геообъектов, (S) — площадь региона.
Такая модель применяется для анализа концентрации событий, объектов инфраструктуры, пользовательской активности, природных явлений и других пространственных распределений.
Вся работа с пространственными объектами в Turf.js строится вокруг GeoJSON. Основные типы:
FeatureFeatureCollectionPointPolygonMultiPolygonТиповая коллекция точек:
const points = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
type: "Point",
coordinates: [37.62, 55.75]
}
}
]
};
Для расчёта плотности ключевым становится сочетание точек и геометрии области анализа.
Один из базовых сценариев — вычисление плотности точек внутри полигона.
Используется комбинация:
@turf/area — вычисление площади@turf/points-within-polygon — фильтрация точекimport area from "@turf/area";
import pointsWithinPolygon from "@turf/points-within-polygon";
const polygon = {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [[[37, 55], [38, 55], [38, 56], [37, 56], [37, 55]]]
}
};
const selectedPoints = pointsWithinPolygon(points, polygon);
const polygonArea = area(polygon);
const density = selectedPoints.features.length / polygonArea;
Значение плотности здесь выражается в количестве точек на квадратный метр (или другую единицу площади, в зависимости от CRS).
Turf.js возвращает площадь в квадратных метрах при использовании стандартной геодезической модели. Для интерпретации данных часто требуется нормализация:
const densityPerKm2 =
selectedPoints.features.length / (polygonArea / 1_000_000);
Более гибкий подход — разбиение пространства на регулярную сетку и расчёт плотности в каждой ячейке.
import squareGrid from "@turf/square-grid";
import pointsWithinPolygon from "@turf/points-within-polygon";
import area from "@turf/area";
const bbox = [36, 54, 39, 57];
const grid = squareGrid(bbox, 10, { units: "kilometers" });
Каждая ячейка сетки становится контейнером для подсчёта точек.
const enrichedGrid = grid.features.map(cell => {
const pts = pointsWithinPolygon(points, cell);
const cellArea = area(cell);
return {
...cell,
properties: {
count: pts.features.length,
density: pts.features.length / cellArea
}
};
});
Hex grid используется чаще для плотностного анализа из-за более равномерного распределения соседних расстояний.
import hexGrid from "@turf/hex-grid";
const hexes = hexGrid(bbox, 5, { units: "kilometers" });
Преимущества:
Для эффективной агрегации используется подход пространственного join:
import collect from "@turf/collect";
const hexWithPoints = collect(hexes, points, "value", "points");
После этого каждая ячейка содержит массив точек, из которого извлекается плотность.
В реальных сценариях точки могут иметь вес:
const weightedDensity = (cellPoints) => {
const weightSum = cellPoints.features.reduce((acc, p) => {
return acc + (p.properties.weight || 1);
}, 0);
return weightSum;
};
Тогда плотность становится:
[ = ]
Для сравнения разных зон применяется нормализация:
const densities = enrichedGrid.map(c => c.properties.density);
const min = Math.min(...densities);
const max = Math.max(...densities);
const normalized = enrichedGrid.map(cell => {
const d = cell.properties.density;
return {
...cell,
properties: {
...cell.properties,
normalizedDensity: (d - min) / (max - min)
}
};
});
При сильной дисперсии значений используется логарифмирование:
const logDensity = Math.log(1 + cellCount / cellArea);
Такой подход стабилизирует визуализацию и уменьшает влияние выбросов.
Хотя Turf.js не предоставляет полноценную KDE-функцию в базовом наборе, аппроксимация может быть реализована через взвешивание точек по расстоянию.
Идея:
import distance from "@turf/distance";
function kernelWeight(d, bandwidth) {
const x = d / bandwidth;
return Math.exp(-x * x);
}
Агрегация по ячейке:
function cellDensity(cell, points, bandwidth) {
let sum = 0;
for (const p of points.features) {
const d = distance(cell, p, { units: "kilometers" });
sum += kernelWeight(d, bandwidth);
}
return sum;
}
При увеличении числа объектов производительность становится критичной.
Применяются техники:
bbox индекс)pointsWithinPolygonimport bbox from "@turf/bbox";
const [minX, minY, maxX, maxY] = bbox(polygon);
В аналитических системах часто комбинируются разные подходы:
Финальная модель:
[ = {grid} + {kernel} + _{weighted}]
const finalDensity =
0.5 * gridDensity +
0.3 * kernelDensity +
0.2 * weightedDensity;
Полученные значения плотности используются для:
Каждая ячейка сетки становится элементом дискретного поля, описывающего пространственное распределение явления.