diff --git a/ChangeLog.md b/ChangeLog.md index f035c450f1531fe8c8002bb04fe38dd1778c8a92..3a49d9eda786bdb4cf7cf09bf966e04c665791f1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -10,6 +10,8 @@ Note: This file only contains high level features or important fixes. ### 4.0.6 - Not yet released +* Plan: Much better conversion of missions to KML for 3d visualization/verification of missions + ### 4.0.5 - Stable * Solo: Fix mission upload failures diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index eeb4682cbedd09c9da808d7a3a9de04891f06621..2f2eab3c2a1333dc03f485f72d38919c2b599b52 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -587,7 +587,7 @@ HEADERS += \ src/MissionManager/FixedWingLandingComplexItem.h \ src/MissionManager/GeoFenceController.h \ src/MissionManager/GeoFenceManager.h \ - src/MissionManager/KML.h \ + src/MissionManager/KMLPlanDomDocument.h \ src/MissionManager/MissionCommandList.h \ src/MissionManager/MissionCommandTree.h \ src/MissionManager/MissionCommandUIInfo.h \ @@ -795,7 +795,7 @@ SOURCES += \ src/MissionManager/FixedWingLandingComplexItem.cc \ src/MissionManager/GeoFenceController.cc \ src/MissionManager/GeoFenceManager.cc \ - src/MissionManager/KML.cc \ + src/MissionManager/KMLPlanDomDocument.cc \ src/MissionManager/MissionCommandList.cc \ src/MissionManager/MissionCommandTree.cc \ src/MissionManager/MissionCommandUIInfo.cc \ diff --git a/src/FlightMap/MapItems/MissionLineView.qml b/src/FlightMap/MapItems/MissionLineView.qml index 447e611d60638c0374f215d86f25ae6ae6ff3045..c8945bb7e59d3dd5aa80afd3c6fb8d4dd5f05ead 100644 --- a/src/FlightMap/MapItems/MissionLineView.qml +++ b/src/FlightMap/MapItems/MissionLineView.qml @@ -7,7 +7,6 @@ * ****************************************************************************/ - import QtQuick 2.3 import QtLocation 5.3 import QtPositioning 5.3 @@ -20,7 +19,7 @@ MapItemView { property bool showSpecialVisual: false delegate: MapPolyline { line.width: 3 - line.color: object && showSpecialVisual && object.specialVisual ? "green" : "#be781c" // Hack, can't get palette to work in here + line.color: object && showSpecialVisual && object.specialVisual ? "green" : QGroundControl.globalPalette.mapMissionTrajectory z: QGroundControl.zOrderWaypointLines path: object && object.coordinate1.isValid && object.coordinate2.isValid ? [ object.coordinate1, object.coordinate2 ] : [] } diff --git a/src/MissionManager/CorridorScanComplexItem.cc b/src/MissionManager/CorridorScanComplexItem.cc index aa003ebc611e3a321dc0f77b5b2e0653820a60e6..01a4b3c2879a04456b00f6b7d53ce2f486a0f73a 100644 --- a/src/MissionManager/CorridorScanComplexItem.cc +++ b/src/MissionManager/CorridorScanComplexItem.cc @@ -11,12 +11,12 @@ #include "JsonHelper.h" #include "MissionController.h" #include "QGCGeo.h" -#include "QGroundControlQmlGlobal.h" #include "QGCQGeoCoordinate.h" #include "SettingsManager.h" #include "AppSettings.h" #include "QGCQGeoCoordinate.h" #include "PlanMasterController.h" +#include "QGCApplication.h" #include diff --git a/src/MissionManager/FixedWingLandingComplexItem.cc b/src/MissionManager/FixedWingLandingComplexItem.cc index d2f8256da6a62d71f073bd088daf2a143ff2f3d8..32db40605970691d73b1035f4266abd097d6e193 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.cc +++ b/src/MissionManager/FixedWingLandingComplexItem.cc @@ -11,7 +11,6 @@ #include "JsonHelper.h" #include "MissionController.h" #include "QGCGeo.h" -#include "QGroundControlQmlGlobal.h" #include "SimpleMissionItem.h" #include "PlanMasterController.h" diff --git a/src/MissionManager/KML.cc b/src/MissionManager/KML.cc deleted file mode 100644 index ed2ffaab119b99dbdc3a4a35d317e13faac861ac..0000000000000000000000000000000000000000 --- a/src/MissionManager/KML.cc +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2020 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - -#include "KML.h" - -#include -#include - -const QString Kml::_version("version=\"1.0\""); -const QString Kml::_encoding("encoding=\"UTF-8\""); -const QString Kml::_opengis("http://www.opengis.net/kml/2.2"); -const QString Kml::_qgckml("QGC KML"); - -Kml::Kml() -{ - //create header - createHeader(); - //name - createTextElement(_docEle, "name", _qgckml); - //open - createTextElement(_docEle, "open", "1"); - //create style - createStyles(); -} - -void Kml::points(const QStringList& points) -{ - //create placemark - QDomElement placemark = _domDocument.createElement("Placemark"); - _docEle.appendChild(placemark); - createTextElement(placemark, "styleUrl", "yellowLineGreenPoly"); - createTextElement(placemark, "name", "Absolute"); - createTextElement(placemark, "visibility", "0"); - createTextElement(placemark, "description", "Transparent purple line"); - - QStringList latLonAlt = points[0].split(","); - QStringList lookAtList({latLonAlt[0], latLonAlt[1], "0" \ - , "-100", "45", "2500"}); - createLookAt(placemark, lookAtList); - - //Add linestring - QDomElement lineString = _domDocument.createElement("LineString"); - placemark.appendChild(lineString); - - //extruder - createTextElement(lineString, "extruder", "1"); - createTextElement(lineString, "tessellate", "1"); - createTextElement(lineString, "altitudeMode", "absolute"); - QString coordinates; - for(const auto& point : points) { - coordinates += point + "\n"; - } - createTextElement(lineString, "coordinates", coordinates); -} - -void Kml::save(QDomDocument& document) -{ - document = _domDocument; -} - -void Kml::createHeader() -{ - QDomProcessingInstruction header = _domDocument.createProcessingInstruction("xml", _version + " " + _encoding); - _domDocument.appendChild(header); - QDomElement kml = _domDocument.createElement("kml"); - kml.setAttribute("xmlns", _opengis); - _docEle = _domDocument.createElement("Document"); - kml.appendChild(_docEle); - _domDocument.appendChild(kml); -} - -void Kml::createStyles() -{ - QDomElement style = _domDocument.createElement("Style"); - style.setAttribute("id", "yellowLineGreenPoly"); - createStyleLine(style, "7f00ffff", "4", "7f00ff00"); - _docEle.appendChild(style); -} - -void Kml::createLookAt(QDomElement& placemark, const QStringList &lookAtList) -{ - QDomElement lookAt = _domDocument.createElement("LookAt"); - placemark.appendChild(lookAt); - createTextElement(lookAt, "longitude", lookAtList[0]); - createTextElement(lookAt, "latitude", lookAtList[1]); - createTextElement(lookAt, "altitude", lookAtList[2]); - createTextElement(lookAt, "heading", lookAtList[3]); - createTextElement(lookAt, "tilt", lookAtList[4]); - createTextElement(lookAt, "range", lookAtList[5]); -} - -void Kml::createTextElement(QDomElement& domEle, const QString& elementName, const QString& textElement) -{ - // textElement - auto element = _domDocument.createElement(elementName); - element.appendChild(_domDocument.createTextNode(textElement)); - domEle.appendChild(element); -} - -void Kml::createStyleLine(QDomElement& domEle, const QString& lineColor, const QString& lineWidth, const QString& polyColor) -{ - /* - - 7f00ffff - 4 - - - 7f00ff00 - - */ - auto lineStyle = _domDocument.createElement("LineStyle"); - auto polyStyle = _domDocument.createElement("PolyStyle"); - domEle.appendChild(lineStyle); - domEle.appendChild(polyStyle); - createTextElement(lineStyle, "color", lineColor); - createTextElement(lineStyle, "width", lineWidth); - createTextElement(polyStyle, "color", polyColor); -} - -Kml::~Kml() -{ -} diff --git a/src/MissionManager/KML.h b/src/MissionManager/KML.h deleted file mode 100644 index 88e08a3fad292613b6404a9f61f56a2aa755d48e..0000000000000000000000000000000000000000 --- a/src/MissionManager/KML.h +++ /dev/null @@ -1,42 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2020 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - -#ifndef KML_H -#define KML_H - -#include -#include - -class Kml -{ - -public: - Kml(); - ~Kml(); - - void points(const QStringList& points); - void polygon(const QStringList& points); - void save(QDomDocument& document); - -private: - void createHeader(); - void createLookAt(QDomElement& placemark, const QStringList &lookAtList); - void createStyles(); - void createStyleLine(QDomElement& domEle, const QString& lineColor, const QString& lineWidth, const QString& polyColor); - void createTextElement(QDomElement& domEle, const QString& elementName, const QString& textElement); - - QDomDocument _domDocument; - QDomElement _docEle; - static const QString _encoding; - static const QString _opengis; - static const QString _qgckml; - static const QString _version; -}; - -#endif diff --git a/src/MissionManager/KMLPlanDomDocument.cc b/src/MissionManager/KMLPlanDomDocument.cc new file mode 100644 index 0000000000000000000000000000000000000000..25e3715101f7193e1d707b86f757f544c1971195 --- /dev/null +++ b/src/MissionManager/KMLPlanDomDocument.cc @@ -0,0 +1,175 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "KMLPlanDomDocument.h" +#include "QGCPalette.h" +#include "QGCApplication.h" +#include "MissionCommandTree.h" +#include "MissionCommandUIInfo.h" +#include "FactMetaData.h" + +#include +#include + +const char* KMLPlanDomDocument::_missionLineStyleName = "MissionLineStyle"; +const char* KMLPlanDomDocument::_ballonStyleName = "BalloonStyle"; + +KMLPlanDomDocument::KMLPlanDomDocument() +{ + QDomProcessingInstruction header = createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\"")); + appendChild(header); + + QDomElement kmlElement = createElement(QStringLiteral("kml")); + kmlElement.setAttribute(QStringLiteral("xmlns"), "http://www.opengis.net/kml/2.2"); + + _documentElement = createElement(QStringLiteral("Document")); + kmlElement.appendChild(_documentElement); + appendChild(kmlElement); + + _addTextElement(_documentElement, "name", QStringLiteral("%1 Plan KML").arg(qgcApp()->applicationName())); + _addTextElement(_documentElement, "open", "1"); + + _addStyles(); +} + +QString KMLPlanDomDocument::_kmlCoordString(const QGeoCoordinate& coord) +{ + return QStringLiteral("%1,%2,%3").arg(QString::number(coord.longitude(), 'f', 7)).arg(QString::number(coord.latitude(), 'f', 7)).arg(QString::number(coord.altitude(), 'f', 2)); +} + +void KMLPlanDomDocument::addMissionItems(Vehicle* vehicle, QList rgMissionItems) +{ + if (rgMissionItems.count() == 0) { + return; + } + + QDomElement itemFolderElement = createElement("Folder"); + _documentElement.appendChild(itemFolderElement); + + _addTextElement(itemFolderElement, "name", "Items"); + + QDomElement flightPathElement = createElement("Placemark"); + _documentElement.appendChild(flightPathElement); + + _addTextElement(flightPathElement, "styleUrl", QStringLiteral("#%1").arg(_missionLineStyleName)); + _addTextElement(flightPathElement, "name", "Flight Path"); + _addTextElement(flightPathElement, "visibility", "1"); + _addLookAt(flightPathElement, rgMissionItems[0]->coordinate()); + + // Build up the mission trajectory line coords + QList rgFlightCoords; + QGeoCoordinate homeCoord = rgMissionItems[0]->coordinate(); + for (const MissionItem* item : rgMissionItems) { + const MissionCommandUIInfo* uiInfo = qgcApp()->toolbox()->missionCommandTree()->getUIInfo(vehicle, item->command()); + if (uiInfo) { + double altAdjustment = item->frame() == MAV_FRAME_GLOBAL ? 0 : homeCoord.altitude(); // Used to convert to amsl + if (uiInfo->isTakeoffCommand() && !vehicle->fixedWing()) { + // These takeoff items go straight up from home position to specified altitude + QGeoCoordinate coord = homeCoord; + coord.setAltitude(item->param7() + altAdjustment); + rgFlightCoords += coord; + } + if (uiInfo->specifiesCoordinate()) { + QGeoCoordinate coord = item->coordinate(); + coord.setAltitude(coord.altitude() + altAdjustment); // convert to amsl + + if (!uiInfo->isStandaloneCoordinate()) { + // Flight path goes through this item + rgFlightCoords += coord; + } + + // Add a place mark for each WP + + QDomElement wpPlacemarkElement = createElement("Placemark"); + _addTextElement(wpPlacemarkElement, "name", QStringLiteral("%1 %2").arg(QString::number(item->sequenceNumber())).arg(item->command() == MAV_CMD_NAV_WAYPOINT ? "" : uiInfo->friendlyName())); + _addTextElement(wpPlacemarkElement, "styleUrl", QStringLiteral("#%1").arg(_ballonStyleName)); + + QDomElement wpPointElement = createElement("Point"); + _addTextElement(wpPointElement, "altitudeMode", "absolute"); + _addTextElement(wpPointElement, "coordinates", _kmlCoordString(coord)); + _addTextElement(wpPointElement, "extrude", "1"); + + QDomElement descriptionElement = createElement("description"); + QString htmlString; + htmlString += QStringLiteral("Index: %1\n").arg(item->sequenceNumber()); + htmlString += uiInfo->friendlyName() + "\n"; + htmlString += QStringLiteral("Alt AMSL: %1 %2\n").arg(QString::number(FactMetaData::metersToAppSettingsDistanceUnits(coord.altitude()).toDouble(), 'f', 2)).arg(FactMetaData::appSettingsDistanceUnitsString()); + htmlString += QStringLiteral("Alt Rel: %1 %2\n").arg(QString::number(FactMetaData::metersToAppSettingsDistanceUnits(coord.altitude() - homeCoord.altitude()).toDouble(), 'f', 2)).arg(FactMetaData::appSettingsDistanceUnitsString()); + htmlString += QStringLiteral("Lat: %1\n").arg(QString::number(coord.latitude(), 'f', 7)); + htmlString += QStringLiteral("Lon: %1\n").arg(QString::number(coord.longitude(), 'f', 7)); + QDomCDATASection cdataSection = createCDATASection(htmlString); + descriptionElement.appendChild(cdataSection); + + wpPlacemarkElement.appendChild(descriptionElement); + wpPlacemarkElement.appendChild(wpPointElement); + itemFolderElement.appendChild(wpPlacemarkElement); + } + } + } + + // Create a LineString element from the coords + + QDomElement lineStringElement = createElement("LineString"); + flightPathElement.appendChild(lineStringElement); + + _addTextElement(lineStringElement, "extruder", "1"); + _addTextElement(lineStringElement, "tessellate", "1"); + _addTextElement(lineStringElement, "altitudeMode", "absolute"); + + QString coordString; + for (const QGeoCoordinate& coord : rgFlightCoords) { + coordString += QStringLiteral("%1\n").arg(_kmlCoordString(coord)); + } + _addTextElement(lineStringElement, "coordinates", coordString); +} + +QString KMLPlanDomDocument::_kmlColorString (const QColor& color) +{ + return QStringLiteral("ff%1%2%3").arg(color.blue(), 2, 16, QChar('0')).arg(color.green(), 2, 16, QChar('0')).arg(color.red(), 2, 16, QChar('0')); +} + +void KMLPlanDomDocument::_addStyles(void) +{ + QGCPalette palette; + + QDomElement styleElement1 = createElement("Style"); + styleElement1.setAttribute("id", _missionLineStyleName); + QDomElement lineStyleElement = createElement("LineStyle"); + _addTextElement(lineStyleElement, "color", _kmlColorString(palette.mapMissionTrajectory())); + _addTextElement(lineStyleElement, "width", "4"); + styleElement1.appendChild(lineStyleElement); + + QDomElement styleElement2 = createElement("Style"); + styleElement2.setAttribute("id", _ballonStyleName); + QDomElement balloonStyleElement = createElement("BalloonStyle"); + _addTextElement(balloonStyleElement, "text", "$[description]"); + styleElement2.appendChild(balloonStyleElement); + + _documentElement.appendChild(styleElement1); + _documentElement.appendChild(styleElement2); +} + +void KMLPlanDomDocument::_addTextElement(QDomElement &element, const QString &name, const QString &value) +{ + QDomElement textElement = createElement(name); + textElement.appendChild(createTextNode(value)); + element.appendChild(textElement); +} + +void KMLPlanDomDocument::_addLookAt(QDomElement& element, const QGeoCoordinate& coord) +{ + QDomElement lookAtElement = createElement("LookAt"); + _addTextElement(lookAtElement, "latitude", QString::number(coord.latitude(), 'f', 7)); + _addTextElement(lookAtElement, "longitude", QString::number(coord.longitude(), 'f', 7)); + _addTextElement(lookAtElement, "altitude", QString::number(coord.longitude(), 'f', 2)); + _addTextElement(lookAtElement, "heading", "-100"); + _addTextElement(lookAtElement, "tilt", "45"); + _addTextElement(lookAtElement, "range", "2500"); + element.appendChild(lookAtElement); +} diff --git a/src/MissionManager/KMLPlanDomDocument.h b/src/MissionManager/KMLPlanDomDocument.h new file mode 100644 index 0000000000000000000000000000000000000000..2afaf1cd9439dd250f476a14d95795455edd1bd7 --- /dev/null +++ b/src/MissionManager/KMLPlanDomDocument.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include +#include +#include + +class MissionItem; +class Vehicle; + +/// Used to convert a Plan to a KML document +class KMLPlanDomDocument : public QDomDocument +{ + +public: + KMLPlanDomDocument(); + + void addMissionItems(Vehicle* vehicle, QList rgMissionItems); + +private: + void _addStyles (void); + QString _kmlColorString (const QColor& color); + void _addTextElement (QDomElement& element, const QString& name, const QString& value); + QString _kmlCoordString (const QGeoCoordinate& coord); + void _addLookAt(QDomElement& element, const QGeoCoordinate& coord); + + QDomElement _documentElement; + + static const char* _missionLineStyleName; + static const char* _ballonStyleName; +}; diff --git a/src/MissionManager/MavCmdInfoCommon.json b/src/MissionManager/MavCmdInfoCommon.json index 613087d70e59f47b0bebc822cf439c5f87845a20..56a42f9b5927985c38623a122366e49c6db17a28 100644 --- a/src/MissionManager/MavCmdInfoCommon.json +++ b/src/MissionManager/MavCmdInfoCommon.json @@ -172,6 +172,7 @@ "friendlyName": "Takeoff", "description": "Launch from the ground and travel towards the specified takeoff position.", "specifiesCoordinate": true, + "isTakeoffCommand": true, "friendlyEdit": true, "category": "Basic", "param1": { @@ -290,6 +291,7 @@ "friendlyName": "VTOL takeoff", "description": "Hover straight up to specified altitude, transition to fixed-wing and fly to the specified takeoff location.", "specifiesCoordinate": true, + "isTakeoffCommand": true, "friendlyEdit": true, "category": "Basic", "param4": { diff --git a/src/MissionManager/MissionCommandList.cc b/src/MissionManager/MissionCommandList.cc index 2a33d3cec5c95b7ef11262132c2754e8e8bac972..f156212a438d7f56ef349ab71e5127b56962b42d 100644 --- a/src/MissionManager/MissionCommandList.cc +++ b/src/MissionManager/MissionCommandList.cc @@ -12,7 +12,6 @@ #include "Vehicle.h" #include "FirmwarePluginManager.h" #include "QGCApplication.h" -#include "QGroundControlQmlGlobal.h" #include "JsonHelper.h" #include "MissionCommandUIInfo.h" diff --git a/src/MissionManager/MissionCommandTree.cc b/src/MissionManager/MissionCommandTree.cc index f7aa8bafa5ad67a2ee0c98b50e4a8c23c00ff051..bee62b823a9e54ff5fdecad7ca9defe0dfa8314a 100644 --- a/src/MissionManager/MissionCommandTree.cc +++ b/src/MissionManager/MissionCommandTree.cc @@ -14,7 +14,6 @@ #include "Vehicle.h" #include "FirmwarePluginManager.h" #include "QGCApplication.h" -#include "QGroundControlQmlGlobal.h" #include "MissionCommandUIInfo.h" #include "MissionCommandList.h" #include "SettingsManager.h" diff --git a/src/MissionManager/MissionCommandUIInfo.cc b/src/MissionManager/MissionCommandUIInfo.cc index cc664d0f7a1ba24077c2badadea250531b98166f..68b58370e9050208011fb0c8fb480fca43f525f3 100644 --- a/src/MissionManager/MissionCommandUIInfo.cc +++ b/src/MissionManager/MissionCommandUIInfo.cc @@ -38,6 +38,7 @@ const char* MissionCommandUIInfo::_standaloneCoordinateJsonKey = "standaloneCoo const char* MissionCommandUIInfo::_specifiesCoordinateJsonKey = "specifiesCoordinate"; const char* MissionCommandUIInfo::_specifiesAltitudeOnlyJsonKey = "specifiesAltitudeOnly"; const char* MissionCommandUIInfo::_isLandCommandJsonKey = "isLandCommand"; +const char* MissionCommandUIInfo::_isTakeoffCommandJsonKey = "isTakeoffCommand"; const char* MissionCommandUIInfo::_unitsJsonKey = "units"; const char* MissionCommandUIInfo::_commentJsonKey = "comment"; const char* MissionCommandUIInfo::_advancedCategory = "Advanced"; @@ -174,6 +175,15 @@ bool MissionCommandUIInfo::isLandCommand(void) const } } +bool MissionCommandUIInfo::isTakeoffCommand(void) const +{ + if (_infoMap.contains(_isTakeoffCommandJsonKey)) { + return _infoMap[_isTakeoffCommandJsonKey].toBool(); + } else { + return false; + } +} + void MissionCommandUIInfo::_overrideInfo(MissionCommandUIInfo* uiInfo) { // Override info values @@ -209,7 +219,7 @@ bool MissionCommandUIInfo::loadJsonInfo(const QJsonObject& jsonObject, bool requ QStringList allKeys; allKeys << _idJsonKey << _rawNameJsonKey << _friendlyNameJsonKey << _descriptionJsonKey << _standaloneCoordinateJsonKey << _specifiesCoordinateJsonKey <<_friendlyEditJsonKey << _param1JsonKey << _param2JsonKey << _param3JsonKey << _param4JsonKey << _param5JsonKey << _param6JsonKey << _param7JsonKey - << _paramRemoveJsonKey << _categoryJsonKey << _specifiesAltitudeOnlyJsonKey << _isLandCommandJsonKey; + << _paramRemoveJsonKey << _categoryJsonKey << _specifiesAltitudeOnlyJsonKey << _isLandCommandJsonKey << _isTakeoffCommandJsonKey; // Look for unknown keys in top level object for (const QString& key: jsonObject.keys()) { @@ -275,6 +285,9 @@ bool MissionCommandUIInfo::loadJsonInfo(const QJsonObject& jsonObject, bool requ if (jsonObject.contains(_isLandCommandJsonKey)) { _infoMap[_isLandCommandJsonKey] = jsonObject.value(_isLandCommandJsonKey).toBool(); } + if (jsonObject.contains(_isTakeoffCommandJsonKey)) { + _infoMap[_isTakeoffCommandJsonKey] = jsonObject.value(_isTakeoffCommandJsonKey).toBool(); + } if (jsonObject.contains(_friendlyEditJsonKey)) { _infoMap[_friendlyEditJsonKey] = jsonObject.value(_friendlyEditJsonKey).toVariant(); } @@ -305,6 +318,9 @@ bool MissionCommandUIInfo::loadJsonInfo(const QJsonObject& jsonObject, bool requ if (!_infoAvailable(_isLandCommandJsonKey)) { _setInfoValue(_isLandCommandJsonKey, false); } + if (!_infoAvailable(_isTakeoffCommandJsonKey)) { + _setInfoValue(_isTakeoffCommandJsonKey, false); + } if (!_infoAvailable(_friendlyEditJsonKey)) { _setInfoValue(_friendlyEditJsonKey, false); } diff --git a/src/MissionManager/MissionCommandUIInfo.h b/src/MissionManager/MissionCommandUIInfo.h index 92201ba16a7db2bb8f4ee80bc9037982337e2d16..59b0a72a4ca147e765f070d33c4a8585438b3e7b 100644 --- a/src/MissionManager/MissionCommandUIInfo.h +++ b/src/MissionManager/MissionCommandUIInfo.h @@ -96,6 +96,7 @@ private: /// specifiesCoordinate bool false true: Command specifies a lat/lon/alt coordinate /// specifiesAltitudeOnly bool false true: Command specifies an altitude only (no coordinate) /// standaloneCoordinate bool false true: Vehicle does not fly through coordinate associated with command (exampl: ROI) +/// isTakeoffCommand bool false true: Command specifies a takeoff command (TAEKOFF, VTOL_TAKEOFF, ...) /// isLandCommand bool false true: Command specifies a land command (LAND, VTOL_LAND, ...) /// friendlyEdit bool false true: Command supports friendly editing dialog, false: Command supports 'Show all values" style editing only /// category string Advanced Category which this command belongs to @@ -120,6 +121,7 @@ public: Q_PROPERTY(bool specifiesCoordinate READ specifiesCoordinate CONSTANT) Q_PROPERTY(bool specifiesAltitudeOnly READ specifiesAltitudeOnly CONSTANT) Q_PROPERTY(bool isLandCommand READ isLandCommand CONSTANT) + Q_PROPERTY(bool isTakeoffCommand READ isTakeoffCommand CONSTANT) Q_PROPERTY(int command READ intCommand CONSTANT) MAV_CMD command(void) const { return _command; } @@ -134,6 +136,7 @@ public: bool specifiesCoordinate (void) const; bool specifiesAltitudeOnly (void) const; bool isLandCommand (void) const; + bool isTakeoffCommand (void) const; /// Load the data in the object from the specified json /// @param jsonObject Json object to load from @@ -194,6 +197,7 @@ private: static const char* _specifiesCoordinateJsonKey; static const char* _specifiesAltitudeOnlyJsonKey; static const char* _isLandCommandJsonKey; + static const char* _isTakeoffCommandJsonKey; static const char* _unitsJsonKey; static const char* _commentJsonKey; static const char* _advancedCategory; diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 27182c2cf7669770d5f1bbe0534f538a055dbe26..2b8e1fc9c466d90420613b4aafda61d761e1c12a 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -28,7 +28,7 @@ #include "MissionSettingsItem.h" #include "QGCQGeoCoordinate.h" #include "PlanMasterController.h" -#include "KML.h" +#include "KMLPlanDomDocument.h" #include "QGCCorePlugin.h" #include "TakeoffMissionItem.h" #include "PlanViewSettings.h" @@ -273,46 +273,14 @@ bool MissionController::_convertToMissionItems(QmlObjectListModel* visualMission return endActionSet; } -void MissionController::convertToKMLDocument(QDomDocument& document) +void MissionController::addMissionToKML(KMLPlanDomDocument& planKML) { QObject* deleteParent = new QObject(); QList rgMissionItems; _convertToMissionItems(_visualItems, rgMissionItems, deleteParent); - if (rgMissionItems.count() == 0) { - return; - } - - const double homePositionAltitude = _settingsItem->coordinate().altitude(); - - QString coord; - QStringList coords; - // Drop home position - bool dropPoint = true; - for(const auto& item : rgMissionItems) { - if(dropPoint) { - dropPoint = false; - continue; - } - const MissionCommandUIInfo* uiInfo = \ - qgcApp()->toolbox()->missionCommandTree()->getUIInfo(_controllerVehicle, item->command()); - - if (uiInfo && uiInfo->specifiesCoordinate() && !uiInfo->isStandaloneCoordinate()) { - double amslAltitude = item->param7() + (item->frame() == MAV_FRAME_GLOBAL ? 0 : homePositionAltitude); - coord = QString::number(item->param6(),'f',7) \ - + "," \ - + QString::number(item->param5(),'f',7) \ - + "," \ - + QString::number(amslAltitude,'f',2); - coords.append(coord); - } - } - + planKML.addMissionItems(_controllerVehicle, rgMissionItems); deleteParent->deleteLater(); - - Kml kml; - kml.points(coords); - kml.save(document); } void MissionController::sendItemsToVehicle(Vehicle* vehicle, QmlObjectListModel* visualMissionItems) diff --git a/src/MissionManager/MissionController.h b/src/MissionManager/MissionController.h index 2274408d6f3c9f86e38e9a09acc1a7625f5bd0c2..c85e726b0079f860ee4bb93b6d0eeba11ba3a85d 100644 --- a/src/MissionManager/MissionController.h +++ b/src/MissionManager/MissionController.h @@ -13,7 +13,7 @@ #include "QmlObjectListModel.h" #include "Vehicle.h" #include "QGCLoggingCategory.h" - +#include "KMLPlanDomDocument.h" #include "QGCGeoBoundingCube.h" #include @@ -195,7 +195,7 @@ public: bool showPlanFromManagerVehicle (void) final; // Create KML file - void convertToKMLDocument(QDomDocument& document); + void addMissionToKML(KMLPlanDomDocument& planKML); // Property accessors diff --git a/src/MissionManager/MissionSettingsItem.cc b/src/MissionManager/MissionSettingsItem.cc index 30c412489f294a09454777bea3128e52d00bc0db..4302a21ea6e5aaad1450987ef716c7b23b55587e 100644 --- a/src/MissionManager/MissionSettingsItem.cc +++ b/src/MissionManager/MissionSettingsItem.cc @@ -11,7 +11,6 @@ #include "JsonHelper.h" #include "MissionController.h" #include "QGCGeo.h" -#include "QGroundControlQmlGlobal.h" #include "SimpleMissionItem.h" #include "SettingsManager.h" #include "AppSettings.h" diff --git a/src/MissionManager/MissionSettingsTest.cc b/src/MissionManager/MissionSettingsTest.cc index 40e45be9db6595d099035777deca364b9fd36456..994d8b9eb5b3874cb99187830b3c2fa4c374615c 100644 --- a/src/MissionManager/MissionSettingsTest.cc +++ b/src/MissionManager/MissionSettingsTest.cc @@ -9,7 +9,6 @@ #include "MissionSettingsTest.h" #include "QGCApplication.h" -#include "QGroundControlQmlGlobal.h" #include "SettingsManager.h" MissionSettingsTest::MissionSettingsTest(void) diff --git a/src/MissionManager/PlanMasterController.cc b/src/MissionManager/PlanMasterController.cc index 953e7f46778042dc8256bdf790a9c202413036e5..0bebc7424dea49bac90bcfcd52860615f4f5f576 100644 --- a/src/MissionManager/PlanMasterController.cc +++ b/src/MissionManager/PlanMasterController.cc @@ -15,7 +15,7 @@ #include "AppSettings.h" #include "JsonHelper.h" #include "MissionManager.h" -#include "KML.h" +#include "KMLPlanDomDocument.h" #include "SurveyPlanCreator.h" #include "StructureScanPlanCreator.h" #include "CorridorScanPlanCreator.h" @@ -492,10 +492,10 @@ void PlanMasterController::saveToKml(const QString& filename) 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); + KMLPlanDomDocument planKML; + _missionController.addMissionToKML(planKML); QTextStream stream(&file); - stream << domDocument.toString(); + stream << planKML.toString(); file.close(); } } diff --git a/src/MissionManager/RallyPointController.cc b/src/MissionManager/RallyPointController.cc index 9a85904d02d61bdcb221482b3c78940142b591df..77be144488159873bc98485c35761ee0fd28f067 100644 --- a/src/MissionManager/RallyPointController.cc +++ b/src/MissionManager/RallyPointController.cc @@ -7,10 +7,6 @@ * ****************************************************************************/ - -/// @file -/// @author Don Gagne - #include "RallyPointController.h" #include "RallyPoint.h" #include "Vehicle.h" @@ -20,7 +16,6 @@ #include "ParameterManager.h" #include "JsonHelper.h" #include "SimpleMissionItem.h" -#include "QGroundControlQmlGlobal.h" #include "SettingsManager.h" #include "AppSettings.h" #include "PlanMasterController.h" diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index 60a2dd0cf90058c210377ab6e3d573c3da28b7bc..7232f759f4359d1c2753b6e59e9c7f798dd128fb 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -12,11 +12,11 @@ #include "JsonHelper.h" #include "MissionController.h" #include "QGCGeo.h" -#include "QGroundControlQmlGlobal.h" #include "QGCQGeoCoordinate.h" #include "SettingsManager.h" #include "AppSettings.h" #include "PlanMasterController.h" +#include "QGCApplication.h" #include diff --git a/src/MissionManager/TakeoffMissionItem.cc b/src/MissionManager/TakeoffMissionItem.cc index b2bf8db06db99bd24970c29b50431170a67cb6e9..e6bfc703066f948354245a8359603b0970ddaf5b 100644 --- a/src/MissionManager/TakeoffMissionItem.cc +++ b/src/MissionManager/TakeoffMissionItem.cc @@ -116,7 +116,9 @@ void TakeoffMissionItem::setCoordinate(const QGeoCoordinate& coordinate) bool TakeoffMissionItem::isTakeoffCommand(MAV_CMD command) { - return command == MAV_CMD_NAV_TAKEOFF || command == MAV_CMD_NAV_VTOL_TAKEOFF; + const MissionCommandUIInfo* uiInfo = qgcApp()->toolbox()->missionCommandTree()->getUIInfo(qgcApp()->toolbox()->multiVehicleManager()->offlineEditingVehicle(), command); + + return uiInfo ? uiInfo->isTakeoffCommand() : false; } void TakeoffMissionItem::_initLaunchTakeoffAtSameLocation(void) diff --git a/src/MissionManager/TransectStyleComplexItem.cc b/src/MissionManager/TransectStyleComplexItem.cc index 727d0bc4857cef213c8918a0bc22fd455fb88f9b..78941c7e9e1d5e2f90cfa85a53c384cfbc917378 100644 --- a/src/MissionManager/TransectStyleComplexItem.cc +++ b/src/MissionManager/TransectStyleComplexItem.cc @@ -11,11 +11,11 @@ #include "JsonHelper.h" #include "MissionController.h" #include "QGCGeo.h" -#include "QGroundControlQmlGlobal.h" #include "QGCQGeoCoordinate.h" #include "SettingsManager.h" #include "AppSettings.h" #include "QGCQGeoCoordinate.h" +#include "QGCApplication.h" #include diff --git a/src/MissionManager/VisualMissionItem.h b/src/MissionManager/VisualMissionItem.h index 616e7f58e3a3563e9083117454951d1c77ab9b30..729ce1c02f54184a1b5932ae7ea925c9135c3bc7 100644 --- a/src/MissionManager/VisualMissionItem.h +++ b/src/MissionManager/VisualMissionItem.h @@ -68,7 +68,7 @@ public: Q_PROPERTY(bool specifiesAltitudeOnly READ specifiesAltitudeOnly NOTIFY specifiesAltitudeOnlyChanged) ///< true: Item has altitude only, no full coordinate Q_PROPERTY(bool isSimpleItem READ isSimpleItem NOTIFY isSimpleItemChanged) ///< Simple or Complex MissionItem Q_PROPERTY(bool isTakeoffItem READ isTakeoffItem NOTIFY isTakeoffItemChanged) ///< true: Takeoff item special case - Q_PROPERTY(bool isLandCommand READ isLandCommand NOTIFY isLandCommandChanged) ///< true: Takeoff item special case + Q_PROPERTY(bool isLandCommand READ isLandCommand NOTIFY isLandCommandChanged) Q_PROPERTY(QString editorQml MEMBER _editorQml CONSTANT) ///< Qml code for editing this item Q_PROPERTY(QString mapVisualQML READ mapVisualQML CONSTANT) ///< QMl code for map visuals Q_PROPERTY(double specifiedFlightSpeed READ specifiedFlightSpeed NOTIFY specifiedFlightSpeedChanged) ///< NaN for not specified diff --git a/src/QGCPalette.cc b/src/QGCPalette.cc index 6abd89938b517fec3e805f05150d8755954cd952..69986af82f6b2e6617ff2a33afbd07e3c2f780a0 100644 --- a/src/QGCPalette.cc +++ b/src/QGCPalette.cc @@ -81,11 +81,15 @@ void QGCPalette::_buildMap() DECLARE_QGC_COLOR(statusPassedText, "#9d9d9d", "#000000", "#707070", "#ffffff") DECLARE_QGC_COLOR(statusPendingText, "#9d9d9d", "#000000", "#707070", "#ffffff") - // Colors are not affecting by theming - DECLARE_QGC_COLOR(mapWidgetBorderLight, "#ffffff", "#ffffff", "#ffffff", "#ffffff") - DECLARE_QGC_COLOR(mapWidgetBorderDark, "#000000", "#000000", "#000000", "#000000") - DECLARE_QGC_COLOR(brandingPurple, "#4A2C6D", "#4A2C6D", "#4A2C6D", "#4A2C6D") - DECLARE_QGC_COLOR(brandingBlue, "#48D6FF", "#6045c5", "#48D6FF", "#6045c5") + // Colors not affecting by theming + // Disabled Enabled + DECLARE_QGC_NONTHEMED_COLOR(brandingPurple, "#4A2C6D", "#4A2C6D") + DECLARE_QGC_NONTHEMED_COLOR(brandingBlue, "#48D6FF", "#6045c5") + + // Colors not affecting by theming or enable/disable + DECLARE_QGC_SINGLE_COLOR(mapWidgetBorderLight, "#ffffff") + DECLARE_QGC_SINGLE_COLOR(mapWidgetBorderDark, "#000000") + DECLARE_QGC_SINGLE_COLOR(mapMissionTrajectory, "#be781c") } void QGCPalette::setColorGroupEnabled(bool enabled) diff --git a/src/QGCPalette.h b/src/QGCPalette.h index 91f597ef9a2ec6c58662913689611a3cc5ec31b4..62fbf5a5e00403157f6ca43650c65687d240ba4f 100644 --- a/src/QGCPalette.h +++ b/src/QGCPalette.h @@ -28,6 +28,34 @@ _colors << #name; \ } +#define DECLARE_QGC_NONTHEMED_COLOR(name, disabledColor, enabledColor) \ + { \ + PaletteColorInfo_t colorInfo = { \ + { QColor(disabledColor), QColor(enabledColor) }, \ + { QColor(disabledColor), QColor(enabledColor) } \ + }; \ + qgcApp()->toolbox()->corePlugin()->paletteOverride(#name, colorInfo); \ + _colorInfoMap[Light][ColorGroupEnabled][QStringLiteral(#name)] = colorInfo[Light][ColorGroupEnabled]; \ + _colorInfoMap[Light][ColorGroupDisabled][QStringLiteral(#name)] = colorInfo[Light][ColorGroupDisabled]; \ + _colorInfoMap[Dark][ColorGroupEnabled][QStringLiteral(#name)] = colorInfo[Dark][ColorGroupEnabled]; \ + _colorInfoMap[Dark][ColorGroupDisabled][QStringLiteral(#name)] = colorInfo[Dark][ColorGroupDisabled]; \ + _colors << #name; \ + } + +#define DECLARE_QGC_SINGLE_COLOR(name, color) \ + { \ + PaletteColorInfo_t colorInfo = { \ + { QColor(color), QColor(color) }, \ + { QColor(color), QColor(color) } \ + }; \ + qgcApp()->toolbox()->corePlugin()->paletteOverride(#name, colorInfo); \ + _colorInfoMap[Light][ColorGroupEnabled][QStringLiteral(#name)] = colorInfo[Light][ColorGroupEnabled]; \ + _colorInfoMap[Light][ColorGroupDisabled][QStringLiteral(#name)] = colorInfo[Light][ColorGroupDisabled]; \ + _colorInfoMap[Dark][ColorGroupEnabled][QStringLiteral(#name)] = colorInfo[Dark][ColorGroupEnabled]; \ + _colorInfoMap[Dark][ColorGroupDisabled][QStringLiteral(#name)] = colorInfo[Dark][ColorGroupDisabled]; \ + _colors << #name; \ + } + #define DEFINE_QGC_COLOR(NAME, SETNAME) \ Q_PROPERTY(QColor NAME READ NAME WRITE SETNAME NOTIFY paletteChanged) \ Q_PROPERTY(QStringList NAME ## Colors READ NAME ## Colors NOTIFY paletteChanged) \ @@ -103,6 +131,7 @@ public: DEFINE_QGC_COLOR(mapIndicatorChild, setMapIndicatorChild) DEFINE_QGC_COLOR(mapWidgetBorderLight, setMapWidgetBorderLight) DEFINE_QGC_COLOR(mapWidgetBorderDark, setMapWidgetBorderDark) + DEFINE_QGC_COLOR(mapMissionTrajectory, setMapMissionTrajectory) DEFINE_QGC_COLOR(brandingPurple, setBrandingPurple) DEFINE_QGC_COLOR(brandingBlue, setBrandingBlue) DEFINE_QGC_COLOR(colorGreen, setColorGreen) diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index c89fa1676a2a77bb397dfa4a0c0653c8a1a62bbb..20ac868a18fa35972fefbcbbae3e67cd196fa04c 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -67,6 +67,7 @@ void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) _gpsRtkFactGroup = qgcApp()->gpsRtkFactGroup(); _airspaceManager = toolbox->airspaceManager(); _adsbVehicleManager = toolbox->adsbVehicleManager(); + _globalPalette = new QGCPalette(this); #if defined(QGC_ENABLE_PAIRING) _pairingManager = toolbox->pairingManager(); #endif diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 1028a0b0ff789f9600dbba3c29c06afc6fe1404b..a3ebcf4d6ffe8d4276e2008c1612dd0b957cf37e 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -24,6 +24,7 @@ #include "AppSettings.h" #include "AirspaceManager.h" #include "ADSBVehicleManager.h" +#include "QGCPalette.h" #if defined(QGC_ENABLE_PAIRING) #include "PairingManager.h" #endif @@ -81,6 +82,7 @@ public: Q_PROPERTY(MicrohardManager* microhardManager READ microhardManager CONSTANT) Q_PROPERTY(bool microhardSupported READ microhardSupported CONSTANT) Q_PROPERTY(bool supportsPairing READ supportsPairing CONSTANT) + Q_PROPERTY(QGCPalette* globalPalette MEMBER _globalPalette CONSTANT) // This palette will always return enabled colors #if defined(QGC_ENABLE_PAIRING) Q_PROPERTY(PairingManager* pairingManager READ pairingManager CONSTANT) #endif @@ -280,6 +282,7 @@ private: TaisyncManager* _taisyncManager = nullptr; MicrohardManager* _microhardManager = nullptr; ADSBVehicleManager* _adsbVehicleManager = nullptr; + QGCPalette* _globalPalette = nullptr; #if defined(QGC_ENABLE_PAIRING) PairingManager* _pairingManager = nullptr; #endif diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 7f4180d75297d2482df8b71de7c1ccb4d9c6cac5..654cf59254e3070563cdf8a767b8524885bed8c9 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -13,7 +13,6 @@ #include "UAS.h" #include "QGCApplication.h" #include "FollowMe.h" -#include "QGroundControlQmlGlobal.h" #include "ParameterManager.h" #include "SettingsManager.h" #include "QGCCorePlugin.h" diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 9bd2e9111d45cff4f8205ba896a1522d0735114a..75708ec4b0ba5557d399f1cafee983b2de56a48a 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -31,7 +31,6 @@ #include "QGCApplication.h" #include "QGCImageProvider.h" #include "MissionCommandTree.h" -#include "QGroundControlQmlGlobal.h" #include "SettingsManager.h" #include "QGCQGeoCoordinate.h" #include "QGCCorePlugin.h"