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() {