36#define DEFAULT_QUADRANT_SEGMENTS 8
38#define CATCH_GEOS(r) \
39 catch (QgsGeosException &) \
44#define CATCH_GEOS_WITH_ERRMSG(r) \
45 catch (QgsGeosException &e) \
49 *errorMsg = e.what(); \
56static void throwQgsGeosException(
const char *fmt, ... )
62 vsnprintf( buffer,
sizeof buffer, fmt, ap );
65 QString message = QString::fromUtf8( buffer );
75 throw QgsGeosException( message );
83 throw QgsGeosException( message );
88static void printGEOSNotice(
const char *fmt, ... )
95 vsnprintf( buffer,
sizeof buffer, fmt, ap );
106#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
109QThreadStorage< QgsGeosContext * > QgsGeosContext::sGeosContext;
115#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=5 )
116 mContext = GEOS_init_r();
117 GEOSContext_setNoticeHandler_r( mContext, printGEOSNotice );
118 GEOSContext_setErrorHandler_r( mContext, throwQgsGeosException );
120 mContext = initGEOS_r( printGEOSNotice, throwQgsGeosException );
126#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=5 )
127 GEOS_finish_r( mContext );
129 finishGEOS_r( mContext );
135#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
136 return sGeosContext.mContext;
138 GEOSContextHandle_t gContext =
nullptr;
139 if ( sGeosContext.hasLocalData() )
141 gContext = sGeosContext.localData()->mContext;
146 gContext = sGeosContext.localData()->mContext;
183 , mPrecision( precision )
210#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
212 throw QgsNotSupportedException( QObject::tr(
"The structured method to make geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
215 throw QgsNotSupportedException( QObject::tr(
"The keep collapsed option for making geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
219 geos.reset( GEOSMakeValid_r( context, mGeos.get() ) );
224 GEOSMakeValidParams *params = GEOSMakeValidParams_create_r( context );
228 GEOSMakeValidParams_setMethod_r( context, params, GEOS_MAKE_VALID_LINEWORK );
232 GEOSMakeValidParams_setMethod_r( context, params, GEOS_MAKE_VALID_STRUCTURE );
236 GEOSMakeValidParams_setKeepCollapsed_r( context,
238 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 ).release();
327 return overlay( geom, OverlayDifference, errorMsg, parameters ).release();
342 catch ( QgsGeosException &e )
344 logError( QStringLiteral(
"GEOS" ), e.what() );
347 *errorMsg = e.what();
356 int partType = GEOSGeomTypeId_r( context, currentPart );
359 if ( partType == GEOS_POINT )
370 if ( partType == GEOS_MULTILINESTRING || partType == GEOS_MULTIPOLYGON || partType == GEOS_GEOMETRYCOLLECTION )
372 int partCount = GEOSGetNumGeometries_r( context, currentPart );
373 for (
int i = 0; i < partCount; ++i )
375 subdivideRecursive( GEOSGetGeometryN_r( context, currentPart, i ), maxNodes, depth, parts, clipRect, gridSize );
386 int vertexCount = GEOSGetNumCoordinates_r( context, currentPart );
387 if ( vertexCount == 0 )
391 else if ( vertexCount < maxNodes )
398 double width = clipRect.
width();
399 double height = clipRect.
height();
400 QgsRectangle halfClipRect1 = clipRect;
401 QgsRectangle halfClipRect2 = clipRect;
402 if ( width > height )
415 halfClipRect1.
setYMinimum( halfClipRect1.
yMinimum() - std::numeric_limits<double>::epsilon() );
416 halfClipRect2.
setYMinimum( halfClipRect2.
yMinimum() - std::numeric_limits<double>::epsilon() );
417 halfClipRect1.
setYMaximum( halfClipRect1.
yMaximum() + std::numeric_limits<double>::epsilon() );
418 halfClipRect2.
setYMaximum( halfClipRect2.
yMaximum() + std::numeric_limits<double>::epsilon() );
422 halfClipRect1.
setXMinimum( halfClipRect1.
xMinimum() - std::numeric_limits<double>::epsilon() );
423 halfClipRect2.
setXMinimum( halfClipRect2.
xMinimum() - std::numeric_limits<double>::epsilon() );
424 halfClipRect1.
setXMaximum( halfClipRect1.
xMaximum() + std::numeric_limits<double>::epsilon() );
425 halfClipRect2.
setXMaximum( halfClipRect2.
xMaximum() + std::numeric_limits<double>::epsilon() );
437 clipPart1.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart1.get(), gridSize ) );
439 subdivideRecursive( clipPart1.get(), maxNodes, depth, parts, halfClipRect1, gridSize );
445 clipPart2.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart2.get(), gridSize ) );
447 subdivideRecursive( clipPart2.get(), maxNodes, depth, parts, halfClipRect2, gridSize );
459 maxNodes = std::max( maxNodes, 8 );
464 subdivideRecursive( mGeos.get(), maxNodes, 0, parts.get(),
mGeometry->boundingBox(), parameters.
gridSize() );
468 return std::move( parts );
473 return overlay( geom, OverlayUnion, errorMsg, parameters ).release();
478 std::vector<geos::unique_ptr> geosGeometries;
479 geosGeometries.reserve( geomList.size() );
485 geosGeometries.emplace_back(
asGeos( g, mPrecision ) );
492 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
495 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
499 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
504 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
505 return result.release();
510 std::vector<geos::unique_ptr> geosGeometries;
511 geosGeometries.reserve( geomList.size() );
517 geosGeometries.emplace_back(
asGeos( g.constGet(), mPrecision ) );
524 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
528 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
532 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
538 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
539 return result.release();
544 return overlay( geom, OverlaySymDifference, errorMsg, parameters ).release();
547static bool isZVerticalLine(
const QgsAbstractGeometry *geom,
double tolerance = 4 * std::numeric_limits<double>::epsilon() )
557 bool isVertical =
true;
560 const int nrPoints = line->numPoints();
568 const double sqrTolerance = tolerance * tolerance;
569 const double *lineX = line->xData();
570 const double *lineY = line->yData();
571 for (
int iVert = nrPoints - 1, jVert = 0; jVert < nrPoints; iVert = jVert++ )
601 otherGeosGeom =
asGeos( &firstPoint, mPrecision );
605 otherGeosGeom =
asGeos( geom, mPrecision );
608 if ( !otherGeosGeom )
616 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
618 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
622 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
638 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
647 GEOSPreparedDistance_r( context, mGeosPrepared.get(), point.get(), &
distance );
651 GEOSDistance_r( context, mGeos.get(), point.get(), &
distance );
675 otherGeosGeom =
asGeos( &firstPoint );
679 otherGeosGeom =
asGeos( geom, mPrecision );
682 if ( !otherGeosGeom )
695 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
697#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
698 return GEOSPreparedDistanceWithin_r( context, mGeosPrepared.get(), otherGeosGeom.get(), maxdist );
700 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
705#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
706 return GEOSDistanceWithin_r( context, mGeos.get(), otherGeosGeom.get(), maxdist );
708 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
723#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
726 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
732#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
733 return GEOSPreparedContainsXY_r( context, mGeosPrepared.get(), x, y ) == 1;
735 return GEOSPreparedContains_r( context, mGeosPrepared.get(), point.get() ) == 1;
739#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
740 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
745 result = ( GEOSContains_r( context, mGeos.get(), point.get() ) == 1 );
747 catch ( QgsGeosException &e )
749 logError( QStringLiteral(
"GEOS" ), e.what() );
752 *errorMsg = e.what();
769 if ( !otherGeosGeom )
792 if ( !otherGeosGeom )
815 if ( !otherGeosGeom )
838 if ( !otherGeosGeom )
854 if ( !mGeos || !geom )
859#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
867 return GEOSPreparedIntersectsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
869 catch ( QgsGeosException &e )
871 logError( QStringLiteral(
"GEOS" ), e.what() );
874 *errorMsg = e.what();
882 return relation( geom, RelationIntersects, errorMsg );
887 return relation( geom, RelationTouches, errorMsg );
892 return relation( geom, RelationCrosses, errorMsg );
897 return relation( geom, RelationWithin, errorMsg );
902 return relation( geom, RelationOverlaps, errorMsg );
907 if ( !mGeos || !geom )
912#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
920 return GEOSPreparedContainsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
922 catch ( QgsGeosException &e )
924 logError( QStringLiteral(
"GEOS" ), e.what() );
927 *errorMsg = e.what();
935 return relation( geom, RelationContains, errorMsg );
940 return relation( geom, RelationDisjoint, errorMsg );
960 char *r = GEOSRelate_r( context, mGeos.get(), geosGeom.get() );
963 result = QString( r );
964 GEOSFree_r( context, r );
967 catch ( QgsGeosException &e )
969 logError( QStringLiteral(
"GEOS" ), e.what() );
972 *errorMsg = e.what();
981 if ( !mGeos || !geom )
996 result = ( GEOSRelatePattern_r( context, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
998 catch ( QgsGeosException &e )
1000 logError( QStringLiteral(
"GEOS" ), e.what() );
1003 *errorMsg = e.what();
1044 QVector<QgsGeometry> &newGeometries,
1047 QString *errorMsg,
bool skipIntersectionCheck )
const
1063 if ( !GEOSisValid_r( context, mGeos.get() ) )
1071 newGeometries.clear();
1078 splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
1082 splitLineGeos = createGeosPointXY( splitLine.
xAt( 0 ), splitLine.
yAt( 0 ),
false, 0,
false, 0, 2, mPrecision );
1089 if ( !GEOSisValid_r( context, splitLineGeos.get() ) || !GEOSisSimple_r( context, splitLineGeos.get() ) )
1097 if ( !topologicalTestPointsSplit( splitLineGeos.get(), topologyTestPoints ) )
1106 returnCode = splitLinearGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1110 returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1139 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, mGeos.get(), splitLine ) );
1140 if ( !intersectionGeom )
1143 bool simple =
false;
1144 int nIntersectGeoms = 1;
1145 if ( GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_LINESTRING
1146 || GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_POINT )
1150 nIntersectGeoms = GEOSGetNumGeometries_r( context, intersectionGeom.get() );
1152 for (
int i = 0; i < nIntersectGeoms; ++i )
1156 currentIntersectGeom = intersectionGeom.get();
1158 currentIntersectGeom = GEOSGetGeometryN_r( context, intersectionGeom.get(), i );
1160 const GEOSCoordSequence *lineSequence = GEOSGeom_getCoordSeq_r( context, currentIntersectGeom );
1161 unsigned int sequenceSize = 0;
1163 if ( GEOSCoordSeq_getSize_r( context, lineSequence, &sequenceSize ) != 0 )
1165 for (
unsigned int i = 0; i < sequenceSize; ++i )
1167 if ( GEOSCoordSeq_getXYZ_r( context, lineSequence, i, &x, &y, &z ) )
1169 testPoints.push_back( QgsPoint( x, y, z ) );
1183 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1185 std::unique_ptr< QgsMultiCurve > multiCurve;
1186 if ( type == GEOS_MULTILINESTRING )
1190 else if ( type == GEOS_LINESTRING )
1192 multiCurve = std::make_unique<QgsMultiCurve>( );
1193 multiCurve->addGeometry(
mGeometry->clone() );
1208 std::unique_ptr< QgsMultiPoint > splitPoints;
1210 std::unique_ptr< QgsAbstractGeometry > splitGeom(
fromGeos( GEOSsplitPoint ) );
1214 splitPoints.reset( qgis::down_cast<QgsMultiPoint *>( splitGeom.release() ) );
1218 splitPoints = std::make_unique< QgsMultiPoint >();
1221 splitPoints->addGeometry( qgis::down_cast<QgsPoint *>( splitGeom.release() ) );
1229 QgsMultiCurve lines;
1232 for (
int geometryIndex = 0; geometryIndex < multiCurve->numGeometries(); ++geometryIndex )
1245 QMap< int, QVector< QPair< double, QgsPoint > > >pointMap;
1246 for (
int splitPointIndex = 0; splitPointIndex < splitPoints->numGeometries(); ++splitPointIndex )
1248 const QgsPoint *intersectionPoint = splitPoints->pointN( splitPointIndex );
1250 QgsPoint segmentPoint2D;
1251 QgsVertexId nextVertex;
1254 line->
closestSegment( *intersectionPoint, segmentPoint2D, nextVertex );
1270 const QPair< double, QgsPoint > pair = qMakePair(
distance, *correctSegmentPoint.get() );
1271 if ( pointMap.contains( nextVertex.
vertex - 1 ) )
1272 pointMap[ nextVertex.
vertex - 1 ].append( pair );
1274 pointMap[ nextVertex.
vertex - 1 ] = QVector< QPair< double, QgsPoint > >() << pair;
1279 for (
auto &p : pointMap )
1281 std::sort( p.begin(), p.end(), [](
const QPair< double, QgsPoint > &a,
const QPair< double, QgsPoint > &b ) { return a.first < b.first; } );
1285 QgsLineString newLine;
1287 QgsPoint splitPoint;
1288 for (
int vertexIndex = 0; vertexIndex < nVertices; ++vertexIndex )
1290 QgsPoint currentPoint = line->
pointN( vertexIndex );
1292 if ( pointMap.contains( vertexIndex ) )
1295 for (
int k = 0; k < pointMap[ vertexIndex ].size(); ++k )
1297 splitPoint = pointMap[ vertexIndex ][k].second;
1298 if ( splitPoint == currentPoint )
1301 newLine = QgsLineString();
1304 else if ( splitPoint == line->
pointN( vertexIndex + 1 ) )
1308 newLine = QgsLineString();
1314 newLine = QgsLineString();
1323 return asGeos( &lines, mPrecision );
1328 Q_UNUSED( skipIntersectionCheck )
1337 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, splitLine, mGeos.get() ) );
1338 if ( !intersectGeom || GEOSisEmpty_r( context, intersectGeom.get() ) )
1342 const int linearIntersect = GEOSRelatePattern_r( context, mGeos.get(), splitLine,
"1********" );
1343 if ( linearIntersect > 0 )
1351 std::vector<geos::unique_ptr> lineGeoms;
1353 const int splitType = GEOSGeomTypeId_r( context, splitGeom.get() );
1354 if ( splitType == GEOS_MULTILINESTRING )
1356 const int nGeoms = GEOSGetNumGeometries_r( context, splitGeom.get() );
1357 lineGeoms.reserve( nGeoms );
1358 for (
int i = 0; i < nGeoms; ++i )
1359 lineGeoms.emplace_back( GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, splitGeom.get(), i ) ) );
1364 lineGeoms.emplace_back( GEOSGeom_clone_r( context, splitGeom.get() ) );
1367 mergeGeometriesMultiTypeSplit( lineGeoms );
1371 newGeometries << QgsGeometry(
fromGeos( lineGeom.get() ) );
1387 if ( !mGeosPrepared )
1393 if ( !skipIntersectionCheck && !GEOSPreparedIntersects_r( context, mGeosPrepared.get(), splitLine ) )
1398 if ( !nodedGeometry )
1407 const int numberOfGeometriesPolygon = numberOfGeometries( polygons.get() );
1408 if ( numberOfGeometriesPolygon == 0 )
1415 std::vector<geos::unique_ptr> testedGeometries;
1420 for (
int i = 0; i < numberOfGeometriesPolygon; i++ )
1422 const GEOSGeometry *polygon = GEOSGetGeometryN_r( context, polygons.get(), i );
1426 testedGeometries.emplace_back( GEOSGeom_clone_r( context, polygon ) );
1429 const size_t nGeometriesThis = numberOfGeometries( mGeos.get() );
1430 if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
1440 mergeGeometriesMultiTypeSplit( testedGeometries );
1443 for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( context, testedGeometries[i].get() ); ++i )
1446 if ( i < testedGeometries.size() )
1453 newGeometries << QgsGeometry(
fromGeos( testedGeometry.get() ) );
1461 if ( !splitLine || !geom )
1466 if ( GEOSGeom_getDimensions_r( context, geom ) == 2 )
1467 geometryBoundary.reset( GEOSBoundary_r( context, geom ) );
1469 geometryBoundary.reset( GEOSGeom_clone_r( context, geom ) );
1472 geos::unique_ptr unionGeometry( GEOSUnion_r( context, splitLineClone.get(), geometryBoundary.get() ) );
1474 return unionGeometry;
1477int QgsGeos::mergeGeometriesMultiTypeSplit( std::vector<geos::unique_ptr> &splitResult )
const
1484 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1485 if ( type != GEOS_GEOMETRYCOLLECTION &&
1486 type != GEOS_MULTILINESTRING &&
1487 type != GEOS_MULTIPOLYGON &&
1488 type != GEOS_MULTIPOINT )
1492 std::vector<geos::unique_ptr> unionGeom;
1494 std::vector<geos::unique_ptr> newSplitResult;
1496 for (
size_t i = 0; i < splitResult.size(); ++i )
1499 bool isPart =
false;
1500 for (
int j = 0; j < GEOSGetNumGeometries_r( context, mGeos.get() ); j++ )
1502 if ( GEOSEquals_r( context, splitResult[i].get(), GEOSGetGeometryN_r( context, mGeos.get(), j ) ) )
1511 unionGeom.emplace_back( std::move( splitResult[i] ) );
1515 std::vector<geos::unique_ptr> geomVector;
1516 geomVector.emplace_back( std::move( splitResult[i] ) );
1518 if ( type == GEOS_MULTILINESTRING )
1519 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, geomVector ) );
1520 else if ( type == GEOS_MULTIPOLYGON )
1521 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, geomVector ) );
1525 splitResult = std::move( newSplitResult );
1528 if ( !unionGeom.empty() )
1530 if ( type == GEOS_MULTILINESTRING )
1531 splitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, unionGeom ) );
1532 else if ( type == GEOS_MULTIPOLYGON )
1533 splitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ) );
1539geos::unique_ptr QgsGeos::createGeosCollection(
int typeId, std::vector<geos::unique_ptr> &geoms )
1541 std::vector<GEOSGeometry *> geomarr;
1542 geomarr.reserve( geoms.size() );
1547 if ( geomUniquePtr )
1549 if ( !GEOSisEmpty_r( context, geomUniquePtr.get() ) )
1553 geomarr.emplace_back( geomUniquePtr.release() );
1561 geomRes.reset( GEOSGeom_createCollection_r( context, typeId, geomarr.data(), geomarr.size() ) );
1563 catch ( QgsGeosException & )
1567 GEOSGeom_destroy_r( context, geom );
1582 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1583 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1584 bool hasZ = ( nCoordDims == 3 );
1585 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1587 switch ( GEOSGeomTypeId_r( context,
geos ) )
1591 if ( GEOSisEmpty_r( context,
geos ) )
1594 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1595 unsigned int nPoints = 0;
1596 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1605 return !point.
isEmpty() ? std::unique_ptr<QgsAbstractGeometry>( point.
clone() ) :
nullptr;
1607 case GEOS_LINESTRING:
1609 return sequenceToLinestring(
geos, hasZ, hasM );
1615 case GEOS_MULTIPOINT:
1617 auto multiPoint = std::make_unique<QgsMultiPoint>();
1618 int nParts = GEOSGetNumGeometries_r( context,
geos );
1619 multiPoint->reserve( nParts );
1620 for (
int i = 0; i < nParts; ++i )
1622 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context, GEOSGetGeometryN_r( context,
geos, i ) );
1625 unsigned int nPoints = 0;
1626 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1628 multiPoint->addGeometry(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1631 return std::move( multiPoint );
1633 case GEOS_MULTILINESTRING:
1635 auto multiLineString = std::make_unique<QgsMultiLineString>();
1636 int nParts = GEOSGetNumGeometries_r( context,
geos );
1637 multiLineString->reserve( nParts );
1638 for (
int i = 0; i < nParts; ++i )
1640 std::unique_ptr< QgsLineString >line( sequenceToLinestring( GEOSGetGeometryN_r( context,
geos, i ), hasZ, hasM ) );
1643 multiLineString->addGeometry( line.release() );
1646 return std::move( multiLineString );
1648 case GEOS_MULTIPOLYGON:
1650 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
1652 int nParts = GEOSGetNumGeometries_r( context,
geos );
1653 multiPolygon->reserve( nParts );
1654 for (
int i = 0; i < nParts; ++i )
1656 std::unique_ptr< QgsPolygon > poly =
fromGeosPolygon( GEOSGetGeometryN_r( context,
geos, i ) );
1659 multiPolygon->addGeometry( poly.release() );
1662 return std::move( multiPolygon );
1664 case GEOS_GEOMETRYCOLLECTION:
1666 auto geomCollection = std::make_unique<QgsGeometryCollection>();
1667 int nParts = GEOSGetNumGeometries_r( context,
geos );
1668 geomCollection->reserve( nParts );
1669 for (
int i = 0; i < nParts; ++i )
1671 std::unique_ptr< QgsAbstractGeometry > geom(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1674 geomCollection->addGeometry( geom.release() );
1677 return std::move( geomCollection );
1686 if ( GEOSGeomTypeId_r( context,
geos ) != GEOS_POLYGON )
1691 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1692 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1693 bool hasZ = ( nCoordDims == 3 );
1694 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1696 auto polygon = std::make_unique<QgsPolygon>();
1701 polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1704 QVector<QgsCurve *> interiorRings;
1705 const int ringCount = GEOSGetNumInteriorRings_r( context,
geos );
1706 interiorRings.reserve( ringCount );
1707 for (
int i = 0; i < ringCount; ++i )
1709 ring = GEOSGetInteriorRingN_r( context,
geos, i );
1712 interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1715 polygon->setInteriorRings( interiorRings );
1720std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring(
const GEOSGeometry *
geos,
bool hasZ,
bool hasM )
1723 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1725 unsigned int nPoints;
1726 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1728 QVector< double > xOut( nPoints );
1729 QVector< double > yOut( nPoints );
1730 QVector< double > zOut;
1732 zOut.resize( nPoints );
1733 QVector< double > mOut;
1735 mOut.resize( nPoints );
1737 double *x = xOut.data();
1738 double *y = yOut.data();
1739 double *z = zOut.data();
1740 double *m = mOut.data();
1742#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
1743 GEOSCoordSeq_copyToArrays_r( context, cs, x, y, hasZ ? z :
nullptr, hasM ? m :
nullptr );
1745 for (
unsigned int i = 0; i < nPoints; ++i )
1748 GEOSCoordSeq_getXYZ_r( context, cs, i, x++, y++, z++ );
1750 GEOSCoordSeq_getXY_r( context, cs, i, x++, y++ );
1753 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, m++ );
1757 auto line = std::make_unique<QgsLineString>( xOut, yOut, zOut, mOut );
1767 int geometryType = GEOSGeomTypeId_r( context, g );
1768 if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING
1769 || geometryType == GEOS_POLYGON )
1773 return GEOSGetNumGeometries_r( context, g );
1789 GEOSCoordSeq_getXYZ_r( context, cs, i, &x, &y, &z );
1791 GEOSCoordSeq_getXY_r( context, cs, i, &x, &y );
1794 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, &m );
1830 int geosType = GEOS_GEOMETRYCOLLECTION;
1837 geosType = GEOS_MULTIPOINT;
1841 geosType = GEOS_MULTILINESTRING;
1845 geosType = GEOS_MULTIPOLYGON;
1860 std::vector<geos::unique_ptr> geomVector;
1861 geomVector.reserve(
c->numGeometries() );
1862 for (
int i = 0; i <
c->numGeometries(); ++i )
1869 geomVector.emplace_back( std::move( geosGeom ) );
1871 return createGeosCollection( geosType, geomVector );
1879 if ( !polyhedralSurface )
1882 std::vector<geos::unique_ptr> geomVector;
1883 geomVector.reserve( polyhedralSurface->
numPatches() );
1884 for (
int i = 0; i < polyhedralSurface->
numPatches(); ++i )
1891 geomVector.emplace_back( std::move( geosPolygon ) );
1894 return createGeosCollection( GEOS_MULTIPOLYGON, geomVector );
1901 return createGeosPoint(
static_cast<const QgsPoint *
>( geom ), coordDims, precision, flags );
1904 return createGeosLinestring(
static_cast<const QgsLineString *
>( geom ), precision, flags );
1907 return createGeosPolygon(
static_cast<const QgsPolygon *
>( geom ), precision, flags );
1919 if ( !mGeos || !geom )
1930 const double gridSize = parameters.
gridSize();
1938 case OverlayIntersection:
1941 opGeom.reset( GEOSIntersectionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1945 opGeom.reset( GEOSIntersection_r( context, mGeos.get(), geosGeom.get() ) );
1949 case OverlayDifference:
1952 opGeom.reset( GEOSDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1956 opGeom.reset( GEOSDifference_r( context, mGeos.get(), geosGeom.get() ) );
1965 unionGeometry.reset( GEOSUnionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1969 unionGeometry.reset( GEOSUnion_r( context, mGeos.get(), geosGeom.get() ) );
1972 if ( unionGeometry && GEOSGeomTypeId_r( context, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1974 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, unionGeometry.get() ) );
1977 unionGeometry = std::move( mergedLines );
1981 opGeom = std::move( unionGeometry );
1985 case OverlaySymDifference:
1988 opGeom.reset( GEOSSymDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1992 opGeom.reset( GEOSSymDifference_r( context, mGeos.get(), geosGeom.get() ) );
1998 catch ( QgsGeosException &e )
2000 logError( QStringLiteral(
"GEOS" ), e.what() );
2003 *errorMsg = e.what();
2009bool QgsGeos::relation(
const QgsAbstractGeometry *geom, Relation r, QString *errorMsg )
const
2011 if ( !mGeos || !geom )
2023 bool result =
false;
2026 if ( mGeosPrepared )
2030 case RelationIntersects:
2031 result = ( GEOSPreparedIntersects_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2033 case RelationTouches:
2034 result = ( GEOSPreparedTouches_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2036 case RelationCrosses:
2037 result = ( GEOSPreparedCrosses_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2039 case RelationWithin:
2040 result = ( GEOSPreparedWithin_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2042 case RelationContains:
2043 result = ( GEOSPreparedContains_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2045 case RelationDisjoint:
2046 result = ( GEOSPreparedDisjoint_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2048 case RelationOverlaps:
2049 result = ( GEOSPreparedOverlaps_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2057 case RelationIntersects:
2058 result = ( GEOSIntersects_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2060 case RelationTouches:
2061 result = ( GEOSTouches_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2063 case RelationCrosses:
2064 result = ( GEOSCrosses_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2066 case RelationWithin:
2067 result = ( GEOSWithin_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2069 case RelationContains:
2070 result = ( GEOSContains_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2072 case RelationDisjoint:
2073 result = ( GEOSDisjoint_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2075 case RelationOverlaps:
2076 result = ( GEOSOverlaps_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2080 catch ( QgsGeosException &e )
2082 logError( QStringLiteral(
"GEOS" ), e.what() );
2085 *errorMsg = e.what();
2125 geos.reset( GEOSBufferWithStyle_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( endCapStyle ),
static_cast< int >( joinStyle ), miterLimit ) );
2175 geos.reset( GEOSGetCentroid_r( context, mGeos.get() ) );
2180 GEOSGeomGetX_r( context,
geos.get(), &x );
2181 GEOSGeomGetY_r( context,
geos.get(), &y );
2217 geos.reset( GEOSPointOnSurface_r( context, mGeos.get() ) );
2219 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
2224 GEOSGeomGetX_r( context,
geos.get(), &x );
2225 GEOSGeomGetY_r( context,
geos.get(), &y );
2242 std::unique_ptr< QgsAbstractGeometry > cHullGeom =
fromGeos( cHull.get() );
2243 return cHullGeom.release();
2248std::unique_ptr< QgsAbstractGeometry >
QgsGeos::concaveHull(
double targetPercent,
bool allowHoles, QString *errorMsg )
const
2250#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
2252 ( void )targetPercent;
2254 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHull requires a QGIS build based on GEOS 3.11 or later" ) );
2265 return concaveHullGeom;
2273#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<12
2275 ( void )invalidEdges;
2277 throw QgsNotSupportedException( QObject::tr(
"Validating coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2282 *errorMsg = QStringLiteral(
"Input geometry was not set" );
2290 const int result = GEOSCoverageIsValid_r( context, mGeos.get(), gapWidth, invalidEdges ? &invalidEdgesGeos :
nullptr );
2291 if ( invalidEdges && invalidEdgesGeos )
2293 *invalidEdges =
fromGeos( invalidEdgesGeos );
2295 if ( invalidEdgesGeos )
2297 GEOSGeom_destroy_r( context, invalidEdgesGeos );
2298 invalidEdgesGeos =
nullptr;
2318#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<12
2320 ( void )preserveBoundary;
2322 throw QgsNotSupportedException( QObject::tr(
"Simplifying coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2327 *errorMsg = QStringLiteral(
"Input geometry was not set" );
2334 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom =
fromGeos( simplified.get() );
2335 return simplifiedGeom;
2346 *errorMsg = QStringLiteral(
"Input geometry was not set" );
2353 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( unioned.get() );
2364 *errorMsg = QObject::tr(
"QGIS geometry cannot be converted to a GEOS geometry",
"GEOS Error" );
2373 char res = GEOSisValidDetail_r( context, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
2374 const bool invalid = res != 1;
2379 error = QString( r );
2380 GEOSFree_r( context, r );
2383 if ( invalid && errorMsg )
2386 static const std::map< QString, QString > sTranslatedErrors
2388 { QStringLiteral(
"topology validation error" ), QObject::tr(
"Topology validation error",
"GEOS Error" ) },
2389 { QStringLiteral(
"repeated point" ), QObject::tr(
"Repeated point",
"GEOS Error" ) },
2390 { QStringLiteral(
"hole lies outside shell" ), QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) },
2391 { QStringLiteral(
"holes are nested" ), QObject::tr(
"Holes are nested",
"GEOS Error" ) },
2392 { QStringLiteral(
"interior is disconnected" ), QObject::tr(
"Interior is disconnected",
"GEOS Error" ) },
2393 { QStringLiteral(
"self-intersection" ), QObject::tr(
"Self-intersection",
"GEOS Error" ) },
2394 { QStringLiteral(
"ring self-intersection" ), QObject::tr(
"Ring self-intersection",
"GEOS Error" ) },
2395 { QStringLiteral(
"nested shells" ), QObject::tr(
"Nested shells",
"GEOS Error" ) },
2396 { QStringLiteral(
"duplicate rings" ), QObject::tr(
"Duplicate rings",
"GEOS Error" ) },
2397 { QStringLiteral(
"too few points in geometry component" ), QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) },
2398 { QStringLiteral(
"invalid coordinate" ), QObject::tr(
"Invalid coordinate",
"GEOS Error" ) },
2399 { QStringLiteral(
"ring is not closed" ), QObject::tr(
"Ring is not closed",
"GEOS Error" ) },
2402 const auto translatedError = sTranslatedErrors.find( error.toLower() );
2403 if ( translatedError != sTranslatedErrors.end() )
2404 *errorMsg = translatedError->second;
2408 if ( g1 && errorLoc )
2414 GEOSGeom_destroy_r( context, g1 );
2424 if ( !mGeos || !geom )
2470GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
2474 std::unique_ptr< QgsLineString > segmentized;
2480 line = segmentized.get();
2487 GEOSCoordSequence *coordSeq =
nullptr;
2489 const int numPoints = line->
numPoints();
2491 const bool hasZ = line->
is3D();
2493#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
2496 if ( !forceClose || ( line->
pointN( 0 ) == line->
pointN( numPoints - 1 ) ) )
2501 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, line->
xData(), line->
yData(), line->
zData(),
nullptr, numPoints );
2504 QgsDebugError( QStringLiteral(
"GEOS Exception: Could not create coordinate sequence for %1 points" ).arg( numPoints ) );
2512 QVector< double > x = line->
xVector();
2513 if ( numPoints > 0 )
2514 x.append( x.at( 0 ) );
2515 QVector< double > y = line->
yVector();
2516 if ( numPoints > 0 )
2517 y.append( y.at( 0 ) );
2518 QVector< double > z = line->
zVector();
2519 if ( hasZ && numPoints > 0 )
2520 z.append( z.at( 0 ) );
2523 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, x.constData(), y.constData(), !hasZ ?
nullptr : z.constData(),
nullptr, numPoints + 1 );
2526 QgsDebugError( QStringLiteral(
"GEOS Exception: Could not create closed coordinate sequence for %1 points" ).arg( numPoints + 1 ) );
2537 const bool hasM =
false;
2548 int numOutPoints = numPoints;
2549 if ( forceClose && ( line->
pointN( 0 ) != line->
pointN( numPoints - 1 ) ) )
2556 coordSeq = GEOSCoordSeq_create_r( context, numOutPoints, coordDims );
2559 QgsDebugError( QStringLiteral(
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions" ).arg( numPoints ).arg( coordDims ) );
2563 const double *xData = line->
xData();
2564 const double *yData = line->
yData();
2565 const double *zData = hasZ ? line->
zData() :
nullptr;
2566 const double *mData = hasM ? line->
mData() :
nullptr;
2568 if ( precision > 0. )
2570 for (
int i = 0; i < numOutPoints; ++i )
2572 if ( i >= numPoints )
2575 xData = line->
xData();
2576 yData = line->
yData();
2577 zData = hasZ ? line->
zData() :
nullptr;
2578 mData = hasM ? line->
mData() :
nullptr;
2582 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
2586 GEOSCoordSeq_setXY_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
2590 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2596 for (
int i = 0; i < numOutPoints; ++i )
2598 if ( i >= numPoints )
2601 xData = line->
xData();
2602 yData = line->
yData();
2603 zData = hasZ ? line->
zData() :
nullptr;
2604 mData = hasM ? line->
mData() :
nullptr;
2608 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, *xData++, *yData++, *zData++ );
2612 GEOSCoordSeq_setXY_r( context, coordSeq, i, *xData++, *yData++ );
2616 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2626geos::unique_ptr
QgsGeos::createGeosPoint( const QgsAbstractGeometry *point,
int coordDims,
double precision, Qgis::GeosCreationFlags )
2632 return createGeosPointXY( pt->
x(), pt->
y(), pt->
is3D(), pt->
z(), pt->
isMeasure(), pt->
m(), coordDims, precision );
2644 if ( coordDims == 2 )
2647 if ( precision > 0. )
2648 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
2650 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, x, y ) );
2654 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( context, 1, coordDims );
2657 QgsDebugError( QStringLiteral(
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions" ).arg( coordDims ) );
2660 if ( precision > 0. )
2662 GEOSCoordSeq_setX_r( context, coordSeq, 0, std::round( x / precision ) * precision );
2663 GEOSCoordSeq_setY_r( context, coordSeq, 0, std::round( y / precision ) * precision );
2666 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, std::round( z / precision ) * precision );
2671 GEOSCoordSeq_setX_r( context, coordSeq, 0, x );
2672 GEOSCoordSeq_setY_r( context, coordSeq, 0, y );
2675 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, z );
2681 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 3, m );
2684 geosPoint.reset( GEOSGeom_createPoint_r( context, coordSeq ) );
2690geos::unique_ptr
QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve,
double precision, Qgis::GeosCreationFlags )
2696 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c, precision );
2709geos::unique_ptr
QgsGeos::createGeosPolygon( const QgsAbstractGeometry *poly,
double precision, Qgis::GeosCreationFlags flags )
2715 const QgsCurve *exteriorRing = polygon->
exteriorRing();
2716 if ( !exteriorRing )
2725 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( context, createCoordinateSequence( exteriorRing, precision,
true ) ) );
2728 QList< const QgsCurve * > holesToExport;
2729 holesToExport.reserve( nInteriorRings );
2730 for (
int i = 0; i < nInteriorRings; ++i )
2732 const QgsCurve *interiorRing = polygon->
interiorRing( i );
2735 holesToExport << interiorRing;
2740 if ( !holesToExport.empty() )
2743 for (
int i = 0; i < holesToExport.size(); ++i )
2745 holes[i] = GEOSGeom_createLinearRing_r( context, createCoordinateSequence( holesToExport[i], precision,
true ) );
2749 geosPolygon.reset( GEOSGeom_createPolygon_r( context, exteriorRingGeos.release(), holes, holesToExport.size() ) );
2769 offset.reset( GEOSOffsetCurve_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( joinStyle ), miterLimit ) );
2781 return fromGeos( res.get() ).release();
2796 GEOSBufferParams_setSingleSided_r( context, bp.get(), 1 );
2797 GEOSBufferParams_setQuadrantSegments_r( context, bp.get(), segments );
2798 GEOSBufferParams_setJoinStyle_r( context, bp.get(),
static_cast< int >( joinStyle ) );
2799 GEOSBufferParams_setMitreLimit_r( context, bp.get(), miterLimit );
2805 geos.reset( GEOSBufferWithParams_r( context, mGeos.get(), bp.get(),
distance ) );
2839 boundaryGeos =
asGeos( boundary );
2867 return std::numeric_limits< double >::quiet_NaN();
2875 return std::numeric_limits< double >::quiet_NaN();
2915 if ( !mGeos || !other )
2935 if ( !mGeos ||
mGeometry->dimension() == 0 )
2947 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
2951 int numGeoms = GEOSGetNumGeometries_r( context, mGeos.get() );
2952 if ( numGeoms == -1 )
2961 bool isMultiGeom =
false;
2962 int geosTypeId = GEOSGeomTypeId_r( context, mGeos.get() );
2963 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
2966 bool isLine = (
mGeometry->dimension() == 1 );
2973 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2977 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2982 if ( reshapedGeometry )
2988 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
2989 return reshapeResult;
2996 bool reshapeTookPlace =
false;
3001 for (
int i = 0; i < numGeoms; ++i )
3004 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3006 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3008 if ( currentReshapeGeometry )
3010 newGeoms[i] = currentReshapeGeometry.release();
3011 reshapeTookPlace =
true;
3015 newGeoms[i] = GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, mGeos.get(), i ) );
3022 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
3026 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
3030 if ( !newMultiGeom )
3036 if ( reshapeTookPlace )
3040 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
3041 return reshapedMultiGeom;
3064 if ( GEOSGeomTypeId_r( context, mGeos.get() ) != GEOS_MULTILINESTRING )
3070 double gridSize = parameters.
gridSize();
3073 geos::unique_ptr geosFixedSize( GEOSGeom_setPrecision_r( context, mGeos.get(), gridSize, 0 ) );
3074 geos.reset( GEOSLineMerge_r( context, geosFixedSize.get() ) );
3077 geos.reset( GEOSLineMerge_r( context, mGeos.get() ) );
3102 if ( mGeosPrepared )
3104 nearestCoord.reset( GEOSPreparedNearestPoints_r( context, mGeosPrepared.get(), otherGeom.get() ) );
3108 nearestCoord.reset( GEOSNearestPoints_r( context, mGeos.get(), otherGeom.get() ) );
3111 ( void )GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx );
3112 ( void )GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny );
3114 catch ( QgsGeosException &e )
3116 logError( QStringLiteral(
"GEOS" ), e.what() );
3119 *errorMsg = e.what();
3124 return std::make_unique< QgsPoint >( nx, ny );
3129 if ( !mGeos || other.
isEmpty() )
3139 if ( !other || other->
isEmpty() )
3157 if ( !nearestCoord )
3160 *errorMsg = QStringLiteral(
"GEOS returned no nearest points" );
3164 ( void )GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx1 );
3165 ( void )GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny1 );
3166 ( void )GEOSCoordSeq_getX_r( context, nearestCoord.get(), 1, &nx2 );
3167 ( void )GEOSCoordSeq_getY_r( context, nearestCoord.get(), 1, &ny2 );
3169 catch ( QgsGeosException &e )
3171 logError( QStringLiteral(
"GEOS" ), e.what() );
3174 *errorMsg = e.what();
3179 auto line = std::make_unique< QgsLineString >();
3203 catch ( QgsGeosException &e )
3205 logError( QStringLiteral(
"GEOS" ), e.what() );
3208 *errorMsg = e.what();
3223 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
3232 catch ( QgsGeosException &e )
3234 logError( QStringLiteral(
"GEOS" ), e.what() );
3237 *errorMsg = e.what();
3254 lineGeosGeometries[validLines] = l.release();
3262 geos::unique_ptr result( GEOSPolygonize_r( context, lineGeosGeometries, validLines ) );
3263 for (
int i = 0; i < validLines; ++i )
3265 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3267 delete[] lineGeosGeometries;
3270 catch ( QgsGeosException &e )
3274 *errorMsg = e.what();
3276 for (
int i = 0; i < validLines; ++i )
3278 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3280 delete[] lineGeosGeometries;
3295 extentGeosGeom =
asGeos( extent, mPrecision );
3296 if ( !extentGeosGeom )
3306 geos.reset( GEOSVoronoiDiagram_r( context, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
3308 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3329 geos.reset( GEOSDelaunayTriangulation_r( context, mGeos.get(), tolerance, edgesOnly ) );
3331 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3343#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
3345 throw QgsNotSupportedException( QObject::tr(
"Calculating constrainedDelaunayTriangulation requires a QGIS build based on GEOS 3.11 or later" ) );
3356 geos.reset( GEOSConstrainedDelaunayTriangulation_r( context, mGeos.get() ) );
3358 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3363 std::unique_ptr< QgsAbstractGeometry > res =
fromGeos(
geos.get() );
3366 return std::unique_ptr< QgsAbstractGeometry >( collection->extractPartsByType(
Qgis::WkbType::Polygon,
true ) );
3378static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
3381 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( context, linestring );
3385 unsigned int coordSeqSize;
3386 if ( GEOSCoordSeq_getSize_r( context, coordSeq, &coordSeqSize ) == 0 )
3389 if ( coordSeqSize < 2 )
3392 GEOSCoordSeq_getX_r( context, coordSeq, 0, &x1 );
3393 GEOSCoordSeq_getY_r( context, coordSeq, 0, &y1 );
3394 GEOSCoordSeq_getX_r( context, coordSeq, coordSeqSize - 1, &x2 );
3395 GEOSCoordSeq_getY_r( context, coordSeq, coordSeqSize - 1, &y2 );
3403 double x1, y1, x2, y2;
3404 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
3407 double rx1, ry1, rx2, ry2;
3408 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
3411 bool intersectionAtOrigLineEndpoint =
3412 ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) !=
3413 ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
3414 bool intersectionAtReshapeLineEndpoint =
3415 ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) ||
3416 ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
3420 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
3424 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
3425 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, geoms, 2 ) );
3430 double x1res, y1res, x2res, y2res;
3431 if ( !_linestringEndpoints( res.get(), x1res, y1res, x2res, y2res ) )
3433 if ( ( x1res == x2 && y1res == y2 ) || ( x2res == x1 && y2res == y1 ) )
3434 res.reset( GEOSReverse_r( context, res.get() ) );
3445 if ( !line || !reshapeLineGeos )
3448 bool atLeastTwoIntersections =
false;
3449 bool oneIntersection =
false;
3450 QgsPointXY oneIntersectionPoint;
3456 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, line, reshapeLineGeos ) );
3457 if ( intersectGeom )
3459 const int geomType = GEOSGeomTypeId_r( context, intersectGeom.get() );
3460 atLeastTwoIntersections = ( geomType == GEOS_MULTIPOINT && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 1 )
3461 || ( geomType == GEOS_GEOMETRYCOLLECTION && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 )
3462 || ( geomType == GEOS_MULTILINESTRING && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 );
3464 if ( GEOSGeomTypeId_r( context, intersectGeom.get() ) == GEOS_POINT )
3466 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( context, intersectGeom.get() );
3468 GEOSCoordSeq_getX_r( context, intersectionCoordSeq, 0, &xi );
3469 GEOSCoordSeq_getY_r( context, intersectionCoordSeq, 0, &yi );
3470 oneIntersection =
true;
3471 oneIntersectionPoint = QgsPointXY( xi, yi );
3475 catch ( QgsGeosException & )
3477 atLeastTwoIntersections =
false;
3481 if ( oneIntersection )
3482 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
3484 if ( !atLeastTwoIntersections )
3488 double x1, y1, x2, y2;
3489 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
3492 geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1,
false, 0,
false, 0, 2, precision );
3493 geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2,
false, 0,
false, 0, 2, precision );
3495 bool isRing =
false;
3496 if ( GEOSGeomTypeId_r( context, line ) == GEOS_LINEARRING
3497 || GEOSEquals_r( context, beginLineVertex.get(), endLineVertex.get() ) == 1 )
3502 if ( !nodedGeometry )
3508 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, nodedGeometry.get() ) );
3514 int numMergedLines = GEOSGetNumGeometries_r( context, mergedLines.get() );
3515 if ( numMergedLines < 2 )
3517 if ( numMergedLines == 1 )
3526 QVector<GEOSGeometry *> resultLineParts;
3527 QVector<GEOSGeometry *> probableParts;
3529 for (
int i = 0; i < numMergedLines; ++i )
3531 const GEOSGeometry *currentGeom = GEOSGetGeometryN_r( context, mergedLines.get(), i );
3534 bool alreadyAdded =
false;
3536 double bufferDistance = std::pow( 10.0L, geomDigits( currentGeom ) - 11 );
3537 for (
const GEOSGeometry *other : std::as_const( resultLineParts ) )
3539 GEOSHausdorffDistance_r( context, currentGeom, other, &
distance );
3542 alreadyAdded =
true;
3549 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( context, currentGeom );
3550 unsigned int currentCoordSeqSize;
3551 GEOSCoordSeq_getSize_r( context, currentCoordSeq, ¤tCoordSeqSize );
3552 if ( currentCoordSeqSize < 2 )
3556 double xBegin, xEnd, yBegin, yEnd;
3557 GEOSCoordSeq_getX_r( context, currentCoordSeq, 0, &xBegin );
3558 GEOSCoordSeq_getY_r( context, currentCoordSeq, 0, &yBegin );
3559 GEOSCoordSeq_getX_r( context, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
3560 GEOSCoordSeq_getY_r( context, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
3561 geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin,
false, 0,
false, 0, 2, precision );
3562 geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd,
false, 0,
false, 0, 2, precision );
3565 int nEndpointsOnOriginalLine = 0;
3566 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
3567 nEndpointsOnOriginalLine += 1;
3569 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
3570 nEndpointsOnOriginalLine += 1;
3573 int nEndpointsSameAsOriginalLine = 0;
3574 if ( GEOSEquals_r( context, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
3575 || GEOSEquals_r( context, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3576 nEndpointsSameAsOriginalLine += 1;
3578 if ( GEOSEquals_r( context, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
3579 || GEOSEquals_r( context, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3580 nEndpointsSameAsOriginalLine += 1;
3583 bool currentGeomOverlapsOriginalGeom =
false;
3584 bool currentGeomOverlapsReshapeLine =
false;
3585 if ( lineContainedInLine( currentGeom, line ) == 1 )
3586 currentGeomOverlapsOriginalGeom =
true;
3588 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
3589 currentGeomOverlapsReshapeLine =
true;
3592 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3594 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3597 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3599 probableParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3601 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3603 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3605 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3607 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3609 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
3611 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3616 if ( isRing && !probableParts.isEmpty() )
3620 double maxLength = -std::numeric_limits<double>::max();
3621 double currentLength = 0;
3622 for (
int i = 0; i < probableParts.size(); ++i )
3624 currentGeom = probableParts.at( i );
3625 GEOSLength_r( context, currentGeom, ¤tLength );
3626 if ( currentLength > maxLength )
3628 maxLength = currentLength;
3629 maxGeom.reset( currentGeom );
3633 GEOSGeom_destroy_r( context, currentGeom );
3636 resultLineParts.push_back( maxGeom.release() );
3640 if ( resultLineParts.empty() )
3643 if ( resultLineParts.size() == 1 )
3645 result.reset( resultLineParts[0] );
3650 for (
int i = 0; i < resultLineParts.size(); ++i )
3652 lineArray[i] = resultLineParts[i];
3656 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
3657 delete [] lineArray;
3660 result.reset( GEOSLineMerge_r( context, multiLineGeom.get() ) );
3664 if ( GEOSGeomTypeId_r( context, result.get() ) != GEOS_LINESTRING )
3670 bool reverseLine =
false;
3674 char isResultCCW = 0, isOriginCCW = 0;
3675 if ( GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, result.get() ), &isResultCCW ) &&
3676 GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, line ), &isOriginCCW )
3680 reverseLine = ( isOriginCCW == 1 && isResultCCW == 0 ) || ( isOriginCCW == 0 && isResultCCW == 1 );
3686 double x1res, y1res, x2res, y2res;
3687 if ( !_linestringEndpoints( result.get(), x1res, y1res, x2res, y2res ) )
3689 geos::unique_ptr beginResultLineVertex = createGeosPointXY( x1res, y1res,
false, 0,
false, 0, 2, precision );
3690 geos::unique_ptr endResultLineVertex = createGeosPointXY( x2res, y2res,
false, 0,
false, 0, 2, precision );
3691 reverseLine = GEOSEquals_r( context, beginLineVertex.get(), endResultLineVertex.get() ) == 1 || GEOSEquals_r( context, endLineVertex.get(), beginResultLineVertex.get() ) == 1;
3694 result.reset( GEOSReverse_r( context, result.get() ) );
3702 int nIntersections = 0;
3703 int lastIntersectingRing = -2;
3707 int nRings = GEOSGetNumInteriorRings_r( context, polygon );
3712 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( context, polygon );
3713 if ( GEOSIntersects_r( context, outerRing, reshapeLineGeos ) == 1 )
3716 lastIntersectingRing = -1;
3717 lastIntersectingGeom = outerRing;
3725 for (
int i = 0; i < nRings; ++i )
3727 innerRings[i] = GEOSGetInteriorRingN_r( context, polygon, i );
3728 if ( GEOSIntersects_r( context, innerRings[i], reshapeLineGeos ) == 1 )
3731 lastIntersectingRing = i;
3732 lastIntersectingGeom = innerRings[i];
3736 catch ( QgsGeosException & )
3741 if ( nIntersections != 1 )
3743 delete [] innerRings;
3748 geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
3749 if ( !reshapeResult )
3751 delete [] innerRings;
3757 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( context, reshapeResult.get() );
3758 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( context, reshapeSequence );
3760 reshapeResult.reset();
3764 newRing = GEOSGeom_createLinearRing_r( context, newCoordSequence );
3766 catch ( QgsGeosException & )
3773 delete [] innerRings;
3778 if ( lastIntersectingRing == -1 )
3779 newOuterRing = newRing;
3781 newOuterRing = GEOSGeom_clone_r( context, outerRing );
3784 QVector<GEOSGeometry *> ringList;
3787 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( context, GEOSGeom_clone_r( context, newOuterRing ),
nullptr, 0 );
3788 if ( outerRingPoly )
3790 ringList.reserve( nRings );
3792 for (
int i = 0; i < nRings; ++i )
3794 if ( lastIntersectingRing == i )
3795 currentRing = newRing;
3797 currentRing = GEOSGeom_clone_r( context, innerRings[i] );
3800 if ( GEOSContains_r( context, outerRingPoly, currentRing ) == 1 )
3801 ringList.push_back( currentRing );
3803 GEOSGeom_destroy_r( context, currentRing );
3806 GEOSGeom_destroy_r( context, outerRingPoly );
3810 for (
int i = 0; i < ringList.size(); ++i )
3811 newInnerRings[i] = ringList.at( i );
3813 delete [] innerRings;
3815 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( context, newOuterRing, newInnerRings, ringList.size() ) );
3816 delete[] newInnerRings;
3818 return reshapedPolygon;
3823 if ( !line1 || !line2 )
3828 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
3835 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, bufferGeom.get(), line1 ) );
3838 double intersectGeomLength;
3841 GEOSLength_r( context, intersectionGeom.get(), &intersectGeomLength );
3842 GEOSLength_r( context, line1, &line1Length );
3844 double intersectRatio = line1Length / intersectGeomLength;
3845 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
3853 if ( !point || !line )
3856 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
3859 geos::unique_ptr lineBuffer( GEOSBuffer_r( context, line, bufferDistance, 8 ) );
3863 bool contained =
false;
3864 if ( GEOSContains_r( context, lineBuffer.get(), point ) == 1 )
3877 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( context, bbox.get() );
3881 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( context, bBoxRing );
3883 if ( !bBoxCoordSeq )
3886 unsigned int nCoords = 0;
3887 if ( !GEOSCoordSeq_getSize_r( context, bBoxCoordSeq, &nCoords ) )
3891 for (
unsigned int i = 0; i < nCoords - 1; ++i )
3894 GEOSCoordSeq_getX_r( context, bBoxCoordSeq, i, &t );
3897 digits = std::ceil( std::log10( std::fabs( t ) ) );
3898 if ( digits > maxDigits )
3901 GEOSCoordSeq_getY_r( context, bBoxCoordSeq, i, &t );
3902 digits = std::ceil( std::log10( std::fabs( t ) ) );
3903 if ( digits > maxDigits )
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.
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.
double frechetDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
double hausdorffDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
bool touches(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom touches this.
static geos::unique_ptr offsetCurve(const GEOSGeometry *geometry, double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit, QString *errorMsg=nullptr)
Directly calculates the offset curve for a GEOS geometry object and returns a GEOS geometry result.
QgsAbstractGeometry * symDifference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const override
Calculate the symmetric difference of this 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.
std::unique_ptr< QgsAbstractGeometry > constrainedDelaunayTriangulation(QString *errorMsg=nullptr) const
Returns a constrained Delaunay triangulation for the vertices of the geometry.
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
std::unique_ptr< QgsAbstractGeometry > closestPoint(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the closest point on the geometry to the other 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.
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 * 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 > mergeLines(QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
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.
std::unique_ptr< QgsAbstractGeometry > 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.
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.
static std::unique_ptr< QgsPolygon > fromGeosPolygon(const GEOSGeometry *geos)
double hausdorffDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
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).
QgsGeos(const QgsAbstractGeometry *geometry, double precision=0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
GEOS geometry engine constructor.
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.
std::unique_ptr< QgsAbstractGeometry > shortestLine(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the shortest line joining this geometry to the other geometry.
std::unique_ptr< QgsAbstractGeometry > concaveHull(double targetPercent, bool allowHoles=false, QString *errorMsg=nullptr) const
Returns a possibly concave geometry that encloses the input geometry.
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).
static QgsGeometry polygonize(const QVector< const QgsAbstractGeometry * > &geometries, QString *errorMsg=nullptr)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
double frechetDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
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...
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.
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
std::unique_ptr< QgsAbstractGeometry > delaunayTriangulation(double tolerance=0.0, bool edgesOnly=false, QString *errorMsg=nullptr) const
Returns the Delaunay triangulation for the vertices of the geometry.
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...
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.
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.
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