我有一个嵌入在小部件应用程序中的 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 的一部分,一个MapPolyline
与MapQuickItem
.
明智的做法是通过模型处理数据,我认为您处理的 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();
}