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;
2415 throw QgsNotSupportedException( QObject::tr(
"Cleaning coverages requires a QGIS build based on GEOS 3.14 or later" ) );
2420 *errorMsg = u
"Input geometry was not set"_s;
2424 GEOSCoverageCleanParams *params =
nullptr;
2453 std::unique_ptr< QgsAbstractGeometry> cleanedGeom =
fromGeos( cleaned.get() );
2457 catch ( QgsGeosException &e )
2467 *errorMsg = e.what();
2468 if ( errorMsg->startsWith(
"InterruptedException"_L1, Qt::CaseInsensitive ) )
2483 *errorMsg = QObject::tr(
"QGIS geometry cannot be converted to a GEOS geometry",
"GEOS Error" );
2494 char res = GEOSisValidDetail_r( context, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
2495 const bool invalid = res != 1;
2500 error = QString( r );
2501 GEOSFree_r( context, r );
2504 if ( invalid && errorMsg )
2507 static const std::map< QString, QString > sTranslatedErrors {
2508 { u
"topology validation error"_s, QObject::tr(
"Topology validation error",
"GEOS Error" ) },
2509 { u
"repeated point"_s, QObject::tr(
"Repeated point",
"GEOS Error" ) },
2510 { u
"hole lies outside shell"_s, QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) },
2511 { u
"holes are nested"_s, QObject::tr(
"Holes are nested",
"GEOS Error" ) },
2512 { u
"interior is disconnected"_s, QObject::tr(
"Interior is disconnected",
"GEOS Error" ) },
2513 { u
"self-intersection"_s, QObject::tr(
"Self-intersection",
"GEOS Error" ) },
2514 { u
"ring self-intersection"_s, QObject::tr(
"Ring self-intersection",
"GEOS Error" ) },
2515 { u
"nested shells"_s, QObject::tr(
"Nested shells",
"GEOS Error" ) },
2516 { u
"duplicate rings"_s, QObject::tr(
"Duplicate rings",
"GEOS Error" ) },
2517 { u
"too few points in geometry component"_s, QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) },
2518 { u
"invalid coordinate"_s, QObject::tr(
"Invalid coordinate",
"GEOS Error" ) },
2519 { u
"ring is not closed"_s, QObject::tr(
"Ring is not closed",
"GEOS Error" ) },
2522 const auto translatedError = sTranslatedErrors.find( error.toLower() );
2523 if ( translatedError != sTranslatedErrors.end() )
2524 *errorMsg = translatedError->second;
2528 if ( g1 && errorLoc )
2534 GEOSGeom_destroy_r( context, g1 );
2544 if ( !mGeos || !geom )
2565 if ( !mGeos || !geom )
2579 bool equal = GEOSEqualsExact_r(
QgsGeosContext::get(), mGeos.get(), geosGeom.get(), epsilon );
2613GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
2617 std::unique_ptr< QgsLineString > segmentized;
2623 line = segmentized.get();
2630 GEOSCoordSequence *coordSeq =
nullptr;
2632 const int numPoints = line->
numPoints();
2634 const bool hasZ = line->
is3D();
2636#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
2639 if ( !forceClose || ( line->
pointN( 0 ) == line->
pointN( numPoints - 1 ) ) )
2644 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, line->
xData(), line->
yData(), line->
zData(),
nullptr, numPoints );
2647 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points"_s.arg( numPoints ) );
2655 QVector< double > x = line->
xVector();
2656 if ( numPoints > 0 )
2657 x.append( x.at( 0 ) );
2658 QVector< double > y = line->
yVector();
2659 if ( numPoints > 0 )
2660 y.append( y.at( 0 ) );
2661 QVector< double > z = line->
zVector();
2662 if ( hasZ && numPoints > 0 )
2663 z.append( z.at( 0 ) );
2666 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, x.constData(), y.constData(), !hasZ ?
nullptr : z.constData(),
nullptr, numPoints + 1 );
2669 QgsDebugError( u
"GEOS Exception: Could not create closed coordinate sequence for %1 points"_s.arg( numPoints + 1 ) );
2680 const bool hasM =
false;
2691 int numOutPoints = numPoints;
2692 if ( forceClose && ( line->
pointN( 0 ) != line->
pointN( numPoints - 1 ) ) )
2699 coordSeq = GEOSCoordSeq_create_r( context, numOutPoints, coordDims );
2702 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions"_s.arg( numPoints ).arg( coordDims ) );
2706 const double *xData = line->
xData();
2707 const double *yData = line->
yData();
2708 const double *zData = hasZ ? line->
zData() :
nullptr;
2709 const double *mData = hasM ? line->
mData() :
nullptr;
2711 if ( precision > 0. )
2713 for (
int i = 0; i < numOutPoints; ++i )
2715 if ( i >= numPoints )
2718 xData = line->
xData();
2719 yData = line->
yData();
2720 zData = hasZ ? line->
zData() :
nullptr;
2721 mData = hasM ? line->
mData() :
nullptr;
2725 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
2729 GEOSCoordSeq_setXY_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
2733 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2739 for (
int i = 0; i < numOutPoints; ++i )
2741 if ( i >= numPoints )
2744 xData = line->
xData();
2745 yData = line->
yData();
2746 zData = hasZ ? line->
zData() :
nullptr;
2747 mData = hasM ? line->
mData() :
nullptr;
2751 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, *xData++, *yData++, *zData++ );
2755 GEOSCoordSeq_setXY_r( context, coordSeq, i, *xData++, *yData++ );
2759 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2769geos::unique_ptr
QgsGeos::createGeosPoint( const QgsAbstractGeometry *point,
int coordDims,
double precision, Qgis::GeosCreationFlags )
2775 return createGeosPointXY( pt->
x(), pt->
y(), pt->
is3D(), pt->
z(), pt->
isMeasure(), pt->
m(), coordDims, precision );
2787 if ( coordDims == 2 )
2790 if ( precision > 0. )
2791 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
2793 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, x, y ) );
2797 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( context, 1, coordDims );
2800 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions"_s.arg( coordDims ) );
2803 if ( precision > 0. )
2805 GEOSCoordSeq_setX_r( context, coordSeq, 0, std::round( x / precision ) * precision );
2806 GEOSCoordSeq_setY_r( context, coordSeq, 0, std::round( y / precision ) * precision );
2809 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, std::round( z / precision ) * precision );
2814 GEOSCoordSeq_setX_r( context, coordSeq, 0, x );
2815 GEOSCoordSeq_setY_r( context, coordSeq, 0, y );
2818 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, z );
2824 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 3, m );
2827 geosPoint.reset( GEOSGeom_createPoint_r( context, coordSeq ) );
2833geos::unique_ptr
QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve,
double precision, Qgis::GeosCreationFlags )
2839 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c, precision );
2852geos::unique_ptr
QgsGeos::createGeosPolygon( const QgsAbstractGeometry *poly,
double precision, Qgis::GeosCreationFlags flags )
2858 const QgsCurve *exteriorRing = polygon->
exteriorRing();
2859 if ( !exteriorRing )
2868 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( context, createCoordinateSequence( exteriorRing, precision,
true ) ) );
2871 QList< const QgsCurve * > holesToExport;
2872 holesToExport.reserve( nInteriorRings );
2873 for (
int i = 0; i < nInteriorRings; ++i )
2875 const QgsCurve *interiorRing = polygon->
interiorRing( i );
2878 holesToExport << interiorRing;
2883 if ( !holesToExport.empty() )
2886 for (
int i = 0; i < holesToExport.size(); ++i )
2888 holes[i] = GEOSGeom_createLinearRing_r( context, createCoordinateSequence( holesToExport[i], precision,
true ) );
2892 geosPolygon.reset( GEOSGeom_createPolygon_r( context, exteriorRingGeos.release(), holes, holesToExport.size() ) );
2913 offset.reset( GEOSOffsetCurve_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( joinStyle ), miterLimit ) );
2925 return fromGeos( res.get() ).release();
2943 GEOSBufferParams_setSingleSided_r( context, bp.get(), 1 );
2944 GEOSBufferParams_setQuadrantSegments_r( context, bp.get(), segments );
2945 GEOSBufferParams_setJoinStyle_r( context, bp.get(),
static_cast< int >( joinStyle ) );
2946 GEOSBufferParams_setMitreLimit_r( context, bp.get(), miterLimit );
2952 geos.reset( GEOSBufferWithParams_r( context, mGeos.get(), bp.get(),
distance ) );
2987 boundaryGeos =
asGeos( boundary );
3017 return std::numeric_limits< double >::quiet_NaN();
3026 return std::numeric_limits< double >::quiet_NaN();
3068 if ( !mGeos || !other )
3089 if ( !mGeos ||
mGeometry->dimension() == 0 )
3107 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
3111 int numGeoms = GEOSGetNumGeometries_r( context, mGeos.get() );
3112 if ( numGeoms == -1 )
3121 bool isMultiGeom =
false;
3122 int geosTypeId = GEOSGeomTypeId_r( context, mGeos.get() );
3123 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
3126 bool isLine = (
mGeometry->dimension() == 1 );
3133 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
3137 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
3142 if ( reshapedGeometry )
3148 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
3149 return reshapeResult;
3156 bool reshapeTookPlace =
false;
3161 for (
int i = 0; i < numGeoms; ++i )
3164 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3166 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3168 if ( currentReshapeGeometry )
3170 newGeoms[i] = currentReshapeGeometry.release();
3171 reshapeTookPlace =
true;
3175 newGeoms[i] = GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, mGeos.get(), i ) );
3182 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
3186 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
3190 if ( !newMultiGeom )
3199 if ( reshapeTookPlace )
3203 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
3204 return reshapedMultiGeom;
3227 if ( GEOSGeomTypeId_r( context, mGeos.get() ) != GEOS_MULTILINESTRING )
3234 double gridSize = parameters.
gridSize();
3237 geos::unique_ptr geosFixedSize( GEOSGeom_setPrecision_r( context, mGeos.get(), gridSize, 0 ) );
3238 geos.reset( GEOSLineMerge_r( context, geosFixedSize.get() ) );
3241 geos.reset( GEOSLineMerge_r( context, mGeos.get() ) );
3267 if ( mGeosPrepared )
3269 nearestCoord.reset( GEOSPreparedNearestPoints_r( context, mGeosPrepared.get(), otherGeom.get() ) );
3273 nearestCoord.reset( GEOSNearestPoints_r( context, mGeos.get(), otherGeom.get() ) );
3276 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx );
3277 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny );
3279 catch ( QgsGeosException &e )
3284 *errorMsg = e.what();
3289 return std::make_unique< QgsPoint >( nx, ny );
3294 if ( !mGeos || other.
isEmpty() )
3304 if ( !other || other->
isEmpty() )
3323 if ( !nearestCoord )
3326 *errorMsg = u
"GEOS returned no nearest points"_s;
3330 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx1 );
3331 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny1 );
3332 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 1, &nx2 );
3333 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 1, &ny2 );
3335 catch ( QgsGeosException &e )
3340 *errorMsg = e.what();
3345 auto line = std::make_unique< QgsLineString >();
3370 catch ( QgsGeosException &e )
3375 *errorMsg = e.what();
3390 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
3400 catch ( QgsGeosException &e )
3405 *errorMsg = e.what();
3422 lineGeosGeometries[validLines] = l.release();
3431 geos::unique_ptr result( GEOSPolygonize_r( context, lineGeosGeometries, validLines ) );
3432 for (
int i = 0; i < validLines; ++i )
3434 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3436 delete[] lineGeosGeometries;
3439 catch ( QgsGeosException &e )
3443 *errorMsg = e.what();
3445 for (
int i = 0; i < validLines; ++i )
3447 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3449 delete[] lineGeosGeometries;
3464 extentGeosGeom =
asGeos( extent, mPrecision );
3465 if ( !extentGeosGeom )
3476 geos.reset( GEOSVoronoiDiagram_r( context, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
3478 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3500 geos.reset( GEOSDelaunayTriangulation_r( context, mGeos.get(), tolerance, edgesOnly ) );
3502 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3514#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
3516 throw QgsNotSupportedException( QObject::tr(
"Calculating constrainedDelaunayTriangulation requires a QGIS build based on GEOS 3.11 or later" ) );
3528 geos.reset( GEOSConstrainedDelaunayTriangulation_r( context, mGeos.get() ) );
3530 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3535 std::unique_ptr< QgsAbstractGeometry > res =
fromGeos(
geos.get() );
3538 return std::unique_ptr< QgsAbstractGeometry >( collection->extractPartsByType(
Qgis::WkbType::Polygon,
true ) );
3550static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
3553 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( context, linestring );
3557 unsigned int coordSeqSize;
3558 if ( GEOSCoordSeq_getSize_r( context, coordSeq, &coordSeqSize ) == 0 )
3561 if ( coordSeqSize < 2 )
3564 GEOSCoordSeq_getX_r( context, coordSeq, 0, &x1 );
3565 GEOSCoordSeq_getY_r( context, coordSeq, 0, &y1 );
3566 GEOSCoordSeq_getX_r( context, coordSeq, coordSeqSize - 1, &x2 );
3567 GEOSCoordSeq_getY_r( context, coordSeq, coordSeqSize - 1, &y2 );
3575 double x1, y1, x2, y2;
3576 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
3579 double rx1, ry1, rx2, ry2;
3580 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
3583 bool intersectionAtOrigLineEndpoint = ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) != ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
3584 bool intersectionAtReshapeLineEndpoint = ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) || ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
3588 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
3592 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
3593 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, geoms, 2 ) );
3598 double x1res, y1res, x2res, y2res;
3599 if ( !_linestringEndpoints( res.get(), x1res, y1res, x2res, y2res ) )
3601 if ( ( x1res == x2 && y1res == y2 ) || ( x2res == x1 && y2res == y1 ) )
3602 res.reset( GEOSReverse_r( context, res.get() ) );
3613 if ( !line || !reshapeLineGeos )
3616 bool atLeastTwoIntersections =
false;
3617 bool oneIntersection =
false;
3618 QgsPointXY oneIntersectionPoint;
3624 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, line, reshapeLineGeos ) );
3625 if ( intersectGeom )
3627 const int geomType = GEOSGeomTypeId_r( context, intersectGeom.get() );
3628 atLeastTwoIntersections = ( geomType == GEOS_MULTIPOINT && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 1 )
3629 || ( geomType == GEOS_GEOMETRYCOLLECTION && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 )
3630 || ( geomType == GEOS_MULTILINESTRING && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 );
3632 if ( GEOSGeomTypeId_r( context, intersectGeom.get() ) == GEOS_POINT )
3634 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( context, intersectGeom.get() );
3636 GEOSCoordSeq_getX_r( context, intersectionCoordSeq, 0, &xi );
3637 GEOSCoordSeq_getY_r( context, intersectionCoordSeq, 0, &yi );
3638 oneIntersection =
true;
3639 oneIntersectionPoint = QgsPointXY( xi, yi );
3643 catch ( QgsGeosException & )
3645 atLeastTwoIntersections =
false;
3649 if ( oneIntersection )
3650 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
3652 if ( !atLeastTwoIntersections )
3656 double x1, y1, x2, y2;
3657 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
3660 geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1,
false, 0,
false, 0, 2, precision );
3661 geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2,
false, 0,
false, 0, 2, precision );
3663 bool isRing =
false;
3664 if ( GEOSGeomTypeId_r( context, line ) == GEOS_LINEARRING || GEOSEquals_r( context, beginLineVertex.get(), endLineVertex.get() ) == 1 )
3669 if ( !nodedGeometry )
3675 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, nodedGeometry.get() ) );
3681 int numMergedLines = GEOSGetNumGeometries_r( context, mergedLines.get() );
3682 if ( numMergedLines < 2 )
3684 if ( numMergedLines == 1 )
3693 QVector<GEOSGeometry *> resultLineParts;
3694 QVector<GEOSGeometry *> probableParts;
3696 for (
int i = 0; i < numMergedLines; ++i )
3698 const GEOSGeometry *currentGeom = GEOSGetGeometryN_r( context, mergedLines.get(), i );
3701 bool alreadyAdded =
false;
3703 double bufferDistance = std::pow( 10.0L, geomDigits( currentGeom ) - 11 );
3704 for (
const GEOSGeometry *other : std::as_const( resultLineParts ) )
3706 GEOSHausdorffDistance_r( context, currentGeom, other, &
distance );
3709 alreadyAdded =
true;
3716 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( context, currentGeom );
3717 unsigned int currentCoordSeqSize;
3718 GEOSCoordSeq_getSize_r( context, currentCoordSeq, ¤tCoordSeqSize );
3719 if ( currentCoordSeqSize < 2 )
3723 double xBegin, xEnd, yBegin, yEnd;
3724 GEOSCoordSeq_getX_r( context, currentCoordSeq, 0, &xBegin );
3725 GEOSCoordSeq_getY_r( context, currentCoordSeq, 0, &yBegin );
3726 GEOSCoordSeq_getX_r( context, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
3727 GEOSCoordSeq_getY_r( context, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
3728 geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin,
false, 0,
false, 0, 2, precision );
3729 geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd,
false, 0,
false, 0, 2, precision );
3732 int nEndpointsOnOriginalLine = 0;
3733 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
3734 nEndpointsOnOriginalLine += 1;
3736 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
3737 nEndpointsOnOriginalLine += 1;
3740 int nEndpointsSameAsOriginalLine = 0;
3741 if ( GEOSEquals_r( context, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1 || GEOSEquals_r( context, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3742 nEndpointsSameAsOriginalLine += 1;
3744 if ( GEOSEquals_r( context, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1 || GEOSEquals_r( context, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3745 nEndpointsSameAsOriginalLine += 1;
3748 bool currentGeomOverlapsOriginalGeom =
false;
3749 bool currentGeomOverlapsReshapeLine =
false;
3750 if ( lineContainedInLine( currentGeom, line ) == 1 )
3751 currentGeomOverlapsOriginalGeom =
true;
3753 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
3754 currentGeomOverlapsReshapeLine =
true;
3757 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3759 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3762 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3764 probableParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3766 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3768 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3770 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3772 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3774 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
3776 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3781 if ( isRing && !probableParts.isEmpty() )
3785 double maxLength = -std::numeric_limits<double>::max();
3786 double currentLength = 0;
3787 for (
int i = 0; i < probableParts.size(); ++i )
3789 currentGeom = probableParts.at( i );
3790 GEOSLength_r( context, currentGeom, ¤tLength );
3791 if ( currentLength > maxLength )
3793 maxLength = currentLength;
3794 maxGeom.reset( currentGeom );
3798 GEOSGeom_destroy_r( context, currentGeom );
3801 resultLineParts.push_back( maxGeom.release() );
3805 if ( resultLineParts.empty() )
3808 if ( resultLineParts.size() == 1 )
3810 result.reset( resultLineParts[0] );
3815 for (
int i = 0; i < resultLineParts.size(); ++i )
3817 lineArray[i] = resultLineParts[i];
3821 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
3825 result.reset( GEOSLineMerge_r( context, multiLineGeom.get() ) );
3829 if ( GEOSGeomTypeId_r( context, result.get() ) != GEOS_LINESTRING )
3835 bool reverseLine =
false;
3839 char isResultCCW = 0, isOriginCCW = 0;
3840 if ( GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, result.get() ), &isResultCCW ) && GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, line ), &isOriginCCW ) )
3843 reverseLine = ( isOriginCCW == 1 && isResultCCW == 0 ) || ( isOriginCCW == 0 && isResultCCW == 1 );
3849 double x1res, y1res, x2res, y2res;
3850 if ( !_linestringEndpoints( result.get(), x1res, y1res, x2res, y2res ) )
3852 geos::unique_ptr beginResultLineVertex = createGeosPointXY( x1res, y1res,
false, 0,
false, 0, 2, precision );
3853 geos::unique_ptr endResultLineVertex = createGeosPointXY( x2res, y2res,
false, 0,
false, 0, 2, precision );
3854 reverseLine = GEOSEquals_r( context, beginLineVertex.get(), endResultLineVertex.get() ) == 1 || GEOSEquals_r( context, endLineVertex.get(), beginResultLineVertex.get() ) == 1;
3857 result.reset( GEOSReverse_r( context, result.get() ) );
3865 int nIntersections = 0;
3866 int lastIntersectingRing = -2;
3870 int nRings = GEOSGetNumInteriorRings_r( context, polygon );
3875 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( context, polygon );
3876 if ( GEOSIntersects_r( context, outerRing, reshapeLineGeos ) == 1 )
3879 lastIntersectingRing = -1;
3880 lastIntersectingGeom = outerRing;
3888 for (
int i = 0; i < nRings; ++i )
3890 innerRings[i] = GEOSGetInteriorRingN_r( context, polygon, i );
3891 if ( GEOSIntersects_r( context, innerRings[i], reshapeLineGeos ) == 1 )
3894 lastIntersectingRing = i;
3895 lastIntersectingGeom = innerRings[i];
3899 catch ( QgsGeosException & )
3904 if ( nIntersections != 1 )
3906 delete[] innerRings;
3911 geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
3912 if ( !reshapeResult )
3914 delete[] innerRings;
3920 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( context, reshapeResult.get() );
3921 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( context, reshapeSequence );
3923 reshapeResult.reset();
3927 newRing = GEOSGeom_createLinearRing_r( context, newCoordSequence );
3929 catch ( QgsGeosException & )
3936 delete[] innerRings;
3941 if ( lastIntersectingRing == -1 )
3942 newOuterRing = newRing;
3944 newOuterRing = GEOSGeom_clone_r( context, outerRing );
3947 QVector<GEOSGeometry *> ringList;
3950 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( context, GEOSGeom_clone_r( context, newOuterRing ),
nullptr, 0 );
3951 if ( outerRingPoly )
3953 ringList.reserve( nRings );
3955 for (
int i = 0; i < nRings; ++i )
3957 if ( lastIntersectingRing == i )
3958 currentRing = newRing;
3960 currentRing = GEOSGeom_clone_r( context, innerRings[i] );
3963 if ( GEOSContains_r( context, outerRingPoly, currentRing ) == 1 )
3964 ringList.push_back( currentRing );
3966 GEOSGeom_destroy_r( context, currentRing );
3969 GEOSGeom_destroy_r( context, outerRingPoly );
3973 for (
int i = 0; i < ringList.size(); ++i )
3974 newInnerRings[i] = ringList.at( i );
3976 delete[] innerRings;
3978 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( context, newOuterRing, newInnerRings, ringList.size() ) );
3979 delete[] newInnerRings;
3981 return reshapedPolygon;
3986 if ( !line1 || !line2 )
3991 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
3998 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, bufferGeom.get(), line1 ) );
4001 double intersectGeomLength;
4004 GEOSLength_r( context, intersectionGeom.get(), &intersectGeomLength );
4005 GEOSLength_r( context, line1, &line1Length );
4007 double intersectRatio = line1Length / intersectGeomLength;
4008 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
4016 if ( !point || !line )
4019 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
4022 geos::unique_ptr lineBuffer( GEOSBuffer_r( context, line, bufferDistance, 8 ) );
4026 bool contained =
false;
4027 if ( GEOSContains_r( context, lineBuffer.get(), point ) == 1 )
4040 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( context, bbox.get() );
4044 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( context, bBoxRing );
4046 if ( !bBoxCoordSeq )
4049 unsigned int nCoords = 0;
4050 if ( !GEOSCoordSeq_getSize_r( context, bBoxCoordSeq, &nCoords ) )
4054 for (
unsigned int i = 0; i < nCoords - 1; ++i )
4057 GEOSCoordSeq_getX_r( context, bBoxCoordSeq, i, &t );
4060 digits = std::ceil( std::log10( std::fabs( t ) ) );
4061 if ( digits > maxDigits )
4064 GEOSCoordSeq_getY_r( context, bBoxCoordSeq, i, &t );
4065 digits = std::ceil( std::log10( std::fabs( t ) ) );
4066 if ( digits > maxDigits )
4074#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4075 : mFeedback( feedback )
4077 GEOSContext_setInterruptCallback_r(
QgsGeosContext::get(), &callback,
reinterpret_cast< void *
>( mFeedback ) );
4088#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4093#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 14 )
4094int QgsScopedGeosContextRegisterFeedback::callback(
void *userData )
4100 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