21#include <nlohmann/json.hpp>
37#include <QPainterPath>
47 bool hasZ = p1.
is3D();
80 int pointCount = std::min( x.size(), y.size() );
81 if ( x.size() == pointCount )
87 mX = x.mid( 0, pointCount );
89 if ( y.size() == pointCount )
95 mY = y.mid( 0, pointCount );
97 if ( !z.isEmpty() && z.count() >= pointCount )
100 if ( z.size() == pointCount )
106 mZ = z.mid( 0, pointCount );
109 if ( !m.isEmpty() && m.count() >= pointCount )
112 if ( m.size() == pointCount )
118 mM = m.mid( 0, pointCount );
131 auto result = std::make_unique< QgsCircularString >();
133 return result.release();
142 const int size = mX.size();
143 const int otherSize = otherLine->mX.size();
144 if ( size > otherSize )
148 else if ( size < otherSize )
155 else if ( !
is3D() && otherLine->
is3D() )
157 const bool considerZ =
is3D();
165 for (
int i = 0; i < size; i++ )
167 const double x = mX[i];
168 const double otherX = otherLine->mX[i];
173 else if ( x > otherX )
178 const double y = mY[i];
179 const double otherY = otherLine->mY[i];
184 else if ( y > otherY )
191 const double z = mZ[i];
192 const double otherZ = otherLine->mZ[i];
198 else if ( z > otherZ )
206 const double m = mM[i];
207 const double otherM = otherLine->mM[i];
213 else if ( m > otherM )
224 return QStringLiteral(
"CircularString" );
251 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
254 double zMin = std::numeric_limits<double>::quiet_NaN();
255 double zMax = std::numeric_limits<double>::quiet_NaN();
258 zMin = *std::min_element( mZ.begin() + i, mZ.begin() + i + 3 );
259 zMax = *std::max_element( mZ.begin() + i, mZ.begin() + i + 3 );
263 bbox =
QgsBox3D( box2d, zMin, zMax );
271 if ( nPoints > 0 && nPoints % 2 == 0 )
273 double z = std::numeric_limits<double>::quiet_NaN();
284 z = mZ[ nPoints - 1 ];
286 bbox.
combineWith( mX[ nPoints - 1 ], mY[ nPoints - 1 ], z );
293 const int size = mX.size();
294 if ( index < 1 || index >= size - 1 )
297 const bool useZ =
is3D();
300 QVector<double> newX( size );
301 QVector<double> newY( size );
302 QVector<double> newZ( useZ ? size : 0 );
303 QVector<double> newM( useM ? size : 0 );
304 auto it = std::copy( mX.constBegin() + index, mX.constEnd() - 1, newX.begin() );
305 it = std::copy( mX.constBegin(), mX.constBegin() + index, it );
306 *it = *newX.constBegin();
307 mX = std::move( newX );
309 it = std::copy( mY.constBegin() + index, mY.constEnd() - 1, newY.begin() );
310 it = std::copy( mY.constBegin(), mY.constBegin() + index, it );
311 *it = *newY.constBegin();
312 mY = std::move( newY );
315 it = std::copy( mZ.constBegin() + index, mZ.constEnd() - 1, newZ.begin() );
316 it = std::copy( mZ.constBegin(), mZ.constBegin() + index, it );
317 *it = *newZ.constBegin();
318 mZ = std::move( newZ );
322 it = std::copy( mM.constBegin() + index, mM.constEnd() - 1, newM.begin() );
323 it = std::copy( mM.constBegin(), mM.constBegin() + index, it );
324 *it = *newM.constBegin();
325 mM = std::move( newM );
331 double centerX, centerY, radius;
340 bbox.combineExtentWith( pt3.
x(), pt3.
y() );
342 QgsPointSequence compassPoints = compassPointsOnSegment( p1Angle, p2Angle, p3Angle, centerX, centerY, radius );
343 QgsPointSequence::const_iterator cpIt = compassPoints.constBegin();
344 for ( ; cpIt != compassPoints.constEnd(); ++cpIt )
346 bbox.combineExtentWith( cpIt->x(), cpIt->y() );
351QgsPointSequence QgsCircularString::compassPointsOnSegment(
double p1Angle,
double p2Angle,
double p3Angle,
double centerX,
double centerY,
double radius )
355 QgsPoint nPoint( centerX, centerY + radius );
356 QgsPoint ePoint( centerX + radius, centerY );
357 QgsPoint sPoint( centerX, centerY - radius );
358 QgsPoint wPoint( centerX - radius, centerY );
360 if ( p3Angle >= p1Angle )
362 if ( p2Angle > p1Angle && p2Angle < p3Angle )
364 if ( p1Angle <= 90 && p3Angle >= 90 )
366 pointList.append( nPoint );
368 if ( p1Angle <= 180 && p3Angle >= 180 )
370 pointList.append( wPoint );
372 if ( p1Angle <= 270 && p3Angle >= 270 )
374 pointList.append( sPoint );
379 pointList.append( ePoint );
380 if ( p1Angle >= 90 || p3Angle <= 90 )
382 pointList.append( nPoint );
384 if ( p1Angle >= 180 || p3Angle <= 180 )
386 pointList.append( wPoint );
388 if ( p1Angle >= 270 || p3Angle <= 270 )
390 pointList.append( sPoint );
396 if ( p2Angle < p1Angle && p2Angle > p3Angle )
398 if ( p1Angle >= 270 && p3Angle <= 270 )
400 pointList.append( sPoint );
402 if ( p1Angle >= 180 && p3Angle <= 180 )
404 pointList.append( wPoint );
406 if ( p1Angle >= 90 && p3Angle <= 90 )
408 pointList.append( nPoint );
413 pointList.append( ePoint );
414 if ( p1Angle <= 270 || p3Angle >= 270 )
416 pointList.append( sPoint );
418 if ( p1Angle <= 180 || p3Angle >= 180 )
420 pointList.append( wPoint );
422 if ( p1Angle <= 90 || p3Angle >= 90 )
424 pointList.append( nPoint );
445 const bool hasZ =
is3D();
449 mX.resize( nVertices );
450 mY.resize( nVertices );
452 mZ.resize( nVertices );
456 mM.resize( nVertices );
459 for (
int i = 0; i < nVertices; ++i )
486 parts.second =
parts.second.remove(
'(' ).remove(
')' );
487 QString secondWithoutParentheses =
parts.second;
488 secondWithoutParentheses = secondWithoutParentheses.simplified().remove(
' ' );
489 if ( (
parts.second.compare( QLatin1String(
"EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
490 secondWithoutParentheses.isEmpty() )
503 int binarySize =
sizeof( char ) +
sizeof( quint32 ) +
sizeof( quint32 );
514 wkb << static_cast<quint32>(
wkbType() );
526 wkt += QLatin1String(
"EMPTY" );
539 std::unique_ptr< QgsLineString > line(
curveToLine() );
540 QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
549 QDomElement elemCurve = doc.createElementNS( ns, QStringLiteral(
"Curve" ) );
554 QDomElement elemSegments = doc.createElementNS( ns, QStringLiteral(
"segments" ) );
555 QDomElement elemArcString = doc.createElementNS( ns, QStringLiteral(
"ArcString" ) );
557 elemSegments.appendChild( elemArcString );
558 elemCurve.appendChild( elemSegments );
566 std::unique_ptr< QgsLineString > line(
curveToLine() );
567 return line->asJsonObject( precision );
579 error = QObject::tr(
"CircularString has less than 3 points and is not empty." );
590 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
621 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
636 bool res =
snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
637 result->mX, result->mY, result->mZ, result->mM,
false );
639 return result.release();
646 std::unique_ptr< QgsLineString > line(
curveToLine() );
647 return line->simplifyByDistance( tolerance );
652 if ( mX.count() <= 3 )
655 double prevX = mX.at( 0 );
656 double prevY = mY.at( 0 );
658 bool useZ = hasZ && useZValues;
659 double prevZ = useZ ? mZ.at( 0 ) : 0;
661 int remaining = mX.count();
664 while ( i + 1 < remaining )
666 double currentCurveX = mX.at( i );
667 double currentCurveY = mY.at( i );
668 double currentX = mX.at( i + 1 );
669 double currentY = mY.at( i + 1 );
670 double currentZ = useZ ? mZ.at( i + 1 ) : 0;
703 return std::min( mX.size(), mY.size() );
708 const int size = mX.size();
712 const double *x = mX.constData();
713 const double *y = mY.constData();
714 const bool useZ =
is3D();
716 const double *z = useZ ? mZ.constData() :
nullptr;
717 const double *m = useM ? mM.constData() :
nullptr;
719 for (
int i = 0; i < size; i += 2 )
748 if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
753 double x = mX.at( i );
754 double y = mY.at( i );
785 if ( index >= 0 && index < mX.size() )
786 return mX.at( index );
793 if ( index >= 0 && index < mY.size() )
794 return mY.at( index );
801 if ( index >= 0 && index < mZ.size() )
802 return mZ.at( index );
809 if ( index >= 0 && index < mM.size() )
810 return mM.at( index );
822 int size = mX.size();
824 double *srcX = mX.data();
825 double *srcY = mY.data();
826 double *srcM = hasM ? mM.data() :
nullptr;
827 double *srcZ = hasZ ? mZ.data() :
nullptr;
830 for (
int i = 0; i < size; ++i )
834 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
835 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
863 int size = mX.size();
865 double *srcX = mX.data();
866 double *srcY = mY.data();
867 double *srcM = hasM ? mM.data() :
nullptr;
868 double *srcZ = hasZ ? mZ.data() :
nullptr;
870 double *destX = srcX;
871 double *destY = srcY;
872 double *destM = srcM;
873 double *destZ = srcZ;
875 int filteredPoints = 0;
876 for (
int i = 0; i < size; ++i )
880 double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
881 double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
883 if ( filter(
QgsPoint( x, y, z, m ) ) )
895 mX.resize( filteredPoints );
896 mY.resize( filteredPoints );
898 mZ.resize( filteredPoints );
900 mM.resize( filteredPoints );
909 int size = mX.size();
911 double *srcX = mX.data();
912 double *srcY = mY.data();
913 double *srcM = hasM ? mM.data() :
nullptr;
914 double *srcZ = hasZ ? mZ.data() :
nullptr;
916 for (
int i = 0; i < size; ++i )
920 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
921 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
935 const bool useZ =
is3D();
938 const int size = mX.size();
940 return std::make_tuple( std::make_unique< QgsCircularString >(), std::make_unique< QgsCircularString >() );
942 index = std::clamp( index, 0, size - 1 );
944 const int part1Size = index + 1;
945 QVector< double > x1( part1Size );
946 QVector< double > y1( part1Size );
947 QVector< double > z1( useZ ? part1Size : 0 );
948 QVector< double > m1( useM ? part1Size : 0 );
950 const double *sourceX = mX.constData();
951 const double *sourceY = mY.constData();
952 const double *sourceZ = useZ ? mZ.constData() :
nullptr;
953 const double *sourceM = useM ? mM.constData() :
nullptr;
955 double *destX = x1.data();
956 double *destY = y1.data();
957 double *destZ = useZ ? z1.data() :
nullptr;
958 double *destM = useM ? m1.data() :
nullptr;
960 std::copy( sourceX, sourceX + part1Size, destX );
961 std::copy( sourceY, sourceY + part1Size, destY );
963 std::copy( sourceZ, sourceZ + part1Size, destZ );
965 std::copy( sourceM, sourceM + part1Size, destM );
967 const int part2Size = size - index;
969 return std::make_tuple( std::make_unique< QgsCircularString >( x1, y1, z1, m1 ), std::make_unique< QgsCircularString >() );
971 QVector< double > x2( part2Size );
972 QVector< double > y2( part2Size );
973 QVector< double > z2( useZ ? part2Size : 0 );
974 QVector< double > m2( useM ? part2Size : 0 );
977 destZ = useZ ? z2.data() :
nullptr;
978 destM = useM ? m2.data() :
nullptr;
979 std::copy( sourceX + index, sourceX + size, destX );
980 std::copy( sourceY + index, sourceY + size, destY );
982 std::copy( sourceZ + index, sourceZ + size, destZ );
984 std::copy( sourceM + index, sourceM + size, destM );
987 return std::make_tuple( std::make_unique< QgsCircularString >(), std::make_unique< QgsCircularString >( x2, y2, z2, m2 ) );
989 return std::make_tuple( std::make_unique< QgsCircularString >( x1, y1, z1, m1 ), std::make_unique< QgsCircularString >( x2, y2, z2, m2 ) );
996 for (
int i = 0; i < nPts; ++i )
998 pts.push_back(
pointN( i ) );
1018 bool hasZ = firstPt.
is3D();
1023 mX.resize(
points.size() );
1024 mY.resize(
points.size() );
1027 mZ.resize(
points.size() );
1035 mM.resize(
points.size() );
1042 for (
int i = 0; i <
points.size(); ++i )
1048 double z =
points.at( i ).z();
1049 mZ[i] = std::isnan( z ) ? 0 : z;
1053 double m =
points.at( i ).m();
1054 mM[i] = std::isnan( m ) ? 0 : m;
1061 if ( !line || line->
isEmpty() )
1104 mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
1117 mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
1135 double *zArray =
nullptr;
1141 std::unique_ptr< double[] > dummyZ;
1142 if ( !hasZ || !transformZ )
1144 dummyZ = std::make_unique<double[]>( nPoints );
1145 zArray = dummyZ.get();
1161 for (
int i = 0; i < nPoints; ++i )
1164 t.map( mX.at( i ), mY.at( i ), &x, &y );
1169 mZ[i] = mZ.at( i ) * zScale + zTranslate;
1173 mM[i] = mM.at( i ) * mScale + mTranslate;
1178void arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
1180 double centerX, centerY, radius;
1182 radius, centerX, centerY );
1187 double diameter = 2 * radius;
1188 path.arcTo( centerX - radius, centerY - radius, diameter, diameter, -p1Angle, -sweepAngle );
1199 if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
1201 path.moveTo( QPointF( mX[0], mY[0] ) );
1204 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
1206 arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
1210 if ( nPoints % 2 == 0 )
1212 path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
1223 if ( position.
vertex >= mX.size() || position.
vertex < 1 )
1228 mX.insert( position.
vertex, vertex.
x() );
1229 mY.insert( position.
vertex, vertex.
y() );
1232 mZ.insert( position.
vertex, vertex.
z() );
1236 mM.insert( position.
vertex, vertex.
m() );
1239 bool vertexNrEven = ( position.
vertex % 2 == 0 );
1254 if ( position.
vertex < 0 || position.
vertex >= mX.size() )
1259 mX[position.
vertex] = newPos.
x();
1260 mY[position.
vertex] = newPos.
y();
1263 mZ[position.
vertex] = newPos.
z();
1267 mM[position.
vertex] = newPos.
m();
1276 if ( nVertices < 4 )
1281 if ( position.
vertex < 0 || position.
vertex > ( nVertices - 1 ) )
1286 if ( position.
vertex < ( nVertices - 2 ) )
1319 double minDist = std::numeric_limits<double>::max();
1322 int minDistLeftOf = 0;
1324 double currentDist = 0.0;
1327 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
1329 currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
1330 if ( currentDist < minDist )
1332 minDist = currentDist;
1333 minDistSegmentPoint = segmentPt;
1337 minDistLeftOf = *leftOf;
1342 if ( minDist == std::numeric_limits<double>::max() )
1345 segmentPt = minDistSegmentPoint;
1346 vertexAfter = minDistVertexAfter;
1347 vertexAfter.
part = 0;
1348 vertexAfter.
ring = 0;
1351 *leftOf =
qgsDoubleNear( minDist, 0.0 ) ? 0 : minDistLeftOf;
1377 for (
int i = 0; i < maxIndex; i += 2 )
1380 QgsPoint p2( mX[i + 1], mY[i + 1] );
1381 QgsPoint p3( mX[i + 2], mY[i + 2] );
1391 mSummedUpArea += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
1394 double midPointX = ( p1.
x() + p3.
x() ) / 2.0;
1395 double midPointY = ( p1.
y() + p3.
y() ) / 2.0;
1397 double radius, centerX, centerY;
1401 double r2 = radius * radius;
1412 double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
1413 double circleChordArea = 0;
1414 if ( circlePointLeftOfLine == centerPointLeftOfLine )
1416 circleChordArea = M_PI * r2 * ( 1 - cov );
1420 circleChordArea = M_PI * r2 * cov;
1423 if ( !circlePointLeftOfLine )
1442double QgsCircularString::closestPointOnArc(
double x1,
double y1,
double x2,
double y2,
double x3,
double y3,
1445 double radius, centerX, centerY;
1470 segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1471 vertexAfter.
vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1478 segmentPt.
setX( pt.
x() );
1479 segmentPt.
setY( pt.
y() );
1485 double sqrDistancePointToCenter = pt.
distanceSquared( centerX, centerY );
1486 *
leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1487 : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1493void QgsCircularString::insertVertexBetween(
int after,
int before,
int pointOnCircle )
1495 double xAfter = mX.at( after );
1496 double yAfter = mY.at( after );
1497 double xBefore = mX.at( before );
1498 double yBefore = mY.at( before );
1499 double xOnCircle = mX.at( pointOnCircle );
1500 double yOnCircle = mY.at( pointOnCircle );
1502 double radius, centerX, centerY;
1505 double x = ( xAfter + xBefore ) / 2.0;
1506 double y = ( yAfter + yBefore ) / 2.0;
1509 mX.insert( before, newVertex.
x() );
1510 mY.insert( before, newVertex.
y() );
1514 mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1518 mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1531 int before = vId.
vertex - 1;
1533 int after = vId.
vertex + 1;
1535 if ( vId.
vertex % 2 != 0 )
1565 int vertex1 = vId.
vertex - 2;
1566 int vertex2 = vId.
vertex - 1;
1567 int vertex3 = vId.
vertex;
1569 QgsPoint( mX[vertex1], mY[vertex1] ),
QgsPoint( mX[vertex2], mY[vertex2] ),
QgsPoint( mX[vertex3], mY[vertex3] ) );
1570 int vertex4 = vId.
vertex + 1;
1571 int vertex5 = vId.
vertex + 2;
1573 QgsPoint( mX[vertex3], mY[vertex3] ),
QgsPoint( mX[vertex4], mY[vertex4] ),
QgsPoint( mX[vertex5], mY[vertex5] ) );
1582 if ( startVertex.
vertex % 2 == 1 )
1585 if ( startVertex.
vertex < 0 || startVertex.
vertex >= mX.count() - 2 )
1588 double x1 = mX.at( startVertex.
vertex );
1589 double y1 = mY.at( startVertex.
vertex );
1590 double x2 = mX.at( startVertex.
vertex + 1 );
1591 double y2 = mY.at( startVertex.
vertex + 1 );
1592 double x3 = mX.at( startVertex.
vertex + 2 );
1593 double y3 = mY.at( startVertex.
vertex + 2 );
1606 if ( fromVertex.
part != 0 || fromVertex.
ring != 0 || toVertex.
part != 0 || toVertex.
ring != 0 )
1609 const int fromVertexNumber = fromVertex.
vertex;
1610 const int toVertexNumber = toVertex.
vertex;
1613 if ( fromVertexNumber < 0 || fromVertexNumber >= nPoints || toVertexNumber < 0 || toVertexNumber >= nPoints )
1616 if ( fromVertexNumber == toVertexNumber )
1619 const double *xData = mX.constData();
1620 const double *yData = mY.constData();
1621 double totalDistance = 0.0;
1625 const int startArc = ( fromVertexNumber / 2 ) * 2;
1628 for (
int i = startArc; i < nPoints - 2; i += 2 )
1631 double x1 = xData[i];
1632 double y1 = yData[i];
1633 double x2 = xData[i + 1];
1634 double y2 = yData[i + 1];
1635 double x3 = xData[i + 2];
1636 double y3 = yData[i + 2];
1639 if ( fromVertexNumber >= i && toVertexNumber <= i + 2 )
1641 if ( fromVertexNumber == i && toVertexNumber == i + 2 )
1646 else if ( fromVertexNumber == i && toVertexNumber == i + 1 )
1649 double centerX, centerY, radius;
1652 return QgsGeometryUtilsBase::calculateArcLength( centerX, centerY, radius, x1, y1, x2, y2, x3, y3, 0, 1 );
1654 else if ( fromVertexNumber == i + 1 && toVertexNumber == i + 2 )
1657 double centerX, centerY, radius;
1660 return QgsGeometryUtilsBase::calculateArcLength( centerX, centerY, radius, x1, y1, x2, y2, x3, y3, 1, 2 );
1662 else if ( fromVertexNumber == i + 1 && toVertexNumber == i + 1 )
1669 bool startInThisSegment = ( fromVertexNumber >= i && fromVertexNumber <= i + 2 );
1670 bool endInThisSegment = ( toVertexNumber >= i && toVertexNumber <= i + 2 );
1671 bool segmentInRange = ( fromVertexNumber < i && toVertexNumber > i + 2 );
1673 if ( startInThisSegment && !endInThisSegment )
1676 if ( fromVertexNumber == i )
1678 else if ( fromVertexNumber == i + 1 )
1681 double centerX, centerY, radius;
1683 totalDistance +=
QgsGeometryUtilsBase::calculateArcLength( centerX, centerY, radius, x1, y1, x2, y2, x3, y3, 1, 2 );
1686 else if ( !startInThisSegment && endInThisSegment )
1689 if ( toVertexNumber == i + 1 )
1692 double centerX, centerY, radius;
1694 totalDistance +=
QgsGeometryUtilsBase::calculateArcLength( centerX, centerY, radius, x1, y1, x2, y2, x3, y3, 0, 1 );
1696 else if ( toVertexNumber == i + 2 )
1700 else if ( segmentInRange )
1707 return totalDistance;
1714 std::reverse( copy->mX.begin(), copy->mX.end() );
1715 std::reverse( copy->mY.begin(), copy->mY.end() );
1718 std::reverse( copy->mZ.begin(), copy->mZ.end() );
1722 std::reverse( copy->mM.begin(), copy->mM.end() );
1734 double distanceTraversed = 0;
1736 if ( totalPoints == 0 )
1745 const double *x = mX.constData();
1746 const double *y = mY.constData();
1747 const double *z =
is3D() ? mZ.constData() :
nullptr;
1748 const double *m =
isMeasure() ? mM.constData() :
nullptr;
1750 double prevX = *x++;
1751 double prevY = *y++;
1752 double prevZ = z ? *z++ : 0.0;
1753 double prevM = m ? *m++ : 0.0;
1757 return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1760 for (
int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1769 double z2 = z ? *z++ : 0.0;
1770 double m2 = m ? *m++ : 0.0;
1774 double z3 = z ? *z++ : 0.0;
1775 double m3 = m ? *m++ : 0.0;
1781 const double distanceToPoint = std::min( distance - distanceTraversed,
segmentLength );
1783 QgsPoint( pointType, x2, y2, z2, m2 ),
1784 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1800 if ( startDistance < 0 && endDistance < 0 )
1803 endDistance = std::max( startDistance, endDistance );
1806 if ( totalPoints == 0 )
1809 QVector< QgsPoint > substringPoints;
1810 substringPoints.reserve( totalPoints );
1818 const double *x = mX.constData();
1819 const double *y = mY.constData();
1820 const double *z =
is3D() ? mZ.constData() :
nullptr;
1821 const double *m =
isMeasure() ? mM.constData() :
nullptr;
1823 double distanceTraversed = 0;
1824 double prevX = *x++;
1825 double prevY = *y++;
1826 double prevZ = z ? *z++ : 0.0;
1827 double prevM = m ? *m++ : 0.0;
1828 bool foundStart =
false;
1830 if ( startDistance < 0 )
1833 for (
int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1842 double z2 = z ? *z++ : 0.0;
1843 double m2 = m ? *m++ : 0.0;
1847 double z3 = z ? *z++ : 0.0;
1848 double m3 = m ? *m++ : 0.0;
1850 bool addedSegmentEnd =
false;
1852 if ( distanceTraversed <= startDistance && startDistance < distanceTraversed +
segmentLength )
1855 const double distanceToStart = startDistance - distanceTraversed;
1857 QgsPoint( pointType, x2, y2, z2, m2 ),
1858 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
1861 const bool endPointOnSegment = distanceTraversed +
segmentLength > endDistance;
1862 if ( endPointOnSegment )
1864 const double distanceToEnd = endDistance - distanceTraversed;
1865 const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
1868 QgsPoint( pointType, x2, y2, z2, m2 ),
1869 QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1871 QgsPoint( pointType, x2, y2, z2, m2 ),
1872 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1873 addedSegmentEnd =
true;
1877 const double midPointDistance = (
segmentLength - distanceToStart ) * 0.5 + distanceToStart;
1880 QgsPoint( pointType, x2, y2, z2, m2 ),
1881 QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1882 <<
QgsPoint( pointType, x3, y3, z3, m3 );
1883 addedSegmentEnd =
true;
1887 if ( !addedSegmentEnd && foundStart && ( distanceTraversed +
segmentLength > endDistance ) )
1890 const double distanceToEnd = endDistance - distanceTraversed;
1893 QgsPoint( pointType, x2, y2, z2, m2 ),
1894 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
1897 QgsPoint( pointType, x2, y2, z2, m2 ),
1898 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1900 else if ( !addedSegmentEnd && foundStart )
1902 substringPoints <<
QgsPoint( pointType, x2, y2, z2, m2 )
1903 <<
QgsPoint( pointType, x3, y3, z3, m3 );
1911 if ( distanceTraversed >= endDistance )
1916 if ( !foundStart &&
qgsDoubleNear( distanceTraversed, startDistance ) )
1918 substringPoints <<
QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1919 <<
QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1920 <<
QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1923 auto result = std::make_unique< QgsCircularString >();
1924 result->setPoints( substringPoints );
1925 return result.release();
1938 mZ.reserve( nPoints );
1939 for (
int i = 0; i < nPoints; ++i )
1956 mM.reserve( nPoints );
1957 for (
int i = 0; i < nPoints; ++i )
1990 std::swap( mX, mY );
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
VertexType
Types of vertex.
@ Curve
An intermediate point on a segment defining the curvature of the segment.
@ Segment
The actual start or end point of a segment.
WkbType
The WKB type describes the number of dimensions a geometry has.
@ CircularString
CircularString.
@ CircularStringZ
CircularStringZ.
TransformDirection
Indicates the direction (forward or inverse) of a transform.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
bool isMeasure() const
Returns true if the geometry contains m values.
QFlags< WkbFlag > WkbFlags
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, Qgis::WkbType baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
QgsAbstractGeometry()=default
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
static endian_t endian()
Returns whether this machine uses big or little endian.
A 3-dimensional box composed of x, y, z coordinates.
void combineWith(const QgsBox3D &box)
Expands the bbox so that it covers both the original rectangle and the given rectangle.
QgsCircularString * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0, bool removeRedundantPoints=false) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
QString geometryType() const override
Returns a unique string representing the geometry type.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
QgsCircularString * clone() const override
Clones the geometry by performing a deep copy.
QgsPoint endPoint() const override
Returns the end point of the curve.
void append(const QgsCircularString *string)
Appends the contents of another circular string to the end of this circular string.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
QgsCircularString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
void swapXy() override
Swaps the x and y coordinates from the geometry.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
double distanceBetweenVertices(QgsVertexId fromVertex, QgsVertexId toVertex) const override
Returns the distance along the curve between two vertices.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
static QgsCircularString fromTwoPointsAndCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint ¢er, bool useShortestArc=true)
Creates a circular string with a single arc representing the curve from p1 to p2 with the specified c...
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
QgsCircularString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
QgsBox3D calculateBoundingBox3D() const override
Calculates the minimal 3D bounding box for the geometry.
QgsPoint startPoint() const override
Returns the starting point of the curve.
bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
bool isEmpty() const override
Returns true if the geometry is empty.
int indexOf(const QgsPoint &point) const final
Returns the index of the first vertex matching the given point, or -1 if a matching vertex is not fou...
bool dropMValue() override
Drops any measure values which exist in the geometry.
int numPoints() const override
Returns the number of points in the curve.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb().
QgsCircularString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
int dimension() const override
Returns the inherent dimension of the geometry.
void setPoints(const QgsPointSequence &points)
Sets the circular string's points.
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.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
QgsAbstractGeometry * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns a WKB representation of the geometry.
void clear() override
Clears the geometry, ie reset it to a null geometry.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
void scroll(int firstVertexIndex) final
Scrolls the curve vertices so that they start with the vertex at the given index.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
double zAt(int index) const override
Returns the z-coordinate of the specified node in the line string.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
QgsCircularString()
Constructs an empty circular string.
QgsPoint pointN(int i) const
Returns the point at index i within the circular string.
std::tuple< std::unique_ptr< QgsCurve >, std::unique_ptr< QgsCurve > > splitCurveAtVertex(int index) const final
Splits the curve at the specified vertex index, returning two curves which represent the portion of t...
double mAt(int index) const override
Returns the m-coordinate of the specified node in the line string.
int compareToSameClass(const QgsAbstractGeometry *other) const final
Compares to an other geometry of the same class, and returns a integer for sorting of the two geometr...
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
Qgis::WkbType readHeader() const
readHeader
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
bool mHasCachedSummedUpArea
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
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, bool removeRedundantPoints) const
Helper function for QgsCurve subclasses to snap to grids.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
bool isCanceled() const
Tells whether the operation has been canceled already.
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.
static double calculateArcLength(double centerX, double centerY, double radius, double x1, double y1, double x2, double y2, double x3, double y3, int fromVertex, int toVertex)
Calculates the precise arc length between two vertices on a circular arc.
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...
static bool circleClockwise(double angle1, double angle2, double angle3)
Returns true if the circle defined by three angles is ordered clockwise.
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 double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1,...
static bool circleAngleBetween(double angle, double angle1, double angle2, bool clockwise)
Returns true if, in a circle, angle is between angle1 and angle2.
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,...
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2).
static void circleCenterRadius(double x1, double y1, double x2, double y2, double x3, double y3, double &radius, double ¢erX, double ¢erY)
Returns radius and center of the circle through (x1 y1), (x2 y2), (x3 y3).
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).
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance)
Returns a point a specified distance toward a second point.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure, QgsAbstractGeometry::WkbFlags flags)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
static QPair< Qgis::WkbType, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
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 QgsPoint interpolatePointOnArc(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double distance)
Interpolates a point on an arc defined by three points, pt1, pt2 and pt3.
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
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.
static Q_DECL_DEPRECATED double sqrDistance2D(double x1, double y1, double x2, double y2)
Returns the squared 2D distance between (x1, y1) and (x2, y2).
static QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::posList DOM element.
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
static QgsPoint segmentMidPointFromCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint ¢er, bool useShortestArc=true)
Calculates the midpoint on the circle passing through p1 and p2, with the specified center coordinate...
Line string geometry type, with support for z-dimension and m-values.
void setPoints(size_t size, const double *x, const double *y, const double *z=nullptr, const double *m=nullptr)
Resets the line string to match the specified point data.
Point geometry type, with support for z-dimension and m-values.
void setY(double y)
Sets the point's y-coordinate.
void setX(double x)
Sets the point's x-coordinate.
double distanceSquared(double x, double y) const
Returns the Cartesian 2D squared distance between this point a specified x, y coordinate.
A rectangle specified with double values.
static Qgis::WkbType dropM(Qgis::WkbType type)
Drops the m dimension (if present) for a WKB type and returns the new type.
static Qgis::WkbType dropZ(Qgis::WkbType type)
Drops the z dimension (if present) for a WKB type and returns the new type.
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
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'. Negative values mean left ...
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsPoint > QgsPointSequence
void arcTo(QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3)
Utility class for identifying a unique vertex within a geometry.