QT地图QtLocation

前言

相信大家在使用QT开发中难免会需要用到地图开发,目前主流是使用QWebEngineView调用浏览器,使用浏览器加载第三方api,如openlayers 和谷歌地图API等。但是这种优点是api支持度高非常简单,容易上手。但是缺点是没法缓冲地图资源,必须依赖地图服务器。
还有一种小众的解决方法是使用Qt自带的QtLocation(如果你的程序不准备使用qml为主的话,你基本就不需要往下看了),这篇文章就是介绍QtLocation的使用方法的。

openlayers 使用方法 :https://blog.youkuaiyun.com/luck_anan/article/details/129662854

需求

1.支持离线地图、在线天地图和本地缓冲地图
2.可以导入导出缓冲的在线资源

流程图

我认为讲半天废话,不放流程图的都是耍流氓
在这里插入图片描述

程序实现

插件编写

缓冲实现

1.这里实现了缓冲入库

class stMapTileData : public QObject
{
    Q_OBJECT
public:
    //瓦片数据
    quint32    hash;    //瓦片图块位置Hash
    QString    format;  //图片格式
    QByteArray byte;    //瓦片图片
    int        x;
    int        y;
    int        zoom;    //地图层级
    quint32    mapID;   //地图ID
};

void CMapGeoFileTileCache::addToSqlite(const QGeoTileSpec &spec, const QString &format, const QByteArray &bytes)
{
    QSharedPointer<stMapTileData> tile = QSharedPointer<stMapTileData>(new stMapTileData);
    tile->hash   = CMapLoadSetting::getTileHash(spec.mapId(), spec.x(), spec.y(), spec.zoom());
    tile->format = format;//图片格式
    tile->byte   = bytes;   //瓦片图片
    tile->x      = spec.x();//x
    tile->y      = spec.y();//y
    tile->zoom   = spec.zoom();//地图层级
    tile->mapID  = spec.mapId();//地图ID

    //不阻塞,这里主要是写入速度太慢
    QMetaObject::invokeMethod(CMapEngineMgr::Instance()->getSqlThread().data(), "slot_writeSql",
                              Qt::QueuedConnection, Q_ARG(QSharedPointer<stMapTileData>, tile));
}
void CSqlDataWorker::slot_writeSql()
{
    if (m_pQuery && !m_queue.isEmpty()){
        auto tile = m_queue.dequeue();
        //如果不存在就插入,存在就更新的方式;
        m_pQuery->prepare(
                "INSERT INTO Tiles(hash, format, tile, size, x, y, zoom, mapID, dateTime) VALUES(?, "
                "?, ?, ?, ?, ?, ?, ?, ?)");
        m_pQuery->addBindValue(tile->hash);
        m_pQuery->addBindValue(tile->format);
        m_pQuery->addBindValue(tile->byte);
        m_pQuery->addBindValue(tile->byte.size());
        m_pQuery->addBindValue(tile->x);
        m_pQuery->addBindValue(tile->y);
        m_pQuery->addBindValue(tile->zoom);
        m_pQuery->addBindValue(tile->mapID);
        m_pQuery->addBindValue(QDateTime::currentDateTime().toMSecsSinceEpoch());
        if (m_pQuery->exec()) {
            //新的数据已存入
        } else {
            // Map数据已存在于数据库
        }
        m_pQuery->finish();
    }
}

2.这里实现是缓冲读取


QSharedPointer<QGeoTileTexture> CMapGeoFileTileCache::getFromSqlite(const QGeoTileSpec &spec)
{
    QSharedPointer<stMapTileData> tile(new stMapTileData) ;//创建一个瓦片
    //获取Hash
    tile->hash = CMapLoadSetting::getTileHash(spec.mapId(), spec.x(), spec.y(), spec.zoom());

    auto start = clock();
    //从数据库读取瓦片数据
    //阻塞等待返回,因为读取数据非常快,阻塞没有影响
    QMetaObject::invokeMethod(CMapEngineMgr::Instance()->getSqlThread().data(), "slot_readTile",
                              Qt::BlockingQueuedConnection, Q_ARG(QSharedPointer<stMapTileData>, tile));
    qDebug() << "read time:" << clock() - start << "ms";
    if (tile->byte.isEmpty())
        return QSharedPointer<QGeoTileTexture>();

    QImage image;
    //一些来自服务器的瓷砖可能是有效的图像,但瓷砖获取器
    //也许可以将它们识别为不应该显示的方块。
    //如果是这种情况,瓷砖获取器应该在文件中写入“NoRetry”。
    if (isTileBogus(tile->byte)) {
        QSharedPointer<QGeoTileTexture> tt(new QGeoTileTexture);
        tt->spec  = spec;
        tt->image = image;
        return tt;
    }

    // 这是一个真正无效的图像。获取器应该再试一次。
    if (!image.loadFromData(tile->byte/*,tile->format.toLatin1()*/)) {
        handleError(spec, QStringLiteral("瓦片图像有问题"));
        return QSharedPointer<QGeoTileTexture>(0);
    }

    // 在这里转换它,而不是在每个QSGTexture::bind()中
    if (image.format() != QImage::Format_RGB32 && image.format() != QImage::Format_ARGB32_Premultiplied)
        image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);

    addToMemoryCache(spec, tile->byte, tile->format);//添加到内存缓冲
    QSharedPointer<QGeoTileTexture> tt = addToTextureCache(spec, image);//添加瓦片到缓冲
    if (tt) {
        return tt;
    }
    return QSharedPointer<QGeoTileTexture>();
}

多地图加载

CMapUrlEngineMgr::CMapUrlEngineMgr(QObject *parent)
    : QObject{parent}
{
    CMapProviderBase * pMapBase =  new CMapProviderTianDi(QGeoMapType::SatelliteMapDay,this); //天地图卫星图
    m_hashProvides["Tianditu Satellite"] =pMapBase;
    pMapBase->setLicense( CMapLoadSetting::Instance()->m_mapMapData["Tianditu Satellite"].strLicense);
    pMapBase->setFormat( CMapLoadSetting::Instance()->m_mapMapData["Tianditu Satellite"].strFormat);
    //m_hashProvides["Tianditu Street"] = new TiTiandituCiaMapProvider(this); //天地图路网标注信息图

    pMapBase = new CMapProviderOffLine(QGeoMapType::SatelliteMapDay,this); //离线卫星图
    m_hashProvides["OffLine Satellite"] = pMapBase;
    pMapBase->setLicense( CMapLoadSetting::Instance()->m_mapMapData["OffLine Satellite"].strLicense);
    pMapBase->setFormat( CMapLoadSetting::Instance()->m_mapMapData["OffLine Satellite"].strFormat);
}


CMapGeoTiledMappingManagerEngine::CMapGeoTiledMappingManagerEngine(const QVariantMap &parameters, QGeoServiceProvider::Error *error, QString *errorString)
        :QGeoTiledMappingManagerEngine()
{
    //设置图层
    QGeoCameraCapabilities cameraCaps;
    cameraCaps.setMinimumZoomLevel(2.0);//最大缩放
    cameraCaps.setMaximumZoomLevel(18.0);//最小缩放 地图层级
    cameraCaps.setSupportsBearing(true);
    cameraCaps.setSupportsTilting(true);
    //    cameraCaps.setMinimumTilt(0);
    //    cameraCaps.setMaximumTilt(80);
    //    cameraCaps.setMinimumFieldOfView(20.0);
    //    cameraCaps.setMaximumFieldOfView(120.0);
    //    cameraCaps.setOverzoomEnabled(true);
    setCameraCapabilities(cameraCaps);

    //宏注册 QByteArray("TefeiMap") 由Json导入
    #define QGCGEOMAPTYPE(a,b,c,d,e,f)  QGeoMapType(a,b,c,d,e,f,QByteArray("demoMap"), cameraCaps)

    //获取所有的 地图 路径样式 包括 离线和天地图 都从这里获取
    QList<QGeoMapType> mapList;
    //地图瓦片引擎
    auto hashProviders = CMapEngineMgr::Instance()->getUrlEngine()->getProviderTable();

    for (auto cIt = hashProviders.cbegin(); cIt != hashProviders.cend(); ++cIt) {
        mapList.append(QGCGEOMAPTYPE(cIt.value()->getMapStyle(), cIt.key(), cIt.key(), false, false, CMapEngineMgr::Instance()->getUrlEngine()->getIdFromType(cIt.key())));
    }
    setSupportedMapTypes(mapList);//设置支持的映射类型

    //没有用QGeoFileTileCache,默认加载地缓存不会释放,还没找到原因,估计要分析它的源码
    //缓冲路径
    QString  strCacheDirectory = CMapLoadSetting::Instance()->m_strCacheSqlDir;
    //设置地图缓冲,如果不需要缓存,可设置setMaxDiskUsage 大小,限制到小内存,,
    //无法 注释setTileCache(); 注释后会默认缓冲图片
    //    QGeoFileTileCache* tileCache = new QGeoFileTileCache(cacheDirectory, this);
    auto tileCache = new CMapGeoFileTileCache(strCacheDirectory, this);
    //auto tileCache = new QGeoFileTileCache(strCacheDirectory, this);
    tileCache->setMaxDiskUsage(1024 * 1024);
    tileCache->setMaxMemoryUsage(1024 * 1024 * 100);//100m内存
    setTileCache(tileCache);

    //创建地图瓦片获取器
    //从网络中加载瓦片
    auto tileFetcher = new CMapGeoTileFetcher(parameters, this);
    setTileFetcher(tileFetcher);

    //结束
    m_prefetchStyle = QGeoTiledMap::PrefetchNeighbourLayer; //QGeoTiledMap::NoPrefetching;//没有预先存取 //可设置缓冲一些瓦片
    *error = QGeoServiceProvider::NoError;
    errorString->clear();

}

程序加载

int main(int argc, char *argv[])
{
	//导入地图插件
    Q_IMPORT_PLUGIN(CMapGeoServiceProviderFactory);
}
Map{
    id:             map
    width:          parent.width
    height:         parent.height
    anchors.bottom: parent.bottom
    anchors.horizontalCenter: parent.horizontalCenter

    zoomLevel:      MapEngineMgr.getMapZoom().rawValue    //缩放等级
    center:         MapEngineMgr.getMapCenter()    //中心点

    //gesture.acceptedGestures: MapGestureArea.PinchGesture //支持的手势

    plugin:         Plugin { name: "demoMap" }//插件名称 对应 MapGeoTiledMappingManagerEngine cpp 第26行 和Json "Provider": "TefeiMap",
    visible:        true
    function updateActiveMapType() {
        for (var i = 0; i < map.supportedMapTypes.length; i++) {
            console.log("地图供应商",map.supportedMapTypes[i].name)
            //在这里可以切换地图
            if (fullMapName === map.supportedMapTypes[i].name) {
                map.activeMapType = map.supportedMapTypes[i]
                return
            }

        }
    }
    Component.onCompleted: {//加载完毕后 更新一次界面
        updateActiveMapType()
    }
}

地图瓦片选择

瓦片选择为 原始瓦片 + 墨卡托投影
在这里插入图片描述

最终实现

在这里插入图片描述

下载地址

测试程序下载地址

测试程序下载地址:

源码下载地址

优快云:
GitHub:
GitLab:

缺陷

必须使用QML,而且API支持较少,文档较少,比不得openlayers这些成熟的前端地图框架。

最后

本人才疏学浅,还有很多不足,欢迎各位指出,也请收下留情,勿喷。

参考

1.qgroundcontrol地面站:http://qgroundcontrol.com/
2.龚建波 QML QtLocation地图应用学习-5:https://blog.youkuaiyun.com/gongjianbo1992/article/details/103655126

转载请注明出处谢谢。

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值