WimaArea.cc 17.3 KB
Newer Older
1 2
#include "WimaArea.h"

3
/*!
4 5
 * \variable WimaArea::epsilonMeter
 * \brief The accuracy used for distance calculations (unit: m).
6
 */
7
const double WimaArea::epsilonMeter = 1e-5;
8 9
/*!
 * \variable WimaArea::maxAltitudeName
10 11
 * \brief A string containing the name of the \c _maxAltitude member. Among
 * other used for storing.
12
 */
13
const char *WimaArea::maxAltitudeName = "maxAltitude";
14 15
/*!
 * \variable WimaArea::wimaAreaName
16 17
 * \brief A string containing the name of this \c WimaArea member. Among other
 * used for storing.
18
 */
19
const char *WimaArea::wimaAreaName = "WimaArea";
20 21 22 23
/*!
 * \variable WimaArea::areaTypeName
 * \brief A string containing \c {"AreaType"}. Among other used for stroing.
 */
24
const char *WimaArea::areaTypeName = "AreaType";
25

26 27 28
const char *WimaArea::borderPolygonOffsetName = "BorderPolygonOffset";
const char *WimaArea::showBorderPolygonName = "ShowBorderPolygon";
const char *WimaArea::settingsGroup = "MeasurementArea";
29

30
// Constructors
31
WimaArea::WimaArea(QObject *parent)
32 33 34 35 36 37 38 39 40 41 42 43 44
    : QGCMapPolygon(parent),
      _metaDataMap(FactMetaData::createMapFromJsonFile(
          QStringLiteral(":/json/WimaArea.SettingsGroup.json"),
          this /* QObject parent */)),
      _borderPolygonOffset(SettingsFact(settingsGroup,
                                        _metaDataMap[borderPolygonOffsetName],
                                        this /* QObject parent */)),
      _showBorderPolygon(SettingsFact(settingsGroup,
                                      _metaDataMap[showBorderPolygonName],
                                      this /* QObject parent */)),
      _borderPolygon(QGCMapPolygon(this)), _wimaAreaInteractive(false) {
  init();
  _maxAltitude = 30;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
45
}
46

Valentin Platzgummer's avatar
Valentin Platzgummer committed
47
WimaArea::WimaArea(const WimaArea &other, QObject *parent)
48 49 50 51 52 53 54 55 56 57 58 59 60
    : QGCMapPolygon(parent),
      _metaDataMap(FactMetaData::createMapFromJsonFile(
          QStringLiteral(":/json/WimaArea.SettingsGroup.json"),
          this /* QObject parent */)),
      _borderPolygonOffset(SettingsFact(settingsGroup,
                                        _metaDataMap[borderPolygonOffsetName],
                                        this /* QObject parent */)),
      _showBorderPolygon(SettingsFact(settingsGroup,
                                      _metaDataMap[showBorderPolygonName],
                                      this /* QObject parent */)),
      _borderPolygon(QGCMapPolygon(this)), _wimaAreaInteractive(false) {
  init();
  *this = other;
61 62 63 64 65
}

/*!
 *\fn WimaArea &WimaArea::operator=(const WimaArea &other)
 *
66 67
 * Assigns \a other to this \c WimaArea and returns a reference to this \c
 *WimaArea.
68 69 70
 *
 * Copies only path and maximum altitude.
 */
71 72
WimaArea &WimaArea::operator=(const WimaArea &other) {
  QGCMapPolygon::operator=(other);
73
  this->setMaxAltitude(other._maxAltitude);
74
  this->setPath(other.path());
75

76
  return *this;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
77
}
78

79 80 81
void WimaArea::setWimaAreaInteractive(bool interactive) {
  if (WimaArea::_wimaAreaInteractive != interactive) {
    WimaArea::_wimaAreaInteractive = interactive;
82

83 84
    emit WimaArea::wimaAreaInteractiveChanged();
  }
85 86
}

87 88 89
/*!
  \fn void WimaArea::setMaxAltitude(double altitude)

90 91
  Sets the \c _maxAltitude member to \a altitude and emits the signal \c
  maxAltitudeChanged() if \c _maxAltitude is not equal to altitude.
92
 */
93 94 95 96 97
void WimaArea::setMaxAltitude(double altitude) {
  if (altitude > 0 && qFuzzyCompare(altitude, _maxAltitude)) {
    _maxAltitude = altitude;
    emit maxAltitudeChanged();
  }
98 99
}

100 101
void WimaArea::setShowBorderPolygon(bool showBorderPolygon) {
  _showBorderPolygon.setRawValue(showBorderPolygon);
102 103
}

104 105 106
void WimaArea::setBorderPolygonOffset(double offset) {
  if (!qFuzzyCompare(_borderPolygonOffset.rawValue().toDouble(), offset)) {
    _borderPolygonOffset.setRawValue(offset);
107

108 109
    emit borderPolygonOffsetChanged();
  }
110 111
}

112 113
void WimaArea::recalcPolygons() {
  if (_showBorderPolygon.rawValue().toBool() == true) {
114

115 116 117 118
    if (_borderPolygon.count() >= 3) {
      //_borderPolygon.verifyClockwiseWinding(); // causes seg. fault
      this->setPath(_borderPolygon.coordinateList());
      this->offset(-_borderPolygonOffset.rawValue().toDouble());
119
    }
120
  } else {
121

122 123 124 125 126
    if (this->count() >= 3) {
      // this->verifyClockwiseWinding(); // causes seg. fault
      _borderPolygon.setPath(this->coordinateList());
      _borderPolygon.offset(_borderPolygonOffset.rawValue().toDouble());
    }
127

128 129
    emit borderPolygonChanged();
  }
130 131
}

132 133 134 135 136 137 138 139 140 141 142
void WimaArea::updatePolygonConnections(QVariant showBorderPolygon) {
  if (showBorderPolygon.toBool() == true) {
    connect(&_borderPolygon, &QGCMapPolygon::pathChanged, this,
            &WimaArea::recalcPolygons);
    disconnect(this, &QGCMapPolygon::pathChanged, this,
               &WimaArea::recalcPolygons);
  } else {
    disconnect(&_borderPolygon, &QGCMapPolygon::pathChanged, this,
               &WimaArea::recalcPolygons);
    connect(this, &QGCMapPolygon::pathChanged, this, &WimaArea::recalcPolygons);
  }
143 144
}

145 146 147 148 149 150 151 152
void WimaArea::recalcInteractivity() {
  if (_wimaAreaInteractive == false) {
    QGCMapPolygon::setInteractive(false);
    _borderPolygon.setInteractive(false);
  } else {
    if (_showBorderPolygon.rawValue().toBool() == true) {
      _borderPolygon.setInteractive(true);
      QGCMapPolygon::setInteractive(false);
153
    } else {
154 155
      _borderPolygon.setInteractive(false);
      QGCMapPolygon::setInteractive(true);
156
    }
157
  }
158 159
}

160
/*!
161 162 163
 * \fn int WimaArea::getClosestVertexIndex(const QGeoCoordinate &coordinate)
 * const Returns the index of the vertex (element of the polygon path) which has
 * the least distance to \a coordinate.
164 165 166
 *
 * \sa QGeoCoordinate
 */
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
int WimaArea::getClosestVertexIndex(const QGeoCoordinate &coordinate) const {
  if (this->count() == 0) {
    qWarning("Polygon count == 0!");
    return -1;
  } else if (this->count() == 1) {
    return 0;
  } else {
    int index = 0;
    double min_dist = coordinate.distanceTo(this->vertexCoordinate(index));
    for (int i = 1; i < this->count(); i++) {
      double dist = coordinate.distanceTo(this->vertexCoordinate(i));
      if (dist < min_dist) {
        min_dist = dist;
        index = i;
      }
182
    }
183 184 185

    return index;
  }
186 187
}

188
/*!
189 190 191
 * \fn  QGeoCoordinate WimaArea::getClosestVertex(const QGeoCoordinate&
 * coordinate) const Returns the vertex of the polygon path with the least
 * distance to \a coordinate.
192 193 194
 *
 * \sa QGeoCoordinate
 */
195 196 197
QGeoCoordinate
WimaArea::getClosestVertex(const QGeoCoordinate &coordinate) const {
  return this->vertexCoordinate(getClosestVertexIndex(coordinate));
198 199
}

200 201
/*!
 * \fn QGCMapPolygon WimaArea::toQGCPolygon(const WimaArea &area)
202 203
 * Converts the \c WimaArea \a area to \c QGCMapPolygon by copying the path
 * only.
204
 */
205 206 207
QGCMapPolygon WimaArea::toQGCPolygon(const WimaArea &area) {
  QGCMapPolygon qgcPoly;
  qgcPoly.setPath(area.path());
208

209
  return QGCMapPolygon(qgcPoly);
210 211
}

212 213
/*!
 * \fn QGCMapPolygon WimaArea::toQGCPolygon() const
214 215
 * Converts the calling \c WimaArea to \c QGCMapPolygon by copying the path
 * only.
216
 */
217
QGCMapPolygon WimaArea::toQGCPolygon() const { return toQGCPolygon(*this); }
218

219
/*!
220 221 222 223 224 225 226
 * \fn bool WimaArea::join(WimaArea &area1, WimaArea &area2, WimaArea
 * &joinedArea, QString &errorString) Joins the areas \a area1 and \a area2 such
 * that a \l {Simple Polygon} is created. Stores the result inside \a
 * joinedArea. Stores error messages in \a errorString. Returns \c true if the
 * algorithm was able to join the areas; false else. The algorithm will be able
 * to join the areas, if either their edges intersect with each other, or one
 * area contains the other.
227
 */
228 229 230 231 232 233 234 235 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
bool WimaArea::join(const WimaArea &area1, const WimaArea &area2,
                    WimaArea &joinedArea, QString &errorString) {
  using namespace GeoUtilities;
  using namespace PolygonCalculus;

  Q_UNUSED(errorString);

  QList<QGeoCoordinate> GeoPolygon1 = area1.coordinateList();
  QList<QGeoCoordinate> GeoPolygon2 = area2.coordinateList();

  //    qWarning("befor joining");
  //    qWarning() << GeoPolygon1;
  //    qWarning() << GeoPolygon2;

  QGeoCoordinate origin = GeoPolygon1[0];

  //    QGeoCoordinate tset = GeoPolygon1[2];

  //    qWarning() << tset;qWarning() << toGeo(toCartesian2D(tset, origin),
  //    origin);

  QPolygonF polygon1;
  toCartesianList(GeoPolygon1, origin, polygon1);
  QPolygonF polygon2;
  toCartesianList(GeoPolygon2, origin, polygon2);

  //    qWarning("after 1 transform");
  //    qWarning() << polygon1;
  //    qWarning() << polygon2;

  QPolygonF joinedPolygon;
  JoinPolygonError retValue =
      PolygonCalculus::join(polygon1, polygon2, joinedPolygon);

  //    qWarning("after joining");
  //    qWarning() << joinedPolygon;

  if (retValue == JoinPolygonError::Disjoint) {
    qWarning("Polygons are disjoint.");
  } else if (retValue == JoinPolygonError::NotSimplePolygon) {
    qWarning("Not a simple polygon.");
  } else if (retValue == JoinPolygonError::PathSizeLow) {
    qWarning("Polygon vertex count is low.");
  } else {
272
    QList<QGeoCoordinate> path;
273 274 275 276 277 278
    toGeoList(joinedPolygon, origin, path);
    //        qWarning("after transform");
    //        qWarning() << path;
    joinedArea.setPath(path);
    return true;
  }
279

280
  return false;
281
}
282

283
/*!
284 285 286 287 288
 * \fn bool WimaArea::join(WimaArea &area1, WimaArea &area2, WimaArea
 * &joinedArea) Joins the areas \a area1 and \a area2 such that a \l {Simple
 * Polygon} is created. Stores the result inside \a joinedArea. Returns \c true
 * if the algorithm was able to join the areas; false else. The algorithm will
 * be able to join the areas, if either their edges intersect with each other,
289 290
 * or one area contains the other.
 */
291 292 293 294
bool WimaArea::join(const WimaArea &area1, const WimaArea &area2,
                    WimaArea &joinedArea) {
  QString dummy;
  return join(area1, area2, joinedArea, dummy);
295 296
}

297 298
/*!
 * \fn bool WimaArea::join(WimaArea &area)
299 300 301 302 303
 * Joins the calling \c WimaArea and the \a area such that a \l {Simple Polygon}
 * is created. Overwrites the calling \c WimaArea with the result, if the
 * algorithm was successful. Returns \c true if the algorithm was able to join
 * the areas; false else. The algorithm will be able to join the areas, if
 * either their edges intersect with each other, or one area contains the other.
304
 */
305 306 307 308 309 310 311 312 313 314
bool WimaArea::join(WimaArea &area) {
  WimaArea joinedArea;
  if (join(*this, area, joinedArea)) {
    // qWarning("WimaArea::join(WimaArea &area)");
    // qWarning() << joinedArea.coordinateList();
    this->setPath(joinedArea.path());
    return true;
  } else {
    return false;
  }
315
}
316

317
/*!
318
 * \fn bool WimaArea::join(WimaArea &area, QString &errorString)
319 320 321
 * Joins the calling \c WimaArea and the \a area such that a \l {Simple Polygon}
 * is created. Overwrites the calling \c WimaArea with the result, if the
 * algorithm was successful.
322 323 324 325
 *
 * Returns \c true if the algorithm was able to join the areas; false else.
 * Stores error messages in \a errorString.
 *
326 327
 * The algorithm will be able to join the areas, if either their edges intersect
 * with each other, or one area contains the other.
328
 */
329 330 331 332 333 334 335 336
bool WimaArea::join(WimaArea &area, QString &errorString) {
  WimaArea joinedArea;
  if (join(*this, area, joinedArea, errorString)) {
    this->setPath(joinedArea.path());
    return true;
  } else {
    return false;
  }
337 338
}

339 340
/*!
 * \fn int WimaArea::nextVertexIndex(int index) const
341 342 343 344 345
 * Returns the index of the next vertex (of the areas path), which is \a index +
 * 1 if \a index is smaller than \c {area.count() - 1}, or 0 if \a index equals
 * \c {area.count() - 1}, or -1 if the \a index is out of bounds. \note The
 * function \c {area.count()} (derived from \c QGCMapPolygon) returns the number
 * of vertices defining the area.
346
 */
347 348 349 350 351 352 353 354 355 356 357
int WimaArea::nextVertexIndex(int index) const {
  if (index >= 0 && index < count() - 1) {
    return index + 1;
  } else if (index == count() - 1) {
    return 0;
  } else {
    qWarning(
        "WimaArea::nextVertexIndex(): Index out of bounds! index:count = %i:%i",
        index, count());
    return -1;
  }
358 359
}

360 361
/*!
 * \fn int WimaArea::previousVertexIndex(int index) const
362 363 364 365 366
 * Returns the index of the previous vertex (of the areas path), which is \a
 * index - 1 if \a index is larger 0, or \c {area.count() - 1} if \a index
 * equals 0, or -1 if the \a index is out of bounds. \note The function \c
 * {area.count()} (derived from \c QGCMapPolygon) returns the number of vertices
 * defining the area.
367
 */
368 369 370 371 372 373 374 375 376 377 378
int WimaArea::previousVertexIndex(int index) const {
  if (index > 0 && index < count()) {
    return index - 1;
  } else if (index == 0) {
    return count() - 1;
  } else {
    qWarning("WimaArea::previousVertexIndex(): Index out of bounds! "
             "index:count = %i:%i",
             index, count());
    return -1;
  }
379 380
}

381 382 383
/*!
 * \fn bool WimaArea::isSelfIntersecting()
 * Returns \c true if the calling area is self intersecting, \c false else.
384 385
 * \note If the calling area is self intersecting, it's not a \l {Simple
 * Polygon}.
386
 */
387 388 389 390 391 392 393 394 395 396
bool WimaArea::isSimplePolygon() const {
  using namespace PolygonCalculus;
  using namespace GeoUtilities;

  if (this->count() > 2) {
    QPolygonF polygon;
    toCartesianList(this->coordinateList(), this->vertexCoordinate(0), polygon);
    return PolygonCalculus::isSimplePolygon(polygon);
  } else
    return false;
397
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
398

399 400 401 402 403 404 405 406 407 408 409
bool WimaArea::containsCoordinate(const QGeoCoordinate &coordinate) const {
  using namespace PlanimetryCalculus;
  using namespace PolygonCalculus;
  using namespace GeoUtilities;

  if (this->count() > 2) {
    QPolygonF polygon;
    toCartesianList(this->coordinateList(), coordinate, polygon);
    return PlanimetryCalculus::contains(polygon, QPointF(0, 0));
  } else
    return false;
410 411
}

412 413 414 415 416 417
/*!
 * \fn void WimaArea::saveToJson(QJsonObject &json)
 * Saves the calling area to \c QJsonObject object and stores it inside \a json.
 *
 * \sa QJsonObject
 */
418 419 420 421 422 423 424
void WimaArea::saveToJson(QJsonObject &json) {
  this->QGCMapPolygon::saveToJson(json);

  json[maxAltitudeName] = _maxAltitude;
  json[borderPolygonOffsetName] = _borderPolygonOffset.rawValue().toDouble();
  json[showBorderPolygonName] = _showBorderPolygon.rawValue().toDouble();
  json[areaTypeName] = wimaAreaName;
425
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
426

427
/*!
428 429
 * \fn bool WimaArea::loadFromJson(const QJsonObject &json, QString&
 * errorString) Loads data from \a json and stores it inside the calling area.
430 431 432 433 434
 * Returns \c true if loading was successful, \c false else.
 * Stores error messages inside \a errorString.
 *
 * \sa QJsonObject
 */
435 436 437 438 439
bool WimaArea::loadFromJson(const QJsonObject &json, QString &errorString) {
  if (this->QGCMapPolygon::loadFromJson(json, false /*no poly required*/,
                                        errorString)) {
    if (json.contains(maxAltitudeName) && json[maxAltitudeName].isDouble()) {
      _maxAltitude = json[maxAltitudeName].toDouble();
440
    } else {
441 442
      errorString.append(tr("Could not load Maximum Altitude value!\n"));
      return false;
443
    }
444

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
    if (json.contains(borderPolygonOffsetName) &&
        json[borderPolygonOffsetName].isDouble()) {
      _borderPolygonOffset.setRawValue(
          json[borderPolygonOffsetName].toDouble());
    } else {
      errorString.append(tr("Could not load border polygon offset value!\n"));
      return false;
    }

    if (json.contains(showBorderPolygonName) &&
        json[showBorderPolygonName].isDouble()) {
      _showBorderPolygon.setRawValue(json[showBorderPolygonName].toBool());
    } else {
      errorString.append(tr("Could not load border polygon offset value!\n"));
      return false;
    }
  } else {
    qWarning() << errorString;
    return false;
  }

  return true;
467
}
468

469 470 471 472
/*!
 * \fn void WimaArea::init()
 * Funtion to be called during construction.
 */
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
void WimaArea::init() {
  this->setObjectName(wimaAreaName);

  if (_showBorderPolygon.rawValue().toBool() == true) {
    connect(&_borderPolygon, &QGCMapPolygon::pathChanged, this,
            &WimaArea::recalcPolygons);

  } else {
    connect(this, &QGCMapPolygon::pathChanged, this, &WimaArea::recalcPolygons);
  }

  connect(&_borderPolygonOffset, &SettingsFact::rawValueChanged, this,
          &WimaArea::recalcPolygons);
  connect(&_showBorderPolygon, &SettingsFact::rawValueChanged, this,
          &WimaArea::updatePolygonConnections);
  connect(&_showBorderPolygon, &SettingsFact::rawValueChanged, this,
          &WimaArea::recalcInteractivity);
  connect(this, &WimaArea::wimaAreaInteractiveChanged, this,
          &WimaArea::recalcInteractivity);
492 493
}

494 495 496 497 498 499
// QDoc Documentation

/*!
    \group WimaAreaGroup
    \title Group of WimaAreas

500 501 502
    Every \c WimaArea of the equally named group uses a \l {Simple Polygon}
   derived from \c {QGCMapPolygon} to define areas inside which certain taskts
   are performed.
503 504 505 506 507 508 509 510 511 512 513
*/

/*!
    \class WimaArea
    \inmodule Wima
    \ingroup WimaArea

    \brief The \c WimaArea class provides the a base class for
    all areas used within the Wima extension.

    \c WimaArea uses a \l {Simple Polygon} derived from \c {QGCMapPolygon}
514 515
    to define areas inside which certain taskts are performed. The polygon
   (often refered to as the path) can be displayed visually on a map.
516 517 518 519 520 521 522 523 524 525 526 527 528 529
*/

/*!
  \variable WimaArea::_maxAltitude
  \brief The maximum altitude vehicles are allowed to fly inside this area.
*/

/*!
  \property WimaArea::maxAltitude
  \brief The maximum altitude at which vehicles are allowed to fly.
*/

/*!
  \property WimaArea::mapVisualQML
530 531
  \brief A string containing the name of the QML file used to displays this area
  on a map.
532 533 534 535
*/

/*!
  \property WimaArea::editorQML
536 537
  \brief A string containing the name of the QML file allowing to edit the
  area's properties.
538 539 540 541 542 543 544 545 546 547 548
*/

/*!
    \externalpage https://en.wikipedia.org/wiki/Simple_polygon
    \title Simple Polygon
*/

/*!
    \externalpage https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
    \title Dijkstra Algorithm
*/