1/**************************************************************************** 2** 3** Copyright (C) 2017 The Qt Company Ltd. 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of the examples of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:BSD$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see https://www.qt.io/terms-conditions. For further 15** information use the contact form at https://www.qt.io/contact-us. 16** 17** BSD License Usage 18** Alternatively, you may use this file under the terms of the BSD license 19** as follows: 20** 21** "Redistribution and use in source and binary forms, with or without 22** modification, are permitted provided that the following conditions are 23** met: 24** * Redistributions of source code must retain the above copyright 25** notice, this list of conditions and the following disclaimer. 26** * Redistributions in binary form must reproduce the above copyright 27** notice, this list of conditions and the following disclaimer in 28** the documentation and/or other materials provided with the 29** distribution. 30** * Neither the name of The Qt Company Ltd nor the names of its 31** contributors may be used to endorse or promote products derived 32** from this software without specific prior written permission. 33** 34** 35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46** 47** $QT_END_LICENSE$ 48** 49****************************************************************************/ 50import QtQuick 2.5 51import QtQuick.Controls 1.4 52import QtLocation 5.9 53import QtPositioning 5.5 54import "../helper.js" as Helper 55 56//! [top] 57Map { 58 id: map 59//! [top] 60 property variant markers 61 property variant mapItems 62 property int markerCounter: 0 // counter for total amount of markers. Resets to 0 when number of markers = 0 63 property int currentMarker 64 property int lastX : -1 65 property int lastY : -1 66 property int pressX : -1 67 property int pressY : -1 68 property int jitterThreshold : 30 69 property bool followme: false 70 property variant scaleLengths: [5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000] 71 property alias routeQuery: routeQuery 72 property alias routeModel: routeModel 73 property alias geocodeModel: geocodeModel 74 property alias slidersExpanded: sliders.expanded 75 76 signal showGeocodeInfo() 77 signal geocodeFinished() 78 signal routeError() 79 signal coordinatesCaptured(double latitude, double longitude) 80 signal showMainMenu(variant coordinate) 81 signal showMarkerMenu(variant coordinate) 82 signal showRouteMenu(variant coordinate) 83 signal showPointMenu(variant coordinate) 84 signal showRouteList() 85 86 function geocodeMessage() 87 { 88 var street, district, city, county, state, countryCode, country, postalCode, latitude, longitude, text 89 latitude = Math.round(geocodeModel.get(0).coordinate.latitude * 10000) / 10000 90 longitude =Math.round(geocodeModel.get(0).coordinate.longitude * 10000) / 10000 91 street = geocodeModel.get(0).address.street 92 district = geocodeModel.get(0).address.district 93 city = geocodeModel.get(0).address.city 94 county = geocodeModel.get(0).address.county 95 state = geocodeModel.get(0).address.state 96 countryCode = geocodeModel.get(0).address.countryCode 97 country = geocodeModel.get(0).address.country 98 postalCode = geocodeModel.get(0).address.postalCode 99 100 text = "<b>Latitude:</b> " + latitude + "<br/>" 101 text +="<b>Longitude:</b> " + longitude + "<br/>" + "<br/>" 102 if (street) text +="<b>Street: </b>"+ street + " <br/>" 103 if (district) text +="<b>District: </b>"+ district +" <br/>" 104 if (city) text +="<b>City: </b>"+ city + " <br/>" 105 if (county) text +="<b>County: </b>"+ county + " <br/>" 106 if (state) text +="<b>State: </b>"+ state + " <br/>" 107 if (countryCode) text +="<b>Country code: </b>"+ countryCode + " <br/>" 108 if (country) text +="<b>Country: </b>"+ country + " <br/>" 109 if (postalCode) text +="<b>PostalCode: </b>"+ postalCode + " <br/>" 110 return text 111 } 112 113 function calculateScale() 114 { 115 var coord1, coord2, dist, text, f 116 f = 0 117 coord1 = map.toCoordinate(Qt.point(0,scale.y)) 118 coord2 = map.toCoordinate(Qt.point(0+scaleImage.sourceSize.width,scale.y)) 119 dist = Math.round(coord1.distanceTo(coord2)) 120 121 if (dist === 0) { 122 // not visible 123 } else { 124 for (var i = 0; i < scaleLengths.length-1; i++) { 125 if (dist < (scaleLengths[i] + scaleLengths[i+1]) / 2 ) { 126 f = scaleLengths[i] / dist 127 dist = scaleLengths[i] 128 break; 129 } 130 } 131 if (f === 0) { 132 f = dist / scaleLengths[i] 133 dist = scaleLengths[i] 134 } 135 } 136 137 text = Helper.formatDistance(dist) 138 scaleImage.width = (scaleImage.sourceSize.width * f) - 2 * scaleImageLeft.sourceSize.width 139 scaleText.text = text 140 } 141 142 function deleteMarkers() 143 { 144 var count = map.markers.length 145 for (var i = 0; i<count; i++){ 146 map.removeMapItem(map.markers[i]) 147 map.markers[i].destroy() 148 } 149 map.markers = [] 150 markerCounter = 0 151 } 152 153 function deleteMapItems() 154 { 155 var count = map.mapItems.length 156 for (var i = 0; i<count; i++){ 157 map.removeMapItem(map.mapItems[i]) 158 map.mapItems[i].destroy() 159 } 160 map.mapItems = [] 161 } 162 163 function addMarker() 164 { 165 var count = map.markers.length 166 markerCounter++ 167 var marker = Qt.createQmlObject ('Marker {}', map) 168 map.addMapItem(marker) 169 marker.z = map.z+1 170 marker.coordinate = mouseArea.lastCoordinate 171 172 //update list of markers 173 var myArray = new Array() 174 for (var i = 0; i<count; i++){ 175 myArray.push(markers[i]) 176 } 177 myArray.push(marker) 178 markers = myArray 179 } 180 181 function addGeoItem(item) 182 { 183 var count = map.mapItems.length 184 var co = Qt.createComponent(item+'.qml') 185 if (co.status == Component.Ready) { 186 var o = co.createObject(map) 187 o.setGeometry(map.markers, currentMarker) 188 map.addMapItem(o) 189 //update list of items 190 var myArray = new Array() 191 for (var i = 0; i<count; i++){ 192 myArray.push(mapItems[i]) 193 } 194 myArray.push(o) 195 mapItems = myArray 196 197 } else { 198 console.log(item + " is not supported right now, please call us later.") 199 } 200 } 201 202 function deleteMarker(index) 203 { 204 //update list of markers 205 var myArray = new Array() 206 var count = map.markers.length 207 for (var i = 0; i<count; i++){ 208 if (index != i) myArray.push(map.markers[i]) 209 } 210 211 map.removeMapItem(map.markers[index]) 212 map.markers[index].destroy() 213 map.markers = myArray 214 if (markers.length == 0) markerCounter = 0 215 } 216 217 function calculateMarkerRoute() 218 { 219 routeQuery.clearWaypoints(); 220 for (var i = currentMarker; i< map.markers.length; i++){ 221 routeQuery.addWaypoint(markers[i].coordinate) 222 } 223 routeQuery.travelModes = RouteQuery.CarTravel 224 routeQuery.routeOptimizations = RouteQuery.ShortestRoute 225 routeQuery.setFeatureWeight(0, 0) 226 routeModel.update(); 227 } 228 229 function calculateCoordinateRoute(startCoordinate, endCoordinate) 230 { 231 //! [routerequest0] 232 // clear away any old data in the query 233 routeQuery.clearWaypoints(); 234 235 // add the start and end coords as waypoints on the route 236 routeQuery.addWaypoint(startCoordinate) 237 routeQuery.addWaypoint(endCoordinate) 238 routeQuery.travelModes = RouteQuery.CarTravel 239 routeQuery.routeOptimizations = RouteQuery.FastestRoute 240 241 //! [routerequest0] 242 243 //! [routerequest0 feature weight] 244 for (var i=0; i<9; i++) { 245 routeQuery.setFeatureWeight(i, 0) 246 } 247 //for (var i=0; i<routeDialog.features.length; i++) { 248 // map.routeQuery.setFeatureWeight(routeDialog.features[i], RouteQuery.AvoidFeatureWeight) 249 //} 250 //! [routerequest0 feature weight] 251 252 //! [routerequest1] 253 routeModel.update(); 254 255 //! [routerequest1] 256 //! [routerequest2] 257 // center the map on the start coord 258 map.center = startCoordinate; 259 //! [routerequest2] 260 } 261 262 function geocode(fromAddress) 263 { 264 //! [geocode1] 265 // send the geocode request 266 geocodeModel.query = fromAddress 267 geocodeModel.update() 268 //! [geocode1] 269 } 270 271 272//! [coord] 273 zoomLevel: (maximumZoomLevel - minimumZoomLevel)/2 274 center { 275 // The Qt Company in Oslo 276 latitude: 59.9485 277 longitude: 10.7686 278 } 279//! [coord] 280 281//! [mapnavigation] 282 // Enable pan, flick, and pinch gestures to zoom in and out 283 gesture.acceptedGestures: MapGestureArea.PanGesture | MapGestureArea.FlickGesture | MapGestureArea.PinchGesture | MapGestureArea.RotationGesture | MapGestureArea.TiltGesture 284 gesture.flickDeceleration: 3000 285 gesture.enabled: true 286//! [mapnavigation] 287 focus: true 288 onCopyrightLinkActivated: Qt.openUrlExternally(link) 289 290 onCenterChanged:{ 291 scaleTimer.restart() 292 if (map.followme) 293 if (map.center != positionSource.position.coordinate) map.followme = false 294 } 295 296 onZoomLevelChanged:{ 297 scaleTimer.restart() 298 if (map.followme) map.center = positionSource.position.coordinate 299 } 300 301 onWidthChanged:{ 302 scaleTimer.restart() 303 } 304 305 onHeightChanged:{ 306 scaleTimer.restart() 307 } 308 309 Component.onCompleted: { 310 markers = new Array(); 311 mapItems = new Array(); 312 } 313 314 Keys.onPressed: { 315 if (event.key === Qt.Key_Plus) { 316 map.zoomLevel++; 317 } else if (event.key === Qt.Key_Minus) { 318 map.zoomLevel--; 319 } else if (event.key === Qt.Key_Left || event.key === Qt.Key_Right || 320 event.key === Qt.Key_Up || event.key === Qt.Key_Down) { 321 var dx = 0; 322 var dy = 0; 323 324 switch (event.key) { 325 326 case Qt.Key_Left: dx = map.width / 4; break; 327 case Qt.Key_Right: dx = -map.width / 4; break; 328 case Qt.Key_Up: dy = map.height / 4; break; 329 case Qt.Key_Down: dy = -map.height / 4; break; 330 331 } 332 333 var mapCenterPoint = Qt.point(map.width / 2.0 - dx, map.height / 2.0 - dy); 334 map.center = map.toCoordinate(mapCenterPoint); 335 } 336 } 337 338 /* @todo 339 Binding { 340 target: map 341 property: 'center' 342 value: positionSource.position.coordinate 343 when: followme 344 }*/ 345 346 PositionSource{ 347 id: positionSource 348 active: followme 349 350 onPositionChanged: { 351 map.center = positionSource.position.coordinate 352 } 353 } 354 355 MapQuickItem { 356 id: poiTheQtComapny 357 sourceItem: Rectangle { width: 14; height: 14; color: "#e41e25"; border.width: 2; border.color: "white"; smooth: true; radius: 7 } 358 coordinate { 359 latitude: 59.9485 360 longitude: 10.7686 361 } 362 opacity: 1.0 363 anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2) 364 } 365 366 MapQuickItem { 367 sourceItem: Text{ 368 text: "The Qt Company" 369 color:"#242424" 370 font.bold: true 371 styleColor: "#ECECEC" 372 style: Text.Outline 373 } 374 coordinate: poiTheQtComapny.coordinate 375 anchorPoint: Qt.point(-poiTheQtComapny.sourceItem.width * 0.5,poiTheQtComapny.sourceItem.height * 1.5) 376 } 377 378 MapSliders { 379 id: sliders 380 z: map.z + 3 381 mapSource: map 382 edge: Qt.LeftEdge 383 } 384 385 Item { 386 id: scale 387 z: map.z + 3 388 visible: scaleText.text != "0 m" 389 anchors.bottom: parent.bottom; 390 anchors.right: parent.right 391 anchors.margins: 20 392 height: scaleText.height * 2 393 width: scaleImage.width 394 395 Image { 396 id: scaleImageLeft 397 source: "../resources/scale_end.png" 398 anchors.bottom: parent.bottom 399 anchors.right: scaleImage.left 400 } 401 Image { 402 id: scaleImage 403 source: "../resources/scale.png" 404 anchors.bottom: parent.bottom 405 anchors.right: scaleImageRight.left 406 } 407 Image { 408 id: scaleImageRight 409 source: "../resources/scale_end.png" 410 anchors.bottom: parent.bottom 411 anchors.right: parent.right 412 } 413 Label { 414 id: scaleText 415 color: "#004EAE" 416 anchors.centerIn: parent 417 text: "0 m" 418 } 419 Component.onCompleted: { 420 map.calculateScale(); 421 } 422 } 423 424 //! [routemodel0] 425 RouteModel { 426 id: routeModel 427 plugin : map.plugin 428 query: RouteQuery { 429 id: routeQuery 430 } 431 onStatusChanged: { 432 if (status == RouteModel.Ready) { 433 switch (count) { 434 case 0: 435 // technically not an error 436 map.routeError() 437 break 438 case 1: 439 map.showRouteList() 440 break 441 } 442 } else if (status == RouteModel.Error) { 443 map.routeError() 444 } 445 } 446 } 447 //! [routemodel0] 448 449 //! [routedelegate0] 450 Component { 451 id: routeDelegate 452 453 MapRoute { 454 id: route 455 route: routeData 456 line.color: "#46a2da" 457 line.width: 5 458 smooth: true 459 opacity: 0.8 460 //! [routedelegate0] 461 MouseArea { 462 id: routeMouseArea 463 anchors.fill: parent 464 hoverEnabled: false 465 property variant lastCoordinate 466 467 onPressed : { 468 map.lastX = mouse.x + parent.x 469 map.lastY = mouse.y + parent.y 470 map.pressX = mouse.x + parent.x 471 map.pressY = mouse.y + parent.y 472 lastCoordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y)) 473 } 474 475 onPositionChanged: { 476 if (mouse.button == Qt.LeftButton) { 477 map.lastX = mouse.x + parent.x 478 map.lastY = mouse.y + parent.y 479 } 480 } 481 482 onPressAndHold:{ 483 if (Math.abs(map.pressX - parent.x- mouse.x ) < map.jitterThreshold 484 && Math.abs(map.pressY - parent.y - mouse.y ) < map.jitterThreshold) { 485 showRouteMenu(lastCoordinate); 486 } 487 } 488 489 } 490 //! [routedelegate1] 491 } 492 } 493 //! [routedelegate1] 494 495 //! [geocodemodel0] 496 GeocodeModel { 497 id: geocodeModel 498 plugin: map.plugin 499 onStatusChanged: { 500 if ((status == GeocodeModel.Ready) || (status == GeocodeModel.Error)) 501 map.geocodeFinished() 502 } 503 onLocationsChanged: 504 { 505 if (count == 1) { 506 map.center.latitude = get(0).coordinate.latitude 507 map.center.longitude = get(0).coordinate.longitude 508 } 509 } 510 } 511 //! [geocodemodel0] 512 513 //! [pointdel0] 514 Component { 515 id: pointDelegate 516 517 MapCircle { 518 id: point 519 radius: 1000 520 color: "#46a2da" 521 border.color: "#190a33" 522 border.width: 2 523 smooth: true 524 opacity: 0.25 525 center: locationData.coordinate 526 //! [pointdel0] 527 MouseArea { 528 anchors.fill:parent 529 id: circleMouseArea 530 hoverEnabled: false 531 property variant lastCoordinate 532 533 onPressed : { 534 map.lastX = mouse.x + parent.x 535 map.lastY = mouse.y + parent.y 536 map.pressX = mouse.x + parent.x 537 map.pressY = mouse.y + parent.y 538 lastCoordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y)) 539 } 540 541 onPositionChanged: { 542 if (Math.abs(map.pressX - parent.x- mouse.x ) > map.jitterThreshold || 543 Math.abs(map.pressY - parent.y -mouse.y ) > map.jitterThreshold) { 544 if (pressed) parent.radius = parent.center.distanceTo( 545 map.toCoordinate(Qt.point(mouse.x, mouse.y))) 546 } 547 if (mouse.button == Qt.LeftButton) { 548 map.lastX = mouse.x + parent.x 549 map.lastY = mouse.y + parent.y 550 } 551 } 552 553 onPressAndHold:{ 554 if (Math.abs(map.pressX - parent.x- mouse.x ) < map.jitterThreshold 555 && Math.abs(map.pressY - parent.y - mouse.y ) < map.jitterThreshold) { 556 showPointMenu(lastCoordinate); 557 } 558 } 559 } 560 //! [pointdel1] 561 } 562 } 563 //! [pointdel1] 564 565 //! [routeview0] 566 MapItemView { 567 model: routeModel 568 delegate: routeDelegate 569 //! [routeview0] 570 autoFitViewport: true 571 //! [routeview1] 572 } 573 //! [routeview1] 574 575 //! [geocodeview] 576 MapItemView { 577 model: geocodeModel 578 delegate: pointDelegate 579 } 580 //! [geocodeview] 581 582 Timer { 583 id: scaleTimer 584 interval: 100 585 running: false 586 repeat: false 587 onTriggered: { 588 map.calculateScale() 589 } 590 } 591 592 MouseArea { 593 id: mouseArea 594 property variant lastCoordinate 595 anchors.fill: parent 596 acceptedButtons: Qt.LeftButton | Qt.RightButton 597 598 onPressed : { 599 map.lastX = mouse.x 600 map.lastY = mouse.y 601 map.pressX = mouse.x 602 map.pressY = mouse.y 603 lastCoordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y)) 604 } 605 606 onPositionChanged: { 607 if (mouse.button == Qt.LeftButton) { 608 map.lastX = mouse.x 609 map.lastY = mouse.y 610 } 611 } 612 613 onDoubleClicked: { 614 var mouseGeoPos = map.toCoordinate(Qt.point(mouse.x, mouse.y)); 615 var preZoomPoint = map.fromCoordinate(mouseGeoPos, false); 616 if (mouse.button === Qt.LeftButton) { 617 map.zoomLevel = Math.floor(map.zoomLevel + 1) 618 } else if (mouse.button === Qt.RightButton) { 619 map.zoomLevel = Math.floor(map.zoomLevel - 1) 620 } 621 var postZoomPoint = map.fromCoordinate(mouseGeoPos, false); 622 var dx = postZoomPoint.x - preZoomPoint.x; 623 var dy = postZoomPoint.y - preZoomPoint.y; 624 625 var mapCenterPoint = Qt.point(map.width / 2.0 + dx, map.height / 2.0 + dy); 626 map.center = map.toCoordinate(mapCenterPoint); 627 628 lastX = -1; 629 lastY = -1; 630 } 631 632 onPressAndHold:{ 633 if (Math.abs(map.pressX - mouse.x ) < map.jitterThreshold 634 && Math.abs(map.pressY - mouse.y ) < map.jitterThreshold) { 635 showMainMenu(lastCoordinate); 636 } 637 } 638 } 639//! [end] 640} 641//! [end] 642