在 GIS 开发中,空间参考系统的正确表达是数据互操作的基础。本文节选自作者新书《GIS基础原理与技术实践》第3章,深入讲解 WKT 与 EPSG 的原理与代码实现。
3.2 空间参考坐标系的表达
通过第2章介绍的地理空间参考系统的知识我们可以知道,一个空间参考坐标系的内容是非常丰富的:需要描述参考椭球参数、基准面参数等;如果是投影坐标系,还需要描述投影参数,并且每个投影算法的参数都不同。而在实际的GIS应用中,对空间参考坐标系的表达是复用程度和通用程度都很高的功能;那么,有没有一种可能,采用一种约定俗成的方式,去描述各种空间参考坐标系呢。答案是肯定的,那就是WKT字符串和EPSG编码。
3.2.1 WKT字符串
WKT(Well-known Text)是开放地理空间信息联盟OGC(Open Geospatial Consortium)制定的一种文本标记语言,用于表示矢量对象和地理空间参考系统。
提示:开放地理空间信息联盟OGC是一个非盈利的国际标准化组织,致力于提供地理信息行业软件和数据及服务的标准化工作。不同的GIS厂商可以按照这些标准定义空间数据存储的编码和开放服务的接口,从而可以保证空间数据的互操作。OGC的成员包括了像ESRI、Google、Oracle等业界强势企业,可以认为是国际上一种比较"权威"的标准化机构。OGC目前提供的标准很多,我们在后面也会接触到一些。
根据最新的OGC标准,使用WKT来表达的空间参考系统的标准叫做"WKT CRS",也就是坐标参考系统的WKT表示。在这个标准规范中,详细定义了如何通过WKT来表示各种空间参考系统。例如我们最常用的WGS84坐标系,其WKT字符串为:
text
GEOGCS["GCS_WGS_1984",
DATUM["D_WGS84",
SPHEROID["WGS84",6378137,298.257223563]],
PRIMEM["Greenwich",0],
UNIT["Degree",0.017453292519943295]]
结合第2章的内容,我们很容易识别其描述的具体含义:GEOGCS表明其是一个地理坐标系,DATUM表示大地基准面,SPHEROID则定义了一个参考椭球体,每一个参数后面的方括号内部则是其具体的参数。而一个投影坐标系则形如:
text
PROJCS["Xian_1980_3_degree_Gauss_Kruger_zone_39",
GEOGCS["GCS_XIAN 1980",
DATUM["D_XIAN 1980",
SPHEROID["Xian_1980",6378140,298.2569978029111]],
PRIMEM["Greenwich",0],
UNIT["Degree",0.017453292519943295]],
PROJECTION["Transverse_Mercator"],
PARAMETER["scale_factor",1],
PARAMETER["central_meridian",117],
PARAMETER["latitude_of_origin",0],
PARAMETER["false_easting",39500000],
PARAMETER["false_northing",0],
UNIT["Meter",1]]
PROJCS标识其是一个投影坐标系;定义投影坐标系的首要内容当然是需要先定义一个地理坐标系,这里的GEOGCS定义了一个XIAN80坐标系;PROJECTION则标识其采用的横轴墨卡托投影算法,PARAMETER则是其投影参数。由于参数false_easting在500000前面加了带号39,中央经线central_meridian为117,所以这是一个高斯-克吕格投影坐标系,使用的地理坐标系为XIAN80坐标系,采用3度带进行投影,带号为39。
使用WKT字符串的好处是易于识别,可以让用户和开发人员清楚的知道我们使用的到底是哪一个空间参考系统。但是让我们创建一个这么长的字符串就比较麻烦了,这需要非常了解WKT对坐标参考的具体规范和标准才行。简单总结来说就是易读不易写。
3.2.2 EPSG编码
与WKT字符串易读不易写的特性相反,使用EPSG编码来表达空间坐标参考系统的特点则是易写不易读。使用EPSG编码的原理在于,世界上空间坐标参考系统固然很多,但是通用的,经常使用的个数确是有限的;我们可以将这些常用的空间坐标参考系统都分配一个编号,这个编号就是EPSG编码。
提示:EPSG的是英文European Petroleum Survey Group的缩写,中文名称为欧洲石油调查组织。该组织维护了一个包含所有空间坐标参考系统相关的实体及其EPSG编码的在线数据库:https://epsg.io/ 。这个数据集在GIS业界内得到了广泛的使用。
EPSG编码非常简单,一般来说就是4位数字及其以上编码。在下表3.3中展示了一些经常使用的空间参考坐标系的EPSG编码。可以看到,一些常用的分带投影坐标系都被赋予了EPSG编码:
表3.3 常用空间参考坐标系及其EPSG编码
| 名称 | EPSG编码 |
|---|---|
| Beijing 1954 | 4214 |
| Xian 1980 | 4610 |
| WGS84 | 4326 |
| CGCS2000 | 4490 |
| CGCS2000 / Gauss-Kruger zone 15 | 4493 |
| WGS 84 / UTM zone 44N | 32644 |
| Web Mercator | 3857 |
比较有意思的是Web墨卡托投影坐标系的EPSG编码。我们在第2.5.3节中介绍过,Web墨卡托投影坐标系在进行投影计算时将地球的参考椭球体近似成正球体处理,是近似等角而不是严格等角。所以Web墨卡托投影是一种伪墨卡托投影(由Google最先发明),造成Web墨卡托投影长期得不到GIS领域业内承认。不过,随着Web墨卡托的广泛使用,EPSG最终还是给予了其编码3857。在这中间的过程中,不同的GIS厂商各自使用不同的编码来表达Web墨卡托投影坐标系:900913、3587、54004、41001、102113、102100、3785。现在这些编码都已废弃,统一使用3857。
3.3 空间参考坐标系的代码实现
虽然已经有WKT字符串和EPSG编码来表达空间坐标参考系统,但是这并不意味着我们需要从头开发一个程序来实现空间参考坐标系的相互转换。我们可以借助第三方开源库来实现这个功能,也就是本节要介绍的PROJ和GDAL。
3.3.1 开源工具PROJ和GDAL
PROJ是一个通用的坐标转换开源软件,最主要的功能就是支持坐标从一个空间坐标参考系转换到另一个坐标参考系。PROJ还公开了一个应用程序编程接口(API),以便于让用户在自己的程序中使用PROJ的功能,而无需自己实现类似的功能。
PROJ是一个C库,优点是强大的坐标转换算法。不过对空间参考坐标系的表达不是很直观,它使用了自己定义的字符串来表达空间参考坐标系,这个字符串在别的地方用的不是很多。所以很多时候是将PROJ集成进入GDAL来使用。
GDAL(Geospatial Data Abstraction Library)是一个开源的GIS数据抽象库,包括栅格数据、矢量数据、空间坐标参考等;并且还提供了对这些GIS数据的读取、写出、处理和转换。基本上来说这个库是做GIS开发的必备库,集成了很多GIS的基础功能,功能强大的同时,最难能可贵的是这些都是开源免费的。
GDAL可以通过WKT字符串和EPSG编码实现对空间坐标系的表达,在集成PROJ之后,GDAL就能实现空间坐标转换的功能。
3.3.2 具体实现
在Visual Studio 2019中新建一个控制台程序,我们按照第3.1节中论述的方法配置好开发环境,引入GDAL第三方库,写出我们第一个示例程序,主要代码如下例3.1所示:
例3.1 空间参考坐标系的表达
cpp
#include <ogr_spatialref.h>
#include <iostream>
using namespace std;
void OutputGcs() {
OGRSpatialReference spatialReference;
spatialReference.importFromEPSG(4326); // WGS84
// spatialReference.importFromEPSG(4214);//BeiJing54
// spatialReference.importFromEPSG(4610);//XIAN80
// spatialReference.importFromEPSG(4490);//CGCS2000
char* pszWKT = nullptr;
spatialReference.exportToPrettyWkt(&pszWKT);
cout << pszWKT << endl;
CPLFree(pszWKT);
pszWKT = nullptr;
}
void OutputPcs() {
OGRSpatialReference spatialReference;
spatialReference.importFromEPSG(4490); // CGCS2000
spatialReference.SetTM(0, 114, 1.0, 38500000, 0);
char* pszWKT = nullptr;
spatialReference.exportToPrettyWkt(&pszWKT);
cout << pszWKT << endl;
CPLFree(pszWKT);
pszWKT = nullptr;
}
int main() {
//设置Proj数据
string projDataPath = getenv("GISBasic");
projDataPath += "/share/proj";
CPLSetConfigOption("PROJ_LIB", projDataPath.c_str());
cout << "地理坐标系,WGS84坐标系:" << endl;
OutputGcs();
cout << "投影坐标系,高斯克吕格投影坐标系:" << endl;
OutputPcs();
}
这段代码输出了两种有代表性的空间参考坐标系:
- 第一种是WGS84坐标系是我们最常用的地理坐标系,很显然,去传入各种地理坐标系的描述是非常麻烦了,最简单的就是直接传入具体的EPSG代码。
- 第二种是国内专业地图最常用的高斯克吕格投影坐标系,不过很可惜GDAL没有封装专门的接口。但是正如第2章中所述,高斯克吕格投影坐标系其实就是横轴墨卡托投影,因此我们可以直接用横轴墨卡托投影的接口。如代码所述,第二种投影坐标系描述的其实是高斯克吕格投影坐标系(3度带),第38度带,中央经线为114。
代码运行结果如下所示,输出了两种空间参考坐标系的WKT字符串:
text
地理坐标系,WGS84坐标系:
GEOGCS["WGS 84",
DATUM["WGS_1984",
SPHEROID["WGS 84",6378137,298.257223563,
AUTHORITY["EPSG","7030"]],
AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich",0,
AUTHORITY["EPSG","8901"]],
UNIT["degree",0.0174532925199433,
AUTHORITY["EPSG","9122"]],
AXIS["Latitude",NORTH],
AXIS["Longitude",EAST],
AUTHORITY["EPSG","4326"]]
投影坐标系,高斯克吕格投影坐标系:
PROJCS["unnamed",
GEOGCS["China Geodetic Coordinate System 2000",
DATUM["China_2000",
SPHEROID["CGCS2000",6378137,298.257222101,
AUTHORITY["EPSG","1024"]],
AUTHORITY["EPSG","1043"]],
PRIMEM["Greenwich",0,
AUTHORITY["EPSG","8901"]],
UNIT["degree",0.0174532925199433,
AUTHORITY["EPSG","9122"]],
AUTHORITY["EPSG","4490"]],
PROJECTION["Transverse_Mercator"],
PARAMETER["latitude_of_origin",0],
PARAMETER["central_meridian",114],
PARAMETER["scale_factor",1],
PARAMETER["false_easting",38500000],
PARAMETER["false_northing",0],
UNIT["metre",1,
AUTHORITY["EPSG","9001"]],
AXIS["Easting",EAST],
AXIS["Northing",NORTH]]
本文节选自作者新书《GIS基础原理与技术实践》第3章。书中系统讲解 GIS 核心理论与多语言实战,适合开发者与高校师生。