0
votes

I am building an application to create and edit gpx files on a map. I want to be able to render a line and set of markers from a single model.

Each GPX point has a co-ordinate, elevation and a time. I am aiming to create a model that can be used to show it on the map and also show the elevation profile.

I have modified the answer to this question QT QmlMap PolyLine not updated properly when underlying model changes in order to base my model on a structure of GPX points.

main.c

#include "mainwindow.h"
#include <QApplication>
#include <QQmlApplicationEngine>
#include "mapmarker.h"
#include <QQmlContext>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    //Added for model registration
    QQmlApplicationEngine engine;


    qmlRegisterType<MarkerModel>("MarkerModel", 1, 0, "MarkerModel");

    engine.load(QUrl(QStringLiteral("qrc:/mainWindow.qml")));

    return app.exec();
}

mainWindow.qml

import QtQuick 2.3
import QtQuick.Window 2.3
import QtLocation 5.9
import MarkerModel 1.0

ApplicationWindow {
    id: appWindow
    width: 1512
    height: 1512
    visible: true
    x:100
    y:100


    MarkerModel{
        id: markerModel
    }


    Plugin {
          id: mapPlugin
          name: "osm"
      }

    Map {
        id: mapView
        anchors.fill: parent
        plugin: mapPlugin

        MapItemView {
            model: markerModel
            delegate: routeMarkers
        }

        MapPolyline {
            line.width: 5
            path: markerModel.path
        }

        Component {
            id: routeMarkers
            MapCircle {
                radius: 10
                center: positionRole
            }
        }

        MouseArea {
            anchors.fill: parent
            onClicked: {
                var coord = parent.toCoordinate(Qt.point(mouse.x,mouse.y))
                markerModel.addMarker(coord)
            }
        }

    }

}

mapmarker.h

#ifndef MAPMARKER_H
#define MAPMARKER_H


#include <QAbstractListModel>
#include <QGeoCoordinate>
#include <QDebug>
#include <QDate>

struct gpxCoordinate {
    QGeoCoordinate latlon;
    float ele;
    QDateTime time;
};

class MarkerModel : public QAbstractListModel {
    Q_OBJECT
    Q_PROPERTY(QVariantList path READ path NOTIFY pathChanged)

public:
    enum MarkerRoles{positionRole = Qt::UserRole, pathRole};

    MarkerModel(QObject *parent=nullptr): QAbstractListModel(parent)
    {
        connect(this, &QAbstractListModel::rowsInserted, this, &MarkerModel::pathChanged);
        connect(this, &QAbstractListModel::rowsRemoved, this, &MarkerModel::pathChanged);
        connect(this, &QAbstractListModel::dataChanged, this, &MarkerModel::pathChanged);
        connect(this, &QAbstractListModel::modelReset, this, &MarkerModel::pathChanged);
        connect(this, &QAbstractListModel::rowsMoved, this, &MarkerModel::pathChanged);
    }

    Q_INVOKABLE void addMarker(const QGeoCoordinate &coordinate, float elevation = 0, QDateTime dateTime = QDateTime::currentDateTime()) {

        gpxCoordinate item;
        item.latlon = coordinate;
        item.ele = elevation;
        item.time = dateTime;

        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        m_coordinates.append(item);
        endInsertRows();
    }



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

    bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override {
        if(row + count > m_coordinates.count() || row < 0)
            return false;
        beginRemoveRows(parent, row, row+count-1);
        for(int i = 0; i < count; ++i)
            m_coordinates.removeAt(row + i);
        endRemoveRows();
        return true;
    }

    bool removeRow(int row, const QModelIndex &parent = QModelIndex()) {
        return removeRows(row, 1, parent);
    }

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {

        if (index.row() < 0 || index.row() >= m_coordinates.count())
            return QVariant();
        if(role == Qt::DisplayRole)
            return QVariant::fromValue(index.row());
        else if(role == MarkerModel::positionRole){
            return QVariant::fromValue(m_coordinates[index.row()].latlon);

        }
        return QVariant();
    }

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

    QVariantList path() const{
        QVariantList path;
        for(const gpxCoordinate & coord: m_coordinates){
            path << QVariant::fromValue(coord.latlon);

        }
        return path;
    }
signals:
    void pathChanged();

private:
    QVector<gpxCoordinate> m_coordinates;
};
#endif // MARKERMODEL_H

I think I have made a really basic mistake somewhere as I can click on the map and draw a polyline but the MapCircles are not rendering. I see the error:-

Unable to assign [undefined] to QGeoCoordinate

When I first click on the map.

Have I misunderstood how models/roles work in Qt QML?

1
mmm, I have corrected trivial things about your code and it works correctly for me in Qt5 5.14.2 on Linux: github.com/eyllanesc/stackoverflow/tree/master/questions/…eyllanesc
Are the circles displaying? If you add mapView.fitViewportToVisibleMapItems(); after adding a marker the map will zoom in and you should see the circles render for each point?CitizenFish

1 Answers

0
votes

I have tracked this down to a build issue. I had some additional paths in my .pro file and was including some libraries that were not being used (spatialite) removing these fixed the issue completely.

I will leave the question up as it may be useful to anyone wanting to do something similar.