Редактирование геометрии

Редактирование геометрии в интерактивных веб-картах опирается на управление векторными слоями, изменением координатных наборов и синхронизацией состояния между визуальным представлением и моделью данных. В Leaflet базовые примитивы (маркер, полилиния, полигон, прямоугольник, круг) изначально предназначены для отображения геометрии, однако полноценное редактирование требует включения дополнительных механизмов и расширений.

Основная модель редактирования строится вокруг изменения массива координат объектов LatLng, который лежит в основе всех векторных слоёв. Любое редактирование сводится к добавлению, удалению или изменению элементов этого массива с последующим перерисовыванием слоя.


Векторные объекты как редактируемая структура данных

Каждый геометрический объект в Leaflet представляет собой слой с набором координат:

  • L.Marker — одна точка (LatLng)
  • L.Polyline — массив точек (ломаная линия)
  • L.Polygon — замкнутый массив точек
  • L.Rectangle — производная от полигона с фиксированной геометрией
  • L.Circle — центр + радиус

Редактирование начинается с доступа к этим параметрам:

const polyline = L.polyline([
  [55.75, 37.61],
  [55.76, 37.62]
]).addTo(map);

const latlngs = polyline.getLatLngs();

Изменение геометрии напрямую:

latlngs.push(L.latLng(55.77, 37.63));
polyline.setLatLngs(latlngs);

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


Интерактивное редактирование маркеров

Маркер является минимальной единицей редактирования, поскольку его геометрия — одна точка.

const marker = L.marker([55.75, 37.61], {
  draggable: true
}).addTo(map);

marker.on('dragend', (e) => {
  const position = e.target.getLatLng();
});

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

События:

  • dragstart
  • drag
  • dragend

Маркер не требует пересчёта геометрии, так как изменяется единственная точка.


Редактирование полилиний и полигонов

Полилинии и полигоны требуют работы с множеством вершин. В стандартном API Leaflet отсутствует полноценный UI для редактирования, поэтому применяется механизм расширения через плагины или ручная реализация.

Изменение координат вручную

const polygon = L.polygon([
  [55.75, 37.61],
  [55.76, 37.62],
  [55.75, 37.63]
]).addTo(map);

const coords = polygon.getLatLngs()[0];
coords[0] = L.latLng(55.751, 37.611);
polygon.setLatLngs(coords);

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


Включение редактирования через Leaflet.draw

Наиболее распространённый механизм редактирования реализуется через библиотеку Leaflet.draw:

Leaflet official site

Она добавляет инструменты рисования и редактирования геометрии.

Инициализация панели редактирования

const drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);

const drawControl = new L.Control.Draw({
  edit: {
    featureGroup: drawnItems
  },
  draw: {
    polygon: true,
    polyline: true,
    rectangle: true,
    circle: true,
    marker: true
  }
});

map.addControl(drawControl);

Обработка результата редактирования

map.on(L.Draw.Event.CREATED, function (e) {
  const layer = e.layer;
  drawnItems.addLayer(layer);
});

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


События редактирования геометрии

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

  • editstart
  • edit
  • editend

Пример:

polygon.on('edit', (e) => {
  const updated = e.target.getLatLngs();
});

Эти события критичны для синхронизации с сервером или локальным хранилищем.


Leaflet.Editable и режимы редактирования

Альтернативный подход реализуется через плагин Leaflet.Editable, который вводит режимы:

  • включение редактирования слоя
  • добавление новых вершин
  • перемещение существующих узлов
map.editTools.startPolygon();

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


Управление вершинами (vertices)

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

Добавление вершины

coords.splice(index, 0, L.latLng(55.755, 37.615));
polygon.setLatLngs(coords);

Удаление вершины

coords.splice(index, 1);
polygon.setLatLngs(coords);

Перемещение вершины

coords[index] = L.latLng(newLat, newLng);
polygon.setLatLngs(coords);

Каждое изменение требует полной синхронизации массива координат с объектом слоя.


Привязка вершин к карте (vertex handles)

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

function enableVertexEditing(polygon) {
  const latlngs = polygon.getLatLngs()[0];

  latlngs.forEach((point, index) => {
    const handle = L.marker(point, { draggable: true }).addTo(map);

    handle.on('drag', (e) => {
      latlngs[index] = e.target.getLatLng();
      polygon.setLatLngs(latlngs);
    });
  });
}

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


Работа с вложенными структурами координат

Полигоны могут содержать несколько колец (outer ring + holes):

const polygon = L.polygon([
  [
    [55.75, 37.61],
    [55.76, 37.62],
    [55.75, 37.63]
  ],
  [
    [55.755, 37.615],
    [55.756, 37.616],
    [55.755, 37.617]
  ]
]);

Редактирование таких структур требует учёта уровней вложенности:

  • внешний контур — latlngs[0]
  • внутренние отверстия — latlngs[1..n]

Синхронизация с GeoJSON

Редактируемые слои часто конвертируются в GeoJSON для хранения:

const geojson = polygon.toGeoJSON();

Обратное восстановление:

L.geoJSON(geojson, {
  onEachFeature: function (feature, layer) {
    layer.editing.enable();
  }
}).addTo(map);

GeoJSON становится универсальным форматом для обмена геометрией между клиентом и сервером.


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

Редактирование геометрии в Leaflet имеет ряд технических ограничений:

  • большое количество вершин снижает производительность перерисовки
  • частые вызовы setLatLngs создают нагрузку на DOM и canvas
  • сложные полигоны требуют оптимизации событий drag

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

  • дебаунс обновлений координат
  • кластеризация точек редактирования
  • временное отключение рендеринга во время drag

Расширение модели редактирования

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

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

Пример ограничения перемещения маркера:

marker.on('drag', (e) => {
  const latlng = e.target.getLatLng();

  if (latlng.lat > 60) {
    e.target.setLatLng([60, latlng.lng]);
  }
});

Архитектурная модель редактируемых слоёв

Редактирование векторной геометрии в Leaflet можно рассматривать как цикл:

  1. Получение текущих координат слоя
  2. Преобразование пользовательского действия в изменение массива LatLng
  3. Обновление слоя через setLatLngs
  4. Перерисовка визуального представления
  5. Событие изменения состояния
  6. Синхронизация с внешним хранилищем

Этот цикл является фундаментом всех систем интерактивного картографического редактирования на базе Leaflet.