Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #2290 from mhugent/geometry_marker
Geometry marker
  • Loading branch information
mhugent committed Sep 5, 2015
2 parents cb5054f + 557701c commit 8611543
Show file tree
Hide file tree
Showing 30 changed files with 592 additions and 79 deletions.
4 changes: 4 additions & 0 deletions python/core/geometry/qgsabstractgeometryv2.sip
Expand Up @@ -116,4 +116,8 @@ class QgsAbstractGeometryV2
virtual bool hasCurvedSegments() const;
/** Returns a geometry without curves. Caller takes ownership*/
virtual QgsAbstractGeometryV2* segmentize() const /Factory/;

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@return rotation in radians, clockwise from north*/
virtual double vertexAngle( const QgsVertexId& vertex ) const = 0;
};
5 changes: 5 additions & 0 deletions python/core/geometry/qgscircularstringv2.sip
Expand Up @@ -55,6 +55,11 @@ class QgsCircularStringV2: public QgsCurveV2

bool hasCurvedSegments() const;

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const;

private:
void segmentize( const QgsPointV2& p1, const QgsPointV2& p2, const QgsPointV2& p3, QList<QgsPointV2>& points ) const;
};
5 changes: 5 additions & 0 deletions python/core/geometry/qgscompoundcurvev2.sip
Expand Up @@ -60,4 +60,9 @@ class QgsCompoundCurveV2: public QgsCurveV2
void sumUpArea( double& sum ) const;

bool hasCurvedSegments() const;

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const;
};
5 changes: 5 additions & 0 deletions python/core/geometry/qgscurvepolygonv2.sip
Expand Up @@ -62,4 +62,9 @@ class QgsCurvePolygonV2: public QgsSurfaceV2

bool hasCurvedSegments() const;
QgsAbstractGeometryV2* segmentize() const /Factory/;

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const;
};
5 changes: 5 additions & 0 deletions python/core/geometry/qgsgeometrycollectionv2.sip
Expand Up @@ -51,4 +51,9 @@ class QgsGeometryCollectionV2: public QgsAbstractGeometryV2
virtual double area() const;

bool hasCurvedSegments() const;

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const;
};
5 changes: 5 additions & 0 deletions python/core/geometry/qgslinestringv2.sip
Expand Up @@ -55,4 +55,9 @@ class QgsLineStringV2: public QgsCurveV2
bool pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const;

void sumUpArea( double& sum ) const;

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const;
};
5 changes: 5 additions & 0 deletions python/core/geometry/qgspointv2.sip
Expand Up @@ -55,4 +55,9 @@ class QgsPointV2: public QgsAbstractGeometryV2

double closestSegment( const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon ) const;
bool nextVertex( QgsVertexId& id, QgsPointV2& vertex ) const;

/** Angle undefined. Always returns 0.0
@param vertex the vertex id
@return 0.0*/
double vertexAngle( const QgsVertexId& vertex ) const;
};
7 changes: 7 additions & 0 deletions python/core/qgsrendercontext.sip
Expand Up @@ -85,4 +85,11 @@ class QgsRenderContext
//! Added in QGIS v2.4
const QgsVectorSimplifyMethod& vectorSimplifyMethod() const;
void setVectorSimplifyMethod( const QgsVectorSimplifyMethod& simplifyMethod );

/** Returns pointer to the unsegmentized geometry
@return the geometry*/
const QgsAbstractGeometryV2* geometry() const;
/** Sets pointer to original (unsegmentized) geometry
@geometry the geometry*/
void setGeometry( const QgsAbstractGeometryV2* geometry );
};
5 changes: 5 additions & 0 deletions src/core/geometry/qgsabstractgeometryv2.h
Expand Up @@ -317,6 +317,11 @@ class CORE_EXPORT QgsAbstractGeometryV2
*/
virtual QgsAbstractGeometryV2* segmentize() const { return clone(); }

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
virtual double vertexAngle( const QgsVertexId& vertex ) const = 0;

protected:
QgsWKBTypes::Type mWkbType;
mutable QgsRectangle mBoundingBox;
Expand Down
55 changes: 55 additions & 0 deletions src/core/geometry/qgscircularstringv2.cpp
Expand Up @@ -953,3 +953,58 @@ void QgsCircularStringV2::insertVertexBetween( int after, int before, int pointO
mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
}
}

double QgsCircularStringV2::vertexAngle( const QgsVertexId& vId ) const
{
int before = vId.vertex - 1;
int vertex = vId.vertex;
int after = vId.vertex + 1;

if ( vId.vertex % 2 != 0 ) // a curve vertex
{
if ( vId.vertex >= 1 && vId.vertex < numPoints() - 1 )
{
return QgsGeometryUtils::circleTangentDirection( QgsPointV2( mX[vertex], mY[vertex] ), QgsPointV2( mX[before], mY[before] ),
QgsPointV2( mX[vertex], mY[vertex] ), QgsPointV2( mX[after], mY[after] ) );
}
}
else //a point vertex
{
if ( vId.vertex == 0 )
{
return QgsGeometryUtils::circleTangentDirection( QgsPointV2( mX[0], mY[0] ), QgsPointV2( mX[0], mY[0] ),
QgsPointV2( mX[1], mY[1] ), QgsPointV2( mX[2], mY[2] ) );
}
if ( vId.vertex >= numPoints() - 1 )
{
if ( numPoints() < 3 )
{
return 0.0;
}
int a = numPoints() - 3;
int b = numPoints() - 2;
int c = numPoints() - 1;
return QgsGeometryUtils::circleTangentDirection( QgsPointV2( mX[c], mY[c] ), QgsPointV2( mX[a], mY[a] ),
QgsPointV2( mX[b], mY[b] ), QgsPointV2( mX[c], mY[c] ) );
}
else
{
if ( vId.vertex + 2 > numPoints() - 1 )
{
return 0.0;
}

int vertex1 = vId.vertex - 2;
int vertex2 = vId.vertex - 1;
int vertex3 = vId.vertex;
double angle1 = QgsGeometryUtils::circleTangentDirection( QgsPointV2( mX[vertex3], mY[vertex3] ),
QgsPointV2( mX[vertex1], mY[vertex1] ), QgsPointV2( mX[vertex2], mY[vertex2] ), QgsPointV2( mX[vertex3], mY[vertex3] ) );
int vertex4 = vId.vertex + 1;
int vertex5 = vId.vertex + 2;
double angle2 = QgsGeometryUtils::circleTangentDirection( QgsPointV2( mX[vertex3], mY[vertex3] ),
QgsPointV2( mX[vertex3], mY[vertex3] ), QgsPointV2( mX[vertex4], mY[vertex4] ), QgsPointV2( mX[vertex5], mY[vertex5] ) );
return QgsGeometryUtils::averageAngle( angle1, angle2 );
}
}
return 0.0;
}
5 changes: 5 additions & 0 deletions src/core/geometry/qgscircularstringv2.h
Expand Up @@ -91,6 +91,11 @@ class CORE_EXPORT QgsCircularStringV2: public QgsCurveV2

bool hasCurvedSegments() const override { return true; }

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const override;

private:
QVector<double> mX;
QVector<double> mY;
Expand Down
22 changes: 22 additions & 0 deletions src/core/geometry/qgscompoundcurvev2.cpp
Expand Up @@ -586,3 +586,25 @@ bool QgsCompoundCurveV2::hasCurvedSegments() const
return false;
}

double QgsCompoundCurveV2::vertexAngle( const QgsVertexId& vertex ) const
{
QList< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
if ( curveIds.size() == 1 )
{
QgsCurveV2* curve = mCurves[curveIds.at( 0 ).first];
return curve->vertexAngle( curveIds.at( 0 ).second );
}
else if ( curveIds.size() > 1 )
{
QgsCurveV2* curve1 = mCurves[curveIds.at( 0 ).first];
QgsCurveV2* curve2 = mCurves[curveIds.at( 1 ).first];
double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second );
double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second );
return QgsGeometryUtils::averageAngle( angle1, angle2 );
}
else
{
return 0.0;
}
}

5 changes: 5 additions & 0 deletions src/core/geometry/qgscompoundcurvev2.h
Expand Up @@ -104,6 +104,11 @@ class CORE_EXPORT QgsCompoundCurveV2: public QgsCurveV2

bool hasCurvedSegments() const override;

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const override;

private:
QList< QgsCurveV2* > mCurves;
/** Turns a vertex id for the compound curve into one or more ids for the subcurves
Expand Down
11 changes: 11 additions & 0 deletions src/core/geometry/qgscurvepolygonv2.cpp
Expand Up @@ -665,3 +665,14 @@ QgsAbstractGeometryV2* QgsCurvePolygonV2::segmentize() const
{
return toPolygon();
}

double QgsCurvePolygonV2::vertexAngle( const QgsVertexId& vertex ) const
{
if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
{
return false;
}

QgsCurveV2* ring = vertex.ring == 0 ? mExteriorRing : mInteriorRings[vertex.ring - 1];
return ring->vertexAngle( vertex );
}
5 changes: 5 additions & 0 deletions src/core/geometry/qgscurvepolygonv2.h
Expand Up @@ -94,6 +94,11 @@ class CORE_EXPORT QgsCurvePolygonV2: public QgsSurfaceV2
bool hasCurvedSegments() const override;
QgsAbstractGeometryV2* segmentize() const override;

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const override;

protected:

QgsCurveV2* mExteriorRing;
Expand Down
16 changes: 16 additions & 0 deletions src/core/geometry/qgsgeometrycollectionv2.cpp
Expand Up @@ -509,3 +509,19 @@ QgsAbstractGeometryV2* QgsGeometryCollectionV2::segmentize() const
}
return geomCollection;
}

double QgsGeometryCollectionV2::vertexAngle( const QgsVertexId& vertex ) const
{
if ( vertex.part >= mGeometries.size() )
{
return 0.0;
}

QgsAbstractGeometryV2* geom = mGeometries[vertex.part];
if ( !geom )
{
return 0.0;
}

return geom->vertexAngle( vertex );
}
5 changes: 5 additions & 0 deletions src/core/geometry/qgsgeometrycollectionv2.h
Expand Up @@ -102,6 +102,11 @@ class CORE_EXPORT QgsGeometryCollectionV2: public QgsAbstractGeometryV2
/** Returns a geometry without curves. Caller takes ownership*/
QgsAbstractGeometryV2* segmentize() const override;

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const override;

protected:
QVector< QgsAbstractGeometryV2* > mGeometries;

Expand Down
88 changes: 88 additions & 0 deletions src/core/geometry/qgsgeometryutils.cpp
Expand Up @@ -357,6 +357,26 @@ bool QgsGeometryUtils::segmentMidPoint( const QgsPointV2& p1, const QgsPointV2&
return true;
}

double QgsGeometryUtils::circleTangentDirection( const QgsPointV2& tangentPoint, const QgsPointV2& cp1,
const QgsPointV2& cp2, const QgsPointV2& cp3 )
{
//calculate circle midpoint
double mX, mY, radius;
circleCenterRadius( cp1, cp2, cp3, radius, mX, mY );

double p1Angle = QgsGeometryUtils::ccwAngle( cp1.y() - mY, cp1.x() - mX );
double p2Angle = QgsGeometryUtils::ccwAngle( cp2.y() - mY, cp2.x() - mX );
double p3Angle = QgsGeometryUtils::ccwAngle( cp3.y() - mY, cp3.x() - mX );
if ( circleClockwise( p1Angle, p2Angle, p3Angle ) )
{
return lineAngle( tangentPoint.x(), tangentPoint.y(), mX, mY );
}
else
{
return lineAngle( mX, mY, tangentPoint.x(), tangentPoint.y() );
}
}

QList<QgsPointV2> QgsGeometryUtils::pointsFromWKT( const QString &wktCoordinateList, bool is3D, bool isMeasure )
{
int dim = 2 + is3D + isMeasure;
Expand Down Expand Up @@ -525,3 +545,71 @@ QStringList QgsGeometryUtils::wktGetChildBlocks( const QString &wkt, const QStri
}
return blocks;
}

double QgsGeometryUtils::lineAngle( double x1, double y1, double x2, double y2 )
{
double at = atan2( y2 - y1, x2 - x1 );
double a = -at + M_PI / 2.0;
if ( a < 0 )
{
a = 2 * M_PI + a;
}
if ( a >= 2 * M_PI )
{
a -= 2 * M_PI;
}
return a;
}

double QgsGeometryUtils::linePerpendicularAngle( double x1, double y1, double x2, double y2 )
{
double a = lineAngle( x1, y1, x2, y2 );
a += ( M_PI / 2.0 );
if ( a >= 2 * M_PI )
{
a -= ( 2 * M_PI );
}
return a;
}

double QgsGeometryUtils::averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 )
{
// calc average angle between the previous and next point
double a1 = linePerpendicularAngle( x1, y1, x2, y2 );
double a2 = linePerpendicularAngle( x2, y2, x3, y3 );
return averageAngle( a1, a2 );
}

double QgsGeometryUtils::averageAngle( double a1, double a2 )
{
double clockwiseDiff = 0.0;
if ( a2 >= a1 )
{
clockwiseDiff = a2 - a1;
}
else
{
clockwiseDiff = a2 + ( 2 * M_PI - a1 );
}
double counterClockwiseDiff = 2 * M_PI - clockwiseDiff;

double resultAngle = 0;
if ( clockwiseDiff <= counterClockwiseDiff )
{
resultAngle = a1 + clockwiseDiff / 2.0;
}
else
{
resultAngle = a1 - counterClockwiseDiff / 2.0;
}

if ( resultAngle >= 2 * M_PI )
{
resultAngle -= 2 * M_PI;
}
else if ( resultAngle < 0 )
{
resultAngle = 2 * M_PI - resultAngle;
}
return resultAngle;
}
12 changes: 12 additions & 0 deletions src/core/geometry/qgsgeometryutils.h
Expand Up @@ -79,6 +79,9 @@ class CORE_EXPORT QgsGeometryUtils
/** Calculates midpoint on circle passing through p1 and p2, closest to given coordinate*/
static bool segmentMidPoint( const QgsPointV2& p1, const QgsPointV2& p2, QgsPointV2& result, double radius, const QgsPointV2& mousePos );

/** Calculates the direction angle of a circle tangent (clockwise from north in radians)*/
static double circleTangentDirection( const QgsPointV2& tangentPoint, const QgsPointV2& cp1, const QgsPointV2& cp2, const QgsPointV2& cp3 );

/** Returns a list of points contained in a WKT string.
*/
static QList<QgsPointV2> pointsFromWKT( const QString& wktCoordinateList, bool is3D, bool isMeasure );
Expand All @@ -92,6 +95,15 @@ class CORE_EXPORT QgsGeometryUtils
static QDomElement pointsToGML3( const QList<QgsPointV2>& points, QDomDocument &doc, int precision, const QString& ns, bool is3D );
/** Returns a geoJSON coordinates string */
static QString pointsToJSON( const QList<QgsPointV2>& points, int precision );
/**Calculates direction of line (clockwise from north direction) in radians*/
static double lineAngle( double x1, double y1, double x2, double y2 );
/**Calculates angle perpendicular to line*/
static double linePerpendicularAngle( double x1, double y1, double x2, double y2 );
/**Angle between two linear segments*/
static double averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 );
/**Averages two angles*/
static double averageAngle( double a1, double a2 );


/** Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents ("Pair(wkbType, "contents")")
*/
Expand Down

0 comments on commit 8611543

Please sign in to comment.