42using namespace Qt::StringLiterals;
44#define DEFAULT_QUADRANT_SEGMENTS 8
46#define CATCH_GEOS( r ) \
47 catch ( QgsGeosException & ) \
52#define CATCH_GEOS_WITH_ERRMSG( r ) \
53 catch ( QgsGeosException & e ) \
57 *errorMsg = e.what(); \
58 if ( errorMsg->startsWith( "InterruptedException"_L1, Qt::CaseInsensitive ) ) \
68static void throwQgsGeosException(
const char *fmt, ... )
74 vsnprintf( buffer,
sizeof buffer, fmt, ap );
77 QString message = QString::fromUtf8( buffer );
87 throw QgsGeosException( message );
95 throw QgsGeosException( message );
100static void printGEOSNotice(
const char *fmt, ... )
102#if defined( QGISDEBUG )
107 vsnprintf( buffer,
sizeof buffer, fmt, ap );
118#if defined( USE_THREAD_LOCAL ) && !defined( Q_OS_WIN )
121QThreadStorage< QgsGeosContext * > QgsGeosContext::sGeosContext;
127 mContext = GEOS_init_r();
128 GEOSContext_setNoticeHandler_r( mContext, printGEOSNotice );
129 GEOSContext_setErrorHandler_r( mContext, throwQgsGeosException );
131#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
136 GEOSCurveToLineParams *curveToLineParams = GEOSCurveToLineParams_create();
137 GEOSContext_setCurveToLineParams_r( mContext, curveToLineParams );
138 GEOSCurveToLineParams_destroy( curveToLineParams );
144 GEOSLineToCurveParams *lineToCurveParams = GEOSLineToCurveParams_create();
145 GEOSContext_setLineToCurveParams_r( mContext, lineToCurveParams );
146 GEOSLineToCurveParams_destroy( lineToCurveParams );
153 GEOS_finish_r( mContext );
158#if defined( USE_THREAD_LOCAL ) && !defined( Q_OS_WIN )
159 return sGeosContext.mContext;
161 GEOSContextHandle_t gContext =
nullptr;
162 if ( sGeosContext.hasLocalData() )
164 gContext = sGeosContext.localData()->mContext;
169 gContext = sGeosContext.localData()->mContext;
203 =
new QgsSettingsEntryBool( u
"line-to-curve-param"_s,
QgsSettingsTree::sTreeGeos,
false, u
"Whether to convert any linear output of a GEOS method to a curved type, if the inputs were converted to curves."_s );
208 , mPrecision( precision )
235#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
237 throw QgsNotSupportedException( QObject::tr(
"The structured method to make geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
240 throw QgsNotSupportedException( QObject::tr(
"The keep collapsed option for making geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
244 geos.reset( GEOSMakeValid_r( context, mGeos.get() ) );
249 GEOSMakeValidParams *params = GEOSMakeValidParams_create_r( context );
254 GEOSMakeValidParams_setMethod_r( context, params, GEOS_MAKE_VALID_LINEWORK );
258 GEOSMakeValidParams_setMethod_r( context, params, GEOS_MAKE_VALID_STRUCTURE );
262 GEOSMakeValidParams_setKeepCollapsed_r( context, params, keepCollapsed ? 1 : 0 );
267 geos.reset( GEOSMakeValidWithParams_r( context, mGeos.get(), params ) );
268 GEOSMakeValidParams_destroy_r( context, params );
270 catch ( QgsGeosException &e )
274 *errorMsg = e.what();
276 GEOSMakeValidParams_destroy_r( context, params );
305 std::unique_ptr< QgsAbstractGeometry > geom =
fromGeos( newPart );
312 mGeosPrepared.reset();
325#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
329 std::unique_ptr< QgsAbstractGeometry > segmentized(
mGeometry->segmentize() );
336#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
359 return overlay( geom, OverlayIntersection, errorMsg, parameters, feedback ).release();
364 return overlay( geom, OverlayDifference, errorMsg, parameters, feedback ).release();
380 catch ( QgsGeosException &e )
385 *errorMsg = e.what();
394 int partType = GEOSGeomTypeId_r( context, currentPart );
397 if ( partType == GEOS_POINT )
408 if ( partType == GEOS_MULTILINESTRING || partType == GEOS_MULTIPOLYGON || partType == GEOS_GEOMETRYCOLLECTION )
410 int partCount = GEOSGetNumGeometries_r( context, currentPart );
411 for (
int i = 0; i < partCount; ++i )
413 subdivideRecursive( GEOSGetGeometryN_r( context, currentPart, i ), maxNodes, depth, parts, clipRect, gridSize );
424 int vertexCount = GEOSGetNumCoordinates_r( context, currentPart );
425 if ( vertexCount == 0 )
429 else if ( vertexCount < maxNodes )
436 double width = clipRect.
width();
437 double height = clipRect.
height();
438 QgsRectangle halfClipRect1 = clipRect;
439 QgsRectangle halfClipRect2 = clipRect;
440 if ( width > height )
453 halfClipRect1.
setYMinimum( halfClipRect1.
yMinimum() - std::numeric_limits<double>::epsilon() );
454 halfClipRect2.
setYMinimum( halfClipRect2.
yMinimum() - std::numeric_limits<double>::epsilon() );
455 halfClipRect1.
setYMaximum( halfClipRect1.
yMaximum() + std::numeric_limits<double>::epsilon() );
456 halfClipRect2.
setYMaximum( halfClipRect2.
yMaximum() + std::numeric_limits<double>::epsilon() );
460 halfClipRect1.
setXMinimum( halfClipRect1.
xMinimum() - std::numeric_limits<double>::epsilon() );
461 halfClipRect2.
setXMinimum( halfClipRect2.
xMinimum() - std::numeric_limits<double>::epsilon() );
462 halfClipRect1.
setXMaximum( halfClipRect1.
xMaximum() + std::numeric_limits<double>::epsilon() );
463 halfClipRect2.
setXMaximum( halfClipRect2.
xMaximum() + std::numeric_limits<double>::epsilon() );
475 clipPart1.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart1.get(), gridSize ) );
477 subdivideRecursive( clipPart1.get(), maxNodes, depth, parts, halfClipRect1, gridSize );
483 clipPart2.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart2.get(), gridSize ) );
485 subdivideRecursive( clipPart2.get(), maxNodes, depth, parts, halfClipRect2, gridSize );
497 maxNodes = std::max( maxNodes, 8 );
503 subdivideRecursive( mGeos.get(), maxNodes, 0, parts.get(),
mGeometry->boundingBox(), parameters.
gridSize() );
507 return std::move( parts );
512 return overlay( geom, OverlayUnion, errorMsg, parameters, feedback ).release();
517 std::vector<geos::unique_ptr> geosGeometries;
518 geosGeometries.reserve( geomList.size() );
524 geosGeometries.emplace_back(
asGeos( g, mPrecision ) );
532 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
535 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
539 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
544 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
545 return result.release();
550 std::vector<geos::unique_ptr> geosGeometries;
551 geosGeometries.reserve( geomList.size() );
557 geosGeometries.emplace_back(
asGeos( g.constGet(), mPrecision ) );
565 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
569 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
573 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
578 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
579 return result.release();
584 return overlay( geom, OverlaySymDifference, errorMsg, parameters, feedback ).release();
587static bool isZVerticalLine(
const QgsAbstractGeometry *geom,
double tolerance = 4 * std::numeric_limits<double>::epsilon() )
597 bool isVertical =
true;
600 const int nrPoints = line->numPoints();
608 const double sqrTolerance = tolerance * tolerance;
609 const double *lineX = line->xData();
610 const double *lineY = line->yData();
611 for (
int iVert = nrPoints - 1, jVert = 0; jVert < nrPoints; iVert = jVert++ )
642 otherGeosGeom =
asGeos( &firstPoint, mPrecision );
646 otherGeosGeom =
asGeos( geom, mPrecision );
649 if ( !otherGeosGeom )
657 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
659 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
663 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
680 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
689 GEOSPreparedDistance_r( context, mGeosPrepared.get(), point.get(), &
distance );
693 GEOSDistance_r( context, mGeos.get(), point.get(), &
distance );
710 return intersects( geom, errorMsg, feedback );
722 otherGeosGeom =
asGeos( &firstPoint );
726 otherGeosGeom =
asGeos( geom, mPrecision );
729 if ( !otherGeosGeom )
743 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
745#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
746 return GEOSPreparedDistanceWithin_r( context, mGeosPrepared.get(), otherGeosGeom.get(), maxdist );
748 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
753#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
754 return GEOSDistanceWithin_r( context, mGeos.get(), otherGeosGeom.get(), maxdist );
756 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
772#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
775 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
781#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
782 return GEOSPreparedContainsXY_r( context, mGeosPrepared.get(), x, y ) == 1;
784 return GEOSPreparedContains_r( context, mGeosPrepared.get(), point.get() ) == 1;
788#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
789 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
794 result = ( GEOSContains_r( context, mGeos.get(), point.get() ) == 1 );
796 catch ( QgsGeosException &e )
801 *errorMsg = e.what();
819 if ( !otherGeosGeom )
843 if ( !otherGeosGeom )
867 if ( !otherGeosGeom )
891 if ( !otherGeosGeom )
907 if ( !mGeos || !geom )
913#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
921 return GEOSPreparedIntersectsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
923 catch ( QgsGeosException &e )
928 *errorMsg = e.what();
936 return relation( geom, RelationIntersects, errorMsg );
941 return relation( geom, RelationTouches, errorMsg, feedback );
946 return relation( geom, RelationCrosses, errorMsg, feedback );
951 return relation( geom, RelationWithin, errorMsg, feedback );
956 return relation( geom, RelationOverlaps, errorMsg, feedback );
961 if ( !mGeos || !geom )
966#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
975 return GEOSPreparedContainsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
977 catch ( QgsGeosException &e )
982 *errorMsg = e.what();
990 return relation( geom, RelationContains, errorMsg, feedback );
995 return relation( geom, RelationDisjoint, errorMsg, feedback );
1016 char *r = GEOSRelate_r( context, mGeos.get(), geosGeom.get() );
1019 result = QString( r );
1020 GEOSFree_r( context, r );
1023 catch ( QgsGeosException &e )
1028 *errorMsg = e.what();
1037 if ( !mGeos || !geom )
1048 bool result =
false;
1054 result = ( GEOSRelatePattern_r( context, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
1056 catch ( QgsGeosException &e )
1061 *errorMsg = e.what();
1106#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
1122 if ( !GEOSisValid_r( context, mGeos.get() ) )
1126 if ( !splitGeosGeom || !GEOSisValid_r( context, splitGeosGeom.get() ) || !GEOSisSimple_r( context, splitGeosGeom.get() ) )
1141 if ( !topologicalTestPointsSplit( splitGeosGeom.get(), topologyTestPoints, errorMsg ) )
1147 newGeometries.clear();
1149 geos::unique_ptr split( GEOSSplit_r( context, mGeos.get(), splitGeosGeom.get() ) );
1156 int nParts = GEOSGetNumGeometries_r( context, split.get() );
1157 for (
int i = 0; i < nParts; ++i )
1159 newGeometries <<
QgsGeometry(
fromGeos( GEOSGetGeometryN_r( context, split.get(), i ) ) );
1170 const QgsLineString *splitLine = qgis::down_cast< const QgsLineString * >( &splitGeom );
1173 return splitGeometry( *splitLine, newGeometries, topological, topologyTestPoints, errorMsg,
false );
1188 const QgsLineString &splitLine, QVector<QgsGeometry> &newGeometries,
bool topological,
QgsPointSequence &topologyTestPoints, QString *errorMsg,
bool skipIntersectionCheck
1204 if ( !GEOSisValid_r( context, mGeos.get() ) )
1211 newGeometries.clear();
1218 splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
1222 splitLineGeos = createGeosPointXY( splitLine.
xAt( 0 ), splitLine.
yAt( 0 ),
false, 0,
false, 0, 2, mPrecision );
1229 if ( !GEOSisValid_r( context, splitLineGeos.get() ) || !GEOSisSimple_r( context, splitLineGeos.get() ) )
1237 if ( !topologicalTestPointsSplit( splitLineGeos.get(), topologyTestPoints, errorMsg ) )
1246 returnCode = splitLinearGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1250 returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1278 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, mGeos.get(), splitLine ) );
1279 if ( !intersectionGeom )
1284 if ( !( GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_POINT
1285 || GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_LINESTRING
1286 || GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_MULTIPOINT
1287 || GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_MULTILINESTRING ) )
1291 *errorMsg = u
"Extracting topological points from curves or polygons is not yet supported."_s;
1296 bool simple =
false;
1297 int nIntersectGeoms = 1;
1298 if ( GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_LINESTRING || GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_POINT )
1302 nIntersectGeoms = GEOSGetNumGeometries_r( context, intersectionGeom.get() );
1304 for (
int i = 0; i < nIntersectGeoms; ++i )
1308 currentIntersectGeom = intersectionGeom.get();
1310 currentIntersectGeom = GEOSGetGeometryN_r( context, intersectionGeom.get(), i );
1312 const GEOSCoordSequence *lineSequence = GEOSGeom_getCoordSeq_r( context, currentIntersectGeom );
1313 unsigned int sequenceSize = 0;
1315 if ( GEOSCoordSeq_getSize_r( context, lineSequence, &sequenceSize ) != 0 )
1317 for (
unsigned int i = 0; i < sequenceSize; ++i )
1319 if ( GEOSCoordSeq_getXYZ_r( context, lineSequence, i, &x, &y, &z ) )
1321 testPoints.push_back( QgsPoint( x, y, z ) );
1335 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1337 std::unique_ptr< QgsMultiCurve > multiCurve;
1338 if ( type == GEOS_MULTILINESTRING )
1342 else if ( type == GEOS_LINESTRING )
1344 multiCurve = std::make_unique<QgsMultiCurve>();
1345 multiCurve->addGeometry(
mGeometry->clone() );
1360 std::unique_ptr< QgsMultiPoint > splitPoints;
1362 std::unique_ptr< QgsAbstractGeometry > splitGeom(
fromGeos( GEOSsplitPoint ) );
1366 splitPoints.reset( qgis::down_cast<QgsMultiPoint *>( splitGeom.release() ) );
1370 splitPoints = std::make_unique< QgsMultiPoint >();
1373 splitPoints->addGeometry( qgis::down_cast<QgsPoint *>( splitGeom.release() ) );
1381 QgsMultiCurve lines;
1384 for (
int geometryIndex = 0; geometryIndex < multiCurve->numGeometries(); ++geometryIndex )
1397 QMap< int, QVector< QPair< double, QgsPoint > > > pointMap;
1398 for (
int splitPointIndex = 0; splitPointIndex < splitPoints->numGeometries(); ++splitPointIndex )
1400 const QgsPoint *intersectionPoint = splitPoints->pointN( splitPointIndex );
1402 QgsPoint segmentPoint2D;
1403 QgsVertexId nextVertex;
1406 line->
closestSegment( *intersectionPoint, segmentPoint2D, nextVertex );
1422 const QPair< double, QgsPoint > pair = qMakePair(
distance, *correctSegmentPoint.get() );
1423 if ( pointMap.contains( nextVertex.
vertex - 1 ) )
1424 pointMap[nextVertex.
vertex - 1].append( pair );
1426 pointMap[nextVertex.
vertex - 1] = QVector< QPair< double, QgsPoint > >() << pair;
1431 for (
auto &p : pointMap )
1433 std::sort( p.begin(), p.end(), [](
const QPair< double, QgsPoint > &a,
const QPair< double, QgsPoint > &b ) { return a.first < b.first; } );
1437 QgsLineString newLine;
1439 QgsPoint splitPoint;
1440 for (
int vertexIndex = 0; vertexIndex < nVertices; ++vertexIndex )
1442 QgsPoint currentPoint = line->
pointN( vertexIndex );
1444 if ( pointMap.contains( vertexIndex ) )
1447 for (
int k = 0; k < pointMap[vertexIndex].size(); ++k )
1449 splitPoint = pointMap[vertexIndex][k].second;
1450 if ( splitPoint == currentPoint )
1453 newLine = QgsLineString();
1456 else if ( splitPoint == line->
pointN( vertexIndex + 1 ) )
1460 newLine = QgsLineString();
1466 newLine = QgsLineString();
1475 return asGeos( &lines, mPrecision );
1480 Q_UNUSED( skipIntersectionCheck )
1489 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, splitLine, mGeos.get() ) );
1490 if ( !intersectGeom || GEOSisEmpty_r( context, intersectGeom.get() ) )
1494 const int linearIntersect = GEOSRelatePattern_r( context, mGeos.get(), splitLine,
"1********" );
1495 if ( linearIntersect > 0 )
1503 std::vector<geos::unique_ptr> lineGeoms;
1505 const int splitType = GEOSGeomTypeId_r( context, splitGeom.get() );
1506 if ( splitType == GEOS_MULTILINESTRING )
1508 const int nGeoms = GEOSGetNumGeometries_r( context, splitGeom.get() );
1509 lineGeoms.reserve( nGeoms );
1510 for (
int i = 0; i < nGeoms; ++i )
1511 lineGeoms.emplace_back( GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, splitGeom.get(), i ) ) );
1515 lineGeoms.emplace_back( GEOSGeom_clone_r( context, splitGeom.get() ) );
1518 mergeGeometriesMultiTypeSplit( lineGeoms );
1522 newGeometries << QgsGeometry(
fromGeos( lineGeom.get() ) );
1538 if ( !mGeosPrepared )
1544 if ( !skipIntersectionCheck && !GEOSPreparedIntersects_r( context, mGeosPrepared.get(), splitLine ) )
1549 if ( !nodedGeometry )
1558 const int numberOfGeometriesPolygon = numberOfGeometries( polygons.get() );
1559 if ( numberOfGeometriesPolygon == 0 )
1566 std::vector<geos::unique_ptr> testedGeometries;
1571 for (
int i = 0; i < numberOfGeometriesPolygon; i++ )
1573 const GEOSGeometry *polygon = GEOSGetGeometryN_r( context, polygons.get(), i );
1577 testedGeometries.emplace_back( GEOSGeom_clone_r( context, polygon ) );
1580 const size_t nGeometriesThis = numberOfGeometries( mGeos.get() );
1581 if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
1591 mergeGeometriesMultiTypeSplit( testedGeometries );
1594 for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( context, testedGeometries[i].get() ); ++i )
1597 if ( i < testedGeometries.size() )
1604 newGeometries << QgsGeometry(
fromGeos( testedGeometry.get() ) );
1612 if ( !splitLine || !geom )
1617 if ( GEOSGeom_getDimensions_r( context, geom ) == 2 )
1618 geometryBoundary.reset( GEOSBoundary_r( context, geom ) );
1620 geometryBoundary.reset( GEOSGeom_clone_r( context, geom ) );
1623 geos::unique_ptr unionGeometry( GEOSUnion_r( context, splitLineClone.get(), geometryBoundary.get() ) );
1625 return unionGeometry;
1628int QgsGeos::mergeGeometriesMultiTypeSplit( std::vector<geos::unique_ptr> &splitResult )
const
1635 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1636 if ( type != GEOS_GEOMETRYCOLLECTION && type != GEOS_MULTILINESTRING && type != GEOS_MULTIPOLYGON && type != GEOS_MULTIPOINT )
1640 std::vector<geos::unique_ptr> unionGeom;
1642 std::vector<geos::unique_ptr> newSplitResult;
1644 for (
size_t i = 0; i < splitResult.size(); ++i )
1647 bool isPart =
false;
1648 for (
int j = 0; j < GEOSGetNumGeometries_r( context, mGeos.get() ); j++ )
1650 if ( GEOSEquals_r( context, splitResult[i].get(), GEOSGetGeometryN_r( context, mGeos.get(), j ) ) )
1659 unionGeom.emplace_back( std::move( splitResult[i] ) );
1663 std::vector<geos::unique_ptr> geomVector;
1664 geomVector.emplace_back( std::move( splitResult[i] ) );
1666 if ( type == GEOS_MULTILINESTRING )
1667 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, geomVector ) );
1668 else if ( type == GEOS_MULTIPOLYGON )
1669 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, geomVector ) );
1673 splitResult = std::move( newSplitResult );
1676 if ( !unionGeom.empty() )
1678 if ( type == GEOS_MULTILINESTRING )
1679 splitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, unionGeom ) );
1680 else if ( type == GEOS_MULTIPOLYGON )
1681 splitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ) );
1687geos::unique_ptr QgsGeos::createGeosCollection(
int typeId, std::vector<geos::unique_ptr> &geoms )
1689 std::vector<GEOSGeometry *> geomarr;
1690 geomarr.reserve( geoms.size() );
1695 if ( geomUniquePtr )
1697 if ( !GEOSisEmpty_r( context, geomUniquePtr.get() ) )
1701 geomarr.emplace_back( geomUniquePtr.release() );
1709 geomRes.reset( GEOSGeom_createCollection_r( context, typeId, geomarr.data(), geomarr.size() ) );
1711 catch ( QgsGeosException & )
1715 GEOSGeom_destroy_r( context, geom );
1730#ifdef GEOS_SUPPORTS_ZM
1731 bool hasZ = GEOSHasZ_r( context,
geos );
1732 bool hasM = GEOSHasM_r( context,
geos );
1734 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1735 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1736 bool hasZ = ( nCoordDims == 3 );
1737 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1740 switch ( GEOSGeomTypeId_r( context,
geos ) )
1744 if ( GEOSisEmpty_r( context,
geos ) )
1747 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1748 unsigned int nPoints = 0;
1749 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1758 return !point.
isEmpty() ? std::unique_ptr<QgsAbstractGeometry>( point.
clone() ) :
nullptr;
1760 case GEOS_LINESTRING:
1762 return sequenceToLinestring(
geos, hasZ, hasM );
1764#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
1765 case GEOS_CIRCULARSTRING:
1767 return sequenceToCircularString(
geos, hasZ, hasM );
1769 case GEOS_COMPOUNDCURVE:
1771 auto compoundCurve = std::make_unique< QgsCompoundCurve >();
1772 const int nCurves = GEOSGetNumCurves_r( context,
geos );
1773 for (
int i = 0; i < nCurves; i++ )
1775 std::unique_ptr< QgsSimpleCurve > curve = sequenceToSimpleCurve( GEOSGetCurveN_r( context,
geos, i ), hasZ, hasM );
1776 compoundCurve->addCurve( curve.release(),
true );
1778 return std::move( compoundCurve );
1785#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
1786 case GEOS_CURVEPOLYGON:
1788 return fromGeosCurvePolygon(
geos );
1791 case GEOS_MULTIPOINT:
1793 auto multiPoint = std::make_unique<QgsMultiPoint>();
1794 int nParts = GEOSGetNumGeometries_r( context,
geos );
1795 multiPoint->reserve( nParts );
1796 for (
int i = 0; i < nParts; ++i )
1798 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context, GEOSGetGeometryN_r( context,
geos, i ) );
1801 unsigned int nPoints = 0;
1802 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1804 multiPoint->addGeometry(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1807 return std::move( multiPoint );
1809 case GEOS_MULTILINESTRING:
1811 auto multiLineString = std::make_unique<QgsMultiLineString>();
1812 int nParts = GEOSGetNumGeometries_r( context,
geos );
1813 multiLineString->reserve( nParts );
1814 for (
int i = 0; i < nParts; ++i )
1816 std::unique_ptr< QgsLineString > line( sequenceToLinestring( GEOSGetGeometryN_r( context,
geos, i ), hasZ, hasM ) );
1819 multiLineString->addGeometry( line.release() );
1822 return std::move( multiLineString );
1824#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
1825 case GEOS_MULTICURVE:
1827 auto multiCurve = std::make_unique<QgsMultiCurve>();
1828 int nParts = GEOSGetNumGeometries_r( context,
geos );
1829 multiCurve->reserve( nParts );
1830 for (
int i = 0; i < nParts; ++i )
1832 std::unique_ptr< QgsAbstractGeometry > curve(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1833 multiCurve->addGeometry( curve.release() );
1835 return std::move( multiCurve );
1838 case GEOS_MULTIPOLYGON:
1840 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
1842 int nParts = GEOSGetNumGeometries_r( context,
geos );
1843 multiPolygon->reserve( nParts );
1844 for (
int i = 0; i < nParts; ++i )
1846 std::unique_ptr< QgsPolygon > poly =
fromGeosPolygon( GEOSGetGeometryN_r( context,
geos, i ) );
1849 multiPolygon->addGeometry( poly.release() );
1852 return std::move( multiPolygon );
1854#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
1855 case GEOS_MULTISURFACE:
1857 auto multiSurface = std::make_unique<QgsMultiSurface>();
1858 int nParts = GEOSGetNumGeometries_r( context,
geos );
1859 multiSurface->reserve( nParts );
1860 for (
int i = 0; i < nParts; ++i )
1862 std::unique_ptr< QgsAbstractGeometry > polygon(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1863 multiSurface->addGeometry( polygon.release() );
1865 return std::move( multiSurface );
1868 case GEOS_GEOMETRYCOLLECTION:
1870 auto geomCollection = std::make_unique<QgsGeometryCollection>();
1871 int nParts = GEOSGetNumGeometries_r( context,
geos );
1872 geomCollection->reserve( nParts );
1873 for (
int i = 0; i < nParts; ++i )
1875 std::unique_ptr< QgsAbstractGeometry > geom(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1878 geomCollection->addGeometry( geom.release() );
1881 return std::move( geomCollection );
1887#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
1888std::unique_ptr<QgsCurvePolygon> QgsGeos::fromGeosCurvePolygon(
const GEOSGeometry *
geos )
1891 if ( GEOSGeomTypeId_r( context,
geos ) != GEOS_CURVEPOLYGON )
1896#ifdef GEOS_SUPPORTS_ZM
1897 bool hasZ = GEOSHasZ_r( context, geos );
1898 bool hasM = GEOSHasM_r( context, geos );
1900 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context, geos );
1901 int nDims = GEOSGeom_getDimensions_r( context, geos );
1902 bool hasZ = ( nCoordDims == 3 );
1903 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1906 auto curvePolygon = std::make_unique<QgsCurvePolygon>();
1908 const GEOSGeometry *ring = GEOSGetExteriorRing_r( context, geos );
1911 if ( GEOSGeomTypeId_r( context, ring ) == GEOS_COMPOUNDCURVE )
1913 curvePolygon->setExteriorRing( qgis::down_cast< QgsCompoundCurve *>(
fromGeos( ring ).release() ) );
1917 curvePolygon->setExteriorRing( sequenceToSimpleCurve( ring, hasZ, hasM ).release() );
1921 QVector<QgsCurve *> interiorRings;
1922 const int ringCount = GEOSGetNumInteriorRings_r( context, geos );
1923 interiorRings.reserve( ringCount );
1924 for (
int i = 0; i < ringCount; ++i )
1926 ring = GEOSGetInteriorRingN_r( context, geos, i );
1929 if ( GEOSGeomTypeId_r( context, ring ) == GEOS_COMPOUNDCURVE )
1931 interiorRings.push_back( qgis::down_cast< QgsCompoundCurve *>(
fromGeos( ring ).release() ) );
1935 interiorRings.push_back( sequenceToSimpleCurve( ring, hasZ, hasM ).release() );
1939 curvePolygon->setInteriorRings( interiorRings );
1941 return curvePolygon;
1948 if ( GEOSGeomTypeId_r( context,
geos ) != GEOS_POLYGON )
1953#ifdef GEOS_SUPPORTS_ZM
1954 bool hasZ = GEOSHasZ_r( context,
geos );
1955 bool hasM = GEOSHasM_r( context,
geos );
1957 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1958 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1959 bool hasZ = ( nCoordDims == 3 );
1960 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1963 auto polygon = std::make_unique<QgsPolygon>();
1968 polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1971 QVector<QgsCurve *> interiorRings;
1972 const int ringCount = GEOSGetNumInteriorRings_r( context,
geos );
1973 interiorRings.reserve( ringCount );
1974 for (
int i = 0; i < ringCount; ++i )
1976 ring = GEOSGetInteriorRingN_r( context,
geos, i );
1979 interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1982 polygon->setInteriorRings( interiorRings );
1987std::unique_ptr<QgsSimpleCurve> QgsGeos::sequenceToSimpleCurve(
const GEOSGeometry *
geos,
bool hasZ,
bool hasM )
1991 const int geometryType = GEOSGeomTypeId_r( context,
geos );
1992 if ( !( geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING || geometryType == GEOS_CIRCULARSTRING ) )
1995 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1997 unsigned int nPoints;
1998 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
2000 QVector< double > xOut( nPoints );
2001 QVector< double > yOut( nPoints );
2002 QVector< double > zOut;
2004 zOut.resize( nPoints );
2005 QVector< double > mOut;
2007 mOut.resize( nPoints );
2009 double *x = xOut.data();
2010 double *y = yOut.data();
2011 double *z = zOut.data();
2012 double *m = mOut.data();
2014#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
2015 GEOSCoordSeq_copyToArrays_r( context, cs, x, y, hasZ ? z :
nullptr, hasM ? m :
nullptr );
2017 for (
unsigned int i = 0; i < nPoints; ++i )
2020 GEOSCoordSeq_getXYZ_r( context, cs, i, x++, y++, z++ );
2022 GEOSCoordSeq_getXY_r( context, cs, i, x++, y++ );
2025 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, m++ );
2030 std::unique_ptr< QgsSimpleCurve > simpleCurve;
2031 if ( geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING )
2033 simpleCurve = std::make_unique<QgsLineString>( xOut, yOut, zOut, mOut );
2035 else if ( geometryType == GEOS_CIRCULARSTRING )
2037 simpleCurve = std::make_unique<QgsCircularString>( xOut, yOut, zOut, mOut );
2042std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring(
const GEOSGeometry *geos,
bool hasZ,
bool hasM )
2044 return std::unique_ptr<QgsLineString>( qgis::down_cast<QgsLineString *>( sequenceToSimpleCurve( geos, hasZ, hasM ).release() ) );
2047#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
2048std::unique_ptr<QgsCircularString> QgsGeos::sequenceToCircularString(
const GEOSGeometry *geos,
bool hasZ,
bool hasM )
2050 return std::unique_ptr<QgsCircularString>( qgis::down_cast<QgsCircularString *>( sequenceToSimpleCurve( geos, hasZ, hasM ).release() ) );
2060 int geometryType = GEOSGeomTypeId_r( context, g );
2061 if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING || geometryType == GEOS_POLYGON )
2065 return GEOSGetNumGeometries_r( context, g );
2081 GEOSCoordSeq_getXYZ_r( context, cs, i, &x, &y, &z );
2083 GEOSCoordSeq_getXY_r( context, cs, i, &x, &y );
2086 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, &m );
2126 geosType = GEOS_MULTIPOINT;
2130#if !( GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 ) )
2133 geosType = GEOS_MULTILINESTRING;
2137#if !( GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 ) )
2140 geosType = GEOS_MULTIPOLYGON;
2143#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
2145 geosType = GEOS_MULTICURVE;
2149 geosType = GEOS_MULTISURFACE;
2154 geosType = GEOS_GEOMETRYCOLLECTION;
2168 std::vector<geos::unique_ptr> geomVector;
2169 geomVector.reserve(
c->numGeometries() );
2170 for (
int i = 0; i <
c->numGeometries(); ++i )
2177 geomVector.emplace_back( std::move( geosGeom ) );
2179 return createGeosCollection( geosType, geomVector );
2186 return createGeosPoint( geom, coordDims, precision, flags );
2191#if !( GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 ) )
2193 return createGeosLinestring( geom, precision, flags );
2195 return createGeosSimpleCurve( geom, precision, flags );
2198 return createGeosCompoundCurve( geom, precision, flags );
2203#if !( GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 ) )
2206 return createGeosPolygon( geom, precision, flags );
2208#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
2210 return createGeosCurvePolygon( geom, precision, flags );
2219 if ( !polyhedralSurface )
2222 std::vector<geos::unique_ptr> geomVector;
2223 geomVector.reserve( polyhedralSurface->
numPatches() );
2224 for (
int i = 0; i < polyhedralSurface->
numPatches(); ++i )
2231 geomVector.emplace_back( std::move( geosPolygon ) );
2234 return createGeosCollection( GEOS_MULTIPOLYGON, geomVector );
2248 if ( !mGeos || !geom )
2259 QgsScopedGeosContextRegisterFeedback interrupt( feedback );
2261 const double gridSize = parameters.
gridSize();
2269 case OverlayIntersection:
2272 opGeom.reset( GEOSIntersectionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
2276 opGeom.reset( GEOSIntersection_r( context, mGeos.get(), geosGeom.get() ) );
2280 case OverlayDifference:
2283 opGeom.reset( GEOSDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
2287 opGeom.reset( GEOSDifference_r( context, mGeos.get(), geosGeom.get() ) );
2296 unionGeometry.reset( GEOSUnionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
2300 unionGeometry.reset( GEOSUnion_r( context, mGeos.get(), geosGeom.get() ) );
2303#if !( GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 ) )
2304 if ( unionGeometry && GEOSGeomTypeId_r( context, unionGeometry.get() ) == GEOS_MULTILINESTRING )
2306 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, unionGeometry.get() ) );
2309 unionGeometry = std::move( mergedLines );
2314 opGeom = std::move( unionGeometry );
2318 case OverlaySymDifference:
2321 opGeom.reset( GEOSSymDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
2325 opGeom.reset( GEOSSymDifference_r( context, mGeos.get(), geosGeom.get() ) );
2331 catch ( QgsGeosException &e )
2336 *errorMsg = e.what();
2342bool QgsGeos::relation(
const QgsAbstractGeometry *geom, Relation r, QString *errorMsg, QgsFeedback *feedback )
const
2344 if ( !mGeos || !geom )
2356 QgsScopedGeosContextRegisterFeedback interrupt( feedback );
2358 bool result =
false;
2361 if ( mGeosPrepared )
2365 case RelationIntersects:
2366 result = ( GEOSPreparedIntersects_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2368 case RelationTouches:
2369 result = ( GEOSPreparedTouches_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2371 case RelationCrosses:
2372 result = ( GEOSPreparedCrosses_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2374 case RelationWithin:
2375 result = ( GEOSPreparedWithin_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2377 case RelationContains:
2378 result = ( GEOSPreparedContains_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2380 case RelationDisjoint:
2381 result = ( GEOSPreparedDisjoint_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2383 case RelationOverlaps:
2384 result = ( GEOSPreparedOverlaps_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2392 case RelationIntersects:
2393 result = ( GEOSIntersects_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2395 case RelationTouches:
2396 result = ( GEOSTouches_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2398 case RelationCrosses:
2399 result = ( GEOSCrosses_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2401 case RelationWithin:
2402 result = ( GEOSWithin_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2404 case RelationContains:
2405 result = ( GEOSContains_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2407 case RelationDisjoint:
2408 result = ( GEOSDisjoint_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2410 case RelationOverlaps:
2411 result = ( GEOSOverlaps_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2415 catch ( QgsGeosException &e )
2420 *errorMsg = e.what();
2465 geos.reset( GEOSBufferWithStyle_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( endCapStyle ),
static_cast< int >( joinStyle ), miterLimit ) );
2518 geos.reset( GEOSGetCentroid_r( context, mGeos.get() ) );
2523 GEOSGeomGetX_r( context,
geos.get(), &x );
2524 GEOSGeomGetY_r( context,
geos.get(), &y );
2561 geos.reset( GEOSPointOnSurface_r( context, mGeos.get() ) );
2563 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
2568 GEOSGeomGetX_r( context,
geos.get(), &x );
2569 GEOSGeomGetY_r( context,
geos.get(), &y );
2587 std::unique_ptr< QgsAbstractGeometry > cHullGeom =
fromGeos( cHull.get() );
2588 return cHullGeom.release();
2595#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
2596 ( void ) allowHoles;
2597 ( void ) targetPercent;
2599 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHull requires a QGIS build based on GEOS 3.11 or later" ) );
2611 return concaveHullGeom;
2619#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
2620 ( void ) allowHoles;
2621 ( void ) targetPercent;
2623 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHullOfPolygons requires a QGIS build based on GEOS 3.11 or later" ) );
2635 return concaveHullGeom;
2643#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12
2645 ( void ) invalidEdges;
2647 throw QgsNotSupportedException( QObject::tr(
"Validating coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2652 *errorMsg = u
"Input geometry was not set"_s;
2661 const int result = GEOSCoverageIsValid_r( context, mGeos.get(), gapWidth, invalidEdges ? &invalidEdgesGeos :
nullptr );
2662 if ( invalidEdges && invalidEdgesGeos )
2664 *invalidEdges =
fromGeos( invalidEdgesGeos );
2666 if ( invalidEdgesGeos )
2668 GEOSGeom_destroy_r( context, invalidEdgesGeos );
2669 invalidEdgesGeos =
nullptr;
2689#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12
2691 ( void ) preserveBoundary;
2693 throw QgsNotSupportedException( QObject::tr(
"Simplifying coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2698 *errorMsg = u
"Input geometry was not set"_s;
2706 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom =
fromGeos( simplified.get() );
2707 return simplifiedGeom;
2718 *errorMsg = u
"Input geometry was not set"_s;
2726 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( unioned.get() );
2734#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 14
2735 ( void ) parameters;
2738 throw QgsNotSupportedException( QObject::tr(
"Cleaning coverages requires a QGIS build based on GEOS 3.14 or later" ) );
2743 *errorMsg = u
"Input geometry was not set"_s;
2747 GEOSCoverageCleanParams *params =
nullptr;
2776 std::unique_ptr< QgsAbstractGeometry> cleanedGeom =
fromGeos( cleaned.get() );
2780 catch ( QgsGeosException &e )
2790 *errorMsg = e.what();
2791 if ( errorMsg->startsWith(
"InterruptedException"_L1, Qt::CaseInsensitive ) )
2806 *errorMsg = QObject::tr(
"QGIS geometry cannot be converted to a GEOS geometry",
"GEOS Error" );
2816 char res = GEOSisValidDetail_r( context, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
2817 const bool invalid = res != 1;
2822 error = QString( r );
2823 GEOSFree_r( context, r );
2826 if ( invalid && errorMsg )
2829 static const std::map< QString, QString > sTranslatedErrors {
2830 { u
"topology validation error"_s, QObject::tr(
"Topology validation error",
"GEOS Error" ) },
2831 { u
"repeated point"_s, QObject::tr(
"Repeated point",
"GEOS Error" ) },
2832 { u
"hole lies outside shell"_s, QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) },
2833 { u
"holes are nested"_s, QObject::tr(
"Holes are nested",
"GEOS Error" ) },
2834 { u
"interior is disconnected"_s, QObject::tr(
"Interior is disconnected",
"GEOS Error" ) },
2835 { u
"self-intersection"_s, QObject::tr(
"Self-intersection",
"GEOS Error" ) },
2836 { u
"ring self-intersection"_s, QObject::tr(
"Ring self-intersection",
"GEOS Error" ) },
2837 { u
"nested shells"_s, QObject::tr(
"Nested shells",
"GEOS Error" ) },
2838 { u
"duplicate rings"_s, QObject::tr(
"Duplicate rings",
"GEOS Error" ) },
2839 { u
"too few points in geometry component"_s, QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) },
2840 { u
"invalid coordinate"_s, QObject::tr(
"Invalid coordinate",
"GEOS Error" ) },
2841 { u
"ring is not closed"_s, QObject::tr(
"Ring is not closed",
"GEOS Error" ) },
2844 const auto translatedError = sTranslatedErrors.find( error.toLower() );
2845 if ( translatedError != sTranslatedErrors.end() )
2846 *errorMsg = translatedError->second;
2850 if ( g1 && errorLoc )
2856 GEOSGeom_destroy_r( context, g1 );
2866 if ( !mGeos || !geom )
2887 if ( !mGeos || !geom )
2901 bool equal = GEOSEqualsExact_r(
QgsGeosContext::get(), mGeos.get(), geosGeom.get(), epsilon );
2935GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
2940#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
2950 std::unique_ptr< QgsLineString > segmentized;
2954 simpleCurve = segmentized.get();
2956#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
2964 GEOSCoordSequence *coordSeq =
nullptr;
2966 const int numPoints = simpleCurve->
numPoints();
2968 const bool hasZ = simpleCurve->
is3D();
2969#ifdef GEOS_SUPPORTS_ZM
2970 const bool hasM = simpleCurve->
isMeasure();
2972 const bool hasM =
false;
2975#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
2978 if ( !forceClose || ( simpleCurve->
pointN( 0 ) == simpleCurve->
pointN( numPoints - 1 ) ) )
2983#ifdef GEOS_SUPPORTS_ZM
2984 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, simpleCurve->
xData(), simpleCurve->
yData(), simpleCurve->
zData(), simpleCurve->
mData(), numPoints );
2986 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, simpleCurve->
xData(), simpleCurve->
yData(), simpleCurve->
zData(),
nullptr, numPoints );
2990 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points"_s.arg( numPoints ) );
2998 QVector< double > x = simpleCurve->
xVector();
2999 if ( numPoints > 0 )
3000 x.append( x.at( 0 ) );
3001 QVector< double > y = simpleCurve->
yVector();
3002 if ( numPoints > 0 )
3003 y.append( y.at( 0 ) );
3004 QVector< double > z = simpleCurve->
zVector();
3005 if ( hasZ && numPoints > 0 )
3006 z.append( z.at( 0 ) );
3007 QVector< double > m = simpleCurve->
mVector();
3008 if ( hasM && numPoints > 0 )
3009 m.append( m.at( 0 ) );
3012 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, x.constData(), y.constData(), !hasZ ?
nullptr : z.constData(), !hasM ?
nullptr : m.constData(), numPoints + 1 );
3015 QgsDebugError( u
"GEOS Exception: Could not create closed coordinate sequence for %1 points"_s.arg( numPoints + 1 ) );
3036 int numOutPoints = numPoints;
3037 if ( forceClose && ( simpleCurve->
pointN( 0 ) != simpleCurve->
pointN( numPoints - 1 ) ) )
3044 coordSeq = GEOSCoordSeq_create_r( context, numOutPoints, coordDims );
3047 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions"_s.arg( numPoints ).arg( coordDims ) );
3051 const double *xData = simpleCurve->
xData();
3052 const double *yData = simpleCurve->
yData();
3053 const double *zData = hasZ ? simpleCurve->
zData() :
nullptr;
3054 const double *mData = hasM ? simpleCurve->
mData() :
nullptr;
3056 if ( precision > 0. )
3058 for (
int i = 0; i < numOutPoints; ++i )
3060 if ( i >= numPoints )
3063 xData = simpleCurve->
xData();
3064 yData = simpleCurve->
yData();
3065 zData = hasZ ? simpleCurve->
zData() :
nullptr;
3066 mData = hasM ? simpleCurve->
mData() :
nullptr;
3070 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
3074 GEOSCoordSeq_setXY_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
3078 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
3084 for (
int i = 0; i < numOutPoints; ++i )
3086 if ( i >= numPoints )
3089 xData = simpleCurve->
xData();
3090 yData = simpleCurve->
yData();
3091 zData = hasZ ? simpleCurve->
zData() :
nullptr;
3092 mData = hasM ? simpleCurve->
mData() :
nullptr;
3096 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, *xData++, *yData++, *zData++ );
3100 GEOSCoordSeq_setXY_r( context, coordSeq, i, *xData++, *yData++ );
3104 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
3114geos::unique_ptr
QgsGeos::createGeosPoint( const QgsAbstractGeometry *point,
int coordDims,
double precision, Qgis::GeosCreationFlags )
3120 return createGeosPointXY( pt->
x(), pt->
y(), pt->
is3D(), pt->
z(), pt->
isMeasure(), pt->
m(), coordDims, precision );
3125#ifndef GEOS_SUPPORTS_ZM
3134 if ( coordDims == 2 )
3137 if ( precision > 0. )
3138 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
3140 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, x, y ) );
3144#ifdef GEOS_SUPPORTS_ZM
3145 GEOSCoordSequence *coordSeq = GEOSCoordSeq_createWithDimensions_r( context, 1, hasZ ? 1 : 0, hasM ? 1 : 0 );
3147 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( context, 1, coordDims );
3151 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions"_s.arg( coordDims ) );
3154 if ( precision > 0. )
3156 GEOSCoordSeq_setX_r( context, coordSeq, 0, std::round( x / precision ) * precision );
3157 GEOSCoordSeq_setY_r( context, coordSeq, 0, std::round( y / precision ) * precision );
3160#ifdef GEOS_SUPPORTS_ZM
3161 GEOSCoordSeq_setZ_r( context, coordSeq, 0, z );
3163 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, std::round( z / precision ) * precision );
3169 GEOSCoordSeq_setX_r( context, coordSeq, 0, x );
3170 GEOSCoordSeq_setY_r( context, coordSeq, 0, y );
3173#ifdef GEOS_SUPPORTS_ZM
3174 GEOSCoordSeq_setZ_r( context, coordSeq, 0, z );
3176 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, z );
3180#ifdef GEOS_SUPPORTS_ZM
3183 GEOSCoordSeq_setM_r( context, coordSeq, 0, m );
3186 geosPoint.reset( GEOSGeom_createPoint_r( context, coordSeq ) );
3192geos::unique_ptr
QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve,
double precision, Qgis::GeosCreationFlags )
3198 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c, precision );
3211#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
3218 if ( !
c->isSimpleCurve() )
3221 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c, precision );
3228 if ( !
c->hasCurvedSegments() )
3241geos::unique_ptr
QgsGeos::createGeosCompoundCurve( const QgsAbstractGeometry *curve,
double precision, Qgis::GeosCreationFlags flags )
3252 const int nCurves =
c->nCurves();
3255 for (
int i = 0; i < nCurves; i++ )
3257 if (
c->curveAt( i )->isSimpleCurve() )
3259 curves[i] = createGeosSimpleCurve(
c->curveAt( i ), precision, flags ).release();
3262 geosCurve.reset( GEOSGeom_createCompoundCurve_r( context, curves, nCurves ) );
3269geos::unique_ptr
QgsGeos::createGeosCurvePolygon( const QgsAbstractGeometry *poly,
double precision, Qgis::GeosCreationFlags flags )
3275 const QgsCurve *exteriorRing = polygon->
exteriorRing();
3276 if ( !exteriorRing )
3288 exteriorRingGeos.reset( createGeosSimpleCurve( exteriorRing, precision, flags ).release() );
3292 exteriorRingGeos.reset( createGeosCompoundCurve( exteriorRing, precision, flags ).release() );
3296 QList< const QgsCurve * > holesToExport;
3297 holesToExport.reserve( nInteriorRings );
3298 for (
int i = 0; i < nInteriorRings; ++i )
3300 const QgsCurve *interiorRing = polygon->
interiorRing( i );
3303 holesToExport << interiorRing;
3308 if ( !holesToExport.empty() )
3311 for (
int i = 0; i < holesToExport.size(); ++i )
3313 if ( holesToExport[i]->isSimpleCurve() )
3315 holes[i] = createGeosSimpleCurve( holesToExport[i], precision, flags ).release();
3319 holes[i] = createGeosCompoundCurve( holesToExport[i], precision, flags ).release();
3324 geosCurvePolygon.reset( GEOSGeom_createCurvePolygon_r( context, exteriorRingGeos.release(), holes, holesToExport.size() ) );
3329 return geosCurvePolygon;
3339 const QgsCurve *exteriorRing = polygon->
exteriorRing();
3340 if ( !exteriorRing )
3349 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( context, createCoordinateSequence( exteriorRing, precision,
true ) ) );
3352 QList< const QgsCurve * > holesToExport;
3353 holesToExport.reserve( nInteriorRings );
3354 for (
int i = 0; i < nInteriorRings; ++i )
3356 const QgsCurve *interiorRing = polygon->
interiorRing( i );
3359 holesToExport << interiorRing;
3364 if ( !holesToExport.empty() )
3367 for (
int i = 0; i < holesToExport.size(); ++i )
3369 holes[i] = GEOSGeom_createLinearRing_r( context, createCoordinateSequence( holesToExport[i], precision,
true ) );
3373 geosPolygon.reset( GEOSGeom_createPolygon_r( context, exteriorRingGeos.release(), holes, holesToExport.size() ) );
3394 offset.reset( GEOSOffsetCurve_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( joinStyle ), miterLimit ) );
3406 return fromGeos( res.get() ).release();
3424 GEOSBufferParams_setSingleSided_r( context, bp.get(), 1 );
3425 GEOSBufferParams_setQuadrantSegments_r( context, bp.get(), segments );
3426 GEOSBufferParams_setJoinStyle_r( context, bp.get(),
static_cast< int >( joinStyle ) );
3427 GEOSBufferParams_setMitreLimit_r( context, bp.get(), miterLimit );
3433 geos.reset( GEOSBufferWithParams_r( context, mGeos.get(), bp.get(),
distance ) );
3468 boundaryGeos =
asGeos( boundary );
3498 return std::numeric_limits< double >::quiet_NaN();
3507 return std::numeric_limits< double >::quiet_NaN();
3549 if ( !mGeos || !other )
3570 if ( !mGeos ||
mGeometry->dimension() == 0 )
3588 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
3592 int numGeoms = GEOSGetNumGeometries_r( context, mGeos.get() );
3593 if ( numGeoms == -1 )
3602 bool isMultiGeom =
false;
3603 int geosTypeId = GEOSGeomTypeId_r( context, mGeos.get() );
3604 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
3607 bool isLine = (
mGeometry->dimension() == 1 );
3614 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
3618 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
3623 if ( reshapedGeometry )
3629 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
3630 return reshapeResult;
3637 bool reshapeTookPlace =
false;
3642 for (
int i = 0; i < numGeoms; ++i )
3645 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3647 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3649 if ( currentReshapeGeometry )
3651 newGeoms[i] = currentReshapeGeometry.release();
3652 reshapeTookPlace =
true;
3656 newGeoms[i] = GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, mGeos.get(), i ) );
3663 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
3667 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
3671 if ( !newMultiGeom )
3680 if ( reshapeTookPlace )
3684 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
3685 return reshapedMultiGeom;
3708 if ( GEOSGeomTypeId_r( context, mGeos.get() ) != GEOS_MULTILINESTRING )
3715 double gridSize = parameters.
gridSize();
3718 geos::unique_ptr geosFixedSize( GEOSGeom_setPrecision_r( context, mGeos.get(), gridSize, 0 ) );
3719 geos.reset( GEOSLineMerge_r( context, geosFixedSize.get() ) );
3722 geos.reset( GEOSLineMerge_r( context, mGeos.get() ) );
3748 if ( mGeosPrepared )
3750 nearestCoord.reset( GEOSPreparedNearestPoints_r( context, mGeosPrepared.get(), otherGeom.get() ) );
3754 nearestCoord.reset( GEOSNearestPoints_r( context, mGeos.get(), otherGeom.get() ) );
3757 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx );
3758 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny );
3760 catch ( QgsGeosException &e )
3765 *errorMsg = e.what();
3770 return std::make_unique< QgsPoint >( nx, ny );
3775 if ( !mGeos || other.
isEmpty() )
3785 if ( !other || other->
isEmpty() )
3804 if ( !nearestCoord )
3807 *errorMsg = u
"GEOS returned no nearest points"_s;
3811 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx1 );
3812 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny1 );
3813 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 1, &nx2 );
3814 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 1, &ny2 );
3816 catch ( QgsGeosException &e )
3821 *errorMsg = e.what();
3826 auto line = std::make_unique< QgsLineString >();
3851 catch ( QgsGeosException &e )
3856 *errorMsg = e.what();
3871 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
3881 catch ( QgsGeosException &e )
3886 *errorMsg = e.what();
3903 lineGeosGeometries[validLines] = l.release();
3912 geos::unique_ptr result( GEOSPolygonize_r( context, lineGeosGeometries, validLines ) );
3913 for (
int i = 0; i < validLines; ++i )
3915 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3917 delete[] lineGeosGeometries;
3920 catch ( QgsGeosException &e )
3924 *errorMsg = e.what();
3926 for (
int i = 0; i < validLines; ++i )
3928 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3930 delete[] lineGeosGeometries;
3945 extentGeosGeom =
asGeos( extent, mPrecision );
3946 if ( !extentGeosGeom )
3957 geos.reset( GEOSVoronoiDiagram_r( context, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
3959 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3981 geos.reset( GEOSDelaunayTriangulation_r( context, mGeos.get(), tolerance, edgesOnly ) );
3983 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3995#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
3997 throw QgsNotSupportedException( QObject::tr(
"Calculating constrainedDelaunayTriangulation requires a QGIS build based on GEOS 3.11 or later" ) );
4009 geos.reset( GEOSConstrainedDelaunayTriangulation_r( context, mGeos.get() ) );
4011 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
4016 std::unique_ptr< QgsAbstractGeometry > res =
fromGeos(
geos.get() );
4019 return std::unique_ptr< QgsAbstractGeometry >( collection->extractPartsByType(
Qgis::WkbType::Polygon,
true ) );
4031static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
4034 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( context, linestring );
4038 unsigned int coordSeqSize;
4039 if ( GEOSCoordSeq_getSize_r( context, coordSeq, &coordSeqSize ) == 0 )
4042 if ( coordSeqSize < 2 )
4045 GEOSCoordSeq_getX_r( context, coordSeq, 0, &x1 );
4046 GEOSCoordSeq_getY_r( context, coordSeq, 0, &y1 );
4047 GEOSCoordSeq_getX_r( context, coordSeq, coordSeqSize - 1, &x2 );
4048 GEOSCoordSeq_getY_r( context, coordSeq, coordSeqSize - 1, &y2 );
4056 double x1, y1, x2, y2;
4057 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
4060 double rx1, ry1, rx2, ry2;
4061 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
4064 bool intersectionAtOrigLineEndpoint = ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) != ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
4065 bool intersectionAtReshapeLineEndpoint = ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) || ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
4069 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
4073 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
4074 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, geoms, 2 ) );
4079 double x1res, y1res, x2res, y2res;
4080 if ( !_linestringEndpoints( res.get(), x1res, y1res, x2res, y2res ) )
4082 if ( ( x1res == x2 && y1res == y2 ) || ( x2res == x1 && y2res == y1 ) )
4083 res.reset( GEOSReverse_r( context, res.get() ) );
4094 if ( !line || !reshapeLineGeos )
4097 bool atLeastTwoIntersections =
false;
4098 bool oneIntersection =
false;
4099 QgsPointXY oneIntersectionPoint;
4105 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, line, reshapeLineGeos ) );
4106 if ( intersectGeom )
4108 const int geomType = GEOSGeomTypeId_r( context, intersectGeom.get() );
4109 atLeastTwoIntersections = ( geomType == GEOS_MULTIPOINT && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 1 )
4110 || ( geomType == GEOS_GEOMETRYCOLLECTION && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 )
4111 || ( geomType == GEOS_MULTILINESTRING && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 );
4113 if ( GEOSGeomTypeId_r( context, intersectGeom.get() ) == GEOS_POINT )
4115 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( context, intersectGeom.get() );
4117 GEOSCoordSeq_getX_r( context, intersectionCoordSeq, 0, &xi );
4118 GEOSCoordSeq_getY_r( context, intersectionCoordSeq, 0, &yi );
4119 oneIntersection =
true;
4120 oneIntersectionPoint = QgsPointXY( xi, yi );
4124 catch ( QgsGeosException & )
4126 atLeastTwoIntersections =
false;
4130 if ( oneIntersection )
4131 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
4133 if ( !atLeastTwoIntersections )
4137 double x1, y1, x2, y2;
4138 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
4141 geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1,
false, 0,
false, 0, 2, precision );
4142 geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2,
false, 0,
false, 0, 2, precision );
4144 bool isRing =
false;
4145 if ( GEOSGeomTypeId_r( context, line ) == GEOS_LINEARRING || GEOSEquals_r( context, beginLineVertex.get(), endLineVertex.get() ) == 1 )
4150 if ( !nodedGeometry )
4156 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, nodedGeometry.get() ) );
4162 int numMergedLines = GEOSGetNumGeometries_r( context, mergedLines.get() );
4163 if ( numMergedLines < 2 )
4165 if ( numMergedLines == 1 )
4174 QVector<GEOSGeometry *> resultLineParts;
4175 QVector<GEOSGeometry *> probableParts;
4177 for (
int i = 0; i < numMergedLines; ++i )
4179 const GEOSGeometry *currentGeom = GEOSGetGeometryN_r( context, mergedLines.get(), i );
4182 bool alreadyAdded =
false;
4184 double bufferDistance = std::pow( 10.0L, geomDigits( currentGeom ) - 11 );
4185 for (
const GEOSGeometry *other : std::as_const( resultLineParts ) )
4187 GEOSHausdorffDistance_r( context, currentGeom, other, &
distance );
4190 alreadyAdded =
true;
4197 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( context, currentGeom );
4198 unsigned int currentCoordSeqSize;
4199 GEOSCoordSeq_getSize_r( context, currentCoordSeq, ¤tCoordSeqSize );
4200 if ( currentCoordSeqSize < 2 )
4204 double xBegin, xEnd, yBegin, yEnd;
4205 GEOSCoordSeq_getX_r( context, currentCoordSeq, 0, &xBegin );
4206 GEOSCoordSeq_getY_r( context, currentCoordSeq, 0, &yBegin );
4207 GEOSCoordSeq_getX_r( context, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
4208 GEOSCoordSeq_getY_r( context, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
4209 geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin,
false, 0,
false, 0, 2, precision );
4210 geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd,
false, 0,
false, 0, 2, precision );
4213 int nEndpointsOnOriginalLine = 0;
4214 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
4215 nEndpointsOnOriginalLine += 1;
4217 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
4218 nEndpointsOnOriginalLine += 1;
4221 int nEndpointsSameAsOriginalLine = 0;
4222 if ( GEOSEquals_r( context, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1 || GEOSEquals_r( context, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
4223 nEndpointsSameAsOriginalLine += 1;
4225 if ( GEOSEquals_r( context, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1 || GEOSEquals_r( context, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
4226 nEndpointsSameAsOriginalLine += 1;
4229 bool currentGeomOverlapsOriginalGeom =
false;
4230 bool currentGeomOverlapsReshapeLine =
false;
4231 if ( lineContainedInLine( currentGeom, line ) == 1 )
4232 currentGeomOverlapsOriginalGeom =
true;
4234 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
4235 currentGeomOverlapsReshapeLine =
true;
4238 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
4240 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
4243 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
4245 probableParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
4247 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
4249 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
4251 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
4253 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
4255 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
4257 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
4262 if ( isRing && !probableParts.isEmpty() )
4266 double maxLength = -std::numeric_limits<double>::max();
4267 double currentLength = 0;
4268 for (
int i = 0; i < probableParts.size(); ++i )
4270 currentGeom = probableParts.at( i );
4271 GEOSLength_r( context, currentGeom, ¤tLength );
4272 if ( currentLength > maxLength )
4274 maxLength = currentLength;
4275 maxGeom.reset( currentGeom );
4279 GEOSGeom_destroy_r( context, currentGeom );
4282 resultLineParts.push_back( maxGeom.release() );
4286 if ( resultLineParts.empty() )
4289 if ( resultLineParts.size() == 1 )
4291 result.reset( resultLineParts[0] );
4296 for (
int i = 0; i < resultLineParts.size(); ++i )
4298 lineArray[i] = resultLineParts[i];
4302 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
4306 result.reset( GEOSLineMerge_r( context, multiLineGeom.get() ) );
4310 if ( GEOSGeomTypeId_r( context, result.get() ) != GEOS_LINESTRING )
4316 bool reverseLine =
false;
4320 char isResultCCW = 0, isOriginCCW = 0;
4321 if ( GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, result.get() ), &isResultCCW ) && GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, line ), &isOriginCCW ) )
4324 reverseLine = ( isOriginCCW == 1 && isResultCCW == 0 ) || ( isOriginCCW == 0 && isResultCCW == 1 );
4330 double x1res, y1res, x2res, y2res;
4331 if ( !_linestringEndpoints( result.get(), x1res, y1res, x2res, y2res ) )
4333 geos::unique_ptr beginResultLineVertex = createGeosPointXY( x1res, y1res,
false, 0,
false, 0, 2, precision );
4334 geos::unique_ptr endResultLineVertex = createGeosPointXY( x2res, y2res,
false, 0,
false, 0, 2, precision );
4335 reverseLine = GEOSEquals_r( context, beginLineVertex.get(), endResultLineVertex.get() ) == 1 || GEOSEquals_r( context, endLineVertex.get(), beginResultLineVertex.get() ) == 1;
4338 result.reset( GEOSReverse_r( context, result.get() ) );
4346 int nIntersections = 0;
4347 int lastIntersectingRing = -2;
4351 int nRings = GEOSGetNumInteriorRings_r( context, polygon );
4356 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( context, polygon );
4357 if ( GEOSIntersects_r( context, outerRing, reshapeLineGeos ) == 1 )
4360 lastIntersectingRing = -1;
4361 lastIntersectingGeom = outerRing;
4369 for (
int i = 0; i < nRings; ++i )
4371 innerRings[i] = GEOSGetInteriorRingN_r( context, polygon, i );
4372 if ( GEOSIntersects_r( context, innerRings[i], reshapeLineGeos ) == 1 )
4375 lastIntersectingRing = i;
4376 lastIntersectingGeom = innerRings[i];
4380 catch ( QgsGeosException & )
4385 if ( nIntersections != 1 )
4387 delete[] innerRings;
4392 geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
4393 if ( !reshapeResult )
4395 delete[] innerRings;
4401 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( context, reshapeResult.get() );
4402 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( context, reshapeSequence );
4404 reshapeResult.reset();
4408 newRing = GEOSGeom_createLinearRing_r( context, newCoordSequence );
4410 catch ( QgsGeosException & )
4417 delete[] innerRings;
4422 if ( lastIntersectingRing == -1 )
4423 newOuterRing = newRing;
4425 newOuterRing = GEOSGeom_clone_r( context, outerRing );
4428 QVector<GEOSGeometry *> ringList;
4431 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( context, GEOSGeom_clone_r( context, newOuterRing ),
nullptr, 0 );
4432 if ( outerRingPoly )
4434 ringList.reserve( nRings );
4436 for (
int i = 0; i < nRings; ++i )
4438 if ( lastIntersectingRing == i )
4439 currentRing = newRing;
4441 currentRing = GEOSGeom_clone_r( context, innerRings[i] );
4444 if ( GEOSContains_r( context, outerRingPoly, currentRing ) == 1 )
4445 ringList.push_back( currentRing );
4447 GEOSGeom_destroy_r( context, currentRing );
4450 GEOSGeom_destroy_r( context, outerRingPoly );
4454 for (
int i = 0; i < ringList.size(); ++i )
4455 newInnerRings[i] = ringList.at( i );
4457 delete[] innerRings;
4459 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( context, newOuterRing, newInnerRings, ringList.size() ) );
4460 delete[] newInnerRings;
4462 return reshapedPolygon;
4467 if ( !line1 || !line2 )
4472 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
4479 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, bufferGeom.get(), line1 ) );
4482 double intersectGeomLength;
4485 GEOSLength_r( context, intersectionGeom.get(), &intersectGeomLength );
4486 GEOSLength_r( context, line1, &line1Length );
4488 double intersectRatio = line1Length / intersectGeomLength;
4489 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
4497 if ( !point || !line )
4500 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
4503 geos::unique_ptr lineBuffer( GEOSBuffer_r( context, line, bufferDistance, 8 ) );
4507 bool contained =
false;
4508 if ( GEOSContains_r( context, lineBuffer.get(), point ) == 1 )
4521 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( context, bbox.get() );
4525 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( context, bBoxRing );
4527 if ( !bBoxCoordSeq )
4530 unsigned int nCoords = 0;
4531 if ( !GEOSCoordSeq_getSize_r( context, bBoxCoordSeq, &nCoords ) )
4535 for (
unsigned int i = 0; i < nCoords - 1; ++i )
4538 GEOSCoordSeq_getX_r( context, bBoxCoordSeq, i, &t );
4541 digits = std::ceil( std::log10( std::fabs( t ) ) );
4542 if ( digits > maxDigits )
4545 GEOSCoordSeq_getY_r( context, bBoxCoordSeq, i, &t );
4546 digits = std::ceil( std::log10( std::fabs( t ) ) );
4547 if ( digits > maxDigits )
4555#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4556 : mFeedback( feedback )
4558 GEOSContext_setInterruptCallback_r(
QgsGeosContext::get(), &callback,
reinterpret_cast< void *
>( mFeedback ) );
4569#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4574#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4575int QgsScopedGeosContextRegisterFeedback::callback(
void *userData )
4581 return feedback && feedback->
isCanceled() ? 1 : 0;
Provides global constants and enumerations for use throughout the application.
BufferSide
Side of line to buffer.
@ Right
Buffer to right of line.
@ LongestBorder
Polygon with longest common border is selected to merge overlapping polygons into.
@ MaximumArea
Polygon with largest area is selected to merge overlapping polygons into.
@ MinimumArea
Polygon with minimum area is selected to merge overlapping polygons into.
@ MinimumIndex
Polygon with smallest input index is selected to merge overlapping polygons into.
GeometryOperationResult
Success or failure of a geometry operation.
@ AddPartNotMultiGeometry
The source geometry is not multi.
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
@ RejectOnInvalidSubGeometry
Don't allow geometries with invalid sub-geometries to be created.
@ SkipEmptyInteriorRings
Skip any empty polygon interior ring.
QFlags< GeosCreationFlag > GeosCreationFlags
Geos geometry creation behavior flags.
JoinStyle
Join styles for buffers.
EndCapStyle
End cap styles for buffers.
CoverageValidityResult
Coverage validity results.
@ Valid
Coverage is valid.
@ Invalid
Coverage is invalid. Invalidity includes polygons that overlap, that have gaps smaller than the gap w...
@ Error
An exception occurred while determining validity.
MakeValidMethod
Algorithms to use when repairing invalid geometries.
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
WkbType
The WKB type describes the number of dimensions a geometry has.
@ CompoundCurve
CompoundCurve.
@ LineStringZM
LineStringZM.
@ MultiPolygon
MultiPolygon.
@ MultiLineString
MultiLineString.
@ CircularString
CircularString.
@ GeometryCollection
GeometryCollection.
@ CurvePolygon
CurvePolygon.
@ LineStringZ
LineStringZ.
@ PolyhedralSurface
PolyhedralSurface.
@ MultiSurface
MultiSurface.
Abstract base class for all geometries.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
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.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual bool isEmpty() const
Returns true if the geometry is empty.
Encapsulates parameters for a coverage cleaning operation.
double maximumGapWidth() const
Returns the maximum gap width.
Qgis::CoverageCleanOverlapMergeStrategy overlapMergeStrategy() const
Returns the overlap merge strategy to use during cleaning.
double snappingDistance() const
Returns the snapping distance.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
Abstract base class for curved geometry type.
virtual bool isSimpleCurve() const =0
This method returns true for classes that can be cast to the QgsSimpleCurve class.
virtual QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
bool isCanceled() const
Tells whether the operation has been canceled already.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
static Qgis::GeometryOperationResult addPart(QgsAbstractGeometry *geometry, std::unique_ptr< QgsAbstractGeometry > part)
Add a part to multi type geometry.
const QgsAbstractGeometry * mGeometry
EngineOperationResult
Success or failure of a geometry operation.
@ NothingHappened
Nothing happened, without any error.
@ InvalidBaseGeometry
The geometry on which the operation occurs is not valid.
@ InvalidInput
The input is not valid.
@ NodedGeometryError
Error occurred while creating a noded geometry.
@ EngineError
Error occurred in the geometry engine.
@ SplitCannotSplitPoint
Points cannot be split.
@ Success
Operation succeeded.
@ MethodNotImplemented
Method not implemented in geometry engine.
QgsGeometryEngine(const QgsAbstractGeometry *geometry)
void logError(const QString &engineName, const QString &message) const
Logs an error message encountered during an operation.
static std::unique_ptr< QgsGeometryCollection > createCollectionOfType(Qgis::WkbType type)
Returns a new geometry collection matching a specified WKB type.
Encapsulates parameters under which a geometry operation is performed.
double gridSize() const
Returns the grid size which will be used to snap vertices of a geometry.
static double sqrDistance2D(double x1, double y1, double x2, double y2)
Returns the squared 2D distance between (x1, y1) and (x2, y2).
A geometry is the spatial representation of a feature.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
Used to create and store a GEOS context object, correctly freeing the context upon destruction.
static GEOSContextHandle_t get()
Returns a thread local instance of a GEOS context, safe for use in the current thread.
std::unique_ptr< QgsAbstractGeometry > singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a single sided buffer for a geometry.
double minimumClearance(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Computes the minimum clearance of a geometry.
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom intersects this.
bool distanceWithin(const QgsAbstractGeometry *geom, double maxdistance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom is within maxdistance distance from this geometry.
std::unique_ptr< QgsAbstractGeometry > concaveHull(double targetPercent, bool allowHoles=false, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a possibly concave geometry that encloses the input geometry.
std::unique_ptr< QgsAbstractGeometry > reshapeGeometry(const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg=nullptr) const
Reshapes the geometry using a line.
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Calculates the distance between this and geom.
std::unique_ptr< QgsAbstractGeometry > sharedPaths(const QgsAbstractGeometry *other, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Find paths shared between the two given lineal geometries (this and other).
std::unique_ptr< QgsAbstractGeometry > minimumClearanceLine(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a LineString whose endpoints define the minimum clearance of a geometry.
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlags())
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
QgsAbstractGeometry * symDifference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const override
Calculate the symmetric difference of this and geom.
std::unique_ptr< QgsAbstractGeometry > closestPoint(const QgsGeometry &other, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the closest point on the geometry to the other geometry.
std::unique_ptr< QgsAbstractGeometry > unionCoverage(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Optimized union algorithm for polygonal inputs that are correctly noded and do not overlap.
bool isFuzzyEqual(const QgsAbstractGeometry *geom, double epsilon, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if this is equal to geom ie.
QgsAbstractGeometry * simplify(double tolerance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Simplifies the geometery.
static const QgsSettingsEntryBool * settingLineToCurveParam
Settings entry - Whether to convert any linear output of a GEOS method to a curved type,...
static geos::unique_ptr offsetCurve(const GEOSGeometry *geometry, double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr)
Directly calculates the offset curve for a GEOS geometry object and returns a GEOS geometry result.
std::unique_ptr< QgsAbstractGeometry > cleanCoverage(const QgsCoverageCleanParameters ¶meters, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Operates on a coverage (represented as a list of polygonal geometry), to fix cases where the geometry...
QgsAbstractGeometry * intersection(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const override
Calculate the intersection of this and geom.
double lineLocatePoint(const QgsPoint &point, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a distance representing the location along this linestring of the closest point on this lines...
std::unique_ptr< QgsAbstractGeometry > subdivide(int maxNodes, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Subdivides the geometry.
bool touches(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom touches this.
static std::unique_ptr< QgsPolygon > fromGeosPolygon(const GEOSGeometry *geos)
std::unique_ptr< QgsAbstractGeometry > largestEmptyCircle(double tolerance, const QgsAbstractGeometry *boundary=nullptr, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Constructs the Largest Empty Circle for a set of obstacle geometries, up to a specified tolerance.
QgsAbstractGeometry * envelope(QString *errorMsg=nullptr) const override
Qgis::CoverageValidityResult validateCoverage(double gapWidth, std::unique_ptr< QgsAbstractGeometry > *invalidEdges, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Analyze a coverage (represented as a collection of polygonal geometry with exactly matching edge geom...
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Buffers the geometry.
QString relate(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Returns the Dimensional Extended 9 Intersection Model (DE-9IM) representation of the relationship bet...
std::unique_ptr< QgsAbstractGeometry > constrainedDelaunayTriangulation(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a constrained Delaunay triangulation for the vertices of the geometry.
bool within(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom is within this.
bool contains(double x, double y, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns true if the geometry contains the point at (x, y).
bool isSimple(QString *errorMsg=nullptr) const override
Determines whether the geometry is simple (according to OGC definition).
bool isValid(QString *errorMsg=nullptr, bool allowSelfTouchingHoles=false, QgsGeometry *errorLoc=nullptr, QgsFeedback *feedback=nullptr) const override
Returns true if the geometry is valid.
std::unique_ptr< QgsAbstractGeometry > minimumWidth(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a linestring geometry which represents the minimum diameter of the geometry.
std::unique_ptr< QgsAbstractGeometry > simplifyCoverageVW(double tolerance, bool preserveBoundary, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Operates on a coverage (represented as a list of polygonal geometry with exactly matching edge geomet...
QgsGeos(const QgsAbstractGeometry *geometry, double precision=0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
GEOS geometry engine constructor.
std::unique_ptr< QgsAbstractGeometry > shortestLine(const QgsGeometry &other, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the shortest line joining this geometry to the other geometry.
QgsAbstractGeometry * convexHull(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Calculate the convex hull of this geometry.
void prepareGeometry() override
Prepares the geometry, so that subsequent calls to spatial relation methods are much faster.
std::unique_ptr< QgsAbstractGeometry > makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Repairs the geometry using GEOS make valid routine.
QgsPoint * pointOnSurface(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Calculate a point that is guaranteed to be on the surface of this.
static std::unique_ptr< QgsAbstractGeometry > fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
std::unique_ptr< QgsAbstractGeometry > node(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a (Multi)LineString representing the fully noded version of a collection of linestrings.
QgsAbstractGeometry * combine(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const override
Calculate the combination of this and geom.
bool disjoint(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom is disjoint from this.
bool relatePattern(const QgsAbstractGeometry *geom, const QString &pattern, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Tests whether two geometries are related by a specified Dimensional Extended 9 Intersection Model (DE...
double hausdorffDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
bool isEqual(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Check if geometries are topologically equivalent.
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the maximum inscribed circle.
std::unique_ptr< QgsAbstractGeometry > voronoiDiagram(const QgsAbstractGeometry *extent=nullptr, double tolerance=0.0, bool edgesOnly=false, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Creates a Voronoi diagram for the nodes contained within the geometry.
double frechetDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
std::unique_ptr< QgsAbstractGeometry > delaunayTriangulation(double tolerance=0.0, bool edgesOnly=false, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Delaunay triangulation for the vertices of the geometry.
bool crosses(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom crosses this.
std::unique_ptr< QgsAbstractGeometry > concaveHullOfPolygons(double lengthRatio, bool allowHoles=false, bool isTight=false, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Constructs a concave hull of a set of polygons, respecting the polygons as constraints.
std::unique_ptr< QgsAbstractGeometry > clip(const QgsRectangle &rectangle, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Performs a fast, non-robust intersection between the geometry and a rectangle.
bool isEmpty(QString *errorMsg=nullptr) const override
static Qgis::GeometryOperationResult addPart(QgsGeometry &geometry, GEOSGeometry *newPart)
Adds a new island polygon to a multipolygon feature.
bool overlaps(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom overlaps this.
QgsPoint * centroid(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Calculates the centroid of this.
std::unique_ptr< QgsAbstractGeometry > mergeLines(QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
QgsAbstractGeometry * difference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const override
Calculate the difference of this and geom.
void geometryChanged() override
Should be called whenever the geometry associated with the engine has been modified and the engine mu...
double area(QString *errorMsg=nullptr) const override
double length(QString *errorMsg=nullptr) const override
double hausdorffDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
double frechetDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
QgsAbstractGeometry * interpolate(double distance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Interpolates a point by distance along the geometry.
static QgsPoint coordSeqPoint(const GEOSCoordSequence *cs, int i, bool hasZ, bool hasM)
static QgsGeometry polygonize(const QVector< const QgsAbstractGeometry * > &geometries, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
static QgsGeometry geometryFromGeos(GEOSGeometry *geos)
Creates a new QgsGeometry object, feeding in a geometry in GEOS format.
QgsGeometryEngine::EngineOperationResult splitGeometry(const QgsAbstractGeometry &splitGeom, QVector< QgsGeometry > &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, QString *errorMsg=nullptr) const override
Splits this geometry according to a given geometry.
Line string geometry type, with support for z-dimension and m-values.
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 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.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
Point geometry type, with support for z-dimension and m-values.
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
bool isEmpty() const override
Returns true if the geometry is empty.
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Polyhedral surface geometry type.
int numPatches() const
Returns the number of patches contained with the polyhedral surface.
const QgsPolygon * patchN(int i) const
Retrieves a patch from the polyhedral surface.
A rectangle specified with double values.
void setYMinimum(double y)
Set the minimum y value.
void setXMinimum(double x)
Set the minimum x value.
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
Scoped object for setting the current thread GEOS context feedback object.
QgsScopedGeosContextRegisterFeedback(QgsFeedback *feedback)
Registers a feedback object for GEOS interruption checking.
~QgsScopedGeosContextRegisterFeedback()
Resets the GEOS interruption checker for the current thread.
static QgsSettingsTreeNode * sTreeGeos
Abstract base class for simple curved geometry type.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
QVector< double > yVector() const
Returns the y vertex values as a vector.
int numPoints() const override
Returns the number of points in the curve.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the simple curve does not have m values.
QVector< double > zVector() const
Returns the z vertex values as a vector.
QVector< double > xVector() const
Returns the x vertex values as a vector.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
const double * yData() const
Returns a const pointer to the y vertex data.
QgsPoint pointN(int i) const
Returns the specified point from inside the simple curve.
const double * xData() const
Returns a const pointer to the x vertex data.
QVector< double > mVector() const
Returns the m vertex values as a vector.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the simple curve does not have z values.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
static Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
Contains geos related utilities and functions.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
std::unique_ptr< GEOSCoordSequence, GeosDeleter > coord_sequence_unique_ptr
Scoped GEOS coordinate sequence pointer.
std::unique_ptr< GEOSBufferParams, GeosDeleter > buffer_params_unique_ptr
Scoped GEOS buffer params pointer.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
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
#define DEFAULT_QUADRANT_SEGMENTS
#define CATCH_GEOS_WITH_ERRMSG(r)
#define QgsDebugError(str)
QLineF segment(int index, QRectF rect, double radius)
Utility class for identifying a unique vertex within a geometry.
void CORE_EXPORT operator()(GEOSGeometry *geom) const
Destroys the GEOS geometry geom, using the static QGIS geos context.
struct GEOSGeom_t GEOSGeometry