Map Viewer Example#

The Map Viewer example shows how to display and interact with a map, search for an address, and find driving directions.

This is a large example covering many basic uses of maps, positioning, and navigation services in Qt Location.

Map Viewer Screenshot

Download this example

# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

"""PySide6 port of the location/mapviewer example from Qt v6.x"""

import os
import sys
from pathlib import Path

from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtGui import QGuiApplication
from PySide6.QtNetwork import QSslSocket
from PySide6.QtCore import QCoreApplication, QMetaObject, QUrl, Q_ARG

HELP = """Usage:
plugin.<parameter_name> <parameter_value> - Sets parameter = value for plugin"""


def parseArgs(args):
    parameters = {}
    while args:
        param = args[0]
        args = args[1:]
        if param.startswith("--plugin."):
            param = param[9:]
            if not args or args[0].startswith("--"):
                parameters[param] = True
            else:
                value = args[0]
                args = args[1:]
                if value in ("true", "on", "enabled"):
                    parameters[param] = True
                elif value in ("false", "off", "disable"):
                    parameters[param] = False
                else:
                    parameters[param] = value
    return parameters


if __name__ == "__main__":
    additionalLibraryPaths = os.environ.get("QTLOCATION_EXTRA_LIBRARY_PATH")
    if additionalLibraryPaths:
        for p in additionalLibraryPaths.split(':'):
            QCoreApplication.addLibraryPath(p)

    application = QGuiApplication(sys.argv)
    name = "QtLocation Mapviewer example"
    QCoreApplication.setApplicationName(name)

    args = sys.argv[1:]
    if "--help" in args:
        print(f"{name}\n\n{HELP}")
        sys.exit(0)

    parameters = parseArgs(args)
    if not parameters.get("osm.useragent"):
        parameters["osm.useragent"] = name

    engine = QQmlApplicationEngine()
    engine.rootContext().setContextProperty("supportsSsl",
                                            QSslSocket.supportsSsl())
    engine.addImportPath(":/imports")
    qml_file = Path(__file__).parent / "mapviewer.qml"
    engine.load(QUrl.fromLocalFile(qml_file))
    engine.quit.connect(QCoreApplication.quit)

    items = engine.rootObjects()
    if not items:
        sys.exit(-1)

    QMetaObject.invokeMethod(items[0], "initializeProviders",
                             Q_ARG("QVariant", parameters))

    ex = application.exec()
    del engine
    sys.exit(ex)
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtPositioning

GeocodeForm {

    property variant address
    signal showPlace(variant address)
    signal closeForm()

    goButton.onClicked: {
        // fill out the Address element
        address.street = street.text
        address.city = city.text
        address.state = stateName.text
        address.country = country.text
        address.postalCode = postalCode.text
        showPlace(address)
    }

    clearButton.onClicked: {
        street.text = ""
        city.text = ""
        stateName.text = ""
        country.text = ""
        postalCode.text = ""
    }

    cancelButton.onClicked: {
        closeForm()
    }

    Component.onCompleted: {
        street.text = address.street
        city.text = address.city
        stateName.text = address.state
        country.text = address.country
        postalCode.text = address.postalCode
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Item {
    property alias goButton: goButton
    property alias clearButton: clearButton
    property alias postalCode: postalCode
    property alias street: street
    property alias city: city
    property alias stateName: stateName
    property alias country: country
    property alias cancelButton: cancelButton
    Rectangle {
        id: tabRectangle
        y: 20
        height: tabTitle.height * 2
        color: "#46a2da"
        anchors.rightMargin: 0
        anchors.leftMargin: 0
        anchors.left: parent.left
        anchors.right: parent.right

        Label {
            id: tabTitle
            color: "#ffffff"
            text: qsTr("Geocode")
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }

    Item {
        id: item2
        anchors.rightMargin: 20
        anchors.leftMargin: 20
        anchors.bottomMargin: 20
        anchors.topMargin: 20
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.top: tabRectangle.bottom


        GridLayout {
            id: gridLayout3
            anchors.rightMargin: 0
            anchors.bottomMargin: 0
            anchors.leftMargin: 0
            anchors.topMargin: 0
            rowSpacing: 10
            rows: 1
            columns: 2
            anchors.fill: parent

            Label {
                id: label2
                text: qsTr("Street")
            }

            TextField {
                id: street
                Layout.fillWidth: true
            }

            Label {
                id: label3
                text: qsTr("City")
            }

            TextField {
                id: city
                Layout.fillWidth: true
            }

            Label {
                id: label4
                text: qsTr("State")
            }

            TextField {
                id: stateName
                Layout.fillWidth: true
            }

            Label {
                id: label5
                text: qsTr("Country")
            }

            TextField {
                id: country
                Layout.fillWidth: true
            }

            Label {
                id: label6
                text: qsTr("Postal Code")
            }

            TextField {
                id: postalCode
                Layout.fillWidth: true
            }

            RowLayout {
                id: rowLayout1
                Layout.columnSpan: 2
                Layout.alignment: Qt.AlignRight

                Button {
                    id: goButton
                    text: qsTr("Proceed")
                }

                Button {
                    id: clearButton
                    text: qsTr("Clear")
                }

                Button {
                    id: cancelButton
                    text: qsTr("Cancel")
                }
            }

            Item {
                Layout.fillHeight: true
                Layout.columnSpan: 2
            }
        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtPositioning

LocaleForm {
    property string locale
    signal selectLanguage(string language)
    signal closeForm()

    goButton.onClicked: {

       if (!languageGroup.checkedButton) return

       if (otherRadioButton.checked) {
           selectLanguage(language.text)
       } else {
           selectLanguage(languageGroup.checkedButton.text)
       }
    }

    clearButton.onClicked: {
        language.text = ""
    }

    cancelButton.onClicked: {
        closeForm()
    }

    Component.onCompleted: {
        switch (locale) {
            case "en":
                enRadioButton.checked = true;
                break
            case "fr":
                frRadioButton.checked = true;
                break
            default:
                otherRadioButton.checked = true;
                language.text = locale
                break
        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Item {
    property alias clearButton: clearButton
    property alias goButton: goButton
    property alias cancelButton: cancelButton
    property alias tabTitle: tabTitle
    property alias languageGroup: languageGroup
    property alias enRadioButton: enRadioButton
    property alias frRadioButton: frRadioButton
    property alias otherRadioButton: otherRadioButton
    property alias language: language

    Rectangle {
        id: tabRectangle
        y: 20
        height: tabTitle.height * 2
        color: "#46a2da"
        anchors.rightMargin: 0
        anchors.leftMargin: 0
        anchors.left: parent.left
        anchors.right: parent.right

        Label {
            id: tabTitle
            color: "#ffffff"
            text: "Locale"
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }

    Item {
        id: item2
        anchors.rightMargin: 20
        anchors.leftMargin: 20
        anchors.bottomMargin: 20
        anchors.topMargin: 20
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.top: tabRectangle.bottom

        GridLayout {
            id: gridLayout3
            anchors.rightMargin: 0
            anchors.bottomMargin: 0
            anchors.leftMargin: 0
            anchors.topMargin: 0
            rowSpacing: 10
            rows: 1
            columns: 2
            anchors.fill: parent

            ButtonGroup { id: languageGroup }
            RadioButton {
                id: enRadioButton
                text: qsTr("en")
                ButtonGroup.group: languageGroup
                Layout.columnSpan: 2
            }

            RadioButton {
                id: frRadioButton
                text: qsTr("fr")
                ButtonGroup.group: languageGroup
                Layout.columnSpan: 2
            }

            RadioButton {
                id: otherRadioButton
                text: qsTr("Other")
                ButtonGroup.group: languageGroup
            }

            TextField {
                id: language
                Layout.fillWidth: true
                placeholderText: qsTr("")
            }

            RowLayout {
                id: rowLayout1
                Layout.columnSpan: 2
                Layout.alignment: Qt.AlignRight

                Button {
                    id: goButton
                    text: qsTr("Proceed")
                }

                Button {
                    id: clearButton
                    text: qsTr("Clear")
                }

                Button {
                    id: cancelButton
                    text: qsTr("Cancel")
                }
            }

            Item {
                Layout.fillHeight: true
                Layout.columnSpan: 2
            }


        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick

MessageForm {
    property string title
    property string message
    property variant backPage

    signal closeForm(variant backPage)

    button.onClicked: {
        closeForm(backPage)
    }

    Component.onCompleted: {
        messageText.text = message
        messageTitle.text = title
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Item {
    id: root
    property alias messageText: messageText
    property alias messageTitle: messageTitle
    property alias button: button

    Rectangle {
        id: tabRectangle
        y: 20
        height: messageTitle.height * 2
        color: "#46a2da"
        anchors.rightMargin: 0
        anchors.leftMargin: 0
        anchors.left: parent.left
        anchors.right: parent.right

        Label {
            id: messageTitle
            color: "#ffffff"
            text: qsTr("type")
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }

    Item {
        anchors.rightMargin: 20
        anchors.leftMargin: 20
        anchors.bottomMargin: 20
        anchors.topMargin: 20
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.top: tabRectangle.bottom

        ColumnLayout {
            id: columnLayout1
            spacing: 20
            anchors.fill: parent

            Label {
                id: messageText
                text: qsTr("message")
                Layout.fillWidth: true
                horizontalAlignment: Text.AlignHCenter
                wrapMode: Text.WordWrap
                textFormat: Text.RichText
            }

            Button {
                id: button
                text: qsTr("OK")
                Layout.alignment: Qt.AlignHCenter
            }

            Item {
                Layout.fillHeight: true
            }
        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtPositioning

//Reverse Geocode Dialog
ReverseGeocodeForm {
    property string title;
    property variant coordinate
    signal showPlace(variant coordinate)
    signal closeForm()

    goButton.onClicked: {
        var coordinate = QtPositioning.coordinate(parseFloat(latitude.text),
                                                          parseFloat(longitude.text));
        if (coordinate.isValid) {
            showPlace(coordinate)
        }
    }

    clearButton.onClicked: {
        latitude.text = ""
        longitude.text = ""
    }

    cancelButton.onClicked: {
        closeForm()
    }

    Component.onCompleted: {
        latitude.text = "" + coordinate.latitude
        longitude.text = "" + coordinate.longitude
        if (title.length != 0) {
            tabTitle.text = title;
        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Item {
    property alias clearButton: clearButton
    property alias goButton: goButton
    property alias longitude: longitude
    property alias latitude: latitude
    property alias cancelButton: cancelButton
    property alias tabTitle: tabTitle
    Rectangle {
        id: tabRectangle
        y: 20
        height: tabTitle.height * 2
        color: "#46a2da"
        anchors.rightMargin: 0
        anchors.leftMargin: 0
        anchors.left: parent.left
        anchors.right: parent.right

        Label {
            id: tabTitle
            color: "#ffffff"
            text: qsTr("Reverse Geocode")
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }

    Item {
        id: item2
        anchors.rightMargin: 20
        anchors.leftMargin: 20
        anchors.bottomMargin: 20
        anchors.topMargin: 20
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.top: tabRectangle.bottom

        GridLayout {
            id: gridLayout3
            anchors.rightMargin: 0
            anchors.bottomMargin: 0
            anchors.leftMargin: 0
            anchors.topMargin: 0
            rowSpacing: 10
            rows: 1
            columns: 2
            anchors.fill: parent

            Label {
                id: label2
                text: qsTr("Latitude")
            }

            TextField {
                id: latitude
                Layout.fillWidth: true
            }

            Label {
                id: label3
                text: qsTr("Longitude")
            }

            TextField {
                id: longitude
                Layout.fillWidth: true
                placeholderText: qsTr("")
            }

            RowLayout {
                id: rowLayout1
                Layout.columnSpan: 2
                Layout.alignment: Qt.AlignRight

                Button {
                    id: goButton
                    text: qsTr("Proceed")
                }

                Button {
                    id: clearButton
                    text: qsTr("Clear")
                }

                Button {
                    id: cancelButton
                    text: qsTr("Cancel")
                }
            }
            Item {
                Layout.fillHeight: true
                Layout.columnSpan: 2
            }
        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtLocation
import QtPositioning

RouteAddressForm {
    property alias plugin : tempGeocodeModel.plugin;
    property variant fromAddress;
    property variant toAddress;
    signal showMessage(string topic, string message)
    signal showRoute(variant startCoordinate,variant endCoordinate)
    signal closeForm()

    goButton.onClicked: {
        tempGeocodeModel.reset()
        fromAddress.country =  fromCountry.text
        fromAddress.street = fromStreet.text
        fromAddress.city =  fromCity.text
        toAddress.country = toCountry.text
        toAddress.street = toStreet.text
        toAddress.city = toCity.text
        tempGeocodeModel.startCoordinate = QtPositioning.coordinate()
        tempGeocodeModel.endCoordinate = QtPositioning.coordinate()
        tempGeocodeModel.query = fromAddress
        tempGeocodeModel.update();
        goButton.enabled = false;
    }

    clearButton.onClicked: {
        fromStreet.text = ""
        fromCity.text = ""
        fromCountry.text = ""
        toStreet.text = ""
        toCity.text = ""
        toCountry.text = ""
    }

    cancelButton.onClicked: {
        closeForm()
    }

    Component.onCompleted: {
        fromStreet.text  = fromAddress.street
        fromCity.text =  fromAddress.city
        fromCountry.text = fromAddress.country
        toStreet.text = toAddress.street
        toCity.text = toAddress.city
        toCountry.text = toAddress.country
    }

    GeocodeModel {
        id: tempGeocodeModel

        property int success: 0
        property variant startCoordinate
        property variant endCoordinate

        onCountChanged: {
            if (success == 1 && count == 1) {
                query = toAddress
                update();
            }
        }

        onStatusChanged: {
            if ((status == GeocodeModel.Ready) && (count == 1)) {
                success++
                if (success == 1) {
                    startCoordinate.latitude = get(0).coordinate.latitude
                    startCoordinate.longitude = get(0).coordinate.longitude
                }
                if (success == 2) {
                    endCoordinate.latitude = get(0).coordinate.latitude
                    endCoordinate.longitude = get(0).coordinate.longitude
                    success = 0
                    if (startCoordinate.isValid && endCoordinate.isValid)
                        showRoute(startCoordinate,endCoordinate)
                    else
                        goButton.enabled = true
                }
            } else if ((status == GeocodeModel.Ready) || (status == GeocodeModel.Error)) {
                var st = (success == 0 ) ? "start" : "end"
                success = 0
                if ((status == GeocodeModel.Ready) && (count == 0 )) {
                    showMessage(qsTr("Geocode Error"),qsTr("Unsuccessful geocode"));
                    goButton.enabled = true;
                }
                else if (status == GeocodeModel.Error) {
                    showMessage(qsTr("Geocode Error"),
                                qsTr("Unable to find location for the") + " " +
                                st + " " +qsTr("point"))
                    goButton.enabled = true;
                }
                else if ((status == GeocodeModel.Ready) && (count > 1 )) {
                    showMessage(qsTr("Ambiguous geocode"),
                                count + " " + qsTr("results found for the") +
                                " " + st + " " +qsTr("point, please specify location"))
                    goButton.enabled = true;
                }
            }
        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause


import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Item {
    property alias fromStreet: fromStreet
    property alias fromCountry: fromCountry
    property alias toStreet: toStreet
    property alias toCity: toCity
    property alias toCountry: toCountry
    property alias fromCity: fromCity
    property alias goButton: goButton
    property alias clearButton: clearButton
    property alias cancelButton: cancelButton

    Rectangle {
        id: tabRectangle
        y: 20
        height: tabTitle.height * 2
        color: "#46a2da"
        anchors.rightMargin: 0
        anchors.leftMargin: 0
        anchors.left: parent.left
        anchors.right: parent.right

        Label {
            id: tabTitle
            color: "#ffffff"
            text: qsTr("Route Address")
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }

    Item {
        id: item2
        anchors.rightMargin: 20
        anchors.leftMargin: 20
        anchors.bottomMargin: 20
        anchors.topMargin: 20
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.top: tabRectangle.bottom

        GridLayout {
            id: gridLayout3
            rowSpacing: 10
            rows: 1
            columns: 2
            anchors.fill: parent

            Label {
                id: label1
                text: qsTr("From")
                font.bold: true
                Layout.columnSpan: 2
                Layout.alignment: Qt.AlignHCenter
            }

            Label {
                id: label2
                text: qsTr("Street")
            }

            TextField {
                id: fromStreet
                Layout.fillWidth: true
            }

            Label {
                id: label3
                text: qsTr("City")
            }

            TextField {
                id: fromCity
                Layout.fillWidth: true
            }

            Label {
                id: label7
                text: qsTr("Country")
            }

            TextField {
                id: fromCountry
                Layout.fillWidth: true
            }

            Label {
                id: label6
                text: qsTr("To")
                font.bold: true
                Layout.columnSpan: 2
                Layout.alignment: Qt.AlignHCenter
            }

            Label {
                id: label4
                text: qsTr("Street")
            }

            TextField {
                id: toStreet
                Layout.fillWidth: true
            }

            Label {
                id: label5
                text: qsTr("City")
            }

            TextField {
                id: toCity
                Layout.fillWidth: true
            }

            Label {
                id: label8
                text: qsTr("Country")
            }

            TextField {
                id: toCountry
                Layout.fillWidth: true
            }

            RowLayout {
                id: rowLayout1
                Layout.columnSpan: 2
                Layout.alignment: Qt.AlignRight

                Button {
                    id: goButton
                    text: qsTr("Proceed")
                }

                Button {
                    id: clearButton
                    text: qsTr("Clear")
                }

                Button {
                    id: cancelButton
                    text: qsTr("Cancel")
                }
            }

            Item {
                    Layout.fillHeight: true
                    Layout.columnSpan: 2
            }
        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtPositioning

RouteCoordinateForm {
    property variant toCoordinate
    property variant fromCoordinate
    signal showRoute(variant startCoordinate,variant endCoordinate)
    signal closeForm()

    goButton.onClicked: {
        var startCoordinate = QtPositioning.coordinate(parseFloat(fromLatitude.text),
                                                       parseFloat(fromLongitude.text));
        var endCoordinate = QtPositioning.coordinate(parseFloat(toLatitude.text),
                                                     parseFloat(toLongitude.text));
        if (startCoordinate.isValid && endCoordinate.isValid) {
           goButton.enabled = false;
           showRoute(startCoordinate,endCoordinate)
        }
    }

    clearButton.onClicked: {
        fromLatitude.text = ""
        fromLongitude.text = ""
        toLatitude.text = ""
        toLongitude.text  = ""
    }

    cancelButton.onClicked: {
        closeForm()
    }

    Component.onCompleted: {
        fromLatitude.text = "" + fromCoordinate.latitude
        fromLongitude.text = "" + fromCoordinate.longitude
        toLatitude.text = "" + toCoordinate.latitude
        toLongitude.text = "" + toCoordinate.longitude
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause


import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Item {
    property alias fromLatitude: fromLatitude
    property alias fromLongitude: fromLongitude
    property alias toLatitude: toLatitude
    property alias toLongitude: toLongitude
    property alias clearButton: clearButton
    property alias goButton: goButton
    property alias cancelButton: cancelButton

    Rectangle {
        id: tabRectangle
        y: 20
        height: tabTitle.height * 2
        color: "#46a2da"
        anchors.rightMargin: 0
        anchors.leftMargin: 0
        anchors.left: parent.left
        anchors.right: parent.right

        Label {
            id: tabTitle
            color: "#ffffff"
            text: qsTr("Route Coordinates")
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }

    Item {
        id: item2
        anchors.rightMargin: 20
        anchors.leftMargin: 20
        anchors.bottomMargin: 20
        anchors.topMargin: 20
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.top: tabRectangle.bottom

        GridLayout {
            id: gridLayout3
            rowSpacing: 10
            rows: 1
            columns: 2
            anchors.fill: parent

            Label {
                id: label1
                text: qsTr("From")
                font.bold: true
                Layout.columnSpan: 2
                Layout.alignment: Qt.AlignHCenter
            }

            Label {
                id: label2
                text: qsTr("Latitude")
            }

            TextField {
                id: fromLatitude
                Layout.fillWidth: true
            }

            Label {
                id: label3
                text: qsTr("Longitude")
            }

            TextField {
                id: fromLongitude
                Layout.fillWidth: true
            }

            Label {
                id: label6
                text: qsTr("To")
                font.bold: true
                Layout.columnSpan: 2
                Layout.alignment: Qt.AlignHCenter
            }

            Label {
                id: label4
                text: qsTr("Latitude")
            }

            TextField {
                id: toLatitude
                Layout.fillWidth: true
            }

            Label {
                id: label5
                text: qsTr("Longitude")
            }

            TextField {
                id: toLongitude
                Layout.fillWidth: true
            }

            RowLayout {
                id: rowLayout1
                Layout.columnSpan: 2
                Layout.alignment: Qt.AlignRight
                Button {
                    id: goButton
                    text: qsTr("Proceed")
                }

                Button {
                    id: clearButton
                    text: qsTr("Clear")
                }

                Button {
                    id: cancelButton
                    text: qsTr("Cancel")
                }
            }
            Item {
                Layout.fillHeight: true
                Layout.columnSpan: 2
            }
        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls
import "../helper.js" as Helper

//! [routeinfomodel0]
ListView {
//! [routeinfomodel0]
    property variant routeModel
    property string totalTravelTime
    property string totalDistance
    signal closeForm()
//! [routeinfomodel1]
    interactive: true
    model: ListModel { id: routeInfoModel }
    header: RouteListHeader {}
    delegate:  RouteListDelegate{
        routeIndex.text: index + 1
        routeInstruction.text: instruction
        routeDistance.text: distance
    }
//! [routeinfomodel1]
    footer: Button {
        anchors.horizontalCenter: parent.horizontalCenter
        text: qsTr("Close")
        onClicked: {
            closeForm()
        }
    }

    Component.onCompleted: {
        //! [routeinfomodel2]
        routeInfoModel.clear()
        if (routeModel.count > 0) {
            for (var i = 0; i < routeModel.get(0).segments.length; i++) {
                routeInfoModel.append({
                    "instruction": routeModel.get(0).segments[i].maneuver.instructionText,
                     "distance": Helper.formatDistance(routeModel.get(0).segments[i].maneuver.distanceToNextInstruction)
                });
            }
        }
        //! [routeinfomodel2]
        totalTravelTime = routeModel.count == 0 ? "" : Helper.formatTime(routeModel.get(0).travelTime)
        totalDistance = routeModel.count == 0 ? "" : Helper.formatDistance(routeModel.get(0).distance)
    }
//! [routeinfomodel3]
}
//! [routeinfomodel3]
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Item {
    id: root
    property bool checked: false
    property alias routeInstruction: instructionLabel
    property alias routeDistance: distanceLabel
    property alias routeIndex: indexLabel

    width: appWindow.width
    height: indexLabel.height * 2

    RowLayout {
        spacing: 10
        anchors.left: parent.left
        anchors.leftMargin: 30
        anchors.verticalCenter: parent.verticalCenter
        Label {
            id: indexLabel
        }
        Label {
            id: instructionLabel
            wrapMode: Text.Wrap
        }
        Label {
            id: distanceLabel
        }
    }

    Rectangle {
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.margins: 15
        height: 1
        color: "#46a2da"
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls

Item {
    property alias travelTime: travelTimeLabel
    property alias distance: distanceLabel
    width: parent.width
    height: tabTitle.height * 3.0

    Rectangle {
        id: tabRectangle
        y:  tabTitle.height
        height: tabTitle.height * 2 - 1
        color: "#46a2da"
        anchors.left: parent.left
        anchors.right: parent.right

        Label {
            id: tabTitle
            color: "#ffffff"
            text: qsTr("Route Information")
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenter: parent.horizontalCenter
        }

        Label {
            id: travelTimeLabel
            text: totalTravelTime
            color: "#ffffff"
            font.bold: true
            anchors.left: parent.left
            anchors.verticalCenter: parent.verticalCenter
        }

        Label {
            id: distanceLabel
            text: totalDistance
            color: "#ffffff"
            font.bold: true
            anchors.right: parent.right
            anchors.verticalCenter: parent.verticalCenter
        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
import QtLocation
import QtPositioning
import "../helper.js" as Helper

//! [top]
MapView {
    id: view
//! [top]
    property variant markers
    property variant mapItems
    property int markerCounter: 0 // counter for total amount of markers. Resets to 0 when number of markers = 0
    property int currentMarker
    property bool followme: false
    property variant scaleLengths: [5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000]
    property alias routeQuery: routeQuery
    property alias routeModel: routeModel
    property alias geocodeModel: geocodeModel
    property alias slidersExpanded: sliders.expanded

    signal showGeocodeInfo()
    signal geocodeFinished()
    signal routeError()
    signal coordinatesCaptured(double latitude, double longitude)
    signal showMainMenu(variant coordinate)
    signal showMarkerMenu(variant coordinate)
    signal showRouteMenu(variant coordinate)
    signal showPointMenu(variant coordinate)
    signal showRouteList()

    function geocodeMessage()
    {
        var street, district, city, county, state, countryCode, country, postalCode, latitude, longitude, text
        latitude = Math.round(geocodeModel.get(0).coordinate.latitude * 10000) / 10000
        longitude =Math.round(geocodeModel.get(0).coordinate.longitude * 10000) / 10000
        street = geocodeModel.get(0).address.street
        district = geocodeModel.get(0).address.district
        city = geocodeModel.get(0).address.city
        county = geocodeModel.get(0).address.county
        state = geocodeModel.get(0).address.state
        countryCode = geocodeModel.get(0).address.countryCode
        country = geocodeModel.get(0).address.country
        postalCode = geocodeModel.get(0).address.postalCode

        text = "<b>Latitude:</b> " + latitude + "<br/>"
        text +="<b>Longitude:</b> " + longitude + "<br/>" + "<br/>"
        if (street) text +="<b>Street: </b>"+ street + " <br/>"
        if (district) text +="<b>District: </b>"+ district +" <br/>"
        if (city) text +="<b>City: </b>"+ city + " <br/>"
        if (county) text +="<b>County: </b>"+ county + " <br/>"
        if (state) text +="<b>State: </b>"+ state + " <br/>"
        if (countryCode) text +="<b>Country code: </b>"+ countryCode + " <br/>"
        if (country) text +="<b>Country: </b>"+ country + " <br/>"
        if (postalCode) text +="<b>PostalCode: </b>"+ postalCode + " <br/>"
        return text
    }

    function calculateScale()
    {
        var coord1, coord2, dist, text, f
        f = 0
        coord1 = view.map.toCoordinate(Qt.point(0,scale.y))
        coord2 = view.map.toCoordinate(Qt.point(0+scaleImage.sourceSize.width,scale.y))
        dist = Math.round(coord1.distanceTo(coord2))

        if (dist === 0) {
            // not visible
        } else {
            for (var i = 0; i < scaleLengths.length-1; i++) {
                if (dist < (scaleLengths[i] + scaleLengths[i+1]) / 2 ) {
                    f = scaleLengths[i] / dist
                    dist = scaleLengths[i]
                    break;
                }
            }
            if (f === 0) {
                f = dist / scaleLengths[i]
                dist = scaleLengths[i]
            }
        }

        text = Helper.formatDistance(dist)
        scaleImage.width = (scaleImage.sourceSize.width * f) - 2 * scaleImageLeft.sourceSize.width
        scaleText.text = text
    }

    function deleteMarkers()
    {
        var count = view.markers.length
        for (var i = count-1; i>=0; i--){
            view.map.removeMapItem(view.markers[i])
        }
        view.markers = []
    }

    function addMarker()
    {
        var count = view.markers.length
        markerCounter++
        var marker = Qt.createQmlObject ('Marker {}', map)
        view.map.addMapItem(marker)
        marker.z = view.map.z+1
        marker.coordinate = tapHandler.lastCoordinate
        markers.push(marker)
    }

    function deleteMarker(index)
    {
        //update list of markers
        var myArray = []
        var count = view.markers.length
        for (var i = 0; i<count; i++){
            if (index !== i) myArray.push(view.markers[i])
        }

        view.map.removeMapItem(view.markers[index])
        view.markers[index].destroy()
        view.markers = myArray
        if (markers.length === 0) markerCounter = 0
    }

    function calculateMarkerRoute()
    {
        routeQuery.clearWaypoints();
        for (var i = currentMarker; i< view.markers.length; i++){
            routeQuery.addWaypoint(markers[i].coordinate)
        }
        routeQuery.travelModes = RouteQuery.CarTravel
        routeQuery.routeOptimizations = RouteQuery.ShortestRoute

        routeModel.update();
    }

    function calculateCoordinateRoute(startCoordinate, endCoordinate)
    {
        //! [routerequest0]
        // clear away any old data in the query
        routeQuery.clearWaypoints();
        // add the start and end coords as waypoints on the route
        routeQuery.addWaypoint(startCoordinate)
        routeQuery.addWaypoint(endCoordinate)
        routeQuery.travelModes = RouteQuery.CarTravel
        routeQuery.routeOptimizations = RouteQuery.FastestRoute
        //! [routerequest0]

        //! [routerequest1]
        routeModel.update();
        //! [routerequest1]

        //! [routerequest2]
        // center the map on the start coord
        view.map.center = startCoordinate;
        //! [routerequest2]
    }

    function geocode(fromAddress)
    {
        //! [geocode1]
        // send the geocode request
        geocodeModel.query = fromAddress
        geocodeModel.update()
        //! [geocode1]
    }


//! [coord]
    map.zoomLevel: (maximumZoomLevel - minimumZoomLevel)/2
    map.center {
        // The Qt Company in Oslo
        latitude: 59.9485
        longitude: 10.7686
    }
//! [coord]

    focus: true
    map.onCopyrightLinkActivated: Qt.openUrlExternally(link)

    map.onCenterChanged:{
        scaleTimer.restart()
        if (view.followme)
            if (view.map.center != positionSource.position.coordinate) view.followme = false
    }

    map.onZoomLevelChanged:{
        scaleTimer.restart()
        if (view.followme) view.map.center = positionSource.position.coordinate
    }

    onWidthChanged:{
        scaleTimer.restart()
    }

    onHeightChanged:{
        scaleTimer.restart()
    }

    Component.onCompleted: {
        markers = [];
        mapItems = [];
    }

    Keys.onPressed: (event) => {
        if (event.key === Qt.Key_Plus) {
            view.map.zoomLevel++;
        } else if (event.key === Qt.Key_Minus) {
            view.map.zoomLevel--;
        } else if (event.key === Qt.Key_Left || event.key === Qt.Key_Right ||
                   event.key === Qt.Key_Up   || event.key === Qt.Key_Down) {
            var dx = 0;
            var dy = 0;

            switch (event.key) {

            case Qt.Key_Left: dx = view.map.width / 4; break;
            case Qt.Key_Right: dx = -view.map.width / 4; break;
            case Qt.Key_Up: dy = view.map.height / 4; break;
            case Qt.Key_Down: dy = -view.map.height / 4; break;

            }

            var mapCenterPoint = Qt.point(view.map.width / 2.0 - dx, view.map.height / 2.0 - dy);
            view.map.center = view.map.toCoordinate(mapCenterPoint);
        }
    }

    PositionSource{
        id: positionSource
        active: followme

        onPositionChanged: {
            view.map.center = positionSource.position.coordinate
        }
    }

    MapQuickItem {
        id: mePoisition
        parent: view.map
        sourceItem: Rectangle { width: 14; height: 14; color: "#251ee4"; border.width: 2; border.color: "white"; smooth: true; radius: 7 }
        coordinate: positionSource.position.coordinate
        opacity: 1.0
        anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2)
        visible: followme
    }
    MapQuickItem {
        parent: view.map
        sourceItem: Text{
            text: qsTr("You're here!")
            color:"#242424"
            font.bold: true
            styleColor: "#ECECEC"
            style: Text.Outline
        }
        coordinate: positionSource.position.coordinate
        anchorPoint: Qt.point(-mePoisition.sourceItem.width * 0.5, mePoisition.sourceItem.height * 1.5)
        visible: followme
    }


    MapQuickItem {
        id: poiTheQtComapny
        parent: view.map
        sourceItem: Rectangle { width: 14; height: 14; color: "#e41e25"; border.width: 2; border.color: "white"; smooth: true; radius: 7 }
        coordinate {
            latitude: 59.9485
            longitude: 10.7686
        }
        opacity: 1.0
        anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2)
    }

    MapQuickItem {
        parent: view.map
        sourceItem: Text{
            text: "The Qt Company"
            color:"#242424"
            font.bold: true
            styleColor: "#ECECEC"
            style: Text.Outline
        }
        coordinate: poiTheQtComapny.coordinate
        anchorPoint: Qt.point(-poiTheQtComapny.sourceItem.width * 0.5, poiTheQtComapny.sourceItem.height * 1.5)
    }

    MapSliders {
        id: sliders
        z: view.map.z + 3
        mapSource: map
        edge: Qt.LeftEdge
    }

    Item {
        id: scale
        z: view.map.z + 3
        visible: scaleText.text !== "0 m"
        anchors.bottom: parent.bottom;
        anchors.right: parent.right
        anchors.margins: 20
        height: scaleText.height * 2
        width: scaleImage.width

        Image {
            id: scaleImageLeft
            source: "../resources/scale_end.png"
            anchors.bottom: parent.bottom
            anchors.right: scaleImage.left
        }
        Image {
            id: scaleImage
            source: "../resources/scale.png"
            anchors.bottom: parent.bottom
            anchors.right: scaleImageRight.left
        }
        Image {
            id: scaleImageRight
            source: "../resources/scale_end.png"
            anchors.bottom: parent.bottom
            anchors.right: parent.right
        }
        Label {
            id: scaleText
            color: "#004EAE"
            anchors.centerIn: parent
            text: "0 m"
        }
        Component.onCompleted: {
            view.calculateScale();
        }
    }

    //! [routemodel0]
    RouteModel {
        id: routeModel
        plugin : view.map.plugin
        query:  RouteQuery {
            id: routeQuery
        }
        onStatusChanged: {
            if (status == RouteModel.Ready) {
                switch (count) {
                case 0:
                    // technically not an error
                    view.routeError()
                    break
                case 1:
                    view.showRouteList()
                    break
                }
            } else if (status == RouteModel.Error) {
                view.routeError()
            }
        }
    }
    //! [routemodel0]

    //! [routedelegate0]
    Component {
        id: routeDelegate

        MapRoute {
            id: route
            route: routeData
            line.color: "#46a2da"
            line.width: 5
            smooth: true
            opacity: 0.8
     //! [routedelegate0]
            TapHandler {
                acceptedButtons: Qt.LeftButton | Qt.RightButton
                onLongPressed: showRouteMenu(view.map.toCoordinate(tapHandler.point.position))
                onSingleTapped: (eventPoint, button) => {
                    if (button === Qt.RightButton)
                        showRouteMenu(view.map.toCoordinate(tapHandler.point.position))
                }
            }
        }
    }

    //! [geocodemodel0]
    GeocodeModel {
        id: geocodeModel
        plugin: view.map.plugin
        onStatusChanged: {
            if ((status == GeocodeModel.Ready) || (status == GeocodeModel.Error))
                view.geocodeFinished()
        }
        onLocationsChanged:
        {
            if (count === 1) {
                view.map.center.latitude = get(0).coordinate.latitude
                view.map.center.longitude = get(0).coordinate.longitude
            }
        }
    }
    //! [geocodemodel0]

    //! [pointdel0]
    Component {
        id: pointDelegate

        MapQuickItem {
            id: point
            parent: view.map
            coordinate: locationData.coordinate

            sourceItem: Image {
                id: pointMarker
                source: "../resources/marker_blue.png"
                //! [pointdel0]

                Text{
                    id: pointText
                    anchors.bottom: pointMarker.top
                    anchors.horizontalCenter: pointMarker.horizontalCenter
                    text: locationData.address.street + ", " + locationData.address.city
                    color:"#242424"
                    font.bold: true
                    styleColor: "#ECECEC"
                    style: Text.Outline
                }

            }
            smooth: true
            autoFadeIn: false
            anchorPoint.x: pointMarker.width/4
            anchorPoint.y: pointMarker.height

            TapHandler {
                onLongPressed: showPointMenu(point.coordinate)
            //! [pointdel1]
            }
        }
    }
    //! [pointdel1]

    //! [routeview0]
    MapItemView {
        parent: view.map
        model: routeModel
        delegate: routeDelegate
    //! [routeview0]
        autoFitViewport: true
    }

    //! [geocodeview]
    MapItemView {
        parent: view.map
        model: geocodeModel
        delegate: pointDelegate
    }
    //! [geocodeview]

    Timer {
        id: scaleTimer
        interval: 100
        running: false
        repeat: false
        onTriggered: view.calculateScale()
    }

    TapHandler {
        id: tapHandler
        property variant lastCoordinate
        acceptedButtons: Qt.LeftButton | Qt.RightButton

        onPressedChanged: (eventPoint, button) => {
            if (pressed) {
                lastCoordinate = view.map.toCoordinate(tapHandler.point.position)
            }
        }

        onSingleTapped: (eventPoint, button) => {
                if (button === Qt.RightButton) {
                    showMainMenu(lastCoordinate)
                }
        }

        onDoubleTapped: (eventPoint, button) => {
            var preZoomPoint = view.map.toCoordinate(eventPoint.position);
            if (button === Qt.LeftButton) {
                view.map.zoomLevel = Math.floor(view.map.zoomLevel + 1)
            } else if (button === Qt.RightButton) {
                view.map.zoomLevel = Math.floor(view.map.zoomLevel - 1)
            }
            var postZoomPoint = view.map.toCoordinate(eventPoint.position);
            var dx = postZoomPoint.latitude - preZoomPoint.latitude;
            var dy = postZoomPoint.longitude - preZoomPoint.longitude;

            view.map.center = QtPositioning.coordinate(view.map.center.latitude - dx,
                                                       view.map.center.longitude - dy);
        }
    }
//! [end]
}
//! [end]
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls

Row {
    id: containerRow

    property var mapSource
    property real fontSize : 14
    property color labelBackground : "transparent"
    property int edge: Qt.RightEdge
    property alias expanded: sliderToggler.checked

    function rightEdge() {
        return (containerRow.edge === Qt.RightEdge);
    }

    layoutDirection: rightEdge() ? Qt.LeftToRight : Qt.RightToLeft
    anchors.top: parent.top
    anchors.bottom: parent.bottom
    anchors.right: rightEdge() ? parent.right : undefined
    anchors.left: rightEdge() ? undefined : parent.left

    AbstractButton {
        id: sliderToggler
        width: 32
        height: 96
        checkable: true
        checked: true
        anchors.verticalCenter: parent.verticalCenter

        transform:  Scale {
                        origin.x: rightEdge() ? 0 : sliderToggler.width / 2
                        xScale: rightEdge() ? 1 : -1
                    }

        background: Rectangle {
            color: "transparent"
        }


        property real shear: 0.333
        property real buttonOpacity: 0.5
        property real mirror : rightEdge() ? 1.0 : -1.0

        Rectangle {
            width: 16
            height: 48
            color: "seagreen"
            antialiasing: true
            opacity: sliderToggler.buttonOpacity
            anchors.top: parent.top
            anchors.left: sliderToggler.checked ?  parent.left : parent.horizontalCenter
            transform: Matrix4x4 {
                property real d : sliderToggler.checked ? 1.0 : -1.0
                matrix:    Qt.matrix4x4(1.0,  d * sliderToggler.shear,    0.0,    0.0,
                                        0.0,    1.0,    0.0,    0.0,
                                        0.0,    0.0,    1.0,    0.0,
                                        0.0,    0.0,    0.0,    1.0)
            }
        }

        Rectangle {
            width: 16
            height: 48
            color: "seagreen"
            antialiasing: true
            opacity: sliderToggler.buttonOpacity
            anchors.top: parent.verticalCenter
            anchors.right: sliderToggler.checked ?  parent.right : parent.horizontalCenter
            transform: Matrix4x4 {
                property real d : sliderToggler.checked ? -1.0 : 1.0
                matrix:    Qt.matrix4x4(1.0,  d * sliderToggler.shear,    0.0,    0.0,
                                        0.0,    1.0,    0.0,    0.0,
                                        0.0,    0.0,    1.0,    0.0,
                                        0.0,    0.0,    0.0,    1.0)
            }
        }
    }

    Rectangle {
        id: sliderContainer
        height: parent.height
        width: sliderRow.width + 10
        visible: sliderToggler.checked
        color: Qt.rgba( 0, 191 / 255.0, 255 / 255.0, 0.07)

        property var labelBorderColor: "transparent"
        property var slidersHeight : sliderContainer.height
                                     - rowSliderValues.height
                                     - rowSliderLabels.height
                                     - sliderColumn.spacing * 2
                                     - sliderColumn.topPadding
                                     - sliderColumn.bottomPadding

        Column {
            id: sliderColumn
            spacing: 10
            topPadding: 16
            bottomPadding: 48
            anchors.centerIn: parent

            // the sliders value labels
            Row {
                id: rowSliderValues
                spacing: sliderRow.spacing
                width: sliderRow.width
                height: 32
                property real entryWidth: zoomSlider.width

                Rectangle{
                    color: labelBackground
                    height: parent.height
                    width: parent.entryWidth
                    border.color: sliderContainer.labelBorderColor
                    Label {
                        id: labelZoomValue
                        text: zoomSlider.value.toFixed(3)
                        font.pixelSize: fontSize
                        rotation: -90
                        anchors.centerIn: parent
                    }
                }
                Rectangle{
                    color: labelBackground
                    height: parent.height
                    width: parent.entryWidth
                    border.color: sliderContainer.labelBorderColor
                    Label {
                        id: labelBearingValue
                        text: bearingSlider.value.toFixed(2)
                        font.pixelSize: fontSize
                        rotation: -90
                        anchors.centerIn: parent
                    }
                }
                Rectangle{
                    color: labelBackground
                    height: parent.height
                    width: parent.entryWidth
                    border.color: sliderContainer.labelBorderColor
                    Label {
                        id: labelTiltValue
                        text: tiltSlider.value.toFixed(2)
                        font.pixelSize: fontSize
                        rotation: -90
                        anchors.centerIn: parent
                    }
                }
                Rectangle{
                    color: labelBackground
                    height: parent.height
                    width: parent.entryWidth
                    border.color: sliderContainer.labelBorderColor
                    Label {
                        id: labelFovValue
                        text: fovSlider.value.toFixed(2)
                        font.pixelSize: fontSize
                        rotation: -90
                        anchors.centerIn: parent
                    }
                }
            } // rowSliderValues

            // The sliders row
            Row {
                id: sliderRow
                height: sliderContainer.slidersHeight

                Slider {
                    id: zoomSlider
                    height: parent.height
                    orientation : Qt.Vertical
                    from : containerRow.mapSource.minimumZoomLevel
                    to : containerRow.mapSource.maximumZoomLevel
                    value : containerRow.mapSource.zoomLevel
                    onValueChanged: {
                            containerRow.mapSource.zoomLevel = value
                    }
                }
                Slider {
                    id: bearingSlider
                    height: parent.height
                    from: 0
                    to: 360
                    orientation : Qt.Vertical
                    value: containerRow.mapSource.bearing
                    onValueChanged: {
                        containerRow.mapSource.bearing = value;
                    }
                }
                Slider {
                    id: tiltSlider
                    height: parent.height
                    orientation : Qt.Vertical
                    from: containerRow.mapSource.minimumTilt;
                    to: containerRow.mapSource.maximumTilt
                    value: containerRow.mapSource.tilt
                    onValueChanged: {
                        containerRow.mapSource.tilt = value;
                    }
                }
                Slider {
                    id: fovSlider
                    height: parent.height
                    orientation : Qt.Vertical
                    from: containerRow.mapSource.minimumFieldOfView
                    to: containerRow.mapSource.maximumFieldOfView
                    value: containerRow.mapSource.fieldOfView
                    onValueChanged: {
                        containerRow.mapSource.fieldOfView = value;
                    }
                }
            } // Row sliders

            // The labels row
            Row {
                id: rowSliderLabels
                spacing: sliderRow.spacing
                width: sliderRow.width
                property real entryWidth: zoomSlider.width
                property real entryHeight: 64

                Rectangle{
                    color: labelBackground
                    height: parent.entryHeight
                    width: parent.entryWidth
                    border.color: sliderContainer.labelBorderColor
                    Label {
                        id: labelZoom
                        text: "Zoom"
                        font.pixelSize: fontSize
                        rotation: -90
                        anchors.centerIn: parent
                    }
                }

                Rectangle{
                    color: labelBackground
                    height: parent.entryHeight
                    width: parent.entryWidth
                    border.color: sliderContainer.labelBorderColor
                    Label {
                        id: labelBearing
                        text: "Bearing"
                        font.pixelSize: fontSize
                        rotation: -90
                        anchors.centerIn: parent
                    }
                }
                Rectangle{
                    color: labelBackground
                    height: parent.entryHeight
                    width: parent.entryWidth
                    border.color: sliderContainer.labelBorderColor
                    Label {
                        id: labelTilt
                        text: "Tilt"
                        font.pixelSize: fontSize
                        rotation: -90
                        anchors.centerIn: parent
                    }
                }
                Rectangle{
                    color: labelBackground
                    height: parent.entryHeight
                    width: parent.entryWidth
                    border.color: sliderContainer.labelBorderColor
                    Label {
                        id: labelFov
                        text: "FoV"
                        font.pixelSize: fontSize
                        rotation: -90
                        anchors.centerIn: parent
                    }
                }
            } // rowSliderLabels
        } // Column
    } // sliderContainer
} // containerRow
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtLocation

//! [mqi-top]
MapQuickItem {
    id: marker
//! [mqi-top]

//! [mqi-anchor]
    anchorPoint.x: image.width/4
    anchorPoint.y: image.height

    HoverHandler {
        id: hoverHandler
    }
    TapHandler {
        id: tapHandler
        acceptedButtons: Qt.RightButton
        gesturePolicy: TapHandler.WithinBounds
        onTapped: {
            mapview.currentMarker = -1
            for (var i = 0; i< mapview.markers.length; i++){
                if (marker == mapview.markers[i]){
                    mapview.currentMarker = i
                    break
                }
            }
            mapview.showMarkerMenu(marker.coordinate)
        }
    }
    DragHandler {
        id: dragHandler
        grabPermissions: PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType
    }

    sourceItem: Image {
        id: image
//! [mqi-anchor]
        source: "../resources/marker.png"
        opacity: hoverHandler.hovered ? 0.6 : 1.0

        Text{
            id: number
            y: image.height/10
            width: image.width
            color: "white"
            font.bold: true
            font.pixelSize: 14
            horizontalAlignment: Text.AlignHCenter
            Component.onCompleted: {
                text = mapview.markerCounter
            }
        }

//! [mqi-closeimage]
    }
//! [mqi-closeimage]

//! [mqi-close]
}
//! [mqi-close]
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtPositioning
import QtLocation

Rectangle{

    function clamp(num, min, max)
    {
      return num < min ? min : num > max ? max : num;
    }

    function minimumScaleFactor()
    {
        var hscalefactor = (400.0 / Math.max(Math.min(mapview.width, 1000), 400)) * 0.5
        var vscalefactor = (400.0 / Math.max(Math.min(mapview.height, 1000), 400)) * 0.5
        return Math.min(hscalefactor,vscalefactor)
    }

    function avgScaleFactor()
    {
        var hscalefactor = (400.0 / Math.max(Math.min(mapview.width, 1000), 400)) * 0.5
        var vscalefactor = (400.0 / Math.max(Math.min(mapview.height, 1000), 400)) * 0.5
        return (hscalefactor+vscalefactor) * 0.5
    }

    id: miniMapRect
    width: Math.floor(mapview.width * avgScaleFactor()) + 2
    height: Math.floor(mapview.height * avgScaleFactor()) + 2
    anchors.right: (parent) ? parent.right : undefined
    anchors.rightMargin: 10
    anchors.top: (parent) ? parent.top : undefined
    anchors.topMargin: 10
    color: "#242424"
    Map {
        id: miniMap
        anchors.top: parent.top
        anchors.topMargin: 1
        anchors.left: parent.left
        anchors.leftMargin: 1
        width: Math.floor(mapview.width * avgScaleFactor())
        height: Math.floor(mapview.height * avgScaleFactor())
        zoomLevel: clamp(mapview.map.zoomLevel - 4.5, 1.0, 5.0) //(map.zoomLevel > minimumZoomLevel + 3) ? minimumZoomLevel + 3 : 1.5
        center: mapview.map.center
        plugin: mapview.map.plugin
        copyrightsVisible: false
        property double mapZoomLevel : mapview.map.zoomLevel

        // cannot use property bindings on map.visibleRegion in MapRectangle because it's non-NOTIFYable
        onCenterChanged: miniMapRectangle.updateCoordinates()
        onMapZoomLevelChanged: miniMapRectangle.updateCoordinates()
        onWidthChanged: miniMapRectangle.updateCoordinates()
        onHeightChanged: miniMapRectangle.updateCoordinates()

        MapRectangle {
            id: miniMapRectangle
            color: "#44ff0000"
            border.width: 1
            border.color: "red"
            autoFadeIn: false

            function getMapVisibleRegion()
            {
                return mapview.map.visibleRegion.boundingGeoRectangle()
            }

            function updateCoordinates()
            {
                topLeft.latitude =  getMapVisibleRegion().topLeft.latitude
                topLeft.longitude=  getMapVisibleRegion().topLeft.longitude
                bottomRight.latitude =  getMapVisibleRegion().bottomRight.latitude
                bottomRight.longitude=  getMapVisibleRegion().bottomRight.longitude
            }
        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls
import QtLocation
import QtPositioning
import "map"
import "menus"
import "helper.js" as Helper

ApplicationWindow {
    id: appWindow
    property variant mapview
    property variant minimap
    property variant plugin
    property variant parameters

    //defaults
    //! [routecoordinate]
    property variant fromCoordinate: QtPositioning.coordinate(59.9483, 10.7695)
    property variant toCoordinate: QtPositioning.coordinate(59.9645, 10.671)
    //! [routecoordinate]

    function createMap(provider)
    {
        if (parameters && parameters.length>0)
            plugin = Qt.createQmlObject ('import QtLocation; Plugin{ name:"' + provider + '"; parameters: appWindow.parameters}', appWindow)
        else
            plugin = Qt.createQmlObject ('import QtLocation; Plugin{ name:"' + provider + '"}', appWindow)

        if (minimap) {
            minimap.destroy()
            minimap = null
        }

        var zoomLevel = null
        var tilt = null
        var bearing = null
        var fov = null
        var center = null
        var panelExpanded = null
        if (mapview) {
            zoomLevel = mapview.zoomLevel
            tilt = mapview.tilt
            bearing = mapview.bearing
            fov = mapview.fieldOfView
            center = mapview.center
            panelExpanded = mapview.slidersExpanded
            mapview.destroy()
        }
        mapview = mapComponent.createObject(page);
        mapview.map.plugin = plugin;

        if (zoomLevel != null) {
            mapview.map.tilt = tilt
            mapview.map.bearing = bearing
            mapview.map.fieldOfView = fov
            mapview.map.zoomLevel = zoomLevel
            mapview.map.center = center
            mapview.map.slidersExpanded = panelExpanded
        } else {
            // Use an integer ZL to enable nearest interpolation, if possible.
            mapview.map.zoomLevel = Math.floor((mapview.map.maximumZoomLevel - mapview.map.minimumZoomLevel)/2)
            // defaulting to 45 degrees, if possible.
            mapview.map.fieldOfView = Math.min(Math.max(45.0, mapview.map.minimumFieldOfView), mapview.maximumFieldOfView)
        }

        mapview.forceActiveFocus()
    }

    function getPlugins()
    {
        var plugin = Qt.createQmlObject ('import QtLocation; Plugin {}', appWindow)
        var myArray = new Array()
        for (var i = 0; i<plugin.availableServiceProviders.length; i++) {
            var tempPlugin = Qt.createQmlObject ('import QtLocation; Plugin {name: "' + plugin.availableServiceProviders[i]+ '"}', appWindow)
            if (tempPlugin.supportsMapping())
                myArray.push(tempPlugin.name)
        }
        myArray.sort()
        return myArray
    }

    function initializeProviders(pluginParameters)
    {
        var parameters = new Array()
        for (var prop in pluginParameters){
            var parameter = Qt.createQmlObject('import QtLocation; PluginParameter{ name: "'+ prop + '"; value: "' + pluginParameters[prop]+'"}',appWindow)
            parameters.push(parameter)
        }
        appWindow.parameters = parameters
        var plugins = getPlugins()
        mainMenu.providerMenu.createMenu(plugins)
        for (var i = 0; i<plugins.length; i++) {
            if (plugins[i] === "osm")
                mainMenu.selectProvider(plugins[i])
        }
    }

    title: qsTr("Mapviewer")
    height: 640
    width: 360
    visible: true
    menuBar: mainMenu

    //! [geocode0]
    Address {
        id :fromAddress
        street: "Sandakerveien 116"
        city: "Oslo"
        country: "Norway"
        state : ""
        postalCode: "0484"
    }
    //! [geocode0]

    Address {
        id: toAddress
        street: "Holmenkollveien 140"
        city: "Oslo"
        country: "Norway"
        postalCode: "0791"
    }

    MainMenu {
        id: mainMenu
        plugin: appWindow.plugin

        function toggleMiniMapState()
        {
            console.log("MiniMap with " + plugin)
            if (minimap) {
                minimap.destroy()
                minimap = null
            } else {
                minimap = Qt.createQmlObject ('import "map"; MiniMap{ z: mapview.z + 2 }', mapview)
            }
        }

        function setLanguage(lang)
        {
            mapview.map.plugin.locales = lang;
            stackView.pop(page)
        }

        onSelectProvider: (providerName) => {
            stackView.pop()
            for (var i = 0; i < providerMenu.count; i++) {
                providerMenu.actionAt(i).checked = providerMenu.actionAt(i).text === providerName
            }

            createMap(providerName)
            if (mapview.error === mapview.NoError) {
                selectMapType(mapview.map.activeMapType)
            } else {
                mainMenu.clearMenu(mapTypeMenu)
            }
        }

        onSelectMapType: (mapType) => {
            stackView.pop(page)
            for (var i = 0; i < mapTypeMenu.count; i++) {
                mapTypeMenu.actionAt(i).checked = mapTypeMenu.actionAt(i).text === mapType.name
            }
            mapview.map.activeMapType = mapType
        }


        onSelectTool: (tool) => {
            switch (tool) {
            case "AddressRoute":
                stackView.pop({item:page, immediate: true})
                stackView.push("forms/RouteAddress.qml" ,
                                   { "plugin": mapview.map.plugin,
                                       "toAddress": toAddress,
                                       "fromAddress": fromAddress})
                stackView.currentItem.showRoute.connect(mapview.calculateCoordinateRoute)
                stackView.currentItem.showMessage.connect(stackView.showMessage)
                stackView.currentItem.closeForm.connect(stackView.closeForm)
                break
            case "CoordinateRoute":
                stackView.pop({item:page, immediate: true})
                stackView.push("forms/RouteCoordinate.qml" ,
                                    { "toCoordinate": toCoordinate,
                                       "fromCoordinate": fromCoordinate})
                stackView.currentItem.showRoute.connect(mapview.calculateCoordinateRoute)
                stackView.currentItem.closeForm.connect(stackView.closeForm)
                break
            case "Geocode":
                stackView.pop({item:page, immediate: true})
                stackView.push("forms/Geocode.qml",
                                   { "address": fromAddress})
                stackView.currentItem.showPlace.connect(mapview.geocode)
                stackView.currentItem.closeForm.connect(stackView.closeForm)
                break
            case "RevGeocode":
                stackView.pop({item:page, immediate: true})
                stackView.push("forms/ReverseGeocode.qml",
                                    { "coordinate": fromCoordinate })
                stackView.currentItem.showPlace.connect(mapview.geocode)
                stackView.currentItem.closeForm.connect(stackView.closeForm)
                break
            case "Language":
                stackView.pop({item:page, immediate: true})
                stackView.push("forms/Locale.qml",
                                   { "locale":  mapview.map.plugin.locales[0]})
                stackView.currentItem.selectLanguage.connect(setLanguage)
                stackView.currentItem.closeForm.connect(stackView.closeForm)
                break
            case "Clear":
                mapview.map.clearData()
                break
            case "Prefetch":
                mapview.map.prefetchData()
                break
            default:
                console.log("Unsupported operation")
            }
        }

        onToggleMapState: (state) => {
            stackView.pop(page)
            switch (state) {
            case "FollowMe":
                mapview.followme = !mapview.followme
                break
            case "MiniMap":
                toggleMiniMapState()
                isMiniMap = minimap
                break
            default:
                console.log("Unsupported operation")
            }
        }
    }

    MapPopupMenu {
        id: mapPopupMenu

        function show(coordinate)
        {
            stackView.pop(page)
            mapPopupMenu.coordinate = coordinate
            mapPopupMenu.markersCount = mapview.markers.length
            mapPopupMenu.mapItemsCount = mapview.mapItems.length
            mapPopupMenu.popup()
        }

        onItemClicked: (item) => {
            stackView.pop(page)
            switch (item) {
            case "addMarker":
                mapview.addMarker()
                break
            case "getCoordinate":
                mapview.coordinatesCaptured(coordinate.latitude, coordinate.longitude)
                break
            case "fitViewport":
                mapview.map.fitViewportToMapItems()
                break
            case "deleteMarkers":
                mapview.deleteMarkers()
                break
            default:
                console.log("Unsupported operation:", item)
            }
        }
    }

    MarkerPopupMenu {
        id: markerPopupMenu

        function show(coordinate)
        {
            stackView.pop(page)
            markerPopupMenu.markersCount = mapview.markers.length
            markerPopupMenu.currentMarker = mapview.currentMarker
            markerPopupMenu.popup()
        }

        function askForCoordinate()
        {
            stackView.push("forms/ReverseGeocode.qml",
                                { "title": qsTr("New Coordinate"),
                                   "coordinate":   mapview.markers[mapview.currentMarker].coordinate})
            stackView.currentItem.showPlace.connect(moveMarker)
            stackView.currentItem.closeForm.connect(stackView.closeForm)
        }

        function moveMarker(coordinate)
        {
            mapview.markers[mapview.currentMarker].coordinate = coordinate;
            mapview.map.center = coordinate;
            stackView.pop(page)
        }

        onItemClicked: (item) => {
            stackView.pop(page)
            switch (item) {
            case "deleteMarker":
                mapview.deleteMarker(mapview.currentMarker)
                break;
            case "getMarkerCoordinate":
                mapview.coordinatesCaptured(mapview.markers[mapview.currentMarker].coordinate.latitude,
                                            mapview.markers[mapview.currentMarker].coordinate.longitude)
                break;
            case "moveMarkerTo":
                askForCoordinate()
                break;
            case "routeToNextPoint":
            case "routeToNextPoints":
                mapview.calculateMarkerRoute()
                break
            case "distanceToNextPoint":
                var coordinate1 = mapview.markers[mapview.currentMarker].coordinate;
                var coordinate2 = mapview.markers[mapview.currentMarker+1].coordinate;
                var distance = Helper.formatDistance(coordinate1.distanceTo(coordinate2));
                stackView.showMessage(qsTr("Distance"),"<b>" + qsTr("Distance:") + "</b> " + distance)
                break
            default:
                console.log("Unsupported operation:", item)
            }
        }
    }

    ItemPopupMenu {
        id: itemPopupMenu

        function show(type,coordinate)
        {
            stackView.pop(page)
            itemPopupMenu.type = type
            itemPopupMenu.popup()
        }

        onItemClicked: {
            stackView.pop(page)
            switch (item) {
            case "showRouteInfo":
                stackView.showRouteListPage()
                break;
            case "deleteRoute":
                mapview.routeModel.reset();
                break;
            case "showPointInfo":
                mapview.showGeocodeInfo()
                break;
            case "deletePoint":
                geocodeModel.reset()
                break;
            default:
                console.log("Unsupported operation")
            }
        }
    }

    StackView {
        id: stackView
        anchors.fill: parent
        focus: true
        initialItem: Item {
            id: page

            Text {
                visible: !supportsSsl && map && mapview.activeMapType && activeMapType.metadata.isHTTPS
                text: "The active map type\n
requires (missing) SSL\n
support"
                horizontalAlignment: Text.AlignHCenter
                font.pixelSize: appWindow.width / 12
                font.bold: true
                color: "grey"
                anchors.centerIn: parent
                z: 12
            }
        }

        function showMessage(title,message,backPage)
        {
            push("forms/Message.qml",
                               {
                                   "title" : title,
                                   "message" : message,
                                   "backPage" : backPage
                               })
            currentItem.closeForm.connect(closeMessage)
        }

        function closeMessage(backPage)
        {
            pop(backPage)
        }

        function closeForm()
        {
            pop(page)
        }

        function showRouteListPage()
        {
            push("forms/RouteList.qml",
                               {
                                   "routeModel" : mapview.routeModel
                               })
            currentItem.closeForm.connect(closeForm)
        }
    }

    Component {
        id: mapComponent

        MapComponent {
            width: page.width
            height: page.height
            onFollowmeChanged: mainMenu.isFollowMe = followme
            map.onSupportedMapTypesChanged: mainMenu.mapTypeMenu.createMenu(map)
            onCoordinatesCaptured: (latitude, longitude) => {
                var text = "<b>" + qsTr("Latitude:") + "</b> " + Helper.roundNumber(latitude,4) + "<br/><b>" + qsTr("Longitude:") + "</b> " + Helper.roundNumber(longitude,4)
                stackView.showMessage(qsTr("Coordinates"),text);
            }
            onGeocodeFinished:{
                if (geocodeModel.status == GeocodeModel.Ready) {
                    if (geocodeModel.count == 0) {
                        stackView.showMessage(qsTr("Geocode Error"),qsTr("Unsuccessful geocode"))
                    } else if (geocodeModel.count > 1) {
                        stackView.showMessage(qsTr("Ambiguous geocode"), geocodeModel.count + " " +
                                              qsTr("results found for the given address, please specify location"))
                    } else {
                        stackView.showMessage(qsTr("Location"), geocodeMessage(),page)
                    }
                } else if (geocodeModel.status == GeocodeModel.Error) {
                    stackView.showMessage(qsTr("Geocode Error"),qsTr("Unsuccessful geocode"))
                }
            }
            onRouteError: stackView.showMessage(qsTr("Route Error"),qsTr("Unable to find a route for the given points"),page)

            onShowGeocodeInfo: stackView.showMessage(qsTr("Location"),geocodeMessage(),page)

            map.onErrorChanged: {
                if (map.error != mapview.NoError) {
                    var title = qsTr("ProviderError")
                    var message =  mapview.errorString + "<br/><br/><b>" + qsTr("Try to select other provider") + "</b>"
                    if (map.error == mapview.MissingRequiredParameterError)
                        message += "<br/>" + qsTr("or see") + " \'mapviewer --help\' "
                                + qsTr("how to pass plugin parameters.")
                    stackView.showMessage(title,message);
                }
            }
            onShowMainMenu: (coordinate) => mapPopupMenu.show(coordinate)
            onShowMarkerMenu: (coordinate) => markerPopupMenu.show(coordinate)
            onShowRouteMenu: (coordinate) => itemPopupMenu.show("Route",coordinate)
            onShowPointMenu: (coordinate) => itemPopupMenu.show("Point",coordinate)
            onShowRouteList: stackView.showRouteListPage()

            TapHandler {
                onTapped: {
                }
            }
        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls

Menu {
    property variant type
    signal itemClicked(string item)

    MenuItem {
        text: qsTr("Info")
        onTriggered: itemClicked("show" + type + "Info")
    }
    MenuItem {
        text: qsTr("Delete")
        onTriggered: itemClicked("delete" + type)
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls
import QtLocation

MenuBar {
    id: menuBar
    property variant providerMenu: providerMenu
    property variant mapTypeMenu: mapTypeMenu
    property variant toolsMenu: toolsMenu
    property variant plugin
    property alias isFollowMe: toolsMenu.isFollowMe
    property alias isMiniMap: toolsMenu.isMiniMap

    signal selectProvider(string providerName)
    signal selectMapType(variant mapType)
    signal selectTool(string tool);
    signal toggleMapState(string state)

    function clearMenu(menu)
    {
        while (menu.count)
            menu.removeItem(menu.itemAt(0))
    }

    Menu {
        id: providerMenu
        title: qsTr("Provider")

        function createMenu(plugins)
        {
            clearMenu(providerMenu)
            for (var i = 0; i < plugins.length; i++) {
                createProviderMenuItem(plugins[i]);
            }
        }

        function createProviderMenuItem(provider)
        {
            var action = Qt.createQmlObject('import QtQuick.Controls; Action{ text: "' + provider + '"; checkable: true; onTriggered: function(){selectProvider("' + provider + '")} }', providerMenu)
            addAction(action)
        }
    }

    Menu {
        id: mapTypeMenu
        title: qsTr("MapType")

        Component {
            id: mapTypeMenuActionComponent
            Action {

            }
        }
        function createMenu(map)
        {
            clearMenu(mapTypeMenu)
            for (var i = 0; i<map.supportedMapTypes.length; i++) {
                createMapTypeMenuItem(map.supportedMapTypes[i], map.activeMapType === map.supportedMapTypes[i]);
            }
        }

        function createMapTypeMenuItem(mapType, checked)
        {
            var action = mapTypeMenuActionComponent.createObject(mapTypeMenu, { text: mapType.name, checkable: true, checked: checked })
            action.triggered.connect(function(){selectMapType(mapType)})
            addAction(action)
        }
    }

    Menu {
        id: toolsMenu
        property bool isFollowMe: false;
        property bool isMiniMap: false;
        property variant plugin: menuBar.plugin

        title: qsTr("Tools")

        Action {
            text: qsTr("Reverse geocode")
            enabled: plugin ? plugin.supportsGeocoding(Plugin.ReverseGeocodingFeature) : false
            onTriggered: selectTool("RevGeocode")
        }
        MenuItem {
            text: qsTr("Geocode")
            enabled: plugin ? plugin.supportsGeocoding() : false
            onTriggered: selectTool("Geocode")
        }
        MenuItem {
            text: qsTr("Route with coordinates")
            enabled: plugin ? plugin.supportsRouting() : false
            onTriggered: selectTool("CoordinateRoute")
        }
        MenuItem {
            text: qsTr("Route with address")
            enabled: plugin ? plugin.supportsRouting() : false
            onTriggered: selectTool("AddressRoute")
        }
        MenuItem {
            text: isMiniMap ? qsTr("Hide minimap") : qsTr("Minimap")
            onTriggered: toggleMapState("MiniMap")
        }
        MenuItem {
            text: isFollowMe ? qsTr("Stop following") : qsTr("Follow me")
            onTriggered: toggleMapState("FollowMe")
        }
        MenuItem {
            text: qsTr("Language")
            onTriggered: selectTool("Language")
        }
        MenuItem {
            text: qsTr("Prefetch Map Data")
            onTriggered: selectTool("Prefetch")
        }
        MenuItem {
            text: qsTr("Clear Map Data")
            onTriggered: selectTool("Clear")
        }
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls

Menu {
    property variant coordinate
    property int markersCount
    property int mapItemsCount
    signal itemClicked(string item)

    MenuItem {
        text: qsTr("Add Marker")
        onTriggered: itemClicked("addMarker")
    }
    MenuItem {
        text: qsTr("Get coordinate")
        onTriggered: itemClicked("getCoordinate")
    }
    MenuItem {
        text: qsTr("Fit Viewport To Markers")
        onTriggered: itemClicked("fitViewport")
    }
    MenuItem {
        text: qsTr("Delete all markers")
        enabled: markersCount > 0
        onTriggered: itemClicked("deleteMarkers")
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

import QtQuick
import QtQuick.Controls

Menu {
    property int currentMarker
    property int markersCount
    signal itemClicked(string item)

    MenuItem {
        text: qsTr("Delete")
        onTriggered: itemClicked("deleteMarker")
    }
    MenuItem {
        text: qsTr("Coordinates")
        onTriggered: itemClicked("getMarkerCoordinate")
    }
    MenuItem {
        text: qsTr("Move to")
        onTriggered: itemClicked("moveMarkerTo")
    }
    MenuItem {
        text: currentMarker < markersCount-2 ? qsTr("Route to next markers")
                                             : qsTr("Route to next marker")
        enabled: currentMarker <= markersCount - 2
        onTriggered: currentMarker < markersCount-2 ? itemClicked("routeToNextPoints")
                                                    : itemClicked("routeToNextPoint")
    }
    MenuItem {
        text: currentMarker < markersCount-2 ? qsTr("Distance to next markers")
                                             : qsTr("Distance to next marker")
        enabled: currentMarker <= markersCount - 2
        onTriggered: currentMarker < markersCount-2 ? itemClicked("distanceToNextPoints")
                                                    : itemClicked("distanceToNextPoint")
    }
}