Qt Location模块中的plugin默认只有 "esri"、"mapbox"、"osm"、"here",一般我们需要换成自己的数据源,github 上有人做了谷歌地图和其他地图的 plugin 。而我目前的需求是加载本地瓦片地图,所以准备实现一个自己的 plugin 。
我们可以通过查看 Qt Location 相关文档,了解自定义 plugin 的步骤:https://doc.qt.io/qt-5/qtlocation-geoservices.html
文档上提到了两点,给插件写一个json描述文件;继承 QGeoServiceProviderFactory 并实现其部分接口。
对于 json 文件的 Features 字段的内容,可以参考:https://doc.qt.io/qt-5/qgeoserviceprovider.html。比如我目前做这个只用到了瓦片加载,那么可以这样写(写的Online实际上是自己处理的网路请求加载本地数据):
{
"Keys": ["mymap"],
"Provider": "mymap",
"Version": 100,
"Experimental": false,
"Features": [
"OnlineMappingFeature"
]
}
对于接口实现部分,插件实现者需要继承QGeoServiceProviderFactory以及要为其提供实现的许多ManagerEngine类的子类(机器翻译),如果插件不提供对应引擎,则相关函数应返回0(nullptr)。如果要制作自己的 plugin 加载瓦片,需要重写createMappingManagerEngine处理相关请求来实现映射。
当然,只是看文档还不足以自己做一个插件,需要参考已有的实现,如:
Qt源码的实现: qt-everywhere-src-5.12.4\qtlocation\src\plugins\geoservices\osm\osm.pro
GitHub:https://github.com/vladest/googlemaps
GitHub:https://github.com/wangpengcheng/OfflineMapTest
目前的实现我只做了瓦片的请求,也就是继承 QGeoServiceProviderFactory 类后实现其 createMappingManagerEngine 接口。具体的请求由 QGeoTileFetcher 的子类来完成,我们继承他后实现 getTileImage 接口。
对于一些请求参数,我是通过 QGeoServiceProviderFactory 虚函数接口中的 parameters 参数来捕获的,我们可以在QML中通过给 Plugin 添加 PluginParameter 来传递参数:
plugin: Plugin {
name: "mymap" //"esri" "mapbox" "osm" "here"
PluginParameter {
name: "baseUrl"
value: applicationDirPath+"/dianzi_gaode_ArcgisServerTiles/_alllayers"
}
PluginParameter {
name: "format"
value: "png"
}
}
目前也有一些没解决的问题,如请求的 url 不对的话程序会很卡;访问某个瓦片后Qt会自己生成缓存,目前还不知道怎么设置路径以及取消缓存(AppData\Local\cache目录)。
(2020-1-18)后来发现之前自定义的模块加载了 cache 但是不会释放,于是 Map 切换多次的话内存会占用很多,参考源码之后借用 QGeoFileTileCache 类来管理瓦片缓存,愉快的解决了 cache 没释放的问题。
实现效果如下图,源码中带有部分瓦片地图,可以直接运行。要注意的是不同的 Qt 版本可能 Location 模块有些接口不同,我使用的 Qt5.12 来制作,一些旧版本请自行查看 Qt 源码来修改。
代码中有个 MyQtLocation.pro 用于运行示例,示例是静态加载的 plugin 。Plugin文件夹下有个单独的 pro文件用于生成 lib dll,生成动态库之后放到 Qt 安装目录的插件目录下(如 E:\Qt\Qt5.12.6\5.12.6\msvc2017\plugins\geoservices ),就可以在别的项目中使用这个 dll 了。
至此,一个简单的瓦片地图插件就完成了,如果要自己做一定要看文档和源码。
代码 github 链接:https://github.com/gongjianbo/MyQtLocation