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 )
520std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve>>
523 std::unique_ptr<QgsLineString> line(
curveToLine() );
526 return std::make_tuple(
nullptr,
nullptr );
528 return line->splitCurveAtVertex( index );
539 std::unique_ptr<QgsLineString> line(
curveToLine() );
541 line->sumUpArea( sum );
546 std::unique_ptr<QgsLineString> line(
curveToLine() );
548 line->sumUpArea3D( sum );
553 if ( index < 0 || index >= mControlPoints.size() )
555 return mControlPoints[index].x();
560 if ( index < 0 || index >= mControlPoints.size() )
562 return mControlPoints[index].y();
567 if ( index < 0 || index >= mControlPoints.size() )
569 return mControlPoints[index].is3D() ? mControlPoints[index].z() : std::numeric_limits<double>::quiet_NaN();
574 if ( index < 0 || index >= mControlPoints.size() )
576 return mControlPoints[index].isMeasure() ? mControlPoints[index].m() : std::numeric_limits<double>::quiet_NaN();
587 for (
QgsPoint &p : mControlPoints )
589 p.addZValue( zValue );
603 for (
QgsPoint &p : mControlPoints )
605 p.addMValue( mValue );
616 for (
QgsPoint &p : mControlPoints )
618 p.setZ( std::numeric_limits<double>::quiet_NaN() );
631 for (
QgsPoint &p : mControlPoints )
633 p.setM( std::numeric_limits<double>::quiet_NaN() );
643 if ( position.
part != 0 || position.
ring != 0 )
647 const int idx = position.
vertex;
648 if ( idx < 0 || idx >= mControlPoints.size() )
652 mControlPoints.remove( idx );
653 if ( idx < mWeights.size() )
655 mWeights.remove( idx );
666 QVector<QgsPoint> newPts;
667 QVector<double> newWeights;
668 for (
int i = 0; i < mControlPoints.size(); ++i )
670 if ( filter( mControlPoints[i] ) )
672 newPts.append( mControlPoints[i] );
673 if ( i < mWeights.size() )
674 newWeights.append( mWeights[i] );
677 mControlPoints = newPts;
678 mWeights = newWeights;
687 Q_ASSERT( numControlPoints >
degree );
689 const int knotsSize = numControlPoints +
degree + 1;
690 QVector<double>
knots;
691 knots.reserve( knotsSize );
692 for (
int i = 0; i < knotsSize; ++i )
696 else if ( i >= numControlPoints )
699 knots.append(
static_cast<double>( i -
degree ) / ( numControlPoints -
degree ) );
706 if ( nAnchors < 2 ||
degree < 1 )
707 return QVector<double>();
709 const int segmentCount = nAnchors - 1;
712 QVector<double>
knots;
713 knots.reserve( totalKnots );
716 for (
int i = 0; i <
degree + 1; ++i )
720 for (
int segmentIndex = 1; segmentIndex < segmentCount; ++segmentIndex )
722 for (
int j = 0; j <
degree; ++j )
723 knots.append(
static_cast<double>( segmentIndex ) );
727 for (
int i = 0; i <
degree + 1; ++i )
728 knots.append(
static_cast<double>( segmentCount ) );
746 const unsigned char headerEndianness = *
static_cast<const unsigned char *
>( wkb );
764 mDegree =
static_cast<int>(
degree );
767 quint32 numControlPoints;
768 wkb >> numControlPoints;
778 const int minBytesPerPoint = 18 + (
is3D ? 8 : 0 ) + (
isMeasure ? 8 : 0 );
779 if ( numControlPoints >
static_cast<quint32
>( wkb.
remaining() / minBytesPerPoint + 1 ) )
782 mControlPoints.clear();
784 mControlPoints.reserve( numControlPoints );
785 mWeights.reserve( numControlPoints );
788 for ( quint32 i = 0; i < numControlPoints; ++i )
791 char pointEndianness;
792 wkb >> pointEndianness;
796 if (
static_cast<unsigned char>( pointEndianness ) != headerEndianness )
800 double x, y, z = 0.0, m = 0.0;
813 if ( weightFlag == 1 )
826 point =
QgsPoint( x, y, std::numeric_limits<double>::quiet_NaN(), m );
830 mControlPoints.append( point );
831 mWeights.append(
weight );
839 const quint32 expectedKnots = numControlPoints +
degree + 1;
840 if ( numKnots != expectedKnots )
844 if ( numKnots *
sizeof(
double ) >
static_cast<quint32
>( wkb.
remaining() ) )
848 mKnots.reserve( numKnots );
851 for ( quint32 i = 0; i < numKnots; ++i )
855 mKnots.append( knot );
865 const QString geomTypeStr = wkt.split(
'(' )[0].trimmed().toUpper();
867 if ( !geomTypeStr.startsWith(
"NURBSCURVE"_L1 ) )
874 if ( geomTypeStr.contains(
"ZM"_L1 ) )
876 else if ( geomTypeStr.endsWith(
'Z' ) || geomTypeStr.endsWith(
" Z"_L1 ) )
878 else if ( geomTypeStr.endsWith(
'M' ) || geomTypeStr.endsWith(
" M"_L1 ) )
885 if (
parts.second.compare(
"EMPTY"_L1, Qt::CaseInsensitive ) == 0 ||
parts.second.isEmpty() )
891 if ( blocks.isEmpty() )
896 int degree = blocks[0].trimmed().toInt( &ok );
900 if ( blocks.size() < 2 )
904 QString pointsStr = blocks[1].trimmed();
907 if ( !pointsStr.startsWith(
'('_L1 ) || !pointsStr.endsWith(
')'_L1 ) )
910 pointsStr = pointsStr.mid( 1, pointsStr.length() - 2 ).trimmed();
913 QStringList pointsCoords = pointsStr.split(
',', Qt::SkipEmptyParts );
916 const thread_local QRegularExpression rx( u
"\\s+"_s );
918 for (
const QString &pointStr : pointsCoords )
920 QStringList coords = pointStr.trimmed().split( rx, Qt::SkipEmptyParts );
922 if ( coords.size() < 2 )
928 double x = coords[0].toDouble( &ok );
932 double y = coords[1].toDouble( &ok );
937 if ( coords.size() >= 3 )
942 double m = coords[2].toDouble( &ok );
945 point =
QgsPoint( x, y, std::numeric_limits<double>::quiet_NaN(), m );
950 double z = coords[2].toDouble( &ok );
958 double z = coords[2].toDouble( &ok );
961 double m = coords[3].toDouble( &ok );
966 else if (
isMeasure() && coords.size() >= 4 )
969 double z = coords[2].toDouble( &ok );
972 double m = coords[3].toDouble( &ok );
982 double z = coords[2].toDouble( &ok );
991 double z = coords[2].toDouble( &ok );
994 double m = coords[3].toDouble( &ok );
1036 mWeights.append( 1.0 );
1040 bool hasWeights =
false;
1041 bool hasKnots =
false;
1044 for (
int i = 2; i < blocks.size(); ++i )
1046 QString block = blocks[i].trimmed();
1048 if ( block.startsWith(
'('_L1 ) )
1051 if ( !block.endsWith(
')'_L1 ) )
1055 block = block.mid( 1, block.length() - 2 ).trimmed();
1056 QStringList values = block.split(
',', Qt::SkipEmptyParts );
1058 QVector<double> parsedValues;
1059 for (
const QString &valueStr : values )
1062 double value = valueStr.trimmed().toDouble( &ok );
1065 parsedValues.append( value );
1068 if ( !hasWeights && parsedValues.size() ==
controlPoints.size() )
1071 mWeights = parsedValues;
1074 else if ( !hasKnots )
1077 mKnots = parsedValues;
1109 if ( mDegree != o->mDegree || mControlPoints.size() != o->mControlPoints.size() || mWeights.size() != o->mWeights.size() || mKnots.size() != o->mKnots.size() )
1114 for (
int i = 0; i < mControlPoints.size(); ++i )
1116 if ( mControlPoints[i].distance( o->mControlPoints[i] ) >= epsilon )
1120 for (
int i = 0; i < mWeights.size(); ++i )
1122 if ( std::fabs( mWeights[i] - o->mWeights[i] ) > epsilon )
1126 for (
int i = 0; i < mKnots.size(); ++i )
1128 if ( std::fabs( mKnots[i] - o->mKnots[i] ) > epsilon )
1142 return u
"NurbsCurve"_s;
1162 if (
id.part != 0 ||
id.ring != 0 )
1166 const int idx =
id.vertex;
1167 if ( idx < 0 || idx >= mControlPoints.size() )
1171 return mControlPoints[idx];
1176 return ( part == 0 && ring == 0 ) ? mControlPoints.size() : 0;
1181 if (
id.part == 0 &&
id.ring == 0 )
1193 if ( mValidityComputed )
1196 error = u
"NURBS curve is invalid"_s;
1200 mValidityComputed =
true;
1205 error = u
"Degree must be >= 1"_s;
1209 const int n = mControlPoints.size();
1210 if ( n < mDegree + 1 )
1212 error = u
"Not enough control points for degree"_s;
1216 if ( mKnots.size() != n + mDegree + 1 )
1218 error = u
"Knot vector size is incorrect"_s;
1222 if ( mWeights.size() != n )
1224 error = u
"Weights vector size mismatch"_s;
1229 for (
int i = 1; i < mKnots.size(); ++i )
1231 if ( mKnots[i] < mKnots[i - 1] )
1233 error = u
"Knot vector values must be non-decreasing"_s;
1244 std::unique_ptr<QgsLineString> line(
curveToLine() );
1246 line->addToPainterPath( path );
1251 std::unique_ptr<QgsLineString> line(
curveToLine() );
1254 return line->curveSubstring( startDistance, endDistance );
1259 std::unique_ptr<QgsLineString> line(
curveToLine() );
1260 return line ? line->length() : 0.0;
1265 std::unique_ptr<QgsLineString> line(
curveToLine() );
1268 return line->segmentLength( startVertex );
1273 std::unique_ptr<QgsLineString> line(
curveToLine() );
1276 return line->distanceBetweenVertices( fromVertex, toVertex );
1282 for (
QgsPoint &pt : result->mControlPoints )
1285 pt.
setX( std::round( pt.
x() / hSpacing ) * hSpacing );
1287 pt.
setY( std::round( pt.
y() / vSpacing ) * vSpacing );
1288 if ( pt.
is3D() && dSpacing > 0 )
1289 pt.
setZ( std::round( pt.
z() / dSpacing ) * dSpacing );
1291 pt.
setM( std::round( pt.
m() / mSpacing ) * mSpacing );
1294 if ( removeRedundantPoints )
1295 result->removeDuplicateNodes();
1302 std::unique_ptr<QgsLineString> line(
curveToLine() );
1305 return line->simplifyByDistance( tolerance );
1310 if ( mControlPoints.size() < 2 )
1313 QVector<QgsPoint> newPoints;
1314 QVector<double> newWeights;
1316 newPoints.reserve( mControlPoints.size() );
1317 newWeights.reserve( mWeights.size() );
1319 newPoints.append( mControlPoints.first() );
1320 if ( !mWeights.isEmpty() )
1321 newWeights.append( mWeights.first() );
1323 for (
int i = 1; i < mControlPoints.size(); ++i )
1325 const double dist = ( useZValues && mControlPoints[i].is3D() && mControlPoints[i - 1].
is3D() )
1326 ? mControlPoints[i].distance3D( mControlPoints[i - 1] )
1327 : mControlPoints[i].distance( mControlPoints[i - 1] );
1329 if ( dist >= epsilon )
1331 newPoints.append( mControlPoints[i] );
1332 if ( i < mWeights.size() )
1333 newWeights.append( mWeights[i] );
1337 const bool changed = ( newPoints.size() != mControlPoints.size() );
1341 mControlPoints = newPoints;
1342 mWeights = newWeights;
1353 std::unique_ptr<QgsLineString> line(
curveToLine() );
1356 return line->vertexAngle( vertex );
1361 for (
QgsPoint &pt : mControlPoints )
1363 const double x = pt.x();
1372 Q_UNUSED( feedback );
1376 for (
QgsPoint &pt : mControlPoints )
1378 double x = pt.x(), y = pt.y(), z = pt.z(), m = pt.m();
1398 std::unique_ptr<QgsLineString> line(
curveToLine() );
1407 return line->closestSegment( pt, segmentPt, vertexAfter, leftOf, epsilon );
1412 for (
QgsPoint &pt : mControlPoints )
1416 double z = transformZ && pt.is3D() ? pt.z() : std::numeric_limits<double>::quiet_NaN();
1420 if ( transformZ && pt.is3D() )
1428 for (
QgsPoint &pt : mControlPoints )
1430 const QPointF p = t.map( QPointF( pt.x(), pt.y() ) );
1435 pt.setZ( pt.z() * zScale + zTranslate );
1436 if ( pt.isMeasure() )
1437 pt.setM( pt.m() * mScale + mTranslate );
1458 if ( mControlPoints.isEmpty() )
1464 for (
const QgsPoint &pt : mControlPoints )
1466 bbox.
combineWith( pt.x(), pt.y(), pt.is3D() ? pt.z() : std::numeric_limits<double>::quiet_NaN() );
1470 std::unique_ptr<QgsLineString> line(
curveToLine() );
1482 mValidityComputed =
false;
1488 if ( position.
part != 0 || position.
ring != 0 )
1491 const int idx = position.
vertex;
1492 if ( idx < 0 || idx >= mControlPoints.size() )
1495 mControlPoints[idx] = newPos;
1502 if ( position.
part != 0 || position.
ring != 0 )
1505 const int idx = position.
vertex;
1506 if ( idx < 0 || idx > mControlPoints.size() )
1509 mControlPoints.insert( idx, vertex );
1510 if ( idx <= mWeights.size() )
1511 mWeights.insert( idx, 1.0 );
1525 const int coordinateDimension = 2 + (
is3D ? 1 : 0 ) + (
isMeasure ? 1 : 0 );
1539 for (
int i = 0; i < mControlPoints.size(); ++i )
1545 size += coordinateDimension * 8;
1551 if ( i < mWeights.size() && std::fabs( mWeights[i] - 1.0 ) > 1e-10 )
1559 size += mKnots.size() * 8;
1566 QByteArray wkbArray;
1572 wkbPtr << static_cast<quint32>(
mWkbType );
1575 wkbPtr << static_cast<quint32>( mDegree );
1578 wkbPtr << static_cast<quint32>( mControlPoints.size() );
1584 for (
int i = 0; i < mControlPoints.size(); ++i )
1586 const QgsPoint &point = mControlPoints[i];
1592 wkbPtr << point.
x() << point.
y();
1595 wkbPtr << point.
z();
1597 wkbPtr << point.
m();
1600 const double weight = ( i < mWeights.size() ) ? mWeights[i] : 1.0;
1601 const bool hasCustomWeight = std::fabs(
weight - 1.0 ) > 1e-10;
1603 wkbPtr << static_cast<char>( hasCustomWeight ? 1 : 0 );
1605 if ( hasCustomWeight )
1612 wkbPtr << static_cast<quint32>( mKnots.size() );
1615 for (
const double knot : mKnots )
1636 wkt += QString::number( mDegree );
1640 for (
int i = 0; i < mControlPoints.size(); ++i )
1645 const QgsPoint &pt = mControlPoints[i];
1657 if ( !mWeights.isEmpty() )
1660 for (
int i = 0; i < mWeights.size(); ++i )
1670 if ( !mKnots.isEmpty() )
1673 for (
int i = 0; i < mKnots.size(); ++i )
1692 std::unique_ptr<QgsLineString> line(
curveToLine() );
1694 return QDomElement();
1695 return line->asGml2( doc, precision, ns, axisOrder );
1702 std::unique_ptr<QgsLineString> line(
curveToLine() );
1704 return QDomElement();
1705 return line->asGml3( doc, precision, ns, axisOrder );
1710 std::unique_ptr<QgsLineString> line(
curveToLine() );
1712 return json::object();
1713 return line->asJsonObject( precision );
1719 std::unique_ptr<QgsLineString> line(
curveToLine() );
1722 return line->asKml( precision );
1732 return mControlPoints.isEmpty();
1737 mControlPoints.clear();
1756 std::unique_ptr<QgsLineString> line(
curveToLine() );
1757 return line ? line->centroid() :
QgsPoint();
1766 if ( mDegree < otherCurve->mDegree )
1768 else if ( mDegree > otherCurve->mDegree )
1771 const int nThis = mControlPoints.size();
1772 const int nOther = otherCurve->mControlPoints.size();
1774 if ( nThis < nOther )
1776 else if ( nThis > nOther )
1779 for (
int i = 0; i < nThis; ++i )
1781 if ( mControlPoints[i].x() < otherCurve->mControlPoints[i].x() )
1783 if ( mControlPoints[i].x() > otherCurve->mControlPoints[i].x() )
1785 if ( mControlPoints[i].y() < otherCurve->mControlPoints[i].y() )
1787 if ( mControlPoints[i].y() > otherCurve->mControlPoints[i].y() )
1791 if ( mWeights.size() < otherCurve->mWeights.size() )
1793 else if ( mWeights.size() > otherCurve->mWeights.size() )
1796 for (
int i = 0; i < mWeights.size(); ++i )
1798 if ( mWeights[i] < otherCurve->mWeights[i] )
1800 else if ( mWeights[i] > otherCurve->mWeights[i] )
1804 if ( mKnots.size() < otherCurve->mKnots.size() )
1806 else if ( mKnots.size() > otherCurve->mKnots.size() )
1809 for (
int i = 0; i < mKnots.size(); ++i )
1811 if ( mKnots[i] < otherCurve->mKnots[i] )
1813 else if ( mKnots[i] > otherCurve->mKnots[i] )
1822 if ( index < 0 || index >= mWeights.size() )
1824 return mWeights[index];
1829 if ( index < 0 || index >= mWeights.size() )
1833 mWeights[index] =
weight;
1840 if ( !
isPolyBezier() || localIndex < 0 || localIndex >= mControlPoints.size() )
1843 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.