在QML中如何使用C++模型向地图增加标记

博主有嵌入小部件应用的QML地图,用MapQuickItem绘制标记和历史轨迹线。想在折线各点(除最后一点)添加带旋转箭头的标记,还给出了Main.qml、MapMarker.Qml、AssetTrails.qml等文件代码,以及一种模型实现方法和主程序代码,询问如何解决该问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我有一个嵌入在小部件应用程序中的 QML 地图。我使用 MapQuickItem 为我在 MAP 上跟踪的资产绘制标记。此外,我绘制了一条前一轨迹点的历史线。由于操作员可以添加和删除项目,我喜欢将地图项目和历史记录作为组包含,以便我可以轻松删除与资产相关的地图项目。

我是 QML 和扩展 JS 的新手,所以通过反复试验完成了我的学习。

我想做的是在折线的每个点上添加一个标记(除了作为实际资产标记的最后一个点)。每个点上的标记也是一个图像(可能是小箭头)。我也想根据移动方向旋转这个箭头。

所以我有我的 Main.qml 文件来绘制地图等。然后我有 MapMarker.Qml 来绘制实际资产的标记。目前图像是硬编码的。然后我有一个 AssetTrails.qml 文件,目前它只是一个 Polyline 组件。我希望我可以将点标记添加到这条线索,因为这样在删除时删除所有项目会很容易。

有人可以向我解释如何解决这个问题吗?这可能吗?

我附上我的 QML 片段以供参考。我意识到这不是很好的代码,但就像我说的,我通过反复试验学习。

Main.qml 函数

function addAsset(location, Name)
{
    // Load the map marker.
    var mapmarkercomp = Qt.createComponent("mapmarker.qml");
    assetMarkers.push(mapmarkercomp.createObject(
                          map, {"coordinate": QtPositioning.coordinate(location.latitude,
                                                                       location.longitude)}));

    if (mapLoadErrorHandler(assetMarkers[assetMarkers.length - 1]))
    {
        map.center = QtPositioning.coordinate(location.latitude,
                                              location.longitude);
        map.zoomLevel = 6;
        assetMarkers[assetMarkers.length - 1].name = Name;
        assetMarkers[assetMarkers.length - 1].followMe = true;
        assetMarkers[assetMarkers.length - 1].transparency = 0;
        map.addMapItem(assetMarkers[assetMarkers.length - 1]);
    }
}
function addAssetHistory(assetPath, assetName, Colour)
{
    // Load the polyline asset trail.
    var polylinecomp = Qt.createComponent("mapassettrail.qml");
    assetTrails.push(polylinecomp.createObject(map, {"line.color": Colour}));
    if (mapLoadErrorHandler(assetTrails[assetTrails.length - 1]))
    {
        assetTrails[assetTrails.length - 1].path = assetPath;
        assetTrails[assetTrails.length - 1].objectName = assetName;
        map.addMapItem(assetTrails[assetTrails.length - 1]);
    }
}
function removeAssetHistory(assetName)
{
    // loop through the assetTrails to find object with the correct asset name and then remove it
    var rr;
    for (rr in assetTrails)
    {
        if (assetTrails[rr].objectName === assetName)
        {
            map.removeMapItem(assetTrails[rr]);
            break;
        }
    }
    assetTrails.splice(rr,1);
}
function updateAssetHistory(assetPath, assetName)
{
    // find the relevant asset trail for the assetName
    var rr;
    for (rr in assetTrails)
    {
        if (assetTrails[rr].objectName === assetName)
        {
            assetTrails[rr].path = ( assetPath);
        }
    }
}

地图标记.qml

import QtQuick 2.5
import QtQuick.Window 2.0
import QtQuick.Controls 1.4
import QtLocation 5.9
import QtPositioning 5.6
import AssetStruct 1.0

MapQuickItem
{
    property int spatialPointHeight: 60
    id: assetMapItem2
    property var name: ""
    property var followMe: false
    property var transparency: 0.5
    anchorPoint.x: assetIcon2.width/2
    anchorPoint.y: assetIcon2.height/2
    visible: true
    sourceItem: Column
    {
        Image
        {
            id: assetIcon2
            sourceSize.width: spatialPointHeight
            sourceSize.height: spatialPointHeight
            width: spatialPointHeight
            height: spatialPointHeight
            // Fade out all icons except for the last one
            opacity: 1 - assetMapItem2.transparency
            source: "qrc:/chopper.png"
            transform: Rotation
            {
                id: assetRotation2
                origin.x: spatialPointHeight/2
                origin.y: spatialPointHeight/2
                angle: 90
            }
        }
        Text
        {
            text: name
            horizontalAlignment: Text.AlignHCenter
            font.bold: true
            width: assetIcon2.width + 10
        }
    }

    Component.onCompleted:
    {
        // map.addMapItem(assetMapItem2);
    }
}

资产跟踪.qml

import QtQuick 2.0
import QtQuick 2.5
import QtQuick.Window 2.0
import QtQuick.Controls 1.4
import QtLocation 5.9
import QtPositioning 5.6
import AssetStruct 1.0

MapPolyline
{
    id: assetPolylineTrail
    line.color: "green"
    line.width: 3
}

所以解释一下我想要做的更多一点。见下图。跟踪的资产称为 Orion1。它的当前位置是斩波器所在的位置。它走过的历史轨迹是紫线。在这条线上看不到以前的报告在这张图片中有很多,所以我想为折线上的每个点添加一个点/箭头的“图像”。如果图像是箭头,我可以使用旋转来指示报告的方向。

根据我的理解,您有一组点,其中一些必须是 a 的一部分,一个MapPolylineMapQuickItem.

明智的做法是通过模型处理数据,我认为您处理的 C++ 多于QML因此这也是一个不错的选择。在此,我创建了一个存储名称、资产坐标和历史坐标列表的项目。继承自的模型QAbstractListModel能够添加元素,该模型被多个MapItemView管理MapPolyline和 的模型所使用MapQuickItem

//assetitem.h

#ifndef ASSETITEM_H
#define ASSETITEM_H

#include <QColor>
#include <QGeoCoordinate>
#include <QString>

class AssetItem{
public:
    QString name() const;
    void setName(const QString &name);

    QGeoCoordinate asset() const;
    void setAsset(const QGeoCoordinate &asset);

    void appendHistory(const QGeoCoordinate &value);
    QList<QGeoCoordinate> getHistory() const;

    QColor getColor() const;
    void setColor(const QColor &color);

private:
    QString mName;
    QGeoCoordinate mAsset;
    QList<QGeoCoordinate> history;
    QColor mColor;
};

#endif // ASSETITEM_H


//assetitem.cpp


#include "assetitem.h"

QString AssetItem::name() const
{
    return mName;
}

void AssetItem::setName(const QString &name)
{
    mName = name;
}

QGeoCoordinate AssetItem::asset() const
{
    return mAsset;
}

void AssetItem::setAsset(const QGeoCoordinate &asset)
{
    if(mAsset.isValid())
        appendHistory(mAsset);
    mAsset = asset;
}

void AssetItem::appendHistory(const QGeoCoordinate &value)
{
    history<< value;
}

QList<QGeoCoordinate> AssetItem::getHistory() const{
    return history;
}

QColor AssetItem::getColor() const
{
    return mColor;
}

void AssetItem::setColor(const QColor &color)
{
    mColor = color;
}

//assetlistmodel.h

#ifndef ASSETLISTMODEL_H
#define ASSETLISTMODEL_H

#include "assetitem.h"

#include <QAbstractListModel>

class AssetListModel : public QAbstractListModel
{
    Q_OBJECT
public:
    using QAbstractListModel::QAbstractListModel;

    enum AirportsRoles{
        NameRole = Qt::UserRole + 1,
        AssetRole,
        HistoryRole,
        ColorRole
    };

    Q_INVOKABLE bool addAsset(QGeoCoordinate coord, const QString & name);
    bool createAsset(QGeoCoordinate coord, const QString & name);

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    QHash<int, QByteArray> roleNames() const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role) override;
private:
    QList<AssetItem> mAssets;
};

#endif // ASSETLISTMODEL_H

//assetlistmodel.cpp

#include "assetlistmodel.h"

bool AssetListModel::addAsset(QGeoCoordinate coord, const QString &name)
{
    auto it = std::find_if(mAssets.begin(), mAssets.end(), [&](AssetItem const& obj){
            return obj.name() == name;
} );
    if(it != mAssets.end()){
        //append
        int row = it - mAssets.begin();
        QModelIndex ix = index(row);
        return  setData(ix, QVariant::fromValue(coord), AssetRole);
    }
    else{
        //create
        return createAsset(coord, name);
    }
}

bool AssetListModel::createAsset(QGeoCoordinate coord, const QString &name)
{
    beginInsertRows(QModelIndex(), rowCount(), rowCount());

    AssetItem it;
    it.setName(name);
    it.setAsset(coord);
    it.setColor(QColor(qrand()%255, qrand()%255, qrand()%255));
    mAssets<< it;
    endInsertRows();
    return true;
}


int AssetListModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return mAssets.count();
}

QVariant AssetListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();
    if(index.row() >= 0 && index.row()<rowCount()){
        const AssetItem &it = mAssets[index.row()];
        if(role==NameRole)
            return it.name();
        else if (role == AssetRole)
            return  QVariant::fromValue(it.asset());
        else if(role == HistoryRole){
            QVariantList history_list;
            QList<QGeoCoordinate> coords = it.getHistory();
            for(const QGeoCoordinate & coord: coords){
                history_list<<QVariant::fromValue(coord);
            }
            return history_list;
        }
        else if(role == ColorRole){
            return it.getColor();
        }
    }
    return QVariant();
}


QHash<int, QByteArray> AssetListModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[AssetRole]= "asset";
    roles[HistoryRole] = "history";
    roles[ColorRole] = "color";
    return roles;
}

bool AssetListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid())
        return false;
    if(index.row() >= 0 && index.row()<rowCount()){
        if (role == AssetRole) {
            QGeoCoordinate new_asset(value.value<QGeoCoordinate>());
            mAssets[index.row()].setAsset(new_asset);
            emit dataChanged(index, index, QVector<int>{AssetRole});
            return true;
        }
    }
    return false;
}

主程序

#include "assetlistmodel.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include<QQmlContext>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    AssetListModel model;
    QGeoCoordinate coord(41.97732, -87.90801);
     model.addAsset(coord, "testing_name1");
     model.addAsset(coord, "testing_name2");

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("assetmodel", &model);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

主文件

import QtQuick 2.9
import QtQuick.Controls 1.4
import QtLocation 5.6
import QtPositioning 5.5

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    visibility: "FullScreen"

    title: qsTr("Hello World")

    Map {
        id: mapOfWorld
        anchors.centerIn: parent;
        anchors.fill: parent
        zoomLevel: 10
        plugin: Plugin {name: "osm"}
        center: QtPositioning.coordinate(41.97732, -87.90801)//KORD

        MapItemView {
            model: assetmodel
            delegate: AssetTrail{
                path: history
                line.color: color
            }
        }

        MapItemView {
            model: assetmodel
            delegate: MapMarker{
                coordinate: asset
            }
        }
    }

    // testing
    property var last_pos1: mapOfWorld.center
    property var last_pos2: mapOfWorld.center

    Timer {
        interval: 500; running: true; repeat: true
        onTriggered: {
            last_pos1 = QtPositioning.coordinate(last_pos1.latitude + 0.1*(Math.random()-0.5),
                                                last_pos1.longitude + 0.1*(Math.random()-0.5))
            assetmodel.addAsset(last_pos1, "testing_name1")
            last_pos2 = QtPositioning.coordinate(last_pos2.latitude + 0.1*(Math.random()-0.5),
                                                last_pos2.longitude + 0.1*(Math.random()-0.5))
            assetmodel.addAsset(last_pos2, "testing_name2")
        }
    }

}

另外一种模型实现方法如下:

#ifndef MARKERMODEL_H
#define MARKERMODEL_H

#include <QAbstractListModel>
#include <QGeoCoordinate>

class MarkerModel : public QAbstractListModel
{
    Q_OBJECT

public:
    using QAbstractListModel::QAbstractListModel;
    enum MarkerRoles{positionRole = Qt::UserRole + 1};

    Q_INVOKABLE void addMarker(const QGeoCoordinate &coordinate){
        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        m_coordinates.append(coordinate);
        endInsertRows();
    }

    int rowCount(const QModelIndex &parent = QModelIndex()) const override{
        Q_UNUSED(parent)
        return m_coordinates.count();
    }

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override{
        if (index.row() < 0 || index.row() >= m_coordinates.count())
            return QVariant();
        if(role== MarkerModel::positionRole)
            return QVariant::fromValue(m_coordinates[index.row()]);
        return QVariant();
    }

    QHash<int, QByteArray> roleNames() const{
        QHash<int, QByteArray> roles;
        roles[positionRole] = "position";
        return roles;
    }

private:
    QList<QGeoCoordinate> m_coordinates;
};

#endif // MARKERMODEL_H
 

main.xml

import QtQuick 2.0
import QtLocation 5.6
import QtPositioning 5.6
import QtQuick.Window 2.0

Rectangle {
    width: Screen.width
    height: Screen.height
    visible: true

    Plugin {
        id: mapPlugin
        name: "osm"
    }

    Map {
        id: mapview
        anchors.fill: parent
        plugin: mapPlugin
        center: QtPositioning.coordinate(59.91, 10.75)
        zoomLevel: 14

        MapItemView{
            model: markerModel
            delegate: mapcomponent
        }
    }

    Component {
        id: mapcomponent
        MapQuickItem {
            id: marker
            anchorPoint.x: image.width/4
            anchorPoint.y: image.height
            coordinate: position

            sourceItem: Image {
                id: image
                source: "http://maps.gstatic.com/mapfiles/ridefinder-images/mm_20_red.png"
            }
        }
    }

    MouseArea {
        anchors.fill: parent

        onPressAndHold:  {
            var coordinate = mapview.toCoordinate(Qt.point(mouse.x,mouse.y))
            markerModel.addMarker(coordinate)
        }
    }
}
 

main.cpp

#include "markermodel.h"

#include <QApplication>
#include <QQuickWidget>
#include <QQmlContext>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QQuickWidget w;
    MarkerModel model;
    w.rootContext()->setContextProperty("markerModel", &model);
    w.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
    w.show();

    return a.exec();
}
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值