aboutsummaryrefslogtreecommitdiffstats
path: root/app/map/MapComponent.qml
diff options
context:
space:
mode:
Diffstat (limited to 'app/map/MapComponent.qml')
-rw-r--r--app/map/MapComponent.qml677
1 files changed, 677 insertions, 0 deletions
diff --git a/app/map/MapComponent.qml b/app/map/MapComponent.qml
new file mode 100644
index 0000000..b4bb330
--- /dev/null
+++ b/app/map/MapComponent.qml
@@ -0,0 +1,677 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtLocation 5.6
+import QtPositioning 5.5
+import "../helper.js" as Helper
+
+//! [top]
+Map {
+ id: map
+//! [top]
+ property variant markers
+ property variant mapItems
+ property variant location
+ property int markerCounter: 0 // counter for total amount of markers. Resets to 0 when number of markers = 0
+ property int currentMarker
+ property int lastX : -1
+ property int lastY : -1
+ property int pressX : -1
+ property int pressY : -1
+ property int jitterThreshold : 30
+ property bool followme: true
+ 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
+
+ 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 = map.toCoordinate(Qt.point(0,scale.y))
+ coord2 = 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 = map.markers.length
+ for (var i = 0; i<count; i++){
+ map.removeMapItem(map.markers[i])
+ map.markers[i].destroy()
+ }
+ map.markers = []
+ markerCounter = 0
+ }
+
+ function deleteMapItems()
+ {
+ var count = map.mapItems.length
+ for (var i = 0; i<count; i++){
+ map.removeMapItem(map.mapItems[i])
+ map.mapItems[i].destroy()
+ }
+ map.mapItems = []
+ }
+
+ function addMarker()
+ {
+ var count = map.markers.length
+ markerCounter++
+ var marker = Qt.createQmlObject ('Marker {}', map)
+ map.addMapItem(marker)
+ marker.z = map.z+1
+ marker.coordinate = mouseArea.lastCoordinate
+
+ //update list of markers
+ var myArray = new Array()
+ for (var i = 0; i<count; i++){
+ myArray.push(markers[i])
+ }
+ myArray.push(marker)
+ markers = myArray
+ }
+
+ function addGeoItem(item)
+ {
+ var count = map.mapItems.length
+ var co = Qt.createComponent(item+'.qml')
+ if (co.status == Component.Ready) {
+ var o = co.createObject(map)
+ o.setGeometry(map.markers, currentMarker)
+ map.addMapItem(o)
+ //update list of items
+ var myArray = new Array()
+ for (var i = 0; i<count; i++){
+ myArray.push(mapItems[i])
+ }
+ myArray.push(o)
+ mapItems = myArray
+
+ } else {
+ console.log(item + " is not supported right now, please call us later.")
+ }
+ }
+
+ function deleteMarker(index)
+ {
+ //update list of markers
+ var myArray = new Array()
+ var count = map.markers.length
+ for (var i = 0; i<count; i++){
+ if (index != i) myArray.push(map.markers[i])
+ }
+
+ map.removeMapItem(map.markers[index])
+ map.markers[index].destroy()
+ map.markers = myArray
+ if (markers.length == 0) markerCounter = 0
+ }
+
+ function calculateMarkerRoute()
+ {
+ routeQuery.clearWaypoints();
+ for (var i = currentMarker; i< map.markers.length; i++){
+ routeQuery.addWaypoint(markers[i].coordinate)
+ }
+ routeQuery.travelModes = RouteQuery.CarTravel
+ routeQuery.routeOptimizations = RouteQuery.ShortestRoute
+ routeQuery.setFeatureWeight(0, 0)
+ 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]
+
+ //! [routerequest0 feature weight]
+ for (var i=0; i<9; i++) {
+ routeQuery.setFeatureWeight(i, 0)
+ }
+ //for (var i=0; i<routeDialog.features.length; i++) {
+ // map.routeQuery.setFeatureWeight(routeDialog.features[i], RouteQuery.AvoidFeatureWeight)
+ //}
+ //! [routerequest0 feature weight]
+
+ //! [routerequest1]
+ routeModel.update();
+
+ //! [routerequest1]
+ //! [routerequest2]
+ // center the map on the start coord
+ map.center = startCoordinate;
+ //! [routerequest2]
+ }
+
+ function geocode(fromAddress)
+ {
+ //! [geocode1]
+ // send the geocode request
+ geocodeModel.query = fromAddress
+ geocodeModel.update()
+ //! [geocode1]
+ }
+
+
+//! [coord]
+ zoomLevel: (maximumZoomLevel - minimumZoomLevel)/2
+//! [coord]
+
+//! [mapnavigation]
+ // Enable pan, flick, and pinch gestures to zoom in and out
+ gesture.acceptedGestures: MapGestureArea.PanGesture | MapGestureArea.FlickGesture | MapGestureArea.PinchGesture
+ gesture.flickDeceleration: 3000
+ gesture.enabled: true
+//! [mapnavigation]
+ focus: true
+ onCopyrightLinkActivated: Qt.openUrlExternally(link)
+
+ onCenterChanged:{
+ scaleTimer.restart()
+ if (map.followme)
+ if (map.center != positionSource.position.coordinate) map.followme = false
+ }
+
+ onZoomLevelChanged:{
+ scaleTimer.restart()
+ if (map.followme) map.center = positionSource.position.coordinate
+ }
+
+ onWidthChanged:{
+ scaleTimer.restart()
+ }
+
+ onHeightChanged:{
+ scaleTimer.restart()
+ }
+
+ Component.onCompleted: {
+ markers = new Array();
+ mapItems = new Array();
+ }
+
+ Keys.onPressed: {
+ if (event.key === Qt.Key_Plus) {
+ map.zoomLevel++;
+ } else if (event.key === Qt.Key_Minus) {
+ 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 = map.width / 4; break;
+ case Qt.Key_Right: dx = -map.width / 4; break;
+ case Qt.Key_Up: dy = map.height / 4; break;
+ case Qt.Key_Down: dy = -map.height / 4; break;
+
+ }
+
+ var mapCenterPoint = Qt.point(map.width / 2.0 - dx, map.height / 2.0 - dy);
+ map.center = map.toCoordinate(mapCenterPoint);
+ }
+ }
+
+ /* @todo
+ Binding {
+ target: map
+ property: 'center'
+ value: positionSource.position.coordinate
+ when: followme
+ }*/
+
+ PositionSource{
+ id: positionSource
+ active: followme
+
+ onPositionChanged: {
+ map.center = positionSource.position.coordinate
+ }
+ }
+
+ MapQuickItem {
+ id: locationPoint
+ sourceItem: Rectangle { width: 14; height: 14; color: "#e41e25"; border.width: 2; border.color: "white"; smooth: true; radius: 7 }
+ coordinate: location
+ opacity: 1.0
+ anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2)
+ }
+
+ Slider {
+ id: zoomSlider;
+ z: map.z + 3
+ minimumValue: map.minimumZoomLevel;
+ maximumValue: map.maximumZoomLevel;
+ anchors.margins: 10
+ anchors.bottom: scale.top
+ anchors.top: parent.top
+ anchors.right: parent.right
+ orientation : Qt.Vertical
+ value: map.zoomLevel
+ onValueChanged: {
+ if (value >= 0)
+ map.zoomLevel = value
+ }
+ }
+
+ GridLayout {
+ z: map.z + 3
+ columns: 1
+ width: 300
+ height: 300
+
+ Button {
+ text: "Lookup Route"
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ onClicked: {
+ stackView.pop({item:page, immediate: true})
+ stackView.push(Qt.resolvedUrl("../forms/RouteAddress.qml"),
+ { "plugin": map.plugin,
+ "toAddress": toAddress,
+ "fromAddress": fromAddress})
+ stackView.currentItem.showRoute.connect(map.calculateCoordinateRoute)
+ stackView.currentItem.showMessage.connect(stackView.showMessage)
+ stackView.currentItem.closeForm.connect(stackView.closeForm)
+ }
+ }
+
+ Button {
+ text: "Reverse GeoCode"
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ onClicked: {
+ stackView.pop({item:page, immediate: true})
+ stackView.push(Qt.resolvedUrl("../forms/ReverseGeocode.qml"),
+ { "coordinate": map.location})
+ stackView.currentItem.showPlace.connect(map.geocode)
+ stackView.currentItem.closeForm.connect(stackView.closeForm)
+ }
+ }
+
+ Button {
+ text: "Show Route List"
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ onClicked: {
+ stackView.showRouteListPage()
+ }
+ }
+
+ Button {
+ text: "Clear Route"
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ enabled: map.routeModel.count
+ onClicked: {
+ map.routeModel.reset();
+ map.zoomLevel = map.maximumZoomLevel
+ map.center = map.location
+ }
+ }
+ }
+
+ Item {
+ id: scale
+ z: 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: {
+ map.calculateScale();
+ }
+ }
+
+ //! [routemodel0]
+ RouteModel {
+ id: routeModel
+ plugin : map.plugin
+ query: RouteQuery {
+ id: routeQuery
+ }
+ onStatusChanged: {
+ if (status == RouteModel.Ready) {
+ switch (count) {
+ case 0:
+ // technically not an error
+ map.routeError()
+ break
+ case 1:
+ map.showRouteList()
+ break
+ }
+ } else if (status == RouteModel.Error) {
+ map.routeError()
+ }
+ }
+ }
+ //! [routemodel0]
+
+ //! [routedelegate0]
+ Component {
+ id: routeDelegate
+
+ MapRoute {
+ id: route
+ route: routeData
+ line.color: "#46a2da"
+ line.width: 5
+ smooth: true
+ opacity: 0.8
+ //! [routedelegate0]
+ MouseArea {
+ id: routeMouseArea
+ anchors.fill: parent
+ hoverEnabled: false
+ property variant lastCoordinate
+
+ onPressed : {
+ map.lastX = mouse.x + parent.x
+ map.lastY = mouse.y + parent.y
+ map.pressX = mouse.x + parent.x
+ map.pressY = mouse.y + parent.y
+ lastCoordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y))
+ }
+
+ onPositionChanged: {
+ if (mouse.button == Qt.LeftButton) {
+ map.lastX = mouse.x + parent.x
+ map.lastY = mouse.y + parent.y
+ }
+ }
+
+ onPressAndHold:{
+ if (Math.abs(map.pressX - parent.x- mouse.x ) < map.jitterThreshold
+ && Math.abs(map.pressY - parent.y - mouse.y ) < map.jitterThreshold) {
+ showRouteMenu(lastCoordinate);
+ }
+ }
+
+ }
+ //! [routedelegate1]
+ }
+ }
+ //! [routedelegate1]
+
+ //! [geocodemodel0]
+ GeocodeModel {
+ id: geocodeModel
+ plugin: map.plugin
+ onStatusChanged: {
+ if ((status == GeocodeModel.Ready) || (status == GeocodeModel.Error))
+ map.geocodeFinished()
+ }
+ onLocationsChanged:
+ {
+ if (count == 1) {
+ map.center.latitude = get(0).coordinate.latitude
+ map.center.longitude = get(0).coordinate.longitude
+ }
+ }
+ }
+ //! [geocodemodel0]
+
+ //! [pointdel0]
+ Component {
+ id: pointDelegate
+
+ MapCircle {
+ id: point
+ color: "#46a2da"
+ border.color: "#190a33"
+ border.width: 2
+ smooth: true
+ opacity: 0.25
+ center: locationData.coordinate
+ //! [pointdel0]
+ MouseArea {
+ anchors.fill:parent
+ id: circleMouseArea
+ hoverEnabled: false
+ property variant lastCoordinate
+
+ onPressed : {
+ map.lastX = mouse.x + parent.x
+ map.lastY = mouse.y + parent.y
+ map.pressX = mouse.x + parent.x
+ map.pressY = mouse.y + parent.y
+ lastCoordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y))
+ }
+
+ onPositionChanged: {
+ if (Math.abs(map.pressX - parent.x- mouse.x ) > map.jitterThreshold ||
+ Math.abs(map.pressY - parent.y -mouse.y ) > map.jitterThreshold) {
+ if (pressed) parent.radius = parent.center.distanceTo(
+ map.toCoordinate(Qt.point(mouse.x, mouse.y)))
+ }
+ if (mouse.button == Qt.LeftButton) {
+ map.lastX = mouse.x + parent.x
+ map.lastY = mouse.y + parent.y
+ }
+ }
+
+ onPressAndHold:{
+ if (Math.abs(map.pressX - parent.x- mouse.x ) < map.jitterThreshold
+ && Math.abs(map.pressY - parent.y - mouse.y ) < map.jitterThreshold) {
+ showPointMenu(lastCoordinate);
+ }
+ }
+ }
+ //! [pointdel1]
+ }
+ }
+ //! [pointdel1]
+
+ //! [routeview0]
+ MapItemView {
+ model: routeModel
+ delegate: routeDelegate
+ //! [routeview0]
+ autoFitViewport: true
+ //! [routeview1]
+ }
+ //! [routeview1]
+
+ //! [geocodeview]
+ MapItemView {
+ model: geocodeModel
+ delegate: pointDelegate
+ }
+ //! [geocodeview]
+
+ Timer {
+ id: scaleTimer
+ interval: 100
+ running: false
+ repeat: false
+ onTriggered: {
+ map.calculateScale()
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+ property variant lastCoordinate
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+
+ onPressed : {
+ map.lastX = mouse.x
+ map.lastY = mouse.y
+ map.pressX = mouse.x
+ map.pressY = mouse.y
+ lastCoordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y))
+ }
+
+ onPositionChanged: {
+ if (mouse.button == Qt.LeftButton) {
+ map.lastX = mouse.x
+ map.lastY = mouse.y
+ }
+ }
+
+ onDoubleClicked: {
+ var mouseGeoPos = map.toCoordinate(Qt.point(mouse.x, mouse.y));
+ var preZoomPoint = map.fromCoordinate(mouseGeoPos, false);
+ if (mouse.button === Qt.LeftButton) {
+ map.zoomLevel++;
+ } else if (mouse.button === Qt.RightButton) {
+ map.zoomLevel--;
+ }
+ var postZoomPoint = map.fromCoordinate(mouseGeoPos, false);
+ var dx = postZoomPoint.x - preZoomPoint.x;
+ var dy = postZoomPoint.y - preZoomPoint.y;
+
+ var mapCenterPoint = Qt.point(map.width / 2.0 + dx, map.height / 2.0 + dy);
+ map.center = map.toCoordinate(mapCenterPoint);
+
+ lastX = -1;
+ lastY = -1;
+ }
+
+ onPressAndHold:{
+ if (Math.abs(map.pressX - mouse.x ) < map.jitterThreshold
+ && Math.abs(map.pressY - mouse.y ) < map.jitterThreshold) {
+ showMainMenu(lastCoordinate);
+ }
+ }
+ }
+//! [end]
+}
+//! [end]