WimaMeasurementArea.cc 13 KB
Newer Older
1
#include "WimaMeasurementArea.h"
2
#include "QtConcurrentRun"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
3 4 5 6
#include "SnakeTile.h"
#include "snake.h"

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

8 9
#include "QGCLoggingCategory.h"

10 11 12 13
#ifndef SNAKE_MAX_TILES
#define SNAKE_MAX_TILES 1000
#endif

14 15
QGC_LOGGING_CATEGORY(WimaMeasurementAreaLog, "WimaMeasurementAreaLog")

16 17 18 19 20 21 22 23 24 25 26 27
TileData::TileData() : tiles(this) {}

TileData::~TileData() { tiles.clearAndDeleteContents(); }

TileData &TileData::operator=(const TileData &other) {
  this->tiles.clearAndDeleteContents();
  for (std::size_t i = 0; i < std::size_t(other.tiles.count()); ++i) {
    const auto *obj = other.tiles.get(i);
    const auto *tile = qobject_cast<const SnakeTile *>(obj);
    if (tile != nullptr) {
      this->tiles.append(new SnakeTile(*tile, this));
    } else {
28
      qCWarning(WimaMeasurementAreaLog) << "TileData::operator=: nullptr";
29 30 31 32 33 34
    }
  }
  this->tileCenterPoints = other.tileCenterPoints;
  return *this;
}

35 36 37 38 39 40 41 42 43
bool TileData::operator==(const TileData &other) const {
  return this->tiles == other.tiles &&
         this->tileCenterPoints == other.tileCenterPoints;
}

bool TileData::operator!=(const TileData &other) const {
  return !this->operator==(other);
}

44 45 46 47 48 49 50 51 52 53 54 55 56
void TileData::clear() {
  this->tiles.clearAndDeleteContents();
  this->tileCenterPoints.clear();
}

size_t TileData::size() const {
  if (tiles.count() == tileCenterPoints.size()) {
    return tiles.count();
  } else {
    return 0;
  }
}

57
const char *WimaMeasurementArea::settingsGroup = "MeasurementArea";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
58 59
const char *WimaMeasurementArea::tileHeightName = "TileHeight";
const char *WimaMeasurementArea::tileWidthName = "TileWidth";
60
const char *WimaMeasurementArea::minTileAreaName = "MinTileAreaPercent";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
61
const char *WimaMeasurementArea::showTilesName = "ShowTiles";
62
const char *WimaMeasurementArea::WimaMeasurementAreaName = "Measurement Area";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
63 64

WimaMeasurementArea::WimaMeasurementArea(QObject *parent)
65 66 67 68
    : WimaArea(parent),
      _metaDataMap(FactMetaData::createMapFromJsonFile(
          QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"),
          this /* QObject parent */)),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
69 70 71 72
      _tileHeight(SettingsFact(settingsGroup, _metaDataMap[tileHeightName],
                               this /* QObject parent */)),
      _tileWidth(SettingsFact(settingsGroup, _metaDataMap[tileWidthName],
                              this /* QObject parent */)),
73 74 75
      _minTileAreaPercent(SettingsFact(settingsGroup,
                                       _metaDataMap[minTileAreaName],
                                       this /* QObject parent */)),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
76 77
      _showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesName],
                              this /* QObject parent */)),
78
      _state(STATE::IDLE) {
79
  init();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
80 81
}

82 83 84 85 86 87
WimaMeasurementArea::WimaMeasurementArea(const WimaMeasurementArea &other,
                                         QObject *parent)
    : WimaArea(other, parent),
      _metaDataMap(FactMetaData::createMapFromJsonFile(
          QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"),
          this /* QObject parent */)),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
88 89 90 91
      _tileHeight(SettingsFact(settingsGroup, _metaDataMap[tileHeightName],
                               this /* QObject parent */)),
      _tileWidth(SettingsFact(settingsGroup, _metaDataMap[tileWidthName],
                              this /* QObject parent */)),
92 93 94
      _minTileAreaPercent(SettingsFact(settingsGroup,
                                       _metaDataMap[minTileAreaName],
                                       this /* QObject parent */)),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
95 96
      _showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesName],
                              this /* QObject parent */)),
97
      _state(STATE::IDLE) {
98
  init();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
99 100
}

101 102 103 104 105
/*!
 * \overload operator=()
 *
 * Calls the inherited operator WimaArea::operator=().
 */
106 107 108 109 110 111
WimaMeasurementArea &WimaMeasurementArea::
operator=(const WimaMeasurementArea &other) {
  WimaArea::operator=(other);
  return *this;
}

112
WimaMeasurementArea::~WimaMeasurementArea() {}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
113

114
QString WimaMeasurementArea::mapVisualQML() const {
115
  return QStringLiteral("WimaMeasurementAreaMapVisual.qml");
116
}
117

118
QString WimaMeasurementArea::editorQML() const {
119
  return QStringLiteral("WimaMeasurementAreaEditor.qml");
120 121
}

122 123
Fact *WimaMeasurementArea::tileHeight() { return &_tileHeight; }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
124 125
Fact *WimaMeasurementArea::tileWidth() { return &_tileWidth; }

126
Fact *WimaMeasurementArea::minTileArea() { return &_minTileAreaPercent; }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
127 128 129

Fact *WimaMeasurementArea::showTiles() { return &_showTiles; }

130 131 132
QmlObjectListModel *WimaMeasurementArea::tiles() {
  return &this->_tileData.tiles;
}
133

134 135 136 137 138 139 140
const QVector<int> &WimaMeasurementArea::progress() const {
  return this->_progress;
}

QVector<int> WimaMeasurementArea::progressQml() const {
  return this->_progress;
}
141

142
const QmlObjectListModel *WimaMeasurementArea::tiles() const {
143 144 145 146 147 148 149 150 151
  return &this->_tileData.tiles;
}

const QVariantList &WimaMeasurementArea::tileCenterPoints() const {
  return this->_tileData.tileCenterPoints;
}

const TileData &WimaMeasurementArea::tileData() const {
  return this->_tileData;
152 153 154
}

int WimaMeasurementArea::maxTiles() const { return SNAKE_MAX_TILES; }
155

156
bool WimaMeasurementArea::ready() const { return this->_state == STATE::IDLE; }
157

158
void WimaMeasurementArea::saveToJson(QJsonObject &json) {
159 160 161 162 163 164 165 166 167 168
  if (ready()) {
    this->WimaArea::saveToJson(json);
    json[tileHeightName] = _tileHeight.rawValue().toDouble();
    json[tileWidthName] = _tileWidth.rawValue().toDouble();
    json[minTileAreaName] = _minTileAreaPercent.rawValue().toDouble();
    json[showTilesName] = _showTiles.rawValue().toBool();
    json[areaTypeName] = WimaMeasurementAreaName;
  } else {
    qCDebug(WimaMeasurementAreaLog) << "saveToJson(): not ready for saveing.";
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
169 170
}

171 172 173
bool WimaMeasurementArea::loadFromJson(const QJsonObject &json,
                                       QString &errorString) {
  if (this->WimaArea::loadFromJson(json, errorString)) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
174
    disableUpdate();
175 176
    bool retVal = true;

177
    if (!json.contains(tileHeightName) || !json[tileHeightName].isDouble()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
178
      errorString.append(tr("Could not load tile height!\n"));
179
      retVal = false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
180 181
    } else {
      _tileHeight.setRawValue(json[tileHeightName].toDouble());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
182 183
    }

184
    if (!json.contains(tileWidthName) || !json[tileWidthName].isDouble()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
185
      errorString.append(tr("Could not load tile width!\n"));
186
      retVal = false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
187 188
    } else {
      _tileWidth.setRawValue(json[tileWidthName].toDouble());
189
    }
190

191
    if (!json.contains(minTileAreaName) || !json[minTileAreaName].isDouble()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
192
      errorString.append(tr("Could not load minimal tile area!\n"));
193
      retVal = false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
194 195
    } else {
      _minTileAreaPercent.setRawValue(json[minTileAreaName].toDouble());
196
    }
197

198
    if (!json.contains(showTilesName) || !json[showTilesName].isBool()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
199 200
      errorString.append(tr("Could not load show tiles !\n"));
      retVal = false;
201
    } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
202
      _showTiles.setRawValue(json[showTilesName].toBool());
203 204
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
205 206 207
    enableUpdate();
    doUpdate();

208 209 210 211
    return retVal;
  } else {
    return false;
  }
212
}
213 214

bool WimaMeasurementArea::setProgress(const QVector<int> &p) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
215
  if (ready()) {
216 217 218
    if (p.size() == this->tiles()->count() && this->_progress != p) {
      this->_progress = p;
      emit progressChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
219
      emit progressAccepted();
220 221 222 223 224
      return true;
    }
  }
  return false;
}
225 226
//!
//! \brief WimaMeasurementArea::doUpdate
227 228
//! \pre WimaMeasurementArea::deferUpdate must be called first, don't call
//! this function directly!
Valentin Platzgummer's avatar
Valentin Platzgummer committed
229 230 231
void WimaMeasurementArea::doUpdate() {
  using namespace snake;
  using namespace boost::units;
232

Valentin Platzgummer's avatar
Valentin Platzgummer committed
233
  auto start = std::chrono::high_resolution_clock::now();
234
  // Check state.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
235
  if (this->_state != STATE::UPDATEING && this->_state != STATE::STOP) {
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
    const auto height = this->_tileHeight.rawValue().toDouble() * si::meter;
    const auto width = this->_tileWidth.rawValue().toDouble() * si::meter;
    const auto tileArea = width * height;
    const auto totalArea = this->area() * si::meter * si::meter;
    const auto estNumTiles = totalArea / tileArea;
    // Check some conditions.
    if (long(std::ceil(estNumTiles.value())) <= SNAKE_MAX_TILES &&
        this->count() >= 3 && this->isSimplePolygon()) {
      setState(STATE::UPDATEING);

      auto polygon = this->coordinateList();
      for (auto &v : polygon) {
        v.setAltitude(0);
      }
      const auto minArea =
          this->_minTileAreaPercent.rawValue().toDouble() / 100 * tileArea;
      auto *th = this->thread();
      auto future = QtConcurrent::run([polygon, th, height, width, minArea] {
        auto start = std::chrono::high_resolution_clock::now();

        DataPtr pData(new TileData());
        // Convert to ENU system.
        QGeoCoordinate origin = polygon.first();
        FPolygon polygonENU;
        areaToEnu(origin, polygon, polygonENU);
        std::vector<FPolygon> tilesENU;
        BoundingBox bbox;
        std::string errorString;
        // Generate tiles.
        if (snake::tiles(polygonENU, height, width, minArea, tilesENU, bbox,
                         errorString)) {
          // Convert to geo system.
          for (const auto &t : tilesENU) {
            auto geoTile = new SnakeTile(pData.get());
            for (const auto &v : t.outer()) {
              QGeoCoordinate geoVertex;
              fromENU(origin, v, geoVertex);
              geoTile->push_back(geoVertex);
            }
            pData->tiles.append(geoTile);
            // Calculate center.
            snake::FPoint center;
            snake::polygonCenter(t, center);
            QGeoCoordinate geoCenter;
            fromENU(origin, center, geoCenter);
            pData->tileCenterPoints.append(QVariant::fromValue(geoCenter));
282
          }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
283
        }
284
        pData->moveToThread(th);
285

286 287 288 289 290 291
        qCDebug(WimaMeasurementAreaLog)
            << "doUpdate(): update time: "
            << std::chrono::duration_cast<std::chrono::milliseconds>(
                   std::chrono::high_resolution_clock::now() - start)
                   .count()
            << " ms";
292

293 294
        return pData;
      }); // QtConcurrent::run()
295

296 297
      this->_watcher.setFuture(future);
    }
298
  }
299 300 301 302 303 304
  qCDebug(WimaMeasurementAreaLog)
      << "doUpdate(): execution time: "
      << std::chrono::duration_cast<std::chrono::milliseconds>(
             std::chrono::high_resolution_clock::now() - start)
             .count()
      << " ms";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
305 306
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
307
void WimaMeasurementArea::deferUpdate() {
308
  if (this->_state == STATE::IDLE || this->_state == STATE::DEFERED) {
309
    qCDebug(WimaMeasurementAreaLog) << "defereUpdate(): defer update.";
310 311 312 313 314 315 316 317
    if (this->_state == STATE::IDLE) {
      this->_progress.clear();
      this->_tileData.clear();
      emit this->progressChanged();
      emit this->tilesChanged();
    }
    this->setState(STATE::DEFERED);
    this->_timer.start(100);
318 319 320
  } else if (this->_state == STATE::UPDATEING) {
    qCDebug(WimaMeasurementAreaLog) << "defereUpdate(): restart.";
    setState(STATE::RESTARTING);
321
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
322 323
}

324 325
void WimaMeasurementArea::storeTiles() {
  auto start = std::chrono::high_resolution_clock::now();
326

327
  if (this->_state == STATE::UPDATEING) {
328 329 330 331 332 333 334 335
    qCDebug(WimaMeasurementAreaLog) << "storeTiles(): update.";

    this->_tileData = *this->_watcher.result();
    // This is expensive. Drawing tiles is expensive too.
    this->_progress = QVector<int>(this->_tileData.tiles.count(), 0);
    this->progressChanged();
    emit this->tilesChanged();
    setState(STATE::IDLE);
336
  } else if (this->_state == STATE::RESTARTING) {
337 338
    qCDebug(WimaMeasurementAreaLog) << "storeTiles(): restart.";
    doUpdate();
339 340
  } else if (this->_state == STATE::STOP) {
    qCDebug(WimaMeasurementAreaLog) << "storeTiles(): stop.";
341 342 343 344 345 346 347
  }
  qCDebug(WimaMeasurementAreaLog)
      << "storeTiles() execution time: "
      << std::chrono::duration_cast<std::chrono::milliseconds>(
             std::chrono::high_resolution_clock::now() - start)
             .count()
      << " ms";
348 349
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
350
void WimaMeasurementArea::disableUpdate() {
351 352 353 354
  setState(STATE::IDLE);
  this->_timer.stop();
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
355
void WimaMeasurementArea::enableUpdate() {
356 357 358 359 360
  if (this->_state == STATE::STOP) {
    setState(STATE::IDLE);
  }
}

361 362
void WimaMeasurementArea::init() {
  this->setObjectName(WimaMeasurementAreaName);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
363 364 365 366
  connect(&this->_tileHeight, &Fact::rawValueChanged, this,
          &WimaMeasurementArea::deferUpdate);
  connect(&this->_tileWidth, &Fact::rawValueChanged, this,
          &WimaMeasurementArea::deferUpdate);
367
  connect(&this->_minTileAreaPercent, &Fact::rawValueChanged, this,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
368 369 370 371 372 373
          &WimaMeasurementArea::deferUpdate);
  connect(this, &WimaArea::pathChanged, this,
          &WimaMeasurementArea::deferUpdate);
  this->_timer.setSingleShot(true);
  connect(&this->_timer, &QTimer::timeout, this,
          &WimaMeasurementArea::doUpdate);
374 375 376
  connect(&this->_watcher,
          &QFutureWatcher<std::unique_ptr<QmlObjectListModel>>::finished, this,
          &WimaMeasurementArea::storeTiles);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
377 378
}

379 380 381 382 383 384 385 386 387 388
void WimaMeasurementArea::setState(WimaMeasurementArea::STATE s) {
  if (this->_state != s) {
    auto oldState = this->_state;
    this->_state = s;
    if (s == STATE::IDLE || oldState == STATE::IDLE) {
      emit readyChanged();
    }
  }
}

389 390
/*!
 * \class WimaMeasurementArea
391 392
 * \brief Class defining the area inside which the actual drone measurements
 * are performed.
393 394 395
 *
 * \sa WimaArea, WimaController, WimaPlaner
 */