# 四.topojson
前言
TopoJSON 和 GeoJSON 一样,本身只是一种文件格式、一种地理数据标准。而为了对 TopoJSON 数据做操作,需要用到一个叫 topojson 的 js 库。
相对于 GeoJSON 有很大优势:消除了大量冗余(比如边界地理数据重复等),使得描述同一地理的 TopoJSON 文件比 GeoJSON 文件体积小了很多;就绘制而言,使用 TopoJSON 可以做到仅绘制外围轮廓、共享边境不重复绘制、查找相邻地区等
要注意的是地图绘制都是基于 GeoJSON 的,TopoJSON 并不能直接用于绘图,它总是要转换成 GeoJSON 格式才能被用于绘图。
# 1.topojson.topology
将 GeoJSON 格式的数据对象转换成 TopoJSON 格式的对象。
第一个参数是包含 GeoJSON 数据的对象,第二个可选参数是指转换过程中处理坐标时的精度。
const topology = topojson.topology({ geo: geoJSON }, 1e6)
要注意的是第一个参数并不能直接传 GeoJSON 数据作为参数,而是需要这种 key-value 的形式,这个 key 会被用于在返回的 topology 对象中进行索引,具体使用看后面。
一个返回的 topology 对象(TopoJSON 对象)通常包含以下几个属性:
- arcs
- bbox
- objects - 几何对象集合
- transform
- type: “Topology”
# 2.topojson.feature
将 TopoJSON 对象中的几何对象转换成 GeoJSON,返回的是 Feature 或 FeatureCollection 几何对象。
第一个参数是要转换的 TopoJSON 对象,第二个参数是该对象内具体的某个几何对象。
const geo_data = topojson.feature(topology, topology.objects.geo)
返回的这个 geo_data 就能当成普通的 GeoJSON 数据来使用。
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
Map,
View,
layer: { Tile: TileLayer, Layer },
source: { State: SourceState, Stamen },
extent: { getCenter, getWidth },
proj: { fromLonLat, toLonLat },
} = ol
class CanvasLayer extends Layer {
constructor(options) {
super(options)
this.features = options.features
this.svg = d3
.select(document.createElement("div"))
.append("svg")
.style("position", "absolute")
this.svg.append("path").datum(this.features).attr("class", "boundary")
}
getSourceState() {
return SourceState.READY
}
render(frameState) {
const width = frameState.size[0]
const height = frameState.size[1]
const projection = frameState.viewState.projection
const d3Projection = d3.geoMercator().scale(1).translate([0, 0])
let d3Path = d3.geoPath().projection(d3Projection)
const pixelBounds = d3Path.bounds(this.features)
const pixelBoundsWidth = pixelBounds[1][0] - pixelBounds[0][0]
const pixelBoundsHeight = pixelBounds[1][1] - pixelBounds[0][1]
const geoBounds = d3.geoBounds(this.features)
const geoBoundsLeftBottom = fromLonLat(geoBounds[0], projection)
const geoBoundsRightTop = fromLonLat(geoBounds[1], projection)
let geoBoundsWidth = geoBoundsRightTop[0] - geoBoundsLeftBottom[0]
if (geoBoundsWidth < 0) {
geoBoundsWidth += getWidth(projection.getExtent())
}
const geoBoundsHeight = geoBoundsRightTop[1] - geoBoundsLeftBottom[1]
const widthResolution = geoBoundsWidth / pixelBoundsWidth
const heightResolution = geoBoundsHeight / pixelBoundsHeight
const r = Math.max(widthResolution, heightResolution)
const scale = r / frameState.viewState.resolution
const center = toLonLat(getCenter(frameState.extent), projection)
const angle = (-frameState.viewState.rotation * 180) / Math.PI
d3Projection
.scale(scale)
.center(center)
.translate([width / 2, height / 2])
.angle(angle)
d3Path = d3Path.projection(d3Projection)
d3Path(this.features)
this.svg.attr("width", width)
this.svg.attr("height", height)
this.svg.select("path").attr("d", d3Path)
return this.svg.node()
}
}
const map = new Map({
layers: [
new TileLayer({
source: new Stamen({
layer: "watercolor",
}),
}),
],
target: this.$refs.map,
view: new View({
center: fromLonLat([-97, 38]),
zoom: 4,
}),
})
d3.json(this.$withBase("/data/topojson/us.json")).then(function (us) {
const layer = new CanvasLayer({
features: topojson.feature(us, us.objects.counties),
})
map.addLayer(layer)
})
},
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# 3.topojson.merge
简单点说就是获得 objects 包含的几何体组成的整体的外轮廓。返回的是一个 GeoJSON 的 MultiPolygon 几何对象。
const geo_outline = topojson.merge(topology, topology.objects.geo.geometries)
# 4.topojson.mesh
获得 objects 构成的整体的完整轮廓线,包括外轮廓和内部共享边界线,但是是一个整体的线,所以没有线条重复的情况。返回的是一个 GeoJSON 的 MultiLineString 对象。
如果不传 objects 参数,那就是处理整个 topology。
可选的 filter 参数是一个函数,它又接收两个参数。这个 filter 参数通常用来过滤不共享的外轮廓或者被共享的内部边界线。
const geo_interior = topojson.mesh(
topology,
topology.objects.geo,
(a, b) => a !== b
)
2
3
4
5
a !== b 会过滤掉外轮廓;a === b 则会过滤掉内部被共享的线。
# 5.topojson.neighbors
会返回一个数组,数组中的子数组对应着 objects 中相应索引的几何对象的相邻几何对象,但是只是包含相邻对象的索引。
const neighbors = topojson.neighbors(topology.objects.geo.geometries)