21#include <nlohmann/json.hpp>
38#include <QPainterPath>
41using namespace Qt::StringLiterals;
51 bool hasZ = p1.
is3D();
84 int pointCount = std::min( x.size(), y.size() );
85 if ( x.size() == pointCount )
91 mX = x.mid( 0, pointCount );
93 if ( y.size() == pointCount )
99 mY = y.mid( 0, pointCount );
101 if ( !z.isEmpty() && z.count() >= pointCount )
104 if ( z.size() == pointCount )
110 mZ = z.mid( 0, pointCount );
113 if ( !m.isEmpty() && m.count() >= pointCount )
116 if ( m.size() == pointCount )
122 mM = m.mid( 0, pointCount );
135 auto result = std::make_unique< QgsCircularString >();
137 return result.release();
146 const int size = mX.size();
147 const int otherSize = otherLine->mX.size();
148 if ( size > otherSize )
152 else if ( size < otherSize )
159 else if ( !
is3D() && otherLine->
is3D() )
161 const bool considerZ =
is3D();
169 for (
int i = 0; i < size; i++ )
171 const double x = mX[i];
172 const double otherX = otherLine->mX[i];
177 else if ( x > otherX )
182 const double y = mY[i];
183 const double otherY = otherLine->mY[i];
188 else if ( y > otherY )
195 const double z = mZ[i];
196 const double otherZ = otherLine->mZ[i];
202 else if ( z > otherZ )
210 const double m = mM[i];
211 const double otherM = otherLine->mM[i];
217 else if ( m > otherM )
228 return u
"CircularString"_s;
255 for (
int i = 0; i < ( nPoints - 2 ); i += 2 )
258 double zMin = std::numeric_limits<double>::quiet_NaN();
259 double zMax = std::numeric_limits<double>::quiet_NaN();
262 zMin = *std::min_element( mZ.begin() + i, mZ.begin() + i + 3 );
263 zMax = *std::max_element( mZ.begin() + i, mZ.begin() + i + 3 );
267 bbox =
QgsBox3D( box2d, zMin, zMax );
275 if ( nPoints > 0 && nPoints % 2 == 0 )
277 double z = std::numeric_limits<double>::quiet_NaN();
290 bbox.
combineWith( mX[nPoints - 1], mY[nPoints - 1], z );
297 const int size = mX.size();
298 if ( index < 1 || index >= size - 1 )
301 const bool useZ =
is3D();
304 QVector<double> newX( size );
305 QVector<double> newY( size );
306 QVector<double> newZ( useZ ? size : 0 );
307 QVector<double> newM( useM ? size : 0 );
308 auto it = std::copy( mX.constBegin() + index, mX.constEnd() - 1, newX.begin() );
309 it = std::copy( mX.constBegin(), mX.constBegin() + index, it );
310 *it = *newX.constBegin();
311 mX = std::move( newX );
313 it = std::copy( mY.constBegin() + index, mY.constEnd() - 1, newY.begin() );
314 it = std::copy( mY.constBegin(), mY.constBegin() + index, it );
315 *it = *newY.constBegin();
316 mY = std::move( newY );
319 it = std::copy( mZ.constBegin() + index, mZ.constEnd() - 1, newZ.begin() );
320 it = std::copy( mZ.constBegin(), mZ.constBegin() + index, it );
321 *it = *newZ.constBegin();
322 mZ = std::move( newZ );
326 it = std::copy( mM.constBegin() + index, mM.constEnd() - 1, newM.begin() );
327 it = std::copy( mM.constBegin(), mM.constBegin() + index, it );
328 *it = *newM.constBegin();
329 mM = std::move( newM );
335 double centerX, centerY, radius;
344 bbox.combineExtentWith( pt3.
x(), pt3.
y() );
346 QgsPointSequence compassPoints = compassPointsOnSegment( p1Angle, p2Angle, p3Angle, centerX, centerY, radius );
347 QgsPointSequence::const_iterator cpIt = compassPoints.constBegin();
348 for ( ; cpIt != compassPoints.constEnd(); ++cpIt )
350 bbox.combineExtentWith( cpIt->x(), cpIt->y() );
355QgsPointSequence QgsCircularString::compassPointsOnSegment(
double p1Angle,
double p2Angle,
double p3Angle,
double centerX,
double centerY,
double radius )
359 QgsPoint nPoint( centerX, centerY + radius );
360 QgsPoint ePoint( centerX + radius, centerY );
361 QgsPoint sPoint( centerX, centerY - radius );
362 QgsPoint wPoint( centerX - radius, centerY );
364 if ( p3Angle >= p1Angle )
366 if ( p2Angle > p1Angle && p2Angle < p3Angle )
368 if ( p1Angle <= 90 && p3Angle >= 90 )
370 pointList.append( nPoint );
372 if ( p1Angle <= 180 && p3Angle >= 180 )
374 pointList.append( wPoint );
376 if ( p1Angle <= 270 && p3Angle >= 270 )
378 pointList.append( sPoint );
383 pointList.append( ePoint );
384 if ( p1Angle >= 90 || p3Angle <= 90 )
386 pointList.append( nPoint );
388 if ( p1Angle >= 180 || p3Angle <= 180 )
390 pointList.append( wPoint );
392 if ( p1Angle >= 270 || p3Angle <= 270 )
394 pointList.append( sPoint );
400 if ( p2Angle < p1Angle && p2Angle > p3Angle )
402 if ( p1Angle >= 270 && p3Angle <= 270 )
404 pointList.append( sPoint );
406 if ( p1Angle >= 180 && p3Angle <= 180 )
408 pointList.append( wPoint );
410 if ( p1Angle >= 90 && p3Angle <= 90 )
412 pointList.append( nPoint );
417 pointList.append( ePoint );
418 if ( p1Angle <= 270 || p3Angle >= 270 )
420 pointList.append( sPoint );
422 if ( p1Angle <= 180 || p3Angle >= 180 )
424 pointList.append( wPoint );
426 if ( p1Angle <= 90 || p3Angle >= 90 )
428 pointList.append( nPoint );
449 const bool hasZ =
is3D();
453 mX.resize( nVertices );
454 mY.resize( nVertices );
456 mZ.resize( nVertices );
460 mM.resize( nVertices );
463 for (
int i = 0; i < nVertices; ++i )
490 parts.second =
parts.second.remove(
'(' ).remove(
')' );
491 QString secondWithoutParentheses =
parts.second;
492 secondWithoutParentheses = secondWithoutParentheses.simplified().remove(
' ' );
493 if ( (
parts.second.compare(
"EMPTY"_L1, Qt::CaseInsensitive ) == 0 ) || secondWithoutParentheses.isEmpty() )
506 int binarySize =
sizeof( char ) +
sizeof( quint32 ) +
sizeof( quint32 );
517 wkb << static_cast<quint32>(
wkbType() );
542 std::unique_ptr< QgsLineString > line(
curveToLine() );
543 QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
552 QDomElement elemCurve = doc.createElementNS( ns, u
"Curve"_s );
557 QDomElement elemSegments = doc.createElementNS( ns, u
"segments"_s );
558 QDomElement elemArcString = doc.createElementNS( ns, u
"ArcString"_s );
560 elemSegments.appendChild( elemArcString );
561 elemCurve.appendChild( elemSegments );
569 std::unique_ptr< QgsLineString > line(
curveToLine() );
570 return line->asJsonObject( precision );
582 error = QObject::tr(
"CircularString has less than 3 points and is not empty." );
593 for (
int i = 0; i < ( nPoints - 2 ); i += 2 )
624 for (
int i = 0; i < ( nPoints - 2 ); i += 2 )
639 bool res =
snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM, result->mX, result->mY, result->mZ, result->mM,
false );
641 return result.release();
648 std::unique_ptr< QgsLineString > line(
curveToLine() );
649 return line->simplifyByDistance( tolerance );
654 if ( mX.count() <= 3 )
657 double prevX = mX.at( 0 );
658 double prevY = mY.at( 0 );
660 bool useZ = hasZ && useZValues;
661 double prevZ = useZ ? mZ.at( 0 ) : 0;
663 int remaining = mX.count();
666 while ( i + 1 < remaining )
668 double currentCurveX = mX.at( i );
669 double currentCurveY = mY.at( i );
670 double currentX = mX.at( i + 1 );
671 double currentY = mY.at( i + 1 );
672 double currentZ = useZ ? mZ.at( i + 1 ) : 0;
705 return std::min( mX.size(), mY.size() );
710 const int size = mX.size();
714 const double *x = mX.constData();
715 const double *y = mY.constData();
716 const bool useZ =
is3D();
718 const double *z = useZ ? mZ.constData() :
nullptr;
719 const double *m = useM ? mM.constData() :
nullptr;
721 for (
int i = 0; i < size; i += 2 )
747 if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
752 double x = mX.at( i );
753 double y = mY.at( i );
784 if ( index >= 0 && index < mX.size() )
785 return mX.at( index );
792 if ( index >= 0 && index < mY.size() )
793 return mY.at( index );
800 if ( index >= 0 && index < mZ.size() )
801 return mZ.at( index );
808 if ( index >= 0 && index < mM.size() )
809 return mM.at( index );
821 int size = mX.size();
823 double *srcX = mX.data();
824 double *srcY = mY.data();
825 double *srcM = hasM ? mM.data() :
nullptr;
826 double *srcZ = hasZ ? mZ.data() :
nullptr;
829 for (
int i = 0; i < size; ++i )
833 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
834 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
862 int size = mX.size();
864 double *srcX = mX.data();
865 double *srcY = mY.data();
866 double *srcM = hasM ? mM.data() :
nullptr;
867 double *srcZ = hasZ ? mZ.data() :
nullptr;
869 double *destX = srcX;
870 double *destY = srcY;
871 double *destM = srcM;
872 double *destZ = srcZ;
874 int filteredPoints = 0;
875 for (
int i = 0; i < size; ++i )
879 double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
880 double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
882 if ( filter(
QgsPoint( x, y, z, m ) ) )
894 mX.resize( filteredPoints );
895 mY.resize( filteredPoints );
897 mZ.resize( filteredPoints );
899 mM.resize( filteredPoints );
908 int size = mX.size();
910 double *srcX = mX.data();
911 double *srcY = mY.data();
912 double *srcM = hasM ? mM.data() :
nullptr;
913 double *srcZ = hasZ ? mZ.data() :
nullptr;
915 for (
int i = 0; i < size; ++i )
919 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
920 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
934 const bool useZ =
is3D();
937 const int size = mX.size();
939 return std::make_tuple( std::make_unique< QgsCircularString >(), std::make_unique< QgsCircularString >() );
941 index = std::clamp( index, 0, size - 1 );
943 const int part1Size = index + 1;
944 QVector< double > x1( part1Size );
945 QVector< double > y1( part1Size );
946 QVector< double > z1( useZ ? part1Size : 0 );
947 QVector< double > m1( useM ? part1Size : 0 );
949 const double *sourceX = mX.constData();
950 const double *sourceY = mY.constData();
951 const double *sourceZ = useZ ? mZ.constData() :
nullptr;
952 const double *sourceM = useM ? mM.constData() :
nullptr;
954 double *destX = x1.data();
955 double *destY = y1.data();
956 double *destZ = useZ ? z1.data() :
nullptr;
957 double *destM = useM ? m1.data() :
nullptr;
959 std::copy( sourceX, sourceX + part1Size, destX );
960 std::copy( sourceY, sourceY + part1Size, destY );
962 std::copy( sourceZ, sourceZ + part1Size, destZ );
964 std::copy( sourceM, sourceM + part1Size, destM );
966 const int part2Size = size - index;
968 return std::make_tuple( std::make_unique< QgsCircularString >( x1, y1, z1, m1 ), std::make_unique< QgsCircularString >() );
970 QVector< double > x2( part2Size );
971 QVector< double > y2( part2Size );
972 QVector< double > z2( useZ ? part2Size : 0 );
973 QVector< double > m2( useM ? part2Size : 0 );
976 destZ = useZ ? z2.data() :
nullptr;
977 destM = useM ? m2.data() :
nullptr;
978 std::copy( sourceX + index, sourceX + size, destX );
979 std::copy( sourceY + index, sourceY + size, destY );
981 std::copy( sourceZ + index, sourceZ + size, destZ );
983 std::copy( sourceM + index, sourceM + size, destM );
986 return std::make_tuple( std::make_unique< QgsCircularString >(), std::make_unique< QgsCircularString >( x2, y2, z2, m2 ) );
988 return std::make_tuple( std::make_unique< QgsCircularString >( x1, y1, z1, m1 ), std::make_unique< QgsCircularString >( x2, y2, z2, m2 ) );
995 for (
int i = 0; i < nPts; ++i )
997 pts.push_back(
pointN( i ) );
1017 bool hasZ = firstPt.
is3D();
1022 mX.resize(
points.size() );
1023 mY.resize(
points.size() );
1026 mZ.resize(
points.size() );
1034 mM.resize(
points.size() );
1041 for (
int i = 0; i <
points.size(); ++i )
1047 double z =
points.at( i ).z();
1048 mZ[i] = std::isnan( z ) ? 0 : z;
1052 double m =
points.at( i ).m();
1053 mM[i] = std::isnan( m ) ? 0 : m;
1060 if ( !line || line->
isEmpty() )
1103 mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
1116 mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
1134 double *zArray =
nullptr;
1140 std::unique_ptr< double[] > dummyZ;
1141 if ( !hasZ || !transformZ )
1143 dummyZ = std::make_unique<double[]>( nPoints );
1144 zArray = dummyZ.get();
1160 for (
int i = 0; i < nPoints; ++i )
1163 t.map( mX.at( i ), mY.at( i ), &x, &y );
1168 mZ[i] = mZ.at( i ) * zScale + zTranslate;
1172 mM[i] = mM.at( i ) * mScale + mTranslate;
1177void arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
1179 double centerX, centerY, radius;
1185 double diameter = 2 * radius;
1186 path.arcTo( centerX - radius, centerY - radius, diameter, diameter, -p1Angle, -sweepAngle );
1197 if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
1199 path.moveTo( QPointF( mX[0], mY[0] ) );
1202 for (
int i = 0; i < ( nPoints - 2 ); i += 2 )
1204 arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
1208 if ( nPoints % 2 == 0 )
1210 path.lineTo( mX[nPoints - 1], mY[nPoints - 1] );
1221 if ( position.
vertex >= mX.size() || position.
vertex < 1 )
1226 mX.insert( position.
vertex, vertex.
x() );
1227 mY.insert( position.
vertex, vertex.
y() );
1230 mZ.insert( position.
vertex, vertex.
z() );
1234 mM.insert( position.
vertex, vertex.
m() );
1237 bool vertexNrEven = ( position.
vertex % 2 == 0 );
1252 if ( position.
vertex < 0 || position.
vertex >= mX.size() )
1257 mX[position.
vertex] = newPos.
x();
1258 mY[position.
vertex] = newPos.
y();
1261 mZ[position.
vertex] = newPos.
z();
1265 mM[position.
vertex] = newPos.
m();
1275 if ( position.
vertex < 0 || position.
vertex > ( nVertices - 1 ) )
1280 if ( nVertices < 4 )
1286 if ( position.
vertex < ( nVertices - 2 ) )
1319 if ( positions.empty() )
1334 QList<QgsVertexId>
vertices( positions.begin(), positions.end() );
1339 for (
size_t i =
vertices.size() - 1; i >= 1; i-- )
1342 int prevVertexNr =
vertices[i - 1].vertex;
1344 if ( vertexNr - 1 == prevVertexNr )
1346 if ( vertexNr < nVertices - 2 )
1363 if ( nVertices -
vertices.size() * 2 < 3 )
1369 QListIterator<QgsVertexId> positionsIt(
vertices );
1370 positionsIt.toBack();
1371 while ( positionsIt.hasPrevious() )
1373 int currentVertexNr = positionsIt.previous().vertex;
1375 if ( currentVertexNr < nVertices - 2 )
1393 double minDist = std::numeric_limits<double>::max();
1396 int minDistLeftOf = 0;
1398 double currentDist = 0.0;
1401 for (
int i = 0; i < ( nPoints - 2 ); i += 2 )
1403 currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
1404 if ( currentDist < minDist )
1406 minDist = currentDist;
1407 minDistSegmentPoint = segmentPt;
1411 minDistLeftOf = *leftOf;
1416 if ( minDist == std::numeric_limits<double>::max() )
1419 segmentPt = minDistSegmentPoint;
1420 vertexAfter = minDistVertexAfter;
1421 vertexAfter.
part = 0;
1422 vertexAfter.
ring = 0;
1425 *leftOf =
qgsDoubleNear( minDist, 0.0 ) ? 0 : minDistLeftOf;
1451 for (
int i = 0; i < maxIndex; i += 2 )
1454 QgsPoint p2( mX[i + 1], mY[i + 1] );
1455 QgsPoint p3( mX[i + 2], mY[i + 2] );
1465 mSummedUpArea += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
1468 double midPointX = ( p1.
x() + p3.
x() ) / 2.0;
1469 double midPointY = ( p1.
y() + p3.
y() ) / 2.0;
1471 double radius, centerX, centerY;
1475 double r2 = radius * radius;
1486 double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
1487 double circleChordArea = 0;
1488 if ( circlePointLeftOfLine == centerPointLeftOfLine )
1490 circleChordArea = M_PI * r2 * ( 1 - cov );
1494 circleChordArea = M_PI * r2 * cov;
1497 if ( !circlePointLeftOfLine )
1550 double normalSign = 1.0;
1570 QVector<double> projX;
1571 QVector<double> projY;
1572 projX.reserve( nrPoints );
1573 projY.reserve( nrPoints );
1574 for (
int i = 0; i < nrPoints; i++ )
1576 const double vecAX = mX[i] - ptA.
x();
1577 const double vecAY = mY[i] - ptA.
y();
1578 const double vecAZ = mZ[i] - ptA.
z();
1580 projX.push_back( vecAX * ux.
x() + vecAY * ux.
y() + vecAZ * ux.
z() );
1581 projY.push_back( vecAX * uy.
x() + vecAY * uy.
y() + vecAZ * uy.
z() );
1598double QgsCircularString::closestPointOnArc(
double x1,
double y1,
double x2,
double y2,
double x3,
double y3,
const QgsPoint &pt,
QgsPoint &segmentPt,
QgsVertexId &vertexAfter,
int *leftOf,
double epsilon )
1600 double radius, centerX, centerY;
1625 segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1626 vertexAfter.
vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1633 segmentPt.
setX( pt.
x() );
1634 segmentPt.
setY( pt.
y() );
1640 double sqrDistancePointToCenter = pt.
distanceSquared( centerX, centerY );
1641 *
leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 ) : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1647void QgsCircularString::insertVertexBetween(
int after,
int before,
int pointOnCircle )
1649 double xAfter = mX.at( after );
1650 double yAfter = mY.at( after );
1651 double xBefore = mX.at( before );
1652 double yBefore = mY.at( before );
1653 double xOnCircle = mX.at( pointOnCircle );
1654 double yOnCircle = mY.at( pointOnCircle );
1656 double radius, centerX, centerY;
1659 double x = ( xAfter + xBefore ) / 2.0;
1660 double y = ( yAfter + yBefore ) / 2.0;
1663 mX.insert( before, newVertex.
x() );
1664 mY.insert( before, newVertex.
y() );
1668 mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1672 mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1685 int before = vId.
vertex - 1;
1687 int after = vId.
vertex + 1;
1689 if ( vId.
vertex % 2 != 0 )
1716 int vertex1 = vId.
vertex - 2;
1717 int vertex2 = vId.
vertex - 1;
1718 int vertex3 = vId.
vertex;
1721 int vertex4 = vId.
vertex + 1;
1722 int vertex5 = vId.
vertex + 2;
1733 if ( startVertex.
vertex % 2 == 1 )
1736 if ( startVertex.
vertex < 0 || startVertex.
vertex >= mX.count() - 2 )
1739 double x1 = mX.at( startVertex.
vertex );
1740 double y1 = mY.at( startVertex.
vertex );
1741 double x2 = mX.at( startVertex.
vertex + 1 );
1742 double y2 = mY.at( startVertex.
vertex + 1 );
1743 double x3 = mX.at( startVertex.
vertex + 2 );
1744 double y3 = mY.at( startVertex.
vertex + 2 );
1757 if ( fromVertex.
part != 0 || fromVertex.
ring != 0 || toVertex.
part != 0 || toVertex.
ring != 0 )
1760 const int fromVertexNumber = fromVertex.
vertex;
1761 const int toVertexNumber = toVertex.
vertex;
1764 if ( fromVertexNumber < 0 || fromVertexNumber >= nPoints || toVertexNumber < 0 || toVertexNumber >= nPoints )
1767 if ( fromVertexNumber == toVertexNumber )
1770 const double *xData = mX.constData();
1771 const double *yData = mY.constData();
1772 double totalDistance = 0.0;
1776 const int startArc = ( fromVertexNumber / 2 ) * 2;
1779 for (
int i = startArc; i < nPoints - 2; i += 2 )
1782 double x1 = xData[i];
1783 double y1 = yData[i];
1784 double x2 = xData[i + 1];
1785 double y2 = yData[i + 1];
1786 double x3 = xData[i + 2];
1787 double y3 = yData[i + 2];
1790 if ( fromVertexNumber >= i && toVertexNumber <= i + 2 )
1792 if ( fromVertexNumber == i && toVertexNumber == i + 2 )
1797 else if ( fromVertexNumber == i && toVertexNumber == i + 1 )
1800 double centerX, centerY, radius;
1803 return QgsGeometryUtilsBase::calculateArcLength( centerX, centerY, radius, x1, y1, x2, y2, x3, y3, 0, 1 );
1805 else if ( fromVertexNumber == i + 1 && toVertexNumber == i + 2 )
1808 double centerX, centerY, radius;
1811 return QgsGeometryUtilsBase::calculateArcLength( centerX, centerY, radius, x1, y1, x2, y2, x3, y3, 1, 2 );
1813 else if ( fromVertexNumber == i + 1 && toVertexNumber == i + 1 )
1820 bool startInThisSegment = ( fromVertexNumber >= i && fromVertexNumber <= i + 2 );
1821 bool endInThisSegment = ( toVertexNumber >= i && toVertexNumber <= i + 2 );
1822 bool segmentInRange = ( fromVertexNumber < i && toVertexNumber > i + 2 );
1824 if ( startInThisSegment && !endInThisSegment )
1827 if ( fromVertexNumber == i )
1829 else if ( fromVertexNumber == i + 1 )
1832 double centerX, centerY, radius;
1834 totalDistance +=
QgsGeometryUtilsBase::calculateArcLength( centerX, centerY, radius, x1, y1, x2, y2, x3, y3, 1, 2 );
1837 else if ( !startInThisSegment && endInThisSegment )
1840 if ( toVertexNumber == i + 1 )
1843 double centerX, centerY, radius;
1845 totalDistance +=
QgsGeometryUtilsBase::calculateArcLength( centerX, centerY, radius, x1, y1, x2, y2, x3, y3, 0, 1 );
1847 else if ( toVertexNumber == i + 2 )
1851 else if ( segmentInRange )
1858 return totalDistance;
1865 std::reverse( copy->mX.begin(), copy->mX.end() );
1866 std::reverse( copy->mY.begin(), copy->mY.end() );
1869 std::reverse( copy->mZ.begin(), copy->mZ.end() );
1873 std::reverse( copy->mM.begin(), copy->mM.end() );
1885 double distanceTraversed = 0;
1887 if ( totalPoints == 0 )
1896 const double *x = mX.constData();
1897 const double *y = mY.constData();
1898 const double *z =
is3D() ? mZ.constData() :
nullptr;
1899 const double *m =
isMeasure() ? mM.constData() :
nullptr;
1901 double prevX = *x++;
1902 double prevY = *y++;
1903 double prevZ = z ? *z++ : 0.0;
1904 double prevM = m ? *m++ : 0.0;
1908 return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1911 for (
int i = 0; i < ( totalPoints - 2 ); i += 2 )
1920 double z2 = z ? *z++ : 0.0;
1921 double m2 = m ? *m++ : 0.0;
1925 double z3 = z ? *z++ : 0.0;
1926 double m3 = m ? *m++ : 0.0;
1932 const double distanceToPoint = std::min( distance - distanceTraversed,
segmentLength );
1933 return new QgsPoint(
QgsGeometryUtils::interpolatePointOnArc(
QgsPoint( pointType, x1, y1, z1, m1 ),
QgsPoint( pointType, x2, y2, z2, m2 ),
QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1949 if ( startDistance < 0 && endDistance < 0 )
1952 endDistance = std::max( startDistance, endDistance );
1955 if ( totalPoints == 0 )
1958 QVector< QgsPoint > substringPoints;
1959 substringPoints.reserve( totalPoints );
1967 const double *x = mX.constData();
1968 const double *y = mY.constData();
1969 const double *z =
is3D() ? mZ.constData() :
nullptr;
1970 const double *m =
isMeasure() ? mM.constData() :
nullptr;
1972 double distanceTraversed = 0;
1973 double prevX = *x++;
1974 double prevY = *y++;
1975 double prevZ = z ? *z++ : 0.0;
1976 double prevM = m ? *m++ : 0.0;
1977 bool foundStart =
false;
1979 if ( startDistance < 0 )
1982 for (
int i = 0; i < ( totalPoints - 2 ); i += 2 )
1991 double z2 = z ? *z++ : 0.0;
1992 double m2 = m ? *m++ : 0.0;
1996 double z3 = z ? *z++ : 0.0;
1997 double m3 = m ? *m++ : 0.0;
1999 bool addedSegmentEnd =
false;
2001 if ( distanceTraversed <= startDistance && startDistance < distanceTraversed +
segmentLength )
2004 const double distanceToStart = startDistance - distanceTraversed;
2006 =
QgsGeometryUtils::interpolatePointOnArc(
QgsPoint( pointType, x1, y1, z1, m1 ),
QgsPoint( pointType, x2, y2, z2, m2 ),
QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
2009 const bool endPointOnSegment = distanceTraversed +
segmentLength > endDistance;
2010 if ( endPointOnSegment )
2012 const double distanceToEnd = endDistance - distanceTraversed;
2013 const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
2016 <<
QgsGeometryUtils::interpolatePointOnArc(
QgsPoint( pointType, x1, y1, z1, m1 ),
QgsPoint( pointType, x2, y2, z2, m2 ),
QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
2017 <<
QgsGeometryUtils::interpolatePointOnArc(
QgsPoint( pointType, x1, y1, z1, m1 ),
QgsPoint( pointType, x2, y2, z2, m2 ),
QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
2018 addedSegmentEnd =
true;
2022 const double midPointDistance = (
segmentLength - distanceToStart ) * 0.5 + distanceToStart;
2025 <<
QgsGeometryUtils::interpolatePointOnArc(
QgsPoint( pointType, x1, y1, z1, m1 ),
QgsPoint( pointType, x2, y2, z2, m2 ),
QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
2026 <<
QgsPoint( pointType, x3, y3, z3, m3 );
2027 addedSegmentEnd =
true;
2031 if ( !addedSegmentEnd && foundStart && ( distanceTraversed +
segmentLength > endDistance ) )
2034 const double distanceToEnd = endDistance - distanceTraversed;
2037 <<
QgsGeometryUtils::interpolatePointOnArc(
QgsPoint( pointType, x1, y1, z1, m1 ),
QgsPoint( pointType, x2, y2, z2, m2 ),
QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
2039 <<
QgsGeometryUtils::interpolatePointOnArc(
QgsPoint( pointType, x1, y1, z1, m1 ),
QgsPoint( pointType, x2, y2, z2, m2 ),
QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
2041 else if ( !addedSegmentEnd && foundStart )
2043 substringPoints <<
QgsPoint( pointType, x2, y2, z2, m2 ) <<
QgsPoint( pointType, x3, y3, z3, m3 );
2051 if ( distanceTraversed >= endDistance )
2056 if ( !foundStart &&
qgsDoubleNear( distanceTraversed, startDistance ) )
2058 substringPoints <<
QgsPoint( pointType, prevX, prevY, prevZ, prevM ) <<
QgsPoint( pointType, prevX, prevY, prevZ, prevM ) <<
QgsPoint( pointType, prevX, prevY, prevZ, prevM );
2061 auto result = std::make_unique< QgsCircularString >();
2062 result->setPoints( substringPoints );
2063 return result.release();
2076 mZ.reserve( nPoints );
2077 for (
int i = 0; i < nPoints; ++i )
2094 mM.reserve( nPoints );
2095 for (
int i = 0; i < nPoints; ++i )
2128 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.
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
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.
bool deleteVertices(const QSet< QgsVertexId > &positions) override
Deletes vertices within 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.
void sumUpArea3D(double &sum) const override
Sums up the 3d area of the curve by iterating over the vertices (shoelace formula).
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 mHasCachedSummedUpArea3D
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.
bool hasVertex(QgsVertexId position) const override
Returns true if the geometry contains a vertex matching the given position.
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 bool checkWeaklyFor3DPlane(const QgsAbstractGeometry *geom, QgsPoint &pt1, QgsPoint &pt2, QgsPoint &pt3, double epsilon=std::numeric_limits< double >::epsilon())
Checks if a 3D geometry has a plane defined by at least 3 non-collinear points.
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.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
void normalize()
Normalizes the current vector in place.
static QgsVector3D crossProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the cross product of two vectors.
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.