CircularSurvey.cc 19.7 KB
Newer Older
1
#include "CircularSurvey.h"
2
#include "RoutingThread.h"
3
// QGC
4 5
#include "JsonHelper.h"
#include "QGCApplication.h"
6
#include "QGCLoggingCategory.h"
7
// Wima
8
#include "snake.h"
9
#define CLIPPER_SCALE 1000000
10 11 12
#include "clipper/clipper.hpp"

#include "Geometry/GenericCircle.h"
13 14
#include "Snake/SnakeTile.h"

15
// boost
16 17 18
#include <boost/units/io.hpp>
#include <boost/units/systems/si.hpp>

Valentin Platzgummer's avatar
Valentin Platzgummer committed
19 20 21
#include "CircularGenerator.h"
#include "LinearGenerator.h"

22 23
// ToDo: Check what happened to _transectsDirty

24 25
QGC_LOGGING_CATEGORY(CircularSurveyLog, "CircularSurveyLog")

26 27 28 29 30
template <typename T>
constexpr typename std::underlying_type<T>::type integral(T value) {
  return static_cast<typename std::underlying_type<T>::type>(value);
}

31
const char *CircularSurvey::settingsGroup = "CircularSurvey";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
32
const char *CircularSurvey::jsonComplexItemTypeValue = "CircularSurvey";
33
const char *CircularSurvey::variantName = "Variant";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
34
const QString CircularSurvey::name(tr("Circular Survey"));
35

Valentin Platzgummer's avatar
Valentin Platzgummer committed
36 37 38 39 40
CircularSurvey::CircularSurvey(PlanMasterController *masterController,
                               bool flyView, const QString &kmlOrShpFile,
                               QObject *parent)
    : TransectStyleComplexItem(masterController, flyView, settingsGroup,
                               parent),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
41
      _state(STATE::IDLE),
42 43
      _metaDataMap(FactMetaData::createMapFromJsonFile(
          QStringLiteral(":/json/CircularSurvey.SettingsGroup.json"), this)),
44
      _variant(settingsGroup, _metaDataMap[variantName]),
45
      _areaData(std::make_shared<AreaData>()),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
46 47
      _pWorker(std::make_unique<RoutingThread>()) {

48 49 50
  Q_UNUSED(kmlOrShpFile)
  _editorQml = "qrc:/qml/CircularSurveyItemEditor.qml";

51
  // Connect facts.
52 53
  connect(&this->_variant, &Fact::rawValueChanged, this,
          &CircularSurvey::_changeVariant);
54

55
  // Connect worker.
56
  connect(this->_pWorker.get(), &RoutingThread::result, this,
57
          &CircularSurvey::_setTransects);
58
  connect(this->_pWorker.get(), &RoutingThread::calculatingChanged, this,
59
          &CircularSurvey::calculatingChanged);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
60 61

  // Register Generators.
62
  auto lg = std::make_shared<routing::LinearGenerator>(this->_areaData);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
63
  registerGenerator(lg->name(), lg);
64
  auto cg = std::make_shared<routing::CircularGenerator>(this->_areaData);
65
  registerGenerator(cg->name(), cg);
66 67
}

68 69 70
CircularSurvey::~CircularSurvey() {}

void CircularSurvey::reverse() {
71
  this->_setState(STATE::REVERT_PATH);
72 73 74
  this->_rebuildTransects();
}

75 76
void CircularSurvey::setPlanData(const AreaData &d) {
  *this->_areaData = d;
77 78
}

79 80
const AreaData &CircularSurvey::planData() const {
  return *this->_areaData;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
81 82
}

83
AreaData &CircularSurvey::planData() { return *this->_areaData; }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
84

85
QStringList CircularSurvey::variantNames() const { return _variantNames; }
86

87 88
bool CircularSurvey::load(const QJsonObject &complexObject, int sequenceNumber,
                          QString &errorString) {
89 90
  // We need to pull version first to determine what validation/conversion
  // needs to be performed
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
  QList<JsonHelper::KeyValidateInfo> versionKeyInfoList = {
      {JsonHelper::jsonVersionKey, QJsonValue::Double, true},
  };
  if (!JsonHelper::validateKeys(complexObject, versionKeyInfoList,
                                errorString)) {
    return false;
  }

  int version = complexObject[JsonHelper::jsonVersionKey].toInt();
  if (version != 1) {
    errorString = tr("Survey items do not support version %1").arg(version);
    return false;
  }

  QList<JsonHelper::KeyValidateInfo> keyInfoList = {
      {VisualMissionItem::jsonTypeKey, QJsonValue::String, true},
      {ComplexMissionItem::jsonComplexItemTypeKey, QJsonValue::String, true},
108
      {variantName, QJsonValue::Double, false},
109 110 111 112 113 114 115 116 117 118
  };

  if (!JsonHelper::validateKeys(complexObject, keyInfoList, errorString)) {
    return false;
  }

  QString itemType = complexObject[VisualMissionItem::jsonTypeKey].toString();
  QString complexType =
      complexObject[ComplexMissionItem::jsonComplexItemTypeKey].toString();
  if (itemType != VisualMissionItem::jsonTypeComplexItemValue ||
Valentin Platzgummer's avatar
Valentin Platzgummer committed
119
      complexType != jsonComplexItemTypeValue) {
120 121 122 123 124
    errorString = tr("%1 does not support loading this complex mission item "
                     "type: %2:%3")
                      .arg(qgcApp()->applicationName())
                      .arg(itemType)
                      .arg(complexType);
125 126 127 128 129 130 131 132 133 134 135 136 137
    return false;
  }

  _ignoreRecalc = true;

  setSequenceNumber(sequenceNumber);

  if (!_surveyAreaPolygon.loadFromJson(complexObject, true /* required */,
                                       errorString)) {
    _surveyAreaPolygon.clear();
    return false;
  }

138
  if (!load(complexObject, sequenceNumber, errorString)) {
139 140 141 142
    _ignoreRecalc = false;
    return false;
  }

143
  _variant.setRawValue(complexObject[variantName].toInt());
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166

  _ignoreRecalc = false;

  if (_cameraShots == 0) {
    // Shot count was possibly not available from plan file
    _recalcCameraShots();
  }

  return true;
}

QString CircularSurvey::mapVisualQML() const {
  return QStringLiteral("CircularSurveyMapVisual.qml");
}

void CircularSurvey::save(QJsonArray &planItems) {
  QJsonObject saveObject;

  _save(saveObject);

  saveObject[JsonHelper::jsonVersionKey] = 1;
  saveObject[VisualMissionItem::jsonTypeKey] =
      VisualMissionItem::jsonTypeComplexItemValue;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
167 168
  saveObject[ComplexMissionItem::jsonComplexItemTypeKey] =
      jsonComplexItemTypeValue;
169

170
  saveObject[variantName] = double(_variant.rawValue().toUInt());
171 172 173 174 175 176 177

  // Polygon shape
  _surveyAreaPolygon.saveToJson(saveObject);

  planItems.append(saveObject);
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
178 179 180
bool CircularSurvey::specifiesCoordinate() const {
  return _transects.count() > 0 ? _transects.first().count() > 0 : false;
}
181

Valentin Platzgummer's avatar
Valentin Platzgummer committed
182 183 184 185
bool CircularSurvey::_switchToGenerator(
    const CircularSurvey::PtrGenerator &newG) {
  if (this->_pGenerator != newG) {
    if (this->_pGenerator != nullptr) {
186 187 188
      disconnect(this->_pGenerator.get(),
                 &routing::GeneratorBase::generatorChanged, this,
                 &CircularSurvey::_rebuildTransects);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
189 190 191
    }

    this->_pGenerator = newG;
192 193 194
    connect(this->_pGenerator.get(), &routing::GeneratorBase::generatorChanged,
            this, &CircularSurvey::_rebuildTransects);
    emit generatorChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
195

Valentin Platzgummer's avatar
Valentin Platzgummer committed
196
    this->_setState(STATE::IDLE);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
197 198 199 200 201 202 203 204
    _rebuildTransects();

    return true;
  } else {
    return false;
  }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
205 206 207 208 209 210 211 212 213 214 215 216 217 218
void CircularSurvey::_setState(CircularSurvey::STATE state) {
  if (this->_state != state) {
    auto oldState = this->_state;
    this->_state = state;
    if (_calculating(oldState) != _calculating(state)) {
      emit calculatingChanged();
    }
  }
}

bool CircularSurvey::_calculating(CircularSurvey::STATE state) const {
  return state == STATE::ROUTING;
}

219
void CircularSurvey::_changeVariant() {
220
  this->_setState(STATE::CHANGE_VARIANT);
221
  this->_rebuildTransects();
222 223
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
224
bool CircularSurvey::_updateWorker() {
225 226
  // Reset data.
  this->_transects.clear();
227
  this->_variantVector.clear();
228 229 230
  this->_variantNames.clear();
  emit variantNamesChanged();

231
  if (this->_areaData->isValid()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
232 233

    // Prepare data.
234
    auto origin = this->_areaData->origin();
235 236
    origin.setAltitude(0);
    if (!origin.isValid()) {
237
      qCDebug(CircularSurveyLog)
238
          << "_updateWorker(): origin invalid." << origin;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
239
      return false;
240 241
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
242
    // Convert safe area.
243
    auto geoSafeArea = this->_areaData->joinedArea().coordinateList();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
244
    if (!(geoSafeArea.size() >= 3)) {
245
      qCDebug(CircularSurveyLog)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
246
          << "_updateWorker(): safe area invalid." << geoSafeArea;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
247
      return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
248 249 250 251 252
    }
    for (auto &v : geoSafeArea) {
      if (v.isValid()) {
        v.setAltitude(0);
      } else {
253
        qCDebug(CircularSurveyLog)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
254 255
            << "_updateWorker(): safe area contains invalid coordinate."
            << geoSafeArea;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
256
        return false;
257 258 259
      }
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
260 261 262 263 264 265 266
    // Routing par.
    RoutingParameter par;
    par.numSolutions = 5;
    auto &safeAreaENU = par.safeArea;
    snake::areaToEnu(origin, geoSafeArea, safeAreaENU);

    // Create generator.
267 268 269 270 271
    if (this->_pGenerator) {
      routing::GeneratorBase::Generator g; // Transect generator.
      if (this->_pGenerator->get(g)) {
        // Start/Restart routing worker.
        this->_pWorker->route(par, g);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
272
        return true;
273
      } else {
274
        qCDebug(CircularSurveyLog)
275
            << "_updateWorker(): generator creation failed.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
276
        return false;
277
      }
278
    } else {
279
      qCDebug(CircularSurveyLog)
280 281 282
          << "_updateWorker(): pGenerator == nullptr, number of registered "
             "generators: "
          << this->_generatorList.size();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
283
      return false;
284 285
    }
  } else {
286
    qCDebug(CircularSurveyLog) << "_updateWorker(): plan data invalid.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
287
    return false;
288
  }
289 290
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
291
void CircularSurvey::_changeVariantWorker() {
292
  auto variant = this->_variant.rawValue().toUInt();
293 294 295 296

  // Find old variant and run. Old run corresponts with empty list.
  std::size_t old_variant = std::numeric_limits<std::size_t>::max();
  for (std::size_t i = 0; i < std::size_t(this->_variantVector.size()); ++i) {
297 298 299 300
    const auto &variantCoordinates = this->_variantVector.at(i);
    if (variantCoordinates.isEmpty()) {
      old_variant = i;
      break;
301 302
    }
  }
303

304
  // Swap route.
305
  if (variant != old_variant) {
306
    // Swap in new variant.
307
    if (variant < std::size_t(this->_variantVector.size())) {
308 309 310
      if (old_variant != std::numeric_limits<std::size_t>::max()) {
        // this->_transects containes a route, swap it back to
        // this->_solutionVector
311 312
        auto &oldVariantCoordinates = this->_variantVector[old_variant];
        oldVariantCoordinates.swap(this->_transects);
313
      }
314 315
      auto &newVariantCoordinates = this->_variantVector[variant];
      this->_transects.swap(newVariantCoordinates);
316 317

    } else { // error
318
      qCDebug(CircularSurveyLog)
319
          << "Variant out of bounds (variant =" << variant << ").";
320
      qCDebug(CircularSurveyLog) << "Resetting variant to zero.";
321 322 323

      disconnect(&this->_variant, &Fact::rawValueChanged, this,
                 &CircularSurvey::_changeVariant);
324
      this->_variant.setCookedValue(QVariant(0));
325 326
      connect(&this->_variant, &Fact::rawValueChanged, this,
              &CircularSurvey::_changeVariant);
327 328

      if (this->_variantVector.size() > 0) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
329
        this->_changeVariantWorker();
330 331
      }
    }
332 333
  }
}
334

335 336 337
void CircularSurvey::_reverseWorker() {
  if (this->_transects.size() > 0) {
    auto &t = this->_transects.front();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
338
    std::reverse(t.begin(), t.end());
339
  }
340 341
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
342
double CircularSurvey::timeBetweenShots() { return 0; }
343 344 345 346 347 348 349 350 351

QString CircularSurvey::commandDescription() const {
  return tr("Circular Survey");
}

QString CircularSurvey::commandName() const { return tr("Circular Survey"); }

QString CircularSurvey::abbreviation() const { return tr("C.S."); }

352 353 354 355 356 357 358 359 360 361 362 363
TransectStyleComplexItem::ReadyForSaveState
CircularSurvey::readyForSaveState() const {
  if (TransectStyleComplexItem::readyForSaveState() ==
      TransectStyleComplexItem::ReadyForSaveState::ReadyForSave) {
    if (this->_state == STATE::IDLE) {
      return ReadyForSaveState::ReadyForSave;
    } else {
      return ReadyForSaveState::NotReadyForSaveData;
    }
  } else {
    return TransectStyleComplexItem::readyForSaveState();
  }
364 365 366 367
}

double CircularSurvey::additionalTimeDelay() const { return 0; }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
368 369
QString CircularSurvey::patternName() const { return name; }

370 371
bool CircularSurvey::registerGenerator(
    const QString &name, std::shared_ptr<routing::GeneratorBase> g) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
372
  if (name.isEmpty()) {
373
    qCDebug(CircularSurveyLog) << "registerGenerator(): empty name string.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
374 375 376
    return false;
  }

377
  if (!g) {
378
    qCDebug(CircularSurveyLog) << "registerGenerator(): empty generator.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
379 380 381 382
    return false;
  }

  if (this->_generatorNameList.contains(name)) {
383 384
    qCDebug(CircularSurveyLog) << "registerGenerator(): generator "
                                  "already registered.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
385 386 387 388 389
    return false;
  } else {
    this->_generatorNameList.push_back(name);
    this->_generatorList.push_back(g);
    if (this->_generatorList.size() == 1) {
390
      _switchToGenerator(g);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
391 392 393 394 395 396 397 398 399 400 401 402
    }

    emit generatorNameListChanged();
    return true;
  }
}

bool CircularSurvey::unregisterGenerator(const QString &name) {
  auto index = this->_generatorNameList.indexOf(name);
  if (index >= 0) {
    // Is this the current generator?
    const auto &g = this->_generatorList.at(index);
403
    if (g == this->_pGenerator) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
404 405 406 407
      if (index > 0) {
        _switchToGenerator(this->_generatorList.at(index - 1));
      } else {
        _switchToGenerator(nullptr);
408
        qCDebug(CircularSurveyLog)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
409 410 411 412 413 414 415 416 417 418
            << "unregisterGenerator(): last generator unregistered.";
      }
    }

    this->_generatorNameList.removeAt(index);
    this->_generatorList.removeAt(index);

    emit generatorNameListChanged();
    return true;
  } else {
419
    qCDebug(CircularSurveyLog)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
420 421 422 423 424 425 426 427 428
        << "unregisterGenerator(): generator " << name << " not registered.";
    return false;
  }
}

bool CircularSurvey::unregisterGenerator(int index) {
  if (index > 0 && index < this->_generatorNameList.size()) {
    return unregisterGenerator(this->_generatorNameList.at(index));
  } else {
429 430 431 432
    qCDebug(CircularSurveyLog) << "unregisterGenerator(): index (" << index
                               << ") out"
                                  "of bounds ( "
                               << this->_generatorList.size() << " ).";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
433 434 435 436 437 438 439 440 441 442
    return false;
  }
}

bool CircularSurvey::switchToGenerator(const QString &name) {
  auto index = this->_generatorNameList.indexOf(name);
  if (index >= 0) {
    _switchToGenerator(this->_generatorList.at(index));
    return true;
  } else {
443
    qCDebug(CircularSurveyLog)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
444 445 446 447 448 449 450 451 452 453
        << "switchToGenerator(): generator " << name << " not registered.";
    return false;
  }
}

bool CircularSurvey::switchToGenerator(int index) {
  if (index >= 0) {
    _switchToGenerator(this->_generatorList.at(index));
    return true;
  } else {
454 455 456 457
    qCDebug(CircularSurveyLog) << "unregisterGenerator(): index (" << index
                               << ") out"
                                  "of bounds ( "
                               << this->_generatorNameList.size() << " ).";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
458 459 460 461
    return false;
  }
}

462
QStringList CircularSurvey::generatorNameList() {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
463 464 465
  return this->_generatorNameList;
}

466 467 468 469 470 471 472 473
routing::GeneratorBase *CircularSurvey::generator() {
  return _pGenerator.get();
}

int CircularSurvey::generatorIndex() {
  return this->_generatorList.indexOf(this->_pGenerator);
}

474 475 476 477
void CircularSurvey::_rebuildTransectsPhase1(void) {
  auto start = std::chrono::high_resolution_clock::now();

  switch (this->_state) {
478 479
  case STATE::SKIPP:
    qCDebug(CircularSurveyLog) << "rebuildTransectsPhase1: skipp.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
480
    this->_setState(STATE::IDLE);
481
    break;
482
  case STATE::CHANGE_VARIANT:
483
    qCDebug(CircularSurveyLog) << "rebuildTransectsPhase1: variant change.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
484
    this->_changeVariantWorker();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
485
    this->_setState(STATE::IDLE);
486
    break;
487
  case STATE::REVERT_PATH:
488
    qCDebug(CircularSurveyLog) << "rebuildTransectsPhase1: reverse.";
489
    this->_reverseWorker();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
490
    this->_setState(STATE::IDLE);
491
    break;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
492
  case STATE::IDLE:
Valentin Platzgummer's avatar
Valentin Platzgummer committed
493 494
  case STATE::ROUTING:
    this->_setState(STATE::ROUTING);
495
    qCDebug(CircularSurveyLog) << "rebuildTransectsPhase1: update.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
496 497 498
    if (!this->_updateWorker()) {
      this->_setState(STATE::IDLE);
    }
499
    break;
500
  }
501

502
  qCDebug(CircularSurveyLog)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
503
      << "rebuildTransectsPhase1(): "
504 505 506 507
      << std::chrono::duration_cast<std::chrono::milliseconds>(
             std::chrono::high_resolution_clock::now() - start)
             .count()
      << " ms";
508 509 510 511 512
}

// no cameraShots in Circular Survey, add if desired
void CircularSurvey::_recalcCameraShots() { _cameraShots = 0; }

513
void CircularSurvey::_setTransects(CircularSurvey::PtrRoutingData pRoute) {
514
  // Store solutions.
515
  auto ori = this->_areaData->origin();
516
  ori.setAltitude(0);
517 518 519
  const auto &transectsENU = pRoute->transects;
  QVector<Variant> variantVector;
  const auto nSolutions = pRoute->solutionVector.size();
520

521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
  for (std::size_t j = 0; j < nSolutions; ++j) {
    Variant var{QList<CoordInfo_t>()};
    const auto &solution = pRoute->solutionVector.at(j);
    if (solution.size() > 0) {
      const auto &route = solution.at(0);
      const auto &path = route.path;
      const auto &info = route.info;
      if (info.size() > 1) {
        // Find index of first waypoint.
        std::size_t idxFirst = 0;
        const auto &infoFirst = info.at(1);
        const auto &firstTransect = transectsENU[infoFirst.index];
        if (firstTransect.size() > 0) {
          const auto &firstWaypoint =
              infoFirst.reversed ? firstTransect.back() : firstTransect.front();
          double th = 0.01;
          for (std::size_t i = 0; i < path.size(); ++i) {
            auto dist = bg::distance(path[i], firstWaypoint);
            if (dist < th) {
              idxFirst = i;
              break;
            }
543 544
          }

545 546 547 548 549 550 551 552 553 554 555 556 557
          // Find index of last waypoint.
          std::size_t idxLast = path.size() - 1;
          const auto &infoLast = info.at(info.size() - 2);
          const auto &lastTransect = transectsENU[infoLast.index];
          if (lastTransect.size() > 0) {
            const auto &lastWaypoint =
                infoLast.reversed ? lastTransect.front() : lastTransect.back();
            for (long i = path.size() - 1; i >= 0; --i) {
              auto dist = bg::distance(path[i], lastWaypoint);
              if (dist < th) {
                idxLast = i;
                break;
              }
558
            }
559 560 561 562 563 564 565 566

            // Convert to geo coordinates.
            auto &list = var.front();
            for (std::size_t i = idxFirst; i <= idxLast; ++i) {
              auto &vertex = path[i];
              QGeoCoordinate c;
              snake::fromENU(ori, vertex, c);
              list.append(CoordInfo_t{c, CoordTypeInterior});
567
            }
568

569
          } else {
570
            qCDebug(CircularSurveyLog)
571 572
                << "_setTransects(): lastTransect.size() == 0";
          }
573
        } else {
574
          qCDebug(CircularSurveyLog)
575
              << "_setTransects(): firstTransect.size() == 0";
576
        }
577
      } else {
578
        qCDebug(CircularSurveyLog)
579
            << "_setTransects(): transectsInfo.size() <= 1";
580
      }
581
    } else {
582
      qCDebug(CircularSurveyLog) << "_setTransects(): solution.size() == 0";
583
    }
584

585 586
    if (var.size() > 0 && var.front().size() > 0) {
      variantVector.push_back(std::move(var));
587 588
    }
  }
589

590 591 592 593
  // Assign routes if no error occured.
  if (variantVector.size() > 0) {
    // Swap first route to _transects.
    this->_variantVector.swap(variantVector);
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617

    // If the transects are getting rebuilt then any previously loaded
    // mission items are now invalid.
    if (_loadedMissionItemsParent) {
      _loadedMissionItems.clear();
      _loadedMissionItemsParent->deleteLater();
      _loadedMissionItemsParent = nullptr;
    }

    // Add route variant names.
    this->_variantNames.clear();
    for (std::size_t i = 1; i <= std::size_t(this->_variantVector.size());
         ++i) {
      this->_variantNames.append(QString::number(i));
    }
    emit variantNamesChanged();

    disconnect(&this->_variant, &Fact::rawValueChanged, this,
               &CircularSurvey::_changeVariant);
    this->_variant.setCookedValue(QVariant(0));
    connect(&this->_variant, &Fact::rawValueChanged, this,
            &CircularSurvey::_changeVariant);
    this->_changeVariantWorker();

Valentin Platzgummer's avatar
Valentin Platzgummer committed
618
    this->_setState(STATE::SKIPP);
619 620
    this->_rebuildTransects();
  } else {
621 622
    qCDebug(CircularSurveyLog)
        << "_setTransects(): failed, variantVector empty.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
623
    this->_setState(STATE::IDLE);
624 625
  }
}
626

627
Fact *CircularSurvey::variant() { return &_variant; }
628

629
bool CircularSurvey::calculating() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
630
  return this->_calculating(this->_state);
631
}
632

633 634 635 636
/*!
    \class CircularSurveyComplexItem
    \inmodule Wima

637 638
    \brief The \c CircularSurveyComplexItem class provides a survey mission
   item with circular transects around a point of interest.
639

640 641 642 643
    CircularSurveyComplexItem class provides a survey mission item with
   circular transects around a point of interest. Within the \c Wima module
   it's used to scan a defined area with constant angle (circular transects)
   to the base station (point of interest).
644 645 646

    \sa WimaArea
*/