GIS

从零发布一个 WMS 服务:搞懂 GeoServer、Tomcat、Cesium 与 OGC 地图服务

(篇一)

最近在上杨帆老师的课程,接触并学习了 WebGIS 与 OGC 地图服务的相关内容。相比于单纯为了完成课程任务,我逐渐意识到,这门课所涉及的知识对我今后的学习和实践确实很有帮助。因此,我打算认真地把这门课学下去,真正理解其中的原理与应用,而不是囫囵吞枣、泛泛而谈。

本文主线:将本地 Shapefile 数据发布到 GeoServer,通过 WMS 对外提供服务,最后使用 Cesium 在三维地球中进行加载和显示。

其次,理解OGC、Web Service、WMS、REST等概念。

一、任务主线

先看最终的数据流:

Shapefile -> GeoServer -> WMS -> HTTP -> Cesium -> 浏览器中的三维地球

三个矢量数据:boundary.shp、buildings.shp、cun.shp 分别表示边界、建筑以及村界。

我们希望把它们发布成网络地图服务,最终让浏览器访问:http://127.0.0.1:8180/exp_geoserver_wms/

就可以在Cesium中看到这些图层。实验环境大致如下:

Windows 11
Tomcat 9.0.79
GeoServer 2.23.2
Cesium 1.88
Chrome

Tomcat使用8180端口。

二、为什么需要GeoServer?

首先要解决一个问题:浏览器能不能直接读取我的 .shp 文件然后显示?

当然可以通过各种前端库实现,但在真正的 WebGIS 系统中,通常不会简单地把原始空间数据文件直接扔给浏览器。

我们更希望有这样一个角色:客户端 -> “我要这一块地图” -> 服务器 -> 读取空间数据 -> 处理、渲染 -> 返回结果

这个“GIS 服务端”就可以由 GeoServer 来承担。

GeoServer 可以读取:Shapefile、PostGIS、GeoTIFF、空间数据库…,并且通过统一的标准对外提供:WMS、WFS、WCS、WMTS…,因此可以把GeoServer理解为:负责把本地或数据库中的空间数据包装成网络 GIS 服务的软件。

三、为什么需要Tomcat?

GeoServer 是 Java Web 应用,本次实验中以:geoserver.war的形式部署到 Tomcat 中。简单来说GeoServer运行在Tomcat提供的java运行环境中。

Tomcat本身并不知道:

  • 什么是 Shapefile?
  • 什么是 WMS?
  • 什么是 EPSG:4326?

它主要负责:接收HTTP请求、找到对应Web应用(如GeoServer)、执行应用、返回HTTP响应。

而地图相关的工作交给GeoServer。

四、WMS服务发布流程

1、创建Workspace

进入 GeoServer 后台后,首先创建工作空间:ntu17。(给自己的地图服务划一个命名空间)

例如以后一个图层可能写成:ntu17:buildings,其中ntu17就是Workspace,这样不同项目之间即使存在同名图层,也不会发生混淆。

2、创建数据源Store

有了 Workspace,下一步就是告诉 GeoServer:我的空间数据在哪里?

这就是 Store。

本实验数据来自 Shapefile,因此创建 Shapefile 类型的数据源。

例如:

buildings.shp

发布之后,关系大致变成:

Workspace
   ↓
ntu17
   ↓
Store
   ↓
Shapefile

如果以后换成 PostGIS,Store 指向的就不再是 .shp 文件,而是数据库。

3、发布Layer

Store 只是告诉 GeoServer:

“这里有数据。”

还要进一步将数据发布成:

Layer

Layer 才是客户端真正能够访问的服务单元。

因此 GeoServer 中一个非常重要的关系是:

Workspace
   ↓
Store
   ↓
Layer

例如:

ntu17
 ↓
数据源
 ↓
buildings

发布完成后,客户端才能请求:

ntu17:buildings

4、配置Style

空间数据+SLD样式->地图

5、使用OpenLayers预览

发布图层后,可以使用 GeoServer 自带的 Layer Preview 进行预览。

这一部分很有意思。

表面上看:

点击 OpenLayers → 地图出现了。

但如果打开 Chrome:

F12 → Network

就会发现背后实际发生了网络请求。

其中最核心的就是:

GetMap

这就正式进入了 WMS。

五、WMS到底是什么?

WMS全称:Web Map Service,即Web地图服务。它是OGC制定的一种地图服务标准。

OGC解决的问题是:

客户端应该用什么方式向地图服务器请求地图?

如果没有标准:

ArcGIS 一种写法
GeoServer 一种写法
SuperMap 又一种写法

客户端开发会非常麻烦。

因此 OGC 制定规则:

大家都按照 WMS 的要求通信。

这也是 OGC 最重要的意义之一:

标准化+互操作

六、GetMap:真正获取地图

WMS 最核心的操作就是:

GetMap

客户端可能向 GeoServer 请求:

SERVICE=WMS
VERSION=1.1.1
REQUEST=GetMap
LAYERS=ntu17:buildings
SRS=EPSG:4326
BBOX=...
WIDTH=256
HEIGHT=256
FORMAT=image/png
TRANSPARENT=true

翻译成人话就是:

我现在使用 WMS 服务,请把 buildings 这个图层在指定空间范围内绘制成一张 256×256 的透明 PNG 图片给我。

其中:

LAYERS

指定需要哪些图层。

LAYERS=ntu17:buildings

BBOX

指定地图空间范围:

minx,miny,maxx,maxy

相当于告诉 GeoServer:

我要地球上的这一块。

WIDTH / HEIGHT

告诉服务器:

把这一块区域画成多大的图片。

例如:

256 × 256

FORMAT

指定返回格式:

image/png

因此整个过程可以总结成:

客户端
   ↓
GetMap
   ↓
GeoServer
   ↓
寻找图层
   ↓
读取 Shapefile
   ↓
应用 SLD
   ↓
按照 BBOX 渲染
   ↓
生成 PNG
   ↓
返回客户端

七、WMS另外两个核心操作

除了 GetMap,WMS 还有两个经常出现的操作。

1. GetCapabilities

可以理解成:

“你这里有什么?”

客户端请求 GetCapabilities 后,服务器会返回一份 XML 服务描述。

里面包括:

有哪些图层
支持哪些坐标系
支持哪些图片格式
支持哪个 WMS 版本
……

所以我更喜欢把它记成:

服务器的服务说明书。

2. GetFeatureInfo

可以理解成:

“我点的这里是什么?”

例如用户点击地图中的一个建筑:

      鼠标
       ↓
   ┌──────┐
   │建筑     │
   └──────┘

服务器可以返回:

名称:教学楼
类型:建筑
高度:20m

因此三个操作可以直接记成:

GetCapabilities
你有什么?
GetMap
给我地图。
GetFeatureInfo
这里是什么?

八、Cesium如何调用WMS?

核心代码其实并不复杂:

var imageryProvider =
    new Cesium.WebMapServiceImageryProvider({
        url: "../geoserver/ntu17/wms",

        layers: "ntu17:ntu17-group",

        parameters: {
            service: "WMS",
            version: "1.1.1",
            request: "GetMap",
            format: "image/png",
            transparent: true
        }
    });

然后:

viewer.imageryLayers.addImageryProvider(
    imageryProvider
);

Cesium 就会开始向 GeoServer 请求地图。

九、整个请求过程

这是整个实验里我认为最值得理解的一部分。

最开始我们输入:

http://127.0.0.1:8180/exp_geoserver_wms/

第一步并不是直接访问 GeoServer。

而是:

浏览器
   ↓
HTTP GET
   ↓
Tomcat
   ↓
index.html

Tomcat 将:

index.html

返回浏览器。

浏览器开始解析:

<script src="...Cesium.js"></script>

发现:

我还需要 Cesium.js。

于是继续发 HTTP 请求。

随后执行 JavaScript。

当运行到:

new Cesium.WebMapServiceImageryProvider(...)

时,JavaScript 又向 GeoServer 发送新的 HTTP 请求。

所以完整过程应该是:

用户输入 URL
      ↓
浏览器
      ↓
GET index.html
      ↓
Tomcat
      ↓
返回 index.html
      ↓
浏览器解析 HTML
      ↓
加载 CSS / JavaScript / Cesium
      ↓
执行 JavaScript
      ↓
Cesium 构造 WMS 请求
      ↓
Tomcat
      ↓
GeoServer
      ↓
读取 Shapefile
      ↓
应用 SLD
      ↓
返回 PNG
      ↓
Cesium
      ↓
把图片贴到三维地球上

注意:

不是 index.html 被发送给 GeoServer。

而是:

浏览器解析 index.html 后执行其中的 JavaScript,由 JavaScript 再去请求GeoServer。

十、为什么 F12 里会看到很多 GetMap?

拖动、缩放地图时,会不断产生新的网络请求。

十一、为什么把前端和 GeoServer 都放进Tomcat WebApps?

本实验采用类似:

http://127.0.0.1:8180/exp_geoserver_wms/
http://127.0.0.1:8180/geoserver/

也就是说:

协议相同
主机相同
端口相同

这样属于:

同源

浏览器访问起来会简单很多。

否则例如:

前端:
http://localhost:8080
GeoServer:
http://localhost:8180

就可能涉及:

CORS
跨域

十二、WMS、WFS、WMTS、WCS

十三、Web Service

WMS 本质上也属于网络服务。

所谓 Web Service,可以简单理解为:

通过网络向其他程序提供功能或数据。

例如:

天气服务
地图服务
翻译服务
订单服务

地图只是其中一种。

早期 Web Service 中经常出现:

XML
SOAP
WSDL

可以简单理解为:

XML
= 数据怎么写
SOAP
= 消息怎么交换
WSDL
= 服务说明书

后来 Web 开发逐渐更多采用:

REST
+
HTTP
+
JSON

这样的方式。

十四、SOA、XML、SOAP、WSDL、RESTful。

1、SOA

Service-Oriented Architecture:面向服务的架构:把一个大系统拆成很多独立的服务,需要什么功能就调用什么服务。

2、Web Service

服务通过 Web 网络真正提供给别人调用。

3、XML

Extensible Markup Language:可扩展标记语言。

<Student>
    <Name>张三</Name>
    <Age>20</Age>
    <Gender>男</Gender>
</Student>

一种结构化表达数据的格式。

4、XSD

XML Schema Definition

规定 XML 应该怎么写。

比如规定:

Student 必须有 Name
Age 必须是整数
Gender 可以是字符串

5、SOAP

Simple Object Access Protocol 简单对象访问协议

SOAP 是客户端和服务器之间交换结构化消息的一种协议。

而 SOAP 通常使用:

XML

来包装消息。

所以:

客户端
   ↓
SOAP消息
   ↓
服务器

SOAP 消息里面常常就是 XML。

十五、WMS 流程中的 XML 与 KVP 请求

1. GetCapabilities 返回的是 XML

如果你请求:

SERVICE=WMS
REQUEST=GetCapabilities

GeoServer 返回的服务说明书通常就是 XML,比如描述:

有哪些图层
支持哪些坐标系
支持哪些格式

所以这里明确涉及 XML。

2. 你用的 SLD,本身就是 XML 格式

你实验里配置过 GeoServer 的 Style / SLD。

SLD 全称 Styled Layer Descriptor,其内容本质上就是 XML,例如类似:

<StyledLayerDescriptor>
    ...
    <PolygonSymbolizer>
        ...
    </PolygonSymbolizer>
</StyledLayerDescriptor>

所以你其实已经用到 XML 了,只是 GeoServer 图形界面帮你屏蔽了很多细节。

3. 但你的 GetMap 请求本身不一定是 XML

你目前 Cesium 发的 WMS 请求主要是这种:

?SERVICE=WMS
&REQUEST=GetMap
&LAYERS=...
&BBOX=...
&FORMAT=image/png

这种叫 KVP(Key-Value Pair,键值对)请求,就是 URL 后面一串参数。

然后 GeoServer 返回:

PNG

所以这一段是:

HTTP + KVP
   ↓
WMS GetMap
   ↓
PNG

而不是:

XML → XML

十六、从传统OGC到OGC API

传统 OGC 服务的请求经常长这样:

SERVICE=WFS
REQUEST=GetFeature
TYPENAME=buildings

也就是说:

调用某一个操作。

现代 OGC API 开始越来越靠近普通 Web API。

例如可能采用类似:

GET /collections/buildings/items

这时:

buildings

直接被看作一个资源。

返回数据也更多使用:

JSON / GeoJSON

因此可以粗略理解为:

传统 OGC:

HTTP
+
SERVICE
+
REQUEST=GetXXX
+
XML/GML

逐渐发展为:

OGC API:

REST风格
+
HTTP
+
JSON

这让 WebGIS 与现代 Web 开发的技术体系越来越接近。

发表评论