在业务系统之间交换位置数据时,很多人第一反应是在XML里写两个数字字段:一个经度、一个纬度,就算完成任务。这种做法在小范围内或许能跑通,但一旦涉及多个系统对接,问题马上暴露:对方不知道你用的是什么坐标系,是WGS84还是CGCS2000?经纬度的顺序是先纬度还是先经度?这一串点是一个多边形的外环还是一条折线?为了解决这类空间数据交换的混乱局面,OGC(开放地理空间信息联盟)制定了GML规范,也就是Geography Markup Language,一种基于XML的地理信息编码语言。理解GML如何组织坐标,对做地图服务、空间数据入库和跨系统空间数据共享都很有帮助。

最朴素的做法有什么坑
假设我们要表达天安门附近的某个位置,最容易想到的XML写法是这样的:
<location> <longitude>116.397428</longitude> <latitude>39.90923</latitude> </location>
这段代码看起来很直观,但存在几个致命缺陷。首先是缺少坐标系声明。116.397428这个经度值在WGS84、GCJ02(火星坐标系)和BD09(百度坐标系)下对应的实际地面位置可以相差几百米,如果接收方不知道坐标系,这个数据基本没有意义。其次是顺序歧义,有些系统习惯先写纬度再写经度,有些则相反,双方不约定清楚就会出错。最后是缺乏结构约束,点、线、面这些不同的几何形态如果都用自定义标签描述,每个系统都要单独写解析逻辑,无法复用通用的工具链。
更深层的问题在于,空间数据不只是坐标数字,还包括测量单位、高程信息、坐标的有效位数、几何之间的拓扑关系等。一个随意设计的XML结构很难承载这些语义,而GML正是把这些语义标准化了的产物。它本质上是一组XML Schema定义,规定了坐标系怎么声明、几何怎么嵌套、坐标怎么排列,任何实现了GML的软件都能正确解读数据,不用逐字段协商。
GML的核心设计:坐标系与几何元素
GML中最重要的概念之一是坐标参考系统(CRS),通过srsName属性来声明。这个属性的值通常是一个URI,指向某个权威定义的坐标系,比如EPSG:4326代表WGS84经纬度坐标系,EPSG:3857代表Web墨卡托投影。下面是一个最简单的GML点对象:
<gml:Point srsName="urn:ogc:def:crs:EPSG::4326" srsDimension="2"> <gml:pos>39.90923 116.397428</gml:pos> </gml:Point>
注意这里的细节:gml:pos元素中的坐标以空格分隔,顺序由坐标系定义决定,EPSG:4326官方定义的轴序是纬度在前、经度在后。srsDimension属性声明了坐标的维度,2表示只有平面坐标,3则表示带高程。这种把坐标顺序交给坐标系定义来约束的做法,避免了双方自行猜测的歧义。
除了点,GML定义了一整套几何元素来覆盖各种空间形态。LineString用一组有序坐标表达折线,Polygon通过外环和内环表达带洞的多边形,MultiPoint、MultiSurface等复合元素表达几何集合。下面是一个带内环的多边形示例,外环是一个大矩形,内环挖掉中间一块区域:
<gml:Polygon srsName="urn:ogc:def:crs:EPSG::4326">
<gml:exterior>
<gml:LinearRing>
<gml:posList>39.90 116.38 39.92 116.38 39.92 116.41 39.90 116.41 39.90 116.38</gml:posList>
</gml:LinearRing>
</gml:exterior>
<gml:interior>
<gml:LinearRing>
<gml:posList>39.905 116.385 39.915 116.385 39.915 116.405 39.905 116.405 39.905 116.385</gml:posList>
</gml:LinearRing>
</gml:interior>
</gml:Polygon>这里有两个值得注意的设计。第一,LinearRing要求首尾坐标相同,形成闭合环,这是GML对环状几何的强约束,解析器可以直接校验。第二,gml:posList把所有坐标平铺在一个元素里,相比每个点一个子节点的写法,大幅减少了标签开销,对于有成千上万个顶点的几何对象来说,文件体积和解析速度的差距非常明显。
版本演进与实际应用中的注意事项
GML经历了多个版本的演进,目前使用最广的是GML 3.x系列。在GML 2.x中,坐标用的是gml:coord元素,每个坐标轴单独一个子元素;GML 3.x推荐使用gml:pos和gml:posList,并引入了对三维坐标、曲线插值等更丰富几何类型的支持。两种写法在老系统的数据里都可能碰到,解析时要注意兼容。此外,GML 3还定义了feature的概念,也就是带属性的空间要素,比如一个地铁站可以有名称、出口数量等属性,同时挂载一个Point几何,这样业务数据和空间数据就能一起传输。
在实际对接中,有几点经验值得留意。一是命名空间必须正确声明,GML文档根元素上要带上xmlns:gml="http://www.opengis.net/gml/3.2"这样的声明,否则解析器无法识别元素。二是srsName的写法有URN和URI两种风格,不同软件支持程度不一,对接前最好确认对方的解析实现。三是如果数据量很大,GML的XML文本形式会比较臃肿,此时可以考虑WFS-T服务返回的GML配合gzip压缩,或者在批量场景下改用更紧凑的GeoPackage、二进制WKB格式,只在需要跨平台文本交换时使用GML。
总结一下,GML解决的核心问题是让空间数据带上完整的语义:坐标系、维度、几何类型、闭合规则都由规范统一约束,接收方拿到文档就能准确还原数据。如果你正在设计一个需要交换位置信息的接口,与其自己发明一套含糊的经纬度标签,不如直接基于GML的子集来做,既省去沟通成本,也能借助现成的GIS工具库完成解析。掌握gml:pos、gml:posList、srsName这几个关键点,读懂和写出合格的GML文档就不再是难事。