diff options
Diffstat (limited to 'examples/location')
34 files changed, 2934 insertions, 0 deletions
diff --git a/examples/location/mapviewer/MapViewer/Main.qml b/examples/location/mapviewer/MapViewer/Main.qml new file mode 100644 index 000000000..f4ae7ea04 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/Main.qml @@ -0,0 +1,460 @@ +// 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: { + } + } + } + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/Geocode.qml b/examples/location/mapviewer/MapViewer/forms/Geocode.qml new file mode 100644 index 000000000..885357dd3 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/Geocode.qml @@ -0,0 +1,42 @@ +// 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 + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/GeocodeForm.ui.qml b/examples/location/mapviewer/MapViewer/forms/GeocodeForm.ui.qml new file mode 100644 index 000000000..cb56370ea --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/GeocodeForm.ui.qml @@ -0,0 +1,136 @@ +// 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 + } + } + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/Locale.qml b/examples/location/mapviewer/MapViewer/forms/Locale.qml new file mode 100644 index 000000000..9ba7dd7f0 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/Locale.qml @@ -0,0 +1,45 @@ +// 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 + } + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/LocaleForm.ui.qml b/examples/location/mapviewer/MapViewer/forms/LocaleForm.ui.qml new file mode 100644 index 000000000..9e1ec1807 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/LocaleForm.ui.qml @@ -0,0 +1,116 @@ +// 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 + } + + + } + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/Message.qml b/examples/location/mapviewer/MapViewer/forms/Message.qml new file mode 100644 index 000000000..583bc2dda --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/Message.qml @@ -0,0 +1,21 @@ +// 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 + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/MessageForm.ui.qml b/examples/location/mapviewer/MapViewer/forms/MessageForm.ui.qml new file mode 100644 index 000000000..426c72757 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/MessageForm.ui.qml @@ -0,0 +1,69 @@ +// 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 + } + } + } +} + diff --git a/examples/location/mapviewer/MapViewer/forms/ReverseGeocode.qml b/examples/location/mapviewer/MapViewer/forms/ReverseGeocode.qml new file mode 100644 index 000000000..31122a2e9 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/ReverseGeocode.qml @@ -0,0 +1,38 @@ +// 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; + } + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/ReverseGeocodeForm.ui.qml b/examples/location/mapviewer/MapViewer/forms/ReverseGeocodeForm.ui.qml new file mode 100644 index 000000000..1d937ee90 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/ReverseGeocodeForm.ui.qml @@ -0,0 +1,103 @@ +// 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 + } + } + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/RouteAddress.qml b/examples/location/mapviewer/MapViewer/forms/RouteAddress.qml new file mode 100644 index 000000000..3676c1374 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/RouteAddress.qml @@ -0,0 +1,105 @@ +// 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; + } + } + } + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/RouteAddressForm.ui.qml b/examples/location/mapviewer/MapViewer/forms/RouteAddressForm.ui.qml new file mode 100644 index 000000000..ee9227013 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/RouteAddressForm.ui.qml @@ -0,0 +1,160 @@ +// 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 + } + } + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/RouteCoordinate.qml b/examples/location/mapviewer/MapViewer/forms/RouteCoordinate.qml new file mode 100644 index 000000000..003556c51 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/RouteCoordinate.qml @@ -0,0 +1,41 @@ +// 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 + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/RouteCoordinateForm.ui.qml b/examples/location/mapviewer/MapViewer/forms/RouteCoordinateForm.ui.qml new file mode 100644 index 000000000..88ff94dc1 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/RouteCoordinateForm.ui.qml @@ -0,0 +1,136 @@ +// 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 + } + } + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/RouteList.qml b/examples/location/mapviewer/MapViewer/forms/RouteList.qml new file mode 100644 index 000000000..8dbda7c01 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/RouteList.qml @@ -0,0 +1,50 @@ +// 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] diff --git a/examples/location/mapviewer/MapViewer/forms/RouteListDelegate.qml b/examples/location/mapviewer/MapViewer/forms/RouteListDelegate.qml new file mode 100644 index 000000000..680318ac3 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/RouteListDelegate.qml @@ -0,0 +1,42 @@ +// 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" + } +} diff --git a/examples/location/mapviewer/MapViewer/forms/RouteListHeader.qml b/examples/location/mapviewer/MapViewer/forms/RouteListHeader.qml new file mode 100644 index 000000000..4f8308091 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/forms/RouteListHeader.qml @@ -0,0 +1,47 @@ +// 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 + } + } +} diff --git a/examples/location/mapviewer/MapViewer/helper.js b/examples/location/mapviewer/MapViewer/helper.js new file mode 100644 index 000000000..a42040518 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/helper.js @@ -0,0 +1,44 @@ +// 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 +} diff --git a/examples/location/mapviewer/MapViewer/map/MapComponent.qml b/examples/location/mapviewer/MapViewer/map/MapComponent.qml new file mode 100644 index 000000000..987455287 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/map/MapComponent.qml @@ -0,0 +1,497 @@ +// 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] diff --git a/examples/location/mapviewer/MapViewer/map/MapSliders.qml b/examples/location/mapviewer/MapViewer/map/MapSliders.qml new file mode 100644 index 000000000..d9c8381b0 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/map/MapSliders.qml @@ -0,0 +1,282 @@ +// 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 diff --git a/examples/location/mapviewer/MapViewer/map/Marker.qml b/examples/location/mapviewer/MapViewer/map/Marker.qml new file mode 100644 index 000000000..c7494cf57 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/map/Marker.qml @@ -0,0 +1,64 @@ +// 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] diff --git a/examples/location/mapviewer/MapViewer/map/MiniMap.qml b/examples/location/mapviewer/MapViewer/map/MiniMap.qml new file mode 100644 index 000000000..f8fc51547 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/map/MiniMap.qml @@ -0,0 +1,78 @@ +// 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 + } + } + } +} diff --git a/examples/location/mapviewer/MapViewer/menus/ItemPopupMenu.qml b/examples/location/mapviewer/MapViewer/menus/ItemPopupMenu.qml new file mode 100644 index 000000000..d559aca6c --- /dev/null +++ b/examples/location/mapviewer/MapViewer/menus/ItemPopupMenu.qml @@ -0,0 +1,19 @@ +// 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) + } +} diff --git a/examples/location/mapviewer/MapViewer/menus/MainMenu.qml b/examples/location/mapviewer/MapViewer/menus/MainMenu.qml new file mode 100644 index 000000000..3523b5c1a --- /dev/null +++ b/examples/location/mapviewer/MapViewer/menus/MainMenu.qml @@ -0,0 +1,122 @@ +// 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") + } + } +} diff --git a/examples/location/mapviewer/MapViewer/menus/MapPopupMenu.qml b/examples/location/mapviewer/MapViewer/menus/MapPopupMenu.qml new file mode 100644 index 000000000..335788df8 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/menus/MapPopupMenu.qml @@ -0,0 +1,30 @@ +// 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") + } +} diff --git a/examples/location/mapviewer/MapViewer/menus/MarkerPopupMenu.qml b/examples/location/mapviewer/MapViewer/menus/MarkerPopupMenu.qml new file mode 100644 index 000000000..338f23859 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/menus/MarkerPopupMenu.qml @@ -0,0 +1,38 @@ +// 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") + } +} diff --git a/examples/location/mapviewer/MapViewer/qmldir b/examples/location/mapviewer/MapViewer/qmldir new file mode 100644 index 000000000..359ca02af --- /dev/null +++ b/examples/location/mapviewer/MapViewer/qmldir @@ -0,0 +1,27 @@ +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 diff --git a/examples/location/mapviewer/MapViewer/resources/marker.png b/examples/location/mapviewer/MapViewer/resources/marker.png Binary files differnew file mode 100644 index 000000000..2116dfdf5 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/resources/marker.png diff --git a/examples/location/mapviewer/MapViewer/resources/marker_blue.png b/examples/location/mapviewer/MapViewer/resources/marker_blue.png Binary files differnew file mode 100644 index 000000000..70f0c2538 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/resources/marker_blue.png diff --git a/examples/location/mapviewer/MapViewer/resources/scale.png b/examples/location/mapviewer/MapViewer/resources/scale.png Binary files differnew file mode 100644 index 000000000..c4f08122a --- /dev/null +++ b/examples/location/mapviewer/MapViewer/resources/scale.png diff --git a/examples/location/mapviewer/MapViewer/resources/scale_end.png b/examples/location/mapviewer/MapViewer/resources/scale_end.png Binary files differnew file mode 100644 index 000000000..94510b125 --- /dev/null +++ b/examples/location/mapviewer/MapViewer/resources/scale_end.png diff --git a/examples/location/mapviewer/doc/mapviewer.rst b/examples/location/mapviewer/doc/mapviewer.rst new file mode 100644 index 000000000..418e2e77e --- /dev/null +++ b/examples/location/mapviewer/doc/mapviewer.rst @@ -0,0 +1,14 @@ +Map Viewer Example +================== + +.. tags:: Android + +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. + +.. image:: mapviewer.webp + :width: 400 + :alt: Map Viewer Screenshot diff --git a/examples/location/mapviewer/doc/mapviewer.webp b/examples/location/mapviewer/doc/mapviewer.webp Binary files differnew file mode 100644 index 000000000..6571a6c89 --- /dev/null +++ b/examples/location/mapviewer/doc/mapviewer.webp diff --git a/examples/location/mapviewer/main.py b/examples/location/mapviewer/main.py new file mode 100644 index 000000000..24ae1623f --- /dev/null +++ b/examples/location/mapviewer/main.py @@ -0,0 +1,75 @@ +# 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) diff --git a/examples/location/mapviewer/mapviewer.pyproject b/examples/location/mapviewer/mapviewer.pyproject new file mode 100644 index 000000000..8e2cadd2b --- /dev/null +++ b/examples/location/mapviewer/mapviewer.pyproject @@ -0,0 +1,33 @@ +{ + "files": ["main.py", + "MapViewer/forms/Geocode.qml", + "MapViewer/forms/GeocodeForm.ui.qml", + "MapViewer/forms/Locale.qml", + "MapViewer/forms/LocaleForm.ui.qml", + "MapViewer/forms/Message.qml", + "MapViewer/forms/MessageForm.ui.qml", + "MapViewer/forms/ReverseGeocode.qml", + "MapViewer/forms/ReverseGeocodeForm.ui.qml", + "MapViewer/forms/RouteAddress.qml", + "MapViewer/forms/RouteAddressForm.ui.qml", + "MapViewer/forms/RouteCoordinate.qml", + "MapViewer/forms/RouteCoordinateForm.ui.qml", + "MapViewer/forms/RouteList.qml", + "MapViewer/forms/RouteListDelegate.qml", + "MapViewer/forms/RouteListHeader.qml", + "MapViewer/map/MapComponent.qml", + "MapViewer/map/MapSliders.qml", + "MapViewer/map/Marker.qml", + "MapViewer/map/MiniMap.qml", + "MapViewer/menus/ItemPopupMenu.qml", + "MapViewer/menus/MainMenu.qml", + "MapViewer/menus/MapPopupMenu.qml", + "MapViewer/menus/MarkerPopupMenu.qml", + "MapViewer/resources/marker_blue.png", + "MapViewer/resources/marker.png", + "MapViewer/resources/scale_end.png", + "MapViewer/resources/scale.png", + "MapViewer/helper.js", + "MapViewer/Main.qml", + "MapViewer/qmldir"] +} |