From d61b7b132057f6902eca1e8b7c9e93cf4c484038 Mon Sep 17 00:00:00 2001 From: Valentin Platzgummer Date: Mon, 15 Jul 2019 22:20:33 +0200 Subject: [PATCH] planicalc and polycalc edited --- src/Wima/Circle.cc | 4 +- src/Wima/PlanimetryCalculus.cc | 115 ++++++++- src/Wima/PlanimetryCalculus.h | 22 +- src/Wima/PolygonCalculus.cc | 323 ++++++++++++++++++++++++ src/Wima/PolygonCalculus.h | 17 ++ src/Wima/SphereCalculus.cc | 436 --------------------------------- src/Wima/SphereCalculus.h | 20 +- 7 files changed, 467 insertions(+), 470 deletions(-) diff --git a/src/Wima/Circle.cc b/src/Wima/Circle.cc index ab3bb19f7..0bb50a0c4 100644 --- a/src/Wima/Circle.cc +++ b/src/Wima/Circle.cc @@ -127,13 +127,13 @@ QPolygonF Circle::approximateSektor(double angleDiscretisation, double alpha1, d double currentAngle = alpha1; // rotate the vertex numberOfCorners-1 times add the origin and append to the polygon. while(currentAngle < alpha2) { - PlanimetryCalculus::rotatePoint(vertex, currentAngle); + PlanimetryCalculus::rotate(vertex, currentAngle); polygon.append(vertex + _circleOrigin); currentAngle = PlanimetryCalculus::truncateAngle(currentAngle + angleDiscretisation); } // append last point if necessarry - PlanimetryCalculus::rotatePoint(vertex, alpha2); + PlanimetryCalculus::rotate(vertex, alpha2); vertex = vertex + _circleOrigin; if ( !qFuzzyIsNull(PlanimetryCalculus::distance(polygon.first(), vertex)) && !qFuzzyIsNull(PlanimetryCalculus::distance(polygon.last(), vertex )) ){ diff --git a/src/Wima/PlanimetryCalculus.cc b/src/Wima/PlanimetryCalculus.cc index 239905211..442d551d5 100644 --- a/src/Wima/PlanimetryCalculus.cc +++ b/src/Wima/PlanimetryCalculus.cc @@ -19,7 +19,7 @@ namespace PlanimetryCalculus { double angleWLDegree = line.angle(); // angle between wold and line coordinate system QPointF originCircleL = circle.origin() - translationVector; - rotatePoint(originCircleL, -angleWLDegree); // circle origin in line corrdinate system + rotate(originCircleL, -angleWLDegree); // circle origin in line corrdinate system double y = originCircleL.y(); double r = circle.radius(); @@ -31,7 +31,7 @@ namespace PlanimetryCalculus { if (x_ori >= 0 && x_ori <= line.length()) { if (calcInstersect) { QPointF intersectionPt = QPointF(x_ori, 0); - rotatePoint(intersectionPt, angleWLDegree); + rotate(intersectionPt, angleWLDegree); intersectionPoints.append(intersectionPt + translationVector); } @@ -50,7 +50,7 @@ namespace PlanimetryCalculus { if (x1 >= 0 && x1 <= line.length()) { // check if intersection point is on the line if (calcInstersect) { QPointF intersectionPt = QPointF(x1, 0); // first intersection point (line system) - rotatePoint(intersectionPt, angleWLDegree); + rotate(intersectionPt, angleWLDegree); intersectionPoints.append(intersectionPt + translationVector); // transform (to world system) and append first intersection point } doesIntersect = true; @@ -58,7 +58,7 @@ namespace PlanimetryCalculus { if (x2 >= 0 && x2 <= line.length()) { // check if intersection point is on the line if (calcInstersect) { QPointF intersectionPt = QPointF(x2, 0); // second intersection point (line system) - rotatePoint(intersectionPt, angleWLDegree); + rotate(intersectionPt, angleWLDegree); intersectionPoints.append(intersectionPt + translationVector); // transform (to world system) and append second intersection point } doesIntersect = true; @@ -76,7 +76,7 @@ namespace PlanimetryCalculus { \fn void rotatePoint(QPointF &point, double alpha) Rotates the \a point counter clockwisely by the angle \a alpha (in radiants). */ - void rotatePoint(QPointF &point, double alpha) + void rotate(QPointF &point, double alpha) { if (!point.isNull()) { double x = point.x(); @@ -87,10 +87,10 @@ namespace PlanimetryCalculus { } } - void rotatePoint(QList &points, double alpha) + void rotate(QList &points, double alpha) { for (int i = 0; i < points.size(); i++) { - rotatePoint(points[i], alpha); + rotate(points[i], alpha); } } @@ -98,15 +98,15 @@ namespace PlanimetryCalculus { \fn void rotatePointDegree(QPointF &point, double alpha) Rotates the \a point counter clockwisely by the angle \a alpha (in degrees). */ - void rotatePointDegree(QPointF &point, double alpha) + void rotateDegree(QPointF &point, double alpha) { - rotatePoint(point, alpha/180*M_PI); + rotate(point, alpha/180*M_PI); } - void rotatePointDegree(QList &points, double alpha) + void rotateDegree(QList &points, double alpha) { for (int i = 0; i < points.size(); i++) { - rotatePointDegree(points[i], alpha); + rotateDegree(points[i], alpha); } } @@ -211,7 +211,7 @@ namespace PlanimetryCalculus { intersectionPoints.append(QPointF(x, -y)); } // Transform the coordinate to the world coordinate system. Alpha is the angle between world and circle1 coordinate system. - rotatePoint(intersectionPoints, alpha); + rotate(intersectionPoints, alpha); return returnValue; } @@ -292,6 +292,97 @@ namespace PlanimetryCalculus { } + /*! + * \fn IntersectType intersects(const QLineF &line1, const QLineF &line2, QPointF &intersectionPt); + * Determines wheter \a line1 and \a line2 intersect and of which type the intersection is. + * Stores the intersection point in \a intersectionPt + * Returns \c NoIntersection if no intersection occured, \c EdgeIntersection if a interisSelfIntersectingsection occured near a endpoint of a line (within epsilonMeter), or \c InteriorIntersection else. + * + * \sa QGeoCoordinate + */ + IntersectType intersects(const QLineF &line1, const QLineF &line2, QPointF &intersectionPt) + { + QPointF pt11(0, 0); + + QPointF intersectionPoint; + + // continue here + } + + /*! + * \overload QList intersects(const QList &polygon, const QList &line, QList &intersectionList, QList > &neighbourList) + * Checks if \a polygon intersect with \a line. + * Stores the intersection points in \a intersectionList. + * + * Stores the indices of the closest two \a area vetices for each of coorespoinding intersection points in \a neighbourList. * + * For example if an intersection point is found between the first and the second vertex of the \a area the intersection point will + * be stored in \a intersectionList and the indices 1 and 2 will be stored in \a neighbourList. + * \a neighbourList has entries of type \c {QPair}, where \c{pair.first} would contain 1 and \c{pair.second} would contain 2, when + * relating to the above example. + * + * Returns the \c IntersectionType of each intersection point within a QList. + * + * \sa QPair, QList + */ + QList intersects(const QList &polygon, const Line &line, QList &intersectionList, QList > &neighbourList) + { + if (polygon.size() >= 3) { // are line a proper line and poly a proper poly?other, + intersectionList.clear(); + neighbourList.clear(); + QList intersectionTypeList; + // Assemble a line form each tow consecutive polygon vertices and check whether it intersects with line + for (int i = 0; i < polygon.size(); i++) { + + Line interatorLine; + QGeoCoordinate currentVertex = polygon[i]; + QGeoCoordinate nextVertex = polygon[nextPolygonIndex(polygon.size(), i)]; + interatorLine.first = currentVertex; + interatorLine.second = nextVertex; + + QGeoCoordinate intersectionPoint; + IntersectionType returnValue = intersects(line, interatorLine, intersectionPoint); + if ( returnValue == IntersectionType::EdgeIntersection + || returnValue == IntersectionType::InteriorIntersection) { + intersectionList.append(intersectionPoint); + + QPair neighbours; + neighbours.first = i; + neighbours.second = nextPolygonIndex(polygon.size(), i); + neighbourList.append(neighbours); + + intersectionTypeList.append(returnValue); + } + } + + if (intersectionList.count() > 0) { + return intersectionTypeList; + } else { + return QList(); + } + } else { + qWarning("WimaArea::intersects(line, poly): line->count() != 2 || poly->count() < 3"); + return QList(); + } + } + + /*! + * \overload bool intersects(const QList &polygon, const QList &line) + * Returns \c true if any intersection between \a polygon and \a line exists, \c false else. + * + * \sa QPair, QList + */ + bool intersects(const QList &polygon, const Line &line) + { + QList dummyGeo; + QList> dummyNeighbour; + intersects(polygon, line, dummyGeo, dummyNeighbour); + + if (dummyGeo.size() > 0) + return true; + + return false; + } + } // end namespace PlanimetryCalculus diff --git a/src/Wima/PlanimetryCalculus.h b/src/Wima/PlanimetryCalculus.h index 12e257463..49ffd568a 100644 --- a/src/Wima/PlanimetryCalculus.h +++ b/src/Wima/PlanimetryCalculus.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -15,17 +16,30 @@ namespace PlanimetryCalculus { NoIntersection, Tangent, Secant, // Circle Line Intersetion + EdgeCornerIntersection, EdgeEdgeIntersection, CornerCornerIntersection, + LinesParallel, LinesEqual, // Line Line intersection + Error // general }; - void rotatePoint(QPointF &point, double alpha); - void rotatePoint(QList &point, double alpha); - void rotatePointDegree(QPointF &point, double alpha); - void rotatePointDegree(QList &points, double alpha); + void rotate(QPointF &point, double alpha); + void rotate(QList &point, double alpha); + void rotate(QLineF &point, double alpha); + void rotate(QPolygonF &point, double alpha); + void rotateDegree(QPointF &point, double alpha); + void rotateDegree(QList &points, double alpha); + void rotateDegree(QLineF &point, double alpha); + void rotateDegree(QPolygonF &point, double alpha); IntersectType intersects(const Circle &circle1, const Circle &circle2); IntersectType intersects(const Circle &circle1, const Circle &circle2, QList &intersectionPoints); IntersectType intersects(const Circle &circle, const QLineF &line); IntersectType intersects(const Circle &circle, const QLineF &line, QList &intersectionPoints); + + + IntersectType intersects(const QLineF &line1, const QLineF &line2, QPointF &intersectionPt); + QList intersects(const QPolygonF &polygon, const QLineF &line, QList &intersectionList, QList> &neighbourList); + IntersectType intersects(const QPolygonF &polygon, const QLineF &line); + double distance(const QPointF &p1, const QPointF p2); double angle(const QPointF &p1, const QPointF p2); double angleDegree(const QPointF &p1, const QPointF p2); diff --git a/src/Wima/PolygonCalculus.cc b/src/Wima/PolygonCalculus.cc index 55bc1ea3b..8d3d03c25 100644 --- a/src/Wima/PolygonCalculus.cc +++ b/src/Wima/PolygonCalculus.cc @@ -1,2 +1,325 @@ #include "PolygonCalculus.h" +namespace PolygonCalculus { + namespace { + + } // end anonymous namespace + + /*! + * \fn int closestVertexIndex(const QPolygonF &polygon, const QPointF &coordinate) + * Returns the vertex index of \a polygon which has the least distance to \a coordinate. + * + * \sa QPointF, QPolygonF + */ + int closestVertexIndex(const QPolygonF &polygon, const QPointF &coordinate) + { + if (polygon.size() == 0) { + qWarning("Path is empty!"); + return -1; + }else if (polygon.size() == 1) { + return 0; + }else { + int index = 0; // the index of the closest vertex + double min_dist = PlanimetryCalculus::distance(coordinate, polygon[index]); + for(int i = 1; i < polygon.size(); i++){ + double dist = PlanimetryCalculus::distance(coordinate, polygon[i]); + if (dist < min_dist){ + min_dist = dist; + index = i; + } + } + return index; + } + } + + /*! + * \fn QPointF closestVertex(const QPolygonF &polygon, const QPointF &coordinate); + * Returns the vertex of \a polygon with the least distance to \a coordinate. + * + * \sa QPointF, QPolygonF + */ + QPointF closestVertex(const QPolygonF &polygon, const QPointF &coordinate) + { + int index = closestVertexIndex(polygon, coordinate); + if (index >=0 ) { + return polygon[index]; + } else { + return QPointF(); + } + } + + /*! + * \fn int nextPolygonIndex(int pathsize, int index) + * Returns the index of the next vertex (of a polygon), which is \a index + 1 if \a index is smaller than \c {\a pathsize - 1}, + * or 0 if \a index equals \c {\a pathsize - 1}, or -1 if the \a index is out of bounds. + * \note \a pathsize is usually obtained by invoking polygon.size() + */ + int nextPolygonIndex(int pathsize, int index) + { + if (index >= 0 && index < pathsize-1) { + return index + 1; + } else if (index == pathsize-1) { + return 0; + } else { + qWarning("nextPolygonIndex(): Index out of bounds! index:count = %i:%i", index, pathsize); + return -1; + } + } + + /*! + * \fn int previousPolygonIndex(int pathsize, int index) + * Returns the index of the previous vertex (of a polygon), which is \a index - 1 if \a index is larger 0, + * or \c {\a pathsize - 1} if \a index equals 0, or -1 if the \a index is out of bounds. + * \note pathsize is usually obtained by invoking polygon.size() + */ + int previousPolygonIndex(int pathsize, int index) + { + if (index > 0 && index = 3 && polygon2.size() >= 3) { + + if ( !isSimplePolygon(polygon1) || !isSimplePolygon(polygon2)) { + return JoinPolygonError::NotSimplePolygon; + } + if ( !hasClockwiseWinding(polygon1)) { + reversePath(polygon1); + } + if ( !hasClockwiseWinding(polygon2)) { + reversePath(polygon2); + } + + const QPolygonF *walkerPoly = &polygon1; // "walk" on this polygon towards higher indices + const QPolygonF *crossPoly = &polygon2; // check for crossings with this polygon while "walking" + // and swicht to this polygon on a intersection, + // continue to walk towards higher indices + + // 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->size(); i++) { + if ( !crossPoly->contains(walkerPoly->value(i)) ) { + crossContainsWalker = false; + startIndex = i; + break; + } + } + + if ( crossContainsWalker == true) { + joinedPolygon.append(*crossPoly); + return JoinPolygonError::PolygonJoined; + } + + QPointF currentVertex = walkerPoly->value(startIndex); + QPointF startVertex = currentVertex; + // possible nextVertex (if no intersection between currentVertex and protoVertex with crossPoly) + int nextVertexIndex = nextPolygonIndex(walkerPoly->size(), startIndex); + QPointF protoNextVertex = walkerPoly->value(nextVertexIndex); + bool switchHappenedPreviously = false; // means switch between crossPoly and walkerPoly + while (1) { + //qDebug("nextVertexIndex: %i", nextVertexIndex); + joinedPolygon.append(currentVertex); + + QLineF walkerPolySegment; + walkerPolySegment.setP1(currentVertex); + walkerPolySegment.setP2(protoNextVertex); + + QList> neighbourList; + QList intersectionList; + //qDebug("IntersectionList.size() on init: %i", intersectionList.size()); + PlanimetryCalculus::intersects(*crossPoly, walkerPolySegment, intersectionList, neighbourList); + + //qDebug("IntersectionList.size(): %i", intersectionList.size()); + + if (intersectionList.size() >= 1) { + int minDistIndex = 0; + + // find the vertex with the least distance to currentVertex + if (intersectionList.size() > 1) { + double minDist = PlanimetryCalculus::distance(currentVertex, intersectionList[minDistIndex]); + for (int i = 1; i < intersectionList.size(); i++) { + double currentDist = PlanimetryCalculus::distance(currentVertex, intersectionList[i]); + + if ( minDist > currentDist ) { + minDist = currentDist; + minDistIndex = i; + } + } + } + + //qDebug("MinDistIndex: %i", minDistIndex); + QPointF protoCurrentVertex = intersectionList.value(minDistIndex); + // If the currentVertex is a intersection point a intersection ocisSelfIntersectingcures with the + // crossPoly. This would cause unwanted switching of crossPoly and walkerPoly, thus intersections + // are only token in to account if they occur beyond a certain distance (_epsilonMeter) or no switching happend in the + // previous step. + + if (switchHappenedPreviously == false){ + //|| protoCurrentVertex.distanceTo(currentVertex) > _epsilonMeter) { + currentVertex = protoCurrentVertex; + QPair neighbours = neighbourList.value(minDistIndex); + protoNextVertex = crossPoly->value(neighbours.second); + nextVertexIndex = neighbours.second; + + // switch walker and cross poly + const QPolygonF *temp = walkerPoly; + walkerPoly = crossPoly; + crossPoly = temp; + + switchHappenedPreviously = true; + } else { + currentVertex = walkerPoly->value(nextVertexIndex); + nextVertexIndex = nextPolygonIndex(walkerPoly->size(), nextVertexIndex); + protoNextVertex = walkerPoly->value(nextVertexIndex); + + switchHappenedPreviously = false; + } + + } else { + currentVertex = walkerPoly->value(nextVertexIndex); + nextVertexIndex = nextPolygonIndex(walkerPoly->size(), nextVertexIndex); + protoNextVertex = walkerPoly->value(nextVertexIndex); + } + + if (currentVertex == startVertex) { + if (polygon1.size() == joinedPolygon.size()) { + return JoinPolygonError::Disjoint; + } else { + return JoinPolygonError::PolygonJoined; + } + } + } + + } else { + return JoinPolygonError::PathSizeLow; + } + } + + /*! + * \fn bool isSimplePolygon(const QPolygonF &polygon); + * Returns \c true if \a polygon is a \l {Simple Polygon}, \c false else. + * \note A polygon is a Simple Polygon iff it is not self intersecting. + */ + bool isSimplePolygon(const QPolygonF &polygon) + { + int i = 0; + if (polygon.size() > 3) { + // check if any edge of the area (formed by two adjacent vertices) intersects with any other edge of the area + while(i < polygon.size()-1) { + QPointF refBeginCoordinate = polygon[i]; + QPointF refEndCoordinate = polygon[nextPolygonIndex(polygon.size(), i)]; + QLineF refLine; + refLine.setP1(refBeginCoordinate); + refLine.setP2(refEndCoordinate); + int j = nextPolygonIndex(polygon.size(), i); + while(j < polygon.size()) { + + QPointF intersectionPt; + QLineF iteratorLine; + iteratorLine.setP1(polygon[j]); + iteratorLine.setP2(polygon[nextPolygonIndex(polygon.size(), j)]); + if ( PlanimetryCalculus::intersects(refLine, iteratorLine, intersectionPt) + == PlanimetryCalculus::InteriorIntersection){ + return false; + } + j++; + } + i++; + } + } + return true; + } + + /*! + * \fn bool hasClockwiseWinding(const QPolygonF &polygon) + * Returns \c true if \a path has clockwise winding, \c false else. + */ + bool hasClockwiseWinding(const QPolygonF &polygon) + { + if (polygon.size() <= 2) { + return false; + } + + double sum = 0; + for (int i=0; i polygon) + * Returns the distance between the coordinate \a c1 and coordinate \a c2, or infinity if the shortest path between + * the two coordinates is not fully inside the \a area. + * + * \sa QGeoCoordinate + */ + double distanceInsidePolygon(const QPointF &c1, const QPointF &c2, QPolygonF polygon) + { + offsetPolygon(polygon, 0.1);// hack to compensate for numerical issues, migh be replaced in the future... + if ( polygon.contains(c1) && polygon.contains(c2)) { + QList intersectionList; + QList> neighbourlist; + QLineF line; + + line.setP1(c1); + line.setP2(c2); + PlanimetryCalculus::intersects(polygon, line, intersectionList, neighbourlist); + + if ( intersectionList.size() == 0 ){ // if an intersection was found the path between c1 and c2 is not fully inside polygon. + return PlanimetryCalculus::distance(c1, c2); + } else { + return std::numeric_limits::infinity(); + } + + } else { + return std::numeric_limits::infinity(); + } + } +} // end PolygonCalculus namespace + diff --git a/src/Wima/PolygonCalculus.h b/src/Wima/PolygonCalculus.h index 96cbaabbf..49f250db6 100644 --- a/src/Wima/PolygonCalculus.h +++ b/src/Wima/PolygonCalculus.h @@ -2,8 +2,25 @@ #define POLYGONCALCULUS_H +#include +#include + +#include "PlanimetryCalculus.h" + namespace PolygonCalculus { + enum JoinPolygonError { NotSimplePolygon, PolygonJoined, Disjoint, PathSizeLow}; + + int closestVertexIndex (const QPolygonF &polygon, const QPointF &coordinate); + QPointF closestVertex (const QPolygonF &polygon, const QPointF &coordinate); + int nextPolygonIndex (int pathsize, int index); + int previousPolygonIndex(int pathsize, int index); + JoinPolygonError joinPolygon (QPolygonF polygon1, QPolygonF polygon2, QPolygonF &joinedPolygon); + bool isSimplePolygon (const QPolygonF &polygon); + bool hasClockwiseWinding (const QPolygonF &path); + void reversePath (QPolygonF &path); + bool offsetPolygon (QPolygonF &polygon, double offset); + double distanceInsidePolygon (const QPointF &c1, const QPointF &c2, QPolygonF polygon); } #endif // POLYGONCALCULUS_H diff --git a/src/Wima/SphereCalculus.cc b/src/Wima/SphereCalculus.cc index 51d96f36c..bfc0db0cc 100644 --- a/src/Wima/SphereCalculus.cc +++ b/src/Wima/SphereCalculus.cc @@ -25,339 +25,9 @@ double SphereCalculus::epsilonMeter() const return _epsilonMeter; } -/*! - * \fn int SphereCalculus::closestVertexIndex(const QList &path, const QGeoCoordinate &coordinate) - * which has the least distance to \a coordinate. - * - * \sa QGeoCoordinate - */ -int SphereCalculus::closestVertexIndex(const QList &path, const QGeoCoordinate &coordinate) -{ - if (path.size() == 0) { - qWarning("Path is empty!"); - return -1; - }else if (path.size() == 1) { - return 0; - }else { - int index = 0; // the index of the closest vertex - double min_dist = coordinate.distanceTo(path[index]); - for(int i = 1; i < path.size(); i++){ - double dist = coordinate.distanceTo(path[i]); - if (dist < min_dist){ - min_dist = dist; - index = i; - } - } - return index; - } -} - -/*! - * \fn QGeoCoordinate SphereCalculus::closestVertex(const QList &path, const QGeoCoordinate &coordinate) - * Returns the vertex of the \a path with the least distance to \a coordinate. - * - * \sa QGeoCoordinate - */ -QGeoCoordinate SphereCalculus::closestVertex(const QList &path, const QGeoCoordinate &coordinate) -{ - int index = closestVertexIndex(path, coordinate); - if (index >=0 ) { - return path[index]; - } else { - return QGeoCoordinate(); - } -} - -/*! - * \fn int SphereCalculus::nextPolygonIndex(int pathsize, int index) - * Returns the index of the next vertex (of a polygon), which is \a index + 1 if \a index is smaller than \c {\a pathsize - 1}, - * or 0 if \a index equals \c {\a pathsize - 1}, or -1 if the \a index is out of bounds. - * \note \a pathsize is usually obtained by invoking polygon.size() - */ -int SphereCalculus::nextPolygonIndex(int pathsize, int index) -{ - if (index >= 0 && index < pathsize-1) { - return index + 1; - } else if (index == pathsize-1) { - return 0; - } else { - qWarning("SphereCalculus::nextPolygonIndex(): Index out of bounds! index:count = %i:%i", index, pathsize); - return -1; - } -} - -/*! - * \fn int SphereCalculus::previousPolygonIndex(int pathsize, int index) - * Returns the index of the previous vertex (of a polygon), which is \a index - 1 if \a index is larger 0, - * or \c {\a pathsize - 1} if \a index equals 0, or -1 if the \a index is out of bounds. - * \note pathsize is usually obtained by invoking polygon.size() - */ -int SphereCalculus::previousPolygonIndex(int pathsize, int index) -{ - if (index > 0 && index polygon1, QList polygon2, QList &joinedPolygon) - * Joins \a polygon1 and \a polygon2 such that a \l {Simple Polygon} is created. - * Stores the result inside \a joinedArea. - * Returns \c NotSimplePolygon1 if \a polygon1 isn't a Simple Polygon, \c NotSimplePolygon2 if \a polygon2 isn't a Simple Polygon, \c Disjoind if the polygons are disjoint, - * \c PathSizeLow if at least one polygon has a size samler than 3, or \c PolygonJoined on success. - * The algorithm will be able to join the areas, if either their edges intersect with each other, - * or one area is inside the other. - */ -SphereCalculus::JoinPolygonError SphereCalculus::joinPolygon(QList polygon1, QList polygon2, QList &joinedPolygon) -{ - if (polygon1.size() >= 3 && polygon2.size() >= 3) { - - if ( isSimplePolygon(polygon1) ) { - qWarning("Polygon 1 is self intersecting.\n"); - return SphereCalculus::JoinPolygonError::NotSimplePolygon1; - } - if ( isSimplePolygon(polygon2) ) { - qWarning("Area 2 is self intersecting.\n"); - return SphereCalculus::JoinPolygonError::NotSimplePolygon2; - } - - joinedPolygon.clear(); - - if ( !hasClockwiseWinding(polygon1)) { - reversePath(polygon1); - } - if ( !hasClockwiseWinding(polygon2)) { - reversePath(polygon2); - } - - const QList *walkerPoly = &polygon1; // "walk" on this polygon towards higher indices - const QList *crossPoly = &polygon2; // check for crossings with this polygon while "walking" - // and swicht to this polygon on a intersection, - // continue to walk towards higher indices - - // 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->size(); i++) { - if ( !crossPoly->contains(walkerPoly->value(i)) ) { - crossContainsWalker = false; - startIndex = i; - break; - } - } - - if ( crossContainsWalker == true) { - joinedPolygon.append(*crossPoly); - return SphereCalculus::JoinPolygonError::PolygonJoined; - } - - QGeoCoordinate currentVertex = walkerPoly->value(startIndex); - QGeoCoordinate startVertex = currentVertex; - // possible nextVertex (if no intersection between currentVertex and protoVertex with crossPoly) - int nextVertexIndex = nextPolygonIndex(walkerPoly->size(), startIndex); - QGeoCoordinate protoNextVertex = walkerPoly->value(nextVertexIndex); - bool switchHappenedPreviously = false; // means switch between crossPoly and walkerPoly - while (1) { - //qDebug("nextVertexIndex: %i", nextVertexIndex); - joinedPolygon.append(currentVertex); - - Line walkerPolySegment; - walkerPolySegment.first = currentVertex; - walkerPolySegment.second = protoNextVertex; - - QList> neighbourList; - QList intersectionList; - //qDebug("IntersectionList.size() on init: %i", intersectionList.size()); - intersects(*crossPoly, walkerPolySegment, intersectionList, neighbourList); - - //qDebug("IntersectionList.size(): %i", intersectionList.size()); - - if (intersectionList.size() >= 1) { - int minDistIndex = 0; - - // find the vertex with the least distance to currentVertex - 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)); - - if ( minDist > currentDist ) { - minDist = currentDist; - minDistIndex = i; - } - } - } - - //qDebug("MinDistIndex: %i", minDistIndex); - QGeoCoordinate protoCurrentVertex = intersectionList.value(minDistIndex); - // If the currentVertex is a intersection point a intersection ocisSelfIntersectingcures with the - // crossPoly. This would cause unwanted switching of crossPoly and walkerPoly, thus intersections - // are only token in to account if they occur beyond a certain distance (_epsilonMeter) or no switching happend in the - // previous step. - - if (switchHappenedPreviously == false - || protoCurrentVertex.distanceTo(currentVertex) > _epsilonMeter) { - currentVertex = protoCurrentVertex; - QPair neighbours = neighbourList.value(minDistIndex); - protoNextVertex = crossPoly->value(neighbours.second); - nextVertexIndex = neighbours.second; - - // switch walker and cross poly - const QList *temp = walkerPoly; - walkerPoly = crossPoly; - crossPoly = temp; - - switchHappenedPreviously = true; - } else { - currentVertex = walkerPoly->value(nextVertexIndex); - nextVertexIndex = nextPolygonIndex(walkerPoly->size(), nextVertexIndex); - protoNextVertex = walkerPoly->value(nextVertexIndex); - - switchHappenedPreviously = false; - } - - } else { - currentVertex = walkerPoly->value(nextVertexIndex); - nextVertexIndex = nextPolygonIndex(walkerPoly->size(), nextVertexIndex); - protoNextVertex = walkerPoly->value(nextVertexIndex); - } - - if (currentVertex == startVertex) { - if (polygon1.size() == joinedPolygon.size()) { - return SphereCalculus::JoinPolygonError::Disjoint; - } else { - return SphereCalculus::JoinPolygonError::PolygonJoined; - } - } - } - - } else { - return SphereCalculus::JoinPolygonError::PathSizeLow; - } -} - -/*! - * \fn SphereCalculus::IntersectionType SphereCalculus::intersects(const QList &line1, const QList &line2, QGeoCoordinate &intersectionPt) - * Determines wheter \a line1 and \a line2 intersect and of which type the intersection is. - * Stores the intersection point in \a intersectionPt - * Returns \c NoIntersection if no intersection occured, \c EdgeIntersection if a interisSelfIntersectingsection occured near a endpoint of a line (within epsilonMeter), or \c InteriorIntersection else. - * - * \sa QGeoCoordinate - */ -SphereCalculus::IntersectionType SphereCalculus::intersects(const Line &line1, const Line &line2, QGeoCoordinate &intersectionPt) -{ - QPointF pt11(0, 0); - - double x, y, z; - QGeoCoordinate origin = line1.first; - convertGeoToNed(line1.second, origin, &x, &y, &z); - QPointF pt12(x, y); - - QLineF kartLine1(pt11, pt12); - - - convertGeoToNed(line2.first, origin, &x, &y, &z); - QPointF pt21(x, y); - convertGeoToNed(line2.second, origin, &x, &y, &z); - QPointF pt22(x, y);; - QLineF kartLine2(pt21, pt22); - QPointF intersectionPoint; - if (kartLine1.intersect(kartLine2, &intersectionPoint) == QLineF::BoundedIntersection) { - - convertNedToGeo(intersectionPoint.x(), intersectionPoint.y(), origin.altitude(), origin, &intersectionPt); - if ( intersectionPt.distanceTo(line1.first) < _epsilonMeter - || intersectionPt.distanceTo(line1.second) < _epsilonMeter - || intersectionPt.distanceTo(line2.first) < _epsilonMeter - || intersectionPt.distanceTo(line2.second) < _epsilonMeter ) - return SphereCalculus::IntersectionType::EdgeIntersection; - return SphereCalculus::IntersectionType::InteriorIntersection; - } - else { - return SphereCalculus::IntersectionType::NoIntersection; - } -} - -/*! - * \overload QList SphereCalculus::intersects(const QList &polygon, const QList &line, QList &intersectionList, QList > &neighbourList) - * Checks if \a polygon intersect with \a line. - * Stores the intersection points in \a intersectionList. - * - * Stores the indices of the closest two \a area vetices for each of coorespoinding intersection points in \a neighbourList. * - * For example if an intersection point is found between the first and the second vertex of the \a area the intersection point will - * be stored in \a intersectionList and the indices 1 and 2 will be stored in \a neighbourList. - * \a neighbourList has entries of type \c {QPair}, where \c{pair.first} would contain 1 and \c{pair.second} would contain 2, when - * relating to the above example. - * - * Returns the \c IntersectionType of each intersection point within a QList. - * - * \sa QPair, QList - */ -QList SphereCalculus::intersects(const QList &polygon, const Line &line, QList &intersectionList, QList > &neighbourList) -{ - if (polygon.size() >= 3) { // are line a proper line and poly a proper poly?other, - intersectionList.clear(); - neighbourList.clear(); - QList intersectionTypeList; - // Assemble a line form each tow consecutive polygon vertices and check whether it intersects with line - for (int i = 0; i < polygon.size(); i++) { - - Line interatorLine; - QGeoCoordinate currentVertex = polygon[i]; - QGeoCoordinate nextVertex = polygon[nextPolygonIndex(polygon.size(), i)]; - interatorLine.first = currentVertex; - interatorLine.second = nextVertex; - - QGeoCoordinate intersectionPoint; - SphereCalculus::IntersectionType returnValue = intersects(line, interatorLine, intersectionPoint); - if ( returnValue == SphereCalculus::IntersectionType::EdgeIntersection - || returnValue == SphereCalculus::IntersectionType::InteriorIntersection) { - intersectionList.append(intersectionPoint); - - QPair neighbours; - neighbours.first = i; - neighbours.second = nextPolygonIndex(polygon.size(), i); - neighbourList.append(neighbours); - - intersectionTypeList.append(returnValue); - } - } - - if (intersectionList.count() > 0) { - return intersectionTypeList; - } else { - return QList(); - } - } else { - qWarning("WimaArea::intersects(line, poly): line->count() != 2 || poly->count() < 3"); - return QList(); - } -} - -/*! - * \overload bool SphereCalculus::intersects(const QList &polygon, const QList &line) - * Returns \c true if any intersection between \a polygon and \a line exists, \c false else. - * - * \sa QPair, QList - */ -bool SphereCalculus::intersects(const QList &polygon, const Line &line) -{ - QList dummyGeo; - QList> dummyNeighbour; - intersects(polygon, line, dummyGeo, dummyNeighbour); - - if (dummyGeo.size() > 0) - return true; - - return false; -} /*! * \fn bool SphereCalculus::dijkstraPath(const QList &polygon, const QGeoCoordinate &c1, const QGeoCoordinate &c2, QList &dijkstraPath) @@ -470,113 +140,7 @@ SphereCalculus::DijkstraError SphereCalculus::dijkstraPath(const QList &polygon) - * Returns \c true if \a polygon is a \l {Simple Polygon}, \c false else. - * \note A polygon is a Simple Polygon iff it is not self intersecting. - */ -bool SphereCalculus::isSimplePolygon(const QList &polygon) -{ - int i = 0; - if (polygon.count() > 3) { - // check if any edge of the area (formed by two adjacent vertices) intersects with any other edge of the area - while(i < polygon.count()-1) { - QGeoCoordinate refBeginCoordinate = polygon[i]; - QGeoCoordinate refEndCoordinate = polygon[nextPolygonIndex(polygon.size(), i)]; - SphereCalculus::Line refLine; - refLine.first = refBeginCoordinate; - refLine.second = refEndCoordinate; - int j = nextPolygonIndex(polygon.size(), i); - while(j < polygon.size()) { - - QGeoCoordinate intersectionPt; - SphereCalculus::Line iteratorLine; - iteratorLine.first = polygon[j]; - iteratorLine.second = polygon[nextPolygonIndex(polygon.size(), j)]; - if ( intersects(refLine, iteratorLine, intersectionPt) - == SphereCalculus::IntersectionType::InteriorIntersection){ - return false; - } - j++; - } - i++; - } - } - return true; -} - -/*! - * \fn bool SphereCalculus::hasClockwiseWinding(const QList &path) - * Returns \c true if \a path has clockwise winding, \c false else. - */ -bool SphereCalculus::hasClockwiseWinding(const QList &path) -{ - if (path.size() <= 2) { - return false; - } - - double sum = 0; - for (int i=0; i &path) - * Reverses the path, i.e. changes the first Vertex with the last, the second with the befor last and so forth. - */ -void SphereCalculus::reversePath(QList &path) -{ - QList pathReversed; - for (int i = 0; i < path.size(); i++) { - pathReversed.prepend(path[i]); - } - path.clear(); - path.append(pathReversed); -} - -bool SphereCalculus::offsetPolygon(QList &polygon, double offset) -{ - -} -/*! - * \fn double SphereCalculus::distanceInsidePolygon(const QGeoCoordinate &c1, const QGeoCoordinate &c2, QList polygon) - * Returns the distance between the coordinate \a c1 and coordinate \a c2, or infinity if the shortest path between - * the two coordinates is not fully inside the \a area. - * - * \sa QGeoCoordinate - */ -double SphereCalculus::distanceInsidePolygon(const QGeoCoordinate &c1, const QGeoCoordinate &c2, QList polygon) -{ - offsetPolygon(polygon, 0.1);// hack to compensate for numerical issues, migh be replaced in the future... - if ( polygon.contains(c1) && polygon.contains(c2)) { - QList intersectionList; - QList> neighbourlist; - SphereCalculus::Line line; - - line.first = c1; - line.second = c2; - intersects(polygon, line, intersectionList, neighbourlist); - - if ( intersectionList.size() == 0 ){ // if an intersection was found the path between c1 and c2 is not fully inside polygon. - return c1.distanceTo(c2); - } else { - return std::numeric_limits::infinity(); - } - - } else { - return std::numeric_limits::infinity(); - } -} /*! \class SphereCalculus diff --git a/src/Wima/SphereCalculus.h b/src/Wima/SphereCalculus.h index 555ed73cf..d344e9b57 100644 --- a/src/Wima/SphereCalculus.h +++ b/src/Wima/SphereCalculus.h @@ -31,23 +31,11 @@ public: double epsilonMeter() const; // Member Methodes - int closestVertexIndex (const QList &path, const QGeoCoordinate &coordinate); - QGeoCoordinate closestVertex (const QList &path, const QGeoCoordinate &coordinate); - int nextPolygonIndex (int pathsize, int index); - int previousPolygonIndex(int pathsize, int index); - JoinPolygonError joinPolygon (QList polygon1, QList polygon2, - QList &joinedPolygon); - IntersectionType intersects (const Line &line1, const Line &line2, - QGeoCoordinate &intersectionPt); - QList intersects (const QList &polygon, const Line &line, - QList &intersectionList, QList> &neighbourList); - bool intersects (const QList &polygon, const Line &line); + + DijkstraError dijkstraPath (const QList &polygon, const QGeoCoordinate& start, const QGeoCoordinate& end, QList& dijkstraPath); - bool isSimplePolygon (const QList &polygon); - bool hasClockwiseWinding (const QList &path); - void reversePath (QList &path); - bool offsetPolygon (QList &polygon, double offset); + @@ -56,7 +44,7 @@ signals: public slots: private: - double distanceInsidePolygon (const QGeoCoordinate& c1, const QGeoCoordinate& c2, QList polygon); + double _epsilonMeter; // The accuracy used for distance calculations (unit: m). -- 2.22.0