39using namespace Qt::StringLiterals;
41#define DEFAULT_QUADRANT_SEGMENTS 8
43#define CATCH_GEOS( r ) \
44 catch ( QgsGeosException & ) \
49#define CATCH_GEOS_WITH_ERRMSG( r ) \
50 catch ( QgsGeosException & e ) \
54 *errorMsg = e.what(); \
55 if ( errorMsg->startsWith( "InterruptedException"_L1, Qt::CaseInsensitive ) ) \
65static void throwQgsGeosException(
const char *fmt, ... )
71 vsnprintf( buffer,
sizeof buffer, fmt, ap );
74 QString message = QString::fromUtf8( buffer );
84 throw QgsGeosException( message );
92 throw QgsGeosException( message );
97static void printGEOSNotice(
const char *fmt, ... )
99#if defined( QGISDEBUG )
104 vsnprintf( buffer,
sizeof buffer, fmt, ap );
115#if defined( USE_THREAD_LOCAL ) && !defined( Q_OS_WIN )
118QThreadStorage< QgsGeosContext * > QgsGeosContext::sGeosContext;
124 mContext = GEOS_init_r();
125 GEOSContext_setNoticeHandler_r( mContext, printGEOSNotice );
126 GEOSContext_setErrorHandler_r( mContext, throwQgsGeosException );
131 GEOS_finish_r( mContext );
136#if defined( USE_THREAD_LOCAL ) && !defined( Q_OS_WIN )
137 return sGeosContext.mContext;
139 GEOSContextHandle_t gContext =
nullptr;
140 if ( sGeosContext.hasLocalData() )
142 gContext = sGeosContext.localData()->mContext;
147 gContext = sGeosContext.localData()->mContext;
184 , mPrecision( precision )
211#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
213 throw QgsNotSupportedException( QObject::tr(
"The structured method to make geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
216 throw QgsNotSupportedException( QObject::tr(
"The keep collapsed option for making geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
220 geos.reset( GEOSMakeValid_r( context, mGeos.get() ) );
225 GEOSMakeValidParams *params = GEOSMakeValidParams_create_r( context );
230 GEOSMakeValidParams_setMethod_r( context, params, GEOS_MAKE_VALID_LINEWORK );
234 GEOSMakeValidParams_setMethod_r( context, params, GEOS_MAKE_VALID_STRUCTURE );
238 GEOSMakeValidParams_setKeepCollapsed_r( context, params, keepCollapsed ? 1 : 0 );
243 geos.reset( GEOSMakeValidWithParams_r( context, mGeos.get(), params ) );
244 GEOSMakeValidParams_destroy_r( context, params );
246 catch ( QgsGeosException &e )
250 *errorMsg = e.what();
252 GEOSMakeValidParams_destroy_r( context, params );
281 std::unique_ptr< QgsAbstractGeometry > geom =
fromGeos( newPart );
288 mGeosPrepared.reset();
322 return overlay( geom, OverlayIntersection, errorMsg, parameters, feedback ).release();
327 return overlay( geom, OverlayDifference, errorMsg, parameters, feedback ).release();
343 catch ( QgsGeosException &e )
348 *errorMsg = e.what();
357 int partType = GEOSGeomTypeId_r( context, currentPart );
360 if ( partType == GEOS_POINT )
371 if ( partType == GEOS_MULTILINESTRING || partType == GEOS_MULTIPOLYGON || partType == GEOS_GEOMETRYCOLLECTION )
373 int partCount = GEOSGetNumGeometries_r( context, currentPart );
374 for (
int i = 0; i < partCount; ++i )
376 subdivideRecursive( GEOSGetGeometryN_r( context, currentPart, i ), maxNodes, depth, parts, clipRect, gridSize );
387 int vertexCount = GEOSGetNumCoordinates_r( context, currentPart );
388 if ( vertexCount == 0 )
392 else if ( vertexCount < maxNodes )
399 double width = clipRect.
width();
400 double height = clipRect.
height();
401 QgsRectangle halfClipRect1 = clipRect;
402 QgsRectangle halfClipRect2 = clipRect;
403 if ( width > height )
416 halfClipRect1.
setYMinimum( halfClipRect1.
yMinimum() - std::numeric_limits<double>::epsilon() );
417 halfClipRect2.
setYMinimum( halfClipRect2.
yMinimum() - std::numeric_limits<double>::epsilon() );
418 halfClipRect1.
setYMaximum( halfClipRect1.
yMaximum() + std::numeric_limits<double>::epsilon() );
419 halfClipRect2.
setYMaximum( halfClipRect2.
yMaximum() + std::numeric_limits<double>::epsilon() );
423 halfClipRect1.
setXMinimum( halfClipRect1.
xMinimum() - std::numeric_limits<double>::epsilon() );
424 halfClipRect2.
setXMinimum( halfClipRect2.
xMinimum() - std::numeric_limits<double>::epsilon() );
425 halfClipRect1.
setXMaximum( halfClipRect1.
xMaximum() + std::numeric_limits<double>::epsilon() );
426 halfClipRect2.
setXMaximum( halfClipRect2.
xMaximum() + std::numeric_limits<double>::epsilon() );
438 clipPart1.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart1.get(), gridSize ) );
440 subdivideRecursive( clipPart1.get(), maxNodes, depth, parts, halfClipRect1, gridSize );
446 clipPart2.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart2.get(), gridSize ) );
448 subdivideRecursive( clipPart2.get(), maxNodes, depth, parts, halfClipRect2, gridSize );
460 maxNodes = std::max( maxNodes, 8 );
466 subdivideRecursive( mGeos.get(), maxNodes, 0, parts.get(),
mGeometry->boundingBox(), parameters.
gridSize() );
470 return std::move( parts );
475 return overlay( geom, OverlayUnion, errorMsg, parameters, feedback ).release();
480 std::vector<geos::unique_ptr> geosGeometries;
481 geosGeometries.reserve( geomList.size() );
487 geosGeometries.emplace_back(
asGeos( g, mPrecision ) );
495 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
498 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
502 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
507 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
508 return result.release();
513 std::vector<geos::unique_ptr> geosGeometries;
514 geosGeometries.reserve( geomList.size() );
520 geosGeometries.emplace_back(
asGeos( g.constGet(), mPrecision ) );
528 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
532 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
536 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
541 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
542 return result.release();
547 return overlay( geom, OverlaySymDifference, errorMsg, parameters, feedback ).release();
550static bool isZVerticalLine(
const QgsAbstractGeometry *geom,
double tolerance = 4 * std::numeric_limits<double>::epsilon() )
560 bool isVertical =
true;
563 const int nrPoints = line->numPoints();
571 const double sqrTolerance = tolerance * tolerance;
572 const double *lineX = line->xData();
573 const double *lineY = line->yData();
574 for (
int iVert = nrPoints - 1, jVert = 0; jVert < nrPoints; iVert = jVert++ )
605 otherGeosGeom =
asGeos( &firstPoint, mPrecision );
609 otherGeosGeom =
asGeos( geom, mPrecision );
612 if ( !otherGeosGeom )
620 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
622 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
626 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
643 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
652 GEOSPreparedDistance_r( context, mGeosPrepared.get(), point.get(), &
distance );
656 GEOSDistance_r( context, mGeos.get(), point.get(), &
distance );
673 return intersects( geom, errorMsg, feedback );
685 otherGeosGeom =
asGeos( &firstPoint );
689 otherGeosGeom =
asGeos( geom, mPrecision );
692 if ( !otherGeosGeom )
706 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
708#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
709 return GEOSPreparedDistanceWithin_r( context, mGeosPrepared.get(), otherGeosGeom.get(), maxdist );
711 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
716#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
717 return GEOSDistanceWithin_r( context, mGeos.get(), otherGeosGeom.get(), maxdist );
719 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
735#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
738 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
744#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
745 return GEOSPreparedContainsXY_r( context, mGeosPrepared.get(), x, y ) == 1;
747 return GEOSPreparedContains_r( context, mGeosPrepared.get(), point.get() ) == 1;
751#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
752 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
757 result = ( GEOSContains_r( context, mGeos.get(), point.get() ) == 1 );
759 catch ( QgsGeosException &e )
764 *errorMsg = e.what();
782 if ( !otherGeosGeom )
806 if ( !otherGeosGeom )
830 if ( !otherGeosGeom )
854 if ( !otherGeosGeom )
870 if ( !mGeos || !geom )
876#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
884 return GEOSPreparedIntersectsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
886 catch ( QgsGeosException &e )
891 *errorMsg = e.what();
899 return relation( geom, RelationIntersects, errorMsg );
904 return relation( geom, RelationTouches, errorMsg, feedback );
909 return relation( geom, RelationCrosses, errorMsg, feedback );
914 return relation( geom, RelationWithin, errorMsg, feedback );
919 return relation( geom, RelationOverlaps, errorMsg, feedback );
924 if ( !mGeos || !geom )
929#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
938 return GEOSPreparedContainsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
940 catch ( QgsGeosException &e )
945 *errorMsg = e.what();
953 return relation( geom, RelationContains, errorMsg, feedback );
958 return relation( geom, RelationDisjoint, errorMsg, feedback );
979 char *r = GEOSRelate_r( context, mGeos.get(), geosGeom.get() );
982 result = QString( r );
983 GEOSFree_r( context, r );
986 catch ( QgsGeosException &e )
991 *errorMsg = e.what();
1000 if ( !mGeos || !geom )
1011 bool result =
false;
1017 result = ( GEOSRelatePattern_r( context, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
1019 catch ( QgsGeosException &e )
1024 *errorMsg = e.what();
1065 const QgsLineString &splitLine, QVector<QgsGeometry> &newGeometries,
bool topological,
QgsPointSequence &topologyTestPoints, QString *errorMsg,
bool skipIntersectionCheck
1081 if ( !GEOSisValid_r( context, mGeos.get() ) )
1088 newGeometries.clear();
1095 splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
1099 splitLineGeos = createGeosPointXY( splitLine.
xAt( 0 ), splitLine.
yAt( 0 ),
false, 0,
false, 0, 2, mPrecision );
1106 if ( !GEOSisValid_r( context, splitLineGeos.get() ) || !GEOSisSimple_r( context, splitLineGeos.get() ) )
1114 if ( !topologicalTestPointsSplit( splitLineGeos.get(), topologyTestPoints ) )
1123 returnCode = splitLinearGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1127 returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1155 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, mGeos.get(), splitLine ) );
1156 if ( !intersectionGeom )
1159 bool simple =
false;
1160 int nIntersectGeoms = 1;
1161 if ( GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_LINESTRING || GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_POINT )
1165 nIntersectGeoms = GEOSGetNumGeometries_r( context, intersectionGeom.get() );
1167 for (
int i = 0; i < nIntersectGeoms; ++i )
1171 currentIntersectGeom = intersectionGeom.get();
1173 currentIntersectGeom = GEOSGetGeometryN_r( context, intersectionGeom.get(), i );
1175 const GEOSCoordSequence *lineSequence = GEOSGeom_getCoordSeq_r( context, currentIntersectGeom );
1176 unsigned int sequenceSize = 0;
1178 if ( GEOSCoordSeq_getSize_r( context, lineSequence, &sequenceSize ) != 0 )
1180 for (
unsigned int i = 0; i < sequenceSize; ++i )
1182 if ( GEOSCoordSeq_getXYZ_r( context, lineSequence, i, &x, &y, &z ) )
1184 testPoints.push_back( QgsPoint( x, y, z ) );
1198 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1200 std::unique_ptr< QgsMultiCurve > multiCurve;
1201 if ( type == GEOS_MULTILINESTRING )
1205 else if ( type == GEOS_LINESTRING )
1207 multiCurve = std::make_unique<QgsMultiCurve>();
1208 multiCurve->addGeometry(
mGeometry->clone() );
1223 std::unique_ptr< QgsMultiPoint > splitPoints;
1225 std::unique_ptr< QgsAbstractGeometry > splitGeom(
fromGeos( GEOSsplitPoint ) );
1229 splitPoints.reset( qgis::down_cast<QgsMultiPoint *>( splitGeom.release() ) );
1233 splitPoints = std::make_unique< QgsMultiPoint >();
1236 splitPoints->addGeometry( qgis::down_cast<QgsPoint *>( splitGeom.release() ) );
1244 QgsMultiCurve lines;
1247 for (
int geometryIndex = 0; geometryIndex < multiCurve->numGeometries(); ++geometryIndex )
1260 QMap< int, QVector< QPair< double, QgsPoint > > > pointMap;
1261 for (
int splitPointIndex = 0; splitPointIndex < splitPoints->numGeometries(); ++splitPointIndex )
1263 const QgsPoint *intersectionPoint = splitPoints->pointN( splitPointIndex );
1265 QgsPoint segmentPoint2D;
1266 QgsVertexId nextVertex;
1269 line->
closestSegment( *intersectionPoint, segmentPoint2D, nextVertex );
1285 const QPair< double, QgsPoint > pair = qMakePair(
distance, *correctSegmentPoint.get() );
1286 if ( pointMap.contains( nextVertex.
vertex - 1 ) )
1287 pointMap[nextVertex.
vertex - 1].append( pair );
1289 pointMap[nextVertex.
vertex - 1] = QVector< QPair< double, QgsPoint > >() << pair;
1294 for (
auto &p : pointMap )
1296 std::sort( p.begin(), p.end(), [](
const QPair< double, QgsPoint > &a,
const QPair< double, QgsPoint > &b ) { return a.first < b.first; } );
1300 QgsLineString newLine;
1302 QgsPoint splitPoint;
1303 for (
int vertexIndex = 0; vertexIndex < nVertices; ++vertexIndex )
1305 QgsPoint currentPoint = line->
pointN( vertexIndex );
1307 if ( pointMap.contains( vertexIndex ) )
1310 for (
int k = 0; k < pointMap[vertexIndex].size(); ++k )
1312 splitPoint = pointMap[vertexIndex][k].second;
1313 if ( splitPoint == currentPoint )
1316 newLine = QgsLineString();
1319 else if ( splitPoint == line->
pointN( vertexIndex + 1 ) )
1323 newLine = QgsLineString();
1329 newLine = QgsLineString();
1338 return asGeos( &lines, mPrecision );
1343 Q_UNUSED( skipIntersectionCheck )
1352 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, splitLine, mGeos.get() ) );
1353 if ( !intersectGeom || GEOSisEmpty_r( context, intersectGeom.get() ) )
1357 const int linearIntersect = GEOSRelatePattern_r( context, mGeos.get(), splitLine,
"1********" );
1358 if ( linearIntersect > 0 )
1366 std::vector<geos::unique_ptr> lineGeoms;
1368 const int splitType = GEOSGeomTypeId_r( context, splitGeom.get() );
1369 if ( splitType == GEOS_MULTILINESTRING )
1371 const int nGeoms = GEOSGetNumGeometries_r( context, splitGeom.get() );
1372 lineGeoms.reserve( nGeoms );
1373 for (
int i = 0; i < nGeoms; ++i )
1374 lineGeoms.emplace_back( GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, splitGeom.get(), i ) ) );
1378 lineGeoms.emplace_back( GEOSGeom_clone_r( context, splitGeom.get() ) );
1381 mergeGeometriesMultiTypeSplit( lineGeoms );
1385 newGeometries << QgsGeometry(
fromGeos( lineGeom.get() ) );
1401 if ( !mGeosPrepared )
1407 if ( !skipIntersectionCheck && !GEOSPreparedIntersects_r( context, mGeosPrepared.get(), splitLine ) )
1412 if ( !nodedGeometry )
1421 const int numberOfGeometriesPolygon = numberOfGeometries( polygons.get() );
1422 if ( numberOfGeometriesPolygon == 0 )
1429 std::vector<geos::unique_ptr> testedGeometries;
1434 for (
int i = 0; i < numberOfGeometriesPolygon; i++ )
1436 const GEOSGeometry *polygon = GEOSGetGeometryN_r( context, polygons.get(), i );
1440 testedGeometries.emplace_back( GEOSGeom_clone_r( context, polygon ) );
1443 const size_t nGeometriesThis = numberOfGeometries( mGeos.get() );
1444 if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
1454 mergeGeometriesMultiTypeSplit( testedGeometries );
1457 for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( context, testedGeometries[i].get() ); ++i )
1460 if ( i < testedGeometries.size() )
1467 newGeometries << QgsGeometry(
fromGeos( testedGeometry.get() ) );
1475 if ( !splitLine || !geom )
1480 if ( GEOSGeom_getDimensions_r( context, geom ) == 2 )
1481 geometryBoundary.reset( GEOSBoundary_r( context, geom ) );
1483 geometryBoundary.reset( GEOSGeom_clone_r( context, geom ) );
1486 geos::unique_ptr unionGeometry( GEOSUnion_r( context, splitLineClone.get(), geometryBoundary.get() ) );
1488 return unionGeometry;
1491int QgsGeos::mergeGeometriesMultiTypeSplit( std::vector<geos::unique_ptr> &splitResult )
const
1498 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1499 if ( type != GEOS_GEOMETRYCOLLECTION && type != GEOS_MULTILINESTRING && type != GEOS_MULTIPOLYGON && type != GEOS_MULTIPOINT )
1503 std::vector<geos::unique_ptr> unionGeom;
1505 std::vector<geos::unique_ptr> newSplitResult;
1507 for (
size_t i = 0; i < splitResult.size(); ++i )
1510 bool isPart =
false;
1511 for (
int j = 0; j < GEOSGetNumGeometries_r( context, mGeos.get() ); j++ )
1513 if ( GEOSEquals_r( context, splitResult[i].get(), GEOSGetGeometryN_r( context, mGeos.get(), j ) ) )
1522 unionGeom.emplace_back( std::move( splitResult[i] ) );
1526 std::vector<geos::unique_ptr> geomVector;
1527 geomVector.emplace_back( std::move( splitResult[i] ) );
1529 if ( type == GEOS_MULTILINESTRING )
1530 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, geomVector ) );
1531 else if ( type == GEOS_MULTIPOLYGON )
1532 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, geomVector ) );
1536 splitResult = std::move( newSplitResult );
1539 if ( !unionGeom.empty() )
1541 if ( type == GEOS_MULTILINESTRING )
1542 splitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, unionGeom ) );
1543 else if ( type == GEOS_MULTIPOLYGON )
1544 splitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ) );
1550geos::unique_ptr QgsGeos::createGeosCollection(
int typeId, std::vector<geos::unique_ptr> &geoms )
1552 std::vector<GEOSGeometry *> geomarr;
1553 geomarr.reserve( geoms.size() );
1558 if ( geomUniquePtr )
1560 if ( !GEOSisEmpty_r( context, geomUniquePtr.get() ) )
1564 geomarr.emplace_back( geomUniquePtr.release() );
1572 geomRes.reset( GEOSGeom_createCollection_r( context, typeId, geomarr.data(), geomarr.size() ) );
1574 catch ( QgsGeosException & )
1578 GEOSGeom_destroy_r( context, geom );
1593 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1594 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1595 bool hasZ = ( nCoordDims == 3 );
1596 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1598 switch ( GEOSGeomTypeId_r( context,
geos ) )
1602 if ( GEOSisEmpty_r( context,
geos ) )
1605 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1606 unsigned int nPoints = 0;
1607 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1616 return !point.
isEmpty() ? std::unique_ptr<QgsAbstractGeometry>( point.
clone() ) :
nullptr;
1618 case GEOS_LINESTRING:
1620 return sequenceToLinestring(
geos, hasZ, hasM );
1626 case GEOS_MULTIPOINT:
1628 auto multiPoint = std::make_unique<QgsMultiPoint>();
1629 int nParts = GEOSGetNumGeometries_r( context,
geos );
1630 multiPoint->reserve( nParts );
1631 for (
int i = 0; i < nParts; ++i )
1633 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context, GEOSGetGeometryN_r( context,
geos, i ) );
1636 unsigned int nPoints = 0;
1637 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1639 multiPoint->addGeometry(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1642 return std::move( multiPoint );
1644 case GEOS_MULTILINESTRING:
1646 auto multiLineString = std::make_unique<QgsMultiLineString>();
1647 int nParts = GEOSGetNumGeometries_r( context,
geos );
1648 multiLineString->reserve( nParts );
1649 for (
int i = 0; i < nParts; ++i )
1651 std::unique_ptr< QgsLineString > line( sequenceToLinestring( GEOSGetGeometryN_r( context,
geos, i ), hasZ, hasM ) );
1654 multiLineString->addGeometry( line.release() );
1657 return std::move( multiLineString );
1659 case GEOS_MULTIPOLYGON:
1661 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
1663 int nParts = GEOSGetNumGeometries_r( context,
geos );
1664 multiPolygon->reserve( nParts );
1665 for (
int i = 0; i < nParts; ++i )
1667 std::unique_ptr< QgsPolygon > poly =
fromGeosPolygon( GEOSGetGeometryN_r( context,
geos, i ) );
1670 multiPolygon->addGeometry( poly.release() );
1673 return std::move( multiPolygon );
1675 case GEOS_GEOMETRYCOLLECTION:
1677 auto geomCollection = std::make_unique<QgsGeometryCollection>();
1678 int nParts = GEOSGetNumGeometries_r( context,
geos );
1679 geomCollection->reserve( nParts );
1680 for (
int i = 0; i < nParts; ++i )
1682 std::unique_ptr< QgsAbstractGeometry > geom(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1685 geomCollection->addGeometry( geom.release() );
1688 return std::move( geomCollection );
1697 if ( GEOSGeomTypeId_r( context,
geos ) != GEOS_POLYGON )
1702 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1703 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1704 bool hasZ = ( nCoordDims == 3 );
1705 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1707 auto polygon = std::make_unique<QgsPolygon>();
1712 polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1715 QVector<QgsCurve *> interiorRings;
1716 const int ringCount = GEOSGetNumInteriorRings_r( context,
geos );
1717 interiorRings.reserve( ringCount );
1718 for (
int i = 0; i < ringCount; ++i )
1720 ring = GEOSGetInteriorRingN_r( context,
geos, i );
1723 interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1726 polygon->setInteriorRings( interiorRings );
1731std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring(
const GEOSGeometry *
geos,
bool hasZ,
bool hasM )
1734 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1736 unsigned int nPoints;
1737 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1739 QVector< double > xOut( nPoints );
1740 QVector< double > yOut( nPoints );
1741 QVector< double > zOut;
1743 zOut.resize( nPoints );
1744 QVector< double > mOut;
1746 mOut.resize( nPoints );
1748 double *x = xOut.data();
1749 double *y = yOut.data();
1750 double *z = zOut.data();
1751 double *m = mOut.data();
1753#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
1754 GEOSCoordSeq_copyToArrays_r( context, cs, x, y, hasZ ? z :
nullptr, hasM ? m :
nullptr );
1756 for (
unsigned int i = 0; i < nPoints; ++i )
1759 GEOSCoordSeq_getXYZ_r( context, cs, i, x++, y++, z++ );
1761 GEOSCoordSeq_getXY_r( context, cs, i, x++, y++ );
1764 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, m++ );
1768 auto line = std::make_unique<QgsLineString>( xOut, yOut, zOut, mOut );
1778 int geometryType = GEOSGeomTypeId_r( context, g );
1779 if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING || geometryType == GEOS_POLYGON )
1783 return GEOSGetNumGeometries_r( context, g );
1799 GEOSCoordSeq_getXYZ_r( context, cs, i, &x, &y, &z );
1801 GEOSCoordSeq_getXY_r( context, cs, i, &x, &y );
1804 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, &m );
1840 int geosType = GEOS_GEOMETRYCOLLECTION;
1847 geosType = GEOS_MULTIPOINT;
1851 geosType = GEOS_MULTILINESTRING;
1855 geosType = GEOS_MULTIPOLYGON;
1870 std::vector<geos::unique_ptr> geomVector;
1871 geomVector.reserve(
c->numGeometries() );
1872 for (
int i = 0; i <
c->numGeometries(); ++i )
1879 geomVector.emplace_back( std::move( geosGeom ) );
1881 return createGeosCollection( geosType, geomVector );
1888 if ( !polyhedralSurface )
1891 std::vector<geos::unique_ptr> geomVector;
1892 geomVector.reserve( polyhedralSurface->
numPatches() );
1893 for (
int i = 0; i < polyhedralSurface->
numPatches(); ++i )
1900 geomVector.emplace_back( std::move( geosPolygon ) );
1903 return createGeosCollection( GEOS_MULTIPOLYGON, geomVector );
1910 return createGeosPoint(
static_cast<const QgsPoint *
>( geom ), coordDims, precision, flags );
1913 return createGeosLinestring(
static_cast<const QgsLineString *
>( geom ), precision, flags );
1916 return createGeosPolygon(
static_cast<const QgsPolygon *
>( geom ), precision, flags );
1928 if ( !mGeos || !geom )
1939 QgsScopedGeosContextRegisterFeedback interrupt( feedback );
1941 const double gridSize = parameters.
gridSize();
1949 case OverlayIntersection:
1952 opGeom.reset( GEOSIntersectionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1956 opGeom.reset( GEOSIntersection_r( context, mGeos.get(), geosGeom.get() ) );
1960 case OverlayDifference:
1963 opGeom.reset( GEOSDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1967 opGeom.reset( GEOSDifference_r( context, mGeos.get(), geosGeom.get() ) );
1976 unionGeometry.reset( GEOSUnionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1980 unionGeometry.reset( GEOSUnion_r( context, mGeos.get(), geosGeom.get() ) );
1983 if ( unionGeometry && GEOSGeomTypeId_r( context, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1985 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, unionGeometry.get() ) );
1988 unionGeometry = std::move( mergedLines );
1992 opGeom = std::move( unionGeometry );
1996 case OverlaySymDifference:
1999 opGeom.reset( GEOSSymDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
2003 opGeom.reset( GEOSSymDifference_r( context, mGeos.get(), geosGeom.get() ) );
2009 catch ( QgsGeosException &e )
2014 *errorMsg = e.what();
2020bool QgsGeos::relation(
const QgsAbstractGeometry *geom, Relation r, QString *errorMsg, QgsFeedback *feedback )
const
2022 if ( !mGeos || !geom )
2034 QgsScopedGeosContextRegisterFeedback interrupt( feedback );
2036 bool result =
false;
2039 if ( mGeosPrepared )
2043 case RelationIntersects:
2044 result = ( GEOSPreparedIntersects_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2046 case RelationTouches:
2047 result = ( GEOSPreparedTouches_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2049 case RelationCrosses:
2050 result = ( GEOSPreparedCrosses_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2052 case RelationWithin:
2053 result = ( GEOSPreparedWithin_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2055 case RelationContains:
2056 result = ( GEOSPreparedContains_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2058 case RelationDisjoint:
2059 result = ( GEOSPreparedDisjoint_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2061 case RelationOverlaps:
2062 result = ( GEOSPreparedOverlaps_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2070 case RelationIntersects:
2071 result = ( GEOSIntersects_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2073 case RelationTouches:
2074 result = ( GEOSTouches_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2076 case RelationCrosses:
2077 result = ( GEOSCrosses_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2079 case RelationWithin:
2080 result = ( GEOSWithin_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2082 case RelationContains:
2083 result = ( GEOSContains_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2085 case RelationDisjoint:
2086 result = ( GEOSDisjoint_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2088 case RelationOverlaps:
2089 result = ( GEOSOverlaps_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2093 catch ( QgsGeosException &e )
2098 *errorMsg = e.what();
2143 geos.reset( GEOSBufferWithStyle_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( endCapStyle ),
static_cast< int >( joinStyle ), miterLimit ) );
2196 geos.reset( GEOSGetCentroid_r( context, mGeos.get() ) );
2201 GEOSGeomGetX_r( context,
geos.get(), &x );
2202 GEOSGeomGetY_r( context,
geos.get(), &y );
2239 geos.reset( GEOSPointOnSurface_r( context, mGeos.get() ) );
2241 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
2246 GEOSGeomGetX_r( context,
geos.get(), &x );
2247 GEOSGeomGetY_r( context,
geos.get(), &y );
2265 std::unique_ptr< QgsAbstractGeometry > cHullGeom =
fromGeos( cHull.get() );
2266 return cHullGeom.release();
2273#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
2274 ( void ) allowHoles;
2275 ( void ) targetPercent;
2277 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHull requires a QGIS build based on GEOS 3.11 or later" ) );
2289 return concaveHullGeom;
2297#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
2298 ( void ) allowHoles;
2299 ( void ) targetPercent;
2301 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHullOfPolygons requires a QGIS build based on GEOS 3.11 or later" ) );
2313 return concaveHullGeom;
2321#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12
2323 ( void ) invalidEdges;
2325 throw QgsNotSupportedException( QObject::tr(
"Validating coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2330 *errorMsg = u
"Input geometry was not set"_s;
2339 const int result = GEOSCoverageIsValid_r( context, mGeos.get(), gapWidth, invalidEdges ? &invalidEdgesGeos :
nullptr );
2340 if ( invalidEdges && invalidEdgesGeos )
2342 *invalidEdges =
fromGeos( invalidEdgesGeos );
2344 if ( invalidEdgesGeos )
2346 GEOSGeom_destroy_r( context, invalidEdgesGeos );
2347 invalidEdgesGeos =
nullptr;
2367#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12
2369 ( void ) preserveBoundary;
2371 throw QgsNotSupportedException( QObject::tr(
"Simplifying coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2376 *errorMsg = u
"Input geometry was not set"_s;
2384 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom =
fromGeos( simplified.get() );
2385 return simplifiedGeom;
2396 *errorMsg = u
"Input geometry was not set"_s;
2404 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( unioned.get() );
2412#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 14
2413 ( void ) parameters;
2416 throw QgsNotSupportedException( QObject::tr(
"Cleaning coverages requires a QGIS build based on GEOS 3.14 or later" ) );
2421 *errorMsg = u
"Input geometry was not set"_s;
2425 GEOSCoverageCleanParams *params =
nullptr;
2454 std::unique_ptr< QgsAbstractGeometry> cleanedGeom =
fromGeos( cleaned.get() );
2458 catch ( QgsGeosException &e )
2468 *errorMsg = e.what();
2469 if ( errorMsg->startsWith(
"InterruptedException"_L1, Qt::CaseInsensitive ) )
2484 *errorMsg = QObject::tr(
"QGIS geometry cannot be converted to a GEOS geometry",
"GEOS Error" );
2495 char res = GEOSisValidDetail_r( context, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
2496 const bool invalid = res != 1;
2501 error = QString( r );
2502 GEOSFree_r( context, r );
2505 if ( invalid && errorMsg )
2508 static const std::map< QString, QString > sTranslatedErrors {
2509 { u
"topology validation error"_s, QObject::tr(
"Topology validation error",
"GEOS Error" ) },
2510 { u
"repeated point"_s, QObject::tr(
"Repeated point",
"GEOS Error" ) },
2511 { u
"hole lies outside shell"_s, QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) },
2512 { u
"holes are nested"_s, QObject::tr(
"Holes are nested",
"GEOS Error" ) },
2513 { u
"interior is disconnected"_s, QObject::tr(
"Interior is disconnected",
"GEOS Error" ) },
2514 { u
"self-intersection"_s, QObject::tr(
"Self-intersection",
"GEOS Error" ) },
2515 { u
"ring self-intersection"_s, QObject::tr(
"Ring self-intersection",
"GEOS Error" ) },
2516 { u
"nested shells"_s, QObject::tr(
"Nested shells",
"GEOS Error" ) },
2517 { u
"duplicate rings"_s, QObject::tr(
"Duplicate rings",
"GEOS Error" ) },
2518 { u
"too few points in geometry component"_s, QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) },
2519 { u
"invalid coordinate"_s, QObject::tr(
"Invalid coordinate",
"GEOS Error" ) },
2520 { u
"ring is not closed"_s, QObject::tr(
"Ring is not closed",
"GEOS Error" ) },
2523 const auto translatedError = sTranslatedErrors.find( error.toLower() );
2524 if ( translatedError != sTranslatedErrors.end() )
2525 *errorMsg = translatedError->second;
2529 if ( g1 && errorLoc )
2535 GEOSGeom_destroy_r( context, g1 );
2545 if ( !mGeos || !geom )
2566 if ( !mGeos || !geom )
2580 bool equal = GEOSEqualsExact_r(
QgsGeosContext::get(), mGeos.get(), geosGeom.get(), epsilon );
2614GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
2618 std::unique_ptr< QgsLineString > segmentized;
2624 line = segmentized.get();
2631 GEOSCoordSequence *coordSeq =
nullptr;
2633 const int numPoints = line->
numPoints();
2635 const bool hasZ = line->
is3D();
2637#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
2640 if ( !forceClose || ( line->
pointN( 0 ) == line->
pointN( numPoints - 1 ) ) )
2645 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, line->
xData(), line->
yData(), line->
zData(),
nullptr, numPoints );
2648 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points"_s.arg( numPoints ) );
2656 QVector< double > x = line->
xVector();
2657 if ( numPoints > 0 )
2658 x.append( x.at( 0 ) );
2659 QVector< double > y = line->
yVector();
2660 if ( numPoints > 0 )
2661 y.append( y.at( 0 ) );
2662 QVector< double > z = line->
zVector();
2663 if ( hasZ && numPoints > 0 )
2664 z.append( z.at( 0 ) );
2667 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, x.constData(), y.constData(), !hasZ ?
nullptr : z.constData(),
nullptr, numPoints + 1 );
2670 QgsDebugError( u
"GEOS Exception: Could not create closed coordinate sequence for %1 points"_s.arg( numPoints + 1 ) );
2681 const bool hasM =
false;
2692 int numOutPoints = numPoints;
2693 if ( forceClose && ( line->
pointN( 0 ) != line->
pointN( numPoints - 1 ) ) )
2700 coordSeq = GEOSCoordSeq_create_r( context, numOutPoints, coordDims );
2703 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions"_s.arg( numPoints ).arg( coordDims ) );
2707 const double *xData = line->
xData();
2708 const double *yData = line->
yData();
2709 const double *zData = hasZ ? line->
zData() :
nullptr;
2710 const double *mData = hasM ? line->
mData() :
nullptr;
2712 if ( precision > 0. )
2714 for (
int i = 0; i < numOutPoints; ++i )
2716 if ( i >= numPoints )
2719 xData = line->
xData();
2720 yData = line->
yData();
2721 zData = hasZ ? line->
zData() :
nullptr;
2722 mData = hasM ? line->
mData() :
nullptr;
2726 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
2730 GEOSCoordSeq_setXY_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
2734 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2740 for (
int i = 0; i < numOutPoints; ++i )
2742 if ( i >= numPoints )
2745 xData = line->
xData();
2746 yData = line->
yData();
2747 zData = hasZ ? line->
zData() :
nullptr;
2748 mData = hasM ? line->
mData() :
nullptr;
2752 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, *xData++, *yData++, *zData++ );
2756 GEOSCoordSeq_setXY_r( context, coordSeq, i, *xData++, *yData++ );
2760 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2770geos::unique_ptr
QgsGeos::createGeosPoint( const QgsAbstractGeometry *point,
int coordDims,
double precision, Qgis::GeosCreationFlags )
2776 return createGeosPointXY( pt->
x(), pt->
y(), pt->
is3D(), pt->
z(), pt->
isMeasure(), pt->
m(), coordDims, precision );
2788 if ( coordDims == 2 )
2791 if ( precision > 0. )
2792 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
2794 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, x, y ) );
2798 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( context, 1, coordDims );
2801 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions"_s.arg( coordDims ) );
2804 if ( precision > 0. )
2806 GEOSCoordSeq_setX_r( context, coordSeq, 0, std::round( x / precision ) * precision );
2807 GEOSCoordSeq_setY_r( context, coordSeq, 0, std::round( y / precision ) * precision );
2810 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, std::round( z / precision ) * precision );
2815 GEOSCoordSeq_setX_r( context, coordSeq, 0, x );
2816 GEOSCoordSeq_setY_r( context, coordSeq, 0, y );
2819 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, z );
2825 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 3, m );
2828 geosPoint.reset( GEOSGeom_createPoint_r( context, coordSeq ) );
2834geos::unique_ptr
QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve,
double precision, Qgis::GeosCreationFlags )
2840 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c, precision );
2853geos::unique_ptr
QgsGeos::createGeosPolygon( const QgsAbstractGeometry *poly,
double precision, Qgis::GeosCreationFlags flags )
2859 const QgsCurve *exteriorRing = polygon->
exteriorRing();
2860 if ( !exteriorRing )
2869 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( context, createCoordinateSequence( exteriorRing, precision,
true ) ) );
2872 QList< const QgsCurve * > holesToExport;
2873 holesToExport.reserve( nInteriorRings );
2874 for (
int i = 0; i < nInteriorRings; ++i )
2876 const QgsCurve *interiorRing = polygon->
interiorRing( i );
2879 holesToExport << interiorRing;
2884 if ( !holesToExport.empty() )
2887 for (
int i = 0; i < holesToExport.size(); ++i )
2889 holes[i] = GEOSGeom_createLinearRing_r( context, createCoordinateSequence( holesToExport[i], precision,
true ) );
2893 geosPolygon.reset( GEOSGeom_createPolygon_r( context, exteriorRingGeos.release(), holes, holesToExport.size() ) );
2914 offset.reset( GEOSOffsetCurve_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( joinStyle ), miterLimit ) );
2926 return fromGeos( res.get() ).release();
2944 GEOSBufferParams_setSingleSided_r( context, bp.get(), 1 );
2945 GEOSBufferParams_setQuadrantSegments_r( context, bp.get(), segments );
2946 GEOSBufferParams_setJoinStyle_r( context, bp.get(),
static_cast< int >( joinStyle ) );
2947 GEOSBufferParams_setMitreLimit_r( context, bp.get(), miterLimit );
2953 geos.reset( GEOSBufferWithParams_r( context, mGeos.get(), bp.get(),
distance ) );
2988 boundaryGeos =
asGeos( boundary );
3018 return std::numeric_limits< double >::quiet_NaN();
3027 return std::numeric_limits< double >::quiet_NaN();
3069 if ( !mGeos || !other )
3090 if ( !mGeos ||
mGeometry->dimension() == 0 )
3108 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
3112 int numGeoms = GEOSGetNumGeometries_r( context, mGeos.get() );
3113 if ( numGeoms == -1 )
3122 bool isMultiGeom =
false;
3123 int geosTypeId = GEOSGeomTypeId_r( context, mGeos.get() );
3124 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
3127 bool isLine = (
mGeometry->dimension() == 1 );
3134 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
3138 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
3143 if ( reshapedGeometry )
3149 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
3150 return reshapeResult;
3157 bool reshapeTookPlace =
false;
3162 for (
int i = 0; i < numGeoms; ++i )
3165 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3167 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3169 if ( currentReshapeGeometry )
3171 newGeoms[i] = currentReshapeGeometry.release();
3172 reshapeTookPlace =
true;
3176 newGeoms[i] = GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, mGeos.get(), i ) );
3183 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
3187 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
3191 if ( !newMultiGeom )
3200 if ( reshapeTookPlace )
3204 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
3205 return reshapedMultiGeom;
3228 if ( GEOSGeomTypeId_r( context, mGeos.get() ) != GEOS_MULTILINESTRING )
3235 double gridSize = parameters.
gridSize();
3238 geos::unique_ptr geosFixedSize( GEOSGeom_setPrecision_r( context, mGeos.get(), gridSize, 0 ) );
3239 geos.reset( GEOSLineMerge_r( context, geosFixedSize.get() ) );
3242 geos.reset( GEOSLineMerge_r( context, mGeos.get() ) );
3268 if ( mGeosPrepared )
3270 nearestCoord.reset( GEOSPreparedNearestPoints_r( context, mGeosPrepared.get(), otherGeom.get() ) );
3274 nearestCoord.reset( GEOSNearestPoints_r( context, mGeos.get(), otherGeom.get() ) );
3277 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx );
3278 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny );
3280 catch ( QgsGeosException &e )
3285 *errorMsg = e.what();
3290 return std::make_unique< QgsPoint >( nx, ny );
3295 if ( !mGeos || other.
isEmpty() )
3305 if ( !other || other->
isEmpty() )
3324 if ( !nearestCoord )
3327 *errorMsg = u
"GEOS returned no nearest points"_s;
3331 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx1 );
3332 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny1 );
3333 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 1, &nx2 );
3334 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 1, &ny2 );
3336 catch ( QgsGeosException &e )
3341 *errorMsg = e.what();
3346 auto line = std::make_unique< QgsLineString >();
3371 catch ( QgsGeosException &e )
3376 *errorMsg = e.what();
3391 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
3401 catch ( QgsGeosException &e )
3406 *errorMsg = e.what();
3423 lineGeosGeometries[validLines] = l.release();
3432 geos::unique_ptr result( GEOSPolygonize_r( context, lineGeosGeometries, validLines ) );
3433 for (
int i = 0; i < validLines; ++i )
3435 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3437 delete[] lineGeosGeometries;
3440 catch ( QgsGeosException &e )
3444 *errorMsg = e.what();
3446 for (
int i = 0; i < validLines; ++i )
3448 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3450 delete[] lineGeosGeometries;
3465 extentGeosGeom =
asGeos( extent, mPrecision );
3466 if ( !extentGeosGeom )
3477 geos.reset( GEOSVoronoiDiagram_r( context, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
3479 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3501 geos.reset( GEOSDelaunayTriangulation_r( context, mGeos.get(), tolerance, edgesOnly ) );
3503 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3515#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
3517 throw QgsNotSupportedException( QObject::tr(
"Calculating constrainedDelaunayTriangulation requires a QGIS build based on GEOS 3.11 or later" ) );
3529 geos.reset( GEOSConstrainedDelaunayTriangulation_r( context, mGeos.get() ) );
3531 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3536 std::unique_ptr< QgsAbstractGeometry > res =
fromGeos(
geos.get() );
3539 return std::unique_ptr< QgsAbstractGeometry >( collection->extractPartsByType(
Qgis::WkbType::Polygon,
true ) );
3551static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
3554 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( context, linestring );
3558 unsigned int coordSeqSize;
3559 if ( GEOSCoordSeq_getSize_r( context, coordSeq, &coordSeqSize ) == 0 )
3562 if ( coordSeqSize < 2 )
3565 GEOSCoordSeq_getX_r( context, coordSeq, 0, &x1 );
3566 GEOSCoordSeq_getY_r( context, coordSeq, 0, &y1 );
3567 GEOSCoordSeq_getX_r( context, coordSeq, coordSeqSize - 1, &x2 );
3568 GEOSCoordSeq_getY_r( context, coordSeq, coordSeqSize - 1, &y2 );
3576 double x1, y1, x2, y2;
3577 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
3580 double rx1, ry1, rx2, ry2;
3581 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
3584 bool intersectionAtOrigLineEndpoint = ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) != ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
3585 bool intersectionAtReshapeLineEndpoint = ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) || ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
3589 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
3593 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
3594 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, geoms, 2 ) );
3599 double x1res, y1res, x2res, y2res;
3600 if ( !_linestringEndpoints( res.get(), x1res, y1res, x2res, y2res ) )
3602 if ( ( x1res == x2 && y1res == y2 ) || ( x2res == x1 && y2res == y1 ) )
3603 res.reset( GEOSReverse_r( context, res.get() ) );
3614 if ( !line || !reshapeLineGeos )
3617 bool atLeastTwoIntersections =
false;
3618 bool oneIntersection =
false;
3619 QgsPointXY oneIntersectionPoint;
3625 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, line, reshapeLineGeos ) );
3626 if ( intersectGeom )
3628 const int geomType = GEOSGeomTypeId_r( context, intersectGeom.get() );
3629 atLeastTwoIntersections = ( geomType == GEOS_MULTIPOINT && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 1 )
3630 || ( geomType == GEOS_GEOMETRYCOLLECTION && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 )
3631 || ( geomType == GEOS_MULTILINESTRING && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 );
3633 if ( GEOSGeomTypeId_r( context, intersectGeom.get() ) == GEOS_POINT )
3635 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( context, intersectGeom.get() );
3637 GEOSCoordSeq_getX_r( context, intersectionCoordSeq, 0, &xi );
3638 GEOSCoordSeq_getY_r( context, intersectionCoordSeq, 0, &yi );
3639 oneIntersection =
true;
3640 oneIntersectionPoint = QgsPointXY( xi, yi );
3644 catch ( QgsGeosException & )
3646 atLeastTwoIntersections =
false;
3650 if ( oneIntersection )
3651 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
3653 if ( !atLeastTwoIntersections )
3657 double x1, y1, x2, y2;
3658 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
3661 geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1,
false, 0,
false, 0, 2, precision );
3662 geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2,
false, 0,
false, 0, 2, precision );
3664 bool isRing =
false;
3665 if ( GEOSGeomTypeId_r( context, line ) == GEOS_LINEARRING || GEOSEquals_r( context, beginLineVertex.get(), endLineVertex.get() ) == 1 )
3670 if ( !nodedGeometry )
3676 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, nodedGeometry.get() ) );
3682 int numMergedLines = GEOSGetNumGeometries_r( context, mergedLines.get() );
3683 if ( numMergedLines < 2 )
3685 if ( numMergedLines == 1 )
3694 QVector<GEOSGeometry *> resultLineParts;
3695 QVector<GEOSGeometry *> probableParts;
3697 for (
int i = 0; i < numMergedLines; ++i )
3699 const GEOSGeometry *currentGeom = GEOSGetGeometryN_r( context, mergedLines.get(), i );
3702 bool alreadyAdded =
false;
3704 double bufferDistance = std::pow( 10.0L, geomDigits( currentGeom ) - 11 );
3705 for (
const GEOSGeometry *other : std::as_const( resultLineParts ) )
3707 GEOSHausdorffDistance_r( context, currentGeom, other, &
distance );
3710 alreadyAdded =
true;
3717 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( context, currentGeom );
3718 unsigned int currentCoordSeqSize;
3719 GEOSCoordSeq_getSize_r( context, currentCoordSeq, ¤tCoordSeqSize );
3720 if ( currentCoordSeqSize < 2 )
3724 double xBegin, xEnd, yBegin, yEnd;
3725 GEOSCoordSeq_getX_r( context, currentCoordSeq, 0, &xBegin );
3726 GEOSCoordSeq_getY_r( context, currentCoordSeq, 0, &yBegin );
3727 GEOSCoordSeq_getX_r( context, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
3728 GEOSCoordSeq_getY_r( context, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
3729 geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin,
false, 0,
false, 0, 2, precision );
3730 geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd,
false, 0,
false, 0, 2, precision );
3733 int nEndpointsOnOriginalLine = 0;
3734 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
3735 nEndpointsOnOriginalLine += 1;
3737 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
3738 nEndpointsOnOriginalLine += 1;
3741 int nEndpointsSameAsOriginalLine = 0;
3742 if ( GEOSEquals_r( context, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1 || GEOSEquals_r( context, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3743 nEndpointsSameAsOriginalLine += 1;
3745 if ( GEOSEquals_r( context, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1 || GEOSEquals_r( context, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3746 nEndpointsSameAsOriginalLine += 1;
3749 bool currentGeomOverlapsOriginalGeom =
false;
3750 bool currentGeomOverlapsReshapeLine =
false;
3751 if ( lineContainedInLine( currentGeom, line ) == 1 )
3752 currentGeomOverlapsOriginalGeom =
true;
3754 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
3755 currentGeomOverlapsReshapeLine =
true;
3758 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3760 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3763 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3765 probableParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3767 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3769 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3771 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3773 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3775 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
3777 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3782 if ( isRing && !probableParts.isEmpty() )
3786 double maxLength = -std::numeric_limits<double>::max();
3787 double currentLength = 0;
3788 for (
int i = 0; i < probableParts.size(); ++i )
3790 currentGeom = probableParts.at( i );
3791 GEOSLength_r( context, currentGeom, ¤tLength );
3792 if ( currentLength > maxLength )
3794 maxLength = currentLength;
3795 maxGeom.reset( currentGeom );
3799 GEOSGeom_destroy_r( context, currentGeom );
3802 resultLineParts.push_back( maxGeom.release() );
3806 if ( resultLineParts.empty() )
3809 if ( resultLineParts.size() == 1 )
3811 result.reset( resultLineParts[0] );
3816 for (
int i = 0; i < resultLineParts.size(); ++i )
3818 lineArray[i] = resultLineParts[i];
3822 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
3826 result.reset( GEOSLineMerge_r( context, multiLineGeom.get() ) );
3830 if ( GEOSGeomTypeId_r( context, result.get() ) != GEOS_LINESTRING )
3836 bool reverseLine =
false;
3840 char isResultCCW = 0, isOriginCCW = 0;
3841 if ( GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, result.get() ), &isResultCCW ) && GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, line ), &isOriginCCW ) )
3844 reverseLine = ( isOriginCCW == 1 && isResultCCW == 0 ) || ( isOriginCCW == 0 && isResultCCW == 1 );
3850 double x1res, y1res, x2res, y2res;
3851 if ( !_linestringEndpoints( result.get(), x1res, y1res, x2res, y2res ) )
3853 geos::unique_ptr beginResultLineVertex = createGeosPointXY( x1res, y1res,
false, 0,
false, 0, 2, precision );
3854 geos::unique_ptr endResultLineVertex = createGeosPointXY( x2res, y2res,
false, 0,
false, 0, 2, precision );
3855 reverseLine = GEOSEquals_r( context, beginLineVertex.get(), endResultLineVertex.get() ) == 1 || GEOSEquals_r( context, endLineVertex.get(), beginResultLineVertex.get() ) == 1;
3858 result.reset( GEOSReverse_r( context, result.get() ) );
3866 int nIntersections = 0;
3867 int lastIntersectingRing = -2;
3871 int nRings = GEOSGetNumInteriorRings_r( context, polygon );
3876 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( context, polygon );
3877 if ( GEOSIntersects_r( context, outerRing, reshapeLineGeos ) == 1 )
3880 lastIntersectingRing = -1;
3881 lastIntersectingGeom = outerRing;
3889 for (
int i = 0; i < nRings; ++i )
3891 innerRings[i] = GEOSGetInteriorRingN_r( context, polygon, i );
3892 if ( GEOSIntersects_r( context, innerRings[i], reshapeLineGeos ) == 1 )
3895 lastIntersectingRing = i;
3896 lastIntersectingGeom = innerRings[i];
3900 catch ( QgsGeosException & )
3905 if ( nIntersections != 1 )
3907 delete[] innerRings;
3912 geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
3913 if ( !reshapeResult )
3915 delete[] innerRings;
3921 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( context, reshapeResult.get() );
3922 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( context, reshapeSequence );
3924 reshapeResult.reset();
3928 newRing = GEOSGeom_createLinearRing_r( context, newCoordSequence );
3930 catch ( QgsGeosException & )
3937 delete[] innerRings;
3942 if ( lastIntersectingRing == -1 )
3943 newOuterRing = newRing;
3945 newOuterRing = GEOSGeom_clone_r( context, outerRing );
3948 QVector<GEOSGeometry *> ringList;
3951 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( context, GEOSGeom_clone_r( context, newOuterRing ),
nullptr, 0 );
3952 if ( outerRingPoly )
3954 ringList.reserve( nRings );
3956 for (
int i = 0; i < nRings; ++i )
3958 if ( lastIntersectingRing == i )
3959 currentRing = newRing;
3961 currentRing = GEOSGeom_clone_r( context, innerRings[i] );
3964 if ( GEOSContains_r( context, outerRingPoly, currentRing ) == 1 )
3965 ringList.push_back( currentRing );
3967 GEOSGeom_destroy_r( context, currentRing );
3970 GEOSGeom_destroy_r( context, outerRingPoly );
3974 for (
int i = 0; i < ringList.size(); ++i )
3975 newInnerRings[i] = ringList.at( i );
3977 delete[] innerRings;
3979 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( context, newOuterRing, newInnerRings, ringList.size() ) );
3980 delete[] newInnerRings;
3982 return reshapedPolygon;
3987 if ( !line1 || !line2 )
3992 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
3999 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, bufferGeom.get(), line1 ) );
4002 double intersectGeomLength;
4005 GEOSLength_r( context, intersectionGeom.get(), &intersectGeomLength );
4006 GEOSLength_r( context, line1, &line1Length );
4008 double intersectRatio = line1Length / intersectGeomLength;
4009 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
4017 if ( !point || !line )
4020 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
4023 geos::unique_ptr lineBuffer( GEOSBuffer_r( context, line, bufferDistance, 8 ) );
4027 bool contained =
false;
4028 if ( GEOSContains_r( context, lineBuffer.get(), point ) == 1 )
4041 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( context, bbox.get() );
4045 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( context, bBoxRing );
4047 if ( !bBoxCoordSeq )
4050 unsigned int nCoords = 0;
4051 if ( !GEOSCoordSeq_getSize_r( context, bBoxCoordSeq, &nCoords ) )
4055 for (
unsigned int i = 0; i < nCoords - 1; ++i )
4058 GEOSCoordSeq_getX_r( context, bBoxCoordSeq, i, &t );
4061 digits = std::ceil( std::log10( std::fabs( t ) ) );
4062 if ( digits > maxDigits )
4065 GEOSCoordSeq_getY_r( context, bBoxCoordSeq, i, &t );
4066 digits = std::ceil( std::log10( std::fabs( t ) ) );
4067 if ( digits > maxDigits )
4075#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4076 : mFeedback( feedback )
4078 GEOSContext_setInterruptCallback_r(
QgsGeosContext::get(), &callback,
reinterpret_cast< void *
>( mFeedback ) );
4089#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4094#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4095int QgsScopedGeosContextRegisterFeedback::callback(
void *userData )
4101 return feedback && feedback->
isCanceled() ? 1 : 0;
Provides global constants and enumerations for use throughout the application.
BufferSide
Side of line to buffer.
@ Right
Buffer to right of line.
@ LongestBorder
Polygon with longest common border is selected to merge overlapping polygons into.
@ MaximumArea
Polygon with largest area is selected to merge overlapping polygons into.
@ MinimumArea
Polygon with minimum area is selected to merge overlapping polygons into.
@ MinimumIndex
Polygon with smallest input index is selected to merge overlapping polygons into.
GeometryOperationResult
Success or failure of a geometry operation.
@ AddPartNotMultiGeometry
The source geometry is not multi.
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
@ RejectOnInvalidSubGeometry
Don't allow geometries with invalid sub-geometries to be created.
@ SkipEmptyInteriorRings
Skip any empty polygon interior ring.
QFlags< GeosCreationFlag > GeosCreationFlags
Geos geometry creation behavior flags.
JoinStyle
Join styles for buffers.
EndCapStyle
End cap styles for buffers.
CoverageValidityResult
Coverage validity results.
@ Valid
Coverage is valid.
@ Invalid
Coverage is invalid. Invalidity includes polygons that overlap, that have gaps smaller than the gap w...
@ Error
An exception occurred while determining validity.
MakeValidMethod
Algorithms to use when repairing invalid geometries.
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
WkbType
The WKB type describes the number of dimensions a geometry has.
@ 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.
Encapsulates parameters for a coverage cleaning operation.
double maximumGapWidth() const
Returns the maximum gap width.
Qgis::CoverageCleanOverlapMergeStrategy overlapMergeStrategy() const
Returns the overlap merge strategy to use during cleaning.
double snappingDistance() const
Returns the snapping distance.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
Abstract base class for curved geometry type.
virtual QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
bool isCanceled() const
Tells whether the operation has been canceled already.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
static Qgis::GeometryOperationResult addPart(QgsAbstractGeometry *geometry, std::unique_ptr< QgsAbstractGeometry > part)
Add a part to multi type geometry.
const QgsAbstractGeometry * mGeometry
EngineOperationResult
Success or failure of a geometry operation.
@ NothingHappened
Nothing happened, without any error.
@ InvalidBaseGeometry
The geometry on which the operation occurs is not valid.
@ InvalidInput
The input is not valid.
@ NodedGeometryError
Error occurred while creating a noded geometry.
@ EngineError
Error occurred in the geometry engine.
@ SplitCannotSplitPoint
Points cannot be split.
@ Success
Operation succeeded.
QgsGeometryEngine(const QgsAbstractGeometry *geometry)
void logError(const QString &engineName, const QString &message) const
Logs an error message encountered during an operation.
static std::unique_ptr< QgsGeometryCollection > createCollectionOfType(Qgis::WkbType type)
Returns a new geometry collection matching a specified WKB type.
Encapsulates parameters under which a geometry operation is performed.
double gridSize() const
Returns the grid size which will be used to snap vertices of a geometry.
static double sqrDistance2D(double x1, double y1, double x2, double y2)
Returns the squared 2D distance between (x1, y1) and (x2, y2).
A geometry is the spatial representation of a feature.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
Used to create and store a proj context object, correctly freeing the context upon destruction.
static GEOSContextHandle_t get()
Returns a thread local instance of a GEOS context, safe for use in the current thread.
std::unique_ptr< QgsAbstractGeometry > singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a single sided buffer for a geometry.
double minimumClearance(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Computes the minimum clearance of a geometry.
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom intersects this.
bool distanceWithin(const QgsAbstractGeometry *geom, double maxdistance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom is within maxdistance distance from this geometry.
std::unique_ptr< QgsAbstractGeometry > concaveHull(double targetPercent, bool allowHoles=false, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a possibly concave geometry that encloses the input geometry.
std::unique_ptr< QgsAbstractGeometry > reshapeGeometry(const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg=nullptr) const
Reshapes the geometry using a line.
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Calculates the distance between this and geom.
std::unique_ptr< QgsAbstractGeometry > sharedPaths(const QgsAbstractGeometry *other, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Find paths shared between the two given lineal geometries (this and other).
std::unique_ptr< QgsAbstractGeometry > minimumClearanceLine(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a LineString whose endpoints define the minimum clearance of a geometry.
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlags())
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
QgsAbstractGeometry * symDifference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const override
Calculate the symmetric difference of this and geom.
EngineOperationResult splitGeometry(const QgsLineString &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, QString *errorMsg=nullptr, bool skipIntersectionCheck=false) const override
Splits this geometry according to a given line.
std::unique_ptr< QgsAbstractGeometry > closestPoint(const QgsGeometry &other, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the closest point on the geometry to the other geometry.
std::unique_ptr< QgsAbstractGeometry > unionCoverage(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Optimized union algorithm for polygonal inputs that are correctly noded and do not overlap.
bool isFuzzyEqual(const QgsAbstractGeometry *geom, double epsilon, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if this is equal to geom ie.
QgsAbstractGeometry * simplify(double tolerance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Simplifies the geometery.
static geos::unique_ptr offsetCurve(const GEOSGeometry *geometry, double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr)
Directly calculates the offset curve for a GEOS geometry object and returns a GEOS geometry result.
std::unique_ptr< QgsAbstractGeometry > cleanCoverage(const QgsCoverageCleanParameters ¶meters, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Operates on a coverage (represented as a list of polygonal geometry), to fix cases where the geometry...
QgsAbstractGeometry * intersection(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const override
Calculate the intersection of this and geom.
double lineLocatePoint(const QgsPoint &point, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a distance representing the location along this linestring of the closest point on this lines...
std::unique_ptr< QgsAbstractGeometry > subdivide(int maxNodes, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Subdivides the geometry.
bool touches(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom touches this.
static std::unique_ptr< QgsPolygon > fromGeosPolygon(const GEOSGeometry *geos)
std::unique_ptr< QgsAbstractGeometry > largestEmptyCircle(double tolerance, const QgsAbstractGeometry *boundary=nullptr, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Constructs the Largest Empty Circle for a set of obstacle geometries, up to a specified tolerance.
QgsAbstractGeometry * envelope(QString *errorMsg=nullptr) const override
Qgis::CoverageValidityResult validateCoverage(double gapWidth, std::unique_ptr< QgsAbstractGeometry > *invalidEdges, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Analyze a coverage (represented as a collection of polygonal geometry with exactly matching edge geom...
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Buffers the geometry.
QString relate(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Returns the Dimensional Extended 9 Intersection Model (DE-9IM) representation of the relationship bet...
std::unique_ptr< QgsAbstractGeometry > constrainedDelaunayTriangulation(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a constrained Delaunay triangulation for the vertices of the geometry.
bool within(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom is within this.
bool contains(double x, double y, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns true if the geometry contains the point at (x, y).
bool isSimple(QString *errorMsg=nullptr) const override
Determines whether the geometry is simple (according to OGC definition).
bool isValid(QString *errorMsg=nullptr, bool allowSelfTouchingHoles=false, QgsGeometry *errorLoc=nullptr, QgsFeedback *feedback=nullptr) const override
Returns true if the geometry is valid.
std::unique_ptr< QgsAbstractGeometry > minimumWidth(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a linestring geometry which represents the minimum diameter of the geometry.
std::unique_ptr< QgsAbstractGeometry > simplifyCoverageVW(double tolerance, bool preserveBoundary, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Operates on a coverage (represented as a list of polygonal geometry with exactly matching edge geomet...
QgsGeos(const QgsAbstractGeometry *geometry, double precision=0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
GEOS geometry engine constructor.
std::unique_ptr< QgsAbstractGeometry > shortestLine(const QgsGeometry &other, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the shortest line joining this geometry to the other geometry.
QgsAbstractGeometry * convexHull(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Calculate the convex hull of this geometry.
void prepareGeometry() override
Prepares the geometry, so that subsequent calls to spatial relation methods are much faster.
std::unique_ptr< QgsAbstractGeometry > makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Repairs the geometry using GEOS make valid routine.
QgsPoint * pointOnSurface(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Calculate a point that is guaranteed to be on the surface of this.
static std::unique_ptr< QgsAbstractGeometry > fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
std::unique_ptr< QgsAbstractGeometry > node(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns a (Multi)LineString representing the fully noded version of a collection of linestrings.
QgsAbstractGeometry * combine(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const override
Calculate the combination of this and geom.
bool disjoint(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom is disjoint from this.
bool relatePattern(const QgsAbstractGeometry *geom, const QString &pattern, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Tests whether two geometries are related by a specified Dimensional Extended 9 Intersection Model (DE...
double hausdorffDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
bool isEqual(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Check if geometries are topologically equivalent.
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the maximum inscribed circle.
std::unique_ptr< QgsAbstractGeometry > voronoiDiagram(const QgsAbstractGeometry *extent=nullptr, double tolerance=0.0, bool edgesOnly=false, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Creates a Voronoi diagram for the nodes contained within the geometry.
double frechetDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
std::unique_ptr< QgsAbstractGeometry > delaunayTriangulation(double tolerance=0.0, bool edgesOnly=false, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Delaunay triangulation for the vertices of the geometry.
bool crosses(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom crosses this.
std::unique_ptr< QgsAbstractGeometry > concaveHullOfPolygons(double lengthRatio, bool allowHoles=false, bool isTight=false, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Constructs a concave hull of a set of polygons, respecting the polygons as constraints.
std::unique_ptr< QgsAbstractGeometry > clip(const QgsRectangle &rectangle, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Performs a fast, non-robust intersection between the geometry and a rectangle.
bool isEmpty(QString *errorMsg=nullptr) const override
static Qgis::GeometryOperationResult addPart(QgsGeometry &geometry, GEOSGeometry *newPart)
Adds a new island polygon to a multipolygon feature.
bool overlaps(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom overlaps this.
QgsPoint * centroid(QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Calculates the centroid of this.
std::unique_ptr< QgsAbstractGeometry > mergeLines(QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
QgsAbstractGeometry * difference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const override
Calculate the difference of this and geom.
void geometryChanged() override
Should be called whenever the geometry associated with the engine has been modified and the engine mu...
double area(QString *errorMsg=nullptr) const override
double length(QString *errorMsg=nullptr) const override
double hausdorffDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
double frechetDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
QgsAbstractGeometry * interpolate(double distance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Interpolates a point by distance along the geometry.
static QgsPoint coordSeqPoint(const GEOSCoordSequence *cs, int i, bool hasZ, bool hasM)
static QgsGeometry polygonize(const QVector< const QgsAbstractGeometry * > &geometries, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
static QgsGeometry geometryFromGeos(GEOSGeometry *geos)
Creates a new QgsGeometry object, feeding in a geometry in GEOS format.
Line string geometry type, with support for z-dimension and m-values.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
Point geometry type, with support for z-dimension and m-values.
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
bool isEmpty() const override
Returns true if the geometry is empty.
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Polyhedral surface geometry type.
int numPatches() const
Returns the number of patches contained with the polyhedral surface.
const QgsPolygon * patchN(int i) const
Retrieves a patch from the polyhedral surface.
A rectangle specified with double values.
void setYMinimum(double y)
Set the minimum y value.
void setXMinimum(double x)
Set the minimum x value.
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
Scoped object for setting the current thread GEOS context feedback object.
QgsScopedGeosContextRegisterFeedback(QgsFeedback *feedback)
Registers a feedback object for GEOS interruption checking.
~QgsScopedGeosContextRegisterFeedback()
Resets the GEOS interruption checker for the current thread.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
QVector< double > yVector() const
Returns the y vertex values as a vector.
int numPoints() const override
Returns the number of points in the curve.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the simple curve does not have m values.
QVector< double > zVector() const
Returns the z vertex values as a vector.
QVector< double > xVector() const
Returns the x vertex values as a vector.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
const double * yData() const
Returns a const pointer to the y vertex data.
QgsPoint pointN(int i) const
Returns the specified point from inside the simple curve.
const double * xData() const
Returns a const pointer to the x vertex data.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the simple curve does not have z values.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
static Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
Contains geos related utilities and functions.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
std::unique_ptr< GEOSCoordSequence, GeosDeleter > coord_sequence_unique_ptr
Scoped GEOS coordinate sequence pointer.
std::unique_ptr< GEOSBufferParams, GeosDeleter > buffer_params_unique_ptr
Scoped GEOS buffer params pointer.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsPoint > QgsPointSequence
#define DEFAULT_QUADRANT_SEGMENTS
#define CATCH_GEOS_WITH_ERRMSG(r)
#define QgsDebugError(str)
QLineF segment(int index, QRectF rect, double radius)
Utility class for identifying a unique vertex within a geometry.
void CORE_EXPORT operator()(GEOSGeometry *geom) const
Destroys the GEOS geometry geom, using the static QGIS geos context.
struct GEOSGeom_t GEOSGeometry