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: 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&¤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: 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