Кластеризация — это процесс объединения множества объектов в группы на основе определённых критериев сходства. В геоинформационных системах и веб-картографии кластеризация чаще всего применяется для работы с большим количеством точек на карте.
Типичные задачи кластеризации:
Представим карту с десятками тысяч точек, обозначающих магазины, пользователей или датчики. Отображение каждой точки отдельно приводит к перегрузке интерфейса и снижению производительности. Кластеризация позволяет объединять близко расположенные объекты в компактные группы.
Библиотека Turf.js предоставляет набор инструментов для пространственной кластеризации непосредственно в браузере или среде Node.js.
Большинство алгоритмов Turf.js работают с объектами формата GeoJSON.
Пример набора точек:
const points = turf.featureCollection([
turf.point([37.6176, 55.7558]),
turf.point([37.6200, 55.7560]),
turf.point([37.6215, 55.7555]),
turf.point([37.6500, 55.7700]),
turf.point([37.6510, 55.7710])
]);
Каждый объект является элементом коллекции
FeatureCollection.
Структура GeoJSON:
{
type: "Feature",
properties: {},
geometry: {
type: "Point",
coordinates: [37.6176, 55.7558]
}
}
Одним из наиболее востребованных алгоритмов в Turf.js является DBSCAN (Density-Based Spatial Clustering of Applications with Noise).
Функция:
turf.clustersDbscan(points, maxDistance, options)
Параметры:
| Параметр | Описание |
|---|---|
| points | Коллекция точек |
| maxDistance | Максимальное расстояние между соседними точками |
| options | Дополнительные настройки |
Пример:
const clustered = turf.clustersDbscan(points, 0.5, {
units: "kilometers"
});
После выполнения каждая точка получает дополнительные свойства:
{
cluster: 0,
dbscan: "core"
}
Возможные значения поля dbscan:
| Значение | Описание |
|---|---|
| core | Центральная точка кластера |
| edge | Пограничная точка |
| noise | Шумовая точка |
Алгоритм строится на понятии плотности размещения объектов.
Для каждой точки выполняются шаги:
Визуально:
Кластер 1
● ● ●
● ●
Кластер 2
● ●
●
Шум
●
Главное преимущество DBSCAN заключается в способности обнаруживать кластеры произвольной формы.
Параметр minPoints определяет минимальное число соседей,
необходимое для создания кластера.
Пример:
const clustered = turf.clustersDbscan(points, 0.5, {
units: "kilometers",
minPoints: 3
});
Интерпретация:
minPoints = 2 — образуется много небольших
кластеров;minPoints = 5 — формируются только плотные группы;Подбор значения зависит от характера данных.
Полученные данные можно просмотреть через свойства объектов.
Пример:
clustered.features.forEach(feature => {
console.log(feature.properties.cluster);
});
Результат:
0
0
0
1
1
Здесь обнаружены два кластера:
Часто требуется определить число сформированных групп.
Пример:
const clusters = new Set();
clustered.features.forEach(feature => {
const id = feature.properties.cluster;
if (id !== undefined) {
clusters.add(id);
}
});
console.log(clusters.size);
Результат:
2
После кластеризации можно извлекать точки отдельных групп.
Пример:
const cluster0 = clustered.features.filter(
feature => feature.properties.cluster === 0
);
Создание новой коллекции:
const clusterCollection = turf.featureCollection(cluster0);
Такой набор можно использовать для дальнейшего анализа.
После нахождения группы объектов часто требуется определить её пространственные границы.
Для этого удобно использовать выпуклую оболочку.
const hull = turf.convex(clusterCollection);
Получается полигон:
{
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [...]
}
}
Такой подход позволяет визуализировать реальные области концентрации объектов.
Центральная точка помогает представить кластер одним объектом.
Для вычисления центра:
const center = turf.center(clusterCollection);
Результат:
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [...]
}
}
Центры широко используются при построении агрегированных карт.
Turf.js также содержит реализацию алгоритма K-Means.
Функция:
turf.clustersKmeans(points, options)
Пример:
const result = turf.clustersKmeans(points, {
numberOfClusters: 3
});
Основное отличие от DBSCAN заключается в необходимости заранее задавать количество кластеров.
Алгоритм выполняет несколько итераций.
Этапы:
Упрощённая схема:
Шаг 1
X X
●
● ●
Шаг 2
X ● X
● ●
Шаг 3
X X
● ● ● ●
Где:
● — точки данных;X — центроиды.Параметр:
numberOfClusters
Пример:
const result = turf.clustersKmeans(points, {
numberOfClusters: 5
});
Важно понимать особенности выбора значения:
Слишком маленькое количество:
Много различных групп
↓
Один большой кластер
Слишком большое количество:
Одна группа
↓
Несколько искусственных кластеров
Оптимальное значение зависит от предметной области и характера данных.
После выполнения K-Means каждая точка получает свойство:
feature.properties.cluster
Пример:
result.features.forEach(feature => {
console.log(feature.properties.cluster);
});
Возможный результат:
0
1
1
2
0
Каждая группа имеет собственный центр.
Получение центров:
const clusters = {};
result.features.forEach(feature => {
const id = feature.properties.cluster;
if (!clusters[id]) {
clusters[id] = [];
}
clusters[id].push(feature);
});
Далее:
for (const id in clusters) {
const collection = turf.featureCollection(clusters[id]);
const centroid = turf.centroid(collection);
console.log(id, centroid);
}
| Характеристика | DBSCAN | K-Means |
|---|---|---|
| Требует число кластеров | Нет | Да |
| Учитывает плотность | Да | Нет |
| Находит шум | Да | Нет |
| Подходит для произвольных форм | Да | Ограниченно |
| Скорость работы | Высокая | Очень высокая |
| Чувствительность к параметрам | Средняя | Высокая |
Практические рекомендации:
После кластеризации можно вычислить размеры групп.
Пример:
const clusterStats = {};
result.features.forEach(feature => {
const id = feature.properties.cluster;
clusterStats[id] ??= 0;
clusterStats[id]++;
});
Результат:
{
0: 45,
1: 120,
2: 78
}
На основе этих данных можно:
Точки могут содержать произвольные свойства.
const point = turf.point(
[37.6176, 55.7558],
{
users: 150,
city: "Москва"
}
);
После кластеризации атрибуты сохраняются:
{
city: "Москва",
users: 150,
cluster: 0
}
Это позволяет выполнять агрегацию показателей внутри групп.
Пример вычисления суммарного количества пользователей:
const totalUsers = clusterFeatures.reduce(
(sum, feature) => sum + feature.properties.users,
0
);
Плотность может определяться как отношение количества объектов к площади занимаемой территории.
Площадь кластера:
const hull = turf.convex(clusterCollection);
const area = turf.area(hull);
Количество объектов:
const count = clusterCollection.features.length;
Плотность:
const density = count / area;
Чем больше значение, тем более компактным является кластер.
Одной из сильнейших сторон DBSCAN является автоматическое определение выбросов.
Поиск шумовых точек:
const outliers = clustered.features.filter(
feature => feature.properties.dbscan === "noise"
);
Полученные объекты могут обозначать:
Исходные данные:
const stores = turf.featureCollection(storePoints);
Кластеризация:
const clusters = turf.clustersDbscan(
stores,
2,
{
units: "kilometers",
minPoints: 4
}
);
Последующая обработка:
const denseAreas = [];
for (const id of clusterIds) {
const features = getClusterFeatures(id);
const collection =
turf.featureCollection(features);
denseAreas.push({
center: turf.center(collection),
border: turf.convex(collection),
count: features.length
});
}
Результат анализа:
При обработке десятков и сотен тысяч точек рекомендуется:
Пример фильтрации по области:
const filtered = turf.pointsWithinPolygon(
points,
regionPolygon
);
После уменьшения объёма данных:
const clustered =
turf.clustersDbscan(filtered, 1, {
units: "kilometers"
});
Такой подход существенно сокращает время вычислений и объём памяти, необходимой для пространственного анализа больших географических наборов данных.