地图这东西,在我印象里,需要用到的话就接入一下百度高德之类的地图API就好啦,之前也一直是这样做的。
但之前需求量是很小的,做比赛,也就是能展示出来就行,图方便,直接拿现成的API接上就好了。
但是接下来有个正在讲究实用项目,是需要有很多个人看地图,在这个应用中,说是地图就是核心也不为过,所以接一个现成的API感觉不是很合适,而且还要交钱也说不定( ˘・з・)
所以,我想要一个能高度自定义,不受第三方约束的一种地图解决方案。
openlayers
openlayers是一个开源的,渲染地图的库。
除了把地图渲染出来并支持用户操作外,它还支持添加各种标记,这是一个我理想中的库。
但是说到底,渲染地图,得有地图才能渲染不是?
所以,还需要一个数据源。
天地图可能是个不错的选择,天地图提供的数据源可以直接拿到openlayers里面使用:
function getSourceUrl(url: string, tk: string) {
return url + `?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&` +
`tk=${tk}`
}
new ol.Map({
target: divRef.current,
layers: [
new layer.Tile({
source: new source.XYZ({
url: getSourceUrl(`http://t0.tianditu.gov.cn/cia_c/wmts`, tdtKey)
})
}),
],
view: new ol.View({
center: [114.39, 23.11],
zoom: 1,
maxZoom: 18,
})
})但是呢,这样用了,我发现一个问题。
众所周知,地图是由一个一个小图像组成的,称为瓦片。
天地图提供每天一万次的API调用,看似不少,实际上瓦片地图,一个瓦片就算一次API调用,我随随便便就调了上千次。
这个数量是远远不够的。
爬取数据
技术学的好,牢饭吃得饱
做的系统不是在互联网上公开的,所以我可以用一下其他手段解决问题。
假如客户端上有这么一个缓存机制,把所有看过的地图都存下来,岂不是能大大节约API调用次数?
假如把客户端上的缓存机制中心化,那岂不是每个图块只要加载一次,又大大节约了API调用次数?
假如所有需要用的图块都缓存了,拿岂不是就不用依赖第三方API了?(ノ>ω<)ノ
绕来绕去,我是在说一件事情,爬取下载所有用到的图块数据,放在自己的服务器上。
爬地图这件事情,Github上已经有现成的工具了。
这个工具可以对不同区域使用不同的下载精度。
至于怎么知道自己想要的区域多大嘛,下面这个仓库提供了中国各地的轮廓数据:
简单粗暴,得到需要的图块数据。
实测,线程数量开超级高也不会引起问题(我开的256线程),只是一言难尽的校园网搞得我做其他事情上不去网。
瓦片服务器
上述的爬取过后,获得了非常多(我有一百三十万个)的小文件,直接在爬取的信息上运行一个静态资源服务器就可以提供瓦片服务了。
但是这么多小文件有一些问题。
不好部署,小文件发送是很慢的,打包解包也麻烦。
有较多的额外占用,19G的文件占用了21G
高并发的时候性能不好。
面对这些问题,我的解决方法是,设计一种文件结构,把这些小文件存储进去。
文件结构
因为这个文件写入过一次后就再也不写了嘛。
设计这样一个简单的文件结构:
文件表起始偏移(8字节)
文件数据(若干长度)
文件一个接一个,只存数据。
文件表数据(若干长度)
存储文件名
存储文件起始偏移
写入时,按照这样的顺序操作:
先空8字节
内存里面建一个空文件表
写入文件数据
文件的数据直接写入文件
文件名和已经写入的文件数据的偏移量先记录在内存中
回去写8字节的文件表偏移
到文件末尾,将内存中的文件表写入文件
读取时,按照这样的顺序操作:
先读文件表偏移
整个文件表读取下来
预处理文件表,获得每个文件的起始和终止偏移
读文件数据
先查表,获得偏移量
读取偏移量区间内的数据
(✪ω✪)
Golang程序实现见Github仓库:
总结
不要在机械硬盘里面做这件事情( º﹃º )
接下来可能还有无人机拍摄的图层要加进来,感觉使用openlayers是相当正确的。
本地部署的地图数据源也相当舒服,不用依赖外部服务。