Фильтрация по пространственным критериям

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

Библиотека Turf.js предоставляет широкий набор инструментов для выполнения таких операций непосредственно в браузере или на сервере Node.js, работая с объектами формата GeoJSON.


Задачи пространственной фильтрации

На практике пространственная фильтрация применяется для решения следующих задач:

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

Для решения подобных задач Turf.js предоставляет специализированные функции пространственного анализа.


Проверка принадлежности точки полигону

Одной из самых распространённых операций является определение того, находится ли точка внутри полигона.

Для этого используется функция booleanPointInPolygon().

Создание исходных данных

import * as turf from "@turf/turf";

const point = turf.point([37.62, 55.75]);

const polygon = turf.polygon([[
    [37.50, 55.70],
    [37.80, 55.70],
    [37.80, 55.85],
    [37.50, 55.85],
    [37.50, 55.70]
]]);

Проверка попадания

const result = turf.booleanPointInPolygon(point, polygon);

console.log(result);

Результат:

true

Если точка находится внутри границ полигона, функция возвращает true, иначе — false.


Фильтрация коллекции точек по полигону

Часто необходимо проверить сразу большое количество объектов.

Исходная коллекция:

const points = turf.featureCollection([
    turf.point([37.60, 55.76], { name: "A" }),
    turf.point([37.90, 55.76], { name: "B" }),
    turf.point([37.70, 55.80], { name: "C" })
]);

Фильтрация:

const filtered = points.features.filter(feature =>
    turf.booleanPointInPolygon(feature, polygon)
);

console.log(filtered);

В результирующий массив попадут только точки, расположенные внутри полигона.

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


Проверка пересечения геометрий

Для определения факта пересечения используется функция booleanIntersects().

Пример с полигонами

const polygon1 = turf.polygon([[
    [0, 0],
    [5, 0],
    [5, 5],
    [0, 5],
    [0, 0]
]]);

const polygon2 = turf.polygon([[
    [3, 3],
    [8, 3],
    [8, 8],
    [3, 8],
    [3, 3]
]]);

Проверка:

const intersects = turf.booleanIntersects(
    polygon1,
    polygon2
);

console.log(intersects);

Результат:

true

Пересечение означает наличие хотя бы одной общей точки между объектами.


Фильтрация объектов по пересечению

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

const parcels = [
    parcel1,
    parcel2,
    parcel3,
    parcel4
];

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

const filteredParcels = parcels.filter(parcel =>
    turf.booleanIntersects(parcel, constructionZone)
);

После выполнения операции остаются исключительно объекты, имеющие пространственный контакт с заданной территорией.


Проверка полного вхождения объекта

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

Для этого применяется функция booleanWithin().

Пример

const building = turf.polygon([[
    [1, 1],
    [2, 1],
    [2, 2],
    [1, 2],
    [1, 1]
]]);

const district = turf.polygon([[
    [0, 0],
    [5, 0],
    [5, 5],
    [0, 5],
    [0, 0]
]]);

Проверка:

const isInside = turf.booleanWithin(
    building,
    district
);

console.log(isInside);

Результат:

true

Полезно при определении объектов, полностью принадлежащих административной территории.


Проверка включения геометрий

Обратной операцией является определение того, содержит ли одна геометрия другую.

Используется функция booleanContains().

const result = turf.booleanContains(
    district,
    building
);

console.log(result);

Результат:

true

Несмотря на схожесть с booleanWithin(), направление проверки имеет принципиальное значение.


Проверка пересечения границ

В некоторых задачах требуется определить факт соприкосновения объектов.

Для этого существует функция booleanTouches().

Пример

const polyA = turf.polygon([[
    [0, 0],
    [4, 0],
    [4, 4],
    [0, 4],
    [0, 0]
]]);

const polyB = turf.polygon([[
    [4, 1],
    [6, 1],
    [6, 3],
    [4, 3],
    [4, 1]
]]);

Проверка:

const touches = turf.booleanTouches(
    polyA,
    polyB
);

Результат:

true

Объекты имеют общую границу, но не перекрывают площади друг друга.


Проверка перекрытия площадей

Для анализа наложения объектов используется функция booleanOverlap().

const overlap = turf.booleanOverlap(
    polygon1,
    polygon2
);

console.log(overlap);

Результат:

true

Перекрытие означает наличие общей области ненулевой площади.

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


Фильтрация по расстоянию

Частой задачей является поиск объектов в пределах определённого радиуса.

Для вычисления расстояния применяется функция distance().

Исходные данные

const center = turf.point([37.62, 55.75]);

const stores = [
    turf.point([37.63, 55.76], { name: "Store A" }),
    turf.point([37.80, 55.80], { name: "Store B" }),
    turf.point([37.61, 55.74], { name: "Store C" })
];

Фильтрация

const nearbyStores = stores.filter(store => {
    const dist = turf.distance(
        center,
        store,
        { units: "kilometers" }
    );

    return dist <= 2;
});

В результате будут получены объекты, расположенные не далее двух километров от центра.


Использование буферных зон

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

Буфер представляет собой область на фиксированном расстоянии от исходной геометрии.

Создание буфера

const buffer = turf.buffer(
    center,
    2,
    { units: "kilometers" }
);

Буфер будет представлен полигоном.

Проверка объектов

const result = stores.filter(store =>
    turf.booleanPointInPolygon(
        store,
        buffer
    )
);

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


Поиск точек внутри буферной зоны

Полный пример:

const station = turf.point([37.62, 55.75]);

const stationBuffer = turf.buffer(
    station,
    1,
    { units: "kilometers" }
);

const nearbyPoints = points.features.filter(point =>
    turf.booleanPointInPolygon(
        point,
        stationBuffer
    )
);

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


Отбор объектов внутри ограничивающего прямоугольника

Для ускорения пространственных запросов часто используется ограничивающий прямоугольник (Bounding Box).

Создание рамки:

const bbox = [
    37.50,
    55.70,
    37.80,
    55.85
];

Преобразование в полигон:

const bboxPolygon = turf.bboxPolygon(bbox);

Фильтрация:

const filtered = points.features.filter(point =>
    turf.booleanPointInPolygon(
        point,
        bboxPolygon
    )
);

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


Использование функции pointsWithinPolygon

Для работы с большими коллекциями точек Turf.js предоставляет специализированную функцию.

Исходные данные

const points = turf.featureCollection([
    turf.point([1, 1]),
    turf.point([2, 2]),
    turf.point([10, 10])
]);

Поиск точек внутри полигона

const result = turf.pointsWithinPolygon(
    points,
    polygon
);

Возвращается новая коллекция GeoJSON, содержащая только подходящие объекты.

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

  • компактный код;
  • работа напрямую с FeatureCollection;
  • отсутствие ручного перебора;
  • высокая читаемость.

Пространственная фильтрация линейных объектов

Дороги, маршруты и трубопроводы обычно представлены линиями.

Проверка пересечения линии и полигона:

const intersects = turf.booleanIntersects(
    road,
    district
);

Фильтрация набора дорог:

const roadsInsideDistrict = roads.filter(road =>
    turf.booleanIntersects(
        road,
        district
    )
);

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


Пространственная фильтрация полигонов

Для коллекции полигонов принцип остаётся тем же.

const selectedAreas = areas.filter(area =>
    turf.booleanWithin(
        area,
        region
    )
);

Либо:

const selectedAreas = areas.filter(area =>
    turf.booleanIntersects(
        area,
        region
    )
);

Выбор функции зависит от требований задачи:

Функция Назначение
booleanWithin Полное нахождение внутри
booleanContains Полное включение
booleanIntersects Любое пересечение
booleanTouches Касание границ
booleanOverlap Перекрытие площадей

Комбинированная пространственная фильтрация

Нередко требуется учитывать несколько критериев одновременно.

Пример: объекты должны находиться внутри района и располагаться не дальше одного километра от станции метро.

const filtered = objects.filter(object => {

    const insideDistrict =
        turf.booleanPointInPolygon(
            object,
            district
        );

    const distanceToMetro =
        turf.distance(
            object,
            metroStation,
            { units: "kilometers" }
        );

    return insideDistrict &&
           distanceToMetro <= 1;
});

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


Оптимизация пространственной фильтрации

При работе с большими объёмами данных важно учитывать производительность.

Наиболее распространённые методы оптимизации:

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

Сначала выполняется грубый отбор по ограничивающему прямоугольнику, затем точная геометрическая проверка.

const bboxPolygon = turf.bboxPolygon(
    turf.bbox(region)
);

После этого:

const candidates = objects.filter(obj =>
    turf.booleanIntersects(
        obj,
        bboxPolygon
    )
);

И только затем:

const result = candidates.filter(obj =>
    turf.booleanWithin(
        obj,
        region
    )
);

Использование специализированных функций

Предпочтительно применять:

turf.pointsWithinPolygon()

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

Минимизация повторных вычислений

Буферы, полигоны и Bounding Box желательно вычислять один раз и переиспользовать.

Плохо:

objects.filter(obj =>
    turf.booleanPointInPolygon(
        obj,
        turf.buffer(center, 1)
    )
);

Хорошо:

const zone = turf.buffer(
    center,
    1,
    { units: "kilometers" }
);

objects.filter(obj =>
    turf.booleanPointInPolygon(
        obj,
        zone
    )
);

Типичный сценарий пространственного отбора

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

  1. Загрузка GeoJSON-объектов.
  2. Формирование области интереса.
  3. Создание буфера или Bounding Box.
  4. Предварительная фильтрация кандидатов.
  5. Выполнение точных пространственных проверок.
  6. Формирование итоговой коллекции.
  7. Передача результатов на карту или в аналитический модуль.

Подобный конвейер позволяет эффективно обрабатывать десятки и сотни тысяч пространственных объектов средствами Turf.js без использования полноценных серверных ГИС-систем.