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


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


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

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


1.实现思路

参照网上的测面积功能,界面效果和测距差不多,在点和线的基础上多了一个填充区域。
在这里插入图片描述
点和线参照上一篇博客:QML Map中测距——QtLocation轻量级地图应用学习

填充区域使用 MapPolygon ,但是这个类接口很少,大部分操作还是借住折线 MapPolyline 来完成。

这个功能最主要的是根据坐标点的集合求面积,在网上找了很多参考代码,大部分思路是球面多边形面积计算,但是计算结果都不一样,有误差。
在这里插入图片描述
最后我用的是别人从高德的API里提取出来的函数。下面给出部分参考链接:

JS实现(首尾太近会算错)

Py实现

Java参照的高德API

根据球面面积计算公式

2. 实现代码及git链接

下面是实现效果:
在这里插入图片描述
Area组件实现代码:

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtLocation 5.12
import QtPositioning 5.12
 
// 计算地图连线围成面积
MapItemGroup{
    
    
    id: control
 
    property bool _pathClose: false
    property double areaValue: 0
    //MapPolygon很多方法没有,所以拿MapPolyline来记录坐标点
    //优化的话自定义cpp类型
    MapPolygon{
    
    
        id: item_polygon
        color: Qt.rgba(0,1,0,0.4);
        border.width: 0
        path: item_line.path
    }
    MapPolyline{
    
    
        id: item_line
        line.width: 1
        line.color: "red"
    }
 
    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"
                //Component.onDestruction: console.log("destory item");
 
                Loader{
    
    
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.top: parent.bottom
                    anchors.margins: 5
                    sourceComponent: (_pathClose&&index==(item_model.count-1))?area_comp:null_comp
                }
            }
            //通过listmodel来设置数据
            coordinate{
    
    
                latitude: latitudeval
                longitude: longitudeval
            }
            anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2)
        }
    }
 
    Component{
    
    
        id: null_comp
        Item{
    
    }
    }
    Component{
    
    
        id: area_comp
        Rectangle{
    
    
            width: area_text.width+5+5+14+5
            height: area_text.height+10
            border.color: "gray"
            Text {
    
    
                id: area_text
                x: 5
                anchors.verticalCenter: parent.verticalCenter
                text: control.areaValue+" m^2"
            }
            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: {
    
    
                        clearPath();
                    }
                }
            }
        }
    }
 
    function appendPoint(coord){
    
    
        item_model.append({
    
    "latitudeval":coord.latitude,"longitudeval":coord.longitude});
        item_line.addCoordinate(coord);
    }
 
    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(){
    
    
        control._pathClose=true;
        while(item_line.pathLength()>item_model.count){
    
    
            item_line.removeCoordinate(item_line.pathLength()-1);
        }
        if(item_line.pathLength()<3){
    
    
            clearPath();
            return;
        }
        control.areaValue=getPolygonArea(item_line.path);
        item_line.addCoordinate(item_line.path[0]);
    }
 
    function clearPath(){
    
    
        item_line.path=[];
        item_model.clear();
    }
 
    //计算方式1:https://www.cnblogs.com/c-w20140301/p/10308431.html
    //根据py代码换砖而来
    //转换为弧度
    function convertToRadian(num){
    
    
        return num*Math.PI/180;
    }
    //计算地图区域面积
    function calculatePolygonArea(path){
    
    
        let area_count=0;
        let path_len=path.length;
        if(path_len<3)
            return area_count;
        let data_list=[];
        for(let i=0;i<path_len;i++){
    
    
            area_count+=convertToRadian(path[(i+1)%path_len].longitude-path[(i)%path_len].longitude)*
                    (2+Math.sin(convertToRadian(path[(i)%path_len].latitude))+
                     Math.sin(convertToRadian(path[(i+1)%path_len].latitude)));
        }
        area_count*=6378137.0 * 6378137.0 / 2.0;
        return Math.abs(area_count);
    }
 
    //计算方式2:https://blog.csdn.net/zdb1314/article/details/80661602
    //应该是提取的高德api里的函数,命名应该是混淆加密之后的
    function getPolygonArea(path){
    
    
        let area_count=0;
        let path_len=path.length;
        if(path_len<3)
            return area_count;
        let data_list=[];
        //WGS84地球半径
        let sJ = 6378137;
        //Math.PI/180
        let Hq = 0.017453292519943295;
        let c = sJ *Hq;
        for(let i=0;i<path_len-1;i++){
    
    
            let h=path[i];
            let k=path[i+1];
            let u=h.longitude*c*Math.cos(h.latitude*Hq);
            let hhh=h.latitude*c;
            let v=k.longitude*c*Math.cos(k.latitude*Hq);
            area_count+=(u*k.latitude*c-v*hhh);
        }
        let eee=path[path_len-1].longitude*c*Math.cos(path[path_len-1].latitude*Hq);
        let g2=path[path_len-1].latitude*c;
        let k=path[0].longitude*c*Math.cos(path[0].latitude*Hq);
        area_count+=eee*path[0].latitude*c-k*g2;
 
        return Math.round(Math.abs(area_count)/2);
    }
}

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

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtLocation 5.12
import QtPositioning 5.12
 
//地图自定义
Item{
    
    
    id: control
    //地图的模式
    // 0:普通浏览
    // 1:测距
    // 2:截图
    // 3:面积
    property int mapMode: 0
    property MapArea currentArea: null
 
    property alias map: the_map
    clip: true
 
    onMapModeChanged: {
    
    
        console.log("map mode",mapMode);
        if(control.mapMode!=3&&currentArea){
    
    
            currentArea.closePath();
            currentArea=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: "Area"
            onCheckedChanged: if(checked)control.mapMode=3;
        }
    }
 
    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: "mymap" //"esri" "mapbox" "osm" "here"
 
            PluginParameter {
    
    
                name: "baseUrl"
                // 自行指定瓦片路径
                value: "file:///"+applicationDirPath+"/dianzi_gaode_ArcgisServerTiles/_alllayers"
            }
            PluginParameter {
    
    
                name: "format"
                value: "png"
            }
        }
 
        //显示缩放等级与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: {
    
    
                // 3 面积
                if(control.mapMode===3){
    
    
                    if(!currentArea){
    
    
                        currentArea=area_comp.createObject(the_map);
                        if(currentArea)
                            the_map.addMapItemGroup(currentArea);
                    }
                    if(currentArea){
    
    
                        var coord=the_map.toCoordinate(Qt.point(mouseX,mouseY),false);
                        currentArea.appendPoint(coord);
                    }
                }
            }
            onDoubleClicked: {
    
    
                // 3 面积
                if(control.mapMode===3){
    
    
                    if(currentArea){
    
    
                        currentArea.closePath();
                        currentArea=null;
                    }
                }
            }
            onPositionChanged: {
    
    
                // 3 面积
                if(control.mapMode===3){
    
    
                    if(currentArea){
    
    
                        var coord=the_map.toCoordinate(Qt.point(mouseX,mouseY),false);
                        currentArea.followMouse(coord);
                    }
                }
            }
        }
    }
 
    Component{
    
    
        id: area_comp
        MapArea{
    
    
 
        }
    }
}

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

猜你喜欢

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