mapviewer.qml Example File

mapviewer/mapviewer.qml
 /****************************************************************************
 **
 ** Copyright (C) 2017 The Qt Company Ltd.
 ** Contact: https://www.qt.io/licensing/
 **
 ** This file is part of the examples of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:BSD$
 ** Commercial License Usage
 ** Licensees holding valid commercial Qt licenses may use this file in
 ** accordance with the commercial license agreement provided with the
 ** Software or, alternatively, in accordance with the terms contained in
 ** a written agreement between you and The Qt Company. For licensing terms
 ** and conditions see https://www.qt.io/terms-conditions. For further
 ** information use the contact form at https://www.qt.io/contact-us.
 **
 ** BSD License Usage
 ** Alternatively, 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 "map"
 import "menus"
 import "helper.js" as Helper

 ApplicationWindow {
     id: appWindow
     property variant map
     property variant minimap
     property variant parameters

     //defaults
     property variant fromCoordinate: QtPositioning.coordinate(59.9483, 10.7695)
     property variant toCoordinate: QtPositioning.coordinate(59.9645, 10.671)

     function createMap(provider)
     {
         var plugin

         if (parameters && parameters.length>0)
             plugin = Qt.createQmlObject ('import QtLocation 5.6; Plugin{ name:"' + provider + '"; parameters: appWindow.parameters}', appWindow)
         else
             plugin = Qt.createQmlObject ('import QtLocation 5.6; 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 (map) {
             zoomLevel = map.zoomLevel
             tilt = map.tilt
             bearing = map.bearing
             fov = map.fieldOfView
             center = map.center
             panelExpanded = map.slidersExpanded
             map.destroy()
         }

         map = mapComponent.createObject(page);
         map.plugin = plugin;

         if (zoomLevel != null) {
             map.tilt = tilt
             map.bearing = bearing
             map.fieldOfView = fov
             map.zoomLevel = zoomLevel
             map.center = center
             map.slidersExpanded = panelExpanded
         } else {
             // Use an integer ZL to enable nearest interpolation, if possible.
             map.zoomLevel = Math.floor((map.maximumZoomLevel - map.minimumZoomLevel)/2)
             // defaulting to 45 degrees, if possible.
             map.fieldOfView = Math.min(Math.max(45.0, map.minimumFieldOfView), map.maximumFieldOfView)
         }

         map.forceActiveFocus()
     }

     function getPlugins()
     {
         var plugin = Qt.createQmlObject ('import QtLocation 5.6; Plugin {}', appWindow)
         var myArray = new Array()
         for (var i = 0; i<plugin.availableServiceProviders.length; i++) {
             var tempPlugin = Qt.createQmlObject ('import QtLocation 5.6; 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 5.6; 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

     Address {
         id :fromAddress
         street: "Sandakerveien 116"
         city: "Oslo"
         country: "Norway"
         state : ""
         postalCode: "0484"
     }

     Address {
         id: toAddress
         street: "Holmenkollveien 140"
         city: "Oslo"
         country: "Norway"
         postalCode: "0791"
     }

     MainMenu {
         id: mainMenu

         function toggleMiniMapState()
         {
             if (minimap) {
                 minimap.destroy()
                 minimap = null
             } else {
                 minimap = Qt.createQmlObject ('import "map"; MiniMap{ z: map.z + 2 }', map)
             }
         }

         function setLanguage(lang)
         {
             map.plugin.locales = lang;
             stackView.pop(page)
         }

         onSelectProvider: {
             stackView.pop()
             for (var i = 0; i < providerMenu.items.length; i++) {
                 providerMenu.items[i].checked = providerMenu.items[i].text === providerName
             }

             createMap(providerName)
             if (map.error === Map.NoError) {
                 selectMapType(map.activeMapType)
                 toolsMenu.createMenu(map);
             } else {
                 mapTypeMenu.clear();
                 toolsMenu.clear();
             }
         }

         onSelectMapType: {
             stackView.pop(page)
             for (var i = 0; i < mapTypeMenu.items.length; i++) {
                 mapTypeMenu.items[i].checked = mapTypeMenu.items[i].text === mapType.name
             }
             map.activeMapType = mapType
         }

         onSelectTool: {
             switch (tool) {
             case "AddressRoute":
                 stackView.pop({item:page, immediate: true})
                 stackView.push({ item: Qt.resolvedUrl("forms/RouteAddress.qml") ,
                                    properties: { "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)
                 break
             case "CoordinateRoute":
                 stackView.pop({item:page, immediate: true})
                 stackView.push({ item: Qt.resolvedUrl("forms/RouteCoordinate.qml") ,
                                    properties: { "toCoordinate": toCoordinate,
                                        "fromCoordinate": fromCoordinate}})
                 stackView.currentItem.showRoute.connect(map.calculateCoordinateRoute)
                 stackView.currentItem.closeForm.connect(stackView.closeForm)
                 break
             case "Geocode":
                 stackView.pop({item:page, immediate: true})
                 stackView.push({ item: Qt.resolvedUrl("forms/Geocode.qml") ,
                                    properties: { "address": fromAddress}})
                 stackView.currentItem.showPlace.connect(map.geocode)
                 stackView.currentItem.closeForm.connect(stackView.closeForm)
                 break
             case "RevGeocode":
                 stackView.pop({item:page, immediate: true})
                 stackView.push({ item: Qt.resolvedUrl("forms/ReverseGeocode.qml") ,
                                    properties: { "coordinate": fromCoordinate}})
                 stackView.currentItem.showPlace.connect(map.geocode)
                 stackView.currentItem.closeForm.connect(stackView.closeForm)
                 break
             case "Language":
                 stackView.pop({item:page, immediate: true})
                 stackView.push({ item: Qt.resolvedUrl("forms/Locale.qml") ,
                                    properties: { "locale":  map.plugin.locales[0]}})
                 stackView.currentItem.selectLanguage.connect(setLanguage)
                 stackView.currentItem.closeForm.connect(stackView.closeForm)
                 break
             case "Clear":
                 map.clearData()
                 break
             case "Prefetch":
                 map.prefetchData()
                 break
             default:
                 console.log("Unsupported operation")
             }
         }

         onToggleMapState: {
             stackView.pop(page)
             switch (state) {
             case "FollowMe":
                 map.followme = !map.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 = map.markers.length
             mapPopupMenu.mapItemsCount = map.mapItems.length
             mapPopupMenu.update()
             mapPopupMenu.popup()
         }

         onItemClicked: {
             stackView.pop(page)
             switch (item) {
             case "addMarker":
                 map.addMarker()
                 break
             case "getCoordinate":
                 map.coordinatesCaptured(coordinate.latitude, coordinate.longitude)
                 break
             case "fitViewport":
                 map.fitViewportToMapItems()
                 break
             case "deleteMarkers":
                 map.deleteMarkers()
                 break
             case "deleteItems":
                 map.deleteMapItems()
                 break
             default:
                 console.log("Unsupported operation")
             }
         }
     }

     MarkerPopupMenu {
         id: markerPopupMenu

         function show(coordinate)
         {
             stackView.pop(page)
             markerPopupMenu.markersCount = map.markers.length
             markerPopupMenu.update()
             markerPopupMenu.popup()
         }

         function askForCoordinate()
         {
             stackView.push({ item: Qt.resolvedUrl("forms/ReverseGeocode.qml") ,
                                properties: { "title": qsTr("New Coordinate"),
                                    "coordinate":   map.markers[map.currentMarker].coordinate}})
             stackView.currentItem.showPlace.connect(moveMarker)
             stackView.currentItem.closeForm.connect(stackView.closeForm)
         }

         function moveMarker(coordinate)
         {
             map.markers[map.currentMarker].coordinate = coordinate;
             map.center = coordinate;
             stackView.pop(page)
         }

         onItemClicked: {
             stackView.pop(page)
             switch (item) {
             case "deleteMarker":
                 map.deleteMarker(map.currentMarker)
                 break;
             case "getMarkerCoordinate":
                 map.coordinatesCaptured(map.markers[map.currentMarker].coordinate.latitude, map.markers[map.currentMarker].coordinate.longitude)
                 break;
             case "moveMarkerTo":
                 askForCoordinate()
                 break;
             case "routeToNextPoint":
             case "routeToNextPoints":
                 map.calculateMarkerRoute()
                 break
             case "distanceToNextPoint":
                 var coordinate1 = map.markers[currentMarker].coordinate;
                 var coordinate2 = map.markers[currentMarker+1].coordinate;
                 var distance = Helper.formatDistance(coordinate1.distanceTo(coordinate2));
                 stackView.showMessage(qsTr("Distance"),"<b>" + qsTr("Distance:") + "</b> " + distance)
                 break
             case "drawImage":
                 map.addGeoItem("ImageItem")
                 break
             case "drawRectangle":
                 map.addGeoItem("RectangleItem")
                 break
             case "drawCircle":
                 map.addGeoItem("CircleItem")
                 break;
             case "drawPolyline":
                 map.addGeoItem("PolylineItem")
                 break;
             case "drawPolygonMenu":
                 map.addGeoItem("PolygonItem")
                 break
             default:
                 console.log("Unsupported operation")
             }
         }
     }

     ItemPopupMenu {
         id: itemPopupMenu

         function show(type,coordinate)
         {
             stackView.pop(page)
             itemPopupMenu.type = type
             itemPopupMenu.update()
             itemPopupMenu.popup()
         }

         onItemClicked: {
             stackView.pop(page)
             switch (item) {
             case "showRouteInfo":
                 stackView.showRouteListPage()
                 break;
             case "deleteRoute":
                 map.routeModel.reset();
                 break;
             case "showPointInfo":
                 map.showGeocodeInfo()
                 break;
             case "deletePoint":
                 map.geocodeModel.reset()
                 break;
             default:
                 console.log("Unsupported operation")
             }
         }
     }

     StackView {
         id: stackView
         anchors.fill: parent
         focus: true
         initialItem: Item {
             id: page

             Text {
                 visible: !supportsSsl && map && map.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({ item: Qt.resolvedUrl("forms/Message.qml") ,
                                properties: {
                                    "title" : title,
                                    "message" : message,
                                    "backPage" : backPage
                                }})
             currentItem.closeForm.connect(closeMessage)
         }

         function closeMessage(backPage)
         {
             pop(backPage)
         }

         function closeForm()
         {
             pop(page)
         }

         function showRouteListPage()
         {
             push({ item: Qt.resolvedUrl("forms/RouteList.qml") ,
                                properties: {
                                    "routeModel" : map.routeModel
                                }})
             currentItem.closeForm.connect(closeForm)
         }
     }

     Component {
         id: mapComponent

         MapComponent{
             width: page.width
             height: page.height
             onFollowmeChanged: mainMenu.isFollowMe = map.followme
             onSupportedMapTypesChanged: mainMenu.mapTypeMenu.createMenu(map)
             onCoordinatesCaptured: {
                 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 (map.geocodeModel.status == GeocodeModel.Ready) {
                     if (map.geocodeModel.count == 0) {
                         stackView.showMessage(qsTr("Geocode Error"),qsTr("Unsuccessful geocode"))
                     } else if (map.geocodeModel.count > 1) {
                         stackView.showMessage(qsTr("Ambiguous geocode"), map.geocodeModel.count + " " +
                                               qsTr("results found for the given address, please specify location"))
                     } else {
                         stackView.showMessage(qsTr("Location"), geocodeMessage(),page)
                     }
                 } else if (map.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)

             onErrorChanged: {
                 if (map.error != Map.NoError) {
                     var title = qsTr("ProviderError")
                     var message =  map.errorString + "<br/><br/><b>" + qsTr("Try to select other provider") + "</b>"
                     if (map.error == Map.MissingRequiredParameterError)
                         message += "<br/>" + qsTr("or see") + " \'mapviewer --help\' "
                                 + qsTr("how to pass plugin parameters.")
                     stackView.showMessage(title,message);
                 }
             }
             onShowMainMenu: mapPopupMenu.show(coordinate)
             onShowMarkerMenu: markerPopupMenu.show(coordinate)
             onShowRouteMenu: itemPopupMenu.show("Route",coordinate)
             onShowPointMenu: itemPopupMenu.show("Point",coordinate)
             onShowRouteList: stackView.showRouteListPage()
         }
     }
 }