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)) {
174
    disableUpdates();
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
    }

182
    if (!json.contains(tileWidthName) || !json[tileWidthName].isDouble()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
183
      errorString.append(tr("Could not load tile width!\n"));
184 185
      retVal = false;
    }
186

187
    if (!json.contains(minTileAreaName) || !json[minTileAreaName].isDouble()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
188
      errorString.append(tr("Could not load minimal tile area!\n"));
189
      retVal = false;
190
    }
191

192
    if (!json.contains(showTilesName) || !json[showTilesName].isBool()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
193 194 195
      errorString.append(tr("Could not load show tiles !\n"));
      retVal = false;
    }
196 197 198 199 200 201 202 203 204 205 206 207 208

    if (retVal) {
      _tileHeight.setRawValue(json[tileHeightName].toDouble());
      _tileWidth.setRawValue(json[tileWidthName].toDouble());
      _minTileAreaPercent.setRawValue(json[minTileAreaName].toDouble());
      _showTiles.setRawValue(json[showTilesName].toBool());

      enableUpdates();
      doUpdate();
    } else {
      enableUpdates();
    }

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

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
234
  auto start = std::chrono::high_resolution_clock::now();
235
  // Check state.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
236
  if (this->_state != STATE::UPDATEING && this->_state != STATE::STOP) {
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 282
    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));
283
          }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
284
        }
285
        pData->moveToThread(th);
286

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

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

297 298
      this->_watcher.setFuture(future);
    }
299
  }
300 301 302 303 304 305
  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
306 307
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
308
void WimaMeasurementArea::deferUpdate() {
309
  if (this->_state == STATE::IDLE || this->_state == STATE::DEFERED) {
310
    qCDebug(WimaMeasurementAreaLog) << "defereUpdate(): defer update.";
311 312 313 314 315 316 317 318
    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);
319 320 321
  } else if (this->_state == STATE::UPDATEING) {
    qCDebug(WimaMeasurementAreaLog) << "defereUpdate(): restart.";
    setState(STATE::RESTARTING);
322
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
323 324
}

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

328
  if (this->_state == STATE::UPDATEING) {
329 330 331 332 333 334 335 336
    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);
337
  } else if (this->_state == STATE::RESTARTING) {
338 339
    qCDebug(WimaMeasurementAreaLog) << "storeTiles(): restart.";
    doUpdate();
340 341
  } else if (this->_state == STATE::STOP) {
    qCDebug(WimaMeasurementAreaLog) << "storeTiles(): stop.";
342 343 344 345 346 347 348
  }
  qCDebug(WimaMeasurementAreaLog)
      << "storeTiles() execution time: "
      << std::chrono::duration_cast<std::chrono::milliseconds>(
             std::chrono::high_resolution_clock::now() - start)
             .count()
      << " ms";
349 350
}

351 352 353 354 355 356 357 358 359 360 361
void WimaMeasurementArea::disableUpdates() {
  setState(STATE::IDLE);
  this->_timer.stop();
}

void WimaMeasurementArea::enableUpdates() {
  if (this->_state == STATE::STOP) {
    setState(STATE::IDLE);
  }
}

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

380 381 382 383 384 385 386 387 388 389
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();
    }
  }
}

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