QML中的地图(以OSM为例)在使用过程中会发现当地图层数很多时,特别是如果使用离线地图且地图层级较多时地图会变得很卡(在线地图加载的层级数多且不清除缓存时也会卡),原因在于QML地图插件对地图瓦片的加载插件采用了从单一瓦片目录文件夹中筛选瓦片文件的方式。具体流程为:当程序需要展示地图时,插件根据当前Map的缩放比例和显示范围计算当前需要加载的瓦片名,然后插件在存放地图的目录中筛选同名的瓦片进行显示,每显示一个瓦片就需要对整个目录中文件筛选一次,而需要显示的地图层数高,相应的显示范围内要加载的瓦片数量也就多,不卡才怪了。
解决办法是对地图瓦片目录进行分层,按照存放路径作为索引,即每个地图层的瓦片存放入一个文件夹中,插件根据地图类型和缩放比例进行查找,这样能大大的提高效率。该示例中只是按照地图层数分文件夹,如果更追求效率可按照我的方法在每一个层内再经行细分。示例中的mapType为自定义的地图类型(Map在Plugin中设置加载地图类型https://blog.csdn.net/zjgo007/article/details/122715773),如不需要在插件中设置地图类型,删除示例中的mapType相关部分即可。地图瓦片层次图:
修改插件源码方法如下(以5.11.2为例):
1、使用QtCreator打开OSM地图插件工程源码位置:F:\Qt\5.11.2\Src\qtlocation\src\plugins\geoservices\osm
2、在"qgeofiletilecacheosm.h"添加变量bool m_offlineMapTileDir,在构造函数中添加参数mapTileDir并对m_offlineMapTileDir初始化,该变量标识是否根据目录作为索引查找瓦片,代码:
qgeofiletilecacheosm.h:
/*添加地图类型标识,只需要分目录时可删除相关部分*/
int m_offlineMapType;
/*添加地图瓦片分目录查找*/
bool m_offlineMapTileDir;
QGeoFileTileCacheOsm(const QVector<QGeoTileProviderOsm *> &providers,
const QString &offlineDirectory = QString(),
const QString &directory = QString(),
const int &mapType = 0,
const bool &mapTileDir = false,
QObject *parent = 0);
qgeofiletilecacheosm.cpp
//对m_offlineMapTileDir初始化
QGeoFileTileCacheOsm::QGeoFileTileCacheOsm(const QVector<QGeoTileProviderOsm *> &providers,
const QString &offlineDirectory,
const QString &directory,
const int &mapType,
const bool &mapTileDir,
QObject *parent)
: QGeoFileTileCache(directory, parent), m_offlineDirectory(offlineDirectory), m_offlineData(false),m_offlineMapType(mapType),m_offlineMapTileDir(mapTileDir), m_providers(providers)
3、在"qgeotiledmappingmanagerengineosm.cpp"文件中添加自定义参数设置,可以搜索已有的参数属性例如:"osm.mapping.offline.directory"便于定位到文件中位置,其后添加自定义参数代码:
/*在插件中定义地图类型,只需要分目录时可删除相关部分*/
int m_offlineMapType=0;
if(parameters.contains(QStringLiteral("osm.mapping.offline.maptype")))
m_offlineMapType = parameters.value(QStringLiteral("osm.mapping.offline.maptype")).toInt();
/*在插件中定义地图是否启动分目录查找*/
bool m_offlineMapTileDir=false;
if(parameters.contains(QStringLiteral("osm.mapping.offline.maptiledir")))
m_offlineMapTileDir = parameters.value(QStringLiteral("osm.mapping.offline.maptiledir")).toBool();
QGeoFileTileCacheOsm *tileCache = new QGeoFileTileCacheOsm(m_providers, m_offlineDirectory, m_cacheDirectory,m_offlineMapType,m_offlineMapTileDir);
/*此为原地图瓦片初始化构造函数*/
// QGeoFileTileCacheOsm *tileCache = new QGeoFileTileCacheOsm(m_providers, m_offlineDirectory, m_cacheDirectory,m_offlineMapType);
4、在"qgeofiletilecacheosm.cpp"中跳转到函数QSharedPointer<QGeoTileTexture> getFromOfflineStorage(const QGeoTileSpec &spec);
该函数为筛选瓦片图片并加载到UI中,函数修改代码:
/*原生成瓦片名和查找瓦片位置代码*/
// const QString fileName = tileSpecToFilename(spec, QStringLiteral("*"), providerId);
// QStringList validTiles m_offlineDirectory.entryList({fileName});
/*修改后的代码,当在插件中设置了地图瓦片根据分目录查找,如果没有设置则按照原地图整个目录进行查找*/
const QString fileName = tileSpecToFilename(spec, QStringLiteral("*"), providerId);
QStringList validTiles;
QDir c_offlineMapTileDir = m_offlineDirectory;
if(m_offlineMapTileDir){
QStringList pathList = fileName.split("-");
QString mapType = pathList.at(2);
QString zoom = pathList.at(3);
if(!c_offlineMapTileDir.cd(mapType))//如果没有该地图类型则返回,不继续筛选
return QSharedPointer<QGeoTileTexture>();
if(!c_offlineMapTileDir.cd(zoom))//如果没有该地图比例瓦片目录则返回,不继续筛选
return QSharedPointer<QGeoTileTexture>();
validTiles = c_offlineMapTileDir.entryList({fileName});
}else{
validTiles = c_offlineMapTileDir.entryList({fileName});
}
//以上是新增
if (!validTiles.size())
return QSharedPointer<QGeoTileTexture>();
QFile file(c_offlineMapTileDir.absoluteFilePath(validTiles.first()));//此处m_offlineDirectory改为c_offlineMapTileDir
5、重新编译该osm工程,编译好的插件位于Qt安装盘符根目录下的plugins\geoservices路径下,将生成的插件文件复制到Qt安装目录中的插件目录(F:\Qt\5.11.2\msvc2015\plugins\geoservices\)替换原插件即可。
6、在QML的插件Plugin中传递自定义参数"osm.mapping.offline.maptiledir",其中该参数的值为布尔型,当需要目录做索引时传入true,如果不设置该参数或者传入false,QML地图采用默认地图瓦片查询方法,代码:
Plugin {
id: mapPlugin
name: "osm" // "mapboxgl", "esri", ...
PluginParameter{
name:"osm.mapping.offline.directory"
value:Analyze.getAppPath()+"/Map"
}
PluginParameter{
name:"osm.mapping.offline.maptype"
value:5
}
PluginParameter{
name:"osm.mapping.offline.maptiledir"
value:true
}
}
7、实测通过该修改插件的方法后,QML地图非常顺滑,该示例使用Qt版本为5.11.2,其他版本可根据上述方法自行编译!