From f55d443a50f23f5ed5f7be4d5d19d64261a8b286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Jos=C3=A9=20Pereira?= Date: Wed, 9 Aug 2017 11:27:04 -0300 Subject: [PATCH] Add KML integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick José Pereira --- src/MissionManager/MissionController.cc | 40 ++++++++++++++++++++++ src/MissionManager/MissionController.h | 4 +++ src/MissionManager/PlanMasterController.cc | 39 +++++++++++++++++++++ src/MissionManager/PlanMasterController.h | 4 +++ src/PlanView/PlanView.qml | 22 +++++++++++- src/Settings/AppSettings.cc | 1 + src/Settings/AppSettings.h | 2 ++ 7 files changed, 111 insertions(+), 1 deletion(-) diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 14abc40d7..bd1acc5f0 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -8,6 +8,7 @@ ****************************************************************************/ +#include "MissionCommandUIInfo.h" #include "MissionController.h" #include "MultiVehicleManager.h" #include "MissionManager.h" @@ -25,6 +26,7 @@ #include "MissionSettingsItem.h" #include "QGCQGeoCoordinate.h" #include "PlanMasterController.h" +#include "KML.h" #ifndef __mobile__ #include "MainWindow.h" @@ -256,6 +258,44 @@ bool MissionController::_convertToMissionItems(QmlObjectListModel* visualMission return endActionSet; } +void MissionController::convertToKMLDocument(QDomDocument& document) +{ + QJsonObject missionJson; + QmlObjectListModel* visualItems = new QmlObjectListModel(); + QList missionItens; + QString error; + save(missionJson); + _loadItemsFromJson(missionJson, visualItems, error); + _convertToMissionItems(visualItems, missionItens, this); + + float altitude = missionJson[_jsonPlannedHomePositionKey].toArray()[2].toDouble(); + + QString coord; + QStringList coords; + // Drop home position + bool dropPoint = true; + for(const auto& item : missionItens) { + if(dropPoint) { + dropPoint = false; + continue; + } + const MissionCommandUIInfo* uiInfo = \ + qgcApp()->toolbox()->missionCommandTree()->getUIInfo(_controllerVehicle, item->command()); + + if (uiInfo && uiInfo->specifiesCoordinate() && !uiInfo->isStandaloneCoordinate()) { + coord = QString::number(item->param6()) \ + + "," \ + + QString::number(item->param5()) \ + + "," \ + + QString::number(item->param7() + altitude); + coords.append(coord); + } + } + Kml kml; + kml.points(coords); + kml.save(document); +} + void MissionController::sendItemsToVehicle(Vehicle* vehicle, QmlObjectListModel* visualMissionItems) { if (vehicle) { diff --git a/src/MissionManager/MissionController.h b/src/MissionManager/MissionController.h index 54362623d..abc553c37 100644 --- a/src/MissionManager/MissionController.h +++ b/src/MissionManager/MissionController.h @@ -26,6 +26,7 @@ class MissionSettingsItem; class AppSettings; class MissionManager; class SimpleMissionItem; +class QDomDocument; Q_DECLARE_LOGGING_CATEGORY(MissionControllerLog) @@ -125,6 +126,9 @@ public: void managerVehicleChanged (Vehicle* managerVehicle) final; bool showPlanFromManagerVehicle (void) final; + // Create KML file + void convertToKMLDocument(QDomDocument& document); + // Property accessors QmlObjectListModel* visualItems (void) { return _visualItems; } diff --git a/src/MissionManager/PlanMasterController.cc b/src/MissionManager/PlanMasterController.cc index 6b9e453f6..ca5bfe040 100644 --- a/src/MissionManager/PlanMasterController.cc +++ b/src/MissionManager/PlanMasterController.cc @@ -14,7 +14,9 @@ #include "AppSettings.h" #include "JsonHelper.h" #include "MissionManager.h" +#include "KML.h" +#include #include #include @@ -372,6 +374,30 @@ void PlanMasterController::saveToFile(const QString& filename) } } +void PlanMasterController::saveToKml(const QString& filename) +{ + if (filename.isEmpty()) { + return; + } + + QString kmlFilename = filename; + if (!QFileInfo(filename).fileName().contains(".")) { + kmlFilename += QString(".%1").arg(kmlFileExtension()); + } + + QFile file(kmlFilename); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qgcApp()->showMessage(tr("KML save error %1 : %2").arg(filename).arg(file.errorString())); + } else { + QDomDocument domDocument; + _missionController.convertToKMLDocument(domDocument); + QTextStream stream(&file); + stream << domDocument.toString(); + file.close(); + } +} + void PlanMasterController::removeAll(void) { _missionController.removeAll(); @@ -417,6 +443,11 @@ QString PlanMasterController::fileExtension(void) const return AppSettings::planFileExtension; } +QString PlanMasterController::kmlFileExtension(void) const +{ + return AppSettings::kmlFileExtension; +} + QStringList PlanMasterController::loadNameFilters(void) const { QStringList filters; @@ -435,6 +466,14 @@ QStringList PlanMasterController::saveNameFilters(void) const return filters; } +QStringList PlanMasterController::saveKmlFilters(void) const +{ + QStringList filters; + + filters << tr("KML Files (*.%1)").arg(kmlFileExtension()) << tr("All Files (*.*)"); + return filters; +} + void PlanMasterController::sendPlanToVehicle(Vehicle* vehicle, const QString& filename) { // Use a transient PlanMasterController to accomplish this diff --git a/src/MissionManager/PlanMasterController.h b/src/MissionManager/PlanMasterController.h index 6e388a4ec..03ba7b6e3 100644 --- a/src/MissionManager/PlanMasterController.h +++ b/src/MissionManager/PlanMasterController.h @@ -43,6 +43,7 @@ public: ///< kml file extension for missions Q_PROPERTY(QStringList loadNameFilters READ loadNameFilters CONSTANT) ///< File filter list loading plan files Q_PROPERTY(QStringList saveNameFilters READ saveNameFilters CONSTANT) ///< File filter list saving plan files + Q_PROPERTY(QStringList saveKmlFilters READ saveKmlFilters CONSTANT) ///< File filter list saving KML files /// Should be called immediately upon Component.onCompleted. /// @param editMode true: controller being used in Plan view, false: controller being used in Fly view @@ -60,6 +61,7 @@ public: Q_INVOKABLE void sendToVehicle(void); Q_INVOKABLE void loadFromFile(const QString& filename); Q_INVOKABLE void saveToFile(const QString& filename); + Q_INVOKABLE void saveToKml(const QString& filename); Q_INVOKABLE void removeAll(void); ///< Removes all from controller only, synce required to remove from vehicle Q_INVOKABLE void removeAllFromVehicle(void); ///< Removes all from vehicle and controller @@ -73,8 +75,10 @@ public: bool dirty (void) const; void setDirty (bool dirty); QString fileExtension (void) const; + QString kmlFileExtension(void) const; QStringList loadNameFilters (void) const; QStringList saveNameFilters (void) const; + QStringList saveKmlFilters (void) const; Vehicle* controllerVehicle(void) { return _controllerVehicle; } Vehicle* managerVehicle(void) { return _managerVehicle; } diff --git a/src/PlanView/PlanView.qml b/src/PlanView/PlanView.qml index c2d515617..30618bf04 100644 --- a/src/PlanView/PlanView.qml +++ b/src/PlanView/PlanView.qml @@ -169,6 +169,7 @@ QGCView { function saveToSelectedFile() { fileDialog.title = qsTr("Save Plan") + fileDialog.plan = true fileDialog.selectExisting = false fileDialog.nameFilters = masterController.saveNameFilters fileDialog.openForSave() @@ -177,6 +178,14 @@ QGCView { function fitViewportToItems() { mapFitFunctions.fitMapViewportToMissionItems() } + + function saveKmlToSelectedFile() { + fileDialog.title = qsTr("Save KML") + fileDialog.plan = false + fileDialog.selectExisting = false + fileDialog.nameFilters = masterController.saveKmlFilters + fileDialog.openForSave() + } } Connections { @@ -228,12 +237,13 @@ QGCView { QGCFileDialog { id: fileDialog qgcView: _qgcView + property var plan: true folder: QGroundControl.settingsManager.appSettings.missionSavePath fileExtension: QGroundControl.settingsManager.appSettings.planFileExtension fileExtension2: QGroundControl.settingsManager.appSettings.missionFileExtension onAcceptedForSave: { - masterController.saveToFile(file) + plan ? masterController.saveToFile(file) : masterController.saveToKml(file) close() } @@ -792,6 +802,16 @@ QGCView { _qgcView.showDialog(removeAllPromptDialog, qsTr("Remove all"), _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No) } } + + QGCButton { + text: qsTr("Save KML...") + Layout.fillWidth: true + enabled: !masterController.syncInProgress + onClicked: { + dropPanel.hide() + masterController.saveKmlToSelectedFile() + } + } } } } diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index f8166d5e8..65068ef39 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -44,6 +44,7 @@ const char* AppSettings::waypointsFileExtension = "waypoints"; const char* AppSettings::fenceFileExtension = "fence"; const char* AppSettings::rallyPointFileExtension = "rally"; const char* AppSettings::telemetryFileExtension = "tlog"; +const char* AppSettings::kmlFileExtension = "kml"; const char* AppSettings::logFileExtension = "ulg"; const char* AppSettings::parameterDirectory = "Parameters"; diff --git a/src/Settings/AppSettings.h b/src/Settings/AppSettings.h index 8df20b4d3..5b29ac370 100644 --- a/src/Settings/AppSettings.h +++ b/src/Settings/AppSettings.h @@ -52,6 +52,7 @@ public: Q_PROPERTY(QString waypointsFileExtension MEMBER waypointsFileExtension CONSTANT) Q_PROPERTY(QString parameterFileExtension MEMBER parameterFileExtension CONSTANT) Q_PROPERTY(QString telemetryFileExtension MEMBER telemetryFileExtension CONSTANT) + Q_PROPERTY(QString kmlFileExtension MEMBER kmlFileExtension CONSTANT) Q_PROPERTY(QString logFileExtension MEMBER logFileExtension CONSTANT) Fact* offlineEditingFirmwareType (void); @@ -115,6 +116,7 @@ public: static const char* fenceFileExtension; static const char* rallyPointFileExtension; static const char* telemetryFileExtension; + static const char* kmlFileExtension; static const char* logFileExtension; // Child directories of savePath for specific file types -- 2.22.0