写在前面的话
哈哈,终于做了一次标题党!giser小伙伴们是不是也像我一样朝思慕想这个功能的出世呢?可惜Mapbox团队只是增加了缓存机制,对此仍未支持。虽说有对本地离散文件瓦片的支持,可面对瓦片数量时,你懂的!无奈之下只能深入源码自力更生,并分享予大家。
一、先从源码说起
经历了一层层剖析源码的各种曲折(Java与C++的双向交互),改造源码的各种尝试(希望对Mapbox现有接口最小化改造),编译调试源码时的焦急等待,效果出来时的那份激动(小雨中、夜色中黄河岸边溜达了好久),才有了这篇技术干货分享。此等经历有兴趣的小伙伴可以亲身体验体验哦,也欢迎留言交流分享源码心得。
最后寻找到的最佳改造方式是什么样的呢?那要从下面这段资源请求(外部设置的uri)的核心代码说起了。
if (AssetFileSource::acceptsURL(resource.url)) {
//Asset request--对应传入URI的协议头为"asset://"
tasks[req] = assetFileSource->request(resource, callback);
} else if (LocalFileSource::acceptsURL(resource.url)) {
//Local file request--对应传入URI的协议头为"file://"
tasks[req] = localFileSource->request(resource, callback);
} else {//--对应传入URI的协议头为"http(s)://"
// Try the offline database--本地离线缓存中查找URI资源
if (resource.hasLoadingMethod(Resource::LoadingMethod::Cache)) {
auto offlineResponse = offlineDatabase->get(resource);
。。。。。。
}
// Get from the online file source--在线请求传入的URI资源
if (resource.hasLoadingMethod(Resource::LoadingMethod::Network)) {
MBGL_TIMING_START(watch);
tasks[req] = onlineFileSource.request(resource, [=] (Response onlineResponse) {
//--将请求到的资源保存到本地缓存
this->offlineDatabase->put(resource, onlineResponse);
。。。。。。
}
}
源码中对应的是Mapbox定义和支持的三种协议头,分别是:
http://,load resources using HyperText Transfer Protocol,通过http协议请求网络资源。
file://,load resources from the Android file system,请求Android本地文件系统资源。
asset://,load resources from the binary packaged assets folder,请求assets包中的资源。
接下来咱来看看开发过程中调用栅格瓦片的常见代码:
mapboxMap.setStyle(styleUri, new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull Style style) {
// 访问天地图注记
TileSet vecTileSet = new TileSet("1.0.0", "http://t0.tianditu.gov.cn/DataServer?T=cia_w&X={x}&Y={y}&L={z}&tk=你的token");
RasterSource vecRasterSource = new RasterSource("TDT_CIA_SOURCEID", vecTileSet, 128);
style.addSource(vecRasterSource);
RasterLayer vecRasterLayer = new RasterLayer("TDT_CIA_LAYERID", "TDT_CIA_SOURCEID");
style.addLayer(vecRasterLayer);
}
});
类推下TileSet中的URI地址可以更换为可访问的asset://和file://协议头,经验证确实是可以的,但这两种协议头只能加载离散的瓦片(栅格或矢量)文件。
二、我们做了什么
经过上节源码剖析的过程,便有了一个大胆设想:可以在不改变现有Mapbox接口的情况下新增一个mbtiles://协议头,将请求mbtiles中瓦片的代码直接植入上节提到的资源请求代码块中,最后妥妥的成功了。
if (AssetFileSource::acceptsURL(resource.url)) { //Asset request
tasks[req] = assetFileSource->request(resource, callback);
} else if (LocalFileSource::acceptsURL(resource.url)) { //Local file request
tasks[req] = localFileSource->request(resource, callback);
} else if(LocalFileSource::acceptsMbtilesURL(resource.url)){ // Local mbtiles request--对应传入扩展的mbtiles://协议头requestMbtilesTile(resource, callback);
}else if(LocalFileSource::acceptsEsriBundle2URL(resource.url)){ // TODO:Local esri arcgis10.2 bundle request // 后续增加对arcgis10.2以下版本bundle紧凑栅格瓦片的支持 //requestEsriBundle2Tile(resource, callback);
}else if(LocalFileSource::acceptsEsriBundle3URL(resource.url)){ // TODO:Local esri arcgis10.3 bundle request // 后续增加对arcgis10.3及以上版本bundle紧凑栅格瓦片的支持 //requestEsriBundle3Tile(resource, callback);
} else {
。。。。。。
}
咱再来看看开发过程中调用栅格瓦片的代码:
mapboxMap.setStyle(styleUri, new Style.OnStyleLoaded() { // 加载本地指定的mbtiles文件(下载的谷歌影像瓦片)
事先下载谷歌影像瓦片并存储为mbtiles格式(没有下载工具也不用担心,开放提供的测试包中提供了一份mbtiles包含了1-10级的的影像瓦片),然后在TileSet中采用mbtiles://协议头加上Android本地mbtiles文件的全路径,成功加载显示,不仅感叹与源码的完美融合。
我们是开放的
开放下载重新编译的Mapbox Android SDK(基于8.2.0分支源码);
开放下载SDK的测试代码;
开放下载一份mbtiles测试数据。
扫码联系获取以上开放资源
备注单位+姓名



关于ChinaGISer
从明天起,做一个幸福的人,喂马、劈柴,周游世界。
从明天起,关心粮食和蔬菜,我有一所房子,面朝大海,春暖花开。
海子所描述的美好愿景,可能是当前我们每个年轻人所期盼的生活吧。
我们就从愿景说起,我们尝试着能否聚拢一群有情怀的GISers,摒弃拿来主义的习性,主张自主的创新、创造和贡献、共享,在开源GIS领域展现中国GISER的力量。
