meface/docs/article/gis/openlayers/73serviceload.md

645 lines
26 KiB
Markdown
Raw Normal View History

2023-11-17 10:54:23 +08:00
---
title: 服务的加载
date: 2020-11-18
author: ac
tags:
- Openlayers
- WMTS
- WFS
- WMS
categories:
- GIS
---
## 1. ol中的服务
对于OGC规范的数据显示OpenLayers 封装了一些数据源和解析器,如`ol.source.WMTS`、`ol.source.ImageWMS`、`ol.source.TileWMS`、`ol.format.WFS`等。这些OGC规范的数据显示与其他类型的数据加载方式类似通过图层Layer+ 数据源Source的方式加载显示在实例化数据对象时设置请求OGC服务的URL地址及相关参数。
| 服务类型 | 接口 | 接口描述 | 类 |
| -------- | ---------- | -------------------------------------- | ------------------------------------------------------------ |
| WMTS | GetTile | 返回单张瓦片 | `ol.layer.Tile`+`ol.source.WMTS` |
| WMS | GetMap | 返回指定范围内的地图图片 | `ol.layer.Image`+`ol.source.ImageWMS`或<br>`ol.layer.Tile`+`ol.source.TileWMS` |
| WFS | GetFeature | 返回指定格式GML或GeoJSON的矢量数据 | `ol.layer.Vector`+`ol.source.Vector` |
下面以GeoServer地图服务器自带提供的Demo数据服务为示例数据演示OGC服务的加载
<img src="./images/image-20201124144843991.png" alt="image-20201124144843991" style="zoom: 80%;" />
### 1.1 WMTS服务
为了更快的将地图数据在前端显示可以为一些数据不会变更或变动较小的服务创建地图缓存Cache。WMTS是一种采用图像金字塔的方式将地图服务按照预先设置的某种切图策略创建的地图缓存服务。
<img src="./images/20200318213818325.png" alt="20200318213818325" style="zoom: 67%;" />
**切图原理**
1.基础概念
- Scale比例尺即地图上的一厘米代表着实际上的多少厘米。原 scale 中表示的实际单位是厘米
- Resolution分辨率实际含义代表当前地图范围内1像素代表多少地图单位地图单位/像素),地图单位取决于数据本身的空间参考。
- dpi :代表每英寸的像素数
2.地图比例尺的换算
在配置切片策略的时候对金字塔的每个级别切图的比例尺参数都需要一个level 和 resolution 这些是用于计算屏幕上1像素代表的实际距离计算。
假设地图的坐标单位是米dpi为96ArcGIS中dpi默认是96 OGC标准输出的resolution is 90 DPI(25.4/0.28)
> 1英寸=2.54厘米; 1英寸=96像素
>
> 最终换算的单位是米;
>
> 如果当前地图比例尺为1:125000000则代表图上1米等于实地125000000米
>
> 米和像素间的换算公式:
>
> 1英寸=0.0254米=96像素
>
> 1像素=0.0254/96 米
>
> 则根据1125000000比例尺图上1像素代表实地距离是 125000000\*0.0254/96 = 33072.9166666667米 , 即 resolution = scale*0.0254/dpi
3.瓦片行列号的计算
假设,地图的切图原点(通常是地图的左上角(minx,maxy)x0,y0地图的瓦片大小是tileSize地图屏幕上1像素代表的实际距是resolution。计算坐标点x,y所在的瓦片的行列号的公式是
<div style="text-align:center">col = floor((x - x0 )/( tileSize *resolution)</div>
<div style="text-align:center"> row = floor((y0 - y )/( tileSize *resolution)) </div>
<div style="text-align:center">resolution = scale*0.0254/dpi</div>
**加载示例**
```html
<!doctype html>
<html >
<head>
<link rel="stylesheet" href="css/ol.css" type="text/css">
<style>
.map {
height: 90vh;
width: 100%;
}
</style>
<script src="lib/ol.js"></script>
<title>OpenLayers example</title>
<meta charset="UTF-8">
</head>
<body>
<h2>OGC WMTS服务加载GeoServer的WMTS服务</h2>
<div id="map" class="map"></div>
<script type="text/javascript">
var nyc = [-73.92722,40.774221];
var map = new ol.Map({
target:"map",
layers:[
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view:new ol.View({
center:ol.proj.fromLonLat(nyc),
zoom:12
})
});
//分辨率数组应与gridset中的Tile Matrix Set的Pixel Size保持一致
var resolutions = [0.703125, 0.3515625, 0.17578125, 0.087890625, 0.0439453125, 0.02197265625, 0.010986328125,0.0054931640625, 0.00274658203125, 0.001373291015625, 6.866455078125E-4, 3.4332275390625E-4,1.71661376953125E-4, 8.58306884765625E-5, 4.291534423828125E-5, 2.1457672119140625E-5, 1.0728836059570312E-5,5.364418029785156E-6, 2.682209014892578E-6, 1.341104507446289E-6, 6.705522537231445E-7, 3.3527612686157227E-7];
//矩阵标识列表应与gridset中的Tile Matrix Set的Name保持一致
var matrixIds = ['EPSG:4326:0', 'EPSG:4326:1', 'EPSG:4326:2', 'EPSG:4326:3', 'EPSG:4326:4','EPSG:4326:5', 'EPSG:4326:6', 'EPSG:4326:7', 'EPSG:4326:8', 'EPSG:4326:9', 'EPSG:4326:10', 'EPSG:4326:11','EPSG:4326:12', 'EPSG:4326:13', 'EPSG:4326:14', 'EPSG:4326:15', 'EPSG:4326:16', 'EPSG:4326:17', 'EPSG:4326:18','EPSG:4326:19', 'EPSG:4326:20', 'EPSG:4326:21'];
var wmtsSource = new ol.source.WMTS({
url: 'http://localhost:8080/geoserver/gwc/service/wmts',
layer: 'tiger:tiger_roads',
matrixSet: 'EPSG:4326',
format: 'image/png',
projection: 'EPSG:4326',
//瓦片网格对象即切图方案gridset的配置
tileGrid: new ol.tilegrid.WMTS({
extent: [-180,-90, 180, 90],//范围
tileSize: [256, 256],
origin: [-180,90], //切图原点(左上角:minx,maxy
resolutions: resolutions,
matrixIds: matrixIds
}),
tileLoadFunction:function(imageTile, src) {
imageTile.getImage().src = src;
}
});
var wmtsLayer = new ol.layer.Tile({
source: wmtsSource,
//设置图层的边界坐标参考与view中保持一致
extent:ol.proj.transformExtent([-74.02722,40.684221,-73.907005,40.878178],
"EPSG:4326","EPSG:3857")
});
map.addLayer(wmtsLayer);
</script>
</body>
</html>
```
<img src="./images/image-2020112417081065.png" alt="image-2020112417081065" style="zoom:80%;" />
**解析**
通过创建`ol.source.WMTS`实例配置WMTS服务信息
- urlWMTS服务的地址
- layer服务的图层名或图层组名格式是命名空间+“:”+图层名|图层组名
- matrixSet切图策略的名称GridSet的名称
- format指定响应瓦片的格式可以在图层信息中的“Tile Caching”中查看
> image/jpeg是只有[r,g,b]三个通道的但image/png是有[r,g,b,a]四个通道的可以实现背景透明的效果
- projection配置服务所属的坐标参考。如果与view中的参考不同会进行重投影
- tileGrid瓦片网格对象即是服务的切图方案的配置对象应与服务对应的GridSet保持一致才能正确的计算瓦片的级别和行列号。
- tileLoadFunction瓦片加载函数。默认是上述的脚本是WMTS服务的GetTile请求URL。
在创建Tile图层实例时增加Extent参数限制数据源加载瓦片的边界服务图层的边界避免出现行列号索引越界的400的请求。
<img src="./images/image-20201125113955112.png" alt="image-20201125113955112" style="zoom: 80%;" />
### 1.2 WMS服务
WMS服务是一个动态数据或用户定制地图需结合SLD标准的理想解决方案。它可以动态生成当前地图视图范围内的图层服务的图片并响应到前端供地图展示还提供根据地图上像素点获取图层要素信息的查询操作。
| 接口 | 描述 |
| --------------- | ------------------------------------ |
| GetCapabilities | 获取服务中的要素类及支持的操作 |
| GetMap | 获取服务器生成的地图图片 |
| GetFeatureInfo | 根据地图上的像素点获取详细的要素信息 |
**GetMap操作**
核心参数指定要在地图上显示的一个或多个图层和样式,地图范围的边界框,目标空间参考系统以及输出的宽度,高度和格式。
| 参数 | 是否必须 | 说明 |
| -------------- | -------- | --------------------------------------- |
| version | 是 | WMS服务版本默认1.3.0 |
| request=GetMap | 是 | 请求类型 |
| layers | 是 | 逗号分隔的一个或多个图层列表 |
| styles | 是 | 逗号分隔的一个或多个图层的样式列表。 |
| crs | 是 | 坐标参考1.1.1是srs1.3.0是crs |
| bbox | 是 | CRS下的边框角(左下,右上) |
| width | 是 | 输出的地图图片宽度 |
| height | 是 | 输出的地图图片高度 |
| format | 是 | 指定请求响应的输出格式 |
| transparent | | 图层是否透明默认true |
| bgcolor | | 响应图片的背景色十六进制的rgb颜色值 |
| exceptions | | 异常的输出格式默认XML |
| time | | |
| elevation | | |
> layers参数中的值和styles样式参数中的值之间存在一一对应关系。
**版本差异**
版本1.1.1和1.3.0之间的主要区别是:
1. 在1.1.1中使用EPSG名称空间指定的地理坐标系被定义为具有经度/纬度的轴顺序。在1.3.0中,顺序为纬度/经度。
2. 在GetMap操作中srs参数在1.3.0中被crs替换。无论版本如何GeoServer都支持这两个参数。
3. 在GetFeatureInfo操作的x和y参数在1.3.0版中被称为i和j。除处于CITE兼容模式下外GeoServer均支持两个参数而与版本无关。
`geoserver/wms?VERSION=1.1.1&REQUEST=GetMap&SRS=epsg:4326&BBOX=-180,-90,180,90&…`
`geoserver/wms?VERSION=1.3.0&REQUEST=GetMap&CRS=epsg:4326&BBOX=-90,-180,90,180&…`
**请求示例**
http://localhost:8090/geoserver/tiger/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image%2Fpng&TRANSPARENT=true&LAYERS=tiger%3Atiger_roads&exceptions=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A4326&STYLES=&WIDTH=476&HEIGHT=768&BBOX=-74.04888153076172%2C40.6494140625%2C-73.88545989990234%2C40.9130859375
<img src="./images/image-20201202103948415.png" alt="image-20201202103948415" style="zoom: 80%;" />
在OpenLayers中加载WMS服务可以使用以下两种方式进行加载
1. `ol.layer.Image`+`ol.source.ImageWMS`
2. `ol.layer.Tile`+`ol.source.TileWMS`
使用`Tile`的方式加载时响应回来的瓦片会被浏览器缓存当地图视口内的WMS服务被缓存后不会重复请求已经缓存的图片但存在的问题是如果首次发送的GetMap请求一直没有响应图片后续将不会再发送该区域范围内的GetMap请求。在这种情况下相比`Tile`的方式ImageWMS的方式是渲染返回的单张图片会有更好的制图效果。
```html
<!doctype html>
<html >
<head>
<link rel="stylesheet" href="css/ol.css" type="text/css">
<style>
.map {
height: 400px;
width: 100%;
}
</style>
<script src="lib/ol.js"></script>
<title>OpenLayers example</title>
<meta charset="UTF-8">
</head>
<body>
<h2>OGC WMS服务方式一ol.layer.Image + ol.source.ImageWMS</h2>
<div id="map" class="map"></div>
<script type="text/javascript">
var layers = [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
new ol.layer.Image({
source:new ol.source.ImageWMS({
url: 'http://localhost:8080/geoserver/tiger/ows',
params: {
//WIDTH, HEIGHT, BBOX and CRS (SRS for WMS version < 1.3.0) 将被动态设置.
'LAYERS': 'tiger:tiger_roads',
'VERSION':'1.1.1'//默认1.3.0,GeoServer为WMS提供1.1.1和1.3.0版本的支持
},
//远程WMS服务器的类型, mapserver, geoserver or qgis
serverType: 'geoserver',
})
}),
];
var nyc = [-73.92722,40.774221];
var map = new ol.Map({
layers: layers,
target: 'map',
view: new ol.View({
center:ol.proj.fromLonLat(nyc),
zoom: 11
})
})
</script>
</body>
</html>
```
<img src="./images/image908212.png" alt="image908212" style="zoom: 50%;" />
```html
<!doctype html>
<html >
<head>
<link rel="stylesheet" href="css/ol.css" type="text/css">
<style>
.map {
height: 400px;
width: 100%;
}
</style>
<script src="lib/ol.js"></script>
<title>OpenLayers example</title>
<meta charset="UTF-8">
</head>
<body>
<h2>OGC WMS服务方式二ol.layer.Tile + ol.source.TileWMS</h2>
<div id="map" class="map"></div>
<script type="text/javascript">
var layers = [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
new ol.layer.Tile({
source: new ol.source.TileWMS({
url: 'http://localhost:8080/geoserver/tiger/ows',
params: {
//WIDTH, HEIGHT, BBOX and CRS (SRS for WMS version < 1.3.0) 将被动态设置.
'LAYERS': 'tiger:tiger_roads',
'TILED': false,
'VERSION':'1.1.0',
'TRANSPARENT':true//
},
//远程WMS服务器的类型
serverType: 'geoserver',
// 用于渲染的不透明度过渡的持续时间。要禁用不透明过渡设置transition为: 0
transition: 0,
projection:'EPSG:4326'
})
})
];
var nyc = [-73.92722,40.774221];
var map = new ol.Map({
layers: layers,
target: 'map',
view: new ol.View({
center:ol.proj.fromLonLat(nyc),
zoom: 11
})
})
</script>
</body>
</html>
```
<img src="./images/image-20201202111347115.png" alt="image-20201202111347115" />
根据请求参数可以知道,`Tile`的请求方式是将地图当前范围按图块大小(`width`*`height`默认为256\*256分割计算出多个`bbox`,发送多个`GetMap`请求获取地图图片。而`ImageWMS`请求方式的参数中的宽高直接是地图当前视图的宽高,将当前地图视图范围作为`bbox`边界,获取单张地图图片。
### 1.3 WFS服务
[Web Feature Service](http://www.opengeospatial.org/standards/wfs) (WFS)是OGC创建的标准用于在互联网上使用HTTP创建修改和交换矢量格式的地理信息。支持对地理要素的插入、更新、删除、检索和发现服务指定了发现操作、查询操作、锁定操作、事务操作和管理存储的、参数化查询表达式的操作。
服务可以根据HTTP客户请求返回GMLGeography Markup Language地理标识语言数据也可以通过OGC Filter构造查询条件并支持基于空间几何关系的查询和基于属性的查询以及基于空间关系和属性域的共同查询。
| 接口 | 操作类型 | 说明 |
| --------------------- | --------------- | -------------------------------------------------------- |
| GetCapabilities | discovery | 获取该WFS服务支持的操作和服务的元数据文档 |
| DescribeFeatureType | discovery | 获取WFS服务支持的功能类型的描述(图层属性字段等) |
| GetPropertyValue | query | 根据要素属性值或部分属性值进行查询,返回符合表达式的要素 |
| GetFeature | query | 从数据源中,获取指定图层符合条件的数据要素集 |
| GetFeatureWithLock | query & locking | 返回并锁定指定图层的符合要求的要素默认锁300s |
| LockFeature | locking | 对指定的要素进行锁定,以保证一致性 |
| Transaction | transaction | 对要素服务中的数据要素进行增删改的操作 |
| CreateStoredQuery | stored query | 在服务端创建一个存储查询表达式 |
| DropStoredQuery | stored query | 在服务端删除一个存储查询表达式 |
| ListStoredQueries | stored query | 获取服务器上的存储查询变量列表 |
| DescribeStoredQueries | stored query | 获取服务器上的每个存储查询表达式的详细元数据。 |
**GetFeature操作**
GetFeature操作返回的是选择的数据源信息和要素集。所以请求是针对特定的图层名称来执行GetFeature请求namespace:featuretype的。
| 参数 | 是否必须 | 说明 |
| ------------------ | -------- | ------------------------------------------------------- |
| version | 是 | 服务版本默认1.1.0 |
| service | 是 | WFS |
| request=GetFeature | 是 | 请求接口 |
| typename | 是 | 指定操作的图层名称(命名空间+图层名) |
| outputformat | | 输出的文本格式(GML2、GML3、JSON、CSV、JSONP、Shapefile) |
| bbox | | 边界 |
| filter | | 查询条件xml文本(CQL/ECQL) |
| sortby | | 排序1.1.0及以上版本才有效 |
| maxfeatures | | 返回要素集的最大记录数 |
| propertyname | | |
| srsname | | |
| faetureid | | 要素id |
**请求示例**
`http://localhost:8080/geoserver/wfs?request=GetFeature&version=1.1.0&typeName=topp:states&outputFormat=GML2&FILTER=%3CFilter%20xmlns=%22http://www.opengis.net/ogc%22%3E%3CFeatureId%20fid=%22states.23%22/%3E%3C/Filter%3E`
![imag08711](./images/imag08711.png)
**请求流程:**
当一个客户端想要访问WFS服务时一般会涉及到以下的流程
1. 通过操作获取WFS服务支持的操作和要素类FeatureType可以理解为WFS中的数据集)
2. 可能通过操作获取WFS服务支持的要素类的定义。
3. 客户端发送某个操作的请求。
4. WFS服务处理请求。
5. WFS服务返回处理的结果和状态。
**加载示例**
在Openlayers中加载WFS服务是在初始化矢量数据源`ol.source.Vector`时,使用其`url`或`loader`参数的加载函数请求WFS服务的`GetFeature`接口,在函数直接返回`GetFeature`接口的要素集并给数据源设置要素集对应的解析器format或是在函数中解析要素集并添加到数据源中最后使用`ol.layer.Vector`渲染矢量要素。
```html
<!doctype html>
<html >
<head>
<link rel="stylesheet" href="css/ol.css" type="text/css">
<style>
.map {
height: 400px;
width: 100%;
}
</style>
<script src="lib/ol.js"></script>
<title>OpenLayers example</title>
<meta charset="UTF-8">
</head>
<body>
<h2>OGC WFS服务加载GeoServer的WFS服务</h2>
<div id="map" class="map"></div>
<script type="text/javascript">
var nyc = [-73.92722,40.774221];
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
new ol.layer.Vector({
source: new ol.source.Vector({
format: new ol.format.GeoJSON(),
url: function(extent) {
//直接返回WFS的GetFeature接口访问地址设置outputFormat为json格式和format中的解析器一致
return 'http://localhost:8080/geoserver/wfs?service=WFS&' +
'version=1.1.0&request=GetFeature&typename=tiger:tiger_roads&' +
'outputFormat=application/json&srsname=EPSG:3857&' +
'bbox=' + extent.join(',') + ',EPSG:3857';
},
/*
* 加载策略,可选值:
* all一次性加载所有的要素
* bbox加载地图当前视图范围内的要素
* tile基于瓦片格网加载要素
*/
strategy: ol.loadingstrategy.bbox
})
})
],
target: 'map',
view: new ol.View({
center:ol.proj.fromLonLat(nyc),
zoom: 11
})
})
</script>
</body>
</html>
```
```html
<!doctype html>
<html >
<head>
<link rel="stylesheet" href="css/ol.css" type="text/css">
<style>
.map {
height: 400px;
width: 100%;
}
</style>
<script src="lib/ol.js"></script>
<title>OpenLayers example</title>
<meta charset="UTF-8">
</head>
<body>
<h2>OGC WFS服务加载GeoServer的WFS服务</h2>
<div id="map" class="map"></div>
<script type="text/javascript">
var vectorSource = new ol.source.Vector({
format: new ol.format.GeoJSON(),
loader: function(extent, resolution, projection) {
var proj = projection.getCode();
var url = 'http://localhost:8080/geoserver/wfs?service=WFS&' +
'version=1.1.0&request=GetFeature&typename=tiger:tiger_roads&' +
'outputFormat=application/json&srsname='+proj+'&' +
'bbox=' + extent.join(',') + ',' +proj;
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
var onError = function() {
vectorSource.removeLoadedExtent(extent);
}
xhr.onerror = onError;
xhr.onload = function() {
if (xhr.status == 200) {
//获取配置的解析器,解析要素集并添加到数据源中
vectorSource.addFeatures(
vectorSource.getFormat().readFeatures(xhr.responseText));
} else {
onError();
}
}
xhr.send();
},
strategy: ol.loadingstrategy.bbox
});
var nyc = [-73.92722,40.774221];
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
new ol.layer.Vector({
source: vectorSource
})
],
target: 'map',
view: new ol.View({
center:ol.proj.fromLonLat(nyc),
zoom: 11
})
})
</script>
</body>
</html>
```
<img src="./images/image7280.png" alt="image7280" style="zoom: 80%;" />
在请求WFS服务时可能会出现跨域问题。
<img src="./images/image444662.png" alt="image444662" style="zoom: 50%;" />
解决方式可以在GeoServer中配置跨域支持。
将geoserver-2.18.1\lib中的jetty-servlets-9.4.12.v20180830.jar和jetty-util-9.4.12.v20180830.jar拷贝到geoserver-2.18.1\webapps\geoserver\WEB-INF\lib目录中再修改geoserver-2.18.1\webapps\geoserver\WEB-INF目录下web.xml文件将下列的注释取消重启GeoServer。
```xml
<!-- Uncomment following filter to enable CORS in Jetty. Do not forget the second config block further down.
-->
<filter>
<filter-name>cross-origin</filter-name>
<filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
<init-param>
<param-name>chainPreflight</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>allowedOrigins</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>allowedMethods</param-name>
<param-value>GET,POST,PUT,DELETE,HEAD,OPTIONS</param-value>
</init-param>
<init-param>
<param-name>allowedHeaders</param-name>
<param-value>*</param-value>
</init-param>
</filter>
<!-- Uncomment following filter to enable CORS-->
<filter-mapping>
<filter-name>cross-origin</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
```
## 参考文章
[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`
- 前端zip校验添加rar格式
- 后端重复上传校验
- 相关shp和项目uuid
- 根据机构编码拼接目录