echarts 画中国地图及省份切换

最近用 ehcarts 写了一个有关中国地图的需求,这篇文章来总结下基本的原理和用法。

Geojson

首先了解一下 GeoJSON ,看下 维基百科 的定义:

GeoJSON 是一种基于 JSON 的地理空间数据交换格式,它定义了几种类型 JSON 对象以及它们组合在一起的方法,以表示有关地理要素、属性和它们的空间范围的数据。

2015年,互联网工程任务组(IETF)与原始规范作者组建了一个 GeoJSON 工作组,一起规范 GeoJSON 标准。在2016年8月,推出了最新的GeoJSON数据格式标准规范(RFC 7946)。

GeoJSON 使用唯一地理坐标参考系统 WGS1984 和十进制度单位,一个 GeoJSON 对象可以是 Geometry, Feature 或者FeatureCollection.

其几何对象包括有点(表示地理位置)、线(表示街道、公路、边界)、多边形(表示国家、省、领土),以及由以上类型组合成的复合几何图形。

简单说就是通过坐标系来描述点、线、面,看几个例子就明白它们是什么了。

单个点:"type": "Point"

image-20220510095405517

多个点,"type": "MultiPoint"

image-20220510095158715

多个线:"type": "MultiLineString"

image-20220510095241321

多个面:"type": "MultiPolygon"

image-20220510095257874

地图 Geojson

中国地图和省份的 geoJson 可以在 echarts-map 或者阿里的 数据可视化中心 进行下载。

image-20220510101045037

echarts 4.x 的版本自带了一些 Geojson 的数据,在 node_modules/echarts/map/json 目录,但可能考虑到一些省区数据不能及时更新,echarts 5 版本就没有自带数据了。

让我们看一下全国地图中山西省的 geoJson 长什么样子。

1
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
{
"type": "FeatureCollection",
"features": [
...
{
...
}
{
"type": "Feature",
"properties": {
"adcode": 140000,
"name": "山西省",
"center": [112.549248, 37.857014],
"centroid": [112.304436, 37.618179],
"childrenNum": 11,
"level": "province",
"parent": { "adcode": 100000 },
"subFeatureIndex": 3,
"acroutes": [100000]
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[110.379257, 34.600612],
[110.424837, 34.588295],
[110.488279, 34.610956],
[110.533242, 34.583368],
[110.610851, 34.607508],
[110.710017, 34.605045],
[110.749437, 34.65232],
[110.791937, 34.649858],
[110.824582, 34.615881],
[110.883712, 34.64395],
[110.903422, 34.669056],
[110.920052, 34.730068],
...
]
]
]
}
},
{
...
}
...
]
}

整体是一个 "type": "FeatureCollection" ,然后有一个 features 数组保存所有省份,每一个都是 "type": "Feature" ,代表单个省份。包含 properties 属性和 geometry 属性。geometry 属性就是所有的坐标信息。

根据坐标信息,计算最大值和最小值的差值,按比例映射到 canvas 上的坐标,然后就可以画出来了,细节的话可以参考 b 站 的这个视频。

image-20220510102224426

echarts 画地图

安装 vueecharts ,先来个简单的正方形。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "正方形"
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
]
]
]
}
}
]
}

然后用 echarts 做引入我们的 json 文件、通过 echarts.registerMap 注册 json 文件、设置 opitonsseries 属性。

1
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
<template>
<div id="main" style="width: 600px; height: 600px"></div>
</template>

<script>
import * as echarts from "echarts";
import test from '../data/test'
export default {
name: "HelloWorld",
props: {
},
mounted() {
var myChart = echarts.init(document.getElementById("main"));
echarts.registerMap('mapName', test); // 注册地图
let option = {
series: [
{
type: "map",
map: 'mapName', // 引入地图数据
},
],
};
myChart.setOption(option);
},
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

image-20220511080857038

然后我们只需要到阿里的 数据可视化中心 把中国地图的 Geojson 数据下载下来,替换上边的 test.json 即可。

image-20220511081044461

值得注意的是,如果我们设置注册的名字为 chinaecharts 会自动给我们加上南沙群岛的放大图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import * as echarts from "echarts";
import china from '../data/china'
export default {
name: "HelloWorld",
props: {
},
mounted() {
var myChart = echarts.init(document.getElementById("main"));
echarts.registerMap('china', china);
let option = {
series: [
{
type: "map",
map: 'china', // 引入地图数据
},
],
};
myChart.setOption(option);
},
};

image-20220511082046489

可能会用到的 options 属性

地图画出来以后,接下来可以照着 echarts 官网 变身为「echarts 配置工程师」了,记得注意一下自己当前的 eharts 版本。

设置悬浮上的数据

我们在 series 中引入 data ,加一点随机数据,其中 name 值是 json 数据中的 properties 对应的 name ,名字一定要一致。

1
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
const option = {
...
series: [
{
type: "map",
map: "china", // 引入地图数据
name: "省份随机数据",
data: [
{
name: "北京市",
value: 21,
},
{
name: "天津市",
value: 12,
},
{
name: "上海市",
value: 99,
},
{
name: "重庆市",
value: 98,
},
{
name: "河北省",
value: 99,
},
{
name: "河南省",
value: 29,
},
{
name: "云南省",
value: 79,
},
{
name: "辽宁省",
value: 38,
},
{
name: "黑龙江省",
value: 4,
},
{
name: "湖南省",
value: 32,
},
{
name: "安徽省",
value: 84,
},
{
name: "山东省",
value: 72,
},
{
name: "新疆维吾尔自治区",
value: 99,
},
{
name: "江苏省",
value: 70,
},
{
name: "浙江省",
value: 85,
},
{
name: "江西省",
value: 11,
},
{
name: "湖北省",
value: 62,
},
{
name: "广西壮族自治区",
value: 13,
},
{
name: "甘肃省",
value: 74,
},
{
name: "山西省",
value: 78,
},
{
name: "内蒙古自治区",
value: 74,
},
{
name: "陕西省",
value: 40,
},
{
name: "吉林省",
value: 9,
},
{
name: "福建省",
value: 90,
},
{
name: "贵州省",
value: 57,
},
{
name: "广东省",
value: 6,
},
{
name: "青海省",
value: 52,
},
{
name: "西藏自治区",
value: 10,
},
{
name: "四川省",
value: 98,
},
{
name: "宁夏回族自治区",
value: 11,
},
{
name: "海南省",
value: 25,
},
{
name: "台湾省",
value: 86,
},
{
name: "香港特别行政区",
value: 8,
},
{
name: "澳门特别行政区",
value: 50,
},
],
},
],
...
}

再补上 tooltip 选项。

1
2
3
4
5
6
7
const option = {
...
tooltip: {
trigger: "item",
},
...
}

image-20220512084334737

视觉映射

我们可以通过 visualMap 选项,将数据分组。

1
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
const option = {
...
visualMap: {
left: "right",
min: 0,
max: 100,
inRange: {
color: [
"#313695",
"#4575b4",
"#74add1",
"#abd9e9",
"#e0f3f8",
"#ffffbf",
"#fee090",
"#fdae61",
"#f46d43",
"#d73027",
"#a50026",
],
},
text: ["High", "Low"],
calculable: true,
},
...
}

设置后之后,我们可以滑动右下角的范围来选取不同的省份。

image-20220512084634727

除了滑块的映射,还支持分区间的,类似下边这种。

image-20220512084908512

其他选项

其他选项这里就不介绍了,可以参考 官网社区 的样例,然后结合自己的需求进行配置即可。

贴几张社区上炫酷的地图:

image-20220512085159589

image-20220512085134411

省份切换

下边再实现一下点击省份切换到对应的省份地图的功能。

知道了上边的东西,思路其实很简单了,我们只需要把所有省份的 Geojson 数据全部下载下来,然后监听 echarts 的点击事件去显示省份即可。

为了逻辑之间的解耦,我们可以再新建一个组件,专门展示省份的数据。

1
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
<template>
<div>
<div id="province" style="width: 600px; height: 600px"></div>
</div>
</template>

<script>
import * as echarts from "echarts";

export default {
name: "Province",
props: {
fileName: String,
},
data() {
return {
option: {
series: [
{
name: "省份数据",
type: "map",
map: "province",
data: [],
},
],
},
};
},
mounted() {
this.initData();
},
methods: {
initData() {
try {
const provinceJSON = require("../data/province/" +
this.fileName);
const myChart = echarts.init(
document.getElementById("province")
);
echarts.registerMap("province", provinceJSON);
myChart.setOption(this.option);
} catch (e) {
alert(`暂无${this.fileName}数据`);
this.$emit("toMap");
}
},
},
};
</script>

<style scoped rel="stylesheet/scss" lang="scss"></style>

我们把省份数据都放到 "../data/province" 目录中,这里简单演示,只下载了两个省份的地图:

image-20220512091724599

通过外部传进来文件的 fileName 注册地图。这里直接通过 require("../data/province" + this.fileName) 来动态引入 Geojson,一定要加上 "../data/province" 前缀来限制文件的位置,关于 webpack 的动态引入的更多细节可以参考 Webpack 打包 commonjs 和 esmodule 动态引入模块的产物对比

我们增加一个 ProvinceMap 组件来调度两个组件的显示隐藏。

1
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
<template>
<div>
<Province
v-if="showProvince"
:fileName="fileName"
@toMap="toMap"
></Province>

<Map v-else @toProvince="toProvince"></Map>
</div>
</template>

<script>
import Province from "./Province.vue";
import Map from "./Map.vue";

export default {
name: "HelloWorld",
components: {
Province,
Map,
},
data() {
return {
showProvince: false,
fileName: null,
};
},
methods: {
// 显示省份数据
toProvince({ fileName } = {}) {
this.fileName = fileName;
this.showProvince = true;
},
// 显示全国地图
toMap() {
this.showProvince = false;
},
},
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped></style>

Map 组件中监听省份的 click ,传递给 ProvinceMap 组件。

1
2
3
4
5
6
7
8
9
initData() {
var myChart = echarts.init(document.getElementById("main"));
echarts.registerMap("china", china);
const option = ....;
myChart.setOption(option);
myChart.on("click", (params) =>
this.$emit("toProvince", { fileName: params.name })
);
},

Province 组件中监听 click,传递给 ProvinceMap 组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
initData() {
try {
const provinceJSON = require("../data/province/" +
this.fileName);
const myChart = echarts.init(
document.getElementById("province")
);
echarts.registerMap("province", provinceJSON);
myChart.setOption(this.option);
myChart.on("click", () => this.$emit("toMap"));
} catch (e) {
alert(`暂无${this.fileName}数据`);
this.$emit("toMap");
}
},

最后看一下实现的效果:

Kapture 2022-05-12 at 10.06.29

通过 GeoJSONecharts ,知道大致的原理,然后其他配置项参考 官网社区 的例子比对上 配置项 慢慢配置即可,文章的整体代码放到了 github,需要的同学可以参考。

ECharts 最初由百度团队开源,并于 2018 年初捐赠给Apache 基金会,2021126 日晚,Apache 基金会官方宣布 ECharts 项目正式毕业,成为 Apache 顶级项目。

平时开发 Echarts ,我们就可以从「切图仔」变成「echarts 配置工程师了」,手动狗头。

windliang wechat