Mapbox 地图教程

Mapbox 地图使用教程

Mapbox 地图初始化

本文主要针对于使用本地 GeoJSON 数据创建地图服务, 最终目的是使用本地 WebServer 启动静态地图页面, 如果需要使用 Mapbox 线上资源, 请参考 Mapbox 线上示例

完整示例 mapbox-sample.zip

创建 HTML 引入资源

打开编辑器, 创建 index.html, 并生成基础 HTML 结构.

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Mapbox Map</title>
</head>
<body></body>
</html>

引入 mapbox-gl.jsmapbox-gl.css 文件, 由于官方的资源库连接速度较慢, 这里使用 BootCDN 的 CDN 服务.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Mapbox Map</title>
+ <script src="https://cdn.bootcss.com/mapbox-gl/1.1.0/mapbox-gl.js"></script>
+ <link href="https://cdn.bootcss.com/mapbox-gl/1.1.0/mapbox-gl.css" rel="stylesheet" />
</head>
<body></body>
</html>

由于本文目的在于使用本地 WebServer 启动地图页面, 需要将上述文件下载到本地, 与 index.html 放在相同目录下, 并修改 HTML 文件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Mapbox Map</title>
- <script src="https://cdn.bootcss.com/mapbox-gl/1.1.0/mapbox-gl.js"></script>
- <link href="https://cdn.bootcss.com/mapbox-gl/1.1.0/mapbox-gl.css" rel="stylesheet" />
+ <script src="./mapbox-gl.js"></script>
+ <link href="./mapbox-gl.css" rel="stylesheet" />
</head>
<body></body>
</html>

创建地图容器

在 HTML 文件 body 标签中间增加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Mapbox Map</title>
<script src="./mapbox-gl.js"></script>
<link href="./mapbox-gl.css" rel="stylesheet" />
</head>
<body>
+ <div id="map"></div>
</body>
</html>

head 标签里增加 map 的样式表

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Mapbox Map</title>
<script src="./mapbox-gl.js"></script>
<link href="./mapbox-gl.css" rel="stylesheet" />
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ }
+ #map {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ width: 100%;
+ }
+ </style>
</head>
<body>
<div id="map"></div>
</body>
</html>

地图初始化

当前目录创建 index.js, 并在 HTML 文件中引入

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Mapbox Map</title>
<script src="./mapbox-gl.js"></script>
<link href="./mapbox-gl.css" rel="stylesheet" />
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
</body>
+ <script src="./index.js"></script>
</html>

index.js 文件中增加地图初始化定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// index.js

const map = new mapboxgl.Map({
container: "map", // 使用 id = map 的元素用于地图容器
style: {
// 样式版本, 本地加载使用 version: 8
version: 8,
// 无数据源使用
sources: {},
// 图层定义
layers: [
// 背景层
{
id: "background",
type: "background",
paint: {
// 背景颜色
"background-color": "#75cff0",
},
},
],
},
});

使用浏览器打开 index.html, 此时显示为一片蓝色背景的地图

image.png

使用本地 GeoJSON 数据

下载地图资源

Mapbox 使用本地数据时, 需要引入

  • 地图资源 maps.zip. 这里使用大熊猫基地的地图数据作为示例.
  • 英文字体包 font.zip. 汉字字体可以使用页面字体加载, 英文字体必须通过 glyphs 加载.
  • 地图雪碧图 sprite.zip. 通常是一个项目使用一组雪碧图, 包含原始大小和 2x 的图片与 json 文件.

雪碧图的生成参考 Mapbox 雪碧图制作 教程.

下载上述文件并解压到前文的根目录下, 此时的目录结构如下, font 目录下为形如 0-255.pbf 的多个 pbf 文件.

image.png

下载安装 serve 用于启动本地 WebServer

需要安装 nodejs.

1
$ npm i -g serve

在当前目录打开命令行, 输入

1
2
3
4
5
6
7
8
9
10
11
12
13
$ serve

# 显示结果如下
### ┌───────────────────────────────────────────────┐
### │ │
### │ Serving! │
### │ │
### │ - Local: http://localhost:5000 │
### │ - On Your Network: http://10.0.75.1:5000 │
### │ │
### │ Copied local address to clipboard! │
### │ │
### └───────────────────────────────────────────────┘

在本地启动了一个 WebServer, 监听 5000 端口, 默认打开当前目录下的 index.html 文件.

在浏览器中打开 http://localhost:5000, 可以看到与前文相同的页面

image.png

增加地图数据源

修改 index.js 文件, 修改 sources 的值

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
// index.js

const map = new mapboxgl.Map({
container: "map", // 使用 id = map 的元素用于地图容器
style: {
// 样式版本, 本地加载使用 version: 8
version: 8,
// 使用 maps 目录下的 GeoJSON 文件作为数据源
sources: {
+ // 图标数据
+ label: {
+ type: "geojson",
+ data: "./maps/label.geojson"
+ },
+ // 面数据
+ polygon: {
+ type: "geojson",
+ data: "./maps/polygon.geojson"
+ },
+ // 线数据
+ line: {
+ type: "geojson",
+ data: "./maps/line.geojson"
+ }
},
// 图层定义
layers: [
// 背景层
{
id: "background",
type: "background",
paint: {
// 背景颜色
"background-color": "#75cff0"
}
}
]
}
});

增加地图样式

加载数据源后, 依然无法看到实际的地图显示, 这是因为还没有添加需要显示的面图层, 增加 layers 中图层

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
// index.js

const map = new mapboxgl.Map({
// 使用 id = map 的元素用于地图容器
container: "map",
// 样式
style: {
// 样式版本, 本地加载使用 version: 8
version: 8,
// 使用 maps 目录下的 GeoJSON 文件作为数据源
sources: {
// 图标数据
label: {
type: "geojson",
data: "./maps/label.geojson"
},
// 面数据
polygon: {
type: "geojson",
data: "./maps/polygon.geojson"
},
// 线数据
line: {
type: "geojson",
data: "./maps/line.geojson"
}
},
// 图层定义
layers: [
// 背景层
{
id: "background",
type: "background",
paint: {
// 背景颜色
"background-color": "#75cff0"
}
},
+ // 填充层
+ {
+ id: "fill",
+ type: "fill",
+ source: "polygon",
+ filter: [
+ "in",
+ "type",
+ "bottom",
+ "grass",
+ "green",
+ "green_down",
+ "green_middle",
+ "river",
+ "highway",
+ "road"
+ ],
+ paint: {
+ // 填充颜色
+ "fill-color": [
+ // 按照 geojson type 属性进行过滤
+ "match",
+ ["get", "type"],
+ "grass",
+ "#d0e0c7",
+ "green",
+ "#9dcaaa",
+ "green_middle",
+ "#B5D7BC",
+ "green_down",
+ "#C5DFBC",
+ "river",
+ "#9ED3F3",
+ "highway",
+ "#F1F1F3",
+ "bottom",
+ "#ffffff",
+ "road",
+ "#FFFDF0",
+ // 默认值
+ "#000000"
+ ],
+ // 填充轮廓颜色
+ "fill-outline-color": [
+ "match",
+ ["get", "type"],
+ "grass",
+ "#d0e0c7",
+ "green",
+ "#9dcaaa",
+ "green_middle",
+ "#B5D7BC",
+ "green_down",
+ "#C5DFBC",
+ "river",
+ "#9ED3F3",
+ "highway",
+ "#F1F1F3",
+ "bottom",
+ "#ffffff",
+ "road",
+ "#FFFDF0",
+ "#000000"
+ ]
+ }
+ }
]
}
});

刷新展示页面, 依然只有地图显示.

由于地图的默认中心设置在 [0,0], 并未处于数据源的中心, 需要在 map 的参数中增加 zoom, minZoom, maxZoom, center 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// index.js

const map = new mapboxgl.Map({
container: "map", // 使用 id = map 的元素用于地图容器
+ // 当前地图显示等级
+ zoom: 16,
+ // 地图最小缩放等级
+ minZoom: 15,
+ // 地图最大缩放等级
+ maxZoom: 18,
+ // 地图中心
+ center: [104.14417, 30.73596]
// 样式
style:{
//.....
}
});

刷新页面, 展示结果如下, 此时可以使用鼠标拖动与缩放地图

image.png

此时刷新页面, 会提示出错

image.png

增加 glyphs 与 sprite 路径

需要指定样式使用的字体包路径, 在 style 中添加属性 glyphs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// index.js

const map = new mapboxgl.Map({
//.......

// 样式
style: {
// 样式版本, 本地加载使用 version: 8
version: 8,
+ // 字体包路径
+ glyphs: "./{fontstack}/{range}.pbf"
//......
}
});

image.png

可以看到文字已经加入地图上, 但图标还未显示成功, 使用相同的方法, 在 style 中增加属性 sprite

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// index.js

const map = new mapboxgl.Map({
//.......

// 样式
style: {
// 样式版本, 本地加载使用 version: 8
version: 8,
// 字体包路径
glyphs: "./{fontstack}/{range}.pbf",
+ // 雪碧图路径
+ sprite: "./sprite/sprite"
//......
}
});

解决图标路径 BUG

刷新页面, 无法加载地图, 报错 Unable to parse URL object

image.png

Mapbox 无法解析相对路径下的 sprite 路径, 需要修改 sprite 属性, 使用当前页面路径进行填充.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// index.js

+ // 当前页面绝对路径, sprite 无法使用相对路径获取, 因此定义 locationURL
+ const locationURL =
+ window.location.origin +
+ window.location.pathname.substring(
+ 0,
+ window.location.pathname.lastIndexOf("/")
+ );

const map = new mapboxgl.Map({
//......

style: {
// 样式版本, 本地加载使用 version: 8
version: 8,
// 字体包路径
glyphs: "./{fontstack}/{range}.pbf",
// 雪碧图路径
- sprite: "./sprite/sprite"
+ sprite: `${locationURL}/sprite/sprite`
//...
}
});

此时的地图展示如下

image.png

完整 index.js 文件如下

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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// index.js

// 当前页面绝对路径, sprite 无法使用相对路径获取, 因此定义 locationURL
const locationURL =
window.location.origin +
window.location.pathname.substring(
0,
window.location.pathname.lastIndexOf("/")
);

const map = new mapboxgl.Map({
container: "map", // 使用 id = map 的元素用于地图容器
// 当前地图显示等级
zoom: 16,
// 地图最小缩放等级
minZoom: 15,
// 地图最大缩放等级
maxZoom: 18,
// 地图中心
center: [104.14417, 30.73596],
// 样式
style: {
// 样式版本, 本地加载使用 version: 8
version: 8,
// 字体包路径
glyphs: "./{fontstack}/{range}.pbf",
// 雪碧图路径
sprite: `${locationURL}/sprite/sprite`,
// 使用 maps 目录下的 GeoJSON 文件作为数据源
sources: {
// 图标数据
label: {
type: "geojson",
data: "./maps/label.geojson",
},
// 面数据
polygon: {
type: "geojson",
data: "./maps/polygon.geojson",
},
// 线数据
line: {
type: "geojson",
data: "./maps/line.geojson",
},
},
// 图层定义
layers: [
// 背景层
{
id: "background",
type: "background",
paint: {
// 背景颜色
"background-color": "#75cff0",
},
},
// 填充层
{
id: "fill",
type: "fill",
source: "polygon",
filter: [
"in",
"type",
"bottom",
"grass",
"green",
"green_down",
"green_middle",
"river",
"highway",
"road",
],
paint: {
// 填充颜色
"fill-color": [
// 按照 geojson type 属性进行过滤
"match",
["get", "type"],
"grass",
"#d0e0c7",
"green",
"#9dcaaa",
"green_middle",
"#B5D7BC",
"green_down",
"#C5DFBC",
"river",
"#9ED3F3",
"highway",
"#F1F1F3",
"bottom",
"#ffffff",
"road",
"#FFFDF0",
// 默认值
"#000000",
],
// 填充轮廓颜色
"fill-outline-color": [
"match",
["get", "type"],
"grass",
"#d0e0c7",
"green",
"#9dcaaa",
"green_middle",
"#B5D7BC",
"green_down",
"#C5DFBC",
"river",
"#9ED3F3",
"highway",
"#F1F1F3",
"bottom",
"#ffffff",
"road",
"#FFFDF0",
"#000000",
],
},
},
// 道路文字
{
id: "roadLabel",
type: "symbol",
source: "line",
filter: ["in", "type", "highway", "motorway"],
layout: {
"symbol-spacing": 350,
"symbol-placement": "line",
"text-field": "{label}",
"text-font": ["font"],
"text-size": ["match", ["get", "type"], "highway", 16, 18],
},
paint: {
"text-color": "#415A59",
"text-halo-color": "#fff",
"text-halo-width": 0.5,
},
},
// 图标与文字
{
id: "label",
type: "symbol",
source: "label",
layout: {
"icon-image": "{type}",
"icon-size": 0.3,
"text-field": "{label}",
"text-font": ["font"],
"text-size": 12,
"text-offset": [0, 1],
"text-anchor": "top",
},
paint: {
"text-color": "#415A59",
"text-halo-color": "#fff",
"text-halo-width": 0.5,
},
},
],
},
});