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****************************************************************************/ 50 51import QtQuick 2.5 52import QtQuick.Controls 1.4 53import QtLocation 5.6 54import QtPositioning 5.5 55import "map" 56import "menus" 57import "helper.js" as Helper 58 59ApplicationWindow { 60 id: appWindow 61 property variant map 62 property variant minimap 63 property variant parameters 64 65 //defaults 66 //! [routecoordinate] 67 property variant fromCoordinate: QtPositioning.coordinate(59.9483, 10.7695) 68 property variant toCoordinate: QtPositioning.coordinate(59.9645, 10.671) 69 //! [routecoordinate] 70 71 function createMap(provider) 72 { 73 var plugin 74 75 if (parameters && parameters.length>0) 76 plugin = Qt.createQmlObject ('import QtLocation 5.6; Plugin{ name:"' + provider + '"; parameters: appWindow.parameters}', appWindow) 77 else 78 plugin = Qt.createQmlObject ('import QtLocation 5.6; Plugin{ name:"' + provider + '"}', appWindow) 79 80 if (minimap) { 81 minimap.destroy() 82 minimap = null 83 } 84 85 var zoomLevel = null 86 var tilt = null 87 var bearing = null 88 var fov = null 89 var center = null 90 var panelExpanded = null 91 if (map) { 92 zoomLevel = map.zoomLevel 93 tilt = map.tilt 94 bearing = map.bearing 95 fov = map.fieldOfView 96 center = map.center 97 panelExpanded = map.slidersExpanded 98 map.destroy() 99 } 100 101 map = mapComponent.createObject(page); 102 map.plugin = plugin; 103 104 if (zoomLevel != null) { 105 map.tilt = tilt 106 map.bearing = bearing 107 map.fieldOfView = fov 108 map.zoomLevel = zoomLevel 109 map.center = center 110 map.slidersExpanded = panelExpanded 111 } else { 112 // Use an integer ZL to enable nearest interpolation, if possible. 113 map.zoomLevel = Math.floor((map.maximumZoomLevel - map.minimumZoomLevel)/2) 114 // defaulting to 45 degrees, if possible. 115 map.fieldOfView = Math.min(Math.max(45.0, map.minimumFieldOfView), map.maximumFieldOfView) 116 } 117 118 map.forceActiveFocus() 119 } 120 121 function getPlugins() 122 { 123 var plugin = Qt.createQmlObject ('import QtLocation 5.6; Plugin {}', appWindow) 124 var myArray = new Array() 125 for (var i = 0; i<plugin.availableServiceProviders.length; i++) { 126 var tempPlugin = Qt.createQmlObject ('import QtLocation 5.6; Plugin {name: "' + plugin.availableServiceProviders[i]+ '"}', appWindow) 127 if (tempPlugin.supportsMapping()) 128 myArray.push(tempPlugin.name) 129 } 130 myArray.sort() 131 return myArray 132 } 133 134 function initializeProviders(pluginParameters) 135 { 136 var parameters = new Array() 137 for (var prop in pluginParameters){ 138 var parameter = Qt.createQmlObject('import QtLocation 5.6; PluginParameter{ name: "'+ prop + '"; value: "' + pluginParameters[prop]+'"}',appWindow) 139 parameters.push(parameter) 140 } 141 appWindow.parameters = parameters 142 var plugins = getPlugins() 143 mainMenu.providerMenu.createMenu(plugins) 144 for (var i = 0; i<plugins.length; i++) { 145 if (plugins[i] === "osm") 146 mainMenu.selectProvider(plugins[i]) 147 } 148 } 149 150 title: qsTr("Mapviewer") 151 height: 640 152 width: 360 153 visible: true 154 menuBar: mainMenu 155 156 //! [geocode0] 157 Address { 158 id :fromAddress 159 street: "Sandakerveien 116" 160 city: "Oslo" 161 country: "Norway" 162 state : "" 163 postalCode: "0484" 164 } 165 //! [geocode0] 166 167 Address { 168 id: toAddress 169 street: "Holmenkollveien 140" 170 city: "Oslo" 171 country: "Norway" 172 postalCode: "0791" 173 } 174 175 MainMenu { 176 id: mainMenu 177 178 function toggleMiniMapState() 179 { 180 if (minimap) { 181 minimap.destroy() 182 minimap = null 183 } else { 184 minimap = Qt.createQmlObject ('import "map"; MiniMap{ z: map.z + 2 }', map) 185 } 186 } 187 188 function setLanguage(lang) 189 { 190 map.plugin.locales = lang; 191 stackView.pop(page) 192 } 193 194 onSelectProvider: { 195 stackView.pop() 196 for (var i = 0; i < providerMenu.items.length; i++) { 197 providerMenu.items[i].checked = providerMenu.items[i].text === providerName 198 } 199 200 createMap(providerName) 201 if (map.error === Map.NoError) { 202 selectMapType(map.activeMapType) 203 toolsMenu.createMenu(map); 204 } else { 205 mapTypeMenu.clear(); 206 toolsMenu.clear(); 207 } 208 } 209 210 onSelectMapType: { 211 stackView.pop(page) 212 for (var i = 0; i < mapTypeMenu.items.length; i++) { 213 mapTypeMenu.items[i].checked = mapTypeMenu.items[i].text === mapType.name 214 } 215 map.activeMapType = mapType 216 } 217 218 219 onSelectTool: { 220 switch (tool) { 221 case "AddressRoute": 222 stackView.pop({item:page, immediate: true}) 223 stackView.push({ item: Qt.resolvedUrl("forms/RouteAddress.qml") , 224 properties: { "plugin": map.plugin, 225 "toAddress": toAddress, 226 "fromAddress": fromAddress}}) 227 stackView.currentItem.showRoute.connect(map.calculateCoordinateRoute) 228 stackView.currentItem.showMessage.connect(stackView.showMessage) 229 stackView.currentItem.closeForm.connect(stackView.closeForm) 230 break 231 case "CoordinateRoute": 232 stackView.pop({item:page, immediate: true}) 233 stackView.push({ item: Qt.resolvedUrl("forms/RouteCoordinate.qml") , 234 properties: { "toCoordinate": toCoordinate, 235 "fromCoordinate": fromCoordinate}}) 236 stackView.currentItem.showRoute.connect(map.calculateCoordinateRoute) 237 stackView.currentItem.closeForm.connect(stackView.closeForm) 238 break 239 case "Geocode": 240 stackView.pop({item:page, immediate: true}) 241 stackView.push({ item: Qt.resolvedUrl("forms/Geocode.qml") , 242 properties: { "address": fromAddress}}) 243 stackView.currentItem.showPlace.connect(map.geocode) 244 stackView.currentItem.closeForm.connect(stackView.closeForm) 245 break 246 case "RevGeocode": 247 stackView.pop({item:page, immediate: true}) 248 stackView.push({ item: Qt.resolvedUrl("forms/ReverseGeocode.qml") , 249 properties: { "coordinate": fromCoordinate}}) 250 stackView.currentItem.showPlace.connect(map.geocode) 251 stackView.currentItem.closeForm.connect(stackView.closeForm) 252 break 253 case "Language": 254 stackView.pop({item:page, immediate: true}) 255 stackView.push({ item: Qt.resolvedUrl("forms/Locale.qml") , 256 properties: { "locale": map.plugin.locales[0]}}) 257 stackView.currentItem.selectLanguage.connect(setLanguage) 258 stackView.currentItem.closeForm.connect(stackView.closeForm) 259 break 260 case "Clear": 261 map.clearData() 262 break 263 case "Prefetch": 264 map.prefetchData() 265 break 266 default: 267 console.log("Unsupported operation") 268 } 269 } 270 271 onToggleMapState: { 272 stackView.pop(page) 273 switch (state) { 274 case "FollowMe": 275 map.followme = !map.followme 276 break 277 case "MiniMap": 278 toggleMiniMapState() 279 isMiniMap = minimap 280 break 281 default: 282 console.log("Unsupported operation") 283 } 284 } 285 } 286 287 MapPopupMenu { 288 id: mapPopupMenu 289 290 function show(coordinate) 291 { 292 stackView.pop(page) 293 mapPopupMenu.coordinate = coordinate 294 mapPopupMenu.markersCount = map.markers.length 295 mapPopupMenu.mapItemsCount = map.mapItems.length 296 mapPopupMenu.update() 297 mapPopupMenu.popup() 298 } 299 300 onItemClicked: { 301 stackView.pop(page) 302 switch (item) { 303 case "addMarker": 304 map.addMarker() 305 break 306 case "getCoordinate": 307 map.coordinatesCaptured(coordinate.latitude, coordinate.longitude) 308 break 309 case "fitViewport": 310 map.fitViewportToMapItems() 311 break 312 case "deleteMarkers": 313 map.deleteMarkers() 314 break 315 case "deleteItems": 316 map.deleteMapItems() 317 break 318 default: 319 console.log("Unsupported operation") 320 } 321 } 322 } 323 324 MarkerPopupMenu { 325 id: markerPopupMenu 326 327 function show(coordinate) 328 { 329 stackView.pop(page) 330 markerPopupMenu.markersCount = map.markers.length 331 markerPopupMenu.update() 332 markerPopupMenu.popup() 333 } 334 335 function askForCoordinate() 336 { 337 stackView.push({ item: Qt.resolvedUrl("forms/ReverseGeocode.qml") , 338 properties: { "title": qsTr("New Coordinate"), 339 "coordinate": map.markers[map.currentMarker].coordinate}}) 340 stackView.currentItem.showPlace.connect(moveMarker) 341 stackView.currentItem.closeForm.connect(stackView.closeForm) 342 } 343 344 function moveMarker(coordinate) 345 { 346 map.markers[map.currentMarker].coordinate = coordinate; 347 map.center = coordinate; 348 stackView.pop(page) 349 } 350 351 onItemClicked: { 352 stackView.pop(page) 353 switch (item) { 354 case "deleteMarker": 355 map.deleteMarker(map.currentMarker) 356 break; 357 case "getMarkerCoordinate": 358 map.coordinatesCaptured(map.markers[map.currentMarker].coordinate.latitude, map.markers[map.currentMarker].coordinate.longitude) 359 break; 360 case "moveMarkerTo": 361 askForCoordinate() 362 break; 363 case "routeToNextPoint": 364 case "routeToNextPoints": 365 map.calculateMarkerRoute() 366 break 367 case "distanceToNextPoint": 368 var coordinate1 = map.markers[currentMarker].coordinate; 369 var coordinate2 = map.markers[currentMarker+1].coordinate; 370 var distance = Helper.formatDistance(coordinate1.distanceTo(coordinate2)); 371 stackView.showMessage(qsTr("Distance"),"<b>" + qsTr("Distance:") + "</b> " + distance) 372 break 373 case "drawImage": 374 map.addGeoItem("ImageItem") 375 break 376 case "drawRectangle": 377 map.addGeoItem("RectangleItem") 378 break 379 case "drawCircle": 380 map.addGeoItem("CircleItem") 381 break; 382 case "drawPolyline": 383 map.addGeoItem("PolylineItem") 384 break; 385 case "drawPolygonMenu": 386 map.addGeoItem("PolygonItem") 387 break 388 default: 389 console.log("Unsupported operation") 390 } 391 } 392 } 393 394 ItemPopupMenu { 395 id: itemPopupMenu 396 397 function show(type,coordinate) 398 { 399 stackView.pop(page) 400 itemPopupMenu.type = type 401 itemPopupMenu.update() 402 itemPopupMenu.popup() 403 } 404 405 onItemClicked: { 406 stackView.pop(page) 407 switch (item) { 408 case "showRouteInfo": 409 stackView.showRouteListPage() 410 break; 411 case "deleteRoute": 412 map.routeModel.reset(); 413 break; 414 case "showPointInfo": 415 map.showGeocodeInfo() 416 break; 417 case "deletePoint": 418 map.geocodeModel.reset() 419 break; 420 default: 421 console.log("Unsupported operation") 422 } 423 } 424 } 425 426 StackView { 427 id: stackView 428 anchors.fill: parent 429 focus: true 430 initialItem: Item { 431 id: page 432 433 Text { 434 visible: !supportsSsl && map && map.activeMapType && activeMapType.metadata.isHTTPS 435 text: "The active map type\n 436requires (missing) SSL\n 437support" 438 horizontalAlignment: Text.AlignHCenter 439 font.pixelSize: appWindow.width / 12 440 font.bold: true 441 color: "grey" 442 anchors.centerIn: parent 443 z: 12 444 } 445 } 446 447 function showMessage(title,message,backPage) 448 { 449 push({ item: Qt.resolvedUrl("forms/Message.qml") , 450 properties: { 451 "title" : title, 452 "message" : message, 453 "backPage" : backPage 454 }}) 455 currentItem.closeForm.connect(closeMessage) 456 } 457 458 function closeMessage(backPage) 459 { 460 pop(backPage) 461 } 462 463 function closeForm() 464 { 465 pop(page) 466 } 467 468 function showRouteListPage() 469 { 470 push({ item: Qt.resolvedUrl("forms/RouteList.qml") , 471 properties: { 472 "routeModel" : map.routeModel 473 }}) 474 currentItem.closeForm.connect(closeForm) 475 } 476 } 477 478 Component { 479 id: mapComponent 480 481 MapComponent{ 482 width: page.width 483 height: page.height 484 onFollowmeChanged: mainMenu.isFollowMe = map.followme 485 onSupportedMapTypesChanged: mainMenu.mapTypeMenu.createMenu(map) 486 onCoordinatesCaptured: { 487 var text = "<b>" + qsTr("Latitude:") + "</b> " + Helper.roundNumber(latitude,4) + "<br/><b>" + qsTr("Longitude:") + "</b> " + Helper.roundNumber(longitude,4) 488 stackView.showMessage(qsTr("Coordinates"),text); 489 } 490 onGeocodeFinished:{ 491 if (map.geocodeModel.status == GeocodeModel.Ready) { 492 if (map.geocodeModel.count == 0) { 493 stackView.showMessage(qsTr("Geocode Error"),qsTr("Unsuccessful geocode")) 494 } else if (map.geocodeModel.count > 1) { 495 stackView.showMessage(qsTr("Ambiguous geocode"), map.geocodeModel.count + " " + 496 qsTr("results found for the given address, please specify location")) 497 } else { 498 stackView.showMessage(qsTr("Location"), geocodeMessage(),page) 499 } 500 } else if (map.geocodeModel.status == GeocodeModel.Error) { 501 stackView.showMessage(qsTr("Geocode Error"),qsTr("Unsuccessful geocode")) 502 } 503 } 504 onRouteError: stackView.showMessage(qsTr("Route Error"),qsTr("Unable to find a route for the given points"),page) 505 506 onShowGeocodeInfo: stackView.showMessage(qsTr("Location"),geocodeMessage(),page) 507 508 onErrorChanged: { 509 if (map.error != Map.NoError) { 510 var title = qsTr("ProviderError") 511 var message = map.errorString + "<br/><br/><b>" + qsTr("Try to select other provider") + "</b>" 512 if (map.error == Map.MissingRequiredParameterError) 513 message += "<br/>" + qsTr("or see") + " \'mapviewer --help\' " 514 + qsTr("how to pass plugin parameters.") 515 stackView.showMessage(title,message); 516 } 517 } 518 onShowMainMenu: mapPopupMenu.show(coordinate) 519 onShowMarkerMenu: markerPopupMenu.show(coordinate) 520 onShowRouteMenu: itemPopupMenu.show("Route",coordinate) 521 onShowPointMenu: itemPopupMenu.show("Point",coordinate) 522 onShowRouteList: stackView.showRouteListPage() 523 } 524 } 525} 526