Создание плагинов

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

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


Модель расширения Turf.js

В Turf.js отсутствует жёстко формализованная система регистрации плагинов. Расширение реализуется через стандартные механизмы JavaScript-модулей:

  • подключение функций через ES Modules или CommonJS
  • агрегация в пользовательский namespace
  • публикация отдельных npm-пакетов
  • композиция функций через функциональный стиль

Плагин в контексте Turf.js — это модуль, который:

  • принимает GeoJSON Feature или FeatureCollection
  • использует внутренние утилиты Turf или повторяет их поведение
  • возвращает корректный GeoJSON-объект
  • не нарушает иммутабельность входных данных

Базовая структура плагина

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

import { featureCollection } from "@turf/helpers";
import bbox from "@turf/bbox";

/**
 * Пользовательская функция пространственной обработки
 */
export function customEnvelope(features) {
    const bounds = bbox(features);

    const [minX, minY, maxX, maxY] = bounds;

    return featureCollection([
        {
            type: "Feature",
            geometry: {
                type: "Polygon",
                coordinates: [[
                    [minX, minY],
                    [maxX, minY],
                    [maxX, maxY],
                    [minX, maxY],
                    [minX, minY]
                ]]
            },
            properties: {}
        }
    ]);
}

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


Использование внутренних утилит Turf

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

  • @turf/helpers — создание GeoJSON объектов
  • @turf/invariant — проверка типов входных данных
  • @turf/meta — итерация по координатам и геометриям
  • @turf/boolean-* — пространственные предикаты
  • @turf/distance, @turf/area — базовые измерения

Пример использования @turf/meta:

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

/**
 * Подсчёт общего количества координат во всех геометриях
 */
export function countCoordinates(geojson) {
    let count = 0;

    coordEach(geojson, () => {
        count += 1;
    });

    return count;
}

Использование coordEach обеспечивает корректную работу со всеми типами геометрий без необходимости ручного обхода вложенных структур.


Проверка входных данных

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

import { isGeoJSON } from "@turf/invariant";

/**
 * Проверка корректности входного объекта
 */
export function safeProcess(input) {
    if (!isGeoJSON(input)) {
        throw new Error("Некорректный GeoJSON объект");
    }

    return input;
}

Такая проверка предотвращает попадание в вычисления структур, не соответствующих спецификации GeoJSON.


Композиция функций как основа плагинов

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

import area from "@turf/area";
import centroid from "@turf/centroid";

/**
 * Обогащение геометрии метаданными
 */
export function enrichWithMetrics(feature) {
    const computedArea = area(feature);
    const center = centroid(feature);

    return {
        ...feature,
        properties: {
            ...feature.properties,
            area: computedArea,
            center: center.geometry.coordinates
        }
    };
}

Функциональная композиция позволяет создавать расширения без модификации исходных объектов библиотеки.


Организация собственного namespace

Для удобства распространения расширений часто формируется единый объект, имитирующий структуру Turf.

import * as helpers from "@turf/helpers";
import { customEnvelope } from "./customEnvelope";
import { enrichWithMetrics } from "./metrics";

export const myTurf = {
    ...helpers,
    customEnvelope,
    enrichWithMetrics
};

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

myTurf.customEnvelope(data);

Работа с FeatureCollection

Большинство плагинов ориентировано на обработку коллекций объектов. FeatureCollection является базовым контейнером в Turf.

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

/**
 * Фильтрация объектов по произвольному условию
 */
export function filterFeatures(collection, predicate) {
    const filtered = collection.features.filter(predicate);

    return featureCollection(filtered);
}

Сохранение структуры FeatureCollection критично для совместимости с другими функциями Turf.


Производительность и избегание мутаций

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

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

Пример оптимизированной обработки:

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

/**
 * Быстрое преобразование координат без создания промежуточных структур
 */
export function shiftCoordinates(geojson, dx, dy) {
    coordEach(geojson, (coord) => {
        coord[0] += dx;
        coord[1] += dy;
    });

    return geojson;
}

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


Поддержка TypeScript в плагинах

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

import { Feature, FeatureCollection, Polygon } from "geojson";

export function validatePolygon(
    feature: Feature<Polygon>
): boolean {
    return feature.geometry.type === "Polygon";
}

Типизация GeoJSON позволяет избежать ошибок на этапе компиляции и улучшает интеграцию с редакторами кода.


Пакетирование и публикация расширений

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

Типичная структура пакета:

turf-custom-plugin/
 ├── src/
 │   ├── index.js
 │   ├── metrics.js
 │   └── envelope.js
 ├── package.json
 ├── README.md

Основной файл экспорта:

export { customEnvelope } from "./envelope";
export { enrichWithMetrics } from "./metrics";

Совместимость с экосистемой Turf

Корректно реализованные расширения учитывают следующие принципы:

  • использование стандартных GeoJSON типов
  • сохранение структуры Feature и FeatureCollection
  • отсутствие скрытых зависимостей состояния
  • предсказуемое поведение при повторных вызовах
  • совместимость с tree-shaking при сборке

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