c++qtqmlopenstreetmap

How to Add Circles to a Map in QtLocation?


I have a Qt application, I use Quick Widget, that displays a map using QtLocation and QtPositioning. I am trying to add circles to the map using coordinates from an array passed from C++. The circles should appear on the map when the component loads.

Here is my QML code:

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtLocation 5.15
import QtPositioning 5.15
import QtQuick.Layouts 1.15

Rectangle {
    id: rec
    function addCircle(latitude, longitude, radius) {
        map.addCircle(latitude, longitude, radius)
    }

    width: 800
    height: 600

    Plugin {
        id: mapPlugin
        name: "osm"
        PluginParameter {
            name: "osm.mapping.custom.host"
            value: "https://tile.openstreetmap.org/"
        }
    }

    Map {
        id: map
        property MapCircle circle
        anchors.fill: parent
        plugin: mapPlugin
        center: QtPositioning.coordinate(70, -175.5)
        zoomLevel: 6

        Component.onCompleted: {
            for (var i = 0; i < myArray.length; i += 2) {
                console.log(QtPositioning.coordinate(myArray[i], myArray[i + 1]));
                var circle = Qt.createQmlObject('import QtPositioning 5.6; import QtLocation 5.6; MapCircle { }', map);
                circle.center = QtPositioning.coordinate(myArray[i], myArray[i + 1]);
                circle.radius = 5000;
                circle.color = 'red';
                circle.z = 1000;
                console.log(circle.center, circle.radius);
                map.addMapItem(circle);
                console.log("Circle added:", circle);
            }
        }
    }
}

And here is my main C++ file:

#include "mainwindow.h"
#include "GSN_model.h"
#include "for_SQLite_db.h"
#include "mapcontroller.h"
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QVariantList>

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;

    // Creating the list
    QVariantList myArray;
    myArray << 70.3 << -175.4 << 69.3 << -175.4 << 71 << -175.4 ;

    // Registering the list in the QML context
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("myArray", myArray);

    const QUrl url(QStringLiteral("qrc:/map.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
        &a, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
    engine.load(url);

    w.show();
    return a.exec();
}

The circles are supposed to be added to the map at startup, and the log shows that they are indeed being added:

qml: 70° 18' 0.0" N, 175° 24' 0.0" W
qml: 70° 18' 0.0" N, 175° 24' 0.0" W 5000
qml: Circle added: QDeclarativeCircleMapItem(0x243b9fe9c80)
qml: 69° 18' 0.0" N, 175° 24' 0.0" W
qml: 69° 18' 0.0" N, 175° 24' 0.0" W 5000
qml: Circle added: QDeclarativeCircleMapItem(0x243b9fea100)
qml: 71° 0' 0.0" N, 175° 24' 0.0" W
qml: 71° 0' 0.0" N, 175° 24' 0.0" W 5000
qml: Circle added: QDeclarativeCircleMapItem(0x243b9feb000)

However, the circles do not appear on the map. What am I doing wrong? How can I correctly add circles to the map in QtLocation?

I tried calling a function from QML

main.cpp

 QQmlApplicationEngine engine;
    MapController mapController;

    engine.rootContext()->setContextProperty("mapController", &mapController);

    const QUrl url(QStringLiteral("qrc:/map.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
        &a, [url, &mapController](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);

            mapController.setRootObject(obj);

            mapController.drawCircle(70.6, -175.6, 10000.0);
            mapController.drawCircle(70.3, -175.4, 5000000);
        }, Qt::QueuedConnection);

    engine.load(url);

mapcontoller.h

#ifndef MAPCONTROLLER_H
#define MAPCONTROLLER_H

#include <QObject>
#include <QGeoCoordinate>

class MapController : public QObject {
    Q_OBJECT
public:
    explicit MapController(QObject *parent = nullptr);

    Q_INVOKABLE void drawCircle(double latitude, double longitude, double radius);

    void setRootObject(QObject *rootObject);

private:
    QObject *m_rootObject;
};

#endif // MAPCONTROLLER_H

mapcontoller.cpp

#include "mapcontroller.h"
#include <QQmlProperty>
#include <QDebug>

MapController::MapController(QObject *parent) : QObject(parent), m_rootObject(nullptr) {}

void MapController::setRootObject(QObject *rootObject) {
    m_rootObject = rootObject;
}

void MapController::drawCircle(double latitude, double longitude, double radius) {
    if (!m_rootObject) {
        qWarning() << "Root object is not set!";
        return;
    }

    QVariant returnedValue;
    QVariant lat = latitude;
    QVariant lon = longitude;
    QVariant rad = radius;

    QMetaObject::invokeMethod(m_rootObject, "addCircle",
                              Q_RETURN_ARG(QVariant, returnedValue),
                              Q_ARG(QVariant, lat),
                              Q_ARG(QVariant, lon),
                              Q_ARG(QVariant, rad));
}

map.qml

...
        Item {
            id: circlesLayer
            width: parent.width
            height: parent.height


        function addCircle(latitude, longitude, radius) {
            console.log(QtPositioning.coordinate(latitude, longitude), radius);
            var circle = Qt.createQmlObject('import QtPositioning 5.6; import QtLocation 5.6; MapCircle { }', map);
            circle.center = QtPositioning.coordinate(latitude, longitude);
            circle.radius = radius;
            circle.color = 'red';
            circle.z = 1000;
            console.log(circle.center, circle.radius);

            map.addMapItem(circle);
            console.log("Circle added:", circle);

        }
    }

But I have encountered the same error


Solution

  • To make things simpler, I think you should return a 2D array as follows:

    QVariantList myArray;
    myArray << QVariant(QVariantList() << QVariant(70.3) << QVariant(-175.4));
    myArray << QVariant(QVariantList() << QVariant(69.3) << QVariant(-175.4));
    myArray << QVariant(QVariantList() << QVariant(71.3) << QVariant(-175.4));
    // myArray = [[70.3,-175.4],[69.3,-175.4],[71.3,-175.4]]
    

    Then, consume that array in a MapItemView to create the MapCircle, i.e.

    Map {
        anchors.fill: parent
        plugin: Plugin { name: "osm" }
        center: QtPositioning.coordinate(70, -175.5)
        zoomLevel: 6
        MapItemView {
            model: myArray
            delegate: MapCircle {
                center: QtPositioning.coordinate(modelData[0], modelData[1])
                radius: 10000.0
                color: "red"
            }
        }
    }
    

    screenshot