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 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1731 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1732 bool hasZ = ( nCoordDims == 3 );
1733 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1735 switch ( GEOSGeomTypeId_r( context,
geos ) )
1739 if ( GEOSisEmpty_r( context,
geos ) )
1742 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1743 unsigned int nPoints = 0;
1744 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1753 return !point.
isEmpty() ? std::unique_ptr<QgsAbstractGeometry>( point.
clone() ) :
nullptr;
1755 case GEOS_LINESTRING:
1757 return sequenceToLinestring(
geos, hasZ, hasM );
1759#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
1760 case GEOS_CIRCULARSTRING:
1762 return sequenceToCircularString(
geos, hasZ, hasM );
1764 case GEOS_COMPOUNDCURVE:
1766 auto compoundCurve = std::make_unique< QgsCompoundCurve >();
1767 const int nCurves = GEOSGetNumCurves_r( context,
geos );
1768 for (
int i = 0; i < nCurves; i++ )
1770 std::unique_ptr< QgsSimpleCurve > curve = sequenceToSimpleCurve( GEOSGetCurveN_r( context,
geos, i ), hasZ, hasM );
1771 compoundCurve->addCurve( curve.release(),
true );
1773 return std::move( compoundCurve );
1780#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
1781 case GEOS_CURVEPOLYGON:
1783 return fromGeosCurvePolygon(
geos );
1786 case GEOS_MULTIPOINT:
1788 auto multiPoint = std::make_unique<QgsMultiPoint>();
1789 int nParts = GEOSGetNumGeometries_r( context,
geos );
1790 multiPoint->reserve( nParts );
1791 for (
int i = 0; i < nParts; ++i )
1793 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context, GEOSGetGeometryN_r( context,
geos, i ) );
1796 unsigned int nPoints = 0;
1797 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1799 multiPoint->addGeometry(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1802 return std::move( multiPoint );
1804 case GEOS_MULTILINESTRING:
1806 auto multiLineString = std::make_unique<QgsMultiLineString>();
1807 int nParts = GEOSGetNumGeometries_r( context,
geos );
1808 multiLineString->reserve( nParts );
1809 for (
int i = 0; i < nParts; ++i )
1811 std::unique_ptr< QgsLineString > line( sequenceToLinestring( GEOSGetGeometryN_r( context,
geos, i ), hasZ, hasM ) );
1814 multiLineString->addGeometry( line.release() );
1817 return std::move( multiLineString );
1819#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
1820 case GEOS_MULTICURVE:
1822 auto multiCurve = std::make_unique<QgsMultiCurve>();
1823 int nParts = GEOSGetNumGeometries_r( context,
geos );
1824 multiCurve->reserve( nParts );
1825 for (
int i = 0; i < nParts; ++i )
1827 std::unique_ptr< QgsAbstractGeometry > curve(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1828 multiCurve->addGeometry( curve.release() );
1830 return std::move( multiCurve );
1833 case GEOS_MULTIPOLYGON:
1835 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
1837 int nParts = GEOSGetNumGeometries_r( context,
geos );
1838 multiPolygon->reserve( nParts );
1839 for (
int i = 0; i < nParts; ++i )
1841 std::unique_ptr< QgsPolygon > poly =
fromGeosPolygon( GEOSGetGeometryN_r( context,
geos, i ) );
1844 multiPolygon->addGeometry( poly.release() );
1847 return std::move( multiPolygon );
1849#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
1850 case GEOS_MULTISURFACE:
1852 auto multiSurface = std::make_unique<QgsMultiSurface>();
1853 int nParts = GEOSGetNumGeometries_r( context,
geos );
1854 multiSurface->reserve( nParts );
1855 for (
int i = 0; i < nParts; ++i )
1857 std::unique_ptr< QgsAbstractGeometry > polygon(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1858 multiSurface->addGeometry( polygon.release() );
1860 return std::move( multiSurface );
1863 case GEOS_GEOMETRYCOLLECTION:
1865 auto geomCollection = std::make_unique<QgsGeometryCollection>();
1866 int nParts = GEOSGetNumGeometries_r( context,
geos );
1867 geomCollection->reserve( nParts );
1868 for (
int i = 0; i < nParts; ++i )
1870 std::unique_ptr< QgsAbstractGeometry > geom(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1873 geomCollection->addGeometry( geom.release() );
1876 return std::move( geomCollection );
1882#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
1883std::unique_ptr<QgsCurvePolygon> QgsGeos::fromGeosCurvePolygon(
const GEOSGeometry *
geos )
1886 if ( GEOSGeomTypeId_r( context,
geos ) != GEOS_CURVEPOLYGON )
1891 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context, geos );
1892 int nDims = GEOSGeom_getDimensions_r( context, geos );
1893 bool hasZ = ( nCoordDims == 3 );
1894 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1896 auto curvePolygon = std::make_unique<QgsCurvePolygon>();
1898 const GEOSGeometry *ring = GEOSGetExteriorRing_r( context, geos );
1901 if ( GEOSGeomTypeId_r( context, ring ) == GEOS_COMPOUNDCURVE )
1903 curvePolygon->setExteriorRing( qgis::down_cast< QgsCompoundCurve *>(
fromGeos( ring ).release() ) );
1907 curvePolygon->setExteriorRing( sequenceToSimpleCurve( ring, hasZ, hasM ).release() );
1911 QVector<QgsCurve *> interiorRings;
1912 const int ringCount = GEOSGetNumInteriorRings_r( context, geos );
1913 interiorRings.reserve( ringCount );
1914 for (
int i = 0; i < ringCount; ++i )
1916 ring = GEOSGetInteriorRingN_r( context, geos, i );
1919 if ( GEOSGeomTypeId_r( context, ring ) == GEOS_COMPOUNDCURVE )
1921 interiorRings.push_back( qgis::down_cast< QgsCompoundCurve *>(
fromGeos( ring ).release() ) );
1925 interiorRings.push_back( sequenceToSimpleCurve( ring, hasZ, hasM ).release() );
1929 curvePolygon->setInteriorRings( interiorRings );
1931 return curvePolygon;
1938 if ( GEOSGeomTypeId_r( context,
geos ) != GEOS_POLYGON )
1943 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1944 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1945 bool hasZ = ( nCoordDims == 3 );
1946 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1948 auto polygon = std::make_unique<QgsPolygon>();
1953 polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1956 QVector<QgsCurve *> interiorRings;
1957 const int ringCount = GEOSGetNumInteriorRings_r( context,
geos );
1958 interiorRings.reserve( ringCount );
1959 for (
int i = 0; i < ringCount; ++i )
1961 ring = GEOSGetInteriorRingN_r( context,
geos, i );
1964 interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1967 polygon->setInteriorRings( interiorRings );
1972std::unique_ptr<QgsSimpleCurve> QgsGeos::sequenceToSimpleCurve(
const GEOSGeometry *
geos,
bool hasZ,
bool hasM )
1976 const int geometryType = GEOSGeomTypeId_r( context,
geos );
1977 if ( !( geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING || geometryType == GEOS_CIRCULARSTRING ) )
1980 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1982 unsigned int nPoints;
1983 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1985 QVector< double > xOut( nPoints );
1986 QVector< double > yOut( nPoints );
1987 QVector< double > zOut;
1989 zOut.resize( nPoints );
1990 QVector< double > mOut;
1992 mOut.resize( nPoints );
1994 double *x = xOut.data();
1995 double *y = yOut.data();
1996 double *z = zOut.data();
1997 double *m = mOut.data();
1999#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
2000 GEOSCoordSeq_copyToArrays_r( context, cs, x, y, hasZ ? z :
nullptr, hasM ? m :
nullptr );
2002 for (
unsigned int i = 0; i < nPoints; ++i )
2005 GEOSCoordSeq_getXYZ_r( context, cs, i, x++, y++, z++ );
2007 GEOSCoordSeq_getXY_r( context, cs, i, x++, y++ );
2010 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, m++ );
2015 std::unique_ptr< QgsSimpleCurve > simpleCurve;
2016 if ( geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING )
2018 simpleCurve = std::make_unique<QgsLineString>( xOut, yOut, zOut, mOut );
2020 else if ( geometryType == GEOS_CIRCULARSTRING )
2022 simpleCurve = std::make_unique<QgsCircularString>( xOut, yOut, zOut, mOut );
2027std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring(
const GEOSGeometry *geos,
bool hasZ,
bool hasM )
2029 return std::unique_ptr<QgsLineString>( qgis::down_cast<QgsLineString *>( sequenceToSimpleCurve( geos, hasZ, hasM ).release() ) );
2032#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
2033std::unique_ptr<QgsCircularString> QgsGeos::sequenceToCircularString(
const GEOSGeometry *geos,
bool hasZ,
bool hasM )
2035 return std::unique_ptr<QgsCircularString>( qgis::down_cast<QgsCircularString *>( sequenceToSimpleCurve( geos, hasZ, hasM ).release() ) );
2045 int geometryType = GEOSGeomTypeId_r( context, g );
2046 if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING || geometryType == GEOS_POLYGON )
2050 return GEOSGetNumGeometries_r( context, g );
2066 GEOSCoordSeq_getXYZ_r( context, cs, i, &x, &y, &z );
2068 GEOSCoordSeq_getXY_r( context, cs, i, &x, &y );
2071 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, &m );
2111 geosType = GEOS_MULTIPOINT;
2115#if !( GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 ) )
2118 geosType = GEOS_MULTILINESTRING;
2122#if !( GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 ) )
2125 geosType = GEOS_MULTIPOLYGON;
2128#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
2130 geosType = GEOS_MULTICURVE;
2134 geosType = GEOS_MULTISURFACE;
2139 geosType = GEOS_GEOMETRYCOLLECTION;
2153 std::vector<geos::unique_ptr> geomVector;
2154 geomVector.reserve(
c->numGeometries() );
2155 for (
int i = 0; i <
c->numGeometries(); ++i )
2162 geomVector.emplace_back( std::move( geosGeom ) );
2164 return createGeosCollection( geosType, geomVector );
2171 return createGeosPoint( geom, coordDims, precision, flags );
2176#if !( GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 ) )
2178 return createGeosLinestring( geom, precision, flags );
2180 return createGeosSimpleCurve( geom, precision, flags );
2183 return createGeosCompoundCurve( geom, precision, flags );
2188#if !( GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 ) )
2191 return createGeosPolygon( geom, precision, flags );
2193#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
2195 return createGeosCurvePolygon( geom, precision, flags );
2204 if ( !polyhedralSurface )
2207 std::vector<geos::unique_ptr> geomVector;
2208 geomVector.reserve( polyhedralSurface->
numPatches() );
2209 for (
int i = 0; i < polyhedralSurface->
numPatches(); ++i )
2216 geomVector.emplace_back( std::move( geosPolygon ) );
2219 return createGeosCollection( GEOS_MULTIPOLYGON, geomVector );
2233 if ( !mGeos || !geom )
2244 QgsScopedGeosContextRegisterFeedback interrupt( feedback );
2246 const double gridSize = parameters.
gridSize();
2254 case OverlayIntersection:
2257 opGeom.reset( GEOSIntersectionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
2261 opGeom.reset( GEOSIntersection_r( context, mGeos.get(), geosGeom.get() ) );
2265 case OverlayDifference:
2268 opGeom.reset( GEOSDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
2272 opGeom.reset( GEOSDifference_r( context, mGeos.get(), geosGeom.get() ) );
2281 unionGeometry.reset( GEOSUnionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
2285 unionGeometry.reset( GEOSUnion_r( context, mGeos.get(), geosGeom.get() ) );
2288#if !( GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 ) )
2289 if ( unionGeometry && GEOSGeomTypeId_r( context, unionGeometry.get() ) == GEOS_MULTILINESTRING )
2291 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, unionGeometry.get() ) );
2294 unionGeometry = std::move( mergedLines );
2299 opGeom = std::move( unionGeometry );
2303 case OverlaySymDifference:
2306 opGeom.reset( GEOSSymDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
2310 opGeom.reset( GEOSSymDifference_r( context, mGeos.get(), geosGeom.get() ) );
2316 catch ( QgsGeosException &e )
2321 *errorMsg = e.what();
2327bool QgsGeos::relation(
const QgsAbstractGeometry *geom, Relation r, QString *errorMsg, QgsFeedback *feedback )
const
2329 if ( !mGeos || !geom )
2341 QgsScopedGeosContextRegisterFeedback interrupt( feedback );
2343 bool result =
false;
2346 if ( mGeosPrepared )
2350 case RelationIntersects:
2351 result = ( GEOSPreparedIntersects_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2353 case RelationTouches:
2354 result = ( GEOSPreparedTouches_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2356 case RelationCrosses:
2357 result = ( GEOSPreparedCrosses_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2359 case RelationWithin:
2360 result = ( GEOSPreparedWithin_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2362 case RelationContains:
2363 result = ( GEOSPreparedContains_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2365 case RelationDisjoint:
2366 result = ( GEOSPreparedDisjoint_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2368 case RelationOverlaps:
2369 result = ( GEOSPreparedOverlaps_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2377 case RelationIntersects:
2378 result = ( GEOSIntersects_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2380 case RelationTouches:
2381 result = ( GEOSTouches_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2383 case RelationCrosses:
2384 result = ( GEOSCrosses_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2386 case RelationWithin:
2387 result = ( GEOSWithin_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2389 case RelationContains:
2390 result = ( GEOSContains_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2392 case RelationDisjoint:
2393 result = ( GEOSDisjoint_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2395 case RelationOverlaps:
2396 result = ( GEOSOverlaps_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2400 catch ( QgsGeosException &e )
2405 *errorMsg = e.what();
2450 geos.reset( GEOSBufferWithStyle_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( endCapStyle ),
static_cast< int >( joinStyle ), miterLimit ) );
2503 geos.reset( GEOSGetCentroid_r( context, mGeos.get() ) );
2508 GEOSGeomGetX_r( context,
geos.get(), &x );
2509 GEOSGeomGetY_r( context,
geos.get(), &y );
2546 geos.reset( GEOSPointOnSurface_r( context, mGeos.get() ) );
2548 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
2553 GEOSGeomGetX_r( context,
geos.get(), &x );
2554 GEOSGeomGetY_r( context,
geos.get(), &y );
2572 std::unique_ptr< QgsAbstractGeometry > cHullGeom =
fromGeos( cHull.get() );
2573 return cHullGeom.release();
2580#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
2581 ( void ) allowHoles;
2582 ( void ) targetPercent;
2584 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHull requires a QGIS build based on GEOS 3.11 or later" ) );
2596 return concaveHullGeom;
2604#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
2605 ( void ) allowHoles;
2606 ( void ) targetPercent;
2608 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHullOfPolygons requires a QGIS build based on GEOS 3.11 or later" ) );
2620 return concaveHullGeom;
2628#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12
2630 ( void ) invalidEdges;
2632 throw QgsNotSupportedException( QObject::tr(
"Validating coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2637 *errorMsg = u
"Input geometry was not set"_s;
2646 const int result = GEOSCoverageIsValid_r( context, mGeos.get(), gapWidth, invalidEdges ? &invalidEdgesGeos :
nullptr );
2647 if ( invalidEdges && invalidEdgesGeos )
2649 *invalidEdges =
fromGeos( invalidEdgesGeos );
2651 if ( invalidEdgesGeos )
2653 GEOSGeom_destroy_r( context, invalidEdgesGeos );
2654 invalidEdgesGeos =
nullptr;
2674#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12
2676 ( void ) preserveBoundary;
2678 throw QgsNotSupportedException( QObject::tr(
"Simplifying coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2683 *errorMsg = u
"Input geometry was not set"_s;
2691 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom =
fromGeos( simplified.get() );
2692 return simplifiedGeom;
2703 *errorMsg = u
"Input geometry was not set"_s;
2711 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( unioned.get() );
2719#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 14
2720 ( void ) parameters;
2723 throw QgsNotSupportedException( QObject::tr(
"Cleaning coverages requires a QGIS build based on GEOS 3.14 or later" ) );
2728 *errorMsg = u
"Input geometry was not set"_s;
2732 GEOSCoverageCleanParams *params =
nullptr;
2761 std::unique_ptr< QgsAbstractGeometry> cleanedGeom =
fromGeos( cleaned.get() );
2765 catch ( QgsGeosException &e )
2775 *errorMsg = e.what();
2776 if ( errorMsg->startsWith(
"InterruptedException"_L1, Qt::CaseInsensitive ) )
2791 *errorMsg = QObject::tr(
"QGIS geometry cannot be converted to a GEOS geometry",
"GEOS Error" );
2801 char res = GEOSisValidDetail_r( context, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
2802 const bool invalid = res != 1;
2807 error = QString( r );
2808 GEOSFree_r( context, r );
2811 if ( invalid && errorMsg )
2814 static const std::map< QString, QString > sTranslatedErrors {
2815 { u
"topology validation error"_s, QObject::tr(
"Topology validation error",
"GEOS Error" ) },
2816 { u
"repeated point"_s, QObject::tr(
"Repeated point",
"GEOS Error" ) },
2817 { u
"hole lies outside shell"_s, QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) },
2818 { u
"holes are nested"_s, QObject::tr(
"Holes are nested",
"GEOS Error" ) },
2819 { u
"interior is disconnected"_s, QObject::tr(
"Interior is disconnected",
"GEOS Error" ) },
2820 { u
"self-intersection"_s, QObject::tr(
"Self-intersection",
"GEOS Error" ) },
2821 { u
"ring self-intersection"_s, QObject::tr(
"Ring self-intersection",
"GEOS Error" ) },
2822 { u
"nested shells"_s, QObject::tr(
"Nested shells",
"GEOS Error" ) },
2823 { u
"duplicate rings"_s, QObject::tr(
"Duplicate rings",
"GEOS Error" ) },
2824 { u
"too few points in geometry component"_s, QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) },
2825 { u
"invalid coordinate"_s, QObject::tr(
"Invalid coordinate",
"GEOS Error" ) },
2826 { u
"ring is not closed"_s, QObject::tr(
"Ring is not closed",
"GEOS Error" ) },
2829 const auto translatedError = sTranslatedErrors.find( error.toLower() );
2830 if ( translatedError != sTranslatedErrors.end() )
2831 *errorMsg = translatedError->second;
2835 if ( g1 && errorLoc )
2841 GEOSGeom_destroy_r( context, g1 );
2851 if ( !mGeos || !geom )
2872 if ( !mGeos || !geom )
2886 bool equal = GEOSEqualsExact_r(
QgsGeosContext::get(), mGeos.get(), geosGeom.get(), epsilon );
2920GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
2925#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
2935 std::unique_ptr< QgsLineString > segmentized;
2939 simpleCurve = segmentized.get();
2941#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
2949 GEOSCoordSequence *coordSeq =
nullptr;
2951 const int numPoints = simpleCurve->
numPoints();
2953 const bool hasZ = simpleCurve->
is3D();
2955#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
2958 if ( !forceClose || ( simpleCurve->
pointN( 0 ) == simpleCurve->
pointN( numPoints - 1 ) ) )
2963 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, simpleCurve->
xData(), simpleCurve->
yData(), simpleCurve->
zData(),
nullptr, numPoints );
2966 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points"_s.arg( numPoints ) );
2974 QVector< double > x = simpleCurve->
xVector();
2975 if ( numPoints > 0 )
2976 x.append( x.at( 0 ) );
2977 QVector< double > y = simpleCurve->
yVector();
2978 if ( numPoints > 0 )
2979 y.append( y.at( 0 ) );
2980 QVector< double > z = simpleCurve->
zVector();
2981 if ( hasZ && numPoints > 0 )
2982 z.append( z.at( 0 ) );
2985 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, x.constData(), y.constData(), !hasZ ?
nullptr : z.constData(),
nullptr, numPoints + 1 );
2988 QgsDebugError( u
"GEOS Exception: Could not create closed coordinate sequence for %1 points"_s.arg( numPoints + 1 ) );
2999 const bool hasM =
false;
3010 int numOutPoints = numPoints;
3011 if ( forceClose && ( simpleCurve->
pointN( 0 ) != simpleCurve->
pointN( numPoints - 1 ) ) )
3018 coordSeq = GEOSCoordSeq_create_r( context, numOutPoints, coordDims );
3021 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions"_s.arg( numPoints ).arg( coordDims ) );
3025 const double *xData = simpleCurve->
xData();
3026 const double *yData = simpleCurve->
yData();
3027 const double *zData = hasZ ? simpleCurve->
zData() :
nullptr;
3028 const double *mData = hasM ? simpleCurve->
mData() :
nullptr;
3030 if ( precision > 0. )
3032 for (
int i = 0; i < numOutPoints; ++i )
3034 if ( i >= numPoints )
3037 xData = simpleCurve->
xData();
3038 yData = simpleCurve->
yData();
3039 zData = hasZ ? simpleCurve->
zData() :
nullptr;
3040 mData = hasM ? simpleCurve->
mData() :
nullptr;
3044 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
3048 GEOSCoordSeq_setXY_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
3052 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
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, *xData++, *yData++, *zData++ );
3074 GEOSCoordSeq_setXY_r( context, coordSeq, i, *xData++, *yData++ );
3078 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
3088geos::unique_ptr
QgsGeos::createGeosPoint( const QgsAbstractGeometry *point,
int coordDims,
double precision, Qgis::GeosCreationFlags )
3094 return createGeosPointXY( pt->
x(), pt->
y(), pt->
is3D(), pt->
z(), pt->
isMeasure(), pt->
m(), coordDims, precision );
3106 if ( coordDims == 2 )
3109 if ( precision > 0. )
3110 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
3112 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, x, y ) );
3116 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( context, 1, coordDims );
3119 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions"_s.arg( coordDims ) );
3122 if ( precision > 0. )
3124 GEOSCoordSeq_setX_r( context, coordSeq, 0, std::round( x / precision ) * precision );
3125 GEOSCoordSeq_setY_r( context, coordSeq, 0, std::round( y / precision ) * precision );
3128 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, std::round( z / precision ) * precision );
3133 GEOSCoordSeq_setX_r( context, coordSeq, 0, x );
3134 GEOSCoordSeq_setY_r( context, coordSeq, 0, y );
3137 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, z );
3143 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 3, m );
3146 geosPoint.reset( GEOSGeom_createPoint_r( context, coordSeq ) );
3152geos::unique_ptr
QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve,
double precision, Qgis::GeosCreationFlags )
3158 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c, precision );
3171#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15 )
3182 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c, precision );
3189 if ( !
c->hasCurvedSegments() )
3202geos::unique_ptr
QgsGeos::createGeosCompoundCurve( const QgsAbstractGeometry *curve,
double precision, Qgis::GeosCreationFlags flags )
3213 const int nCurves =
c->nCurves();
3216 for (
int i = 0; i < nCurves; i++ )
3221 curves[i] = createGeosSimpleCurve(
c->curveAt( i ), precision, flags ).release();
3224 geosCurve.reset( GEOSGeom_createCompoundCurve_r( context, curves, nCurves ) );
3231geos::unique_ptr
QgsGeos::createGeosCurvePolygon( const QgsAbstractGeometry *poly,
double precision, Qgis::GeosCreationFlags flags )
3237 const QgsCurve *exteriorRing = polygon->
exteriorRing();
3238 if ( !exteriorRing )
3251 exteriorRingGeos.reset( createGeosSimpleCurve( exteriorRing, precision, flags ).release() );
3255 exteriorRingGeos.reset( createGeosCompoundCurve( exteriorRing, precision, flags ).release() );
3259 QList< const QgsCurve * > holesToExport;
3260 holesToExport.reserve( nInteriorRings );
3261 for (
int i = 0; i < nInteriorRings; ++i )
3263 const QgsCurve *interiorRing = polygon->
interiorRing( i );
3266 holesToExport << interiorRing;
3271 if ( !holesToExport.empty() )
3274 for (
int i = 0; i < holesToExport.size(); ++i )
3279 holes[i] = createGeosSimpleCurve( holesToExport[i], precision, flags ).release();
3283 holes[i] = createGeosCompoundCurve( holesToExport[i], precision, flags ).release();
3288 geosCurvePolygon.reset( GEOSGeom_createCurvePolygon_r( context, exteriorRingGeos.release(), holes, holesToExport.size() ) );
3293 return geosCurvePolygon;
3303 const QgsCurve *exteriorRing = polygon->
exteriorRing();
3304 if ( !exteriorRing )
3313 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( context, createCoordinateSequence( exteriorRing, precision,
true ) ) );
3316 QList< const QgsCurve * > holesToExport;
3317 holesToExport.reserve( nInteriorRings );
3318 for (
int i = 0; i < nInteriorRings; ++i )
3320 const QgsCurve *interiorRing = polygon->
interiorRing( i );
3323 holesToExport << interiorRing;
3328 if ( !holesToExport.empty() )
3331 for (
int i = 0; i < holesToExport.size(); ++i )
3333 holes[i] = GEOSGeom_createLinearRing_r( context, createCoordinateSequence( holesToExport[i], precision,
true ) );
3337 geosPolygon.reset( GEOSGeom_createPolygon_r( context, exteriorRingGeos.release(), holes, holesToExport.size() ) );
3358 offset.reset( GEOSOffsetCurve_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( joinStyle ), miterLimit ) );
3370 return fromGeos( res.get() ).release();
3388 GEOSBufferParams_setSingleSided_r( context, bp.get(), 1 );
3389 GEOSBufferParams_setQuadrantSegments_r( context, bp.get(), segments );
3390 GEOSBufferParams_setJoinStyle_r( context, bp.get(),
static_cast< int >( joinStyle ) );
3391 GEOSBufferParams_setMitreLimit_r( context, bp.get(), miterLimit );
3397 geos.reset( GEOSBufferWithParams_r( context, mGeos.get(), bp.get(),
distance ) );
3432 boundaryGeos =
asGeos( boundary );
3462 return std::numeric_limits< double >::quiet_NaN();
3471 return std::numeric_limits< double >::quiet_NaN();
3513 if ( !mGeos || !other )
3534 if ( !mGeos ||
mGeometry->dimension() == 0 )
3552 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
3556 int numGeoms = GEOSGetNumGeometries_r( context, mGeos.get() );
3557 if ( numGeoms == -1 )
3566 bool isMultiGeom =
false;
3567 int geosTypeId = GEOSGeomTypeId_r( context, mGeos.get() );
3568 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
3571 bool isLine = (
mGeometry->dimension() == 1 );
3578 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
3582 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
3587 if ( reshapedGeometry )
3593 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
3594 return reshapeResult;
3601 bool reshapeTookPlace =
false;
3606 for (
int i = 0; i < numGeoms; ++i )
3609 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3611 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3613 if ( currentReshapeGeometry )
3615 newGeoms[i] = currentReshapeGeometry.release();
3616 reshapeTookPlace =
true;
3620 newGeoms[i] = GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, mGeos.get(), i ) );
3627 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
3631 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
3635 if ( !newMultiGeom )
3644 if ( reshapeTookPlace )
3648 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
3649 return reshapedMultiGeom;
3672 if ( GEOSGeomTypeId_r( context, mGeos.get() ) != GEOS_MULTILINESTRING )
3679 double gridSize = parameters.
gridSize();
3682 geos::unique_ptr geosFixedSize( GEOSGeom_setPrecision_r( context, mGeos.get(), gridSize, 0 ) );
3683 geos.reset( GEOSLineMerge_r( context, geosFixedSize.get() ) );
3686 geos.reset( GEOSLineMerge_r( context, mGeos.get() ) );
3712 if ( mGeosPrepared )
3714 nearestCoord.reset( GEOSPreparedNearestPoints_r( context, mGeosPrepared.get(), otherGeom.get() ) );
3718 nearestCoord.reset( GEOSNearestPoints_r( context, mGeos.get(), otherGeom.get() ) );
3721 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx );
3722 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny );
3724 catch ( QgsGeosException &e )
3729 *errorMsg = e.what();
3734 return std::make_unique< QgsPoint >( nx, ny );
3739 if ( !mGeos || other.
isEmpty() )
3749 if ( !other || other->
isEmpty() )
3768 if ( !nearestCoord )
3771 *errorMsg = u
"GEOS returned no nearest points"_s;
3775 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx1 );
3776 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny1 );
3777 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 1, &nx2 );
3778 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 1, &ny2 );
3780 catch ( QgsGeosException &e )
3785 *errorMsg = e.what();
3790 auto line = std::make_unique< QgsLineString >();
3815 catch ( QgsGeosException &e )
3820 *errorMsg = e.what();
3835 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
3845 catch ( QgsGeosException &e )
3850 *errorMsg = e.what();
3867 lineGeosGeometries[validLines] = l.release();
3876 geos::unique_ptr result( GEOSPolygonize_r( context, lineGeosGeometries, validLines ) );
3877 for (
int i = 0; i < validLines; ++i )
3879 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3881 delete[] lineGeosGeometries;
3884 catch ( QgsGeosException &e )
3888 *errorMsg = e.what();
3890 for (
int i = 0; i < validLines; ++i )
3892 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3894 delete[] lineGeosGeometries;
3909 extentGeosGeom =
asGeos( extent, mPrecision );
3910 if ( !extentGeosGeom )
3921 geos.reset( GEOSVoronoiDiagram_r( context, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
3923 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3945 geos.reset( GEOSDelaunayTriangulation_r( context, mGeos.get(), tolerance, edgesOnly ) );
3947 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3959#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
3961 throw QgsNotSupportedException( QObject::tr(
"Calculating constrainedDelaunayTriangulation requires a QGIS build based on GEOS 3.11 or later" ) );
3973 geos.reset( GEOSConstrainedDelaunayTriangulation_r( context, mGeos.get() ) );
3975 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3980 std::unique_ptr< QgsAbstractGeometry > res =
fromGeos(
geos.get() );
3983 return std::unique_ptr< QgsAbstractGeometry >( collection->extractPartsByType(
Qgis::WkbType::Polygon,
true ) );
3995static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
3998 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( context, linestring );
4002 unsigned int coordSeqSize;
4003 if ( GEOSCoordSeq_getSize_r( context, coordSeq, &coordSeqSize ) == 0 )
4006 if ( coordSeqSize < 2 )
4009 GEOSCoordSeq_getX_r( context, coordSeq, 0, &x1 );
4010 GEOSCoordSeq_getY_r( context, coordSeq, 0, &y1 );
4011 GEOSCoordSeq_getX_r( context, coordSeq, coordSeqSize - 1, &x2 );
4012 GEOSCoordSeq_getY_r( context, coordSeq, coordSeqSize - 1, &y2 );
4020 double x1, y1, x2, y2;
4021 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
4024 double rx1, ry1, rx2, ry2;
4025 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
4028 bool intersectionAtOrigLineEndpoint = ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) != ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
4029 bool intersectionAtReshapeLineEndpoint = ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) || ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
4033 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
4037 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
4038 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, geoms, 2 ) );
4043 double x1res, y1res, x2res, y2res;
4044 if ( !_linestringEndpoints( res.get(), x1res, y1res, x2res, y2res ) )
4046 if ( ( x1res == x2 && y1res == y2 ) || ( x2res == x1 && y2res == y1 ) )
4047 res.reset( GEOSReverse_r( context, res.get() ) );
4058 if ( !line || !reshapeLineGeos )
4061 bool atLeastTwoIntersections =
false;
4062 bool oneIntersection =
false;
4063 QgsPointXY oneIntersectionPoint;
4069 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, line, reshapeLineGeos ) );
4070 if ( intersectGeom )
4072 const int geomType = GEOSGeomTypeId_r( context, intersectGeom.get() );
4073 atLeastTwoIntersections = ( geomType == GEOS_MULTIPOINT && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 1 )
4074 || ( geomType == GEOS_GEOMETRYCOLLECTION && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 )
4075 || ( geomType == GEOS_MULTILINESTRING && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 );
4077 if ( GEOSGeomTypeId_r( context, intersectGeom.get() ) == GEOS_POINT )
4079 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( context, intersectGeom.get() );
4081 GEOSCoordSeq_getX_r( context, intersectionCoordSeq, 0, &xi );
4082 GEOSCoordSeq_getY_r( context, intersectionCoordSeq, 0, &yi );
4083 oneIntersection =
true;
4084 oneIntersectionPoint = QgsPointXY( xi, yi );
4088 catch ( QgsGeosException & )
4090 atLeastTwoIntersections =
false;
4094 if ( oneIntersection )
4095 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
4097 if ( !atLeastTwoIntersections )
4101 double x1, y1, x2, y2;
4102 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
4105 geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1,
false, 0,
false, 0, 2, precision );
4106 geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2,
false, 0,
false, 0, 2, precision );
4108 bool isRing =
false;
4109 if ( GEOSGeomTypeId_r( context, line ) == GEOS_LINEARRING || GEOSEquals_r( context, beginLineVertex.get(), endLineVertex.get() ) == 1 )
4114 if ( !nodedGeometry )
4120 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, nodedGeometry.get() ) );
4126 int numMergedLines = GEOSGetNumGeometries_r( context, mergedLines.get() );
4127 if ( numMergedLines < 2 )
4129 if ( numMergedLines == 1 )
4138 QVector<GEOSGeometry *> resultLineParts;
4139 QVector<GEOSGeometry *> probableParts;
4141 for (
int i = 0; i < numMergedLines; ++i )
4143 const GEOSGeometry *currentGeom = GEOSGetGeometryN_r( context, mergedLines.get(), i );
4146 bool alreadyAdded =
false;
4148 double bufferDistance = std::pow( 10.0L, geomDigits( currentGeom ) - 11 );
4149 for (
const GEOSGeometry *other : std::as_const( resultLineParts ) )
4151 GEOSHausdorffDistance_r( context, currentGeom, other, &
distance );
4154 alreadyAdded =
true;
4161 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( context, currentGeom );
4162 unsigned int currentCoordSeqSize;
4163 GEOSCoordSeq_getSize_r( context, currentCoordSeq, ¤tCoordSeqSize );
4164 if ( currentCoordSeqSize < 2 )
4168 double xBegin, xEnd, yBegin, yEnd;
4169 GEOSCoordSeq_getX_r( context, currentCoordSeq, 0, &xBegin );
4170 GEOSCoordSeq_getY_r( context, currentCoordSeq, 0, &yBegin );
4171 GEOSCoordSeq_getX_r( context, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
4172 GEOSCoordSeq_getY_r( context, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
4173 geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin,
false, 0,
false, 0, 2, precision );
4174 geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd,
false, 0,
false, 0, 2, precision );
4177 int nEndpointsOnOriginalLine = 0;
4178 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
4179 nEndpointsOnOriginalLine += 1;
4181 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
4182 nEndpointsOnOriginalLine += 1;
4185 int nEndpointsSameAsOriginalLine = 0;
4186 if ( GEOSEquals_r( context, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1 || GEOSEquals_r( context, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
4187 nEndpointsSameAsOriginalLine += 1;
4189 if ( GEOSEquals_r( context, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1 || GEOSEquals_r( context, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
4190 nEndpointsSameAsOriginalLine += 1;
4193 bool currentGeomOverlapsOriginalGeom =
false;
4194 bool currentGeomOverlapsReshapeLine =
false;
4195 if ( lineContainedInLine( currentGeom, line ) == 1 )
4196 currentGeomOverlapsOriginalGeom =
true;
4198 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
4199 currentGeomOverlapsReshapeLine =
true;
4202 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
4204 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
4207 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
4209 probableParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
4211 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
4213 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
4215 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
4217 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
4219 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
4221 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
4226 if ( isRing && !probableParts.isEmpty() )
4230 double maxLength = -std::numeric_limits<double>::max();
4231 double currentLength = 0;
4232 for (
int i = 0; i < probableParts.size(); ++i )
4234 currentGeom = probableParts.at( i );
4235 GEOSLength_r( context, currentGeom, ¤tLength );
4236 if ( currentLength > maxLength )
4238 maxLength = currentLength;
4239 maxGeom.reset( currentGeom );
4243 GEOSGeom_destroy_r( context, currentGeom );
4246 resultLineParts.push_back( maxGeom.release() );
4250 if ( resultLineParts.empty() )
4253 if ( resultLineParts.size() == 1 )
4255 result.reset( resultLineParts[0] );
4260 for (
int i = 0; i < resultLineParts.size(); ++i )
4262 lineArray[i] = resultLineParts[i];
4266 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
4270 result.reset( GEOSLineMerge_r( context, multiLineGeom.get() ) );
4274 if ( GEOSGeomTypeId_r( context, result.get() ) != GEOS_LINESTRING )
4280 bool reverseLine =
false;
4284 char isResultCCW = 0, isOriginCCW = 0;
4285 if ( GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, result.get() ), &isResultCCW ) && GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, line ), &isOriginCCW ) )
4288 reverseLine = ( isOriginCCW == 1 && isResultCCW == 0 ) || ( isOriginCCW == 0 && isResultCCW == 1 );
4294 double x1res, y1res, x2res, y2res;
4295 if ( !_linestringEndpoints( result.get(), x1res, y1res, x2res, y2res ) )
4297 geos::unique_ptr beginResultLineVertex = createGeosPointXY( x1res, y1res,
false, 0,
false, 0, 2, precision );
4298 geos::unique_ptr endResultLineVertex = createGeosPointXY( x2res, y2res,
false, 0,
false, 0, 2, precision );
4299 reverseLine = GEOSEquals_r( context, beginLineVertex.get(), endResultLineVertex.get() ) == 1 || GEOSEquals_r( context, endLineVertex.get(), beginResultLineVertex.get() ) == 1;
4302 result.reset( GEOSReverse_r( context, result.get() ) );
4310 int nIntersections = 0;
4311 int lastIntersectingRing = -2;
4315 int nRings = GEOSGetNumInteriorRings_r( context, polygon );
4320 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( context, polygon );
4321 if ( GEOSIntersects_r( context, outerRing, reshapeLineGeos ) == 1 )
4324 lastIntersectingRing = -1;
4325 lastIntersectingGeom = outerRing;
4333 for (
int i = 0; i < nRings; ++i )
4335 innerRings[i] = GEOSGetInteriorRingN_r( context, polygon, i );
4336 if ( GEOSIntersects_r( context, innerRings[i], reshapeLineGeos ) == 1 )
4339 lastIntersectingRing = i;
4340 lastIntersectingGeom = innerRings[i];
4344 catch ( QgsGeosException & )
4349 if ( nIntersections != 1 )
4351 delete[] innerRings;
4356 geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
4357 if ( !reshapeResult )
4359 delete[] innerRings;
4365 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( context, reshapeResult.get() );
4366 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( context, reshapeSequence );
4368 reshapeResult.reset();
4372 newRing = GEOSGeom_createLinearRing_r( context, newCoordSequence );
4374 catch ( QgsGeosException & )
4381 delete[] innerRings;
4386 if ( lastIntersectingRing == -1 )
4387 newOuterRing = newRing;
4389 newOuterRing = GEOSGeom_clone_r( context, outerRing );
4392 QVector<GEOSGeometry *> ringList;
4395 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( context, GEOSGeom_clone_r( context, newOuterRing ),
nullptr, 0 );
4396 if ( outerRingPoly )
4398 ringList.reserve( nRings );
4400 for (
int i = 0; i < nRings; ++i )
4402 if ( lastIntersectingRing == i )
4403 currentRing = newRing;
4405 currentRing = GEOSGeom_clone_r( context, innerRings[i] );
4408 if ( GEOSContains_r( context, outerRingPoly, currentRing ) == 1 )
4409 ringList.push_back( currentRing );
4411 GEOSGeom_destroy_r( context, currentRing );
4414 GEOSGeom_destroy_r( context, outerRingPoly );
4418 for (
int i = 0; i < ringList.size(); ++i )
4419 newInnerRings[i] = ringList.at( i );
4421 delete[] innerRings;
4423 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( context, newOuterRing, newInnerRings, ringList.size() ) );
4424 delete[] newInnerRings;
4426 return reshapedPolygon;
4431 if ( !line1 || !line2 )
4436 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
4443 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, bufferGeom.get(), line1 ) );
4446 double intersectGeomLength;
4449 GEOSLength_r( context, intersectionGeom.get(), &intersectGeomLength );
4450 GEOSLength_r( context, line1, &line1Length );
4452 double intersectRatio = line1Length / intersectGeomLength;
4453 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
4461 if ( !point || !line )
4464 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
4467 geos::unique_ptr lineBuffer( GEOSBuffer_r( context, line, bufferDistance, 8 ) );
4471 bool contained =
false;
4472 if ( GEOSContains_r( context, lineBuffer.get(), point ) == 1 )
4485 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( context, bbox.get() );
4489 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( context, bBoxRing );
4491 if ( !bBoxCoordSeq )
4494 unsigned int nCoords = 0;
4495 if ( !GEOSCoordSeq_getSize_r( context, bBoxCoordSeq, &nCoords ) )
4499 for (
unsigned int i = 0; i < nCoords - 1; ++i )
4502 GEOSCoordSeq_getX_r( context, bBoxCoordSeq, i, &t );
4505 digits = std::ceil( std::log10( std::fabs( t ) ) );
4506 if ( digits > maxDigits )
4509 GEOSCoordSeq_getY_r( context, bBoxCoordSeq, i, &t );
4510 digits = std::ceil( std::log10( std::fabs( t ) ) );
4511 if ( digits > maxDigits )
4519#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4520 : mFeedback( feedback )
4522 GEOSContext_setInterruptCallback_r(
QgsGeosContext::get(), &callback,
reinterpret_cast< void *
>( mFeedback ) );
4533#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4538#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4539int QgsScopedGeosContextRegisterFeedback::callback(
void *userData )
4545 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 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.
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