QGC 增加测距功能

本文介绍在QGroundControl中实现测距功能的方法,通过C++控制类与QML显示的结合,实现在地图上测量两点间距离的功能,包括添加坐标、清除坐标及计算总距离。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

一,参照百度地图,在QGC上实现测距功能,效果如下:

 

二,实现思路

三,主要代码实现

(1),C++ 控制类

#ifndef GFMEASURECONTROLLERNEW_H
#define GFMEASURECONTROLLERNEW_H


#include "PlanElementController.h"
#include "QmlObjectListModel.h"
#include "Vehicle.h"
#include "QGCLoggingCategory.h"
#include "MavlinkQmlSingleton.h"
#include "VisualMissionItem.h"
#include "MultiVehicleManager.h"
#include "MissionManager.h"
#include "CoordinateVector.h"
#include "FirmwarePlugin.h"
#include "QGCApplication.h"
#include "SimpleMissionItem.h"
#include "SurveyMissionItem.h"
#include "QGCMapPolygon.h"
#include <QHash>
#include <QList>
#include <QObject>

class CoordinateVector;

typedef QPair<VisualMissionItem*,VisualMissionItem*> VisualItemPair;
typedef QHash<VisualItemPair, CoordinateVector*> CoordVectHashTable;

class GFMeasureController : public QObject
{
    Q_OBJECT

public:
    GFMeasureController(QObject* parent = NULL);
    ~GFMeasureController();

    Q_PROPERTY(QmlObjectListModel*  visualItems         READ visualItems                                NOTIFY visualItemsChanged)
    Q_PROPERTY(QmlObjectListModel*  waypointLines       READ waypointLines                              NOTIFY waypointLinesChanged)


    Q_INVOKABLE void addCoordinate(QGeoCoordinate coordinate);
    Q_INVOKABLE void clearCoordinate();
    Q_INVOKABLE double totalDistance();

    QmlObjectListModel* visualItems         (void) { return _visualItems; }
    QmlObjectListModel* waypointLines       (void) { return &_waypointLines; }

private:
    QmlObjectListModel* _visualItems;
    QmlObjectListModel  _waypointLines;
    CoordVectHashTable  _linesTable;

    void _recalcWaypointLines(void);

signals:
    void visualItemsChanged(void);
    void waypointLinesChanged(void);
};

#endif

 

 

/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/


#include <QtAlgorithms>
#include "RallyPoint.h"
#include "GFMeasureController.h"
#include "MultiVehicleManager.h"
#include "MissionManager.h"
#include "CoordinateVector.h"
#include "FirmwarePlugin.h"
#include "QGCApplication.h"
#include "SimpleMissionItem.h"
#include "SurveyMissionItem.h"
#include "PlantProtectionMissionItem.h"
#include "JsonHelper.h"
#include "ParameterManager.h"
#include "QGroundControlQmlGlobal.h"
#include "ObstaclePolygon.h"
#include "QGCGeo.h"
#ifndef __mobile__
#include "MainWindow.h"
#include "QGCFileDialog.h"
#endif

GFMeasureController::GFMeasureController(QObject *parent)
    : QObject(parent)
    ,_visualItems(NULL)
{
    _visualItems = new QmlObjectListModel(this);
}

GFMeasureController::~GFMeasureController()
{

}

void GFMeasureController::addCoordinate(QGeoCoordinate coordinate)
{
    SimpleMissionItem * newItem = new SimpleMissionItem(NULL,this);
    newItem->setCoordinate(coordinate);
    _visualItems->append(newItem);
    emit visualItemsChanged();


    _recalcWaypointLines();
}

void GFMeasureController::clearCoordinate()
{
    if (_visualItems) {
        _visualItems->deleteListAndContents();
    }
    QmlObjectListModel* newVisualItems = new QmlObjectListModel(this);
    _visualItems = newVisualItems;
    _waypointLines.clear();

    emit visualItemsChanged();
    emit waypointLinesChanged();
}

double GFMeasureController::totalDistance()
{
    double distance = 0.0;
    if(_visualItems->count()>=0)
    {
        QGeoCoordinate item1 = qobject_cast<VisualMissionItem*>(_visualItems->get(0))->coordinate();
        for(int i=1;i<_visualItems->count();i++)
        {
            QGeoCoordinate item2 = qobject_cast<VisualMissionItem*>(_visualItems->get(i))->coordinate();
            distance+=item2.distanceTo(item1);
            item1=item2;
        }
    }
    QString distanceStr = QString::number(distance,'f',2);
    return distanceStr.toDouble();
}

void GFMeasureController::_recalcWaypointLines()
{
    VisualMissionItem*  lastCoordinateItem =    qobject_cast<VisualMissionItem*>(_visualItems->get(0));

    CoordVectHashTable old_table = _linesTable;
    _linesTable.clear();
    _waypointLines.clear();
    for(int i=0;i<_visualItems->count();i++)
    {
        VisualMissionItem* item = qobject_cast<VisualMissionItem*>(_visualItems->get(i));//第一点
        VisualItemPair pair(lastCoordinateItem, item);

        auto linevect       = new CoordinateVector( lastCoordinateItem->coordinate(),item->coordinate(), this);
        _linesTable[pair] = linevect;
        lastCoordinateItem = item;

    }
    QObjectList objs;
    objs.reserve(_linesTable.count());
    foreach(CoordinateVector *vect, _linesTable.values()) {
        objs.append(vect);
    }

    // We don't delete here because many links may still be valid
    _waypointLines.swapObjectList(objs);
    qDeleteAll(old_table);
    emit waypointLinesChanged();

}

(2),QML 显示

                    //标尺
    GFMeasureController{
        id:measureController
    }



                //测距
                MapItemView {
                    id:measureCoordinateList
                    model: measureController.visualItems
                    delegate: measureComponent
                }
                Component {
                    id: measureComponent
                    MapQuickItem {
                        id:measureMapItem
                        anchorPoint:    Qt.point(sourceItem.width/2 , sourceItem.height / 2)
                        coordinate:     object.coordinate
                        visible:        true
                        sourceItem:Rectangle {
                            id:rect
                            width:          _width
                            height:         _width
                            radius:         _width / 2
                            border.width:    1
                            border.color:   "red"
                            color:          "white"

                            property real _width: ScreenTools.defaultFontPixelHeight * ScreenTools.smallFontPointRatio * 1

                        }
                        z:              QGroundControl.zOrderMapItems
                    }
                }

                //标签
                MapItemView {
                    id:measureLabelMapItem
                    model: measureController.visualItems
                    delegate:   measureLabelComponent
                }
                Component {
                    id: measureLabelComponent

                    MapQuickItem {
                        id:measureLabelItem
                        anchorPoint:    Qt.point(-10 , 25)
                        coordinate:     object.coordinate
                        visible:        true

                        property int preIndex: measureController.visualItems.count>0?measureController.visualItems.indexOf(object)-1:-1
                        property int lastIndex: measureController.visualItems.count-1
                        property var preCoor: preIndex>=0?measureController.visualItems.get(preIndex).coordinate:null
                        property real distance:preIndex>=0?(object.coordinate.distanceTo(preCoor)):0
                        sourceItem: Rectangle {
                            border.width:    1
                            border.color:   "gray"
                            color:          "white"
                            radius: 2
                            width: lab.contentWidth+10
                            height: measureColumn.height+3
                            Column{
                                id:measureColumn
                                anchors.left: parent.left
                                anchors.right: parent.right
                                anchors.leftMargin: 5
                                anchors.rightMargin: 5
                                spacing:    ScreenTools.defaultFontPixelHeight
                                width: lab.contentWidth
                                height: lab.contentHeight
                                Text{
                                    id:lab
                                    text: preIndex==-1?qsTr("起点"):(preIndex== lastIndex-1&&!isMeasureDistance?qsTr("总长:")+measureController.totalDistance()+qsTr("米"):measureLabelItem.distance.toFixed(1)+qsTr(" 米"))
                                    horizontalAlignment:Text.AlignHCenter

                                }
                            }

                        }
                        z:   QGroundControl.zOrderMapItems
                    }
                }

                //连线
                MissionLineView {
                    model:       measureController.waypointLines
                }

                //结束点
                MapQuickItem {
                    id:measureExitItem
                    anchorPoint:    Qt.point(-10 , 5)
                    property int lastIndex: measureController.visualItems.count-1
                    property var lastCoor: lastIndex>=0?measureController.visualItems.get(lastIndex).coordinate:null
                    coordinate:     lastCoor
                    z:              QGroundControl.zOrderMapItems
                    visible:        lastIndex>=0&&!isMeasureDistance
                    sourceItem: Rectangle {
                        border.width:    2
                        border.color:   "red"
                        color:          "white"
                        radius: 1
                        width: measureExitColumn.width+10
                        height: measureExitColumn.height
                        Column{
                            id:measureExitColumn
                            anchors.left: parent.left
                            anchors.right: parent.right
                            anchors.leftMargin: 5
                            anchors.rightMargin: 5
                            width: labExit.contentWidth
                            height: labExit.contentHeight
                            Text{
                                id:labExit
                                text:"X"
                                color:"red"
                            }
                        }
                        QGCMouseArea {
                            fillItem:   parent
                            onClicked:
                            {
                                measureController.clearCoordinate()
                            }
                        }
                    }
                }

                //鼠标移动点
                MapQuickItem {
                    id:measureMoveMapItem
                    visible:        true
                    coordinate :null
                    anchorPoint:    Qt.point(-10 , 5)
                    property real distance: 0
                    sourceItem:Rectangle {
                        id:rect
                        border.width:    1
                        border.color:   "red"
                        color:          "white"
                        width: measureColumn.width+6
                        height: measureColumn.height+3
                        Column{
                            id:measureColumn
                            anchors.left: parent.left
                            anchors.right: parent.right
                            anchors.leftMargin: 3
                            anchors.rightMargin: 3
                            width: lab2.contentWidth
                            height: lab.contentHeight+lab2.contentHeight+5
                            Text{
                                id:lab
                                text: measureMoveMapItem.distance.toFixed(1)+qsTr(" 米")
                                horizontalAlignment:Text.AlignHCenter
                                verticalAlignment:Text.verticalCenter
                            }
                            Text{
                                id:lab2
                                text: qsTr("单击确定地点,双击结束")
                                horizontalAlignment:Text.AlignHCenter
                                verticalAlignment:Text.verticalCenter
                            }
                        }

                    }
                    z:              QGroundControl.zOrderMapItems
                }

                //移动线
                MapPolyline {
                    id:measureMoveLine
                    line.width: 2
                    line.color: 'orange'
                    path: [
                        measureMoveMapItem.coordinate,measureController.visualItems.get(measureController.visualItems.count-1).coordinate
                    ]
                }

QML 鼠标事件

MouseArea {
                    anchors.fill:   parent
                    enabled:        true
                    hoverEnabled:true
                    cursorShape: isMeasureDistance?Qt.PointingHandCursor:Qt.ArrowCursor
                    onPositionChanged: {//测距
                        var coordinate = editorMap.toCoordinate(Qt.point(mouse.x, mouse.y))
                        if(isMeasureDistance)
                        {
                            measureMoveMapItem.coordinate=coordinate
                            measureMoveMapItem.visible=true
                            measureMoveLine.visible=true

                            var coor1 = measureController.visualItems.get(measureController.visualItems.count-1).coordinate
                            var distance = coordinate.distanceTo(coor1)
                            measureMoveMapItem.distance=distance
                        }else{
                            measureMoveMapItem.visible=false
                            measureMoveLine.visible=false
                        }
                    }
                    onClicked: {//测距
                        if(isMeasureDistance){
                            qgcView.mouseX = mouseX
                            qgcView.mouseY = mouseY
                            var clickCoordinate = editorMap.toCoordinate(Qt.point(mouse.x, mouse.y))
                            measureController.addCoordinate(clickCoordinate)
                        }
                    }

 

<think>好的,用户想了解如何在QGroundControl(QGC)中实现增加航线距离显示的功能,并且需要修改源码。首先,我需要确认用户的需求:他们希望在规划航线时,能够看到各个航点之间的距离,或者整个航线的总距离。这需要深入QGC的源码,找到与航线规划相关的部分,添加距离计算和显示的代码。 接下来,我需要回忆QGC的结构。QGC使用Qt框架,主要代码在C++和QML中。航线规划相关的界面可能在src/PlanView或类似的目录下。可能需要修改MissionItemEditor或FlightMap组件的代码。 然后,考虑如何计算航点间的距离。QGC可能有现成的地理计算工具类,比如QGCMapPolyline或QGCMapUtils,里面可能有计算两点间距离的方法。需要查找这些类中的相关函数,例如计算两个经纬度点之间的距离,使用Haversine公式或者WGS84椭球模型。 接下来,显示距离的位置。用户可能希望在每个线段旁边显示距离,或者在侧边栏显示总距离。如果是地图上的线段,可能需要修改QML中的MapPolyline组件,添加Text或Label元素来显示距离。如果是侧边栏,可能需要修改MissionItemEditor.qml,添加一个显示总距离的文本框。 然后,考虑如何动态更新距离。当用户拖动航点或者添加/删除航点时,需要实时更新显示的距离。这可能需要连接到QGC的信号槽机制,当航点变化时触发距离计算和界面更新。 此外,还需要处理单位转换,QGC通常支持米和公里,也可能需要支持英尺和英里,需要检查单位设置,并相应转换距离显示。 可能的步骤包括: 1. 在MissionItem类中添加距离计算的方法,或者找到现有的方法。 2. 在航线模型(MissionController)中汇总所有航段距离,计算总距离。 3. 在QML界面中添加显示总距离的UI元素,绑定到后端的数据。 4. 对于每个航段,在MapPolyline上添加Text标签,显示该段的距离。 5. 确保所有更改在航点变化时自动更新,可能需要处理信号如waypointsChanged。 需要注意的难点包括:正确获取每个航点的坐标,确保坐标转换正确,处理地图上的文本标注避免重叠,以及性能优化,避免频繁计算导致界面卡顿。 另外,用户可能需要了解如何编译和测试修改后的QGC代码,确保他们具备开发环境,如Qt Creator,能够编译QGC的源码,并进行调试。 最后,可能需要参考QGC的开发者文档,或者查看已有的类似功能的实现方式,例如已有的航点高度显示或其他属性,模仿其实现方法来增加距离显示。</think>在QGroundControl(QGC)中实现航线距离显示功能需要对源码进行修改,以下是关键步骤和实现思路: --- ### **1. 功能定位与代码分析** - **目标位置**:距离显示通常需要两种形式: 1. **航段距离**:相邻航点间的直线距离(显示在地图连线上) 2. **总距离**:所有航段距离之和(显示在侧边栏或状态栏) - **源码入口**: - 航线编辑逻辑:`src/PlanView/MissionItemEditor.qml` - 地图绘制组件:`src/FlightMap/MapItems/` 目录下的 `MissionItem` 相关类 - 数据模型:`src/MissionManager/MissionController.cc` --- ### **2. 计算航段距离** - **使用QGC地理工具类**: ```cpp // 示例:通过 QGeoCoordinate 计算两点间距离(WGS84椭球模型) QGeoCoordinate coord1, coord2; double distance = coord1.distanceTo(coord2); // 返回米为单位 ``` - **汇总总距离**: - 在 `MissionController` 类中遍历所有航点,累加相邻航点距离。 --- ### **3. 修改地图显示(航段距离标注)** - **修改 `MapPolyline` 组件**: ```qml // 在 FlightMap/MapItems/MapPolyline.qml增加 Text 组件 MapItemView { model: missionController.waypoints delegate: MapPolyline { // ... 原有代码 ... MapText { text: qsTr("%1 m").format(model.distance) coordinate: interpolatedPosition // 计算线段中点坐标 color: "black" } } } ``` - **插值中点坐标**:通过 `QGeoCoordinate::atDistanceAndAzimuth` 计算线段中点。 --- ### **4. 侧边栏显示总距离** - **在 `MissionItemEditor.qml` 中添加UI控件**: ```qml Row { spacing: 10 Text { text: "总距离:" } Text { text: missionController.totalDistance.toFixed(1) + " 米" } } ``` - **绑定数据**:在 `MissionController` 中暴露 `totalDistance` 属性。 --- ### **5. 动态更新机制** - **信号与槽连接**: ```cpp // MissionController.cc 中监听航点变化 connect(this, &MissionController::waypointsChanged, this, &MissionController::updateTotalDistance); ``` - **单位转换**:根据用户设置(米/公里/英尺)动态转换显示单位。 --- ### **6. 编译与测试** 1. **环境准备**:确保已配置Qt环境(建议Qt 5.15+)。 2. **编译代码**: ```bash mkdir build && cd build qmake ../qgroundcontrol.pro make -j4 ``` 3. **测试功能**:拖动航点观察距离更新,验证数值准确性。 --- ### **注意事项** - **性能优化**:避免频繁计算(如拖拽航点时需适当节流)。 - **代码风格**:遵循QGC的代码规范(如变量命名、信号命名)。 - **提交贡献**:如果功能通用,可向QGC官方提交Pull Request。 建议参考 `QGCMapPolyline` 和 `MissionItem` 类的现有实现,以保持代码一致性。如需进一步实现细节,可查阅QGC开发者文档:https://dev.qgroundcontrol.com/master/en/
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

土拨鼠不是老鼠

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

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

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

打赏作者

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

抵扣说明:

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

余额充值