24#include <nlohmann/json.hpp>
40#include <QPainterPath>
43using namespace Qt::StringLiterals;
45using namespace nlohmann;
61 if ( !mControlPoints.isEmpty() )
63 const QgsPoint &firstPoint = mControlPoints.first();
64 if ( firstPoint.is3D() )
65 mWkbType = QgsWkbTypes::addZ( mWkbType );
66 if ( firstPoint.isMeasure() )
67 mWkbType = QgsWkbTypes::addM( mWkbType );
78 const int n = mControlPoints.size();
79 if ( n < 2 || mDegree < 1 )
82 if ( mDegree != n - 1 )
88 if ( mKnots.size() != n + mDegree + 1 )
91 for (
int i = 0; i <= mDegree; ++i )
96 for (
int i = n; i < mKnots.size(); ++i )
107 for (
const double w : mWeights )
122 const int controlPointCount = mControlPoints.size();
123 const int degree = mDegree;
127 if (
degree < 1 || controlPointCount <
degree + 1 || ( controlPointCount - 1 ) %
degree != 0 )
130 const int segmentCount = ( controlPointCount - 1 ) /
degree;
131 const int expectedKnotCount = controlPointCount +
degree + 1;
133 if ( mKnots.size() != expectedKnotCount )
137 for (
int i = 1; i < expectedKnotCount; ++i )
139 if ( mKnots[i] < mKnots[i - 1] && !
qgsDoubleNear( mKnots[i], mKnots[i - 1] ) )
144 for (
int i = 0; i <
degree + 1; ++i )
151 const double lastKnotValue =
static_cast<double>( segmentCount );
152 for (
int i = expectedKnotCount - (
degree + 1 ); i < expectedKnotCount; ++i )
159 for (
int segmentIndex = 1; segmentIndex < segmentCount; ++segmentIndex )
161 const double knotValue =
static_cast<double>( segmentIndex );
162 const int startIndex = (
degree + 1 ) + ( segmentIndex - 1 ) *
degree;
163 for (
int j = 0; j <
degree; ++j )
187static int findKnotSpan(
const int degree,
const double u,
const QVector<double> &knots,
const int nPoints )
190 if ( u >= knots[nPoints] )
194 if ( u <= knots[degree] )
200 int mid = ( low + high ) / 2;
202 while ( u < knots[mid] || u >= knots[mid + 1] )
204 if ( u < knots[mid] )
208 mid = ( low + high ) / 2;
218 const int n = mControlPoints.size();
232 return mControlPoints.first();
234 return mControlPoints.last();
236 const bool hasZ = !mControlPoints.
isEmpty() && mControlPoints.first().is3D();
237 const bool hasM = !mControlPoints.isEmpty() && mControlPoints.first().isMeasure();
240 const double u = mKnots[mDegree] + t * ( mKnots[n] - mKnots[mDegree] );
243 const int span = findKnotSpan( mDegree, u, mKnots, n );
248 std::vector<double> tempX( mDegree + 1 );
249 std::vector<double> tempY( mDegree + 1 );
250 std::vector<double> tempZ( mDegree + 1 );
251 std::vector<double> tempM( mDegree + 1 );
252 std::vector<double> tempW( mDegree + 1 );
255 for (
int j = 0; j <= mDegree; ++j )
257 const int cpIdx = span - mDegree + j;
258 const QgsPoint &cp = mControlPoints[cpIdx];
259 const double w = ( cpIdx < mWeights.size() ) ? mWeights[cpIdx] : 1.0;
262 tempX[j] = cp.
x() * w;
263 tempY[j] = cp.
y() * w;
264 tempZ[j] = hasZ ? cp.
z() * w : 0.0;
265 tempM[j] = hasM ? cp.
m() : 0.0;
270 for (
int k = 1; k <= mDegree; ++k )
272 for (
int j = mDegree; j >= k; --j )
274 const int knotIdx = span - mDegree + j;
275 const double denom = mKnots[knotIdx + mDegree - k + 1] - mKnots[knotIdx];
279 const double alpha = ( u - mKnots[knotIdx] ) / denom;
280 const double oneMinusAlpha = 1.0 - alpha;
283 tempX[j] = oneMinusAlpha * tempX[j - 1] + alpha * tempX[j];
284 tempY[j] = oneMinusAlpha * tempY[j - 1] + alpha * tempY[j];
286 tempZ[j] = oneMinusAlpha * tempZ[j - 1] + alpha * tempZ[j];
288 tempM[j] = oneMinusAlpha * tempM[j - 1] + alpha * tempM[j];
291 tempW[j] = oneMinusAlpha * tempW[j - 1] + alpha * tempW[j];
298 double x = tempX[mDegree];
299 double y = tempY[mDegree];
300 double z = tempZ[mDegree];
301 double m = tempM[mDegree];
302 const double w = tempW[mDegree];
319 return QgsPoint( x, y, std::numeric_limits<double>::quiet_NaN(), m );
326 if ( mControlPoints.size() < 2 )
335 if (
is3D() && closed )
336 closed &=
qgsDoubleNear( startPt.
z(), endPt.
z() ) || ( std::isnan( startPt.
z() ) && std::isnan( endPt.
z() ) );
343 if ( mControlPoints.size() < 2 )
355 Q_UNUSED( toleranceType );
359 const int steps = std::max( 2,
static_cast<int>( 2 * M_PI / tolerance ) );
362 for (
int i = 0; i <= steps; ++i )
364 const double t =
static_cast<double>( i ) / steps;
366 line->addVertex( pt );
374 std::unique_ptr<QgsLineString> line(
curveToLine() );
381 std::unique_ptr<QgsLineString> line(
curveToLine() );
383 line->drawAsPolygon( p );
388 std::unique_ptr<QgsLineString> line(
curveToLine() );
389 return line ? line->asQPolygonF() : QPolygonF();
408 if ( o->mDegree != mDegree )
413 if ( mControlPoints != o->mControlPoints )
416 if ( mWeights != o->mWeights )
419 if ( mKnots != o->mKnots )
427 for (
int i = 0; i < mControlPoints.size(); ++i )
429 if (
qgsDoubleNear( mControlPoints[i].distance( point ), 0.0 ) )
439 std::unique_ptr<QgsLineString> line(
curveToLine() );
444 return line->interpolatePoint( distance );
449 return mControlPoints.size();
454 if ( node < 0 || node >= mControlPoints.size() )
458 point = mControlPoints[node];
465 pts.reserve( pts.size() + mControlPoints.size() );
466 for (
const QgsPoint &p : mControlPoints )
475 std::reverse( rev->mControlPoints.begin(), rev->mControlPoints.end() );
476 std::reverse( rev->mWeights.begin(), rev->mWeights.end() );
479 if ( !rev->mKnots.isEmpty() )
481 const double maxKnot = rev->mKnots.last();
482 const double minKnot = rev->mKnots.first();
483 std::reverse( rev->mKnots.begin(), rev->mKnots.end() );
484 for (
double &knot : rev->mKnots )
486 knot = maxKnot + minKnot - knot;
496 if ( !
isClosed() || firstVertexIndex <= 0 || firstVertexIndex >= mControlPoints.size() )
502 std::rotate( mControlPoints.begin(), mControlPoints.begin() + firstVertexIndex, mControlPoints.end() );
503 std::rotate( mWeights.begin(), mWeights.begin() + firstVertexIndex, mWeights.end() );
506 if ( !mKnots.isEmpty() && firstVertexIndex < mKnots.size() )
508 const double delta = mKnots[firstVertexIndex] - mKnots[0];
509 std::rotate( mKnots.begin(), mKnots.begin() + firstVertexIndex, mKnots.end() );
511 for (
double &knot : mKnots )
522 std::unique_ptr<QgsLineString> line(
curveToLine() );
525 return std::make_tuple(
nullptr,
nullptr );
527 return line->splitCurveAtVertex( index );
538 std::unique_ptr<QgsLineString> line(
curveToLine() );
540 line->sumUpArea( sum );
545 std::unique_ptr<QgsLineString> line(
curveToLine() );
547 line->sumUpArea3D( sum );
552 if ( index < 0 || index >= mControlPoints.size() )
554 return mControlPoints[index].x();
559 if ( index < 0 || index >= mControlPoints.size() )
561 return mControlPoints[index].y();
566 if ( index < 0 || index >= mControlPoints.size() )
568 return mControlPoints[index].is3D() ? mControlPoints[index].z() : std::numeric_limits<double>::quiet_NaN();
573 if ( index < 0 || index >= mControlPoints.size() )
575 return mControlPoints[index].isMeasure() ? mControlPoints[index].m() : std::numeric_limits<double>::quiet_NaN();
586 for (
QgsPoint &p : mControlPoints )
588 p.addZValue( zValue );
602 for (
QgsPoint &p : mControlPoints )
604 p.addMValue( mValue );
615 for (
QgsPoint &p : mControlPoints )
617 p.setZ( std::numeric_limits<double>::quiet_NaN() );
630 for (
QgsPoint &p : mControlPoints )
632 p.setM( std::numeric_limits<double>::quiet_NaN() );
642 if ( position.
part != 0 || position.
ring != 0 )
646 const int idx = position.
vertex;
647 if ( idx < 0 || idx >= mControlPoints.size() )
651 mControlPoints.remove( idx );
652 if ( idx < mWeights.size() )
654 mWeights.remove( idx );
665 if ( positions.isEmpty() )
678 if ( mControlPoints.size() - positions.size() <= mDegree )
684 QList<QgsVertexId> sortedPositions( positions.begin(), positions.end() );
685 std::sort( sortedPositions.begin(), sortedPositions.end(), [](
const QgsVertexId &a,
const QgsVertexId &b ) { return a.vertex > b.vertex; } );
689 int idx = position.vertex;
690 mControlPoints.remove( idx );
691 if ( idx < mWeights.size() )
692 mWeights.remove( idx );
702 QVector<QgsPoint> newPts;
703 QVector<double> newWeights;
704 for (
int i = 0; i < mControlPoints.size(); ++i )
706 if ( filter( mControlPoints[i] ) )
708 newPts.append( mControlPoints[i] );
709 if ( i < mWeights.size() )
710 newWeights.append( mWeights[i] );
713 mControlPoints = newPts;
714 mWeights = newWeights;
723 Q_ASSERT( numControlPoints >
degree );
725 const int knotsSize = numControlPoints +
degree + 1;
726 QVector<double>
knots;
727 knots.reserve( knotsSize );
728 for (
int i = 0; i < knotsSize; ++i )
732 else if ( i >= numControlPoints )
735 knots.append(
static_cast<double>( i -
degree ) / ( numControlPoints -
degree ) );
742 if ( nAnchors < 2 ||
degree < 1 )
743 return QVector<double>();
745 const int segmentCount = nAnchors - 1;
748 QVector<double>
knots;
749 knots.reserve( totalKnots );
752 for (
int i = 0; i <
degree + 1; ++i )
756 for (
int segmentIndex = 1; segmentIndex < segmentCount; ++segmentIndex )
758 for (
int j = 0; j <
degree; ++j )
759 knots.append(
static_cast<double>( segmentIndex ) );
763 for (
int i = 0; i <
degree + 1; ++i )
764 knots.append(
static_cast<double>( segmentCount ) );
782 const unsigned char headerEndianness = *
static_cast<const unsigned char *
>( wkb );
800 mDegree =
static_cast<int>(
degree );
803 quint32 numControlPoints;
804 wkb >> numControlPoints;
814 const int minBytesPerPoint = 18 + (
is3D ? 8 : 0 ) + (
isMeasure ? 8 : 0 );
815 if ( numControlPoints >
static_cast<quint32
>( wkb.
remaining() / minBytesPerPoint + 1 ) )
818 mControlPoints.clear();
820 mControlPoints.reserve( numControlPoints );
821 mWeights.reserve( numControlPoints );
824 for ( quint32 i = 0; i < numControlPoints; ++i )
827 char pointEndianness;
828 wkb >> pointEndianness;
832 if (
static_cast<unsigned char>( pointEndianness ) != headerEndianness )
836 double x, y, z = 0.0, m = 0.0;
849 if ( weightFlag == 1 )
862 point =
QgsPoint( x, y, std::numeric_limits<double>::quiet_NaN(), m );
866 mControlPoints.append( point );
867 mWeights.append(
weight );
875 const quint32 expectedKnots = numControlPoints +
degree + 1;
876 if ( numKnots != expectedKnots )
880 if ( numKnots *
sizeof(
double ) >
static_cast<quint32
>( wkb.
remaining() ) )
884 mKnots.reserve( numKnots );
887 for ( quint32 i = 0; i < numKnots; ++i )
891 mKnots.append( knot );
901 const QString geomTypeStr = wkt.split(
'(' )[0].trimmed().toUpper();
903 if ( !geomTypeStr.startsWith(
"NURBSCURVE"_L1 ) )
910 if ( geomTypeStr.contains(
"ZM"_L1 ) )
912 else if ( geomTypeStr.endsWith(
'Z' ) || geomTypeStr.endsWith(
" Z"_L1 ) )
914 else if ( geomTypeStr.endsWith(
'M' ) || geomTypeStr.endsWith(
" M"_L1 ) )
921 if (
parts.second.compare(
"EMPTY"_L1, Qt::CaseInsensitive ) == 0 ||
parts.second.isEmpty() )
927 if ( blocks.isEmpty() )
932 int degree = blocks[0].trimmed().toInt( &ok );
936 if ( blocks.size() < 2 )
940 QString pointsStr = blocks[1].trimmed();
943 if ( !pointsStr.startsWith(
'('_L1 ) || !pointsStr.endsWith(
')'_L1 ) )
946 pointsStr = pointsStr.mid( 1, pointsStr.length() - 2 ).trimmed();
949 QStringList pointsCoords = pointsStr.split(
',', Qt::SkipEmptyParts );
952 const thread_local QRegularExpression rx( u
"\\s+"_s );
954 for (
const QString &pointStr : pointsCoords )
956 QStringList coords = pointStr.trimmed().split( rx, Qt::SkipEmptyParts );
958 if ( coords.size() < 2 )
964 double x = coords[0].toDouble( &ok );
968 double y = coords[1].toDouble( &ok );
973 if ( coords.size() >= 3 )
978 double m = coords[2].toDouble( &ok );
981 point =
QgsPoint( x, y, std::numeric_limits<double>::quiet_NaN(), m );
986 double z = coords[2].toDouble( &ok );
994 double z = coords[2].toDouble( &ok );
997 double m = coords[3].toDouble( &ok );
1002 else if (
isMeasure() && coords.size() >= 4 )
1005 double z = coords[2].toDouble( &ok );
1008 double m = coords[3].toDouble( &ok );
1018 double z = coords[2].toDouble( &ok );
1027 double z = coords[2].toDouble( &ok );
1030 double m = coords[3].toDouble( &ok );
1072 mWeights.append( 1.0 );
1076 bool hasWeights =
false;
1077 bool hasKnots =
false;
1080 for (
int i = 2; i < blocks.size(); ++i )
1082 QString block = blocks[i].trimmed();
1084 if ( block.startsWith(
'('_L1 ) )
1087 if ( !block.endsWith(
')'_L1 ) )
1091 block = block.mid( 1, block.length() - 2 ).trimmed();
1092 QStringList values = block.split(
',', Qt::SkipEmptyParts );
1094 QVector<double> parsedValues;
1095 for (
const QString &valueStr : values )
1098 double value = valueStr.trimmed().toDouble( &ok );
1101 parsedValues.append( value );
1104 if ( !hasWeights && parsedValues.size() ==
controlPoints.size() )
1107 mWeights = parsedValues;
1110 else if ( !hasKnots )
1113 mKnots = parsedValues;
1145 if ( mDegree != o->mDegree || mControlPoints.size() != o->mControlPoints.size() || mWeights.size() != o->mWeights.size() || mKnots.size() != o->mKnots.size() )
1150 for (
int i = 0; i < mControlPoints.size(); ++i )
1152 if ( mControlPoints[i].distance( o->mControlPoints[i] ) >= epsilon )
1156 for (
int i = 0; i < mWeights.size(); ++i )
1158 if ( std::fabs( mWeights[i] - o->mWeights[i] ) > epsilon )
1162 for (
int i = 0; i < mKnots.size(); ++i )
1164 if ( std::fabs( mKnots[i] - o->mKnots[i] ) > epsilon )
1178 return u
"NurbsCurve"_s;
1198 if (
id.part != 0 ||
id.ring != 0 )
1202 const int idx =
id.vertex;
1203 if ( idx < 0 || idx >= mControlPoints.size() )
1207 return mControlPoints[idx];
1212 return ( part == 0 && ring == 0 ) ? mControlPoints.size() : 0;
1217 if (
id.part == 0 &&
id.ring == 0 )
1229 if ( mValidityComputed )
1232 error = u
"NURBS curve is invalid"_s;
1236 mValidityComputed =
true;
1241 error = u
"Degree must be >= 1"_s;
1245 const int n = mControlPoints.size();
1246 if ( n < mDegree + 1 )
1248 error = u
"Not enough control points for degree"_s;
1252 if ( mKnots.size() != n + mDegree + 1 )
1254 error = u
"Knot vector size is incorrect"_s;
1258 if ( mWeights.size() != n )
1260 error = u
"Weights vector size mismatch"_s;
1265 for (
int i = 1; i < mKnots.size(); ++i )
1267 if ( mKnots[i] < mKnots[i - 1] )
1269 error = u
"Knot vector values must be non-decreasing"_s;
1280 std::unique_ptr<QgsLineString> line(
curveToLine() );
1282 line->addToPainterPath( path );
1287 std::unique_ptr<QgsLineString> line(
curveToLine() );
1290 return line->curveSubstring( startDistance, endDistance );
1295 std::unique_ptr<QgsLineString> line(
curveToLine() );
1296 return line ? line->length() : 0.0;
1301 std::unique_ptr<QgsLineString> line(
curveToLine() );
1304 return line->segmentLength( startVertex );
1309 std::unique_ptr<QgsLineString> line(
curveToLine() );
1312 return line->distanceBetweenVertices( fromVertex, toVertex );
1318 for (
QgsPoint &pt : result->mControlPoints )
1321 pt.
setX( std::round( pt.
x() / hSpacing ) * hSpacing );
1323 pt.
setY( std::round( pt.
y() / vSpacing ) * vSpacing );
1324 if ( pt.
is3D() && dSpacing > 0 )
1325 pt.
setZ( std::round( pt.
z() / dSpacing ) * dSpacing );
1327 pt.
setM( std::round( pt.
m() / mSpacing ) * mSpacing );
1330 if ( removeRedundantPoints )
1331 result->removeDuplicateNodes();
1338 std::unique_ptr<QgsLineString> line(
curveToLine() );
1341 return line->simplifyByDistance( tolerance );
1346 if ( mControlPoints.size() < 2 )
1349 QVector<QgsPoint> newPoints;
1350 QVector<double> newWeights;
1352 newPoints.reserve( mControlPoints.size() );
1353 newWeights.reserve( mWeights.size() );
1355 newPoints.append( mControlPoints.first() );
1356 if ( !mWeights.isEmpty() )
1357 newWeights.append( mWeights.first() );
1359 for (
int i = 1; i < mControlPoints.size(); ++i )
1361 const double dist = ( useZValues && mControlPoints[i].is3D() && mControlPoints[i - 1].
is3D() ) ? mControlPoints[i].distance3D( mControlPoints[i - 1] )
1362 : mControlPoints[i].distance( mControlPoints[i - 1] );
1364 if ( dist >= epsilon )
1366 newPoints.append( mControlPoints[i] );
1367 if ( i < mWeights.size() )
1368 newWeights.append( mWeights[i] );
1372 const bool changed = ( newPoints.size() != mControlPoints.size() );
1376 mControlPoints = newPoints;
1377 mWeights = newWeights;
1388 std::unique_ptr<QgsLineString> line(
curveToLine() );
1391 return line->vertexAngle( vertex );
1396 for (
QgsPoint &pt : mControlPoints )
1398 const double x = pt.x();
1407 Q_UNUSED( feedback );
1411 for (
QgsPoint &pt : mControlPoints )
1413 double x = pt.x(), y = pt.y(), z = pt.z(), m = pt.m();
1433 std::unique_ptr<QgsLineString> line(
curveToLine() );
1442 return line->closestSegment( pt, segmentPt, vertexAfter, leftOf, epsilon );
1447 for (
QgsPoint &pt : mControlPoints )
1451 double z = transformZ && pt.is3D() ? pt.z() : std::numeric_limits<double>::quiet_NaN();
1455 if ( transformZ && pt.is3D() )
1463 for (
QgsPoint &pt : mControlPoints )
1465 const QPointF p = t.map( QPointF( pt.x(), pt.y() ) );
1470 pt.setZ( pt.z() * zScale + zTranslate );
1471 if ( pt.isMeasure() )
1472 pt.setM( pt.m() * mScale + mTranslate );
1493 if ( mControlPoints.isEmpty() )
1499 for (
const QgsPoint &pt : mControlPoints )
1501 bbox.
combineWith( pt.x(), pt.y(), pt.is3D() ? pt.z() : std::numeric_limits<double>::quiet_NaN() );
1505 std::unique_ptr<QgsLineString> line(
curveToLine() );
1517 mValidityComputed =
false;
1523 if ( position.
part != 0 || position.
ring != 0 )
1526 const int idx = position.
vertex;
1527 if ( idx < 0 || idx >= mControlPoints.size() )
1530 mControlPoints[idx].setX( newPos.
x() );
1531 mControlPoints[idx].setY( newPos.
y() );
1534 mControlPoints[idx].setZ( newPos.
z() );
1537 mControlPoints[idx].setM( newPos.
m() );
1545 if ( position.
part != 0 || position.
ring != 0 )
1548 const int idx = position.
vertex;
1549 if ( idx < 0 || idx > mControlPoints.size() )
1552 mControlPoints.insert( idx, vertex );
1553 if ( idx <= mWeights.size() )
1554 mWeights.insert( idx, 1.0 );
1568 const int coordinateDimension = 2 + (
is3D ? 1 : 0 ) + (
isMeasure ? 1 : 0 );
1582 for (
int i = 0; i < mControlPoints.size(); ++i )
1588 size += coordinateDimension * 8;
1594 if ( i < mWeights.size() && std::fabs( mWeights[i] - 1.0 ) > 1e-10 )
1602 size += mKnots.size() * 8;
1609 QByteArray wkbArray;
1615 wkbPtr << static_cast<quint32>(
mWkbType );
1618 wkbPtr << static_cast<quint32>( mDegree );
1621 wkbPtr << static_cast<quint32>( mControlPoints.size() );
1627 for (
int i = 0; i < mControlPoints.size(); ++i )
1629 const QgsPoint &point = mControlPoints[i];
1635 wkbPtr << point.
x() << point.
y();
1638 wkbPtr << point.
z();
1640 wkbPtr << point.
m();
1643 const double weight = ( i < mWeights.size() ) ? mWeights[i] : 1.0;
1644 const bool hasCustomWeight = std::fabs(
weight - 1.0 ) > 1e-10;
1646 wkbPtr << static_cast<char>( hasCustomWeight ? 1 : 0 );
1648 if ( hasCustomWeight )
1655 wkbPtr << static_cast<quint32>( mKnots.size() );
1658 for (
const double knot : mKnots )
1679 wkt += QString::number( mDegree );
1683 for (
int i = 0; i < mControlPoints.size(); ++i )
1688 const QgsPoint &pt = mControlPoints[i];
1700 if ( !mWeights.isEmpty() )
1703 for (
int i = 0; i < mWeights.size(); ++i )
1713 if ( !mKnots.isEmpty() )
1716 for (
int i = 0; i < mKnots.size(); ++i )
1735 std::unique_ptr<QgsLineString> line(
curveToLine() );
1737 return QDomElement();
1738 return line->asGml2( doc, precision, ns, axisOrder );
1745 std::unique_ptr<QgsLineString> line(
curveToLine() );
1747 return QDomElement();
1748 return line->asGml3( doc, precision, ns, axisOrder );
1753 std::unique_ptr<QgsLineString> line(
curveToLine() );
1755 return json::object();
1756 return line->asJsonObject( precision );
1762 std::unique_ptr<QgsLineString> line(
curveToLine() );
1765 return line->asKml( precision );
1775 return mControlPoints.isEmpty();
1780 mControlPoints.clear();
1799 std::unique_ptr<QgsLineString> line(
curveToLine() );
1800 return line ? line->centroid() :
QgsPoint();
1809 if ( mDegree < otherCurve->mDegree )
1811 else if ( mDegree > otherCurve->mDegree )
1814 const int nThis = mControlPoints.size();
1815 const int nOther = otherCurve->mControlPoints.size();
1817 if ( nThis < nOther )
1819 else if ( nThis > nOther )
1822 for (
int i = 0; i < nThis; ++i )
1824 if ( mControlPoints[i].x() < otherCurve->mControlPoints[i].x() )
1826 if ( mControlPoints[i].x() > otherCurve->mControlPoints[i].x() )
1828 if ( mControlPoints[i].y() < otherCurve->mControlPoints[i].y() )
1830 if ( mControlPoints[i].y() > otherCurve->mControlPoints[i].y() )
1834 if ( mWeights.size() < otherCurve->mWeights.size() )
1836 else if ( mWeights.size() > otherCurve->mWeights.size() )
1839 for (
int i = 0; i < mWeights.size(); ++i )
1841 if ( mWeights[i] < otherCurve->mWeights[i] )
1843 else if ( mWeights[i] > otherCurve->mWeights[i] )
1847 if ( mKnots.size() < otherCurve->mKnots.size() )
1849 else if ( mKnots.size() > otherCurve->mKnots.size() )
1852 for (
int i = 0; i < mKnots.size(); ++i )
1854 if ( mKnots[i] < otherCurve->mKnots[i] )
1856 else if ( mKnots[i] > otherCurve->mKnots[i] )
1865 if ( index < 0 || index >= mWeights.size() )
1867 return mWeights[index];
1872 if ( index < 0 || index >= mWeights.size() )
1876 mWeights[index] =
weight;
1883 if ( !
isPolyBezier() || localIndex < 0 || localIndex >= mControlPoints.size() )
1886 return ( localIndex % mDegree ) == 0;
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
VertexType
Types of vertex.
@ ControlPoint
A NURBS control point (does not lie on the curve).
WkbType
The WKB type describes the number of dimensions a geometry has.
@ NurbsCurveM
NurbsCurveM.
@ NurbsCurveZ
NurbsCurveZ.
@ NurbsCurveZM
NurbsCurveZM.
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.
virtual QString geometryType() const =0
Returns a unique string representing the geometry type.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
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.
bool intersects(const QgsBox3D &other) const
Returns true if box intersects with another box.
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
void combineWith(const QgsBox3D &box)
Expands the bbox so that it covers both the original rectangle and the given rectangle.
int remaining() const
remaining
Qgis::WkbType readHeader() const
readHeader
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
QgsBox3D mBoundingBox
Cached bounding box.
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.
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
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 (...
Line string geometry type, with support for z-dimension and m-values.
std::tuple< std::unique_ptr< QgsCurve >, std::unique_ptr< QgsCurve > > splitCurveAtVertex(int index) const override
Splits the curve at the specified vertex index, returning two curves which represent the portion of t...
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
QgsPoint startPoint() const override
Returns the starting point of the curve.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
QPolygonF asQPolygonF() const override
Returns a QPolygonF representing the points.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
bool isRational() const
Returns true if this curve is rational (has non-uniform weights).
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
QgsNurbsCurve()
Constructor for an empty NURBS curve geometry.
QgsPoint endPoint() const override
Returns the end point of the curve.
int indexOf(const QgsPoint &point) const override
Returns the index of the first vertex matching the given point, or -1 if a matching vertex is not fou...
bool transform(QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback=nullptr) override
Transforms the vertices from the geometry in place, using the specified geometry transformer object.
QgsPoint centroid() const override
Returns the centroid of the geometry.
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
QgsCurve * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
int partCount() const override
Returns count of parts contained in the geometry.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
bool fuzzyDistanceEqual(const QgsAbstractGeometry &other, double epsilon=1e-8) const override
Performs fuzzy distance comparison between this geometry and other using an epsilon.
bool deleteVertices(const QSet< QgsVertexId > &positions) override
Deletes vertices within the geometry.
QgsAbstractGeometry * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
double zAt(int index) const override
Returns the z-coordinate of the specified node in the line string.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
QString geometryType() const override
Returns a unique string representing the geometry type.
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
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.
static QVector< double > generateKnotsForBezierConversion(int nAnchors, int degree=3)
Generates a knot vector for converting piecewise Bézier curves to NURBS.
bool dropMValue() override
Drops any measure values which exist in the geometry.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
QVector< double > knots() const
Returns the knot vector of the NURBS curve.
void scroll(int firstVertexIndex) override
Scrolls the curve vertices so that they start with the vertex at the given index.
bool isEmpty() const override
Returns true if the geometry is empty.
bool isAnchorVertex(int localIndex) const
Returns true if the control point at localIndex is an anchor vertex in a poly-Bézier curve.
bool isPolyBezier() const
Returns true if this curve represents a poly-Bézier curve structure.
void filterVertices(const std::function< bool(const QgsPoint &)> &filter) override
bool isBSpline() const
Returns true if this curve represents a B-spline (non-rational NURBS).
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
int numPoints() const override
Returns the number of points in the curve.
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.
QgsBox3D calculateBoundingBox3D() const override
Calculates the minimal 3D bounding box for the geometry.
QgsBox3D boundingBox3D() const override
Returns the 3D bounding box for the geometry.
bool isClosed() const override
Returns true if the curve is closed.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb().
QgsAbstractGeometry * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
int vertexNumberFromVertexId(QgsVertexId id) const override
Returns the vertex number corresponding to a vertex id.
void swapXy() override
Swaps the x and y coordinates from the geometry.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
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.
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.
bool setWeight(int index, double weight)
Sets the weight at the specified control point index.
bool fuzzyEqual(const QgsAbstractGeometry &other, double epsilon=1e-8) const override
Performs fuzzy comparison between this geometry and other using an epsilon.
QgsCurve * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
double mAt(int index) const override
Returns the m-coordinate of the specified node in the line string.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
bool boundingBoxIntersects(const QgsRectangle &rectangle) const override
Returns true if the bounding box of this geometry intersects with a rectangle.
double weight(int index) const
Returns the weight at the specified control point index.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns a WKB representation of the geometry.
bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
int degree() const
Returns the degree of the NURBS curve.
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 sumUpArea3D(double &sum) const override
Sums up the 3d area of the curve by iterating over the vertices (shoelace formula).
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 deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
static QVector< double > generateUniformKnots(int numControlPoints, int degree)
Generates a uniform clamped knot vector for a NURBS curve.
QVector< QgsPoint > controlPoints() const
Returns the control points of the NURBS curve.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
bool isBezier() const
Returns true if this curve represents a Bézier curve.
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
bool isClosed2D() const override
Returns true if the curve is closed.
QgsCurve * toCurveType() const override
Returns the geometry converted to the more generic curve type.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
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...
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
QgsPoint evaluate(double t) const
Evaluates the NURBS curve at parameter t ∈ [0,1].
int dimension() const override
Returns the inherent dimension of the geometry.
QVector< double > weights() const
Returns the weight vector of the NURBS curve.
QgsAbstractGeometry * 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.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
QgsNurbsCurve * clone() const override
Clones the geometry by performing a deep copy.
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
double distanceBetweenVertices(QgsVertexId fromVertex, QgsVertexId toVertex) const override
Returns the distance along the curve between two vertices.
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
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.
void setM(double m)
Sets the point's m-value.
bool isEmpty() const override
Returns true if the geometry is empty.
void setZ(double z)
Sets the point's z-coordinate.
A rectangle specified with double values.
bool intersects(const QgsRectangle &rect) const
Returns true when rectangle intersects with other rectangle.
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 Q_INVOKABLE bool isNurbsType(Qgis::WkbType type)
Returns true if the WKB type is a NURBS curve type.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
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
Utility class for identifying a unique vertex within a geometry.