Трансформация геометрии

Трансформация геометрии — это процесс изменения координат объектов на карте с целью изменения их положения, размеров, ориентации или системы координат. В библиотеке OpenLayers трансформации применяются как к отдельным геометрическим объектам, так и к наборам объектов, представленным слоями и источниками данных.

К основным операциям трансформации относятся:

  • перемещение (Translate);
  • масштабирование (Scale);
  • вращение (Rotate);
  • преобразование проекций (Projection Transform);
  • применение пользовательских матриц преобразования;
  • интерактивное редактирование трансформаций через элементы управления.

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


Геометрические объекты OpenLayers

Все пространственные объекты наследуются от базового класса Geometry.

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

import Point from 'ol/geom/Point';
import LineString from 'ol/geom/LineString';
import Polygon from 'ol/geom/Polygon';

Примеры создания объектов:

const point = new Point([30, 50]);

const line = new LineString([
    [10, 10],
    [20, 20],
    [30, 15]
]);

const polygon = new Polygon([
    [
        [0, 0],
        [100, 0],
        [100, 100],
        [0, 100],
        [0, 0]
    ]
]);

Каждый объект содержит набор координат, которые могут быть изменены при помощи трансформаций.


Получение и изменение координат

Перед выполнением трансформации полезно понимать, как работают координаты.

Получение координат точки:

const coordinates = point.getCoordinates();

console.log(coordinates);

Изменение координат:

point.setCoordinates([100, 200]);

Для сложных геометрий:

const coords = polygon.getCoordinates();

polygon.setCoordinates(coords);

Практически все трансформации внутри OpenLayers сводятся к изменению координатных массивов.


Перемещение геометрии

Метод translate()

Для смещения объекта используется метод translate().

Синтаксис:

geometry.translate(deltaX, deltaY);

Где:

  • deltaX — смещение по оси X;
  • deltaY — смещение по оси Y.

Пример:

const point = new Point([100, 100]);

point.translate(50, 25);

console.log(point.getCoordinates());

Результат:

[150, 125]

Геометрия перемещается без изменения размеров и формы.


Перемещение линии

const line = new LineString([
    [0, 0],
    [10, 10],
    [20, 20]
]);

line.translate(100, 50);

После выполнения:

[
    [100, 50],
    [110, 60],
    [120, 70]
]

Каждая вершина получает одинаковое смещение.


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

polygon.translate(500, 300);

Все вершины полигона будут перемещены на одинаковое расстояние.


Масштабирование геометрии

Метод scale()

Позволяет изменять размеры объекта.

Синтаксис:

geometry.scale(scaleX, scaleY);

Пример:

polygon.scale(2, 2);

Полигон станет в два раза больше относительно центра.


Масштабирование по одной оси

polygon.scale(2, 1);

Результат:

  • ширина увеличится в два раза;
  • высота останется прежней.

Такой приём часто используется для создания эффекта растяжения объекта.


Масштабирование относительно произвольной точки

По умолчанию масштабирование выполняется относительно центра геометрии.

Можно указать собственную точку привязки:

polygon.scale(
    1.5,
    1.5,
    [0, 0]
);

Третий аргумент задаёт координаты центра трансформации.


Пример увеличения прямоугольника

Исходный объект:

const rectangle = new Polygon([
    [
        [0, 0],
        [100, 0],
        [100, 50],
        [0, 50],
        [0, 0]
    ]
]);

Увеличение:

rectangle.scale(3, 3);

Размеры увеличатся в три раза относительно центра объекта.


Вращение геометрии

Метод rotate()

Синтаксис:

geometry.rotate(angle, anchor);

Параметры:

  • angle — угол в радианах;
  • anchor — центр вращения.

Поворот точки

const point = new Point([100, 0]);

point.rotate(
    Math.PI / 2,
    [0, 0]
);

Поворот на 90 градусов вокруг начала координат.


Поворот линии

line.rotate(
    Math.PI / 4,
    [0, 0]
);

Угол:

Math.PI / 4

соответствует 45 градусам.


Таблица перевода градусов в радианы

Градусы Радианы
45° π / 4
90° π / 2
180° π
270° 3π / 2
360°

Поскольку OpenLayers использует радианы, преобразование градусов часто выполняется так:

const radians = degrees * Math.PI / 180;

Вращение полигона вокруг центра

Получение центра:

import {getCenter} from 'ol/extent';

const center = getCenter(
    polygon.getExtent()
);

Вращение:

polygon.rotate(
    Math.PI / 6,
    center
);

Полигон повернётся на 30 градусов вокруг своего центра.


Комбинирование трансформаций

Трансформации можно выполнять последовательно.

polygon.translate(100, 100);

polygon.scale(2, 2);

polygon.rotate(
    Math.PI / 3,
    [100, 100]
);

Порядок операций имеет большое значение.

Например:

translate → rotate

и

rotate → translate

могут давать совершенно разные результаты.


Трансформация через изменение координат

Иногда встроенных методов недостаточно.

В этом случае координаты изменяются вручную.

Пример:

const coords = point.getCoordinates();

coords[0] += 50;
coords[1] += 30;

point.setCoordinates(coords);

Изменение всех вершин линии

const coords = line.getCoordinates();

const transformed = coords.map(coord => [
    coord[0] + 100,
    coord[1] + 50
]);

line.setCoordinates(transformed);

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


Метод applyTransform()

Для сложных преобразований используется метод applyTransform().

Синтаксис:

geometry.applyTransform(callback);

Функция получает массив координат и изменяет их.


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

point.applyTransform(function(flatCoords) {

    for (let i = 0; i < flatCoords.length; i += 2) {

        flatCoords[i] += 100;

        flatCoords[i + 1] += 50;
    }
});

Получается эквивалент операции перемещения.


Нелинейная трансформация

line.applyTransform(function(flatCoords) {

    for (let i = 0; i < flatCoords.length; i += 2) {

        flatCoords[i + 1] =
            flatCoords[i + 1] +
            Math.sin(flatCoords[i]) * 20;
    }
});

Результатом станет волнообразное искажение линии.


Трансформация проекций

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

OpenLayers содержит встроенные средства работы с проекциями.


Импорт функции transform

import {transform} from 'ol/proj';

Преобразование координат

Из географической системы WGS84:

EPSG:4326

в веб-проекцию:

EPSG:3857
const result = transform(
    [37.6176, 55.7558],
    'EPSG:4326',
    'EPSG:3857'
);

Обратное преобразование

const coordinates = transform(
    projected,
    'EPSG:3857',
    'EPSG:4326'
);

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


Трансформация геометрии между проекциями

Метод transform() имеется и у самой геометрии.

Пример:

point.transform(
    'EPSG:4326',
    'EPSG:3857'
);

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


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

polygon.transform(
    'EPSG:4326',
    'EPSG:3857'
);

Все вершины будут автоматически пересчитаны.


Клонирование перед трансформацией

Трансформации изменяют исходный объект.

Для сохранения оригинала используется клонирование.

const copy = polygon.clone();

copy.scale(2, 2);

Исходный полигон останется без изменений.


Пример работы с копией

const original = feature.getGeometry();

const transformed =
    original.clone();

transformed.rotate(
    Math.PI / 4,
    [0, 0]
);

Получаются две независимые версии одной геометрии.


Трансформация объектов Feature

Геометрия обычно находится внутри объекта Feature.

const geometry =
    feature.getGeometry();

После этого можно применять любые преобразования:

geometry.translate(
    200,
    100
);

или

geometry.rotate(
    Math.PI / 2,
    [0, 0]
);

Карта автоматически отобразит изменения.


Интерактивная трансформация объектов

Для взаимодействия пользователя с объектами используются специальные взаимодействия OpenLayers.

Наиболее часто применяются:

  • Select;
  • Modify;
  • Translate.

Перемещение объекта мышью

Подключение взаимодействия:

import Translate from 'ol/interaction/Translate';
import Sel ect from 'ol/interaction/Select';

Создание выбора:

const select =
    new Select();

Создание перемещения:

const translate =
    new Translate({
        features:
            select.getFeatures()
    });

Добавление на карту:

map.addInteraction(select);

map.addInteraction(
    translate
);

Теперь выбранный объект можно перетаскивать мышью.


События трансформации

Взаимодействие Translate генерирует события.

Начало перемещения:

translate.on(
    'translatestart',
    event => {

        console.log(
            'Начало'
        );
    }
);

Процесс перемещения:

translate.on(
    'translating',
    event => {

        console.log(
            'Перемещение'
        );
    }
);

Завершение:

translate.on(
    'translateend',
    event => {

        console.log(
            'Завершено'
        );
    }
);

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


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

При работе с большим количеством объектов рекомендуется:

  • избегать постоянного клонирования геометрий;
  • объединять несколько преобразований в одну операцию;
  • использовать applyTransform() для массовой обработки координат;
  • выполнять трансформации до добавления объектов на карту;
  • минимизировать количество перерисовок слоя.

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


Практический пример комплексной трансформации

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

const polygon = new Polygon([
    [
        [0, 0],
        [50, 0],
        [50, 50],
        [0, 50],
        [0, 0]
    ]
]);

Получение центра:

import {getCenter} fr om 'ol/extent';

const center =
    getCenter(
        polygon.getExtent()
    );

Последовательность преобразований:

polygon.translate(
    100,
    100
);

polygon.scale(
    1.5,
    1.5
);

polygon.rotate(
    Math.PI / 4,
    center
);

В результате объект:

  • переместится на новую позицию;
  • увеличится в размере;
  • повернётся на 45 градусов.

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