问题
I'm workin on an OL3 app, where the user will be able to draw, modify and delete polygons and save the changes to GeoServer via WFS-T.
For starting point I've used the solution from here: wfs-t example app I've changed the code a little bit to use a polygon layer from my GeoServer. The draw, modify and delete polygon functions are working great, if I modify or delete a polygon it's also saved, but the new polygon creation is not saved and I can not figure out why. The original app is working without any problem.
I hope somebody also tried to use this app as a starting point and solved this problem. Could someone give me any idea what is the problem?
Tha most important part of the code:
var dirty = {};
var formatWFS = new ol.format.WFS();
var formatGML = new ol.format.GML({
featureNS: 'http://www.openplans.org/topp',
featureType: 'poly',
srsName: 'EPSG:3857'
});
var transactWFS = function(p,f) {
switch(p) {
case 'insert':
node = formatWFS.writeTransaction([f],null,null,formatGML);
break;
case 'update':
node = formatWFS.writeTransaction(null,[f],null,formatGML);
break;
case 'delete':
node = formatWFS.writeTransaction(null,null,[f],formatGML);
break;
}
s = new XMLSerializer();
str = s.serializeToString(node);
$.ajax('http://localhost:8080/geoserver/wfs',{
type: 'POST',
dataType: 'xml',
processData: false,
contentType: 'text/xml',
data: str
}).done();
}
$('.btn-floating').hover(
function() {
$(this).addClass('darken-2');},
function() {
$(this).removeClass('darken-2');}
);
$('.btnMenu').on('click', function(event) {
$('.btnMenu').removeClass('orange');
$(this).addClass('orange');
map.removeInteraction(interaction);
select.getFeatures().clear();
map.removeInteraction(select);
switch($(this).attr('id')) {
case 'btnSelect':
interaction = new ol.interaction.Select({
style: new ol.style.Style({
stroke: new ol.style.Stroke({color: '#f50057', width: 2})
})
});
map.addInteraction(interaction);
interaction.getFeatures().on('add', function(e) {
props = e.element.getProperties();
if (props.status){$('#popup-status').html(props.status);}else{$('#popup-status').html('n/a');}
if (props.tiendas){$('#popup-tiendas').html(props.tiendas);}else{$('#popup-tiendas').html('n/a');}
coord = $('.ol-mouse-position').html().split(',');
overlayPopup.setPosition(coord);
});
break;
case 'btnEdit':
map.addInteraction(select);
interaction = new ol.interaction.Modify({
features: select.getFeatures()
});
map.addInteraction(interaction);
snap = new ol.interaction.Snap({
source: layerVector.getSource()
});
map.addInteraction(snap);
dirty = {};
select.getFeatures().on('add', function(e) {
e.element.on('change', function(e) {
dirty[e.target.getId()] = true;
});
});
select.getFeatures().on('remove', function(e) {
f = e.element;
if (dirty[f.getId()]){
delete dirty[f.getId()];
featureProperties = f.getProperties();
delete featureProperties.boundedBy;
var clone = new ol.Feature(featureProperties);
clone.setId(f.getId());
transactWFS('update',clone);
}
});
break;
case 'btnDrawPoly':
interaction = new ol.interaction.Draw({
type: 'Polygon',
source: layerVector.getSource()
});
map.addInteraction(interaction);
interaction.on('drawend', function(e) {
transactWFS('insert',e.feature);
});
break;
case 'btnDelete':
interaction = new ol.interaction.Select();
map.addInteraction(interaction);
interaction.getFeatures().on('change:length', function(e) {
transactWFS('delete',e.target.item(0));
interaction.getFeatures().clear();
selectPointerMove.getFeatures().clear();
});
break;
default:
break;
}
});
I'm using a single shape file as data store.
The request to the GeoServer after finishing a polygon:
<Transaction xmlns="http://www.opengis.net/wfs" service="WFS" version="1.1.0" xsi:schemaLocation="http
://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" xmlns:xsi="http://www.w3.org/2001
/XMLSchema-instance"><Insert><poly xmlns="http://www.openplans.org/topp"><geometry><Polygon xmlns="http
://www.opengis.net/gml"><exterior><LinearRing><posList>2274170.418847337 5923526.286802612 2329612.7433635183
5979783.939620501 2373640.4716557795 5936979.203780803 2330835.735816081 5891728.483035979 2274170.418847337
5923526.286802612</posList></LinearRing></exterior></Polygon></geometry></poly></Insert></Transaction
>
The response from GeoServer:
<?xml version="1.0" encoding="UTF-8"?><wfs:TransactionResponse xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:sf="http://www.openplans.org/spearfish" xmlns:wfs="http://www.opengis.net/wfs" xmlns:gml="http
://www.opengis.net/gml" xmlns:ogc="http://www.opengis.net/ogc" xmlns:ows="http://www.opengis.net/ows"
xmlns:tiger="http://www.census.gov" xmlns:topp="http://www.openplans.org/topp" xmlns:xlink="http://www
.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xsi:schemaLocation
="http://www.opengis.net/wfs http://localhost:8080/geoserver/schemas/wfs/1.1.0/wfs.xsd"><wfs:TransactionSummary
><wfs:totalInserted>1</wfs:totalInserted><wfs:totalUpdated>0</wfs:totalUpdated><wfs:totalDeleted>0</wfs
:totalDeleted></wfs:TransactionSummary><wfs:TransactionResults/><wfs:InsertResults><wfs:Feature><ogc
:FeatureId fid="new0"/></wfs:Feature></wfs:InsertResults></wfs:TransactionResponse>
回答1:
That code sample looks familiar... I wrote this ages ago and surprised this is still working and how many people have used it since. I am pretty certain that the problem is that the geometry column is not called geometry. I believe this must always be called geometry. I have update the example since to work with Geoserver 2.8 and OpenLayers 3.16.
In my setup I use a simple geometry field in the Postgis table. This is not a production environment but allows you to have all types of geometry (point, line, polygon) in the same table. I do not define the EPSG code here but I always use EPSG:3857 during development. If you set the table like this you can test whether the WFS-T posts work.
CREATE TABLE wfs_geom
(
id bigint NOT NULL,
geometry geometry,
CONSTRAINT wfs_geom_pkey PRIMARY KEY (id)
)
WITH (
OIDS=FALSE
);
ALTER TABLE wfs_geom
OWNER TO geoserver;
This is in a new jsfiddle but I can also put this in a stackoverflow code snippet.
https://jsfiddle.net/goldrydigital/13Lwsfmf/
var formatWFS = new ol.format.WFS();
var formatGML = new ol.format.GML({
featureNS: 'https://geolytix.net/wfs',
featureType: 'wfs_geom',
srsName: 'EPSG:3857'
});
var xs = new XMLSerializer();
var sourceWFS = new ol.source.Vector({
loader: function (extent) {
$.ajax('https://maps.geolytix.net/geoserver/geolytix.wfs/wfs', {
type: 'GET',
data: {
service: 'WFS',
version: '1.1.0',
request: 'GetFeature',
typename: 'wfs_geom',
srsname: 'EPSG:3857',
bbox: extent.join(',') + ',EPSG:3857'
}
}).done(function (response) {
sourceWFS.addFeatures(formatWFS.readFeatures(response));
});
},
//strategy: ol.loadingstrategy.tile(ol.tilegrid.createXYZ()),
strategy: ol.loadingstrategy.bbox,
projection: 'EPSG:3857'
});
var layerWFS = new ol.layer.Vector({
source: sourceWFS
});
var interaction;
var interactionSelectPointerMove = new ol.interaction.Select({
condition: ol.events.condition.pointerMove
});
var interactionSelect = new ol.interaction.Select({
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#FF2828'
})
})
});
var interactionSnap = new ol.interaction.Snap({
source: layerWFS.getSource()
});
var map = new ol.Map({
target: 'map',
controls: [],
interactions: [
interactionSelectPointerMove,
new ol.interaction.MouseWheelZoom(),
new ol.interaction.DragPan()
],
layers: [
new ol.layer.Tile({
source: new ol.source.OSM({
url: 'https://cartodb-basemaps-{a-d}.global.ssl.fastly.net/dark_nolabels/{z}/{x}/{y}.png',
opaque: false,
attributions: []
})
}),
layerWFS
],
view: new ol.View({
center: ol.proj.fromLonLat([-1.7, 53.2]),
zoom: 6
})
});
//wfs-t
var dirty = {};
var transactWFS = function (mode, f) {
var node;
switch (mode) {
case 'insert':
node = formatWFS.writeTransaction([f], null, null, formatGML);
break;
case 'update':
node = formatWFS.writeTransaction(null, [f], null, formatGML);
break;
case 'delete':
node = formatWFS.writeTransaction(null, null, [f], formatGML);
break;
}
var payload = xs.serializeToString(node);
$.ajax('https://maps.geolytix.net/geoserver/geolytix.wfs/wfs', {
type: 'POST',
dataType: 'xml',
processData: false,
contentType: 'text/xml',
data: payload
}).done(function() {
sourceWFS.clear();
});
};
$('button').click(function () {
$(this).siblings().removeClass('btn-active');
$(this).addClass('btn-active');
map.removeInteraction(interaction);
interactionSelect.getFeatures().clear();
map.removeInteraction(interactionSelect);
switch ($(this).attr('id')) {
case 'btnEdit':
map.addInteraction(interactionSelect);
interaction = new ol.interaction.Modify({
features: interactionSelect.getFeatures()
});
map.addInteraction(interaction);
map.addInteraction(interactionSnap);
dirty = {};
interactionSelect.getFeatures().on('add', function (e) {
e.element.on('change', function (e) {
dirty[e.target.getId()] = true;
});
});
interactionSelect.getFeatures().on('remove', function (e) {
var f = e.element;
if (dirty[f.getId()]) {
delete dirty[f.getId()];
var featureProperties = f.getProperties();
delete featureProperties.boundedBy;
var clone = new ol.Feature(featureProperties);
clone.setId(f.getId());
transactWFS('update', clone);
}
});
break;
case 'btnPoint':
interaction = new ol.interaction.Draw({
type: 'Point',
source: layerWFS.getSource()
});
map.addInteraction(interaction);
interaction.on('drawend', function (e) {
transactWFS('insert', e.feature);
});
break;
case 'btnLine':
interaction = new ol.interaction.Draw({
type: 'LineString',
source: layerWFS.getSource()
});
map.addInteraction(interaction);
interaction.on('drawend', function (e) {
transactWFS('insert', e.feature);
});
break;
case 'btnArea':
interaction = new ol.interaction.Draw({
type: 'Polygon',
source: layerWFS.getSource()
});
interaction.on('drawend', function (e) {
transactWFS('insert', e.feature);
});
map.addInteraction(interaction);
break;
case 'btnDelete':
interaction = new ol.interaction.Select();
interaction.getFeatures().on('add', function (e) {
transactWFS('delete', e.target.item(0));
interactionSelectPointerMove.getFeatures().clear();
interaction.getFeatures().clear();
});
map.addInteraction(interaction);
break;
default:
break;
}
});
html,
body {
height: 100%;
width: 100%;
padding: 0;
margin: 0;
border: 0;
}
.map {
height: 100%;
width: 100%;
}
#btnPoint {
position: absolute;
top: 10px;
left: 10px;
}
#btnLine {
position: absolute;
top: 10px;
left: 80px;
}
#btnArea {
position: absolute;
top: 10px;
left: 150px;
}
#btnEdit {
position: absolute;
top: 10px;
left: 220px;
}
#btnDelete {
position: absolute;
top: 10px;
left: 290px;
}
.btn-active {
background-color: #0d47a1 !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.16.0/ol.css" type="text/css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.1.3/material.indigo-pink.min.css">
<script src="https://code.getmdl.io/1.1.3/material.min.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<button id="btnPoint" class="mdl-button mdl-js-button mdl-button--fab mdl-button--colored">
<i class="material-icons">add_location</i>
</button>
<button id="btnLine" class="mdl-button mdl-js-button mdl-button--fab mdl-button--colored">
<i class="material-icons">timeline</i>
</button>
<button id="btnArea" class="mdl-button mdl-js-button mdl-button--fab mdl-button--colored">
<i class="material-icons">signal_cellular_null</i>
</button>
<button id="btnEdit" class="mdl-button mdl-js-button mdl-button--fab mdl-button--colored">
<i class="material-icons">build</i>
</button>
<button id="btnDelete" class="mdl-button mdl-js-button mdl-button--fab mdl-button--colored">
<i class="material-icons">delete</i>
</button>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.14/proj4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.16.0/ol.js"></script>
</html>
回答2:
GeoServer is actually correctly processing your request, the problem is that what you are trying to do is not allowed by your underlying datastore. Shapefiles must have a geometry attribute called the_geom
so when you send a geometry called poly
the shapefile writer ignores it when writing the feature. If you were using a database (e.g. PostGIS) then everything would be fine.
来源:https://stackoverflow.com/questions/37115620/openlayers3-wfs-t-save-drawing