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 QVector<QgsPoint> newPts;
666 QVector<double> newWeights;
667 for (
int i = 0; i < mControlPoints.size(); ++i )
669 if ( filter( mControlPoints[i] ) )
671 newPts.append( mControlPoints[i] );
672 if ( i < mWeights.size() )
673 newWeights.append( mWeights[i] );
676 mControlPoints = newPts;
677 mWeights = newWeights;
686 Q_ASSERT( numControlPoints >
degree );
688 const int knotsSize = numControlPoints +
degree + 1;
689 QVector<double>
knots;
690 knots.reserve( knotsSize );
691 for (
int i = 0; i < knotsSize; ++i )
695 else if ( i >= numControlPoints )
698 knots.append(
static_cast<double>( i -
degree ) / ( numControlPoints -
degree ) );
705 if ( nAnchors < 2 ||
degree < 1 )
706 return QVector<double>();
708 const int segmentCount = nAnchors - 1;
711 QVector<double>
knots;
712 knots.reserve( totalKnots );
715 for (
int i = 0; i <
degree + 1; ++i )
719 for (
int segmentIndex = 1; segmentIndex < segmentCount; ++segmentIndex )
721 for (
int j = 0; j <
degree; ++j )
722 knots.append(
static_cast<double>( segmentIndex ) );
726 for (
int i = 0; i <
degree + 1; ++i )
727 knots.append(
static_cast<double>( segmentCount ) );
745 const unsigned char headerEndianness = *
static_cast<const unsigned char *
>( wkb );
763 mDegree =
static_cast<int>(
degree );
766 quint32 numControlPoints;
767 wkb >> numControlPoints;
777 const int minBytesPerPoint = 18 + (
is3D ? 8 : 0 ) + (
isMeasure ? 8 : 0 );
778 if ( numControlPoints >
static_cast<quint32
>( wkb.
remaining() / minBytesPerPoint + 1 ) )
781 mControlPoints.clear();
783 mControlPoints.reserve( numControlPoints );
784 mWeights.reserve( numControlPoints );
787 for ( quint32 i = 0; i < numControlPoints; ++i )
790 char pointEndianness;
791 wkb >> pointEndianness;
795 if (
static_cast<unsigned char>( pointEndianness ) != headerEndianness )
799 double x, y, z = 0.0, m = 0.0;
812 if ( weightFlag == 1 )
825 point =
QgsPoint( x, y, std::numeric_limits<double>::quiet_NaN(), m );
829 mControlPoints.append( point );
830 mWeights.append(
weight );
838 const quint32 expectedKnots = numControlPoints +
degree + 1;
839 if ( numKnots != expectedKnots )
843 if ( numKnots *
sizeof(
double ) >
static_cast<quint32
>( wkb.
remaining() ) )
847 mKnots.reserve( numKnots );
850 for ( quint32 i = 0; i < numKnots; ++i )
854 mKnots.append( knot );
864 const QString geomTypeStr = wkt.split(
'(' )[0].trimmed().toUpper();
866 if ( !geomTypeStr.startsWith(
"NURBSCURVE"_L1 ) )
873 if ( geomTypeStr.contains(
"ZM"_L1 ) )
875 else if ( geomTypeStr.endsWith(
'Z' ) || geomTypeStr.endsWith(
" Z"_L1 ) )
877 else if ( geomTypeStr.endsWith(
'M' ) || geomTypeStr.endsWith(
" M"_L1 ) )
884 if (
parts.second.compare(
"EMPTY"_L1, Qt::CaseInsensitive ) == 0 ||
parts.second.isEmpty() )
890 if ( blocks.isEmpty() )
895 int degree = blocks[0].trimmed().toInt( &ok );
899 if ( blocks.size() < 2 )
903 QString pointsStr = blocks[1].trimmed();
906 if ( !pointsStr.startsWith(
'('_L1 ) || !pointsStr.endsWith(
')'_L1 ) )
909 pointsStr = pointsStr.mid( 1, pointsStr.length() - 2 ).trimmed();
912 QStringList pointsCoords = pointsStr.split(
',', Qt::SkipEmptyParts );
915 const thread_local QRegularExpression rx( u
"\\s+"_s );
917 for (
const QString &pointStr : pointsCoords )
919 QStringList coords = pointStr.trimmed().split( rx, Qt::SkipEmptyParts );
921 if ( coords.size() < 2 )
927 double x = coords[0].toDouble( &ok );
931 double y = coords[1].toDouble( &ok );
936 if ( coords.size() >= 3 )
941 double m = coords[2].toDouble( &ok );
944 point =
QgsPoint( x, y, std::numeric_limits<double>::quiet_NaN(), m );
949 double z = coords[2].toDouble( &ok );
957 double z = coords[2].toDouble( &ok );
960 double m = coords[3].toDouble( &ok );
965 else if (
isMeasure() && coords.size() >= 4 )
968 double z = coords[2].toDouble( &ok );
971 double m = coords[3].toDouble( &ok );
981 double z = coords[2].toDouble( &ok );
990 double z = coords[2].toDouble( &ok );
993 double m = coords[3].toDouble( &ok );
1035 mWeights.append( 1.0 );
1039 bool hasWeights =
false;
1040 bool hasKnots =
false;
1043 for (
int i = 2; i < blocks.size(); ++i )
1045 QString block = blocks[i].trimmed();
1047 if ( block.startsWith(
'('_L1 ) )
1050 if ( !block.endsWith(
')'_L1 ) )
1054 block = block.mid( 1, block.length() - 2 ).trimmed();
1055 QStringList values = block.split(
',', Qt::SkipEmptyParts );
1057 QVector<double> parsedValues;
1058 for (
const QString &valueStr : values )
1061 double value = valueStr.trimmed().toDouble( &ok );
1064 parsedValues.append( value );
1067 if ( !hasWeights && parsedValues.size() ==
controlPoints.size() )
1070 mWeights = parsedValues;
1073 else if ( !hasKnots )
1076 mKnots = parsedValues;
1108 if ( mDegree != o->mDegree || mControlPoints.size() != o->mControlPoints.size() || mWeights.size() != o->mWeights.size() || mKnots.size() != o->mKnots.size() )
1113 for (
int i = 0; i < mControlPoints.size(); ++i )
1115 if ( mControlPoints[i].distance( o->mControlPoints[i] ) >= epsilon )
1119 for (
int i = 0; i < mWeights.size(); ++i )
1121 if ( std::fabs( mWeights[i] - o->mWeights[i] ) > epsilon )
1125 for (
int i = 0; i < mKnots.size(); ++i )
1127 if ( std::fabs( mKnots[i] - o->mKnots[i] ) > epsilon )
1141 return u
"NurbsCurve"_s;
1161 if (
id.part != 0 ||
id.ring != 0 )
1165 const int idx =
id.vertex;
1166 if ( idx < 0 || idx >= mControlPoints.size() )
1170 return mControlPoints[idx];
1175 return ( part == 0 && ring == 0 ) ? mControlPoints.size() : 0;
1180 if (
id.part == 0 &&
id.ring == 0 )
1192 if ( mValidityComputed )
1195 error = u
"NURBS curve is invalid"_s;
1199 mValidityComputed =
true;
1204 error = u
"Degree must be >= 1"_s;
1208 const int n = mControlPoints.size();
1209 if ( n < mDegree + 1 )
1211 error = u
"Not enough control points for degree"_s;
1215 if ( mKnots.size() != n + mDegree + 1 )
1217 error = u
"Knot vector size is incorrect"_s;
1221 if ( mWeights.size() != n )
1223 error = u
"Weights vector size mismatch"_s;
1228 for (
int i = 1; i < mKnots.size(); ++i )
1230 if ( mKnots[i] < mKnots[i - 1] )
1232 error = u
"Knot vector values must be non-decreasing"_s;
1243 std::unique_ptr<QgsLineString> line(
curveToLine() );
1245 line->addToPainterPath( path );
1250 std::unique_ptr<QgsLineString> line(
curveToLine() );
1253 return line->curveSubstring( startDistance, endDistance );
1258 std::unique_ptr<QgsLineString> line(
curveToLine() );
1259 return line ? line->length() : 0.0;
1264 std::unique_ptr<QgsLineString> line(
curveToLine() );
1267 return line->segmentLength( startVertex );
1272 std::unique_ptr<QgsLineString> line(
curveToLine() );
1275 return line->distanceBetweenVertices( fromVertex, toVertex );
1281 for (
QgsPoint &pt : result->mControlPoints )
1284 pt.
setX( std::round( pt.
x() / hSpacing ) * hSpacing );
1286 pt.
setY( std::round( pt.
y() / vSpacing ) * vSpacing );
1287 if ( pt.
is3D() && dSpacing > 0 )
1288 pt.
setZ( std::round( pt.
z() / dSpacing ) * dSpacing );
1290 pt.
setM( std::round( pt.
m() / mSpacing ) * mSpacing );
1293 if ( removeRedundantPoints )
1294 result->removeDuplicateNodes();
1301 std::unique_ptr<QgsLineString> line(
curveToLine() );
1304 return line->simplifyByDistance( tolerance );
1309 if ( mControlPoints.size() < 2 )
1312 QVector<QgsPoint> newPoints;
1313 QVector<double> newWeights;
1315 newPoints.reserve( mControlPoints.size() );
1316 newWeights.reserve( mWeights.size() );
1318 newPoints.append( mControlPoints.first() );
1319 if ( !mWeights.isEmpty() )
1320 newWeights.append( mWeights.first() );
1322 for (
int i = 1; i < mControlPoints.size(); ++i )
1324 const double dist = ( useZValues && mControlPoints[i].is3D() && mControlPoints[i - 1].
is3D() ) ? mControlPoints[i].distance3D( mControlPoints[i - 1] )
1325 : mControlPoints[i].distance( mControlPoints[i - 1] );
1327 if ( dist >= epsilon )
1329 newPoints.append( mControlPoints[i] );
1330 if ( i < mWeights.size() )
1331 newWeights.append( mWeights[i] );
1335 const bool changed = ( newPoints.size() != mControlPoints.size() );
1339 mControlPoints = newPoints;
1340 mWeights = newWeights;
1351 std::unique_ptr<QgsLineString> line(
curveToLine() );
1354 return line->vertexAngle( vertex );
1359 for (
QgsPoint &pt : mControlPoints )
1361 const double x = pt.x();
1370 Q_UNUSED( feedback );
1374 for (
QgsPoint &pt : mControlPoints )
1376 double x = pt.x(), y = pt.y(), z = pt.z(), m = pt.m();
1396 std::unique_ptr<QgsLineString> line(
curveToLine() );
1405 return line->closestSegment( pt, segmentPt, vertexAfter, leftOf, epsilon );
1410 for (
QgsPoint &pt : mControlPoints )
1414 double z = transformZ && pt.is3D() ? pt.z() : std::numeric_limits<double>::quiet_NaN();
1418 if ( transformZ && pt.is3D() )
1426 for (
QgsPoint &pt : mControlPoints )
1428 const QPointF p = t.map( QPointF( pt.x(), pt.y() ) );
1433 pt.setZ( pt.z() * zScale + zTranslate );
1434 if ( pt.isMeasure() )
1435 pt.setM( pt.m() * mScale + mTranslate );
1456 if ( mControlPoints.isEmpty() )
1462 for (
const QgsPoint &pt : mControlPoints )
1464 bbox.
combineWith( pt.x(), pt.y(), pt.is3D() ? pt.z() : std::numeric_limits<double>::quiet_NaN() );
1468 std::unique_ptr<QgsLineString> line(
curveToLine() );
1480 mValidityComputed =
false;
1486 if ( position.
part != 0 || position.
ring != 0 )
1489 const int idx = position.
vertex;
1490 if ( idx < 0 || idx >= mControlPoints.size() )
1493 mControlPoints[idx].setX( newPos.
x() );
1494 mControlPoints[idx].setY( newPos.
y() );
1497 mControlPoints[idx].setZ( newPos.
z() );
1500 mControlPoints[idx].setM( newPos.
m() );
1508 if ( position.
part != 0 || position.
ring != 0 )
1511 const int idx = position.
vertex;
1512 if ( idx < 0 || idx > mControlPoints.size() )
1515 mControlPoints.insert( idx, vertex );
1516 if ( idx <= mWeights.size() )
1517 mWeights.insert( idx, 1.0 );
1531 const int coordinateDimension = 2 + (
is3D ? 1 : 0 ) + (
isMeasure ? 1 : 0 );
1545 for (
int i = 0; i < mControlPoints.size(); ++i )
1551 size += coordinateDimension * 8;
1557 if ( i < mWeights.size() && std::fabs( mWeights[i] - 1.0 ) > 1e-10 )
1565 size += mKnots.size() * 8;
1572 QByteArray wkbArray;
1578 wkbPtr << static_cast<quint32>(
mWkbType );
1581 wkbPtr << static_cast<quint32>( mDegree );
1584 wkbPtr << static_cast<quint32>( mControlPoints.size() );
1590 for (
int i = 0; i < mControlPoints.size(); ++i )
1592 const QgsPoint &point = mControlPoints[i];
1598 wkbPtr << point.
x() << point.
y();
1601 wkbPtr << point.
z();
1603 wkbPtr << point.
m();
1606 const double weight = ( i < mWeights.size() ) ? mWeights[i] : 1.0;
1607 const bool hasCustomWeight = std::fabs(
weight - 1.0 ) > 1e-10;
1609 wkbPtr << static_cast<char>( hasCustomWeight ? 1 : 0 );
1611 if ( hasCustomWeight )
1618 wkbPtr << static_cast<quint32>( mKnots.size() );
1621 for (
const double knot : mKnots )
1642 wkt += QString::number( mDegree );
1646 for (
int i = 0; i < mControlPoints.size(); ++i )
1651 const QgsPoint &pt = mControlPoints[i];
1663 if ( !mWeights.isEmpty() )
1666 for (
int i = 0; i < mWeights.size(); ++i )
1676 if ( !mKnots.isEmpty() )
1679 for (
int i = 0; i < mKnots.size(); ++i )
1698 std::unique_ptr<QgsLineString> line(
curveToLine() );
1700 return QDomElement();
1701 return line->asGml2( doc, precision, ns, axisOrder );
1708 std::unique_ptr<QgsLineString> line(
curveToLine() );
1710 return QDomElement();
1711 return line->asGml3( doc, precision, ns, axisOrder );
1716 std::unique_ptr<QgsLineString> line(
curveToLine() );
1718 return json::object();
1719 return line->asJsonObject( precision );
1725 std::unique_ptr<QgsLineString> line(
curveToLine() );
1728 return line->asKml( precision );
1738 return mControlPoints.isEmpty();
1743 mControlPoints.clear();
1762 std::unique_ptr<QgsLineString> line(
curveToLine() );
1763 return line ? line->centroid() :
QgsPoint();
1772 if ( mDegree < otherCurve->mDegree )
1774 else if ( mDegree > otherCurve->mDegree )
1777 const int nThis = mControlPoints.size();
1778 const int nOther = otherCurve->mControlPoints.size();
1780 if ( nThis < nOther )
1782 else if ( nThis > nOther )
1785 for (
int i = 0; i < nThis; ++i )
1787 if ( mControlPoints[i].x() < otherCurve->mControlPoints[i].x() )
1789 if ( mControlPoints[i].x() > otherCurve->mControlPoints[i].x() )
1791 if ( mControlPoints[i].y() < otherCurve->mControlPoints[i].y() )
1793 if ( mControlPoints[i].y() > otherCurve->mControlPoints[i].y() )
1797 if ( mWeights.size() < otherCurve->mWeights.size() )
1799 else if ( mWeights.size() > otherCurve->mWeights.size() )
1802 for (
int i = 0; i < mWeights.size(); ++i )
1804 if ( mWeights[i] < otherCurve->mWeights[i] )
1806 else if ( mWeights[i] > otherCurve->mWeights[i] )
1810 if ( mKnots.size() < otherCurve->mKnots.size() )
1812 else if ( mKnots.size() > otherCurve->mKnots.size() )
1815 for (
int i = 0; i < mKnots.size(); ++i )
1817 if ( mKnots[i] < otherCurve->mKnots[i] )
1819 else if ( mKnots[i] > otherCurve->mKnots[i] )
1828 if ( index < 0 || index >= mWeights.size() )
1830 return mWeights[index];
1835 if ( index < 0 || index >= mWeights.size() )
1839 mWeights[index] =
weight;
1846 if ( !
isPolyBezier() || localIndex < 0 || localIndex >= mControlPoints.size() )
1849 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.
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.
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.