Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In QML, (how) can I make MapItemGroup as a MapItemView's delegate component?

Situation: I am able to use QML Map item with Model/View/delegate. I am able to work with individual items.

Problem: As a next step, I would like to be able to draw multiple items. I need to put multiple QML MapItems (like MapCircle, MapRectangle, etc..) in a single delegate component. In general, QML supports multiple items within a delegate. The problem is with MapItemView's delegate: it does not support multiple children items.

My Approach:

  1. I thought using MapItemGroup would work. But it seems like I am missing something. The documentation is also not so extensive on how I can make it work as a delegate component. The attached snippet shows this implementation.

    Qt's Documentation on MapItemGroup

In the code below:

  • delegateCircle, delegateRect work fine
  • delegateGroup is not displayed

A simple implementation:

import QtQuick 2.10
import QtPositioning 5.6
import QtLocation 5.9
import QtQuick.Controls 2.3 as QQc2

QQc2.ApplicationWindow {
    visible: true
    width: 640
    height: 480
    // Some list model
    ListModel {
        id: someModel
        ListElement {lat: 0; lon: 0}
        ListElement {lat: 5; lon: 0}
        ListElement {lat: 5; lon: 5}
        ListElement {lat: 0; lon: 5}
    }

    Map {
        id: map
        anchors.fill: parent
        plugin: Plugin {name: "osm"}
        center: QtPositioning.coordinate(2.5, 2.5)
        zoomLevel: 6

        // Some views to test the model
        // delegateCircle, delegateRect work fine
        // delegateGroup is not displayed 
        MapItemView {
            model: someModel
            delegate: MapCircle {
                id: delegateCircle
                border.color: "red"
                border.width: 1
                center: QtPositioning.coordinate(model.lat, model.lon)
                radius: 50*1000
            }
        }

        MapItemView {
            model: someModel
            delegate: MapRectangle {
                id: delegateRect
                border.color: "green"
                border.width: 3
                topLeft     : QtPositioning.coordinate(model.lat+1, model.lon-1)
                bottomRight : QtPositioning.coordinate(model.lat-1, model.lon+1)
            }
        }

        MapItemView {
            model: someModel
            delegate: MapItemGroup {
                id: delegateGroup
                MapCircle {
                    id: innerCircle
                    border.color: "green"
                    border.width: 3
                    center: QtPositioning.coordinate(model.lat, model.lon)
                    radius: 75*1000
                }

                MapRectangle {
                    id: innerRect
                    border.color: "red"
                    border.width: 6
                    topLeft     : QtPositioning.coordinate(model.lat+2, model.lon-2)
                    bottomRight : QtPositioning.coordinate(model.lat-2, model.lon+2)
                }
            }
        }
    }
}

The output of the above code is: The output of the QML code

  1. I also tried using the MapItemGroup as a sourceItem of MapQuickItem type. This did not work either.

What I want to achieve:

Well. I need to draw multiple map items using a MapItemView. Any other solution/method (including c++ backend program) is welcome.

EDIT

Thank you @GrecKo and @Yoann. Both of your solutions work. However, I chose to continue with Instantiator since it is more suited for my application.

I also found this interesting after seeing your solution: a developer's discussion on populating a model using Repeater and Instantiator.

like image 425
tegginamaniss Avatar asked Jan 28 '23 10:01

tegginamaniss


2 Answers

Unfortunately, MapItemView only works with MapItem-derived items, and MapItemGroup is not one of them.

You could use a Repeater and it will work for delegates created from the start, but it won't work if you add rows in your model later on. I explained in this other answer why Repeater is not well suited for a Map.

In my answer I advise to use MapItemView but it's not applicable here. Hopefully, there is still one last available solution :
Instantiator with Map.addMapItemGroup() and Map.removeMapItemGroup().

import QtQuick 2.10
import QtPositioning 5.6
import QtLocation 5.9
import QtQuick.Controls 2.3 as QQc2
import QtQml 2.2

QQc2.ApplicationWindow {
    visible: true
    width: 640
    height: 480

    ListModel {
        id: someModel
        ListElement {lat: 0; lon: 0}
        ListElement {lat: 5; lon: 0}
        ListElement {lat: 5; lon: 5}
        ListElement {lat: 0; lon: 5}
    }

    Timer {
        interval: 1000
        running: true
        repeat: true
        property bool toggle: true
        onTriggered: {
            if (toggle)
                someModel.append({lat: 2.5, lon: 2.5});
            else
                someModel.remove(4);
            toggle = !toggle;
        }
    }

    Map {
        id: map
        anchors.fill: parent
        plugin: Plugin {name: "osm"}
        center: QtPositioning.coordinate(2.5, 2.5)
        zoomLevel: 6

        Instantiator {
            model: someModel
            delegate: MapItemGroup {
                id: delegateGroup
                MapCircle {
                    id: innerCircle
                    border.color: "green"
                    border.width: 3
                    center: QtPositioning.coordinate(model.lat, model.lon)
                    radius: 75*1000
                }

                MapRectangle {
                    id: innerRect
                    border.color: "red"
                    border.width: 6
                    topLeft     : QtPositioning.coordinate(model.lat+2, model.lon-2)
                    bottomRight : QtPositioning.coordinate(model.lat-2, model.lon+2)
                }
            }
            onObjectAdded: map.addMapItemGroup(object)
            onObjectRemoved: map.removeMapItemGroup(object)
        }
    }
}
like image 89
GrecKo Avatar answered Apr 30 '23 13:04

GrecKo


That was fixed in Qt 5.12, so You may use Your code as is now with

import QtLocation 5.12
import QtPositioning 5.12
like image 28
Aleksey Kontsevich Avatar answered Apr 30 '23 11:04

Aleksey Kontsevich