【WebGIS】OGC API

339 阅读15分钟

当我们在浏览器输入一个地图网站时,实际上访问的是一个URL地址,比如请求一个地图瓦片的URL可能是 map.server.com/wms?service… ,这个URL不仅仅是一个网络地址,它还包含了按照特定规则组织的参数。这里的"特定规则"就是我们说的API接口,它定义了前端和后端如何交流。 就拿OGC的WMS服务来说,它规定了请求地图时必须包含哪些参数(如图层、范围、投影等),这些参数该如何书写,服务器又该如何解释这些参数并返回什么格式的数据。这就像制定了一种"通用语言",只要前端按照这种"语言规则"发送请求,支持OGC标准的地图服务器(如GeoServer)就能理解并正确响应。

而高德地图选择使用自己的API(比如"map.amap.com/tiles?x=204… 这些参数格式与OGC标准完全不同。这就是为什么高德必须开发自己的后端服务器,因为像GeoServer这样的标准OGC服务器无法理解高德特有的请求格式。

OGC——GIS专用API

WMS(Web Map Service)

  • 前端请求
首先输入URL地址

GET /geoserver/wms?
    SERVICE=WMS&               声明这是WMS服务
    REQUEST=GetMap&            获取地图图片
    LAYERS=rivers,roads&       需要显示的图层,如"rivers,roads"
    STYLES=blue,black&         图层的渲染样式,如"blue,black"
    BBOX=-180,-90,180,90&      地图范围,格式为"minX,minY,maxX,maxY"
    WIDTH=800&                 输出图片的像素高
    HEIGHT=600                 输出图片的像素宽
    FORMAT=image/png/jpeg      输出格式可选

浏览器把URL地址封装为一个HTTP请求



GET /geoserver/wms?SERVICE=WMS&......(就是URL)
Host: example.com
User-Agent: Mozilla/5.0 ...
Connection: keep-alive
Accept: image/png/jpeg      #返回img或png或jpeg
...
  • 后端响应——PNG格式
--------------【图片数据的响应】---------------
89 50 4E 47 0D 0A 1A 0A   // PNG文件头标识  
00 00 00 0D               // IHDR块长度     
49 48 44 52               // "IHDR"标识
00 00 01 00               // 宽度(256像素)
00 00 01 00               // 高度(256像素)
08                        // 位深度
06                        // 颜色类型
[... 后续是图片数据 ...]
--------------------------------------------

WMTS(Web Map Tile Service)

GET /geoserver/gwc/service/wmts?
    SERVICE=WMTS&                  # 声明这是WMTS服务
    REQUEST=GetTile&               # 请求操作类型:获取瓦片
    VERSION=1.0.0&                 # WMTS服务版本号
    LAYER=basemap&                 # 图层名称(瓦片服务图层)
    STYLE=default&                 # 图层样式(通常为default)
    TILEMATRIXSET=EPSG:3857&       # 瓦片矩阵集(坐标系及瓦片规则)
    TILEMATRIX=14&                 # 瓦片矩阵级别(缩放级别)
    TILEROW=5287&                  # 瓦片行号(Y坐标)
    TILECOL=1389&                  # 瓦片列号(X坐标)
    FORMAT=image/png               # 瓦片数据的格式(如PNG或JPEG)

WFS(Web Feature Service)

  • 前端请求
GET /geoserver/wfs?
    SERVICE=WFS&           # 声明这是WFS服务
    VERSION=2.0.0&         # 版本号
    REQUEST=GetFeature&    # 获取要素数据
    TYPENAME=rivers&       # 要素图层名称
    SRSNAME=EPSG:4326&     # 空间参考系
    BBOX=116,39,117,40&    # 空间范围过滤(左下角和右上角经纬度)
    MAXFEATURES=100&       # 最大返回要素数
    OUTPUTFORMAT=application/json/xml/gml  # 输出格式可选
  • 后端响应——JSON格式
{
  "type": "Feature",
  "geometry": {
    "type": "Polygon",
    "coordinates": [ // 建筑物轮廓坐标
    [116.3, 39.9],  [116.31, 39.9], [116.31, 39.91], ... ]},
  "properties": {
    "建筑名称": "电视大厦",
    "建筑高度": 100,
  }
}
  • 后端响应——XML格式
<gml:Feature>
  <gml:geometry>
    <gml:Polygon>
      <gml:coordinates>116.3,39.9 116.31,39.9 ...</gml:coordinates>
    </gml:Polygon>
  </gml:geometry>
  <gml:properties>
    <建筑名称>电视大厦</建筑名称>
    <建筑高度>100</建筑高度>
  </gml:properties>
</gml:Feature>

WPS(Web Processing Service)

GET /geoserver/wps?
    SERVICE=WPS&                     # 声明这是WPS服务
    VERSION=1.0.0&                   # WPS服务协议版本号
    REQUEST=Execute&                 # 请求操作类型:执行某个地理处理任务
    IDENTIFIER=buffer&               # 处理服务的标识符(例如缓冲区分析)
    DATAINPUTS=geometry=POLYGON((116 39, 117 39, 117 40, 116 40, 116 39));distance=0.01& # 输入数据,包括几何和缓冲距离
    RESPONSEDOCUMENT=true&           # 请求返回文档格式的响应
    OUTPUTFORMAT=application/json    # 输出数据格式(如GeoJSON)

WCS(Web Coverage Service)

  • 前端请求
GET /geoserver/wcs?
    SERVICE=WCS&                 # 声明这是WCS服务
    VERSION=2.0.1&               # WCS服务的协议版本,常见值为2.0.1或1.1.2
    REQUEST=GetCoverage&         # 获取覆盖数据的请求
    COVERAGEID=dem&              # 要请求的栅格数据的标识符(图层名称)
    SUBSET=Lat(39,40)&           # 按纬度范围筛选(单位与坐标系一致)
    SUBSET=Long(116,117)&        # 按经度范围筛选(单位与坐标系一致)
    RESOLUTION=0.001,0.001       # 数据的空间分辨率(经纬度单位)
    FORMAT=image/tiff&           # 输出数据的格式(GeoTIFF为常用格式)
  • 后端响应——GeoTiff格式

一般摄影用的TIFF:主要存颜色,而遥感用的TIFF:存实际物理量和地理信息。 所以遥感的Tiff本身就有意义,并且还延伸了GeoTiff。GeoTIFF实际上是在TIFF规范基础上:定义了一套存储地理信息的标准,规定了如何保存地理参考信息,兼容TIFF的所有功能实际上。现在我们说的"普通TIFF"遥感影像,通常已经包含了这些地理信息,因为TIFF本身就支持多波段,支持不同数据类型(8bit、16bit等),可以存储元数据。日常你在GIS类软件里导出的遥感影像TIFF,其实都是GeoTIFF格式,只是没有特别强调"Geo"这个前缀。

// 普通TIFF的图像结构
Band 1: [数值数组]  // 比如近红外波段
Band 2: [数值数组]  // 比如红波段
Band 3: [数值数组]  // 比如绿波段

// GeoTIFF在TIFF基础上增加了地理标签
GeoTags:
    ModelTiepoints: [x,y,z, lon,lat,elevation]  // 控制点
    ModelPixelScale: [x-scale, y-scale, 0]      // 像素分辨率
    GeoKeyDirectory: 
        GTModelTypeGeoKey: 2          // 模型类型
        GTRasterTypeGeoKey: 1         // 栅格类型
        GTCitationGeoKey: "WGS84"     // 坐标系统
        GeographicTypeGeoKey: 4326    // EPSG代码

WMS与WCS服务的本质差异

这里写给那些没有遥感背景的GISer。

WMS (PNG/JPEG)就是普通图片的二进制数据,直接可以显示,但无法获取具体数值。WCS (GeoTIFF)是图片格式 + 地理信息,这个图片不是普通图片,因为每个像素的值是实际地理数值(如海拔),而不是单纯的RGB 0~255的颜色数值,因此可以用于空间分析。

普通照片和遥感影像虽然都能在屏幕上显示图像,但它们记录和使用数据的方式存在根本性的差异。普通照片中的RGB数值本质上就是用来表达色彩的,每个像素的红、绿、蓝三个数值直接对应显示器上的颜色强度,它们的意义仅限于视觉表达,这就决定了照片主要用于展示和艺术处理,比如调整亮度、添加滤镜或进行图像美化。

而遥感影像的波段数值则完全不同,它们记录的是地物的物理特性,可能是地物对不同波长光的反射率、地表温度的亮温值,甚至是雷达遥感中的散射强度。这些数值本身与色彩无关,只是为了便于人眼观察,我们可以通过软件将这些物理量映射到RGB色彩空间,呈现出"真彩色"或"假彩色"的效果。比如遥感影像里的森林是绿色,那是因为蓝波段反射率 0.1、绿波段反射率 0.3、红波段反射率 0.1,经过拉伸映射到 RGB 值为 (102, 204, 102),显示为绿色。普通照片里的森林,是因为直接记录RGB值为 (102, 204, 102),显示为绿色。

正是因为存储的是物理量,遥感影像可以用于各种专业分析,例如计算植被指数、估算地表温度或监测地物变化等。这种本质差异也导致了处理这两类数据时思维方式的不同。摄影师在处理照片时,会思考如何调整色彩来获得理想的视觉效果。而遥感专家在拿到一幅影像时,首先考虑的是这个影像包含了哪些波段数据,这些波段能够计算出什么指标,能够反映地物什么特征,完全是一种数据分析导向的思维方式,而不是着眼于视觉效果的艺术思维。可以说,遥感影像中的色彩显示只是其众多表达方式中的一种,而不是数据本身的核心价值所在。

OGC与RestFul的对比

HTTP 作为最基础的通信协议,定义了网络通信的基本规则,包括请求方法、URL格式、状态码、报文结构等内容,确保信息能够在网络上可靠地传输

  • RESTful 是遵循 HTTP 协议的 API 设计理念,没有添加新的协议规则
  • OGC 也是遵循HTTP协议的API设计产品,增加了特定的地理信息处理规则和特定的参数

在Web开发中,API设计风格直接影响到系统的灵活性、可扩展性和可维护性。OGC API和RESTful API是两种常见的API设计风格,它们在地理数据的传输和操作上有着显著的区别。首先,RESTful API的核心思想是将所有操作视为对资源URL的操作,通过HTTP标准方法进行这些操作。作为一种风格,不同开发者与不同的业务需求都可以设计多种不同的操作,下面例子我展示了 API 通用性设计的三个核心原则:参数化查询、资源抽象和减少冗余。

想象设计一个查询城市基础信息的 API。最初的设计是这样的:

GET /city/beijing/info
GET /city/shanghai/info
GET /city/guangzhou/info

这种设计看起来直观,但实际上是在为每个城市创建独立的端点。如果我们要支持全国所有的城市,这种方式会让代码极度臃肿。所以改进后的版本如下

GET /cities?name=beijing

通过参数化查询,我们用一个统一的端点替代了多个独立端点。这不仅让代码更简洁,还为我们提供了更大的灵活性。比如,我们可以轻松添加更多的查询参数

GET /cities?name=beijing&fields=population,area,elevation
GET /cities?province=hebei&min_population=1000000

但是真正的通用性设计还需要考虑资源的抽象。城市实际上是一种地理位置,那么可以这样设计

GET /locations?type=city&name=beijing
GET /locations?type=landmark&name=forbidden_city
GET /locations?latitude=39.9042&longitude=116.4074

现在我们的 API 不仅能处理城市,还能处理任何类型的地理位置。这就是资源抽象的威力:用一个通用的概念涵盖多种具体的应用场景。表面上看,一个通用的端点比多个专用端点要复杂。但实际上,它减少了代码的冗余。想象一下,如果我们需要为每种类型的地理位置都维护一个独立的端点,那才是真正的复杂。通过资源抽象,我们可以在一个地方统一处理所有的位置相关逻辑,无论是城市、地标还是具体坐标。所以我们最后的思路如下:

// 统一的位置处理逻辑
function handleLocationQuery(params) {
    // 通用的参数验证
    validateParams(params);
    
    // 根据类型选择适当的数据处理方法
    switch(params.type) {
        case 'city':
            return processCityData(params);
        case 'landmark':
            return processLandmarkData(params);
        case 'coordinates':
            return processCoordinates(params);
        // 轻松扩展新的类型
    }
}

OGC是规范

RestfulAPI是设计阶段的风格,OGC是已经设计完毕后的规范,换句话说,WMS就是WMS,就是说你只要调用WMS就得完完全全遵循它的规范,你也拓展不了功能,你也别想漏掉参数...以WMS为例,任何支持WMS的系统,都会遵循严格的参数约定。比如,SERVICE=WMSREQUEST=GetMap必须在请求中明确指定,其他参数如图层、坐标参考系等也必须符合OGC标准。这种标准化的设计使得不同GIS系统(如GeoServer、QGIS Server、ArcGIS Server)之间能够无缝协作,保证了系统之间的互操作性。

OGC的资源与请求绑定

OGC服务与RESTful API的另一个显著区别在于资源定位的方式。在RESTful架构中,资源通过URL路径来定位,用不同的方法操作资源。而OGC服务中的资源和操作是紧密绑定的,通过固定的请求格式来完成。以WMS为例,GET /geoserver/wms?SERVICE=WMS&REQUEST=GetMap的请求中,操作(如GetMap)和资源(如图层roads)是同时定义的,不能随意分开。OGC服务并不像RESTful API那样注重资源和行为的解耦,它的请求和操作是高度绑定的。例如,WMS请求的GET /geoserver/wms?SERVICE=WMS&REQUEST=GetMap就是一个明确的操作请求,指定了获取地图图像的操作,而图层(如LAYERS=roads)和坐标系统(如SRS=EPSG:4326)等参数也都必须符合特定规范。

OGC的返回数据格式

同时,OGC服务通常使用XML等格式返回数据,而RESTful API则倾向于使用JSON格式。OGC服务的数据格式更适用于地理信息和地图数据的传输,能够处理复杂的空间数据结构。而RESTful API的灵活性和简洁性则更适用于一般数据的交换。

OGC+RestFulAPI配合

OGC的设计专注于地理数据的需求。GIS服务涉及到空间范围(如BBOX)、坐标系统(CRS)、图层样式(STYLES)等多个复杂的参数,这些参数之间有着复杂的依赖关系。OGC通过严格的参数结构和查询字符串的方式,能够有效地处理这些复杂的空间数据请求。这种设计方式比RESTful风格的路径参数更加清晰、易于理解,特别是在处理涉及空间分析和地图渲染的场景中。在一个WebGIS应用中,业务逻辑可以通过RESTful API进行处理,如查询项目资料、创建分析任务等;而地图数据的请求和分析则使用OGC风格的接口,这样的组合使用能够充分发挥两者的优势,使系统既具备灵活性,又能高效地处理复杂的地理数据。

API设计与落地阶段

当我们谈论API的“死板”与“灵活”时,实际上是在混淆了API设计阶段使用阶段这两个阶段。以WMS为例,它要求请求中必须包含一些特定的参数,如bbox(边界框)、layers(图层)等,表面上看起来非常“死板”,但这种“死板”本质是其背后的业务逻辑,WMS服务需要这些特定的参数才能确定返回哪个地图区域,哪些图层的数据。它就像一个函数,必须接收特定的参数才能执行正确的计算,缺少任何参数都会导致无法生成正确的响应。实际上,这种“死板”是合乎逻辑的,并且是系统设计的核心需求。

同样,RESTful风格的API也并非没有“死板”的部分。比如,获取某个用户信息的接口 /api/users/{id},要求前端必须提供一个有效的用户ID才能返回该用户的信息。如果缺少id参数,后端就无法正确返回数据。在这个层面上,任何API在使用阶段都是“死板”的,因为前端必须严格按照接口设计的规定格式发送请求,后端才能处理并生成正确的响应。

真正的“灵活性”出现在API设计阶段。在这一阶段,开发者有能力自由定义API的路径、参数和响应格式。对于WMS服务,你完全可以修改GeoServer的源码,让它接收新的参数,或者做一些自定义的业务逻辑处理。同样,在设计RESTful API时,你也可以自由决定API的路径、允许的参数和返回格式。但一旦接口设计完成,前端就必须严格遵循这一设计,按照规定的格式来发起请求。这就是为什么我们称API的本质是一套“接口规范”:它规定了前后端之间如何进行可靠的通信。

因此,OGC API与RESTful API的根本区别不在于一个是“死板”而另一个是“灵活”,而在于它们各自的标准化程度适用场景。OGC API(例如WMS)是一个被广泛采用的标准规范,旨在确保不同的GIS系统能够互相通信和互操作。相比之下,RESTful API通常是根据具体项目需求而定制的,具有更大的灵活性。

不过,值得注意的是,OGC API的设计哲学要求它保持标准化,确保不同GIS系统间的互操作性。因此,修改OGC服务(如GeoServer的WMS服务)来接收自定义参数并处理特殊业务逻辑,其实是不可取的。如果你修改了GeoServer,使其接受一个新的自定义参数并处理特定的业务逻辑,那么这个修改后的服务虽然基于WMS,但实际上已经不再是标准的WMS服务。其他遵循OGC标准的客户端(如QGIS、Leaflet等)将无法再与该服务兼容,这违背了OGC标准化的初衷——确保不同系统之间能够顺畅地交互。因此,更准确的做法是:如果你需要在OGC服务的基础上添加新的功能,应该开发一个独立的自定义API,而不是修改原有的OGC服务。这样可以确保OGC服务保持标准化,仍能与其他GIS系统互操作,同时通过自定义API来实现你所需要的特殊业务逻辑。这也是为什么许多商业地图服务商通常会选择完全自定义的API,而不是修改OGC标准的原因——他们需要更大的灵活性来实现自有的业务需求,而不受OGC标准的限制。