自建OpenStreetsmap地图瓦片服务
迫于openstreetsmap官方的瓦片服务器(tile server)速度太慢,而提供矢量瓦片(vector tile)服务的mapbox和maptiler的免费额度太少,更新慢。笔者最近尝试基于tileserver-gl,openmaptiles和tilemaker等工具自建了一个openstreetsmap的地图瓦片服务器。
数据准备
我们所需的地图数据有两种格式,分别为pbf和mbtiles。其中,mbtiles格式是可以直接被tileserver-gl使用的,而pbf格式需要转换为mbtiles之后才能被tileserver-gl读取。
mbtiles
如果希望快速搭建地图服务器,我们可以直接在https://extract.bbbike.org/这个网站上选择自己想要的区域并下载。下载时格式选择MB Vector Tiles Openmaptiles
即可。**但这样下载下来的mbtiles只有地标的name
字段,其他的翻译全部丢失了。**如果不介意这一点,可以在下载完后直接跳到「服务搭建」一节。
PBF
如果希望下载带有完整数据的PBF文件进行修改,我们可以在网站上选择下载PBF格式的地图,或者去 https://download.geofabrik.de/ 上按照国家下载PBF格式的地图。
转换格式
转换格式可以用两个软件,分别是tilemaker和openmaptiles。tilemaker的转换速度很快,不过转换出的mbtiles地图还是有上一节提到的「丢失属性」的问题,而openmaptiler在属性保留这一方面做的很好,而且还能从wikidata里自动下载对应地点的翻译(可选),不过转换的速度很慢。
根据笔者自己测试的转换速度比较,转换的是https://download.geofabrik.de/上下载的中国大陆地区地图,文件大小大约900M,用一台96C372G的机器进行转换。tilemaker转换用了大约半小时,而openmaptiler花了12个小时左右。转换完之后的mbtiles大小约3.5G。
使用tilemaker转换
首先下载tilemaker的二进制文件,之后准备好输入的文件,进行转换即可,机器可能需要安装luajit
,sqlite3
,shapelib
等依赖。:
/root/build/tilemaker --input /data/asia.osm.pbf --output /data/asia.mbtiles --config /root/resources/config-openmaptiles.json --process /root/resources/process-openmaptiles.lua
其中,json配置文件和lua文件直接使用发行版自带的配置文件即可,ZOOM
的值最大使用14即可,更大的值不会带来更多的细节。
使用openmaptiles转换
参考openmaptiles的README进行转换即可,机器需要预装docker
和docker-compose
,具体依赖可以参考这里。首先clone repo,之后在目录中执行(以下的几步需要拉取若干个docker镜像,加起来大约有10G,加上处理的数据,需要在磁盘中至少预留30G的空间比较保险):
(可选)如果要拉取维基百科的数据,则需要给docker添加代理。在.env
中添加:
http_proxy: http://192.168.59.100:8118
https_proxy: http://192.168.59.100:8118
HTTP_PROXY: http://192.168.59.100:8118
HTTPS_PROXY: http://192.168.59.100:8118
(可选)如果PBF数据是从bbbike上切出来的,需要预处理一下数据的边界(mydata即下载下来的数据文件名):
mkdir -p data
mv mydata.osm.pbf data/
make generate-bbox-file area=mydata
./quickstart.sh mydata
如果需要增大执行数据库操作和地图切割操作的线程,可以更改.env
里的MAX_PARALLEL_PSQL
和COPY_CONCURRENCY
。
初始化数据文件夹:
make
准备数据库:
make start-db
导入PBF的数据:
make import-data
导入边界数据:
make import-osm
make import-borders
(可选)
导入维基百科的附加数据:
make import-wikidata
清理数据库:
make clean
make
make import-sql
生成地图边界:
make generate-bbox-file # compute data bbox -- not needed for the whole planet
生成mbtiles
文件:
make generate-tiles # generate tiles
执行完成之后,在data
文件夹下就能看到一个名为tiles.mbtiles
的地图数据文件,复制出来即可。
服务搭建
笔者自己的配置文件repo:https://github.com/sparkcyf/tileserver-gl-config
将中文字体的repo(用于瓦片地图渲染中文):https://github.com/klokantech/klokantech-gl-fonts.git ,将repo中的
KlokanTech Noto Sans CJK Regular
重命名成Noto Sans Regular
,并放入下面提到的fonts文件夹。将前面的到的
tiles.mbtiles
地图数据文件放入相应的data
文件夹。
随后使用repo中的docker-compose文件拉起容器,注意修改配置路径为自己的路径:
version: '3.3'
services:
tileserver-gl:
image: maptiler/tileserver-gl:latest
build: .
command: --public_url https://example.com/osm-tile/ --no-cors --config /config/config.json
ports:
- "58085:8080"
volumes:
- '/data/mirrors-zfs/cra-service/vector-tile-server/map:/data'
- '/data/mirrors-zfs/cra-service/vector-tile-server/config:/config'
- '/data/mirrors-zfs/cra-service/vector-tile-server/style/styles:/app/node_modules/tileserver-gl-styles/styles'
- '/data/mirrors-zfs/cra-service/vector-tile-server/style/fonts:/app/node_modules/tileserver-gl-styles/fonts'
docker compose中提到的styles,config等文件都已经放在了上面的repo中了。另外记得修改--public_url
后面的值,以及docker暴露出来的端口。
**如果修改了样式文件或配置文件,重启容器即可重载配置。**有关tileserver-gl的更多用法,可以参考这里:https://tileserver.readthedocs.io/en/latest/
(可选)修改sprite
在部分的样式文件中,一些图标(比如麦当劳,肯德基,高速公路标志等)需要从外联的样式文件加载,可能会造成瓦片地图渲染缓慢。如果需要替换成本地文件或网络上的其他文件,可以修改样式json文件中的"sprite"
一节。sprite文件的具体类型可以参考这个repo。
全部配置完成之后,运行docker-compose up
,等待镜像被拉起即可查看地图了。
如果打开地图时发现地图是空的,有可能是因为你并没有下载地图对应位置的数据,这时修改url末尾的经纬度即可(下面的url中#
后面的那一串,分别是缩放级别/纬度/经度):
example.com/osm-tile/styles/osm-street/#11.16/22.5429/114.0402
在其他地图中引用瓦片服务器
可以参考mapbox写的这个例子,更改style
变量即可:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://cdn.maptiler.com/mapbox-gl-js/v1.5.1/mapbox-gl.js"></script>
<link href="https://cdn.maptiler.com/mapbox-gl-js/v1.5.1/mapbox-gl.css" rel="stylesheet" />
<link href="/static/css/cloud_base.css?t=1627890485" rel="stylesheet" />
<style>
#map {position: absolute; top: 0; right: 0; bottom: 0; left: 0;}
</style>
</head>
<body>
<div id="map">
<a href="https://www.maptiler.com" style="position:absolute;left:10px;bottom:10px;z-index:999;"><img src="https://api.maptiler.com/resources/logo.svg" alt="MapTiler logo"></a>
</div>
<p><a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a> <a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a></p>
<script>
// You can remove the following line if you don't need support for RTL (right-to-left) labels:
mapboxgl.setRTLTextPlugin('https://cdn.maptiler.com/mapbox-gl-js/plugins/mapbox-gl-rtl-text/v0.1.2/mapbox-gl-rtl-text.js');
var map = new mapboxgl.Map({
container: 'map',
style: 'https://example.com/path/styles/your-style/style.json',
center: [113.99548, 22.60003],
zoom: 15.92
});
map.addControl(new MapboxLanguage({
defaultLanguage: 'en'
}));
</script>
</body>
</html>
https://github.com/systemed/tilemaker/issues/187
参考文档
- https://blog.csdn.net/fbvukn/article/details/109072579
- https://stackoverflow.com/questions/65849406/is-there-a-way-to-generate-a-mbtiles-file-from-osm-pbf-file
- https://blog.kleunen.nl/blog/tilemaker-generate-map (如何转换PBF到mbtiles)
- https://yasoob.me/posts/custom-map-with-tileserver-gl/ (如何配置tileserver-gl的config.json)