--- title: WFS服务的CRUD date: 2020-11-25 author: ac tags: - wfs - Openlayers categories: - GIS --- > CRUD的原理 在Openlayers中对WFS服务CRUD的支持都封装在`ol.format.WFS`的解析器里面,查询使用其中的`writeGetFeature`方法创建查询要素的XML文本,增加、修改、删除使用其中的`writeTransaction`方法创建事务的XML文本,最后通过发送`POST`请求将XML文本发送到地图服务器进行CRUD的操作。 ### 1 GetFeature WFS的查询是通过`GetFeature`接口实现的。在发送`GetFeature`请求时,使用Filter Encoding或CQL(Common Query Language)增加过滤的表达式,来筛选要素。 #### 1.1 CQL filters 在WFS GetFeature GET请求中,可以使用cql_filter参数来指定ECQL(Extended Common Query Language)格式的过滤器,通过过滤器筛选符合条件的要素集。 CQL是OGC创建一种查询语言,与基于xml的过滤器编码语言不同,CQL使用熟悉的基于文本的语法编写。因此,它更具可读性,更易于编码。然而,CQL也有一些局限性。例如,它不能对id过滤器进行编码,并且它要求在任何比较操作符的左侧都有一个属性。由于这个原因,GeoServer提供了CQL的扩展版本,称为ECQL。ECQL消除了CQL的限制,提供了一种与SQL有更强相似性的更灵活的语言。 CQL和ECQL常用与`WMS`服务的`GetMap`请求和`WFS`的`GetFeature`请求中,另外CQL还应用于SLD样式文件中。它们在功能上类似于SQL的“WHERE”子句。过滤器是使用条件指定的。 版本适用范围: - Filter Encoding 1.0 适用于 WFS1.0和SLD1.0 - Filter Encoding 1.1 适用于 WFS1.1。ECQL完全适用于1.1的标准。 - Filter Encoding 2.0 适用于 WFS2.0 > Filter Encoding 是一种基于XML的过滤器编码语言。 示例: ```xml ``` image854218 **比较运算符** - ``:等于 - ``:不等于 - ``:小于 - ``:小于或等于 - ``:大于 - ``:大于或等于 - ``:模糊匹配 - ``:不等于null - ``:在区间内 > 这些比较运算符中都包含两个元素,属性名称\和属性值\,根据版本不同会有所变化,但都是用于确定运算的属性和属性值。 **逻辑运算** - `` - 逻辑与 - `` - 逻辑或 - `` - 非 **空间运算** - `` - 测试几何属性字段中几何要素是否与给定的几何要素相交 - `` - 测试两个几何是否不相交 - `` - 测试几何属性字段中几何要素是否包含给定的几何要素 - `` - 测试几何属性字段中几何要素是否在另一个给定的几何要素内 `Openlayers`将这些与`GetFeature`操作相关的过滤操作都封装在`ol.format.filter`命名空间下:
image45038
其中的`ol.format.filter.During`是用于比较日期的,如果要素表中存在日期字段或时间戳字段,在进行比较时,需要转成`ISO-8601`时间格式的字符串形式。 >2020年12月8日20点0分0秒可以写成(带时区):2020-12-08T20:00:00+08:00 或 20201208T200000+08 **Filter示例** ```html OpenLayers example

OGC WFS服务:加载GeoServer的WFS服务

``` 示例中创建了一个面作为空间运算的输入,并创建对应的数据源和图层将其添加map中。通过`ol.format.WFS`中的`writeGetFeature()`方法创建`getFeature`操作的XML文本。再使用jquery或fetch发送`POST `请求获取要素集,最后使用对应的解析器解析响应结果,添加到矢量数据源中。 > jquery发送请求的Headers中的Content-Type默认是application/x-www-form-urlencoded表单,当发送XML文本是需要将其指定为application/xml。而fetch()方法发送的请求,会根据body中的数据自动设置Content-Type的类型。 当`outputFormat`参数设置为`application/json`时,请求结果会以`GeoJSON`的格式响应回来,可以使用`ol.format.GeoJSON`解析器进行解析。当`outputFormat`参数没有被设置时,默认是以XML的形式返回,可以使用`ol.format.WFS`解析器进行解析。 #### 1.2版本差异 1. 返回的GML版本不同 - WFS2.0.0请求默认返回的格式是GML3.2; - WFS1.1.0请求默认返回的格式是GML3; - WFS1.0.0请求默认返回的格式是GML2; 2. GeoServer 对于每种支持的GML格式,可以选择不同的SRS格式。 SRS Style:对返回数据的轴顺序有影响,对几何字段的坐标也会有影响。EPSG Code:以格式EPSG:XXXX(例如EPSG:4326)返回典型的EPSG号。这会以经度/纬度(x / y)的顺序格式化地理坐标。 | 版本 | GML | SRS | Axis ordering | | --------------------- | ------ | ------------------------------------------------------------ | ----------------------- | | WFS1.0.0 | GML2 | OGC HTTP URL (http://www.opengis.net/gml/srs/epsg.xml#4326)或EPSG:xxxx | longitude/latitude(x/y) | | WFS1.1.0 | GML3 | urn:x-ogc:def:crs:EPSG:xxxx | latitude/longitude(y/x) | | WFS1.1.1
WFS 2.0.0 | GML3.2 | urn:x-ogc:def:crs:EPSG:xxxx | latitude/longitude(y/x) | 3. WFS 1.1.0和2.0.0支持动态重新投影数据,这支持在本机SRS以外的SRS中返回数据。 4. WFS 2.0.0引入了新版本的过滤器编码规范,增加了对时间过滤器的支持。 5. WFS 2.0.0支持通过GetFeature请求进行联接。 6. WFS 2.0.0增加了通过startIndex和count参数分页GetFeature请求的结果的功能。GeoServer现在在WFS 1.0.0和1.1.0中支持此功能。 7. WFS 2.0.0支持存储的查询,这是存储在服务器上的常规WFS查询,因此可以通过将适当的标识符与WFS请求一起传递来调用它们。 8. WFS 2.0.0支持SOAP(简单对象访问协议)作为OGC接口的替代方法。 ### 2 Transaction 在OWS规范中知道,Web服务体系规范的HTTP接口参数有XML和KVP两种形式,但WFS要求服务的`Transaction`接口必须由XML描述,另外空间数据交互必须由GML进行,数据过滤采用CQL语言。 每个事务将由零个或多个插入、更新和删除元素组成,每个事务元素按顺序执行。 > 在GeoServer中,每个事务都是原子的,这意味着如果任何元素失败,事务就会被放弃,数据也不会被修改。支持事务的WFS服务器有时称为WFS- t服务器。GeoServer完全支持事务。 #### 2.1 GML 地理标记语言(GML)是一种用于表示地理特征的XML语法。GML是地理系统的建模语言,也是Internet上地理事务的开放交换格式。与大多数基于XML的语法一样,语法分为两部分——描述文档的`schema`模式和包含实际数据的实例文档。 image753
GML2 中Geometry schema的UML图
其中``是坐标``的子元素,用于描述坐标值。还添加了``用于描述一个范围 extent,坐标值为范围的左下角(最小值)和右上角(最大值)点。 `GML2`中几何标签的示例: 1. Coordinates ```xml 1.03,2.167 4.167,2.34 ``` 等价于 ```xml 1.032.167 4.1672.34 ``` 注意:坐标值必须是相同的维度。 - cs(coordinate separated):坐标之间的分隔符; - ts(tuples separated):坐标元组之间分隔符; 2. Point ```xml 5.0,40.0 ``` Point 标签下面可以有 coordinates 标签用于描述点位信息。还可以使用ID、srsName 属性,srsName 指定要素的空间参考。 3. Box ```xml 0.0,0.0 100.0,100.0 ``` Box用于描述一个范围 extent,坐标值为范围的左下角(最小值)和右上角(最大值)点。 4. LineString ```xml 0.0,0.0 100.0,100.0 ``` LineString是一个分段的线状路径,用于描述线要素,至少两个点。 5. LinearRing ```xml 0.0,0.0 100.0,0.0 50.0,100.0 0.0,0.0 ``` 是一段封闭的分段的线状路径,(coordinates 标签中)至少4个坐标点,三个坐标可以确定 LinearRing,第四个坐标用于闭合,与第一个坐标相同。用于构造面元素(Polygon),所以不需要SRS属性。 6. Polygon ```xml 0.0,0.0 100.0,0.0 100.0,100.0 0.0,100.0 0.0,0.0 10.0,10.0 10.0,40.0 40.0,40.0 40.0,10.0 10.0,10.0 60.0,60.0 60.0,90.0 90.0,90.0 90.0,60.0 60.0,60.0 ``` 用于描述面要素(surface)。必须有一个且最多只有一个 Exterior boundary 和 零个或多个 Interior boundary。Interior boundary 之间不能相交或包含。路径(坐标)的顺序是顺时针或逆时针不重要。 7. MultiPoint ```xml 56.1,0.45 46.71,9.25 56.88,10.44 ``` > 注意:srsName 属性只能出现在最外层要素的标签内,且其包含的子要素不能再出现 srsName属性。 8. MultiLineString ```xml 56.1,0.45 67.23,0.67 46.71,9.25 56.88,10.44 324.1,219.7 0.45,0.56 ``` 注意:跟 MuliPointElement 标签一样,srsName 属性只能出现在最外层要素上。 9. MultiPolygon ```xml 0.0,0.0 10.0,0.0 10.0,10.0 0.0,10.0 0.0,0.0 40.0,40.0 50.0,40.0 50.0,50.0 40.0,50.0 40.0,40.0 ``` 10. GeometryCollection ```xml 50.0,50.0 0.0,0.0 0.0,50.0 100.0,50.0 100.0,100.0 0.0,0.0 100.0,0.0 50.0,100.0 0.0,0.0 ``` geometryMember 子标签可以包含任意类型的几何对象。 #### 2.2 Insert 使用``标签创建一个新的要素,添加到WFS服务的数据源中。默认是使用`GML3`(WFS1.1.0)来描述要创建的新要素,也可以使用`inputFormat`属性来兼容旧的`GML2`(inputFormat='text/xml;subtype=2.1.2')。当新的要素添加到数据源中时,会默认给新生的要输生成一个`gid`作为唯一标识来标识该要素。若想指定`gid`的值,可以只用`idgen`属性,将其设置为`UseExisting`,在插入的要素标签中添加`gml:id`属性指定`gid`的值,如果所给的值已经存在或数据类型不匹配,则会插入失败。 ```xml A94 测试 -8237795.685217111,4980323.905513588 -8238827.585098961,4978584.963120101 ``` image9076 > PostGIS导入的shapefile文件的srid默认为0。新增加的要素带有空间参考srid,所以如果在导入文件的时候没有指定srid,则会不一致,新增的要素在PostgreSQL的几何查看器中显示不出来。 可能出现的问题:`is read-only` 解决方法:放开write权限。在GeoServer左侧的"Security"中的"Data"栏,点击"Rule path"中的"..w",勾选"Grant access to any role"下面的单选框,点击保存。 ```xml {http://localhost:8080/test}tiger_roads is read-only ``` 在`writeTransaction`方法中笔者没有查找到关于`idgen`属性的配置方式,直接在创建Feature实例时添加`gid`是无效的,`gid`的生成方式是由服务端自动生成。 ```html OpenLayers example

OGC WFS服务:Transaction Insert

``` #### 2.3 Update ``标签用于修改单个Feature要素或要素集合,多个``标签可以出现在同一个`Transaction`事务里面。``标签里面包含一个或多个用于指定要修改的属性的``标签和一个用于筛选要素的``标签,对于没有使用` name 测试Insert412 test:name 测试Insert4* ``` 从上面的示例中可以看出,发送Transaction事务的XML文本可以实现类似于SQL语句一样的`Update`操作,但在`writeTransaction`方法中并没有提供类似`Filter`过滤的参数,而是直接将需要修改的Feature要素作为输入,进行修改。通常的作法是: 1. 先加载WFS服务; 2. 在通过`ol.interaction.Select`和`ol.interaction.Modify`控件选择WFS服务的要素,并对其进行修改,监听修改完的事件,获取要素修改后的`geometry`及其属性; 3. 还可以提供一个表单或弹出层界面来修改属性值; 4. 将最终修改得到的要素传入`writeTransaction`方法创建事务的XML文本,发送请求提交。 ```html OpenLayers example

OGC WFS服务:Transaction Update

``` 修改的要素必须要有id,因为最终发送的Transaction的XML文本中,是通过`GmlObjectId`作为过滤表达式Filter筛选要素的。 > 修改操作需要注意,字段的类型。如果所给的属性值于字段类型不匹配或长度越界了,将会修改失败。 #### 2.4 Delete ``用于删除一个或多个要素,跟``、``标签一样通过``标签筛选要操作的要素。如果没有使用``标签对要素进行筛选,则该事务中的删除操作将无效。 ```xml ``` 删除操作和更新操作一样也是根据Feature的id来进行操作的。这里我们使用`ol.interaction.Select`控件来选择要删除的要素,在确定删除操作成功后,从矢量数据源中移除被删除的要素和清空在`Select`控件中已选要素的集合。 ```html OpenLayers example

OGC WFS服务:Transaction Delete

``` ### 3 WFS运算示例 > WFS版本使用1.1.0 #### 3.1 简单查询 1. 查询该服务(图层)的所有要素 ```xml ``` 2. 指定返回的结果集中要素的属性(属性名称需要大写,XML是大小写敏感的,且需要添加工作空间) ```xml topp:STATE_NAME ``` 3. 指定响应数据的格式 ```xml topp:STATE_NAME ``` 4. 查询多个服务(图层) ```xml ``` #### 3.2 比较运算 比较运算符可以说是表达式,应该放在Filter标签里面 1. 查找哪个州的总人口person等于3145585.0 ```xml topp:PERSONS 3145585.0 ``` 2. 模糊匹配,州名称以W开头的 ```xml topp:STATE_NAME W* ``` wildCard :指定匹配零个或多个字符串字符的任何序列的模式字符 singleChar :指定匹配任何单个字符串字符的模式字符 escapeChar :指定可用于转义模式字符的转义字符 3. 查询state_name属性为null的记录 ```xml topp:STATE_NAME ``` 4. 查询人口总数在[2477574,4781468]之间的州,并根据总人口persons属性倒序排序 ```xml test:persons test:persons 2477574 4781468 test:persons DESC ``` 注意: - 需要将state的shapefile文件导入postgresql中,发布postgis表的数据图层,否则将会因为无法序列化,而不能排序 - 入库后,postgresql表中的属性字段是默认小写的,所以PropertyName标签中的属性值应小写 - 用于排序的字段必须是结果集中存在的字段,即存在标签\值为排序属性 #### 3.3空间运算 1. Intersects:相交 ```xml test:cfcc test:name geom -8236751.648901709 4974597.071767016 -8235757.967534002 4975476.097592296 ``` ![image55382](./images/image55382.png) 2. Disjoint:不相交。下面将逻辑运算和空间运算结合(取不相交的非,得到与上面1一样的结果) ```xml test:cfcc test:name geom -8236751.648901709 4974597.071767016 -8235757.967534002 4975476.097592296 ``` 3. Contains:判断几何对象是否包含另一个几何(几何属性的要素是否包含所给的几何对象) ```xml test:cfcc test:name geom -8234663.297809127 4976806.185109221 ``` 常见的包含关系存在于线包含点,面包含线。 4. Within:判断几何属性字段中的几何要素是否被给定的另一个几何对象包含 ```xml test:cfcc test:name geom -8236070.085302306 4974636.883205217 -8234716.512732034 4974464.899891576 -8234716.512732034 4973573.134464388 -8236391.757990729 4973614.537951903 -8236070.085302306 4974636.883205217 ``` ![image47126](./images/image47126.png) #### 3.4 Transaction 1. Insert ```xml A94 测试 -8237795.685217111,4980323.905513588 -8238827.585098961,4978584.963120101 ``` 2. update ```xml name 测试Insert312 ``` 3. delete ```xml ``` ### 参考文章 [1] [WFS Reference](https://docs.geoserver.org/latest/en/user/services/wfs/reference.html) :`https://docs.geoserver.org/latest/en/user/services/wfs/reference.html` [2] [Filter Reference](https://docs.geoserver.org/2.12.2/user/filter/filter_reference.html) :`https://docs.geoserver.org/2.12.2/user/filter/filter_reference.html` [3] [Filter Encoding](https://www.ogc.org/standards/filter) :`https://www.ogc.org/standards/filter`