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,16 +249,18 @@ void AreaData::intersection() {
while (!bg::covered_by(large, safeAreaENU)) {
snake::offsetPolygon(large, small, -0.1);
large = std::move(small);
qDebug() << "intersection(): shrink";
}
// Convert.
measurementArea->clear();
for (auto it = large.outer().begin(); it != large.outer().end() - 1;
++it) {
QGeoCoordinate c;
snake::fromENU(origin, *it, c);
measurementArea->appendVertex(c);
// 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;
++it) {
QGeoCoordinate c;
snake::fromENU(origin, *it, c);
measurementArea->appendVertex(c);
}
}
}
}
......@@ -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();
doUpdate();
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,
......
......@@ -4,29 +4,56 @@ import QtQuick.Layouts 1.11
import QtQuick.Controls 1.4
import QGroundControl.Controls 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.ScreenTools 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
property real _margin: ScreenTools.defaultFontPixelWidth / 2
columnSpacing: _margin
rowSpacing: _margin
columns: 2
columnSpacing: _margin
rowSpacing: _margin
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()
}
}
ExclusiveGroup{id:areaGroup}
onCheckedChanged: {
if (checked) {
areasCorrectTimer.start()
} else {
areasCorrectTimer.stop()
}
}
Repeater{
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 {
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
SectionHeader {
id: commandsHeader
Layout.fillWidth: true
Layout.columnSpan: parent.columns
text: qsTr("Commands and Errors")
}
QGCButton{
text: "Check Rules"
enabled: _root.checked
GridLayout {
columnSpacing: _margin
rowSpacing: _margin
columns: 2
Layout.columnSpan: 2
Layout.fillWidth: true
onClicked: {
_areaData.isCorrect()
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 {
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
}
}
QGCButton{
text: "Intersection"
enabled: _root.checked
Layout.fillWidth: true
onClicked: {
_areaData.intersection()
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
......
import QtQuick 2.3
import QtQuick 2.3
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 QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Vehicle 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FactSystem 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.Palette 1.0
import QGroundControl.FlightMap 1.0
import QtQuick.Dialogs 1.2
import QtQuick.Extras 1.4
import QtQuick.Layouts 1.3
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Vehicle 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FactSystem 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.Palette 1.0
import QGroundControl.FlightMap 1.0
import MeasurementComplexItem 1.0 as MCI
Rectangle {
id: _root
height: visible ? (mainColumn.height + (_margin * 2)) : 0
width: availableWidth
color: qgcPal.windowShadeDark
radius: _radius
id: _root
height: visible ? (mainColumn.height + (_margin * 2)) : 0
width: availableWidth
color: qgcPal.windowShadeDark
radius: _radius
// 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 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 _missionItem: missionItem
property var _areaData: missionItem.areaData
QGCPalette { id: qgcPal; colorGroupEnabled: true }
ColumnLayout { // main Column
id: mainColumn
anchors.margins: _margin
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
QGCLabel {
id: tipLabel
Layout.fillWidth: true
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
text: qsTr("Use the Area Editor to modify areas.")
visible: areaDataEditor.visible
}
QGCPalette {
id: qgcPal
colorGroupEnabled: true
}
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
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()){
_missionItem.stopEditing()
}
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
missionItem: _root._missionItem
availableWidth: mainColumn.width
checked: !_missionItem.editing
visible: checked
}
MCI.AreaDataEditor {
id: areaEditor
checked: visible
missionItem: _root._missionItem
availableWidth: parent.width
onAbort: {
missionItem.abortEditing()
tabBar.currentIndex = tabBar.parameterEditorIndex
}
}
MCI.AreaDataEditor{
id:areaDataEditor
MCI.ParameterEditor {
id: parameterEditor
missionItem: _root._missionItem
availableWidth: mainColumn.width
checked: _missionItem.editing
visible: checked
checked: visible
missionItem: _root._missionItem
availableWidth: mainColumn.width
}
}
} // 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