Unverified Commit 70c5153b authored by Don Gagne's avatar Don Gagne Committed by GitHub

Merge pull request #6539 from DonLakeFlyer/LoadKML

Support for Sync - Load KML
parents 9b98f66f 03d3d538
...@@ -515,6 +515,7 @@ HEADERS += \ ...@@ -515,6 +515,7 @@ HEADERS += \
src/Joystick/Joystick.h \ src/Joystick/Joystick.h \
src/Joystick/JoystickManager.h \ src/Joystick/JoystickManager.h \
src/JsonHelper.h \ src/JsonHelper.h \
src/KMLFileHelper.h \
src/LogCompressor.h \ src/LogCompressor.h \
src/MG.h \ src/MG.h \
src/MissionManager/CameraCalc.h \ src/MissionManager/CameraCalc.h \
...@@ -711,6 +712,7 @@ SOURCES += \ ...@@ -711,6 +712,7 @@ SOURCES += \
src/Joystick/Joystick.cc \ src/Joystick/Joystick.cc \
src/Joystick/JoystickManager.cc \ src/Joystick/JoystickManager.cc \
src/JsonHelper.cc \ src/JsonHelper.cc \
src/KMLFileHelper.cc \
src/LogCompressor.cc \ src/LogCompressor.cc \
src/MissionManager/CameraCalc.cc \ src/MissionManager/CameraCalc.cc \
src/MissionManager/CameraSection.cc \ src/MissionManager/CameraSection.cc \
......
/****************************************************************************
*
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#include "KMLFileHelper.h"
#include <QFile>
QDomDocument KMLFileHelper::loadFile(const QString& kmlFile, QString& errorString)
{
QFile file(kmlFile);
errorString.clear();
if (!file.exists()) {
errorString = tr("File not found: %1").arg(kmlFile);
return QDomDocument();
}
if (!file.open(QIODevice::ReadOnly)) {
errorString = tr("Unable to open file: %1 error: $%2").arg(kmlFile).arg(file.errorString());
return QDomDocument();
}
QDomDocument doc;
QString errorMessage;
int errorLine;
if (!doc.setContent(&file, &errorMessage, &errorLine)) {
errorString = tr("Unable to parse KML file: %1 error: %2 line: %3").arg(kmlFile).arg(errorMessage).arg(errorLine);
return QDomDocument();
}
return doc;
}
QVariantList KMLFileHelper::determineFileContents(const QString& kmlFile)
{
QString errorString;
KMLFileContents fileContents = determineFileContents(kmlFile, errorString);
QVariantList varList;
varList.append(QVariant::fromValue(fileContents));
varList.append(QVariant::fromValue(errorString));
return varList;
}
KMLFileHelper::KMLFileContents KMLFileHelper::determineFileContents(const QString& kmlFile, QString& errorString)
{
QDomDocument domDocument = KMLFileHelper::loadFile(kmlFile, errorString);
if (!errorString.isEmpty()) {
return Error;
}
QDomNodeList rgNodes = domDocument.elementsByTagName("Polygon");
if (rgNodes.count()) {
return Polygon;
}
rgNodes = domDocument.elementsByTagName("LineString");
if (rgNodes.count()) {
return Polyline;
}
errorString = tr("No known type found in KML file.");
return Error;
}
bool KMLFileHelper::loadPolygonFromFile(const QString& kmlFile, QList<QGeoCoordinate>& vertices, QString& errorString)
{
errorString.clear();
vertices.clear();
QDomDocument domDocument = KMLFileHelper::loadFile(kmlFile, errorString);
if (!errorString.isEmpty()) {
return false;
}
QDomNodeList rgNodes = domDocument.elementsByTagName("Polygon");
if (rgNodes.count() == 0) {
errorString = tr("Unable to find Polygon node in KML");
return false;
}
QDomNode coordinatesNode = rgNodes.item(0).namedItem("outerBoundaryIs").namedItem("LinearRing").namedItem("coordinates");
if (coordinatesNode.isNull()) {
errorString = tr("Internal error: Unable to find coordinates node in KML");
return false;
}
QString coordinatesString = coordinatesNode.toElement().text().simplified();
QStringList rgCoordinateStrings = coordinatesString.split(" ");
QList<QGeoCoordinate> rgCoords;
for (int i=0; i<rgCoordinateStrings.count()-1; i++) {
QString coordinateString = rgCoordinateStrings[i];
QStringList rgValueStrings = coordinateString.split(",");
QGeoCoordinate coord;
coord.setLongitude(rgValueStrings[0].toDouble());
coord.setLatitude(rgValueStrings[1].toDouble());
rgCoords.append(coord);
}
// Determine winding, reverse if needed
double sum = 0;
for (int i=0; i<rgCoords.count(); i++) {
QGeoCoordinate coord1 = rgCoords[i];
QGeoCoordinate coord2 = (i == rgCoords.count() - 1) ? rgCoords[0] : rgCoords[i+1];
sum += (coord2.longitude() - coord1.longitude()) * (coord2.latitude() + coord1.latitude());
}
bool reverse = sum < 0.0;
if (reverse) {
QList<QGeoCoordinate> rgReversed;
for (int i=0; i<rgCoords.count(); i++) {
rgReversed.prepend(rgCoords[i]);
}
rgCoords = rgReversed;
}
vertices = rgCoords;
return true;
}
bool KMLFileHelper::loadPolylineFromFile(const QString& kmlFile, QList<QGeoCoordinate>& coords, QString& errorString)
{
errorString.clear();
coords.clear();
QDomDocument domDocument = KMLFileHelper::loadFile(kmlFile, errorString);
if (!errorString.isEmpty()) {
return false;
}
QDomNodeList rgNodes = domDocument.elementsByTagName("LineString");
if (rgNodes.count() == 0) {
errorString = tr("Unable to find LineString node in KML");
return false;
}
QDomNode coordinatesNode = rgNodes.item(0).namedItem("coordinates");
if (coordinatesNode.isNull()) {
errorString = tr("Internal error: Unable to find coordinates node in KML");
return false;
}
QString coordinatesString = coordinatesNode.toElement().text().simplified();
QStringList rgCoordinateStrings = coordinatesString.split(" ");
QList<QGeoCoordinate> rgCoords;
for (int i=0; i<rgCoordinateStrings.count()-1; i++) {
QString coordinateString = rgCoordinateStrings[i];
QStringList rgValueStrings = coordinateString.split(",");
QGeoCoordinate coord;
coord.setLongitude(rgValueStrings[0].toDouble());
coord.setLatitude(rgValueStrings[1].toDouble());
rgCoords.append(coord);
}
coords = rgCoords;
return true;
}
/****************************************************************************
*
* (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.
*
****************************************************************************/
#pragma once
#include <QObject>
#include <QDomDocument>
#include <QList>
#include <QGeoCoordinate>
/// The QGCMapPolygon class provides a polygon which can be displayed on a map using a map visuals control.
/// It maintains a representation of the polygon on QVariantList and QmlObjectListModel format.
class KMLFileHelper : public QObject
{
Q_OBJECT
public:
enum KMLFileContents {
Polygon,
Polyline,
Error
};
Q_ENUM(KMLFileContents)
Q_INVOKABLE static QVariantList determineFileContents(const QString& kmlFile);
static KMLFileContents determineFileContents(const QString& kmlFile, QString& errorString);
static QDomDocument loadFile(const QString& kmlFile, QString& errorString);
static bool loadPolygonFromFile(const QString& kmlFile, QList<QGeoCoordinate>& vertices, QString& errorString);
static bool loadPolylineFromFile(const QString& kmlFile, QList<QGeoCoordinate>& coords, QString& errorString);
};
...@@ -27,7 +27,7 @@ const char* CorridorScanComplexItem::_jsonEntryPointKey = "EntryPoint"; ...@@ -27,7 +27,7 @@ const char* CorridorScanComplexItem::_jsonEntryPointKey = "EntryPoint";
const char* CorridorScanComplexItem::jsonComplexItemTypeValue = "CorridorScan"; const char* CorridorScanComplexItem::jsonComplexItemTypeValue = "CorridorScan";
CorridorScanComplexItem::CorridorScanComplexItem(Vehicle* vehicle, bool flyView, QObject* parent) CorridorScanComplexItem::CorridorScanComplexItem(Vehicle* vehicle, bool flyView, const QString& kmlFile, QObject* parent)
: TransectStyleComplexItem (vehicle, flyView, settingsGroup, parent) : TransectStyleComplexItem (vehicle, flyView, settingsGroup, parent)
, _entryPoint (0) , _entryPoint (0)
, _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CorridorScan.SettingsGroup.json"), this)) , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CorridorScan.SettingsGroup.json"), this))
...@@ -50,6 +50,12 @@ CorridorScanComplexItem::CorridorScanComplexItem(Vehicle* vehicle, bool flyView, ...@@ -50,6 +50,12 @@ CorridorScanComplexItem::CorridorScanComplexItem(Vehicle* vehicle, bool flyView,
connect(&_corridorPolyline, &QGCMapPolyline::pathChanged, this, &CorridorScanComplexItem::_rebuildCorridorPolygon); connect(&_corridorPolyline, &QGCMapPolyline::pathChanged, this, &CorridorScanComplexItem::_rebuildCorridorPolygon);
connect(&_corridorWidthFact, &Fact::valueChanged, this, &CorridorScanComplexItem::_rebuildCorridorPolygon); connect(&_corridorWidthFact, &Fact::valueChanged, this, &CorridorScanComplexItem::_rebuildCorridorPolygon);
if (!kmlFile.isEmpty()) {
_corridorPolyline.loadKMLFile(kmlFile);
_corridorPolyline.setDirty(false);
}
setDirty(false);
} }
void CorridorScanComplexItem::save(QJsonArray& planItems) void CorridorScanComplexItem::save(QJsonArray& planItems)
......
...@@ -23,7 +23,10 @@ class CorridorScanComplexItem : public TransectStyleComplexItem ...@@ -23,7 +23,10 @@ class CorridorScanComplexItem : public TransectStyleComplexItem
Q_OBJECT Q_OBJECT
public: public:
CorridorScanComplexItem(Vehicle* vehicle, bool flyView, QObject* parent); /// @param vehicle Vehicle which this is being contructed for
/// @param flyView true: Created for use in the Fly View, false: Created for use in the Plan View
/// @param kmlFile Polyline comes from this file, empty for default polyline
CorridorScanComplexItem(Vehicle* vehicle, bool flyView, const QString& kmlFile, QObject* parent);
Q_PROPERTY(QGCMapPolyline* corridorPolyline READ corridorPolyline CONSTANT) Q_PROPERTY(QGCMapPolyline* corridorPolyline READ corridorPolyline CONSTANT)
Q_PROPERTY(Fact* corridorWidth READ corridorWidth CONSTANT) Q_PROPERTY(Fact* corridorWidth READ corridorWidth CONSTANT)
......
...@@ -23,7 +23,7 @@ void CorridorScanComplexItemTest::init(void) ...@@ -23,7 +23,7 @@ void CorridorScanComplexItemTest::init(void)
UnitTest::init(); UnitTest::init();
_offlineVehicle = new Vehicle(MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, qgcApp()->toolbox()->firmwarePluginManager(), this); _offlineVehicle = new Vehicle(MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, qgcApp()->toolbox()->firmwarePluginManager(), this);
_corridorItem = new CorridorScanComplexItem(_offlineVehicle, false /* flyView */, this); _corridorItem = new CorridorScanComplexItem(_offlineVehicle, false /* flyView */, QString() /* kmlFile */, this /* parent */);
// vehicleSpeed need for terrain calcs // vehicleSpeed need for terrain calcs
MissionController::MissionFlightStatus_t missionFlightStatus; MissionController::MissionFlightStatus_t missionFlightStatus;
......
...@@ -389,25 +389,48 @@ int MissionController::insertROIMissionItem(QGeoCoordinate coordinate, int i) ...@@ -389,25 +389,48 @@ int MissionController::insertROIMissionItem(QGeoCoordinate coordinate, int i)
int MissionController::insertComplexMissionItem(QString itemName, QGeoCoordinate mapCenterCoordinate, int i) int MissionController::insertComplexMissionItem(QString itemName, QGeoCoordinate mapCenterCoordinate, int i)
{ {
ComplexMissionItem* newItem; ComplexMissionItem* newItem;
bool surveyStyleItem = false;
int sequenceNumber = _nextSequenceNumber(); int sequenceNumber = _nextSequenceNumber();
if (itemName == _surveyMissionItemName) { if (itemName == _surveyMissionItemName) {
newItem = new SurveyComplexItem(_controllerVehicle, _flyView, _visualItems); newItem = new SurveyComplexItem(_controllerVehicle, _flyView, QString() /* kmlFile */, _visualItems /* parent */);
newItem->setCoordinate(mapCenterCoordinate); newItem->setCoordinate(mapCenterCoordinate);
surveyStyleItem = true;
} else if (itemName == _fwLandingMissionItemName) { } else if (itemName == _fwLandingMissionItemName) {
newItem = new FixedWingLandingComplexItem(_controllerVehicle, _flyView, _visualItems); newItem = new FixedWingLandingComplexItem(_controllerVehicle, _flyView, _visualItems /* parent */);
} else if (itemName == _structureScanMissionItemName) { } else if (itemName == _structureScanMissionItemName) {
newItem = new StructureScanComplexItem(_controllerVehicle, _flyView, _visualItems); newItem = new StructureScanComplexItem(_controllerVehicle, _flyView, QString() /* kmlFile */, _visualItems /* parent */);
} else if (itemName == _corridorScanMissionItemName) { } else if (itemName == _corridorScanMissionItemName) {
newItem = new CorridorScanComplexItem(_controllerVehicle, _flyView, _visualItems); newItem = new CorridorScanComplexItem(_controllerVehicle, _flyView, QString() /* kmlFile */, _visualItems /* parent */);
surveyStyleItem = true;
} else { } else {
qWarning() << "Internal error: Unknown complex item:" << itemName; qWarning() << "Internal error: Unknown complex item:" << itemName;
return sequenceNumber; return sequenceNumber;
} }
return _insertComplexMissionItemWorker(newItem, i);
}
int MissionController::insertComplexMissionItemFromKML(QString itemName, QString kmlFile, int i)
{
ComplexMissionItem* newItem;
if (itemName == _surveyMissionItemName) {
newItem = new SurveyComplexItem(_controllerVehicle, _flyView, kmlFile, _visualItems);
} else if (itemName == _structureScanMissionItemName) {
newItem = new StructureScanComplexItem(_controllerVehicle, _flyView, kmlFile, _visualItems);
} else if (itemName == _corridorScanMissionItemName) {
newItem = new CorridorScanComplexItem(_controllerVehicle, _flyView, kmlFile, _visualItems);
} else {
qWarning() << "Internal error: Unknown complex item:" << itemName;
return _nextSequenceNumber();
}
return _insertComplexMissionItemWorker(newItem, i);
}
int MissionController::_insertComplexMissionItemWorker(ComplexMissionItem* complexItem, int i)
{
int sequenceNumber = _nextSequenceNumber();
bool surveyStyleItem = qobject_cast<SurveyComplexItem*>(complexItem) || qobject_cast<CorridorScanComplexItem*>(complexItem);
if (surveyStyleItem) { if (surveyStyleItem) {
bool rollSupported = false; bool rollSupported = false;
bool pitchSupported = false; bool pitchSupported = false;
...@@ -434,14 +457,18 @@ int MissionController::insertComplexMissionItem(QString itemName, QGeoCoordinate ...@@ -434,14 +457,18 @@ int MissionController::insertComplexMissionItem(QString itemName, QGeoCoordinate
} }
} }
newItem->setSequenceNumber(sequenceNumber); complexItem->setSequenceNumber(sequenceNumber);
_initVisualItem(newItem); _initVisualItem(complexItem);
_visualItems->insert(i, newItem); if (i == -1) {
_visualItems->append(complexItem);
} else {
_visualItems->insert(i, complexItem);
}
_recalcAll(); _recalcAll();
return newItem->sequenceNumber(); return complexItem->sequenceNumber();
} }
void MissionController::removeMissionItem(int index) void MissionController::removeMissionItem(int index)
...@@ -529,7 +556,7 @@ bool MissionController::_loadJsonMissionFileV1(const QJsonObject& json, QmlObjec ...@@ -529,7 +556,7 @@ bool MissionController::_loadJsonMissionFileV1(const QJsonObject& json, QmlObjec
return false; return false;
} }
SurveyComplexItem* item = new SurveyComplexItem(_controllerVehicle, _flyView, visualItems); SurveyComplexItem* item = new SurveyComplexItem(_controllerVehicle, _flyView, QString() /* kmlFile */, visualItems /* parent */);
const QJsonObject itemObject = itemValue.toObject(); const QJsonObject itemObject = itemValue.toObject();
if (item->load(itemObject, itemObject["id"].toInt(), errorString)) { if (item->load(itemObject, itemObject["id"].toInt(), errorString)) {
surveyItems.append(item); surveyItems.append(item);
...@@ -687,7 +714,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec ...@@ -687,7 +714,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec
if (complexItemType == SurveyComplexItem::jsonComplexItemTypeValue) { if (complexItemType == SurveyComplexItem::jsonComplexItemTypeValue) {
qCDebug(MissionControllerLog) << "Loading Survey: nextSequenceNumber" << nextSequenceNumber; qCDebug(MissionControllerLog) << "Loading Survey: nextSequenceNumber" << nextSequenceNumber;
SurveyComplexItem* surveyItem = new SurveyComplexItem(_controllerVehicle, _flyView, visualItems); SurveyComplexItem* surveyItem = new SurveyComplexItem(_controllerVehicle, _flyView, QString() /* kmlFile */, visualItems);
if (!surveyItem->load(itemObject, nextSequenceNumber++, errorString)) { if (!surveyItem->load(itemObject, nextSequenceNumber++, errorString)) {
return false; return false;
} }
...@@ -705,7 +732,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec ...@@ -705,7 +732,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec
visualItems->append(landingItem); visualItems->append(landingItem);
} else if (complexItemType == StructureScanComplexItem::jsonComplexItemTypeValue) { } else if (complexItemType == StructureScanComplexItem::jsonComplexItemTypeValue) {
qCDebug(MissionControllerLog) << "Loading Structure Scan: nextSequenceNumber" << nextSequenceNumber; qCDebug(MissionControllerLog) << "Loading Structure Scan: nextSequenceNumber" << nextSequenceNumber;
StructureScanComplexItem* structureItem = new StructureScanComplexItem(_controllerVehicle, _flyView, visualItems); StructureScanComplexItem* structureItem = new StructureScanComplexItem(_controllerVehicle, _flyView, QString() /* kmlFile */, visualItems);
if (!structureItem->load(itemObject, nextSequenceNumber++, errorString)) { if (!structureItem->load(itemObject, nextSequenceNumber++, errorString)) {
return false; return false;
} }
...@@ -714,7 +741,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec ...@@ -714,7 +741,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec
visualItems->append(structureItem); visualItems->append(structureItem);
} else if (complexItemType == CorridorScanComplexItem::jsonComplexItemTypeValue) { } else if (complexItemType == CorridorScanComplexItem::jsonComplexItemTypeValue) {
qCDebug(MissionControllerLog) << "Loading Corridor Scan: nextSequenceNumber" << nextSequenceNumber; qCDebug(MissionControllerLog) << "Loading Corridor Scan: nextSequenceNumber" << nextSequenceNumber;
CorridorScanComplexItem* corridorItem = new CorridorScanComplexItem(_controllerVehicle, _flyView, visualItems); CorridorScanComplexItem* corridorItem = new CorridorScanComplexItem(_controllerVehicle, _flyView, QString() /* kmlFile */, visualItems);
if (!corridorItem->load(itemObject, nextSequenceNumber++, errorString)) { if (!corridorItem->load(itemObject, nextSequenceNumber++, errorString)) {
return false; return false;
} }
......
...@@ -25,6 +25,7 @@ class MissionSettingsItem; ...@@ -25,6 +25,7 @@ class MissionSettingsItem;
class AppSettings; class AppSettings;
class MissionManager; class MissionManager;
class SimpleMissionItem; class SimpleMissionItem;
class ComplexMissionItem;
class QDomDocument; class QDomDocument;
Q_DECLARE_LOGGING_CATEGORY(MissionControllerLog) Q_DECLARE_LOGGING_CATEGORY(MissionControllerLog)
...@@ -89,6 +90,10 @@ public: ...@@ -89,6 +90,10 @@ public:
Q_PROPERTY(int batteryChangePoint READ batteryChangePoint NOTIFY batteryChangePointChanged) Q_PROPERTY(int batteryChangePoint READ batteryChangePoint NOTIFY batteryChangePointChanged)
Q_PROPERTY(int batteriesRequired READ batteriesRequired NOTIFY batteriesRequiredChanged) Q_PROPERTY(int batteriesRequired READ batteriesRequired NOTIFY batteriesRequiredChanged)
Q_PROPERTY(QString surveyComplexItemName READ surveyComplexItemName CONSTANT)
Q_PROPERTY(QString corridorScanComplexItemName READ corridorScanComplexItemName CONSTANT)
Q_PROPERTY(QString structureScanComplexItemName READ structureScanComplexItemName CONSTANT)
Q_INVOKABLE void removeMissionItem(int index); Q_INVOKABLE void removeMissionItem(int index);
/// Add a new simple mission item to the list /// Add a new simple mission item to the list
...@@ -108,6 +113,12 @@ public: ...@@ -108,6 +113,12 @@ public:
/// @return Sequence number for new item /// @return Sequence number for new item
Q_INVOKABLE int insertComplexMissionItem(QString itemName, QGeoCoordinate mapCenterCoordinate, int i); Q_INVOKABLE int insertComplexMissionItem(QString itemName, QGeoCoordinate mapCenterCoordinate, int i);
/// Add a new complex mission item to the list
/// @param itemName: Name of complex item to create (from complexMissionItemNames)
/// @param i: index to insert at, -1 for end
/// @return Sequence number for new item
Q_INVOKABLE int insertComplexMissionItemFromKML(QString itemName, QString kmlFile, int i);
Q_INVOKABLE void resumeMission(int resumeIndex); Q_INVOKABLE void resumeMission(int resumeIndex);
/// Updates the altitudes of the items in the current mission to the new default altitude /// Updates the altitudes of the items in the current mission to the new default altitude
...@@ -148,13 +159,16 @@ public: ...@@ -148,13 +159,16 @@ public:
// Property accessors // Property accessors
QmlObjectListModel* visualItems (void) { return _visualItems; } QmlObjectListModel* visualItems (void) { return _visualItems; }
QmlObjectListModel* waypointLines (void) { return &_waypointLines; } QmlObjectListModel* waypointLines (void) { return &_waypointLines; }
QVariantList waypointPath (void) { return _waypointPath; } QVariantList waypointPath (void) { return _waypointPath; }
QStringList complexMissionItemNames (void) const; QStringList complexMissionItemNames (void) const;
QGeoCoordinate plannedHomePosition (void) const; QGeoCoordinate plannedHomePosition (void) const;
VisualMissionItem* currentPlanViewItem (void) const; VisualMissionItem* currentPlanViewItem (void) const;
double progressPct (void) const { return _progressPct; } double progressPct (void) const { return _progressPct; }
QString surveyComplexItemName (void) const { return _surveyMissionItemName; }
QString corridorScanComplexItemName (void) const { return _corridorScanMissionItemName; }
QString structureScanComplexItemName(void) const { return _structureScanMissionItemName; }
int currentMissionIndex (void) const; int currentMissionIndex (void) const;
int resumeMissionIndex (void) const; int resumeMissionIndex (void) const;
...@@ -242,6 +256,7 @@ private: ...@@ -242,6 +256,7 @@ private:
void _addWaypointLineSegment(CoordVectHashTable& prevItemPairHashTable, VisualItemPair& pair); void _addWaypointLineSegment(CoordVectHashTable& prevItemPairHashTable, VisualItemPair& pair);
void _addCommandTimeDelay(SimpleMissionItem* simpleItem, bool vtolInHover); void _addCommandTimeDelay(SimpleMissionItem* simpleItem, bool vtolInHover);
void _addTimeDistance(bool vtolInHover, double hoverTime, double cruiseTime, double extraTime, double distance, int seqNum); void _addTimeDistance(bool vtolInHover, double hoverTime, double cruiseTime, double extraTime, double distance, int seqNum);
int _insertComplexMissionItemWorker(ComplexMissionItem* complexItem, int i);
private: private:
MissionManager* _missionManager; MissionManager* _missionManager;
......
...@@ -484,7 +484,7 @@ QStringList PlanMasterController::saveNameFilters(void) const ...@@ -484,7 +484,7 @@ QStringList PlanMasterController::saveNameFilters(void) const
return filters; return filters;
} }
QStringList PlanMasterController::saveKmlFilters(void) const QStringList PlanMasterController::fileKmlFilters(void) const
{ {
QStringList filters; QStringList filters;
......
...@@ -43,7 +43,7 @@ public: ...@@ -43,7 +43,7 @@ public:
///< kml file extension for missions ///< kml file extension for missions
Q_PROPERTY(QStringList loadNameFilters READ loadNameFilters CONSTANT) ///< File filter list loading plan files 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 saveNameFilters READ saveNameFilters CONSTANT) ///< File filter list saving plan files
Q_PROPERTY(QStringList saveKmlFilters READ saveKmlFilters CONSTANT) ///< File filter list saving KML files Q_PROPERTY(QStringList fileKmlFilters READ fileKmlFilters CONSTANT) ///< File filter list for load/save KML files
/// Should be called immediately upon Component.onCompleted. /// Should be called immediately upon Component.onCompleted.
Q_INVOKABLE void start(bool flyView); Q_INVOKABLE void start(bool flyView);
...@@ -81,7 +81,7 @@ public: ...@@ -81,7 +81,7 @@ public:
QString kmlFileExtension(void) const; QString kmlFileExtension(void) const;
QStringList loadNameFilters (void) const; QStringList loadNameFilters (void) const;
QStringList saveNameFilters (void) const; QStringList saveNameFilters (void) const;
QStringList saveKmlFilters (void) const; QStringList fileKmlFilters (void) const;
QJsonDocument saveToJson (); QJsonDocument saveToJson ();
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "JsonHelper.h" #include "JsonHelper.h"
#include "QGCQGeoCoordinate.h" #include "QGCQGeoCoordinate.h"
#include "QGCApplication.h" #include "QGCApplication.h"
#include "KMLFileHelper.h"
#include <QGeoRectangle> #include <QGeoRectangle>
#include <QDebug> #include <QDebug>
...@@ -453,71 +454,11 @@ void QGCMapPolygon::offset(double distance) ...@@ -453,71 +454,11 @@ void QGCMapPolygon::offset(double distance)
bool QGCMapPolygon::loadKMLFile(const QString& kmlFile) bool QGCMapPolygon::loadKMLFile(const QString& kmlFile)
{ {
QFile file(kmlFile); QString errorString;
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<QGeoCoordinate> rgCoords; QList<QGeoCoordinate> rgCoords;
for (int i=0; i<rgCoordinateStrings.count()-1; i++) { if (!KMLFileHelper::loadPolygonFromFile(kmlFile, rgCoords, errorString)) {
QString coordinateString = rgCoordinateStrings[i]; qgcApp()->showMessage(errorString);
return false;
QStringList rgValueStrings = coordinateString.split(",");
QGeoCoordinate coord;
coord.setLongitude(rgValueStrings[0].toDouble());
coord.setLatitude(rgValueStrings[1].toDouble());
rgCoords.append(coord);
}
// Determine winding, reverse if needed
double sum = 0;
for (int i=0; i<rgCoords.count(); i++) {
QGeoCoordinate coord1 = rgCoords[i];
QGeoCoordinate coord2 = (i == rgCoords.count() - 1) ? rgCoords[0] : rgCoords[i+1];
sum += (coord2.longitude() - coord1.longitude()) * (coord2.latitude() + coord1.latitude());
}
bool reverse = sum < 0.0;
if (reverse) {
QList<QGeoCoordinate> rgReversed;
for (int i=0; i<rgCoords.count(); i++) {
rgReversed.prepend(rgCoords[i]);
}
rgCoords = rgReversed;
} }
clear(); clear();
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "JsonHelper.h" #include "JsonHelper.h"
#include "QGCQGeoCoordinate.h" #include "QGCQGeoCoordinate.h"
#include "QGCApplication.h" #include "QGCApplication.h"
#include "KMLFileHelper.h"
#include <QGeoRectangle> #include <QGeoRectangle>
#include <QDebug> #include <QDebug>
...@@ -338,52 +339,11 @@ QList<QGeoCoordinate> QGCMapPolyline::offsetPolyline(double distance) ...@@ -338,52 +339,11 @@ QList<QGeoCoordinate> QGCMapPolyline::offsetPolyline(double distance)
bool QGCMapPolyline::loadKMLFile(const QString& kmlFile) bool QGCMapPolyline::loadKMLFile(const QString& kmlFile)
{ {
QFile file(kmlFile); QString errorString;
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("LineString");
if (rgNodes.count() == 0) {
qgcApp()->showMessage(tr("Unable to find LineString node in KML"));
return false;
}
QDomNode coordinatesNode = rgNodes.item(0).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<QGeoCoordinate> rgCoords; QList<QGeoCoordinate> rgCoords;
for (int i=0; i<rgCoordinateStrings.count()-1; i++) { if (!KMLFileHelper::loadPolylineFromFile(kmlFile, rgCoords, errorString)) {
QString coordinateString = rgCoordinateStrings[i]; qgcApp()->showMessage(errorString);
return false;
QStringList rgValueStrings = coordinateString.split(",");
QGeoCoordinate coord;
coord.setLongitude(rgValueStrings[0].toDouble());
coord.setLatitude(rgValueStrings[1].toDouble());
rgCoords.append(coord);
} }
clear(); clear();
......
...@@ -83,7 +83,7 @@ void SectionTest::_commonScanTest(Section* section) ...@@ -83,7 +83,7 @@ void SectionTest::_commonScanTest(Section* section)
waypointVisualItems.append(&simpleItem); waypointVisualItems.append(&simpleItem);
QmlObjectListModel complexVisualItems; QmlObjectListModel complexVisualItems;
SurveyComplexItem surveyItem(_offlineVehicle, false /* fly View */, this); SurveyComplexItem surveyItem(_offlineVehicle, false /* fly View */, QString() /* kmlFile */, this /* parent */);
complexVisualItems.append(&surveyItem); complexVisualItems.append(&surveyItem);
// This tests the common cases which should not lead to scan succeess // This tests the common cases which should not lead to scan succeess
......
...@@ -30,7 +30,7 @@ const char* StructureScanComplexItem::jsonComplexItemTypeValue = "StructureSc ...@@ -30,7 +30,7 @@ const char* StructureScanComplexItem::jsonComplexItemTypeValue = "StructureSc
const char* StructureScanComplexItem::_jsonCameraCalcKey = "CameraCalc"; const char* StructureScanComplexItem::_jsonCameraCalcKey = "CameraCalc";
const char* StructureScanComplexItem::_jsonAltitudeRelativeKey = "altitudeRelative"; const char* StructureScanComplexItem::_jsonAltitudeRelativeKey = "altitudeRelative";
StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, bool flyView, QObject* parent) StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, bool flyView, const QString& kmlFile, QObject* parent)
: ComplexMissionItem (vehicle, flyView, parent) : ComplexMissionItem (vehicle, flyView, parent)
, _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/StructureScan.SettingsGroup.json"), this /* QObject parent */)) , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/StructureScan.SettingsGroup.json"), this /* QObject parent */))
, _sequenceNumber (0) , _sequenceNumber (0)
...@@ -75,6 +75,13 @@ StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, bool flyVie ...@@ -75,6 +75,13 @@ StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, bool flyVie
connect(&_layersFact, &Fact::valueChanged, this, &StructureScanComplexItem::_recalcCameraShots); connect(&_layersFact, &Fact::valueChanged, this, &StructureScanComplexItem::_recalcCameraShots);
_recalcLayerInfo(); _recalcLayerInfo();
if (!kmlFile.isEmpty()) {
_structurePolygon.loadKMLFile(kmlFile);
_structurePolygon.setDirty(false);
}
setDirty(false);
} }
void StructureScanComplexItem::_setScanDistance(double scanDistance) void StructureScanComplexItem::_setScanDistance(double scanDistance)
......
...@@ -25,7 +25,10 @@ class StructureScanComplexItem : public ComplexMissionItem ...@@ -25,7 +25,10 @@ class StructureScanComplexItem : public ComplexMissionItem
Q_OBJECT Q_OBJECT
public: public:
StructureScanComplexItem(Vehicle* vehicle, bool flyView, QObject* parent); /// @param vehicle Vehicle which this is being contructed for
/// @param flyView true: Created for use in the Fly View, false: Created for use in the Plan View
/// @param kmlFile Polygon comes from this file, empty for default polygon
StructureScanComplexItem(Vehicle* vehicle, bool flyView, const QString& kmlFile, QObject* parent);
Q_PROPERTY(CameraCalc* cameraCalc READ cameraCalc CONSTANT) Q_PROPERTY(CameraCalc* cameraCalc READ cameraCalc CONSTANT)
Q_PROPERTY(Fact* altitude READ altitude CONSTANT) Q_PROPERTY(Fact* altitude READ altitude CONSTANT)
......
...@@ -24,7 +24,7 @@ void StructureScanComplexItemTest::init(void) ...@@ -24,7 +24,7 @@ void StructureScanComplexItemTest::init(void)
_rgSignals[dirtyChangedIndex] = SIGNAL(dirtyChanged(bool)); _rgSignals[dirtyChangedIndex] = SIGNAL(dirtyChanged(bool));
_offlineVehicle = new Vehicle(MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, qgcApp()->toolbox()->firmwarePluginManager(), this); _offlineVehicle = new Vehicle(MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, qgcApp()->toolbox()->firmwarePluginManager(), this);
_structureScanItem = new StructureScanComplexItem(_offlineVehicle, false /* flyView */, this); _structureScanItem = new StructureScanComplexItem(_offlineVehicle, false /* flyView */, QString() /* kmlFile */, this /* parent */);
_structureScanItem->setDirty(false); _structureScanItem->setDirty(false);
_multiSpy = new MultiSignalSpy(); _multiSpy = new MultiSignalSpy();
...@@ -121,7 +121,7 @@ void StructureScanComplexItemTest::_testSaveLoad(void) ...@@ -121,7 +121,7 @@ void StructureScanComplexItemTest::_testSaveLoad(void)
_structureScanItem->save(items); _structureScanItem->save(items);
QString errorString; QString errorString;
StructureScanComplexItem* newItem = new StructureScanComplexItem(_offlineVehicle, false /* flyView */, this); StructureScanComplexItem* newItem = new StructureScanComplexItem(_offlineVehicle, false /* flyView */, QString() /* kmlFile */, this /* parent */);
QVERIFY(newItem->load(items[0].toObject(), 10, errorString)); QVERIFY(newItem->load(items[0].toObject(), 10, errorString));
QVERIFY(errorString.isEmpty()); QVERIFY(errorString.isEmpty());
_validateItem(newItem); _validateItem(newItem);
......
...@@ -59,7 +59,7 @@ const char* SurveyComplexItem::_jsonV3FixedValueIsAltitudeKey = "fixedVa ...@@ -59,7 +59,7 @@ const char* SurveyComplexItem::_jsonV3FixedValueIsAltitudeKey = "fixedVa
const char* SurveyComplexItem::_jsonV3Refly90DegreesKey = "refly90Degrees"; const char* SurveyComplexItem::_jsonV3Refly90DegreesKey = "refly90Degrees";
const char* SurveyComplexItem::_jsonFlyAlternateTransectsKey = "flyAlternateTransects"; const char* SurveyComplexItem::_jsonFlyAlternateTransectsKey = "flyAlternateTransects";
SurveyComplexItem::SurveyComplexItem(Vehicle* vehicle, bool flyView, QObject* parent) SurveyComplexItem::SurveyComplexItem(Vehicle* vehicle, bool flyView, const QString& kmlFile, QObject* parent)
: TransectStyleComplexItem (vehicle, flyView, settingsGroup, parent) : TransectStyleComplexItem (vehicle, flyView, settingsGroup, parent)
, _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/Survey.SettingsGroup.json"), this)) , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/Survey.SettingsGroup.json"), this))
, _gridAngleFact (settingsGroup, _metaDataMap[gridAngleName]) , _gridAngleFact (settingsGroup, _metaDataMap[gridAngleName])
...@@ -91,6 +91,12 @@ SurveyComplexItem::SurveyComplexItem(Vehicle* vehicle, bool flyView, QObject* pa ...@@ -91,6 +91,12 @@ SurveyComplexItem::SurveyComplexItem(Vehicle* vehicle, bool flyView, QObject* pa
// FIXME: Shouldn't these be in TransectStyleComplexItem? They are also in CorridorScanComplexItem constructur // FIXME: Shouldn't these be in TransectStyleComplexItem? They are also in CorridorScanComplexItem constructur
connect(&_cameraCalc, &CameraCalc::distanceToSurfaceRelativeChanged, this, &SurveyComplexItem::coordinateHasRelativeAltitudeChanged); connect(&_cameraCalc, &CameraCalc::distanceToSurfaceRelativeChanged, this, &SurveyComplexItem::coordinateHasRelativeAltitudeChanged);
connect(&_cameraCalc, &CameraCalc::distanceToSurfaceRelativeChanged, this, &SurveyComplexItem::exitCoordinateHasRelativeAltitudeChanged); connect(&_cameraCalc, &CameraCalc::distanceToSurfaceRelativeChanged, this, &SurveyComplexItem::exitCoordinateHasRelativeAltitudeChanged);
if (!kmlFile.isEmpty()) {
_surveyAreaPolygon.loadKMLFile(kmlFile);
_surveyAreaPolygon.setDirty(false);
}
setDirty(false);
} }
void SurveyComplexItem::save(QJsonArray& planItems) void SurveyComplexItem::save(QJsonArray& planItems)
......
...@@ -21,7 +21,10 @@ class SurveyComplexItem : public TransectStyleComplexItem ...@@ -21,7 +21,10 @@ class SurveyComplexItem : public TransectStyleComplexItem
Q_OBJECT Q_OBJECT
public: public:
SurveyComplexItem(Vehicle* vehicle, bool flyView, QObject* parent); /// @param vehicle Vehicle which this is being contructed for
/// @param flyView true: Created for use in the Fly View, false: Created for use in the Plan View
/// @param kmlFile Polygon comes from this file, empty for default polygon
SurveyComplexItem(Vehicle* vehicle, bool flyView, const QString& kmlFile, QObject* parent);
Q_PROPERTY(Fact* gridAngle READ gridAngle CONSTANT) Q_PROPERTY(Fact* gridAngle READ gridAngle CONSTANT)
Q_PROPERTY(Fact* flyAlternateTransects READ flyAlternateTransects CONSTANT) Q_PROPERTY(Fact* flyAlternateTransects READ flyAlternateTransects CONSTANT)
......
...@@ -29,7 +29,7 @@ void SurveyComplexItemTest::init(void) ...@@ -29,7 +29,7 @@ void SurveyComplexItemTest::init(void)
_rgSurveySignals[surveyDirtyChangedIndex] = SIGNAL(dirtyChanged(bool)); _rgSurveySignals[surveyDirtyChangedIndex] = SIGNAL(dirtyChanged(bool));
_offlineVehicle = new Vehicle(MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, qgcApp()->toolbox()->firmwarePluginManager(), this); _offlineVehicle = new Vehicle(MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, qgcApp()->toolbox()->firmwarePluginManager(), this);
_surveyItem = new SurveyComplexItem(_offlineVehicle, false /* flyView */, this); _surveyItem = new SurveyComplexItem(_offlineVehicle, false /* flyView */, QString() /* kmlFile */, this /* parent */);
_surveyItem->turnAroundDistance()->setRawValue(0); // Unit test written for no turnaround distance _surveyItem->turnAroundDistance()->setRawValue(0); // Unit test written for no turnaround distance
_surveyItem->setDirty(false); _surveyItem->setDirty(false);
_mapPolygon = _surveyItem->surveyAreaPolygon(); _mapPolygon = _surveyItem->surveyAreaPolygon();
......
...@@ -106,6 +106,8 @@ TransectStyleComplexItem::TransectStyleComplexItem(Vehicle* vehicle, bool flyVie ...@@ -106,6 +106,8 @@ TransectStyleComplexItem::TransectStyleComplexItem(Vehicle* vehicle, bool flyVie
connect(this, &TransectStyleComplexItem::visualTransectPointsChanged, this, &TransectStyleComplexItem::greatestDistanceToChanged); connect(this, &TransectStyleComplexItem::visualTransectPointsChanged, this, &TransectStyleComplexItem::greatestDistanceToChanged);
connect(this, &TransectStyleComplexItem::followTerrainChanged, this, &TransectStyleComplexItem::_followTerrainChanged); connect(this, &TransectStyleComplexItem::followTerrainChanged, this, &TransectStyleComplexItem::_followTerrainChanged);
setDirty(false);
} }
void TransectStyleComplexItem::_setCameraShots(int cameraShots) void TransectStyleComplexItem::_setCameraShots(int cameraShots)
......
...@@ -24,6 +24,7 @@ import QGroundControl.FactSystem 1.0 ...@@ -24,6 +24,7 @@ import QGroundControl.FactSystem 1.0
import QGroundControl.FactControls 1.0 import QGroundControl.FactControls 1.0
import QGroundControl.Palette 1.0 import QGroundControl.Palette 1.0
import QGroundControl.Controllers 1.0 import QGroundControl.Controllers 1.0
import QGroundControl.KMLFileHelper 1.0
/// Mission Editor /// Mission Editor
...@@ -77,6 +78,11 @@ QGCView { ...@@ -77,6 +78,11 @@ QGCView {
_missionController.setCurrentPlanViewIndex(sequenceNumber, true) _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
} }
function insertComplexMissionItemFromKML(complexItemName, kmlFile, index) {
var sequenceNumber = _missionController.insertComplexMissionItemFromKML(complexItemName, kmlFile, index)
_missionController.setCurrentPlanViewIndex(sequenceNumber, true)
}
property bool _firstMissionLoadComplete: false property bool _firstMissionLoadComplete: false
property bool _firstFenceLoadComplete: false property bool _firstFenceLoadComplete: false
property bool _firstRallyLoadComplete: false property bool _firstRallyLoadComplete: false
...@@ -192,7 +198,7 @@ QGCView { ...@@ -192,7 +198,7 @@ QGCView {
return return
} }
fileDialog.title = qsTr("Save Plan") fileDialog.title = qsTr("Save Plan")
fileDialog.plan = true fileDialog.planFiles = true
fileDialog.selectExisting = false fileDialog.selectExisting = false
fileDialog.nameFilters = masterController.saveNameFilters fileDialog.nameFilters = masterController.saveNameFilters
fileDialog.fileExtension = QGroundControl.settingsManager.appSettings.planFileExtension fileDialog.fileExtension = QGroundControl.settingsManager.appSettings.planFileExtension
...@@ -204,15 +210,25 @@ QGCView { ...@@ -204,15 +210,25 @@ QGCView {
mapFitFunctions.fitMapViewportToMissionItems() mapFitFunctions.fitMapViewportToMissionItems()
} }
function loadKmlFromSelectedFile() {
fileDialog.title = qsTr("Load KML")
fileDialog.planFiles = false
fileDialog.selectExisting = true
fileDialog.nameFilters = masterController.fileKmlFilters
fileDialog.fileExtension = QGroundControl.settingsManager.appSettings.kmlFileExtension
fileDialog.fileExtension2 = ""
fileDialog.openForLoad()
}
function saveKmlToSelectedFile() { function saveKmlToSelectedFile() {
if (!readyForSaveSend()) { if (!readyForSaveSend()) {
waitingOnDataMessage() waitingOnDataMessage()
return return
} }
fileDialog.title = qsTr("Save KML") fileDialog.title = qsTr("Save KML")
fileDialog.plan = false fileDialog.planFiles = false
fileDialog.selectExisting = false fileDialog.selectExisting = false
fileDialog.nameFilters = masterController.saveKmlFilters fileDialog.nameFilters = masterController.fileKmlFilters
fileDialog.fileExtension = QGroundControl.settingsManager.appSettings.kmlFileExtension fileDialog.fileExtension = QGroundControl.settingsManager.appSettings.kmlFileExtension
fileDialog.fileExtension2 = "" fileDialog.fileExtension2 = ""
fileDialog.openForSave() fileDialog.openForSave()
...@@ -259,22 +275,96 @@ QGCView { ...@@ -259,22 +275,96 @@ QGCView {
QGCFileDialog { QGCFileDialog {
id: fileDialog id: fileDialog
qgcView: _qgcView qgcView: _qgcView
property bool plan: true
folder: QGroundControl.settingsManager.appSettings.missionSavePath folder: QGroundControl.settingsManager.appSettings.missionSavePath
property bool planFiles: true ///< true: working with plan files, false: working with kml file
onAcceptedForSave: { onAcceptedForSave: {
plan ? masterController.saveToFile(file) : masterController.saveToKml(file) if (planFiles) {
masterController.saveToFile(file)
} else {
masterController.saveToKml(file)
}
close() close()
} }
onAcceptedForLoad: { onAcceptedForLoad: {
masterController.loadFromFile(file) if (planFiles) {
masterController.fitViewportToItems() masterController.loadFromFile(file)
_missionController.setCurrentPlanViewIndex(0, true) masterController.fitViewportToItems()
_missionController.setCurrentPlanViewIndex(0, true)
} else {
var retList = KMLFileHelper.determineFileContents(file)
if (retList[0] == KMLFileHelper.Error) {
_qgcView.showMessage("Error", retList[1], StandardButton.Ok)
} else if (retList[0] == KMLFileHelper.Polygon) {
kmlPolygonSelectDialogKMLFile = file
_qgcView.showDialog(kmlPolygonSelectDialog, fileDialog.title, _qgcView.showDialogDefaultWidth, StandardButton.Ok | StandardButton.Cancel)
} else if (retList[0] == KMLFileHelper.Polyline) {
insertComplexMissionItemFromKML(_missionController.corridorScanComplexItemName, file, -1)
}
}
close() close()
} }
} }
property string kmlPolygonSelectDialogKMLFile
Component {
id: kmlPolygonSelectDialog
QGCViewDialog {
property var editVehicle: _activeVehicle ? _activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle
function accept() {
var complexItemName
if (surveyRadio.checked) {
complexItemName = _missionController.surveyComplexItemName
} else {
complexItemName = _missionController.structureScanComplexItemName
}
insertComplexMissionItemFromKML(complexItemName, kmlPolygonSelectDialogKMLFile, -1)
hideDialog()
}
Component.onCompleted: {
if (editVehicle.fixedWing) {
// Only Survey available
accept()
}
}
ExclusiveGroup {
id: radioGroup
}
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: ScreenTools.defaultFontPixelHeight
QGCLabel {
anchors.left: parent.left
anchors.right: parent.right
wrapMode: Text.WordWrap
text: qsTr("What would you like to create from the polygon specified by the KML file?")
}
QGCRadioButton {
id: surveyRadio
text: qsTr("Survey")
checked: true
exclusiveGroup: radioGroup
}
QGCRadioButton {
text: qsTr("Structure Scan")
exclusiveGroup: radioGroup
visible: !editVehicle.fixedWing
}
}
}
}
Component { Component {
id: moveDialog id: moveDialog
...@@ -808,7 +898,7 @@ QGCView { ...@@ -808,7 +898,7 @@ QGCView {
} }
QGCButton { QGCButton {
text: qsTr("Save To File...") text: qsTr("Save Plan...")
Layout.fillWidth: true Layout.fillWidth: true
enabled: !masterController.syncInProgress enabled: !masterController.syncInProgress
onClicked: { onClicked: {
...@@ -818,7 +908,7 @@ QGCView { ...@@ -818,7 +908,7 @@ QGCView {
} }
QGCButton { QGCButton {
text: qsTr("Load From File...") text: qsTr("Load Plan...")
Layout.fillWidth: true Layout.fillWidth: true
enabled: !masterController.syncInProgress enabled: !masterController.syncInProgress
onClicked: { onClicked: {
...@@ -832,14 +922,16 @@ QGCView { ...@@ -832,14 +922,16 @@ QGCView {
} }
QGCButton { QGCButton {
text: qsTr("Remove All") text: qsTr("Load KML...")
Layout.fillWidth: true Layout.fillWidth: true
onClicked: { enabled: !masterController.syncInProgress
onClicked: {
dropPanel.hide() dropPanel.hide()
_qgcView.showDialog(removeAllPromptDialog, qsTr("Remove all"), _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No) masterController.loadKmlFromSelectedFile()
} }
} }
QGCButton { QGCButton {
text: qsTr("Save KML...") text: qsTr("Save KML...")
Layout.fillWidth: true Layout.fillWidth: true
...@@ -854,6 +946,15 @@ QGCView { ...@@ -854,6 +946,15 @@ QGCView {
masterController.saveKmlToSelectedFile() masterController.saveKmlToSelectedFile()
} }
} }
QGCButton {
text: qsTr("Remove All")
Layout.fillWidth: true
onClicked: {
dropPanel.hide()
_qgcView.showDialog(removeAllPromptDialog, qsTr("Remove all"), _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
}
}
} }
} }
} }
......
...@@ -84,6 +84,7 @@ ...@@ -84,6 +84,7 @@
#include "VisualMissionItem.h" #include "VisualMissionItem.h"
#include "EditPositionDialogController.h" #include "EditPositionDialogController.h"
#include "FactValueSliderListModel.h" #include "FactValueSliderListModel.h"
#include "KMLFileHelper.h"
#ifndef NO_SERIAL_LINK #ifndef NO_SERIAL_LINK
#include "SerialLink.h" #include "SerialLink.h"
...@@ -139,6 +140,11 @@ static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*) ...@@ -139,6 +140,11 @@ static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
return qmlGlobal; return qmlGlobal;
} }
static QObject* kmlFileHelperSingletonFactory(QQmlEngine*, QJSEngine*)
{
return new KMLFileHelper;
}
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
#ifdef __mobile__ #ifdef __mobile__
: QGuiApplication (argc, argv) : QGuiApplication (argc, argv)
...@@ -394,6 +400,7 @@ void QGCApplication::_initCommon(void) ...@@ -394,6 +400,7 @@ void QGCApplication::_initCommon(void)
// Register Qml Singletons // Register Qml Singletons
qmlRegisterSingletonType<QGroundControlQmlGlobal> ("QGroundControl", 1, 0, "QGroundControl", qgroundcontrolQmlGlobalSingletonFactory); qmlRegisterSingletonType<QGroundControlQmlGlobal> ("QGroundControl", 1, 0, "QGroundControl", qgroundcontrolQmlGlobalSingletonFactory);
qmlRegisterSingletonType<ScreenToolsController> ("QGroundControl.ScreenToolsController", 1, 0, "ScreenToolsController", screenToolsControllerSingletonFactory); qmlRegisterSingletonType<ScreenToolsController> ("QGroundControl.ScreenToolsController", 1, 0, "ScreenToolsController", screenToolsControllerSingletonFactory);
qmlRegisterSingletonType<KMLFileHelper> ("QGroundControl.KMLFileHelper", 1, 0, "KMLFileHelper", kmlFileHelperSingletonFactory);
} }
bool QGCApplication::_initForNormalAppBoot(void) bool QGCApplication::_initForNormalAppBoot(void)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment