Commit 458ab210 authored by Valentin Platzgummer's avatar Valentin Platzgummer

clearup temp commit

parent d99141e6
[Dolphin]
Timestamp=2020,7,8,11,13,28
Version=4
ViewMode=1
#include "WimaPlanData.h"
AreaData::AreaData(QObject *parent) : QObject(parent) {}
AreaData::AreaData(const AreaData &other, QObject *parent)
: QObject(parent) {
*this = other;
}
AreaData &AreaData::operator=(const AreaData &other) {
this->append(other.measurementArea());
this->append(other.serviceArea());
this->append(other.joinedArea());
this->append(other.corridor());
return *this;
}
void AreaData::append(const WimaJoinedAreaData &areaData) {
if (_joinedArea != areaData) {
_joinedArea = areaData;
emit joinedAreaChanged();
}
}
void AreaData::append(const WimaServiceAreaData &areaData) {
if (_serviceArea != areaData) {
_serviceArea = areaData;
emit serviceAreaChanged();
}
}
void AreaData::append(const WimaCorridorData &areaData) {
if (_corridor != areaData) {
_corridor = areaData;
emit corridorChanged();
}
}
void AreaData::append(const WimaMeasurementAreaData &areaData) {
if (_measurementArea != areaData) {
_measurementArea = areaData;
emit measurementAreaChanged();
if (_measurementArea.coordinateList().size() > 0) {
setOrigin(_measurementArea.coordinateList().first());
} else {
setOrigin(QGeoCoordinate());
}
}
}
void AreaData::append(const WimaJoinedArea &areaData) {
if (_joinedArea != areaData) {
_joinedArea = areaData;
emit joinedAreaChanged();
}
}
void AreaData::append(const WimaArea &areaData) {
if (_serviceArea != areaData) {
_serviceArea = areaData;
emit serviceAreaChanged();
}
}
void AreaData::append(const WimaCorridor &areaData) {
if (_corridor != areaData) {
_corridor = areaData;
emit corridorChanged();
}
}
void AreaData::append(const WimaMeasurementArea &areaData) {
if (_measurementArea != areaData) {
_measurementArea = areaData;
emit measurementAreaChanged();
if (_measurementArea.coordinateList().size() > 0) {
setOrigin(_measurementArea.coordinateList().first());
} else {
setOrigin(QGeoCoordinate());
}
}
}
void AreaData::clear() { *this = AreaData(); }
const QGeoCoordinate &AreaData::origin() const { return _origin; }
bool AreaData::isValid() {
return _measurementArea.coordinateList().size() >= 3 &&
_serviceArea.coordinateList().size() >= 3 && _origin.isValid();
}
const WimaJoinedAreaData &AreaData::joinedArea() const {
return this->_joinedArea;
}
const WimaServiceAreaData &AreaData::serviceArea() const {
return this->_serviceArea;
}
const WimaCorridorData &AreaData::corridor() const {
return this->_corridor;
}
const WimaMeasurementAreaData &AreaData::measurementArea() const {
return this->_measurementArea;
}
WimaJoinedAreaData &AreaData::joinedArea() { return this->_joinedArea; }
WimaServiceAreaData &AreaData::serviceArea() { return this->_serviceArea; }
WimaCorridorData &AreaData::corridor() { return this->_corridor; }
WimaMeasurementAreaData &AreaData::measurementArea() {
return this->_measurementArea;
}
bool AreaData::operator==(const AreaData &other) const {
return this->_joinedArea == other._joinedArea &&
this->_measurementArea == other._measurementArea &&
this->_corridor == other._corridor &&
this->_serviceArea == other._serviceArea;
}
bool AreaData::operator!=(const AreaData &other) const {
return !(*this == other);
}
void AreaData::setOrigin(const QGeoCoordinate &origin) {
if (this->_origin != origin) {
this->_origin = origin;
emit originChanged();
}
}
#include "CircularGenerator.h"
#include "QGCLoggingCategory.h"
QGC_LOGGING_CATEGORY(CircularGeneratorLog, "CircularGeneratorLog")
#define CLIPPER_SCALE 1000000
#include "Wima/Geometry/GenericCircle.h"
#include "clipper/clipper.hpp"
using namespace ClipperLib;
template <> inline auto get<0>(const IntPoint &p) { return p.X; }
template <> inline auto get<1>(const IntPoint &p) { return p.Y; }
#include "SnakeTile.h"
#include "Wima/RoutingThread.h"
namespace routing {
bool circularTransects(const snake::FPoint &reference,
const snake::FPolygon &polygon,
const std::vector<snake::FPolygon> &tiles,
snake::Length deltaR, snake::Angle deltaAlpha,
snake::Length minLength, snake::Transects &transects);
const char *CircularGenerator::settingsGroup = "CircularGenerator";
const char *CircularGenerator::distanceName = "TransectDistance";
const char *CircularGenerator::deltaAlphaName = "DeltaAlpha";
const char *CircularGenerator::minLengthName = "MinLength";
const char *CircularGenerator::refPointLatitudeName = "ReferencePointLat";
const char *CircularGenerator::refPointLongitudeName = "ReferencePointLong";
const char *CircularGenerator::refPointAltitudeName = "ReferencePointAlt";
CircularGenerator::CircularGenerator(QObject *parent)
: CircularGenerator(nullptr, parent) {}
CircularGenerator::CircularGenerator(GeneratorBase::Data d, QObject *parent)
: GeneratorBase(d, parent), _connectionsEstablished(false),
_metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/CircularGenerator.SettingsGroup.json"), this)),
_distance(settingsGroup, _metaDataMap[distanceName]),
_deltaAlpha(settingsGroup, _metaDataMap[deltaAlphaName]),
_minLength(settingsGroup, _metaDataMap[minLengthName]) {
establishConnections();
}
QString CircularGenerator::editorQml() {
return QStringLiteral("CircularGeneratorEditor.qml");
}
QString CircularGenerator::mapVisualQml() {
return QStringLiteral("CircularGeneratorMapVisual.qml");
}
QString CircularGenerator::name() {
return QStringLiteral("Circular Generator");
}
QString CircularGenerator::abbreviation() { return QStringLiteral("C. Gen."); }
bool CircularGenerator::get(Generator &generator) {
if (this->_d) {
if (this->_d->isValid()) {
// Prepare data.
auto origin = this->_d->origin();
origin.setAltitude(0);
if (!origin.isValid()) {
qCDebug(CircularGeneratorLog) << "get(): origin invalid." << origin;
return false;
}
auto ref = this->_reference;
ref.setAltitude(0);
if (!ref.isValid()) {
qCDebug(CircularGeneratorLog) << "get(): reference invalid." << ref;
return false;
}
snake::FPoint reference;
snake::toENU(origin, ref, reference);
auto geoPolygon = this->_d->measurementArea().coordinateList();
for (auto &v : geoPolygon) {
if (v.isValid()) {
v.setAltitude(0);
} else {
qCDebug(CircularGeneratorLog) << "get(): measurement area invalid.";
for (const auto &w : geoPolygon) {
qCDebug(CircularGeneratorLog) << w;
}
return false;
}
}
auto pPolygon = std::make_shared<snake::FPolygon>();
snake::areaToEnu(origin, geoPolygon, *pPolygon);
// Progress and tiles.
const auto &progress = this->_d->measurementArea().progress();
const auto *tiles = this->_d->measurementArea().tiles();
auto pTiles = std::make_shared<std::vector<snake::FPolygon>>();
if (progress.size() == tiles->count()) {
for (int i = 0; i < tiles->count(); ++i) {
if (progress[i] == 100) {
const auto *obj = (*tiles)[int(i)];
const auto *tile = qobject_cast<const SnakeTile *>(obj);
if (tile != nullptr) {
snake::FPolygon tileENU;
snake::areaToEnu(origin, tile->coordinateList(), tileENU);
pTiles->push_back(std::move(tileENU));
} else {
qCDebug(CircularGeneratorLog)
<< "get(): progress.size() != tiles->count().";
return false;
}
}
}
} else {
qCDebug(CircularGeneratorLog)
<< "get(): progress.size() != tiles->count().";
return false;
}
auto geoDepot = this->_d->serviceArea().depot();
if (!geoDepot.isValid()) {
qCDebug(CircularGeneratorLog) << "get(): depot invalid." << geoDepot;
return false;
}
snake::FPoint depot;
snake::toENU(origin, geoDepot, depot);
// Fetch transect parameter.
auto distance =
snake::Length(this->_distance.rawValue().toDouble() * bu::si::meter);
auto minLength =
snake::Length(this->_minLength.rawValue().toDouble() * bu::si::meter);
auto alpha = snake::Angle(this->_deltaAlpha.rawValue().toDouble() *
bu::degree::degree);
generator = [reference, depot, pPolygon, pTiles, distance, alpha,
minLength](snake::Transects &transects) -> bool {
bool value = circularTransects(reference, *pPolygon, *pTiles, distance,
alpha, minLength, transects);
transects.insert(transects.begin(), snake::FLineString{depot});
return value;
};
return true;
} else {
qCDebug(CircularGeneratorLog) << "get(): data invalid.";
return false;
}
} else {
qCDebug(CircularGeneratorLog) << "get(): data member not set.";
return false;
}
}
QGeoCoordinate CircularGenerator::reference() const { return _reference; }
void CircularGenerator::setReference(const QGeoCoordinate &reference) {
if (_reference != reference) {
_reference = reference;
emit referenceChanged();
}
}
void CircularGenerator::resetReferenceIfInvalid() {
if (!this->_reference.isValid()) {
resetReference();
}
}
void CircularGenerator::resetReference() {
if (this->_d->measurementArea().center().isValid()) {
setReference(this->_d->measurementArea().center());
} else {
qCWarning(CircularGeneratorLog)
<< "measurement area center" << this->_d->measurementArea().center();
}
}
void CircularGenerator::establishConnections() {
if (this->_d && !this->_connectionsEstablished) {
connect(this->_d.get(), &AreaData::originChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::progressChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::tileDataChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::centerChanged, this,
&CircularGenerator::resetReferenceIfInvalid);
connect(&this->_d->measurementArea(), &WimaMeasurementAreaData::pathChanged,
this, &GeneratorBase::generatorChanged);
connect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->joinedArea(), &WimaJoinedAreaData::pathChanged, this,
&GeneratorBase::generatorChanged);
connect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->deltaAlpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this, &CircularGenerator::referenceChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
}
}
void CircularGenerator::deleteConnections() {
if (this->_d && this->_connectionsEstablished) {
disconnect(this->_d.get(), &AreaData::originChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::progressChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::tileDataChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->measurementArea(), &WimaMeasurementAreaData::center,
this, &CircularGenerator::resetReferenceIfInvalid);
disconnect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::pathChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged,
this, &GeneratorBase::generatorChanged);
disconnect(&this->_d->joinedArea(), &WimaJoinedAreaData::pathChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->deltaAlpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this, &CircularGenerator::referenceChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = false;
}
}
Fact *CircularGenerator::distance() { return &_distance; }
Fact *CircularGenerator::deltaAlpha() { return &_deltaAlpha; }
Fact *CircularGenerator::minLength() { return &_minLength; }
bool circularTransects(const snake::FPoint &reference,
const snake::FPolygon &polygon,
const std::vector<snake::FPolygon> &tiles,
snake::Length deltaR, snake::Angle deltaAlpha,
snake::Length minLength, snake::Transects &transects) {
auto s1 = std::chrono::high_resolution_clock::now();
// Check preconitions
if (polygon.outer().size() >= 3) {
using namespace boost::units;
// Convert geo polygon to ENU polygon.
std::string error;
// Check validity.
if (!bg::is_valid(polygon, error)) {
qCDebug(CircularGeneratorLog) << "circularTransects(): "
"invalid polygon.";
qCDebug(CircularGeneratorLog) << error.c_str();
std::stringstream ss;
ss << bg::wkt(polygon);
qCDebug(CircularGeneratorLog) << ss.str().c_str();
} else {
// Calculate polygon distances and angles.
std::vector<snake::Length> distances;
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)
// << to_string(snake::Degree(alpha)).c_str();
// qCDebug(CircularGeneratorLog) << "x = " << p.get<0>() << "y
// = "
// << p.get<1>();
//#endif
}
auto rMin = deltaR; // minimal circle radius
snake::Angle alpha1(0 * degree::degree);
snake::Angle alpha2(360 * degree::degree);
// Determine r_min by successive approximation
if (!bg::within(reference, polygon.outer())) {
rMin = bg::distance(reference, polygon) * si::meter;
}
auto rMax = (*std::max_element(distances.begin(),
distances.end())); // maximal circle radius
// Scale parameters and coordinates.
const auto rMinScaled =
ClipperLib::cInt(std::round(rMin.value() * CLIPPER_SCALE));
const auto deltaRScaled =
ClipperLib::cInt(std::round(deltaR.value() * CLIPPER_SCALE));
auto referenceScaled = ClipperLib::IntPoint{
ClipperLib::cInt(std::round(reference.get<0>() * CLIPPER_SCALE)),
ClipperLib::cInt(std::round(reference.get<1>() * CLIPPER_SCALE))};
// Generate circle sectors.
auto rScaled = rMinScaled;
const auto nTran = long(std::ceil(((rMax - rMin) / deltaR).value()));
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();
// qCDebug(CircularGeneratorLog) << "alpha2:
// "
// << to_string(snake::Degree(alpha2)).c_str();
// qCDebug(CircularGeneratorLog) << "n: "
// << to_string((alpha2 - alpha1) / deltaAlpha).c_str();
// qCDebug(CircularGeneratorLog)
// << "nSectors: " << nSectors; qCDebug(CircularGeneratorLog) <<
// "rMin: " << to_string(rMin).c_str();
// qCDebug(CircularGeneratorLog)
// << "rMax: " << to_string(rMax).c_str();
// qCDebug(CircularGeneratorLog) << "nTran: " << nTran;
//#endif
using ClipperCircle =
GenericCircle<ClipperLib::cInt, ClipperLib::IntPoint>;
for (auto &sector : sectors) {
ClipperCircle circle(rScaled, referenceScaled);
approximate(circle, nSectors, sector);
rScaled += deltaRScaled;
}
// Clip sectors to polygonENU.
ClipperLib::Path polygonClipper;
snake::FPolygon shrinked;
snake::offsetPolygon(polygon, shrinked, -0.3);
auto &outer = shrinked.outer();
polygonClipper.reserve(outer.size());
for (auto it = outer.begin(); it < outer.end() - 1; ++it) {
auto x = ClipperLib::cInt(std::round(it->get<0>() * CLIPPER_SCALE));
auto y = ClipperLib::cInt(std::round(it->get<1>() * CLIPPER_SCALE));
polygonClipper.push_back(ClipperLib::IntPoint{x, y});
}
ClipperLib::Clipper clipper;
clipper.AddPath(polygonClipper, ClipperLib::ptClip, true);
clipper.AddPaths(sectors, ClipperLib::ptSubject, false);
ClipperLib::PolyTree transectsClipper;
clipper.Execute(ClipperLib::ctIntersection, transectsClipper,
ClipperLib::pftNonZero, ClipperLib::pftNonZero);
// Subtract holes.
if (tiles.size() > 0) {
vector<ClipperLib::Path> processedTiles;
for (const auto &tile : tiles) {
ClipperLib::Path path;
for (const auto &v : tile.outer()) {
path.push_back(ClipperLib::IntPoint{
static_cast<ClipperLib::cInt>(v.get<0>() * CLIPPER_SCALE),
static_cast<ClipperLib::cInt>(v.get<1>() * CLIPPER_SCALE)});
}
processedTiles.push_back(std::move(path));
}
clipper.Clear();
for (const auto &child : transectsClipper.Childs) {
clipper.AddPath(child->Contour, ClipperLib::ptSubject, false);
}
clipper.AddPaths(processedTiles, ClipperLib::ptClip, true);
transectsClipper.Clear();
clipper.Execute(ClipperLib::ctDifference, transectsClipper,
ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
// Extract transects from PolyTree and convert them to
// BoostLineString
for (const auto &child : transectsClipper.Childs) {
snake::FLineString transect;
transect.reserve(child->Contour.size());
for (const auto &vertex : child->Contour) {
auto x = static_cast<double>(vertex.X) / CLIPPER_SCALE;
auto y = static_cast<double>(vertex.Y) / CLIPPER_SCALE;
transect.push_back(snake::FPoint(x, y));
}
transects.push_back(transect);
}
// Join sectors which where slit due to clipping.
const double th = 0.01;
for (auto ito = transects.begin(); ito < transects.end(); ++ito) {
for (auto iti = ito + 1; iti < transects.end(); ++iti) {
auto dist1 = bg::distance(ito->front(), iti->front());
if (dist1 < th) {
snake::FLineString temp;
for (auto it = iti->end() - 1; it >= iti->begin(); --it) {
temp.push_back(*it);
}
temp.insert(temp.end(), ito->begin(), ito->end());
*ito = temp;
transects.erase(iti);
break;
}
auto dist2 = bg::distance(ito->front(), iti->back());
if (dist2 < th) {
snake::FLineString temp;
temp.insert(temp.end(), iti->begin(), iti->end());
temp.insert(temp.end(), ito->begin(), ito->end());
*ito = temp;
transects.erase(iti);
break;
}
auto dist3 = bg::distance(ito->back(), iti->front());
if (dist3 < th) {
snake::FLineString temp;
temp.insert(temp.end(), ito->begin(), ito->end());
temp.insert(temp.end(), iti->begin(), iti->end());
*ito = temp;
transects.erase(iti);
break;
}
auto dist4 = bg::distance(ito->back(), iti->back());
if (dist4 < th) {
snake::FLineString temp;
temp.insert(temp.end(), ito->begin(), ito->end());
for (auto it = iti->end() - 1; it >= iti->begin(); --it) {
temp.push_back(*it);
}
*ito = temp;
transects.erase(iti);
break;
}
}
}
// Remove short transects
for (auto it = transects.begin(); it < transects.end();) {
if (bg::length(*it) < minLength.value()) {
it = transects.erase(it);
} else {
++it;
}
}
qCDebug(CircularGeneratorLog)
<< "circularTransects(): transect gen. time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - s1)
.count()
<< " ms";
return true;
}
}
return false;
}
} // namespace routing
#include "GeneratorBase.h"
#include <QGeoCoordinate>
namespace routing {
class CircularGenerator : public GeneratorBase {
Q_OBJECT
public:
CircularGenerator(QObject *parent = nullptr);
CircularGenerator(Data d, QObject *parent = nullptr);
Q_PROPERTY(QGeoCoordinate reference READ reference WRITE setReference NOTIFY
referenceChanged)
Q_PROPERTY(Fact *distance READ distance CONSTANT)
Q_PROPERTY(Fact *deltaAlpha READ deltaAlpha CONSTANT)
Q_PROPERTY(Fact *minLength READ minLength CONSTANT)
virtual QString editorQml() override;
virtual QString mapVisualQml() override;
virtual QString name() override;
virtual QString abbreviation() override;
virtual bool get(Generator &generator) override;
QGeoCoordinate reference() const;
Fact *distance();
Fact *deltaAlpha();
Fact *minLength();
void setReference(const QGeoCoordinate &reference);
static const char *settingsGroup;
static const char *distanceName;
static const char *deltaAlphaName;
static const char *minLengthName;
static const char *refPointLongitudeName;
static const char *refPointLatitudeName;
static const char *refPointAltitudeName;
signals:
void referenceChanged();