Транзакции WFS-T

В основе редактирования пространственных данных в веб-картографии лежит механизм WFS-T (Web Feature Service – Transactional), позволяющий не только запрашивать геометрию с сервера, но и изменять её: добавлять, обновлять и удалять объекты напрямую через HTTP-запросы, оформленные в XML. В OpenLayers этот механизм реализуется через форматирование запросов WFS Transaction и работу с векторными слоями, связанными с удалёнными источниками данных.

WFS-T строится поверх стандартного WFS и расширяет его возможностями записи. В классической схеме взаимодействия участвуют три компонента:

  • клиент (OpenLayers в браузере),
  • сервис WFS-T (например, GeoServer),
  • источник пространственных данных (PostGIS, Oracle Spatial и др.).

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

Ключевая особенность заключается в том, что обмен происходит не в JSON (как в большинстве современных REST API), а в строго структурированном XML формате GML + WFS Transaction.

Основные операции WFS-T

WFS-T поддерживает три базовых типа операций:

  • Insert — создание нового объекта
  • Update — изменение существующего объекта
  • Delete — удаление объекта

Каждая операция описывается внутри единого блока <Transaction>.

Пример структуры запроса:

<wfs:Transaction service="WFS" version="1.1.0"
  xmlns:wfs="http://www.opengis.net/wfs"
  xmlns:gml="http://www.opengis.net/gml"
  xmlns:feature="http://example.com/feature">
  
  <wfs:Insert>
    <feature:road>
      <feature:name>Main Street</feature:name>
      <feature:geometry>
        <gml:Point>
          <gml:coordinates>30.5,50.4</gml:coordinates>
        </gml:Point>
      </feature:geometry>
    </feature:road>
  </wfs:Insert>

</wfs:Transaction>

OpenLayers автоматически генерирует подобные структуры через ol/format/WFS.

Формирование транзакций в OpenLayers

В клиентской части ключевую роль играет форматтер:

  • ol/format/WFS
  • ol/format/GML2, GML3, GML32

Также используется сериализация объектов:

  • ol/format/Feature

Базовый процесс включает три шага:

  1. Сбор изменённых объектов
  2. Преобразование их в XML
  3. Отправка на сервер через XHR или fetch

Пример формирования Insert-транзакции:

import WFS from 'ol/format/WFS';
import GML from 'ol/format/GML';
import {XMLSerializer} from 'xmldom';

const formatWFS = new WFS();
const formatGML = new GML({
  featureNS: 'http://example.com/feature',
  featureType: 'road'
});

const node = formatWFS.writeTransaction(
  [feature],   // insert
  null,        // update
  null,        // delete
  formatGML
);

const xml = new XMLSerializer().serializeToString(node);

Векторный слой как источник транзакций

WFS-T почти всегда работает в связке с VectorSource. Типичная схема выглядит следующим образом:

  • VectorSource хранит локальные изменения
  • Draw и Modify взаимодействуют с пользователем
  • изменения помечаются как dirty features
  • затем отправляются транзакцией

Ключевые компоненты:

  • ol/source/Vector
  • ol/interaction/Draw
  • ol/interaction/Modify
  • ol/interaction/Select

Локальное редактирование происходит без сервера, а синхронизация выполняется пакетно через WFS-T.

Добавление новых объектов (Insert)

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

draw.on('drawend', function (event) {
  const feature = event.feature;
  feature.setId(undefined); // новый объект

  const node = formatWFS.writeTransaction(
    [feature],
    null,
    null,
    formatGML
  );
});

На стороне сервера Insert требует:

  • корректного featureNS
  • соответствующего featureType
  • валидной геометрии GML

Обновление объектов (Update)

Update-транзакции требуют наличия идентификатора объекта. OpenLayers использует feature.getId() для генерации фильтра.

const modifiedFeature = feature;

const node = formatWFS.writeTransaction(
  null,
  [modifiedFeature],
  null,
  formatGML
);

В XML это преобразуется в:

<wfs:Update typeName="feature:road">
  <wfs:Property>
    <wfs:Name>name</wfs:Name>
    <wfs:Value>New Name</wfs:Value>
  </wfs:Property>
  <ogc:Filter>
    <ogc:FeatureId fid="road.123"/>
  </ogc:Filter>
</wfs:Update>

Удаление объектов (Delete)

Удаление строится на основе фильтра по идентификатору:

const node = formatWFS.writeTransaction(
  null,
  null,
  [feature],
  formatGML
);

XML-структура:

<wfs:Delete typeName="feature:road">
  <ogc:Filter>
    <ogc:FeatureId fid="road.123"/>
  </ogc:Filter>
</wfs:Delete>

Пространственные и атрибутивные фильтры

Помимо удаления по ID, WFS-T поддерживает фильтрацию:

  • BBOX
  • сравнение атрибутов
  • пространственные операторы (Intersects, Within)

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

<ogc:Filter>
  <ogc:BBOX>
    <gml:Envelope>
      <gml:lowerCorner>10 10</gml:lowerCorner>
      <gml:upperCorner>20 20</gml:upperCorner>
    </gml:Envelope>
  </ogc:BBOX>
</ogc:Filter>

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

Критическим параметром является srsName. OpenLayers должен согласовывать:

  • проекцию карты (обычно EPSG:3857)
  • проекцию сервера (часто EPSG:4326)

При сериализации:

const formatGML = new GML({
  featureNS: 'http://example.com/feature',
  featureType: 'road',
  srsName: 'EPSG:3857'
});

Несовпадение SRS приводит к смещению геометрии или ошибкам транзакции.

Управление идентификаторами объектов

WFS-T требует стабильных идентификаторов:

  • fid (feature id)
  • server-side generated IDs
  • client-side temporary IDs

OpenLayers хранит идентификатор внутри feature.setId().

Особенность:

  • Insert может не иметь ID
  • Update/Delete требуют строго определённого ID

Работа с GeoServer как типичной реализацией WFS-T

На практике наиболее часто используется GeoServer. Его особенности:

  • поддержка WFS 1.0.0 / 1.1.0 / 2.0.0
  • обязательное объявление namespace
  • строгая валидация GML
  • настройка слоя как “Transactional”

Типовые ошибки:

  • неверный featureType
  • отсутствие разрешений на Transaction
  • несовпадение схемы XML

Батчевые транзакции

OpenLayers позволяет объединять изменения:

  • несколько Insert
  • несколько Update
  • несколько Delete

Пример комбинированной транзакции:

const node = formatWFS.writeTransaction(
  [feature1, feature2],
  [feature3],
  [feature4],
  formatGML
);

Это уменьшает количество HTTP-запросов и повышает производительность.

Обработка ответа сервера

Ответ WFS-T также XML:

<wfs:TransactionResponse>
  <wfs:TransactionSummary>
    <wfs:totalInserted>1</wfs:totalInserted>
    <wfs:totalUpdated>2</wfs:totalUpdated>
    <wfs:totalDeleted>1</wfs:totalDeleted>
  </wfs:TransactionSummary>
</wfs:TransactionResponse>

OpenLayers может парсить ответ через ol/format/WFS.

Типичные проблемы интеграции

В практических сценариях возникают следующие сложности:

  • несоответствие схемы GML и слоя
  • неправильный namespace
  • отсутствие версии WFS
  • ошибки сериализации координат
  • потеря precision координат при преобразовании

Особенно критична проблема несовпадения featureNS, так как сервер может полностью отклонить транзакцию.

Редактирование через взаимодействия (Interactions)

Редактирование обычно строится через:

  • Draw — создание геометрии
  • Modify — изменение вершин
  • Snap — привязка к существующим объектам

Эти взаимодействия формируют поток изменений, который затем агрегируется в WFS-T транзакции.

import Snap from 'ol/interaction/Snap';

map.addInteraction(new Snap({
  source: vectorSource
}));

Snap повышает точность геометрии и уменьшает ошибки топологии.

Синхронизация локального состояния с сервером

Стандартная архитектура:

  • локальные изменения в VectorSource
  • отметка feature как изменённого
  • очередь транзакций
  • отправка пакетом
  • очистка состояния после успешного ответа

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