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

3 4 5 6 7
const double WimaArea::numericalAccuracy    = 1e-3; // meters
const char* WimaArea::maxAltitudeName       = "maxAltitude";
const char* WimaArea::wimaAreaName          = "WimaArea";
const char* WimaArea::areaTypeName          = "AreaType";

8

Valentin Platzgummer's avatar
Valentin Platzgummer committed
9 10
WimaArea::WimaArea()
    : QGCMapPolygon (nullptr)
11
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
12 13 14
    this->setObjectName(wimaAreaName);
    _maxAltitude = 30;
    _wimaVehicle = new WimaVehicle(this);
15 16
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
17 18
WimaArea::WimaArea(QObject *parent)
    :   QGCMapPolygon (parent)
19
{
20
    this->setObjectName(wimaAreaName);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
21 22
    _maxAltitude = 30;
    _wimaVehicle = new WimaVehicle(this);
23 24
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
25 26 27 28 29 30 31
WimaArea::WimaArea(const QGCMapPolygon &other, QObject *parent)
    : QGCMapPolygon (other, parent)
{
    this->setObjectName(wimaAreaName);
    _maxAltitude = 30;
    _wimaVehicle = new WimaVehicle(this);
}
32

Valentin Platzgummer's avatar
Valentin Platzgummer committed
33 34 35 36 37 38 39 40 41 42 43
WimaArea::WimaArea(const WimaArea &other, QObject *parent)
    : WimaArea (parent)
{
    this->setObjectName(wimaAreaName);
    this->setPath(other.path());
    this->setCenter(other.center());
    this->setCenterDrag(other.centerDrag());
    this->setInteractive(other.interactive());
    _maxAltitude = other.maxAltitude();
    _wimaVehicle = other.vehicle();
}
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61


void WimaArea::setMaxAltitude(double alt)
{
    if(alt > 0 && alt != _maxAltitude){
        _maxAltitude = alt;
        emit maxAltitudeChanged();
    }
}

void WimaArea::setVehicle(WimaVehicle *vehicle)
{
    if(_wimaVehicle != vehicle){
        _wimaVehicle->deleteLater();
        _wimaVehicle =  vehicle;
        emit vehicleChanged();
    }
}
62

63 64 65 66 67 68 69 70 71 72 73
/*QList<WimaArea *>* WimaArea::splitArea<T>(WimaArea *polygonToSplitt, int numberOfFractions)
{
    if(numberOfFractions > 0 && polygonToSplitt != nullptr){
        WimaArea* poly;
        if(p)
        = new WimaArea(polygonToSplitt, this);
        QList<WimaArea*>* list = new QList<WimaArea*>();
        list->append(poly);
        return list;
    }
    return nullptr;
74
}
75 76 77 78 79 80 81


QList<QGCMapPolygon*>* WimaArea::splitArea(int numberOfFractions)
{
    return splitPolygonArea(this, numberOfFractions);
}*/

82
int WimaArea::getClosestVertexIndex(const QGeoCoordinate &coordinate) const
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
{
    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;
            }
        }

        return index;
    }
}

104
QGeoCoordinate WimaArea::getClosestVertex(const QGeoCoordinate& coordinate) const
105 106 107 108
{
    return this->vertexCoordinate(getClosestVertexIndex(coordinate));
}

109
QGCMapPolygon WimaArea::toQGCPolygon(const WimaArea &poly)
110
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
111 112 113 114 115
    QGCMapPolygon qgcPoly;
    qgcPoly.setPath(poly.path());
    qgcPoly.setCenter(poly.center());
    qgcPoly.setCenterDrag(poly.centerDrag());
    qgcPoly.setInteractive(poly.interactive());
116

Valentin Platzgummer's avatar
Valentin Platzgummer committed
117
    return QGCMapPolygon(qgcPoly);
118 119
}

120 121 122 123 124
QGCMapPolygon WimaArea::toQGCPolygon() const
{
    return toQGCPolygon(*this);
}

125
void WimaArea::join(QList<WimaArea *>* polyList,  WimaArea* joinedPoly)
126
{
127
    return;
128 129
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
130
void WimaArea::join(WimaArea &poly1, WimaArea &poly2, WimaArea &joinedPoly)
131
{
132

133 134
        if (poly1.count() >= 3 && poly2.count() >= 3) {

135 136
            joinedPoly.clear();

Valentin Platzgummer's avatar
Valentin Platzgummer committed
137 138
            poly1.verifyClockwiseWinding();
            poly2.verifyClockwiseWinding();
139

Valentin Platzgummer's avatar
Valentin Platzgummer committed
140 141
            WimaArea* walkerPoly    = &poly1; // "walk" on this polygon towards higher indices
            WimaArea* crossPoly     = &poly2; // check for crossings with this polygon while "walking"
142 143
                                             // and swicht to this polygon on a intersection,
                                             // continue to walk towards higher indices
144 145 146 147 148 149 150 151 152 153 154 155 156 157

            // begin with the first index which is not inside crosspoly, if all Vertices are inside crosspoly return crosspoly
            int startIndex = 0;
            bool crossContainsWalker = true;
            for (int i = 0; i < walkerPoly->count(); i++) {
                if ( !crossPoly->containsCoordinate(walkerPoly->vertexCoordinate(i)) ) {
                    crossContainsWalker = false;
                    startIndex = i;
                    break;
                }
            }


            if ( crossContainsWalker == true) {
158
                joinedPoly.appendVertices(crossPoly->coordinateList());
159 160 161 162 163 164
                return;
            }


            QGeoCoordinate currentVertex    = walkerPoly->vertexCoordinate(startIndex);
            QGeoCoordinate startVertex      = currentVertex;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
165
            // possible nextVertex (if no intersection between currentVertex and protoVertex with crossPoly)
166 167 168 169
            QGeoCoordinate protoNextVertex  = walkerPoly->vertexCoordinate(walkerPoly->nextVertexIndex(startIndex));

            int nextVertexIndex = walkerPoly->nextVertexIndex(startIndex);
            while (1) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
170
                //qDebug("nextVertexIndex: %i", nextVertexIndex);
171
                joinedPoly.appendVertex(currentVertex);
172 173 174

                QGCMapPolyline walkerPolySegment;
                walkerPolySegment.appendVertex(currentVertex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
175
                walkerPolySegment.appendVertex(protoNextVertex);
176

177 178
                QList<QPair<int, int>> neighbourList;
                QList<QGeoCoordinate> intersectionList;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
179
                //qDebug("IntersectionList.size() on init: %i", intersectionList.size());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
180
                intersects(walkerPolySegment, *crossPoly, intersectionList, neighbourList);
181

Valentin Platzgummer's avatar
Valentin Platzgummer committed
182
                //qDebug("IntersectionList.size(): %i", intersectionList.size());
183

184 185
                if (intersectionList.size() >= 1) {
                    int minDistIndex = 0;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
186

187 188 189 190
                    if (intersectionList.size() > 1) {
                        double minDist = currentVertex.distanceTo(intersectionList.value(minDistIndex));
                        for (int i = 1; i < intersectionList.size(); i++) {
                            double currentDist = currentVertex.distanceTo(intersectionList.value(i));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
191

192 193 194 195 196 197
                            if ( minDist > currentDist ) {
                                minDist         = currentDist;
                                minDistIndex    = i;
                            }
                        }
                    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
198

Valentin Platzgummer's avatar
Valentin Platzgummer committed
199
                    //qDebug("MinDistIndex: %i", minDistIndex);
200 201 202 203 204 205 206 207 208 209 210 211
                    QGeoCoordinate protoCurrentVertex = intersectionList.value(minDistIndex);
                    // take numerical erros into account
                    if (protoCurrentVertex.distanceTo(currentVertex) > WimaArea::numericalAccuracy) {
                        currentVertex                   = protoCurrentVertex;
                        QPair<int, int> neighbours      = neighbourList.value(minDistIndex);
                        protoNextVertex                 = crossPoly->vertexCoordinate(neighbours.second);
                        nextVertexIndex                 = neighbours.second;

                        // swap walker and cross poly
                        WimaArea* temp  = walkerPoly;
                        walkerPoly      = crossPoly;
                        crossPoly       = temp;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
212
                    } else {
213 214 215
                        currentVertex   = walkerPoly->vertexCoordinate(nextVertexIndex);
                        protoNextVertex = walkerPoly->vertexCoordinate(walkerPoly->nextVertexIndex(nextVertexIndex));
                        nextVertexIndex = walkerPoly->nextVertexIndex(nextVertexIndex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
216
                    }
217

218
                } else {
219 220 221 222 223 224 225
                    currentVertex   = walkerPoly->vertexCoordinate(nextVertexIndex);
                    protoNextVertex = walkerPoly->vertexCoordinate(walkerPoly->nextVertexIndex(nextVertexIndex));
                    nextVertexIndex = walkerPoly->nextVertexIndex(nextVertexIndex);
                }

                if (currentVertex == startVertex) {
                    return;
226
                }
227
            }
228 229 230

        } else {
            qWarning("WimaArea::joinPolygons(poly1, poly2): poly->count() < 3");
231
            return;
232
        }
233
}
234

Valentin Platzgummer's avatar
Valentin Platzgummer committed
235
void WimaArea::join(WimaArea &poly)
236
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
237 238
    WimaArea joinedArea;
    join(*this, poly, joinedArea);
239 240 241
    this->setPath(joinedArea.path());
    return;
}
242

243
bool WimaArea::isDisjunct(QList<WimaArea *>* polyList)
244 245 246 247
{
    // needs improvement
    if (polyList != nullptr){
        for (int i = 0;i < polyList->size()-1; i++) {
248
            WimaArea* currPoly = polyList->value(i);
249
            for (int j = i+1; i < polyList->size(); j++) {
250
                if (isDisjunct(currPoly, polyList->value(j))) {
251 252 253 254 255 256 257 258 259 260
                    return false;
                }
            }
        }
        return true;
    } else {
        qWarning("WimaArea::isDisjunct(polyList): polyList == nullptr!");
        return false;
    }
}
261

262
bool WimaArea::isDisjunct(WimaArea *poly1, WimaArea *poly2)
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
{
    if (poly1 != nullptr && poly2 != nullptr) {
        QGCMapPolygon* poly1Copy = new QGCMapPolygon(this);
        poly1Copy->setPath(poly1->path());
        poly1Copy->offset(numericalAccuracy);// take numerical errors in account
        for(int i = 0; i < poly2->count(); i++){
            if (poly1Copy->containsCoordinate(poly2->vertexCoordinate(i))){
                return false;
            }
        }
        return true;
    } else {
        qWarning("WimaArea::isDisjunct(poly1, poly2):  poly1 == nullptr || poly2 == nullptr!");
        return false;
    }
278 279
}

280
int WimaArea::nextVertexIndex(int index) const
281 282 283 284 285 286 287 288 289 290 291
{
    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;
    }
}

292
int WimaArea::previousVertexIndex(int index) const
293 294 295 296 297 298 299 300 301 302 303
{
    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;
    }
}

304
bool WimaArea::intersects(const QGCMapPolyline &line1, const QGCMapPolyline &line2, QGeoCoordinate &intersectionPt)
305
{
306 307

        if (line1.count() == 2 && line2.count() == 2 ) {
308 309 310
            QPointF pt11(0, 0);

            double x, y, z;
311 312
            QGeoCoordinate origin = line1.vertexCoordinate(0);
            convertGeoToNed(line1.vertexCoordinate(1), origin, &x, &y, &z);
313 314 315 316 317
            QPointF pt12(x, y);

            QLineF kartLine1(pt11, pt12);


318
            convertGeoToNed(line2.vertexCoordinate(0), origin, &x, &y, &z);
319 320
            QPointF pt21(x, y);

321
            convertGeoToNed(line2.vertexCoordinate(1), origin, &x, &y, &z);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
322
            QPointF pt22(x, y);;
323 324 325 326

            QLineF kartLine2(pt21, pt22);

            QPointF intersectionPoint;
327
            if (kartLine1.intersect(kartLine2, &intersectionPoint) == QLineF::BoundedIntersection) {
328
                convertNedToGeo(intersectionPoint.x(), intersectionPoint.y(), origin.altitude(), origin, &intersectionPt);
329
                return true;
330
            } else {
331
                return false;
332 333 334 335
            }


        } else {
336 337
            qWarning("WimaArea::intersect(line1, line2):  line1->count() != 2 || line2->count() != 2!");
            return false;
338 339 340
        }
}

341
bool WimaArea::intersects(const QGCMapPolyline& line, const WimaArea& poly, QList<QGeoCoordinate>& intersectionList, QList<QPair<int, int> >& neighbourlist)
342
{
343 344
        neighbourlist.clear();
        intersectionList.clear();
345

346
        if (line.count() == 2 && poly.count() >= 3) {
347

348
            for (int i = 0; i < poly.count(); i++) {
349
                QGCMapPolyline polySegment;
350 351
                QGeoCoordinate currentVertex = poly.vertexCoordinate(i);
                QGeoCoordinate nextVertex = poly.vertexCoordinate(poly.nextVertexIndex(i));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
352 353
                polySegment.appendVertex(currentVertex);
                polySegment.appendVertex(nextVertex);
354

355
                QGeoCoordinate intersectionPoint;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
356
                //bool retVal2 = intersects(line, line, &intersectionPoint);
357
                bool retVal = intersects(line, polySegment, intersectionPoint);
358 359 360


                if (retVal != false){
361
                    intersectionList.append(intersectionPoint);
362

363 364
                    QPair<int, int>     neighbours;
                    neighbours.first    = i;
365 366
                    neighbours.second   = poly.nextVertexIndex(i);
                    neighbourlist.append(neighbours);
367 368
                }
            }
369

370
            if (intersectionList.count() > 0) {
371 372 373 374
                return true;
            } else {
                return false;
            }
375 376
        } else {
            qWarning("WimaArea::intersects(line, poly): line->count() != 2 || poly->count() < 3");
377
            return false;
378 379 380
        }
}

381
double WimaArea::distInsidePoly(const QGeoCoordinate &c1, const QGeoCoordinate &c2, const WimaArea &poly)
382
{
383
        WimaArea bigPoly(poly);
384
        bigPoly.offset(0.1); // hack to compensate for numerical issues, migh be replaced in the future...
385
        if ( bigPoly.containsCoordinate(c1) && bigPoly.containsCoordinate(c2)) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
386 387 388 389
            QList<QGeoCoordinate>   intersectionList;
            QList<QPair<int, int>>  neighbourlist;
            QGCMapPolyline line;

390 391 392
            line.appendVertex(c1);
            line.appendVertex(c2);
            intersects(line, bigPoly, intersectionList, neighbourlist);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
393 394 395

            //if ( intersectionList.size() == (c1InPolyRim || c2InPolyRim ? 2:0) ){
            if ( intersectionList.size() == 0 ){
396
                return c1.distanceTo(c2);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
397 398
            } else {
                return std::numeric_limits<qreal>::infinity();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
399 400
            }

401 402
        } else {
            return std::numeric_limits<qreal>::infinity();
403 404 405
        }
}

406
void WimaArea::dijkstraPath(const QGeoCoordinate &start, const QGeoCoordinate &end, const WimaArea &poly, QList<QGeoCoordinate> &dijkstraPath)
407
{
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
    struct Node{
        QGeoCoordinate coordinate;
        double distance = std::numeric_limits<qreal>::infinity();
        Node* predecessorNode = nullptr;
    };

    QList<Node> nodeList;
    QList<Node*> workingSet;


    // initialize
    // start
    Node startNode;
    startNode.coordinate    = start;
    startNode.distance      = 0;
    nodeList.append(startNode);

    //poly
    for (int i = 0; i < poly.count(); i++) {
        Node node;
        node.coordinate = poly.vertexCoordinate(i);
        nodeList.append(node);
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
431

432 433 434 435
    //end
    Node endNode;
    endNode.coordinate  = end;
    nodeList.append(endNode);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
436

437 438 439 440 441
    // working set
    for (int i = 0; i < nodeList.size(); i++) {
        Node* nodePtr = &nodeList[i];
        workingSet.append(nodePtr);
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
442 443


444 445 446 447 448 449 450 451 452 453 454 455
    // Dijkstra Algorithm
    // https://de.wikipedia.org/wiki/Dijkstra-Algorithmus
    while (workingSet.size() > 0) {
        // serach Node with minimal distance
        double minDist = std::numeric_limits<qreal>::infinity();
        int minDistIndex = 0;
        for (int i = 0; i < workingSet.size(); i++) {
            Node* node = workingSet.value(i);
            double dist = node->distance;
            if (dist < minDist) {
                minDist = dist;
                minDistIndex = i;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
456
            }
457 458
        }
        Node* u = workingSet.takeAt(minDistIndex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
459 460


461 462 463
        //update distance
        for (int i = 0; i < workingSet.size(); i++) {
            Node* v = workingSet[i];
Valentin Platzgummer's avatar
Valentin Platzgummer committed
464

465 466 467 468 469 470
            // is neighbour?
            double dist = distInsidePoly(u->coordinate, v->coordinate, poly);
            double alternative = u->distance + dist;
            if (alternative < v->distance)  {
                v->distance         = alternative;
                v->predecessorNode  = u;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
471 472 473
            }
        }

474
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
475

476 477 478 479 480 481
    // create path
    Node* Node = &nodeList.last();
    if (Node->predecessorNode == nullptr) {
        qWarning("WimaArea::dijkstraPath(): Error, no path found!");
        return;
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
482

483 484 485 486 487 488 489
    while (1) {
        dijkstraPath.prepend(Node->coordinate);

        //Update Node
        Node = Node->predecessorNode;
        if (Node == nullptr) {
            break;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
490
        }
491 492
    }
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
493

494 495 496 497 498 499 500
void WimaArea::saveToJson(QJsonObject &json)
{
    this->QGCMapPolygon::saveToJson(json);
    json[maxAltitudeName]   = _maxAltitude;
    json[areaTypeName]      = wimaAreaName;
    // add WimaVehicle if necessary..
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
501

502 503 504 505 506 507 508
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();
            return true;
        } else {
509
            errorString.append(tr("Could not load Maximum Altitude value!\n"));
510 511
            return false;
        }
512
    } else {
513 514
        qWarning() << errorString;
        return false;
515 516
    }
}
517