Написание собственных функций

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

Основой расширяемости является унифицированная модель данных GeoJSON и набор вспомогательных утилит из пакетов @turf/invariant, @turf/helpers, @turf/meta. Эти инструменты обеспечивают безопасное извлечение координат, нормализацию геометрий и итерацию по структурам разного уровня вложенности.


Базовые принципы написания функций Turf

Любая функция, совместимая с экосистемой Turf.js, опирается на несколько обязательных правил:

1. Единый входной формат Все входные данные представлены как GeoJSON:

  • Feature<Point>
  • Feature<LineString>
  • Feature<Polygon>
  • FeatureCollection

2. Чистота функции Отсутствие побочных эффектов. Функция не изменяет входные данные, а возвращает новый объект.

3. Возврат GeoJSON Результат всегда оформляется как валидный GeoJSON объект через @turf/helpers.

4. Поддержка коллекций Функции должны корректно обрабатывать как одиночные геометрии, так и коллекции.


Работа с нормализацией входных данных

В Turf.js входные данные приводятся к единому виду с помощью @turf/invariant.

Ключевые утилиты:

  • getCoord — извлечение координаты из Point
  • getCoords — извлечение массива координат
  • getGeom — получение геометрии
  • geojsonType — проверка типа GeoJSON
  • collectionOf — валидация FeatureCollection

Пример нормализации:

import { getGeom, geojsonType } from "@turf/invariant";

function example(feature) {
    geojsonType(feature, "Feature", "example");
    const geom = getGeom(feature);
    return geom;
}

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


Итерация по геометриям

Для обхода координат используются функции из @turf/meta:

  • coordEach
  • geomEach
  • featureEach
  • segmentEach

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

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

function sumCoordinates(feature) {
    let sum = 0;

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

    return sum;
}

Различия инструментов

  • coordEach — работа на уровне координат
  • geomEach — работа на уровне геометрий
  • featureEach — работа на уровне Feature
  • segmentEach — работа с сегментами линий

Шаблон собственной функции Turf

Типовая структура функции повторяет единый паттерн:

import { feature } from "@turf/helpers";
import { coordEach } from "@turf/meta";
import { getGeom, geojsonType } from "@turf/invariant";

function customOperation(input) {
    geojsonType(input, "Feature", "customOperation");

    const geom = getGeom(input);
    let resultValue = 0;

    coordEach(geom, (coord) => {
        resultValue += coord[0] * coord[1];
    });

    return feature(geom, resultValue);
}

export default customOperation;

Основные элементы шаблона:

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

Обработка различных типов геометрий

Point

Point содержит одну координату, обработка упрощается:

const [x, y] = getCoord(feature);

LineString

LineString требует работы с последовательностью точек:

coordEach(line, (coord, index) => {
    // обработка сегментов линии
});

Polygon

Polygon включает внешнее кольцо и внутренние отверстия:

coordEach(polygon, (coord, index, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) => {
    // учет колец и дыр
});

Работа с коллекциями объектов

FeatureCollection требует отдельной стратегии обработки:

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

function processCollection(fc) {
    let results = [];

    featureEach(fc, (feature) => {
        results.push(feature);
    });

    return featureCollection(results);
}

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


Использование пространственных вычислений

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

  • расстояние между точками
  • площадь полигона
  • вычисление bounding box
  • направление между координатами

Пример использования расстояния:

import distance from "@turf/distance";

function totalPathLength(line) {
    let total = 0;

    coordEach(line, (coord, index, coords) => {
        if (index > 0) {
            total += distance(coords[index - 1], coord);
        }
    });

    return total;
}

Валидация и безопасность входных данных

При разработке функций критично учитывать корректность GeoJSON.

Основные проверки:

  • тип объекта (Feature, FeatureCollection)
  • тип геометрии
  • наличие координат
  • корректная структура массива
import { geojsonType } from "@turf/invariant";

function safeFunction(input) {
    geojsonType(input, "FeatureCollection", "safeFunction");
    // дальнейшая обработка
}

Дополнительно может использоваться ручная проверка координат:

if (!Array.isArray(coord) || coord.length < 2) {
    return null;
}

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

При обработке крупных FeatureCollection критичны следующие аспекты:

1. Минимизация аллокаций Избегается создание промежуточных массивов внутри циклов.

2. Использование meta-итераторов coordEach и featureEach работают быстрее ручных рекурсий.

3. Ранний выход При поиске условий допускается прерывание обработки через исключения или флаги.

let found = false;

coordEach(feature, (coord) => {
    if (coord[0] > 1000) {
        found = true;
    }
});

Типизация и использование TypeScript

При расширении Turf.js в TypeScript определяется строгая сигнатура входов и выходов:

import { Feature, Geometry } from "geojson";

export default function custom(
    input: Feature<Geometry>
): Feature<Geometry | null> {
    return input;
}

Типизация помогает зафиксировать:

  • допустимые GeoJSON структуры
  • формат возвращаемого значения
  • ограничения по геометриям

Организация модульной структуры

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

/custom-function
  index.js
  index.d.ts
  test.js
  package.json

Стандартный экспорт:

export default function customFunction(input) {
    return input;
}

Такой формат обеспечивает совместимость с экосистемой Turf и позволяет интеграцию через npm-пакеты.


Использование вспомогательных функций Turf

Внутри собственных реализаций часто применяются утилиты:

  • @turf/helpers — создание Feature и FeatureCollection
  • @turf/meta — обход структур
  • @turf/invariant — проверка и нормализация
  • @turf/bbox — вычисление границ

Пример с bounding box:

import bbox from "@turf/bbox";

function envelopeArea(feature) {
    const [minX, minY, maxX, maxY] = bbox(feature);
    return (maxX - minX) * (maxY - minY);
}

Композиция функций

Сильной стороной Turf.js является композиция модулей. Пользовательские функции часто строятся как цепочки операций:

function composed(input) {
    const step1 = transform(input);
    const step2 = filter(step1);
    return aggregate(step2);
}

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