地图数据中的GeoJson与TopoJson
近期工作中遇到了图表库地图数据的合并以及压缩的需求,需要将目前图表库中目前使用的GeoJSON数据格式替换为TopoJSON,现在将GeoJSON与TopoJSON的异同进行整理如下。
地图图表是数据可视化中的一种常用的图表类型,主要用于表达基于地理位置的区域性数据。
目前常用于表示地理信息的数据结构主要有两种格式:GeoJSON 与 TopoJSON。
GeoJSON
GeoJSON 是用于描述一系列几何形状的数据结构,其语法采用 JSON 的语法规范,同时也是一个独立的JavaScript对象,可以用于表示几何体(Geometry)、特征(Feature)或者特征集合(FeatureCollection)。
GeoJSON可以包含多个几何类型子对象,用type属性区分。当type取下列值时表示几何体:
- Point(点)
- MultiPoint(多点)
- LineString(线)
- MultiLineString(多线)
- Polygon(面)
- MultiPolygon(多面)
所有几何体对象都包含coordinates属性,用于表示坐标点,其至少应包含两个元素,也可以包含更多元素以表示更高维度,但必须按照x,y,z顺序排列。
示例如下:
- 点对象:
|
|
- 线对象:
|
|
- 面对象:
|
|
当type取GeometryCollection(几何体集合)时,GeometryCollection必须包含一个geometries变量,其值是一个几何对象数组。
|
|
{
“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中的边缘信息时分别存储于每一个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>