MapComponent.qml Example File

mapviewer/content/map/MapComponent.qml
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** 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 Digia Plc and its Subsidiary(-ies) 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.0
import QtLocation 5.3
import QtPositioning 5.2
import QtLocation.examples 5.0

Map {
    id: map
    zoomLevel: (maximumZoomLevel - minimumZoomLevel)/2

    center {
        latitude: -27.5796
        longitude: 153.1003
    }

    // Enable pinch gestures to zoom in and out
    gesture.flickDeceleration: 3000
    gesture.enabled: true

    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
    signal resetState()

    property int lastX : -1
    property int lastY : -1
    property int pressX : -1
    property int pressY : -1
    property int jitterThreshold : 30
    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]
    signal showDistance(string distance);
    signal requestLocale()

    /* @todo
    Binding {
        target: map
        property: 'center'
        value: positionSource.position.coordinate
        when: followme
    }*/

    PositionSource{
        id: positionSource
        active: followme

        onPositionChanged: {
            map.center = positionSource.position.coordinate
        }
    }

    MapCircle {
            id: poiEightMilePlains
            center {
                latitude: -27.5758
                longitude: 153.0881
            }

            radius: 1800
            color: "green"
            border.width: 2
            border.color: "#242424"
            opacity: 0.7
        }

    MapQuickItem {
        sourceItem: Text{
            text: "Eight Mile Plains"
            color:"#242424"
            font.bold: true
            styleColor: "#ECECEC"
            style: Text.Outline
        }
        coordinate {
            latitude: -27.59
            longitude: 153.084
        }
        anchorPoint.x: 0
        anchorPoint.y: 0
    }

    MapQuickItem {
        id: poiNokia
        sourceItem: Rectangle { width: 14; height: 14; color: "#1c94fc"; border.width: 2; border.color: "#242424"; smooth: true; radius: 7 }
        coordinate {
           latitude: -27.5796
           longitude: 153.1003
        }
        opacity:0.7
        anchorPoint.x: sourceItem.width/2
        anchorPoint.y: sourceItem.height/2
    }

    MapQuickItem {
        sourceItem: Text{
            text: "Nokia"
            color:"#242424"
            font.bold: true
            styleColor: "#ECECEC"
            style: Text.Outline
        }
        coordinate: poiNokia.coordinate
        anchorPoint.x: -poiNokia.sourceItem.width * 0.5
        anchorPoint.y: poiNokia.sourceItem.height * 1.5
    }

    Slider {
        id: zoomSlider;
        minimum: map.minimumZoomLevel;
        maximum: map.maximumZoomLevel;
        opacity: 1
        visible: parent.visible
        z: map.z + 3
        anchors {
            bottom: parent.bottom;
            bottomMargin: 15; rightMargin: 10; leftMargin: 90
            left: parent.left
        }
        width: parent.width - anchors.rightMargin - anchors.leftMargin

        value: map.zoomLevel

        Binding {
            target: zoomSlider; property: "value"; value: map.zoomLevel
        }

        onValueChanged: {
            map.zoomLevel = value
            map.state=""
            map.resetState()
        }
    }

    Button {
        id: languageButton
        text: "en"
        width: 30
        height: 30
        z: map.z + 2
        anchors.bottom: zoomSlider.top
        anchors.bottomMargin: 8
        anchors.right: zoomSlider.right
        onClicked: {
            map.state = "LanguageMenu"
        }
    }

    Menu {
        id:languageMenu
        horizontalOrientation: false
        autoWidth: true
        opacity: 0
        z: map.z + 4
        anchors.bottom: languageButton.top
        anchors.right: languageButton.left
        onClicked: {
            switch (button) {
            case "en":
            case "fr": {
                setLanguage(button);
                break;
            }
            case "Other": {
                map.requestLocale()
            }
            }
            map.state = ""
        }
        Component.onCompleted: {
            addItem("en")
            addItem("fr")
            addItem("Other")
        }
    }

    property RouteQuery routeQuery: RouteQuery {}

    property RouteModel routeModel: RouteModel {
        plugin : map.plugin
        query: routeQuery

        onStatusChanged: {
            if (status == RouteModel.Ready) {
                switch (count) {
                case 0:
                    clearAll() // technically not an error
                    map.routeError()
                    break
                case 1:
                    routeInfoModel.update()
                    break
                }
            } else if (status == RouteModel.Error) {
                clearAll()
                map.routeError()
            }
        }

        function clearAll() {
            routeInfoModel.update()
        }
    }

    property GeocodeModel geocodeModel: 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
            }
        }
    }

    signal showGeocodeInfo()
    signal moveMarker()

    signal geocodeFinished()
    signal routeError()
    signal coordinatesCaptured(double latitude, double longitude)

    Component.onCompleted: {
        markers = new Array();
        mapItems = new Array();
    }

    Component {
        id: routeDelegate

        MapRoute {
            route: routeData

            line.color: routeMouseArea.containsMouse ? "lime" : "red"
            line.width: 5
            smooth: true
            opacity: 0.8
            MouseArea {
                id: routeMouseArea
                anchors.fill: parent
                hoverEnabled: false

                onPressed : {
                    map.resetState();
                    map.state = ""
                    map.lastX = mouse.x + parent.x
                    map.lastY = mouse.y + parent.y
                    map.pressX = mouse.x + parent.x
                    map.pressY = mouse.y + parent.y
                }

                onPositionChanged: {
                    if (map.state != "RoutePopupMenu" ||
                        Math.abs(map.pressX - parent.x- mouse.x ) > map.jitterThreshold ||
                        Math.abs(map.pressY - parent.y -mouse.y ) > map.jitterThreshold) {
                        map.state = ""
                    }
                    if ((mouse.button == Qt.LeftButton) & (map.state == "")) {
                        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) {
                        map.state = "RoutePopupMenu"
                    }
                }
            }
        }
    }

    Component {
        id: pointDelegate

        MapCircle {
            radius: 1000
            color: circleMouseArea.containsMouse ? "lime" : "red"
            opacity: 0.6
            center: locationData.coordinate
            MouseArea {
                anchors.fill:parent
                id: circleMouseArea
                hoverEnabled: false

                onPressed : {
                    map.resetState();
                    map.state = ""
                    map.lastX = mouse.x + parent.x
                    map.lastY = mouse.y + parent.y
                    map.pressX = mouse.x + parent.x
                    map.pressY = mouse.y + parent.y
                }

                onPositionChanged: {
                    if (map.state != "PointPopupMenu" ||
                        Math.abs(map.pressX - parent.x- mouse.x ) > map.jitterThreshold ||
                        Math.abs(map.pressY - parent.y -mouse.y ) > map.jitterThreshold) {
                        map.state = ""
                        if (pressed) parent.radius = parent.center.distanceTo(
                                         map.toCoordinate(Qt.point(mouse.x, mouse.y)))
                    }
                    if ((mouse.button == Qt.LeftButton) & (map.state == "")) {
                        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) {
                        map.state = "PointPopupMenu"
                    }
                }
            }
        }
    }

    Component {
        id: routeInfoDelegate
        Row {
            spacing: 10
            Text {
                id: indexText
                text: index + 1
                color: "#242424"
                font.bold: true
                font.pixelSize: 14
            }
            Text {
                id: instructionText
                text: instruction
                color: "#242424"
                wrapMode: Text.Wrap
                width: textArea.width - indexText.width - distanceText.width - spacing*4
                font.pixelSize: 14
            }
            Text {
                id: distanceText
                text: distance
                color: "#242424"
                font.bold: true
                font.pixelSize: 14
            }
        }
    }

    Component{
        id: routeInfoHeader
        Item {
            width: textArea.width
            height: travelTime.height + line.anchors.topMargin + line.height
            Text {
                id: travelTime
                text: routeInfoModel.travelTime
                color: "#242424"
                font.bold: true
                font.pixelSize: 14
                anchors.left: parent.left
            }
            Text {
                id: distance
                text: routeInfoModel.distance
                color: "#242424"
                font.bold: true
                font.pixelSize: 14
                anchors.right: parent.right
            }
            Rectangle {
                id: line
                color: "#242424"
                width: parent.width
                height: 2
                anchors.left: parent.left
                anchors.topMargin: 1
                anchors.top: distance.bottom
            }
        }
    }

    ListModel {
        id: routeInfoModel

        property string travelTime
        property string distance

        function update() {
            clear()
            if (routeModel.count > 0) {
                for (var i = 0; i < routeModel.get(0).segments.length; i++) {
                    append({
                        "instruction": routeModel.get(0).segments[i].maneuver.instructionText,
                        "distance": formatDistance(routeModel.get(0).segments[i].maneuver.distanceToNextInstruction)
                    });
                }
            }
            travelTime = routeModel.count == 0 ? "" : formatTime(routeModel.get(0).travelTime)
            distance = routeModel.count == 0 ? "" : formatDistance(routeModel.get(0).distance)
        }
    }

    MapItemView {
        model: routeModel
        delegate: routeDelegate
        autoFitViewport: true
    }

    MapItemView {
        model: geocodeModel
        delegate: pointDelegate
    }

    Item {
        id: infoTab
        parent: scale.parent
        z: map.z + 2
        height: parent.height - 180
        width: parent.width
        x: -5 - infoRect.width
        y: 60
        visible: (routeInfoModel.count > 0)
        Image {
            id: catchImage
            source: "../../resources/catch.png"
            anchors.verticalCenter: parent.verticalCenter
            anchors.right: parent.right
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    if (infoTab.x == -5) infoTab.x -= infoRect.width
                    else infoTab.x = -5
                    map.state = ""
                }
            }
        }

        Behavior on x {
            PropertyAnimation { properties: "x"; duration: 300; easing.type: Easing.InOutQuad }
        }

        Rectangle {
            id: infoRect
            width: parent.width - catchImage.sourceSize.width
            height: parent.height
            color: "#ECECEC"
            opacity: 1
            radius: 5
            MouseArea {
                anchors.fill: parent
                hoverEnabled: false
            }
            Item {
                id: textArea
                anchors.left: parent.left
                anchors.leftMargin: 10
                anchors.top: parent.top
                anchors.topMargin: 10
                width: parent.width -15
                height: parent.height - 20
                ListView {
                    id: routeInfoView
                    model: routeInfoModel
                    delegate: routeInfoDelegate
                    header: routeInfoHeader
                    anchors.fill: parent
                    clip: true
                }
            }
        }
    }

    Item {//scale
        id: scale
        parent: zoomSlider.parent
        visible: scaleText.text != "0 m"
        z: map.z + 2
        opacity: 0.6
        anchors {
            bottom: zoomSlider.top;
            bottomMargin: 8;
            left: zoomSlider.left
        }
        Image {
            id: scaleImageLeft
            source: "../../resources/scale_end.png"
            anchors.bottom: parent.bottom
            anchors.left: parent.left
        }
        Image {
            id: scaleImage
            source: "../../resources/scale.png"
            anchors.bottom: parent.bottom
            anchors.left: scaleImageLeft.right
        }
        Image {
            id: scaleImageRight
            source: "../../resources/scale_end.png"
            anchors.bottom: parent.bottom
            anchors.left: scaleImage.right
        }
        Text {
            id: scaleText
            color: "#004EAE"
            horizontalAlignment: Text.AlignHCenter
            anchors.bottom: parent.bottom
            anchors.left: parent.left
            anchors.bottomMargin: 3
            text: "0 m"
            font.pixelSize: 14
        }
        Component.onCompleted: {
            map.calculateScale();
        }
    }

    Timer {
        id: scaleTimer
        interval: 100
        running: false
        repeat: false
        onTriggered: {
            map.calculateScale()
        }
    }

    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()
    }

    Menu {
        id: markerMenu
        horizontalOrientation: false
        autoWidth: true
        z: map.z + 4
        opacity: 0

        width: 150
        x: 0
        y: 0
        onClicked: {
            map.state = ""
            switch (button) {
                case "Delete": {//remove marker
                    map.deleteMarker(currentMarker)
                    break;
                }
                case "Move to": {//move marker
                    map.moveMarker()
                    break;
                }
                case "Coordinates": {//show marker's coordinates
                    map.coordinatesCaptured(markers[currentMarker].coordinate.latitude, markers[currentMarker].coordinate.longitude)
                    break;
                }
                case "Distance to next point": {
                    showDistance(formatDistance(map.markers[currentMarker].coordinate.distanceTo(map.markers[currentMarker+1].coordinate)));
                    break;
                }
                case "Route to next points"://calculate route
                case "Route to next point": {
                    map.calculateRoute()
                    break;
                }
                case "Draw...": {
                    map.drawItemPopup()
                    break;
                }
            }
        }
    }

    Menu {
        id: drawMenu
        horizontalOrientation: false
        autoWidth: true
        z: map.z + 4
        opacity: 0

        width: 150
        x: 0
        y: 0
        onClicked: {
            map.state = ""
            switch (button) {
            case "Polyline": {
                addGeoItem("PolylineItem")
                break;
            }

            case "Rectangle": {
                addGeoItem("RectangleItem")
                break;
            }

            case "Circle": {
                addGeoItem("CircleItem")
                break;
            }

            case "Polygon": {
                addGeoItem("PolygonItem")
                break;
            }

            case "Image": {
                addGeoItem("ImageItem")
                break;
            }

            case "Video": {
                addGeoItem("VideoItem")
                break;
            }

            case "3D QML Item": {
                addGeoItem("3dItem")
                break;
            }
            }
        }
    }

    Menu {
        id: popupMenu
        horizontalOrientation: false
        autoWidth: true
        z: map.z + 4
        opacity: 0

        width: 150
        x: 0
        y: 0

        onClicked: {
            switch (button) {
            case "Add Marker": {
                addMarker()
                break;
            }
            case "Get coordinate": {
                map.coordinatesCaptured(mouseArea.lastCoordinate.latitude, mouseArea.lastCoordinate.longitude)
                break;
            }
            case "Fit Viewport To Map Items": {
                map.fitViewportToMapItems()
                break;
            }

            case "Delete all markers": {
                deleteMarkers()
                break;
            }

            case "Delete all items": {
                deleteMapItems()
                break;
            }
            }
            map.state = ""
        }
    }

    Menu {
        id: routeMenu
        horizontalOrientation: false
        autoWidth: true
        z: map.z + 4
        opacity: 0

        width: 150
        x: 0
        y: 0

        onClicked: {
            switch (button) {
                case "Delete": {//delete route
                    routeModel.reset()
                    routeInfoModel.update()
                    break;
                }
            }
            map.state = ""
        }
        Component.onCompleted: {
            addItem("Delete")
        }
    }

    Menu {
        id: pointMenu
        horizontalOrientation: false
        autoWidth: true
        z: map.z + 4
        opacity: 0

        width: 150
        x: 0
        y: 0

        onClicked: {
            switch (button) {
                case "Info": {
                    map.showGeocodeInfo()
                    break;
                }
                case "Delete": {
                    geocodeModel.reset()
                    break;
                }
            }
            map.state = ""
        }
        Component.onCompleted: {
            addItem("Info")
            addItem("Delete")
        }
    }

    Rectangle {
        id: infoLabel
        width: backgroundRect.width + 10
        height: infoText.height + 5
        y: 440
        anchors.left: map.left
        z: map.z + 1
        color: "dimgrey"
        opacity: (infoText.text !="") ? 0.8 : 0

        Behavior on opacity {
            NumberAnimation { duration: 200 }
        }
        Text {
            id: infoText
            width: parent.width
            elide: Text.ElideLeft
            maximumLineCount: 4
            wrapMode: Text.Wrap
            font.bold: true
            font.pixelSize: 14
            style: Text.Raised;
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.verticalCenter: parent.verticalCenter
            anchors.leftMargin: 5
            anchors.rightMargin: 5
            color: "white"
        }
    }

    MouseArea {
        id: mouseArea
        property variant lastCoordinate
        anchors.fill: parent

        onPressed : {
            map.resetState();
            map.state = ""
            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))
            //            if (mouse.button == Qt.MiddleButton)
            //                addMarker()
        }

        onPositionChanged: {
            if (map.state != "PopupMenu" ||
                Math.abs(map.pressX - mouse.x ) > map.jitterThreshold ||
                Math.abs(map.pressY - mouse.y ) > map.jitterThreshold) {
                map.state = ""
            }
            if ((mouse.button == Qt.LeftButton) & (map.state == "")) {
                //                if ((map.lastX != -1) && (map.lastY != -1)) {
                //                    var dx = mouse.x - map.lastX
                //                    var dy = mouse.y - map.lastY
                //                    map.pan(-dx, -dy)
                //                }
                map.lastX = mouse.x
                map.lastY = mouse.y
            }
        }

        onDoubleClicked: {
            map.center = map.toCoordinate(Qt.point(mouse.x, mouse.y))
            if (mouse.button == Qt.LeftButton){
                map.zoomLevel += 1
            } else if (mouse.button == Qt.RightButton) map.zoomLevel -= 1
            lastX = -1
            lastY = -1
        }

        onPressAndHold:{
            if (Math.abs(map.pressX - mouse.x ) < map.jitterThreshold
                    && Math.abs(map.pressY - mouse.y ) < map.jitterThreshold) {
                popupMenu.clear()
                popupMenu.addItem("Add Marker")
                popupMenu.addItem("Get coordinate")
                popupMenu.addItem("Fit Viewport To Map Items")

                if (map.markers.length>0) {
                    popupMenu.addItem("Delete all markers")
                }

                if (map.mapItems.length>0) {
                    popupMenu.addItem("Delete all items")
                }
                map.state = "PopupMenu"
            }
          }
        }

    Keys.onPressed: {
        if ((event.key == Qt.Key_Plus) || (event.key == Qt.Key_VolumeUp)) {
            map.zoomLevel += 1
        } else if ((event.key == Qt.Key_Minus) || (event.key == Qt.Key_VolumeDown)){
            map.zoomLevel -= 1
        }
    }

    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 = 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 markerPopup(){
        var array
        var length = map.markers.length

        markerMenu.clear()
        markerMenu.addItem("Delete")
        markerMenu.addItem("Coordinates")
        markerMenu.addItem("Move to")
        markerMenu.addItem("Draw...")

        if (currentMarker == length-2){
            markerMenu.addItem("Route to next point")
            markerMenu.addItem("Distance to next point")

        }
        if (currentMarker < length-2){
            markerMenu.addItem("Route to next points")
            markerMenu.addItem("Distance to next point")
        }
        map.state = "MarkerPopupMenu"
    }

    function drawItemPopup(){
        var array
        var length = map.markers.length

        drawMenu.clear()

        drawMenu.addItem("Image")
        drawMenu.addItem("Video")
        drawMenu.addItem("3D QML Item")

        if (currentMarker <= length-2){
            drawMenu.addItem("Rectangle")
            drawMenu.addItem("Circle")
            drawMenu.addItem("Polyline")
        }
        if (currentMarker < length-2){
            drawMenu.addItem("Polygon")
        }
        map.state = "DrawItemMenu"
    }

    function calculateRoute(){
        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 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
    }

    function setLanguage(lang) {
        map.plugin.locales = lang;
        if (map.plugin.locales.length  >  0) {
            languageButton.text = map.plugin.locales[0];
        }
    }

    // states of map
    states: [
        State {
            name: "PopupMenu"
            PropertyChanges { target: popupMenu; opacity: 1}
            PropertyChanges { target: popupMenu; x: ((map.lastX + popupMenu.width > map.width) ? map.width - popupMenu.width : map.lastX)}
            PropertyChanges { target: popupMenu; y: ((map.lastY + popupMenu.height > map.height - 40) ? map.height - popupMenu.height - 40 : map.lastY)}
        },
        State {
            name: "MarkerPopupMenu"
            PropertyChanges { target: markerMenu; opacity: 1}
            PropertyChanges { target: markerMenu; x: ((markers[currentMarker].lastMouseX + markerMenu.width > map.width) ? map.width - markerMenu.width : markers[currentMarker].lastMouseX )}
            PropertyChanges { target: markerMenu; y: ((markers[currentMarker].lastMouseY + markerMenu.height > map.height - 40) ? map.height - markerMenu.height - 40 : markers[currentMarker].lastMouseY)}
        },
        State {
            name: "DrawItemMenu"
            PropertyChanges { target: drawMenu; opacity: 1}
            PropertyChanges { target: drawMenu; x: ((markers[currentMarker].lastMouseX + drawMenu.width > map.width) ? map.width - drawMenu.width : markers[currentMarker].lastMouseX )}
            PropertyChanges { target: drawMenu; y: ((markers[currentMarker].lastMouseY + drawMenu.height > map.height - 40) ? map.height - drawMenu.height - 40 : markers[currentMarker].lastMouseY)}
        },
        State {
            name: "RoutePopupMenu"
            PropertyChanges { target: routeMenu; opacity: 1}
            PropertyChanges { target: routeMenu; x: ((map.lastX + routeMenu.width > map.width) ? map.width - routeMenu.width : map.lastX)}
            PropertyChanges { target: routeMenu; y: ((map.lastY + routeMenu.height > map.height - 40) ? map.height - routeMenu.height - 40 : map.lastY)}
        },
        State {
            name: "PointPopupMenu"
            PropertyChanges { target: pointMenu; opacity: 1}
            PropertyChanges { target: pointMenu; x: ((map.lastX + pointMenu.width > map.width) ? map.width - pointMenu.width : map.lastX)}
            PropertyChanges { target: pointMenu; y: ((map.lastY + pointMenu.height > map.height - 40) ? map.height - pointMenu.height - 40 : map.lastY)}
        },
        State {
            name: "LanguageMenu"
            PropertyChanges { target: languageMenu; opacity: 1}
        }
    ]
}

© 2015 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.