一、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"相关特性,则需要实现该虚函数 |
navguardfactory.h |
#pragma once #include <QtCore/QObject> #include <QtLocation/QGeoServiceProviderFactory> QT_BEGIN_NAMESPACE class NavGuardFactory : public QObject, public 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 ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const; }; 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(QStaticPlugin) qt_static_plugin_NavGuardPlugin() { QT_PREPEND_NAMESPACE(QStaticPlugin) plugin = { qt_plugin_instance, qt_plugin_query_metadata}; return plugin; } #endif QT_USE_NAMESPACE NavGuardFactory::NavGuardFactory() {} QGeoMappingManagerEngine *NavGuardFactory::createMappingManagerEngine( const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const { return new NavGuardEngine(parameters, error, errorString); } |
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 ¶meters, 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 ¶meters, 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(256, 256)); // 瓦片获取,默认接口是通过网络请求获取 //parameters 就是QML中设置的 PluginParameter NavGuardFetcher *tile_fetcher = new NavGuardFetcher(parameters, this); 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_dir, this); 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(this, nullptr); 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 ¶meters, 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(reply, spec, format); } QString NavGuardFetcher::getUrl(const QGeoTileSpec &spec) const { 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(), 2, 10, QLatin1Char('0')).toUpper(); const QString yy = QString("%1").arg(spec.y(), 8, 16, QLatin1Char('0')).toUpper(); const QString xx = QString("%1").arg(spec.x(), 8, 16, QLatin1Char('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 tileX, int tileY, int zoomLevel) const { QString quadKey; for (int i = zoomLevel; i > 0; i--) { 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 NavGuardReply: public QGeoTiledMapReply { Q_OBJECT public: NavGuardReply(QNetworkReply *reply, const QGeoTileSpec &spec, const QString &imageFormat, QObject *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(spec, parent) { if (!reply) { setError(UnknownError, QStringLiteral("Null reply")); return; } connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); connect(this, &QGeoTiledMapReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &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::CommunicationError, reply->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 argc, char *argv[]) { QGuiApplication app(argc, argv); qmlRegisterType<NavGuardCore>("moudelNavGuardCore", 1, 0, "NavGuardCore"); QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("applicationDirPath", QCoreApplication::applicationDirPath()); const QUrl url(QStringLiteral("qrc:/Main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const 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 { id: rootWindow flags: Qt.Window | Qt.FramelessWindowHint visible: true title: qsTr("NavGuard") minimumWidth: 800; minimumHeight: 480 width: minimumWidth; height: minimumHeight; property string mapMode: "offline" property string mapStyle: "bright" property string mapCachePath: applicationDirPath+"/NavGuard_Cache" NavGuardCore { id: navguardcore } Component.onCompleted: { navguardcore.setCachePath(mapCachePath) } Rectangle { anchors.fill: parent Map { id: map anchors.fill: parent plugin: Plugin { id: mapPlugin name: "navguard" // 模式 - online offline PluginParameter { name: "mode" value: mapMode } // 主题 - bright dark PluginParameter { name: "style" value: mapStyle } // 路径 PluginParameter { name: "path" value: mapCachePath } } // 初始中心点 center: QtPositioning.coordinate(36.31804845, 120.44626736) // 初始缩放等级 zoomLevel: 12 // 最小缩放等级 minimumZoomLevel: 1 // 最大缩放等级 maximumZoomLevel: 18 // 垂直视角0-60 tilt: 0 // 地图方位0-360 bearing: 0 MapQuickItem { id: point_1 zoomLevel: 0 coordinate: QtPositioning.coordinate(36.31806729, 120.44627839) anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2) sourceItem: Rectangle { 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 { id: levelControl anchors.top: parent.top anchors.right: parent.right anchors.margins: 10 width: 200 from: map.minimumZoomLevel to: map.maximumZoomLevel value: map.zoomLevel onValueChanged: { map.zoomLevel = value } } Slider { id: tiltControl anchors.top: levelControl.bottom anchors.right: parent.right anchors.margins: 10 width: 200 from: 0 to: 60 value: map.tilt onValueChanged: { map.tilt = value } } Slider { id: bearingControl anchors.top: tiltControl.bottom anchors.right: parent.right anchors.margins: 10 width: 200 from: 0 to: 360 value: map.bearing onValueChanged: { map.bearing = value } } } } |