QML Map中测距——QtLocation轻量级地图应用学习


本文转载于:QML QtLocation轻量级地图应用学习:实现测距功能


所有的热爱都要不遗余力,真正喜欢它便给它更高的优先级,和更多的时间吧!

QML其它文章请点击这里:     QT QUICK QML 学习笔记


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

猜你喜欢

转载自blog.csdn.net/qq_16504163/article/details/109379164