Объединение точек в коллекции

В Turf.js точка представляется как GeoJSON-объект типа Point, обёрнутый в структуру Feature. Базовая единица работы с наборами точек — коллекция FeatureCollection, которая позволяет хранить произвольное количество геообъектов.

const point = turf.point([30.5, 50.5]);

const collection = turf.featureCollection([
  turf.point([30.5, 50.5]),
  turf.point([30.6, 50.6]),
  turf.point([30.7, 50.7])
]);

Ключевая особенность: Turf.js не оперирует «массивами координат» напрямую в геооперациях, а всегда требует строгую GeoJSON-структуру.


Формирование единой геометрии из набора точек

Объединение точек в коллекции обычно означает приведение множества Point-объектов к одной геометрии типа MultiPoint. Это позволяет рассматривать набор точек как единый объект, сохраняя при этом их индивидуальные координаты.

const multiPoint = turf.multiPoint([
  [30.5, 50.5],
  [30.6, 50.6],
  [30.7, 50.7]
]);

MultiPoint — это наиболее прямолинейная форма объединения без потери данных и без изменения пространственных отношений.


Объединение через преобразование FeatureCollection

Когда точки уже находятся в FeatureCollection, часто требуется преобразовать их в единый объект. Для этого используется ручное извлечение координат.

const points = turf.featureCollection([
  turf.point([10, 10]),
  turf.point([20, 20]),
  turf.point([30, 30])
]);

const coords = points.features.map(f => f.geometry.coordinates);

const merged = turf.multiPoint(coords);

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


Использование turf.combine для объединения геометрий

Функция turf.combine применяется для объединения разных типов геометрий в один объект, чаще всего при работе со смешанными коллекциями. Несмотря на то, что она чаще используется для линий и полигонов, точки также могут быть включены в общий процесс нормализации.

const collection = turf.featureCollection([
  turf.point([10, 10]),
  turf.point([20, 20]),
  turf.point([30, 30])
]);

const combined = turf.combine(collection);

Результат возвращает структуру, где геометрии сгруппированы по типам (например, MultiPoint), что упрощает последующую обработку в аналитических задачах.


Очистка и устранение дубликатов координат

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

function removeDuplicates(features) {
  const seen = new Set();

  return features.filter(f => {
    const key = f.geometry.coordinates.join(',');
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

const cleaned = turf.featureCollection(
  removeDuplicates(points.features)
);

const result = turf.multiPoint(
  cleaned.features.map(f => f.geometry.coordinates)
);

Такой подход важен при работе с GPS-данными и телеметрией, где дублирование встречается регулярно.


Группировка точек по свойствам

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

function groupByProperty(collection, prop) {
  return collection.features.reduce((acc, feature) => {
    const key = feature.properties[prop];
    if (!acc[key]) acc[key] = [];
    acc[key].push(feature);
    return acc;
  }, {});
}

const grouped = groupByProperty(points, 'type');

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

const multiPointsByGroup = Object.keys(grouped).map(key =>
  turf.multiPoint(grouped[key].map(f => f.geometry.coordinates))
);

Кластеризация точек перед объединением

При больших наборах данных простое объединение теряет смысл без пространственной логики. В таких случаях применяется кластеризация, например DBSCAN через turf.clustersDbscan.

const clustered = turf.clustersDbscan(points, 1, {
  units: 'kilometers'
});

Каждой точке добавляется свойство cluster, после чего можно собрать объединённые группы:

function buildClusters(collection) {
  const clusters = {};

  collection.features.forEach(f => {
    const id = f.properties.cluster;
    if (id === undefined) return;

    if (!clusters[id]) clusters[id] = [];
    clusters[id].push(f.geometry.coordinates);
  });

  return Object.values(clusters).map(coords =>
    turf.multiPoint(coords)
  );
}

const resultClusters = buildClusters(clustered);

Такой подход позволяет объединять точки не произвольно, а на основе плотности распределения.


Нормализация структуры данных перед объединением

Перед любыми операциями объединения требуется привести данные к единому формату:

  • все координаты должны быть в одном порядке [lng, lat]
  • отсутствующие свойства должны быть обработаны
  • геометрии должны быть валидными GeoJSON Feature
function normalize(feature) {
  if (!feature.geometry || feature.geometry.type !== 'Point') {
    return null;
  }

  const [lng, lat] = feature.geometry.coordinates;

  if (typeof lng !== 'number' || typeof lat !== 'number') {
    return null;
  }

  return feature;
}

const normalized = turf.featureCollection(
  points.features.map(normalize).filter(Boolean)
);

Преобразование точек в аналитические структуры

Объединённые точки часто используются как вход для дальнейших геоопераций:

  • расчёт центра масс (turf.center)
  • построение выпуклой оболочки (turf.convex)
  • анализ плотности распределения
  • построение heatmap-слоёв
const center = turf.center(multiPoint);

const convexHull = turf.convex(
  turf.featureCollection(multiPoint.geometry.coordinates.map(c =>
    turf.point(c)
  ))
);

Хотя convex чаще применяется к FeatureCollection, объединённые структуры упрощают подготовку данных.


Практическая модель объединения точек в один объект

Типовой рабочий конвейер:

  1. Приём исходного FeatureCollection
  2. Очистка от дубликатов
  3. Нормализация координат
  4. Группировка по признаку или кластеризация
  5. Преобразование каждой группы в MultiPoint
const cleaned = removeDuplicates(points.features);

const grouped = groupByProperty(
  turf.featureCollection(cleaned),
  'category'
);

const result = Object.values(grouped).map(group =>
  turf.multiPoint(group.map(f => f.geometry.coordinates))
);

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