MeasurementComplexItem.cc 37.9 KB
Newer Older
1
#include "MeasurementComplexItem.h"
2 3 4

#include "CircularGenerator.h"
#include "LinearGenerator.h"
5
#include "RoutingThread.h"
6 7 8 9
#include "geometry/GenericCircle.h"
#include "geometry/MeasurementArea.h"
#include "geometry/SafeArea.h"
#include "geometry/clipper/clipper.hpp"
10
#include "geometry/geometry.h"
11 12
#include "nemo_interface/SnakeTile.h"

13 14
// QGC
#include "JsonHelper.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
15
#include "PlanMasterController.h"
16 17 18 19 20 21 22
#include "QGCApplication.h"
#include "QGCLoggingCategory.h"

// boost
#include <boost/units/io.hpp>
#include <boost/units/systems/si.hpp>

Valentin Platzgummer's avatar
Valentin Platzgummer committed
23
QGC_LOGGING_CATEGORY(MeasurementComplexItemLog, "MeasurementComplexItemLog")
24 25 26 27 28 29

template <typename T>
constexpr typename std::underlying_type<T>::type integral(T value) {
  return static_cast<typename std::underlying_type<T>::type>(value);
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
30 31 32 33
const char *MeasurementComplexItem::settingsGroup = "MeasurementComplexItem";
const char *MeasurementComplexItem::jsonComplexItemTypeValue =
    "MeasurementComplexItem";
const QString MeasurementComplexItem::name(tr("Measurement"));
34 35

namespace {
36
const char *variantIndexKey = "VariantIndex";
37 38 39
const char *altitudeKey = "Altitude";
const char *areaDataKey = "AreaData";
const char *variantNamesKey = "VariantNames";
40 41
const char *generatorArrayKey = "GeneratorArray";
const char *variantArrayKey = "VariantArray";
42
const char *generatorIndexKey = "GeneratorIndex";
43
} // namespace
Valentin Platzgummer's avatar
Valentin Platzgummer committed
44 45 46 47

MeasurementComplexItem::MeasurementComplexItem(
    PlanMasterController *masterController, bool flyView,
    const QString &kmlOrShpFile, QObject *parent)
48
    : ComplexMissionItem(masterController, flyView, parent), _sequenceNumber(0),
49
      _followTerrain(false), _state(STATE::IDLE),
50
      _metaDataMap(FactMetaData::createMapFromJsonFile(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
51 52
          QStringLiteral(":/json/MeasurementComplexItem.SettingsGroup.json"),
          this)),
53
      _altitude(settingsGroup, _metaDataMap[altitudeKey]),
54
      _variantIndex(settingsGroup, _metaDataMap[variantIndexKey]),
55 56 57
      _pAreaData(new AreaData(this)), _pEditorData(new AreaData(this)),
      _pCurrentData(_pAreaData), _pGenerator(nullptr),
      _pWorker(new RoutingThread(this)) {
58

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
  // Setup altitude.
  _altitude.setRawValue(qgcApp()
                            ->toolbox()
                            ->settingsManager()
                            ->appSettings()
                            ->defaultMissionItemAltitude()
                            ->rawValue());
  connect(&_altitude, &SettingsFact::rawValueChanged, [this] {
    emit this->minAMSLAltitudeChanged(this->_altitude.rawValue().toDouble());
  });
  connect(&_altitude, &SettingsFact::rawValueChanged, [this] {
    emit this->maxAMSLAltitudeChanged(this->_altitude.rawValue().toDouble());
  });
  connect(&_altitude, &SettingsFact::rawValueChanged, [this] {
    emit this->amslEntryAltChanged(this->_altitude.rawValue().toDouble());
  });
  connect(&_altitude, &SettingsFact::rawValueChanged, [this] {
    emit this->amslExitAltChanged(this->_altitude.rawValue().toDouble());
  });
  connect(&_altitude, &SettingsFact::rawValueChanged, this,
          &MeasurementComplexItem::_onAltitudeChanged);

81
  Q_UNUSED(kmlOrShpFile)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
82
  _editorQml = "qrc:/qml/MeasurementItemEditor.qml";
83 84

  // Connect facts.
85 86
  connect(&this->_variantIndex, &Fact::rawValueChanged, this,
          &MeasurementComplexItem::_changeVariantIndex);
87 88

  // Connect worker.
89
  connect(this->_pWorker, &RoutingThread::result, this,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
90 91
          &MeasurementComplexItem::_storeRoutingData);

92
  // Connect coordinate and exitCoordinate.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
93 94 95 96 97 98 99 100
  connect(this, &MeasurementComplexItem::routeChanged,
          [this] { emit this->coordinateChanged(this->coordinate()); });
  connect(this, &MeasurementComplexItem::routeChanged,
          [this] { emit this->exitCoordinateChanged(this->exitCoordinate()); });
  connect(this, &MeasurementComplexItem::routeChanged, [this] {
    emit this->exitCoordinateSameAsEntryChanged(
        this->exitCoordinateSameAsEntry());
  });
101

102 103 104 105 106 107 108 109 110 111 112 113 114 115
  // Connect isIncomplete.
  connect(this, &MeasurementComplexItem::idleChanged, [this] {
    if (this->idle()) {
      if (this->route().size() > 0 && this->_isIncomplete == true) {
        this->_isIncomplete = false;
        emit this->isIncompleteChanged();
      }
    } else {
      if (this->_isIncomplete == false) {
        this->_isIncomplete = true;
        emit this->isIncompleteChanged();
      }
    }
  });
116 117

  // Connect readyForSave
118 119 120
  connect(this, &MeasurementComplexItem::idleChanged, this,
          &MeasurementComplexItem::readyForSaveStateChanged);

121 122 123 124
  // Connect flightPathSegments
  connect(this, &MeasurementComplexItem::routeChanged, this,
          &MeasurementComplexItem::_updateFlightpathSegments);

125 126 127 128
  // Connect complexDistance.
  connect(this, &MeasurementComplexItem::routeChanged,
          [this] { emit this->complexDistanceChanged(); });

129
  resetGenerators();
130
  startEditing();
131 132
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
133
MeasurementComplexItem::~MeasurementComplexItem() {}
134

Valentin Platzgummer's avatar
Valentin Platzgummer committed
135
void MeasurementComplexItem::reverseRoute() { _reverseRoute(); }
136

Valentin Platzgummer's avatar
Valentin Platzgummer committed
137
const AreaData *MeasurementComplexItem::areaData() const {
138
  return this->_pCurrentData;
139 140
}

141
AreaData *MeasurementComplexItem::areaData() { return this->_pCurrentData; }
142

Valentin Platzgummer's avatar
Valentin Platzgummer committed
143 144
QVariantList MeasurementComplexItem::route() { return _route; }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
145 146 147
QStringList MeasurementComplexItem::variantNames() const {
  return _variantNames;
}
148

Valentin Platzgummer's avatar
Valentin Platzgummer committed
149 150
bool MeasurementComplexItem::load(const QJsonObject &complexObject,
                                  int sequenceNumber, QString &errorString) {
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
  // We need to pull version first to determine what validation/conversion
  // needs to be performed
  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;
  }

167 168 169 170 171 172 173 174 175
  {
    QList<JsonHelper::KeyValidateInfo> keyInfoList = {
        {VisualMissionItem::jsonTypeKey, QJsonValue::String, true},
        {ComplexMissionItem::jsonComplexItemTypeKey, QJsonValue::String, true},
    };

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

177 178 179 180 181 182 183 184 185 186 187 188
    QString itemType = complexObject[VisualMissionItem::jsonTypeKey].toString();
    QString complexType =
        complexObject[ComplexMissionItem::jsonComplexItemTypeKey].toString();
    if (itemType != VisualMissionItem::jsonTypeComplexItemValue ||
        complexType != jsonComplexItemTypeValue) {
      errorString = tr("%1 does not support loading this complex mission item "
                       "type: %2:%3")
                        .arg(qgcApp()->applicationName())
                        .arg(itemType)
                        .arg(complexType);
      return false;
    }
189 190 191
  }

  setSequenceNumber(sequenceNumber);
192 193
  startEditing();

194 195 196 197
  // load variant index
  if (complexObject.contains(variantIndexKey) &&
      complexObject[variantIndexKey].isDouble()) {
    _variantIndex.setRawValue(complexObject[variantIndexKey].toInt());
198 199 200
  }

  // load altitude
201 202 203 204 205
  if (complexObject.contains(altitudeKey) &&
      complexObject[altitudeKey].isDouble()) {
    _altitude.setRawValue(complexObject[altitudeKey].toDouble());
  } else {
    errorString.append(tr("No altitude found in file.\n"));
206 207
    abortEditing();
    return false;
208 209 210 211 212 213 214
  }

  // load AreaData.
  if (complexObject.contains(areaDataKey) &&
      complexObject[areaDataKey].isObject()) {
    QString e;
    if (_pCurrentData->load(complexObject[areaDataKey].toObject(), e)) {
215
      if (!_pCurrentData->isCorrect(false /*don't show gui message*/)) {
216
        errorString.append(_pCurrentData->errorString());
217 218 219
        abortEditing();
        return false;
      }
220

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
    } else {
      // this is critical, proceeding is not
      // reasonable.
      errorString.append(e);
      abortEditing();
      return false;
    }
  } else {
    // this is critical, if no area data present, proceeding is not reasonable.
    errorString.append(tr("No area data found in file. Abort loading.\n"));
    abortEditing();
    return false;
  }

  // load Generators.
236 237
  if (complexObject.contains(generatorArrayKey) &&
      complexObject[generatorArrayKey].isArray()) {
238 239 240 241

    QVector<PtrGenerator> generatorList;
    QObject parent;

242
    for (const auto valueRef : complexObject[generatorArrayKey].toArray()) {
243 244 245 246 247 248 249
      const auto jsonGen = valueRef.toObject();

      if (jsonGen.contains(routing::GeneratorBase::typeKey) &&
          jsonGen[routing::GeneratorBase::typeKey].isString()) {
        QString e;

        // create generator
250
        auto gen = pGeneratorFactory->create(jsonGen, e, &parent /*parent*/);
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268

        if (gen != nullptr) {
          // remove generators of same type and insert this generator.
          for (int i = 0; i < _generatorList.size();) {
            auto otherGen = generator(i);
            if (gen->type() == otherGen->type()) {
              removeGenerator(i);
            } else {
              ++i;
            }
          }
          gen->setData(this->_pAreaData);
          generatorList.append(gen);
        } else {
          // error loading generator.
          errorString.append(
              tr("Error loading generator of type ") +
              jsonGen[routing::GeneratorBase::typeKey].toString() + ".\n");
269
          if (!pGeneratorFactory->registered(
270 271 272 273 274 275 276 277 278 279
                  jsonGen[routing::GeneratorBase::typeKey].toString())) {
            errorString.append(tr("This type is unknown.\n"));
            qCritical()
                << "MeasurementComplexItem::load(): generator of type :"
                << jsonGen[routing::GeneratorBase::typeKey]
                << " not registered with the GeneratorFactory. This can either "
                   "mean that the file contains a invalid entry or "
                   "that the generator was not registered. In the latter case "
                   "use the REGISTER_GENERATOR() for registration";
          }
280 281
          abortEditing();
          return false;
282 283 284
        }

      } else {
285 286 287
        errorString.append(tr("Can not determine type of generator.\n"));
        abortEditing();
        return false;
288 289 290 291 292
      }

      // insert generators
      for (const auto gen : generatorList) {
        gen->setParent(this);
293
        addGenerator(gen);
294 295 296 297 298
      }
    }
  } else {
    errorString.append(
        tr("No generators found in file. Leaving generators unchanged.\n"));
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
    abortEditing();
    return false;
  }

  // load generator index
  bool indexLoaded = false;
  if (complexObject.contains(generatorIndexKey) &&
      complexObject[generatorIndexKey].isDouble()) {
    int index = complexObject[generatorIndexKey].toDouble();
    if (index >= 0 && index < _generatorList.size()) {
      indexLoaded = true;
      switchToGenerator(index);
    }
  }
  if (!indexLoaded) {
    switchToGenerator(0);
315 316 317
  }

  // load Route Variants
318 319
  bool variantsSuccess = true;
  QVector<Variant> variantVector;
320 321
  if (complexObject.contains(variantArrayKey) &&
      complexObject[variantArrayKey].isArray()) {
322 323

    // load variants to variantVector for further processing.
324
    for (const auto valueRef : complexObject[variantArrayKey].toArray()) {
325
      if (valueRef.isArray()) {
326
        const auto jsonVariant = valueRef.toArray();
327 328
        Variant variant;
        QString e;
329
        if (JsonHelper::loadGeoCoordinateArray(jsonVariant, false, variant,
330 331 332 333
                                               e)) {
          if (variant.size() > 0) {
            variantVector.append(std::move(variant));
          } else {
334 335 336
            qCDebug(MeasurementComplexItemLog)
                << "Empty route variant skipped.\n"
                << valueRef.type();
337 338
          }
        } else {
339 340 341
          qCDebug(MeasurementComplexItemLog)
              << "Error loading route variant: " << e;
          variantsSuccess = false;
342 343 344 345
        }
      } else {
        qCDebug(MeasurementComplexItemLog)
            << "json variant is not an array but of type: " << valueRef.type();
346
        variantsSuccess = false;
347 348 349
      }
    }

350 351 352 353 354 355 356 357 358 359
    // Check if variantVector and variants are non empty
    if (variantVector.size() == 0) {
      variantsSuccess = false;
    }
    for (const auto &var : variantVector) {
      if (var.size() == 0) {
        variantsSuccess = false;
      }
    }

360
    // Check if variants are covered by safe area.
361
    if (variantsSuccess) {
362 363 364 365
      auto safeAreaArray = _pCurrentData->safeAreaArray();

      if (safeAreaArray.size() > 0 && safeAreaArray.at(0) != nullptr) {
        auto safeArea = safeAreaArray[0];
366 367
        QGeoCoordinate origin =
            safeArea->pathModel().value<QGCQGeoCoordinate *>(0)->coordinate();
368 369
        geometry::FPolygon safeAreaENU;
        geometry::areaToEnu(origin, safeArea->coordinateList(), safeAreaENU);
370
        for (const auto &variant : variantVector) {
371
          geometry::FLineString varENU;
372
          for (const auto &vertex : variant) {
373 374
            geometry::FPoint vertexENU;
            geometry::toENU(origin, vertex.value<QGeoCoordinate>(), vertexENU);
375 376 377 378 379 380 381 382 383 384 385 386
            varENU.push_back(vertexENU);
          }

          if (!bg::covered_by(varENU, safeAreaENU)) {
            variantsSuccess = false;
            break;
          }
        }
      } else {
        variantsSuccess = false;
      }
    }
387
  } else {
388
    variantsSuccess = false;
389
  }
390

391
  if (variantsSuccess) {
392
    _variantVector.swap(variantVector);
393 394 395 396 397 398 399

    // load variant names
    bool variantNamesLoaded = true;
    if (complexObject.contains(variantNamesKey) &&
        complexObject[variantNamesKey].isArray()) {
      QStringList variantNames;

400
      for (const auto &name : complexObject[variantNamesKey].toArray()) {
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
        if (name.isString()) {
          variantNames.append(name.toString());
        } else {
          variantNamesLoaded = false;
          break;
        }
      }

      if (variantNames.size() != _variantVector.size()) {
        variantNamesLoaded = false;
      }

      if (variantNamesLoaded) {
        _variantNames.swap(variantNames);
        emit variantNamesChanged();
      }
    } else {
      qCWarning(MeasurementComplexItemLog)
          << "Not able to load variant names. variantNamesKey missing or wrong "
             "type";
      if (complexObject.contains(variantNamesKey)) {
        qCWarning(MeasurementComplexItemLog)
            << "variantNamesKey type: "
            << complexObject[variantNamesKey].type();
      }
    }

    // create std. variant names if loading failed
    if (!variantNamesLoaded) {
      qCWarning(MeasurementComplexItemLog) << "Creating std. 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();
    }

439 440
    stopEditing(
        false /*doUpdate*/); // does noting if editing was already stopped
441 442 443 444

    _changeVariantIndex();
  } else {
    stopEditing(); // stop editing and trigger update
445
  }
446

447
  return true;
448 449
}

450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
double
MeasurementComplexItem::greatestDistanceTo(const QGeoCoordinate &other) const {
  double d = -1 * std::numeric_limits<double>::infinity();
  if (other.isValid()) {
    if (this->_route.size() > 0) {
      std::for_each(this->_route.cbegin(), this->_route.cend(),
                    [&d, &other](const QVariant &variant) {
                      auto vertex = variant.value<QGeoCoordinate>();
                      d = std::max(d, vertex.distanceTo(other));
                    });
    }
  } else {
    qCDebug(MeasurementComplexItemLog)
        << "greatestDistanceTo(): invalid QGeoCoordinate: " << other;
  }
  return d;
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
468 469 470 471 472 473 474 475
bool MeasurementComplexItem::dirty() const { return _dirty; }

bool MeasurementComplexItem::isSimpleItem() const { return false; }

bool MeasurementComplexItem::isStandaloneCoordinate() const { return false; }

QString MeasurementComplexItem::mapVisualQML() const {
  return QStringLiteral("MeasurementItemMapVisual.qml");
476 477
}

478
void MeasurementComplexItem::save(QJsonArray &planItems) {
479
  if (idle()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
480
    QJsonObject saveObject;
481

Valentin Platzgummer's avatar
Valentin Platzgummer committed
482 483 484 485 486
    saveObject[JsonHelper::jsonVersionKey] = 1;
    saveObject[VisualMissionItem::jsonTypeKey] =
        VisualMissionItem::jsonTypeComplexItemValue;
    saveObject[ComplexMissionItem::jsonComplexItemTypeKey] =
        jsonComplexItemTypeValue;
487

488
    // Variant and altitude.
489
    saveObject[variantIndexKey] = double(_variantIndex.rawValue().toUInt());
490
    saveObject[altitudeKey] = double(_altitude.rawValue().toUInt());
491

492
    // Variant names.
493
    QJsonArray jsonVariantNames;
494 495 496
    for (auto const &name : _variantNames) {
      jsonVariantNames.append(name);
    }
497
    saveObject[variantNamesKey] = jsonVariantNames;
498 499

    // AreaData.
500 501 502 503 504 505
    QJsonObject jsonAreaData;
    if (!_pAreaData->save(jsonAreaData)) {
      qCDebug(MeasurementComplexItemLog)
          << "save(): not able to save area data";
      return;
    }
506
    saveObject[areaDataKey] = jsonAreaData;
507 508

    // Generators.
509 510 511
    QJsonArray generatorArray;
    for (int i = 0; i < _generatorList.size(); ++i) {
      auto const gen = _generatorList[i];
512 513
      QJsonObject obj;
      if (!gen->save(obj)) {
514
        qCDebug(MeasurementComplexItemLog)
515
            << "save(): not able to save generator with name: " << gen->name();
516 517
        return;
      } else {
518
        generatorArray.append(obj);
519 520
      }
    }
521
    saveObject[generatorArrayKey] = generatorArray;
522

523
    // generator index
524
    saveObject[generatorIndexKey] = generatorIndex();
525

526
    // Route Variants
527
    QJsonArray variantsArray;
528 529
    for (auto const &route : _variantVector) {
      QJsonValue variant;
530
      if (route.size() > 0) {
531
        JsonHelper::saveGeoCoordinateArray(route, false, variant);
532
      } else {
533
        JsonHelper::saveGeoCoordinateArray(_route, false, variant);
534 535 536
      }
      variantsArray.append(variant);
    }
537
    saveObject[variantArrayKey] = variantsArray;
538

Valentin Platzgummer's avatar
Valentin Platzgummer committed
539
    planItems.append(saveObject);
540 541
  } else {
    qCDebug(MeasurementComplexItemLog) << "save(): called while not idle.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
542
  }
543 544
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
double MeasurementComplexItem::amslEntryAlt() const {
  return _altitude.rawValue().toDouble() +
         this->_masterController->missionController()
             ->plannedHomePosition()
             .altitude();
}

double MeasurementComplexItem::amslExitAlt() const { return amslEntryAlt(); }

double MeasurementComplexItem::minAMSLAltitude() const {
  return amslEntryAlt();
}

double MeasurementComplexItem::maxAMSLAltitude() const {
  return amslEntryAlt();
}

562 563 564 565 566 567 568 569 570 571 572 573
QString MeasurementComplexItem::commandDescription() const {
  return QStringLiteral("Measurement");
}

QString MeasurementComplexItem::commandName() const {
  return QStringLiteral("Measurement");
}

QString MeasurementComplexItem::abbreviation() const {
  return QStringLiteral("M");
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
574 575 576 577 578 579 580
bool MeasurementComplexItem::specifiesCoordinate() const {
  return _route.count() > 0;
}

bool MeasurementComplexItem::specifiesAltitudeOnly() const { return false; }

QGeoCoordinate MeasurementComplexItem::coordinate() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
581 582
  return this->_route.size() > 0 ? _route.first().value<QGeoCoordinate>()
                                 : QGeoCoordinate();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
583 584 585
}

QGeoCoordinate MeasurementComplexItem::exitCoordinate() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
586 587
  return this->_route.size() > 0 ? _route.last().value<QGeoCoordinate>()
                                 : QGeoCoordinate();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
}

int MeasurementComplexItem::sequenceNumber() const { return _sequenceNumber; }

double MeasurementComplexItem::specifiedFlightSpeed() {
  return std::numeric_limits<double>::quiet_NaN();
}

double MeasurementComplexItem::specifiedGimbalYaw() {
  return std::numeric_limits<double>::quiet_NaN();
}

double MeasurementComplexItem::specifiedGimbalPitch() {
  return std::numeric_limits<double>::quiet_NaN();
}

void MeasurementComplexItem::appendMissionItems(QList<MissionItem *> &items,
                                                QObject *missionItemParent) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
606

607
  if (idle()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
    qCDebug(MeasurementComplexItemLog) << "appendMissionItems()";

    int seqNum = this->_sequenceNumber;

    MAV_FRAME mavFrame =
        followTerrain() ? MAV_FRAME_GLOBAL : MAV_FRAME_GLOBAL_RELATIVE_ALT;

    for (const auto &variant : this->_route) {
      auto vertex = variant.value<QGeoCoordinate>();
      MissionItem *item = new MissionItem(
          seqNum++, MAV_CMD_NAV_WAYPOINT, mavFrame,
          0,   // hold time
          0.0, // No acceptance radius specified
          0.0, // Pass through waypoint
          std::numeric_limits<double>::quiet_NaN(), // Yaw unchanged
          vertex.latitude(), vertex.longitude(), vertex.altitude(),
          true,  // autoContinue
          false, // isCurrentItem
          missionItemParent);
      items.append(item);
    }
  } else {
    qCDebug(MeasurementComplexItemLog)
        << "appendMissionItems(): called while not ready().";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
632 633 634 635 636 637
  }
}

void MeasurementComplexItem::setMissionFlightStatus(
    const MissionController::MissionFlightStatus_t &missionFlightStatus) {
  ComplexMissionItem::setMissionFlightStatus(missionFlightStatus);
638 639
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
640
void MeasurementComplexItem::applyNewAltitude(double newAltitude) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
641
  this->_altitude.setRawValue(newAltitude);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
642 643 644 645 646
}

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

bool MeasurementComplexItem::_setGenerator(PtrGenerator newG) {
647 648
  if (this->_pGenerator != newG) {
    if (this->_pGenerator != nullptr) {
649
      disconnect(this->_pGenerator, &routing::GeneratorBase::generatorChanged,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
650
                 this, &MeasurementComplexItem::_updateRoute);
651 652 653
    }

    this->_pGenerator = newG;
654 655 656 657 658 659

    if (this->_pGenerator != nullptr) {
      connect(this->_pGenerator, &routing::GeneratorBase::generatorChanged,
              this, &MeasurementComplexItem::_updateRoute);
    }

660 661
    emit generatorChanged();

Valentin Platzgummer's avatar
Valentin Platzgummer committed
662 663 664 665
    if (!editing()) {
      this->_setState(STATE::IDLE);
      _updateRoute();
    }
666 667 668 669 670 671 672

    return true;
  } else {
    return false;
  }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
673
void MeasurementComplexItem::_setState(MeasurementComplexItem::STATE state) {
674 675 676
  if (this->_state != state) {
    auto oldState = this->_state;
    this->_state = state;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
677

678 679 680
    if (_calculating(oldState) != _calculating(state)) {
      emit calculatingChanged();
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
681 682 683 684

    if (_editing(oldState) != _editing(state)) {
      emit editingChanged();
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
685

686 687
    if (_idle(oldState) != _idle(state)) {
      emit idleChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
688
    }
689 690 691
  }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
692
bool MeasurementComplexItem::_calculating(MeasurementComplexItem::STATE state) {
693 694 695
  return state == STATE::ROUTING;
}

696 697 698 699
bool MeasurementComplexItem::_editing(MeasurementComplexItem::STATE state) {
  return state == STATE::EDITING;
}

700
bool MeasurementComplexItem::_idle(MeasurementComplexItem::STATE state) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
701 702 703
  return state == STATE::IDLE;
}

704
void MeasurementComplexItem::_updateFlightpathSegments() {
705 706
  bool hasCollisionOld = _cTerrainCollisionSegments > 0;
  _cTerrainCollisionSegments = 0;
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731

  _flightPathSegments.beginReset();
  _flightPathSegments.clearAndDeleteContents();

  if (_route.size() > 2) {
    bool ok = false;
    double alt = _altitude.rawValue().toDouble(&ok) +
                 _masterController->missionController()
                     ->plannedHomePosition()
                     .altitude();
    if (ok) {
      auto prev = _route.cbegin();
      for (auto next = _route.cbegin() + 1; next != _route.end(); ++next) {
        auto v1 = prev->value<QGeoCoordinate>();
        auto v2 = next->value<QGeoCoordinate>();
        _appendFlightPathSegment(v1, alt, v2, alt);
        prev = next;
      }
    } else {
      qCCritical(MeasurementComplexItemLog) << "_altitude fact not ok.";
    }
  }

  _flightPathSegments.endReset();

732 733 734 735 736
  // Terrain collsision.
  bool hasCollision = _cTerrainCollisionSegments > 0;
  if (hasCollisionOld != hasCollision) {
    emit terrainCollisionChanged(hasCollision);
  }
737 738 739 740 741
  auto measurementAreaArray = _pAreaData->measurementAreaArray();
  for (auto area : measurementAreaArray) {
    if (area != nullptr) {
      area->setShowAltColor(hasCollision);
    }
742 743 744 745
  }

  _masterController->missionController()->recalcTerrainProfile();
}
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769

void MeasurementComplexItem::_onAltitudeChanged() {
  // Apply altitude to variants and route.
  auto alt = _altitude.rawValue().toDouble();
  for (auto &var : _variantVector) {

    Variant *pVar;
    if (var.size() > 0) {
      pVar = &var;
    } else {
      pVar = &_route;
    }

    for (auto &qVariant : *pVar) {
      auto vertex = qVariant.value<QGeoCoordinate>();
      vertex.setAltitude(alt);
      qVariant = QVariant::fromValue(vertex);
    }
  }

  if (_route.size() > 0) {
    emit routeChanged();
  }
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
770 771
void MeasurementComplexItem::_setAreaData(
    MeasurementComplexItem::PtrAreaData data) {
772 773
  if (_pCurrentData != data) {
    _pCurrentData = data;
774 775 776 777
    emit areaDataChanged();
  }
}

778 779 780 781
void MeasurementComplexItem::_updateRoute() {
  if (!editing()) {
    // Reset data.
    this->_route.clear();
782
    emit routeChanged();
783 784 785 786 787 788
    this->_variantVector.clear();
    this->_variantNames.clear();
    emit variantNamesChanged();

    if (this->_pAreaData->isCorrect()) {

789 790 791 792 793 794 795 796 797 798 799 800 801 802
      auto measurmentAreaArray = _pAreaData->measurementAreaArray();
      bool measurementComplete = true;
      for (const auto &area : measurmentAreaArray) {
        if (!area->measurementCompleted()) {
          measurementComplete = false;
        }
      }

      if (measurementComplete) {
        qCDebug(MeasurementComplexItemLog)
            << "_updateWorker(): measurement complete!";
        return;
      }

803 804 805 806
      // Prepare data.
      auto origin = this->_pAreaData->origin();
      origin.setAltitude(0);
      if (!origin.isValid()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
807
        qCDebug(MeasurementComplexItemLog)
808 809
            << "_updateWorker(): origin invalid." << origin;
        return;
810 811
      }

812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
      // Convert safe area.
      auto serviceArea =
          getGeoArea<const SafeArea *>(*this->_pAreaData->areaList());
      auto geoSafeArea = serviceArea->coordinateList();
      if (!(geoSafeArea.size() >= 3)) {
        qCDebug(MeasurementComplexItemLog)
            << "_updateWorker(): safe area invalid." << geoSafeArea;
        return;
      }
      for (auto &v : geoSafeArea) {
        if (v.isValid()) {
          v.setAltitude(0);
        } else {
          qCDebug(MeasurementComplexItemLog)
              << "_updateWorker(): safe area contains invalid coordinate."
              << geoSafeArea;
          return;
        }
      }
831

832 833 834 835
      // Routing par.
      RoutingParameter par;
      par.numSolutions = 5;
      auto &safeAreaENU = par.safeArea;
836
      geometry::areaToEnu(origin, geoSafeArea, safeAreaENU);
837 838 839

      // Create generator.
      if (this->_pGenerator != nullptr) {
840
        routing::GeneratorBase::Work g; // Transect generator.
841 842 843 844 845 846 847 848 849 850
        if (this->_pGenerator->get(g)) {
          // Start/Restart routing worker.
          this->_pWorker->route(par, g);
          _setState(STATE::ROUTING);
          return;
        } else {
          qCDebug(MeasurementComplexItemLog)
              << "_updateWorker(): generator creation failed.";
          return;
        }
851
      } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
852
        qCDebug(MeasurementComplexItemLog)
853 854 855 856
            << "_updateWorker(): pGenerator == nullptr, number of registered "
               "generators: "
            << this->_generatorList.size();
        return;
857 858
      }
    } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
859
      qCDebug(MeasurementComplexItemLog)
860
          << "_updateWorker(): area data invalid.";
861
      return;
862 863 864 865
    }
  }
}

866
void MeasurementComplexItem::_changeVariantIndex() {
867
  if (idle()) {
868
    auto variant = this->_variantIndex.rawValue().toUInt();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
869

870
    // Find old variant. Old variant corresponts with empty list.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
871 872 873 874 875 876 877
    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) {
      const auto &variantCoordinates = this->_variantVector.at(i);
      if (variantCoordinates.isEmpty()) {
        old_variant = i;
        break;
      }
878 879
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
880 881 882 883 884 885 886
    // Swap route.
    if (variant != old_variant) {
      // Swap in new variant.
      if (variant < std::size_t(this->_variantVector.size())) {
        if (old_variant != std::numeric_limits<std::size_t>::max()) {
          // this->_route containes a route, swap it back to
          // this->_solutionVector
887 888
          auto &oldRoute = this->_variantVector[old_variant];
          oldRoute.swap(this->_route);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
889
        }
890 891
        auto &newRoute = this->_variantVector[variant];
        this->_route.swap(newRoute);
892
        emit routeChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
893 894 895 896
      } else { // error
        qCDebug(MeasurementComplexItemLog)
            << "Variant out of bounds (variant =" << variant << ").";
        qCDebug(MeasurementComplexItemLog) << "Resetting variant to zero.";
897

898 899 900 901 902
        disconnect(&this->_variantIndex, &Fact::rawValueChanged, this,
                   &MeasurementComplexItem::_changeVariantIndex);
        this->_variantIndex.setCookedValue(QVariant(0));
        connect(&this->_variantIndex, &Fact::rawValueChanged, this,
                &MeasurementComplexItem::_changeVariantIndex);
903

Valentin Platzgummer's avatar
Valentin Platzgummer committed
904
        if (this->_variantVector.size() > 0) {
905
          this->_changeVariantIndex();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
906
        }
907 908 909 910 911
      }
    }
  }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
912
void MeasurementComplexItem::_reverseRoute() {
913
  if (idle()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
914 915 916 917
    if (this->_route.size() > 0) {
      auto &t = this->_route;
      std::reverse(t.begin(), t.end());
    }
918
    emit routeChanged();
919 920 921
  }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
922
ComplexMissionItem::ReadyForSaveState
Valentin Platzgummer's avatar
Valentin Platzgummer committed
923
MeasurementComplexItem::readyForSaveState() const {
924
  if (idle()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
925
    return ReadyForSaveState::ReadyForSave;
926
  } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
927
    return ReadyForSaveState::NotReadyForSaveData;
928 929 930
  }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
931 932 933 934
bool MeasurementComplexItem::exitCoordinateSameAsEntry() const {
  return this->_route.size() > 0 ? this->_route.first() == this->_route.last()
                                 : false;
}
935

Valentin Platzgummer's avatar
Valentin Platzgummer committed
936 937 938 939 940 941
void MeasurementComplexItem::setDirty(bool dirty) {
  if (this->_dirty != dirty) {
    this->_dirty = dirty;
    emit dirtyChanged(this->_dirty);
  }
}
942

Valentin Platzgummer's avatar
Valentin Platzgummer committed
943 944 945 946 947 948 949 950 951 952 953 954 955
void MeasurementComplexItem::setCoordinate(const QGeoCoordinate &coordinate) {
  Q_UNUSED(coordinate);
}

void MeasurementComplexItem::setSequenceNumber(int sequenceNumber) {
  if (this->_sequenceNumber != sequenceNumber) {
    this->_sequenceNumber = sequenceNumber;
    emit sequenceNumberChanged(this->_sequenceNumber);
  }
}

QString MeasurementComplexItem::patternName() const { return name; }

956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973
double MeasurementComplexItem::complexDistance() const {
  double d = 0;
  if (this->_route.size() > 1) {
    auto vertex = _route.first().value<QGeoCoordinate>();
    std::for_each(this->_route.cbegin() + 1, this->_route.cend(),
                  [&vertex, &d](const QVariant &variant) {
                    auto otherVertex = variant.value<QGeoCoordinate>();
                    d += vertex.distanceTo(otherVertex);
                    vertex = otherVertex;
                  });
  }
  return d;
}

int MeasurementComplexItem::lastSequenceNumber() const {
  return _sequenceNumber + std::max(0, this->_route.size() - 1);
}

974
bool MeasurementComplexItem::addGenerator(routing::GeneratorBase *g) {
975

976
  if (g == nullptr) {
977
    qCDebug(MeasurementComplexItemLog) << "addGenerator(): empty generator.";
978
    Q_ASSERT(g != nullptr);
979 980 981
    return false;
  }

982 983 984 985 986 987 988
  for (const auto &otherGenerator : _generatorList) {
    if (otherGenerator->name() == g->name()) {
      qCDebug(MeasurementComplexItemLog)
          << "addGenerator(): generator with name " << g->name()
          << " already added.";
      Q_ASSERT(otherGenerator->name() == g->name());
      return false;
989
    }
990
  }
991

992 993 994
  this->_generatorList.push_back(g);
  if (this->_generatorList.size() == 1) {
    _setGenerator(g);
995
  }
996 997 998

  emit generatorListChanged();
  return true;
999 1000
}

1001
bool MeasurementComplexItem::removeGenerator(const QString &name) {
1002 1003 1004 1005 1006
  return removeGenerator(generatorIndex(name));
}

bool MeasurementComplexItem::removeGenerator(int index) {
  if (index >= 0 && index < this->_generatorList.size()) {
1007 1008 1009 1010
    // Is this the current generator?
    const auto &g = this->_generatorList.at(index);
    if (g == this->_pGenerator) {
      if (index > 0) {
1011
        _setGenerator(this->_generatorList.at(index - 1));
1012 1013
      } else if (index + 1 < _generatorList.size()) {
        _setGenerator(this->_generatorList.at(index + 1));
1014
      } else {
1015
        _setGenerator(nullptr);
1016 1017 1018
      }
    }

1019
    auto gen = this->_generatorList.takeAt(index);
1020

1021 1022 1023 1024
    // Should the generator be deleted?
    if (gen->parent() == this || gen->parent() == nullptr) {
      gen->deleteLater();
    }
1025

1026 1027
    emit generatorListChanged();
    return true;
1028
  } else {
1029 1030 1031 1032
    qCDebug(MeasurementComplexItemLog) << "removeGenerator(): index (" << index
                                       << ") out"
                                          "of bounds ( "
                                       << this->_generatorList.size() << " ).";
1033 1034 1035 1036
    return false;
  }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1037
bool MeasurementComplexItem::switchToGenerator(const QString &name) {
1038
  return switchToGenerator(generatorIndex(name));
1039 1040
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1041
bool MeasurementComplexItem::switchToGenerator(int index) {
1042
  if (index >= 0 && index < _generatorList.size()) {
1043
    _setGenerator(this->_generatorList.at(index));
1044 1045
    return true;
  } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1046
    qCDebug(MeasurementComplexItemLog)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1047
        << "switchToGenerator(): index (" << index
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1048 1049
        << ") out"
           "of bounds ( "
1050
        << this->_generatorList.size() << " ).";
1051 1052 1053 1054
    return false;
  }
}

1055 1056 1057 1058
void MeasurementComplexItem::resetGenerators() {
  while (_generatorList.size() > 0) {
    removeGenerator(0);
  }
1059

1060 1061
  auto lg =
      pGeneratorFactory->create(routing::LinearGenerator::typeString, this);
1062 1063 1064
  lg->setData(this->_pAreaData);
  addGenerator(lg);

1065 1066
  auto cg =
      pGeneratorFactory->create(routing::CircularGenerator::typeString, this);
1067 1068 1069 1070 1071 1072 1073
  cg->setData(this->_pAreaData);
  addGenerator(cg);
}

QList<MeasurementComplexItem::PtrGenerator>
MeasurementComplexItem::generatorList() const {
  return _generatorList;
1074 1075
}

1076
QStringList MeasurementComplexItem::generatorNameList() const {
1077 1078 1079 1080 1081
  QStringList list;
  for (const auto gen : _generatorList) {
    list.append(gen->name());
  }
  return list;
1082 1083
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1084 1085 1086
routing::GeneratorBase *MeasurementComplexItem::generator() {
  return _pGenerator;
}
1087

1088 1089 1090 1091 1092 1093
const routing::GeneratorBase *MeasurementComplexItem::generator() const {
  return _pGenerator;
}

const routing::GeneratorBase *
MeasurementComplexItem::generator(int index) const {
1094
  if (index >= 0 && index < _generatorList.size()) {
1095 1096 1097 1098 1099 1100 1101
    return _generatorList[index];
  } else {
    return nullptr;
  }
}

routing::GeneratorBase *MeasurementComplexItem::generator(int index) {
1102
  if (index >= 0 && index < _generatorList.size()) {
1103 1104 1105 1106 1107 1108 1109
    return _generatorList[index];
  } else {
    return nullptr;
  }
}

int MeasurementComplexItem::generatorIndex() const {
1110 1111 1112
  return this->_generatorList.indexOf(this->_pGenerator);
}

1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
int MeasurementComplexItem::generatorIndex(const QString &name) {
  int index = -1;
  for (int i = 0; i < _generatorList.size(); ++i) {
    const auto gen = _generatorList[i];
    if (gen->name() == name) {
      index = i;
      break;
    }
  }
  return index;
}

1125 1126
void MeasurementComplexItem::startEditing() {
  if (!editing()) {
1127 1128
    *_pEditorData = *_pAreaData;
    _setAreaData(_pEditorData);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1129
    _setState(STATE::EDITING);
1130 1131 1132
  }
}

1133
bool MeasurementComplexItem::stopEditing(bool doUpdate) {
1134
  if (editing()) {
1135
    bool isDifferent = *_pEditorData != *_pAreaData;
1136 1137
    bool correct = _pEditorData->isCorrect();
    if (correct) {
1138
      *_pAreaData = *_pEditorData;
1139
    }
1140
    _setAreaData(_pAreaData);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1141
    _setState(STATE::IDLE);
1142 1143 1144
    bool updated = false;
    if (doUpdate && correct && isDifferent) {
      updated = true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1145
      _updateRoute();
1146
    }
1147

1148
    return updated;
1149
  }
1150
  return false;
1151 1152
}

1153 1154 1155 1156 1157 1158 1159
void MeasurementComplexItem::abortEditing() {
  if (editing()) {
    _setAreaData(_pAreaData);
    _setState(STATE::IDLE);
  }
}

1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177
void MeasurementComplexItem::reset() {
  if (editing()) {
    *_pEditorData = *_pAreaData;
  }
}

bool MeasurementComplexItem::initialize(const QGeoCoordinate &bottomLeft,
                                        const QGeoCoordinate &topRight) {
  bool r1 = _pAreaData->initialize(bottomLeft, topRight);
  bool r2 = _pEditorData->initialize(bottomLeft, topRight);

  return r1 && r2;
}

bool MeasurementComplexItem::initialized() {
  return _pCurrentData->initialized();
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1178
void MeasurementComplexItem::_storeRoutingData(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1179
    MeasurementComplexItem::PtrRoutingData pRoute) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1180 1181
  if (this->_state == STATE::ROUTING) {
    // Store solutions.
1182
    auto ori = this->_pAreaData->origin();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1183 1184
    ori.setAltitude(0);
    QVector<Variant> variantVector;
1185
    const std::size_t nSolutions = pRoute->solutionVector.size();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1186 1187 1188 1189 1190 1191 1192

    for (std::size_t j = 0; j < nSolutions; ++j) {
      Variant var;
      const auto &solution = pRoute->solutionVector.at(j);
      if (solution.size() > 0) {
        const auto &route = solution.at(0);
        const auto &path = route.path;
1193 1194 1195 1196 1197

        // Convert to geo coordinates.

        for (const auto &vertex : path) {
          QGeoCoordinate c;
1198
          geometry::fromENU(ori, vertex, c);
1199
          var.append(QVariant::fromValue(c));
1200 1201
        }
      } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1202
        qCDebug(MeasurementComplexItemLog)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1203
            << "_setTransects(): solution.size() == 0";
1204 1205
      }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1206 1207 1208
      if (var.size() > 0) {
        variantVector.push_back(std::move(var));
      }
1209 1210
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1211 1212 1213 1214
    // Assign routes if no error occured.
    if (variantVector.size() > 0) {
      // Swap first route to _route.
      this->_variantVector.swap(variantVector);
1215

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1216 1217 1218 1219 1220 1221 1222
      // 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();
1223

1224
      // Set variant to 0.
1225 1226 1227 1228 1229
      disconnect(&this->_variantIndex, &Fact::rawValueChanged, this,
                 &MeasurementComplexItem::_changeVariantIndex);
      this->_variantIndex.setCookedValue(QVariant(0));
      connect(&this->_variantIndex, &Fact::rawValueChanged, this,
              &MeasurementComplexItem::_changeVariantIndex);
1230

1231
      // Select first variant as route.
1232
      this->_route.swap(this->_variantVector.first());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1233
      emit routeChanged();
1234 1235

      this->_setState(STATE::IDLE);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1236 1237 1238 1239 1240
    } else {
      qCDebug(MeasurementComplexItemLog)
          << "_setTransects(): failed, variantVector empty.";
      this->_setState(STATE::IDLE);
    }
1241 1242 1243
  }
}

1244
Fact *MeasurementComplexItem::variantIndex() { return &_variantIndex; }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1245 1246

Fact *MeasurementComplexItem::altitude() { return &this->_altitude; }
1247

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1248
bool MeasurementComplexItem::calculating() const {
1249 1250 1251
  return this->_calculating(this->_state);
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1252 1253
bool MeasurementComplexItem::editing() const { return _editing(this->_state); }

1254
bool MeasurementComplexItem::idle() const { return _idle(this->_state); }
1255

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1256
bool MeasurementComplexItem::followTerrain() const { return _followTerrain; }