1.实现思路
参照网上的测面积功能,界面效果和测距差不多,在点和线的基础上多了一个填充区域。
点和线参照上一篇博客:https://blog.csdn.net/gongjianbo1992/article/details/103674047
填充区域使用 MapPolygon ,但是这个类接口很少,大部分操作还是借住折线 MapPolyline 来完成。
这个功能最主要的是根据坐标点的集合求面积,在网上找了很多参考代码,大部分思路是球面多边形面积计算,但是计算结果都不一样,有误差。
最后我用的是别人从高德的API里提取出来的函数。下面给出部分参考链接:
JS实现(首尾太近会算错):https://blog.csdn.net/neil89/article/details/49331641
Py实现:https://www.cnblogs.com/c-w20140301/p/10308431.html
Java参照的高德API:https://blog.csdn.net/zdb1314/article/details/80661602
根据球面面积计算公式:https://wenku.baidu.com/view/4e213e27162ded630b1c59eef8c75fbfc67d94e2.html
2.实现代码及git链接
下面是实现效果:
Area组件实现代码:
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtLocation 5.12
import QtPositioning 5.12// 计算地图连线围成面积
MapItemGroup{id: controlproperty bool _pathClose: falseproperty double areaValue: 0//MapPolygon很多方法没有,所以拿MapPolyline来记录坐标点//优化的话自定义cpp类型MapPolygon{id: item_polygoncolor: Qt.rgba(0,1,0,0.4);border.width: 0path: item_line.path}MapPolyline{id: item_lineline.width: 1line.color: "red"}MapItemView{id: item_viewadd: Transition {}remove: Transition {}model: ListModel{id: item_model}delegate: MapQuickItem{id: ietm_delegatesourceItem: Rectangle {width: 14height: 14radius: 7color: "white"border.width: 2border.color: "red"//Component.onDestruction: console.log("destory item");Loader{anchors.horizontalCenter: parent.horizontalCenteranchors.top: parent.bottomanchors.margins: 5sourceComponent: (_pathClose&&index==(item_model.count-1))?area_comp:null_comp}}//通过listmodel来设置数据coordinate{latitude: latitudevallongitude: longitudeval}anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2)}}Component{id: null_compItem{}}Component{id: area_compRectangle{width: area_text.width+5+5+14+5height: area_text.height+10border.color: "gray"Text {id: area_textx: 5anchors.verticalCenter: parent.verticalCentertext: control.areaValue+" m^2"}Rectangle{width: 14height: 14anchors.right: parent.rightanchors.rightMargin: 5anchors.verticalCenter: parent.verticalCenterborder.color: "red"Text {color: "red"anchors.centerIn: parenttext: "+"rotation: 45}MouseArea{anchors.fill: parentonClicked: {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/180let 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: 0property MapArea currentArea: nullproperty alias map: the_mapclip: trueonMapModeChanged: {console.log("map mode",mapMode);if(control.mapMode!=3&¤tArea){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: trueonCheckedChanged: if(checked)control.mapMode=0;}RadioButton{text: "Area"onCheckedChanged: if(checked)control.mapMode=3;}}Map {id: the_mapanchors.fill: parentanchors.topMargin: 40minimumZoomLevel: 4maximumZoomLevel: 16zoomLevel: 10center: QtPositioning.coordinate(30.6562, 104.0657)plugin: Plugin { //这里使用了自定义plugin请忽略name: "mymap" //"esri" "mapbox" "osm" "here"PluginParameter {name: "baseUrl"// 自行指定瓦片路径value: "file:///"+applicationDirPath+"/dianzi_gaode_ArcgisServerTiles/_alllayers"}PluginParameter {name: "format"value: "png"}}//显示缩放等级与centerRectangle{anchors{left: the_map.leftbottom: the_map.bottommargins: 5}width: content.width+20height: content.height+10Text {id: contentx: 10y: 5font.pixelSize: 14text: "Zoom Level "+Math.floor(the_map.zoomLevel)+" Center:"+the_map.center.latitude+" "+the_map.center.longitude}}MouseArea{id: map_mouseanchors.fill: parentenabled: control.mapMode!=0//画了一个点后跟随鼠标,除非双击hoverEnabled: trueonClicked: {// 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_compMapArea{}}
}
代码 github 链接:https://github.com/gongjianbo/MyQtLocation