Отражение объектов

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

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

Представление геометрии в Turf.js

Все операции выполняются над объектами GeoJSON:

  • Point
  • LineString
  • Polygon
  • Feature
  • FeatureCollection

Каждый объект рассматривается как набор координат в плоской декартовой системе. Turf.js предоставляет набор утилит для трансформации:

  • @turf/helpers
  • @turf/transform-translate
  • @turf/transform-rotate
  • @turf/transform-scale
  • @turf/bbox
  • @turf/center

Геометрия трактуется как неизменяемая структура, поэтому каждая операция возвращает новый объект.

Отражение относительно осей координат

Самый простой случай — отражение относительно оси X или Y. В терминах координат:

  • относительно оси X: (x, y) → (x, -y)
  • относительно оси Y: (x, y) → (-x, y)

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

Масштабирование как базовый механизм отражения

Функция transformScale позволяет выполнять инверсию координат через отрицательные коэффициенты:

import { transformScale } from "@turf/transform-scale";

const mirrored = transformScale(feature, -1, -1);

Такое преобразование выполняет центральную симметрию относительно центра масштабирования. Важный аспект — центр операции.

По умолчанию используется центр bounding box, что определяется через:

  • bbox(feature)
  • вычисление центра прямоугольника

Это приводит к отражению относительно геометрического центра, а не начала координат.

Управление центром отражения

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

import { transformTranslate } from "@turf/transform-translate";
import { transformScale } from "@turf/transform-scale";
import { center } from "@turf/center";

const c = center(feature);

const moved = transformTranslate(feature, -c.geometry.coordinates[0], -c.geometry.coordinates[1]);
const scaled = transformScale(moved, -1, -1);
const restored = transformTranslate(scaled, c.geometry.coordinates[0], c.geometry.coordinates[1]);

Такой подход позволяет зафиксировать центр отражения в произвольной точке.

Отражение относительно оси X или Y через масштабирование

Центральная симметрия не всегда эквивалентна осевому отражению. Для имитации отражения относительно оси X или Y применяется раздельное масштабирование координат.

Отражение относительно оси Y

Инверсия X-координаты:

const mirrorY = transformScale(feature, -1, 1);

Отражение относительно оси X

Инверсия Y-координаты:

const mirrorX = transformScale(feature, 1, -1);

Эти операции корректны только при условии, что ось проходит через центр трансформации.

Ограничения масштабного отражения

Использование transformScale имеет ряд особенностей:

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

Для точного контроля требуется комбинирование трансформаций.

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

Наиболее общий случай — отражение относительно линии вида Ax + By + C = 0. Turf.js не предоставляет прямой функции, поэтому применяется композиция преобразований:

  1. Перенос линии в начало координат
  2. Поворот линии к оси X
  3. Отражение по оси Y
  4. Обратный поворот
  5. Обратный перенос

Алгоритм композиции трансформаций

Пусть задана линия отражения. Тогда последовательность операций:

1. Перенос системы координат

const translated = transformTranslate(feature, -cx, -cy);

где (cx, cy) — точка на линии отражения.

2. Поворот линии к оси X

const rotated = transformRotate(translated, -angle);

Угол определяется направлением линии:

const angle = Math.atan2(y2 - y1, x2 - x1);

3. Отражение относительно оси X через масштабирование

const mirrored = transformScale(rotated, 1, -1);

4. Обратный поворот

const unrotated = transformRotate(mirrored, angle);

5. Возврат в исходную систему координат

const finalResult = transformTranslate(unrotated, cx, cy);

Полная реализация отражения относительно линии

import { transformTranslate } from "@turf/transform-translate";
import { transformRotate } from "@turf/transform-rotate";
import { transformScale } from "@turf/transform-scale";

function reflectAcrossLine(feature, x1, y1, x2, y2) {
  const cx = x1;
  const cy = y1;

  const angle = Math.atan2(y2 - y1, x2 - x1);

  const translated = transformTranslate(feature, -cx, -cy);
  const rotated = transformRotate(translated, -angle);
  const mirrored = transformScale(rotated, 1, -1);
  const unrotated = transformRotate(mirrored, angle);
  const restored = transformTranslate(unrotated, cx, cy);

  return restored;
}

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

При работе с Polygon и MultiPolygon важно учитывать:

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

Для FeatureCollection применяется итерация:

collection.features.map(f => reflectAcrossLine(f, x1, y1, x2, y2));

Использование bbox как опорной системы

В некоторых сценариях отражение выполняется относительно границ объекта:

import bbox from "@turf/bbox";

const [minX, minY, maxX, maxY] = bbox(feature);

Центр вычисляется как:

  • (minX + maxX) / 2
  • (minY + maxY) / 2

Это позволяет стабилизировать отражение в пределах геометрического контура.

Численные особенности и точность

При многократных трансформациях возникают эффекты:

  • накопление погрешности floating point
  • смещение координат при повторных rotate/scale
  • деградация геометрии при большом количестве вершин

Для минимизации ошибок применяется:

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

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

Стоимость операции линейно зависит от числа вершин:

  • O(n) для LineString
  • O(n log n) в сложных композициях FeatureCollection
  • дополнительные накладные расходы при rotate (тригонометрия)

Оптимизация достигается:

  • предварительным упрощением геометрии
  • батчевой обработкой FeatureCollection
  • исключением лишних translate/rotate цепочек

Комбинирование отражения с другими преобразованиями

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

  • transformTranslate для позиционирования
  • transformRotate для выравнивания объектов
  • transformScale для изменения масштаба сцены

Композиция формирует полноценную аффинную матрицу преобразований, эквивалентную:

M = T × R × S × R⁻¹ × T⁻¹

где каждая операция соответствует шагу отражения относительно произвольной оси.