31#define DEFAULT_QUADRANT_SEGMENTS 8
33#define CATCH_GEOS(r) \
34 catch (GEOSException &) \
39#define CATCH_GEOS_WITH_ERRMSG(r) \
40 catch (GEOSException &e) \
44 *errorMsg = e.what(); \
51static void throwGEOSException(
const char *fmt, ... )
57 vsnprintf( buffer,
sizeof buffer, fmt, ap );
60 QString message = QString::fromUtf8( buffer );
70 throw GEOSException( message );
78 throw GEOSException( message );
83static void printGEOSNotice(
const char *fmt, ... )
90 vsnprintf( buffer,
sizeof buffer, fmt, ap );
100 GEOSContextHandle_t ctxt;
104 ctxt = initGEOS_r( printGEOSNotice, throwGEOSException );
109 finishGEOS_r( ctxt );
112 GEOSInit(
const GEOSInit &rh ) =
delete;
113 GEOSInit &operator=(
const GEOSInit &rh ) =
delete;
120 GEOSGeom_destroy_r( geosinit()->ctxt, geom );
125 GEOSPreparedGeom_destroy_r( geosinit()->ctxt, geom );
130 GEOSBufferParams_destroy_r( geosinit()->ctxt, params );
135 GEOSCoordSeq_destroy_r( geosinit()->ctxt, sequence );
147 cacheGeos( allowInvalidSubGeom );
170#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
172 throw QgsNotSupportedException( QObject::tr(
"The structured method to make geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
175 throw QgsNotSupportedException( QObject::tr(
"The keep collapsed option for making geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
179 geos.reset( GEOSMakeValid_r( geosinit()->ctxt, mGeos.get() ) );
184 GEOSMakeValidParams *params = GEOSMakeValidParams_create_r( geosinit()->ctxt );
188 GEOSMakeValidParams_setMethod_r( geosinit()->ctxt, params, GEOS_MAKE_VALID_LINEWORK );
192 GEOSMakeValidParams_setMethod_r( geosinit()->ctxt, params, GEOS_MAKE_VALID_STRUCTURE );
196 GEOSMakeValidParams_setKeepCollapsed_r( geosinit()->ctxt,
198 keepCollapsed ? 1 : 0 );
203 geos.reset( GEOSMakeValidWithParams_r( geosinit()->ctxt, mGeos.get(), params ) );
204 GEOSMakeValidParams_destroy_r( geosinit()->ctxt, params );
206 catch ( GEOSException &e )
210 *errorMsg = e.what();
212 GEOSMakeValidParams_destroy_r( geosinit()->ctxt, params );
241 std::unique_ptr< QgsAbstractGeometry > geom =
fromGeos( newPart );
248 mGeosPrepared.reset();
261 mGeosPrepared.reset( GEOSPrepare_r( geosinit()->ctxt, mGeos.get() ) );
265void QgsGeos::cacheGeos(
bool allowInvalidSubGeom )
const
282 return overlay( geom, OverlayIntersection, errorMsg, parameters ).release();
287 return overlay( geom, OverlayDifference, errorMsg, parameters ).release();
302 catch ( GEOSException &e )
304 logError( QStringLiteral(
"GEOS" ), e.what() );
307 *errorMsg = e.what();
318 int partType = GEOSGeomTypeId_r( geosinit()->ctxt, currentPart );
321 if ( partType == GEOS_POINT )
332 if ( partType == GEOS_MULTILINESTRING || partType == GEOS_MULTIPOLYGON || partType == GEOS_GEOMETRYCOLLECTION )
334 int partCount = GEOSGetNumGeometries_r( geosinit()->ctxt, currentPart );
335 for (
int i = 0; i < partCount; ++i )
337 subdivideRecursive( GEOSGetGeometryN_r( geosinit()->ctxt, currentPart, i ), maxNodes, depth, parts, clipRect, gridSize );
348 int vertexCount = GEOSGetNumCoordinates_r( geosinit()->ctxt, currentPart );
349 if ( vertexCount == 0 )
353 else if ( vertexCount < maxNodes )
360 double width = clipRect.
width();
361 double height = clipRect.
height();
364 if ( width > height )
377 halfClipRect1.
setYMinimum( halfClipRect1.
yMinimum() - std::numeric_limits<double>::epsilon() );
378 halfClipRect2.
setYMinimum( halfClipRect2.
yMinimum() - std::numeric_limits<double>::epsilon() );
379 halfClipRect1.
setYMaximum( halfClipRect1.
yMaximum() + std::numeric_limits<double>::epsilon() );
380 halfClipRect2.
setYMaximum( halfClipRect2.
yMaximum() + std::numeric_limits<double>::epsilon() );
384 halfClipRect1.
setXMinimum( halfClipRect1.
xMinimum() - std::numeric_limits<double>::epsilon() );
385 halfClipRect2.
setXMinimum( halfClipRect2.
xMinimum() - std::numeric_limits<double>::epsilon() );
386 halfClipRect1.
setXMaximum( halfClipRect1.
xMaximum() + std::numeric_limits<double>::epsilon() );
387 halfClipRect2.
setXMaximum( halfClipRect2.
xMaximum() + std::numeric_limits<double>::epsilon() );
399 clipPart1.reset( GEOSIntersectionPrec_r( geosinit()->ctxt, mGeos.get(), clipPart1.get(), gridSize ) );
401 subdivideRecursive( clipPart1.get(), maxNodes, depth, parts, halfClipRect1, gridSize );
407 clipPart2.reset( GEOSIntersectionPrec_r( geosinit()->ctxt, mGeos.get(), clipPart2.get(), gridSize ) );
409 subdivideRecursive( clipPart2.get(), maxNodes, depth, parts, halfClipRect2, gridSize );
421 maxNodes = std::max( maxNodes, 8 );
430 return std::move( parts );
435 return overlay( geom, OverlayUnion, errorMsg, parameters ).release();
440 std::vector<geos::unique_ptr> geosGeometries;
441 geosGeometries.reserve( geomList.size() );
447 geosGeometries.emplace_back(
asGeos( g, mPrecision ) );
453 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
456 geomUnion.reset( GEOSUnaryUnionPrec_r( geosinit()->ctxt, geomCollection.get(), parameters.
gridSize() ) );
460 geomUnion.reset( GEOSUnaryUnion_r( geosinit()->ctxt, geomCollection.get() ) );
465 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
466 return result.release();
471 std::vector<geos::unique_ptr> geosGeometries;
472 geosGeometries.reserve( geomList.size() );
478 geosGeometries.emplace_back(
asGeos( g.constGet(), mPrecision ) );
484 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
488 geomUnion.reset( GEOSUnaryUnionPrec_r( geosinit()->ctxt, geomCollection.get(), parameters.
gridSize() ) );
492 geomUnion.reset( GEOSUnaryUnion_r( geosinit()->ctxt, geomCollection.get() ) );
498 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
499 return result.release();
504 return overlay( geom, OverlaySymDifference, errorMsg, parameters ).release();
516 if ( !otherGeosGeom )
525 GEOSPreparedDistance_r( geosinit()->ctxt, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
529 GEOSDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &
distance );
545 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
553 GEOSPreparedDistance_r( geosinit()->ctxt, mGeosPrepared.get(), point.get(), &
distance );
557 GEOSDistance_r( geosinit()->ctxt, mGeos.get(), point.get(), &
distance );
573 if ( !otherGeosGeom )
588#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
589 return GEOSPreparedDistanceWithin_r( geosinit()->ctxt, mGeosPrepared.get(), otherGeosGeom.get(), maxdist );
591 GEOSPreparedDistance_r( geosinit()->ctxt, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
596#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
597 return GEOSDistanceWithin_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), maxdist );
599 GEOSDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &
distance );
610 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
619 return GEOSPreparedContains_r( geosinit()->ctxt, mGeosPrepared.get(), point.get() ) == 1;
622 result = ( GEOSContains_r( geosinit()->ctxt, mGeos.get(), point.get() ) == 1 );
624 catch ( GEOSException &e )
626 logError( QStringLiteral(
"GEOS" ), e.what() );
629 *errorMsg = e.what();
646 if ( !otherGeosGeom )
653 GEOSHausdorffDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &
distance );
669 if ( !otherGeosGeom )
676 GEOSHausdorffDistanceDensify_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), densifyFraction, &
distance );
692 if ( !otherGeosGeom )
699 GEOSFrechetDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &
distance );
715 if ( !otherGeosGeom )
722 GEOSFrechetDistanceDensify_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), densifyFraction, &
distance );
731 return relation( geom, RelationIntersects, errorMsg );
736 return relation( geom, RelationTouches, errorMsg );
741 return relation( geom, RelationCrosses, errorMsg );
746 return relation( geom, RelationWithin, errorMsg );
751 return relation( geom, RelationOverlaps, errorMsg );
756 return relation( geom, RelationContains, errorMsg );
761 return relation( geom, RelationDisjoint, errorMsg );
780 char *r = GEOSRelate_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() );
783 result = QString( r );
784 GEOSFree_r( geosinit()->ctxt, r );
787 catch ( GEOSException &e )
789 logError( QStringLiteral(
"GEOS" ), e.what() );
792 *errorMsg = e.what();
801 if ( !mGeos || !geom )
815 result = ( GEOSRelatePattern_r( geosinit()->ctxt, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
817 catch ( GEOSException &e )
819 logError( QStringLiteral(
"GEOS" ), e.what() );
822 *errorMsg = e.what();
839 if ( GEOSArea_r( geosinit()->ctxt, mGeos.get(), &
area ) != 1 )
855 if ( GEOSLength_r( geosinit()->ctxt, mGeos.get(), &
length ) != 1 )
863 QVector<QgsGeometry> &newGeometries,
866 QString *errorMsg,
bool skipIntersectionCheck )
const
881 if ( !GEOSisValid_r( geosinit()->ctxt, mGeos.get() ) )
889 newGeometries.clear();
896 splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
900 splitLineGeos = createGeosPointXY( splitLine.
xAt( 0 ), splitLine.
yAt( 0 ),
false, 0,
false, 0, 2, mPrecision );
907 if ( !GEOSisValid_r( geosinit()->ctxt, splitLineGeos.get() ) || !GEOSisSimple_r( geosinit()->ctxt, splitLineGeos.get() ) )
915 if ( !topologicalTestPointsSplit( splitLineGeos.get(), topologyTestPoints ) )
924 returnCode = splitLinearGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
928 returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
956 geos::unique_ptr intersectionGeom( GEOSIntersection_r( geosinit()->ctxt, mGeos.get(), splitLine ) );
957 if ( !intersectionGeom )
961 int nIntersectGeoms = 1;
962 if ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectionGeom.get() ) == GEOS_LINESTRING
963 || GEOSGeomTypeId_r( geosinit()->ctxt, intersectionGeom.get() ) == GEOS_POINT )
967 nIntersectGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, intersectionGeom.get() );
969 for (
int i = 0; i < nIntersectGeoms; ++i )
973 currentIntersectGeom = intersectionGeom.get();
975 currentIntersectGeom = GEOSGetGeometryN_r( geosinit()->ctxt, intersectionGeom.get(), i );
977 const GEOSCoordSequence *lineSequence = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, currentIntersectGeom );
978 unsigned int sequenceSize = 0;
980 if ( GEOSCoordSeq_getSize_r( geosinit()->ctxt, lineSequence, &sequenceSize ) != 0 )
982 for (
unsigned int i = 0; i < sequenceSize; ++i )
984 if ( GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, lineSequence, i, &x, &y, &z ) )
986 testPoints.push_back(
QgsPoint( x, y, z ) );
999 int type = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
1001 std::unique_ptr< QgsMultiCurve > multiCurve;
1002 if ( type == GEOS_MULTILINESTRING )
1004 multiCurve.reset( qgsgeometry_cast<QgsMultiCurve *>(
mGeometry->
clone() ) );
1006 else if ( type == GEOS_LINESTRING )
1024 std::unique_ptr< QgsMultiPoint > splitPoints;
1026 std::unique_ptr< QgsAbstractGeometry > splitGeom(
fromGeos( GEOSsplitPoint ) );
1028 if ( qgsgeometry_cast<QgsMultiPoint *>( splitGeom.get() ) )
1030 splitPoints.reset( qgsgeometry_cast<QgsMultiPoint *>( splitGeom.release() ) );
1032 else if ( qgsgeometry_cast<QgsPoint *>( splitGeom.get() ) )
1034 splitPoints = std::make_unique< QgsMultiPoint >();
1035 if ( qgsgeometry_cast<QgsPoint *>( splitGeom.get() ) )
1037 splitPoints->addGeometry( qgsgeometry_cast<QgsPoint *>( splitGeom.release() ) );
1045 for (
int geometryIndex = 0; geometryIndex < multiCurve->numGeometries(); ++geometryIndex )
1047 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( multiCurve->geometryN( geometryIndex ) );
1050 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( multiCurve->geometryN( geometryIndex ) );
1058 QMap< int, QVector< QPair< double, QgsPoint > > >pointMap;
1059 for (
int splitPointIndex = 0; splitPointIndex < splitPoints->numGeometries(); ++splitPointIndex )
1061 const QgsPoint *intersectionPoint = splitPoints->pointN( splitPointIndex );
1067 line->
closestSegment( *intersectionPoint, segmentPoint2D, nextVertex );
1083 const QPair< double, QgsPoint > pair = qMakePair(
distance, *correctSegmentPoint.get() );
1084 if ( pointMap.contains( nextVertex.
vertex - 1 ) )
1085 pointMap[ nextVertex.
vertex - 1 ].append( pair );
1087 pointMap[ nextVertex.
vertex - 1 ] = QVector< QPair< double, QgsPoint > >() << pair;
1092 for (
auto &p : pointMap )
1094 std::sort( p.begin(), p.end(), [](
const QPair< double, QgsPoint > &a,
const QPair< double, QgsPoint > &b ) { return a.first < b.first; } );
1101 for (
int vertexIndex = 0; vertexIndex < nVertices; ++vertexIndex )
1105 if ( pointMap.contains( vertexIndex ) )
1108 for (
int k = 0; k < pointMap[ vertexIndex ].size(); ++k )
1110 splitPoint = pointMap[ vertexIndex ][k].second;
1111 if ( splitPoint == currentPoint )
1117 else if ( splitPoint == line->
pointN( vertexIndex + 1 ) )
1136 return asGeos( &lines, mPrecision );
1141 Q_UNUSED( skipIntersectionCheck )
1148 GEOSContextHandle_t geosctxt = geosinit()->ctxt;
1150 geos::unique_ptr intersectGeom( GEOSIntersection_r( geosctxt, splitLine, mGeos.get() ) );
1151 if ( !intersectGeom || GEOSisEmpty_r( geosctxt, intersectGeom.get() ) )
1155 const int linearIntersect = GEOSRelatePattern_r( geosctxt, mGeos.get(), splitLine,
"1********" );
1156 if ( linearIntersect > 0 )
1164 std::vector<geos::unique_ptr> lineGeoms;
1166 const int splitType = GEOSGeomTypeId_r( geosctxt, splitGeom.get() );
1167 if ( splitType == GEOS_MULTILINESTRING )
1169 const int nGeoms = GEOSGetNumGeometries_r( geosctxt, splitGeom.get() );
1170 lineGeoms.reserve( nGeoms );
1171 for (
int i = 0; i < nGeoms; ++i )
1172 lineGeoms.emplace_back( GEOSGeom_clone_r( geosctxt, GEOSGetGeometryN_r( geosctxt, splitGeom.get(), i ) ) );
1177 lineGeoms.emplace_back( GEOSGeom_clone_r( geosctxt, splitGeom.get() ) );
1180 mergeGeometriesMultiTypeSplit( lineGeoms );
1200 if ( !mGeosPrepared )
1203 GEOSContextHandle_t geosctxt = geosinit()->ctxt;
1206 if ( !skipIntersectionCheck && !GEOSPreparedIntersects_r( geosctxt, mGeosPrepared.get(), splitLine ) )
1211 if ( !nodedGeometry )
1220 const int numberOfGeometriesPolygon = numberOfGeometries( polygons.get() );
1221 if ( numberOfGeometriesPolygon == 0 )
1228 std::vector<geos::unique_ptr> testedGeometries;
1233 for (
int i = 0; i < numberOfGeometriesPolygon; i++ )
1235 const GEOSGeometry *polygon = GEOSGetGeometryN_r( geosctxt, polygons.get(), i );
1239 testedGeometries.emplace_back( GEOSGeom_clone_r( geosctxt, polygon ) );
1242 const size_t nGeometriesThis = numberOfGeometries( mGeos.get() );
1243 if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
1253 mergeGeometriesMultiTypeSplit( testedGeometries );
1256 for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( geosctxt, testedGeometries[i].get() ); ++i )
1259 if ( i < testedGeometries.size() )
1274 if ( !splitLine || !geom )
1278 if ( GEOSGeomTypeId_r( geosinit()->ctxt, geom ) == GEOS_POLYGON || GEOSGeomTypeId_r( geosinit()->ctxt, geom ) == GEOS_MULTIPOLYGON )
1279 geometryBoundary.reset( GEOSBoundary_r( geosinit()->ctxt, geom ) );
1281 geometryBoundary.reset( GEOSGeom_clone_r( geosinit()->ctxt, geom ) );
1283 geos::unique_ptr splitLineClone( GEOSGeom_clone_r( geosinit()->ctxt, splitLine ) );
1284 geos::unique_ptr unionGeometry( GEOSUnion_r( geosinit()->ctxt, splitLineClone.get(), geometryBoundary.get() ) );
1286 return unionGeometry;
1289int QgsGeos::mergeGeometriesMultiTypeSplit( std::vector<geos::unique_ptr> &splitResult )
const
1295 int type = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
1296 if ( type != GEOS_GEOMETRYCOLLECTION &&
1297 type != GEOS_MULTILINESTRING &&
1298 type != GEOS_MULTIPOLYGON &&
1299 type != GEOS_MULTIPOINT )
1303 std::vector<geos::unique_ptr> unionGeom;
1305 std::vector<geos::unique_ptr> newSplitResult;
1307 for (
size_t i = 0; i < splitResult.size(); ++i )
1310 bool isPart =
false;
1311 for (
int j = 0; j < GEOSGetNumGeometries_r( geosinit()->ctxt, mGeos.get() ); j++ )
1313 if ( GEOSEquals_r( geosinit()->ctxt, splitResult[i].get(), GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), j ) ) )
1322 unionGeom.emplace_back( std::move( splitResult[i] ) );
1326 std::vector<geos::unique_ptr> geomVector;
1327 geomVector.emplace_back( std::move( splitResult[i] ) );
1329 if ( type == GEOS_MULTILINESTRING )
1330 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, geomVector ) );
1331 else if ( type == GEOS_MULTIPOLYGON )
1332 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, geomVector ) );
1336 splitResult = std::move( newSplitResult );
1339 if ( !unionGeom.empty() )
1341 if ( type == GEOS_MULTILINESTRING )
1342 splitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, unionGeom ) );
1343 else if ( type == GEOS_MULTIPOLYGON )
1344 splitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ) );
1350geos::unique_ptr QgsGeos::createGeosCollection(
int typeId, std::vector<geos::unique_ptr> &geoms )
1352 std::vector<GEOSGeometry *> geomarr;
1353 geomarr.reserve( geoms.size() );
1357 if ( geomUniquePtr )
1359 if ( !GEOSisEmpty_r( geosinit()->ctxt, geomUniquePtr.get() ) )
1363 geomarr.emplace_back( geomUniquePtr.release() );
1371 geomRes.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, typeId, geomarr.data(), geomarr.size() ) );
1373 catch ( GEOSException & )
1377 GEOSGeom_destroy_r( geosinit()->ctxt, geom );
1391 int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit()->ctxt,
geos );
1392 int nDims = GEOSGeom_getDimensions_r( geosinit()->ctxt,
geos );
1393 bool hasZ = ( nCoordDims == 3 );
1394 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1396 switch ( GEOSGeomTypeId_r( geosinit()->ctxt,
geos ) )
1400 if ( GEOSisEmpty_r( geosinit()->ctxt,
geos ) )
1403 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt,
geos );
1404 unsigned int nPoints = 0;
1405 GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1406 return nPoints > 0 ? std::unique_ptr<QgsAbstractGeometry>(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() ) :
nullptr;
1408 case GEOS_LINESTRING:
1410 return sequenceToLinestring(
geos, hasZ, hasM );
1416 case GEOS_MULTIPOINT:
1418 std::unique_ptr< QgsMultiPoint > multiPoint(
new QgsMultiPoint() );
1419 int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt,
geos );
1420 multiPoint->reserve( nParts );
1421 for (
int i = 0; i < nParts; ++i )
1423 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt,
geos, i ) );
1426 unsigned int nPoints = 0;
1427 GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1429 multiPoint->addGeometry(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1432 return std::move( multiPoint );
1434 case GEOS_MULTILINESTRING:
1437 int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt,
geos );
1438 multiLineString->reserve( nParts );
1439 for (
int i = 0; i < nParts; ++i )
1441 std::unique_ptr< QgsLineString >line( sequenceToLinestring( GEOSGetGeometryN_r( geosinit()->ctxt,
geos, i ), hasZ, hasM ) );
1444 multiLineString->addGeometry( line.release() );
1447 return std::move( multiLineString );
1449 case GEOS_MULTIPOLYGON:
1451 std::unique_ptr< QgsMultiPolygon > multiPolygon(
new QgsMultiPolygon() );
1453 int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt,
geos );
1454 multiPolygon->reserve( nParts );
1455 for (
int i = 0; i < nParts; ++i )
1457 std::unique_ptr< QgsPolygon > poly =
fromGeosPolygon( GEOSGetGeometryN_r( geosinit()->ctxt,
geos, i ) );
1460 multiPolygon->addGeometry( poly.release() );
1463 return std::move( multiPolygon );
1465 case GEOS_GEOMETRYCOLLECTION:
1468 int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt,
geos );
1469 geomCollection->reserve( nParts );
1470 for (
int i = 0; i < nParts; ++i )
1472 std::unique_ptr< QgsAbstractGeometry > geom(
fromGeos( GEOSGetGeometryN_r( geosinit()->ctxt,
geos, i ) ) );
1475 geomCollection->addGeometry( geom.release() );
1478 return std::move( geomCollection );
1486 if ( GEOSGeomTypeId_r( geosinit()->ctxt,
geos ) != GEOS_POLYGON )
1491 int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit()->ctxt,
geos );
1492 int nDims = GEOSGeom_getDimensions_r( geosinit()->ctxt,
geos );
1493 bool hasZ = ( nCoordDims == 3 );
1494 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1496 std::unique_ptr< QgsPolygon > polygon(
new QgsPolygon() );
1498 const GEOSGeometry *ring = GEOSGetExteriorRing_r( geosinit()->ctxt,
geos );
1501 polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1504 QVector<QgsCurve *> interiorRings;
1505 const int ringCount = GEOSGetNumInteriorRings_r( geosinit()->ctxt,
geos );
1506 interiorRings.reserve( ringCount );
1507 for (
int i = 0; i < ringCount; ++i )
1509 ring = GEOSGetInteriorRingN_r( geosinit()->ctxt,
geos, i );
1512 interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1515 polygon->setInteriorRings( interiorRings );
1520std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring(
const GEOSGeometry *
geos,
bool hasZ,
bool hasM )
1522 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt,
geos );
1524 unsigned int nPoints;
1525 GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1527 QVector< double > xOut( nPoints );
1528 QVector< double > yOut( nPoints );
1529 QVector< double > zOut;
1531 zOut.resize( nPoints );
1532 QVector< double > mOut;
1534 mOut.resize( nPoints );
1536 double *x = xOut.data();
1537 double *y = yOut.data();
1538 double *z = zOut.data();
1539 double *m = mOut.data();
1541#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
1542 GEOSCoordSeq_copyToArrays_r( geosinit()->ctxt, cs, x, y, hasZ ? z :
nullptr, hasM ? m :
nullptr );
1544 for (
unsigned int i = 0; i < nPoints; ++i )
1547 GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, cs, i, x++, y++, z++ );
1549 GEOSCoordSeq_getXY_r( geosinit()->ctxt, cs, i, x++, y++ );
1552 GEOSCoordSeq_getOrdinate_r( geosinit()->ctxt, cs, i, 3, m++ );
1556 std::unique_ptr< QgsLineString > line(
new QgsLineString( xOut, yOut, zOut, mOut ) );
1565 int geometryType = GEOSGeomTypeId_r( geosinit()->ctxt, g );
1566 if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING
1567 || geometryType == GEOS_POLYGON )
1571 return GEOSGetNumGeometries_r( geosinit()->ctxt, g );
1585 GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, cs, i, &x, &y, &z );
1587 GEOSCoordSeq_getXY_r( geosinit()->ctxt, cs, i, &x, &y );
1590 GEOSCoordSeq_getOrdinate_r( geosinit()->ctxt, cs, i, 3, &m );
1626 int geosType = GEOS_GEOMETRYCOLLECTION;
1633 geosType = GEOS_MULTIPOINT;
1637 geosType = GEOS_MULTILINESTRING;
1641 geosType = GEOS_MULTIPOLYGON;
1656 std::vector<geos::unique_ptr> geomVector;
1657 geomVector.reserve(
c->numGeometries() );
1658 for (
int i = 0; i <
c->numGeometries(); ++i )
1661 if ( !allowInvalidSubGeom && !geosGeom )
1665 geomVector.emplace_back( std::move( geosGeom ) );
1667 return createGeosCollection( geosType, geomVector );
1674 return createGeosPoint(
static_cast<const QgsPoint *
>( geom ), coordDims,
precision );
1692 if ( !mGeos || !geom )
1703 const double gridSize = parameters.
gridSize();
1710 case OverlayIntersection:
1713 opGeom.reset( GEOSIntersectionPrec_r( geosinit()->ctxt, mGeos.get(), geosGeom.get(), gridSize ) );
1717 opGeom.reset( GEOSIntersection_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1721 case OverlayDifference:
1724 opGeom.reset( GEOSDifferencePrec_r( geosinit()->ctxt, mGeos.get(), geosGeom.get(), gridSize ) );
1728 opGeom.reset( GEOSDifference_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1737 unionGeometry.reset( GEOSUnionPrec_r( geosinit()->ctxt, mGeos.get(), geosGeom.get(), gridSize ) );
1741 unionGeometry.reset( GEOSUnion_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1744 if ( unionGeometry && GEOSGeomTypeId_r( geosinit()->ctxt, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1746 geos::unique_ptr mergedLines( GEOSLineMerge_r( geosinit()->ctxt, unionGeometry.get() ) );
1749 unionGeometry = std::move( mergedLines );
1753 opGeom = std::move( unionGeometry );
1757 case OverlaySymDifference:
1760 opGeom.reset( GEOSSymDifferencePrec_r( geosinit()->ctxt, mGeos.get(), geosGeom.get(), gridSize ) );
1764 opGeom.reset( GEOSSymDifference_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1770 catch ( GEOSException &e )
1772 logError( QStringLiteral(
"GEOS" ), e.what() );
1775 *errorMsg = e.what();
1781bool QgsGeos::relation(
const QgsAbstractGeometry *geom, Relation r, QString *errorMsg )
const
1783 if ( !mGeos || !geom )
1794 bool result =
false;
1797 if ( mGeosPrepared )
1801 case RelationIntersects:
1802 result = ( GEOSPreparedIntersects_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1804 case RelationTouches:
1805 result = ( GEOSPreparedTouches_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1807 case RelationCrosses:
1808 result = ( GEOSPreparedCrosses_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1810 case RelationWithin:
1811 result = ( GEOSPreparedWithin_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1813 case RelationContains:
1814 result = ( GEOSPreparedContains_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1816 case RelationDisjoint:
1817 result = ( GEOSPreparedDisjoint_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1819 case RelationOverlaps:
1820 result = ( GEOSPreparedOverlaps_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1828 case RelationIntersects:
1829 result = ( GEOSIntersects_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1831 case RelationTouches:
1832 result = ( GEOSTouches_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1834 case RelationCrosses:
1835 result = ( GEOSCrosses_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1837 case RelationWithin:
1838 result = ( GEOSWithin_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1840 case RelationContains:
1841 result = ( GEOSContains_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1843 case RelationDisjoint:
1844 result = ( GEOSDisjoint_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1846 case RelationOverlaps:
1847 result = ( GEOSOverlaps_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1851 catch ( GEOSException &e )
1853 logError( QStringLiteral(
"GEOS" ), e.what() );
1856 *errorMsg = e.what();
1874 geos.reset( GEOSBuffer_r( geosinit()->ctxt, mGeos.get(),
distance, segments ) );
1890 geos.reset( GEOSBufferWithStyle_r( geosinit()->ctxt, mGeos.get(),
distance, segments,
static_cast< int >( endCapStyle ),
static_cast< int >( joinStyle ), miterLimit ) );
1905 geos.reset( GEOSTopologyPreserveSimplify_r( geosinit()->ctxt, mGeos.get(), tolerance ) );
1920 geos.reset( GEOSInterpolate_r( geosinit()->ctxt, mGeos.get(),
distance ) );
1939 geos.reset( GEOSGetCentroid_r( geosinit()->ctxt, mGeos.get() ) );
1944 GEOSGeomGetX_r( geosinit()->ctxt,
geos.get(), &x );
1945 GEOSGeomGetY_r( geosinit()->ctxt,
geos.get(), &y );
1961 geos.reset( GEOSEnvelope_r( geosinit()->ctxt, mGeos.get() ) );
1980 geos.reset( GEOSPointOnSurface_r( geosinit()->ctxt, mGeos.get() ) );
1982 if ( !
geos || GEOSisEmpty_r( geosinit()->ctxt,
geos.get() ) != 0 )
1987 GEOSGeomGetX_r( geosinit()->ctxt,
geos.get(), &x );
1988 GEOSGeomGetY_r( geosinit()->ctxt,
geos.get(), &y );
2004 geos::unique_ptr cHull( GEOSConvexHull_r( geosinit()->ctxt, mGeos.get() ) );
2005 std::unique_ptr< QgsAbstractGeometry > cHullGeom =
fromGeos( cHull.get() );
2006 return cHullGeom.release();
2013#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
2015 ( void )targetPercent;
2017 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHull requires a QGIS build based on GEOS 3.11 or later" ) );
2028 return concaveHullGeom.release();
2036#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<12
2038 ( void )invalidEdges;
2040 throw QgsNotSupportedException( QObject::tr(
"Validating coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2045 *errorMsg = QStringLiteral(
"Input geometry was not set" );
2052 const int result = GEOSCoverageIsValid_r( geosinit()->ctxt, mGeos.get(), gapWidth, invalidEdges ? &invalidEdgesGeos :
nullptr );
2053 if ( invalidEdges && invalidEdgesGeos )
2055 *invalidEdges =
fromGeos( invalidEdgesGeos );
2057 if ( invalidEdgesGeos )
2059 GEOSGeom_destroy_r( geosinit()->ctxt, invalidEdgesGeos );
2060 invalidEdgesGeos =
nullptr;
2080#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<12
2082 ( void )preserveBoundary;
2084 throw QgsNotSupportedException( QObject::tr(
"Simplifying coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2089 *errorMsg = QStringLiteral(
"Input geometry was not set" );
2095 geos::unique_ptr simplified( GEOSCoverageSimplifyVW_r( geosinit()->ctxt, mGeos.get(), tolerance, preserveBoundary ? 1 : 0 ) );
2096 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom =
fromGeos( simplified.get() );
2097 return simplifiedGeom;
2108 *errorMsg = QStringLiteral(
"Input geometry was not set" );
2114 geos::unique_ptr unioned( GEOSCoverageUnion_r( geosinit()->ctxt, mGeos.get() ) );
2115 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( unioned.get() );
2126 *errorMsg = QObject::tr(
"QGIS geometry cannot be converted to a GEOS geometry",
"GEOS Error" );
2134 char res = GEOSisValidDetail_r( geosinit()->ctxt, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
2135 const bool invalid = res != 1;
2140 error = QString( r );
2141 GEOSFree_r( geosinit()->ctxt, r );
2144 if ( invalid && errorMsg )
2148 if ( translatedErrors.empty() )
2151 translatedErrors.insert( QStringLiteral(
"topology validation error" ), QObject::tr(
"Topology validation error",
"GEOS Error" ) );
2152 translatedErrors.insert( QStringLiteral(
"repeated point" ), QObject::tr(
"Repeated point",
"GEOS Error" ) );
2153 translatedErrors.insert( QStringLiteral(
"hole lies outside shell" ), QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) );
2154 translatedErrors.insert( QStringLiteral(
"holes are nested" ), QObject::tr(
"Holes are nested",
"GEOS Error" ) );
2155 translatedErrors.insert( QStringLiteral(
"interior is disconnected" ), QObject::tr(
"Interior is disconnected",
"GEOS Error" ) );
2156 translatedErrors.insert( QStringLiteral(
"self-intersection" ), QObject::tr(
"Self-intersection",
"GEOS Error" ) );
2157 translatedErrors.insert( QStringLiteral(
"ring self-intersection" ), QObject::tr(
"Ring self-intersection",
"GEOS Error" ) );
2158 translatedErrors.insert( QStringLiteral(
"nested shells" ), QObject::tr(
"Nested shells",
"GEOS Error" ) );
2159 translatedErrors.insert( QStringLiteral(
"duplicate rings" ), QObject::tr(
"Duplicate rings",
"GEOS Error" ) );
2160 translatedErrors.insert( QStringLiteral(
"too few points in geometry component" ), QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) );
2161 translatedErrors.insert( QStringLiteral(
"invalid coordinate" ), QObject::tr(
"Invalid coordinate",
"GEOS Error" ) );
2162 translatedErrors.insert( QStringLiteral(
"ring is not closed" ), QObject::tr(
"Ring is not closed",
"GEOS Error" ) );
2165 *errorMsg = translatedErrors.value( error.toLower(), error );
2167 if ( g1 && errorLoc )
2173 GEOSGeom_destroy_r( geosinit()->ctxt, g1 );
2183 if ( !mGeos || !geom )
2195 bool equal = GEOSEquals_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() );
2210 return GEOSisEmpty_r( geosinit()->ctxt, mGeos.get() );
2224 return GEOSisSimple_r( geosinit()->ctxt, mGeos.get() );
2229GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
2231 GEOSContextHandle_t ctxt = geosinit()->ctxt;
2233 std::unique_ptr< QgsLineString > segmentized;
2234 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( curve );
2239 line = segmentized.get();
2246 GEOSCoordSequence *coordSeq =
nullptr;
2248 const int numPoints = line->
numPoints();
2250 const bool hasZ = line->
is3D();
2252#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
2255 if ( !forceClose || ( line->
pointN( 0 ) == line->
pointN( numPoints - 1 ) ) )
2260 coordSeq = GEOSCoordSeq_copyFromArrays_r( ctxt, line->
xData(), line->
yData(), line->
zData(),
nullptr, numPoints );
2263 QgsDebugError( QStringLiteral(
"GEOS Exception: Could not create coordinate sequence for %1 points" ).arg( numPoints ) );
2271 QVector< double > x = line->
xVector();
2272 if ( numPoints > 0 )
2273 x.append( x.at( 0 ) );
2274 QVector< double > y = line->
yVector();
2275 if ( numPoints > 0 )
2276 y.append( y.at( 0 ) );
2277 QVector< double > z = line->
zVector();
2278 if ( hasZ && numPoints > 0 )
2279 z.append( z.at( 0 ) );
2282 coordSeq = GEOSCoordSeq_copyFromArrays_r( ctxt, x.constData(), y.constData(), !hasZ ?
nullptr : z.constData(),
nullptr, numPoints + 1 );
2285 QgsDebugError( QStringLiteral(
"GEOS Exception: Could not create closed coordinate sequence for %1 points" ).arg( numPoints + 1 ) );
2296 const bool hasM =
false;
2307 int numOutPoints = numPoints;
2308 if ( forceClose && ( line->
pointN( 0 ) != line->
pointN( numPoints - 1 ) ) )
2315 coordSeq = GEOSCoordSeq_create_r( ctxt, numOutPoints, coordDims );
2318 QgsDebugError( QStringLiteral(
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions" ).arg( numPoints ).arg( coordDims ) );
2322 const double *xData = line->
xData();
2323 const double *yData = line->
yData();
2324 const double *zData = hasZ ? line->
zData() :
nullptr;
2325 const double *mData = hasM ? line->
mData() :
nullptr;
2329 for (
int i = 0; i < numOutPoints; ++i )
2331 if ( i >= numPoints )
2334 xData = line->
xData();
2335 yData = line->
yData();
2336 zData = hasZ ? line->
zData() :
nullptr;
2337 mData = hasM ? line->
mData() :
nullptr;
2349 GEOSCoordSeq_setOrdinate_r( ctxt, coordSeq, i, 3, *mData++ );
2355 for (
int i = 0; i < numOutPoints; ++i )
2357 if ( i >= numPoints )
2360 xData = line->
xData();
2361 yData = line->
yData();
2362 zData = hasZ ? line->
zData() :
nullptr;
2363 mData = hasM ? line->
mData() :
nullptr;
2367 GEOSCoordSeq_setXYZ_r( ctxt, coordSeq, i, *xData++, *yData++, *zData++ );
2371 GEOSCoordSeq_setXY_r( ctxt, coordSeq, i, *xData++, *yData++ );
2375 GEOSCoordSeq_setOrdinate_r( ctxt, coordSeq, i, 3, *mData++ );
2387 const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( point );
2394geos::unique_ptr QgsGeos::createGeosPointXY(
double x,
double y,
bool hasZ,
double z,
bool hasM,
double m,
int coordDims,
double precision )
2402 if ( coordDims == 2 )
2408 geosPoint.reset( GEOSGeom_createPointFromXY_r( geosinit()->ctxt, x, y ) );
2412 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( geosinit()->ctxt, 1, coordDims );
2415 QgsDebugError( QStringLiteral(
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions" ).arg( coordDims ) );
2420 GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, 0, std::round( x /
precision ) *
precision );
2421 GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, 0, std::round( y /
precision ) *
precision );
2424 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 2, std::round( z /
precision ) *
precision );
2429 GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, 0, x );
2430 GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, 0, y );
2433 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 2, z );
2439 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 3, m );
2442 geosPoint.reset( GEOSGeom_createPoint_r( geosinit()->ctxt, coordSeq ) );
2450 const QgsCurve *
c = qgsgeometry_cast<const QgsCurve *>( curve );
2454 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c,
precision );
2461 geosGeom.reset( GEOSGeom_createLineString_r( geosinit()->ctxt, coordSeq ) );
2469 const QgsCurvePolygon *polygon = qgsgeometry_cast<const QgsCurvePolygon *>( poly );
2474 if ( !exteriorRing )
2482 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( geosinit()->ctxt, createCoordinateSequence( exteriorRing,
precision,
true ) ) );
2491 for (
int i = 0; i < nHoles; ++i )
2494 holes[i] = GEOSGeom_createLinearRing_r( geosinit()->ctxt, createCoordinateSequence( interiorRing,
precision,
true ) );
2496 geosPolygon.reset( GEOSGeom_createPolygon_r( geosinit()->ctxt, exteriorRingGeos.release(), holes, nHoles ) );
2516 offset.reset( GEOSOffsetCurve_r( geosinit()->ctxt, mGeos.get(),
distance, segments,
static_cast< int >( joinStyle ), miterLimit ) );
2519 std::unique_ptr< QgsAbstractGeometry > offsetGeom =
fromGeos( offset.get() );
2520 return offsetGeom.release();
2534 GEOSBufferParams_setSingleSided_r( geosinit()->ctxt, bp.get(), 1 );
2535 GEOSBufferParams_setQuadrantSegments_r( geosinit()->ctxt, bp.get(), segments );
2536 GEOSBufferParams_setJoinStyle_r( geosinit()->ctxt, bp.get(),
static_cast< int >( joinStyle ) );
2537 GEOSBufferParams_setMitreLimit_r( geosinit()->ctxt, bp.get(), miterLimit );
2543 geos.reset( GEOSBufferWithParams_r( geosinit()->ctxt, mGeos.get(), bp.get(),
distance ) );
2559 geos.reset( GEOSMaximumInscribedCircle_r( geosinit()->ctxt, mGeos.get(), tolerance ) );
2577 boundaryGeos =
asGeos( boundary );
2579 geos.reset( GEOSLargestEmptyCircle_r( geosinit()->ctxt, mGeos.get(), boundaryGeos.get(), tolerance ) );
2595 geos.reset( GEOSMinimumWidth_r( geosinit()->ctxt, mGeos.get() ) );
2605 return std::numeric_limits< double >::quiet_NaN();
2612 if ( GEOSMinimumClearance_r( geosinit()->ctxt, mGeos.get(), &res ) != 0 )
2613 return std::numeric_limits< double >::quiet_NaN();
2629 geos.reset( GEOSMinimumClearanceLine_r( geosinit()->ctxt, mGeos.get() ) );
2645 geos.reset( GEOSNode_r( geosinit()->ctxt, mGeos.get() ) );
2653 if ( !mGeos || !other )
2665 geos.reset( GEOSSharedPaths_r( geosinit()->ctxt, mGeos.get(), otherGeos.get() ) );
2685 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
2688 int numGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, mGeos.get() );
2689 if ( numGeoms == -1 )
2698 bool isMultiGeom =
false;
2699 int geosTypeId = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
2700 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
2710 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2714 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2719 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
2720 return reshapeResult;
2727 bool reshapeTookPlace =
false;
2732 for (
int i = 0; i < numGeoms; ++i )
2735 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2737 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2739 if ( currentReshapeGeometry )
2741 newGeoms[i] = currentReshapeGeometry.release();
2742 reshapeTookPlace =
true;
2746 newGeoms[i] = GEOSGeom_clone_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ) );
2753 newMultiGeom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
2757 newMultiGeom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
2761 if ( !newMultiGeom )
2767 if ( reshapeTookPlace )
2771 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
2772 return reshapedMultiGeom;
2794 if ( GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() ) != GEOS_MULTILINESTRING )
2800 geos.reset( GEOSLineMerge_r( geosinit()->ctxt, mGeos.get() ) );
2824 if ( mGeosPrepared )
2826 nearestCoord.reset( GEOSPreparedNearestPoints_r( geosinit()->ctxt, mGeosPrepared.get(), otherGeom.get() ) );
2830 nearestCoord.reset( GEOSNearestPoints_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() ) );
2833 ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 0, &nx );
2834 ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 0, &ny );
2836 catch ( GEOSException &e )
2838 logError( QStringLiteral(
"GEOS" ), e.what() );
2841 *errorMsg = e.what();
2851 if ( !mGeos || other.
isEmpty() )
2861 if ( !other || other->
isEmpty() )
2878 if ( !nearestCoord )
2881 *errorMsg = QStringLiteral(
"GEOS returned no nearest points" );
2885 ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 0, &nx1 );
2886 ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 0, &ny1 );
2887 ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 1, &nx2 );
2888 ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 1, &ny2 );
2890 catch ( GEOSException &e )
2892 logError( QStringLiteral(
"GEOS" ), e.what() );
2895 *errorMsg = e.what();
2922 distance = GEOSProject_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() );
2924 catch ( GEOSException &e )
2926 logError( QStringLiteral(
"GEOS" ), e.what() );
2929 *errorMsg = e.what();
2944 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
2951 distance = GEOSProject_r( geosinit()->ctxt, mGeos.get(), point.get() );
2953 catch ( GEOSException &e )
2955 logError( QStringLiteral(
"GEOS" ), e.what() );
2958 *errorMsg = e.what();
2975 lineGeosGeometries[validLines] = l.release();
2982 geos::unique_ptr result( GEOSPolygonize_r( geosinit()->ctxt, lineGeosGeometries, validLines ) );
2983 for (
int i = 0; i < validLines; ++i )
2985 GEOSGeom_destroy_r( geosinit()->ctxt, lineGeosGeometries[i] );
2987 delete[] lineGeosGeometries;
2990 catch ( GEOSException &e )
2994 *errorMsg = e.what();
2996 for (
int i = 0; i < validLines; ++i )
2998 GEOSGeom_destroy_r( geosinit()->ctxt, lineGeosGeometries[i] );
3000 delete[] lineGeosGeometries;
3015 extentGeosGeom =
asGeos( extent, mPrecision );
3016 if ( !extentGeosGeom )
3025 geos.reset( GEOSVoronoiDiagram_r( geosinit()->ctxt, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
3027 if ( !
geos || GEOSisEmpty_r( geosinit()->ctxt,
geos.get() ) != 0 )
3047 geos.reset( GEOSDelaunayTriangulation_r( geosinit()->ctxt, mGeos.get(), tolerance, edgesOnly ) );
3049 if ( !
geos || GEOSisEmpty_r( geosinit()->ctxt,
geos.get() ) != 0 )
3061#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
3063 throw QgsNotSupportedException( QObject::tr(
"Calculating constrainedDelaunayTriangulation requires a QGIS build based on GEOS 3.11 or later" ) );
3073 geos.reset( GEOSConstrainedDelaunayTriangulation_r( geosinit()->ctxt, mGeos.get() ) );
3075 if ( !
geos || GEOSisEmpty_r( geosinit()->ctxt,
geos.get() ) != 0 )
3080 std::unique_ptr< QgsAbstractGeometry > res =
fromGeos(
geos.get() );
3081 if (
const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( res.get() ) )
3083 return std::unique_ptr< QgsAbstractGeometry >( collection->extractPartsByType(
Qgis::WkbType::Polygon,
true ) );
3095static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
3097 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, linestring );
3101 unsigned int coordSeqSize;
3102 if ( GEOSCoordSeq_getSize_r( geosinit()->ctxt, coordSeq, &coordSeqSize ) == 0 )
3105 if ( coordSeqSize < 2 )
3108 GEOSCoordSeq_getX_r( geosinit()->ctxt, coordSeq, 0, &x1 );
3109 GEOSCoordSeq_getY_r( geosinit()->ctxt, coordSeq, 0, &y1 );
3110 GEOSCoordSeq_getX_r( geosinit()->ctxt, coordSeq, coordSeqSize - 1, &x2 );
3111 GEOSCoordSeq_getY_r( geosinit()->ctxt, coordSeq, coordSeqSize - 1, &y2 );
3119 double x1, y1, x2, y2;
3120 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
3123 double rx1, ry1, rx2, ry2;
3124 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
3127 bool intersectionAtOrigLineEndpoint =
3128 ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) !=
3129 ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
3130 bool intersectionAtReshapeLineEndpoint =
3131 ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) ||
3132 ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
3135 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
3139 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
3140 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
3141 geos::unique_ptr res( GEOSLineMerge_r( geosinit()->ctxt, multiGeom.get() ) );
3151 if ( !line || !reshapeLineGeos )
3154 bool atLeastTwoIntersections =
false;
3155 bool oneIntersection =
false;
3161 geos::unique_ptr intersectGeom( GEOSIntersection_r( geosinit()->ctxt, line, reshapeLineGeos ) );
3162 if ( intersectGeom )
3164 const int geomType = GEOSGeomTypeId_r( geosinit()->ctxt, intersectGeom.get() );
3165 atLeastTwoIntersections = ( geomType == GEOS_MULTIPOINT && GEOSGetNumGeometries_r( geosinit()->ctxt, intersectGeom.get() ) > 1 )
3166 || ( geomType == GEOS_GEOMETRYCOLLECTION && GEOSGetNumGeometries_r( geosinit()->ctxt, intersectGeom.get() ) > 0 )
3167 || ( geomType == GEOS_MULTILINESTRING && GEOSGetNumGeometries_r( geosinit()->ctxt, intersectGeom.get() ) > 0 );
3169 if ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectGeom.get() ) == GEOS_POINT )
3171 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, intersectGeom.get() );
3173 GEOSCoordSeq_getX_r( geosinit()->ctxt, intersectionCoordSeq, 0, &xi );
3174 GEOSCoordSeq_getY_r( geosinit()->ctxt, intersectionCoordSeq, 0, &yi );
3175 oneIntersection =
true;
3180 catch ( GEOSException & )
3182 atLeastTwoIntersections =
false;
3186 if ( oneIntersection )
3187 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
3189 if ( !atLeastTwoIntersections )
3193 double x1, y1, x2, y2;
3194 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
3200 bool isRing =
false;
3201 if ( GEOSGeomTypeId_r( geosinit()->ctxt, line ) == GEOS_LINEARRING
3202 || GEOSEquals_r( geosinit()->ctxt, beginLineVertex.get(), endLineVertex.get() ) == 1 )
3207 if ( !nodedGeometry )
3213 geos::unique_ptr mergedLines( GEOSLineMerge_r( geosinit()->ctxt, nodedGeometry.get() ) );
3219 int numMergedLines = GEOSGetNumGeometries_r( geosinit()->ctxt, mergedLines.get() );
3220 if ( numMergedLines < 2 )
3222 if ( numMergedLines == 1 )
3224 geos::unique_ptr result( GEOSGeom_clone_r( geosinit()->ctxt, reshapeLineGeos ) );
3231 QVector<GEOSGeometry *> resultLineParts;
3232 QVector<GEOSGeometry *> probableParts;
3234 for (
int i = 0; i < numMergedLines; ++i )
3236 const GEOSGeometry *currentGeom = GEOSGetGeometryN_r( geosinit()->ctxt, mergedLines.get(), i );
3239 bool alreadyAdded =
false;
3241 double bufferDistance = std::pow( 10.0L, geomDigits( currentGeom ) - 11 );
3242 for (
const GEOSGeometry *other : std::as_const( resultLineParts ) )
3244 GEOSHausdorffDistance_r( geosinit()->ctxt, currentGeom, other, &
distance );
3247 alreadyAdded =
true;
3254 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, currentGeom );
3255 unsigned int currentCoordSeqSize;
3256 GEOSCoordSeq_getSize_r( geosinit()->ctxt, currentCoordSeq, ¤tCoordSeqSize );
3257 if ( currentCoordSeqSize < 2 )
3261 double xBegin, xEnd, yBegin, yEnd;
3262 GEOSCoordSeq_getX_r( geosinit()->ctxt, currentCoordSeq, 0, &xBegin );
3263 GEOSCoordSeq_getY_r( geosinit()->ctxt, currentCoordSeq, 0, &yBegin );
3264 GEOSCoordSeq_getX_r( geosinit()->ctxt, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
3265 GEOSCoordSeq_getY_r( geosinit()->ctxt, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
3270 int nEndpointsOnOriginalLine = 0;
3271 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
3272 nEndpointsOnOriginalLine += 1;
3274 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
3275 nEndpointsOnOriginalLine += 1;
3278 int nEndpointsSameAsOriginalLine = 0;
3279 if ( GEOSEquals_r( geosinit()->ctxt, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
3280 || GEOSEquals_r( geosinit()->ctxt, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3281 nEndpointsSameAsOriginalLine += 1;
3283 if ( GEOSEquals_r( geosinit()->ctxt, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
3284 || GEOSEquals_r( geosinit()->ctxt, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3285 nEndpointsSameAsOriginalLine += 1;
3288 bool currentGeomOverlapsOriginalGeom =
false;
3289 bool currentGeomOverlapsReshapeLine =
false;
3290 if ( lineContainedInLine( currentGeom, line ) == 1 )
3291 currentGeomOverlapsOriginalGeom =
true;
3293 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
3294 currentGeomOverlapsReshapeLine =
true;
3297 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3299 resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
3302 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3304 probableParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
3306 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3308 resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
3310 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3312 resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
3314 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
3316 resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
3321 if ( isRing && !probableParts.isEmpty() )
3325 double maxLength = -std::numeric_limits<double>::max();
3326 double currentLength = 0;
3327 for (
int i = 0; i < probableParts.size(); ++i )
3329 currentGeom = probableParts.at( i );
3330 GEOSLength_r( geosinit()->ctxt, currentGeom, ¤tLength );
3331 if ( currentLength > maxLength )
3333 maxLength = currentLength;
3334 maxGeom.reset( currentGeom );
3338 GEOSGeom_destroy_r( geosinit()->ctxt, currentGeom );
3341 resultLineParts.push_back( maxGeom.release() );
3345 if ( resultLineParts.empty() )
3348 if ( resultLineParts.size() == 1 )
3350 result.reset( resultLineParts[0] );
3355 for (
int i = 0; i < resultLineParts.size(); ++i )
3357 lineArray[i] = resultLineParts[i];
3361 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
3362 delete [] lineArray;
3365 result.reset( GEOSLineMerge_r( geosinit()->ctxt, multiLineGeom.get() ) );
3369 if ( GEOSGeomTypeId_r( geosinit()->ctxt, result.get() ) != GEOS_LINESTRING )
3380 int nIntersections = 0;
3381 int lastIntersectingRing = -2;
3384 int nRings = GEOSGetNumInteriorRings_r( geosinit()->ctxt, polygon );
3389 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( geosinit()->ctxt, polygon );
3390 if ( GEOSIntersects_r( geosinit()->ctxt, outerRing, reshapeLineGeos ) == 1 )
3393 lastIntersectingRing = -1;
3394 lastIntersectingGeom = outerRing;
3402 for (
int i = 0; i < nRings; ++i )
3404 innerRings[i] = GEOSGetInteriorRingN_r( geosinit()->ctxt, polygon, i );
3405 if ( GEOSIntersects_r( geosinit()->ctxt, innerRings[i], reshapeLineGeos ) == 1 )
3408 lastIntersectingRing = i;
3409 lastIntersectingGeom = innerRings[i];
3413 catch ( GEOSException & )
3418 if ( nIntersections != 1 )
3420 delete [] innerRings;
3426 if ( !reshapeResult )
3428 delete [] innerRings;
3434 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, reshapeResult.get() );
3435 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( geosinit()->ctxt, reshapeSequence );
3437 reshapeResult.reset();
3439 newRing = GEOSGeom_createLinearRing_r( geosinit()->ctxt, newCoordSequence );
3442 delete [] innerRings;
3447 if ( lastIntersectingRing == -1 )
3448 newOuterRing = newRing;
3450 newOuterRing = GEOSGeom_clone_r( geosinit()->ctxt, outerRing );
3453 QVector<GEOSGeometry *> ringList;
3456 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( geosinit()->ctxt, GEOSGeom_clone_r( geosinit()->ctxt, newOuterRing ),
nullptr, 0 );
3457 if ( outerRingPoly )
3459 ringList.reserve( nRings );
3461 for (
int i = 0; i < nRings; ++i )
3463 if ( lastIntersectingRing == i )
3464 currentRing = newRing;
3466 currentRing = GEOSGeom_clone_r( geosinit()->ctxt, innerRings[i] );
3469 if ( GEOSContains_r( geosinit()->ctxt, outerRingPoly, currentRing ) == 1 )
3470 ringList.push_back( currentRing );
3472 GEOSGeom_destroy_r( geosinit()->ctxt, currentRing );
3475 GEOSGeom_destroy_r( geosinit()->ctxt, outerRingPoly );
3479 for (
int i = 0; i < ringList.size(); ++i )
3480 newInnerRings[i] = ringList.at( i );
3482 delete [] innerRings;
3484 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( geosinit()->ctxt, newOuterRing, newInnerRings, ringList.size() ) );
3485 delete[] newInnerRings;
3487 return reshapedPolygon;
3492 if ( !line1 || !line2 )
3497 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
3503 geos::unique_ptr intersectionGeom( GEOSIntersection_r( geosinit()->ctxt, bufferGeom.get(), line1 ) );
3506 double intersectGeomLength;
3509 GEOSLength_r( geosinit()->ctxt, intersectionGeom.get(), &intersectGeomLength );
3510 GEOSLength_r( geosinit()->ctxt, line1, &line1Length );
3512 double intersectRatio = line1Length / intersectGeomLength;
3513 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
3521 if ( !point || !line )
3524 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
3526 geos::unique_ptr lineBuffer( GEOSBuffer_r( geosinit()->ctxt, line, bufferDistance, 8 ) );
3530 bool contained =
false;
3531 if ( GEOSContains_r( geosinit()->ctxt, lineBuffer.get(), point ) == 1 )
3543 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( geosinit()->ctxt, bbox.get() );
3547 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, bBoxRing );
3549 if ( !bBoxCoordSeq )
3552 unsigned int nCoords = 0;
3553 if ( !GEOSCoordSeq_getSize_r( geosinit()->ctxt, bBoxCoordSeq, &nCoords ) )
3557 for (
unsigned int i = 0; i < nCoords - 1; ++i )
3560 GEOSCoordSeq_getX_r( geosinit()->ctxt, bBoxCoordSeq, i, &t );
3563 digits = std::ceil( std::log10( std::fabs( t ) ) );
3564 if ( digits > maxDigits )
3567 GEOSCoordSeq_getY_r( geosinit()->ctxt, bBoxCoordSeq, i, &t );
3568 digits = std::ceil( std::log10( std::fabs( t ) ) );
3569 if ( digits > maxDigits )
3578 return geosinit()->ctxt;
The Qgis class provides global constants 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.
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.
@ GeometryCollection
GeometryCollection.
Abstract base class for all geometries.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual bool isEmpty() const
Returns true if the geometry is empty.
virtual int dimension() const =0
Returns the inherent dimension of the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
Curve polygon geometry type.
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.
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.
A geometry engine is a low-level representation of a QgsAbstractGeometry object, optimised for use wi...
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.
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.
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...
Does vector analysis using the geos library and handles import, export, exception handling*.
QgsGeometry delaunayTriangulation(double tolerance=0.0, bool edgesOnly=false, QString *errorMsg=nullptr) const
Returns the Delaunay triangulation for the vertices of the geometry.
bool touches(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom touches this.
QgsAbstractGeometry * symDifference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const override
Calculate the symmetric difference of this and geom.
double hausdorffDistanceDensify(const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
bool isValid(QString *errorMsg=nullptr, bool allowSelfTouchingHoles=false, QgsGeometry *errorLoc=nullptr) const override
Returns true if the geometry is valid.
std::unique_ptr< QgsAbstractGeometry > subdivide(int maxNodes, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const
Subdivides the geometry.
QgsAbstractGeometry * intersection(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const override
Calculate the intersection of this and geom.
double hausdorffDistance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
std::unique_ptr< QgsAbstractGeometry > constrainedDelaunayTriangulation(QString *errorMsg=nullptr) const
Returns a constrained Delaunay triangulation for the vertices of the geometry.
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0)
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
QgsAbstractGeometry * concaveHull(double targetPercent, bool allowHoles=false, QString *errorMsg=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.
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
QgsAbstractGeometry * difference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const override
Calculate the 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 > sharedPaths(const QgsAbstractGeometry *other, QString *errorMsg=nullptr) const
Find paths shared between the two given lineal geometries (this and other).
std::unique_ptr< QgsAbstractGeometry > clip(const QgsRectangle &rectangle, QString *errorMsg=nullptr) const
Performs a fast, non-robust intersection between the geometry and a rectangle.
std::unique_ptr< QgsAbstractGeometry > node(QString *errorMsg=nullptr) const
Returns a (Multi)LineString representing the fully noded version of a collection of linestrings.
double minimumClearance(QString *errorMsg=nullptr) const
Computes the minimum clearance of a geometry.
std::unique_ptr< QgsAbstractGeometry > singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit, QString *errorMsg=nullptr) const
Returns a single sided buffer for a geometry.
bool disjoint(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom is disjoint from this.
std::unique_ptr< QgsAbstractGeometry > makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false, QString *errorMsg=nullptr) const
Repairs the geometry using GEOS make valid routine.
QgsGeometry shortestLine(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the shortest line joining this geometry to the other geometry.
QgsAbstractGeometry * simplify(double tolerance, QString *errorMsg=nullptr) const override
std::unique_ptr< QgsAbstractGeometry > unionCoverage(QString *errorMsg=nullptr) const
Optimized union algorithm for polygonal inputs that are correctly noded and do not overlap.
QgsGeometry closestPoint(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the closest point on the geometry to the other geometry.
static std::unique_ptr< QgsPolygon > fromGeosPolygon(const GEOSGeometry *geos)
std::unique_ptr< QgsAbstractGeometry > minimumClearanceLine(QString *errorMsg=nullptr) const
Returns a LineString whose endpoints define the minimum clearance of a geometry.
QgsAbstractGeometry * envelope(QString *errorMsg=nullptr) const override
QString relate(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Returns the Dimensional Extended 9 Intersection Model (DE-9IM) representation of the relationship bet...
bool crosses(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom crosses this.
QgsAbstractGeometry * convexHull(QString *errorMsg=nullptr) const override
Calculate the convex hull of this.
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
std::unique_ptr< QgsAbstractGeometry > minimumWidth(QString *errorMsg=nullptr) const
Returns a linestring geometry which represents the minimum diameter of the geometry.
bool isSimple(QString *errorMsg=nullptr) const override
Determines whether the geometry is simple (according to OGC definition).
QgsAbstractGeometry * combine(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const override
Calculate the combination of this and geom.
bool within(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom is within this.
void prepareGeometry() override
Prepares the geometry, so that subsequent calls to spatial relation methods are much faster.
static std::unique_ptr< QgsAbstractGeometry > fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
QgsAbstractGeometry * interpolate(double distance, QString *errorMsg=nullptr) const override
bool distanceWithin(const QgsAbstractGeometry *geom, double maxdistance, QString *errorMsg=nullptr) const override
Checks if geom is within maxdistance distance from this geometry.
bool isEqual(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if this is equal to geom.
bool contains(double x, double y, QString *errorMsg=nullptr) const
Returns true if the geometry contains the point at (x, y).
QgsGeos(const QgsAbstractGeometry *geometry, double precision=0, bool allowInvalidSubGeom=true)
GEOS geometry engine constructor.
QgsGeometry mergeLines(QString *errorMsg=nullptr) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometry polygonize(const QVector< const QgsAbstractGeometry * > &geometries, QString *errorMsg=nullptr)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
bool relatePattern(const QgsAbstractGeometry *geom, const QString &pattern, QString *errorMsg=nullptr) const override
Tests whether two geometries are related by a specified Dimensional Extended 9 Intersection Model (DE...
QgsAbstractGeometry * offsetCurve(double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit, QString *errorMsg=nullptr) const override
Offsets a curve.
static GEOSContextHandle_t getGEOSHandler()
QgsPoint * centroid(QString *errorMsg=nullptr) const override
Calculates the centroid of this.
double lineLocatePoint(const QgsPoint &point, QString *errorMsg=nullptr) const
Returns a distance representing the location along this linestring of the closest point on this lines...
std::unique_ptr< QgsAbstractGeometry > simplifyCoverageVW(double tolerance, bool preserveBoundary, QString *errorMsg=nullptr) const
Operates on a coverage (represented as a list of polygonal geometry with exactly matching edge geomet...
bool isEmpty(QString *errorMsg=nullptr) const override
static Qgis::GeometryOperationResult addPart(QgsGeometry &geometry, GEOSGeometry *newPart)
Adds a new island polygon to a multipolygon feature.
double frechetDistance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
QgsGeometry voronoiDiagram(const QgsAbstractGeometry *extent=nullptr, double tolerance=0.0, bool edgesOnly=false, QString *errorMsg=nullptr) const
Creates a Voronoi diagram for the nodes contained within the geometry.
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom intersects this.
void geometryChanged() override
Should be called whenever the geometry associated with the engine has been modified and the engine mu...
bool overlaps(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom overlaps this.
double area(QString *errorMsg=nullptr) const override
double length(QString *errorMsg=nullptr) const override
std::unique_ptr< QgsAbstractGeometry > largestEmptyCircle(double tolerance, const QgsAbstractGeometry *boundary=nullptr, QString *errorMsg=nullptr) const
Constructs the Largest Empty Circle for a set of obstacle geometries, up to a specified tolerance.
Qgis::CoverageValidityResult validateCoverage(double gapWidth, std::unique_ptr< QgsAbstractGeometry > *invalidEdges, QString *errorMsg=nullptr) const
Analyze a coverage (represented as a collection of polygonal geometry with exactly matching edge geom...
double frechetDistanceDensify(const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
static QgsPoint coordSeqPoint(const GEOSCoordSequence *cs, int i, bool hasZ, bool hasM)
QgsPoint * pointOnSurface(QString *errorMsg=nullptr) const override
Calculate a point that is guaranteed to be on the surface of this.
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.
Multi curve geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi line string geometry collection.
Multi point geometry collection.
Multi polygon geometry collection.
Custom exception class which is raised when an operation is not supported.
A class to represent a 2D point.
Point geometry type, with support for z-dimension and m-values.
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
A rectangle specified with double values.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
void setYMinimum(double y)
Set the minimum y value.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
void setXMinimum(double x)
Set the minimum x value.
double width() const
Returns the width of the rectangle.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
bool isNull() const
Test if the rectangle is null (holding no spatial information).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
bool isEmpty() const
Returns true if the rectangle has no area.
double height() const
Returns the height of the rectangle.
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 bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB 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)
QMap< QString, QString > QgsStringMap
QVector< QgsPoint > QgsPointSequence
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
#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