Перебор элементов коллекций

В основе большинства операций в геопространственных вычислениях лежит структура данных GeoJSON. В Turf.js работа почти всегда происходит не с одиночными координатами, а с коллекциями объектов: Feature, FeatureCollection, массивами геометрий и вложенными структурами.

Коллекции в Turf.js требуют особого подхода к перебору, поскольку данные могут быть вложенными, неоднородными и содержать смешанные типы геометрий.

Типы коллекций

GeoJSON определяет несколько уровней группировки:

  • FeatureCollection — набор объектов Feature
  • Feature — объект с геометрией и свойствами
  • GeometryCollection — набор геометрий
  • массив координат внутри геометрий

Каждый уровень требует корректного способа обхода, иначе часть данных может быть пропущена.


Базовые принципы перебора

Перебор в Turf.js строится вокруг унифицированного подхода:

  • обход всех объектов независимо от типа геометрии
  • извлечение координат без ручного рекурсивного разбора
  • сохранение структуры данных или её упрощение

Вместо прямого использования for по вложенным массивам используются функции из модуля @turf/meta.


featureEach: перебор FeatureCollection

Функция featureEach используется для обхода всех Feature внутри FeatureCollection.

import { featureEach } from "@turf/meta";

const collection = {
  type: "FeatureCollection",
  features: [
    { type: "Feature", geometry: { type: "Point", coordinates: [10, 20] }, properties: {} },
    { type: "Feature", geometry: { type: "Point", coordinates: [30, 40] }, properties: {} }
  ]
};

featureEach(collection, (currentFeature, featureIndex) => {
  console.log(featureIndex, currentFeature.geometry);
});

Поведение функции

  • игнорирует вложенные структуры
  • гарантирует последовательный обход всех Feature
  • не раскрывает координаты внутри геометрий

coordEach: перебор координат

Для работы непосредственно с координатами используется coordEach.

import { coordEach } from "@turf/meta";

coordEach(collection, (currentCoord, coordIndex) => {
  console.log(coordIndex, currentCoord);
});

Особенности перебора координат

  • обходит все уровни вложенности координат
  • работает с Point, LineString, Polygon и Multi-*
  • не различает тип геометрии на уровне callback

Пример структуры обхода

Для Polygon:

{
  type: "Polygon",
  coordinates: [
    [
      [0, 0],
      [10, 0],
      [10, 10],
      [0, 0]
    ]
  ]
}

coordEach последовательно вернёт каждую координату без учёта колец.


propEach: доступ к свойствам объектов

propEach используется для перебора только свойства properties каждого Feature.

import { propEach } from "@turf/meta";

propEach(collection, (properties, featureIndex) => {
  console.log(featureIndex, properties);
});

Назначение

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

geomEach: перебор геометрий

Функция geomEach позволяет работать только с геометриями, игнорируя свойства.

import { geomEach } from "@turf/meta";

geomEach(collection, (geometry, featureIndex) => {
  console.log(featureIndex, geometry.type);
});

Применение

  • анализ типов геометрий
  • геометрические трансформации
  • подготовка данных для пространственных операций

flattenEach: нормализация Multi-геометрий

GeoJSON поддерживает мульти-геометрии:

  • MultiPoint
  • MultiLineString
  • MultiPolygon

flattenEach преобразует их в последовательность простых геометрий.

import { flattenEach } from "@turf/meta";

flattenEach(collection, (currentFeature, featureIndex, multiFeatureIndex) => {
  console.log(featureIndex, multiFeatureIndex, currentFeature.geometry.type);
});

Поведение

  • MultiPolygon разбивается на отдельные Polygon
  • MultiLineString — на LineString
  • MultiPoint — на Point

Особенности индексации

При переборе коллекций важно учитывать несколько типов индексов:

  • featureIndex — индекс Feature в FeatureCollection
  • coordIndex — глобальный индекс координат
  • multiFeatureIndex — индекс части мульти-геометрии

Эти индексы не взаимозаменяемы и отражают разные уровни структуры данных.


Рекурсивная природа GeoJSON

GeoJSON допускает вложенность:

  • FeatureCollection → Feature → Geometry → Coordinates

Каждый уровень требует отдельного подхода к обходу. Turf.js скрывает рекурсию внутри meta-функций, обеспечивая единообразный API.


Сравнение методов перебора

featureEach

  • уровень: Feature
  • доступ: геометрия и свойства
  • не раскрывает координаты

coordEach

  • уровень: координаты
  • максимальная детализация
  • игнорирует структуру Feature

propEach

  • уровень: properties
  • работа только с метаданными

geomEach

  • уровень: Geometry
  • без свойств

flattenEach

  • уровень: нормализованные геометрии
  • разбиение Multi-структур

Изменение данных при переборе

При работе с коллекциями часто возникает необходимость трансформации данных.

Пример масштабирования координат:

import { coordEach } from "@turf/meta";

coordEach(collection, (coord) => {
  coord[0] *= 2;
  coord[1] *= 2;
});

Важное поведение

  • изменение происходит по ссылке
  • исходный объект модифицируется
  • копирование не выполняется автоматически

Безопасное клонирование перед обходом

Для предотвращения мутаций исходных данных используется копирование:

const cloned = JSON.parse(JSON.stringify(collection));

coordEach(cloned, (coord) => {
  coord[0] += 1;
});

При этом сохраняется исходная структура GeoJSON.


Производительность перебора

При работе с большими коллекциями (десятки тысяч объектов) критичны следующие аспекты:

  • coordEach наиболее затратен по времени
  • propEach — наиболее лёгкий
  • flattenEach добавляет накладные операции разбиения

Оптимизация достигается выбором минимально необходимого уровня перебора.


Вложенные сценарии обхода

Комбинации функций позволяют реализовать сложные сценарии анализа:

import { featureEach, coordEach } from "@turf/meta";

featureEach(collection, (feature) => {
  coordEach(feature, (coord) => {
    coord[2] = coord[2] || 0;
  });
});

Здесь происходит переход от уровня Feature к уровню координат внутри каждой геометрии.


Особенности работы с GeometryCollection

GeometryCollection требует особого внимания, так как:

  • содержит разнородные геометрии
  • не всегда имеет properties
  • требует использования geomEach или coordEach

Пример:

const geometryCollection = {
  type: "GeometryCollection",
  geometries: [
    { type: "Point", coordinates: [0, 0] },
    { type: "LineString", coordinates: [[1, 1], [2, 2]] }
  ]
};

Фильтрация во время перебора

Внутри callback-функций можно реализовывать фильтрацию:

import { featureEach } from "@turf/meta";

const result = [];

featureEach(collection, (feature) => {
  if (feature.geometry.type === "Point") {
    result.push(feature);
  }
});

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


Итерация как основа геоаналитики

Большинство операций Turf.js — это надстройка над базовым перебором:

  • расчёт расстояний
  • агрегация точек
  • построение полигонов
  • анализ плотности

Все они в конечном счёте опираются на обход коллекций через meta-функции.


Поведение callback-функций

Callback в функциях перебора имеет следующие свойства:

  • синхронное выполнение
  • отсутствие встроенного прерывания (break)
  • возможность передачи пользовательского контекста через замыкания

Ограничения перебора

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

Практика комбинирования обходов

Сложные задачи часто требуют каскадного перебора:

  • сначала Feature
  • затем геометрии
  • затем координаты
import { featureEach, coordEach } from "@turf/meta";

featureEach(collection, (feature) => {
  coordEach(feature, (coord) => {
    coord[0] = Math.round(coord[0]);
    coord[1] = Math.round(coord[1]);
  });
});

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