写点什么

网络要素服务(WFS)详解

  • 2024-01-24
    福建
  • 本文字数:7947 字

    阅读完需:约 26 分钟

1. 概述


WMS 是一个返回图片地图的服务,图片本身就是栅格数据的一种,而对于矢量数据则可以进行矢量栅格化;因此,WMS 的数据源既可以是栅格数据,也可以是矢量数据。而 WFS 则不同,它是一个专门针对于矢量数据的服务,其返回的也是矢量要素本身。在 Web 环境中,图片是很容易进行可视化展示的,甚至图片本身就是 GUI 中一类很重要的元素。但矢量要素则不同,是不太容易可视化的。例如,如果要在前端的 HTML5 页面中展示获取的要素,就需要调用 HTML5 的 Canvas 元素来进行绘图,这其中涉及到繁复的操作不说,也很有可能会有性能问题。因此,WFS 并不关心可视化问题,而是为返回 GIS 矢量数据而设计的,同时还支持矢量的查询、增加、删除以及修改等事务性操作。


WFS 与 WMS 一样,同样使用 HTTP 来实现的各种操作,不同的是由于进行请求要求发送复杂的 XML 数据,简单的 Get 请求方式可能会受到数据量的限制,这种情况下需要使用 Post 方式进行请求。而在 Web 前端环境中,XML 数据并不方便使用(最方便的是 JSON 数据),经常要考虑到繁琐的字符串拼接以及字符转义的问题。另一方面,由于 WFS 需要传输的参数比较多,在其标准规范《OpenGIS_Web_Map_Service_WMS_Implementation_Specification》使用了 XML Schema(描述 XML 结构的语言)这一复杂的语言来描述需要传递的 XML 数据;并且一个操作的数据描述还分散在文档不同的地方。官方的参考资料尚且如此复杂,普通 GIS 从业人员也就很少愿意主动去使用,这无疑限制了造成 WFS 的应用场景。应该来说,WFS 的设计出来的年代比较早,XML 格式还是主流,如果使用 JSON 格式来进行数据传输,应该会方便不少。


目前 WFS 有 2.0.2、2.0.0、1.1.3、1.1.0 和 1.0.0 等多个版本,不过有 4 种操作是每个版本都有并且比较常见的,如下表 1 所示。由于有的操作与 WMS 比较类似,有的操作又比较繁琐,在下面的介绍中就不再对参数进行穷举说明,以实际的例子为主。


【表 1 WFS 支持的操作】



2. GetCapabilities


这个操作与 WMS 的 GetCapabilities 操作比较类似,都是生成描述服务器提供的 WFS 服务能力的元数据信息。例如我们在浏览器地址栏中输入如下地址:


http://localhost:8080/geoserver/wfs?service=wfs&version=2.0.0&request=GetCapabilities
复制代码


此时会返回一个 XML 文件,如下图所示:



3. DescribeFeatureType


在请求实际数据之前,往往需要知道要请求要素类型的信息,此时可以使用 DescribeFeatureType 操作。除此之外,该操作还可以获取属性的字段名称,以及字段类型。例如我们获取第 8.1.3 节发布的矢量要素 test:multipolygons 的类型,可通过如下地址来进行访问:


http://localhost:8080/geoserver/wfs?service=wfs&version=2.0.0&request=DescribeFeatureType&typeName=test:multipolygons&outputFormat=application/json
复制代码


由于我们设置了输出类型为 JSON,因此会返回一个 JSON 数据,如下图 8.34 所示:



4. GetFeature


4.1 Get 访问方式


接下来就是 WFS 中最重要的操作 GetFeature 了,通过该操作可以返回矢量数据源的要素信息,包括几何信息和属性信息。例如,要获取矢量要素的全部信息,可通过如下地址来进行访问:


http://localhost:8080/geoserver/wfs?service=wfs&version=2.0.0&request=GetFeature&typeNames=test:multipolygons&outputFormat=application/json
复制代码


此时返回的是所有的 350 个要素信息,如下图所示:



很多时候返回所有的要素信息并不是我们想要的,我们希望进行空间查询,例如查找一个矩形范围内要素,那么可以通过在浏览器中输入如下地址来实现:


http://localhost:8080/geoserver/wfs?service=wfs&version=2.0.0&request=GetFeature&typeNames=test:multipolygons&outputFormat=application/json&srsName=EPSG:4326&bbox=38.8954267799311,-77.039412232917,38.8965224165805,-77.0380063000187
复制代码


其中 srsName 表示空间坐标参考,bbox 表示具体的四至范围。此时的返回结果如下图所示,可以看到返回的矢量要素只有 21 个了:



如果我们要进行属性查询,例如查找特定要素 ID 的特定属性值,可通过在浏览器中输入如下地址来实现:


http://localhost:8080/geoserver/wfs?service=wfs&version=2.0.0&request=GetFeature&typeNames=test:multipolygons&outputFormat=application/json&featureID=multipolygons.2&propertyName=name,building
复制代码


featureID 表示要素 Id,propertyName 表示要素字段名。此时返回的结果可以看到该要素具体的属性值,如下图所示:



4.2 Post 访问方式


以上几种方式都是通过在浏览器中输入如下地址,也就是通过 HTTP 协议的 Get 请求来实现。但是如果进行空间查询的参数数据量特别大,比如查询一个多边形范围内的要素就很麻烦了。虽然仍然可以通过给 Get 请求的 filter 参数传递一个 XML 格式的文本字符串的方式来实现,但是可能会受到 URL 长度的限制。因此,复杂的空间查询最好通过 POST 请求来实现。


不过,使用 Post 访问方式的示例就要麻烦一点。为了避免在访问 WFS 服务时遇到跨域问题,我们需要发布一个静态网页,通过 JavaScript 来实现 Post 请求。具体操作是新建一个 test.html 文件夹,内容如下例 1 所示:


【例 1 给 WFS 发送 Post 请求】


<!DOCTYPE html><html lang="en">
<head> <meta charset="UTF-8"> <title>test handle response</title> <script> var url = "http://localhost:8080/geoserver/wfs"; var xhr = new XMLHttpRequest(); xhr.open("POST", url); //xhr.open("GET", url); xhr.setRequestHeader("Content-Type", "text/xml"); xhr.onload = function (e) { if (xhr.readyState === 4) { if (xhr.status === 200) { console.log(xhr.responseText); } else { console.error(xhr.statusText); } } };
xhr.onerror = function (e) { console.error(xhr.statusText); };
var xml = `<?xml version='1.0' encoding='UTF-8'?><wfs:GetFeature service=\"WFS\" version=\"2.0.0\" outputFormat=\"json\" xmlns:wfs=\"http://www.opengis.net/wfs/2.0\" xmlns:fes=\"http://www.opengis.net/fes/2.0\" xmlns:gml=\"http://www.opengis.net/gml/3.2\" xmlns:test=\"https://test\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd http://www.opengis.net/gml/3.2 http://schemas.opengis.net/gml/3.2.1/gml.xsd\"> <wfs:Query typeNames='test:multipolygons'> <fes:Filter> <fes:Intersects> <fes:ValueReference>test:the_geom</fes:ValueReference> <gml:Envelope srsName=\"EPSG:4326\"> <gml:lowerCorner> -77.039412232917 38.8954267799311 </gml:lowerCorner> <gml:upperCorner> -77.0380063000187 38.8965224165805 </gml:upperCorner> </gml:Envelope> </fes:Intersects> </fes:Filter> </wfs:Query></wfs:GetFeature>`; xhr.send(xml); </script></head><body></body></html>
复制代码


然后将这个文件放入到一个新的文件夹 geoservertest,最后将 geoservertest 文件夹放入到 Tomcat 的项目发布目录 webapps 中,如下图所示:



在这个示例中,使用了 XMLHttpRequest 来发送 Post 请求,并且在请求头中标明数据内容是一个 XML 文件。我们这里使用的是一个 XML 格式的文本字符串,实际上我们要传输的 XML 数据内容经过格式化如下所示:


<?xml version='1.0' encoding='UTF-8'?><wfs:GetFeature service="WFS" version="2.0.0" outputFormat="json"  xmlns:wfs="http://www.opengis.net/wfs/2.0"  xmlns:fes="http://www.opengis.net/fes/2.0"  xmlns:gml="http://www.opengis.net/gml/3.2"  xmlns:test="https://test"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd http://www.opengis.net/gml/3.2 http://schemas.opengis.net/gml/3.2.1/gml.xsd">  <wfs:Query typeNames='test:multipolygons'>    <fes:Filter>      <fes:Intersects>        <fes:ValueReference>test:the_geom</fes:ValueReference>        <gml:Envelope srsName="EPSG:4326">          <gml:lowerCorner>            -77.039412232917 38.8954267799311          </gml:lowerCorner>          <gml:upperCorner>            -77.0380063000187 38.8965224165805          </gml:upperCorner>        </gml:Envelope>      </fes:Intersects>    </fes:Filter>  </wfs:Query></wfs:GetFeature>
复制代码


我们可以看到 XML 其中一些属性和属性的值就是之前的参数,例如 service="WFS"、version="2.0.0"、outputFormat="json"以及 typeNames='test:multipolygons'。而 fes:filter 正是前面提到的用于设置过滤数据的元素;fes:Intersects 则表示相交,test:the_geom 表示相交查询要素的几何字段名称;gml:Envelope 整个节点则通过 GML(Geographic Markup Language,地理标记语言)描述了一个矩形范围。


我们在浏览器输入访问地址:http://localhost:8080/geoservertest/test.html ,打开浏览器调试器,可以看到在浏览器控制台输出了返回的信息。也可以检查该访问请求,查看具体的返回信息,如下图所示。可以看到返回的要素个数和前面 Get 请求的结果一样,也是 21 个要素。这是因为我们空间查询输入的四至范围是一样的。不过 Post 请求可以通过 GML 构造复杂的几何要素来进行空间查询,这时 Get 请求不能做到的。



5. Transaction


Transaction 操作可以创建、修改和删除 WFS 发布的要素,加上 GetFeature 的查询操作,就组成了类似于处理常规数据库数据的“增删改查”操作。区别只在 WFS 服务的 Transaction 和 GetFeature 操作针对的是远端的地理空间数据。这也是将这个操作命名为 Transaction(事务)的原因。简要来说,Transaction 操作支持四个动作(Action),分别是 Insert(插入)、Replace(替换)、Update(更新)和 Delete(删除)。由于 Transaction 操作也比较复杂,通常使用 Post 请求来实现。


还是使用例 1 所示的 test.html 页面来进行 WFS 的 Transaction 操作。由于 WFS 操作 Post 请求发送的请求的文件头都差不多,区别主要在于发送的内容,也就是 XML 数据;那么我们就只需要修改发送的 XML 格式字符串就可以了。因此,Transaction 操作所使用的示例与例 1 相同,这里只列出具体的 XML 数据。


5.1 Insert


既然我们要插入一个要素,首先就需要描述一个要素信息来进行传输。但是 WFS 要求请求的要素信息都是 GML 描述的,比如这里我们的示例矢量数据类型是面要素(multipolygon),那么应该如何去描述呢?最简单的方式是通过 GetFeature 查看默认格式的要素信息,就可以看到 GML 描述的要素,如下所示:


<test:multipolygons gml:id="multipolygons.5">  <gml:name/>  <test:the_geom>      <gml:MultiSurface srsName="http://www.opengis.net/gml/srs/epsg.xml#4326" srsDimension="2" gml:id="multipolygons.5.the_geom">          <gml:surfaceMember>              <gml:Polygon gml:id="multipolygons.5.the_geom.1">                  <gml:exterior>                      <gml:LinearRing>                          <gml:posList>-77.0383595 38.8960779 -77.0383609 38.8961371 -77.0383618 38.8961764 ... -77.0383595 38.8960779</gml:posList>                      </gml:LinearRing>                  </gml:exterior>                  <gml:interior>                      <gml:LinearRing>                          <gml:posList>-77.0386713 38.8958537 -77.0387129 38.8958542 -77.0387253 38.8958338 ... -77.0386713 38.8958537</gml:posList>                      </gml:LinearRing>                  </gml:interior>              </gml:Polygon>          </gml:surfaceMember>      </gml:MultiSurface>  </test:the_geom>  <test:osm_id>3211113</test:osm_id>  <test:osm_way_id/>  <test:type>multipolygon</test:type> </test:multipolygons>
复制代码


这段 GML 描述,如果我们对矢量比较熟悉的话,理解起来就会非常容易。一个面要素可能有一个外环和多个内环。环是起点和终点为同一个点的线串,线串由一系列连续的点组成。我们可以仿照这个格式,也创建一个 GML 格式的要素信息,将其嵌入到要传输的 XML 数据中。具体的插入要素要发送 Post 请求的 XML 数据如下所示:


<?xml version="1.0"?><wfs:Transaction service="WFS" version="2.0.0"    xmlns:test="https://test"    xmlns:fes="http://www.opengis.net/fes/2.0"    xmlns:gml="http://www.opengis.net/gml/3.2"    xmlns:wfs="http://www.opengis.net/wfs/2.0"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd http://www.opengis.net/gml/3.2 http://schemas.opengis.net/gml/3.2.1/gml.xsd">    <wfs:Insert>        <test:multipolygons gml:id="multipolygons.351">            <test:the_geom>                <gml:MultiSurface srsName="http://www.opengis.net/gml/srs/epsg.xml#4326" srsDimension="2" gml:id="multipolygons.352.the_geom">                    <gml:surfaceMember>                        <gml:Polygon gml:id="multipolygons.351.the_geom.1">                            <gml:exterior>                                <gml:LinearRing>                                    <gml:posList>-77.039412232917 38.8954267799311 -77.039412232917 38.8965224165805 -77.0380063000187 38.8965224165805 -77.0380063000187 38.8954267799311 -77.039412232917 38.8954267799311</gml:posList>                                </gml:LinearRing>                            </gml:exterior>                        </gml:Polygon>                    </gml:surfaceMember>                </gml:MultiSurface>            </test:the_geom>        </test:multipolygons>    </wfs:Insert></wfs:Transaction>
复制代码


在这个 XML 中我们可以看到一些熟悉的配置,例如 service="WFS",version="2.0.0"等。wfs:Insert 表示使用 wfs 的插入操作,test:multipolygons 则索引到我们要插入的要素图层名称。test 是我们在前文中创建的工作空间,我们同时还创建了对应的命名空间 URI:https://test ;工作空间需要与命名空间 URI 相关联,这也是为什么要写 xmlns:test="https://test"。除此之外,剩下的就是通过 GML 描述的面要素了,可以看到我们构建了一个四边形。


同样的还是在浏览器输入访问地址 http://localhost:8080/geoservertest/test.html 来发送 Post 请求。如果一切顺利的话,再通过 GetFeature 操作(http://localhost:8080/geoserver/wfs?service=wfs&version=2.0.0&request=GetFeature&typeNames=test:multipolygons&outputFormat=application/json )就可以看到刚刚插入的新的要素,如下图所示:



5.2 Replace


有了 Insert 操作作为基础,理解 Replace 的实现就非常容易了。Replace 操作 Post 请求需要传输的 XML 数据如下:


<?xml version='1.0' encoding='UTF-8'?><wfs:Transaction version="2.0.0" service="WFS"    xmlns:test="https://test"    xmlns:fes="http://www.opengis.net/fes/2.0"    xmlns:wfs="http://www.opengis.net/wfs/2.0"    xmlns:gml="http://www.opengis.net/gml/3.2"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs/2.0			http://schemas.opengis.net/wfs/2.0/wfs.xsd">    <wfs:Replace>        <test:multipolygons gml:id="multipolygons.351">            <test:the_geom>                <gml:MultiSurface srsName="http://www.opengis.net/gml/srs/epsg.xml#4326" srsDimension="2" gml:id="multipolygons.352.the_geom">                    <gml:surfaceMember>                        <gml:Polygon gml:id="multipolygons.352.the_geom.1">                            <gml:exterior>                                <gml:LinearRing>                                    <gml:posList>-77.039412232917 38.8954267799311 -77.039412232917 38.8965224165805 -77.0380063000187 38.8965224165805 -77.039412232917 38.8954267799311                                    </gml:posList>                                </gml:LinearRing>                            </gml:exterior>                        </gml:Polygon>                    </gml:surfaceMember>                </gml:MultiSurface>            </test:the_geom>        </test:multipolygons>        <fes:Filter>            <fes:ResourceId rid="multipolygons.351"/>        </fes:Filter>    </wfs:Replace></wfs:Transaction>
复制代码


可以看到 XML 数据内容与 Insert 操作差不多,不过要注意的是多了一个 fes:Filter 元素来帮助选定到具体需要替换的要素。最后通过 GetFeature 操作查询替换的要素如下图所示,可以看到我们将一个四边形要素替换成了三角形:



5.3 Update


前面 Insert 和 Replace 操作的对象都是要素的几何信息,其实要素的属性信息也可以修改。例如可以通过 Update 操作来更新要素的属性信息,其 Post 请求需要传输的 XML 数据如下:


可以看到我们为这个新增加并且替换后的要素更新了两个属性字段(name和other_tags)的值,通过GetFeature操作查询要素的结果如下图所示:
图8.42 WFS的Transaction操作的Update(更新)结果
5.4 Delete最后就让我们形成一个回环,将这个新增并且修改的矢量要素删除掉吧,Delete操作的Post请求需要传输的XML数据如下:
复制代码


可以看到我们为这个新增加并且替换后的要素更新了两个属性字段(name 和 other_tags)的值,通过 GetFeature 操作查询要素的结果如下图所示:



5.4 Delete


最后就让我们形成一个回环,将这个新增并且修改的矢量要素删除掉吧,Delete 操作的 Post 请求需要传输的 XML 数据如下:


<?xml version='1.0' encoding='UTF-8'?><wfs:Transaction version="2.0.0" service="WFS"    xmlns:fes="http://www.opengis.net/fes/2.0"    xmlns:wfs="http://www.opengis.net/wfs/2.0"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs/2.0                       http://schemas.opengis.net/wfs/2.0/wfs.xsd">    <wfs:Delete typeName="test:multipolygons">        <fes:Filter>            <fes:ResourceId rid="multipolygons.351"/>        </fes:Filter>    </wfs:Delete></wfs:Transaction>
复制代码


经过 GetFeature 操作查询后,我们发现这个矢量数据的要素个数又回到了 350 个,如下图所示:



6 注意事项


除了以上四种常用的操作,WFS 还有一些其他操作,有的操作还是特定版本特有的,篇幅所限笔者这里就不介绍了。另外,相信读者也能感受到,WFS 提供的一些操作确实非常复杂繁琐。对于空间数据的增删改查,直接使用地理数据库+定制的后端接口也许更为方便安全一些。


文章转载自:charlee44

原文链接:https://www.cnblogs.com/charlee44/p/17978722

体验地址:http://www.jnpfsoft.com/?from=001

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
网络要素服务(WFS)详解_开发_快乐非自愿限量之名_InfoQ写作社区