Browse Source

Merge pull request #7872 from DonLakeFlyer/PolygonTools

Polygon tools, Survey changes
QGC4.4
Don Gagne 6 years ago committed by GitHub
parent
commit
9276e16504
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      ChangeLog.md
  2. 1
      qgroundcontrol.qrc
  3. 1
      src/FlightMap/MapItems/MissionItemIndicatorDrag.qml
  4. 2
      src/FlightMap/MapItems/MissionLineView.qml
  5. 39
      src/MissionManager/QGCMapPolygon.cc
  6. 18
      src/MissionManager/QGCMapPolygon.h
  7. 450
      src/MissionManager/QGCMapPolygonVisuals.qml
  8. 6
      src/PlanView/PlanView.qml
  9. 548
      src/PlanView/SurveyItemEditor.qml
  10. 35
      src/PlanView/SurveyMapVisual.qml
  11. 24
      src/QmlControls/KMLOrSHPFileDialog.qml
  12. 28
      src/QmlControls/QGCDynamicObjectManager.qml
  13. 1
      src/QmlControls/QGroundControl/Controls/qmldir

1
ChangeLog.md

@ -6,6 +6,7 @@ Note: This file only contains high level features or important fixes. @@ -6,6 +6,7 @@ Note: This file only contains high level features or important fixes.
### 3.6.0 - Daily Build
* New Polygon editing tools ui. Includes ability to trace polygon by clicking.
* ArduCopter/Rover: Follow Me setup page
* More performant flight path display algorithm. Mobile builds no longer show limited path length.
* ArduCopter/Rover: Add support for Follow Me

1
qgroundcontrol.qrc

@ -85,6 +85,7 @@ @@ -85,6 +85,7 @@
<file alias="QGroundControl/Controls/HeightIndicator.qml">src/QmlControls/HeightIndicator.qml</file>
<file alias="QGroundControl/Controls/IndicatorButton.qml">src/QmlControls/IndicatorButton.qml</file>
<file alias="QGroundControl/Controls/JoystickThumbPad.qml">src/QmlControls/JoystickThumbPad.qml</file>
<file alias="QGroundControl/Controls/KMLOrSHPFileDialog.qml">src/QmlControls/KMLOrSHPFileDialog.qml</file>
<file alias="QGroundControl/Controls/LogReplayStatusBar.qml">src/QmlControls/LogReplayStatusBar.qml</file>
<file alias="QGroundControl/Controls/MainWindowSavedState.qml">src/QmlControls/MainWindowSavedState.qml</file>
<file alias="QGroundControl/Controls/MAVLinkMessageButton.qml">src/QmlControls/MAVLinkMessageButton.qml</file>

1
src/FlightMap/MapItems/MissionItemIndicatorDrag.qml

@ -23,6 +23,7 @@ Rectangle { @@ -23,6 +23,7 @@ Rectangle {
height: _itemIndicatorHeight + (_touchMarginVertical * 2)
color: "transparent"
z: QGroundControl.zOrderMapItems + 1 // Above item icons
visible: itemCoordinate.isValid
// Properties which must be specific by consumer
property var mapControl ///< Map control which contains this item

2
src/FlightMap/MapItems/MissionLineView.qml

@ -24,6 +24,6 @@ MapItemView { @@ -24,6 +24,6 @@ MapItemView {
line.color: "#be781c" // Hack, can't get palette to work in here
z: QGroundControl.zOrderWaypointLines
path: object ? [ object.coordinate1, object.coordinate2 ] : [ ]
path: object && object.coordinate1.isValid && object.coordinate2.isValid ? [ object.coordinate1, object.coordinate2 ] : []
}
}

39
src/MissionManager/QGCMapPolygon.cc

@ -29,6 +29,7 @@ QGCMapPolygon::QGCMapPolygon(QObject* parent) @@ -29,6 +29,7 @@ QGCMapPolygon::QGCMapPolygon(QObject* parent)
, _centerDrag (false)
, _ignoreCenterUpdates (false)
, _interactive (false)
, _resetActive (false)
{
_init();
}
@ -39,6 +40,7 @@ QGCMapPolygon::QGCMapPolygon(const QGCMapPolygon& other, QObject* parent) @@ -39,6 +40,7 @@ QGCMapPolygon::QGCMapPolygon(const QGCMapPolygon& other, QObject* parent)
, _centerDrag (false)
, _ignoreCenterUpdates (false)
, _interactive (false)
, _resetActive (false)
{
*this = other;
@ -265,11 +267,14 @@ void QGCMapPolygon::appendVertices(const QList<QGeoCoordinate>& coordinates) @@ -265,11 +267,14 @@ void QGCMapPolygon::appendVertices(const QList<QGeoCoordinate>& coordinates)
{
QList<QObject*> objects;
_beginResetIfNotActive();
for (const QGeoCoordinate& coordinate: coordinates) {
objects.append(new QGCQGeoCoordinate(coordinate, this));
_polygonPath.append(QVariant::fromValue(coordinate));
}
_polygonModel.append(objects);
_endResetIfNotActive();
emit pathChanged();
}
@ -448,8 +453,10 @@ void QGCMapPolygon::offset(double distance) @@ -448,8 +453,10 @@ void QGCMapPolygon::offset(double distance)
}
// Update internals
_beginResetIfNotActive();
clear();
appendVertices(rgNewPolygon);
_endResetIfNotActive();
}
bool QGCMapPolygon::loadKMLOrSHPFile(const QString& file)
@ -461,8 +468,10 @@ bool QGCMapPolygon::loadKMLOrSHPFile(const QString& file) @@ -461,8 +468,10 @@ bool QGCMapPolygon::loadKMLOrSHPFile(const QString& file)
return false;
}
_beginResetIfNotActive();
clear();
appendVertices(rgCoords);
_endResetIfNotActive();
return true;
}
@ -509,7 +518,37 @@ void QGCMapPolygon::verifyClockwiseWinding(void) @@ -509,7 +518,37 @@ void QGCMapPolygon::verifyClockwiseWinding(void)
rgReversed.prepend(varCoord.value<QGeoCoordinate>());
}
_beginResetIfNotActive();
clear();
appendVertices(rgReversed);
_endResetIfNotActive();
}
}
void QGCMapPolygon::beginReset(void)
{
_resetActive = true;
_polygonModel.beginReset();
}
void QGCMapPolygon::endReset(void)
{
_resetActive = false;
_polygonModel.endReset();
emit pathChanged();
emit centerChanged(_center);
}
void QGCMapPolygon::_beginResetIfNotActive(void)
{
if (!_resetActive) {
beginReset();
}
}
void QGCMapPolygon::_endResetIfNotActive(void)
{
if (!_resetActive) {
endReset();
}
}

18
src/MissionManager/QGCMapPolygon.h

@ -36,6 +36,8 @@ public: @@ -36,6 +36,8 @@ public:
Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter NOTIFY centerChanged)
Q_PROPERTY(bool centerDrag READ centerDrag WRITE setCenterDrag NOTIFY centerDragChanged)
Q_PROPERTY(bool interactive READ interactive WRITE setInteractive NOTIFY interactiveChanged)
Q_PROPERTY(bool isValid READ isValid NOTIFY countChanged)
Q_PROPERTY(bool empty READ empty NOTIFY countChanged)
Q_INVOKABLE void clear(void);
Q_INVOKABLE void appendVertex(const QGeoCoordinate& coordinate);
@ -69,6 +71,9 @@ public: @@ -69,6 +71,9 @@ public:
/// Adjust polygon winding order to be clockwise (if needed)
Q_INVOKABLE void verifyClockwiseWinding(void);
Q_INVOKABLE void beginReset (void);
Q_INVOKABLE void endReset (void);
/// Saves the polygon to the json object.
/// @param json Json object to save to
void saveToJson(QJsonObject& json);
@ -94,6 +99,8 @@ public: @@ -94,6 +99,8 @@ public:
QGeoCoordinate center (void) const { return _center; }
bool centerDrag (void) const { return _centerDrag; }
bool interactive (void) const { return _interactive; }
bool isValid (void) const { return _polygonModel.count() >= 3; }
bool empty (void) const { return _polygonModel.count() == 0; }
QVariantList path (void) const { return _polygonPath; }
QmlObjectListModel* qmlPathModel(void) { return &_polygonModel; }
@ -122,10 +129,12 @@ private slots: @@ -122,10 +129,12 @@ private slots:
void _updateCenter(void);
private:
void _init(void);
QPolygonF _toPolygonF(void) const;
QGeoCoordinate _coordFromPointF(const QPointF& point) const;
QPointF _pointFFromCoord(const QGeoCoordinate& coordinate) const;
void _init (void);
QPolygonF _toPolygonF (void) const;
QGeoCoordinate _coordFromPointF (const QPointF& point) const;
QPointF _pointFFromCoord (const QGeoCoordinate& coordinate) const;
void _beginResetIfNotActive (void);
void _endResetIfNotActive (void);
QVariantList _polygonPath;
QmlObjectListModel _polygonModel;
@ -134,6 +143,7 @@ private: @@ -134,6 +143,7 @@ private:
bool _centerDrag;
bool _ignoreCenterUpdates;
bool _interactive;
bool _resetActive;
};
#endif

450
src/MissionManager/QGCMapPolygonVisuals.qml

@ -7,11 +7,12 @@ @@ -7,11 +7,12 @@
*
****************************************************************************/
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtLocation 5.3
import QtPositioning 5.3
import QtQuick.Dialogs 1.2
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtLocation 5.3
import QtPositioning 5.3
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.11
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
@ -32,64 +33,74 @@ Item { @@ -32,64 +33,74 @@ Item {
property int borderWidth: 0
property color borderColor: "black"
property var _polygonComponent
property var _dragHandlesComponent
property var _splitHandlesComponent
property var _centerDragHandleComponent
property bool _circle: false
property bool _circleMode: false
property real _circleRadius
property bool _circleRadiusDrag: false
property var _circleRadiusDragCoord: QtPositioning.coordinate()
property bool _editCircleRadius: false
property bool _traceMode: false
property string _instructionText: _polygonToolsText
property var _savedVertices: [ ]
property bool _savedCircleMode
property real _zorderDragHandle: QGroundControl.zOrderMapItems + 3 // Highest to prevent splitting when items overlap
property real _zorderSplitHandle: QGroundControl.zOrderMapItems + 2
property real _zorderCenterHandle: QGroundControl.zOrderMapItems + 1 // Lowest such that drag or split takes precedence
function addVisuals() {
_polygonComponent = polygonComponent.createObject(mapControl)
mapControl.addMapItem(_polygonComponent)
readonly property string _polygonToolsText: qsTr("Polygon Tools")
readonly property string _traceText: qsTr("Click in the map to add vertices. Click 'Done Tracing' when finished.")
function addCommonVisuals() {
if (_objMgrCommonVisuals.empty) {
_objMgrCommonVisuals.createObject(polygonComponent, mapControl, true)
}
}
function removeVisuals() {
_polygonComponent.destroy()
function removeCommonVisuals() {
_objMgrCommonVisuals.destroyObjects()
}
function addHandles() {
if (!_dragHandlesComponent) {
_dragHandlesComponent = dragHandlesComponent.createObject(mapControl)
_splitHandlesComponent = splitHandlesComponent.createObject(mapControl)
_centerDragHandleComponent = centerDragHandleComponent.createObject(mapControl)
function addEditingVisuals() {
if (_objMgrEditingVisuals.empty) {
_objMgrEditingVisuals.createObjects([ dragHandlesComponent, splitHandlesComponent, centerDragHandleComponent ], mapControl, false /* addToMap */)
}
}
function removeHandles() {
if (_dragHandlesComponent) {
_dragHandlesComponent.destroy()
_dragHandlesComponent = undefined
}
if (_splitHandlesComponent) {
_splitHandlesComponent.destroy()
_splitHandlesComponent = undefined
function removeEditingVisuals() {
_objMgrEditingVisuals.destroyObjects()
}
function addToolVisuals() {
if (_objMgrToolVisuals.empty) {
_objMgrToolVisuals.createObject(editHeaderComponent, mapControl)
}
if (_centerDragHandleComponent) {
_centerDragHandleComponent.destroy()
_centerDragHandleComponent = undefined
}
function removeToolVisuals() {
_objMgrToolVisuals.destroyObjects()
}
function addCircleVisuals() {
if (_objMgrCircleVisuals.empty) {
_objMgrCircleVisuals.createObject(radiusVisualsComponent, mapControl)
}
}
/// Calculate the default/initial 4 sided polygon
function defaultPolygonVertices() {
// Initial polygon is inset to take 2/3rds space
var rect = Qt.rect(map.centerViewport.x, map.centerViewport.y, map.centerViewport.width, map.centerViewport.height)
var rect = Qt.rect(mapControl.centerViewport.x, mapControl.centerViewport.y, mapControl.centerViewport.width, mapControl.centerViewport.height)
rect.x += (rect.width * 0.25) / 2
rect.y += (rect.height * 0.25) / 2
rect.width *= 0.75
rect.height *= 0.75
var centerCoord = map.toCoordinate(Qt.point(rect.x + (rect.width / 2), rect.y + (rect.height / 2)), false /* clipToViewPort */)
var topLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y), false /* clipToViewPort */)
var topRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y), false /* clipToViewPort */)
var bottomLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y + rect.height), false /* clipToViewPort */)
var bottomRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y + rect.height), false /* clipToViewPort */)
var centerCoord = mapControl.toCoordinate(Qt.point(rect.x + (rect.width / 2), rect.y + (rect.height / 2)), false /* clipToViewPort */)
var topLeftCoord = mapControl.toCoordinate(Qt.point(rect.x, rect.y), false /* clipToViewPort */)
var topRightCoord = mapControl.toCoordinate(Qt.point(rect.x + rect.width, rect.y), false /* clipToViewPort */)
var bottomLeftCoord = mapControl.toCoordinate(Qt.point(rect.x, rect.y + rect.height), false /* clipToViewPort */)
var bottomRightCoord = mapControl.toCoordinate(Qt.point(rect.x + rect.width, rect.y + rect.height), false /* clipToViewPort */)
// Initial polygon has max width and height of 3000 meters
var halfWidthMeters = Math.min(topLeftCoord.distanceTo(topRightCoord), 3000) / 2
@ -102,82 +113,111 @@ Item { @@ -102,82 +113,111 @@ Item {
return [ topLeftCoord, topRightCoord, bottomRightCoord, bottomLeftCoord, centerCoord ]
}
/// Add an initial 4 sided polygon
function addInitialPolygon() {
if (mapPolygon.count < 3) {
initialVertices = defaultPolygonVertices()
mapPolygon.appendVertex(initialVertices[0])
mapPolygon.appendVertex(initialVertices[1])
mapPolygon.appendVertex(initialVertices[2])
mapPolygon.appendVertex(initialVertices[3])
}
}
/// Reset polygon back to initial default
function resetPolygon() {
var initialVertices = defaultPolygonVertices()
function _resetPolygon() {
mapPolygon.beginReset()
mapPolygon.clear()
var initialVertices = defaultPolygonVertices()
for (var i=0; i<4; i++) {
mapPolygon.appendVertex(initialVertices[i])
}
_circle = false
mapPolygon.endReset()
_circleMode = false
}
function setCircleRadius(center, radius) {
function _createCircularPolygon(center, radius) {
var unboundCenter = center.atDistanceAndAzimuth(0, 0)
_circleRadius = radius
var segments = 16
var angleIncrement = 360 / segments
var angle = 0
mapPolygon.beginReset()
mapPolygon.clear()
_circleRadius = radius
for (var i=0; i<segments; i++) {
var vertex = unboundCenter.atDistanceAndAzimuth(_circleRadius, angle)
var vertex = unboundCenter.atDistanceAndAzimuth(radius, angle)
mapPolygon.appendVertex(vertex)
angle += angleIncrement
}
_circle = true
mapPolygon.endReset()
_circleMode = true
}
/// Reset polygon to a circle which fits within initial polygon
function resetCircle() {
function _resetCircle() {
var initialVertices = defaultPolygonVertices()
var width = initialVertices[0].distanceTo(initialVertices[1])
var height = initialVertices[1].distanceTo(initialVertices[2])
var radius = Math.min(width, height) / 2
var center = initialVertices[4]
setCircleRadius(center, radius)
_createCircularPolygon(center, radius)
}
onInteractiveChanged: {
function _handleInteractiveChanged() {
if (interactive) {
addHandles()
addEditingVisuals()
addToolVisuals()
} else {
removeHandles()
_traceMode = false
removeEditingVisuals()
removeToolVisuals()
}
}
Component.onCompleted: {
addVisuals()
if (interactive) {
addHandles()
function _saveCurrentVertices() {
_savedVertices = [ ]
_savedCircleMode = _circleMode
for (var i=0; i<mapPolygon.count; i++) {
_savedVertices.push(mapPolygon.vertexCoordinate(i))
}
}
function _restorePreviousVertices() {
mapPolygon.beginReset()
mapPolygon.clear()
for (var i=0; i<_savedVertices.length; i++) {
mapPolygon.appendVertex(_savedVertices[i])
}
mapPolygon.endReset()
_circleMode = _savedCircleMode
}
onInteractiveChanged: _handleInteractiveChanged()
on_TraceModeChanged: {
if (_traceMode) {
_instructionText = _traceText
_objMgrTraceVisuals.createObject(traceMouseAreaComponent, mapControl, false)
} else {
_instructionText = _polygonToolsText
_objMgrTraceVisuals.destroyObjects()
}
}
on_CircleModeChanged: {
if (_circleMode) {
addCircleVisuals()
} else {
_objMgrCircleVisuals.destroyObjects()
}
}
Component.onDestruction: {
removeVisuals()
removeHandles()
Component.onCompleted: {
addCommonVisuals()
_handleInteractiveChanged()
}
QGCDynamicObjectManager { id: _objMgrCommonVisuals }
QGCDynamicObjectManager { id: _objMgrToolVisuals }
QGCDynamicObjectManager { id: _objMgrEditingVisuals }
QGCDynamicObjectManager { id: _objMgrTraceVisuals }
QGCDynamicObjectManager { id: _objMgrCircleVisuals }
QGCPalette { id: qgcPal }
QGCFileDialog {
KMLOrSHPFileDialog {
id: kmlOrSHPLoadDialog
folder: QGroundControl.settingsManager.appSettings.missionSavePath
title: qsTr("Select Polygon File")
selectExisting: true
nameFilters: ShapeFileHelper.fileDialogKMLOrSHPFilters
fileExtension: QGroundControl.settingsManager.appSettings.kmlFileExtension
fileExtension2: QGroundControl.settingsManager.appSettings.shpFileExtension
onAcceptedForLoad: {
mapPolygon.loadKMLOrSHPFile(file)
@ -203,7 +243,7 @@ Item { @@ -203,7 +243,7 @@ Item {
QGCMenuItem {
id: removeVertexItem
visible: !_circle
visible: !_circleMode
text: qsTr("Remove vertex")
onTriggered: {
if (menu._editingVertexIndex >= 0) {
@ -217,37 +257,22 @@ Item { @@ -217,37 +257,22 @@ Item {
}
QGCMenuItem {
text: qsTr("Circle" )
onTriggered: resetCircle()
}
QGCMenuItem {
text: qsTr("Polygon")
onTriggered: resetPolygon()
}
QGCMenuItem {
text: qsTr("Set radius..." )
visible: _circle
visible: _circleMode
onTriggered: _editCircleRadius = true
}
QGCMenuItem {
text: qsTr("Edit position..." )
visible: _circle
visible: _circleMode
onTriggered: mainWindow.showComponentDialog(editCenterPositionDialog, qsTr("Edit Center Position"), mainWindow.showDialogDefaultWidth, StandardButton.Close)
}
QGCMenuItem {
text: qsTr("Edit position..." )
visible: !_circle && menu._editingVertexIndex >= 0
visible: !_circleMode && menu._editingVertexIndex >= 0
onTriggered: mainWindow.showComponentDialog(editVertexPositionDialog, qsTr("Edit Vertex Position"), mainWindow.showDialogDefaultWidth, StandardButton.Close)
}
QGCMenuItem {
text: qsTr("Load KML/SHP...")
onTriggered: kmlOrSHPLoadDialog.openForLoad()
}
}
Component {
@ -269,7 +294,7 @@ Item { @@ -269,7 +294,7 @@ Item {
id: mapQuickItem
anchorPoint.x: sourceItem.width / 2
anchorPoint.y: sourceItem.height / 2
visible: !_circle
visible: !_circleMode
property int vertexIndex
@ -323,7 +348,7 @@ Item { @@ -323,7 +348,7 @@ Item {
id: dragArea
mapControl: _root.mapControl
z: _zorderDragHandle
visible: !_circle
visible: !_circleMode
onDragStop: mapPolygon.verifyClockwiseWinding()
property int polygonVertex
@ -380,7 +405,7 @@ Item { @@ -380,7 +405,7 @@ Item {
anchorPoint.x: dragHandle.width / 2
anchorPoint.y: dragHandle.height / 2
z: _zorderDragHandle
visible: !_circle
visible: !_circleMode
property int polygonVertex
@ -463,74 +488,219 @@ Item { @@ -463,74 +488,219 @@ Item {
onItemCoordinateChanged: mapPolygon.center = itemCoordinate
onDragStart: mapPolygon.centerDrag = true
onDragStop: mapPolygon.centerDrag = false
}
}
onClicked: menu.popupCenter()
Component {
id: centerDragHandleComponent
function setRadiusFromDialog() {
var radius = QGroundControl.appSettingsDistanceUnitsToMeters(radiusField.text)
setCircleRadius(mapPolygon.center, radius)
_editCircleRadius = false
Item {
property var dragHandle
property var dragArea
Component.onCompleted: {
dragHandle = centerDragHandle.createObject(mapControl)
dragHandle.coordinate = Qt.binding(function() { return mapPolygon.center })
mapControl.addMapItem(dragHandle)
dragArea = centerDragAreaComponent.createObject(mapControl, { "itemIndicator": dragHandle, "itemCoordinate": mapPolygon.center })
}
Component.onDestruction: {
dragHandle.destroy()
dragArea.destroy()
}
}
}
Component {
id: editHeaderComponent
Item {
x: mapControl.centerViewport.left + _viewportMargins
y: mapControl.centerViewport.top + _viewportMargins
width: mapControl.centerViewport.width - (_viewportMargins * 2)
height: editHeaderRowLayout.y + editHeaderRowLayout.height + _viewportMargins
z: QGroundControl.zOrderMapItems + 2
property real _radius: ScreenTools.defaultFontPixelWidth / 2
property real _viewportMargins: ScreenTools.defaultFontPixelWidth
Rectangle {
anchors.margins: _margin
anchors.left: parent.right
width: radiusColumn.width + (_margin *2)
height: radiusColumn.height + (_margin *2)
color: qgcPal.window
border.color: qgcPal.text
visible: _editCircleRadius
Column {
id: radiusColumn
anchors.margins: _margin
anchors.left: parent.left
anchors.top: parent.top
spacing: _margin
QGCLabel { text: qsTr("Radius:") }
QGCTextField {
id: radiusField
showUnits: true
unitsLabel: QGroundControl.appSettingsDistanceUnitsString
text: QGroundControl.metersToAppSettingsDistanceUnits(_circleRadius).toFixed(2)
onEditingFinished: setRadiusFromDialog()
inputMethodHints: Qt.ImhFormattedNumbersOnly
anchors.fill: parent
radius: _radius
color: "white"
opacity: 0.75
}
RowLayout {
id: editHeaderRowLayout
anchors.margins: _viewportMargins
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
QGCButton {
text: qsTr("Basic Polygon")
visible: !_traceMode
onClicked: _resetPolygon()
}
QGCButton {
text: qsTr("Circular Polygon")
visible: !_traceMode
onClicked: _resetCircle()
}
QGCButton {
text: _traceMode ? qsTr("Done Tracing") : qsTr("Trace Polygon")
onClicked: {
if (_traceMode) {
if (mapPolygon.count < 3) {
_restorePreviousVertices()
}
_traceMode = false
} else {
_saveCurrentVertices()
_circleMode = false
_traceMode = true
mapPolygon.clear();
}
}
}
QGCButton {
text: qsTr("Load KML/SHP...")
onClicked: kmlOrSHPLoadDialog.openForLoad()
visible: !_traceMode
}
QGCLabel {
anchors.right: radiusColumn.right
anchors.top: radiusColumn.top
text: "X"
id: instructionLabel
color: "black"
text: _instructionText
Layout.fillWidth: true
}
}
}
}
// Mouse area to capture clicks for tracing a polygon
Component {
id: traceMouseAreaComponent
MouseArea {
anchors.fill: map
preventStealing: true
z: QGroundControl.zOrderMapItems + 1 // Over item indicators
onClicked: {
if (mouse.button === Qt.LeftButton) {
mapPolygon.appendVertex(mapControl.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */))
}
}
}
}
Component {
id: radiusDragHandleComponent
MapQuickItem {
id: mapQuickItem
anchorPoint.x: dragHandle.width / 2
anchorPoint.y: dragHandle.height / 2
z: QGroundControl.zOrderMapItems + 2
sourceItem: Rectangle {
id: dragHandle
width: ScreenTools.defaultFontPixelHeight * 1.5
height: width
radius: width / 2
color: "white"
opacity: .90
}
}
}
QGCMouseArea {
fillItem: parent
onClicked: setRadiusFromDialog()
Component {
id: radiusDragAreaComponent
MissionItemIndicatorDrag {
mapControl: _root.mapControl
property real _lastRadius
onItemCoordinateChanged: {
var radius = mapPolygon.center.distanceTo(itemCoordinate)
// Prevent signalling re-entrancy
if (!_circleRadiusDrag && Math.abs(radius - _lastRadius) > 0.1) {
_circleRadiusDrag = true
_createCircularPolygon(mapPolygon.center, radius)
_circleRadiusDragCoord = itemCoordinate
_circleRadiusDrag = false
_lastRadius = radius
}
}
/*
onItemCoordinateChanged: delayTimer.radius = mapPolygon.center.distanceTo(itemCoordinate)
onDragStart: delayTimer.start()
onDragStop: { delayTimer.stop(); delayTimer.update() }
// Use a delayed update to increase performance of redraw while dragging
Timer {
id: delayTimer
interval: 100
repeat: true
property real radius
property real _lastRadius
onRadiusChanged: console.log(radius)
function update() {
// Prevent signalling re-entrancy
if (!_circleRadiusDrag && radius != _lastRadius) {
_circleRadiusDrag = true
_createCircularPolygon(mapPolygon.center, radius)
_circleRadiusDragCoord = itemCoordinate
_circleRadiusDrag = false
_lastRadius = radius
}
}
onTriggered: update()
}
*/
}
}
Component {
id: centerDragHandleComponent
id: radiusVisualsComponent
Item {
property var dragHandle
property var dragArea
property var circleCenterCoord: mapPolygon.center
Component.onCompleted: {
dragHandle = centerDragHandle.createObject(mapControl)
dragHandle.coordinate = Qt.binding(function() { return mapPolygon.center })
mapControl.addMapItem(dragHandle)
dragArea = centerDragAreaComponent.createObject(mapControl, { "itemIndicator": dragHandle, "itemCoordinate": mapPolygon.center })
function _calcRadiusDragCoord() {
_circleRadiusDragCoord = circleCenterCoord.atDistanceAndAzimuth(_circleRadius, 90)
}
Component.onDestruction: {
dragHandle.destroy()
dragArea.destroy()
onCircleCenterCoordChanged: {
if (!_circleRadiusDrag) {
_calcRadiusDragCoord()
}
}
QGCDynamicObjectManager {
id: _objMgr
}
Component.onCompleted: {
_calcRadiusDragCoord()
var radiusDragHandle = _objMgr.createObject(radiusDragHandleComponent, mapControl, true)
radiusDragHandle.coordinate = Qt.binding(function() { return _circleRadiusDragCoord })
var radiusDragIndicator = radiusDragAreaComponent.createObject(mapControl, { "itemIndicator": radiusDragHandle, "itemCoordinate": _circleRadiusDragCoord })
_objMgr.addObject(radiusDragIndicator)
}
}
}

6
src/PlanView/PlanView.qml

@ -405,10 +405,10 @@ Item { @@ -405,10 +405,10 @@ Item {
planView: true
// This is the center rectangle of the map which is not obscured by tools
property rect centerViewport: Qt.rect(_leftToolWidth, 0, editorMap.width - _leftToolWidth - _rightPanelWidth, editorMap.height - _statusHeight)
property rect centerViewport: Qt.rect(_leftToolWidth, 0, editorMap.width - _leftToolWidth - _rightToolWidth, mapScale.y)
property real _leftToolWidth: toolStrip.x + toolStrip.width
property real _statusHeight: waypointValuesDisplay.visible ? editorMap.height - waypointValuesDisplay.y : 0
property real _leftToolWidth: toolStrip.x + toolStrip.width
property real _rightToolWidth: rightPanel.width + rightPanel.anchors.rightMargin
readonly property real animationDuration: 500

548
src/PlanView/SurveyItemEditor.qml

@ -54,101 +54,119 @@ Rectangle { @@ -54,101 +54,119 @@ Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
QGCTabBar {
id: tabBar
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: !missionItem.surveyAreaPolygon.isValid
Component.onCompleted: currentIndex = QGroundControl.settingsManager.planViewSettings.displayPresetsTabFirst.rawValue ? 2 : 0
QGCTabButton { text: qsTr("Grid") }
QGCTabButton { text: qsTr("Camera") }
QGCTabButton { text: qsTr("Presets") }
QGCLabel {
Layout.fillWidth: true
wrapMode: Text.WordWrap
text: qsTr("Use the Polygon Tools to create the polygon which outlines your survey area.")
}
}
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: tabBar.currentIndex == 0
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: missionItem.surveyAreaPolygon.isValid
QGCLabel {
QGCTabBar {
id: tabBar
anchors.left: parent.left
anchors.right: parent.right
text: qsTr("WARNING: Photo interval is below minimum interval (%1 secs) supported by camera.").arg(_cameraMinTriggerInterval.toFixed(1))
wrapMode: Text.WordWrap
color: qgcPal.warningText
visible: missionItem.cameraShots > 0 && _cameraMinTriggerInterval !== 0 && _cameraMinTriggerInterval > missionItem.timeBetweenShots
}
CameraCalcGrid {
cameraCalc: missionItem.cameraCalc
vehicleFlightIsFrontal: true
distanceToSurfaceLabel: qsTr("Altitude")
distanceToSurfaceAltitudeMode: missionItem.followTerrain ?
QGroundControl.AltitudeModeAboveTerrain :
missionItem.cameraCalc.distanceToSurfaceRelative
frontalDistanceLabel: qsTr("Trigger Dist")
sideDistanceLabel: qsTr("Spacing")
}
Component.onCompleted: currentIndex = QGroundControl.settingsManager.planViewSettings.displayPresetsTabFirst.rawValue ? 2 : 0
SectionHeader {
id: transectsHeader
text: qsTr("Transects")
QGCTabButton { text: qsTr("Grid") }
QGCTabButton { text: qsTr("Camera") }
QGCTabButton { text: qsTr("Presets") }
}
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columnSpacing: _margin
rowSpacing: _margin
columns: 2
visible: transectsHeader.checked
QGCLabel { text: qsTr("Angle") }
FactTextField {
fact: missionItem.gridAngle
Layout.fillWidth: true
onUpdated: angleSlider.value = missionItem.gridAngle.value
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: tabBar.currentIndex == 0
QGCLabel {
anchors.left: parent.left
anchors.right: parent.right
text: qsTr("WARNING: Photo interval is below minimum interval (%1 secs) supported by camera.").arg(_cameraMinTriggerInterval.toFixed(1))
wrapMode: Text.WordWrap
color: qgcPal.warningText
visible: missionItem.cameraShots > 0 && _cameraMinTriggerInterval !== 0 && _cameraMinTriggerInterval > missionItem.timeBetweenShots
}
QGCSlider {
id: angleSlider
minimumValue: 0
maximumValue: 359
stepSize: 1
tickmarksEnabled: false
Layout.fillWidth: true
Layout.columnSpan: 2
Layout.preferredHeight: ScreenTools.defaultFontPixelHeight * 1.5
onValueChanged: missionItem.gridAngle.value = value
Component.onCompleted: value = missionItem.gridAngle.value
updateValueWhileDragging: true
CameraCalcGrid {
cameraCalc: missionItem.cameraCalc
vehicleFlightIsFrontal: true
distanceToSurfaceLabel: qsTr("Altitude")
distanceToSurfaceAltitudeMode: missionItem.followTerrain ?
QGroundControl.AltitudeModeAboveTerrain :
missionItem.cameraCalc.distanceToSurfaceRelative
frontalDistanceLabel: qsTr("Trigger Dist")
sideDistanceLabel: qsTr("Spacing")
}
QGCLabel {
text: qsTr("Turnaround dist")
SectionHeader {
id: transectsHeader
text: qsTr("Transects")
}
FactTextField {
fact: missionItem.turnAroundDistance
Layout.fillWidth: true
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columnSpacing: _margin
rowSpacing: _margin
columns: 2
visible: transectsHeader.checked
QGCLabel { text: qsTr("Angle") }
FactTextField {
fact: missionItem.gridAngle
Layout.fillWidth: true
onUpdated: angleSlider.value = missionItem.gridAngle.value
}
QGCSlider {
id: angleSlider
minimumValue: 0
maximumValue: 359
stepSize: 1
tickmarksEnabled: false
Layout.fillWidth: true
Layout.columnSpan: 2
Layout.preferredHeight: ScreenTools.defaultFontPixelHeight * 1.5
onValueChanged: missionItem.gridAngle.value = value
Component.onCompleted: value = missionItem.gridAngle.value
updateValueWhileDragging: true
}
QGCLabel {
text: qsTr("Turnaround dist")
}
FactTextField {
fact: missionItem.turnAroundDistance
Layout.fillWidth: true
}
}
}
QGCButton {
text: qsTr("Rotate Entry Point")
onClicked: missionItem.rotateEntryPoint();
}
QGCButton {
text: qsTr("Rotate Entry Point")
onClicked: missionItem.rotateEntryPoint();
}
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: transectsHeader.checked
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: transectsHeader.checked
/*
/*
Temporarily removed due to bug https://github.com/mavlink/qgroundcontrol/issues/7005
FactCheckBox {
text: qsTr("Split concave polygons")
@ -158,231 +176,232 @@ Rectangle { @@ -158,231 +176,232 @@ Rectangle {
}
*/
QGCOptionsComboBox {
Layout.fillWidth: true
model: [
{
text: qsTr("Hover and capture image"),
fact: missionItem.hoverAndCapture,
enabled: !missionItem.followTerrain,
visible: missionItem.hoverAndCaptureAllowed
},
{
text: qsTr("Refly at 90 deg offset"),
fact: missionItem.refly90Degrees,
enabled: !missionItem.followTerrain,
visible: true
},
{
text: qsTr("Images in turnarounds"),
fact: missionItem.cameraTriggerInTurnAround,
enabled: missionItem.hoverAndCaptureAllowed ? !missionItem.hoverAndCapture.rawValue : true,
visible: true
},
{
text: qsTr("Fly alternate transects"),
fact: missionItem.flyAlternateTransects,
enabled: true,
visible: _vehicle ? (_vehicle.fixedWing || _vehicle.vtol) : false
},
{
text: qsTr("Relative altitude"),
enabled: missionItem.cameraCalc.isManualCamera && !missionItem.followTerrain,
visible: QGroundControl.corePlugin.options.showMissionAbsoluteAltitude || (!missionItem.cameraCalc.distanceToSurfaceRelative && !missionItem.followTerrain),
checked: missionItem.cameraCalc.distanceToSurfaceRelative
QGCOptionsComboBox {
Layout.fillWidth: true
model: [
{
text: qsTr("Hover and capture image"),
fact: missionItem.hoverAndCapture,
enabled: !missionItem.followTerrain,
visible: missionItem.hoverAndCaptureAllowed
},
{
text: qsTr("Refly at 90 deg offset"),
fact: missionItem.refly90Degrees,
enabled: !missionItem.followTerrain,
visible: true
},
{
text: qsTr("Images in turnarounds"),
fact: missionItem.cameraTriggerInTurnAround,
enabled: missionItem.hoverAndCaptureAllowed ? !missionItem.hoverAndCapture.rawValue : true,
visible: true
},
{
text: qsTr("Fly alternate transects"),
fact: missionItem.flyAlternateTransects,
enabled: true,
visible: _vehicle ? (_vehicle.fixedWing || _vehicle.vtol) : false
},
{
text: qsTr("Relative altitude"),
enabled: missionItem.cameraCalc.isManualCamera && !missionItem.followTerrain,
visible: QGroundControl.corePlugin.options.showMissionAbsoluteAltitude || (!missionItem.cameraCalc.distanceToSurfaceRelative && !missionItem.followTerrain),
checked: missionItem.cameraCalc.distanceToSurfaceRelative
}
]
onItemClicked: {
if (index == 4) {
missionItem.cameraCalc.distanceToSurfaceRelative = !missionItem.cameraCalc.distanceToSurfaceRelative
console.log(missionItem.cameraCalc.distanceToSurfaceRelative)
}
}
]
}
}
onItemClicked: {
if (index == 4) {
missionItem.cameraCalc.distanceToSurfaceRelative = !missionItem.cameraCalc.distanceToSurfaceRelative
console.log(missionItem.cameraCalc.distanceToSurfaceRelative)
SectionHeader {
id: terrainHeader
text: qsTr("Terrain")
checked: missionItem.followTerrain
}
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: terrainHeader.checked
QGCCheckBox {
id: followsTerrainCheckBox
text: qsTr("Vehicle follows terrain")
checked: missionItem.followTerrain
onClicked: missionItem.followTerrain = checked
}
GridLayout {
Layout.fillWidth: true
columnSpacing: _margin
rowSpacing: _margin
columns: 2
visible: followsTerrainCheckBox.checked
QGCLabel { text: qsTr("Tolerance") }
FactTextField {
fact: missionItem.terrainAdjustTolerance
Layout.fillWidth: true
}
QGCLabel { text: qsTr("Max Climb Rate") }
FactTextField {
fact: missionItem.terrainAdjustMaxClimbRate
Layout.fillWidth: true
}
QGCLabel { text: qsTr("Max Descent Rate") }
FactTextField {
fact: missionItem.terrainAdjustMaxDescentRate
Layout.fillWidth: true
}
}
}
}
SectionHeader {
id: terrainHeader
text: qsTr("Terrain")
checked: missionItem.followTerrain
}
SectionHeader {
id: statsHeader
text: qsTr("Statistics")
}
TransectStyleComplexItemStats {
anchors.left: parent.left
anchors.right: parent.right
visible: statsHeader.checked
}
} // Grid Column
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: tabBar.currentIndex == 1
CameraCalcCamera {
cameraCalc: missionItem.cameraCalc
vehicleFlightIsFrontal: true
distanceToSurfaceLabel: qsTr("Altitude")
distanceToSurfaceAltitudeMode: missionItem.followTerrain ?
QGroundControl.AltitudeModeAboveTerrain :
missionItem.cameraCalc.distanceToSurfaceRelative
frontalDistanceLabel: qsTr("Trigger Dist")
sideDistanceLabel: qsTr("Spacing")
}
} // Camera Column
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: terrainHeader.checked
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: tabBar.currentIndex == 2
QGCLabel {
Layout.fillWidth: true
text: qsTr("Presets")
wrapMode: Text.WordWrap
}
QGCCheckBox {
id: followsTerrainCheckBox
text: qsTr("Vehicle follows terrain")
checked: missionItem.followTerrain
onClicked: missionItem.followTerrain = checked
QGCComboBox {
id: presetCombo
Layout.fillWidth: true
model: missionItem.presetNames
}
GridLayout {
RowLayout {
Layout.fillWidth: true
columnSpacing: _margin
rowSpacing: _margin
columns: 2
visible: followsTerrainCheckBox.checked
QGCLabel { text: qsTr("Tolerance") }
FactTextField {
fact: missionItem.terrainAdjustTolerance
QGCButton {
Layout.fillWidth: true
text: qsTr("Apply Preset")
enabled: missionItem.presetNames.length != 0
onClicked: missionItem.loadPreset(presetCombo.textAt(presetCombo.currentIndex))
}
QGCLabel { text: qsTr("Max Climb Rate") }
FactTextField {
fact: missionItem.terrainAdjustMaxClimbRate
QGCButton {
Layout.fillWidth: true
text: qsTr("Delete Preset")
enabled: missionItem.presetNames.length != 0
onClicked: missionItem.deletePreset(presetCombo.textAt(presetCombo.currentIndex))
}
QGCLabel { text: qsTr("Max Descent Rate") }
FactTextField {
fact: missionItem.terrainAdjustMaxDescentRate
Layout.fillWidth: true
}
}
}
SectionHeader {
id: statsHeader
text: qsTr("Statistics")
}
TransectStyleComplexItemStats {
anchors.left: parent.left
anchors.right: parent.right
visible: statsHeader.checked
}
} // Grid Column
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: tabBar.currentIndex == 1
CameraCalcCamera {
cameraCalc: missionItem.cameraCalc
vehicleFlightIsFrontal: true
distanceToSurfaceLabel: qsTr("Altitude")
distanceToSurfaceAltitudeMode: missionItem.followTerrain ?
QGroundControl.AltitudeModeAboveTerrain :
missionItem.cameraCalc.distanceToSurfaceRelative
frontalDistanceLabel: qsTr("Trigger Dist")
sideDistanceLabel: qsTr("Spacing")
}
} // Camera Column
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: tabBar.currentIndex == 2
QGCLabel {
Layout.fillWidth: true
text: qsTr("Presets")
wrapMode: Text.WordWrap
}
QGCComboBox {
id: presetCombo
Layout.fillWidth: true
model: missionItem.presetNames
}
RowLayout {
Layout.fillWidth: true
Item { height: ScreenTools.defaultFontPixelHeight; width: 1 }
QGCButton {
Layout.alignment: Qt.AlignCenter
Layout.fillWidth: true
text: qsTr("Apply Preset")
enabled: missionItem.presetNames.length != 0
onClicked: missionItem.loadPreset(presetCombo.textAt(presetCombo.currentIndex))
text: qsTr("Save Settings As New Preset")
onClicked: mainWindow.showComponentDialog(savePresetDialog, qsTr("Save Preset"), mainWindow.showDialogDefaultWidth, StandardButton.Save | StandardButton.Cancel)
}
QGCButton {
SectionHeader {
id: presectsTransectsHeader
anchors.left: undefined
anchors.right: undefined
Layout.fillWidth: true
text: qsTr("Delete Preset")
enabled: missionItem.presetNames.length != 0
onClicked: missionItem.deletePreset(presetCombo.textAt(presetCombo.currentIndex))
text: qsTr("Transects")
}
}
Item { height: ScreenTools.defaultFontPixelHeight; width: 1 }
GridLayout {
Layout.fillWidth: true
columnSpacing: _margin
rowSpacing: _margin
columns: 2
visible: presectsTransectsHeader.checked
QGCButton {
Layout.alignment: Qt.AlignCenter
Layout.fillWidth: true
text: qsTr("Save Settings As New Preset")
onClicked: mainWindow.showComponentDialog(savePresetDialog, qsTr("Save Preset"), mainWindow.showDialogDefaultWidth, StandardButton.Save | StandardButton.Cancel)
}
QGCLabel { text: qsTr("Angle") }
FactTextField {
fact: missionItem.gridAngle
Layout.fillWidth: true
onUpdated: presetsAngleSlider.value = missionItem.gridAngle.value
}
SectionHeader {
id: presectsTransectsHeader
anchors.left: undefined
anchors.right: undefined
Layout.fillWidth: true
text: qsTr("Transects")
}
QGCSlider {
id: presetsAngleSlider
minimumValue: 0
maximumValue: 359
stepSize: 1
tickmarksEnabled: false
Layout.fillWidth: true
Layout.columnSpan: 2
Layout.preferredHeight: ScreenTools.defaultFontPixelHeight * 1.5
onValueChanged: missionItem.gridAngle.value = value
Component.onCompleted: value = missionItem.gridAngle.value
updateValueWhileDragging: true
}
GridLayout {
Layout.fillWidth: true
columnSpacing: _margin
rowSpacing: _margin
columns: 2
visible: presectsTransectsHeader.checked
QGCLabel { text: qsTr("Angle") }
FactTextField {
fact: missionItem.gridAngle
Layout.fillWidth: true
onUpdated: presetsAngleSlider.value = missionItem.gridAngle.value
QGCButton {
Layout.columnSpan: 2
Layout.fillWidth: true
text: qsTr("Rotate Entry Point")
onClicked: missionItem.rotateEntryPoint();
}
}
QGCSlider {
id: presetsAngleSlider
minimumValue: 0
maximumValue: 359
stepSize: 1
tickmarksEnabled: false
Layout.fillWidth: true
Layout.columnSpan: 2
Layout.preferredHeight: ScreenTools.defaultFontPixelHeight * 1.5
onValueChanged: missionItem.gridAngle.value = value
Component.onCompleted: value = missionItem.gridAngle.value
updateValueWhileDragging: true
SectionHeader {
id: presetsStatsHeader
anchors.left: undefined
anchors.right: undefined
Layout.fillWidth: true
text: qsTr("Statistics")
}
QGCButton {
Layout.columnSpan: 2
TransectStyleComplexItemStats {
Layout.fillWidth: true
text: qsTr("Rotate Entry Point")
onClicked: missionItem.rotateEntryPoint();
visible: presetsStatsHeader.checked
}
}
SectionHeader {
id: presetsStatsHeader
anchors.left: undefined
anchors.right: undefined
Layout.fillWidth: true
text: qsTr("Statistics")
}
TransectStyleComplexItemStats {
Layout.fillWidth: true
visible: presetsStatsHeader.checked
}
} // Camera Column
} // Main editing column
} // Top level Column
Component {
id: savePresetDialog
@ -418,4 +437,17 @@ Rectangle { @@ -418,4 +437,17 @@ Rectangle {
}
}
}
KMLOrSHPFileDialog {
id: kmlOrSHPLoadDialog
title: qsTr("Select Polygon File")
selectExisting: true
onAcceptedForLoad: {
missionItem.surveyAreaPolygon.loadKMLOrSHPFile(file)
missionItem.resetState = false
//editorMap.mapFitFunctions.fitMapViewportToMissionItems()
close()
}
}
} // Rectangle

35
src/PlanView/SurveyMapVisual.qml

@ -21,39 +21,4 @@ import QGroundControl.FlightMap 1.0 @@ -21,39 +21,4 @@ import QGroundControl.FlightMap 1.0
/// Survey Complex Mission Item visuals
TransectStyleMapVisuals {
polygonInteractive: true
property var _mapPolygon: object.surveyAreaPolygon
/// Add an initial 4 sided polygon if there is none
function _addInitialPolygon() {
if (_mapPolygon.count < 3) {
// Initial polygon is inset to take 2/3rds space
var rect = Qt.rect(map.centerViewport.x, map.centerViewport.y, map.centerViewport.width, map.centerViewport.height)
rect.x += (rect.width * 0.25) / 2
rect.y += (rect.height * 0.25) / 2
rect.width *= 0.75
rect.height *= 0.75
var centerCoord = map.toCoordinate(Qt.point(rect.x + (rect.width / 2), rect.y + (rect.height / 2)), false /* clipToViewPort */)
var topLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y), false /* clipToViewPort */)
var topRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y), false /* clipToViewPort */)
var bottomLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y + rect.height), false /* clipToViewPort */)
var bottomRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y + rect.height), false /* clipToViewPort */)
// Initial polygon has max width and height of 3000 meters
var halfWidthMeters = Math.min(topLeftCoord.distanceTo(topRightCoord), 3000) / 2
var halfHeightMeters = Math.min(topLeftCoord.distanceTo(bottomLeftCoord), 3000) / 2
topLeftCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, -90).atDistanceAndAzimuth(halfHeightMeters, 0)
topRightCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, 90).atDistanceAndAzimuth(halfHeightMeters, 0)
bottomLeftCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, -90).atDistanceAndAzimuth(halfHeightMeters, 180)
bottomRightCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, 90).atDistanceAndAzimuth(halfHeightMeters, 180)
_mapPolygon.appendVertex(topLeftCoord)
_mapPolygon.appendVertex(topRightCoord)
_mapPolygon.appendVertex(bottomRightCoord)
_mapPolygon.appendVertex(bottomLeftCoord)
}
}
Component.onCompleted: _addInitialPolygon()
}

24
src/QmlControls/KMLOrSHPFileDialog.qml

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
/****************************************************************************
*
* (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.
*
****************************************************************************/
import QtQuick 2.11
import QGroundControl 1.0
import QGroundControl.Controls 1.0
import QGroundControl.ShapeFileHelper 1.0
QGCFileDialog {
id: kmlOrSHPLoadDialog
folder: QGroundControl.settingsManager.appSettings.missionSavePath
title: qsTr("Select Polygon File")
selectExisting: true
nameFilters: ShapeFileHelper.fileDialogKMLOrSHPFilters
fileExtension: QGroundControl.settingsManager.appSettings.kmlFileExtension
fileExtension2: QGroundControl.settingsManager.appSettings.shpFileExtension
}

28
src/QmlControls/QGCDynamicObjectManager.qml

@ -9,30 +9,42 @@ import QGroundControl.ScreenTools 1.0 @@ -9,30 +9,42 @@ import QGroundControl.ScreenTools 1.0
Item {
visible: false
property var rgDynamicObjects: [ ]
property var rgDynamicObjects: [ ]
property bool empty: rgDynamicObjects.length === 0
function createObject(sourceComponent, parentObject, parentObjectIsMap) {
Component.onDestruction: destroyObjects()
function createObject(sourceComponent, parentObject, addMapItem) {
var obj = sourceComponent.createObject(parentObject)
if (obj.status === Component.Error) {
console.log(obj.errorString())
}
rgDynamicObjects.push(obj)
if (arguments.length < 3) {
parentObjectIsMap = false
addMapItem = false
}
if (parentObjectIsMap) {
map.addMapItem(obj)
if (addMapItem) {
parentObject.addMapItem(obj)
}
return obj
}
function createObjects(rgSourceComponents, parentObject, parentObjectIsMap) {
function createObjects(rgSourceComponents, parentObject, addMapItem) {
if (arguments.length < 3) {
parentObjectIsMap = false
addMapItem = false
}
for (var i=0; i<rgSourceComponents.length; i++) {
createObject(rgSourceComponents[i], parentObject, parentObjectIsMap)
createObject(rgSourceComponents[i], parentObject, addMapItem)
}
}
/// Adds the object to the list. If mapControl is specified it will aso be added to the map.
function addObject(object, mapControl) {
rgDynamicObjects.push(object)
if (arguments.length == 2) {
mapControl.addMapItem(object)
}
return object
}
function destroyObjects() {

1
src/QmlControls/QGroundControl/Controls/qmldir

@ -23,6 +23,7 @@ HackFileDialog 1.0 HackFileDialog.qml @@ -23,6 +23,7 @@ HackFileDialog 1.0 HackFileDialog.qml
HeightIndicator 1.0 HeightIndicator.qml
IndicatorButton 1.0 IndicatorButton.qml
JoystickThumbPad 1.0 JoystickThumbPad.qml
KMLOrSHPFileDialog 1.0 KMLOrSHPFileDialog.qml
LogReplayStatusBar 1.0 LogReplayStatusBar.qml
MainWindowSavedState 1.0 MainWindowSavedState.qml
MAVLinkMessageButton 1.0 MAVLinkMessageButton.qml

Loading…
Cancel
Save