Commit 01c12f05 authored by Valentin Platzgummer's avatar Valentin Platzgummer

circular survey: runs working

parent 9282a10c
...@@ -100,10 +100,12 @@ ...@@ -100,10 +100,12 @@
{ {
"Alpha": 23, "Alpha": 23,
"MinLength": 5, "MinLength": 5,
"NumRuns": 1,
"ReferencePointAlt": 0, "ReferencePointAlt": 0,
"ReferencePointLat": 47.76807182520187, "ReferencePointLat": 47.76807182520187,
"ReferencePointLong": 16.530610531894183, "ReferencePointLong": 16.530610531894183,
"TransectDistance": 10, "Run": 1,
"TransectDistance": 2,
"TransectStyleComplexItem": { "TransectStyleComplexItem": {
"CameraCalc": { "CameraCalc": {
"AdjustedFootprintFrontal": 25, "AdjustedFootprintFrontal": 25,
...@@ -128,8 +130,8 @@ ...@@ -128,8 +130,8 @@
0, 0,
0, 0,
null, null,
47.7677958621741, 47.76782117293054,
16.53051039683979, 16.53045952207802,
15 15
], ],
"type": "SimpleItem" "type": "SimpleItem"
...@@ -144,8 +146,8 @@ ...@@ -144,8 +146,8 @@
0, 0,
0, 0,
null, null,
47.76795620865596, 47.76801762181985,
16.530658248522045, 16.531145995440625,
15 15
], ],
"type": "SimpleItem" "type": "SimpleItem"
...@@ -160,8 +162,8 @@ ...@@ -160,8 +162,8 @@
0, 0,
0, 0,
null, null,
47.768085758380195, 47.76812651838593,
16.53111095084737, 16.53090512987649,
15 15
], ],
"type": "SimpleItem" "type": "SimpleItem"
...@@ -176,8 +178,8 @@ ...@@ -176,8 +178,8 @@
0, 0,
0, 0,
null, null,
47.768170929071175, 47.768125250928335,
16.531067144987073, 16.530839239105784,
15 15
], ],
"type": "SimpleItem" "type": "SimpleItem"
...@@ -192,8 +194,8 @@ ...@@ -192,8 +194,8 @@
0, 0,
0, 0,
null, null,
47.76812500283788, 47.768187963203616,
16.530906658047133, 16.531058383792516,
15 15
], ],
"type": "SimpleItem" "type": "SimpleItem"
...@@ -208,8 +210,8 @@ ...@@ -208,8 +210,8 @@
0, 0,
0, 0,
null, null,
47.768170929071175, 47.768204997344334,
16.531067144987073, 16.531049622605572,
15 15
], ],
"type": "SimpleItem" "type": "SimpleItem"
...@@ -224,8 +226,8 @@ ...@@ -224,8 +226,8 @@
0, 0,
0, 0,
null, null,
47.76811004588309, 47.76812144975848,
16.530512964621895, 16.530757670470322,
15 15
], ],
"type": "SimpleItem" "type": "SimpleItem"
...@@ -235,6 +237,70 @@ ...@@ -235,6 +237,70 @@
"command": 16, "command": 16,
"doJumpId": 9, "doJumpId": 9,
"frame": 3, "frame": 3,
"params": [
0,
0,
0,
null,
47.76811764852164,
16.530676101820056,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 10,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768222031484335,
16.53104086139957,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 11,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768239065614615,
16.53103210018785,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 12,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768113847226864,
16.530594533208365,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 13,
"frame": 3,
"params": [ "params": [
0, 0,
0, 0,
...@@ -249,7 +315,7 @@ ...@@ -249,7 +315,7 @@
{ {
"autoContinue": true, "autoContinue": true,
"command": 16, "command": 16,
"doJumpId": 10, "doJumpId": 14,
"frame": 3, "frame": 3,
"params": [ "params": [
0, 0,
...@@ -265,7 +331,71 @@ ...@@ -265,7 +331,71 @@
{ {
"autoContinue": true, "autoContinue": true,
"command": 16, "command": 16,
"doJumpId": 11, "doJumpId": 15,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76827313388202,
16.53101457776058,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 16,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76810624447235,
16.53043139603398,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 17,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76815568510607,
16.530399305509537,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 18,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76832423627323,
16.530988294070095,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 19,
"frame": 3, "frame": 3,
"params": [ "params": [
0, 0,
...@@ -281,7 +411,7 @@ ...@@ -281,7 +411,7 @@
{ {
"autoContinue": true, "autoContinue": true,
"command": 16, "command": 16,
"doJumpId": 12, "doJumpId": 20,
"frame": 3, "frame": 3,
"params": [ "params": [
0, 0,
...@@ -297,15 +427,15 @@ ...@@ -297,15 +427,15 @@
{ {
"autoContinue": true, "autoContinue": true,
"command": 16, "command": 16,
"doJumpId": 13, "doJumpId": 21,
"frame": 3, "frame": 3,
"params": [ "params": [
0, 0,
0, 0,
0, 0,
null, null,
47.76834127040819, 47.76831125125834,
16.53097953282404, 16.530806346203697,
15 15
], ],
"type": "SimpleItem" "type": "SimpleItem"
...@@ -313,7 +443,247 @@ ...@@ -313,7 +443,247 @@
{ {
"autoContinue": true, "autoContinue": true,
"command": 16, "command": 16,
"doJumpId": 14, "doJumpId": 22,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768358304533436,
16.530970771572267,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 23,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768307202146545,
16.53099705529708,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 24,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76809864148597,
16.530268258893802,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 25,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768102443012644,
16.530349827431266,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 26,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76829016801015,
16.531005816531692,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 27,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768170929071175,
16.531067144987073,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 28,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76812500283788,
16.530906658047133,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 29,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76809124406109,
16.53085697602152,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 30,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76815389493801,
16.53107590617591,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 31,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768136860795146,
16.531084667345684,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 32,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76805748523563,
16.53080729404687,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 33,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76802372639744,
16.53075761214986,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 34,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76811982666054,
16.531093428523086,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 35,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76810279252523,
16.53110218969476,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 36,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76798996754654,
16.530707930317146,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 37,
"frame": 3, "frame": 3,
"params": [ "params": [
0, 0,
...@@ -329,15 +699,111 @@ ...@@ -329,15 +699,111 @@
{ {
"autoContinue": true, "autoContinue": true,
"command": 16, "command": 16,
"doJumpId": 15, "doJumpId": 38,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768085758380195,
16.53111095084737,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 39,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76806872423443,
16.5311197120076,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 40,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76792244976166,
16.53060856681793,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 41,
"frame": 3, "frame": 3,
"params": [ "params": [
0, 0,
0, 0,
0, 0,
null, null,
47.7677958621741, 47.76788869084566,
16.53051039683979, 16.53055888517811,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 42,
"frame": 3,
"params": [
0,
0,
0,
null,
47.768051690105956,
16.531128473148772,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 43,
"frame": 3,
"params": [
0,
0,
0,
null,
47.76803465596775,
16.53113723429756,
15
],
"type": "SimpleItem"
},
{
"autoContinue": true,
"command": 16,
"doJumpId": 44,
"frame": 3,
"params": [
0,
0,
0,
null,
47.767854931889964,
16.530509203589244,
15 15
], ],
"type": "SimpleItem" "type": "SimpleItem"
...@@ -347,32 +813,48 @@ ...@@ -347,32 +813,48 @@
"TurnAroundDistance": 10, "TurnAroundDistance": 10,
"VisualTransectPoints": [ "VisualTransectPoints": [
[ [
47.7677958621741, 47.76782117293054,
16.53051039683979 16.53045952207802
], ],
[ [
47.76795620865596, 47.76801762181985,
16.530658248522045 16.531145995440625
], ],
[ [
47.768085758380195, 47.76812651838593,
16.53111095084737 16.53090512987649
], ],
[ [
47.768170929071175, 47.768125250928335,
16.531067144987073 16.530839239105784
], ],
[ [
47.76812500283788, 47.768187963203616,
16.530906658047133 16.531058383792516
], ],
[ [
47.768170929071175, 47.768204997344334,
16.531067144987073 16.531049622605572
], ],
[ [
47.76811004588309, 47.76812144975848,
16.530512964621895 16.530757670470322
],
[
47.76811764852164,
16.530676101820056
],
[
47.768222031484335,
16.53104086139957
],
[
47.768239065614615,
16.53103210018785
],
[
47.768113847226864,
16.530594533208365
], ],
[ [
47.76811004588309, 47.76811004588309,
...@@ -382,6 +864,22 @@ ...@@ -382,6 +864,22 @@
47.76825609974418, 47.76825609974418,
16.531023338983744 16.531023338983744
], ],
[
47.76827313388202,
16.53101457776058
],
[
47.76810624447235,
16.53043139603398
],
[
47.76815568510607,
16.530399305509537
],
[
47.76832423627323,
16.530988294070095
],
[ [
47.76834127040819, 47.76834127040819,
16.53097953282404 16.53097953282404
...@@ -391,21 +889,106 @@ ...@@ -391,21 +889,106 @@
16.530564330637993 16.530564330637993
], ],
[ [
47.76834127040819, 47.76831125125834,
16.53097953282404 16.530806346203697
],
[
47.768358304533436,
16.530970771572267
],
[
47.768307202146545,
16.53099705529708
],
[
47.76809864148597,
16.530268258893802
],
[
47.768102443012644,
16.530349827431266
],
[
47.76829016801015,
16.531005816531692
],
[
47.768170929071175,
16.531067144987073
],
[
47.76812500283788,
16.530906658047133
],
[
47.76809124406109,
16.53085697602152
],
[
47.76815389493801,
16.53107590617591
],
[
47.768136860795146,
16.531084667345684
],
[
47.76805748523563,
16.53080729404687
],
[
47.76802372639744,
16.53075761214986
],
[
47.76811982666054,
16.531093428523086
],
[
47.76810279252523,
16.53110218969476
],
[
47.76798996754654,
16.530707930317146
], ],
[ [
47.76795620865596, 47.76795620865596,
16.530658248522045 16.530658248522045
], ],
[ [
47.7677958621741, 47.768085758380195,
16.53051039683979 16.53111095084737
],
[
47.76806872423443,
16.5311197120076
],
[
47.76792244976166,
16.53060856681793
],
[
47.76788869084566,
16.53055888517811
],
[
47.768051690105956,
16.531128473148772
],
[
47.76803465596775,
16.53113723429756
],
[
47.767854931889964,
16.530509203589244
] ]
], ],
"version": 1 "version": 1
}, },
"Type": 1, "Type": 1,
"Variant": 0,
"complexItemType": "CircularSurvey", "complexItemType": "CircularSurvey",
"polygon": [ "polygon": [
[ [
...@@ -442,7 +1025,7 @@ ...@@ -442,7 +1025,7 @@
"AltitudeMode": 1, "AltitudeMode": 1,
"autoContinue": true, "autoContinue": true,
"command": 21, "command": 21,
"doJumpId": 19, "doJumpId": 48,
"frame": 3, "frame": 3,
"params": [ "params": [
0, 0,
......
...@@ -130,7 +130,7 @@ Rectangle { ...@@ -130,7 +130,7 @@ Rectangle {
columnSpacing: _margin columnSpacing: _margin
rowSpacing: _margin rowSpacing: _margin
columns: 4 columns: 6
Repeater{ Repeater{
id: variantRepeater id: variantRepeater
...@@ -141,7 +141,7 @@ Rectangle { ...@@ -141,7 +141,7 @@ Rectangle {
property int len: missionItem.variantNames.length property int len: missionItem.variantNames.length
model: len model: len
QGCRadioButton { delegate: QGCRadioButton {
checked: index === variantRepeater.variant checked: index === variantRepeater.variant
text: variantRepeater.names[index] text: variantRepeater.names[index]
...@@ -154,6 +154,44 @@ Rectangle { ...@@ -154,6 +154,44 @@ Rectangle {
} }
} }
} }
QGCLabel {
text: qsTr("Runs")
}
FactTextField {
fact: missionItem.numRuns
}
GridLayout{
Layout.columnSpan: 2
columnSpacing: _margin
rowSpacing: _margin
columns: 6
Repeater{
id: runRepeater
property var fact: missionItem.run
property int run: fact.value
property var names: missionItem.runNames
property int len: missionItem.runNames.length
model: len > 1 ? len : 0
QGCRadioButton {
checked: index === runRepeater.run
text: runRepeater.names[index]
onCheckedChanged: {
if (checked){
missionItem.run.value = index
}
checked = Qt.binding(function(){ return index === runRepeater.run})
}
}
}
}
} }
SectionHeader { SectionHeader {
......
...@@ -50,6 +50,8 @@ const char *CircularSurvey::refPointLatitudeName = "ReferencePointLat"; ...@@ -50,6 +50,8 @@ const char *CircularSurvey::refPointLatitudeName = "ReferencePointLat";
const char *CircularSurvey::refPointLongitudeName = "ReferencePointLong"; const char *CircularSurvey::refPointLongitudeName = "ReferencePointLong";
const char *CircularSurvey::refPointAltitudeName = "ReferencePointAlt"; const char *CircularSurvey::refPointAltitudeName = "ReferencePointAlt";
const char *CircularSurvey::variantName = "Variant"; const char *CircularSurvey::variantName = "Variant";
const char *CircularSurvey::numRunsName = "NumRuns";
const char *CircularSurvey::runName = "Run";
CircularSurvey::CircularSurvey(Vehicle *vehicle, bool flyView, CircularSurvey::CircularSurvey(Vehicle *vehicle, bool flyView,
const QString &kmlOrShpFile, QObject *parent) const QString &kmlOrShpFile, QObject *parent)
...@@ -62,6 +64,8 @@ CircularSurvey::CircularSurvey(Vehicle *vehicle, bool flyView, ...@@ -62,6 +64,8 @@ CircularSurvey::CircularSurvey(Vehicle *vehicle, bool flyView,
_minLength(settingsGroup, _metaDataMap[minLengthName]), _minLength(settingsGroup, _metaDataMap[minLengthName]),
_type(settingsGroup, _metaDataMap[typeName]), _type(settingsGroup, _metaDataMap[typeName]),
_variant(settingsGroup, _metaDataMap[variantName]), _variant(settingsGroup, _metaDataMap[variantName]),
_numRuns(settingsGroup, _metaDataMap[numRunsName]),
_run(settingsGroup, _metaDataMap[runName]),
_pWorker(std::make_unique<RoutingThread>()), _state(STATE::DEFAULT), _pWorker(std::make_unique<RoutingThread>()), _state(STATE::DEFAULT),
_hidePolygon(false) { _hidePolygon(false) {
Q_UNUSED(kmlOrShpFile) Q_UNUSED(kmlOrShpFile)
...@@ -84,6 +88,11 @@ CircularSurvey::CircularSurvey(Vehicle *vehicle, bool flyView, ...@@ -84,6 +88,11 @@ CircularSurvey::CircularSurvey(Vehicle *vehicle, bool flyView,
&CircularSurvey::_rebuildTransects); &CircularSurvey::_rebuildTransects);
connect(&this->_variant, &Fact::rawValueChanged, this, connect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant); &CircularSurvey::_changeVariant);
connect(&this->_run, &Fact::rawValueChanged, this,
&CircularSurvey::_changeRun);
connect(&this->_numRuns, &Fact::rawValueChanged, this,
&CircularSurvey::_rebuildTransects);
// Connect worker. // Connect worker.
connect(this->_pWorker.get(), &RoutingThread::result, this, connect(this->_pWorker.get(), &RoutingThread::result, this,
&CircularSurvey::_setTransects); &CircularSurvey::_setTransects);
...@@ -122,6 +131,8 @@ bool CircularSurvey::hidePolygon() const { return _hidePolygon; } ...@@ -122,6 +131,8 @@ bool CircularSurvey::hidePolygon() const { return _hidePolygon; }
QList<QString> CircularSurvey::variantNames() const { return _variantNames; } QList<QString> CircularSurvey::variantNames() const { return _variantNames; }
QList<QString> CircularSurvey::runNames() const { return _runNames; }
QGeoCoordinate CircularSurvey::depot() const { return this->_depot; } QGeoCoordinate CircularSurvey::depot() const { return this->_depot; }
QList<QGeoCoordinate> CircularSurvey::safeArea() const { QList<QGeoCoordinate> CircularSurvey::safeArea() const {
...@@ -180,6 +191,8 @@ bool CircularSurvey::load(const QJsonObject &complexObject, int sequenceNumber, ...@@ -180,6 +191,8 @@ bool CircularSurvey::load(const QJsonObject &complexObject, int sequenceNumber,
{minLengthName, QJsonValue::Double, true}, {minLengthName, QJsonValue::Double, true},
{typeName, QJsonValue::Double, true}, {typeName, QJsonValue::Double, true},
{variantName, QJsonValue::Double, false}, {variantName, QJsonValue::Double, false},
{numRunsName, QJsonValue::Double, false},
{runName, QJsonValue::Double, false},
{refPointLatitudeName, QJsonValue::Double, true}, {refPointLatitudeName, QJsonValue::Double, true},
{refPointLongitudeName, QJsonValue::Double, true}, {refPointLongitudeName, QJsonValue::Double, true},
{refPointAltitudeName, QJsonValue::Double, true}, {refPointAltitudeName, QJsonValue::Double, true},
...@@ -222,6 +235,8 @@ bool CircularSurvey::load(const QJsonObject &complexObject, int sequenceNumber, ...@@ -222,6 +235,8 @@ bool CircularSurvey::load(const QJsonObject &complexObject, int sequenceNumber,
_minLength.setRawValue(complexObject[minLengthName].toDouble()); _minLength.setRawValue(complexObject[minLengthName].toDouble());
_type.setRawValue(complexObject[typeName].toInt()); _type.setRawValue(complexObject[typeName].toInt());
_variant.setRawValue(complexObject[variantName].toInt()); _variant.setRawValue(complexObject[variantName].toInt());
_numRuns.setRawValue(complexObject[numRunsName].toInt());
_run.setRawValue(complexObject[runName].toInt());
_referencePoint.setLongitude(complexObject[refPointLongitudeName].toDouble()); _referencePoint.setLongitude(complexObject[refPointLongitudeName].toDouble());
_referencePoint.setLatitude(complexObject[refPointLatitudeName].toDouble()); _referencePoint.setLatitude(complexObject[refPointLatitudeName].toDouble());
_referencePoint.setAltitude(complexObject[refPointAltitudeName].toDouble()); _referencePoint.setAltitude(complexObject[refPointAltitudeName].toDouble());
...@@ -256,6 +271,8 @@ void CircularSurvey::save(QJsonArray &planItems) { ...@@ -256,6 +271,8 @@ void CircularSurvey::save(QJsonArray &planItems) {
saveObject[minLengthName] = _minLength.rawValue().toDouble(); saveObject[minLengthName] = _minLength.rawValue().toDouble();
saveObject[typeName] = double(_type.rawValue().toUInt()); saveObject[typeName] = double(_type.rawValue().toUInt());
saveObject[variantName] = double(_variant.rawValue().toUInt()); saveObject[variantName] = double(_variant.rawValue().toUInt());
saveObject[numRunsName] = double(_numRuns.rawValue().toUInt());
saveObject[runName] = double(_numRuns.rawValue().toUInt());
saveObject[refPointLongitudeName] = _referencePoint.longitude(); saveObject[refPointLongitudeName] = _referencePoint.longitude();
saveObject[refPointLatitudeName] = _referencePoint.latitude(); saveObject[refPointLatitudeName] = _referencePoint.latitude();
saveObject[refPointAltitudeName] = _referencePoint.altitude(); saveObject[refPointAltitudeName] = _referencePoint.altitude();
...@@ -335,13 +352,20 @@ void CircularSurvey::_changeVariant() { ...@@ -335,13 +352,20 @@ void CircularSurvey::_changeVariant() {
this->_rebuildTransects(); this->_rebuildTransects();
} }
void CircularSurvey::_changeRun() {
this->_state = STATE::RUN_CHANGE;
this->_rebuildTransects();
}
void CircularSurvey::_updateWorker() { void CircularSurvey::_updateWorker() {
// Reset data. // Reset data.
this->_transects.clear(); this->_transects.clear();
this->_rawTransects.clear(); this->_rawTransects.clear();
this->_routes.clear(); this->_variantVector.clear();
this->_variantNames.clear(); this->_variantNames.clear();
this->_runNames.clear();
emit variantNamesChanged(); emit variantNamesChanged();
emit runNamesChanged();
// Prepare data. // Prepare data.
auto ref = this->_referencePoint; auto ref = this->_referencePoint;
...@@ -354,8 +378,22 @@ void CircularSurvey::_updateWorker() { ...@@ -354,8 +378,22 @@ void CircularSurvey::_updateWorker() {
v.setAltitude(0); v.setAltitude(0);
} }
auto depot = this->_depot; auto depot = this->_depot;
// Routing par.
RoutingParameter par; RoutingParameter par;
par.numSolutionsPerRun = 5; par.numSolutionsPerRun = 5;
if (this->_numRuns.rawValue().toUInt() < 1) {
disconnect(&this->_numRuns, &Fact::rawValueChanged, this,
&CircularSurvey::_rebuildTransects);
this->_numRuns.setCookedValue(QVariant(1));
connect(&this->_numRuns, &Fact::rawValueChanged, this,
&CircularSurvey::_rebuildTransects);
}
par.numRuns = this->_numRuns.rawValue().toUInt();
// Convert safe area.
auto &safeAreaENU = par.safeArea; auto &safeAreaENU = par.safeArea;
bool useDepot = false; bool useDepot = false;
if (this->_depot.isValid() && this->_safeArea.size() >= 3) { if (this->_depot.isValid() && this->_safeArea.size() >= 3) {
...@@ -364,6 +402,8 @@ void CircularSurvey::_updateWorker() { ...@@ -364,6 +402,8 @@ void CircularSurvey::_updateWorker() {
} else { } else {
snake::areaToEnu(ref, polygon, safeAreaENU); snake::areaToEnu(ref, polygon, safeAreaENU);
} }
// Fetch transect parameter.
auto distance = snake::Length(this->_transectDistance.rawValue().toDouble() * auto distance = snake::Length(this->_transectDistance.rawValue().toDouble() *
bu::si::meter); bu::si::meter);
auto minLength = auto minLength =
...@@ -407,24 +447,82 @@ void CircularSurvey::_updateWorker() { ...@@ -407,24 +447,82 @@ void CircularSurvey::_updateWorker() {
this->_transectsDirty = true; this->_transectsDirty = true;
} }
void CircularSurvey::_changeVariantWorker() { void CircularSurvey::_changeVariantRunWorker() {
auto variant = this->_variant.rawValue().toUInt(); auto variant = this->_variant.rawValue().toUInt();
// Find old route variant. Old variant corresponts with empty list. auto run = this->_run.rawValue().toUInt();
std::size_t old_variant = 0;
for (std::size_t i = 0; i < std::size_t(this->_routes.size()); ++i) { // Find old variant and run. Old run corresponts with empty list.
const auto &r = this->_routes[i]; std::size_t old_variant = std::numeric_limits<std::size_t>::max();
std::size_t old_run = std::numeric_limits<std::size_t>::max();
for (std::size_t i = 0; i < std::size_t(this->_variantVector.size()); ++i) {
const auto &solution = this->_variantVector.at(i);
for (std::size_t j = 0; j < std::size_t(solution.size()); ++j) {
const auto &r = solution[j];
if (r.isEmpty()) { if (r.isEmpty()) {
old_variant = i; old_variant = i;
break; old_run = j;
// break
i = std::numeric_limits<std::size_t>::max() - 1;
j = std::numeric_limits<std::size_t>::max() - 1;
}
} }
} }
// Swap route.
if (variant != old_variant || run != old_run) {
// Swap in new variant, if condition. // Swap in new variant, if condition.
if (variant < std::size_t(this->_routes.size()) && if (variant < std::size_t(this->_variantVector.size()) &&
old_variant < std::size_t(this->_routes.size()) && run < std::size_t(this->_variantVector.at(variant).size())) {
variant != old_variant) { if (old_variant != std::numeric_limits<std::size_t>::max()) {
this->_routes[old_variant].swap(this->_transects); // this->_transects containes a route, swap it back to
this->_transects.swap(this->_routes[variant]); // this->_solutionVector
auto &old_solution = this->_variantVector[old_variant];
auto &old_route = old_solution[old_run];
old_route.swap(this->_transects);
}
auto &solution = this->_variantVector[variant];
auto &route = solution[run];
this->_transects.swap(route);
if (variant != old_variant) {
// Add run names.
this->_runNames.clear();
for (std::size_t i = 1; i <= std::size_t(solution.size()); ++i) {
this->_runNames.append(QString::number(i));
}
emit runNamesChanged();
}
} else { // error
qWarning() << "Variant or run out of bounds (variant = " << variant
<< ", run = " << run << ").";
qWarning() << "Resetting variant and run.";
disconnect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant);
disconnect(&this->_run, &Fact::rawValueChanged, this,
&CircularSurvey::_changeRun);
if (old_variant < std::size_t(this->_variantVector.size())) {
this->_variant.setCookedValue(QVariant::fromValue(old_variant));
auto &solution = this->_variantVector[old_variant];
if (old_run < std::size_t(solution.size())) {
this->_run.setCookedValue(QVariant::fromValue(old_run));
} else {
this->_run.setCookedValue(QVariant(0));
}
} else {
this->_variant.setCookedValue(QVariant(0));
this->_run.setCookedValue(QVariant(0));
}
connect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant);
connect(&this->_run, &Fact::rawValueChanged, this,
&CircularSurvey::_changeRun);
if (this->_variantVector.size() > 0 &&
this->_variantVector.front().size() > 0) {
this->_changeVariantRunWorker();
}
}
} }
} }
...@@ -451,7 +549,7 @@ void CircularSurvey::_storeWorker() { ...@@ -451,7 +549,7 @@ void CircularSurvey::_storeWorker() {
} }
// Store raw transects. // Store raw transects.
const auto &pRoutingData = this->_workerOutput; const auto &pRoutingData = this->_pRoutingData;
const auto &ori = this->_referencePoint; const auto &ori = this->_referencePoint;
const auto &transectsENU = pRoutingData->transects; const auto &transectsENU = pRoutingData->transects;
std::size_t startIdx = 0; std::size_t startIdx = 0;
...@@ -472,25 +570,29 @@ void CircularSurvey::_storeWorker() { ...@@ -472,25 +570,29 @@ void CircularSurvey::_storeWorker() {
} }
} }
// Store routes. // Store solutions.
auto nRoutes = pRoutingData->routeVector.size(); QVector<Runs> solutionVector;
QVector<Transects> routes(nRoutes, Transects{QList<CoordInfo_t>()}); const auto nSolutions = pRoutingData->solutionVector.size();
for (std::size_t k = 0; k < nRoutes; ++k) { for (std::size_t j = 0; j < nSolutions; ++j) {
const auto &transectsInfo = pRoutingData->routeInfoVector.at(k); const auto &solution = pRoutingData->solutionVector.at(j);
if (transectsInfo.size() > 1) { const auto nRuns = solution.size();
const auto &route = pRoutingData->routeVector.at(k); // Store runs.
Runs runs(nRuns, Transects{QList<CoordInfo_t>()});
for (std::size_t k = 0; k < nRuns; ++k) {
const auto &route = solution.at(k);
const auto &path = route.path;
const auto &info = route.info;
if (info.size() > 1) {
// Find index of first waypoint. // Find index of first waypoint.
std::size_t idxFirst = 0; std::size_t idxFirst = 0;
const auto &infoFirst = const auto &infoFirst = depotValid ? info.at(1) : info.at(0);
depotValid ? transectsInfo.at(1) : transectsInfo.at(0);
const auto &firstTransect = transectsENU[infoFirst.index]; const auto &firstTransect = transectsENU[infoFirst.index];
if (firstTransect.size() > 0) { if (firstTransect.size() > 0) {
const auto &firstWaypoint = const auto &firstWaypoint =
infoFirst.reversed ? firstTransect.back() : firstTransect.front(); infoFirst.reversed ? firstTransect.back() : firstTransect.front();
double th = 0.01; double th = 0.01;
for (std::size_t i = 0; i < route.size(); ++i) { for (std::size_t i = 0; i < path.size(); ++i) {
auto dist = bg::distance(route[i], firstWaypoint); auto dist = bg::distance(path[i], firstWaypoint);
if (dist < th) { if (dist < th) {
idxFirst = i; idxFirst = i;
break; break;
...@@ -498,14 +600,14 @@ void CircularSurvey::_storeWorker() { ...@@ -498,14 +600,14 @@ void CircularSurvey::_storeWorker() {
} }
// Find index of last waypoint. // Find index of last waypoint.
std::size_t idxLast = route.size() - 1; std::size_t idxLast = path.size() - 1;
const auto &infoLast = transectsInfo.at(transectsInfo.size() - 2); const auto &infoLast = info.at(info.size() - 2);
const auto &lastTransect = transectsENU[infoLast.index]; const auto &lastTransect = transectsENU[infoLast.index];
if (lastTransect.size() > 0) { if (lastTransect.size() > 0) {
const auto &lastWaypoint = const auto &lastWaypoint =
infoLast.reversed ? lastTransect.front() : lastTransect.back(); infoLast.reversed ? lastTransect.front() : lastTransect.back();
for (long i = route.size() - 1; i >= 0; --i) { for (long i = path.size() - 1; i >= 0; --i) {
auto dist = bg::distance(route[i], lastWaypoint); auto dist = bg::distance(path[i], lastWaypoint);
if (dist < th) { if (dist < th) {
idxLast = i; idxLast = i;
break; break;
...@@ -513,9 +615,9 @@ void CircularSurvey::_storeWorker() { ...@@ -513,9 +615,9 @@ void CircularSurvey::_storeWorker() {
} }
// Convert to geo coordinates. // Convert to geo coordinates.
auto &list = routes[k].front(); auto &list = runs[k].front();
for (std::size_t i = idxFirst; i <= idxLast; ++i) { for (std::size_t i = idxFirst; i <= idxLast; ++i) {
auto &vertex = route[i]; auto &vertex = path[i];
QGeoCoordinate c; QGeoCoordinate c;
snake::fromENU(ori, vertex, c); snake::fromENU(ori, vertex, c);
list.append(CoordInfo_t{c, CoordTypeInterior}); list.append(CoordInfo_t{c, CoordTypeInterior});
...@@ -530,33 +632,59 @@ void CircularSurvey::_storeWorker() { ...@@ -530,33 +632,59 @@ void CircularSurvey::_storeWorker() {
qWarning() << "CS::_storeWorker(): transectsInfo.size() <= 1"; qWarning() << "CS::_storeWorker(): transectsInfo.size() <= 1";
} }
} }
// Remove empty runs.
// Remove empty routes.
bool error = true; bool error = true;
std::size_t n = 0; for (auto it = runs.begin(); it < runs.end();) {
for (auto it = routes.begin(); it < routes.end();) {
if (it->size() > 0 && it->front().size() > 0) { if (it->size() > 0 && it->front().size() > 0) {
error = false; error = false;
++it; ++it;
++n;
} else { } else {
it = routes.erase(it); it = runs.erase(it);
}
}
if (!error) {
solutionVector.push_back(std::move(runs));
}
}
// Remove empty solutions.
std::size_t nSol = 0;
for (auto it = solutionVector.begin(); it < solutionVector.end();) {
if (it->size() > 0 && it->front().size() > 0) {
++it;
++nSol;
} else {
it = solutionVector.erase(it);
} }
} }
// Assign routes if no error occured. // Assign routes if no error occured.
if (!error) { if (nSol > 0) {
// Swap first route to _transects. // Swap first route to _transects.
this->_transects.swap(routes.front()); this->_variantVector.swap(solutionVector);
this->_routes.swap(routes);
// Add route variant names. // Add route variant names.
for (std::size_t i = 1; i <= n; ++i) { this->_variantNames.clear();
for (std::size_t i = 1; i <= std::size_t(this->_variantVector.size());
++i) {
this->_variantNames.append(QString::number(i)); this->_variantNames.append(QString::number(i));
} }
emit variantNamesChanged(); emit variantNamesChanged();
this->_variant.setCookedValue(QVariant(0));
// Swap in rawTransects. // Swap in rawTransects.
this->_rawTransects.swap(rawTransects); this->_rawTransects.swap(rawTransects);
disconnect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant);
disconnect(&this->_run, &Fact::rawValueChanged, this,
&CircularSurvey::_changeRun);
this->_variant.setCookedValue(QVariant(0));
this->_run.setCookedValue(QVariant(0));
connect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant);
connect(&this->_run, &Fact::rawValueChanged, this,
&CircularSurvey::_changeRun);
this->_changeVariantRunWorker();
// Mark transect as stored and ready. // Mark transect as stored and ready.
this->_transectsDirty = false; this->_transectsDirty = false;
} }
...@@ -601,7 +729,13 @@ void CircularSurvey::_rebuildTransectsPhase1(void) { ...@@ -601,7 +729,13 @@ void CircularSurvey::_rebuildTransectsPhase1(void) {
#ifdef SHOW_CIRCULAR_SURVEY_TIME #ifdef SHOW_CIRCULAR_SURVEY_TIME
qWarning() << "CS::rebuildTransectsPhase1: variant change."; qWarning() << "CS::rebuildTransectsPhase1: variant change.";
#endif #endif
this->_changeVariantWorker(); this->_changeVariantRunWorker();
break;
case STATE::RUN_CHANGE:
#ifdef SHOW_CIRCULAR_SURVEY_TIME
qWarning() << "CS::rebuildTransectsPhase1: run change.";
#endif
this->_changeVariantRunWorker();
break; break;
case STATE::REVERSE: case STATE::REVERSE:
#ifdef SHOW_CIRCULAR_SURVEY_TIME #ifdef SHOW_CIRCULAR_SURVEY_TIME
...@@ -643,7 +777,7 @@ void CircularSurvey::_recalcComplexDistance() { ...@@ -643,7 +777,7 @@ void CircularSurvey::_recalcComplexDistance() {
void CircularSurvey::_recalcCameraShots() { _cameraShots = 0; } void CircularSurvey::_recalcCameraShots() { _cameraShots = 0; }
void CircularSurvey::_setTransects(CircularSurvey::PtrRoutingData pRoute) { void CircularSurvey::_setTransects(CircularSurvey::PtrRoutingData pRoute) {
this->_workerOutput = pRoute; this->_pRoutingData = pRoute;
this->_state = STATE::STORE; this->_state = STATE::STORE;
this->_rebuildTransects(); this->_rebuildTransects();
} }
...@@ -654,6 +788,10 @@ Fact *CircularSurvey::type() { return &_type; } ...@@ -654,6 +788,10 @@ Fact *CircularSurvey::type() { return &_type; }
Fact *CircularSurvey::variant() { return &_variant; } Fact *CircularSurvey::variant() { return &_variant; }
Fact *CircularSurvey::numRuns() { return &_numRuns; }
Fact *CircularSurvey::run() { return &_run; }
int CircularSurvey::typeCount() const { return int(integral(Type::Count)); } int CircularSurvey::typeCount() const { return int(integral(Type::Count)); }
bool CircularSurvey::calculating() const { bool CircularSurvey::calculating() const {
...@@ -740,7 +878,8 @@ bool circularTransects(const QGeoCoordinate &ref, const QGeoCoordinate &depot, ...@@ -740,7 +878,8 @@ bool circularTransects(const QGeoCoordinate &ref, const QGeoCoordinate &depot,
//#ifdef DEBUG_CIRCULAR_SURVEY //#ifdef DEBUG_CIRCULAR_SURVEY
// qWarning() << "CS::circularTransects(): sector parameres:"; // qWarning() << "CS::circularTransects(): sector parameres:";
// qWarning() << "alpha1: " << // qWarning() << "alpha1: " <<
// to_string(snake::Degree(alpha1)).c_str(); qWarning() << "alpha2: " // to_string(snake::Degree(alpha1)).c_str(); qWarning() << "alpha2:
// "
// << to_string(snake::Degree(alpha2)).c_str(); qWarning() << "n: " // << to_string(snake::Degree(alpha2)).c_str(); qWarning() << "n: "
// << to_string((alpha2 - alpha1) / deltaAlpha).c_str(); qWarning() // << to_string((alpha2 - alpha1) / deltaAlpha).c_str(); qWarning()
// << "nSectors: " << nSectors; qWarning() << "rMin: " << // << "nSectors: " << nSectors; qWarning() << "rMin: " <<
......
...@@ -35,11 +35,14 @@ public: ...@@ -35,11 +35,14 @@ public:
Q_PROPERTY(Fact *minLength READ minLength CONSTANT) Q_PROPERTY(Fact *minLength READ minLength CONSTANT)
Q_PROPERTY(Fact *type READ type CONSTANT) Q_PROPERTY(Fact *type READ type CONSTANT)
Q_PROPERTY(Fact *variant READ variant CONSTANT) Q_PROPERTY(Fact *variant READ variant CONSTANT)
Q_PROPERTY(Fact *numRuns READ numRuns CONSTANT)
Q_PROPERTY(Fact *run READ run CONSTANT)
Q_PROPERTY(int typeCount READ typeCount CONSTANT) Q_PROPERTY(int typeCount READ typeCount CONSTANT)
Q_PROPERTY(bool calculating READ calculating NOTIFY calculatingChanged) Q_PROPERTY(bool calculating READ calculating NOTIFY calculatingChanged)
Q_PROPERTY(bool hidePolygon READ hidePolygon NOTIFY hidePolygonChanged) Q_PROPERTY(bool hidePolygon READ hidePolygon NOTIFY hidePolygonChanged)
Q_PROPERTY( Q_PROPERTY(
QList<QString> variantNames READ variantNames NOTIFY variantNamesChanged) QList<QString> variantNames READ variantNames NOTIFY variantNamesChanged)
Q_PROPERTY(QList<QString> runNames READ runNames NOTIFY runNamesChanged)
Q_INVOKABLE void resetReference(void); Q_INVOKABLE void resetReference(void);
Q_INVOKABLE void reverse(void); Q_INVOKABLE void reverse(void);
...@@ -57,10 +60,13 @@ public: ...@@ -57,10 +60,13 @@ public:
Fact *minLength(); Fact *minLength();
Fact *type(); Fact *type();
Fact *variant(); Fact *variant();
Fact *numRuns();
Fact *run();
int typeCount() const; int typeCount() const;
bool calculating() const; bool calculating() const;
bool hidePolygon() const; bool hidePolygon() const;
QList<QString> variantNames() const; QList<QString> variantNames() const;
QList<QString> runNames() const;
QGeoCoordinate depot() const; QGeoCoordinate depot() const;
QList<QGeoCoordinate> safeArea() const; QList<QGeoCoordinate> safeArea() const;
const QList<QList<QGeoCoordinate>> &rawTransects() const; const QList<QList<QGeoCoordinate>> &rawTransects() const;
...@@ -87,6 +93,8 @@ public: ...@@ -87,6 +93,8 @@ public:
static const char *minLengthName; static const char *minLengthName;
static const char *typeName; static const char *typeName;
static const char *variantName; static const char *variantName;
static const char *numRunsName;
static const char *runName;
static const char *CircularSurveyName; static const char *CircularSurveyName;
static const char *refPointLongitudeName; static const char *refPointLongitudeName;
static const char *refPointLatitudeName; static const char *refPointLatitudeName;
...@@ -99,6 +107,7 @@ signals: ...@@ -99,6 +107,7 @@ signals:
void depotChanged(); void depotChanged();
void safeAreaChanged(); void safeAreaChanged();
void variantNamesChanged(); void variantNamesChanged();
void runNamesChanged();
private slots: private slots:
// Overrides from TransectStyleComplexItem // Overrides from TransectStyleComplexItem
...@@ -113,10 +122,13 @@ private: ...@@ -113,10 +122,13 @@ private:
void _buildAndAppendMissionItems(QList<MissionItem *> &items, void _buildAndAppendMissionItems(QList<MissionItem *> &items,
QObject *missionItemParent); QObject *missionItemParent);
void _changeVariant(); void _changeVariant();
void _changeRun();
void _updateWorker(); void _updateWorker();
void _changeVariantWorker(); void _changeVariantRunWorker();
void _reverseWorker(); void _reverseWorker();
void _storeWorker(); void _storeWorker();
void _changeRunWorker();
// center of the circular lanes, e.g. base station // center of the circular lanes, e.g. base station
QGeoCoordinate _referencePoint; QGeoCoordinate _referencePoint;
...@@ -131,22 +143,29 @@ private: ...@@ -131,22 +143,29 @@ private:
SettingsFact _type; SettingsFact _type;
SettingsFact _variant; SettingsFact _variant;
QList<QString> _variantNames; QList<QString> _variantNames;
SettingsFact _numRuns;
SettingsFact _run;
QList<QString> _runNames;
// Worker // Worker
using PtrWorker = std::shared_ptr<RoutingThread>; using PtrWorker = std::shared_ptr<RoutingThread>;
PtrWorker _pWorker; PtrWorker _pWorker;
PtrRoutingData _workerOutput; PtrRoutingData _pRoutingData;
// Data and State. // Routing data.
QGeoCoordinate _depot; QGeoCoordinate _depot;
QList<QGeoCoordinate> _safeArea; QList<QGeoCoordinate> _safeArea;
QList<QList<QGeoCoordinate>> _rawTransects; QList<QList<QGeoCoordinate>> _rawTransects;
QVector<Transects> _routes; using Runs = QVector<Transects>;
QVector<Runs> _variantVector;
// State.
enum class STATE { enum class STATE {
DEFAULT, DEFAULT,
STORE, STORE,
REVERSE, REVERSE,
VARIANT_CHANGE, VARIANT_CHANGE,
RUN_CHANGE,
}; };
STATE _state; STATE _state;
......
...@@ -73,10 +73,13 @@ void RoutingThread::run() { ...@@ -73,10 +73,13 @@ void RoutingThread::run() {
#endif #endif
} else { } else {
// Prepare data for routing. // Prepare data for routing.
auto &routeInfoVector = pRouteData->routeInfoVector; auto &solutionVector = pRouteData->solutionVector;
auto &routeVector = pRouteData->routeVector;
snake::RouteParameter snakePar; snake::RouteParameter snakePar;
snakePar.numSolutionsPerRun = numSolutionsPerRun; snakePar.numSolutionsPerRun = numSolutionsPerRun;
snakePar.numRuns = numRuns;
// Set time limit to 10 min.
const auto maxRoutingTime = std::chrono::minutes(10); const auto maxRoutingTime = std::chrono::minutes(10);
const auto routingEnd = const auto routingEnd =
std::chrono::high_resolution_clock::now() + maxRoutingTime; std::chrono::high_resolution_clock::now() + maxRoutingTime;
...@@ -87,11 +90,11 @@ void RoutingThread::run() { ...@@ -87,11 +90,11 @@ void RoutingThread::run() {
}; };
// Route transects. // Route transects.
bool success = snake::route(safeAreaENU, transectsENU, routeInfoVector, bool success =
routeVector, snakePar); snake::route(safeAreaENU, transectsENU, solutionVector, snakePar);
// Check if routing was successful. // Check if routing was successful.
if ((!success || routeVector.size() < 1) && !this->_restart) { if ((!success || solutionVector.size() < 1) && !this->_restart) {
#ifdef DEBUG_CIRCULAR_SURVEY #ifdef DEBUG_CIRCULAR_SURVEY
qWarning() << "RoutingWorker::run(): " qWarning() << "RoutingWorker::run(): "
"routing failed."; "routing failed.";
......
...@@ -12,8 +12,7 @@ ...@@ -12,8 +12,7 @@
struct RoutingData { struct RoutingData {
snake::Transects transects; snake::Transects transects;
std::vector<snake::Route> routeVector; std::vector<snake::Solution> solutionVector;
std::vector<snake::RouteInfo> routeInfoVector;
std::string errorString; std::string errorString;
}; };
......
...@@ -627,8 +627,7 @@ bool transectsFromScenario(Length distance, Length minLength, Angle angle, ...@@ -627,8 +627,7 @@ bool transectsFromScenario(Length distance, Length minLength, Angle angle,
} }
bool route(const FPolygon &area, const Transects &transects, bool route(const FPolygon &area, const Transects &transects,
std::vector<RouteInfo> &routeInfoVector, std::vector<Solution> &solutionVector, const RouteParameter &par) {
std::vector<Route> &routeVector, const RouteParameter &par) {
#ifdef SNAKE_SHOW_TIME #ifdef SNAKE_SHOW_TIME
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
...@@ -797,9 +796,18 @@ bool route(const FPolygon &area, const Transects &transects, ...@@ -797,9 +796,18 @@ bool route(const FPolygon &area, const Transects &transects,
#endif #endif
// Create Routing Index Manager. // Create Routing Index Manager.
long numVehicles = 1; auto minNumTransectsPerRun =
std::max<std::size_t>(1, par.minNumTransectsPerRun);
auto maxRuns = std::max<std::size_t>(
1, std::floor(double(transects.size()) / minNumTransectsPerRun));
auto numRuns = std::max<std::size_t>(1, par.numRuns);
numRuns = std::min<std::size_t>(numRuns, maxRuns);
RoutingIndexManager::NodeIndex depot(0); RoutingIndexManager::NodeIndex depot(0);
RoutingIndexManager manager(nNodes, numVehicles, depot); // std::vector<RoutingIndexManager::NodeIndex> depots(numRuns, depot);
// RoutingIndexManager manager(nNodes, numRuns, depots, depots);
RoutingIndexManager manager(nNodes, numRuns, depot);
// Create Routing Model. // Create Routing Model.
RoutingModel routing(manager); RoutingModel routing(manager);
// Create and register a transit callback. // Create and register a transit callback.
...@@ -812,6 +820,14 @@ bool route(const FPolygon &area, const Transects &transects, ...@@ -812,6 +820,14 @@ bool route(const FPolygon &area, const Transects &transects,
}); });
// Define cost of each arc. // Define cost of each arc.
routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
// Add distance dimension.
if (numRuns > 1) {
routing.AddDimension(transitCallbackIndex, 0, 300000000,
true, // start cumul to zero
"Distance");
routing.GetMutableDimension("Distance")
->SetGlobalSpanCostCoefficient(100000000);
}
// Define disjunctions. // Define disjunctions.
#ifdef SNAKE_DEBUG #ifdef SNAKE_DEBUG
...@@ -834,7 +850,8 @@ bool route(const FPolygon &area, const Transects &transects, ...@@ -834,7 +850,8 @@ bool route(const FPolygon &area, const Transects &transects,
searchParameters.set_first_solution_strategy( searchParameters.set_first_solution_strategy(
FirstSolutionStrategy::PATH_CHEAPEST_ARC); FirstSolutionStrategy::PATH_CHEAPEST_ARC);
// Number of solutions. // Number of solutions.
searchParameters.set_number_of_solutions_to_collect(par.numSolutionsPerRun); auto numSolutionsPerRun = std::max<std::size_t>(1, par.numSolutionsPerRun);
searchParameters.set_number_of_solutions_to_collect(numSolutionsPerRun);
// Set costume limit. // Set costume limit.
auto *solver = routing.solver(); auto *solver = routing.solver();
auto *limit = solver->MakeCustomLimit(par.stop); auto *limit = solver->MakeCustomLimit(par.stop);
...@@ -862,7 +879,16 @@ bool route(const FPolygon &area, const Transects &transects, ...@@ -862,7 +879,16 @@ bool route(const FPolygon &area, const Transects &transects,
par.errorString = "User terminated."; par.errorString = "User terminated.";
return false; return false;
} }
if (pSolutions->size() == 0) {
std::stringstream ss;
ss << "No solution found." << std::endl;
par.errorString = ss.str();
return false;
}
//================================================================
// Construc route.
//================================================================
#ifdef SNAKE_SHOW_TIME #ifdef SNAKE_SHOW_TIME
start = std::chrono::high_resolution_clock::now(); start = std::chrono::high_resolution_clock::now();
#endif #endif
...@@ -879,11 +905,14 @@ bool route(const FPolygon &area, const Transects &transects, ...@@ -879,11 +905,14 @@ bool route(const FPolygon &area, const Transects &transects,
par.errorString = ss.str(); par.errorString = ss.str();
continue; continue;
} }
//================================================================ // Iterate over all routes.
// Construc route. Solution routeVector;
//================================================================ for (std::size_t vehicle = 0; vehicle < numRuns; ++vehicle) {
if (!routing.IsVehicleUsed(**solution, vehicle))
continue;
// Create index list. // Create index list.
auto index = routing.Start(0); auto index = routing.Start(vehicle);
std::vector<size_t> route_idx; std::vector<size_t> route_idx;
route_idx.push_back(manager.IndexToNode(index).value()); route_idx.push_back(manager.IndexToNode(index).value());
while (!routing.IsEnd(index)) { while (!routing.IsEnd(index)) {
...@@ -901,32 +930,33 @@ bool route(const FPolygon &area, const Transects &transects, ...@@ -901,32 +930,33 @@ bool route(const FPolygon &area, const Transects &transects,
} }
std::cout << std::endl; std::cout << std::endl;
#endif #endif
if (route_idx.size() < 2) { if (route_idx.size() < 2) {
std::stringstream ss; std::stringstream ss;
ss << par.errorString << "Error while assembling route " << counter << "." ss << par.errorString
<< std::endl; << "Error while assembling route (solution = " << counter
<< ", run = " << vehicle << ")." << std::endl;
par.errorString = ss.str(); par.errorString = ss.str();
continue; continue;
} }
// Construct route. // Assemble route.
Route r; Route r;
RouteInfo routeInfo; auto &path = r.path;
auto &info = r.info;
for (size_t i = 0; i < route_idx.size() - 1; ++i) { for (size_t i = 0; i < route_idx.size() - 1; ++i) {
size_t nodeIndex0 = route_idx[i]; size_t nodeIndex0 = route_idx[i];
size_t nodeIndex1 = route_idx[i + 1]; size_t nodeIndex1 = route_idx[i + 1];
const auto &n2t0 = nodeToTransectList[nodeIndex0]; const auto &n2t0 = nodeToTransectList[nodeIndex0];
routeInfo.emplace_back(n2t0.transectsIndex, n2t0.reversed); info.emplace_back(n2t0.transectsIndex, n2t0.reversed);
// Copy transect to route. // Copy transect to route.
const auto &t = transects[n2t0.transectsIndex]; const auto &t = transects[n2t0.transectsIndex];
if (n2t0.reversed) { // transect reversal needed? if (n2t0.reversed) { // transect reversal needed?
for (auto it = t.end() - 1; it > t.begin(); --it) { for (auto it = t.end() - 1; it > t.begin(); --it) {
r.push_back(*it); path.push_back(*it);
} }
} else { } else {
for (auto it = t.begin(); it < t.end() - 1; ++it) { for (auto it = t.begin(); it < t.end() - 1; ++it) {
r.push_back(*it); path.push_back(*it);
} }
} }
// Connect transects. // Connect transects.
...@@ -935,8 +965,9 @@ bool route(const FPolygon &area, const Transects &transects, ...@@ -935,8 +965,9 @@ bool route(const FPolygon &area, const Transects &transects,
nodeList[nodeIndex0].fromIndex, nodeList[nodeIndex0].fromIndex,
nodeList[nodeIndex1].toIndex, idxList)) { nodeList[nodeIndex1].toIndex, idxList)) {
std::stringstream ss; std::stringstream ss;
ss << par.errorString << "Error while assembling route " << counter ss << par.errorString
<< "." << std::endl; << "Error while assembling route (solution = " << counter
<< ", run = " << vehicle << ")." << std::endl;
par.errorString = ss.str(); par.errorString = ss.str();
continue; continue;
} }
...@@ -945,248 +976,42 @@ bool route(const FPolygon &area, const Transects &transects, ...@@ -945,248 +976,42 @@ bool route(const FPolygon &area, const Transects &transects,
} }
for (auto idx : idxList) { for (auto idx : idxList) {
auto p = int2Float(vertices[idx]); auto p = int2Float(vertices[idx]);
r.push_back(p); path.push_back(p);
} }
} }
// Append last transect info. // Append last transect info.
const auto &n2t0 = nodeToTransectList.back(); const auto &n2t0 = nodeToTransectList.back();
routeInfo.emplace_back(n2t0.transectsIndex, n2t0.reversed); info.emplace_back(n2t0.transectsIndex, n2t0.reversed);
if (r.size() < 2 || routeInfo.size() < 2) { if (path.size() < 2 || info.size() < 2) {
std::stringstream ss; std::stringstream ss;
ss << par.errorString << "Route " << counter << " empty." << std::endl; ss << par.errorString << "Route empty (solution = " << counter
<< ", run = " << vehicle << ")." << std::endl;
par.errorString = ss.str(); par.errorString = ss.str();
continue; continue;
} }
routeVector.push_back(std::move(r)); routeVector.push_back(std::move(r));
routeInfoVector.push_back(std::move(routeInfo));
} }
#ifdef SNAKE_SHOW_TIME if (routeVector.size() > 0) {
delta = std::chrono::duration_cast<std::chrono::milliseconds>( solutionVector.push_back(std::move(routeVector));
std::chrono::high_resolution_clock::now() - start);
cout << "reconstruct route: " << delta.count() << " ms" << endl;
#endif
if (routeVector.size() > 0 && routeVector.size() == routeInfoVector.size()) {
return true;
} else { } else {
return false; std::stringstream ss;
} ss << par.errorString << "Solution " << counter << " empty." << std::endl;
} par.errorString = ss.str();
bool route_old(const FPolygon &area, const Transects &transects,
std::vector<TransectInfo> &transectInfo, Route &r,
std::function<bool()> stop, string &errorString) {
//=======================================
// Route Transects using Google or-tools.
//=======================================
// Create vertex list;
FLineString vertices;
size_t n0 = 0;
for (const auto &t : transects) {
n0 += std::min<std::size_t>(t.size(), 2);
}
vertices.reserve(n0);
struct LocalInfo {
LocalInfo(size_t n, bool f) : index(n), front(f) {}
size_t index;
bool front;
};
std::vector<LocalInfo> localTransectInfo;
for (size_t i = 0; i < transects.size(); ++i) {
const auto &t = transects[i];
vertices.push_back(t.front());
localTransectInfo.push_back(LocalInfo{i, true});
if (t.size() >= 2) {
vertices.push_back(t.back());
localTransectInfo.push_back(LocalInfo{i, false});
}
}
for (long i = 0; i < long(area.outer().size()) - 1; ++i) {
vertices.push_back(area.outer()[i]);
} }
for (auto &ring : area.inners()) {
for (auto &vertex : ring)
vertices.push_back(vertex);
}
size_t n1 = vertices.size();
// Generate routing model.
Matrix<double> connectionGraph(n1, n1);
// Offset joined area.
FPolygon areaOffset;
offsetPolygon(area, areaOffset, detail::offsetConstant);
// Generate routing model.
#ifdef SNAKE_SHOW_TIME
auto start = std::chrono::high_resolution_clock::now();
#endif
graphFromPolygon(areaOffset, vertices, connectionGraph);
#ifdef SNAKE_SHOW_TIME
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start);
cout << "Execution time graphFromPolygon(): " << delta.count() << " ms"
<< endl;
#endif
Matrix<double> distanceMatrix(connectionGraph);
#ifdef SNAKE_SHOW_TIME
start = std::chrono::high_resolution_clock::now();
#endif
if (!toDistanceMatrix(distanceMatrix)) {
errorString = "Error while generating distance matrix.";
return false;
} }
#ifdef SNAKE_SHOW_TIME #ifdef SNAKE_SHOW_TIME
delta = std::chrono::duration_cast<std::chrono::milliseconds>( delta = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start); std::chrono::high_resolution_clock::now() - start);
cout << "Execution time toDistanceMatrix(): " << delta.count() << " ms" cout << "reconstruct route: " << delta.count() << " ms" << endl;
<< endl;
#endif #endif
Matrix<int64_t> dm(n0, n0);
for (size_t i = 0; i < n0; ++i) {
dm(i, i) = 0;
for (size_t j = i + 1; j < n0; ++j) {
dm(i, j) = int64_t(distanceMatrix(i, j) * CLIPPER_SCALE);
dm(j, i) = int64_t(distanceMatrix(i, j) * CLIPPER_SCALE);
}
}
// Create Routing Index Manager.
RoutingIndexManager manager(
dm.n() /*num indices*/, 1 /*vehicles*/,
RoutingIndexManager::NodeIndex(0) /*depot index*/);
// Create Routing Model.
RoutingModel routing(manager);
// Create and register a transit callback. if (solutionVector.size() > 0) {
const int transitCallbackIndex = routing.RegisterTransitCallback( return true;
[&dm, &manager](int64 from_index, int64 to_index) -> int64 {
// Convert from routing variable Index to distance matrix NodeIndex.
auto from_node = manager.IndexToNode(from_index).value();
auto to_node = manager.IndexToNode(to_index).value();
return dm(from_node, to_node);
});
// Define cost of each arc.
routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
// Define Constraints (this constraints have a huge impact on the
// solving time, improvments could be done, e.g. SearchFilter).
#ifdef SNAKE_DEBUG
std::cout << "snake::route(): Constraints:" << std::endl;
#endif
Solver *solver = routing.solver();
size_t index = 0;
for (size_t i = 0; i < transects.size(); ++i) {
const auto &t = transects[i];
#ifdef SNAKE_DEBUG
std::cout << "i = " << i << ": t.size() = " << t.size() << std::endl;
#endif
if (t.size() >= 2) {
auto idx0 = manager.NodeToIndex(RoutingIndexManager::NodeIndex(index));
auto idx1 =
manager.NodeToIndex(RoutingIndexManager::NodeIndex(index + 1));
auto cond0 = routing.NextVar(idx0)->IsEqual(idx1);
auto cond1 = routing.NextVar(idx1)->IsEqual(idx0);
auto c = solver->MakeNonEquality(cond0, cond1);
solver->AddConstraint(c);
#ifdef SNAKE_DEBUG
std::cout << "( next(" << index << ") == " << index + 1 << " ) != ( next("
<< index + 1 << ") == " << index << " )" << std::endl;
#endif
index += 2;
} else { } else {
index += 1;
}
}
// Set first solution heuristic.
auto searchParameters = DefaultRoutingSearchParameters();
searchParameters.set_first_solution_strategy(
FirstSolutionStrategy::PATH_CHEAPEST_ARC);
// Set costume limit.
auto *limit = solver->MakeCustomLimit(stop);
routing.AddSearchMonitor(limit);
// Solve the problem.
#ifdef SNAKE_SHOW_TIME
start = std::chrono::high_resolution_clock::now();
#endif
const Assignment *solution = routing.SolveWithParameters(searchParameters);
#ifdef SNAKE_SHOW_TIME
delta = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start);
cout << "Execution time routing.SolveWithParameters(): " << delta.count()
<< " ms" << endl;
#endif
if (!solution || solution->Size() <= 1) {
errorString = "Not able to solve the routing problem.";
return false;
} else if (stop()) {
errorString = "User terminated.";
return false; return false;
} }
// Extract index list from solution.
index = routing.Start(0);
std::vector<size_t> route_idx;
route_idx.push_back(manager.IndexToNode(index).value());
while (!routing.IsEnd(index)) {
index = solution->Value(routing.NextVar(index));
route_idx.push_back(manager.IndexToNode(index).value());
}
// Helper Lambda.
auto idx2Vertex = [&vertices](const std::vector<size_t> &idxArray,
std::vector<FPoint> &path) {
for (auto idx : idxArray)
path.push_back(vertices[idx]);
};
// Construct route.
for (size_t i = 0; i < route_idx.size() - 1; ++i) {
size_t idx0 = route_idx[i];
size_t idx1 = route_idx[i + 1];
const auto &info1 = localTransectInfo[idx0];
const auto &info2 = localTransectInfo[idx1];
if (info1.index == info2.index) { // same transect?
TransectInfo trInfo(info1.index,
!info1.front ? true : false /*transect reversed?*/);
transectInfo.push_back(trInfo);
if (!info1.front) { // transect reversal needed?
FLineString reversedTransect;
const auto &t = transects[info1.index];
for (auto it = t.end() - 1; it >= t.begin(); --it) {
reversedTransect.push_back(*it);
}
for (auto it = reversedTransect.begin();
it < reversedTransect.end() - 1; ++it) {
r.push_back(*it);
}
} else {
const auto &t = transects[info1.index];
for (auto it = t.begin(); it < t.end() - 1; ++it) {
r.push_back(*it);
}
}
} else {
std::vector<size_t> idxList;
shortestPathFromGraph(connectionGraph, idx0, idx1, idxList);
if (i != route_idx.size() - 2) {
idxList.pop_back();
}
idx2Vertex(idxList, r);
}
}
return true;
}
bool route_old(const FPolygon &area, const Transects &transects,
std::vector<TransectInfo> &transectInfo, Route &r,
string &errorString) {
auto stop = [] { return false; };
return route_old(area, transects, transectInfo, r, stop, errorString);
} }
FPoint int2Float(const IPoint &ip) { return int2Float(ip, stdScale); } FPoint int2Float(const IPoint &ip) { return int2Float(ip, stdScale); }
......
...@@ -215,7 +215,6 @@ bool tiles(const FPolygon &area, Length tileHeight, Length tileWidth, ...@@ -215,7 +215,6 @@ bool tiles(const FPolygon &area, Length tileHeight, Length tileWidth,
using Transects = vector<FLineString>; using Transects = vector<FLineString>;
using Progress = vector<int>; using Progress = vector<int>;
using Route = FLineString;
bool transectsFromScenario(Length distance, Length minLength, Angle angle, bool transectsFromScenario(Length distance, Length minLength, Angle angle,
const FPolygon &mArea, const FPolygon &mArea,
...@@ -228,27 +227,26 @@ struct TransectInfo { ...@@ -228,27 +227,26 @@ struct TransectInfo {
size_t index; size_t index;
bool reversed; bool reversed;
}; };
using RouteInfo = std::vector<TransectInfo>; struct Route {
FLineString path;
std::vector<TransectInfo> info;
};
using Solution =
std::vector<Route>; // Every route corresponds to one run/vehicle
struct RouteParameter { struct RouteParameter {
RouteParameter() RouteParameter()
: numSolutionsPerRun(1), numRuns(1), stop([] { return false; }) {} : numSolutionsPerRun(1), numRuns(1), minNumTransectsPerRun(5),
stop([] { return false; }) {}
std::size_t numSolutionsPerRun; std::size_t numSolutionsPerRun;
std::size_t numRuns; std::size_t numRuns;
std::size_t minNumTransectsPerRun;
std::function<bool(void)> stop; std::function<bool(void)> stop;
mutable std::string errorString; mutable std::string errorString;
}; };
bool route(const FPolygon &area, const Transects &transects, bool route(const FPolygon &area, const Transects &transects,
std::vector<RouteInfo> &routeInfoVector, std::vector<Solution> &solutionVector,
std::vector<Route> &routeVector,
const RouteParameter &par = RouteParameter()); const RouteParameter &par = RouteParameter());
bool route_old(const FPolygon &area, const Transects &transects,
std::vector<TransectInfo> &transectInfo, Route &r,
string &errorString);
bool route_old(const FPolygon &area, const Transects &transects,
std::vector<TransectInfo> &transectInfo, Route &r,
std::function<bool(void)> stop, string &errorString);
namespace detail { namespace detail {
const double offsetConstant = const double offsetConstant =
0.1; // meter, polygon offset to compenstate for numerical inaccurracies. 0.1; // meter, polygon offset to compenstate for numerical inaccurracies.
......
...@@ -798,34 +798,36 @@ void WimaController::_storeRoute(RoutingThread::PtrRoutingData data) { ...@@ -798,34 +798,36 @@ void WimaController::_storeRoute(RoutingThread::PtrRoutingData data) {
// Copy waypoints to waypoint manager. // Copy waypoints to waypoint manager.
_snakeWM.clear(); _snakeWM.clear();
if (data->routeVector.size() > 0 && data->routeVector.front().size() > 0 && if (data->solutionVector.size() > 0 &&
data->routeInfoVector.size() > 0) { data->solutionVector.front().size() > 0) {
// Store route. // Store route.
const auto &transectsENU = data->transects; const auto &transectsENU = data->transects;
const auto &routeInfo = data->routeInfoVector.front(); const auto &solution = data->solutionVector.front();
const auto &route = data->routeVector.front(); const auto &route = solution.front();
const auto &path = route.path;
const auto &info = route.info;
// Find index of first waypoint. // Find index of first waypoint.
std::size_t idxFirst = 0; std::size_t idxFirst = 0;
const auto &infoFirst = routeInfo.front(); const auto &infoFirst = info.front();
const auto &firstTransect = transectsENU[infoFirst.index]; const auto &firstTransect = transectsENU[infoFirst.index];
const auto &firstWaypoint = const auto &firstWaypoint =
infoFirst.reversed ? firstTransect.back() : firstTransect.front(); infoFirst.reversed ? firstTransect.back() : firstTransect.front();
double th = 0.001; double th = 0.001;
for (std::size_t i = 0; i < route.size(); ++i) { for (std::size_t i = 0; i < path.size(); ++i) {
auto dist = bg::distance(route[i], firstWaypoint); auto dist = bg::distance(path[i], firstWaypoint);
if (dist < th) { if (dist < th) {
idxFirst = i; idxFirst = i;
break; break;
} }
} }
// Find index of last waypoint. // Find index of last waypoint.
std::size_t idxLast = route.size() - 1; std::size_t idxLast = path.size() - 1;
const auto &infoLast = routeInfo.back(); const auto &infoLast = info.back();
const auto &lastTransect = transectsENU[infoLast.index]; const auto &lastTransect = transectsENU[infoLast.index];
const auto &lastWaypoint = const auto &lastWaypoint =
infoLast.reversed ? lastTransect.front() : lastTransect.back(); infoLast.reversed ? lastTransect.front() : lastTransect.back();
for (long i = route.size() - 1; i >= 0; --i) { for (long i = path.size() - 1; i >= 0; --i) {
auto dist = bg::distance(route[i], lastWaypoint); auto dist = bg::distance(path[i], lastWaypoint);
if (dist < th) { if (dist < th) {
idxLast = i; idxLast = i;
break; break;
...@@ -834,7 +836,7 @@ void WimaController::_storeRoute(RoutingThread::PtrRoutingData data) { ...@@ -834,7 +836,7 @@ void WimaController::_storeRoute(RoutingThread::PtrRoutingData data) {
// Convert to geo coordinates and append to waypoint manager. // Convert to geo coordinates and append to waypoint manager.
const auto &ori = this->_origin; const auto &ori = this->_origin;
for (std::size_t i = idxFirst; i <= idxLast; ++i) { for (std::size_t i = idxFirst; i <= idxLast; ++i) {
auto &vertex = route[i]; auto &vertex = path[i];
QGeoCoordinate c; QGeoCoordinate c;
snake::fromENU(ori, vertex, c); snake::fromENU(ori, vertex, c);
_snakeWM.push_back(c); _snakeWM.push_back(c);
......
...@@ -38,5 +38,18 @@ ...@@ -38,5 +38,18 @@
"shortDescription": "Route variant.", "shortDescription": "Route variant.",
"type": "uint64", "type": "uint64",
"defaultValue": 0 "defaultValue": 0
},
{
"name": "NumRuns",
"shortDescription": "The number of runs.",
"type": "uint64",
"min": 1,
"defaultValue": 1
},
{
"name": "Run",
"shortDescription": "The current run.",
"type": "uint64",
"defaultValue": 0
} }
] ]
...@@ -128,40 +128,6 @@ Rectangle { ...@@ -128,40 +128,6 @@ Rectangle {
} // Tile Column } // Tile Column
SectionHeader {
id: transectsHeader
text: qsTr("Transects")
}
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: transectsHeader.checked
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columnSpacing: _margin
rowSpacing: _margin
columns: 2
QGCLabel { text: qsTr("Distance") }
FactTextField {
fact: areaItem.transectDistance
Layout.fillWidth: true
}
QGCLabel { text: qsTr("Min. Length") }
FactTextField {
fact: areaItem.minTransectLength
Layout.fillWidth: true
}
} // Transects GridLayout
} // Transects Column
SectionHeader { SectionHeader {
id: statsHeader id: statsHeader
text: qsTr("Statistics") text: qsTr("Statistics")
......
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