QML QtLocation地图应用学习-2:实现测距功能

1.实现思路

参照百度or高德地图的测距功能,主要由两种元素组成,标记点和连线。

其中连线很好解决,Qt 提供了 MapPolyline 类型,可以用来绘制折线,并且提供了增删的便捷函数:

对于标记点,我们可以用 MapQuickItem 来实现,里面包含一个标记圆圈、长度Text、删除按钮。对于一组 MapQuickItem,我使用 MapItemView 来管理。而求两个坐标点的距离,直接调用的  coordinate 类型的 distanceTo 函数,总长直接遍历求距离就行了。

接下来就是封装一个 Ruler 的组件,实现多次测距的绘制。每次开始测距时,通过 createObject 动态创建一个 Ruler 组件,然后往里面添加坐标点。坐标点我是在 Map 上放了一个 MouseArea ,然后用 Map 的 toCoordinate 方法把 point 转换成坐标点。

此外,如果要自己计算两点距离,可以参考网上球面两点距离的公式,如:

上面参考的表示法应该是十进制的,如果是度分秒的形式,可以先转化下:

2.实现代码及git链接

下面是实现效果:

有一点还没解决,那就是 MapPolyline 这种图元设置 layer 实现平滑效果的话,放大之后很卡。另外就是,一个 Ruler 所有元素被删之后我也没有析构它。

Ruler组件的实现代码:

//MapRuler.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtLocation 5.12
import QtPositioning 5.12

// 一次测距里包含多个标记点以及连线
MapItemGroup{
    id: control

    MapPolyline {
        id: item_line
        line.color: "red"
        line.width: 2
        //平滑后放大有点卡
        //layer.enabled: true
        //layer.smooth: true
        //layer.samples: 8
        function getDistanceCount(){
            var distance_count=0;
            for(var i=1;i<pathLength();i++){
                distance_count+=item_line.coordinateAt(i).distanceTo(item_line.coordinateAt(i-1));
            }
            return Math.round(distance_count);
        }
    }

    MapItemView{
        id: item_view
        add: Transition {}
        remove: Transition {}
        model: ListModel{
            id: item_model
        }
        delegate: MapQuickItem{
            id: ietm_delegate
            sourceItem: Rectangle {
                width: 14
                height: 14
                radius: 7
                color: "white"
                border.width: 2
                border.color: "red"
                Rectangle{
                    anchors.left: parent.right
                    anchors.top: parent.bottom
                    width: item_text.width+5+5+14+5
                    height: item_text.height+10
                    border.color: "gray"
                    Text {
                        id: item_text
                        x: 5
                        anchors.verticalCenter: parent.verticalCenter
                        text: index<=0
                              ? "起点"
                              : (index==item_model.count-1)
                                ? ("总长 "+item_line.getDistanceCount()/1000+" km")
                                :(Math.round(ietm_delegate.coordinate.distanceTo(item_line.coordinateAt(index-1)))/1000+" km")
                    }
                    Rectangle{
                        width: 14
                        height: 14
                        anchors.right: parent.right
                        anchors.rightMargin: 5
                        anchors.verticalCenter: parent.verticalCenter
                        border.color: "red"
                        Text {
                            color: "red"
                            anchors.centerIn: parent
                            text: "+"
                            rotation: 45
                        }
                        MouseArea{
                            anchors.fill: parent
                            onClicked: {
                                //最后一个全部删除,否则一个一个的删除
                                //为0的时候发送信号给group请求删除
                                if(index==item_model.count-1){
                                    item_line.path=[];
                                    item_model.clear();
                                    //control.destroy();
                                }else{
                                    item_line.removeCoordinate(index);
                                    item_model.remove(index);
                                }
                            }
                        }
                    }
                }

                //Component.onDestruction: console.log("destory item");
            }
            //通过listmodel来设置数据
            coordinate{
                latitude: latitudeval
                longitude: longitudeval
            }
            anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2)
        }
    }

    function appendPoint(coord){
        //var coord=the_map.toCoordinate(Qt.point(mouseX,mouseY),false);
        //console.log("area",coord.latitude,coord.longitude);
        item_model.append({"latitudeval":coord.latitude,"longitudeval":coord.longitude});
        item_line.addCoordinate(coord);
        //mouse_area._closePath=false;
        //console.log("ruler append",item_model.count,item_line.pathLength())
    }

    function followMouse(coord){
        if(item_line.pathLength()<=0)
            return;
        if(item_line.pathLength()===item_model.count){
            item_line.addCoordinate(coord);
        }else{
            item_line.replaceCoordinate(item_line.pathLength()-1,coord);
        }
    }

    function closePath(){
        while(item_line.pathLength()>item_model.count){
            item_line.removeCoordinate(item_line.pathLength()-1);
        }
    }
}

在 Window 中调用下面组件来展示 Demo:

//Demo.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtLocation 5.12
import QtPositioning 5.12

//地图自定义
Item{
    id: control
    //地图的模式
    // 0:普通浏览
    // 1:测距
    property int mapMode: 0
    property MapRuler currentRuler: null

    property alias map: the_map
    clip: true

    onMapModeChanged: {
        console.log("map mode",mapMode);
        if(control.mapMode!=1&&currentRuler){
            currentRuler.closePath();
            currentRuler=null;
        }
    }

    //缩放等级,维度,精度
    function viewPoint(zoomLevel,latitude,longitude){
        the_map.zoomLevel=zoomLevel;
        the_map.center=QtPositioning.coordinate(latitude, longitude);
    }


    Row{
        RadioButton{
            text: "Normal"
            checked: true
            onCheckedChanged: if(checked)control.mapMode=0;
        }
        RadioButton{
            text: "Ruler"
            onCheckedChanged: if(checked)control.mapMode=1;
        }
    }

    Map {
        id: the_map
        anchors.fill: parent
        anchors.topMargin: 40
        minimumZoomLevel: 4
        maximumZoomLevel: 16
        zoomLevel: 10
        center: QtPositioning.coordinate(30.6562, 104.0657)

        plugin: Plugin {
            name: "esri" //"esri" "mapbox" "osm" "here"
        }

        //显示缩放等级与center
        Rectangle{
            anchors{
                left: the_map.left
                bottom: the_map.bottom
                margins: 5
            }

            width: content.width+20
            height: content.height+10
            Text {
                id: content
                x: 10
                y: 5
                font.pixelSize: 14
                text: "Zoom Level "+Math.floor(the_map.zoomLevel)+" Center:"+the_map.center.latitude+"  "+the_map.center.longitude

            }
        }

        MouseArea{
            id: map_mouse
            anchors.fill: parent
            enabled: control.mapMode!=0

            //画了一个点后跟随鼠标,除非双击
            hoverEnabled: true
            onClicked: {
                // 1 测距
                if(control.mapMode===1){
                    if(!currentRuler){
                        currentRuler=ruler_comp.createObject(the_map);
                        if(currentRuler)
                            the_map.addMapItemGroup(currentRuler);
                    }
                    if(currentRuler){
                        var coord=the_map.toCoordinate(Qt.point(mouseX,mouseY),false);
                        currentRuler.appendPoint(coord);
                    }
                }
            }
            onDoubleClicked: {
                // 1 测距
                if(control.mapMode===1){
                    if(currentRuler){
                        currentRuler.closePath();
                        currentRuler=null;
                    }
                }
            }
            onPositionChanged: {
                // 1 测距
                if(control.mapMode===1){
                    if(currentRuler){
                        var coord=the_map.toCoordinate(Qt.point(mouseX,mouseY),false);
                        currentRuler.followMouse(coord);
                    }
                }
            }
        }
    }

    Component{
        id: ruler_comp
        MapRuler{

        }
    }
}

代码 github 链接:https://github.com/gongjianbo/MyQtLocation

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚建波

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值