В основе редактирования пространственных данных в веб-картографии лежит механизм WFS-T (Web Feature Service – Transactional), позволяющий не только запрашивать геометрию с сервера, но и изменять её: добавлять, обновлять и удалять объекты напрямую через HTTP-запросы, оформленные в XML. В OpenLayers этот механизм реализуется через форматирование запросов WFS Transaction и работу с векторными слоями, связанными с удалёнными источниками данных.
WFS-T строится поверх стандартного WFS и расширяет его возможностями записи. В классической схеме взаимодействия участвуют три компонента:
OpenLayers формирует транзакционные XML-запросы и отправляет их на сервер, который выполняет операции над геометрией и атрибутами.
Ключевая особенность заключается в том, что обмен происходит не в JSON (как в большинстве современных REST API), а в строго структурированном XML формате GML + WFS Transaction.
WFS-T поддерживает три базовых типа операций:
Каждая операция описывается внутри единого блока
<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.
В клиентской части ключевую роль играет форматтер:
ol/format/WFSol/format/GML2, GML3,
GML32Также используется сериализация объектов:
ol/format/FeatureБазовый процесс включает три шага:
Пример формирования 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/Vectorol/interaction/Drawol/interaction/Modifyol/interaction/SelectЛокальное редактирование происходит без сервера, а синхронизация выполняется пакетно через WFS-T.
Процесс добавления включает создание объекта и привязку его к слою:
draw.on('drawend', function (event) {
const feature = event.feature;
feature.setId(undefined); // новый объект
const node = formatWFS.writeTransaction(
[feature],
null,
null,
formatGML
);
});
На стороне сервера Insert требует:
featureNSfeatureTypeUpdate-транзакции требуют наличия идентификатора объекта. 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>
Удаление строится на основе фильтра по идентификатору:
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 поддерживает фильтрацию:
Пример логики фильтрации:
<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
должен согласовывать:
При сериализации:
const formatGML = new GML({
featureNS: 'http://example.com/feature',
featureType: 'road',
srsName: 'EPSG:3857'
});
Несовпадение SRS приводит к смещению геометрии или ошибкам транзакции.
WFS-T требует стабильных идентификаторов:
fid (feature id)OpenLayers хранит идентификатор внутри
feature.setId().
Особенность:
На практике наиболее часто используется GeoServer. Его особенности:
Типовые ошибки:
featureTypeOpenLayers позволяет объединять изменения:
Пример комбинированной транзакции:
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.
В практических сценариях возникают следующие сложности:
Особенно критична проблема несовпадения featureNS, так
как сервер может полностью отклонить транзакцию.
Редактирование обычно строится через:
Draw — создание геометрииModify — изменение вершинSnap — привязка к существующим объектамЭти взаимодействия формируют поток изменений, который затем агрегируется в WFS-T транзакции.
import Snap from 'ol/interaction/Snap';
map.addInteraction(new Snap({
source: vectorSource
}));
Snap повышает точность геометрии и уменьшает ошибки топологии.
Стандартная архитектура:
Это позволяет реализовать отложенную синхронизацию и офлайн-подобные сценарии редактирования пространственных данных.