Commit 1b05f323 authored by Valentin Platzgummer's avatar Valentin Platzgummer

MeasurementItemEditor improved

parent 31d4efc3
......@@ -34,14 +34,13 @@ DebugBuild {
DEFINES += DEBUG
#DEFINES += SNAKE_SHOW_TIME
#DEFINES += SNAKE_DEBUG
DEFINES += SNAKE_SHOW_TIME
DEFINES += DEBUG_CIRCULAR_SURVEY
#DEFINES += SNAKE_SHOW_TIME
DEFINES += ROS_BRIDGE_DEBUG
}
else {
DESTDIR = $${OUT_PWD}/release
#DEFINES += ROS_BRIDGE_DEBUG
DEFINES += SNAKE_SHOW_TIME
#DEFINES += SNAKE_SHOW_TIME
#DEFINES += SNAKE_DEBUG
DEFINES += NDEBUG
}
......
......@@ -18,8 +18,7 @@ AreaData::AreaData(QObject *parent) : QObject(parent) {}
AreaData::~AreaData() {}
AreaData::AreaData(const AreaData &other, QObject *parent)
: QObject(parent), _showErrorMessages(true) {
AreaData::AreaData(const AreaData &other, QObject *parent) : QObject(parent) {
*this = other;
}
......@@ -93,21 +92,21 @@ const QmlObjectListModel *AreaData::areaList() const { return &_areaList; }
QGeoCoordinate AreaData::origin() const { return _origin; }
bool AreaData::isCorrect() {
bool AreaData::isCorrect(bool showError) {
if (!initialized()) {
qCWarning(AreaDataLog) << "isCorrect(): not initialized";
return false;
}
// Check if areas are correct
if (!_areasCorrect()) {
if (!_areasCorrect(showError)) {
return false;
}
// Check if areas where added.
MeasurementArea *measurementArea = nullptr;
SafeArea *safeArea = nullptr;
if (!_getAreas(&measurementArea, &safeArea)) {
if (!_getAreas(&measurementArea, &safeArea, showError)) {
return false;
}
......@@ -128,7 +127,8 @@ bool AreaData::isCorrect() {
// qDebug() << ss.str().c_str();
if (!bg::covered_by(measurementAreaENU, safeAreaENU)) {
_processError(tr("Measurement Area not inside Safe Area. Please adjust "
"the Measurement Area.\n"));
"the Measurement Area.\n"),
showError);
return false;
}
......@@ -209,11 +209,11 @@ bool AreaData::initialized() {
measurementArea->count() >= 3 && safeArea->count() >= 3;
}
void AreaData::intersection() {
if (initialized() && _areasCorrect()) {
void AreaData::intersection(bool showError) {
if (initialized() && _areasCorrect(showError)) {
MeasurementArea *measurementArea = nullptr;
SafeArea *safeArea = nullptr;
if (_getAreas(&measurementArea, &safeArea)) {
if (_getAreas(&measurementArea, &safeArea, showError)) {
// convert to ENU
const auto origin = this->origin();
......@@ -230,7 +230,8 @@ void AreaData::intersection() {
if (outputENU.size() < 1 || outputENU[0].outer().size() < 4) {
_processError(
"Intersection did't deliver any result. Measurement Area and "
"Safe Area must touch each other.");
"Safe Area must touch each other.",
showError);
return;
}
......@@ -238,7 +239,8 @@ void AreaData::intersection() {
_processError(
"Hint: Only simple polygons can be displayed. If Intersection"
"produces polygons with holes or multi polygons, only "
"partial information can be displayed.");
"partial information can be displayed.",
showError);
}
// Shrink the result if safeAreaENU doesn't cover it.
......@@ -247,9 +249,10 @@ void AreaData::intersection() {
while (!bg::covered_by(large, safeAreaENU)) {
snake::offsetPolygon(large, small, -0.1);
large = std::move(small);
qDebug() << "intersection(): shrink";
}
// Check if result is different from input.
if (!bg::equals(large, measurementAreaENU)) {
// Convert.
measurementArea->clear();
for (auto it = large.outer().begin(); it != large.outer().end() - 1;
......@@ -260,6 +263,7 @@ void AreaData::intersection() {
}
}
}
}
}
MeasurementArea *AreaData::measurementArea() {
......@@ -302,7 +306,7 @@ bool AreaData::load(const QJsonObject &obj, QString &errorString) {
// load MeasurementArea
if (jsonArea[GeoArea::areaTypeKey].toString() ==
MeasurementArea::name) {
MeasurementArea::nameString) {
auto area = getGeoArea<MeasurementArea *>(_areaList);
......@@ -325,7 +329,8 @@ bool AreaData::load(const QJsonObject &obj, QString &errorString) {
}
}
// load SafeArea
else if (jsonArea[GeoArea::areaTypeKey].toString() == SafeArea::name) {
else if (jsonArea[GeoArea::areaTypeKey].toString() ==
SafeArea::nameString) {
auto area = getGeoArea<SafeArea *>(_areaList);
if (area == nullptr) {
......@@ -394,7 +399,6 @@ bool AreaData::save(QJsonObject &obj) {
} else {
qDebug(AreaDataLog) << "save(): not able to save area: "
<< area->objectName();
_processError(tr("Not able to save area: ") + area->objectName());
return false;
}
}
......@@ -411,21 +415,21 @@ void AreaData::_setOrigin(const QGeoCoordinate &origin) {
}
}
void AreaData::_processError(const QString &str) {
void AreaData::_processError(const QString &str, bool showError) {
this->_errorString = str;
emit error();
if (_showErrorMessages) {
if (showError) {
qgcApp()->informationMessageBoxOnMainThread(tr("Area Editor"),
this->errorString());
}
}
bool AreaData::_areasCorrect() {
bool AreaData::_areasCorrect(bool showError) {
// Check if areas are correct.
for (int i = 0; i < _areaList.count(); ++i) {
auto *area = _areaList.value<GeoArea *>(0);
auto *area = _areaList.value<GeoArea *>(i);
if (!area->isCorrect()) {
_processError(area->errorString());
_processError(area->errorString(), showError);
return false;
}
}
......@@ -433,30 +437,25 @@ bool AreaData::_areasCorrect() {
return true;
}
bool AreaData::_getAreas(MeasurementArea **measurementArea,
SafeArea **safeArea) {
bool AreaData::_getAreas(MeasurementArea **measurementArea, SafeArea **safeArea,
bool showError) {
*measurementArea = getGeoArea<MeasurementArea *>(_areaList);
if (*measurementArea == nullptr) {
_processError(
tr("Measurement Area missing. Please define a measurement area."));
tr("Measurement Area missing. Please define a measurement area."),
showError);
return false;
}
*safeArea = getGeoArea<SafeArea *>(_areaList);
if (*safeArea == nullptr) {
_processError(tr("Safe Area missing. Please define a safe area."));
_processError(tr("Safe Area missing. Please define a safe area."),
showError);
return false;
}
return true;
}
void AreaData::setShowErrorMessages(bool showErrorMessages) {
if (showErrorMessages != _showErrorMessages) {
_showErrorMessages = showErrorMessages;
emit showErrorMessagesChanged();
}
}
void AreaData::_updateOrigin() {
auto *measurementArea = getGeoArea<MeasurementArea *>(_areaList);
if (measurementArea != nullptr) {
......@@ -464,6 +463,4 @@ void AreaData::_updateOrigin() {
}
}
bool AreaData::showErrorMessages() const { return _showErrorMessages; }
QString AreaData::errorString() const { return _errorString; }
......@@ -20,8 +20,7 @@ public:
AreaData &operator=(const AreaData &other);
Q_PROPERTY(QmlObjectListModel *areaList READ areaList NOTIFY areaListChanged)
Q_PROPERTY(bool showErrorMessages READ showErrorMessages WRITE
setShowErrorMessages NOTIFY showErrorMessagesChanged)
Q_PROPERTY(QString errorString READ errorString NOTIFY error)
// Member Methodes
//!
......@@ -51,7 +50,7 @@ public:
//! \note Origin might change if the list of areas changes.
QGeoCoordinate origin() const;
Q_INVOKABLE bool isCorrect();
Q_INVOKABLE bool isCorrect(bool showError = true);
//!
//! \brief initialize Initializes the areas in a valid way, such that they
//! area inside the bounding box. \param bottomLeft bottom left corner of the
......@@ -69,7 +68,7 @@ public:
//! either.
//!
Q_INVOKABLE bool initialized();
Q_INVOKABLE void intersection();
Q_INVOKABLE void intersection(bool showError = true);
Q_INVOKABLE MeasurementArea *measurementArea();
Q_INVOKABLE SafeArea *safeArea();
......@@ -82,25 +81,21 @@ public:
QString errorString() const; // Contains a message about the last error.
bool showErrorMessages() const;
void setShowErrorMessages(bool showErrorMessages);
signals:
void areaListChanged();
void originChanged();
void error(); // Emitted if errorString() contains a new message.
void showErrorMessagesChanged();
private slots:
void _updateOrigin();
private:
void _setOrigin(const QGeoCoordinate &origin);
void _processError(const QString &str);
bool _areasCorrect();
bool _getAreas(MeasurementArea **measurementArea, SafeArea **safeArea);
void _processError(const QString &str, bool showError);
bool _areasCorrect(bool showError);
bool _getAreas(MeasurementArea **measurementArea, SafeArea **safeArea,
bool showError);
QGeoCoordinate _origin;
QmlObjectListModel _areaList;
QString _errorString;
bool _showErrorMessages;
};
......@@ -406,6 +406,7 @@ void CircularGenerator::setMeasurementArea(MeasurementArea *area) {
&CircularGenerator::resetReferenceIfInvalid);
connect(_measurementArea, &MeasurementArea::pathChanged, this,
&GeneratorBase::generatorChanged);
resetReferenceIfInvalid();
}
emit generatorChanged();
......@@ -438,16 +439,13 @@ bool circularTransects(const snake::FPoint &reference,
distances.reserve(polygon.outer().size());
std::vector<snake::Angle> angles;
angles.reserve(polygon.outer().size());
//#ifdef DEBUG_CIRCULAR_SURVEY
// qCDebug(CircularGeneratorLog) << "circularTransects():";
//#endif
for (const auto &p : polygon.outer()) {
snake::Length distance = bg::distance(reference, p) * si::meter;
distances.push_back(distance);
snake::Angle alpha = (std::atan2(p.get<1>(), p.get<0>())) * si::radian;
alpha = alpha < 0 * si::radian ? alpha + 2 * M_PI * si::radian : alpha;
angles.push_back(alpha);
//#ifdef DEBUG_CIRCULAR_SURVEY
// qCDebug(CircularGeneratorLog) << "distances, angles,
// coordinates:"; qCDebug(CircularGeneratorLog) <<
// to_string(distance).c_str(); qCDebug(CircularGeneratorLog)
......@@ -455,7 +453,6 @@ bool circularTransects(const snake::FPoint &reference,
// qCDebug(CircularGeneratorLog) << "x = " << p.get<0>() << "y
// = "
// << p.get<1>();
//#endif
}
auto rMin = deltaR; // minimal circle radius
......@@ -484,7 +481,6 @@ bool circularTransects(const snake::FPoint &reference,
vector<ClipperLib::Path> sectors(nTran, ClipperLib::Path());
const auto nSectors =
long(std::round(((alpha2 - alpha1) / deltaAlpha).value()));
//#ifdef DEBUG_CIRCULAR_SURVEY
// qCDebug(CircularGeneratorLog) << "circularTransects(): sector
// parameres:"; qCDebug(CircularGeneratorLog) << "alpha1: " <<
// to_string(snake::Degree(alpha1)).c_str();
......@@ -499,7 +495,6 @@ bool circularTransects(const snake::FPoint &reference,
// qCDebug(CircularGeneratorLog)
// << "rMax: " << to_string(rMax).c_str();
// qCDebug(CircularGeneratorLog) << "nTran: " << nTran;
//#endif
using ClipperCircle =
GenericCircle<ClipperLib::cInt, ClipperLib::IntPoint>;
for (auto &sector : sectors) {
......
......@@ -67,6 +67,7 @@ bool GeneratorFactory::registerGenerator(const QString &type,
GeneratorFactory::Creator creator) {
const auto pair = _creatorMap.insert(std::make_pair(type, creator));
auto success = pair.second;
Q_ASSERT(success);
return success;
}
......
......@@ -34,12 +34,12 @@ public:
const QString &kmlOrShpFile, QObject *parent);
~MeasurementComplexItem();
Q_PROPERTY(Fact *variant READ variant CONSTANT)
Q_PROPERTY(Fact *altitude READ variant CONSTANT)
Q_PROPERTY(Fact *variantIndex READ variantIndex CONSTANT)
Q_PROPERTY(Fact *altitude READ altitude CONSTANT)
Q_PROPERTY(
QStringList variantNames READ variantNames NOTIFY variantNamesChanged)
Q_PROPERTY(QStringList generatorNameList READ generatorNameList NOTIFY
generatorNameListChanged)
generatorListChanged)
Q_PROPERTY(bool calculating READ calculating NOTIFY calculatingChanged)
Q_PROPERTY(bool editing READ editing NOTIFY editingChanged)
Q_PROPERTY(bool idle READ idle NOTIFY idleChanged)
......@@ -95,18 +95,56 @@ public:
virtual QString abbreviation(void) const override final;
// Generator
bool addGenerator(const QString &name, routing::GeneratorBase *g);
//!
//! \brief addGenerator Adds a generator.
//!
//! Adds a generator. The generator will be added to the generatorList() and
//! it's name to the generatorNameList(). Generators are identified by index
//! or name(), hence their name must be unique. A generator can not be added
//! if name() is already inside generatorNameList(). \param g Pointer to a
//! generator. \return Returns true if the generator was addes successfully,
//! false either.
//!
bool addGenerator(routing::GeneratorBase *g);
//!
//! \brief removeGenerator Removes the generator.
//! \param name The name of the generator to be removed.
//! \return Returns true if the generator was removed successfully.
//! \note The generator will be deleted if this or nullptr is it's parent.
bool removeGenerator(const QString &name);
//!
//! \brief removeGenerator Removes the generator.
//! \param index The index of the generator to be removed
//! \return Returns true if the generator was removed successfully.
//! \note See above.
bool removeGenerator(int index);
Q_INVOKABLE bool switchToGenerator(const QString &name);
Q_INVOKABLE bool switchToGenerator(int index);
//!
//! \brief resetGenerators Resets the generators as they where after creation
//! of this object.
//!
Q_INVOKABLE void resetGenerators();
QList<PtrGenerator> generatorList() const;
QStringList generatorNameList() const;
routing::GeneratorBase *generator();
const routing::GeneratorBase *generator() const;
routing::GeneratorBase *generator(int index);
const routing::GeneratorBase *generator(int index) const;
//!
//! \brief generatorIndex
//! \return Returns the index of the current generator.
int generatorIndex() const;
//!
//! \brief generatorIndex
//! \param name
//! \return Returns the index of the generator with the name \p name, or -1 if
//! the generator is unknown.
//!
int generatorIndex(const QString &name);
// Editing.
//!
......@@ -142,9 +180,9 @@ public:
AreaData *areaData();
QVariantList route();
Fact *variant();
Fact *altitude();
Fact *variantIndex();
QStringList variantNames() const;
Fact *altitude();
bool calculating() const;
bool editing() const; // set to true on creation
......@@ -158,7 +196,7 @@ public:
signals:
void variantNamesChanged();
void generatorNameListChanged();
void generatorListChanged();
void generatorChanged();
void calculatingChanged();
......@@ -173,7 +211,7 @@ private slots:
// Worker functions.
void _storeRoutingData(PtrRoutingData pRoute);
void _updateRoute();
void _changeVariant();
void _changeVariantIndex();
void _reverseRoute();
private:
......@@ -184,6 +222,7 @@ private:
static bool _editing(STATE state);
static bool _idle(STATE state);
void _updateFlightpathSegments();
void _onAltitudeChanged();
// Hirarcical stuff.
int _sequenceNumber;
......@@ -195,7 +234,7 @@ private:
// Facts
QMap<QString, FactMetaData *> _metaDataMap;
SettingsFact _altitude;
SettingsFact _variant;
SettingsFact _variantIndex;
QStringList _variantNames;
// Area data
......@@ -205,7 +244,6 @@ private:
// Generators
QList<PtrGenerator> _generatorList;
QStringList _generatorNameList;
PtrGenerator _pGenerator;
// Routing.
......
......@@ -9,7 +9,7 @@
QGC_LOGGING_CATEGORY(GeoAreaLog, "GeoAreaLog")
const char *GeoArea::name = "GeoArea";
const char *GeoArea::nameString = "GeoArea";
const char *GeoArea::areaTypeKey = "AreaType";
const char *GeoArea::settingsGroup = "GeoArea";
......@@ -59,7 +59,7 @@ bool GeoArea::isCorrect() {
std::stringstream ss;
ss << bg::wkt(polygonENU);
qCWarning(GeoAreaLog) << "polygonENU: " << ss.str().c_str();
setErrorString(tr("Area invalid. Area must be a simple polygon."));
setErrorString(this->objectName() + tr(" must be a simple polygon."));
}
}
return false;
......@@ -81,7 +81,7 @@ bool GeoArea::covers(const QGeoCoordinate &c) {
}
void GeoArea::init() {
this->setObjectName(name);
this->setObjectName(nameString);
// connect(this, &GeoArea::pathChanged, [this] {
// if (this->objectName() != "Tile") {
// qDebug() << this->objectName() << " path: " << this->path() << "\n";
......
......@@ -30,12 +30,13 @@ public:
//!
//! \brief covers Checks if GeoArea covers c.
//! \param c
//! \return Returns true if c is inside, or on the border of GeoArea.
//! \return Returns true if c is inside, or on the border of GeoArea, false
//! either.
//!
Q_INVOKABLE bool covers(const QGeoCoordinate &c);
// static Members
static const char *name;
static const char *nameString;
static const char *areaTypeKey;
static const char *settingsGroup;
......
......@@ -5,14 +5,22 @@
#include <boost/units/systems/si.hpp>
#include "JsonHelper.h"
#include "QGCLoggingCategory.h"
#include <QJsonArray>
#ifndef SNAKE_MAX_TILES
#define SNAKE_MAX_TILES 1000
#endif
QGC_LOGGING_CATEGORY(MeasurementAreaLog, "MeasurementAreaLog")
namespace {
const char *tileCenterPointsKey = "TileCenterPoints";
const char *tileArrayKey = "TileArray";
} // namespace
TileData::TileData() : tiles(this) {}
TileData::~TileData() { tiles.clearAndDeleteContents(); }
......@@ -50,6 +58,82 @@ bool TileData::operator!=(const TileData &other) const {
return !this->operator==(other);
}
void TileData::saveToJson(QJsonObject &json) {
// save center points
QJsonValue jsonCenterPoints;
JsonHelper::saveGeoCoordinateArray(tileCenterPoints, false, jsonCenterPoints);
json[tileCenterPointsKey] = std::move(jsonCenterPoints);
// save tiles
QJsonArray jsonTiles;
for (int i = 0; i < tiles.count(); ++i) {
auto tile = tiles.value<SnakeTile *>(i);
if (tile != nullptr) {
QJsonObject jsonTile;
tile->saveToJson(jsonTile);
jsonTiles.append(jsonTile);
} else {
qCritical() << "TileData::saveToJson(): Object other than SnakeTile "
"inside tiles (QmlObjectListModel))";
Q_ASSERT(tile != nullptr);
}
}
json[tileArrayKey] = std::move(jsonTiles);
}
bool TileData::loadFromJson(const QJsonObject &json, QString &errorString) {
clear();
// load tiles
if (json.contains(tileArrayKey) && json[tileArrayKey].isArray()) {
QString e;
for (const auto &jsonTile : json[tileArrayKey].toArray()) {
auto tile = new SnakeTile(this);
if (tile->loadFromJson(jsonTile.toObject(), e)) {
tiles.append(tile);
} else {
tile->deleteLater();
errorString.append(e);
return false;
}
}
} else {
errorString.append(tr("Not able to load tiles.\n"));
qCWarning(MeasurementAreaLog)
<< "Not able to load tiles. tileArrayKey missing or wrong type.";
if (json.contains(tileArrayKey)) {
qCWarning(MeasurementAreaLog)
<< "tile array type: " << json[tileArrayKey].type();
}
return false;
}
// load center points
if (json.contains(tileCenterPointsKey) &&
json[tileCenterPointsKey].isArray()) {
QString e;
if (!JsonHelper::loadGeoCoordinateArray(json[tileCenterPointsKey], false,
tileCenterPoints, e)) {
errorString.append(e);
errorString.append("\n");
return false;
}
} else {
errorString.append(tr("Not able to load center points.\n"));
qCWarning(MeasurementAreaLog)
<< "Not able to load center points. tileCenterPointsKey missing or "
"wrong type.";
if (json.contains(tileCenterPointsKey)) {
qCWarning(MeasurementAreaLog)
<< "center points type: " << json[tileCenterPointsKey].type();
}
return false;
}
return true;
}
void TileData::clear() {
this->tiles.clearAndDeleteContents();
this->tileCenterPoints.clear();
......@@ -68,7 +152,9 @@ const char *tileHeightKey = "TileHeight";
const char *tileWidthName = "TileWidth";
const char *minTileAreaKey = "MinTileAreaPercent";
const char *showTilesKey = "ShowTiles";
const char *MeasurementArea::name = "Measurement Area";
const char *progressKey = "Progress";
const char *tileKey = "Tiles";
const char *MeasurementArea::nameString = "Measurement Area";
MeasurementArea::MeasurementArea(QObject *parent)
: GeoArea(parent),
......@@ -193,7 +279,20 @@ bool MeasurementArea::saveToJson(QJsonObject &json) {
json[tileWidthName] = _tileWidth.rawValue().toDouble();
json[minTileAreaKey] = _minTileAreaPercent.rawValue().toDouble();
json[showTilesKey] = _showTiles.rawValue().toBool();
json[areaTypeKey] = name;
json[areaTypeKey] = nameString;
// save progess
QJsonArray jsonProgess;
for (const auto &p : _progress) {
jsonProgess.append(p);
}
json[progressKey] = std::move(jsonProgess);
// save tiles
QJsonObject jsonTiles;
_tileData.saveToJson(jsonTiles);
json[tileKey] = std::move(jsonTiles);
return true;
} else {
qCDebug(MeasurementAreaLog)
......@@ -211,6 +310,7 @@ bool MeasurementArea::loadFromJson(const QJsonObject &json,
disableUpdate();
bool retVal = true;
// load parameters necessary for tile calculation.
if (!json.contains(tileHeightKey) || !json[tileHeightKey].isDouble()) {
errorString.append(tr("Could not load tile height!\n"));
retVal = false;
......@@ -232,15 +332,59 @@ bool MeasurementArea::loadFromJson(const QJsonObject &json,
_minTileAreaPercent.setRawValue(json[minTileAreaKey].toDouble());
}
if (!json.contains(showTilesKey) || !json[showTilesKey].isBool()) {
errorString.append(tr("Could not load show tiles !\n"));
retVal = false;
} else {
// load less important parameters
if (json.contains(showTilesKey) || !json[showTilesKey].isBool()) {
_showTiles.setRawValue(json[showTilesKey].toBool());
}
// load tiles and progress
bool tileError = false;
if (json.contains(tileKey) && json[tileKey].isObject()) {
QString e;
if (!_tileData.loadFromJson(json[tileKey].toObject(), e)) {
qCWarning(MeasurementAreaLog) << "TileData::loadFromJson(): " << e;
tileError = true;
} else {
progressChanged();
}
} else {
tileError = true;
}
bool progressError = false;
QVector<int> prog;
if (json.contains(progressKey) && json[progressKey].isArray()) {
for (const auto &p : json[progressKey].toArray()) {
if (p.isDouble()) {
prog.append(p.toDouble());
} else {
progressError = true;
break;
}
}
// check if entries are in range
if (!progressError) {
for (const auto &p : prog) {
if (p < 0 || p > 100) {
progressError = true;
break;
}
}
}
} else {
progressError = true;
}
if (!progressError) {
_progress.swap(prog);
progressChanged();
}
// do update if error occurred.
enableUpdate();
if (progressError || tileError) {
doUpdate();
}
return retVal;
} else {
......@@ -408,7 +552,7 @@ void MeasurementArea::enableUpdate() {
}
void MeasurementArea::init() {
this->setObjectName(name);
this->setObjectName(nameString);
connect(&this->_tileHeight, &Fact::rawValueChanged, this,
&MeasurementArea::deferUpdate);
connect(&this->_tileWidth, &Fact::rawValueChanged, this,
......
......@@ -21,6 +21,9 @@ public:
bool operator==(const TileData &other) const;
bool operator!=(const TileData &other) const;
void saveToJson(QJsonObject &json);
bool loadFromJson(const QJsonObject &json, QString &errorString);
void clear();
std::size_t size() const;
};
......@@ -68,7 +71,7 @@ public:
// Static Variables
static const char *settingsGroup;
static const char *name;
static const char *nameString;
signals:
void tilesChanged();
......
......@@ -8,7 +8,7 @@
QGC_LOGGING_CATEGORY(SafeAreaLog, "SafeAreaLog")
const char *SafeArea::name = "Safe Area";
const char *SafeArea::nameString = "Safe Area";
const char *depotKey = "Depot Point";
SafeArea::SafeArea(QObject *parent) : GeoArea(parent) { init(); }
......@@ -84,7 +84,7 @@ bool SafeArea::setDepot(const QGeoCoordinate &newDepot) {
bool SafeArea::saveToJson(QJsonObject &json) {
if (this->GeoArea::saveToJson(json)) {
json[areaTypeKey] = name;
json[areaTypeKey] = nameString;
QJsonValue jsonDepot;
JsonHelper::saveGeoCoordinate(_depot, false, jsonDepot);
......@@ -135,6 +135,6 @@ bool SafeArea::isCorrect() {
}
void SafeArea::init() {
this->setObjectName(name);
this->setObjectName(nameString);
connect(this, &GeoArea::pathChanged, this, &SafeArea::putDepotInside);
}
......@@ -27,7 +27,7 @@ public:
QGeoCoordinate depotQml(void) const;
// static Members
static const char *name;
static const char *nameString;
signals:
void depotChanged(void);
......
......@@ -4,14 +4,14 @@
"QGC.MetaData.Facts":
[
{
"name": "Variant",
"shrotDesc": "Route variant.",
"name": "VariantIndex",
"shrotDesc": "Route variant index.",
"type": "uint64",
"default": 0
},
{
"name": "Altitude",
"shrotDesc": "Altitude",
"shrotDesc": "Altitude.",
"type": "double",
"units": "m",
"min": 1,
......
......@@ -7,11 +7,15 @@ import QGroundControl.FactControls 1.0
import QGroundControl.ScreenTools 1.0
GridLayout {
id:_root
id: _root
property bool checked: true
property var missionItem: undefined
property int availableWidth: 300
property bool areasCorrect: false
property string errorString: ""
signal abort
property var _areaData: missionItem.areaData
property real _margin: ScreenTools.defaultFontPixelWidth / 2
......@@ -21,12 +25,35 @@ GridLayout {
columns: 2
Component.onCompleted: {
console.assert(missionItem !== undefined, "please set the missionItem property")
console.assert(missionItem !== undefined,
"please set the missionItem property")
if (checked) {
areasCorrectTimer.start()
}
}
onCheckedChanged: {
if (checked) {
areasCorrectTimer.start()
} else {
areasCorrectTimer.stop()
}
}
ExclusiveGroup {
id: areaGroup
}
ExclusiveGroup{id:areaGroup}
QGCLabel {
id: tipLabel
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
text: qsTr("Use the Area Editor to modify areas.")
Layout.fillWidth: true
Layout.columnSpan: parent.columns
}
Repeater{
Repeater {
id: areaSelector
property int selectedIndex: -1
......@@ -39,36 +66,37 @@ GridLayout {
Layout.columnSpan: 2
onCheckedChanged: {
if (checked){
if (checked) {
areaSelector.selectedIndex = index
}
}
Component.onCompleted: {
if (index === 0){
if (index === 0) {
checked = true
}
object.interactive = Qt.binding(function(){return checked && _root.checked})
object.interactive = Qt.binding(function () {
return checked && _root.checked
})
}
}
} // area Repeater
ColumnLayout {
id:editorParent
id: editorParent
Layout.fillWidth: true
Layout.maximumWidth: parent.width
Layout.columnSpan: 2
}
Repeater{
id:areaEditorRepeater
Repeater {
id: areaEditorRepeater
Layout.maximumWidth: parent.width
model: _missionItem.areaData.areaList
delegate: Item{
id:editor
delegate: Item {
id: editor
visible: index == areaSelector.selectedIndex
property var _visualItem: undefined
......@@ -78,12 +106,19 @@ GridLayout {
if (geoArea.editorQML && !_visualItem) {
var component = Qt.createComponent(geoArea.editorQML)
if (component.status === Component.Error) {
console.log("Error loading Qml: ", geoArea.editorQML, component.errorString())
console.log("Error loading Qml: ", geoArea.editorQML,
component.errorString())
} else {
_visualItem = component.createObject(editorParent, {
geoArea: editor.geoArea,
visible: Qt.binding(function(){return editor.visible}),
availableWidth: Qt.binding(function(){return editorParent.width})})
"geoArea": editor.geoArea,
"visible": Qt.binding(function () {
return editor.visible
}),
"availableWidth": Qt.binding(
function () {
return editorParent.width
})
})
}
}
}
......@@ -96,23 +131,66 @@ GridLayout {
} // editor
} // areaEditorRepeater
QGCButton{
text: "Check Rules"
enabled: _root.checked
SectionHeader {
id: commandsHeader
Layout.fillWidth: true
onClicked: {
_areaData.isCorrect()
Layout.columnSpan: parent.columns
text: qsTr("Commands and Errors")
}
GridLayout {
columnSpacing: _margin
rowSpacing: _margin
columns: 2
Layout.columnSpan: 2
Layout.fillWidth: true
visible: commandsHeader.checked
QGCLabel {
text: qsTr("Message: ") + _root.errorString
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
color: "orange"
Layout.columnSpan: parent.columns
Layout.fillWidth: true
visible: !_root.areasCorrect
}
QGCButton{
QGCButton {
text: "Intersection"
enabled: _root.checked
Layout.fillWidth: true
Layout.columnSpan: parent.columns
onClicked: {
_areaData.intersection()
}
}
}
QGCButton {
text: "Reset"
onClicked: {
_root.abort()
}
Layout.fillWidth: true
Layout.columnSpan: parent.columns
}
}
Timer {
id: areasCorrectTimer
running: false
interval: 100
repeat: true
onTriggered: {
_root.areasCorrect = _missionItem.areaData.isCorrect(
false /*show gui message*/
)
if (!_root.areasCorrect) {
_root.errorString = _missionItem.areaData.errorString
} else {
_root.errorString = ""
}
}
}
}
......@@ -41,7 +41,7 @@ GridLayout {
}
QGCButton {
text: qsTr("Reset Ref.")
text: qsTr("Reset Reference")
onClicked: generator.resetReference();
Layout.fillWidth: true
Layout.columnSpan: 2
......
......@@ -3,7 +3,7 @@ import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Extras 1.4
import QtQuick.Layouts 1.2
import QtQuick.Layouts 1.3
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
......@@ -25,89 +25,105 @@ Rectangle {
// The following properties must be available up the hierarchy chain
//property real availableWidth ///< Width for control
//property var missionItem ///< Mission Item for editor
property real _margin: ScreenTools.defaultFontPixelWidth / 2
property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5
property var _vehicle: QGroundControl.multiVehicleManager.activeVehicle ?
QGroundControl.multiVehicleManager.activeVehicle :
QGroundControl.multiVehicleManager.offlineEditingVehicle
property var _vehicle: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle
property var _missionItem: missionItem
property var _areaData: missionItem.areaData
QGCPalette { id: qgcPal; colorGroupEnabled: true }
QGCPalette {
id: qgcPal
colorGroupEnabled: true
}
ColumnLayout { // main Column
Column {
// main Column
id: mainColumn
anchors.margins: _margin
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
QGCTabBar {
id: tabBar
anchors.left: parent.left
anchors.right: parent.right
enabled: !editing || editing && correct
readonly property int areaEditorIndex: 0
readonly property int parameterEditorIndex: 1
readonly property int nemoControlsIndex: 2
property bool editing: _missionItem.editing
property bool correct: false
QGCLabel {
id: tipLabel
Layout.fillWidth: true
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
text: qsTr("Use the Area Editor to modify areas.")
visible: areaDataEditor.visible
Component.onCompleted: currentIndex = editing ? areaEditorIndex : parameterEditorIndex
QGCTabButton {
icon.source: "/qmlimages/PatternCamera.png"
icon.height: ScreenTools.defaultFontPixelHeight
}
QGCTabButton {
icon.source: "/qmlimages/PatternGrid.png"
icon.height: ScreenTools.defaultFontPixelHeight
}
QGCTabButton {
icon.source: "/qmlimages/PatternPresets.png"
icon.height: ScreenTools.defaultFontPixelHeight
}
GridLayout {
id: editorSelector
Layout.fillWidth: true
columnSpacing: _margin
rowSpacing: _margin
QGCButton{
text: "Open Area Editor"
visible: parameterEditor.visible
Layout.fillWidth: true
Layout.columnSpan: 2
onClicked:{
_missionItem.startEditing()
onEditingChanged: {
if (editing) {
areasCorrectTimer.start()
} else {
areasCorrectTimer.stop()
}
}
QGCButton{
text: "Done"
Layout.fillWidth: true
visible: areaDataEditor.visible
onClicked: {
if (_areaData.isCorrect()){
onCurrentIndexChanged: {
if (currentIndex === areaEditorIndex) {
_missionItem.startEditing()
} else {
_missionItem.stopEditing()
}
}
}
QGCButton{
text: "Abort"
visible: areaDataEditor.visible
Layout.fillWidth: true
onClicked:{
missionItem.abortEditing()
Timer {
id: areasCorrectTimer
running: false
interval: 100
repeat: true
onTriggered: {
tabBar.correct = _missionItem.areaData.isCorrect(
false /*show gui message*/
)
}
}
}
} // editorSelector
MCI.ParameterEditor{
id:parameterEditor
StackLayout {
width: parent.width
currentIndex: tabBar.currentIndex
MCI.AreaDataEditor {
id: areaEditor
checked: visible
missionItem: _root._missionItem
availableWidth: mainColumn.width
checked: !_missionItem.editing
visible: checked
availableWidth: parent.width
onAbort: {
missionItem.abortEditing()
tabBar.currentIndex = tabBar.parameterEditorIndex
}
}
MCI.AreaDataEditor{
id:areaDataEditor
MCI.ParameterEditor {
id: parameterEditor
checked: visible
missionItem: _root._missionItem
availableWidth: mainColumn.width
checked: _missionItem.editing
visible: checked
}
}
} // main Column
} // Rectangle
......@@ -85,6 +85,7 @@ GridLayout {
GridLayout{
Layout.columnSpan: parent.columns
Layout.maximumWidth: parent.width
columns: 3
columnSpacing: _margin
rowSpacing: _margin
......@@ -99,6 +100,7 @@ GridLayout {
delegate: QGCRadioButton {
checked: index === variantIndex
text: variantRepeater.names[index] ? variantRepeater.names[index]: ""
Layout.fillWidth: true
property int variantIndex: missionItem.variantIndex.value
......
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