Skip to content
WimaArea.cc 18.4 KiB
Newer Older
 * \variable WimaArea::epsilonMeter
 * \brief The accuracy used for distance calculations (unit: m).
const double WimaArea::epsilonMeter = 1e-5;
/*!
 * \variable WimaArea::maxAltitudeName
 * \brief A string containing the name of the \c _maxAltitude member. Among
 * other used for storing.
const char *WimaArea::maxAltitudeName = "maxAltitude";
/*!
 * \variable WimaArea::wimaAreaName
 * \brief A string containing the name of this \c WimaArea member. Among other
 * used for storing.
const char *WimaArea::wimaAreaName = "WimaArea";
/*!
 * \variable WimaArea::areaTypeName
 * \brief A string containing \c {"AreaType"}. Among other used for stroing.
 */
const char *WimaArea::areaTypeName = "AreaType";
const char *WimaArea::borderPolygonOffsetName = "BorderPolygonOffset";
const char *WimaArea::showBorderPolygonName = "ShowBorderPolygon";
const char *WimaArea::settingsGroup = "MeasurementArea";
WimaArea::WimaArea(QObject *parent)
    : 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
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
WimaArea::WimaArea(const WimaArea &other, QObject *parent)
    : 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;
}

/*!
 *\fn WimaArea &WimaArea::operator=(const WimaArea &other)
 *
 * Assigns \a other to this \c WimaArea and returns a reference to this \c
 *WimaArea.
WimaArea &WimaArea::operator=(const WimaArea &other) {
  QGCMapPolygon::operator=(other);
  this->setMaxAltitude(other._maxAltitude);
  this->setPath(other.path());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
}
void WimaArea::setWimaAreaInteractive(bool interactive) {
  if (WimaArea::_wimaAreaInteractive != interactive) {
    WimaArea::_wimaAreaInteractive = interactive;
    emit WimaArea::wimaAreaInteractiveChanged();
  }
/*!
  \fn void WimaArea::setMaxAltitude(double altitude)

  Sets the \c _maxAltitude member to \a altitude and emits the signal \c
  maxAltitudeChanged() if \c _maxAltitude is not equal to altitude.
void WimaArea::setMaxAltitude(double altitude) {
  if (altitude > 0 && qFuzzyCompare(altitude, _maxAltitude)) {
    _maxAltitude = altitude;
    emit maxAltitudeChanged();
  }
void WimaArea::setShowBorderPolygon(bool showBorderPolygon) {
  _showBorderPolygon.setRawValue(showBorderPolygon);
void WimaArea::setBorderPolygonOffset(double offset) {
  if (!qFuzzyCompare(_borderPolygonOffset.rawValue().toDouble(), offset)) {
    _borderPolygonOffset.setRawValue(offset);
    emit borderPolygonOffsetChanged();
  }
void WimaArea::recalcPolygons() {
  if (_showBorderPolygon.rawValue().toBool() == true) {
    if (_borderPolygon.count() >= 3) {
      //_borderPolygon.verifyClockwiseWinding(); // causes seg. fault
      this->setPath(_borderPolygon.coordinateList());
      this->offset(-_borderPolygonOffset.rawValue().toDouble());
    if (this->count() >= 3) {
      // this->verifyClockwiseWinding(); // causes seg. fault
      _borderPolygon.setPath(this->coordinateList());
      _borderPolygon.offset(_borderPolygonOffset.rawValue().toDouble());
    }
    emit borderPolygonChanged();
  }
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);
  }
void WimaArea::recalcInteractivity() {
  if (_wimaAreaInteractive == false) {
    QGCMapPolygon::setInteractive(false);
    _borderPolygon.setInteractive(false);
  } else {
    if (_showBorderPolygon.rawValue().toBool() == true) {
      _borderPolygon.setInteractive(true);
      QGCMapPolygon::setInteractive(false);
    } else {
      _borderPolygon.setInteractive(false);
      QGCMapPolygon::setInteractive(true);
 * \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.
 *
 * \sa QGeoCoordinate
 */
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;
      }
 * \fn  QGeoCoordinate WimaArea::getClosestVertex(const QGeoCoordinate&
 * coordinate) const Returns the vertex of the polygon path with the least
 * distance to \a coordinate.
 *
 * \sa QGeoCoordinate
 */
QGeoCoordinate
WimaArea::getClosestVertex(const QGeoCoordinate &coordinate) const {
  return this->vertexCoordinate(getClosestVertexIndex(coordinate));
/*!
 * \fn QGCMapPolygon WimaArea::toQGCPolygon(const WimaArea &area)
 * Converts the \c WimaArea \a area to \c QGCMapPolygon by copying the path
 * only.
QGCMapPolygon WimaArea::toQGCPolygon(const WimaArea &area) {
  QGCMapPolygon qgcPoly;
  qgcPoly.setPath(area.path());
  return QGCMapPolygon(qgcPoly);
/*!
 * \fn QGCMapPolygon WimaArea::toQGCPolygon() const
 * Converts the calling \c WimaArea to \c QGCMapPolygon by copying the path
 * only.
QGCMapPolygon WimaArea::toQGCPolygon() const { return toQGCPolygon(*this); }
 * \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.
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 {
    QVector<QGeoCoordinate> path;
    toGeoList(joinedPolygon, origin, path);
    //        qWarning("after transform");
    //        qWarning() << path;
    joinedArea.setPath(path);
    return true;
  }
 * \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,
bool WimaArea::join(const WimaArea &area1, const WimaArea &area2,
                    WimaArea &joinedArea) {
  QString dummy;
  return join(area1, area2, joinedArea, dummy);
/*!
 * \fn bool WimaArea::join(WimaArea &area)
 * 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.
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;
  }
 * \fn bool WimaArea::join(WimaArea &area, QString &errorString)
 * 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.
 * Stores error messages in \a errorString.
 *
 * The algorithm will be able to join the areas, if either their edges intersect
 * with each other, or one area contains the other.
bool WimaArea::join(WimaArea &area, QString &errorString) {
  WimaArea joinedArea;
  if (join(*this, area, joinedArea, errorString)) {
    this->setPath(joinedArea.path());
    return true;
  } else {
    return false;
  }
/*!
 * \fn int WimaArea::nextVertexIndex(int index) const
 * 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.
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;
  }
/*!
 * \fn int WimaArea::previousVertexIndex(int index) const
 * 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.
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;
  }
/*!
 * \fn bool WimaArea::isSelfIntersecting()
 * Returns \c true if the calling area is self intersecting, \c false else.
 * \note If the calling area is self intersecting, it's not a \l {Simple
 * Polygon}.
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;
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;
/*!
 * \fn void WimaArea::saveToJson(QJsonObject &json)
 * Saves the calling area to \c QJsonObject object and stores it inside \a json.
 *
 * \sa QJsonObject
 */
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;
 * \fn bool WimaArea::loadFromJson(const QJsonObject &json, QString&
 * errorString) Loads data from \a json and stores it inside the calling area.
 * Returns \c true if loading was successful, \c false else.
 * Stores error messages inside \a errorString.
 *
 * \sa QJsonObject
 */
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();
    } else {
      errorString.append(tr("Could not load Maximum Altitude value!\n"));
      return false;
    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;
/*!
 * \fn void WimaArea::init()
 * Funtion to be called during construction.
 */
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);
/*!
 * \fn void print(const WimaArea &area)
 * Prints the data contained in \a area to the console.
 */
void print(const WimaArea &area) {
  QString message;
  print(area, message);
  qWarning() << message;
/*!
 * \fn void print(const WimaArea &area)
 * Prints the data contained in \a area to the \a outputString.
 */
void print(const WimaArea &area, QString &outputString) {
  outputString.append(QString("Type: %1\n").arg(area.objectName()));
  print(static_cast<const QGCMapPolygon &>(area), outputString);
  outputString.append(QString("Maximum Altitude: %1\n").arg(area._maxAltitude));
  outputString.append(
      QString("Border Polygon Offset: %1\n")
          .arg(area._borderPolygonOffset.rawValue().toDouble()));

  outputString.append(
      QString("Border Polygon Coordinates\n")
          .arg(area._borderPolygonOffset.rawValue().toDouble()));
  for (int i = 0; i < area._borderPolygon.count(); i++) {
    QGeoCoordinate coordinate = area._borderPolygon.vertexCoordinate(i);
    outputString.append(
        QString("%1\n").arg(coordinate.toString(QGeoCoordinate::Degrees)));
  }
// QDoc Documentation

/*!
    \group WimaAreaGroup
    \title Group of WimaAreas

    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.
*/

/*!
    \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}
    to define areas inside which certain taskts are performed. The polygon
   (often refered to as the path) can be displayed visually on a map.
*/

/*!
  \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
  \brief A string containing the name of the QML file used to displays this area
  on a map.
*/

/*!
  \property WimaArea::editorQML
  \brief A string containing the name of the QML file allowing to edit the
  area's properties.
*/

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

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