geoserver 2.20.4
postgis: SELECT PostGIS_full_version();
POSTGIS="3.2.0 3.2.0" [EXTENSION] PGSQL="130" GEOS="3.10.1-CAPI-1.16.0" PROJ="7.2.1" LIBXML="2.9.9" LIBJSON="0.12" LIBPROTOBUF="1.2.1" WAGYU="0.5.0 (Internal)"
postgresql: SELECT version();
PostgreSQL 13.5, compiled by Visual C++ build 1914, 64-bit
openlayers: "ol": "^9.1.0",
参考9.2.24 api:
OpenLayers v9.2.4 API - Class: WFS
先测试一下。使用qgis 画一个面,在dbeaver中复制出来,在geoserver 演示这里编辑,点击提交之后是这样
geom里的字段要和postgis里的一致,像这样:如果写不一样,如the_geom, 插入的记录就会geom是空的
完整xml:
<wfs:Transaction service="WFS" version="1.0.0" xmlns:wfs="http://www.opengis.net/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:ysc="ysc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd ysc http://192.168.9.158:9092/geoserver/project01/wfs/DescribeFeatureType?typename=project01:sheng"> <wfs:Insert> <ysc:sheng> <ysc:geom> <gml:MultiPolygon srsName="EPSG:4326"> <gml:polygonMember> <gml:Polygon> <gml:outerBoundaryIs> <gml:LinearRing> <gml:coordinates decimal="." cs="," ts=" "> 117.24109987655501,32.97967999727664 118.04820218611013,33.00850507976075 118.05396720260696,32.633779007467304 117.25839492604548,32.63954402396413 117.24109987655501,32.97967999727664 </gml:coordinates> </gml:LinearRing> </gml:outerBoundaryIs> </gml:Polygon> </gml:polygonMember> </gml:MultiPolygon> </ysc:geom> <ysc:TYPE>alley</ysc:TYPE> </ysc:sheng> </wfs:Insert> </wfs:Transaction>
注意我这里的命名空间没写规范,但也通过了
如果命名空间写得不对就报Feature type 'sheng' is not available:
如果命名空间不写,就报error on line 7 at column 22: Encoding error, 其实根本找不出line7 哪里错误
坐标的注意点:小数点用decimal="." 经纬度之间用cs="," 坐标之间用空格ts=" " 要仔细看坐标后是不是带了空格
<gml:coordinates decimal="." cs="," ts=" ">
117.24109987655501,32.97967999727664
118.04820218611013,33.00850507976075
118.05396720260696,32.633779007467304
117.25839492604548,32.63954402396413
117.24109987655501,32.97967999727664
</gml:coordinates>
空格多一个也是允许的:
使用openlayers提交
//创建feature var newFeature = new Feature(); newFeature.setGeometryName('geom'); //关键,这里必须要和postgis库里的图形字段名geom 一致 newFeature.setGeometry(new MultiPolygon([[[ //GeoServer 的 WFS 1.1.0 默认遵循 OGC 标准,即 EPSG:4326 的坐标顺序是 [纬度, 经度] //WFS 1.0.0 更灵活,可以支持 [经度, 纬度]。 [32.97967999727664, 117.24109987655501], [33.00850507976075, 118.04820218611013], [32.633779007467304, 118.05396720260696], [32.63954402396413, 117.25839492604548], [32.97967999727664, 117.24109987655501] ]]])); newFeature.set('gml_id','999-1'); //设置属性字段gml_id const featureRequest = new WFS().writeTransaction([newFeature], null, null, { featureNS: "ysc", featureType: 'sheng', // 注意:这里应该是字符串,不能用数组 srsName: "EPSG:4326", }) let str = new XMLSerializer().serializeToString(featureRequest) axios.post('http://192.168.9.158:9092/geoserver/project01/wfs', str, { headers: {'Content-Type': 'text/xml'}, }).then((res)=>{ console.log('响应:', res.data); }).catch((err) => { console.error('错误:', err.response ? err.response.data : err.message); })
这里的str 默认生成的是 1.1.0标准:
" <Transaction xmlns="http://www.opengis.net/wfs" service="WFS" version="1.1.0" 看这里 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"> <Insert> <sheng xmlns="ysc"> <geom> <MultiPolygon xmlns="http://www.opengis.net/gml" srsName="EPSG:4326"> <polygonMember> <Polygon srsName="EPSG:4326"> <exterior> <LinearRing srsName="EPSG:4326"> <posList srsDimension="2">117.24109987655501 32.97967999727664 118.04820218611013 33.00850507976075 118.05396720260696 32.633779007467304 117.25839492604548 32.63954402396413 117.24109987655501 32.97967999727664</posList> </LinearRing> </exterior> </Polygon> </polygonMember> </MultiPolygon> </geom> <gml_id>999-1</gml_id> </sheng> </Insert> </Transaction>"
强制替换为 格式:
//创建feature var newFeature = new Feature(); newFeature.setGeometryName('geom'); //关键,这里必须要和postgis库里的图形字段名geom 一致 newFeature.setGeometry(new MultiPolygon([[[ //GeoServer 的 WFS 1.1.0 默认遵循 OGC 标准,即 EPSG:4326 的坐标顺序是 [纬度, 经度] //WFS 1.0.0 更灵活,可以支持 [经度, 纬度]。 [32.97967999727664, 117.24109987655501], [33.00850507976075, 118.04820218611013], [32.633779007467304, 118.05396720260696], [32.63954402396413, 117.25839492604548], [32.97967999727664, 117.24109987655501] ]]])); newFeature.set('gml_id','999-1'); //设置属性字段gml_id const featureRequest = new WFS().writeTransaction([newFeature], null, null, { featureNS: "ysc", featureType: 'sheng', // 注意:这里应该是字符串,不能用数组 srsName: "EPSG:4326", }) let str = new XMLSerializer().serializeToString(featureRequest) // 替换posList为coordinates(如果需要) str = str.replace(/<posList[^>]*>([^<]*)<\/posList>/g, function(match, coords) { return `<coordinates decimal="." cs="," ts=",">${coords.replace(/ /g, ',')}</coordinates>`; }); axios.post('http://192.168.9.158:9092/geoserver/project01/wfs', str, { headers: {'Content-Type': 'text/xml'}, }).then((res)=>{ console.log('响应:', res.data); }).catch((err) => { console.error('错误:', err.response ? err.response.data : err.message); })
" <Transaction xmlns="http://www.opengis.net/wfs" service="WFS" version="1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"> <Insert> <sheng xmlns="ysc"> <geom> <MultiPolygon xmlns="http://www.opengis.net/gml" srsName="EPSG:4326"> <polygonMember> <Polygon srsName="EPSG:4326"> <exterior> <LinearRing srsName="EPSG:4326"> <coordinates decimal="." cs="," ts=",">117.24109987655501,32.97967999727664,118.04820218611013,33.00850507976075,118.05396720260696,32.633779007467304,117.25839492604548,32.63954402396413,117.24109987655501,32.97967999727664</coordinates> </LinearRing> </exterior> </Polygon> </polygonMember> </MultiPolygon> </geom> <gml_id>999-1</gml_id> </sheng> </Insert> </Transaction>"
如果指定 1.0.0版本:
默认或指定 1.1.0版本:
如果是openlayers 代码画
createPolygonDraw(callback){ let polygonDraw = new Draw({ source: this.GetLayerByTitle('画笔').getSource(), type: 'MultiPolygon', //geometryName:"geom",//关键点 style: { 'circle-radius': 5, 'circle-fill-color': '#52c41a', 'stroke-color': '#52c41a', 'stroke-width': 2, 'fill-color': 'rgba(82, 196, 26,0.4)', } }); polygonDraw.on("drawend", event => { if(callback) callback(event) }) ol.Inst().GetMap().addInteraction(polygonDraw) } function draw_polygon_save(){ ol.Inst().createPolygonDraw((e)=>{ console.log('event',e)
var newFeature = new Feature(); newFeature.setGeometryName('geom'); newFeature.setGeometry(e.feature.getGeometry()) newFeature.set('gml_id','999-2');
const featureRequest = new WFS().writeTransaction([newFeature], null, null, { featureNS: "ysc", featureType: 'sheng', // 注意:这里应该是字符串,不能用数组 srsName: "EPSG:4326", version: "1.1.0", }) let str = new XMLSerializer().serializeToString(featureRequest) str = str.replace(/<posList[^>]*>([^<]*)<\/posList>/g, (match, coords) => { // 1. 将空格分隔的字符串拆分为数组 const numbers = coords.trim().split(/\s+/); // 2. 检查坐标数量是否为偶数(必须是纬度、经度成对出现) if (numbers.length % 2 !== 0) { console.error("坐标数量不是偶数!", numbers); return match; // 返回原始内容避免破坏XML } // 3. 交换每对值的位置(从 "lon lat" 改为 "lat lon") const swapped = []; for (let i = 0; i < numbers.length; i += 2) { swapped.push(numbers[i + 1], numbers[i]); // 经度 ↔ 纬度 } // 4. 重新组合为空格分隔的字符串 return `<posList srsDimension="2">${swapped.join(" ")}</posList>`; }); axios.post('http://192.168.9.158:9092/geoserver/project01/wfs', str, { headers: {'Content-Type': 'text/xml'}, }).then((res)=>{ console.log('响应:', res.data); }).catch((err) => { console.error('错误:', err.response ? err.response.data : err.message); }) }) }
坐标反转没有好的办法,我使用的版本不同 ?