Qt Quick - GIS地图绘制

一、Location模块

        Qt Location模块可用于轻量级的地图应用开发

        在QML中使用Qt Location模块,还需要引入Qt Positioning模块,因为Qt Location依赖它:

                import QtPositioning 5.12

                import QtLocation 5.12

二、瓦片地图源

高德地图源:

https://wprd01.is.autonavi.com/appmaptile?&style=6&lang=zh_cn&scl=1<ype=0&x={x}&y={y}&z={z}

https://wprd01.is.autonavi.com/appmaptile?&style=7&lang=zh_cn&scl=1<ype=0&x={x}&y={y}&z={z}

https://wprd01.is.autonavi.com/appmaptile?&style=8&lang=zh_cn&scl=1<ype=0&x={x}&y={y}&z={z}

https://wprd01.is.autonavi.com/appmaptile?&style=9&lang=zh_cn&scl=1<ype=0&x={x}&y={y}&z={z}

Bing地图源:

http://dynamic.t0.tiles.ditu.live.com/comp/ch/a{q}.jpg?it=G,OS,L&mkt=zh-cn&cstl=w4c&ur=cn

http://dynamic.t0.tiles.ditu.live.com/comp/ch/r{q}.jpg?it=G,OS,L&mkt=zh-cn&cstl=w4c&ur=cn

http://dynamic.t0.tiles.ditu.live.com/comp/ch/h{q}.jpg?it=G,OS,L&mkt=zh-cn&cstl=w4c&ur=cn

http://dynamic.t0.tiles.ditu.live.com/comp/ch/h{q}.jpg?it=G,OS,L&mkt=zh-cn&cstl=vbd&ur=cn

https://r0.tiles.ditu.live.com/tiles/a{q}.jpg?g=1001&mkt=zh-cn

三、Map类

        Map 类型用于显示地球的地图或图像,还能够显示与地图表面关联的交互式对象;

        Map 类型包括对捏合和轻拂手势的支持,以控制缩放和平移,尤其适合触摸屏;

        Map使用OpenGL(ES)和Qt Scene Graph堆栈来渲染地图,因此在可以使用GL加速硬件的情况下,其表现非常出色。

1、基础示例

        要想展示一个地图,需要使用Map组件,并为其设置Plugin,Plugin指定了相关的位置服务插件,有esri、mapbox、osm等(默认只有OSM服务),osm 要把openssl动态库放到运行目录。

import QtQuick 2.9
import QtQuick.Window 2.3
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.3
import QtPositioning 5.12
import QtLocation 5.12

Window  {
    id: rootWindow

    flags: Qt.Window | Qt.FramelessWindowHint
    visible: true

    title: qsTr("NavGuard")

    minimumWidth: 800; minimumHeight: 480
    width: minimumWidth; height: minimumHeight;

    Plugin {
        id: mapPlugin
        name: "osm"

    }

    Map {
        anchors.fill: parent

        plugin: mapPlugin

        // 初始中心点
        center: QtPositioning.coordinate(30.67, 104.07)
        // 初始缩放等级
        zoomLevel: 12
        // 最小缩放等级
        minimumZoomLevel: 1
        // 最大缩放等级
        maximumZoomLevel: 14
        // 背景色,没有正常加载时显色的图块颜色
        color: "green"
        // 地图方位0-360
        bearing: 0
        // 垂直视角0-60
        tilt: 30
    }
}

2、添加图元

        QtLocation 模块中定义了很多小部件的类型,可以在 Map 中绘制点线等图元;

        MapQuickItem 可以在地图上显示任意 Qt Quick 对象,通过 sourceItem 属性指定 Qt Quick 对象,coordinate 标定坐标点,anchorPoint 表示sourceItem左上角相对于coordinate的偏移((0,0)的话左上角是在coordinate的位置),zoomLevel 表示缩放等级,0则不会缩放,否则是和对应的地图缩放等级一起放大缩小的。

import QtQuick 2.9
import QtQuick.Window 2.3
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.3
import QtPositioning 5.12
import QtLocation 5.12

Window  {
    id: rootWindow

    flags: Qt.Window | Qt.FramelessWindowHint
    visible: true

    title: qsTr("NavGuard")

    minimumWidth: 800; minimumHeight: 480
    width: minimumWidth; height: minimumHeight;

    Plugin {
        id: mapPlugin
        name: "osm"

        PluginParameter {
            name: "osm.mapping.custom.host"
            value: "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
        }
    }

    Map {
        id: item_map
        anchors.fill: parent

        plugin: mapPlugin

        // 初始中心点
        center: QtPositioning.coordinate(30.67, 104.07)
        // 初始缩放等级
        zoomLevel: 12
        // 最小缩放等级
        minimumZoomLevel: 1
        // 最大缩放等级
        maximumZoomLevel: 14
        // 背景色,没有正常加载时显色的图块颜色
//        color: "green"
        // 地图方位0-360
        bearing: 0
        // 垂直视角0-60
        tilt: 30

        // 在地图上显示任意Qt Quick对象
        MapQuickItem {
            id: point_1
            // 缩放等级默认0固定大小,否则会和缩放等级一起放大缩小
            zoomLevel: 0
            // 指示的坐标点
            coordinate: QtPositioning.coordinate(30.67, 104.07)
            // sourceItem左上角相对于coordinate的偏移
            anchorPoint: Qt.point(sourceItem.width/2,sourceItem.height/2)
            // Qt Quick对象
            sourceItem: Rectangle{
                width: 14
                height: 14
                radius: 7
                color: "green"
                border.color: "red"
                border.width: 1
                MouseArea{
                    anchors.fill: parent
                    onClicked: console.log("click")
                }
                // coordinate类型有经纬度高度三个属性
                // latitude 纬度
                // longitude 经度
                // altitude 海拔高度,单位米
                // 以及计算距离和方位角的方法
                // 这里用文本显示两个点的距离
                Text{
                    text: "   "+Math.round(point_1.coordinate.distanceTo(
                                               point_2.coordinate))/1000+" km"
                    color: "green"
                    font.bold: true
                    font.pixelSize: 16
                }
            }
        }

        MapQuickItem {
            id: point_2
            zoomLevel: 10 // 和缩放等级一起放大缩小
            coordinate: QtPositioning.coordinate(30.67, 104.04)
            anchorPoint: Qt.point(sourceItem.width/2,sourceItem.height/2)
            sourceItem: Rectangle{
                width: 14
                height: 14
                radius: 7
                color: "green"
                border.color: "red"
                border.width: 1
            }
        }

        // 此外,还有MapCircle,MapRectangle,MapPolygon等图元类型,和地图成比例的
        // 画多边形
        MapPolygon {
            id: point_3
            color: "blue"
            border.width: 1
            border.color: "red"
            // 根据坐标点绘制多边形
            path:[QtPositioning.coordinate(30.70, 104.08)]
            Component.onCompleted: {
                point_3.addCoordinate(QtPositioning.coordinate(30.72, 104.08))
                point_3.addCoordinate(QtPositioning.coordinate(30.72, 104.10))
                point_3.addCoordinate(QtPositioning.coordinate(30.69, 104.12))
            }
        }

        // 绘制折线
        MapPolyline {
            line.width: 2
            line.color: "green"
            // 路径列表 path : list<coordinate>
            path: [point_1.coordinate, point_2.coordinate, point_3.path[0]]
        }

        Rectangle {
            x: 10
            y: 10
            height: item_center.height+20
            width: item_center.width+20
            color: "green"
            Text {
                id: item_center
                x: 10
                y: 10
                color: "white"
                font.pixelSize: 16
                font.bold: true
                // 展示缩放等级 和 map的中心点经纬度
                text: "zoom level:"+Math.floor(item_map.zoomLevel)+
                      " center:"+item_map.center.latitude+
                      "  "+item_map.center.longitude
            }
        }
    }
}

3、自定义图元

        依靠canvas实现地图复杂图元自定义绘制,通过fromCoordinate()方法实现Map组件将地图的坐标转换为屏幕坐标(经纬度(地理坐标)转换为像素位置(屏幕坐标))。

        虚线绘制:

// MapDashLine.qml

import QtQuick 2.9
import QtPositioning 5.12

Item {
    id: mapDashLine
    anchors.fill: parent

    property var beginCoordinate: QtPositioning.coordinate()
    property var endCoordinate: QtPositioning.coordinate()
    property var lineDash: [4,4]
    property color lineColor: "crimson"
    property int lineWidth: 2
    property color textColor: "crimson"
    property int textPixelSize: 14

    readonly property var mapItem: mapDashLine.parent

    Canvas {
        id: myCanvas
        anchors.fill: parent
        onPaint: {
            if(!mapDashLine.beginCoordinate.isValid || !mapDashLine.endCoordinate.isValid)
                return
            var ctx = getContext("2d")
            ctx.clearRect(0, 0, myCanvas.width, myCanvas.height)
            ctx.strokeStyle = mapDashLine.lineColor
            ctx.lineWidth = mapDashLine.lineWidth
            ctx.setLineDash(mapDashLine.lineDash)
            // 绘制虚线
            ctx.beginPath()
            var beginPos = mapDashLine.mapItem.fromCoordinate(mapDashLine.beginCoordinate, false)
            ctx.moveTo(beginPos.x, beginPos.y)
            var endPos = mapDashLine.mapItem.fromCoordinate(mapDashLine.endCoordinate, false)
            ctx.lineTo(endPos.x, endPos.y)
            ctx.stroke()
            ctx.save()
            // 绘制文字
            var azimuth = endCoordinate.azimuthTo(beginCoordinate)
            if(azimuth>=180)
                azimuth = azimuth - 180
            var distance = endCoordinate.distanceTo(beginCoordinate)
            var text = (distance/1000).toFixed(0)+"Km"
            ctx.fillStyle = mapDashLine.textColor
            ctx.font = mapDashLine.textPixelSize+"px Arial"
            ctx.textAlign = "center"
            var centerX = (beginPos.x+endPos.x)/2
            var centerY = (beginPos.y+endPos.y)/2
            ctx.translate(centerX,centerY)
            ctx.rotate(azimuth*Math.PI/180-Math.PI/2)
            ctx.fillText(text,0,-mapDashLine.textPixelSize/2)
            ctx.restore()
        }
    }

    onBeginCoordinateChanged: {
        update()
    }
    onEndCoordinateChanged: {
        update()
    }
    onLineDashChanged: {
        update()
    }
    onLineColorChanged: {
        update()
    }
    onLineWidthChanged: {
        update()
    }
    onTextColorChanged: {
        update()
    }
    onTextPixelSizeChanged: {
        update()
    }

    Connections {
        target: mapDashLine.mapItem
        function onZoomLevelChanged() {
            update()
        }
        function onVisibleRegionChanged() {
            update()
        }
    }

    function update() {
        myCanvas.requestPaint()
    }
}
// 使用示例

MapDashLine {
	id: mapDashLine
	beginCoordinate: QtPositioning.coordinate(36.31806729, 120.44627839)
	endCoordinate: QtPositioning.coordinate(36.31806729, 121.44627839)
}

四、地图插件(Bing瓦片请求/无需Key)

1、插件构建(CMakeLists)

cmake_minimum_required(VERSION 3.5)

project(NavGuard)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)

set(CMAKE_AUTOMOC ON)

set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)

set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt5 REQUIRED COMPONENTS

    Core

    Widgets

    Quick

    Concurrent

    Network

    Positioning

    Location

)

add_definitions(-DQT_LOCATION_PRIVATE_HEADERS)

include_directories(${Qt5Location_PRIVATE_INCLUDE_DIRS})

add_definitions(-DNavGuardPlugin_Static)

if(NavGuardPlugin_Static)

    set(NAVGUARD_PLUGIN_PATH ${PROJECT_SOURCE_DIR}/src/navguardplugin)

    include_directories(${NAVGUARD_PLUGIN_PATH})

    add_subdirectory(${NAVGUARD_PLUGIN_PATH})

endif()

set(SRC_FILES

    ${PROJECT_SOURCE_DIR}/src/main.cpp

    ${PROJECT_SOURCE_DIR}/src/navguardcore/navguardcore.cpp

    ${PROJECT_SOURCE_DIR}/src/navguardplugin/navguardfactory.cpp

    ${PROJECT_SOURCE_DIR}/src/navguardplugin/navguardengine.cpp

    ${PROJECT_SOURCE_DIR}/src/navguardplugin/navguardfetcher.cpp

    ${PROJECT_SOURCE_DIR}/src/navguardplugin/navguardreply.cpp

)

qt5_add_resources(RESOURCE_FILES

    ${PROJECT_SOURCE_DIR}/src/appforms/appforms.qrc

)

add_executable(${PROJECT_NAME}

    ${SRC_FILES}

    ${RESOURCE_FILES}

)

target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE

    Qt5::Core

    Qt5::Widgets

    Qt5::Quick

    Qt5::Concurrent

    Qt5::Network

    Qt5::Positioning

    Qt5::Location

)

2、插件文件(json)

插件文件示例(navguardplugin.json):

{

    "Keys": ["navguard"],

    "Provider": "navguard",

    "Version": 100,

    "Experimental": false,

    "Features": [

        "OnlineMappingFeature"

    ]

}

其中"Features"表示该插件支持的哪些特性,比如"OnlineMappingFeature"表示支持在线地图显示、则我们需要实现一个QGeoMappingManagerEngine引擎;

每个引擎能支持的所有功能介绍如下所示:

QGeoRoutingManagerEngine

NoRoutingFeatures - Routing is not supported (KO: this option can be omitted)

OnlineRoutingFeature - Online routing support

OfflineRoutingFeature - Offline routing support

LocalizedRoutingFeature - Localized routing support

RouteUpdatesFeature - Support for dynamic path update

AlternativeRoutesFeature - Support for multiple alternative routes

ExcludeAreasRoutingFeature - Support for excluding routing factors

AnyRoutingFeatures - Supports anything your heart desires for routing

QGeoCodingManagerEngine

NoGeocodingFeatures - Geocoding is not supported

OnlineGeocodingFeature - Online geocoding support

OfflineGeocodingFeature - Offline geocoding support

ReverseGeocodingFeature - Reverse geocoding support

LocalizedGeocodingFeature - Localized geocoding support

AnyGeocodingFeatures - All of the above except NoGeocodingFeatures

QGeoMappingManagerEngine

NoMappingFeatures - No mapping supported

OnlineMappingFeature - Support for online maps

OfflineMappingFeature - Support for offline maps

LocalizedMappingFeature - Support for maps with localization

AnyMappingFeatures - All of the above except NoMappingFeatures

QPlaceManagerEngine

NoPlacesFeatures - Point-of-interest is not supported

OnlinePlacesFeature - Online Point-of-interest support

OfflinePlacesFeature - Offline Point-of-interest support

SavePlaceFeature - Support for saving custom points to the map

RemovePlaceFeature - Support for removing Point-of-interes on a map

SaveCategoryFeature - Create and save custom Point-of-interest categories

RemoveCategoryFeature - Remove Point-of-interest categories

PlaceRecommendationsFeature - Support for Recommended Point-of-interest according to keywords

SearchSuggestionsFeature - Suggestions support according to the search query part

LocalizedPlacesFeature - Localization support for Point-of-interes

NotificationsFeature - Support for Point-of-interes change notifications

PlaceMatchingFeature - Support for Point-of-interes comparison from two different providers

AnyPlacesFeatures - It's Time for You to Relax

3、插件入口(QGeoServiceProviderFactory)

实现一个QT地图服务插件,我们需要子类化QGeoServiceProviderFactory,以及实现 4个接口虚函数:

QGeoCodingManagerEngine* createGeocodingManagerEngine() : 位置搜索引擎,实现位置地理编码,比如配置文件有"OnlineGeocodingFeature"相关特性,则需要实现该虚函数
QGeoMappingManagerEngine* createMappingManagerEngine() : 地图映射管理器引擎,实现地图显示,比如配置文件有"OnlineMappingFeature"相关特性,则需要实现该虚函数
QGeoRoutingManagerEngine* createRoutingManagerEngine() : 路径规划引擎,实现路径规划,比如配置文件有"OnlineRoutingFeature"相关特性,则需要实现该虚函数
QPlaceManagerEngine* createPlaceManagerEngine() : 位置搜索引擎,实现位置搜索,比如配置文件有"OnlinePlacesFeature"相关特性,则需要实现该虚函数

navguardfactory.h

#pragma once

#include <QtCore/QObject>

#include <QtLocation/QGeoServiceProviderFactory>

QT_BEGIN_NAMESPACE

class NavGuardFactory : public QObjectpublic QGeoServiceProviderFactory {

    Q_OBJECT

    Q_INTERFACES(QGeoServiceProviderFactory)

    Q_PLUGIN_METADATA(IID "org.qt-project.qt.geoservice.serviceproviderfactory/5.0"

                      FILE "navguardplugin.json")

public:

    NavGuardFactory();

//    QGeoCodingManagerEngine *createGeocodingManagerEngine(const QVariantMap ¶meters,

//                                                        QGeoServiceProvider::Error *error,

//                                                        QString *errorString) const;

//    QGeoRoutingManagerEngine *createRoutingManagerEngine(const QVariantMap ¶meters,

//                                                        QGeoServiceProvider::Error *error,

//                                                        QString *errorString) const;

//    QPlaceManagerEngine *createPlaceManagerEngine(const QVariantMap ¶meters,

//                                                        QGeoServiceProvider::Error *error,

//                                                        QString *errorString) const;

    QGeoMappingManagerEngine *createMappingManagerEngine(const QVariantMap &parameters,

                                                        QGeoServiceProvider::Error *error,

                                                        QString *errorStringconst;

};

QT_END_NAMESPACE

navguardfactory.cpp

#include "navguardfactory.h"

#include "navguardengine.h"

#include <QtLocation/private/qgeotiledmappingmanagerengine_p.h>

#ifdef NavGuardPlugin_Static

Q_EXTERN_C Q_DECL_EXPORT const char *qt_plugin_query_metadata();

Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance();

const QT_PREPEND_NAMESPACE(QStaticPluginqt_static_plugin_NavGuardPlugin() {

    QT_PREPEND_NAMESPACE(QStaticPluginplugin = { qt_plugin_instanceqt_plugin_query_metadata};

    return plugin;

}

#endif

QT_USE_NAMESPACE

NavGuardFactory::NavGuardFactory() {}

QGeoMappingManagerEngine *NavGuardFactory::createMappingManagerEngine(

    const QVariantMap &parametersQGeoServiceProvider::Error *errorQString *errorStringconst

{

    return new NavGuardEngine(parameterserrorerrorString);

}

4、地图服务(QGeoTiledMappingManagerEngine)

地图瓦片管理类、设置地图一些属性。

navguardengine.h

#pragma once

#include <QtLocation/QGeoServiceProvider>

#include <QtLocation/private/qgeotiledmappingmanagerengine_p.h>

QT_BEGIN_NAMESPACE

class NavGuardEngine : public QGeoTiledMappingManagerEngine {

    Q_OBJECT

public:

    NavGuardEngine(

            const QVariantMap &parameters,

            QGeoServiceProvider::Error *error,

            QString *errorString);

    ~NavGuardEngine();

    QGeoMap *createMap() override;

};

QT_END_NAMESPACE

navguardengine.cpp

#include "navguardengine.h"

#include "navguardfetcher.h"

#include <QtLocation/private/qgeocameracapabilities_p.h>

#include <QtLocation/private/qgeomaptype_p.h>

#include <QtLocation/private/qgeotiledmap_p.h>

#include <QtLocation/private/qgeofiletilecache_p.h>

QT_BEGIN_NAMESPACE

NavGuardEngine::NavGuardEngine(

        const QVariantMap &parameters,

        QGeoServiceProvider::Error *error,

        QString *errorString)

{

    // 地图视角相关设置,对应到QMLMap类型属性

    QGeoCameraCapabilities camera_caps;

    camera_caps.setMinimumZoomLevel(1.0);

    camera_caps.setMaximumZoomLevel(18.0);

    camera_caps.setSupportsBearing(true);

    camera_caps.setSupportsTilting(true);

    camera_caps.setMinimumTilt(0);

    camera_caps.setMaximumTilt(60);

    camera_caps.setMinimumFieldOfView(20.0);

    camera_caps.setMaximumFieldOfView(120.0);

    camera_caps.setOverzoomEnabled(true);

    setCameraCapabilities(camera_caps);

    // 瓦片大小

    setTileSize(QSize(256256));

    // 瓦片获取,默认接口是通过网络请求获取

    //parameters 就是QML中设置的 PluginParameter

    NavGuardFetcher *tile_fetcher = new NavGuardFetcher(parametersthis);

    setTileFetcher(tile_fetcher);

    // 瓦片缓存-参考QtLocation的esri部分源码

    // 如果没有用QGeoFileTileCache,默认加载地缓存不会释放,详情需分析他的源码

    QString cache_dir;

    if (parameters.contains("mode")) {

        QString navGuardMode = parameters.value("mode").toString();

        QString navGuardPath = parameters.value("path").toString();

        cache_dir = navGuardPath + QString("/Cache");

    } else {

        qWarning() << "mode not set!";

        cache_dir = QAbstractGeoTileCache::baseLocationCacheDirectory() + QString("Cache");

    }

    qInfo() << "cache_dir: " << cache_dir;

    // 自定义QGeoFileTileCache

    QGeoFileTileCache *tile_cache = new QGeoFileTileCache(cache_dirthis);

    const int cache_size = 512 * 1024 * 1024;

    tile_cache->setMaxDiskUsage(cache_size);

    const int memory_size = 36 * 1024 * 1024;

    tile_cache->setMaxMemoryUsage(memory_size);

    setTileCache(tile_cache);

    m_prefetchStyle = QGeoTiledMap::NoPrefetching;

    *error = QGeoServiceProvider::NoError;

    errorString->clear();

}

NavGuardEngine::~NavGuardEngine() {}

QGeoMap *NavGuardEngine::createMap()

{

    // todo自定义QGeoTiledMap

    QGeoTiledMap *map = new QGeoTiledMap(thisnullptr);

    map->setPrefetchStyle(m_prefetchStyle);

    return map;

}

QT_END_NAMESPACE

5、瓦片请求(QGeoTileFetcher)

navguardfetcher.h

#pragma once

#include <QtLocation/private/qgeotilefetcher_p.h>

#include <QNetworkAccessManager>

QT_BEGIN_NAMESPACE

class NavGuardFetcher : public QGeoTileFetcher {

    Q_OBJECT

public:

    NavGuardFetcher(

            const QVariantMap ¶meters,

            QGeoMappingManagerEngine *parent);

private:

    QGeoTiledMapReply* getTileImage(const QGeoTileSpec &spec) override;

    QString getUrl(const QGeoTileSpec &spec) const;

    QString tileXYToQuadKey(int tileX, int tileY, int zoomLevel) const;

private:

    QString mapUrl;

    QString style{"bright"};

    QString format{"png"};

    QNetworkAccessManager* networkManager;

};

QT_END_NAMESPACE

navguardfetcher.cpp

#include "navguardfetcher.h"

#include "navguardreply.h"

#include <QtNetwork/QNetworkAccessManager>

#include <QtNetwork/QNetworkRequest>

#include <QtLocation/private/qgeotilespec_p.h>

#include <QtLocation/private/qgeotilefetcher_p_p.h>

#include <QUrl>

#include <QDebug>

QT_BEGIN_NAMESPACE

NavGuardFetcher::NavGuardFetcher(

        const QVariantMap &parameters,

        QGeoMappingManagerEngine *parent)

    : QGeoTileFetcher(parent)

    , networkManager(new QNetworkAccessManager(this)) {

    // 链接地址

    if (parameters.contains("mode")) {

        QString navGuardMode = parameters.value("mode").toString();

        if (navGuardMode != "online") {

            qInfo() << "offline mode.";

            mapUrl = QUrl::fromLocalFile(parameters.value("path").toString()).toString();

        } else {

            qInfo() << "online mode.";

            mapUrl = "online";

        }

    }

    // 瓦片风格

    if(parameters.contains("style")) {

        style = parameters.value("style").toString();

    }

}

QGeoTiledMapReply *NavGuardFetcher::getTileImage(const QGeoTileSpec &spec)

{

    // 设置网络请求头

    QNetworkRequest request;

    request.setRawHeader("Accept""*/*");

    request.setUrl(getUrl(spec));

    QNetworkReply *reply = networkManager->get(request);

    return new NavGuardReply(replyspecformat);

}

QString NavGuardFetcher::getUrl(const QGeoTileSpec &specconst

{

    if (mapUrl == "online") {

        // Bing 瓦片 URL 处理

        QString quadKey = tileXYToQuadKey(spec.x(), spec.y(), spec.zoom());

        QString url;

        if(style == "bright")

           url = QString("http://dynamic.t0.tiles.ditu.live.com/comp/ch/a%1.jpg?it=G,OS,L&mkt=zh-cn&cstl=w4c&ur=cn")

                   .arg(quadKey);

        else

           url = QString("http://dynamic.t0.tiles.ditu.live.com/comp/ch/h%1.jpg?it=G,OS,L&mkt=zh-cn&cstl=vbd&ur=cn")

                   .arg(quadKey);

        return url;

    } else {

        // 本地文件 URL 处理

        const QString zz = QString("%1").arg(spec.zoom(), 210QLatin1Char('0')).toUpper();

        const QString yy = QString("%1").arg(spec.y(), 816QLatin1Char('0')).toUpper();

        const QString xx = QString("%1").arg(spec.x(), 816QLatin1Char('0')).toUpper();

        return QString("%1/L%2/R%3/C%4.%5")

                .arg(mapUrl)

                .arg(zz)

                .arg(yy)

                .arg(xx)

                .arg(format);

    }

}

// Bing QuadKey 生成函数

QString NavGuardFetcher::tileXYToQuadKey(int tileXint tileYint zoomLevelconst

{

    QString quadKey;

    for (int i = zoomLeveli > 0i--) {

        int digit = 0;

        int mask = 1 << (i - 1);

        if ((tileX & mask) != 0) {

            digit++;

        }

        if ((tileY & mask) != 0) {

            digit += 2;

        }

        quadKey.append(QString::number(digit));

    }

    return quadKey;

}

QT_END_NAMESPACE

6、请求响应(QGeoTiledMapReply)

navguardreply.h

#pragma once

#include <QtNetwork/QNetworkReply>

#include <QtLocation/private/qgeotiledmapreply_p.h>

QT_BEGIN_NAMESPACE

class NavGuardReplypublic QGeoTiledMapReply {

    Q_OBJECT

public:

    NavGuardReply(QNetworkReply *replyconst QGeoTileSpec &spec,

                          const QString &imageFormatQObject *parent = nullptr);

private Q_SLOTS:

    void networkReplyFinished();

    void networkReplyError(QNetworkReply::NetworkError error);

};

QT_END_NAMESPACE

navguardreply.cpp

#include "navguardreply.h"

QT_BEGIN_NAMESPACE

NavGuardReply::NavGuardReply(

        QNetworkReply *reply,

        const QGeoTileSpec &spec,

        const QString &imageFormat,

        QObject *parent)

    :QGeoTiledMapReply(specparent)

{

    if (!reply) {

        setError(UnknownErrorQStringLiteral("Null reply"));

        return;

    }

    connect(replySIGNAL(finished()), thisSLOT(networkReplyFinished()));

    connect(replySIGNAL(error(QNetworkReply::NetworkError)),

            thisSLOT(networkReplyError(QNetworkReply::NetworkError)));

    connect(this, &QGeoTiledMapReply::abortedreply, &QNetworkReply::abort);

    connect(this, &QObject::destroyedreply, &QObject::deleteLater);

    setMapImageFormat(imageFormat);

}

void NavGuardReply::networkReplyFinished()

{

    QNetworkReply *reply = static_cast<QNetworkReply *>(sender());

    reply->deleteLater();

    // Already handled in networkReplyError

    if (reply->error() != QNetworkReply::NoError)

        return;

    setMapImageData(reply->readAll());

    setFinished(true);

}

void NavGuardReply::networkReplyError(QNetworkReply::NetworkError error)

{

    QNetworkReply *reply = static_cast<QNetworkReply *>(sender());

    reply->deleteLater();

    if (error == QNetworkReply::OperationCanceledError)

        setFinished(true);

    else

        setError(QGeoTiledMapReply::CommunicationErrorreply->errorString());

    // 可以在这里返回自定义的图片

}

QT_END_NAMESPACE

​​​​​​​7、离线瓦片

本地瓦片存储示例:

<mapUrl>/L<zz>/R<yy>/C<xx>.<format>

/tiles/L12/R0000012A/C0000034F.png

表示层级为 12,行号为 0x12A,列号为 0x34F 的瓦片文件。

​​​​​​​8、QML插件调用

main.cpp

#include <QGuiApplication>

#include <QQmlApplicationEngine>

#include <QQmlContext>

#ifdef NavGuardPlugin_Static

#include <QtPlugin>

Q_IMPORT_PLUGIN(NavGuardPlugin);

#endif

#include "src/navguardcore/navguardcore.h"

int main(int argcchar *argv[])

{

    QGuiApplication app(argcargv);

    qmlRegisterType<NavGuardCore>("moudelNavGuardCore"10"NavGuardCore");

    QQmlApplicationEngine engine;

    engine.rootContext()->setContextProperty("applicationDirPath"QCoreApplication::applicationDirPath());

    const QUrl url(QStringLiteral("qrc:/Main.qml"));

    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,

                     &app, [url](QObject *objconst QUrl &objUrl) {

        if (!obj && url == objUrl)

            QCoreApplication::exit(-1);

    }, Qt::QueuedConnection);

    engine.load(url);

    return app.exec();

}

Main.qml

import QtQuick 2.9

import QtQuick.Window 2.3

import QtQuick.Controls 2.12

import QtQuick.Layouts 1.3

import QtPositioning 5.12

import QtLocation 5.12

import moudelNavGuardCore 1.0

Window  {

    idrootWindow

    flagsQt.Window | Qt.FramelessWindowHint

    visible: true

    titleqsTr("NavGuard")

    minimumWidth: 800; minimumHeight: 480

    widthminimumWidthheightminimumHeight;

    property string mapMode"offline"

    property string mapStyle"bright"

    property string mapCachePathapplicationDirPath+"/NavGuard_Cache"

    NavGuardCore { idnavguardcore }

    Component.onCompleted: {

        navguardcore.setCachePath(mapCachePath)

    }

    Rectangle {

        anchors.fillparent

        Map {

            idmap

            anchors.fillparent

            pluginPlugin {

                idmapPlugin

                name"navguard"

                // 模式 - online offline

                PluginParameter {

                    name"mode"

                    valuemapMode

                }

                // 主题 - bright dark

                PluginParameter {

                    name"style"

                    valuemapStyle

                }

                // 路径

                PluginParameter {

                    name"path"

                    valuemapCachePath

                }

            }

            // 初始中心点

            centerQtPositioning.coordinate(36.31804845, 120.44626736)

            // 初始缩放等级

            zoomLevel: 12

            // 最小缩放等级

            minimumZoomLevel: 1

            // 最大缩放等级

            maximumZoomLevel: 18

            // 垂直视角0-60

            tilt: 0

            // 地图方位0-360

            bearing: 0

            MapQuickItem {

                idpoint_1

                zoomLevel: 0

                coordinateQtPositioning.coordinate(36.31806729, 120.44627839)

                anchorPointQt.point(sourceItem.width/2, sourceItem.height/2)

                sourceItemRectangle {

                    width: 14

                    height: 14

                    radius: 7

                    color"blue"

                    border.color"red"

                    border.width: 1

                    Text {

                        text"XiangfuDING"

                        color"green"

                        font.bold: true

                        font.pixelSize: 16

                        x: 5

                        y: -20

                    }

                }

            }

        }

        Slider {

            idlevelControl

            anchors.topparent.top

            anchors.rightparent.right

            anchors.margins: 10

            width: 200

            frommap.minimumZoomLevel

            tomap.maximumZoomLevel

            valuemap.zoomLevel

            onValueChanged: {

                map.zoomLevel = value

            }

        }

        Slider {

            idtiltControl

            anchors.toplevelControl.bottom

            anchors.rightparent.right

            anchors.margins: 10

            width: 200

            from: 0

            to: 60

            valuemap.tilt

            onValueChanged: {

                map.tilt = value

            }

        }

        Slider {

            idbearingControl

            anchors.toptiltControl.bottom

            anchors.rightparent.right

            anchors.margins: 10

            width: 200

            from: 0

            to: 360

            valuemap.bearing

            onValueChanged: {

                map.bearing = value

            }

        }

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值