Работа с нестандартными форматами

Работа с геопространственными данными в Turf.js неизбежно выходит за пределы идеального GeoJSON-мира, в котором каждый объект строго соответствует спецификации RFC 7946. Реальные данные приходят в смешанных форматах, с нарушенной структурой, дополнительными координатными измерениями, нестандартными системами координат и частично повреждёнными геометриями. Именно работа с такими представлениями определяет устойчивость геообработки.


GeoJSON задаёт строгую модель: координаты представлены как массивы чисел [lon, lat], иногда с дополнительными измерениями [lon, lat, alt]. На практике встречаются отклонения:

  • координаты в виде строк "37.61, 55.75"
  • порядок [lat, lon] вместо [lon, lat]
  • наличие временного компонента [lon, lat, timestamp]
  • вложенные структуры с разной глубиной
  • частично отсутствующие значения null, undefined

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


Нормализация входных данных

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

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

Типичная функция нормализации координат:

function normalizeCoord(coord) {
  if (typeof coord === "string") {
    const [a, b] = coord.split(",").map(Number);
    return [a, b];
  }

  if (Array.isArray(coord)) {
    return coord.slice(0, 2).map(Number);
  }

  return null;
}

После нормализации данные можно безопасно передавать в Turf-операции вроде turf.point, turf.lineString или turf.polygon.


Работа с дополнительными измерениями (Z и M координаты)

GeoJSON допускает расширенные координаты:

  • [lon, lat, alt] — высота
  • [lon, lat, m] — мера (M-значение)
  • [lon, lat, alt, m] — комбинированный формат

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

При необходимости сохранения Z-значений применяется стратегия сохранения метаданных:

function stripZKeepMeta(coord) {
  return {
    xy: [coord[0], coord[1]],
    z: coord.length > 2 ? coord[2] : null
  };
}

Это важно при моделировании рельефа, 3D-трассировке маршрутов и гидрологических данных, где высота влияет на анализ, но не поддерживается напрямую в геометрических функциях Turf.


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

Нестандартные данные часто содержат ошибки:

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

Turf предоставляет инструменты для диагностики и частичного исправления:

  • turf.cleanCoords — удаление лишних точек
  • turf.booleanValid — проверка валидности геометрии
  • turf.truncate — округление координат

Пример фильтрации:

function validateLine(line) {
  const cleaned = turf.cleanCoords(line);

  if (cleaned.geometry.coordinates.length < 2) {
    return null;
  }

  return cleaned;
}

Работа с WKT (Well-Known Text)

WKT не является нативным форматом GeoJSON, но широко используется в GIS-системах. Типичные примеры:

POINT (30 10)
LINESTRING (30 10, 10 30, 40 40)
POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))

Для интеграции WKT-данных применяется промежуточное преобразование:

function wktPointToGeoJSON(wkt) {
  const match = wkt.match(/POINT\s*\(([^)]+)\)/);
  const [x, y] = match[1].split(" ").map(Number);

  return turf.point([x, y]);
}

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


KML и внешние GIS-форматы

KML (Keyhole Markup Language) часто содержит:

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

Перед использованием в Turf данные приводятся к GeoJSON через промежуточные библиотеки (например, togeojson). После конвертации важно:

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

Обработка FeatureCollection нестандартной структуры

Типичная проблема — смешение типов объектов:

{
  "type": "FeatureCollection",
  "features": [
    { "type": "Feature", "geometry": { "type": "Point" } },
    { "type": "Feature", "geometry": null },
    { "type": "Feature", "geometry": { "type": "LineString", "coordinates": [] } }
  ]
}

Фильтрация:

function filterValidFeatures(fc) {
  return {
    ...fc,
    features: fc.features.filter(f =>
      f.geometry &&
      f.geometry.coordinates &&
      f.geometry.coordinates.length > 0
    )
  };
}

Работа с «грязными» координатами

В реальных наборах данных часто встречаются:

  • NaN значения
  • бесконечности
  • нулевые координаты вне контекста
  • дубликаты точек

Очистка выполняется через последовательные шаги:

function sanitizeCoords(coords) {
  return coords
    .filter(c => Array.isArray(c))
    .map(c => c.map(Number))
    .filter(c => c.every(n => Number.isFinite(n)));
}

После этого данные становятся пригодными для turf.lineString и turf.polygon.


Работа с разными системами координат

Turf.js работает исключительно в WGS84 (EPSG:4326). Поэтому любые входные данные из других систем координат требуют предварительного преобразования.

Типичный сценарий:

  • EPSG:3857 (Web Mercator) — данные картографических сервисов
  • EPSG:326xx — UTM зоны
  • локальные проекции

Преобразование выполняется через внешние библиотеки:

  • proj4
  • arcgis-to-geojson-utils

Пример:

function toWGS84([x, y]) {
  return proj4("EPSG:3857", "EPSG:4326", [x, y]);
}

После трансформации данные становятся совместимыми с Turf-операциями.


Потоковая обработка больших нестандартных наборов

При работе с большими файлами возникают проблемы:

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

Используется потоковая модель:

  • разбиение на чанки
  • обработка по одному Feature
  • агрегация результатов
function processStream(features, fn) {
  const results = [];

  for (const feature of features) {
    if (!feature.geometry) continue;
    results.push(fn(feature));
  }

  return results;
}

Объединение разноформатных источников

Часто данные приходят одновременно из:

  • API картографических сервисов
  • CSV таблиц с координатами
  • GPS треков
  • пользовательских вводов

Унификация требует приведения всех источников к Feature:

function toFeature(input) {
  if (Array.isArray(input)) {
    return turf.point(input);
  }

  if (input.lat && input.lon) {
    return turf.point([input.lon, input.lat]);
  }

  return null;
}

Обработка смешанных геометрий

Нестандартные наборы могут содержать:

  • MultiPoint + Point
  • LineString с разной детализацией
  • Polygon с разным количеством колец

Перед анализом выполняется группировка по типу:

function groupByGeometry(fc) {
  return fc.features.reduce((acc, f) => {
    const type = f.geometry.type;
    acc[type] = acc[type] || [];
    acc[type].push(f);
    return acc;
  }, {});
}

Это позволяет применять разные Turf-функции к каждому классу объектов.


Работа с повреждёнными JSON-структурами

Геоданные часто приходят с ошибками сериализации:

  • лишние запятые
  • обрезанные строки
  • некорректные escape-последовательности

Решение заключается в предварительной валидации и мягком парсинге:

function safeParse(json) {
  try {
    return JSON.parse(json);
  } catch (e) {
    return null;
  }
}

Итоговая модель обработки нестандартных данных

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

  1. Приём данных из произвольного источника
  2. Приведение к координатному массиву
  3. Очистка и фильтрация
  4. Нормализация GeoJSON структуры
  5. Преобразование CRS (если требуется)
  6. Применение Turf-операций
  7. Постобработка результата

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