39using namespace Qt::StringLiterals;
41#define DEFAULT_QUADRANT_SEGMENTS 8
43#define CATCH_GEOS( r ) \
44 catch ( QgsGeosException & ) \
49#define CATCH_GEOS_WITH_ERRMSG( r ) \
50 catch ( QgsGeosException & e ) \
54 *errorMsg = e.what(); \
55 if ( errorMsg->startsWith( "InterruptedException"_L1, Qt::CaseInsensitive ) ) \
65static void throwQgsGeosException(
const char *fmt, ... )
71 vsnprintf( buffer,
sizeof buffer, fmt, ap );
74 QString message = QString::fromUtf8( buffer );
84 throw QgsGeosException( message );
92 throw QgsGeosException( message );
97static void printGEOSNotice(
const char *fmt, ... )
99#if defined( QGISDEBUG )
104 vsnprintf( buffer,
sizeof buffer, fmt, ap );
115#if defined( USE_THREAD_LOCAL ) && !defined( Q_OS_WIN )
118QThreadStorage< QgsGeosContext * > QgsGeosContext::sGeosContext;
124 mContext = GEOS_init_r();
125 GEOSContext_setNoticeHandler_r( mContext, printGEOSNotice );
126 GEOSContext_setErrorHandler_r( mContext, throwQgsGeosException );
131 GEOS_finish_r( mContext );
136#if defined( USE_THREAD_LOCAL ) && !defined( Q_OS_WIN )
137 return sGeosContext.mContext;
139 GEOSContextHandle_t gContext =
nullptr;
140 if ( sGeosContext.hasLocalData() )
142 gContext = sGeosContext.localData()->mContext;
147 gContext = sGeosContext.localData()->mContext;
184 , mPrecision( precision )
211#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
213 throw QgsNotSupportedException( QObject::tr(
"The structured method to make geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
216 throw QgsNotSupportedException( QObject::tr(
"The keep collapsed option for making geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
220 geos.reset( GEOSMakeValid_r( context, mGeos.get() ) );
225 GEOSMakeValidParams *params = GEOSMakeValidParams_create_r( context );
230 GEOSMakeValidParams_setMethod_r( context, params, GEOS_MAKE_VALID_LINEWORK );
234 GEOSMakeValidParams_setMethod_r( context, params, GEOS_MAKE_VALID_STRUCTURE );
238 GEOSMakeValidParams_setKeepCollapsed_r( context, params, keepCollapsed ? 1 : 0 );
243 geos.reset( GEOSMakeValidWithParams_r( context, mGeos.get(), params ) );
244 GEOSMakeValidParams_destroy_r( context, params );
246 catch ( QgsGeosException &e )
250 *errorMsg = e.what();
252 GEOSMakeValidParams_destroy_r( context, params );
281 std::unique_ptr< QgsAbstractGeometry > geom =
fromGeos( newPart );
288 mGeosPrepared.reset();
322 return overlay( geom, OverlayIntersection, errorMsg, parameters, feedback ).release();
327 return overlay( geom, OverlayDifference, errorMsg, parameters, feedback ).release();
343 catch ( QgsGeosException &e )
348 *errorMsg = e.what();
357 int partType = GEOSGeomTypeId_r( context, currentPart );
360 if ( partType == GEOS_POINT )
371 if ( partType == GEOS_MULTILINESTRING || partType == GEOS_MULTIPOLYGON || partType == GEOS_GEOMETRYCOLLECTION )
373 int partCount = GEOSGetNumGeometries_r( context, currentPart );
374 for (
int i = 0; i < partCount; ++i )
376 subdivideRecursive( GEOSGetGeometryN_r( context, currentPart, i ), maxNodes, depth, parts, clipRect, gridSize );
387 int vertexCount = GEOSGetNumCoordinates_r( context, currentPart );
388 if ( vertexCount == 0 )
392 else if ( vertexCount < maxNodes )
399 double width = clipRect.
width();
400 double height = clipRect.
height();
401 QgsRectangle halfClipRect1 = clipRect;
402 QgsRectangle halfClipRect2 = clipRect;
403 if ( width > height )
416 halfClipRect1.
setYMinimum( halfClipRect1.
yMinimum() - std::numeric_limits<double>::epsilon() );
417 halfClipRect2.
setYMinimum( halfClipRect2.
yMinimum() - std::numeric_limits<double>::epsilon() );
418 halfClipRect1.
setYMaximum( halfClipRect1.
yMaximum() + std::numeric_limits<double>::epsilon() );
419 halfClipRect2.
setYMaximum( halfClipRect2.
yMaximum() + std::numeric_limits<double>::epsilon() );
423 halfClipRect1.
setXMinimum( halfClipRect1.
xMinimum() - std::numeric_limits<double>::epsilon() );
424 halfClipRect2.
setXMinimum( halfClipRect2.
xMinimum() - std::numeric_limits<double>::epsilon() );
425 halfClipRect1.
setXMaximum( halfClipRect1.
xMaximum() + std::numeric_limits<double>::epsilon() );
426 halfClipRect2.
setXMaximum( halfClipRect2.
xMaximum() + std::numeric_limits<double>::epsilon() );
438 clipPart1.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart1.get(), gridSize ) );
440 subdivideRecursive( clipPart1.get(), maxNodes, depth, parts, halfClipRect1, gridSize );
446 clipPart2.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart2.get(), gridSize ) );
448 subdivideRecursive( clipPart2.get(), maxNodes, depth, parts, halfClipRect2, gridSize );
460 maxNodes = std::max( maxNodes, 8 );
466 subdivideRecursive( mGeos.get(), maxNodes, 0, parts.get(),
mGeometry->boundingBox(), parameters.
gridSize() );
470 return std::move( parts );
475 return overlay( geom, OverlayUnion, errorMsg, parameters, feedback ).release();
480 std::vector<geos::unique_ptr> geosGeometries;
481 geosGeometries.reserve( geomList.size() );
487 geosGeometries.emplace_back(
asGeos( g, mPrecision ) );
495 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
498 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
502 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
507 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
508 return result.release();
513 std::vector<geos::unique_ptr> geosGeometries;
514 geosGeometries.reserve( geomList.size() );
520 geosGeometries.emplace_back(
asGeos( g.constGet(), mPrecision ) );
528 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
532 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
536 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
541 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
542 return result.release();
547 return overlay( geom, OverlaySymDifference, errorMsg, parameters, feedback ).release();
550static bool isZVerticalLine(
const QgsAbstractGeometry *geom,
double tolerance = 4 * std::numeric_limits<double>::epsilon() )
560 bool isVertical =
true;
563 const int nrPoints = line->numPoints();
571 const double sqrTolerance = tolerance * tolerance;
572 const double *lineX = line->xData();
573 const double *lineY = line->yData();
574 for (
int iVert = nrPoints - 1, jVert = 0; jVert < nrPoints; iVert = jVert++ )
605 otherGeosGeom =
asGeos( &firstPoint, mPrecision );
609 otherGeosGeom =
asGeos( geom, mPrecision );
612 if ( !otherGeosGeom )
620 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
622 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
626 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
643 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
652 GEOSPreparedDistance_r( context, mGeosPrepared.get(), point.get(), &
distance );
656 GEOSDistance_r( context, mGeos.get(), point.get(), &
distance );
673 return intersects( geom, errorMsg, feedback );
685 otherGeosGeom =
asGeos( &firstPoint );
689 otherGeosGeom =
asGeos( geom, mPrecision );
692 if ( !otherGeosGeom )
706 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
708#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
709 return GEOSPreparedDistanceWithin_r( context, mGeosPrepared.get(), otherGeosGeom.get(), maxdist );
711 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
716#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
717 return GEOSDistanceWithin_r( context, mGeos.get(), otherGeosGeom.get(), maxdist );
719 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
735#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
738 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
744#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
745 return GEOSPreparedContainsXY_r( context, mGeosPrepared.get(), x, y ) == 1;
747 return GEOSPreparedContains_r( context, mGeosPrepared.get(), point.get() ) == 1;
751#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
752 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
757 result = ( GEOSContains_r( context, mGeos.get(), point.get() ) == 1 );
759 catch ( QgsGeosException &e )
764 *errorMsg = e.what();
782 if ( !otherGeosGeom )
806 if ( !otherGeosGeom )
830 if ( !otherGeosGeom )
854 if ( !otherGeosGeom )
870 if ( !mGeos || !geom )
876#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
884 return GEOSPreparedIntersectsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
886 catch ( QgsGeosException &e )
891 *errorMsg = e.what();
899 return relation( geom, RelationIntersects, errorMsg );
904 return relation( geom, RelationTouches, errorMsg, feedback );
909 return relation( geom, RelationCrosses, errorMsg, feedback );
914 return relation( geom, RelationWithin, errorMsg, feedback );
919 return relation( geom, RelationOverlaps, errorMsg, feedback );
924 if ( !mGeos || !geom )
929#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
938 return GEOSPreparedContainsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
940 catch ( QgsGeosException &e )
945 *errorMsg = e.what();
953 return relation( geom, RelationContains, errorMsg, feedback );
958 return relation( geom, RelationDisjoint, errorMsg, feedback );
979 char *r = GEOSRelate_r( context, mGeos.get(), geosGeom.get() );
982 result = QString( r );
983 GEOSFree_r( context, r );
986 catch ( QgsGeosException &e )
991 *errorMsg = e.what();
1000 if ( !mGeos || !geom )
1011 bool result =
false;
1017 result = ( GEOSRelatePattern_r( context, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
1019 catch ( QgsGeosException &e )
1024 *errorMsg = e.what();
1065 const QgsLineString &splitLine, QVector<QgsGeometry> &newGeometries,
bool topological,
QgsPointSequence &topologyTestPoints, QString *errorMsg,
bool skipIntersectionCheck
1081 if ( !GEOSisValid_r( context, mGeos.get() ) )
1088 newGeometries.clear();
1095 splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
1099 splitLineGeos = createGeosPointXY( splitLine.
xAt( 0 ), splitLine.
yAt( 0 ),
false, 0,
false, 0, 2, mPrecision );
1106 if ( !GEOSisValid_r( context, splitLineGeos.get() ) || !GEOSisSimple_r( context, splitLineGeos.get() ) )
1114 if ( !topologicalTestPointsSplit( splitLineGeos.get(), topologyTestPoints ) )
1123 returnCode = splitLinearGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1127 returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1155 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, mGeos.get(), splitLine ) );
1156 if ( !intersectionGeom )
1159 bool simple =
false;
1160 int nIntersectGeoms = 1;
1161 if ( GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_LINESTRING || GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_POINT )
1165 nIntersectGeoms = GEOSGetNumGeometries_r( context, intersectionGeom.get() );
1167 for (
int i = 0; i < nIntersectGeoms; ++i )
1171 currentIntersectGeom = intersectionGeom.get();
1173 currentIntersectGeom = GEOSGetGeometryN_r( context, intersectionGeom.get(), i );
1175 const GEOSCoordSequence *lineSequence = GEOSGeom_getCoordSeq_r( context, currentIntersectGeom );
1176 unsigned int sequenceSize = 0;
1178 if ( GEOSCoordSeq_getSize_r( context, lineSequence, &sequenceSize ) != 0 )
1180 for (
unsigned int i = 0; i < sequenceSize; ++i )
1182 if ( GEOSCoordSeq_getXYZ_r( context, lineSequence, i, &x, &y, &z ) )
1184 testPoints.push_back( QgsPoint( x, y, z ) );
1198 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1200 std::unique_ptr< QgsMultiCurve > multiCurve;
1201 if ( type == GEOS_MULTILINESTRING )
1205 else if ( type == GEOS_LINESTRING )
1207 multiCurve = std::make_unique<QgsMultiCurve>();
1208 multiCurve->addGeometry(
mGeometry->clone() );
1223 std::unique_ptr< QgsMultiPoint > splitPoints;
1225 std::unique_ptr< QgsAbstractGeometry > splitGeom(
fromGeos( GEOSsplitPoint ) );
1229 splitPoints.reset( qgis::down_cast<QgsMultiPoint *>( splitGeom.release() ) );
1233 splitPoints = std::make_unique< QgsMultiPoint >();
1236 splitPoints->addGeometry( qgis::down_cast<QgsPoint *>( splitGeom.release() ) );
1244 QgsMultiCurve lines;
1247 for (
int geometryIndex = 0; geometryIndex < multiCurve->numGeometries(); ++geometryIndex )
1260 QMap< int, QVector< QPair< double, QgsPoint > > > pointMap;
1261 for (
int splitPointIndex = 0; splitPointIndex < splitPoints->numGeometries(); ++splitPointIndex )
1263 const QgsPoint *intersectionPoint = splitPoints->pointN( splitPointIndex );
1265 QgsPoint segmentPoint2D;
1266 QgsVertexId nextVertex;
1269 line->
closestSegment( *intersectionPoint, segmentPoint2D, nextVertex );
1285 const QPair< double, QgsPoint > pair = qMakePair(
distance, *correctSegmentPoint.get() );
1286 if ( pointMap.contains( nextVertex.
vertex - 1 ) )
1287 pointMap[nextVertex.
vertex - 1].append( pair );
1289 pointMap[nextVertex.
vertex - 1] = QVector< QPair< double, QgsPoint > >() << pair;
1294 for (
auto &p : pointMap )
1296 std::sort( p.begin(), p.end(), [](
const QPair< double, QgsPoint > &a,
const QPair< double, QgsPoint > &b ) { return a.first < b.first; } );
1300 QgsLineString newLine;
1302 QgsPoint splitPoint;
1303 for (
int vertexIndex = 0; vertexIndex < nVertices; ++vertexIndex )
1305 QgsPoint currentPoint = line->
pointN( vertexIndex );
1307 if ( pointMap.contains( vertexIndex ) )
1310 for (
int k = 0; k < pointMap[vertexIndex].size(); ++k )
1312 splitPoint = pointMap[vertexIndex][k].second;
1313 if ( splitPoint == currentPoint )
1316 newLine = QgsLineString();
1319 else if ( splitPoint == line->
pointN( vertexIndex + 1 ) )
1323 newLine = QgsLineString();
1329 newLine = QgsLineString();
1338 return asGeos( &lines, mPrecision );
1343 Q_UNUSED( skipIntersectionCheck )
1352 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, splitLine, mGeos.get() ) );
1353 if ( !intersectGeom || GEOSisEmpty_r( context, intersectGeom.get() ) )
1357 const int linearIntersect = GEOSRelatePattern_r( context, mGeos.get(), splitLine,
"1********" );
1358 if ( linearIntersect > 0 )
1366 std::vector<geos::unique_ptr> lineGeoms;
1368 const int splitType = GEOSGeomTypeId_r( context, splitGeom.get() );
1369 if ( splitType == GEOS_MULTILINESTRING )
1371 const int nGeoms = GEOSGetNumGeometries_r( context, splitGeom.get() );
1372 lineGeoms.reserve( nGeoms );
1373 for (
int i = 0; i < nGeoms; ++i )
1374 lineGeoms.emplace_back( GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, splitGeom.get(), i ) ) );
1378 lineGeoms.emplace_back( GEOSGeom_clone_r( context, splitGeom.get() ) );
1381 mergeGeometriesMultiTypeSplit( lineGeoms );
1385 newGeometries << QgsGeometry(
fromGeos( lineGeom.get() ) );
1401 if ( !mGeosPrepared )
1407 if ( !skipIntersectionCheck && !GEOSPreparedIntersects_r( context, mGeosPrepared.get(), splitLine ) )
1412 if ( !nodedGeometry )
1421 const int numberOfGeometriesPolygon = numberOfGeometries( polygons.get() );
1422 if ( numberOfGeometriesPolygon == 0 )
1429 std::vector<geos::unique_ptr> testedGeometries;
1434 for (
int i = 0; i < numberOfGeometriesPolygon; i++ )
1436 const GEOSGeometry *polygon = GEOSGetGeometryN_r( context, polygons.get(), i );
1440 testedGeometries.emplace_back( GEOSGeom_clone_r( context, polygon ) );
1443 const size_t nGeometriesThis = numberOfGeometries( mGeos.get() );
1444 if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
1454 mergeGeometriesMultiTypeSplit( testedGeometries );
1457 for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( context, testedGeometries[i].get() ); ++i )
1460 if ( i < testedGeometries.size() )
1467 newGeometries << QgsGeometry(
fromGeos( testedGeometry.get() ) );
1475 if ( !splitLine || !geom )
1480 if ( GEOSGeom_getDimensions_r( context, geom ) == 2 )
1481 geometryBoundary.reset( GEOSBoundary_r( context, geom ) );
1483 geometryBoundary.reset( GEOSGeom_clone_r( context, geom ) );
1486 geos::unique_ptr unionGeometry( GEOSUnion_r( context, splitLineClone.get(), geometryBoundary.get() ) );
1488 return unionGeometry;
1491int QgsGeos::mergeGeometriesMultiTypeSplit( std::vector<geos::unique_ptr> &splitResult )
const
1498 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1499 if ( type != GEOS_GEOMETRYCOLLECTION && type != GEOS_MULTILINESTRING && type != GEOS_MULTIPOLYGON && type != GEOS_MULTIPOINT )
1503 std::vector<geos::unique_ptr> unionGeom;
1505 std::vector<geos::unique_ptr> newSplitResult;
1507 for (
size_t i = 0; i < splitResult.size(); ++i )
1510 bool isPart =
false;
1511 for (
int j = 0; j < GEOSGetNumGeometries_r( context, mGeos.get() ); j++ )
1513 if ( GEOSEquals_r( context, splitResult[i].get(), GEOSGetGeometryN_r( context, mGeos.get(), j ) ) )
1522 unionGeom.emplace_back( std::move( splitResult[i] ) );
1526 std::vector<geos::unique_ptr> geomVector;
1527 geomVector.emplace_back( std::move( splitResult[i] ) );
1529 if ( type == GEOS_MULTILINESTRING )
1530 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, geomVector ) );
1531 else if ( type == GEOS_MULTIPOLYGON )
1532 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, geomVector ) );
1536 splitResult = std::move( newSplitResult );
1539 if ( !unionGeom.empty() )
1541 if ( type == GEOS_MULTILINESTRING )
1542 splitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, unionGeom ) );
1543 else if ( type == GEOS_MULTIPOLYGON )
1544 splitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ) );
1550geos::unique_ptr QgsGeos::createGeosCollection(
int typeId, std::vector<geos::unique_ptr> &geoms )
1552 std::vector<GEOSGeometry *> geomarr;
1553 geomarr.reserve( geoms.size() );
1558 if ( geomUniquePtr )
1560 if ( !GEOSisEmpty_r( context, geomUniquePtr.get() ) )
1564 geomarr.emplace_back( geomUniquePtr.release() );
1572 geomRes.reset( GEOSGeom_createCollection_r( context, typeId, geomarr.data(), geomarr.size() ) );
1574 catch ( QgsGeosException & )
1578 GEOSGeom_destroy_r( context, geom );
1593 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1594 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1595 bool hasZ = ( nCoordDims == 3 );
1596 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1598 switch ( GEOSGeomTypeId_r( context,
geos ) )
1602 if ( GEOSisEmpty_r( context,
geos ) )
1605 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1606 unsigned int nPoints = 0;
1607 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1616 return !point.
isEmpty() ? std::unique_ptr<QgsAbstractGeometry>( point.
clone() ) :
nullptr;
1618 case GEOS_LINESTRING:
1620 return sequenceToLinestring(
geos, hasZ, hasM );
1626 case GEOS_MULTIPOINT:
1628 auto multiPoint = std::make_unique<QgsMultiPoint>();
1629 int nParts = GEOSGetNumGeometries_r( context,
geos );
1630 multiPoint->reserve( nParts );
1631 for (
int i = 0; i < nParts; ++i )
1633 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context, GEOSGetGeometryN_r( context,
geos, i ) );
1636 unsigned int nPoints = 0;
1637 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1639 multiPoint->addGeometry(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1642 return std::move( multiPoint );
1644 case GEOS_MULTILINESTRING:
1646 auto multiLineString = std::make_unique<QgsMultiLineString>();
1647 int nParts = GEOSGetNumGeometries_r( context,
geos );
1648 multiLineString->reserve( nParts );
1649 for (
int i = 0; i < nParts; ++i )
1651 std::unique_ptr< QgsLineString > line( sequenceToLinestring( GEOSGetGeometryN_r( context,
geos, i ), hasZ, hasM ) );
1654 multiLineString->addGeometry( line.release() );
1657 return std::move( multiLineString );
1659 case GEOS_MULTIPOLYGON:
1661 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
1663 int nParts = GEOSGetNumGeometries_r( context,
geos );
1664 multiPolygon->reserve( nParts );
1665 for (
int i = 0; i < nParts; ++i )
1667 std::unique_ptr< QgsPolygon > poly =
fromGeosPolygon( GEOSGetGeometryN_r( context,
geos, i ) );
1670 multiPolygon->addGeometry( poly.release() );
1673 return std::move( multiPolygon );
1675 case GEOS_GEOMETRYCOLLECTION:
1677 auto geomCollection = std::make_unique<QgsGeometryCollection>();
1678 int nParts = GEOSGetNumGeometries_r( context,
geos );
1679 geomCollection->reserve( nParts );
1680 for (
int i = 0; i < nParts; ++i )
1682 std::unique_ptr< QgsAbstractGeometry > geom(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1685 geomCollection->addGeometry( geom.release() );
1688 return std::move( geomCollection );
1697 if ( GEOSGeomTypeId_r( context,
geos ) != GEOS_POLYGON )
1702 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1703 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1704 bool hasZ = ( nCoordDims == 3 );
1705 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1707 auto polygon = std::make_unique<QgsPolygon>();
1712 polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1715 QVector<QgsCurve *> interiorRings;
1716 const int ringCount = GEOSGetNumInteriorRings_r( context,
geos );
1717 interiorRings.reserve( ringCount );
1718 for (
int i = 0; i < ringCount; ++i )
1720 ring = GEOSGetInteriorRingN_r( context,
geos, i );
1723 interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1726 polygon->setInteriorRings( interiorRings );
1731std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring(
const GEOSGeometry *
geos,
bool hasZ,
bool hasM )
1734 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1736 unsigned int nPoints;
1737 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1739 QVector< double > xOut( nPoints );
1740 QVector< double > yOut( nPoints );
1741 QVector< double > zOut;
1743 zOut.resize( nPoints );
1744 QVector< double > mOut;
1746 mOut.resize( nPoints );
1748 double *x = xOut.data();
1749 double *y = yOut.data();
1750 double *z = zOut.data();
1751 double *m = mOut.data();
1753#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
1754 GEOSCoordSeq_copyToArrays_r( context, cs, x, y, hasZ ? z :
nullptr, hasM ? m :
nullptr );
1756 for (
unsigned int i = 0; i < nPoints; ++i )
1759 GEOSCoordSeq_getXYZ_r( context, cs, i, x++, y++, z++ );
1761 GEOSCoordSeq_getXY_r( context, cs, i, x++, y++ );
1764 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, m++ );
1768 auto line = std::make_unique<QgsLineString>( xOut, yOut, zOut, mOut );
1778 int geometryType = GEOSGeomTypeId_r( context, g );
1779 if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING || geometryType == GEOS_POLYGON )
1783 return GEOSGetNumGeometries_r( context, g );
1799 GEOSCoordSeq_getXYZ_r( context, cs, i, &x, &y, &z );
1801 GEOSCoordSeq_getXY_r( context, cs, i, &x, &y );
1804 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, &m );
1840 int geosType = GEOS_GEOMETRYCOLLECTION;
1847 geosType = GEOS_MULTIPOINT;
1851 geosType = GEOS_MULTILINESTRING;
1855 geosType = GEOS_MULTIPOLYGON;
1870 std::vector<geos::unique_ptr> geomVector;
1871 geomVector.reserve(
c->numGeometries() );
1872 for (
int i = 0; i <
c->numGeometries(); ++i )
1879 geomVector.emplace_back( std::move( geosGeom ) );
1881 return createGeosCollection( geosType, geomVector );
1888 if ( !polyhedralSurface )
1891 std::vector<geos::unique_ptr> geomVector;
1892 geomVector.reserve( polyhedralSurface->
numPatches() );
1893 for (
int i = 0; i < polyhedralSurface->
numPatches(); ++i )
1900 geomVector.emplace_back( std::move( geosPolygon ) );
1903 return createGeosCollection( GEOS_MULTIPOLYGON, geomVector );
1910 return createGeosPoint(
static_cast<const QgsPoint *
>( geom ), coordDims, precision, flags );
1913 return createGeosLinestring(
static_cast<const QgsLineString *
>( geom ), precision, flags );
1916 return createGeosPolygon(
static_cast<const QgsPolygon *
>( geom ), precision, flags );
1928 if ( !mGeos || !geom )
1939 QgsScopedGeosContextRegisterFeedback interrupt( feedback );
1941 const double gridSize = parameters.
gridSize();
1949 case OverlayIntersection:
1952 opGeom.reset( GEOSIntersectionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1956 opGeom.reset( GEOSIntersection_r( context, mGeos.get(), geosGeom.get() ) );
1960 case OverlayDifference:
1963 opGeom.reset( GEOSDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1967 opGeom.reset( GEOSDifference_r( context, mGeos.get(), geosGeom.get() ) );
1976 unionGeometry.reset( GEOSUnionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1980 unionGeometry.reset( GEOSUnion_r( context, mGeos.get(), geosGeom.get() ) );
1983 if ( unionGeometry && GEOSGeomTypeId_r( context, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1985 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, unionGeometry.get() ) );
1988 unionGeometry = std::move( mergedLines );
1992 opGeom = std::move( unionGeometry );
1996 case OverlaySymDifference:
1999 opGeom.reset( GEOSSymDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
2003 opGeom.reset( GEOSSymDifference_r( context, mGeos.get(), geosGeom.get() ) );
2009 catch ( QgsGeosException &e )
2014 *errorMsg = e.what();
2020bool QgsGeos::relation(
const QgsAbstractGeometry *geom, Relation r, QString *errorMsg, QgsFeedback *feedback )
const
2022 if ( !mGeos || !geom )
2034 QgsScopedGeosContextRegisterFeedback interrupt( feedback );
2036 bool result =
false;
2039 if ( mGeosPrepared )
2043 case RelationIntersects:
2044 result = ( GEOSPreparedIntersects_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2046 case RelationTouches:
2047 result = ( GEOSPreparedTouches_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2049 case RelationCrosses:
2050 result = ( GEOSPreparedCrosses_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2052 case RelationWithin:
2053 result = ( GEOSPreparedWithin_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2055 case RelationContains:
2056 result = ( GEOSPreparedContains_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2058 case RelationDisjoint:
2059 result = ( GEOSPreparedDisjoint_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2061 case RelationOverlaps:
2062 result = ( GEOSPreparedOverlaps_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2070 case RelationIntersects:
2071 result = ( GEOSIntersects_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2073 case RelationTouches:
2074 result = ( GEOSTouches_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2076 case RelationCrosses:
2077 result = ( GEOSCrosses_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2079 case RelationWithin:
2080 result = ( GEOSWithin_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2082 case RelationContains:
2083 result = ( GEOSContains_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2085 case RelationDisjoint:
2086 result = ( GEOSDisjoint_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2088 case RelationOverlaps:
2089 result = ( GEOSOverlaps_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2093 catch ( QgsGeosException &e )
2098 *errorMsg = e.what();
2143 geos.reset( GEOSBufferWithStyle_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( endCapStyle ),
static_cast< int >( joinStyle ), miterLimit ) );
2196 geos.reset( GEOSGetCentroid_r( context, mGeos.get() ) );
2201 GEOSGeomGetX_r( context,
geos.get(), &x );
2202 GEOSGeomGetY_r( context,
geos.get(), &y );
2239 geos.reset( GEOSPointOnSurface_r( context, mGeos.get() ) );
2241 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
2246 GEOSGeomGetX_r( context,
geos.get(), &x );
2247 GEOSGeomGetY_r( context,
geos.get(), &y );
2265 std::unique_ptr< QgsAbstractGeometry > cHullGeom =
fromGeos( cHull.get() );
2266 return cHullGeom.release();
2273#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
2274 ( void ) allowHoles;
2275 ( void ) targetPercent;
2277 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHull requires a QGIS build based on GEOS 3.11 or later" ) );
2289 return concaveHullGeom;
2297#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
2298 ( void ) allowHoles;
2299 ( void ) targetPercent;
2301 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHullOfPolygons requires a QGIS build based on GEOS 3.11 or later" ) );
2313 return concaveHullGeom;
2321#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12
2323 ( void ) invalidEdges;
2325 throw QgsNotSupportedException( QObject::tr(
"Validating coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2330 *errorMsg = u
"Input geometry was not set"_s;
2339 const int result = GEOSCoverageIsValid_r( context, mGeos.get(), gapWidth, invalidEdges ? &invalidEdgesGeos :
nullptr );
2340 if ( invalidEdges && invalidEdgesGeos )
2342 *invalidEdges =
fromGeos( invalidEdgesGeos );
2344 if ( invalidEdgesGeos )
2346 GEOSGeom_destroy_r( context, invalidEdgesGeos );
2347 invalidEdgesGeos =
nullptr;
2367#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12
2369 ( void ) preserveBoundary;
2371 throw QgsNotSupportedException( QObject::tr(
"Simplifying coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2376 *errorMsg = u
"Input geometry was not set"_s;
2384 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom =
fromGeos( simplified.get() );
2385 return simplifiedGeom;
2396 *errorMsg = u
"Input geometry was not set"_s;
2404 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( unioned.get() );
2415 *errorMsg = QObject::tr(
"QGIS geometry cannot be converted to a GEOS geometry",
"GEOS Error" );
2426 char res = GEOSisValidDetail_r( context, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
2427 const bool invalid = res != 1;
2432 error = QString( r );
2433 GEOSFree_r( context, r );
2436 if ( invalid && errorMsg )
2439 static const std::map< QString, QString > sTranslatedErrors {
2440 { u
"topology validation error"_s, QObject::tr(
"Topology validation error",
"GEOS Error" ) },
2441 { u
"repeated point"_s, QObject::tr(
"Repeated point",
"GEOS Error" ) },
2442 { u
"hole lies outside shell"_s, QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) },
2443 { u
"holes are nested"_s, QObject::tr(
"Holes are nested",
"GEOS Error" ) },
2444 { u
"interior is disconnected"_s, QObject::tr(
"Interior is disconnected",
"GEOS Error" ) },
2445 { u
"self-intersection"_s, QObject::tr(
"Self-intersection",
"GEOS Error" ) },
2446 { u
"ring self-intersection"_s, QObject::tr(
"Ring self-intersection",
"GEOS Error" ) },
2447 { u
"nested shells"_s, QObject::tr(
"Nested shells",
"GEOS Error" ) },
2448 { u
"duplicate rings"_s, QObject::tr(
"Duplicate rings",
"GEOS Error" ) },
2449 { u
"too few points in geometry component"_s, QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) },
2450 { u
"invalid coordinate"_s, QObject::tr(
"Invalid coordinate",
"GEOS Error" ) },
2451 { u
"ring is not closed"_s, QObject::tr(
"Ring is not closed",
"GEOS Error" ) },
2454 const auto translatedError = sTranslatedErrors.find( error.toLower() );
2455 if ( translatedError != sTranslatedErrors.end() )
2456 *errorMsg = translatedError->second;
2460 if ( g1 && errorLoc )
2466 GEOSGeom_destroy_r( context, g1 );
2476 if ( !mGeos || !geom )
2497 if ( !mGeos || !geom )
2511 bool equal = GEOSEqualsExact_r(
QgsGeosContext::get(), mGeos.get(), geosGeom.get(), epsilon );
2545GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
2549 std::unique_ptr< QgsLineString > segmentized;
2555 line = segmentized.get();
2562 GEOSCoordSequence *coordSeq =
nullptr;
2564 const int numPoints = line->
numPoints();
2566 const bool hasZ = line->
is3D();
2568#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
2571 if ( !forceClose || ( line->
pointN( 0 ) == line->
pointN( numPoints - 1 ) ) )
2576 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, line->
xData(), line->
yData(), line->
zData(),
nullptr, numPoints );
2579 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points"_s.arg( numPoints ) );
2587 QVector< double > x = line->
xVector();
2588 if ( numPoints > 0 )
2589 x.append( x.at( 0 ) );
2590 QVector< double > y = line->
yVector();
2591 if ( numPoints > 0 )
2592 y.append( y.at( 0 ) );
2593 QVector< double > z = line->
zVector();
2594 if ( hasZ && numPoints > 0 )
2595 z.append( z.at( 0 ) );
2598 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, x.constData(), y.constData(), !hasZ ?
nullptr : z.constData(),
nullptr, numPoints + 1 );
2601 QgsDebugError( u
"GEOS Exception: Could not create closed coordinate sequence for %1 points"_s.arg( numPoints + 1 ) );
2612 const bool hasM =
false;
2623 int numOutPoints = numPoints;
2624 if ( forceClose && ( line->
pointN( 0 ) != line->
pointN( numPoints - 1 ) ) )
2631 coordSeq = GEOSCoordSeq_create_r( context, numOutPoints, coordDims );
2634 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions"_s.arg( numPoints ).arg( coordDims ) );
2638 const double *xData = line->
xData();
2639 const double *yData = line->
yData();
2640 const double *zData = hasZ ? line->
zData() :
nullptr;
2641 const double *mData = hasM ? line->
mData() :
nullptr;
2643 if ( precision > 0. )
2645 for (
int i = 0; i < numOutPoints; ++i )
2647 if ( i >= numPoints )
2650 xData = line->
xData();
2651 yData = line->
yData();
2652 zData = hasZ ? line->
zData() :
nullptr;
2653 mData = hasM ? line->
mData() :
nullptr;
2657 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
2661 GEOSCoordSeq_setXY_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
2665 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2671 for (
int i = 0; i < numOutPoints; ++i )
2673 if ( i >= numPoints )
2676 xData = line->
xData();
2677 yData = line->
yData();
2678 zData = hasZ ? line->
zData() :
nullptr;
2679 mData = hasM ? line->
mData() :
nullptr;
2683 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, *xData++, *yData++, *zData++ );
2687 GEOSCoordSeq_setXY_r( context, coordSeq, i, *xData++, *yData++ );
2691 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2701geos::unique_ptr
QgsGeos::createGeosPoint( const QgsAbstractGeometry *point,
int coordDims,
double precision, Qgis::GeosCreationFlags )
2707 return createGeosPointXY( pt->
x(), pt->
y(), pt->
is3D(), pt->
z(), pt->
isMeasure(), pt->
m(), coordDims, precision );
2719 if ( coordDims == 2 )
2722 if ( precision > 0. )
2723 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
2725 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, x, y ) );
2729 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( context, 1, coordDims );
2732 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions"_s.arg( coordDims ) );
2735 if ( precision > 0. )
2737 GEOSCoordSeq_setX_r( context, coordSeq, 0, std::round( x / precision ) * precision );
2738 GEOSCoordSeq_setY_r( context, coordSeq, 0, std::round( y / precision ) * precision );
2741 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, std::round( z / precision ) * precision );
2746 GEOSCoordSeq_setX_r( context, coordSeq, 0, x );
2747 GEOSCoordSeq_setY_r( context, coordSeq, 0, y );
2750 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, z );
2756 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 3, m );
2759 geosPoint.reset( GEOSGeom_createPoint_r( context, coordSeq ) );
2765geos::unique_ptr
QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve,
double precision, Qgis::GeosCreationFlags )
2771 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c, precision );
2784geos::unique_ptr
QgsGeos::createGeosPolygon( const QgsAbstractGeometry *poly,
double precision, Qgis::GeosCreationFlags flags )
2790 const QgsCurve *exteriorRing = polygon->
exteriorRing();
2791 if ( !exteriorRing )
2800 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( context, createCoordinateSequence( exteriorRing, precision,
true ) ) );
2803 QList< const QgsCurve * > holesToExport;
2804 holesToExport.reserve( nInteriorRings );
2805 for (
int i = 0; i < nInteriorRings; ++i )
2807 const QgsCurve *interiorRing = polygon->
interiorRing( i );
2810 holesToExport << interiorRing;
2815 if ( !holesToExport.empty() )
2818 for (
int i = 0; i < holesToExport.size(); ++i )
2820 holes[i] = GEOSGeom_createLinearRing_r( context, createCoordinateSequence( holesToExport[i], precision,
true ) );
2824 geosPolygon.reset( GEOSGeom_createPolygon_r( context, exteriorRingGeos.release(), holes, holesToExport.size() ) );
2845 offset.reset( GEOSOffsetCurve_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( joinStyle ), miterLimit ) );
2857 return fromGeos( res.get() ).release();
2875 GEOSBufferParams_setSingleSided_r( context, bp.get(), 1 );
2876 GEOSBufferParams_setQuadrantSegments_r( context, bp.get(), segments );
2877 GEOSBufferParams_setJoinStyle_r( context, bp.get(),
static_cast< int >( joinStyle ) );
2878 GEOSBufferParams_setMitreLimit_r( context, bp.get(), miterLimit );
2884 geos.reset( GEOSBufferWithParams_r( context, mGeos.get(), bp.get(),
distance ) );
2919 boundaryGeos =
asGeos( boundary );
2949 return std::numeric_limits< double >::quiet_NaN();
2958 return std::numeric_limits< double >::quiet_NaN();
3000 if ( !mGeos || !other )
3021 if ( !mGeos ||
mGeometry->dimension() == 0 )
3039 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
3043 int numGeoms = GEOSGetNumGeometries_r( context, mGeos.get() );
3044 if ( numGeoms == -1 )
3053 bool isMultiGeom =
false;
3054 int geosTypeId = GEOSGeomTypeId_r( context, mGeos.get() );
3055 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
3058 bool isLine = (
mGeometry->dimension() == 1 );
3065 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
3069 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
3074 if ( reshapedGeometry )
3080 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
3081 return reshapeResult;
3088 bool reshapeTookPlace =
false;
3093 for (
int i = 0; i < numGeoms; ++i )
3096 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3098 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3100 if ( currentReshapeGeometry )
3102 newGeoms[i] = currentReshapeGeometry.release();
3103 reshapeTookPlace =
true;
3107 newGeoms[i] = GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, mGeos.get(), i ) );
3114 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
3118 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
3122 if ( !newMultiGeom )
3131 if ( reshapeTookPlace )
3135 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
3136 return reshapedMultiGeom;
3159 if ( GEOSGeomTypeId_r( context, mGeos.get() ) != GEOS_MULTILINESTRING )
3166 double gridSize = parameters.
gridSize();
3169 geos::unique_ptr geosFixedSize( GEOSGeom_setPrecision_r( context, mGeos.get(), gridSize, 0 ) );
3170 geos.reset( GEOSLineMerge_r( context, geosFixedSize.get() ) );
3173 geos.reset( GEOSLineMerge_r( context, mGeos.get() ) );
3199 if ( mGeosPrepared )
3201 nearestCoord.reset( GEOSPreparedNearestPoints_r( context, mGeosPrepared.get(), otherGeom.get() ) );
3205 nearestCoord.reset( GEOSNearestPoints_r( context, mGeos.get(), otherGeom.get() ) );
3208 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx );
3209 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny );
3211 catch ( QgsGeosException &e )
3216 *errorMsg = e.what();
3221 return std::make_unique< QgsPoint >( nx, ny );
3226 if ( !mGeos || other.
isEmpty() )
3236 if ( !other || other->
isEmpty() )
3255 if ( !nearestCoord )
3258 *errorMsg = u
"GEOS returned no nearest points"_s;
3262 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx1 );
3263 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny1 );
3264 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 1, &nx2 );
3265 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 1, &ny2 );
3267 catch ( QgsGeosException &e )
3272 *errorMsg = e.what();
3277 auto line = std::make_unique< QgsLineString >();
3302 catch ( QgsGeosException &e )
3307 *errorMsg = e.what();
3322 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
3332 catch ( QgsGeosException &e )
3337 *errorMsg = e.what();
3354 lineGeosGeometries[validLines] = l.release();
3363 geos::unique_ptr result( GEOSPolygonize_r( context, lineGeosGeometries, validLines ) );
3364 for (
int i = 0; i < validLines; ++i )
3366 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3368 delete[] lineGeosGeometries;
3371 catch ( QgsGeosException &e )
3375 *errorMsg = e.what();
3377 for (
int i = 0; i < validLines; ++i )
3379 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3381 delete[] lineGeosGeometries;
3396 extentGeosGeom =
asGeos( extent, mPrecision );
3397 if ( !extentGeosGeom )
3408 geos.reset( GEOSVoronoiDiagram_r( context, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
3410 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3432 geos.reset( GEOSDelaunayTriangulation_r( context, mGeos.get(), tolerance, edgesOnly ) );
3434 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3446#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
3448 throw QgsNotSupportedException( QObject::tr(
"Calculating constrainedDelaunayTriangulation requires a QGIS build based on GEOS 3.11 or later" ) );
3460 geos.reset( GEOSConstrainedDelaunayTriangulation_r( context, mGeos.get() ) );
3462 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3467 std::unique_ptr< QgsAbstractGeometry > res =
fromGeos(
geos.get() );
3470 return std::unique_ptr< QgsAbstractGeometry >( collection->extractPartsByType(
Qgis::WkbType::Polygon,
true ) );
3482static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
3485 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( context, linestring );
3489 unsigned int coordSeqSize;
3490 if ( GEOSCoordSeq_getSize_r( context, coordSeq, &coordSeqSize ) == 0 )
3493 if ( coordSeqSize < 2 )
3496 GEOSCoordSeq_getX_r( context, coordSeq, 0, &x1 );
3497 GEOSCoordSeq_getY_r( context, coordSeq, 0, &y1 );
3498 GEOSCoordSeq_getX_r( context, coordSeq, coordSeqSize - 1, &x2 );
3499 GEOSCoordSeq_getY_r( context, coordSeq, coordSeqSize - 1, &y2 );
3507 double x1, y1, x2, y2;
3508 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
3511 double rx1, ry1, rx2, ry2;
3512 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
3515 bool intersectionAtOrigLineEndpoint = ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) != ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
3516 bool intersectionAtReshapeLineEndpoint = ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) || ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
3520 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
3524 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
3525 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, geoms, 2 ) );
3530 double x1res, y1res, x2res, y2res;
3531 if ( !_linestringEndpoints( res.get(), x1res, y1res, x2res, y2res ) )
3533 if ( ( x1res == x2 && y1res == y2 ) || ( x2res == x1 && y2res == y1 ) )
3534 res.reset( GEOSReverse_r( context, res.get() ) );
3545 if ( !line || !reshapeLineGeos )
3548 bool atLeastTwoIntersections =
false;
3549 bool oneIntersection =
false;
3550 QgsPointXY oneIntersectionPoint;
3556 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, line, reshapeLineGeos ) );
3557 if ( intersectGeom )
3559 const int geomType = GEOSGeomTypeId_r( context, intersectGeom.get() );
3560 atLeastTwoIntersections = ( geomType == GEOS_MULTIPOINT && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 1 )
3561 || ( geomType == GEOS_GEOMETRYCOLLECTION && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 )
3562 || ( geomType == GEOS_MULTILINESTRING && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 );
3564 if ( GEOSGeomTypeId_r( context, intersectGeom.get() ) == GEOS_POINT )
3566 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( context, intersectGeom.get() );
3568 GEOSCoordSeq_getX_r( context, intersectionCoordSeq, 0, &xi );
3569 GEOSCoordSeq_getY_r( context, intersectionCoordSeq, 0, &yi );
3570 oneIntersection =
true;
3571 oneIntersectionPoint = QgsPointXY( xi, yi );
3575 catch ( QgsGeosException & )
3577 atLeastTwoIntersections =
false;
3581 if ( oneIntersection )
3582 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
3584 if ( !atLeastTwoIntersections )
3588 double x1, y1, x2, y2;
3589 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
3592 geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1,
false, 0,
false, 0, 2, precision );
3593 geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2,
false, 0,
false, 0, 2, precision );
3595 bool isRing =
false;
3596 if ( GEOSGeomTypeId_r( context, line ) == GEOS_LINEARRING || GEOSEquals_r( context, beginLineVertex.get(), endLineVertex.get() ) == 1 )
3601 if ( !nodedGeometry )
3607 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, nodedGeometry.get() ) );
3613 int numMergedLines = GEOSGetNumGeometries_r( context, mergedLines.get() );
3614 if ( numMergedLines < 2 )
3616 if ( numMergedLines == 1 )
3625 QVector<GEOSGeometry *> resultLineParts;
3626 QVector<GEOSGeometry *> probableParts;
3628 for (
int i = 0; i < numMergedLines; ++i )
3630 const GEOSGeometry *currentGeom = GEOSGetGeometryN_r( context, mergedLines.get(), i );
3633 bool alreadyAdded =
false;
3635 double bufferDistance = std::pow( 10.0L, geomDigits( currentGeom ) - 11 );
3636 for (
const GEOSGeometry *other : std::as_const( resultLineParts ) )
3638 GEOSHausdorffDistance_r( context, currentGeom, other, &
distance );
3641 alreadyAdded =
true;
3648 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( context, currentGeom );
3649 unsigned int currentCoordSeqSize;
3650 GEOSCoordSeq_getSize_r( context, currentCoordSeq, ¤tCoordSeqSize );
3651 if ( currentCoordSeqSize < 2 )
3655 double xBegin, xEnd, yBegin, yEnd;
3656 GEOSCoordSeq_getX_r( context, currentCoordSeq, 0, &xBegin );
3657 GEOSCoordSeq_getY_r( context, currentCoordSeq, 0, &yBegin );
3658 GEOSCoordSeq_getX_r( context, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
3659 GEOSCoordSeq_getY_r( context, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
3660 geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin,
false, 0,
false, 0, 2, precision );
3661 geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd,
false, 0,
false, 0, 2, precision );
3664 int nEndpointsOnOriginalLine = 0;
3665 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
3666 nEndpointsOnOriginalLine += 1;
3668 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
3669 nEndpointsOnOriginalLine += 1;
3672 int nEndpointsSameAsOriginalLine = 0;
3673 if ( GEOSEquals_r( context, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1 || GEOSEquals_r( context, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3674 nEndpointsSameAsOriginalLine += 1;
3676 if ( GEOSEquals_r( context, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1 || GEOSEquals_r( context, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3677 nEndpointsSameAsOriginalLine += 1;
3680 bool currentGeomOverlapsOriginalGeom =
false;
3681 bool currentGeomOverlapsReshapeLine =
false;
3682 if ( lineContainedInLine( currentGeom, line ) == 1 )
3683 currentGeomOverlapsOriginalGeom =
true;
3685 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
3686 currentGeomOverlapsReshapeLine =
true;
3689 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3691 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3694 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3696 probableParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3698 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3700 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3702 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3704 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3706 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
3708 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3713 if ( isRing && !probableParts.isEmpty() )
3717 double maxLength = -std::numeric_limits<double>::max();
3718 double currentLength = 0;
3719 for (
int i = 0; i < probableParts.size(); ++i )
3721 currentGeom = probableParts.at( i );
3722 GEOSLength_r( context, currentGeom, ¤tLength );
3723 if ( currentLength > maxLength )
3725 maxLength = currentLength;
3726 maxGeom.reset( currentGeom );
3730 GEOSGeom_destroy_r( context, currentGeom );
3733 resultLineParts.push_back( maxGeom.release() );
3737 if ( resultLineParts.empty() )
3740 if ( resultLineParts.size() == 1 )
3742 result.reset( resultLineParts[0] );
3747 for (
int i = 0; i < resultLineParts.size(); ++i )
3749 lineArray[i] = resultLineParts[i];
3753 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
3757 result.reset( GEOSLineMerge_r( context, multiLineGeom.get() ) );
3761 if ( GEOSGeomTypeId_r( context, result.get() ) != GEOS_LINESTRING )
3767 bool reverseLine =
false;
3771 char isResultCCW = 0, isOriginCCW = 0;
3772 if ( GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, result.get() ), &isResultCCW ) && GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, line ), &isOriginCCW ) )
3775 reverseLine = ( isOriginCCW == 1 && isResultCCW == 0 ) || ( isOriginCCW == 0 && isResultCCW == 1 );
3781 double x1res, y1res, x2res, y2res;
3782 if ( !_linestringEndpoints( result.get(), x1res, y1res, x2res, y2res ) )
3784 geos::unique_ptr beginResultLineVertex = createGeosPointXY( x1res, y1res,
false, 0,
false, 0, 2, precision );
3785 geos::unique_ptr endResultLineVertex = createGeosPointXY( x2res, y2res,
false, 0,
false, 0, 2, precision );
3786 reverseLine = GEOSEquals_r( context, beginLineVertex.get(), endResultLineVertex.get() ) == 1 || GEOSEquals_r( context, endLineVertex.get(), beginResultLineVertex.get() ) == 1;
3789 result.reset( GEOSReverse_r( context, result.get() ) );
3797 int nIntersections = 0;
3798 int lastIntersectingRing = -2;
3802 int nRings = GEOSGetNumInteriorRings_r( context, polygon );
3807 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( context, polygon );
3808 if ( GEOSIntersects_r( context, outerRing, reshapeLineGeos ) == 1 )
3811 lastIntersectingRing = -1;
3812 lastIntersectingGeom = outerRing;
3820 for (
int i = 0; i < nRings; ++i )
3822 innerRings[i] = GEOSGetInteriorRingN_r( context, polygon, i );
3823 if ( GEOSIntersects_r( context, innerRings[i], reshapeLineGeos ) == 1 )
3826 lastIntersectingRing = i;
3827 lastIntersectingGeom = innerRings[i];
3831 catch ( QgsGeosException & )
3836 if ( nIntersections != 1 )
3838 delete[] innerRings;
3843 geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
3844 if ( !reshapeResult )
3846 delete[] innerRings;
3852 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( context, reshapeResult.get() );
3853 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( context, reshapeSequence );
3855 reshapeResult.reset();
3859 newRing = GEOSGeom_createLinearRing_r( context, newCoordSequence );
3861 catch ( QgsGeosException & )
3868 delete[] innerRings;
3873 if ( lastIntersectingRing == -1 )
3874 newOuterRing = newRing;
3876 newOuterRing = GEOSGeom_clone_r( context, outerRing );
3879 QVector<GEOSGeometry *> ringList;
3882 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( context, GEOSGeom_clone_r( context, newOuterRing ),
nullptr, 0 );
3883 if ( outerRingPoly )
3885 ringList.reserve( nRings );
3887 for (
int i = 0; i < nRings; ++i )
3889 if ( lastIntersectingRing == i )
3890 currentRing = newRing;
3892 currentRing = GEOSGeom_clone_r( context, innerRings[i] );
3895 if ( GEOSContains_r( context, outerRingPoly, currentRing ) == 1 )
3896 ringList.push_back( currentRing );
3898 GEOSGeom_destroy_r( context, currentRing );
3901 GEOSGeom_destroy_r( context, outerRingPoly );
3905 for (
int i = 0; i < ringList.size(); ++i )
3906 newInnerRings[i] = ringList.at( i );
3908 delete[] innerRings;
3910 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( context, newOuterRing, newInnerRings, ringList.size() ) );
3911 delete[] newInnerRings;
3913 return reshapedPolygon;
3918 if ( !line1 || !line2 )
3923 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
3930 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, bufferGeom.get(), line1 ) );
3933 double intersectGeomLength;
3936 GEOSLength_r( context, intersectionGeom.get(), &intersectGeomLength );
3937 GEOSLength_r( context, line1, &line1Length );
3939 double intersectRatio = line1Length / intersectGeomLength;
3940 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
3948 if ( !point || !line )
3951 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
3954 geos::unique_ptr lineBuffer( GEOSBuffer_r( context, line, bufferDistance, 8 ) );
3958 bool contained =
false;
3959 if ( GEOSContains_r( context, lineBuffer.get(), point ) == 1 )
3972 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( context, bbox.get() );
3976 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( context, bBoxRing );
3978 if ( !bBoxCoordSeq )
3981 unsigned int nCoords = 0;
3982 if ( !GEOSCoordSeq_getSize_r( context, bBoxCoordSeq, &nCoords ) )
3986 for (
unsigned int i = 0; i < nCoords - 1; ++i )
3989 GEOSCoordSeq_getX_r( context, bBoxCoordSeq, i, &t );
3992 digits = std::ceil( std::log10( std::fabs( t ) ) );
3993 if ( digits > maxDigits )
3996 GEOSCoordSeq_getY_r( context, bBoxCoordSeq, i, &t );
3997 digits = std::ceil( std::log10( std::fabs( t ) ) );
3998 if ( digits > maxDigits )
4006#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4007 : mFeedback( feedback )
4009 GEOSContext_setInterruptCallback_r(
QgsGeosContext::get(), &callback,
reinterpret_cast< void *
>( mFeedback ) );
4020#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4025#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4026int QgsScopedGeosContextRegisterFeedback::callback(
void *userData )
4032 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.
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.
@ LineStringZM
LineStringZM.
@ GeometryCollection
GeometryCollection.
@ LineStringZ
LineStringZ.
@ PolyhedralSurface
PolyhedralSurface.
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.
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.
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 proj 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.
EngineOperationResult splitGeometry(const QgsLineString &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, QString *errorMsg=nullptr, bool skipIntersectionCheck=false) const override
Splits this geometry according to a given line.
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 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.
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.
Line string geometry type, with support for z-dimension and m-values.
const double * yData() const
Returns a const pointer to the y vertex data.
const double * xData() const
Returns a const pointer to the x vertex data.
QVector< double > xVector() const
Returns the x vertex values as a vector.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the linestring does not have z values.
int numPoints() const override
Returns the number of points in the curve.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
QVector< double > yVector() const
Returns the y vertex values as a vector.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
QVector< double > zVector() const
Returns the z vertex values as a vector.
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.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the linestring does not have m values.
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.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
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 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