Расчет плотности

Плотность в контексте геоаналитики представляет собой отношение количества объектов к площади или объёму пространства, в котором они распределены. В большинстве задач Turf.js речь идёт о двумерных данных, поэтому базовая формула сводится к нормализации числа точек относительно площади:

[ = ]

где (N) — количество геообъектов, (S) — площадь региона.

Такая модель применяется для анализа концентрации событий, объектов инфраструктуры, пользовательской активности, природных явлений и других пространственных распределений.


Базовая структура данных в Turf.js

Вся работа с пространственными объектами в Turf.js строится вокруг GeoJSON. Основные типы:

  • Feature
  • FeatureCollection
  • Point
  • Polygon
  • MultiPolygon

Типовая коллекция точек:

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);

Гридовая модель плотности

Более гибкий подход — разбиение пространства на регулярную сетку и расчёт плотности в каждой ячейке.

Square Grid

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" });

Преимущества:

  • минимизация искажений соседства
  • равномерная агрегация
  • визуальная стабильность при heatmap-подобных анализах

Подсчёт объектов внутри ячеек

Для эффективной агрегации используется подход пространственного 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);

Такой подход стабилизирует визуализацию и уменьшает влияние выбросов.


Kernel Density Approximation

Хотя 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 (bbox индекс)
  • пространственное разбиение через grid
  • кэширование результатов pointsWithinPolygon
  • уменьшение точности координат
import bbox from "@turf/bbox";

const [minX, minY, maxX, maxY] = bbox(polygon);

Комбинированные модели плотности

В аналитических системах часто комбинируются разные подходы:

  • grid density (структурная)
  • kernel density (плавная)
  • weighted density (семантическая)

Финальная модель:

[ = {grid} + {kernel} + _{weighted}]

const finalDensity =
  0.5 * gridDensity +
  0.3 * kernelDensity +
  0.2 * weightedDensity;

Интерпретация результатов

Полученные значения плотности используются для:

  • построения heatmap-слоёв
  • поиска кластеров активности
  • анализа перегруженных зон
  • выявления аномалий распределения
  • планирования инфраструктуры

Каждая ячейка сетки становится элементом дискретного поля, описывающего пространственное распределение явления.