mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Consider curved geometries in marker line symbol layer
This commit is contained in:
parent
a5a963ccfe
commit
56316ddcad
@ -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;
|
||||
};
|
||||
|
@ -55,6 +55,10 @@ class QgsCircularStringV2: public QgsCurveV2
|
||||
|
||||
bool hasCurvedSegments() const;
|
||||
|
||||
/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
|
||||
@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;
|
||||
};
|
||||
|
@ -60,4 +60,8 @@ 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.
|
||||
@return rotation in radians, clockwise from north*/
|
||||
double vertexAngle( const QgsVertexId& vertex ) const;
|
||||
};
|
||||
|
@ -62,4 +62,8 @@ class QgsCurvePolygonV2: public QgsSurfaceV2
|
||||
|
||||
bool hasCurvedSegments() const;
|
||||
QgsAbstractGeometryV2* segmentize() const /Factory/;
|
||||
|
||||
/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
|
||||
@return rotation in radians, clockwise from north*/
|
||||
double vertexAngle( const QgsVertexId& vertex ) const;
|
||||
};
|
||||
|
@ -51,4 +51,8 @@ class QgsGeometryCollectionV2: public QgsAbstractGeometryV2
|
||||
virtual double area() const;
|
||||
|
||||
bool hasCurvedSegments() const;
|
||||
|
||||
/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
|
||||
@return rotation in radians, clockwise from north*/
|
||||
double vertexAngle( const QgsVertexId& vertex ) const;
|
||||
};
|
||||
|
@ -55,4 +55,8 @@ 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.
|
||||
@return rotation in radians, clockwise from north*/
|
||||
double vertexAngle( const QgsVertexId& vertex ) const;
|
||||
};
|
||||
|
@ -55,4 +55,7 @@ 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*/
|
||||
double vertexAngle( const QgsVertexId& vertex ) const;
|
||||
};
|
||||
|
@ -317,6 +317,10 @@ class CORE_EXPORT QgsAbstractGeometryV2
|
||||
*/
|
||||
virtual QgsAbstractGeometryV2* segmentize() const { return clone(); }
|
||||
|
||||
/** 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;
|
||||
|
||||
protected:
|
||||
QgsWKBTypes::Type mWkbType;
|
||||
mutable QgsRectangle mBoundingBox;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -91,6 +91,10 @@ 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.
|
||||
@return rotation in radians, clockwise from north*/
|
||||
double vertexAngle( const QgsVertexId& vertex ) const override;
|
||||
|
||||
private:
|
||||
QVector<double> mX;
|
||||
QVector<double> mY;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,6 +104,10 @@ class CORE_EXPORT QgsCompoundCurveV2: public QgsCurveV2
|
||||
|
||||
bool hasCurvedSegments() const override;
|
||||
|
||||
/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
|
||||
@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
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -94,6 +94,10 @@ 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.
|
||||
@return rotation in radians, clockwise from north*/
|
||||
double vertexAngle( const QgsVertexId& vertex ) const override;
|
||||
|
||||
protected:
|
||||
|
||||
QgsCurveV2* mExteriorRing;
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -102,6 +102,10 @@ 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.
|
||||
@return rotation in radians, clockwise from north*/
|
||||
double vertexAngle( const QgsVertexId& vertex ) const override;
|
||||
|
||||
protected:
|
||||
QVector< QgsAbstractGeometryV2* > mGeometries;
|
||||
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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 );
|
||||
@ -93,6 +96,12 @@ class CORE_EXPORT QgsGeometryUtils
|
||||
/** Returns a geoJSON coordinates string */
|
||||
static QString pointsToJSON( const QList<QgsPointV2>& points, int precision );
|
||||
|
||||
static double lineAngle( double x1, double y1, double x2, double y2 );
|
||||
static double linePerpendicularAngle( double x1, double y1, double x2, double y2 );
|
||||
static double averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 );
|
||||
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")")
|
||||
*/
|
||||
static QPair<QgsWKBTypes::Type, QString> wktReadBlock( const QString& wkt );
|
||||
|
@ -500,3 +500,34 @@ void QgsLineStringV2::close()
|
||||
}
|
||||
addVertex( startPoint() );
|
||||
}
|
||||
|
||||
double QgsLineStringV2::vertexAngle( const QgsVertexId& vertex ) const
|
||||
{
|
||||
if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
|
||||
{
|
||||
if ( isClosed() )
|
||||
{
|
||||
QPointF previous = mCoords[numPoints() - 1 ];
|
||||
QPointF current = mCoords[0];
|
||||
QPointF after = mCoords[1];
|
||||
return QgsGeometryUtils::averageAngle( previous.x(), previous.y(), current.x(), current.y(), after.x(), after.y() );
|
||||
}
|
||||
else if ( vertex.vertex == 0 )
|
||||
{
|
||||
return QgsGeometryUtils::linePerpendicularAngle( mCoords[0].x(), mCoords[0].y(), mCoords[1].x(), mCoords[1].y() );
|
||||
}
|
||||
else
|
||||
{
|
||||
int a = numPoints() - 2;
|
||||
int b = numPoints() - 1;
|
||||
return QgsGeometryUtils::linePerpendicularAngle( mCoords[a].x(), mCoords[a].y(), mCoords[b].x(), mCoords[b].y() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QPointF previous = mCoords[vertex.vertex - 1 ];
|
||||
QPointF current = mCoords[vertex.vertex];
|
||||
QPointF after = mCoords[vertex.vertex + 1];
|
||||
return QgsGeometryUtils::averageAngle( previous.x(), previous.y(), current.x(), current.y(), after.x(), after.y() );
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +90,10 @@ class CORE_EXPORT QgsLineStringV2: public QgsCurveV2
|
||||
/** Appends first point if not already closed*/
|
||||
void close();
|
||||
|
||||
/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
|
||||
@return rotation in radians, clockwise from north*/
|
||||
double vertexAngle( const QgsVertexId& vertex ) const override;
|
||||
|
||||
private:
|
||||
QPolygonF mCoords;
|
||||
QVector<double> mZ;
|
||||
|
@ -85,6 +85,9 @@ class CORE_EXPORT QgsPointV2: public QgsAbstractGeometryV2
|
||||
double closestSegment( const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon ) const override;
|
||||
bool nextVertex( QgsVertexId& id, QgsPointV2& vertex ) const override;
|
||||
|
||||
/** Angle undefined. Always returns 0.0*/
|
||||
double vertexAngle( const QgsVertexId& vertex ) const override { Q_UNUSED( vertex ); return 0.0; }
|
||||
|
||||
private:
|
||||
double mX;
|
||||
double mY;
|
||||
|
@ -141,7 +141,9 @@ class CORE_EXPORT QgsRenderContext
|
||||
*/
|
||||
const QgsExpressionContext& expressionContext() const { return mExpressionContext; }
|
||||
|
||||
/** Returns pointer to the unsegmentized geometry*/
|
||||
const QgsAbstractGeometryV2* geometry() const { return mGeometry; }
|
||||
/** Sets pointer to original (unsegmentized) geometry*/
|
||||
void setGeometry( const QgsAbstractGeometryV2* geometry ) { mGeometry = geometry; }
|
||||
|
||||
private:
|
||||
|
@ -999,7 +999,7 @@ void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points,
|
||||
offsetAlongLine *= QgsSymbolLayerV2Utils::lineWidthScaleFactor( rc, mOffsetAlongLineUnit, mOffsetAlongLineMapUnitScale );
|
||||
}
|
||||
|
||||
if ( !mRotateMarker && offsetAlongLine == 0 && context.renderContext().geometry()
|
||||
if ( offsetAlongLine == 0 && context.renderContext().geometry()
|
||||
&& context.renderContext().geometry()->hasCurvedSegments() && ( placement == Vertex || placement == CurvePoint ) )
|
||||
{
|
||||
const QgsCoordinateTransform* ct = context.renderContext().coordinateTransform();
|
||||
@ -1024,7 +1024,8 @@ void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points,
|
||||
|
||||
if ( mRotateMarker )
|
||||
{
|
||||
//todo: function to calculate angle...
|
||||
double angle = context.renderContext().geometry()->vertexAngle( vId );
|
||||
mMarker->setAngle( angle * 180 / M_PI );
|
||||
}
|
||||
mMarker->renderPoint( mapPoint, context.feature(), rc, -1, context.selected() );
|
||||
}
|
||||
@ -1042,13 +1043,17 @@ void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points,
|
||||
i = points.count() - 1;
|
||||
maxCount = points.count();
|
||||
}
|
||||
else
|
||||
else if ( placement == Vertex )
|
||||
{
|
||||
i = 0;
|
||||
maxCount = points.count();
|
||||
if ( points.first() == points.last() )
|
||||
isRing = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
|
||||
{
|
||||
|
@ -31,6 +31,12 @@ class TestQgsGeometryUtils: public QObject
|
||||
void testSegmentMidPoint();
|
||||
void testCircleLength_data();
|
||||
void testCircleLength();
|
||||
void testLineAngle_data();
|
||||
void testLineAngle();
|
||||
void testLinePerpendicularAngle_data();
|
||||
void testLinePerpendicularAngle();
|
||||
void testAverageAngle_data();
|
||||
void testAverageAngle();
|
||||
};
|
||||
|
||||
void TestQgsGeometryUtils::testLeftOfLine_data()
|
||||
@ -170,6 +176,90 @@ void TestQgsGeometryUtils::testCircleLength()
|
||||
QVERIFY( qgsDoubleNear( expected, QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 ) ) );
|
||||
}
|
||||
|
||||
void TestQgsGeometryUtils::testLineAngle_data()
|
||||
{
|
||||
QTest::addColumn<double>( "x1" );
|
||||
QTest::addColumn<double>( "y1" );
|
||||
QTest::addColumn<double>( "x2" );
|
||||
QTest::addColumn<double>( "y2" );
|
||||
QTest::addColumn<double>( "expected" );
|
||||
|
||||
QTest::newRow( "lineAngle1" ) << 0.0 << 0.0 << 10.0 << 10.0 << 45.0;
|
||||
QTest::newRow( "lineAngle2" ) << 0.0 << 0.0 << 10.0 << 0.0 << 90.0;
|
||||
QTest::newRow( "lineAngle3" ) << 0.0 << 0.0 << 10.0 << -10.0 << 135.0;
|
||||
QTest::newRow( "lineAngle4" ) << 0.0 << 0.0 << 0.0 << -10.0 << 180.0;
|
||||
QTest::newRow( "lineAngle5" ) << 0.0 << 0.0 << -10.0 << -10.0 << 225.0;
|
||||
QTest::newRow( "lineAngle6" ) << 0.0 << 0.0 << -10.0 << 0.0 << 270.0;
|
||||
QTest::newRow( "lineAngle7" ) << 0.0 << 0.0 << -10.0 << 10.0 << 315.0;
|
||||
QTest::newRow( "lineAngle8" ) << 0.0 << 0.0 << 0.0 << 10.0 << 0.0;
|
||||
}
|
||||
|
||||
void TestQgsGeometryUtils::testLineAngle()
|
||||
{
|
||||
QFETCH( double, x1 );
|
||||
QFETCH( double, y1 );
|
||||
QFETCH( double, x2 );
|
||||
QFETCH( double, y2 );
|
||||
QFETCH( double, expected );
|
||||
|
||||
double lineAngle = QgsGeometryUtils::lineAngle( x1, y1, x2, y2 ) * 180 / M_PI;
|
||||
QVERIFY( qgsDoubleNear( lineAngle, expected ) );
|
||||
}
|
||||
|
||||
void TestQgsGeometryUtils::testLinePerpendicularAngle_data()
|
||||
{
|
||||
QTest::addColumn<double>( "x1" );
|
||||
QTest::addColumn<double>( "y1" );
|
||||
QTest::addColumn<double>( "x2" );
|
||||
QTest::addColumn<double>( "y2" );
|
||||
QTest::addColumn<double>( "expected" );
|
||||
|
||||
QTest::newRow( "lineAngle1" ) << 0.0 << 0.0 << 10.0 << 10.0 << 135.0;
|
||||
QTest::newRow( "lineAngle2" ) << 0.0 << 0.0 << 10.0 << 0.0 << 180.0;
|
||||
QTest::newRow( "lineAngle3" ) << 0.0 << 0.0 << 10.0 << -10.0 << 225.0;
|
||||
QTest::newRow( "lineAngle4" ) << 0.0 << 0.0 << 0.0 << -10.0 << 270.0;
|
||||
QTest::newRow( "lineAngle5" ) << 0.0 << 0.0 << -10.0 << -10.0 << 315.0;
|
||||
QTest::newRow( "lineAngle6" ) << 0.0 << 0.0 << -10.0 << 0.0 << 0.0;
|
||||
QTest::newRow( "lineAngle7" ) << 0.0 << 0.0 << -10.0 << 10.0 << 45.0;
|
||||
QTest::newRow( "lineAngle8" ) << 0.0 << 0.0 << 0.0 << 10.0 << 90.0;
|
||||
}
|
||||
|
||||
void TestQgsGeometryUtils::testLinePerpendicularAngle()
|
||||
{
|
||||
QFETCH( double, x1 );
|
||||
QFETCH( double, y1 );
|
||||
QFETCH( double, x2 );
|
||||
QFETCH( double, y2 );
|
||||
QFETCH( double, expected );
|
||||
|
||||
double pAngle = QgsGeometryUtils::linePerpendicularAngle( x1, y1, x2, y2 ) * 180 / M_PI;
|
||||
QVERIFY( qgsDoubleNear( pAngle, expected ) );
|
||||
}
|
||||
|
||||
void TestQgsGeometryUtils::testAverageAngle_data()
|
||||
{
|
||||
QTest::addColumn<double>( "angle1" );
|
||||
QTest::addColumn<double>( "angle2" );
|
||||
QTest::addColumn<double>( "expected" );
|
||||
|
||||
QTest::newRow( "testAverage1" ) << 45.0 << 135.0 << 90.0;
|
||||
QTest::newRow( "testAverage2" ) << 315.0 << 45.0 << 0.0;
|
||||
QTest::newRow( "testAverage3" ) << 45.0 << 315.0 << 0.0;
|
||||
QTest::newRow( "testAverage4" ) << 315.0 << 270.0 << 292.5;
|
||||
QTest::newRow( "testAverage5" ) << 140.0 << 240.0 << 190.0;
|
||||
QTest::newRow( "testAverage6" ) << 240.0 << 140.0 << 190.0;
|
||||
}
|
||||
|
||||
void TestQgsGeometryUtils::testAverageAngle()
|
||||
{
|
||||
QFETCH( double, angle1 );
|
||||
QFETCH( double, angle2 );
|
||||
QFETCH( double, expected );
|
||||
|
||||
double averageAngle = QgsGeometryUtils::averageAngle( angle1 * M_PI / 180.0, angle2 * M_PI / 180.0 ) * 180.0 / M_PI;
|
||||
QVERIFY( qgsDoubleNear( averageAngle, expected, 0.0000000001 ) );
|
||||
}
|
||||
|
||||
|
||||
QTEST_MAIN( TestQgsGeometryUtils )
|
||||
#include "testqgsgeometryutils.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user