28 #include <QJsonObject>
30 #include <QPainterPath>
32 #include <nlohmann/json.hpp>
42 bool hasZ = p1.
is3D();
87 if ( mX.count() != otherLine->mX.count() )
90 for (
int i = 0; i < mX.count(); ++i )
108 auto result = qgis::make_unique< QgsCircularString >();
110 return result.release();
115 return QStringLiteral(
"CircularString" );
142 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
146 bbox = segmentBoundingBox(
QgsPoint( mX[i], mY[i] ),
QgsPoint( mX[i + 1], mY[i + 1] ),
QgsPoint( mX[i + 2], mY[i + 2] ) );
155 if ( nPoints > 0 && nPoints % 2 == 0 )
168 double centerX, centerY, radius;
177 bbox.combineExtentWith( pt3.
x(), pt3.
y() );
179 QgsPointSequence compassPoints = compassPointsOnSegment( p1Angle, p2Angle, p3Angle, centerX, centerY, radius );
180 QgsPointSequence::const_iterator cpIt = compassPoints.constBegin();
181 for ( ; cpIt != compassPoints.constEnd(); ++cpIt )
183 bbox.combineExtentWith( cpIt->x(), cpIt->y() );
188 QgsPointSequence QgsCircularString::compassPointsOnSegment(
double p1Angle,
double p2Angle,
double p3Angle,
double centerX,
double centerY,
double radius )
192 QgsPoint nPoint( centerX, centerY + radius );
193 QgsPoint ePoint( centerX + radius, centerY );
194 QgsPoint sPoint( centerX, centerY - radius );
195 QgsPoint wPoint( centerX - radius, centerY );
197 if ( p3Angle >= p1Angle )
199 if ( p2Angle > p1Angle && p2Angle < p3Angle )
201 if ( p1Angle <= 90 && p3Angle >= 90 )
203 pointList.append( nPoint );
205 if ( p1Angle <= 180 && p3Angle >= 180 )
207 pointList.append( wPoint );
209 if ( p1Angle <= 270 && p3Angle >= 270 )
211 pointList.append( sPoint );
216 pointList.append( ePoint );
217 if ( p1Angle >= 90 || p3Angle <= 90 )
219 pointList.append( nPoint );
221 if ( p1Angle >= 180 || p3Angle <= 180 )
223 pointList.append( wPoint );
225 if ( p1Angle >= 270 || p3Angle <= 270 )
227 pointList.append( sPoint );
233 if ( p2Angle < p1Angle && p2Angle > p3Angle )
235 if ( p1Angle >= 270 && p3Angle <= 270 )
237 pointList.append( sPoint );
239 if ( p1Angle >= 180 && p3Angle <= 180 )
241 pointList.append( wPoint );
243 if ( p1Angle >= 90 && p3Angle <= 90 )
245 pointList.append( nPoint );
250 pointList.append( ePoint );
251 if ( p1Angle <= 270 || p3Angle >= 270 )
253 pointList.append( sPoint );
255 if ( p1Angle <= 180 || p3Angle >= 180 )
257 pointList.append( wPoint );
259 if ( p1Angle <= 90 || p3Angle >= 90 )
261 pointList.append( nPoint );
286 mX.resize( nVertices );
287 mY.resize( nVertices );
288 hasZ ? mZ.resize( nVertices ) : mZ.clear();
289 hasM ? mM.resize( nVertices ) : mM.clear();
290 for (
int i = 0; i < nVertices; ++i )
317 parts.second =
parts.second.remove(
'(' ).remove(
')' );
318 QString secondWithoutParentheses =
parts.second;
319 secondWithoutParentheses = secondWithoutParentheses.simplified().remove(
' ' );
320 if ( (
parts.second.compare( QLatin1String(
"EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
321 secondWithoutParentheses.isEmpty() )
334 int binarySize =
sizeof( char ) +
sizeof( quint32 ) +
sizeof( quint32 );
345 wkb << static_cast<quint32>(
wkbType() );
357 wkt += QLatin1String(
"EMPTY" );
370 std::unique_ptr< QgsLineString > line(
curveToLine() );
371 QDomElement gml = line->asGml2( doc,
precision, ns, axisOrder );
380 QDomElement elemCurve = doc.createElementNS( ns, QStringLiteral(
"Curve" ) );
385 QDomElement elemSegments = doc.createElementNS( ns, QStringLiteral(
"segments" ) );
386 QDomElement elemArcString = doc.createElementNS( ns, QStringLiteral(
"ArcString" ) );
388 elemSegments.appendChild( elemArcString );
389 elemCurve.appendChild( elemSegments );
397 std::unique_ptr< QgsLineString > line(
curveToLine() );
411 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
442 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
456 bool res =
snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
457 result->mX, result->mY, result->mZ, result->mM );
459 return result.release();
466 if ( mX.count() <= 3 )
469 double prevX = mX.at( 0 );
470 double prevY = mY.at( 0 );
472 bool useZ = hasZ && useZValues;
473 double prevZ = useZ ? mZ.at( 0 ) : 0;
475 int remaining = mX.count();
478 while ( i + 1 < remaining )
480 double currentCurveX = mX.at( i );
481 double currentCurveY = mY.at( i );
482 double currentX = mX.at( i + 1 );
483 double currentY = mY.at( i + 1 );
484 double currentZ = useZ ? mZ.at( i + 1 ) : 0;
517 return std::min( mX.size(), mY.size() );
522 if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
527 double x = mX.at( i );
528 double y = mY.at( i );
559 if ( index >= 0 && index < mX.size() )
560 return mX.at( index );
567 if ( index >= 0 && index < mY.size() )
568 return mY.at( index );
577 int size = mX.size();
579 double *srcX = mX.data();
580 double *srcY = mY.data();
581 double *srcM = hasM ? mM.data() :
nullptr;
582 double *srcZ = hasZ ? mZ.data() :
nullptr;
584 double *destX = srcX;
585 double *destY = srcY;
586 double *destM = srcM;
587 double *destZ = srcZ;
589 int filteredPoints = 0;
590 for (
int i = 0; i < size; ++i )
594 double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
595 double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
597 if ( filter(
QgsPoint( x, y, z, m ) ) )
609 mX.resize( filteredPoints );
610 mY.resize( filteredPoints );
612 mZ.resize( filteredPoints );
614 mM.resize( filteredPoints );
623 int size = mX.size();
625 double *srcX = mX.data();
626 double *srcY = mY.data();
627 double *srcM = hasM ? mM.data() :
nullptr;
628 double *srcZ = hasZ ? mZ.data() :
nullptr;
630 for (
int i = 0; i < size; ++i )
634 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
635 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
651 for (
int i = 0; i < nPts; ++i )
653 pts.push_back(
pointN( i ) );
673 bool hasZ = firstPt.
is3D();
678 mX.resize(
points.size() );
679 mY.resize(
points.size() );
682 mZ.resize(
points.size() );
690 mM.resize(
points.size() );
697 for (
int i = 0; i <
points.size(); ++i )
703 double z =
points.at( i ).z();
704 mZ[i] = std::isnan( z ) ? 0 : z;
708 double m =
points.at( i ).m();
709 mM[i] = std::isnan( m ) ? 0 : m;
725 double *zArray = mZ.data();
729 bool useDummyZ = !hasZ || !transformZ;
732 zArray =
new double[nPoints];
733 for (
int i = 0; i < nPoints; ++i )
752 for (
int i = 0; i < nPoints; ++i )
755 t.map( mX.at( i ), mY.at( i ), &x, &y );
760 mZ[i] = mZ.at( i ) * zScale + zTranslate;
764 mM[i] = mM.at( i ) * mScale + mTranslate;
777 if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
779 path.moveTo( QPointF( mX[0], mY[0] ) );
782 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
786 for (
int j = 1; j < pt.size(); ++j )
788 path.lineTo( pt.at( j ).x(), pt.at( j ).y() );
796 if ( nPoints % 2 == 0 )
798 path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
803 void QgsCircularString::arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
805 double centerX, centerY, radius;
807 radius, centerX, centerY );
812 double diameter = 2 * radius;
813 path.arcTo( centerX - radius, centerY - radius, diameter, diameter, p1Angle, sweepAngle );
824 if ( position.
vertex >= mX.size() || position.
vertex < 1 )
829 mX.insert( position.
vertex, vertex.
x() );
830 mY.insert( position.
vertex, vertex.
y() );
833 mZ.insert( position.
vertex, vertex.
z() );
837 mM.insert( position.
vertex, vertex.
m() );
840 bool vertexNrEven = ( position.
vertex % 2 == 0 );
855 if ( position.
vertex < 0 || position.
vertex >= mX.size() )
860 mX[position.
vertex] = newPos.
x();
861 mY[position.
vertex] = newPos.
y();
864 mZ[position.
vertex] = newPos.
z();
868 mM[position.
vertex] = newPos.
m();
882 if ( position.
vertex < 0 || position.
vertex > ( nVertices - 1 ) )
887 if ( position.
vertex < ( nVertices - 2 ) )
920 double minDist = std::numeric_limits<double>::max();
923 int minDistLeftOf = 0;
925 double currentDist = 0.0;
928 for (
int i = 0; i < ( nPoints - 2 ) ; i += 2 )
930 currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter,
leftOf, epsilon );
931 if ( currentDist < minDist )
933 minDist = currentDist;
934 minDistSegmentPoint = segmentPt;
943 if ( minDist == std::numeric_limits<double>::max() )
946 segmentPt = minDistSegmentPoint;
947 vertexAfter = minDistVertexAfter;
948 vertexAfter.
part = 0;
949 vertexAfter.
ring = 0;
972 for (
int i = 0; i < maxIndex; i += 2 )
975 QgsPoint p2( mX[i + 1], mY[i + 1] );
976 QgsPoint p3( mX[i + 2], mY[i + 2] );
986 sum += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
989 double midPointX = ( p1.
x() + p3.
x() ) / 2.0;
990 double midPointY = ( p1.
y() + p3.
y() ) / 2.0;
992 double radius, centerX, centerY;
996 double r2 = radius * radius;
1007 double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
1008 double circleChordArea = 0;
1009 if ( circlePointLeftOfLine == centerPointLeftOfLine )
1011 circleChordArea = M_PI * r2 * ( 1 - cov );
1015 circleChordArea = M_PI * r2 * cov;
1018 if ( !circlePointLeftOfLine )
1020 sum += circleChordArea;
1024 sum -= circleChordArea;
1034 double QgsCircularString::closestPointOnArc(
double x1,
double y1,
double x2,
double y2,
double x3,
double y3,
1037 double radius, centerX, centerY;
1062 segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1063 vertexAfter.
vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1070 segmentPt.
setX( pt.
x() );
1071 segmentPt.
setY( pt.
y() );
1077 double sqrDistancePointToCenter = ( pt.
x() - centerX ) * ( pt.
x() - centerX ) + ( pt.
y() - centerY ) * ( pt.
y() - centerY );
1078 *
leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1079 : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1085 void QgsCircularString::insertVertexBetween(
int after,
int before,
int pointOnCircle )
1087 double xAfter = mX.at( after );
1088 double yAfter = mY.at( after );
1089 double xBefore = mX.at( before );
1090 double yBefore = mY.at( before );
1091 double xOnCircle = mX.at( pointOnCircle );
1092 double yOnCircle = mY.at( pointOnCircle );
1094 double radius, centerX, centerY;
1097 double x = ( xAfter + xBefore ) / 2.0;
1098 double y = ( yAfter + yBefore ) / 2.0;
1101 mX.insert( before, newVertex.
x() );
1102 mY.insert( before, newVertex.
y() );
1106 mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1110 mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1123 int before = vId.
vertex - 1;
1125 int after = vId.
vertex + 1;
1127 if ( vId.
vertex % 2 != 0 )
1157 int vertex1 = vId.
vertex - 2;
1158 int vertex2 = vId.
vertex - 1;
1159 int vertex3 = vId.
vertex;
1161 QgsPoint( mX[vertex1], mY[vertex1] ),
QgsPoint( mX[vertex2], mY[vertex2] ),
QgsPoint( mX[vertex3], mY[vertex3] ) );
1162 int vertex4 = vId.
vertex + 1;
1163 int vertex5 = vId.
vertex + 2;
1165 QgsPoint( mX[vertex3], mY[vertex3] ),
QgsPoint( mX[vertex4], mY[vertex4] ),
QgsPoint( mX[vertex5], mY[vertex5] ) );
1174 if ( startVertex.
vertex < 0 || startVertex.
vertex >= mX.count() - 2 )
1177 if ( startVertex.
vertex % 2 == 1 )
1180 double x1 = mX.at( startVertex.
vertex );
1181 double y1 = mY.at( startVertex.
vertex );
1182 double x2 = mX.at( startVertex.
vertex + 1 );
1183 double y2 = mY.at( startVertex.
vertex + 1 );
1184 double x3 = mX.at( startVertex.
vertex + 2 );
1185 double y3 = mY.at( startVertex.
vertex + 2 );
1192 std::reverse( copy->mX.begin(), copy->mX.end() );
1193 std::reverse( copy->mY.begin(), copy->mY.end() );
1196 std::reverse( copy->mZ.begin(), copy->mZ.end() );
1200 std::reverse( copy->mM.begin(), copy->mM.end() );
1210 double distanceTraversed = 0;
1212 if ( totalPoints == 0 )
1221 const double *x = mX.constData();
1222 const double *y = mY.constData();
1223 const double *z =
is3D() ? mZ.constData() :
nullptr;
1224 const double *m =
isMeasure() ? mM.constData() :
nullptr;
1226 double prevX = *x++;
1227 double prevY = *y++;
1228 double prevZ = z ? *z++ : 0.0;
1229 double prevM = m ? *m++ : 0.0;
1233 return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1236 for (
int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1245 double z2 = z ? *z++ : 0.0;
1246 double m2 = m ? *m++ : 0.0;
1250 double z3 = z ? *z++ : 0.0;
1251 double m3 = m ? *m++ : 0.0;
1257 const double distanceToPoint = std::min( distance - distanceTraversed,
segmentLength );
1259 QgsPoint( pointType, x2, y2, z2, m2 ),
1260 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1276 if ( startDistance < 0 && endDistance < 0 )
1279 endDistance = std::max( startDistance, endDistance );
1281 double distanceTraversed = 0;
1283 if ( totalPoints == 0 )
1286 QVector< QgsPoint > substringPoints;
1294 const double *x = mX.constData();
1295 const double *y = mY.constData();
1296 const double *z =
is3D() ? mZ.constData() :
nullptr;
1297 const double *m =
isMeasure() ? mM.constData() :
nullptr;
1299 double prevX = *x++;
1300 double prevY = *y++;
1301 double prevZ = z ? *z++ : 0.0;
1302 double prevM = m ? *m++ : 0.0;
1303 bool foundStart =
false;
1305 if (
qgsDoubleNear( startDistance, 0.0 ) || startDistance < 0 )
1307 substringPoints <<
QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1311 substringPoints.reserve( totalPoints );
1313 for (
int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1322 double z2 = z ? *z++ : 0.0;
1323 double m2 = m ? *m++ : 0.0;
1327 double z3 = z ? *z++ : 0.0;
1328 double m3 = m ? *m++ : 0.0;
1330 bool addedSegmentEnd =
false;
1332 if ( distanceTraversed < startDistance && distanceTraversed + segmentLength > startDistance )
1335 const double distanceToStart = startDistance - distanceTraversed;
1337 QgsPoint( pointType, x2, y2, z2, m2 ),
1338 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
1341 const bool endPointOnSegment = distanceTraversed +
segmentLength > endDistance;
1342 if ( endPointOnSegment )
1344 const double distanceToEnd = endDistance - distanceTraversed;
1345 const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
1348 QgsPoint( pointType, x2, y2, z2, m2 ),
1349 QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1351 QgsPoint( pointType, x2, y2, z2, m2 ),
1352 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1353 addedSegmentEnd =
true;
1357 const double midPointDistance = (
segmentLength - distanceToStart ) * 0.5 + distanceToStart;
1360 QgsPoint( pointType, x2, y2, z2, m2 ),
1361 QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1362 <<
QgsPoint( pointType, x3, y3, z3, m3 );
1363 addedSegmentEnd =
true;
1367 if ( !addedSegmentEnd && foundStart && ( distanceTraversed +
segmentLength > endDistance ) )
1370 const double distanceToEnd = endDistance - distanceTraversed;
1373 QgsPoint( pointType, x2, y2, z2, m2 ),
1374 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
1377 QgsPoint( pointType, x2, y2, z2, m2 ),
1378 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1380 else if ( !addedSegmentEnd && foundStart )
1382 substringPoints <<
QgsPoint( pointType, x2, y2, z2, m2 )
1383 <<
QgsPoint( pointType, x3, y3, z3, m3 );
1387 if ( distanceTraversed > endDistance )
1396 std::unique_ptr< QgsCircularString > result = qgis::make_unique< QgsCircularString >();
1397 result->setPoints( substringPoints );
1398 return result.release();
1411 mZ.reserve( nPoints );
1412 for (
int i = 0; i < nPoints; ++i )
1429 mM.reserve( nPoints );
1430 for (
int i = 0; i < nPoints; ++i )
1463 std::swap( mX, mY );