前言

近期,突发奇想,因为学校的地图并不太直观,要么细节数据不全,要么操作很难,需要点击多级才可以进入查看
因此,决定自己动手创建一份地图,方便日常使用

最后在询问了 AI 之后,决定使用 Tileserver-GL+MapLiberGL+OSM 来创建自己的地图

部署 Tileserver-GL

安装 Tileserver-GL

根据官方的教程, 使用 docker 来安装 Tileserver-GL 是最为便捷的

但是,官方并没有说明后期需要导入数据的时候应该如何操作,我在这里也踩了很多坑

最后,我根据自己的情况,写了一个docker-compose.yml文件,内容如下,可以参考一下:

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

services:
tileserver:
image: maptiler/tileserver-gl
ports:
- "8080:8080"
volumes:
- "/home/u0_a244/TileServer-GL/data:/data"
- "/home/u0_a244/TileServer-GL/styles:/styles"
- "/home/u0_a244/TileServer-GL/config.json:/config.json"
- "/home/u0_a244/TileServer-GL/fonts:/fonts"
command: ["--config", "/config.json"]

其中,ports 部分是将容器的 8080 端口映射到宿主机的 8080 端口
volumes 部分是将宿主机的相关目录挂载到容器中

在我的环境中,/home/u0_a244/TileServer-GL/ 是我用来存放 Tileserver-GL 相关文件的目录,数据如下:

1
2
3
4
5
6
7
8
9
10
11

~/TileServer-GL$ tree -L 2
.
├── config.json
├── data
│   └── guangdong.mbtiles
├── docker-compose.yml
├── fonts
│   └── MiSans Semibold
└── styles
└── osm_liberty.json

请根据自己的实际情况,修改 docker-compose.yml 中的路径

至于这些文件是怎么来的,后面会讲到

修改配置文件

在前面的 docker-compose.yml 文件中,我挂载了一个 config.json 文件,这个文件是用来配置 Tileserver-GL 的

文档可以在官网找到

我这里使用的配置文件为:

config.json
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
{
"options": {
"paths": {
"root": "",
"styles": "styles",
"fonts": "fonts",
"mbtiles": "data"
},
"domains": [
"my-domain:8080"
],
"formatOptions": {
"jpeg": {
"quality": 80
},
"webp": {
"quality": 90
}
},
"maxScaleFactor": 3,
"maxSize": 2048,
"pbfAlias": "pbf",
"serveAllFonts": false,
"serveAllStyles": false,
"serveStaticMaps": true,
"allowRemoteMarkerIcons": true,
"allowInlineMarkerImages": true,
"staticAttributionText": "© OpenMapTiles © OpenStreetMaps",
"tileMargin": 0
},
"styles": {
"osm-liberty": {
"style": "osm_liberty.json",
"serve_rendered": true
},
"remote": {
"style": "https://demotiles.maplibre.org/style.json"
}
},
"data": {
"openmaptiles": {
"mbtiles": "guangdong.mbtiles"
}
}
}

这里重点需要修改的是domains部分,将 my-domain:8080 修改为你自己的域名或者 IP 地址加端口号,否则无法正常请求(挂载到服务器用的时候绝对不能写成 localhost)

此外,mbtiles 部分需要修改为你自己的 mbtiles 文件名(因为我在文件上方已经说明了 mbtiles 是在 data 目录下的,所以就不用再写 data/ 了)

styles 部分是用来配置样式文件的,也需要改成自己的样式文件名,后面会讲到如何获取和修改样式文件

获取 mbtiles 数据

Tileserver-GL 需要 mbtiles 格式的数据才能运行,获取 mbtiles 数据有很多种方式,这里介绍两种比较常用的方式

方式一:BBBike 下载

BBBike 是一个提供地图数据下载的网站,支持多种格式的地图数据下载,包括 mbtiles 格式

  1. 访问 BBBike 官网
  2. 在地图上选择你需要的区域,可以通过搜索城市名称来快速定位
  3. 选择输出格式为 mbtiles
  4. 提交下载请求,填入邮箱,等待 10 分钟左右处理完成后下载 mbtiles 文件

这个方式的优点是简单快捷,适合下载小范围的地图数据

但是因为地图的生成方式不是自己控制的,后续在数据处理和渲染的时候会遇到不少问题

这部分我会在渲染样式的时候讲到

方式二:使用 MapTiler 下载

MapTiler 官方提供了数据下载,可以直接访问 MapTiler 数据下载页面,选择区域下载即可

但是它的免费数据下载有一些限制:

  • OpenStreetMap Vectors (2020)
  • Satellite Low-Res (2016)
  • Non-commercial

反正我是觉得 2020 年的数据有点旧了,而且所以就没有选这个

方式三:使用 OSM 数据 + tilemaker 自己生成 mbtiles

  1. 下载 OSM 数据,可以从 Geofabrik 下载所需区域的 PBF 文件
  2. 安装 tilemaker,可以参考 tilemaker 的 GitHub 页面

其实,如果并不在意程序的最新版本的话,可以直接在GitHub的 Releases 页面下载编译好的二进制文件
但是需要注意的是,Windows 下的 v3.0.0 版本是有问题的,Windows用户 建议下载 v2.4.0 版本
另外,还需要下载配置文件 config-openmaptiles.json 和处理脚本 process-openmaptiles.lua,可以从该仓库的 resources 目录中获取

  1. 使用 tilemaker 将 PBF 文件转换为 mbtiles 文件,实例命令如下:
1
tilemaker --input "guangdong-260113.osm.pbf"  --output "guangdong.mbtiles" --config .\config-openmaptiles.json --process .\process-openmaptiles.lua

其中,--input 指定输入的 PBF 文件,--output 指定输出的 mbtiles 文件,--config--process 分别指定配置文件和处理脚本,需要自己替换成实际的文件路径

如果想裁切数据,可以使用 --bbox 参数,格式为 minLon,minLat,maxLon,maxLat,例如:

1
tilemaker.exe --input "guangdong-260113.osm.pbf" --bbox 113,22.34,114,22.50 --output "guangdong.mbtiles" --config .\config-openmaptiles.json --process .\process-openmaptiles.lua

还有值得注意的是,process-openmaptiles.lua 文件开头的几个带 language 的变量需要根据自己后面的 style.json 文件来决定,否则可能会导致某些标签无法显示,我后面会再次提到

然后打完这个命令之后,等待一段时间(我生成一个学校的大小用时大概为 30s, 使用 i7-12700H CPU,占用 5.5GB 内存),就可以得到一个 mbtiles 文件了

最后记得将它移动到 Tileserver-GL 的 data 目录下

获取 Tileserver-GL 样式文件

虽然默认直接启动,它是会自动加载自带的样式文件的,但是因为调整不方便,而且并不是很好看,我就选择了使用其他的 style 文件

我是在 Maputnik 上的左上角找到了一个叫做 OSM Liberty 的样式文件,预览后觉得还可以,就点击左上角的 Save 按钮下载

但是,由于各个文件的渲染逻辑不同,为了不渲染出现问题,需要对样式文件进行一些修改

修改名称读取

打开下载下来的 osm-liberty.json 文件,ctrl+f找到 name 部分,将某些的 name:latin 进行修改

具体是怎么改,还是要看回去上一节中 process-openmaptiles.lua 文件中 language 变量的设置

比如我设置的是:

process-openmaptiles.lua
1
2
3
4
5
6
7
8
-- Preferred language can be (for example) "en" for English, "de" for German, or nil to use OSM's name tag:
preferred_language = nil
-- This is written into the following vector tile attribute (usually "name:latin")
preferred_language_attribute = "name"
-- If OSM's name tag differs, then write it into this attribute (usually "name_int"):
default_language_attribute = "name_int"
-- Also write these languages if they differ - for example, { "de", "fr" }
additional_languages = {"zh","en"}

在生成 mbtiles 的时候,根据这个设置就会将 OpenStreetMap 中的 name 标签写入 name 属性中,name:zh 标签写入 name:zh 属性中,name:en 标签写入 name:en 属性中 (理论上是,但是不知道为什么我生成的文件中并没有 name:zh 标签)

所以,我就应该把全部 name:latin 改成 name,因为生成的文件根本就没有 name:latin 标签

修改地图路径

在样式文件中,通常都会直接指定地图 tiles 的路径,但是这个也可以通过引用 config.json 文件中的数据源来读取

比如我就写成了

style.json
1
2
3
4
5
6
7
"sources": {
"openmaptiles": {
"type": "vector",
"url": "mbtiles://{openmaptiles}",
"attribution": "© OpenStreetMap contributors"
},
}

这样就可以直接引用 config.json 文件中 data 部分的 openmaptiles 数据源了

如果你的 config.json 文件中 data 部分的名称不是 openmaptiles 的话,需要相应地修改这里的名称

修改字体

默认字体是无法显示中文的,所以需要修改字体

首先是下载字体,我这里使用的是 MiSans ,先获取到字体的 ttf 或者 otf 文件

然后,需要用特殊的工具将字体转换为 PBF 格式

可以使用 font-maker 这个网站来转换

也可以使用 fonts 这个工具来转换

效果应该都差不多的

转换完成后,将生成的存有 pbf 文件夹整个放到 Tileserver-GL 的 fonts 目录下

最后,修改样式文件中的字体引用,找到以下位置修改:

style.json
1
"glyphs": "{fontstack}/{range}.pbf",

此外,还需要搜索整个文件,找到带有 text-font 的地方,将字体名称修改为你刚才转换的字体文件夹的名称

例如我写的是 "text-font": ["MiSans Semibold"],

启动 Tileserver-GL

完成以上步骤后,就可以启动 Tileserver-GL 了
在 Tileserver-GL 目录下,运行以下命令:

1
docker-compose up -d

然后,打开浏览器,访问 http://your-domain:8080 (将 your-domain 替换为你的域名或者 IP 地址),就可以看到自己的地图了

创建前端 html 页面

最后,如果想要创建一个前端的 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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Map</title>
<link href="https://unpkg.com/maplibre-gl@2.1.9/dist/maplibre-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>
<script src="https://unpkg.com/maplibre-gl@2.1.9/dist/maplibre-gl.js"></script>
<script>
const map = new maplibregl.Map({
container: 'map',
style: 'http://your-domain:8080/styles/osm-liberty/style.json',
center: [113.5, 22.4],
zoom: 12
});
</script>
</body>
</html>

这里的 style 地址需要修改为你自己的 Tileserver-GL 的样式地址,建议使用 https 来避免跨域问题

后面再在上方加 POI 标记、自定义画线什么的,参照 MapLibre GL JS 官方文档 就可以了,或者问 AI 也行,实测错误率不高

反正我后面使用的 Overpass API 来获取 POI 数据,然后使用 MapLibre GL JS 的 Marker 功能来渲染这些数据,效果还不错