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.

# 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")
}
}