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 ( positions.isEmpty() )
655 if ( mControlPoints.size() - positions.size() <= mDegree )
661 QList<QgsVertexId> sortedPositions( positions.begin(), positions.end() );
662 std::sort( sortedPositions.begin(), sortedPositions.end(), [](
const QgsVertexId &a,
const QgsVertexId &b ) { return a.vertex > b.vertex; } );
666 int idx = position.vertex;
667 mControlPoints.remove( idx );
668 if ( idx < mWeights.size() )
669 mWeights.remove( idx );
679 QVector<QgsPoint> newPts;
680 QVector<double> newWeights;
681 for (
int i = 0; i < mControlPoints.size(); ++i )
683 if ( filter( mControlPoints[i] ) )
685 newPts.append( mControlPoints[i] );
686 if ( i < mWeights.size() )
687 newWeights.append( mWeights[i] );
690 mControlPoints = newPts;
691 mWeights = newWeights;
700 Q_ASSERT( numControlPoints >
degree );
702 const int knotsSize = numControlPoints +
degree + 1;
703 QVector<double>
knots;
704 knots.reserve( knotsSize );
705 for (
int i = 0; i < knotsSize; ++i )
709 else if ( i >= numControlPoints )
712 knots.append(
static_cast<double>( i -
degree ) / ( numControlPoints -
degree ) );
719 if ( nAnchors < 2 ||
degree < 1 )
720 return QVector<double>();
722 const int segmentCount = nAnchors - 1;
725 QVector<double>
knots;
726 knots.reserve( totalKnots );
729 for (
int i = 0; i <
degree + 1; ++i )
733 for (
int segmentIndex = 1; segmentIndex < segmentCount; ++segmentIndex )
735 for (
int j = 0; j <
degree; ++j )
736 knots.append(
static_cast<double>( segmentIndex ) );
740 for (
int i = 0; i <
degree + 1; ++i )
741 knots.append(
static_cast<double>( segmentCount ) );
759 const unsigned char headerEndianness = *
static_cast<const unsigned char *
>( wkb );
777 mDegree =
static_cast<int>(
degree );
780 quint32 numControlPoints;
781 wkb >> numControlPoints;
791 const int minBytesPerPoint = 18 + (
is3D ? 8 : 0 ) + (
isMeasure ? 8 : 0 );
792 if ( numControlPoints >
static_cast<quint32
>( wkb.
remaining() / minBytesPerPoint + 1 ) )
795 mControlPoints.clear();
797 mControlPoints.reserve( numControlPoints );
798 mWeights.reserve( numControlPoints );
801 for ( quint32 i = 0; i < numControlPoints; ++i )
804 char pointEndianness;
805 wkb >> pointEndianness;
809 if (
static_cast<unsigned char>( pointEndianness ) != headerEndianness )
813 double x, y, z = 0.0, m = 0.0;
826 if ( weightFlag == 1 )
839 point =
QgsPoint( x, y, std::numeric_limits<double>::quiet_NaN(), m );
843 mControlPoints.append( point );
844 mWeights.append(
weight );
852 const quint32 expectedKnots = numControlPoints +
degree + 1;
853 if ( numKnots != expectedKnots )
857 if ( numKnots *
sizeof(
double ) >
static_cast<quint32
>( wkb.
remaining() ) )
861 mKnots.reserve( numKnots );
864 for ( quint32 i = 0; i < numKnots; ++i )
868 mKnots.append( knot );
878 const QString geomTypeStr = wkt.split(
'(' )[0].trimmed().toUpper();
880 if ( !geomTypeStr.startsWith(
"NURBSCURVE"_L1 ) )
887 if ( geomTypeStr.contains(
"ZM"_L1 ) )
889 else if ( geomTypeStr.endsWith(
'Z' ) || geomTypeStr.endsWith(
" Z"_L1 ) )
891 else if ( geomTypeStr.endsWith(
'M' ) || geomTypeStr.endsWith(
" M"_L1 ) )
898 if (
parts.second.compare(
"EMPTY"_L1, Qt::CaseInsensitive ) == 0 ||
parts.second.isEmpty() )
904 if ( blocks.isEmpty() )
909 int degree = blocks[0].trimmed().toInt( &ok );
913 if ( blocks.size() < 2 )
917 QString pointsStr = blocks[1].trimmed();
920 if ( !pointsStr.startsWith(
'('_L1 ) || !pointsStr.endsWith(
')'_L1 ) )
923 pointsStr = pointsStr.mid( 1, pointsStr.length() - 2 ).trimmed();
926 QStringList pointsCoords = pointsStr.split(
',', Qt::SkipEmptyParts );
929 const thread_local QRegularExpression rx( u
"\\s+"_s );
931 for (
const QString &pointStr : pointsCoords )
933 QStringList coords = pointStr.trimmed().split( rx, Qt::SkipEmptyParts );
935 if ( coords.size() < 2 )
941 double x = coords[0].toDouble( &ok );
945 double y = coords[1].toDouble( &ok );
950 if ( coords.size() >= 3 )
955 double m = coords[2].toDouble( &ok );
958 point =
QgsPoint( x, y, std::numeric_limits<double>::quiet_NaN(), m );
963 double z = coords[2].toDouble( &ok );
971 double z = coords[2].toDouble( &ok );
974 double m = coords[3].toDouble( &ok );
979 else if (
isMeasure() && coords.size() >= 4 )
982 double z = coords[2].toDouble( &ok );
985 double m = coords[3].toDouble( &ok );
995 double z = coords[2].toDouble( &ok );
1004 double z = coords[2].toDouble( &ok );
1007 double m = coords[3].toDouble( &ok );
1049 mWeights.append( 1.0 );
1053 bool hasWeights =
false;
1054 bool hasKnots =
false;
1057 for (
int i = 2; i < blocks.size(); ++i )
1059 QString block = blocks[i].trimmed();
1061 if ( block.startsWith(
'('_L1 ) )
1064 if ( !block.endsWith(
')'_L1 ) )
1068 block = block.mid( 1, block.length() - 2 ).trimmed();
1069 QStringList values = block.split(
',', Qt::SkipEmptyParts );
1071 QVector<double> parsedValues;
1072 for (
const QString &valueStr : values )
1075 double value = valueStr.trimmed().toDouble( &ok );
1078 parsedValues.append( value );
1081 if ( !hasWeights && parsedValues.size() ==
controlPoints.size() )
1084 mWeights = parsedValues;
1087 else if ( !hasKnots )
1090 mKnots = parsedValues;
1122 if ( mDegree != o->mDegree || mControlPoints.size() != o->mControlPoints.size() || mWeights.size() != o->mWeights.size() || mKnots.size() != o->mKnots.size() )
1127 for (
int i = 0; i < mControlPoints.size(); ++i )
1129 if ( mControlPoints[i].distance( o->mControlPoints[i] ) >= epsilon )
1133 for (
int i = 0; i < mWeights.size(); ++i )
1135 if ( std::fabs( mWeights[i] - o->mWeights[i] ) > epsilon )
1139 for (
int i = 0; i < mKnots.size(); ++i )
1141 if ( std::fabs( mKnots[i] - o->mKnots[i] ) > epsilon )
1155 return u
"NurbsCurve"_s;
1175 if (
id.part != 0 ||
id.ring != 0 )
1179 const int idx =
id.vertex;
1180 if ( idx < 0 || idx >= mControlPoints.size() )
1184 return mControlPoints[idx];
1189 return ( part == 0 && ring == 0 ) ? mControlPoints.size() : 0;
1194 if (
id.part == 0 &&
id.ring == 0 )
1206 if ( mValidityComputed )
1209 error = u
"NURBS curve is invalid"_s;
1213 mValidityComputed =
true;
1218 error = u
"Degree must be >= 1"_s;
1222 const int n = mControlPoints.size();
1223 if ( n < mDegree + 1 )
1225 error = u
"Not enough control points for degree"_s;
1229 if ( mKnots.size() != n + mDegree + 1 )
1231 error = u
"Knot vector size is incorrect"_s;
1235 if ( mWeights.size() != n )
1237 error = u
"Weights vector size mismatch"_s;
1242 for (
int i = 1; i < mKnots.size(); ++i )
1244 if ( mKnots[i] < mKnots[i - 1] )
1246 error = u
"Knot vector values must be non-decreasing"_s;
1257 std::unique_ptr<QgsLineString> line(
curveToLine() );
1259 line->addToPainterPath( path );
1264 std::unique_ptr<QgsLineString> line(
curveToLine() );
1267 return line->curveSubstring( startDistance, endDistance );
1272 std::unique_ptr<QgsLineString> line(
curveToLine() );
1273 return line ? line->length() : 0.0;
1278 std::unique_ptr<QgsLineString> line(
curveToLine() );
1281 return line->segmentLength( startVertex );
1286 std::unique_ptr<QgsLineString> line(
curveToLine() );
1289 return line->distanceBetweenVertices( fromVertex, toVertex );
1295 for (
QgsPoint &pt : result->mControlPoints )
1298 pt.
setX( std::round( pt.
x() / hSpacing ) * hSpacing );
1300 pt.
setY( std::round( pt.
y() / vSpacing ) * vSpacing );
1301 if ( pt.
is3D() && dSpacing > 0 )
1302 pt.
setZ( std::round( pt.
z() / dSpacing ) * dSpacing );
1304 pt.
setM( std::round( pt.
m() / mSpacing ) * mSpacing );
1307 if ( removeRedundantPoints )
1308 result->removeDuplicateNodes();
1315 std::unique_ptr<QgsLineString> line(
curveToLine() );
1318 return line->simplifyByDistance( tolerance );
1323 if ( mControlPoints.size() < 2 )
1326 QVector<QgsPoint> newPoints;
1327 QVector<double> newWeights;
1329 newPoints.reserve( mControlPoints.size() );
1330 newWeights.reserve( mWeights.size() );
1332 newPoints.append( mControlPoints.first() );
1333 if ( !mWeights.isEmpty() )
1334 newWeights.append( mWeights.first() );
1336 for (
int i = 1; i < mControlPoints.size(); ++i )
1338 const double dist = ( useZValues && mControlPoints[i].is3D() && mControlPoints[i - 1].
is3D() ) ? mControlPoints[i].distance3D( mControlPoints[i - 1] )
1339 : mControlPoints[i].distance( mControlPoints[i - 1] );
1341 if ( dist >= epsilon )
1343 newPoints.append( mControlPoints[i] );
1344 if ( i < mWeights.size() )
1345 newWeights.append( mWeights[i] );
1349 const bool changed = ( newPoints.size() != mControlPoints.size() );
1353 mControlPoints = newPoints;
1354 mWeights = newWeights;
1365 std::unique_ptr<QgsLineString> line(
curveToLine() );
1368 return line->vertexAngle( vertex );
1373 for (
QgsPoint &pt : mControlPoints )
1375 const double x = pt.x();
1384 Q_UNUSED( feedback );
1388 for (
QgsPoint &pt : mControlPoints )
1390 double x = pt.x(), y = pt.y(), z = pt.z(), m = pt.m();
1410 std::unique_ptr<QgsLineString> line(
curveToLine() );
1419 return line->closestSegment( pt, segmentPt, vertexAfter, leftOf, epsilon );
1424 for (
QgsPoint &pt : mControlPoints )
1428 double z = transformZ && pt.is3D() ? pt.z() : std::numeric_limits<double>::quiet_NaN();
1432 if ( transformZ && pt.is3D() )
1440 for (
QgsPoint &pt : mControlPoints )
1442 const QPointF p = t.map( QPointF( pt.x(), pt.y() ) );
1447 pt.setZ( pt.z() * zScale + zTranslate );
1448 if ( pt.isMeasure() )
1449 pt.setM( pt.m() * mScale + mTranslate );
1470 if ( mControlPoints.isEmpty() )
1476 for (
const QgsPoint &pt : mControlPoints )
1478 bbox.
combineWith( pt.x(), pt.y(), pt.is3D() ? pt.z() : std::numeric_limits<double>::quiet_NaN() );
1482 std::unique_ptr<QgsLineString> line(
curveToLine() );
1494 mValidityComputed =
false;
1500 if ( position.
part != 0 || position.
ring != 0 )
1503 const int idx = position.
vertex;
1504 if ( idx < 0 || idx >= mControlPoints.size() )
1507 mControlPoints[idx].setX( newPos.
x() );
1508 mControlPoints[idx].setY( newPos.
y() );
1511 mControlPoints[idx].setZ( newPos.
z() );
1514 mControlPoints[idx].setM( newPos.
m() );
1522 if ( position.
part != 0 || position.
ring != 0 )
1525 const int idx = position.
vertex;
1526 if ( idx < 0 || idx > mControlPoints.size() )
1529 mControlPoints.insert( idx, vertex );
1530 if ( idx <= mWeights.size() )
1531 mWeights.insert( idx, 1.0 );
1545 const int coordinateDimension = 2 + (
is3D ? 1 : 0 ) + (
isMeasure ? 1 : 0 );
1559 for (
int i = 0; i < mControlPoints.size(); ++i )
1565 size += coordinateDimension * 8;
1571 if ( i < mWeights.size() && std::fabs( mWeights[i] - 1.0 ) > 1e-10 )
1579 size += mKnots.size() * 8;
1586 QByteArray wkbArray;
1592 wkbPtr << static_cast<quint32>(
mWkbType );
1595 wkbPtr << static_cast<quint32>( mDegree );
1598 wkbPtr << static_cast<quint32>( mControlPoints.size() );
1604 for (
int i = 0; i < mControlPoints.size(); ++i )
1606 const QgsPoint &point = mControlPoints[i];
1612 wkbPtr << point.
x() << point.
y();
1615 wkbPtr << point.
z();
1617 wkbPtr << point.
m();
1620 const double weight = ( i < mWeights.size() ) ? mWeights[i] : 1.0;
1621 const bool hasCustomWeight = std::fabs(
weight - 1.0 ) > 1e-10;
1623 wkbPtr << static_cast<char>( hasCustomWeight ? 1 : 0 );
1625 if ( hasCustomWeight )
1632 wkbPtr << static_cast<quint32>( mKnots.size() );
1635 for (
const double knot : mKnots )
1656 wkt += QString::number( mDegree );
1660 for (
int i = 0; i < mControlPoints.size(); ++i )
1665 const QgsPoint &pt = mControlPoints[i];
1677 if ( !mWeights.isEmpty() )
1680 for (
int i = 0; i < mWeights.size(); ++i )
1690 if ( !mKnots.isEmpty() )
1693 for (
int i = 0; i < mKnots.size(); ++i )
1712 std::unique_ptr<QgsLineString> line(
curveToLine() );
1714 return QDomElement();
1715 return line->asGml2( doc, precision, ns, axisOrder );
1722 std::unique_ptr<QgsLineString> line(
curveToLine() );
1724 return QDomElement();
1725 return line->asGml3( doc, precision, ns, axisOrder );
1734 std::unique_ptr<QgsLineString> line(
curveToLine() );
1736 return json::object();
1737 return line->asJsonObject( precision );
1743 std::unique_ptr<QgsLineString> line(
curveToLine() );
1746 return line->asKml( precision );
1756 return mControlPoints.isEmpty();
1761 mControlPoints.clear();
1780 std::unique_ptr<QgsLineString> line(
curveToLine() );
1781 return line ? line->centroid() :
QgsPoint();
1790 if ( mDegree < otherCurve->mDegree )
1792 else if ( mDegree > otherCurve->mDegree )
1795 const int nThis = mControlPoints.size();
1796 const int nOther = otherCurve->mControlPoints.size();
1798 if ( nThis < nOther )
1800 else if ( nThis > nOther )
1803 for (
int i = 0; i < nThis; ++i )
1805 if ( mControlPoints[i].x() < otherCurve->mControlPoints[i].x() )
1807 if ( mControlPoints[i].x() > otherCurve->mControlPoints[i].x() )
1809 if ( mControlPoints[i].y() < otherCurve->mControlPoints[i].y() )
1811 if ( mControlPoints[i].y() > otherCurve->mControlPoints[i].y() )
1815 if ( mWeights.size() < otherCurve->mWeights.size() )
1817 else if ( mWeights.size() > otherCurve->mWeights.size() )
1820 for (
int i = 0; i < mWeights.size(); ++i )
1822 if ( mWeights[i] < otherCurve->mWeights[i] )
1824 else if ( mWeights[i] > otherCurve->mWeights[i] )
1828 if ( mKnots.size() < otherCurve->mKnots.size() )
1830 else if ( mKnots.size() > otherCurve->mKnots.size() )
1833 for (
int i = 0; i < mKnots.size(); ++i )
1835 if ( mKnots[i] < otherCurve->mKnots[i] )
1837 else if ( mKnots[i] > otherCurve->mKnots[i] )
1846 if ( index < 0 || index >= mWeights.size() )
1848 return mWeights[index];
1853 if ( index < 0 || index >= mWeights.size() )
1857 mWeights[index] =
weight;
1864 if ( !
isPolyBezier() || localIndex < 0 || localIndex >= mControlPoints.size() )
1867 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).
GeoJsonProfile
GeoJson export Profile according to OGC Features and Geometries JSON - Part 1: Core https://docs....
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.
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).
json asJsonObject(int precision=17, Qgis::GeoJsonProfile profile=Qgis::GeoJsonProfile::Legacy) const override
Returns a json object representation of the geometry with the given precision and profile.
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 type 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...
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.
bool isSimpleCurve() const override
This method returns true for classes that can be cast to the QgsSimpleCurve class.
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.