23#include <nlohmann/json.hpp>
33#include <QDomDocument>
38using namespace Qt::StringLiterals;
62 double *x =
mX.data();
63 double *y =
mY.data();
88QgsLineString::QgsLineString(
const QVector<double> &x,
const QVector<double> &y,
const QVector<double> &z,
const QVector<double> &m,
bool is25DType )
91 int pointCount = std::min( x.size(), y.size() );
92 if ( x.size() == pointCount )
98 mX = x.mid( 0, pointCount );
100 if ( y.size() == pointCount )
106 mY = y.mid( 0, pointCount );
108 if ( !z.isEmpty() && z.count() >= pointCount )
111 if ( z.size() == pointCount )
117 mZ = z.mid( 0, pointCount );
120 if ( !m.isEmpty() && m.count() >= pointCount )
123 if ( m.size() == pointCount )
129 mM = m.mid( 0, pointCount );
185 return std::make_unique< QgsLineString >();
187 QVector<double> x( segments + 1 );
188 QVector<double> y( segments + 1 );
192 const bool hasZ = start.
is3D() && controlPoint1.
is3D() && controlPoint2.
is3D() && end.
is3D();
195 z.resize( segments + 1 );
201 m.resize( segments + 1 );
204 double *
xData = x.data();
205 double *
yData = y.data();
206 double *
zData = z.data();
207 double *
mData = m.data();
209 const double step = 1.0 / segments;
211 for (
int i = 0; i <= segments; ++i )
213 const double t = i * step;
216 double iz = std::numeric_limits<double>::quiet_NaN();
217 double im = std::numeric_limits<double>::quiet_NaN();
253 return std::make_unique< QgsLineString >( x, y, z, m );
260 x.resize( polygon.count() );
261 y.resize( polygon.count() );
262 double *
xData = x.data();
263 double *
yData = y.data();
265 const QPointF *src = polygon.data();
266 for (
int i = 0; i < polygon.size(); ++i )
273 return std::make_unique< QgsLineString >( x, y );
289 const int size =
mX.size();
293 const double *x =
mX.constData();
294 const double *y =
mY.constData();
295 const bool useZ =
is3D();
297 const double *z = useZ ?
mZ.constData() :
nullptr;
298 const double *m = useM ?
mM.constData() :
nullptr;
300 for (
int i = 0; i < size; ++i )
319 error = QObject::tr(
"LineString has less than 2 points and is not empty." );
330 bool res =
snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing,
mX,
mY,
mZ,
mM, result->mX, result->mY, result->mZ, result->mM, removeRedundantPoints );
332 return result.release();
339 if (
mX.count() <= 2 )
342 double prevX =
mX.at( 0 );
343 double prevY =
mY.at( 0 );
345 bool useZ = hasZ && useZValues;
346 double prevZ = useZ ?
mZ.at( 0 ) : 0;
348 int remaining =
mX.count();
349 while ( i < remaining )
351 double currentX =
mX.at( i );
352 double currentY =
mY.at( i );
353 double currentZ = useZ ?
mZ.at( i ) : 0;
387 if (
is3D() && closed )
388 closed &=
qgsDoubleNear(
mZ.first(),
mZ.last() ) || ( std::isnan(
mZ.first() ) && std::isnan(
mZ.last() ) );
401 return mBoundingBox.toRectangle().intersects( rectangle );
403 const int nb =
mX.size();
413 || rectangle.
contains(
mX.at(
static_cast< int >( nb * 0.2 ) ),
mY.at(
static_cast< int >( nb * 0.2 ) ) )
414 || rectangle.
contains(
mX.at(
static_cast< int >( nb * 0.4 ) ),
mY.at(
static_cast< int >( nb * 0.4 ) ) )
415 || rectangle.
contains(
mX.at(
static_cast< int >( nb * 0.6 ) ),
mY.at(
static_cast< int >( nb * 0.6 ) ) )
416 || rectangle.
contains(
mX.at(
static_cast< int >( nb * 0.8 ) ),
mY.at(
static_cast< int >( nb * 0.8 ) ) )
417 || rectangle.
contains(
mX.at( nb - 1 ),
mY.at( nb - 1 ) ) )
426 double xmin = std::numeric_limits<double>::max();
427 double ymin = std::numeric_limits<double>::max();
428 double zmin = -std::numeric_limits<double>::max();
429 double xmax = -std::numeric_limits<double>::max();
430 double ymax = -std::numeric_limits<double>::max();
431 double zmax = -std::numeric_limits<double>::max();
433 const double *x =
mX.constData();
434 const double *y =
mY.constData();
435 const double *z =
is3D() ?
mZ.constData() :
nullptr;
436 bool foundPointInRectangle =
false;
437 for (
int i = 0; i < nb; ++i )
439 const double px = *x++;
440 xmin = std::min( xmin, px );
441 xmax = std::max( xmax, px );
442 const double py = *y++;
443 ymin = std::min( ymin, py );
444 ymax = std::max( ymax, py );
447 const double pz = *z++;
448 zmin = std::min( zmin, pz );
449 zmax = std::max( zmax, pz );
452 if ( !foundPointInRectangle && rectangle.
contains( px, py ) )
454 foundPointInRectangle =
true;
470 if ( foundPointInRectangle )
495 const int nb =
mX.size();
505 || box3d.
contains(
mX.at(
static_cast< int >( nb * 0.2 ) ),
mY.at(
static_cast< int >( nb * 0.2 ) ),
mZ.at(
static_cast< int >( nb * 0.2 ) ) )
506 || box3d.
contains(
mX.at(
static_cast< int >( nb * 0.4 ) ),
mY.at(
static_cast< int >( nb * 0.4 ) ),
mZ.at(
static_cast< int >( nb * 0.4 ) ) )
507 || box3d.
contains(
mX.at(
static_cast< int >( nb * 0.6 ) ),
mY.at(
static_cast< int >( nb * 0.6 ) ),
mZ.at(
static_cast< int >( nb * 0.6 ) ) )
508 || box3d.
contains(
mX.at(
static_cast< int >( nb * 0.8 ) ),
mY.at(
static_cast< int >( nb * 0.8 ) ),
mZ.at(
static_cast< int >( nb * 0.8 ) ) )
509 || box3d.
contains(
mX.at( nb - 1 ),
mY.at( nb - 1 ),
mZ.at( nb - 1 ) ) )
518 double xmin = std::numeric_limits<double>::max();
519 double ymin = std::numeric_limits<double>::max();
520 double zmin = std::numeric_limits<double>::max();
521 double xmax = -std::numeric_limits<double>::max();
522 double ymax = -std::numeric_limits<double>::max();
523 double zmax = -std::numeric_limits<double>::max();
525 const double *x =
mX.constData();
526 const double *y =
mY.constData();
527 const double *z =
mZ.constData();
528 bool foundPointInBox =
false;
529 for (
int i = 0; i < nb; ++i )
531 const double px = *x++;
532 xmin = std::min( xmin, px );
533 xmax = std::max( xmax, px );
534 const double py = *y++;
535 ymin = std::min( ymin, py );
536 ymax = std::max( ymax, py );
537 const double pz = *z++;
538 zmin = std::min( zmin, pz );
539 zmax = std::max( zmax, pz );
541 if ( !foundPointInBox && box3d.
contains( px, py, pz ) )
543 foundPointInBox =
true;
559 if ( foundPointInBox )
572 QVector< QgsVertexId > res;
573 if (
mX.count() <= 1 )
576 const double *x =
mX.constData();
577 const double *y =
mY.constData();
579 bool useZ = hasZ && useZValues;
580 const double *z = useZ ?
mZ.constData() :
nullptr;
584 double prevZ = z ? *z++ : 0;
587 for (
int i = 1; i <
mX.count(); ++i )
589 double currentX = *x++;
590 double currentY = *y++;
591 double currentZ = useZ ? *z++ : 0;
609 const int nb =
mX.size();
612 const double *x =
mX.constData();
613 const double *y =
mY.constData();
614 QPointF *dest =
points.data();
615 for (
int i = 0; i < nb; ++i )
617 *dest++ = QPointF( *x++, *y++ );
623void simplifySection(
int i,
int j,
const double *x,
const double *y, std::vector< bool > &usePoint,
const double distanceToleranceSquared,
const double epsilon )
630 double maxDistanceSquared = -1.0;
635 for (
int k = i + 1; k < j; k++ )
639 if ( distanceSquared > maxDistanceSquared )
641 maxDistanceSquared = distanceSquared;
645 if ( maxDistanceSquared <= distanceToleranceSquared )
647 for (
int k = i + 1; k < j; k++ )
654 simplifySection( i, maxIndex, x, y, usePoint, distanceToleranceSquared, epsilon );
655 simplifySection( maxIndex, j, x, y, usePoint, distanceToleranceSquared, epsilon );
668 const double distanceToleranceSquared = tolerance * tolerance;
669 const double *
xData =
mX.constData();
670 const double *
yData =
mY.constData();
671 const double *
zData =
mZ.constData();
672 const double *
mData =
mM.constData();
674 const int size =
mX.size();
676 std::vector< bool > usePoint( size,
true );
678 constexpr double epsilon = 4 * std::numeric_limits<double>::epsilon();
681 QVector< double > newX;
682 newX.reserve( size );
683 QVector< double > newY;
684 newY.reserve( size );
686 const bool hasZ =
is3D();
688 QVector< double > newZ;
690 newZ.reserve( size );
691 QVector< double > newM;
693 newM.reserve( size );
695 for (
int i = 0, n = size; i < n; ++i )
697 if ( usePoint[i] || i == n - 1 )
699 newX.append(
xData[i] );
700 newY.append(
yData[i] );
702 newZ.append(
zData[i] );
704 newM.append(
mData[i] );
708 const bool simplifyRing =
isRing();
709 const int newSize = newX.size();
710 if ( simplifyRing && newSize > 3 )
715 if ( distanceSquared <= distanceToleranceSquared )
718 newX.last() = newX.first();
720 newY.last() = newY.first();
724 newZ.last() = newZ.first();
729 newM.last() = newM.first();
744 auto result2D = std::minmax_element(
mX.begin(),
mX.end() );
745 const double xmin = *result2D.first;
746 const double xmax = *result2D.second;
747 result2D = std::minmax_element(
mY.begin(),
mY.end() );
748 const double ymin = *result2D.first;
749 const double ymax = *result2D.second;
751 double zmin = std::numeric_limits< double >::quiet_NaN();
752 double zmax = std::numeric_limits< double >::quiet_NaN();
756 auto resultZ = std::minmax_element(
mZ.begin(),
mZ.end() );
757 zmin = *resultZ.first;
758 zmax = *resultZ.second;
761 return QgsBox3D( xmin, ymin, zmin, xmax, ymax, zmax );
779 QDomElement elemLineString = doc.createElementNS( ns, u
"LineString"_s );
782 return elemLineString;
786 return elemLineString;
794 QDomElement elemLineString = doc.createElementNS( ns, u
"LineString"_s );
797 return elemLineString;
800 return elemLineString;
815 kml.append(
"<LinearRing>"_L1 );
819 kml.append(
"<LineString>"_L1 );
822 kml.append(
"<altitudeMode>"_L1 );
825 kml.append(
"absolute"_L1 );
829 kml.append(
"clampToGround"_L1 );
831 kml.append(
"</altitudeMode>"_L1 );
832 kml.append(
"<coordinates>"_L1 );
834 int nPoints =
mX.size();
835 for (
int i = 0; i < nPoints; ++i )
839 kml.append(
" "_L1 );
842 kml.append(
","_L1 );
846 kml.append(
","_L1 );
851 kml.append(
",0"_L1 );
854 kml.append(
"</coordinates>"_L1 );
857 kml.append(
"</LinearRing>"_L1 );
861 kml.append(
"</LineString>"_L1 );
875 const int size =
mX.size();
879 const double *x =
mX.constData();
880 const double *y =
mY.constData();
886 for (
int i = 1; i < size; ++i )
890 total += std::sqrt( dx * dx + dy * dy );
900 QVector< double > x1, y1, z1, m1;
901 QVector< double > x2, y2, z2, m2;
904 std::unique_ptr< QgsLineString > first;
905 if ( x1.isEmpty() || ( x1.size() < 2 && x2.size() >= 2 ) )
906 first = std::make_unique< QgsLineString >();
908 first = std::make_unique< QgsLineString >( x1, y1, z1, m1 );
910 std::unique_ptr< QgsLineString > second;
911 if ( x2.isEmpty() || x2.size() < 2 )
912 second = std::make_unique< QgsLineString >();
914 second = std::make_unique< QgsLineString >( x2, y2, z2, m2 );
916 return std::make_tuple( std::move( first ), std::move( second ) );
921 const double *allPointsX =
xData();
922 const double *allPointsY =
yData();
924 QVector<double> partX;
925 QVector<double> partY;
926 QSet<QgsPointXY> partPointSet;
928 QVector<QgsLineString *> disjointParts;
929 for (
size_t i = 0; i < allPointsCount; i++ )
931 const QgsPointXY point( *allPointsX++, *allPointsY++ );
932 if ( partPointSet.contains( point ) )
936 disjointParts.push_back(
new QgsLineString( partX, partY ) );
938 partX = { partX.last() };
939 partY = { partY.last() };
940 partPointSet = {
QgsPointXY( partX[0], partY[0] ) };
942 partX.push_back( point.
x() );
943 partY.push_back( point.
y() );
944 partPointSet.insert( point );
947 if ( partX.size() > 1 || disjointParts.size() == 0 )
948 disjointParts.push_back(
new QgsLineString( partX, partY ) );
950 return disjointParts;
958 const int size =
mX.size();
962 const double *x =
mX.constData();
963 const double *y =
mY.constData();
964 const double *z =
mZ.constData();
971 for (
int i = 1; i < size; ++i )
976 total += std::sqrt( dx * dx + dy * dy + dz * dz );
998 Q_UNUSED( tolerance )
999 Q_UNUSED( toleranceType )
1019 const bool hasZ =
static_cast< bool >( z );
1020 const bool hasM =
static_cast< bool >( m );
1041 double *destX =
mX.data();
1042 double *destY =
mY.data();
1043 double *destZ =
nullptr;
1053 double *destM =
nullptr;
1064 for (
size_t i = 0; i < size; ++i )
1091 const double distance,
const std::function<
bool(
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double,
double )> &visitPoint
1097 double distanceTraversed = 0;
1099 if ( totalPoints == 0 )
1102 const double *x =
mX.constData();
1103 const double *y =
mY.constData();
1104 const double *z =
is3D() ?
mZ.constData() :
nullptr;
1105 const double *m =
isMeasure() ?
mM.constData() :
nullptr;
1107 double prevX = *x++;
1108 double prevY = *y++;
1109 double prevZ = z ? *z++ : 0.0;
1110 double prevM = m ? *m++ : 0.0;
1114 visitPoint( prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM );
1118 double pZ = std::numeric_limits<double>::quiet_NaN();
1119 double pM = std::numeric_limits<double>::quiet_NaN();
1120 double nextPointDistance = distance;
1121 const double eps = 4 * nextPointDistance * std::numeric_limits<double>::epsilon();
1122 for (
int i = 1; i < totalPoints; ++i )
1124 double thisX = *x++;
1125 double thisY = *y++;
1126 double thisZ = z ? *z++ : 0.0;
1127 double thisM = m ? *m++ : 0.0;
1133 const double distanceToPoint = std::min( nextPointDistance - distanceTraversed,
segmentLength );
1136 pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY, z ? &prevZ :
nullptr, z ? &thisZ :
nullptr, z ? &pZ :
nullptr, m ? &prevM :
nullptr, m ? &thisM :
nullptr, m ? &pM :
nullptr );
1138 if ( !visitPoint( pX, pY, pZ, pM, prevX, prevY, prevZ, prevM, thisX, thisY, thisZ, thisM ) )
1141 nextPointDistance += distance;
1163 std::unique_ptr< QgsPoint > res;
1164 visitPointsByRegularDistance( distance, [&](
double x,
double y,
double z,
double m,
double,
double,
double,
double,
double,
double,
double,
double ) ->
bool {
1165 res = std::make_unique< QgsPoint >( pointType, x, y, z, m );
1168 return res.release();
1173 return lineLocatePointByMPrivate( m, x, y, z, distanceFromStart, use3DDistance,
false );
1176bool QgsLineString::lineLocatePointByMPrivate(
double m,
double &x,
double &y,
double &z,
double &distanceFromStart,
bool use3DDistance,
bool haveInterpolatedM )
const
1181 distanceFromStart = 0;
1183 if ( totalPoints == 0 )
1186 const double *
xData =
mX.constData();
1187 const double *
yData =
mY.constData();
1188 const double *
mData =
mM.constData();
1190 const double *
zData =
is3D() ?
mZ.constData() :
nullptr;
1191 use3DDistance &=
static_cast< bool >(
zData );
1193 double prevX = *
xData++;
1194 double prevY = *
yData++;
1196 double prevM = *
mData++;
1199 while ( i < totalPoints )
1201 double thisX = *
xData++;
1202 double thisY = *
yData++;
1204 double thisM = *
mData++;
1207 if ( std::isnan( thisM ) )
1209 if ( haveInterpolatedM )
1213 std::unique_ptr< QgsLineString > interpolatedM(
interpolateM( use3DDistance ) );
1214 return interpolatedM->lineLocatePointByMPrivate( m, x, y, z, distanceFromStart, use3DDistance,
true );
1226 double totalLengthOfSegmentsWithConstantM = 0;
1227 for (
int j = 0; j < ( totalPoints - i ); ++j )
1237 distanceFromStart += totalLengthOfSegmentsWithConstantM / 2;
1247 const double delta = ( m - prevM ) / ( thisM - prevM );
1252 z = prevZ + ( thisZ - prevZ ) * delta;
1253 distanceFromStart += distanceToPoint;
1270 if ( startDistance < 0 && endDistance < 0 )
1273 endDistance = std::max( startDistance, endDistance );
1276 if ( totalPoints == 0 )
1279 QVector< QgsPoint > substringPoints;
1280 substringPoints.reserve( totalPoints );
1288 const double *x =
mX.constData();
1289 const double *y =
mY.constData();
1290 const double *z =
is3D() ?
mZ.constData() :
nullptr;
1291 const double *m =
isMeasure() ?
mM.constData() :
nullptr;
1293 double distanceTraversed = 0;
1294 double prevX = *x++;
1295 double prevY = *y++;
1296 double prevZ = z ? *z++ : 0.0;
1297 double prevM = m ? *m++ : 0.0;
1298 bool foundStart =
false;
1300 if ( startDistance < 0 )
1303 for (
int i = 1; i < totalPoints; ++i )
1305 double thisX = *x++;
1306 double thisY = *y++;
1307 double thisZ = z ? *z++ : 0.0;
1308 double thisM = m ? *m++ : 0.0;
1312 if ( distanceTraversed <= startDistance && startDistance < distanceTraversed +
segmentLength )
1315 const double distanceToStart = startDistance - distanceTraversed;
1316 double startX, startY;
1320 pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToStart, startX, startY, z ? &prevZ :
nullptr, z ? &thisZ :
nullptr, z ? &startZ :
nullptr, m ? &prevM :
nullptr, m ? &thisM :
nullptr, m ? &startM :
nullptr );
1321 substringPoints <<
QgsPoint( pointType, startX, startY, startZ, startM );
1324 if ( foundStart && ( distanceTraversed +
segmentLength > endDistance ) )
1327 const double distanceToEnd = endDistance - distanceTraversed;
1332 pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToEnd, endX, endY, z ? &prevZ :
nullptr, z ? &thisZ :
nullptr, z ? &endZ :
nullptr, m ? &prevM :
nullptr, m ? &thisM :
nullptr, m ? &endM :
nullptr );
1333 substringPoints <<
QgsPoint( pointType, endX, endY, endZ, endM );
1335 else if ( foundStart )
1337 substringPoints <<
QgsPoint( pointType, thisX, thisY, thisZ, thisM );
1345 if ( distanceTraversed >= endDistance )
1350 if ( !foundStart &&
qgsDoubleNear( distanceTraversed, startDistance ) )
1352 substringPoints <<
QgsPoint( pointType, prevX, prevY, prevZ, prevM ) <<
QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1377 if ( path.isEmpty() || path.currentPosition() != QPointF(
mX.at( 0 ),
mY.at( 0 ) ) )
1379 path.moveTo(
mX.at( 0 ),
mY.at( 0 ) );
1382 for (
int i = 1; i < nPoints; ++i )
1384 path.lineTo(
mX.at( i ),
mY.at( i ) );
1397 return compoundCurve;
1402 if (
mX.size() < 2 ||
mY.size() < 2 )
1405 const bool extendStart = startDistance > 0;
1406 const bool extendEnd = endDistance > 0;
1411 const double currentLen = std::sqrt( std::pow(
mX.at( 0 ) -
mX.at( 1 ), 2 ) + std::pow(
mY.at( 0 ) -
mY.at( 1 ), 2 ) );
1412 const double newLen = currentLen + startDistance;
1413 mX[0] =
mX.at( 1 ) + (
mX.at( 0 ) -
mX.at( 1 ) ) / currentLen * newLen;
1414 mY[0] =
mY.at( 1 ) + (
mY.at( 0 ) -
mY.at( 1 ) ) / currentLen * newLen;
1419 const int last =
mX.size() - 1;
1420 const double currentLen = std::sqrt( std::pow(
mX.at( last ) -
mX.at( last - 1 ), 2 ) + std::pow(
mY.at( last ) -
mY.at( last - 1 ), 2 ) );
1421 const double newLen = currentLen + endDistance;
1422 mX[last] =
mX.at( last - 1 ) + (
mX.at( last ) -
mX.at( last - 1 ) ) / currentLen * newLen;
1423 mY[last] =
mY.at( last - 1 ) + (
mY.at( last ) -
mY.at( last - 1 ) ) / currentLen * newLen;
1426 if ( extendStart || extendEnd )
1432 auto result = std::make_unique< QgsLineString >();
1434 return result.release();
1439 return u
"LineString"_s;
1460 mX.insert( position.
vertex, vertex.
x() );
1461 mY.insert( position.
vertex, vertex.
y() );
1464 mZ.insert( position.
vertex, vertex.
z() );
1468 mM.insert( position.
vertex, vertex.
m() );
1503 if ( positions.isEmpty() )
1508 QList<QgsVertexId>
vertices( positions.begin(), positions.end() );
1528 mX.remove( position.vertex );
1529 mY.remove( position.vertex );
1532 mZ.remove( position.vertex );
1536 mM.remove( position.vertex );
1557 mX.append( pt.
x() );
1558 mY.append( pt.
y() );
1561 mZ.append( pt.
z() );
1565 mM.append( pt.
m() );
1572 double sqrDist = std::numeric_limits<double>::max();
1573 double leftOfDist = std::numeric_limits<double>::max();
1575 double prevLeftOfX = 0.0;
1576 double prevLeftOfY = 0.0;
1577 double testDist = 0;
1578 double segmentPtX, segmentPtY;
1583 const int size =
mX.size();
1584 if ( size == 0 || size == 1 )
1590 const double *
xData =
mX.constData();
1591 const double *
yData =
mY.constData();
1592 for (
int i = 1; i < size; ++i )
1594 double prevX =
xData[i - 1];
1595 double prevY =
yData[i - 1];
1596 double currentX =
xData[i];
1597 double currentY =
yData[i];
1599 if ( testDist < sqrDist )
1602 segmentPt.
setX( segmentPtX );
1603 segmentPt.
setY( segmentPtY );
1604 vertexAfter.
part = 0;
1605 vertexAfter.
ring = 0;
1616 if (
qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
1628 prevLeftOf = *leftOf;
1629 leftOfDist = testDist;
1630 prevLeftOfX = prevX;
1631 prevLeftOfY = prevY;
1633 else if ( testDist < leftOfDist )
1636 leftOfDist = testDist;
1670 double totalLineLength = 0.0;
1671 double prevX =
mX.at( 0 );
1672 double prevY =
mY.at( 0 );
1678 double currentX =
mX.at( i );
1679 double currentY =
mY.at( i );
1680 double segmentLength = std::sqrt( std::pow( currentX - prevX, 2.0 ) + std::pow( currentY - prevY, 2.0 ) );
1696 return QgsPoint( sumX / totalLineLength, sumY / totalLineLength );
1714 const int maxIndex =
mX.size();
1721 const double *x =
mX.constData();
1722 const double *y =
mY.constData();
1723 double prevX = *x++;
1724 double prevY = *y++;
1725 for (
int i = 1; i < maxIndex; ++i )
1727 mSummedUpArea += prevX * ( *y - prevY ) - prevY * ( *x - prevX );
1775 if ( planeNormal.
z() < 0 )
1777 planeNormal = -planeNormal;
1782 if ( planeNormal.
y() < 0 )
1783 planeNormal = -planeNormal;
1787 if ( planeNormal.
x() < 0 )
1788 planeNormal = -planeNormal;
1792 const double *x =
mX.constData();
1793 const double *y =
mY.constData();
1794 const double *z =
mZ.constData();
1796 double prevX = *x++;
1797 double prevY = *y++;
1798 double prevZ = *z++;
1800 double normalX = 0.0;
1801 double normalY = 0.0;
1802 double normalZ = 0.0;
1804 for (
unsigned int i = 1; i <
mX.size(); ++i )
1806 normalX += prevY * ( *z - prevZ ) - prevZ * ( *y - prevY );
1807 normalY += prevZ * ( *x - prevX ) - prevX * ( *z - prevZ );
1808 normalZ += prevX * ( *y - prevY ) - prevY * ( *x - prevX );
1815 mSummedUpArea3D = 0.5 * ( normalX * planeNormal.
x() + normalY * planeNormal.
y() + normalZ * planeNormal.
z() );
1838 if (
mX.count() < 2 )
1850 double currentX =
mX.at( 0 );
1851 double currentY =
mY.at( 0 );
1852 double afterX =
mX.at( 1 );
1853 double afterY =
mY.at( 1 );
1856 else if ( vertex.
vertex == 0 )
1869 double previousX =
mX.at( vertex.
vertex - 1 );
1870 double previousY =
mY.at( vertex.
vertex - 1 );
1871 double currentX =
mX.at( vertex.
vertex );
1872 double currentY =
mY.at( vertex.
vertex );
1873 double afterX =
mX.at( vertex.
vertex + 1 );
1874 double afterY =
mY.at( vertex.
vertex + 1 );
1881 if ( startVertex.
vertex < 0 || startVertex.
vertex >=
mX.count() - 1 )
1886 return std::sqrt( dx * dx + dy * dy );
1905 addZValue( std::numeric_limits<double>::quiet_NaN() );
1918 std::unique_ptr< QgsLineString > cloned(
clone() );
1925 if (
isEmpty() || ( nbpoints < 2 ) )
1930 const double range = end - start;
1931 double lineLength =
length();
1932 double lengthSoFar = 0.0;
1935 double *mOut = cloned->mM.data();
1937 for (
int i = 1; i < nbpoints; ++i )
1940 if ( lineLength > 0.0 )
1941 *mOut++ = start + range * lengthSoFar / lineLength;
1942 else if ( lineLength == 0.0 && nbpoints > 1 )
1943 *mOut++ = start + range * i / ( nbpoints - 1 );
1957 if ( totalPoints < 2 )
1958 return std::unique_ptr< QgsLineString >(
clone() );
1960 const double *
xData =
mX.constData();
1961 const double *
yData =
mY.constData();
1962 const double *
mData =
mM.constData();
1963 const double *
zData =
is3D() ?
mZ.constData() :
nullptr;
1964 use3DDistance &=
static_cast< bool >(
zData );
1966 QVector< double > xOut( totalPoints );
1967 QVector< double > yOut( totalPoints );
1968 QVector< double > mOut( totalPoints );
1969 QVector< double > zOut(
static_cast< bool >(
zData ) ? totalPoints : 0 );
1971 double *xOutData = xOut.data();
1972 double *yOutData = yOut.data();
1973 double *mOutData = mOut.data();
1974 double *zOutData =
static_cast< bool >(
zData ) ? zOut.data() :
nullptr;
1977 double currentSegmentLength = 0;
1978 double lastValidM = std::numeric_limits< double >::quiet_NaN();
1979 double prevX = *
xData;
1980 double prevY = *
yData;
1982 while ( i < totalPoints )
1984 double thisX = *
xData++;
1985 double thisY = *
yData++;
1987 double thisM = *
mData++;
1991 if ( !std::isnan( thisM ) )
1993 *xOutData++ = thisX;
1994 *yOutData++ = thisY;
1995 *mOutData++ = thisM;
1997 *zOutData++ = thisZ;
2004 double scanAheadM = thisM;
2005 while ( i + j + 1 < totalPoints && std::isnan( scanAheadM ) )
2007 scanAheadM =
mData[j];
2010 if ( std::isnan( scanAheadM ) )
2015 *xOutData++ = thisX;
2016 *yOutData++ = thisY;
2017 *mOutData++ = scanAheadM;
2019 *zOutData++ = thisZ;
2020 for ( ; i < j; ++i )
2024 *xOutData++ = thisX;
2025 *yOutData++ = thisY;
2026 *mOutData++ = scanAheadM;
2029 *zOutData++ = *
zData++;
2031 lastValidM = scanAheadM;
2037 double scanAheadX = thisX;
2038 double scanAheadY = thisY;
2039 double scanAheadZ = thisZ;
2040 double distanceToNextValidM = currentSegmentLength;
2041 std::vector< double > scanAheadSegmentLengths;
2042 scanAheadSegmentLengths.emplace_back( currentSegmentLength );
2044 double nextValidM = std::numeric_limits< double >::quiet_NaN();
2045 while ( i + j < totalPoints - 1 )
2047 double nextScanAheadX =
xData[j];
2048 double nextScanAheadY =
yData[j];
2049 double nextScanAheadZ =
zData ?
zData[j] : 0;
2050 double nextScanAheadM =
mData[j];
2051 const double scanAheadSegmentLength = use3DDistance ?
QgsGeometryUtilsBase::distance3D( scanAheadX, scanAheadY, scanAheadZ, nextScanAheadX, nextScanAheadY, nextScanAheadZ )
2053 scanAheadSegmentLengths.emplace_back( scanAheadSegmentLength );
2054 distanceToNextValidM += scanAheadSegmentLength;
2056 if ( !std::isnan( nextScanAheadM ) )
2058 nextValidM = nextScanAheadM;
2062 scanAheadX = nextScanAheadX;
2063 scanAheadY = nextScanAheadY;
2064 scanAheadZ = nextScanAheadZ;
2068 if ( std::isnan( nextValidM ) )
2071 *xOutData++ = thisX;
2072 *yOutData++ = thisY;
2073 *mOutData++ = lastValidM;
2075 *zOutData++ = thisZ;
2077 for ( ; i < totalPoints; ++i )
2079 *xOutData++ = *
xData++;
2080 *yOutData++ = *
yData++;
2081 *mOutData++ = lastValidM;
2083 *zOutData++ = *
zData++;
2090 const double delta = ( nextValidM - lastValidM ) / distanceToNextValidM;
2091 *xOutData++ = thisX;
2092 *yOutData++ = thisY;
2093 *mOutData++ = lastValidM + delta * scanAheadSegmentLengths[0];
2094 double totalScanAheadLength = scanAheadSegmentLengths[0];
2096 *zOutData++ = thisZ;
2097 for (
int k = 1; k <= j; ++i, ++k )
2101 *xOutData++ = thisX;
2102 *yOutData++ = thisY;
2103 totalScanAheadLength += scanAheadSegmentLengths[k];
2104 *mOutData++ = lastValidM + delta * totalScanAheadLength;
2107 *zOutData++ = *
zData++;
2109 lastValidM = nextValidM;
2118 return std::make_unique< QgsLineString >( xOut, yOut, zOut, mOut );
2124 if ( fromVertex.
part != 0 || fromVertex.
ring != 0 || toVertex.
part != 0 || toVertex.
ring != 0 )
2127 const int fromVertexNumber = fromVertex.
vertex;
2128 const int toVertexNumber = toVertex.
vertex;
2131 if ( fromVertexNumber > toVertexNumber )
2137 if ( fromVertexNumber < 0 || fromVertexNumber >= nPoints || toVertexNumber < 0 || toVertexNumber >= nPoints )
2140 if ( fromVertexNumber == toVertexNumber )
2143 const bool is3DGeometry =
is3D();
2144 const double *
xData =
mX.constData();
2145 const double *
yData =
mY.constData();
2146 const double *
zData = is3DGeometry ?
mZ.constData() :
nullptr;
2147 double totalDistance = 0.0;
2150 for (
int i = fromVertexNumber; i < toVertexNumber; ++i )
2161 totalDistance += std::sqrt( dx * dx + dy * dy + dz * dz );
2164 return totalDistance;
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
VertexType
Types of vertex.
@ Segment
The actual start or end point of a segment.
WkbType
The WKB type describes the number of dimensions a geometry has.
@ LineString25D
LineString25D.
@ LineStringM
LineStringM.
@ LineStringZM
LineStringZM.
@ LineStringZ
LineStringZ.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual bool convertTo(Qgis::WkbType type)
Converts the geometry to a specified type.
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
bool isMeasure() const
Returns true if the geometry contains m values.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
AxisOrder
Axis order for GML generation.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, Qgis::WkbType baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
A 3-dimensional box composed of x, y, z coordinates.
bool contains(const QgsBox3D &other) const
Returns true when box contains other box.
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Compound curve geometry type.
void addCurve(QgsCurve *c, bool extendPrevious=false)
Adds a curve to the geometry (takes ownership).
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
bool mHasCachedSummedUpArea
virtual bool isRing() const
Returns true if the curve is a ring.
bool mHasCachedSummedUpArea3D
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
bool snapToGridPrivate(double hSpacing, double vSpacing, double dSpacing, double mSpacing, const QVector< double > &srcX, const QVector< double > &srcY, const QVector< double > &srcZ, const QVector< double > &srcM, QVector< double > &outX, QVector< double > &outY, QVector< double > &outZ, QVector< double > &outM, bool removeRedundantPoints) const
Helper function for QgsCurve subclasses to snap to grids.
QgsBox3D mBoundingBox
Cached bounding box.
bool hasVertex(QgsVertexId position) const override
Returns true if the geometry contains a vertex matching the given position.
Convenience functions for geometry utils.
static void pointOnLineWithDistance(double x1, double y1, double x2, double y2, double distance, double &x, double &y, double *z1=nullptr, double *z2=nullptr, double *z=nullptr, double *m1=nullptr, double *m2=nullptr, double *m=nullptr)
Calculates the point a specified distance from (x1, y1) toward a second point (x2,...
static double distance2D(double x1, double y1, double x2, double y2)
Returns the 2D distance between (x1, y1) and (x2, y2).
static double lineAngle(double x1, double y1, double x2, double y2)
Calculates the direction of line joining two points in radians, clockwise from the north direction.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1,...
static double distance3D(double x1, double y1, double z1, double x2, double y2, double z2)
Returns the 3D distance between (x1, y1, z1) and (x2, y2, z2).
static void interpolatePointOnCubicBezier(double p0x, double p0y, double p0z, double p0m, double p1x, double p1y, double p1z, double p1m, double p2x, double p2y, double p2z, double p2m, double p3x, double p3y, double p3z, double p3m, double t, bool hasZ, bool hasM, double &outX, double &outY, double &outZ, double &outM)
Evaluates a point on a cubic Bézier curve defined by four control points.
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon)
Returns the squared distance between a point and a line.
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2).
static json pointsToJson(const QgsPointSequence &points, int precision)
Returns coordinates as json object.
static QDomElement pointsToGML2(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::coordinates DOM element.
static bool checkWeaklyFor3DPlane(const QgsAbstractGeometry *geom, QgsPoint &pt1, QgsPoint &pt2, QgsPoint &pt3, double epsilon=std::numeric_limits< double >::epsilon())
Checks if a 3D geometry has a plane defined by at least 3 non-collinear points.
static QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::posList DOM element.
Represents a single 2D line segment, consisting of a 2D start and end vertex only.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
static std::unique_ptr< QgsLineString > fromBezierCurve(const QgsPoint &start, const QgsPoint &controlPoint1, const QgsPoint &controlPoint2, const QgsPoint &end, int segments=30)
Returns a new linestring created by segmentizing the bezier curve between start and end,...
bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const override
Returns the point and vertex type of a point within the curve.
bool isClosed() const override
Returns true if the curve is closed.
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
QVector< QgsLineString * > splitToDisjointXYParts() const
Divides the linestring into parts that don't share any points or lines.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
double length3D() const
Returns the length in 3D world of the line string.
static std::unique_ptr< QgsLineString > fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
QgsLineString * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
void sumUpArea(double &sum) const override
Calculates the shoelace/triangle formula sum for the points in the linestring.
void clear() override
Clears the geometry, ie reset it to a null 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.
void sumUpArea3D(double &sum) const override
Calculates the shoelace/triangle formula sum for the points in the linestring.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
QgsLineString()
Constructor for an empty linestring geometry.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
std::unique_ptr< QgsLineString > interpolateM(bool use3DDistance=true) const
Returns a copy of this line with all missing (NaN) m values interpolated from m values of surrounding...
void setPoints(size_t size, const double *x, const double *y, const double *z=nullptr, const double *m=nullptr)
Resets the line string to match the specified point data.
QgsPoint centroid() const override
Returns the centroid 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.
QPolygonF asQPolygonF() const override
Returns a QPolygonF representing the points.
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 boundingBoxIntersects(const QgsRectangle &rectangle) const override
Returns true if the bounding box of this geometry intersects with a rectangle.
bool deleteVertices(const QSet< QgsVertexId > &positions) override
Deletes vertices within the geometry.
QString geometryType() const override
Returns a unique string representing the geometry type.
std::unique_ptr< QgsLineString > measuredLine(double start, double end) const
Re-write the measure ordinate (or add one, if it isn't already there) interpolating the measure betwe...
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.
QgsLineString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
void close()
Closes the line string by appending the first point to the end of the line, if it is not already clos...
int indexOf(const QgsPoint &point) const final
Returns the index of the first vertex matching the given point, or -1 if a matching vertex is not fou...
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
void extend(double startDistance, double endDistance)
Extends the line geometry by extrapolating out the start or end of the line by a specified distance.
QgsCompoundCurve * toCurveType() const override
Returns the geometry converted to the more generic curve type QgsCompoundCurve.
double distanceBetweenVertices(QgsVertexId fromVertex, QgsVertexId toVertex) const override
Returns the distance along the curve between two vertices.
QgsLineString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
QgsBox3D calculateBoundingBox3D() const override
Calculates the minimal 3D bounding box for the geometry.
std::tuple< std::unique_ptr< QgsCurve >, std::unique_ptr< QgsCurve > > splitCurveAtVertex(int index) const final
Splits the curve at the specified vertex index, returning two curves which represent the portion of t...
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
void visitPointsByRegularDistance(double distance, const std::function< bool(double x, double y, double z, double m, double startSegmentX, double startSegmentY, double startSegmentZ, double startSegmentM, double endSegmentX, double endSegmentY, double endSegmentZ, double endSegmentM) > &visitPoint) const
Visits regular points along the linestring, spaced by distance.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
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.
QVector< QgsVertexId > collectDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) const
Returns a list of any duplicate nodes contained in the geometry, within the specified tolerance.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Q_DECL_DEPRECATED QgsBox3D calculateBoundingBox3d() const
Calculates the minimal 3D bounding box for the geometry.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
QgsLineString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
bool convertTo(Qgis::WkbType type) override
Converts the geometry to a specified type.
bool isClosed2D() const override
Returns true if the curve is closed.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
QgsLineString * 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.
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.
A rectangle specified with double values.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
bool dropMValue() override
Drops any measure values which exist in the geometry.
int numPoints() const override
Returns the number of points in the curve.
bool isEmpty() const override
Returns true if the geometry is empty.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the simple curve does not have m values.
void splitCurveAtVertexProtected(int index, QVector< double > &x1, QVector< double > &y1, QVector< double > &z1, QVector< double > &m1, QVector< double > &x2, QVector< double > &y2, QVector< double > &z2, QVector< double > &m2) const
Returns coordinate vectors for the split curves.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
void clear() override
Clears the geometry, ie reset it to a null geometry.
const double * yData() const
Returns a const pointer to the y vertex data.
QgsPoint startPoint() const override
Returns the starting point of the curve.
QgsPoint pointN(int i) const
Returns the specified point from inside the simple curve.
QgsSimpleCurve * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
const double * xData() const
Returns a const pointer to the x vertex data.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the simple curve does not have z values.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
void normalize()
Normalizes the current vector in place.
static QgsVector3D crossProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the cross product of two vectors.
static Qgis::WkbType zmType(Qgis::WkbType type, bool hasZ, bool hasM)
Returns the modified input geometry type according to hasZ / hasM.
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.
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).
QVector< QgsPoint > QgsPointSequence
void simplifySection(int i, int j, const double *x, const double *y, std::vector< bool > &usePoint, const double distanceToleranceSquared, const double epsilon)
QLineF segment(int index, QRectF rect, double radius)
double distance2D(const QgsPolylineXY &coords)
Utility class for identifying a unique vertex within a geometry.