OpenLayers – How to Modify Existing Feature Attributes by Feature ID

geoserveropenlayerswfs-t

I am using OpenLayers to view features from a layer from GeoServer.

I use WMTS to display the layer when the zoom is within a certain range. I use a WMS query to look up the feature id, and other attributes, of a feature when the user clicks the map.

What I would like to be able to do is to change the values for some of the additional attributes associated with the feature that was clicked on.

From what I've read, it seems that I should be using WFS-T to update the feature. Given only the id of a feature, the name of an attribute to be updated, and the updated value for that attribute, how would I update the value through WFS-T?

Note 1: I see that the WFS class from ol/format/WFS has a method: writeTransaction, which I believe is what I should be using. However, it is not clear to me how to get the Feature object given only the id of the feature.

Here is the code that I have so far:

const WMTSMINZOOM = 14;
const WMTSMAXZOOM = 22;
const layerName = "db:TEST1";
const WFSREQUESTPREFIX = "http://localhost:8080/geoserver/ows?service=wfs&version=2.0.0" +
                     "&request=GetFeature&typename=" + layerName +
                     "&outputFormat=application/json";

var parser = new WMTSCapabilities();
var map;

var wmsSource = new TileWMS({
url: 'http://localhost:8080/geoserver/ows?SERVICE=WMS&',
params: {'LAYERS': layerName},  // bbox can be set in params?
serverType: 'geoserver',
crossOrigin: 'anonymous'
});

fetch('http://localhost:8080/geoserver/gwc/service/wmts?REQUEST=GetCapabilities').then(function(response) {
    return response.text();
}).then(function(text) {
    var result = parser.read(text);
    var options = optionsFromCapabilities(result, {
        layer: layerName,
        matrixSet: 'EPSG:3857'
    });

    map = new Map({
        target: 'map',
        layers: [
            new TileLayer({
                source: new OSM(),
                opacity: .7
            }),
            new TileLayer({
                opacity: 1,
                source: new WMTS((options))
            })
        ],
        view: new View({
            center: [0, 0],
            zoom: 1
        })
    })

    map.getLayers().getArray()[1].setVisible(false);
    map.on("moveend", function(event) {
        var theMap = event.map;
        var zoomLevel = theMap.getView().getZoom();
        var wmtsLayer = theMap.getLayers().getArray()[1];
        if (zoomLevel >= WMTSMINZOOM && zoomLevel <= WMTSMAXZOOM) {
            wmtsLayer.setVisible(true);
        } else {
            wmtsLayer.setVisible(false);
        }
        console.log(zoomLevel);
    });

    map.on('singleclick', function(event) {
        document.getElementById('info').innerHTML = '';
        var viewResolution = map.getView().getResolution();
        var url1 = wmsSource.getGetFeatureInfoUrl(
            event.coordinate, viewResolution, 'EPSG:3857',{'INFO_FORMAT': 'text/html'});
        if (url1) {
            document.getElementById('info').innerHTML = 
                '<iframe seamless src="' + url1 + '"></iframe>';
        }

        var url2 = wmsSource.getGetFeatureInfoUrl(
            event.coordinate, viewResolution, 'EPSG:3857',{'INFO_FORMAT': 'application/json'});
        if (url2) {
            fetch(url2).then(function(response) {
                return response.json()
            }).then(function(p) {
                // Removing any existing selection layers

                if (map.getLayers().getArray().length > 2) {
                    var rLayer = map.getLayers().getArray()[2];
                    map.removeLayer(rLayer);
                }

                // Get new vector layer using WFS
                var id = p["features"][0]["id"];
                console.log(id);
                var wfsRequest = WFSREQUESTPREFIX + "&featureID=" + id;

                var vectorSource = new VectorSource({
                    format: new GeoJSON(),
                    url: wfsRequest
                });
                var vectorLayer = new VectorLayer({
                    source: vectorSource,
                    style: new Style({
                        stroke: new Stroke({
                            color: 'rgba(0, 0, 255, 1.0)',
                            width: 2
                        })
                    })
                });
                map.addLayer(vectorLayer);
            });
        }
    });

});

Note 2: I see that VectorSource has a method getFeatures(), and, since I already have a vector source for a layer containing the feature clicked on, I figured I could use that method to get the Feature object that I want to update. However, when I called getFeatures() on my var vectorSource I get an array of size 0.

Best Answer

I found a solution:

const WFSREQUESTPREFIX = "http://localhost:8080/geoserver/ows?service=wfs&version=2.0.0" +
                         "&request=GetFeature&typename=" + fullLayerName +
                         "&outputFormat=application/json";
// fullLayerName is layerPrefix:layerName

var wfsRequest = WFSREQUESTPREFIX + "&featureID=" + id;
var vectorSource = new VectorSource({
    format: new GeoJSON(),
    url: wfsRequest
});
vectorSource.on('addfeature', function(f) {  // vectorSource should have only a single feature
    f.feature.set(propertyName, newValue);
    f.feature.setGeometryName("geom");  // do this if geometry isn't named 'geometry'
    var wfs = new WFS({});
    var transaction = wfs.writeTransaction(null, [f.feature], null, {
        featureNS: namespaceURI,
        featurePrefix: layerPrefix,
        featureType: layerName,
        srsName: srs
    });
    var data = new XMLSerializer().serializeToString(transaction);

    $.ajax({
        type: "POST",
        url: "http://localhost:8080/geoserver/wfs",
        data: data,
        contentType: "text/xml"
    });
});
Related Question