前言
- WFS的简单介绍
- WFS请求与响应的格式
- WFS服务器与客户端
- Genius Server WFS请求常见的几个bug
内容概览
WFS的简单介绍
OGC的WMS和WMTS规范都是有关空间数据显示的标准,而WFS(Web Feature Service)则允许用户在分布式的环境下通过HTTP对空间数据进行增、删、改、查。
具体来说,WebGIS服务器除了能够返回一张张地图图像之外,还可以返回绘制该地图图像所使用的真实地理数据。用户利用这些传输到客户端的地理数据可以进行数据渲染可视化、空间分析等操作。而前后端的这种数据交互就是基于WFS规范的。
那么也就能很清楚的说明WMS与WFS之间的区别了。WMS是由服务器将地图图像发送给客户端,而WFS是服务器将矢量数据发送给客户端。也就是在使用WMS时地图由服务器绘制,在使用WFS时地图由客户端绘制。另外最最重要的,使用WFS可以对WebGIS服务器中的地理数据(存储在空间数据库中)直接进行增、删、改、查。
WFS请求与相应的格式
WFS服务一般支持如下功能:
- GetCapabilities —— 获取WFS服务的元数据(介绍服务中的要素类和支持的操作)
- DescribeFeatureType —— 获取WFS服务支持的要素类的定义(要素类的元数据,比如要素包含哪些字段)
- GetFeature —— 获取要素数据
- GetGmlObject —— 通过XLink获取GML对象
- Transaction —— 创建、更新、删除要素数据的事务操作
- LockFeature —— 在事务过程中锁定要素
实际中,WebGIS服务器针对这些功能并不是必须全部实现,而是实现全部或部分。
因此,根据依据这些功能的支持与否,可以将WFS分为3类:
- Basic WFS —— 必须支持GetCapabilities、DescribeFeature Type、GetFeature功能
- XLink WFS —— 必须在Basic WFS基础上加上GetGmlObject操作
- Transaction WFS —— 也称为WFS-T,必须在Basic WFS基础上加上Transaction功能以及支持编辑数据,另外也可以加上GetGmlObject或LockFeature功能
下面是WFS中GetFeatures操作的例子,该请求用于获取osm工作区yt_ysl图层的要素数据
Request URL: http://192.168.80.161:9999/geniusserver/mapservices/kfq_zhgw/wfs
Request Method: POST
Request Payload
<GetFeature xmlns="http://www.opengis.net/wfs" service="WFS" version="1.1.0" outputFormat="application/json" maxFeatures="10" startIndex="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">
<Query typeName="osm:yt_ysl" srsName="EPSG:4326" xmlns:osm="http://openstreemap.org">
<Filter xmlns="http://www.opengis.net/ogc">
<PropertyIsLike wildCard="*" singleChar="." escapeChar="!">
<PropertyName>roadname</PropertyName>
<Literal>*八角大街*</Literal>
</PropertyIsLike>
</Filter>
</Query>
<Query typeName="osm:yt_wsl" srsName="EPSG:4326" xmlns:osm="http://openstreemap.org">
<Filter xmlns="http://www.opengis.net/ogc">
<PropertyIsLike wildCard="*" singleChar="." escapeChar="!">
<PropertyName>roadname</PropertyName>
<Literal>*八角大街*</Literal>
</PropertyIsLike>
</Filter>
</Query>
<Query typeName="osm:yt_hsl" srsName="EPSG:4326" xmlns:osm="http://openstreemap.org">
<Filter xmlns="http://www.opengis.net/ogc">
<PropertyIsLike wildCard="*" singleChar="." escapeChar="!">
<PropertyName>roadname</PropertyName>
<Literal>*八角大街*</Literal>
</PropertyIsLike>
</Filter>
</Query>
</GetFeature>
在上述请求中:
- service=WFS 表示使用WFS服务
- version=1.1.0 表示使用1.1.0版本
- outputFormat=”application/json” 表示返回json数据
- request=GetFeature 表示执行GetFeature操作
- typeName=”osm:yt_ysl” 表示针对的是服务器中osm工作区的名为yt_ysl的图层
返回结果如下:
{"type": "FeatureCollection",
"bbox": [ -90, -180, 90, 180],
"features": [
{
"type":"Feature",
"id":"yt_ysl.23080",
"bbox":[121.115639, 37.636511, 121.115641, 37.636568],
"geometry":
{"coordinates":[[121.115641,37.636568],[121.115639,37.636511]],"type":"LineString"},
"properties":{
"gid":23080,
"__gid":23083,
"fid":23083,
"starttime":"2012-05-03",
"endtime":"9999-12-30",
"company":null,
"belongcode":null,
"s_h":16.938,
"e_h":16.883,
"lnote":null,
"pressure":null,
"prj_no":null,
"s_deep":1.05,
"roadname":"八角大街",
"pipesize":null,
"e_deep":1.22,
"gxsub":null,
"embed":"直埋",
"bhmaterial":"4167230.39",
"totalhole":0,
"cabnum":null,
"usedhole":0,
"structdata":null,
"material":"砼",
"flowdir":"0",
"ltype":null,
"psize":"200",
"holeseat":null,
"enabled":0,
"s_x":466074.85300255,
"s_y":4167229.12000084,
"e_x":466074.59200096,
"e_y":4167222.77400017,
"codeid":"37067203PSYS0014948",
"strcodeid":"37067203PSYS0009249",
"endcodeid":"37067203PSYS0009250",
"s_exp":"YS0155339",
"e_exp":"YS0155338",
"shape_len":6.35136497141,
"us_id":"d988b8da-2aec-4888-9170-4a9ad0b48af4",
"psize_num":200,
"basin":null,
"test_type":null,
"test_group":null,
"river_basi":null,
"geom_lengt":6.352102821360244
}
}
,{
"type":"Feature",
"id":"yt_ysl.25106",
"bbox":[121.136391, 37.636406, 121.136392, 37.636421],
"geometry":
{"coordinates":[[121.136392,37.636406],[121.136391,37.636421]],"type":"LineString"},
"properties":{
"gid":25106,
"__gid":25109,
"fid":25109,
"starttime":"2012-05-03",
"endtime":"9999-12-30",
"company":null,
"belongcode":null,
"s_h":3.506,
"e_h":3.468,
"lnote":null,
"pressure":null,
"prj_no":null,
"s_deep":0.5,
"roadname":"八角大街",
"pipesize":null,
"e_deep":0.57,
"gxsub":null,
"embed":"直埋",
"bhmaterial":null,
"totalhole":0,
"cabnum":null,
"usedhole":0,
"structdata":null,
"material":"砼",
"flowdir":"0",
"ltype":null,
"psize":"200",
"holeseat":null,
"enabled":0,
"s_x":467906.32999992,
"s_y":4167203.74900246,
"e_x":467906.2310009,
"e_y":4167205.51099968,
"codeid":"37067203PSYS0014104",
"strcodeid":"37067203PSYS0010192",
"endcodeid":"37067203PSYS0010186",
"s_exp":"YS0155956",
"e_exp":"YS0155955",
"shape_len":1.76477902299,
"us_id":"ba9c170a-f37d-4fd0-8514-37b0a6ad4431",
"psize_num":200,
"basin":null,
"test_type":null,
"test_group":null,
"river_basi":null,
"geom_lengt":1.764982313469694
}
}
,{
"type":"Feature",
"id":"yt_ysl.15643",
"bbox":[121.13449, 37.637696, 121.134501, 37.637706],
"geometry":
{"coordinates":[[121.13449,37.637706],[121.134501,37.637696]],"type":"LineString"},
"properties":{
"gid":15643,
"__gid":15641,
"fid":15641,
"starttime":"2012-05-03",
"endtime":"9999-12-30",
"company":null,
"belongcode":null,
"s_h":2.867,
"e_h":1.948,
"lnote":null,
"pressure":null,
"prj_no":null,
"s_deep":0.66,
"roadname":"八角大街",
"pipesize":null,
"e_deep":1.7,
"gxsub":null,
"embed":"直埋",
"bhmaterial":null,
"totalhole":0,
"cabnum":null,
"usedhole":0,
"structdata":null,
"material":"砼",
"flowdir":"0",
"ltype":null,
"psize":"200",
"holeseat":null,
"enabled":0,
"s_x":467739.00100136,
"s_y":4167348.71999931,
"e_x":467739.93499947,
"e_y":4167347.6380024,
"codeid":"37067203PSYS0014135",
"strcodeid":"37067203PSYS0009985",
"endcodeid":"37067203PSYS0010159",
"s_exp":"YS0155618",
"e_exp":"YS0155619",
"shape_len":1.42936349471,
"us_id":"4c6ff8cd-9f80-4f66-8dd1-c89d8b1d7ef2",
"psize_num":200,
"basin":null,
"test_type":null,
"test_group":null,
"river_basi":null,
"geom_lengt":1.429496655918879
}
}
,{
"type":"Feature",
"id":"yt_ysl.15686",
"bbox":[121.139367, 37.638788, 121.139604, 37.638983],
"geometry":
{"coordinates":[[121.139604,37.638788],[121.139367,37.638983]],"type":"LineString"},
"properties":{
"gid":15686,
"__gid":15685,
"fid":15685,
"starttime":"2012-05-03",
"endtime":"9999-12-30",
"company":null,
"belongcode":null,
"s_h":0.581,
"e_h":0.564,
"lnote":null,
"pressure":null,
"prj_no":null,
"s_deep":3.02,
"roadname":"八角大街",
"pipesize":null,
"e_deep":3.02,
"gxsub":null,
"embed":"直埋",
"bhmaterial":null,
"totalhole":0,
"cabnum":null,
"usedhole":0,
"structdata":null,
"material":"砼",
"flowdir":"0",
"ltype":null,
"psize":"600",
"holeseat":null,
"enabled":0,
"s_x":468190.88699913,
"s_y":4167467.12800026,
"e_x":468169.99500084,
"e_y":4167488.80500221,
"codeid":"37067203PSYS0014136",
"strcodeid":"37067203PSYS0010144",
"endcodeid":"37067203PSYS0010145",
"s_exp":"YS0155659",
"e_exp":"YS0155657",
"shape_len":30.1059461403,
"us_id":"877963b4-20a2-4be2-ae58-915f52251b74",
"psize_num":600,
"basin":null,
"test_type":null,
"test_group":null,
"river_basi":null,
"geom_lengt":30.109333463688763
}
}
,{
"type":"Feature",
"id":"yt_ysl.15706",
"bbox":[121.104538, 37.636343, 121.104539, 37.636471],
"geometry":
{"coordinates":[[121.104538,37.636471],[121.104539,37.636343]],"type":"LineString"},
"properties":{
"gid":15706,
"__gid":15705,
"fid":15705,
"starttime":"2012-05-03",
"endtime":"9999-12-30",
"company":null,
"belongcode":null,
"s_h":36.135,
"e_h":35.675,
"lnote":null,
"pressure":null,
"prj_no":null,
"s_deep":1,
"roadname":"八角大街",
"pipesize":null,
"e_deep":1.17,
"gxsub":null,
"embed":"直埋",
"bhmaterial":null,
"totalhole":0,
"cabnum":null,
"usedhole":0,
"structdata":null,
"material":"砼",
"flowdir":"0",
"ltype":null,
"psize":"200",
"holeseat":null,
"enabled":0,
"s_x":465094.73500252,
"s_y":4167222.37600136,
"e_x":465094.79200173,
"e_y":4167208.14299965,
"codeid":"37067203PSYS0014678",
"strcodeid":"37067203PSYS0009278",
"endcodeid":"37067203PSYS0009279",
"s_exp":"YS0155410",
"e_exp":"YS0155409",
"shape_len":14.2331141357,
"us_id":"cb71c1da-85de-4817-ba58-70de123613d7",
"psize_num":200,
"basin":null,
"test_type":null,
"test_group":null,
"river_basi":null,
"geom_lengt":14.234547374728436
}
}
,{
"type":"Feature",
"id":"yt_ysl.15752",
"bbox":[121.137233, 37.636493, 121.13757, 37.636493],
"geometry":
{"coordinates":[[121.137233,37.636493],[121.13757,37.636493]],"type":"LineString"},
"properties":{
"gid":15752,
"__gid":15753,
"fid":15753,
"starttime":"2012-05-03",
"endtime":"9999-12-30",
"company":null,
"belongcode":null,
"s_h":2.105,
"e_h":2.049,
"lnote":null,
"pressure":null,
"prj_no":null,
"s_deep":2,
"roadname":"八角大街",
"pipesize":null,
"e_deep":2.05,
"gxsub":null,
"embed":"直埋",
"bhmaterial":null,
"totalhole":0,
"cabnum":null,
"usedhole":0,
"structdata":null,
"material":"砼",
"flowdir":"0",
"ltype":null,
"psize":"600",
"holeseat":null,
"enabled":0,
"s_x":467980.55999947,
"s_y":4167213.15800285,
"e_x":468010.34499931,
"e_y":4167213.104002,
"codeid":"37067203PSYS0014077",
"strcodeid":"37067203PSYS0010029",
"endcodeid":"37067203PSYS0010030",
"s_exp":"YS0126941",
"e_exp":"YS0126942",
"shape_len":29.7850489508,
"us_id":"8491b858-a679-44cc-bcec-19ae4d4768d6",
"psize_num":600,
"basin":null,
"test_type":null,
"test_group":null,
"river_basi":null,
"geom_lengt":29.788392134660874
}
}
,{
"type":"Feature",
"id":"yt_ysl.15873",
"bbox":[121.109768, 37.636302, 121.10977, 37.636329],
"geometry":
{"coordinates":[[121.109768,37.636302],[121.10977,37.636329]],"type":"LineString"},
"properties":{
"gid":15873,
"__gid":15875,
"fid":15875,
"starttime":"2012-05-03",
"endtime":"9999-12-30",
"company":null,
"belongcode":null,
"s_h":22.67,
"e_h":21.992,
"lnote":null,
"pressure":null,
"prj_no":null,
"s_deep":0.92,
"roadname":"八角大街",
"pipesize":null,
"e_deep":1.63,
"gxsub":null,
"embed":"直埋",
"bhmaterial":null,
"totalhole":0,
"cabnum":null,
"usedhole":0,
"structdata":null,
"material":"砼",
"flowdir":"0",
"ltype":null,
"psize":"200",
"holeseat":null,
"enabled":0,
"s_x":465556.35099983,
"s_y":4167201.65500069,
"e_x":465556.49400139,
"e_y":4167204.66399956,
"codeid":"37067203PSYS0014696",
"strcodeid":"37067203PSYS0009447",
"endcodeid":"37067203PSYS0009385",
"s_exp":"YS0155384",
"e_exp":"YS0155385",
"shape_len":3.0123960563,
"us_id":"d733fd08-41b5-4647-84fb-247d1b324ed2",
"psize_num":200,
"basin":null,
"test_type":null,
"test_group":null,
"river_basi":null,
"geom_lengt":3.01279499743254
}
}
,{
"type":"Feature",
"id":"yt_ysl.15874",
"bbox":[121.13478, 37.637888, 121.135084, 37.638102],
"geometry":
{"coordinates":[[121.135084,37.638102],[121.13478,37.637888]],"type":"LineString"},
"properties":{
"gid":15874,
"__gid":15876,
"fid":15876,
"starttime":"2012-05-03",
"endtime":"9999-12-30",
"company":null,
"belongcode":null,
"s_h":0.605,
"e_h":0.616,
"lnote":null,
"pressure":null,
"prj_no":null,
"s_deep":3.03,
"roadname":"八角大街",
"pipesize":null,
"e_deep":3.03,
"gxsub":null,
"embed":"直埋",
"bhmaterial":null,
"totalhole":0,
"cabnum":null,
"usedhole":0,
"structdata":null,
"material":"砼",
"flowdir":"0",
"ltype":null,
"psize":"600",
"holeseat":null,
"enabled":0,
"s_x":467791.61900139,
"s_y":4167392.4450016,
"e_x":467764.6380024,
"e_y":4167368.79100227,
"codeid":"37067203PSYS0014343",
"strcodeid":"37067203PSYS0009979",
"endcodeid":"37067203PSYS0009980",
"s_exp":"YS0155627",
"e_exp":"YS0155625",
"shape_len":35.8815562232,
"us_id":"5060f4af-d04f-4b7c-b0cf-fbbe190f585e",
"psize_num":600,
"basin":null,
"test_type":null,
"test_group":null,
"river_basi":null,
"geom_lengt":35.88558081966261
}
}
,{
"type":"Feature",
"id":"yt_ysl.15997",
"bbox":[121.135075, 37.6364, 121.135076, 37.636416],
"geometry":
{"coordinates":[[121.135076,37.6364],[121.135075,37.636416]],"type":"LineString"},
"properties":{
"gid":15997,
"__gid":16000,
"fid":16000,
"starttime":"2012-05-03",
"endtime":"9999-12-30",
"company":null,
"belongcode":null,
"s_h":3.282,
"e_h":3.302,
"lnote":null,
"pressure":null,
"prj_no":null,
"s_deep":0.42,
"roadname":"八角大街",
"pipesize":null,
"e_deep":0.4,
"gxsub":null,
"embed":"直埋",
"bhmaterial":null,
"totalhole":0,
"cabnum":null,
"usedhole":0,
"structdata":null,
"material":"砼",
"flowdir":"0",
"ltype":null,
"psize":"200",
"holeseat":null,
"enabled":0,
"s_x":467790.14900017,
"s_y":4167203.61499977,
"e_x":467790.11900139,
"e_y":4167205.34700203,
"codeid":"37067203PSYS0014096",
"strcodeid":"37067203PSYS0010200",
"endcodeid":"37067203PSYS0010190",
"s_exp":"YS0155944",
"e_exp":"YS0155943",
"shape_len":1.73225979576,
"us_id":"ed3d1026-619e-49aa-b20b-935450d9e9fb",
"psize_num":200,
"basin":null,
"test_type":null,
"test_group":null,
"river_basi":null,
"geom_lengt":1.732357849797768
}
}
,{
"type":"Feature",
"id":"yt_ysl.16159",
"bbox":[121.132317, 37.634123, 121.132334, 37.634123],
"geometry":
{"coordinates":[[121.132317,37.634123],[121.132334,37.634123]],"type":"LineString"},
"properties":{
"gid":16159,
"__gid":16162,
"fid":16162,
"starttime":"2012-05-03",
"endtime":"9999-12-30",
"company":null,
"belongcode":null,
"s_h":2.966,
"e_h":0.694,
"lnote":null,
"pressure":null,
"prj_no":null,
"s_deep":0.7,
"roadname":"八角大街",
"pipesize":null,
"e_deep":3.12,
"gxsub":null,
"embed":"直埋",
"bhmaterial":null,
"totalhole":0,
"cabnum":null,
"usedhole":0,
"structdata":null,
"material":"砼",
"flowdir":"0",
"ltype":null,
"psize":"200",
"holeseat":null,
"enabled":0,
"s_x":467545.64800072,
"s_y":4166951.81200218,
"e_x":467547.12800026,
"e_y":4166951.81900215,
"codeid":"37067203PSYS0014373",
"strcodeid":"37067203PSYS0010132",
"endcodeid":"37067203PSYS0009948",
"s_exp":"YS0155739",
"e_exp":"YS0155740",
"shape_len":1.48001655396,
"us_id":"6bd48eda-a11a-4539-adec-6eef5284c721",
"psize_num":200,
"basin":null,
"test_type":null,
"test_group":null,
"river_basi":null,
"geom_lengt":1.480216325095753
}
}
]
}
WFS服务器与客户端
虽然WFS的请求与相应语法初看起来有些吓人,但是在实际使用过程中,并不需要我们手工来拼接,GIS软件通常都支持查看与发布wfs服务
下面就来讲解,如何根据openlayer的WFS api 开实现矢量数据的获取与地图的展示的
openlayer wfs查询
openlayer wfs服务查询的关键代码
// generate a GetFeature request
const featureRequest = new WFS().writeGetFeature({
srsName: 'EPSG:3857',
featureNS: 'http://openstreemap.org',
featurePrefix: 'osm',
featureTypes: ['yt_ysl'],
outputFormat: 'application/json',
filter: andFilter(
likeFilter('roadname', '*八角大街*'),
equalToFilter('embed', '直埋')
)
})
fetch('http://192.168.80.161:9999/geniusserver/mapservices/kfq_zhgw/wfs', {
method: 'POST',
body: new XMLSerializer().serializeToString(featureRequest)
}).then((response) => {
return response.json()
}).then((json) => {
const features = new GeoJSON().readFeatures(json)
vectorSource.addFeatures(features)
map.getView().fit(vectorSource.getExtent())
})
Genius Server WFS请求的几个注意事项
Genius Server,并不是支持wfs所有的api操作的,而且有些操作写法和常规的wfs服务有区别,下面就提示几个防止大家踩坑
Genius WFS 不支持GET请求
大家在进行wfs请求的时候,只能使用POST请求,而且只支持XML格式
Genius WFS 不支持bbox,只能采用Filter查询
<GetFeature xmlns="http://www.opengis.net/wfs" service="WFS" version="1.1.0" outputFormat="application/json" maxFeatures="10" startIndex="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">
<Query typeName="osm:yt_ysl" srsName="EPSG:4326" xmlns:osm="http://openstreemap.org">
<Filter xmlns="http://www.opengis.net/ogc">
<Intersects>
<PropertyName>geom</PropertyName>
<Polygon xmlns="http://www.opengis.net/gml" srsName="EPSG:4326">
<exterior>
<LinearRing srsName="EPSG:4326">
<posList srsDimension="2">121.191148 37.558649 121.196147 37.558649 121.196147 37.567761 121.191148 37.567761</posList>
</LinearRing>
</exterior>
</Polygon>
</Intersects>
</Filter>
</Query>
<Query typeName="osm:yt_wsl" srsName="EPSG:4326" xmlns:osm="http://openstreemap.org">
<Filter xmlns="http://www.opengis.net/ogc">
<Intersects>
<PropertyName>geom</PropertyName>
<Polygon xmlns="http://www.opengis.net/gml" srsName="EPSG:4326">
<exterior>
<LinearRing srsName="EPSG:4326">
<posList srsDimension="2">121.191148 37.558649 121.196147 37.558649 121.196147 37.567761 121.191148 37.567761</posList>
</LinearRing>
</exterior>
</Polygon>
</Intersects>
</Filter>
</Query>
<Query typeName="osm:yt_hsl" srsName="EPSG:4326" xmlns:osm="http://openstreemap.org">
<Filter xmlns="http://www.opengis.net/ogc">
<Intersects>
<PropertyName>geom</PropertyName>
<Polygon xmlns="http://www.opengis.net/gml" srsName="EPSG:4326">
<exterior>
<LinearRing srsName="EPSG:4326">
<posList srsDimension="2">121.191148 37.558649 121.196147 37.558649 121.196147 37.567761 121.191148 37.567761</posList>
</LinearRing>
</exterior>
</Polygon>
</Intersects>
</Filter>
</Query>
</GetFeature>
Genius WFS 区域查询的时候,区域不能是闭合点(正常polygon等都是闭合点,但是Genius奇葩)
Polygon不能是闭合的,正常的四边形 polygon是5个点,但是 genius wfs查询的时候,只能是四个点,不包括闭合点