由于公司项目需要,使用了QGC中的地图部分代码,该部分是一个以sqlite为核心的瓦片存储和管理的库,其中对瓦片有三层缓存机制,分别是磁盘缓存,数据库缓存和内存缓存。现将使用过程中遇到的问题记录如下:
项目中需要使用多个地图,针对多个项目展示不同的地图,而qgc本身的机制其实只维护了一个地图。 解决方案:在创建qgc地图的接口中创建多个地图,针对每个地图创建新的缓存管理(此处需要修改缓存创建的机制)。但是这样做只解决了sqlite缓存的问题,仍有两个问题没有解决,即磁盘和内存缓存。内存缓存只在地图变化过程中使用,且根据其机制不会产生太大影响,可以暂时不管,但磁盘缓存会导致每张地图新建和初始化时加载一样的数据。由于未找到关闭磁盘缓存的办法,目前采用的方法是将磁盘缓存最大允许使用空间设置为0,从而达到不使用的目的,该方法在某台测试机器中将导致计算异常而崩溃,其他机器表现正常。
qgc目前只提供了网络地图源,甲方要求使用离线地图源。 解决方案:QGeoMapReplyQGC中定义了瓦片加载的顺序和机制:优先从缓存中读取,若读取失败,则从网络中获取,此时只需在失败后增加从本地获取的逻辑即可,如:
void QGeoTiledMapReplyQGC::cacheError(QGCMapTask::TaskType type, QString errorString)
{
/qDebug() << "-----QGeoTiledMapReplyQGC::cacheError errStr: " << errorString; if(!getQGCMapEngine()->isInternetActive()) { if ((UrlFactory::MapType)tileSpec().mapId() == UrlFactory::MapType::AirmapElevation) { emit terrainDone(QByteArray(), QNetworkReply::NetworkSessionFailedError); } else { setError(QGeoTiledMapReply::CommunicationError, "Network not available"); setFinished(true); } } else { if(type != QGCMapTask::taskFetchTile) { qWarning() << "QGeoTiledMapReplyQGC::cacheError() for wrong task"; }/
if (QGeoLocInterface::instance()->isLocal())
{
//tile not in cache. Get it from the local disk.
HqTileFeatchMgr *locMgr = HqTileFeatchMgr::instance();
_localReply = locMgr->newFeatch(_tileSpec);
connect(_localReply, &QGeoTileFeatchHQ::finished, this, &QGeoTiledMapReplyQGC::localReplyFinished);
connect(_localReply, &QGeoTileFeatchHQ::error, this, &QGeoTiledMapReplyQGC::localReplyError);
}
else
{
//-- Tile not in cache. Get it off the Internet.
loadFromInternet();
}
//- Wait for an answer up to 10 seconds
connect(&_timer, &QTimer::timeout, this, &QGeoTiledMapReplyQGC::timeout);
_timer.setSingleShot(true);
_timer.start(10000);
_requestCount++;
}
需要将自定义瓦片加载显示到地图中。 解决方案:该需求其实与离线地图的需求一致,按照墨卡托投影切分瓦片并创建对应的文件夹层级关系,后续步骤就与离线地图一致了,只需优先加载自定义瓦片即可。
qgc地图在加载和显示过程中特别慢,哪怕是离线地图加载速度也很慢。解决方案:该问题经调试发现其罪魁祸首是qgc使用sqlite作为数据库的机制导致的,因地图需要自动刷新,故每张生成的瓦片都需要更新每一级缓存,在sqlite的查询和更新中需要大量时间,从而导致显示延后。要解决该问题,可以从几个角度入手,一是限制sqlite数据库的大小,使其不至于缓存太多数据而导致查询时低效能,二是减少sqlite的存储,对于所有瓦片都需要从sqlite存和查(取)这种处理,减少其频次,三是直接取消sqlite的存取,直接从网络或者本地加载,可根据实际情况调整,在我的项目中,由于是本地图元,故直接取消了该缓存,且实际测试来看,从网络直接获取展示,速度也并不慢(但该方案会导致缩放不平滑)。参照如下:
void
QGeoTiledMapReplyQGC::cacheError(QGCMapTask::TaskType type, QString errorString)
{
/qDebug() << "-----QGeoTiledMapReplyQGC::cacheError errStr: " << errorString; if(!getQGCMapEngine()->isInternetActive()) { if ((UrlFactory::MapType)tileSpec().mapId() == UrlFactory::MapType::AirmapElevation) { emit terrainDone(QByteArray(), QNetworkReply::NetworkSessionFailedError); } else { setError(QGeoTiledMapReply::CommunicationError, "Network not available"); setFinished(true); } } else { if(type != QGCMapTask::taskFetchTile) { qWarning() << "QGeoTiledMapReplyQGC::cacheError() for wrong task"; }/
if (QGeoLocInterface::instance()->isLocal())
{
//tile not in cache. Get it from the local disk.
HqTileFeatchMgr *locMgr = HqTileFeatchMgr::instance();
_localReply = locMgr->newFeatch(_tileSpec);
connect(_localReply, &QGeoTileFeatchHQ::finished, this, &QGeoTiledMapReplyQGC::localReplyFinished);
connect(_localReply, &QGeoTileFeatchHQ::error, this, &QGeoTiledMapReplyQGC::localReplyError);
}
else
{
//-- Tile not in cache. Get it off the Internet.
loadFromInternet();
}
//- Wait for an answer up to 10 seconds
connect(&_timer, &QTimer::timeout, this, &QGeoTiledMapReplyQGC::timeout);
_timer.setSingleShot(true);
_timer.start(10000);
_requestCount++;
}
QGeoTiledMapReplyQGC::QGeoTiledMapReplyQGC(QNetworkAccessManager *networkManager, const QNetworkRequest &request, const QGeoTileSpec &spec, QObject *parent)
: QGeoTiledMapReply(spec, parent)
, _netReply(NULL)
, _request(request)
, _networkManager(networkManager)
, _tileSpec(spec)
{
if(_request.url().isEmpty()) {
if(!_badMapbox.size()) {
QFile b(":/res/notile.png");
if(b.open(QFile::ReadOnly))
_badMapbox = b.readAll();
}
setMapImageData(_badMapbox);
setMapImageFormat("png");
setFinished(true);
setCached(false);
}
else {
// remove sqlite cache
// QGCFetchTileTask* task = getQGCMapEngine()->createFetchTileTask((UrlFactory::MapType)spec.mapId(), spec.x(), spec.y(), spec.zoom());
// connect(task, &QGCFetchTileTask::tileFetched, this, &QGeoTiledMapReplyQGC::cacheReply);
// connect(task, &QGCMapTask::error, this, &QGeoTiledMapReplyQGC::cacheError);
// getQGCMapEngine()->addTask(task);
cacheError(QGCMapTask::taskFetchTile, "");
}
}