diff --git a/UnitTest.qrc b/UnitTest.qrc
index 7d46cc26966de43e37a3f4f0c5bb4216b4098838..61510fde340a034be48ab0aee7593c72c27ebf2d 100644
--- a/UnitTest.qrc
+++ b/UnitTest.qrc
@@ -9,5 +9,9 @@
src/MissionManager/UnitTest/MavCmdInfoVTOL.json
src/MissionManager/UnitTest/MissionPlanner.waypoints
src/MissionManager/UnitTest/OldFileFormat.mission
+ src/MissionManager/UnitTest/GoodPolygon.kml
+ src/MissionManager/UnitTest/MissingPolygonNode.kml
+ src/MissionManager/UnitTest/BadXml.kml
+ src/MissionManager/UnitTest/BadCoordinatesNode.kml
diff --git a/src/MissionManager/QGCMapPolygon.cc b/src/MissionManager/QGCMapPolygon.cc
index 460c7c0a56337de9357f29eee099e17d5edf1f91..aa1e0b3898a2f0afd2dc9d81ea6bb26c008fd94a 100644
--- a/src/MissionManager/QGCMapPolygon.cc
+++ b/src/MissionManager/QGCMapPolygon.cc
@@ -11,11 +11,14 @@
#include "QGCGeo.h"
#include "JsonHelper.h"
#include "QGCQGeoCoordinate.h"
+#include "QGCApplication.h"
#include
#include
#include
#include
+#include
+#include
const char* QGCMapPolygon::jsonPolygonKey = "polygon";
@@ -434,3 +437,80 @@ void QGCMapPolygon::offset(double distance)
appendVertex(rgNewPolygon[i]);
}
}
+
+bool QGCMapPolygon::loadKMLFile(const QString& kmlFile)
+{
+ QFile file(kmlFile);
+
+ if (!file.exists()) {
+ qgcApp()->showMessage(tr("File not found: %1").arg(kmlFile));
+ return false;
+ }
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ qgcApp()->showMessage(tr("Unable to open file: %1 error: $%2").arg(kmlFile).arg(file.errorString()));
+ return false;
+ }
+
+ QDomDocument doc;
+ QString errorMessage;
+ int errorLine;
+ if (!doc.setContent(&file, &errorMessage, &errorLine)) {
+ qgcApp()->showMessage(tr("Unable to parse KML file: %1 error: %2 line: %3").arg(kmlFile).arg(errorMessage).arg(errorLine));
+ return false;
+ }
+
+ QDomNodeList rgNodes = doc.elementsByTagName("Polygon");
+ if (rgNodes.count() == 0) {
+ qgcApp()->showMessage(tr("Unable to find Polygon node in KML"));
+ return false;
+ }
+
+ QDomNode coordinatesNode = rgNodes.item(0).namedItem("outerBoundaryIs").namedItem("LinearRing").namedItem("coordinates");
+ if (coordinatesNode.isNull()) {
+ qgcApp()->showMessage(tr("Internal error: Unable to find coordinates node in KML"));
+ return false;
+ }
+
+ QString coordinatesString = coordinatesNode.toElement().text().simplified();
+ QStringList rgCoordinateStrings = coordinatesString.split(" ");
+
+ QList rgCoords;
+ for (int i=0; i rgReversed;
+
+ for (int i=0; i coordinateList(void) const;
diff --git a/src/MissionManager/QGCMapPolygonTest.cc b/src/MissionManager/QGCMapPolygonTest.cc
index 4f366d8c2084f7a8c035d77bacd1dc5a53ecadfe..d9160c9d5e24d6c27b7a077d283778db6814941c 100644
--- a/src/MissionManager/QGCMapPolygonTest.cc
+++ b/src/MissionManager/QGCMapPolygonTest.cc
@@ -197,3 +197,20 @@ void QGCMapPolygonTest::_testVertexManipulation(void)
QCOMPARE(polyList.count(), 0);
QCOMPARE(_pathModel->count(), 0);
}
+
+void QGCMapPolygonTest::_testKMLLoad(void)
+{
+ QVERIFY(_mapPolygon->loadKMLFile(QStringLiteral(":/unittest/GoodPolygon.kml")));
+
+ setExpectedMessageBox(QMessageBox::Ok);
+ QVERIFY(!_mapPolygon->loadKMLFile(QStringLiteral(":/unittest/BadXml.kml")));
+ checkExpectedMessageBox();
+
+ setExpectedMessageBox(QMessageBox::Ok);
+ QVERIFY(!_mapPolygon->loadKMLFile(QStringLiteral(":/unittest/MissingPolygonNode.kml")));
+ checkExpectedMessageBox();
+
+ setExpectedMessageBox(QMessageBox::Ok);
+ QVERIFY(!_mapPolygon->loadKMLFile(QStringLiteral(":/unittest/BadCoordinatesNode.kml")));
+ checkExpectedMessageBox();
+}
diff --git a/src/MissionManager/QGCMapPolygonTest.h b/src/MissionManager/QGCMapPolygonTest.h
index a70e4a9f2aa18420eece4cee2b08abea405c8d28..7f36e926ff372188dbcf5d7bca555fbefb19f880 100644
--- a/src/MissionManager/QGCMapPolygonTest.h
+++ b/src/MissionManager/QGCMapPolygonTest.h
@@ -29,6 +29,7 @@ protected:
private slots:
void _testDirty(void);
void _testVertexManipulation(void);
+ void _testKMLLoad(void);
private:
enum {
diff --git a/src/MissionManager/QGCMapPolygonVisuals.qml b/src/MissionManager/QGCMapPolygonVisuals.qml
index ab6ebc6b44c4c60d38ae1656532483dfa7affc3f..0c5300f523d4371ecfce839e154e1e6e2975724a 100644
--- a/src/MissionManager/QGCMapPolygonVisuals.qml
+++ b/src/MissionManager/QGCMapPolygonVisuals.qml
@@ -22,8 +22,9 @@ import QGroundControl.FlightMap 1.0
Item {
id: _root
- property var mapControl ///< Map control to place item in
- property var mapPolygon ///< QGCMapPolygon object
+ property var qgcView ///< QGCView for popping dialogs
+ property var mapControl ///< Map control to place item in
+ property var mapPolygon ///< QGCMapPolygon object
property bool interactive: mapPolygon.interactive
property color interiorColor: "transparent"
property real interiorOpacity: 1
@@ -146,6 +147,10 @@ Item {
setCircleRadius(center, radius)
}
+ function loadKMLFile() {
+ mapPolygon.loadKMLFile("/Users/Don/Downloads/polygon.kml")
+ }
+
onInteractiveChanged: {
if (interactive) {
addHandles()
@@ -168,6 +173,21 @@ Item {
QGCPalette { id: qgcPal }
+ QGCFileDialog {
+ id: kmlLoadDialog
+ qgcView: _root.qgcView
+ folder: QGroundControl.settingsManager.appSettings.missionSavePath
+ title: qsTr("Select KML File")
+ selectExisting: true
+ nameFilters: [ qsTr("KML files (*.kml)") ]
+
+
+ onAcceptedForLoad: {
+ mapPolygon.loadKMLFile(file)
+ close()
+ }
+ }
+
Component {
id: polygonComponent
@@ -393,8 +413,8 @@ Item {
}
MenuItem {
- text: qsTr("Load KML...")
- enabled: false
+ text: qsTr("Load KML...")
+ onTriggered: kmlLoadDialog.openForLoad()
}
}
diff --git a/src/MissionManager/UnitTest/BadCoordinatesNode.kml b/src/MissionManager/UnitTest/BadCoordinatesNode.kml
new file mode 100755
index 0000000000000000000000000000000000000000..e9f6c3f91bfb6676e9ecc03181bd754d5feaacc7
--- /dev/null
+++ b/src/MissionManager/UnitTest/BadCoordinatesNode.kml
@@ -0,0 +1,47 @@
+Incorrectly formed XML test file
+
+
+
+ Polygon.kmz
+
+
+
+
+ normal
+ #s_ylw-pushpin
+
+
+ highlight
+ #s_ylw-pushpin_hl
+
+
+
+ Untitled Polygon
+ #m_ylw-pushpin
+
+ 1
+
+
+ -122.1059149362712,47.65965281788451,0 -122.1044593196253,47.66002598220988,0 -122.1047336695092,47.66034166158975,0 -122.1061470943783,47.6599810708829,0 -122.1059149362712,47.65965281788451,0
+
+
+
+
+
+
diff --git a/src/MissionManager/UnitTest/BadXml.kml b/src/MissionManager/UnitTest/BadXml.kml
new file mode 100755
index 0000000000000000000000000000000000000000..49840fd905490db68e56d1567693914e922e7def
--- /dev/null
+++ b/src/MissionManager/UnitTest/BadXml.kml
@@ -0,0 +1,49 @@
+Incorrectly formed XML test file
+
+
+
+ Polygon.kmz
+
+
+
+
+ normal
+ #s_ylw-pushpin
+
+
+ highlight
+ #s_ylw-pushpin_hl
+
+
+
+ Untitled Polygon
+ #m_ylw-pushpin
+
+ 1
+
+
+
+ -122.1059149362712,47.65965281788451,0 -122.1044593196253,47.66002598220988,0 -122.1047336695092,47.66034166158975,0 -122.1061470943783,47.6599810708829,0 -122.1059149362712,47.65965281788451,0
+
+
+
+
+
+
+
diff --git a/src/MissionManager/UnitTest/GoodPolygon.kml b/src/MissionManager/UnitTest/GoodPolygon.kml
new file mode 100755
index 0000000000000000000000000000000000000000..712e859f3aa35a44910612b05fd173b2c349e01b
--- /dev/null
+++ b/src/MissionManager/UnitTest/GoodPolygon.kml
@@ -0,0 +1,48 @@
+
+
+
+ Polygon.kmz
+
+
+
+
+ normal
+ #s_ylw-pushpin
+
+
+ highlight
+ #s_ylw-pushpin_hl
+
+
+
+ Untitled Polygon
+ #m_ylw-pushpin
+
+ 1
+
+
+
+ -122.1059149362712,47.65965281788451,0 -122.1044593196253,47.66002598220988,0 -122.1047336695092,47.66034166158975,0 -122.1061470943783,47.6599810708829,0 -122.1059149362712,47.65965281788451,0
+
+
+
+
+
+
+
diff --git a/src/MissionManager/UnitTest/MissingPolygonNode.kml b/src/MissionManager/UnitTest/MissingPolygonNode.kml
new file mode 100755
index 0000000000000000000000000000000000000000..20f4109ff33adfe1477c05f65948eef53215f702
--- /dev/null
+++ b/src/MissionManager/UnitTest/MissingPolygonNode.kml
@@ -0,0 +1,38 @@
+
+
+
+ Polygon.kmz
+
+
+
+
+ normal
+ #s_ylw-pushpin
+
+
+ highlight
+ #s_ylw-pushpin_hl
+
+
+
+ Untitled Polygon
+ #m_ylw-pushpin
+
+
+
diff --git a/src/PlanView/FWLandingPatternMapVisual.qml b/src/PlanView/FWLandingPatternMapVisual.qml
index 9685adc5b7550a3f39efa945a9e3082e6fb75c75..b6fc98884562e196a6efabe84b8c4016dbeb816c 100644
--- a/src/PlanView/FWLandingPatternMapVisual.qml
+++ b/src/PlanView/FWLandingPatternMapVisual.qml
@@ -22,7 +22,8 @@ import QGroundControl.FlightMap 1.0
Item {
id: _root
- property var map ///< Map control to place item in
+ property var map ///< Map control to place item in
+ property var qgcView ///< QGCView to use for popping dialogs
signal clicked(int sequenceNumber)
diff --git a/src/PlanView/MissionItemMapVisual.qml b/src/PlanView/MissionItemMapVisual.qml
index c5c6d4c8dc0520303a5197e7082273e1d2dd916b..96d07ff67e432bbb20d331ed5c32b23c03dcc1c1 100644
--- a/src/PlanView/MissionItemMapVisual.qml
+++ b/src/PlanView/MissionItemMapVisual.qml
@@ -21,7 +21,8 @@ import QGroundControl.Controls 1.0
Item {
id: _root
- property var map ///< Map control to place item in
+ property var map ///< Map control to place item in
+ property var qgcView ///< QGCView to use for popping dialogs
signal clicked(int sequenceNumber)
@@ -33,7 +34,7 @@ Item {
if (component.status === Component.Error) {
console.log("Error loading Qml: ", object.mapVisualQML, component.errorString())
}
- _visualItem = component.createObject(map, { "map": _root.map })
+ _visualItem = component.createObject(map, { "map": _root.map, "qgcView": _root.qgcView })
_visualItem.clicked.connect(_root.clicked)
}
}
diff --git a/src/PlanView/PlanView.qml b/src/PlanView/PlanView.qml
index 2012d6eebb240eaed3f95d2576c36f60de9ca655..45b57ba852ce51a646fe9abaaabe8ba115ac1223 100644
--- a/src/PlanView/PlanView.qml
+++ b/src/PlanView/PlanView.qml
@@ -346,6 +346,7 @@ QGCView {
delegate: MissionItemMapVisual {
map: editorMap
+ qgcView: _qgcView
onClicked: _missionController.setCurrentPlanViewIndex(sequenceNumber, false)
visible: _editingLayer == _layerMission
}
diff --git a/src/PlanView/SimpleItemMapVisual.qml b/src/PlanView/SimpleItemMapVisual.qml
index 2677f6504bf828e1a8b9b90bf1aee9414c0ea218..49009ccaae7f120a330e89fe196c0c492239e2a1 100644
--- a/src/PlanView/SimpleItemMapVisual.qml
+++ b/src/PlanView/SimpleItemMapVisual.qml
@@ -21,7 +21,9 @@ import QGroundControl.FlightMap 1.0
/// Simple Mission Item visuals
Item {
id: _root
- property var map ///< Map control to place item in
+
+ property var map ///< Map control to place item in
+ property var qgcView ///< QGCView to use for popping dialogs
property var _missionItem: object
property var _itemVisual
diff --git a/src/PlanView/StructureScanMapVisual.qml b/src/PlanView/StructureScanMapVisual.qml
index 0f691ea5f255fddef379f0e727a6528866f7467d..21f97ce10e81f111048f975f2f9c11a959a5ebc2 100644
--- a/src/PlanView/StructureScanMapVisual.qml
+++ b/src/PlanView/StructureScanMapVisual.qml
@@ -22,7 +22,8 @@ import QGroundControl.FlightMap 1.0
Item {
id: _root
- property var map ///< Map control to place item in
+ property var map ///< Map control to place item in
+ property var qgcView ///< QGCView to use for popping dialogs
property var _missionItem: object
property var _structurePolygon: object.structurePolygon
@@ -85,6 +86,7 @@ Item {
}
QGCMapPolygonVisuals {
+ qgcView: _root.qgcView
mapControl: map
mapPolygon: _structurePolygon
interactive: _missionItem.isCurrentItem
@@ -95,6 +97,7 @@ Item {
}
QGCMapPolygonVisuals {
+ qgcView: _root.qgcView
mapControl: map
mapPolygon: _flightPolygon
interactive: false
diff --git a/src/PlanView/SurveyMapVisual.qml b/src/PlanView/SurveyMapVisual.qml
index 4110ddcbe9e0c675ab50c70c9ac8175c7582400f..08a52be380aa7cfa768bb91fd59eed983fe0a5c1 100644
--- a/src/PlanView/SurveyMapVisual.qml
+++ b/src/PlanView/SurveyMapVisual.qml
@@ -22,7 +22,8 @@ import QGroundControl.FlightMap 1.0
Item {
id: _root
- property var map ///< Map control to place item in
+ property var map ///< Map control to place item in
+ property var qgcView ///< QGCView to use for popping dialogs
property var _missionItem: object
property var _mapPolygon: object.mapPolygon