Commit d61b7b13 authored by Valentin Platzgummer's avatar Valentin Platzgummer

planicalc and polycalc edited

parent 68512dd5
...@@ -127,13 +127,13 @@ QPolygonF Circle::approximateSektor(double angleDiscretisation, double alpha1, d ...@@ -127,13 +127,13 @@ QPolygonF Circle::approximateSektor(double angleDiscretisation, double alpha1, d
double currentAngle = alpha1; double currentAngle = alpha1;
// rotate the vertex numberOfCorners-1 times add the origin and append to the polygon. // rotate the vertex numberOfCorners-1 times add the origin and append to the polygon.
while(currentAngle < alpha2) { while(currentAngle < alpha2) {
PlanimetryCalculus::rotatePoint(vertex, currentAngle); PlanimetryCalculus::rotate(vertex, currentAngle);
polygon.append(vertex + _circleOrigin); polygon.append(vertex + _circleOrigin);
currentAngle = PlanimetryCalculus::truncateAngle(currentAngle + angleDiscretisation); currentAngle = PlanimetryCalculus::truncateAngle(currentAngle + angleDiscretisation);
} }
// append last point if necessarry // append last point if necessarry
PlanimetryCalculus::rotatePoint(vertex, alpha2); PlanimetryCalculus::rotate(vertex, alpha2);
vertex = vertex + _circleOrigin; vertex = vertex + _circleOrigin;
if ( !qFuzzyIsNull(PlanimetryCalculus::distance(polygon.first(), vertex)) if ( !qFuzzyIsNull(PlanimetryCalculus::distance(polygon.first(), vertex))
&& !qFuzzyIsNull(PlanimetryCalculus::distance(polygon.last(), vertex )) ){ && !qFuzzyIsNull(PlanimetryCalculus::distance(polygon.last(), vertex )) ){
......
...@@ -19,7 +19,7 @@ namespace PlanimetryCalculus { ...@@ -19,7 +19,7 @@ namespace PlanimetryCalculus {
double angleWLDegree = line.angle(); // angle between wold and line coordinate system double angleWLDegree = line.angle(); // angle between wold and line coordinate system
QPointF originCircleL = circle.origin() - translationVector; 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 y = originCircleL.y();
double r = circle.radius(); double r = circle.radius();
...@@ -31,7 +31,7 @@ namespace PlanimetryCalculus { ...@@ -31,7 +31,7 @@ namespace PlanimetryCalculus {
if (x_ori >= 0 && x_ori <= line.length()) { if (x_ori >= 0 && x_ori <= line.length()) {
if (calcInstersect) { if (calcInstersect) {
QPointF intersectionPt = QPointF(x_ori, 0); QPointF intersectionPt = QPointF(x_ori, 0);
rotatePoint(intersectionPt, angleWLDegree); rotate(intersectionPt, angleWLDegree);
intersectionPoints.append(intersectionPt + translationVector); intersectionPoints.append(intersectionPt + translationVector);
} }
...@@ -50,7 +50,7 @@ namespace PlanimetryCalculus { ...@@ -50,7 +50,7 @@ namespace PlanimetryCalculus {
if (x1 >= 0 && x1 <= line.length()) { // check if intersection point is on the line if (x1 >= 0 && x1 <= line.length()) { // check if intersection point is on the line
if (calcInstersect) { if (calcInstersect) {
QPointF intersectionPt = QPointF(x1, 0); // first intersection point (line system) 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 intersectionPoints.append(intersectionPt + translationVector); // transform (to world system) and append first intersection point
} }
doesIntersect = true; doesIntersect = true;
...@@ -58,7 +58,7 @@ namespace PlanimetryCalculus { ...@@ -58,7 +58,7 @@ namespace PlanimetryCalculus {
if (x2 >= 0 && x2 <= line.length()) { // check if intersection point is on the line if (x2 >= 0 && x2 <= line.length()) { // check if intersection point is on the line
if (calcInstersect) { if (calcInstersect) {
QPointF intersectionPt = QPointF(x2, 0); // second intersection point (line system) 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 intersectionPoints.append(intersectionPt + translationVector); // transform (to world system) and append second intersection point
} }
doesIntersect = true; doesIntersect = true;
...@@ -76,7 +76,7 @@ namespace PlanimetryCalculus { ...@@ -76,7 +76,7 @@ namespace PlanimetryCalculus {
\fn void rotatePoint(QPointF &point, double alpha) \fn void rotatePoint(QPointF &point, double alpha)
Rotates the \a point counter clockwisely by the angle \a alpha (in radiants). 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()) { if (!point.isNull()) {
double x = point.x(); double x = point.x();
...@@ -87,10 +87,10 @@ namespace PlanimetryCalculus { ...@@ -87,10 +87,10 @@ namespace PlanimetryCalculus {
} }
} }
void rotatePoint(QList<QPointF> &points, double alpha) void rotate(QList<QPointF> &points, double alpha)
{ {
for (int i = 0; i < points.size(); i++) { for (int i = 0; i < points.size(); i++) {
rotatePoint(points[i], alpha); rotate(points[i], alpha);
} }
} }
...@@ -98,15 +98,15 @@ namespace PlanimetryCalculus { ...@@ -98,15 +98,15 @@ namespace PlanimetryCalculus {
\fn void rotatePointDegree(QPointF &point, double alpha) \fn void rotatePointDegree(QPointF &point, double alpha)
Rotates the \a point counter clockwisely by the angle \a alpha (in degrees). 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<QPointF> &points, double alpha) void rotateDegree(QList<QPointF> &points, double alpha)
{ {
for (int i = 0; i < points.size(); i++) { for (int i = 0; i < points.size(); i++) {
rotatePointDegree(points[i], alpha); rotateDegree(points[i], alpha);
} }
} }
...@@ -211,7 +211,7 @@ namespace PlanimetryCalculus { ...@@ -211,7 +211,7 @@ namespace PlanimetryCalculus {
intersectionPoints.append(QPointF(x, -y)); intersectionPoints.append(QPointF(x, -y));
} }
// Transform the coordinate to the world coordinate system. Alpha is the angle between world and circle1 coordinate system. // 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; return returnValue;
} }
...@@ -292,6 +292,97 @@ namespace PlanimetryCalculus { ...@@ -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<IntersectionType> intersects(const QList<QGeoCoordinate> &polygon, const QList<QGeoCoordinate> &line, QList<QGeoCoordinate> &intersectionList, QList<QPair<int, int> > &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<int, int>}, 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<IntersectionType> intersects(const QList<QGeoCoordinate> &polygon, const Line &line, QList<QGeoCoordinate> &intersectionList, QList<QPair<int, int> > &neighbourList)
{
if (polygon.size() >= 3) { // are line a proper line and poly a proper poly?other,
intersectionList.clear();
neighbourList.clear();
QList<IntersectionType> 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<int, int> 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<IntersectionType>();
}
} else {
qWarning("WimaArea::intersects(line, poly): line->count() != 2 || poly->count() < 3");
return QList<IntersectionType>();
}
}
/*!
* \overload bool intersects(const QList<QGeoCoordinate> &polygon, const QList<QGeoCoordinate> &line)
* Returns \c true if any intersection between \a polygon and \a line exists, \c false else.
*
* \sa QPair, QList
*/
bool intersects(const QList<QGeoCoordinate> &polygon, const Line &line)
{
QList<QGeoCoordinate> dummyGeo;
QList<QPair<int, int>> dummyNeighbour;
intersects(polygon, line, dummyGeo, dummyNeighbour);
if (dummyGeo.size() > 0)
return true;
return false;
}
} // end namespace PlanimetryCalculus } // end namespace PlanimetryCalculus
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <QLineF> #include <QLineF>
#include <QPointF> #include <QPointF>
#include <QPolygonF>
#include <QtMath> #include <QtMath>
#include <QLineF> #include <QLineF>
...@@ -15,17 +16,30 @@ namespace PlanimetryCalculus { ...@@ -15,17 +16,30 @@ namespace PlanimetryCalculus {
NoIntersection, Tangent, Secant, // Circle Line Intersetion NoIntersection, Tangent, Secant, // Circle Line Intersetion
EdgeCornerIntersection, EdgeEdgeIntersection, CornerCornerIntersection,
LinesParallel, LinesEqual, // Line Line intersection
Error // general Error // general
}; };
void rotatePoint(QPointF &point, double alpha); void rotate(QPointF &point, double alpha);
void rotatePoint(QList<QPointF> &point, double alpha); void rotate(QList<QPointF> &point, double alpha);
void rotatePointDegree(QPointF &point, double alpha); void rotate(QLineF &point, double alpha);
void rotatePointDegree(QList<QPointF> &points, double alpha); void rotate(QPolygonF &point, double alpha);
void rotateDegree(QPointF &point, double alpha);
void rotateDegree(QList<QPointF> &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);
IntersectType intersects(const Circle &circle1, const Circle &circle2, QList<QPointF> &intersectionPoints); IntersectType intersects(const Circle &circle1, const Circle &circle2, QList<QPointF> &intersectionPoints);
IntersectType intersects(const Circle &circle, const QLineF &line); IntersectType intersects(const Circle &circle, const QLineF &line);
IntersectType intersects(const Circle &circle, const QLineF &line, QList<QPointF> &intersectionPoints); IntersectType intersects(const Circle &circle, const QLineF &line, QList<QPointF> &intersectionPoints);
IntersectType intersects(const QLineF &line1, const QLineF &line2, QPointF &intersectionPt);
QList<IntersectType> intersects(const QPolygonF &polygon, const QLineF &line, QList<QPointF> &intersectionList, QList<QPair<int, int>> &neighbourList);
IntersectType intersects(const QPolygonF &polygon, const QLineF &line);
double distance(const QPointF &p1, const QPointF p2); double distance(const QPointF &p1, const QPointF p2);
double angle(const QPointF &p1, const QPointF p2); double angle(const QPointF &p1, const QPointF p2);
double angleDegree(const QPointF &p1, const QPointF p2); double angleDegree(const QPointF &p1, const QPointF p2);
......
#include "PolygonCalculus.h" #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 <pathsize) {
return index - 1;
} else if (index == 0) {
return pathsize-1;
} else {
qWarning("previousVertexIndex(): Index out of bounds! index:count = %i:%i", index, pathsize);
return -1;
}
}
/*!
* \fn JoinPolygonError joinPolygon(QPolygonF polygon1, QPolygonF polygon2, QPolygonF &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.
* The algorithm assumes that \a joinedPolygon is empty.
*/
JoinPolygonError joinPolygon(QPolygonF polygon1, QPolygonF polygon2, QPolygonF &joinedPolygon)
{
if (polygon1.size() >= 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<QPair<int, int>> neighbourList;
QList<QPointF> 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<int, int> 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.size(); i++) {
QPointF coord1 = polygon[i];
QPointF coord2 = polygon[nextPolygonIndex(polygon.size(), i)];
sum += (coord2.x() - coord1.x()) * (coord2.y() + coord1.y());
}
if (sum < 0.0)
return false;
return true;
}
/*!
* \fn void reversePath(QPolygonF &polygon)
* Reverses the path, i.e. changes the first Vertex with the last, the second with the befor last and so forth.
*/
void reversePath(QPolygonF &polygon)
{
QPolygonF pathReversed;
for (int i = 0; i < polygon.size(); i++) {
pathReversed.prepend(polygon[i]);
}
polygon.clear();
polygon.append(pathReversed);
}
bool offsetPolygon(QPolygonF &polygon, double offset)
{
}
/*!
* \fn double distanceInsidePolygon(const QGeoCoordinate &c1, const QGeoCoordinate &c2, QList<QGeoCoordinate> 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<QPointF> intersectionList;
QList<QPair<int, int>> 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<qreal>::infinity();
}
} else {
return std::numeric_limits<qreal>::infinity();
}
}
} // end PolygonCalculus namespace
...@@ -2,8 +2,25 @@ ...@@ -2,8 +2,25 @@
#define POLYGONCALCULUS_H #define POLYGONCALCULUS_H
#include <QPointF>
#include <QPolygonF>
#include "PlanimetryCalculus.h"
namespace PolygonCalculus { 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 #endif // POLYGONCALCULUS_H
...@@ -25,339 +25,9 @@ double SphereCalculus::epsilonMeter() const ...@@ -25,339 +25,9 @@ double SphereCalculus::epsilonMeter() const
return _epsilonMeter; return _epsilonMeter;
} }
/*!
* \fn int SphereCalculus::closestVertexIndex(const QList<QGeoCoordinate> &path, const QGeoCoordinate &coordinate)
* which has the least distance to \a coordinate.
*
* \sa QGeoCoordinate
*/
int SphereCalculus::closestVertexIndex(const QList<QGeoCoordinate> &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<QGeoCoordinate> &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<QGeoCoordinate> &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 <pathsize) {
return index - 1;
} else if (index == 0) {
return pathsize-1;
} else {
qWarning("WimaArea::previousVertexIndex(): Index out of bounds! index:count = %i:%i", index, pathsize);
return -1;
}
}
/*!
* \fn SphereCalculus::JoinPolygonError SphereCalculus::joinPolygon(QList<QGeoCoordinate> polygon1, QList<QGeoCoordinate> polygon2, QList<QGeoCoordinate> &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<QGeoCoordinate> polygon1, QList<QGeoCoordinate> polygon2, QList<QGeoCoordinate> &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<QGeoCoordinate> *walkerPoly = &polygon1; // "walk" on this polygon towards higher indices
const QList<QGeoCoordinate> *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<QPair<int, int>> neighbourList;
QList<QGeoCoordinate> 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<int, int> neighbours = neighbourList.value(minDistIndex);
protoNextVertex = crossPoly->value(neighbours.second);
nextVertexIndex = neighbours.second;
// switch walker and cross poly
const QList<QGeoCoordinate> *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<QGeoCoordinate> &line1, const QList<QGeoCoordinate> &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::IntersectionType> SphereCalculus::intersects(const QList<QGeoCoordinate> &polygon, const QList<QGeoCoordinate> &line, QList<QGeoCoordinate> &intersectionList, QList<QPair<int, int> > &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<int, int>}, 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::IntersectionType> SphereCalculus::intersects(const QList<QGeoCoordinate> &polygon, const Line &line, QList<QGeoCoordinate> &intersectionList, QList<QPair<int, int> > &neighbourList)
{
if (polygon.size() >= 3) { // are line a proper line and poly a proper poly?other,
intersectionList.clear();
neighbourList.clear();
QList<SphereCalculus::IntersectionType> 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<int, int> 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<IntersectionType>();
}
} else {
qWarning("WimaArea::intersects(line, poly): line->count() != 2 || poly->count() < 3");
return QList<IntersectionType>();
}
}
/*!
* \overload bool SphereCalculus::intersects(const QList<QGeoCoordinate> &polygon, const QList<QGeoCoordinate> &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<QGeoCoordinate> &polygon, const Line &line)
{
QList<QGeoCoordinate> dummyGeo;
QList<QPair<int, int>> dummyNeighbour;
intersects(polygon, line, dummyGeo, dummyNeighbour);
if (dummyGeo.size() > 0)
return true;
return false;
}
/*! /*!
* \fn bool SphereCalculus::dijkstraPath(const QList<QGeoCoordinate> &polygon, const QGeoCoordinate &c1, const QGeoCoordinate &c2, QList<QGeoCoordinate> &dijkstraPath) * \fn bool SphereCalculus::dijkstraPath(const QList<QGeoCoordinate> &polygon, const QGeoCoordinate &c1, const QGeoCoordinate &c2, QList<QGeoCoordinate> &dijkstraPath)
...@@ -470,113 +140,7 @@ SphereCalculus::DijkstraError SphereCalculus::dijkstraPath(const QList<QGeoCoord ...@@ -470,113 +140,7 @@ SphereCalculus::DijkstraError SphereCalculus::dijkstraPath(const QList<QGeoCoord
return SphereCalculus::DijkstraError::PathFound; return SphereCalculus::DijkstraError::PathFound;
} }
/*!
* \fn bool SphereCalculus::isSimplePolygon(const QList<QGeoCoordinate> &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<QGeoCoordinate> &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<QGeoCoordinate> &path)
* Returns \c true if \a path has clockwise winding, \c false else.
*/
bool SphereCalculus::hasClockwiseWinding(const QList<QGeoCoordinate> &path)
{
if (path.size() <= 2) {
return false;
}
double sum = 0;
for (int i=0; i <path.size(); i++) {
QGeoCoordinate coord1 = path[i];
QGeoCoordinate coord2 = path[nextPolygonIndex(path.size(), i)];
sum += (coord2.longitude() - coord1.longitude()) * (coord2.latitude() + coord1.latitude());
}
if (sum < 0.0)
return false;
return true;
}
/*!
* \fn void SphereCalculus::reversePath(QList<QGeoCoordinate> &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<QGeoCoordinate> &path)
{
QList<QGeoCoordinate> pathReversed;
for (int i = 0; i < path.size(); i++) {
pathReversed.prepend(path[i]);
}
path.clear();
path.append(pathReversed);
}
bool SphereCalculus::offsetPolygon(QList<QGeoCoordinate> &polygon, double offset)
{
}
/*!
* \fn double SphereCalculus::distanceInsidePolygon(const QGeoCoordinate &c1, const QGeoCoordinate &c2, QList<QGeoCoordinate> 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<QGeoCoordinate> 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<QGeoCoordinate> intersectionList;
QList<QPair<int, int>> 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<qreal>::infinity();
}
} else {
return std::numeric_limits<qreal>::infinity();
}
}
/*! /*!
\class SphereCalculus \class SphereCalculus
......
...@@ -31,23 +31,11 @@ public: ...@@ -31,23 +31,11 @@ public:
double epsilonMeter() const; double epsilonMeter() const;
// Member Methodes // Member Methodes
int closestVertexIndex (const QList<QGeoCoordinate> &path, const QGeoCoordinate &coordinate);
QGeoCoordinate closestVertex (const QList<QGeoCoordinate> &path, const QGeoCoordinate &coordinate);
int nextPolygonIndex (int pathsize, int index);
int previousPolygonIndex(int pathsize, int index);
JoinPolygonError joinPolygon (QList<QGeoCoordinate> polygon1, QList<QGeoCoordinate> polygon2,
QList<QGeoCoordinate> &joinedPolygon);
IntersectionType intersects (const Line &line1, const Line &line2,
QGeoCoordinate &intersectionPt);
QList<IntersectionType> intersects (const QList<QGeoCoordinate> &polygon, const Line &line,
QList<QGeoCoordinate> &intersectionList, QList<QPair<int, int>> &neighbourList);
bool intersects (const QList<QGeoCoordinate> &polygon, const Line &line);
DijkstraError dijkstraPath (const QList<QGeoCoordinate> &polygon, const QGeoCoordinate& start, DijkstraError dijkstraPath (const QList<QGeoCoordinate> &polygon, const QGeoCoordinate& start,
const QGeoCoordinate& end, QList<QGeoCoordinate>& dijkstraPath); const QGeoCoordinate& end, QList<QGeoCoordinate>& dijkstraPath);
bool isSimplePolygon (const QList<QGeoCoordinate> &polygon);
bool hasClockwiseWinding (const QList<QGeoCoordinate> &path);
void reversePath (QList<QGeoCoordinate> &path);
bool offsetPolygon (QList<QGeoCoordinate> &polygon, double offset);
...@@ -56,7 +44,7 @@ signals: ...@@ -56,7 +44,7 @@ signals:
public slots: public slots:
private: private:
double distanceInsidePolygon (const QGeoCoordinate& c1, const QGeoCoordinate& c2, QList<QGeoCoordinate> polygon);
double _epsilonMeter; // The accuracy used for distance calculations (unit: m). double _epsilonMeter; // The accuracy used for distance calculations (unit: m).
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment