地图数据中的GeoJson与TopoJson

地图数据中的GeoJson与TopoJson

近期工作中遇到了图表库地图数据的合并以及压缩的需求,需要将目前图表库中目前使用的GeoJSON数据格式替换为TopoJSON,现在将GeoJSON与TopoJSON的异同进行整理如下。

地图图表是数据可视化中的一种常用的图表类型,主要用于表达基于地理位置的区域性数据。

目前常用于表示地理信息的数据结构主要有两种格式:GeoJSONTopoJSON

GeoJSON

GeoJSON 是用于描述一系列几何形状的数据结构,其语法采用 JSON 的语法规范,同时也是一个独立的JavaScript对象,可以用于表示几何体(Geometry)、特征(Feature)或者特征集合(FeatureCollection)

GeoJSON可以包含多个几何类型子对象,用type属性区分。当type取下列值时表示几何体:

  • Point(点)
  • MultiPoint(多点)
  • LineString(线)
  • MultiLineString(多线)
  • Polygon(面)
  • MultiPolygon(多面)

所有几何体对象都包含coordinates属性,用于表示坐标点,其至少应包含两个元素,也可以包含更多元素以表示更高维度,但必须按照x,y,z顺序排列。
示例如下:

  • 点对象:
1
2
3
{
"type": "Point",
"coordinates": [100, 100] }
  • 线对象:
1
{ "type": "LineString", "coordinates": [[100, 100], [200, 200]] }
  • 面对象:
1
{ "type": "Polygon", "coordinates":[[[100, 100], [100, 200], [200, 200], [200, 100], [100, 100]]] }

当type取GeometryCollection(几何体集合)时,GeometryCollection必须包含一个geometries变量,其值是一个几何对象数组。

1
2
{ "type": "GeometryCollection", "geometries": [ { "type": "Point", "coordinates": [100, 100] }, { "type": "LineString", "coordinates": [ [100, 100], [200, 200] ] } ] }
```   当type取Feature(特征)时,表示其除了包含几何体信息外,还包含有一个 properties属性,其内容可以是任意的JSON对象或null。

{
“type”: “Feature”,
“properties”: {
“id”: “CHN_北京”,
“name”: “北京”,
},
“geometry”: {
“type”: “Point”,
“coordinates”: [ 116.3671875, 39.9771201]
}
}


当type取FeatureCollection(特征集合)时,表示包含多个Feature Objects,其需要包含features属性,内容为由多个feature组成的数组。

### TopoJSON

在D3.js中还有一种常用的数据格式TopoJSON,TopoJSON是GeoJSON 按拓扑学编码后的扩展形式,是由D3.js的作者Mike Bostock制定的。通过将图形中公共点统一存储来减少冗余,可以有效减小GeoJSON文件的体积。

TopoJSON主要通过以下方式减小文件的体积。

- 通过arcs(弧线)来描述数据,每条弧只被定义一次,可以被多次引用,引用是只需要调用index即可。  
将浮点数转化为整数,最后通过transform进行图形的变换。
- 除了第一个点以外,其他点存储的均是相对位置。  
引用官方的一个示例:

1
{ "type": "Topology", "objects": { "example": { "type": "GeometryCollection", "geometries": [ { "type": "Point", "properties": { "prop0": "value0" }, "coordinates": [102, 0.5] }, { "type": "LineString", "properties": { "prop0": "value0", "prop1": 0 }, "arcs": [0] }, { "type": "Polygon", "properties": { "prop0": "value0", "prop1": { "this": "that" } }, "arcs": [[-2]] } ] } }, "arcs": [ [[102, 0], [103, 1], [104, 0], [105, 1]], [[100, 0], [101, 0], [101, 1], [100, 1], [100, 0]] ] }
值得说明的是,由于描述的数据时有向的,引用arcs的数字正值表示顺序,负值表示逆序。 - 介绍完GeoJSON与TopoJSON,来讲一讲我在工作中遇到的需求。 ![](地图数据中的GeoJson与TopoJson/1.png) 如图所示:当选中了地图中的两个相连的区域时,区域公共边依然会显示出来,需求是连续的区域不显示公共边。但由于GeoJSON中的边缘信息时分别存储于每一个Geometry中,所以想要找到所有重叠的公共点有很高的时间复杂度,因此考虑将GeoJSON转换为TopoJSON处理。 GeoJSON与TopoJSON的互相转换可以通过TopoJSON提供的转换器实现:
1
npm install topojson geo2topo geo.json > topo.json npm install topojson-client topo2geo states=geo.json < topo.json
注意states需要是TopoJSON中存在的对象名。 TopoJSON由于将公共边提取出来一同存储,并且还提供了merge函数用于区域合并,因此使用TopoJSON可以轻松实现以上需求,同时还能减小地图数据文件的体积。 示例代码如下: ``` HTML <!DOCTYPE html> <meta charset="utf-8"> <style> .state { fill: #DDD; stroke: #FFF; } .state.selected { fill: #0CF; stroke: #000; } </style> <body> <script src="https://d3js.org/d3.v3.js"></script> <script src="https://d3js.org/topojson.v2.js"></script> <script> var width = 960, height = 600; var projection = d3.geo.mercator() .translate([-width / 1.2, height * 1.2]) .scale((width) / 1.5); var path = d3.geo.path() .projection(projection); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); var selected = d3.set([ "CHN_西藏", "CHN_新疆", "CHN_青海" ]); d3.json("out.json", function(error, china) { if (error) throw error; var rr = svg.append("path") .datum(topojson.feature(china, china.objects.geo)) .attr("class", "state"); rr.attr("d", path); svg.append("path") .datum(topojson.merge(china, china.objects.geo.geometries.filter(function(d) { return selected.has(d.properties.id); }))) .attr("class", "state selected") .attr("d", path); }); </script>

geojson-spec
topojson-specification
简化、转换 GeoJSON 和 TopoJSON