QML地图Map中提供了供绘制图形的组件,例如MapPolyline,MapCircle等,但是这些组件在绘制复杂轨迹时就显得功能不够全面,因此我将QPainterPath在Map中进行使用并进行绘制,并使用C++和Qml中的函数进行相互调用计算获取点屏幕坐标和经纬度坐标。例子中使用了QPainterPath的QPainterPath::pointAtPercent获取绘制的轨迹全过程中的各个位置的经纬度。效果如图:


QML主要功能为地图显示,其中Plugin中定义的路径为我地图瓦片存放路径,你们需要修改为自己的或者直接使用在线地图。我们自定义了一个类 MapPainter,该类继承至QQuickPaintedItem,并通过元对象系统注册至QML中,覆盖到Map上方作为画布使用。
Demo项目地址:
https://download.csdn.net/download/zjgo007/87259998
https://download.csdn.net/download/zjgo007/87259998https://github.com/zjgo007/QmlDemo/tree/master/MapPainter
https://github.com/zjgo007/QmlDemo/tree/master/MapPainter
main.qml
- import QtQuick 2.9
- import QtQuick.Window 2.2
- import QtLocation 5.9
- import QtPositioning 5.9
- import QtQuick.Controls 2.9
-
- import MapPainter 1.0
-
- Window {
- id:window
- width: 640
- height: 480
- visible: true
- title: qsTr("Map Painter Path")
-
- Plugin {
- id: mapPlugin
- name: "osm" // "mapboxgl", "esri", ...
-
- PluginParameter{//自定义地图瓦片路径
- name:"osm.mapping.offline.directory"
- value: "G:/Map/"
- }
-
- PluginParameter{
- name:"osm.mapping.offline.maptiledir"
- value:true
- }
-
- }
-
- Map {
- id: myMap
- center: QtPositioning.coordinate(24,104)
- anchors.fill: parent
- plugin: mapPlugin
-
- zoomLevel: 8
- color: "#00000000"
- copyrightsVisible: false
- activeMapType: supportedMapTypes[2]
-
- // onVisibleRegionChanged: {//Qt 5.15以上使用
- // mapPainter.mapRegionChanged()
- // }
-
- onCenterChanged: {//Qt 5.15以下使用
- mapPainter.mapRegionChanged()
- }
-
- onZoomLevelChanged: {//Qt 5.15以下使用
- mapPainter.mapRegionChanged()
- }
-
- MapPainter{
- id:mapPainter
- anchors.fill: parent
- }
-
- MapQuickItem{
- id: anchorMarker
- width: 50
- height: 36
- anchorPoint.x: image.width/2
- anchorPoint.y: image.height
- coordinate: myMap.center
-
- sourceItem: Item{
- Image {
- id:image
- source: "qrc:/anchor.png"
- sourceSize.height: 36
- sourceSize.width: 50
- }
- Text {
- id: label
- y:-15
- color: "#00ffff"
- text: qsTr("")
- font.bold: true
- font.pointSize: 11
- font.family: "微软雅黑"
- }
- }
- }
- MouseArea {
- id: mouseArea_measure
- anchors.fill: parent
- onClicked: {
- var coordinate = myMap.toCoordinate(Qt.point(mouse.x, mouse.y))
- mapPainter.addPathPoint(mouse.x, mouse.y,coordinate)
- anchorMarker.coordinate = coordinate
- }
- }
- }
-
- Slider {
- id: slider
- x: 430
- y: 10
- stepSize: 0.01
- value: 1
- onValueChanged: {
- var coordinate = mapPainter.mapPathData(value)
- anchorMarker.coordinate = coordinate
- label.text = "("+coordinate.latitude.toFixed(4)+","+coordinate.longitude.toFixed(4)+")"
- }
- }
- Component.onCompleted: {
- mapPainter.setQmlObject(window)
- }
-
- function transGeoToPoint(coordinate){
- return myMap.fromCoordinate(coordinate,false)
- }
-
- function transPointToGeo(pointf){
- return myMap.toCoordinate(pointf,false)
- }
- }
其中需要注意的是,在QML的Component.onCompleted信号发出后,需要该QML的QObject传递至MapPainter中,便于在C++中调用qml里定义的函数,这两个函数用于经纬度坐标和屏幕坐标转换。
- Component.onCompleted: {
- mapPainter.setQmlObject(window)
- }
-
- function transGeoToPoint(coordinate){
- return myMap.fromCoordinate(coordinate,false)
- }
-
- function transPointToGeo(pointf){
- return myMap.toCoordinate(pointf,false)
- }
在C++中,addPathPoint(qreal x,qreal y,QGeoCoordinate coordinate)函数用于传入鼠标点击位置的屏幕坐标和经纬度坐标。mapRegionChanged()用于标记当前地图已被平移或者缩放,需要重新绘制轨迹。
同时自定义了类GeoPainterPath,该类记录了鼠标绘制轨迹的点位置和绘制方式,我只简单的放了MoveTo和LineTo,其他绘制方式可自行添加。
mappainter.h
- #ifndef MAPPAINTER_H
- #define MAPPAINTER_H
-
- #include
- #include
- #include
- #include
- #include
-
- class GeoPainterPath
- {
- public:
- GeoPainterPath() {}
- ~GeoPainterPath(){}
-
- enum PainterType{
- None,
- MoveTo,
- LineTo
- };
-
- void addGeoPath(PainterType type,QGeoCoordinate coordinate);
-
- PainterType painterType(int index);
-
- QGeoCoordinate coordinate(int index);
-
- int size();
-
- void clear();
-
- private:
- QList
typeList; - QList
geoList; - };
-
- class MapPainter : public QQuickPaintedItem
- {
- Q_OBJECT
- public:
- MapPainter(QQuickItem *parent = nullptr);
- ~MapPainter();
- virtual void paint(QPainter *painter) Q_DECL_OVERRIDE;
-
- Q_INVOKABLE void setQmlObject(QObject* object);
-
- public slots:
- void addPathPoint(qreal x,qreal y,QGeoCoordinate coordinate);
- void updatePainterPath();
- void mapRegionChanged();
-
- QGeoCoordinate mapPathData(qreal percent);
-
- private:
- QPainterPath* testPath;
- bool pathDirty;
- GeoPainterPath mGeoPainterPath;
- QObject* qmlObject;
- };
-
-
- #endif // MAPPAINTER_H
mappainter.cpp
- #include "mappainter.h"
-
- MapPainter::MapPainter(QQuickItem *parent):QQuickPaintedItem(parent),pathDirty(false)
- {
- testPath = new QPainterPath();
- connect(this,&QQuickPaintedItem::widthChanged,this,&MapPainter::mapRegionChanged);
- connect(this,&QQuickPaintedItem::heightChanged,this,&MapPainter::mapRegionChanged);
- }
-
- MapPainter::~MapPainter()
- {
- delete testPath;
- }
-
- void MapPainter::paint(QPainter *painter)
- {
- if(pathDirty)
- updatePainterPath();
-
- painter->setPen(QPen(QColor("red"), 2, Qt::SolidLine,
- Qt::RoundCap, Qt::RoundJoin));
-
- painter->drawPath(*testPath);
- }
-
- void MapPainter::setQmlObject(QObject *object)
- {
- qmlObject = object;
- }
-
- void MapPainter::addPathPoint(qreal x, qreal y, QGeoCoordinate coordinate)
- {
- if(testPath->elementCount()==0){
- testPath->moveTo(x,y);
- mGeoPainterPath.addGeoPath(GeoPainterPath::MoveTo,coordinate);
- }else{
- testPath->lineTo(x,y);
- mGeoPainterPath.addGeoPath(GeoPainterPath::LineTo,coordinate);
- }
- update();
- }
-
- void MapPainter::updatePainterPath()
- {
- if(qmlObject){
- testPath->clear();
- int size = mGeoPainterPath.size();
- for(int i=0;i
- QGeoCoordinate coordinate = mGeoPainterPath.coordinate(i);
- QVariant mapPointVar;
- QMetaObject::invokeMethod(qmlObject,"transGeoToPoint",Qt::DirectConnection,
- Q_RETURN_ARG(QVariant,mapPointVar),
- Q_ARG(QVariant,QVariant::fromValue(coordinate))
- );
- GeoPainterPath::PainterType painterType = mGeoPainterPath.painterType(i);
- QPointF mapPoint = mapPointVar.toPointF();
- switch (painterType) {
- case GeoPainterPath::MoveTo:
- testPath->moveTo(mapPoint);
- break;
- case GeoPainterPath::LineTo:
- testPath->lineTo(mapPoint);
- break;
- default:
- break;
- }
- }
- pathDirty = false;
- }
- }
-
- void MapPainter::mapRegionChanged()
- {
- pathDirty = true;
- update();
- }
-
- QGeoCoordinate MapPainter::mapPathData(qreal percent)
- {
- QPointF pointf = testPath->pointAtPercent(percent);
- QVariant coordinateVar;
- QMetaObject::invokeMethod(qmlObject,"transPointToGeo",Qt::DirectConnection,
- Q_RETURN_ARG(QVariant,coordinateVar),
- Q_ARG(QVariant,QVariant::fromValue(pointf)));
- return coordinateVar.value
(); - }
-
- void GeoPainterPath::addGeoPath(PainterType type, QGeoCoordinate coordinate)
- {
- typeList.append(type);
- geoList.append(coordinate);
- }
-
- GeoPainterPath::PainterType GeoPainterPath::painterType(int index)
- {
- if(index>=typeList.size()){
- return PainterType::None;
- }
- return typeList.at(index);
- }
-
- QGeoCoordinate GeoPainterPath::coordinate(int index)
- {
- if(index>=geoList.size()){
- return QGeoCoordinate();
- }
- return geoList.at(index);
- }
-
- int GeoPainterPath::size()
- {
- return geoList.size();
- }
-
- void GeoPainterPath::clear()
- {
- typeList.clear();
- geoList.clear();
- }