28 #include <QPainterPath> 45 if ( mX.count() != otherLine->mX.count() )
48 for (
int i = 0; i < mX.count(); ++i )
66 auto result = qgis::make_unique< QgsCircularString >();
68 return result.release();
73 return QStringLiteral(
"CircularString" );
100 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
104 bbox = segmentBoundingBox(
QgsPoint( mX[i], mY[i] ),
QgsPoint( mX[i + 1], mY[i + 1] ),
QgsPoint( mX[i + 2], mY[i + 2] ) );
113 if ( nPoints > 0 && nPoints % 2 == 0 )
126 double centerX, centerY, radius;
137 QgsPointSequence compassPoints = compassPointsOnSegment( p1Angle, p2Angle, p3Angle, centerX, centerY, radius );
138 QgsPointSequence::const_iterator cpIt = compassPoints.constBegin();
139 for ( ; cpIt != compassPoints.constEnd(); ++cpIt )
141 bbox.combineExtentWith( cpIt->x(), cpIt->y() );
146 QgsPointSequence QgsCircularString::compassPointsOnSegment(
double p1Angle,
double p2Angle,
double p3Angle,
double centerX,
double centerY,
double radius )
150 QgsPoint nPoint( centerX, centerY + radius );
151 QgsPoint ePoint( centerX + radius, centerY );
152 QgsPoint sPoint( centerX, centerY - radius );
153 QgsPoint wPoint( centerX - radius, centerY );
155 if ( p3Angle >= p1Angle )
157 if ( p2Angle > p1Angle && p2Angle < p3Angle )
159 if ( p1Angle <= 90 && p3Angle >= 90 )
161 pointList.append( nPoint );
163 if ( p1Angle <= 180 && p3Angle >= 180 )
165 pointList.append( wPoint );
167 if ( p1Angle <= 270 && p3Angle >= 270 )
169 pointList.append( sPoint );
174 pointList.append( ePoint );
175 if ( p1Angle >= 90 || p3Angle <= 90 )
177 pointList.append( nPoint );
179 if ( p1Angle >= 180 || p3Angle <= 180 )
181 pointList.append( wPoint );
183 if ( p1Angle >= 270 || p3Angle <= 270 )
185 pointList.append( sPoint );
191 if ( p2Angle < p1Angle && p2Angle > p3Angle )
193 if ( p1Angle >= 270 && p3Angle <= 270 )
195 pointList.append( sPoint );
197 if ( p1Angle >= 180 && p3Angle <= 180 )
199 pointList.append( wPoint );
201 if ( p1Angle >= 90 && p3Angle <= 90 )
203 pointList.append( nPoint );
208 pointList.append( ePoint );
209 if ( p1Angle <= 270 || p3Angle >= 270 )
211 pointList.append( sPoint );
213 if ( p1Angle <= 180 || p3Angle >= 180 )
215 pointList.append( wPoint );
217 if ( p1Angle <= 90 || p3Angle >= 90 )
219 pointList.append( nPoint );
244 mX.resize( nVertices );
245 mY.resize( nVertices );
246 hasZ ? mZ.resize( nVertices ) : mZ.clear();
247 hasM ? mM.resize( nVertices ) : mM.clear();
248 for (
int i = 0; i < nVertices; ++i )
281 int binarySize =
sizeof( char ) +
sizeof( quint32 ) +
sizeof( quint32 );
285 wkbArray.resize( binarySize );
288 wkb << static_cast<quint32>(
wkbType() );
307 std::unique_ptr< QgsLineString > line(
curveToLine() );
308 QDomElement gml = line->asGml2( doc, precision, ns );
317 QDomElement elemCurve = doc.createElementNS( ns, QStringLiteral(
"Curve" ) );
322 QDomElement elemSegments = doc.createElementNS( ns, QStringLiteral(
"segments" ) );
323 QDomElement elemArcString = doc.createElementNS( ns, QStringLiteral(
"ArcString" ) );
325 elemSegments.appendChild( elemArcString );
326 elemCurve.appendChild( elemSegments );
333 std::unique_ptr< QgsLineString > line(
curveToLine() );
334 QString json = line->asJson( precision );
348 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
379 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
393 bool res =
snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
394 result->mX, result->mY, result->mZ, result->mM );
396 return result.release();
403 if ( mX.count() <= 3 )
406 double prevX = mX.at( 0 );
407 double prevY = mY.at( 0 );
409 bool useZ = hasZ && useZValues;
410 double prevZ = useZ ? mZ.at( 0 ) : 0;
412 int remaining = mX.count();
415 while ( i + 1 < remaining )
417 double currentCurveX = mX.at( i );
418 double currentCurveY = mY.at( i );
419 double currentX = mX.at( i + 1 );
420 double currentY = mY.at( i + 1 );
421 double currentZ = useZ ? mZ.at( i + 1 ) : 0;
454 return std::min( mX.size(), mY.size() );
459 if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
464 double x = mX.at( i );
465 double y = mY.at( i );
496 if ( index >= 0 && index < mX.size() )
497 return mX.at( index );
504 if ( index >= 0 && index < mY.size() )
505 return mY.at( index );
514 for (
int i = 0; i < nPts; ++i )
516 pts.push_back(
pointN( i ) );
524 if ( points.empty() )
535 const QgsPoint &firstPt = points.at( 0 );
536 bool hasZ = firstPt.
is3D();
541 mX.resize( points.size() );
542 mY.resize( points.size() );
545 mZ.resize( points.size() );
553 mM.resize( points.size() );
560 for (
int i = 0; i < points.size(); ++i )
562 mX[i] = points[i].x();
563 mY[i] = points[i].y();
566 double z = points.at( i ).z();
567 mZ[i] = std::isnan( z ) ? 0 : z;
571 double m = points.at( i ).m();
572 mM[i] = std::isnan( m ) ? 0 : m;
588 double *zArray = mZ.data();
592 bool useDummyZ = !hasZ || !transformZ;
595 zArray =
new double[nPoints];
596 for (
int i = 0; i < nPoints; ++i )
615 for (
int i = 0; i < nPoints; ++i )
618 t.map( mX.at( i ), mY.at( i ), &x, &y );
623 mZ[i] = mZ.at( i ) * zScale + zTranslate;
627 mM[i] = mM.at( i ) * mScale + mTranslate;
640 if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
642 path.moveTo( QPointF( mX[0], mY[0] ) );
645 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
649 for (
int j = 1; j < pt.size(); ++j )
651 path.lineTo( pt.at( j ).x(), pt.at( j ).y() );
659 if ( nPoints % 2 == 0 )
661 path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
666 void QgsCircularString::arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
668 double centerX, centerY, radius;
670 radius, centerX, centerY );
675 double diameter = 2 * radius;
676 path.arcTo( centerX - radius, centerY - radius, diameter, diameter, p1Angle, sweepAngle );
687 if ( position.
vertex >= mX.size() || position.
vertex < 1 )
692 mX.insert( position.
vertex, vertex.
x() );
693 mY.insert( position.
vertex, vertex.
y() );
696 mZ.insert( position.
vertex, vertex.
z() );
700 mM.insert( position.
vertex, vertex.
m() );
703 bool vertexNrEven = ( position.
vertex % 2 == 0 );
718 if ( position.
vertex < 0 || position.
vertex >= mX.size() )
723 mX[position.
vertex] = newPos.
x();
724 mY[position.
vertex] = newPos.
y();
727 mZ[position.
vertex] = newPos.
z();
731 mM[position.
vertex] = newPos.
m();
745 if ( position.
vertex < 0 || position.
vertex > ( nVertices - 1 ) )
750 if ( position.
vertex < ( nVertices - 2 ) )
783 double minDist = std::numeric_limits<double>::max();
786 int minDistLeftOf = 0;
788 double currentDist = 0.0;
791 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
793 currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
794 if ( currentDist < minDist )
796 minDist = currentDist;
797 minDistSegmentPoint = segmentPt;
806 if ( minDist == std::numeric_limits<double>::max() )
809 segmentPt = minDistSegmentPoint;
810 vertexAfter = minDistVertexAfter;
811 vertexAfter.
part = 0;
812 vertexAfter.
ring = 0;
835 for (
int i = 0; i < maxIndex; i += 2 )
838 QgsPoint p2( mX[i + 1], mY[i + 1] );
839 QgsPoint p3( mX[i + 2], mY[i + 2] );
849 sum += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
852 double midPointX = ( p1.
x() + p3.
x() ) / 2.0;
853 double midPointY = ( p1.
y() + p3.
y() ) / 2.0;
855 double radius, centerX, centerY;
859 double r2 = radius * radius;
870 double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
871 double circleChordArea = 0;
872 if ( circlePointLeftOfLine == centerPointLeftOfLine )
874 circleChordArea = M_PI * r2 * ( 1 - cov );
878 circleChordArea = M_PI * r2 * cov;
881 if ( !circlePointLeftOfLine )
883 sum += circleChordArea;
887 sum -= circleChordArea;
897 double QgsCircularString::closestPointOnArc(
double x1,
double y1,
double x2,
double y2,
double x3,
double y3,
900 double radius, centerX, centerY;
925 segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
926 vertexAfter.
vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
933 segmentPt.
setX( pt.
x() );
934 segmentPt.
setY( pt.
y() );
940 double sqrDistancePointToCenter = ( pt.
x() - centerX ) * ( pt.
x() - centerX ) + ( pt.
y() - centerY ) * ( pt.
y() - centerY );
941 *
leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
942 : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
948 void QgsCircularString::insertVertexBetween(
int after,
int before,
int pointOnCircle )
950 double xAfter = mX.at( after );
951 double yAfter = mY.at( after );
952 double xBefore = mX.at( before );
953 double yBefore = mY.at( before );
954 double xOnCircle = mX.at( pointOnCircle );
955 double yOnCircle = mY.at( pointOnCircle );
957 double radius, centerX, centerY;
960 double x = ( xAfter + xBefore ) / 2.0;
961 double y = ( yAfter + yBefore ) / 2.0;
964 mX.insert( before, newVertex.
x() );
965 mY.insert( before, newVertex.
y() );
969 mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
973 mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
986 int before = vId.
vertex - 1;
988 int after = vId.
vertex + 1;
990 if ( vId.
vertex % 2 != 0 )
1020 int vertex1 = vId.
vertex - 2;
1021 int vertex2 = vId.
vertex - 1;
1022 int vertex3 = vId.
vertex;
1024 QgsPoint( mX[vertex1], mY[vertex1] ),
QgsPoint( mX[vertex2], mY[vertex2] ),
QgsPoint( mX[vertex3], mY[vertex3] ) );
1025 int vertex4 = vId.
vertex + 1;
1026 int vertex5 = vId.
vertex + 2;
1028 QgsPoint( mX[vertex3], mY[vertex3] ),
QgsPoint( mX[vertex4], mY[vertex4] ),
QgsPoint( mX[vertex5], mY[vertex5] ) );
1037 if ( startVertex.
vertex < 0 || startVertex.
vertex >= mX.count() - 2 )
1040 if ( startVertex.
vertex % 2 == 1 )
1043 double x1 = mX.at( startVertex.
vertex );
1044 double y1 = mY.at( startVertex.
vertex );
1045 double x2 = mX.at( startVertex.
vertex + 1 );
1046 double y2 = mY.at( startVertex.
vertex + 1 );
1047 double x3 = mX.at( startVertex.
vertex + 2 );
1048 double y3 = mY.at( startVertex.
vertex + 2 );
1055 std::reverse( copy->mX.begin(), copy->mX.end() );
1056 std::reverse( copy->mY.begin(), copy->mY.end() );
1059 std::reverse( copy->mZ.begin(), copy->mZ.end() );
1063 std::reverse( copy->mM.begin(), copy->mM.end() );
1078 mZ.reserve( nPoints );
1079 for (
int i = 0; i < nPoints; ++i )
1096 mM.reserve( nPoints );
1097 for (
int i = 0; i < nPoints; ++i )
bool isMeasure() const
Returns true if the geometry contains m values.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
A rectangle specified with double values.
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
int numPoints() const override
Returns the number of points in the curve.
static bool circleAngleBetween(double angle, double angle1, double angle2, bool clockwise)
Returns true if, in a circle, angle is between angle1 and angle2.
static double ccwAngle(double dy, double dx)
Returns the counter clockwise angle between a line with components dx, dy and the line with dx > 0 an...
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml") const override
Returns a GML3 representation of the geometry.
static QPair< QgsWkbTypes::Type, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Angle between two linear segments.
bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
QString asJson(int precision=17) const override
Returns a GeoJSON representation of the geometry.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
static double circleLength(double x1, double y1, double x2, double y2, double x3, double y3)
Length of a circular string segment defined by pt1, pt2, pt3.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *DBL_EPSILON) const override
Searches for the closest segment of the geometry to a given point.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
static endian_t endian()
Returns whether this machine uses big or little endian.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
QgsPoint pointN(int i) const
Returns the point at index i within the circular string.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
static Type dropM(Type type)
Drops the m dimension (if present) for a WKB type and returns the new type.
QgsWkbTypes::Type mWkbType
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
void clear() override
Clears the geometry, ie reset it to a null geometry.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
static bool circleClockwise(double angle1, double angle2, double angle3)
Returns true if circle is ordered clockwise.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
QgsCircularString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership...
Type
The WKB type describes the number of dimensions a geometry has.
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
Utility class for identifying a unique vertex within a geometry.
QString geometryType() const override
Returns a unique string representing the geometry type.
double length() const override
Returns the length of the geometry.
static QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D)
Returns a gml::posList DOM element.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
static double circleTangentDirection(const QgsPoint &tangentPoint, const QgsPoint &cp1, const QgsPoint &cp2, const QgsPoint &cp3)
Calculates the direction angle of a circle tangent (clockwise from north in radians) ...
QgsCircularString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
static bool angleOnCircle(double angle, double angle1, double angle2, double angle3)
Returns true if an angle is between angle1 and angle3 on a circle described by angle1, angle2 and angle3.
Abstract base class for curved geometry type.
int dimension() const override
Returns the inherent dimension of the geometry.
bool removeDuplicateNodes(double epsilon=4 *DBL_EPSILON, bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Point geometry type, with support for z-dimension and m-values.
QgsPoint endPoint() const override
Returns the end point of the curve.
void setX(double x)
Sets the point's x-coordinate.
bool snapToGridPrivate(double hSpacing, double vSpacing, double dSpacing, double mSpacing, const QVector< double > &srcX, const QVector< double > &srcY, const QVector< double > &srcZ, const QVector< double > &srcM, QVector< double > &outX, QVector< double > &outY, QVector< double > &outZ, QVector< double > &outM) const
Helper function for QgsCurve subclasses to snap to grids.
void setY(double y)
Sets the point's y-coordinate.
QVector< QgsPoint > QgsPointSequence
void combineExtentWith(const QgsRectangle &rect)
Expand the rectangle so that covers both the original rectangle and the given rectangle.
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance)
Returns a point a specified distance toward a second point.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
static double sqrDistance2D(const QgsPoint &pt1, const QgsPoint &pt2)
Returns the squared 2D distance between two points.
static Type dropZ(Type type)
Drops the z dimension (if present) for a WKB type and returns the new type.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
static double sweepAngle(double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3)
Calculates angle of a circular string part defined by pt1, pt2, pt3.
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
QgsCircularString * clone() const override
Clones the geometry by performing a deep copy.
Line string geometry type, with support for z-dimension and m-values.
QgsCircularString * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid...
static int leftOfLine(double x, double y, double x1, double y1, double x2, double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> ( x2, y2).
void setPoints(const QgsPointSequence &points)
Sets the circular string's points.
QByteArray asWkb() const override
Returns a WKB representation of the geometry.
static void circleCenterRadius(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius, double ¢erX, double ¢erY)
Returns radius and center of the circle through pt1, pt2, pt3.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
static bool hasM(Type type)
Tests whether a WKB type contains m values.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Circular string geometry type.
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
static Type flatType(Type type)
Returns the flat type for a WKB type.
QgsWkbTypes::Type readHeader() const
readHeader
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml") const override
Returns a GML2 representation of the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether 'thepoint' is left or right of the line from 'p1' to 'p2'. Negativ values mean left a...
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
bool isEmpty() const override
Returns true if the geometry is empty.
QgsPoint startPoint() const override
Returns the starting point of the curve.
QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve...
bool dropMValue() override
Drops any measure values which exist in the geometry.
static void segmentizeArc(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, QgsPointSequence &points, double tolerance=M_PI_2/90, QgsAbstractGeometry::SegmentationToleranceType toleranceType=QgsAbstractGeometry::MaximumAngle, bool hasZ=false, bool hasM=false)
Convert circular arc defined by p1, p2, p3 (p1/p3 being start resp.