SnakeDataManager.cc 14.5 KB
Newer Older
1 2 3 4 5 6 7 8
#include "SnakeDataManager.h"

#include <QGeoCoordinate>
#include <QMutexLocker>

#include "QGCApplication.h"
#include "QGCToolbox.h"
#include "SettingsFact.h"
9 10
#include "SettingsManager.h"
#include "WimaSettings.h"
11

Valentin Platzgummer's avatar
Valentin Platzgummer committed
12
#include <iostream>
13 14
#include <memory>
#include <mutex>
15
#include <shared_mutex>
16 17 18

#include "Wima/Snake/SnakeTiles.h"
#include "Wima/Snake/SnakeTilesLocal.h"
19
#include "snake.h"
20 21 22 23 24 25

using QVariantList = QList<QVariant>;

using UniqueLock = std::unique_lock<shared_timed_mutex>;
using SharedLock = std::shared_lock<shared_timed_mutex>;

Valentin Platzgummer's avatar
Valentin Platzgummer committed
26
class SnakeDataManager::Impl {
27
public:
Valentin Platzgummer's avatar
Valentin Platzgummer committed
28 29 30 31
  struct Input {

    Input()
        : tileWidth(5 * si::meter), tileHeight(5 * si::meter),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
32 33
          minTileArea(1 * si::meter * si::meter), scenarioChanged(true),
          lineDistance(2 * si::meter), minTransectLength(1 * si::meter),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
          routeParametersChanged(true) {}

    QList<QGeoCoordinate> mArea;
    QGeoCoordinate ENUOrigin;
    QList<QGeoCoordinate> sArea;
    QList<QGeoCoordinate> corridor;
    Length tileWidth;
    Length tileHeight;
    Area minTileArea;
    std::atomic_bool scenarioChanged;

    Length lineDistance;
    Length minTransectLength;
    std::atomic_bool routeParametersChanged;

    mutable std::shared_timed_mutex mutex;
  };
Valentin Platzgummer's avatar
Valentin Platzgummer committed
51

Valentin Platzgummer's avatar
Valentin Platzgummer committed
52
  struct Output {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
53 54
    Output() : calcInProgress(false) {}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
55 56 57 58
    SnakeTiles tiles;
    QVariantList tileCenterPoints;
    SnakeTilesLocal tilesENU;
    QVector<QPointF> tileCenterPointsENU;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
59
    QGeoCoordinate ENUOrigin;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
60 61 62 63 64 65 66 67 68 69 70

    QVector<QGeoCoordinate> waypoints;
    QVector<QGeoCoordinate> arrivalPath;
    QVector<QGeoCoordinate> returnPath;
    QVector<QPointF> waypointsENU;
    QVector<QPointF> arrivalPathENU;
    QVector<QPointF> returnPathENU;

    QString errorMessage;

    std::atomic_bool calcInProgress;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
71 72 73
    std::atomic_bool tilesUpdated;
    std::atomic_bool waypointsUpdated;
    std::atomic_bool progressUpdated;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
74

Valentin Platzgummer's avatar
Valentin Platzgummer committed
75
    QNemoProgress progress;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
76 77 78
    mutable std::shared_timed_mutex mutex;
  };

Valentin Platzgummer's avatar
Valentin Platzgummer committed
79
  Impl(SnakeDataManager *p);
80 81 82 83

  bool precondition() const;
  bool doTopicServiceSetup();

Valentin Platzgummer's avatar
Valentin Platzgummer committed
84
  // Internal data.
85 86
  snake::Scenario scenario;
  SnakeDataManager *parent;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
87

Valentin Platzgummer's avatar
Valentin Platzgummer committed
88
  // Input
Valentin Platzgummer's avatar
Valentin Platzgummer committed
89 90 91
  Input input;
  // Output
  Output output;
92 93
};

Valentin Platzgummer's avatar
Valentin Platzgummer committed
94
SnakeDataManager::Impl::Impl(SnakeDataManager *p) : parent(p) {}
95

Valentin Platzgummer's avatar
Valentin Platzgummer committed
96
bool SnakeDataManager::Impl::precondition() const { return true; }
97

98 99 100 101 102
template <class Callable> class CommandRAII {
public:
  CommandRAII(Callable &fun) : _fun(fun) {}

  ~CommandRAII() { _fun(); }
103

104 105 106 107 108
private:
  Callable _fun;
};

SnakeDataManager::SnakeDataManager(QObject *parent)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
109
    : QThread(parent), pImpl(std::make_unique<Impl>(this)) {}
110 111 112

void SnakeDataManager::setMeasurementArea(
    const QList<QGeoCoordinate> &measurementArea) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
113
  this->pImpl->input.scenarioChanged = true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
114
  UniqueLock lk(this->pImpl->input.mutex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
115
  this->pImpl->input.mArea = measurementArea;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
116
  for (auto &vertex : this->pImpl->input.mArea) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
117 118
    vertex.setAltitude(0);
  }
119 120
}

121 122
void SnakeDataManager::setServiceArea(
    const QList<QGeoCoordinate> &serviceArea) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
123
  this->pImpl->input.scenarioChanged = true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
124 125 126
  UniqueLock lk(this->pImpl->input.mutex);
  this->pImpl->input.sArea = serviceArea;
  for (auto &vertex : this->pImpl->input.sArea) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
127 128
    vertex.setAltitude(0);
  }
129 130
}

131
void SnakeDataManager::setCorridor(const QList<QGeoCoordinate> &corridor) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
132
  this->pImpl->input.scenarioChanged = true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
133 134 135
  UniqueLock lk(this->pImpl->input.mutex);
  this->pImpl->input.corridor = corridor;
  for (auto &vertex : this->pImpl->input.corridor) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
136 137
    vertex.setAltitude(0);
  }
138 139
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
140
SnakeTiles SnakeDataManager::tiles() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
141
  SharedLock lk(this->pImpl->output.mutex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
142
  return this->pImpl->output.tiles;
143 144
}

145
QVariantList SnakeDataManager::tileCenterPoints() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
146 147
  SharedLock lk(this->pImpl->output.mutex);
  return this->pImpl->output.tileCenterPoints;
148 149
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
150
QNemoProgress SnakeDataManager::progress() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
151
  SharedLock lk(this->pImpl->output.mutex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
152
  return this->pImpl->output.progress;
153 154
}

155
bool SnakeDataManager::calcInProgress() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
156
  return this->pImpl->output.calcInProgress.load();
157 158
}

159
QString SnakeDataManager::errorMessage() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
160 161
  SharedLock lk(this->pImpl->output.mutex);
  return this->pImpl->output.errorMessage;
162 163
}

164
bool SnakeDataManager::success() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
165 166
  SharedLock lk(this->pImpl->output.mutex);
  return this->pImpl->output.errorMessage.isEmpty() ? true : false;
167 168
}

169
QVector<QGeoCoordinate> SnakeDataManager::waypoints() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
170 171
  SharedLock lk(this->pImpl->output.mutex);
  return this->pImpl->output.waypoints;
172 173
}

174
QVector<QGeoCoordinate> SnakeDataManager::arrivalPath() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
175 176
  SharedLock lk(this->pImpl->output.mutex);
  return this->pImpl->output.arrivalPath;
177 178
}

179
QVector<QGeoCoordinate> SnakeDataManager::returnPath() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
180 181
  SharedLock lk(this->pImpl->output.mutex);
  return this->pImpl->output.returnPath;
182 183
}

184
Length SnakeDataManager::lineDistance() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
185 186
  SharedLock lk(this->pImpl->input.mutex);
  return this->pImpl->input.lineDistance;
187 188
}

189
void SnakeDataManager::setLineDistance(Length lineDistance) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
190 191 192
  UniqueLock lk(this->pImpl->input.mutex);
  this->pImpl->input.routeParametersChanged = true;
  this->pImpl->input.lineDistance = lineDistance;
193 194
}

195
Area SnakeDataManager::minTileArea() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
196
  SharedLock lk(this->pImpl->input.mutex);
197
  return this->pImpl->scenario.minTileArea();
198 199
}

200
void SnakeDataManager::setMinTileArea(Area minTileArea) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
201 202
  this->pImpl->input.scenarioChanged = true;
  UniqueLock lk(this->pImpl->input.mutex);
203
  this->pImpl->scenario.setMinTileArea(minTileArea);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
204 205
}

206
Length SnakeDataManager::tileHeight() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
207
  SharedLock lk(this->pImpl->input.mutex);
208 209
  return this->pImpl->scenario.tileHeight();
}
210

211
void SnakeDataManager::setTileHeight(Length tileHeight) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
212 213
  this->pImpl->input.scenarioChanged = true;
  UniqueLock lk(this->pImpl->input.mutex);
214 215
  this->pImpl->scenario.setTileHeight(tileHeight);
}
216

217
Length SnakeDataManager::tileWidth() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
218
  SharedLock lk(this->pImpl->input.mutex);
219 220
  return this->pImpl->scenario.tileWidth();
}
221

222
void SnakeDataManager::setTileWidth(Length tileWidth) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
223 224
  UniqueLock lk(this->pImpl->input.mutex);
  this->pImpl->input.scenarioChanged = true;
225 226
  this->pImpl->scenario.setTileWidth(tileWidth);
}
227

228
void SnakeDataManager::run() {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
229 230 231 232
#ifndef NDEBUG
  auto startTime = std::chrono::high_resolution_clock::now();
#endif

Valentin Platzgummer's avatar
Valentin Platzgummer committed
233 234
  this->pImpl->output.calcInProgress.store(true);
  emit calcInProgressChanged(this->pImpl->output.calcInProgress.load());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
235 236 237 238 239 240 241 242
  auto onExit = [this, &startTime] {
#ifndef NDEBUG
    qDebug() << "SnakeDataManager::run() execution time: "
             << std::chrono::duration_cast<std::chrono::milliseconds>(
                    std::chrono::high_resolution_clock::now() - startTime)
                    .count()
             << " ms";
#endif
Valentin Platzgummer's avatar
Valentin Platzgummer committed
243 244
    this->pImpl->output.calcInProgress.store(true);
    emit calcInProgressChanged(this->pImpl->output.calcInProgress.load());
245 246 247
  };
  CommandRAII<decltype(onExit)> onExitRAII(onExit);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
248 249 250
  this->pImpl->output.tilesUpdated = false;
  this->pImpl->output.waypointsUpdated = false;
  this->pImpl->output.progressUpdated = false;
251

Valentin Platzgummer's avatar
Valentin Platzgummer committed
252 253 254
  {
    // Check preconditions.
    SharedLock lk(this->pImpl->input.mutex);
255

Valentin Platzgummer's avatar
Valentin Platzgummer committed
256 257
    if (!this->pImpl->precondition())
      return;
258

Valentin Platzgummer's avatar
Valentin Platzgummer committed
259 260 261 262
    if (this->pImpl->input.mArea.size() < 3) {
      UniqueLock uLock(this->pImpl->output.mutex);
      this->pImpl->output.errorMessage = "Measurement area invalid: size < 3.";
      return;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
263
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
264 265 266 267
    if (this->pImpl->input.sArea.size() < 3) {
      UniqueLock uLock(this->pImpl->output.mutex);
      this->pImpl->output.errorMessage = "Service area invalid: size < 3.";
      return;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
268
    }
269

Valentin Platzgummer's avatar
Valentin Platzgummer committed
270 271 272 273 274 275 276 277 278
    // Update Scenario if necessary
    if (this->pImpl->input.scenarioChanged) {
      // Set Origin
      QGeoCoordinate origin;
      {
        UniqueLock uLock(this->pImpl->output.mutex);
        this->pImpl->output.ENUOrigin = this->pImpl->input.mArea.front();
        origin = this->pImpl->output.ENUOrigin;
      }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
279

Valentin Platzgummer's avatar
Valentin Platzgummer committed
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
      // Update measurement area.
      auto &mAreaEnu = this->pImpl->scenario.measurementArea();
      auto &mArea = this->pImpl->input.mArea;
      mAreaEnu.clear();
      for (auto geoVertex : mArea) {
        snake::BoostPoint p;
        snake::toENU(origin, geoVertex, p);
        mAreaEnu.outer().push_back(p);
      }

      // Update service area.
      auto &sAreaEnu = this->pImpl->scenario.serviceArea();
      auto &sArea = this->pImpl->input.sArea;
      sAreaEnu.clear();
      for (auto geoVertex : sArea) {
        snake::BoostPoint p;
        snake::toENU(origin, geoVertex, p);
        sAreaEnu.outer().push_back(p);
      }

      // Update corridor.
      auto &corridorEnu = this->pImpl->scenario.corridor();
      auto &corridor = this->pImpl->input.corridor;
      corridor.clear();
      for (auto geoVertex : corridor) {
        snake::BoostPoint p;
        snake::toENU(origin, geoVertex, p);
        corridorEnu.outer().push_back(p);
      }

      // Update parametes.
      this->pImpl->scenario.setTileHeight(this->pImpl->input.tileHeight);
      this->pImpl->scenario.setTileWidth(this->pImpl->input.tileWidth);
      this->pImpl->scenario.setMinTileArea(this->pImpl->input.minTileArea);

      this->pImpl->input.scenarioChanged = false;
      if (!this->pImpl->scenario.update()) {
        this->pImpl->output.errorMessage =
            this->pImpl->scenario.errorString.c_str();
        return;
      }

      this->pImpl->output.tilesUpdated = true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
323
    }
324
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
325

Valentin Platzgummer's avatar
Valentin Platzgummer committed
326 327 328 329 330
  snake::flight_plan::Transects transectsRouted;
  snake::flight_plan::Route route;
  std::vector<int> progress;
  if (this->pImpl->input.scenarioChanged ||
      this->pImpl->input.routeParametersChanged) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
331
    // Sample data
Valentin Platzgummer's avatar
Valentin Platzgummer committed
332 333 334 335 336 337
    SharedLock inputLock(this->pImpl->input.mutex);
    SharedLock outputLock(this->pImpl->output.mutex);
    // Verify progress.
    size_t nTiles = this->pImpl->scenario.tiles().size();
    if (this->pImpl->output.progress.progress().size() != nTiles) {
      outputLock.unlock();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
338 339 340
      for (size_t i = 0; i < nTiles; ++i) {
        progress.push_back(0);
      }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
341
      this->pImpl->output.progressUpdated = true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
342
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
343 344 345 346 347

    const auto &scenario = this->pImpl->scenario;
    const auto lineDistance = this->pImpl->input.lineDistance;
    const auto minTransectLength = this->pImpl->input.minTransectLength;
    inputLock.unlock();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
348 349 350 351

    // Create transects.
    std::string errorString;
    snake::Angle alpha(-scenario.mAreaBoundingBox().angle * degree::degree);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
352
    std::cout << "Transects angle: " << alpha << std::endl;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
353 354 355 356 357 358 359 360
    snake::flight_plan::Transects transects;
    transects.push_back(
        bg::model::linestring<snake::BoostPoint>{scenario.homePositon()});

    bool value = snake::flight_plan::transectsFromScenario(
        lineDistance, minTransectLength, alpha, scenario, progress, transects,
        errorString);
    if (!value) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
361 362 363
      UniqueLock lk(this->pImpl->output.mutex);
      this->pImpl->output.errorMessage = errorString.c_str();
    } else if (transects.size() > 1) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
364 365 366
      value = snake::flight_plan::route(scenario.joinedArea(), transects,
                                        transectsRouted, route, errorString);
      if (!value) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
367 368 369 370
        UniqueLock lk(this->pImpl->output.mutex);
        this->pImpl->output.errorMessage = errorString.c_str();
      } else {
        this->pImpl->output.waypointsUpdated = true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
371 372
      }
    }
373 374
  }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
375 376 377
  UniqueLock lk(this->pImpl->output.mutex);
  // Store tiles.
  if (this->pImpl->output.tilesUpdated) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
378
    // Clear some output data.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
379 380 381 382
    this->pImpl->output.tiles.polygons().clear();
    this->pImpl->output.tileCenterPoints.clear();
    this->pImpl->output.tilesENU.polygons().clear();
    this->pImpl->output.tileCenterPointsENU.clear();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
383
    // Convert and store scenario data.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
384
    const auto &origin = this->pImpl->output.ENUOrigin;
385 386 387 388 389 390
    const auto &tiles = this->pImpl->scenario.tiles();
    const auto &centerPoints = this->pImpl->scenario.tileCenterPoints();
    for (unsigned int i = 0; i < tiles.size(); ++i) {
      const auto &tile = tiles[i];
      SnakeTile geoTile;
      SnakeTileLocal enuTile;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
391
      for (size_t i = 0; i < tile.outer().size() - 1; ++i) {
392 393 394 395 396 397 398 399 400 401 402 403
        auto &p = tile.outer()[i];
        QPointF enuVertex(p.get<0>(), p.get<1>());
        QGeoCoordinate geoVertex;
        snake::fromENU(origin, p, geoVertex);
        enuTile.polygon().points().push_back(enuVertex);
        geoTile.push_back(geoVertex);
      }
      const auto &boostPoint = centerPoints[i];
      QPointF enuVertex(boostPoint.get<0>(), boostPoint.get<1>());
      QGeoCoordinate geoVertex;
      snake::fromENU(origin, boostPoint, geoVertex);
      geoTile.setCenter(geoVertex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
404 405 406 407 408
      this->pImpl->output.tiles.polygons().push_back(geoTile);
      this->pImpl->output.tileCenterPoints.push_back(
          QVariant::fromValue(geoVertex));
      this->pImpl->output.tilesENU.polygons().push_back(enuTile);
      this->pImpl->output.tileCenterPointsENU.push_back(enuVertex);
409 410
    }
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
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 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
  // Store progress.
  if (this->pImpl->output.progressUpdated) {
    size_t nTiles = progress.size();
    this->pImpl->output.progress.progress().clear();
    this->pImpl->output.progress.progress().reserve(nTiles);
    for (const auto &p : progress) {
      this->pImpl->output.progress.progress().push_back(p);
    }
  }
  // Store waypoints.
  if (this->pImpl->output.waypointsUpdated) {
    // Store arrival path.
    const auto &origin = this->pImpl->output.ENUOrigin;
    const auto &firstWaypoint = transectsRouted.front().front();
    long startIdx = 0;
    for (long i = 0; i < long(route.size()); ++i) {
      const auto &boostVertex = route[i];
      if (boostVertex == firstWaypoint) {
        startIdx = i;
        break;
      }
      QPointF enuVertex{boostVertex.get<0>(), boostVertex.get<1>()};
      QGeoCoordinate geoVertex;
      snake::fromENU(origin, boostVertex, geoVertex);
      this->pImpl->output.arrivalPathENU.push_back(enuVertex);
      this->pImpl->output.arrivalPath.push_back(geoVertex);
    }
    // Store return path.
    long endIdx = 0;
    const auto &lastWaypoint = transectsRouted.back().back();
    for (long i = route.size() - 1; i >= 0; --i) {
      const auto &boostVertex = route[i];
      if (boostVertex == lastWaypoint) {
        endIdx = i;
        break;
      }
      QPointF enuVertex{boostVertex.get<0>(), boostVertex.get<1>()};
      QGeoCoordinate geoVertex;
      snake::fromENU(origin, boostVertex, geoVertex);
      this->pImpl->output.returnPathENU.push_back(enuVertex);
      this->pImpl->output.returnPath.push_back(geoVertex);
    }
    // Store waypoints.
    for (long i = startIdx; i <= endIdx; ++i) {
      const auto &boostVertex = route[i];
      QPointF enuVertex{boostVertex.get<0>(), boostVertex.get<1>()};
      QGeoCoordinate geoVertex;
      snake::fromENU(origin, boostVertex, geoVertex);
      this->pImpl->output.waypointsENU.push_back(enuVertex);
      this->pImpl->output.waypoints.push_back(geoVertex);
    }
462
  }
463
}