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.0
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
            MapMouseArea {
                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
            MapMouseArea {
                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(mouseToCoordinate(mouse))
                    }
                    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"
        }
    }

    MapMouseArea {
        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 = mouseArea.mouseToCoordinate(mouse)
            //            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 = mouseArea.mouseToCoordinate(mouse)
            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

        //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}
        }
    ]
}