Выборка объектов по условиям

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

Turf.js работает с типами GeoJSON:

  • Feature
  • FeatureCollection
  • Point, LineString, Polygon и их многовариантные формы

Каждый объект содержит:

  • геометрию (geometry)
  • свойства (properties)

Выборка объектов почти всегда реализуется как преобразование FeatureCollection в новый набор объектов, удовлетворяющих заданным условиям.

const points = turf.featureCollection([
  turf.point([30, 50], { id: 1, type: "A" }),
  turf.point([31, 51], { id: 2, type: "B" }),
  turf.point([32, 52], { id: 3, type: "A" })
]);

Атрибутивная выборка

Атрибутивная фильтрация не зависит от геометрии и выполняется стандартными средствами JavaScript. Turf.js не предоставляет отдельного API для фильтрации по свойствам, поскольку GeoJSON уже совместим с функциональными подходами языка.

const filtered = turf.featureCollection(
  points.features.filter(f => f.properties.type === "A")
);

Такой подход применяется как базовый этап перед пространственными вычислениями, снижая объём данных.

Типовые сценарии:

  • выбор по категориям (type, class)
  • фильтрация по числовым диапазонам
  • исключение объектов с отсутствующими полями
const filtered = turf.featureCollection(
  points.features.filter(f => f.properties.id > 1)
);

Пространственные предикаты

Основная сила Turf.js заключается в геометрических проверках. Они реализованы через булевы функции, возвращающие true или false.

Проверка попадания точки в полигон

Функция booleanPointInPolygon определяет принадлежность точки области.

const pt = turf.point([30, 50]);

const poly = turf.polygon([[
  [29, 49],
  [33, 49],
  [33, 53],
  [29, 53],
  [29, 49]
]]);

const result = turf.booleanPointInPolygon(pt, poly);

Этот предикат используется для:

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

Пересечение геометрий

booleanIntersects проверяет наличие любого пересечения между объектами.

const line = turf.lineString([[30, 50], [35, 55]]);
const polygon = poly;

const intersects = turf.booleanIntersects(line, polygon);

Применяется при:

  • анализе дорожной сети
  • проверке конфликтов объектов
  • поиске пересекающихся зон интереса

Полное включение (within)

booleanWithin проверяет, находится ли один объект полностью внутри другого.

const smallPoly = turf.polygon([[
  [30.5, 50.5],
  [31, 50.5],
  [31, 51],
  [30.5, 51],
  [30.5, 50.5]
]]);

const isInside = turf.booleanWithin(smallPoly, poly);

Разница между within и pointInPolygon заключается в поддержке любых геометрий, а не только точек.

Комбинированная выборка

На практике пространственная выборка почти всегда комбинируется с атрибутивной.

const selected = points.features.filter(f => {
  return f.properties.type === "A" &&
         turf.booleanPointInPolygon(f, poly);
});

const result = turf.featureCollection(selected);

Такой подход формирует базовую модель пространственно-атрибутивного запроса.

Предварительная фильтрация через bounding box

Для оптимизации вычислений часто используется ограничивающий прямоугольник (bbox). Он позволяет быстро исключить заведомо неподходящие объекты до точных проверок.

const bbox = turf.bbox(poly);

const candidate = points.features.filter(f => {
  const [x, y] = f.geometry.coordinates;
  return x >= bbox[0] && x <= bbox[2] &&
         y >= bbox[1] && y <= bbox[3];
});

После этого выполняется точная проверка:

const final = candidate.filter(f =>
  turf.booleanPointInPolygon(f, poly)
);

Такой двухэтапный подход существенно снижает вычислительную нагрузку при больших наборах данных.

Выборка линий и полигонов

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

const roads = turf.featureCollection([
  turf.lineString([[30, 50], [31, 51]]),
  turf.lineString([[40, 60], [41, 61]])
]);

const clipped = roads.features.filter(f =>
  turf.booleanIntersects(f, poly)
);

Здесь происходит выбор всех линий, которые хотя бы частично попадают в заданную область.

Геометрическая инверсия условий

Часто требуется не только включение, но и исключение объектов:

const excluded = points.features.filter(f =>
  !turf.booleanPointInPolygon(f, poly)
);

Инверсия используется при:

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

Пространственная выборка с несколькими условиями

Сложные запросы строятся через комбинацию нескольких предикатов.

const selected = points.features.filter(f => {
  const inPolygon = turf.booleanPointInPolygon(f, poly);
  const isTypeA = f.properties.type === "A";
  const idRange = f.properties.id >= 10 && f.properties.id <= 100;

  return inPolygon && isTypeA && idRange;
});

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

Оптимизация массовой выборки

При работе с большими коллекциями критично уменьшать количество геометрических операций. Используются следующие подходы:

1. Предварительное разбиение данных

Разделение FeatureCollection по типам или регионам:

const grouped = points.features.reduce((acc, f) => {
  const key = f.properties.type;
  if (!acc[key]) acc[key] = [];
  acc[key].push(f);
  return acc;
}, {});

2. Использование bbox-фильтрации до точных проверок

Как промежуточный слой перед boolean* функциями.

3. Кэширование геометрий

Повторное использование полигонов без пересоздания объектов снижает накладные расходы.

Выборка ближайших объектов как частный случай

Хотя задача поиска ближайших объектов относится к ранжированию, она часто используется как форма выборки.

const from = turf.point([30, 50]);

const nearest = points.features
  .map(f => ({
    feature: f,
    dist: turf.distance(from, f)
  }))
  .sort((a, b) => a.dist - b.dist)
  .slice(0, 5)
  .map(x => x.feature);

Пайплайновая модель обработки

Выборка часто организуется как последовательность преобразований:

  1. первичная фильтрация по атрибутам
  2. bbox-отсечение
  3. точные пространственные проверки
  4. агрегация результата
const result = turf.featureCollection(
  points.features
    .filter(f => f.properties.type === "A")
    .filter(f => {
      const [x, y] = f.geometry.coordinates;
      return x > 29 && x < 35 && y > 49 && y < 53;
    })
    .filter(f => turf.booleanPointInPolygon(f, poly))
);

Работа с пересекающимися наборами данных

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

const result = [];

for (const a of collectionA.features) {
  for (const b of collectionB.features) {
    if (turf.booleanIntersects(a, b)) {
      result.push(a);
      break;
    }
  }
}

Такая модель применяется в задачах:

  • сопоставления маршрутов и зон
  • анализа конфликтов геометрий
  • пространственного джойна

Пространственные фильтры как основа анализа данных

Комбинация booleanPointInPolygon, booleanIntersects, booleanWithin и атрибутивных условий формирует универсальный механизм выборки, который заменяет специализированные запросы баз данных в прикладных сценариях обработки геоданных в JavaScript.