WimaMeasurementArea.cc 11.5 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 10 11
#ifndef SNAKE_MAX_TILES
#define SNAKE_MAX_TILES 1000
#endif

12
const char *WimaMeasurementArea::settingsGroup = "MeasurementArea";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
13 14 15 16 17 18
const char *WimaMeasurementArea::tileHeightName = "TileHeight";
const char *WimaMeasurementArea::tileWidthName = "TileWidth";
const char *WimaMeasurementArea::minTileAreaName = "MinTileArea";
const char *WimaMeasurementArea::transectDistanceName = "TransectDistance";
const char *WimaMeasurementArea::minTransectLengthName = "MinTransectLength";
const char *WimaMeasurementArea::showTilesName = "ShowTiles";
19
const char *WimaMeasurementArea::WimaMeasurementAreaName = "Measurement Area";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
20

21 22
void tileDeleter(QmlObjectListModel *tiles) { tiles->clearAndDeleteContents(); }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
23
WimaMeasurementArea::WimaMeasurementArea(QObject *parent)
24 25 26 27
    : WimaArea(parent),
      _metaDataMap(FactMetaData::createMapFromJsonFile(
          QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"),
          this /* QObject parent */)),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41
      _tileHeight(SettingsFact(settingsGroup, _metaDataMap[tileHeightName],
                               this /* QObject parent */)),
      _tileWidth(SettingsFact(settingsGroup, _metaDataMap[tileWidthName],
                              this /* QObject parent */)),
      _minTileArea(SettingsFact(settingsGroup, _metaDataMap[minTileAreaName],
                                this /* QObject parent */)),
      _transectDistance(SettingsFact(settingsGroup,
                                     _metaDataMap[transectDistanceName],
                                     this /* QObject parent */)),
      _minTransectLength(SettingsFact(settingsGroup,
                                      _metaDataMap[minTransectLengthName],
                                      this /* QObject parent */)),
      _showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesName],
                              this /* QObject parent */)),
42 43
      _pTiles(new QmlObjectListModel(), &tileDeleter), _calculating(false),
      _polygonValid(false) {
44
  init();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
45 46
}

47 48 49 50 51 52
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
53 54 55 56 57 58 59 60 61 62 63 64 65 66
      _tileHeight(SettingsFact(settingsGroup, _metaDataMap[tileHeightName],
                               this /* QObject parent */)),
      _tileWidth(SettingsFact(settingsGroup, _metaDataMap[tileWidthName],
                              this /* QObject parent */)),
      _minTileArea(SettingsFact(settingsGroup, _metaDataMap[minTileAreaName],
                                this /* QObject parent */)),
      _transectDistance(SettingsFact(settingsGroup,
                                     _metaDataMap[transectDistanceName],
                                     this /* QObject parent */)),
      _minTransectLength(SettingsFact(settingsGroup,
                                      _metaDataMap[minTransectLengthName],
                                      this /* QObject parent */)),
      _showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesName],
                              this /* QObject parent */)),
67 68
      _pTiles(new QmlObjectListModel(), &tileDeleter), _calculating(false),
      _polygonValid(false) {
69
  init();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
70 71
}

72 73 74 75 76
/*!
 * \overload operator=()
 *
 * Calls the inherited operator WimaArea::operator=().
 */
77 78 79 80 81 82 83
WimaMeasurementArea &WimaMeasurementArea::
operator=(const WimaMeasurementArea &other) {
  WimaArea::operator=(other);

  return *this;
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
84
WimaMeasurementArea::~WimaMeasurementArea() {
85
  this->_pTiles->clearAndDeleteContents();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
86 87
}

88 89 90
QString WimaMeasurementArea::mapVisualQML() const {
  return "WimaMeasurementAreaMapVisual.qml";
}
91

92 93
QString WimaMeasurementArea::editorQML() const {
  return "WimaMeasurementAreaEditor.qml";
94 95
}

96 97
Fact *WimaMeasurementArea::tileHeight() { return &_tileHeight; }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
98 99 100 101 102 103 104 105 106 107
Fact *WimaMeasurementArea::tileWidth() { return &_tileWidth; }

Fact *WimaMeasurementArea::minTileArea() { return &_minTileArea; }

Fact *WimaMeasurementArea::transectDistance() { return &_transectDistance; }

Fact *WimaMeasurementArea::minTransectLength() { return &_minTransectLength; }

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

108 109
QmlObjectListModel *WimaMeasurementArea::tiles() { return this->_pTiles.get(); }

110 111 112 113 114
const QmlObjectListModel *WimaMeasurementArea::tiles() const {
  return this->_pTiles.get();
}

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

116
bool WimaMeasurementArea::ready() const { return !_calculating; }
117

118 119
void WimaMeasurementArea::saveToJson(QJsonObject &json) {
  this->WimaArea::saveToJson(json);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
120 121 122 123 124 125
  json[tileHeightName] = _tileHeight.rawValue().toDouble();
  json[tileWidthName] = _tileWidth.rawValue().toDouble();
  json[minTileAreaName] = _minTileArea.rawValue().toDouble();
  json[transectDistanceName] = _transectDistance.rawValue().toDouble();
  json[minTransectLengthName] = _minTransectLength.rawValue().toDouble();
  json[showTilesName] = _showTiles.rawValue().toBool();
126
  json[areaTypeName] = WimaMeasurementAreaName;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
127 128
}

129 130 131 132 133
bool WimaMeasurementArea::loadFromJson(const QJsonObject &json,
                                       QString &errorString) {
  if (this->WimaArea::loadFromJson(json, errorString)) {
    bool retVal = true;

Valentin Platzgummer's avatar
Valentin Platzgummer committed
134 135
    if (json.contains(tileHeightName) && json[tileHeightName].isDouble()) {
      _tileHeight.setRawValue(json[tileHeightName].toDouble());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
136
    } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
137
      errorString.append(tr("Could not load tile height!\n"));
138
      retVal = false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
139 140
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
141
    if (json.contains(tileWidthName) && json[tileWidthName].isDouble()) {
142
      _tileWidth.setRawValue(json[tileWidthName].toDouble());
143
    } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
144
      errorString.append(tr("Could not load tile width!\n"));
145 146
      retVal = false;
    }
147

Valentin Platzgummer's avatar
Valentin Platzgummer committed
148
    if (json.contains(minTileAreaName) && json[minTileAreaName].isDouble()) {
149
      _minTileArea.setRawValue(json[minTileAreaName].toDouble());
150
    } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
151
      errorString.append(tr("Could not load minimal tile area!\n"));
152
      retVal = false;
153
    }
154

Valentin Platzgummer's avatar
Valentin Platzgummer committed
155 156
    if (json.contains(transectDistanceName) &&
        json[transectDistanceName].isDouble()) {
157
      _transectDistance.setRawValue(json[transectDistanceName].toDouble());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
158 159 160 161 162 163 164
    } else {
      errorString.append(tr("Could not load transect distance!\n"));
      retVal = false;
    }

    if (json.contains(minTransectLengthName) &&
        json[minTransectLengthName].isDouble()) {
165
      _minTransectLength.setRawValue(json[minTransectLengthName].toDouble());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
166 167 168 169 170 171
    } else {
      errorString.append(tr("Could not load minimal transect length!\n"));
      retVal = false;
    }

    if (json.contains(showTilesName) && json[showTilesName].isBool()) {
172
      _showTiles.setRawValue(json[showTilesName].toBool());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
173 174 175 176
    } else {
      errorString.append(tr("Could not load show tiles !\n"));
      retVal = false;
    }
177 178 179 180
    return retVal;
  } else {
    return false;
  }
181
}
182 183 184 185
//!
//! \brief WimaMeasurementArea::doUpdate
//! \pre WimaMeasurementArea::deferUpdate must be called first, don't call this
//! function directly!
Valentin Platzgummer's avatar
Valentin Platzgummer committed
186 187 188 189 190 191
void WimaMeasurementArea::doUpdate() {
  using namespace snake;
  using namespace boost::units;
#ifdef SNAKE_SHOW_TIME
  auto start = std::chrono::high_resolution_clock::now();
#endif
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
  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;
  if (!this->_calculating &&
      long(std::ceil(estNumTiles.value())) <= SNAKE_MAX_TILES &&
      this->count() >= 3 && this->isSimplePolygon()) {
    this->_calculating = true;
    if (!this->_polygonValid) {
      this->_polygon = this->coordinateList();
      for (auto &v : this->_polygon) {
        v.setAltitude(0);
      }
      this->_polygonValid = true;
    }
    const auto &polygon = this->_polygon;
    const auto minArea =
Valentin Platzgummer's avatar
Valentin Platzgummer committed
210
        this->_minTileArea.rawValue().toDouble() * si::meter * si::meter;
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
    auto *th = this->thread();
    auto future = QtConcurrent::run([polygon, th, height, width, minArea] {
#ifdef SNAKE_SHOW_TIME
      auto start = std::chrono::high_resolution_clock::now();
#endif
      TilesPtr pTiles(new QmlObjectListModel(), &tileDeleter);
      QGeoCoordinate origin = polygon.first();
      BoostPolygon polygonENU;
      areaToEnu(origin, polygon, polygonENU);
      std::vector<BoostPolygon> tilesENU;
      BoundingBox bbox;
      std::string errorString;
      if (snake::tiles(polygonENU, height, width, minArea, tilesENU, bbox,
                       errorString)) {
        for (const auto &t : tilesENU) {
          auto geoTile = new SnakeTile(pTiles.get());
          for (const auto &v : t.outer()) {
            QGeoCoordinate geoVertex;
            fromENU(origin, v, geoVertex);
            geoTile->push_back(geoVertex);
          }
          pTiles->append(geoTile);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
233 234
        }
      }
235 236 237 238 239 240 241 242 243 244 245 246 247
      pTiles->moveToThread(th);
#ifdef SNAKE_SHOW_TIME
      qDebug()
          << "WimaMeasurementArea::doUpdate concurrent update execution time: "
          << std::chrono::duration_cast<std::chrono::milliseconds>(
                 std::chrono::high_resolution_clock::now() - start)
                 .count()
          << " ms";
#endif
      return pTiles;
    }); // QtConcurrent::run()

    this->_watcher.setFuture(future);
248
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
249 250 251 252 253 254 255
#ifdef SNAKE_SHOW_TIME
  qDebug() << "WimaMeasurementArea::doUpdate execution time: "
           << std::chrono::duration_cast<std::chrono::milliseconds>(
                  std::chrono::high_resolution_clock::now() - start)
                  .count()
           << " ms";
#endif
Valentin Platzgummer's avatar
Valentin Platzgummer committed
256 257
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
258 259 260
void WimaMeasurementArea::deferUpdate() {
  if (this->_timer.isActive()) {
    this->_timer.stop();
261
  }
262 263 264 265
  if (this->_pTiles->count() > 0) {
    this->_pTiles->clearAndDeleteContents();
    emit this->tilesChanged();
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
266
  this->_timer.start(100);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
267 268
}

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
void WimaMeasurementArea::storeTiles() {
#ifdef SNAKE_SHOW_TIME
  auto start = std::chrono::high_resolution_clock::now();
#endif
  this->_pTiles = this->_watcher.result();
  this->_calculating = false;
  emit this
      ->tilesChanged(); // This is expensive. Drawing tiles is expensive too.
#ifdef SNAKE_SHOW_TIME
  qDebug() << "WimaMeasurementArea::storeTiles() execution time: "
           << std::chrono::duration_cast<std::chrono::milliseconds>(
                  std::chrono::high_resolution_clock::now() - start)
                  .count()
           << " ms";
#endif
}

286 287
void WimaMeasurementArea::init() {
  this->setObjectName(WimaMeasurementAreaName);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
288 289 290 291 292 293 294 295
  connect(&this->_tileHeight, &Fact::rawValueChanged, this,
          &WimaMeasurementArea::deferUpdate);
  connect(&this->_tileWidth, &Fact::rawValueChanged, this,
          &WimaMeasurementArea::deferUpdate);
  connect(&this->_minTileArea, &Fact::rawValueChanged, this,
          &WimaMeasurementArea::deferUpdate);
  connect(this, &WimaArea::pathChanged, this,
          &WimaMeasurementArea::deferUpdate);
296 297
  connect(this, &WimaArea::pathChanged,
          [this] { this->_polygonValid = false; });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
298 299 300
  this->_timer.setSingleShot(true);
  connect(&this->_timer, &QTimer::timeout, this,
          &WimaMeasurementArea::doUpdate);
301 302 303
  connect(&this->_watcher,
          &QFutureWatcher<std::unique_ptr<QmlObjectListModel>>::finished, this,
          &WimaMeasurementArea::storeTiles);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
304 305
}

306 307
/*!
 * \class WimaMeasurementArea
308 309
 * \brief Class defining the area inside which the actual drone measurements
 * are performed.
310 311 312
 *
 * \sa WimaArea, WimaController, WimaPlaner
 */