Draw interaction для рисования

Назначение и роль Draw interaction

Draw — это базовый интерактивный механизм в OpenLayers, предназначенный для создания геометрий на карте в режиме пользовательского ввода. Он относится к классу интеракций (ol/interaction/Interaction) и работает совместно с векторным источником данных (ol/source/Vector), куда записываются создаваемые объекты.

Ключевая особенность Draw заключается в том, что он не просто фиксирует координаты, а формирует полноценные геометрические объекты: точки, линии, полигоны, окружности и мультигеометрии. При этом процесс рисования синхронизирован с визуальным представлением на карте.


Подключение и базовая структура

Для работы Draw необходимы три компонента:

  • карта (Map)
  • векторный слой (VectorLayer)
  • векторный источник (VectorSource)
  • интеракция Draw
import Map from 'ol/Map';
import View from 'ol/View';
import Draw from 'ol/interaction/Draw';
import VectorSource from 'ol/source/Vector';
import VectorLayer from 'ol/layer/Vector';

const source = new VectorSource();

const vector = new VectorLayer({
  source: source
});

const map = new Map({
  target: 'map',
  layers: [vector],
  view: new View({
    center: [0, 0],
    zoom: 2
  })
});

Создание Draw interaction

Минимальная конфигурация Draw требует указания типа геометрии через параметр type.

import Draw from 'ol/interaction/Draw';

const draw = new Draw({
  source: source,
  type: 'Point'
});

map.addInteraction(draw);

Доступные типы:

  • Point
  • LineString
  • Polygon
  • Circle
  • MultiPoint
  • MultiLineString
  • MultiPolygon
  • GeometryCollection

Жизненный цикл рисования

Процесс создания геометрии состоит из нескольких фаз:

  1. Инициализация интеракции
  2. drawstart — начало рисования
  3. Добавление вершин при движении мыши
  4. Фиксация вершин кликом
  5. drawend — завершение
draw.on('drawstart', function (event) {
  const feature = event.feature;
  console.log('Начато рисование:', feature);
});

draw.on('drawend', function (event) {
  const feature = event.feature;
  console.log('Завершено рисование:', feature);
});

События позволяют интегрировать Draw в бизнес-логику: вычисление площади, сохранение в базе, валидация.


Рисование линий и полигонов

LineString

const drawLine = new Draw({
  source: source,
  type: 'LineString'
});

map.addInteraction(drawLine);

Polygon

const drawPolygon = new Draw({
  source: source,
  type: 'Polygon'
});

map.addInteraction(drawPolygon);

Polygon автоматически замыкается при завершении.


Ограничение количества точек

Для контроля геометрии можно использовать параметры:

  • minPoints
  • maxPoints
const draw = new Draw({
  source: source,
  type: 'LineString',
  maxPoints: 5
});

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


Freehand-режим

Режим свободного рисования позволяет создавать геометрию непрерывным движением мыши.

const draw = new Draw({
  source: source,
  type: 'LineString',
  freehand: true
});

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

import { shiftKeyOnly } from 'ol/events/condition';

const draw = new Draw({
  source: source,
  type: 'Polygon',
  freehandCondition: shiftKeyOnly
});

Геометрия Circle

Circle в OpenLayers — особый тип геометрии, который хранится как центр и радиус.

const drawCircle = new Draw({
  source: source,
  type: 'Circle'
});

При завершении создаётся объект ol/geom/Circle, который может быть преобразован в полигон при экспорте.


geometryFunction и кастомизация формы

geometryFunction позволяет переопределить стандартную логику построения геометрии.

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

import { createBox } from 'ol/interaction/Draw';

const drawBox = new Draw({
  source: source,
  type: 'Circle',
  geometryFunction: createBox()
});

map.addInteraction(drawBox);

Также можно реализовать собственную функцию:

const draw = new Draw({
  source: source,
  type: 'LineString',
  geometryFunction: function (coordinates, geometry) {
    if (!geometry) {
      geometry = new LineString([]);
    }

    geometry.setCoordinates(coordinates);
    return geometry;
  }
});

Условия активации (condition)

Draw может активироваться только при выполнении определённых условий (например, нажатие клавиш).

import { altKeyOnly } from 'ol/events/condition';

const draw = new Draw({
  source: source,
  type: 'Polygon',
  condition: altKeyOnly
});

Это полезно при совместном использовании с другими интеракциями (панорамирование, выбор объектов).


Валидация и контроль геометрии

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

draw.on('drawstart', function (event) {
  const feature = event.feature;
  const geometry = feature.getGeometry();

  geometry.on('change', function () {
    const coords = geometry.getCoordinates();
    console.log(coords);
  });
});

Это позволяет реализовать:

  • ограничение площади
  • контроль длины
  • запрет самопересечений (логика внешняя)
  • динамическую подсказку пользователю

Стилизация процесса рисования

Draw поддерживает два уровня стилей:

  • style — стиль во время рисования
  • стиль слоя — итоговый стиль
import Style from 'ol/style/Style';
import Stroke from 'ol/style/Stroke';
import Fill from 'ol/style/Fill';

const draw = new Draw({
  source: source,
  type: 'Polygon',
  style: new Style({
    stroke: new Stroke({
      color: 'blue',
      width: 2
    }),
    fill: new Fill({
      color: 'rgba(0, 0, 255, 0.1)'
    })
  })
});

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

Draw часто используется вместе с Snap, чтобы вершины “прилипали” к существующим объектам.

import Snap from 'ol/interaction/Snap';

const snap = new Snap({
  source: source
});

map.addInteraction(draw);
map.addInteraction(snap);

Комбинация Draw + Snap обеспечивает точное редактирование геометрии без разрывов.


Удаление и повторное использование

Интеракции можно динамически включать и отключать:

map.removeInteraction(draw);
map.addInteraction(draw);

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


Работа с многочастными геометриями

MultiPolygon и другие мульти-типы позволяют создавать сложные структуры.

const draw = new Draw({
  source: source,
  type: 'MultiPolygon'
});

Каждая новая часть добавляется как отдельный полигон внутри общей коллекции.


Интеграция с источником данных

Все созданные объекты автоматически добавляются в VectorSource:

const features = source.getFeatures();

features.forEach(f => {
  console.log(f.getGeometry().getType());
});

Источник может быть связан с:

  • GeoJSON форматами
  • WFS сервисами
  • локальным хранилищем
  • серверной синхронизацией

Очистка и управление данными

source.clear();

или удаление отдельных объектов:

source.removeFeature(feature);

Это часто используется при повторном запуске Draw-сессий.


Ограничения и особенности поведения

Draw имеет ряд внутренних особенностей:

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

Расширенные сценарии использования

Draw применяется в сложных интерфейсах:

  • редакторы геоданных
  • системы кадастра
  • GIS-анализ
  • измерительные инструменты (площадь/периметр)
  • построение зон покрытия
  • разметка объектов в аналитических системах

В таких системах Draw обычно комбинируется с:

  • Modify interaction
  • Sel ect interaction
  • Translate interaction
  • Overlay подсказками

Синхронизация с внешними состояниями

Draw может быть связан с состоянием приложения:

let active = false;

function toggleDraw() {
  if (active) {
    map.removeInteraction(draw);
  } else {
    map.addInteraction(draw);
  }
  active = !active;
}

Это позволяет строить полноценные режимы редактирования карты.


Работа с координатами и трансформациями

При необходимости координаты можно преобразовывать:

import { transform } fr om 'ol/proj';

const coords = feature.getGeometry().getCoordinates();

const transformed = coords.map(c =>
  transform(c, 'EPSG:3857', 'EPSG:4326')
);

Это важно при отправке данных на сервер или сохранении в GeoJSON.


Геометрическая точность и поведение курсора

OpenLayers использует внутреннюю интерполяцию для:

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

Это влияет на ощущение “плавности” рисования и может настраиваться через стили и события, хотя не всегда явно документировано через API Draw.


Типовые ошибки при использовании Draw

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

Архитектурная роль Draw в приложении

Draw обычно выступает как слой пользовательского ввода в архитектуре GIS-интерфейса. Он не хранит данные, не управляет их жизненным циклом и не отвечает за бизнес-логику. Его задача ограничена:

  • создание геометрии
  • временная визуализация
  • передача результата в source или внешнюю систему

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