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, 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(Path(__file__).parent)
    engine.loadFromModule("MapViewer", "Main")
    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

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")
    }
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

.pragma library

function roundNumber(number, digits)
{
    var multiple = Math.pow(10, digits);
    return Math.round(number * multiple) / multiple;
}

function formatTime(sec)
{
    var value = sec
    var seconds = value % 60
    value /= 60
    value = (value > 1) ? Math.round(value) : 0
    var minutes = value % 60
    value /= 60
    value = (value > 1) ? Math.round(value) : 0
    var hours = value
    if (hours > 0) value = hours + "h:"+ minutes + "m"
    else value = minutes + "min"
    return value
}

function formatDistance(meters)
{
    var dist = Math.round(meters)
    if (dist > 1000 ){
        if (dist > 100000){
            dist = Math.round(dist / 1000)
        }
        else{
            dist = Math.round(dist / 100)
            dist = dist / 10
        }
        dist = dist + " km"
    }
    else{
        dist = dist + " m"
    }
    return dist
}
// 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 MapViewer

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: {
                }
            }
        }
    }
}
module MapViewer
typeinfo mapviewer.qmltypes
Main 1.0 Main.qml
Helper 1.0 helper.js
MapComponent 1.0 map/MapComponent.qml
MapSliders 1.0 map/MapSliders.qml
Marker 1.0 map/Marker.qml
MiniMap 1.0 map/MiniMap.qml
ItemPopupMenu 1.0 menus/ItemPopupMenu.qml
MainMenu 1.0 menus/MainMenu.qml
MapPopupMenu 1.0 menus/MapPopupMenu.qml
MarkerPopupMenu 1.0 menus/MarkerPopupMenu.qml
Geocode 1.0 forms/Geocode.qml
GeocodeForm 1.0 forms/GeocodeForm.ui.qml
Message 1.0 forms/Message.qml
MessageForm 1.0 forms/MessageForm.ui.qml
ReverseGeocode 1.0 forms/ReverseGeocode.qml
ReverseGeocodeForm 1.0 forms/ReverseGeocodeForm.ui.qml
RouteCoordinate 1.0 forms/RouteCoordinate.qml
Locale 1.0 forms/Locale.qml
LocaleForm 1.0 forms/LocaleForm.ui.qml
RouteAddress 1.0 forms/RouteAddress.qml
RouteAddressForm 1.0 forms/RouteAddressForm.ui.qml
RouteCoordinateForm 1.0 forms/RouteCoordinateForm.ui.qml
RouteList 1.0 forms/RouteList.qml
RouteListDelegate 1.0 forms/RouteListDelegate.qml
RouteListHeader 1.0 forms/RouteListHeader.qml