38using namespace Qt::StringLiterals;
40#define DEFAULT_QUADRANT_SEGMENTS 8
42#define CATCH_GEOS( r ) \
43 catch ( QgsGeosException & ) \
48#define CATCH_GEOS_WITH_ERRMSG( r ) \
49 catch ( QgsGeosException & e ) \
53 *errorMsg = e.what(); \
60static void throwQgsGeosException(
const char *fmt, ... )
66 vsnprintf( buffer,
sizeof buffer, fmt, ap );
69 QString message = QString::fromUtf8( buffer );
79 throw QgsGeosException( message );
87 throw QgsGeosException( message );
92static void printGEOSNotice(
const char *fmt, ... )
94#if defined( QGISDEBUG )
99 vsnprintf( buffer,
sizeof buffer, fmt, ap );
110#if defined( USE_THREAD_LOCAL ) && !defined( Q_OS_WIN )
113QThreadStorage< QgsGeosContext * > QgsGeosContext::sGeosContext;
119 mContext = GEOS_init_r();
120 GEOSContext_setNoticeHandler_r( mContext, printGEOSNotice );
121 GEOSContext_setErrorHandler_r( mContext, throwQgsGeosException );
126 GEOS_finish_r( mContext );
131#if defined( USE_THREAD_LOCAL ) && !defined( Q_OS_WIN )
132 return sGeosContext.mContext;
134 GEOSContextHandle_t gContext =
nullptr;
135 if ( sGeosContext.hasLocalData() )
137 gContext = sGeosContext.localData()->mContext;
142 gContext = sGeosContext.localData()->mContext;
179 , mPrecision( precision )
206#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
208 throw QgsNotSupportedException( QObject::tr(
"The structured method to make geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
211 throw QgsNotSupportedException( QObject::tr(
"The keep collapsed option for making geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
215 geos.reset( GEOSMakeValid_r( context, mGeos.get() ) );
220 GEOSMakeValidParams *params = GEOSMakeValidParams_create_r( context );
224 GEOSMakeValidParams_setMethod_r( context, params, GEOS_MAKE_VALID_LINEWORK );
228 GEOSMakeValidParams_setMethod_r( context, params, GEOS_MAKE_VALID_STRUCTURE );
232 GEOSMakeValidParams_setKeepCollapsed_r( context, params, keepCollapsed ? 1 : 0 );
237 geos.reset( GEOSMakeValidWithParams_r( context, mGeos.get(), params ) );
238 GEOSMakeValidParams_destroy_r( context, params );
240 catch ( QgsGeosException &e )
244 *errorMsg = e.what();
246 GEOSMakeValidParams_destroy_r( context, params );
275 std::unique_ptr< QgsAbstractGeometry > geom =
fromGeos( newPart );
282 mGeosPrepared.reset();
316 return overlay( geom, OverlayIntersection, errorMsg, parameters ).release();
321 return overlay( geom, OverlayDifference, errorMsg, parameters ).release();
336 catch ( QgsGeosException &e )
341 *errorMsg = e.what();
350 int partType = GEOSGeomTypeId_r( context, currentPart );
353 if ( partType == GEOS_POINT )
364 if ( partType == GEOS_MULTILINESTRING || partType == GEOS_MULTIPOLYGON || partType == GEOS_GEOMETRYCOLLECTION )
366 int partCount = GEOSGetNumGeometries_r( context, currentPart );
367 for (
int i = 0; i < partCount; ++i )
369 subdivideRecursive( GEOSGetGeometryN_r( context, currentPart, i ), maxNodes, depth, parts, clipRect, gridSize );
380 int vertexCount = GEOSGetNumCoordinates_r( context, currentPart );
381 if ( vertexCount == 0 )
385 else if ( vertexCount < maxNodes )
392 double width = clipRect.
width();
393 double height = clipRect.
height();
394 QgsRectangle halfClipRect1 = clipRect;
395 QgsRectangle halfClipRect2 = clipRect;
396 if ( width > height )
409 halfClipRect1.
setYMinimum( halfClipRect1.
yMinimum() - std::numeric_limits<double>::epsilon() );
410 halfClipRect2.
setYMinimum( halfClipRect2.
yMinimum() - std::numeric_limits<double>::epsilon() );
411 halfClipRect1.
setYMaximum( halfClipRect1.
yMaximum() + std::numeric_limits<double>::epsilon() );
412 halfClipRect2.
setYMaximum( halfClipRect2.
yMaximum() + std::numeric_limits<double>::epsilon() );
416 halfClipRect1.
setXMinimum( halfClipRect1.
xMinimum() - std::numeric_limits<double>::epsilon() );
417 halfClipRect2.
setXMinimum( halfClipRect2.
xMinimum() - std::numeric_limits<double>::epsilon() );
418 halfClipRect1.
setXMaximum( halfClipRect1.
xMaximum() + std::numeric_limits<double>::epsilon() );
419 halfClipRect2.
setXMaximum( halfClipRect2.
xMaximum() + std::numeric_limits<double>::epsilon() );
431 clipPart1.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart1.get(), gridSize ) );
433 subdivideRecursive( clipPart1.get(), maxNodes, depth, parts, halfClipRect1, gridSize );
439 clipPart2.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart2.get(), gridSize ) );
441 subdivideRecursive( clipPart2.get(), maxNodes, depth, parts, halfClipRect2, gridSize );
453 maxNodes = std::max( maxNodes, 8 );
458 subdivideRecursive( mGeos.get(), maxNodes, 0, parts.get(),
mGeometry->boundingBox(), parameters.
gridSize() );
462 return std::move( parts );
467 return overlay( geom, OverlayUnion, errorMsg, parameters ).release();
472 std::vector<geos::unique_ptr> geosGeometries;
473 geosGeometries.reserve( geomList.size() );
479 geosGeometries.emplace_back(
asGeos( g, mPrecision ) );
486 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
489 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
493 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
498 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
499 return result.release();
504 std::vector<geos::unique_ptr> geosGeometries;
505 geosGeometries.reserve( geomList.size() );
511 geosGeometries.emplace_back(
asGeos( g.constGet(), mPrecision ) );
518 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
522 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
526 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
531 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
532 return result.release();
537 return overlay( geom, OverlaySymDifference, errorMsg, parameters ).release();
540static bool isZVerticalLine(
const QgsAbstractGeometry *geom,
double tolerance = 4 * std::numeric_limits<double>::epsilon() )
550 bool isVertical =
true;
553 const int nrPoints = line->numPoints();
561 const double sqrTolerance = tolerance * tolerance;
562 const double *lineX = line->xData();
563 const double *lineY = line->yData();
564 for (
int iVert = nrPoints - 1, jVert = 0; jVert < nrPoints; iVert = jVert++ )
594 otherGeosGeom =
asGeos( &firstPoint, mPrecision );
598 otherGeosGeom =
asGeos( geom, mPrecision );
601 if ( !otherGeosGeom )
609 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
611 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
615 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
631 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
640 GEOSPreparedDistance_r( context, mGeosPrepared.get(), point.get(), &
distance );
644 GEOSDistance_r( context, mGeos.get(), point.get(), &
distance );
673 otherGeosGeom =
asGeos( &firstPoint );
677 otherGeosGeom =
asGeos( geom, mPrecision );
680 if ( !otherGeosGeom )
693 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
695#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
696 return GEOSPreparedDistanceWithin_r( context, mGeosPrepared.get(), otherGeosGeom.get(), maxdist );
698 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
703#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
704 return GEOSDistanceWithin_r( context, mGeos.get(), otherGeosGeom.get(), maxdist );
706 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
721#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
724 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
730#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
731 return GEOSPreparedContainsXY_r( context, mGeosPrepared.get(), x, y ) == 1;
733 return GEOSPreparedContains_r( context, mGeosPrepared.get(), point.get() ) == 1;
737#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 );
743 result = ( GEOSContains_r( context, mGeos.get(), point.get() ) == 1 );
745 catch ( QgsGeosException &e )
750 *errorMsg = e.what();
767 if ( !otherGeosGeom )
790 if ( !otherGeosGeom )
813 if ( !otherGeosGeom )
836 if ( !otherGeosGeom )
852 if ( !mGeos || !geom )
857#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
865 return GEOSPreparedIntersectsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
867 catch ( QgsGeosException &e )
872 *errorMsg = e.what();
880 return relation( geom, RelationIntersects, errorMsg );
885 return relation( geom, RelationTouches, errorMsg );
890 return relation( geom, RelationCrosses, errorMsg );
895 return relation( geom, RelationWithin, errorMsg );
900 return relation( geom, RelationOverlaps, errorMsg );
905 if ( !mGeos || !geom )
910#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12 )
918 return GEOSPreparedContainsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
920 catch ( QgsGeosException &e )
925 *errorMsg = e.what();
933 return relation( geom, RelationContains, errorMsg );
938 return relation( geom, RelationDisjoint, errorMsg );
958 char *r = GEOSRelate_r( context, mGeos.get(), geosGeom.get() );
961 result = QString( r );
962 GEOSFree_r( context, r );
965 catch ( QgsGeosException &e )
970 *errorMsg = e.what();
979 if ( !mGeos || !geom )
994 result = ( GEOSRelatePattern_r( context, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
996 catch ( QgsGeosException &e )
1001 *errorMsg = e.what();
1042 const QgsLineString &splitLine, QVector<QgsGeometry> &newGeometries,
bool topological,
QgsPointSequence &topologyTestPoints, QString *errorMsg,
bool skipIntersectionCheck
1058 if ( !GEOSisValid_r( context, mGeos.get() ) )
1065 newGeometries.clear();
1072 splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
1076 splitLineGeos = createGeosPointXY( splitLine.
xAt( 0 ), splitLine.
yAt( 0 ),
false, 0,
false, 0, 2, mPrecision );
1083 if ( !GEOSisValid_r( context, splitLineGeos.get() ) || !GEOSisSimple_r( context, splitLineGeos.get() ) )
1091 if ( !topologicalTestPointsSplit( splitLineGeos.get(), topologyTestPoints ) )
1100 returnCode = splitLinearGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1104 returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1132 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, mGeos.get(), splitLine ) );
1133 if ( !intersectionGeom )
1136 bool simple =
false;
1137 int nIntersectGeoms = 1;
1138 if ( GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_LINESTRING || GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_POINT )
1142 nIntersectGeoms = GEOSGetNumGeometries_r( context, intersectionGeom.get() );
1144 for (
int i = 0; i < nIntersectGeoms; ++i )
1148 currentIntersectGeom = intersectionGeom.get();
1150 currentIntersectGeom = GEOSGetGeometryN_r( context, intersectionGeom.get(), i );
1152 const GEOSCoordSequence *lineSequence = GEOSGeom_getCoordSeq_r( context, currentIntersectGeom );
1153 unsigned int sequenceSize = 0;
1155 if ( GEOSCoordSeq_getSize_r( context, lineSequence, &sequenceSize ) != 0 )
1157 for (
unsigned int i = 0; i < sequenceSize; ++i )
1159 if ( GEOSCoordSeq_getXYZ_r( context, lineSequence, i, &x, &y, &z ) )
1161 testPoints.push_back( QgsPoint( x, y, z ) );
1175 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1177 std::unique_ptr< QgsMultiCurve > multiCurve;
1178 if ( type == GEOS_MULTILINESTRING )
1182 else if ( type == GEOS_LINESTRING )
1184 multiCurve = std::make_unique<QgsMultiCurve>();
1185 multiCurve->addGeometry(
mGeometry->clone() );
1200 std::unique_ptr< QgsMultiPoint > splitPoints;
1202 std::unique_ptr< QgsAbstractGeometry > splitGeom(
fromGeos( GEOSsplitPoint ) );
1206 splitPoints.reset( qgis::down_cast<QgsMultiPoint *>( splitGeom.release() ) );
1210 splitPoints = std::make_unique< QgsMultiPoint >();
1213 splitPoints->addGeometry( qgis::down_cast<QgsPoint *>( splitGeom.release() ) );
1221 QgsMultiCurve lines;
1224 for (
int geometryIndex = 0; geometryIndex < multiCurve->numGeometries(); ++geometryIndex )
1237 QMap< int, QVector< QPair< double, QgsPoint > > > pointMap;
1238 for (
int splitPointIndex = 0; splitPointIndex < splitPoints->numGeometries(); ++splitPointIndex )
1240 const QgsPoint *intersectionPoint = splitPoints->pointN( splitPointIndex );
1242 QgsPoint segmentPoint2D;
1243 QgsVertexId nextVertex;
1246 line->
closestSegment( *intersectionPoint, segmentPoint2D, nextVertex );
1262 const QPair< double, QgsPoint > pair = qMakePair(
distance, *correctSegmentPoint.get() );
1263 if ( pointMap.contains( nextVertex.
vertex - 1 ) )
1264 pointMap[nextVertex.
vertex - 1].append( pair );
1266 pointMap[nextVertex.
vertex - 1] = QVector< QPair< double, QgsPoint > >() << pair;
1271 for (
auto &p : pointMap )
1273 std::sort( p.begin(), p.end(), [](
const QPair< double, QgsPoint > &a,
const QPair< double, QgsPoint > &b ) { return a.first < b.first; } );
1277 QgsLineString newLine;
1279 QgsPoint splitPoint;
1280 for (
int vertexIndex = 0; vertexIndex < nVertices; ++vertexIndex )
1282 QgsPoint currentPoint = line->
pointN( vertexIndex );
1284 if ( pointMap.contains( vertexIndex ) )
1287 for (
int k = 0; k < pointMap[vertexIndex].size(); ++k )
1289 splitPoint = pointMap[vertexIndex][k].second;
1290 if ( splitPoint == currentPoint )
1293 newLine = QgsLineString();
1296 else if ( splitPoint == line->
pointN( vertexIndex + 1 ) )
1300 newLine = QgsLineString();
1306 newLine = QgsLineString();
1315 return asGeos( &lines, mPrecision );
1320 Q_UNUSED( skipIntersectionCheck )
1329 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, splitLine, mGeos.get() ) );
1330 if ( !intersectGeom || GEOSisEmpty_r( context, intersectGeom.get() ) )
1334 const int linearIntersect = GEOSRelatePattern_r( context, mGeos.get(), splitLine,
"1********" );
1335 if ( linearIntersect > 0 )
1343 std::vector<geos::unique_ptr> lineGeoms;
1345 const int splitType = GEOSGeomTypeId_r( context, splitGeom.get() );
1346 if ( splitType == GEOS_MULTILINESTRING )
1348 const int nGeoms = GEOSGetNumGeometries_r( context, splitGeom.get() );
1349 lineGeoms.reserve( nGeoms );
1350 for (
int i = 0; i < nGeoms; ++i )
1351 lineGeoms.emplace_back( GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, splitGeom.get(), i ) ) );
1355 lineGeoms.emplace_back( GEOSGeom_clone_r( context, splitGeom.get() ) );
1358 mergeGeometriesMultiTypeSplit( lineGeoms );
1362 newGeometries << QgsGeometry(
fromGeos( lineGeom.get() ) );
1378 if ( !mGeosPrepared )
1384 if ( !skipIntersectionCheck && !GEOSPreparedIntersects_r( context, mGeosPrepared.get(), splitLine ) )
1389 if ( !nodedGeometry )
1398 const int numberOfGeometriesPolygon = numberOfGeometries( polygons.get() );
1399 if ( numberOfGeometriesPolygon == 0 )
1406 std::vector<geos::unique_ptr> testedGeometries;
1411 for (
int i = 0; i < numberOfGeometriesPolygon; i++ )
1413 const GEOSGeometry *polygon = GEOSGetGeometryN_r( context, polygons.get(), i );
1417 testedGeometries.emplace_back( GEOSGeom_clone_r( context, polygon ) );
1420 const size_t nGeometriesThis = numberOfGeometries( mGeos.get() );
1421 if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
1431 mergeGeometriesMultiTypeSplit( testedGeometries );
1434 for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( context, testedGeometries[i].get() ); ++i )
1437 if ( i < testedGeometries.size() )
1444 newGeometries << QgsGeometry(
fromGeos( testedGeometry.get() ) );
1452 if ( !splitLine || !geom )
1457 if ( GEOSGeom_getDimensions_r( context, geom ) == 2 )
1458 geometryBoundary.reset( GEOSBoundary_r( context, geom ) );
1460 geometryBoundary.reset( GEOSGeom_clone_r( context, geom ) );
1463 geos::unique_ptr unionGeometry( GEOSUnion_r( context, splitLineClone.get(), geometryBoundary.get() ) );
1465 return unionGeometry;
1468int QgsGeos::mergeGeometriesMultiTypeSplit( std::vector<geos::unique_ptr> &splitResult )
const
1475 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1476 if ( type != GEOS_GEOMETRYCOLLECTION && type != GEOS_MULTILINESTRING && type != GEOS_MULTIPOLYGON && type != GEOS_MULTIPOINT )
1480 std::vector<geos::unique_ptr> unionGeom;
1482 std::vector<geos::unique_ptr> newSplitResult;
1484 for (
size_t i = 0; i < splitResult.size(); ++i )
1487 bool isPart =
false;
1488 for (
int j = 0; j < GEOSGetNumGeometries_r( context, mGeos.get() ); j++ )
1490 if ( GEOSEquals_r( context, splitResult[i].get(), GEOSGetGeometryN_r( context, mGeos.get(), j ) ) )
1499 unionGeom.emplace_back( std::move( splitResult[i] ) );
1503 std::vector<geos::unique_ptr> geomVector;
1504 geomVector.emplace_back( std::move( splitResult[i] ) );
1506 if ( type == GEOS_MULTILINESTRING )
1507 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, geomVector ) );
1508 else if ( type == GEOS_MULTIPOLYGON )
1509 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, geomVector ) );
1513 splitResult = std::move( newSplitResult );
1516 if ( !unionGeom.empty() )
1518 if ( type == GEOS_MULTILINESTRING )
1519 splitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, unionGeom ) );
1520 else if ( type == GEOS_MULTIPOLYGON )
1521 splitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ) );
1527geos::unique_ptr QgsGeos::createGeosCollection(
int typeId, std::vector<geos::unique_ptr> &geoms )
1529 std::vector<GEOSGeometry *> geomarr;
1530 geomarr.reserve( geoms.size() );
1535 if ( geomUniquePtr )
1537 if ( !GEOSisEmpty_r( context, geomUniquePtr.get() ) )
1541 geomarr.emplace_back( geomUniquePtr.release() );
1549 geomRes.reset( GEOSGeom_createCollection_r( context, typeId, geomarr.data(), geomarr.size() ) );
1551 catch ( QgsGeosException & )
1555 GEOSGeom_destroy_r( context, geom );
1570 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1571 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1572 bool hasZ = ( nCoordDims == 3 );
1573 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1575 switch ( GEOSGeomTypeId_r( context,
geos ) )
1579 if ( GEOSisEmpty_r( context,
geos ) )
1582 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1583 unsigned int nPoints = 0;
1584 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1593 return !point.
isEmpty() ? std::unique_ptr<QgsAbstractGeometry>( point.
clone() ) :
nullptr;
1595 case GEOS_LINESTRING:
1597 return sequenceToLinestring(
geos, hasZ, hasM );
1603 case GEOS_MULTIPOINT:
1605 auto multiPoint = std::make_unique<QgsMultiPoint>();
1606 int nParts = GEOSGetNumGeometries_r( context,
geos );
1607 multiPoint->reserve( nParts );
1608 for (
int i = 0; i < nParts; ++i )
1610 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context, GEOSGetGeometryN_r( context,
geos, i ) );
1613 unsigned int nPoints = 0;
1614 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1616 multiPoint->addGeometry(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1619 return std::move( multiPoint );
1621 case GEOS_MULTILINESTRING:
1623 auto multiLineString = std::make_unique<QgsMultiLineString>();
1624 int nParts = GEOSGetNumGeometries_r( context,
geos );
1625 multiLineString->reserve( nParts );
1626 for (
int i = 0; i < nParts; ++i )
1628 std::unique_ptr< QgsLineString > line( sequenceToLinestring( GEOSGetGeometryN_r( context,
geos, i ), hasZ, hasM ) );
1631 multiLineString->addGeometry( line.release() );
1634 return std::move( multiLineString );
1636 case GEOS_MULTIPOLYGON:
1638 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
1640 int nParts = GEOSGetNumGeometries_r( context,
geos );
1641 multiPolygon->reserve( nParts );
1642 for (
int i = 0; i < nParts; ++i )
1644 std::unique_ptr< QgsPolygon > poly =
fromGeosPolygon( GEOSGetGeometryN_r( context,
geos, i ) );
1647 multiPolygon->addGeometry( poly.release() );
1650 return std::move( multiPolygon );
1652 case GEOS_GEOMETRYCOLLECTION:
1654 auto geomCollection = std::make_unique<QgsGeometryCollection>();
1655 int nParts = GEOSGetNumGeometries_r( context,
geos );
1656 geomCollection->reserve( nParts );
1657 for (
int i = 0; i < nParts; ++i )
1659 std::unique_ptr< QgsAbstractGeometry > geom(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1662 geomCollection->addGeometry( geom.release() );
1665 return std::move( geomCollection );
1674 if ( GEOSGeomTypeId_r( context,
geos ) != GEOS_POLYGON )
1679 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1680 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1681 bool hasZ = ( nCoordDims == 3 );
1682 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1684 auto polygon = std::make_unique<QgsPolygon>();
1689 polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1692 QVector<QgsCurve *> interiorRings;
1693 const int ringCount = GEOSGetNumInteriorRings_r( context,
geos );
1694 interiorRings.reserve( ringCount );
1695 for (
int i = 0; i < ringCount; ++i )
1697 ring = GEOSGetInteriorRingN_r( context,
geos, i );
1700 interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1703 polygon->setInteriorRings( interiorRings );
1708std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring(
const GEOSGeometry *
geos,
bool hasZ,
bool hasM )
1711 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1713 unsigned int nPoints;
1714 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1716 QVector< double > xOut( nPoints );
1717 QVector< double > yOut( nPoints );
1718 QVector< double > zOut;
1720 zOut.resize( nPoints );
1721 QVector< double > mOut;
1723 mOut.resize( nPoints );
1725 double *x = xOut.data();
1726 double *y = yOut.data();
1727 double *z = zOut.data();
1728 double *m = mOut.data();
1730#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
1731 GEOSCoordSeq_copyToArrays_r( context, cs, x, y, hasZ ? z :
nullptr, hasM ? m :
nullptr );
1733 for (
unsigned int i = 0; i < nPoints; ++i )
1736 GEOSCoordSeq_getXYZ_r( context, cs, i, x++, y++, z++ );
1738 GEOSCoordSeq_getXY_r( context, cs, i, x++, y++ );
1741 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, m++ );
1745 auto line = std::make_unique<QgsLineString>( xOut, yOut, zOut, mOut );
1755 int geometryType = GEOSGeomTypeId_r( context, g );
1756 if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING || geometryType == GEOS_POLYGON )
1760 return GEOSGetNumGeometries_r( context, g );
1776 GEOSCoordSeq_getXYZ_r( context, cs, i, &x, &y, &z );
1778 GEOSCoordSeq_getXY_r( context, cs, i, &x, &y );
1781 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, &m );
1817 int geosType = GEOS_GEOMETRYCOLLECTION;
1824 geosType = GEOS_MULTIPOINT;
1828 geosType = GEOS_MULTILINESTRING;
1832 geosType = GEOS_MULTIPOLYGON;
1847 std::vector<geos::unique_ptr> geomVector;
1848 geomVector.reserve(
c->numGeometries() );
1849 for (
int i = 0; i <
c->numGeometries(); ++i )
1856 geomVector.emplace_back( std::move( geosGeom ) );
1858 return createGeosCollection( geosType, geomVector );
1865 if ( !polyhedralSurface )
1868 std::vector<geos::unique_ptr> geomVector;
1869 geomVector.reserve( polyhedralSurface->
numPatches() );
1870 for (
int i = 0; i < polyhedralSurface->
numPatches(); ++i )
1877 geomVector.emplace_back( std::move( geosPolygon ) );
1880 return createGeosCollection( GEOS_MULTIPOLYGON, geomVector );
1887 return createGeosPoint(
static_cast<const QgsPoint *
>( geom ), coordDims, precision, flags );
1890 return createGeosLinestring(
static_cast<const QgsLineString *
>( geom ), precision, flags );
1893 return createGeosPolygon(
static_cast<const QgsPolygon *
>( geom ), precision, flags );
1905 if ( !mGeos || !geom )
1916 const double gridSize = parameters.
gridSize();
1924 case OverlayIntersection:
1927 opGeom.reset( GEOSIntersectionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1931 opGeom.reset( GEOSIntersection_r( context, mGeos.get(), geosGeom.get() ) );
1935 case OverlayDifference:
1938 opGeom.reset( GEOSDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1942 opGeom.reset( GEOSDifference_r( context, mGeos.get(), geosGeom.get() ) );
1951 unionGeometry.reset( GEOSUnionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1955 unionGeometry.reset( GEOSUnion_r( context, mGeos.get(), geosGeom.get() ) );
1958 if ( unionGeometry && GEOSGeomTypeId_r( context, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1960 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, unionGeometry.get() ) );
1963 unionGeometry = std::move( mergedLines );
1967 opGeom = std::move( unionGeometry );
1971 case OverlaySymDifference:
1974 opGeom.reset( GEOSSymDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1978 opGeom.reset( GEOSSymDifference_r( context, mGeos.get(), geosGeom.get() ) );
1984 catch ( QgsGeosException &e )
1989 *errorMsg = e.what();
1995bool QgsGeos::relation(
const QgsAbstractGeometry *geom, Relation r, QString *errorMsg )
const
1997 if ( !mGeos || !geom )
2009 bool result =
false;
2012 if ( mGeosPrepared )
2016 case RelationIntersects:
2017 result = ( GEOSPreparedIntersects_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2019 case RelationTouches:
2020 result = ( GEOSPreparedTouches_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2022 case RelationCrosses:
2023 result = ( GEOSPreparedCrosses_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2025 case RelationWithin:
2026 result = ( GEOSPreparedWithin_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2028 case RelationContains:
2029 result = ( GEOSPreparedContains_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2031 case RelationDisjoint:
2032 result = ( GEOSPreparedDisjoint_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2034 case RelationOverlaps:
2035 result = ( GEOSPreparedOverlaps_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2043 case RelationIntersects:
2044 result = ( GEOSIntersects_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2046 case RelationTouches:
2047 result = ( GEOSTouches_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2049 case RelationCrosses:
2050 result = ( GEOSCrosses_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2052 case RelationWithin:
2053 result = ( GEOSWithin_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2055 case RelationContains:
2056 result = ( GEOSContains_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2058 case RelationDisjoint:
2059 result = ( GEOSDisjoint_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2061 case RelationOverlaps:
2062 result = ( GEOSOverlaps_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2066 catch ( QgsGeosException &e )
2071 *errorMsg = e.what();
2111 geos.reset( GEOSBufferWithStyle_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( endCapStyle ),
static_cast< int >( joinStyle ), miterLimit ) );
2161 geos.reset( GEOSGetCentroid_r( context, mGeos.get() ) );
2166 GEOSGeomGetX_r( context,
geos.get(), &x );
2167 GEOSGeomGetY_r( context,
geos.get(), &y );
2203 geos.reset( GEOSPointOnSurface_r( context, mGeos.get() ) );
2205 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
2210 GEOSGeomGetX_r( context,
geos.get(), &x );
2211 GEOSGeomGetY_r( context,
geos.get(), &y );
2228 std::unique_ptr< QgsAbstractGeometry > cHullGeom =
fromGeos( cHull.get() );
2229 return cHullGeom.release();
2234std::unique_ptr< QgsAbstractGeometry >
QgsGeos::concaveHull(
double targetPercent,
bool allowHoles, QString *errorMsg )
const
2236#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
2237 ( void ) allowHoles;
2238 ( void ) targetPercent;
2240 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHull requires a QGIS build based on GEOS 3.11 or later" ) );
2251 return concaveHullGeom;
2259#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12
2261 ( void ) invalidEdges;
2263 throw QgsNotSupportedException( QObject::tr(
"Validating coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2268 *errorMsg = u
"Input geometry was not set"_s;
2276 const int result = GEOSCoverageIsValid_r( context, mGeos.get(), gapWidth, invalidEdges ? &invalidEdgesGeos :
nullptr );
2277 if ( invalidEdges && invalidEdgesGeos )
2279 *invalidEdges =
fromGeos( invalidEdgesGeos );
2281 if ( invalidEdgesGeos )
2283 GEOSGeom_destroy_r( context, invalidEdgesGeos );
2284 invalidEdgesGeos =
nullptr;
2304#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12
2306 ( void ) preserveBoundary;
2308 throw QgsNotSupportedException( QObject::tr(
"Simplifying coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2313 *errorMsg = u
"Input geometry was not set"_s;
2320 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom =
fromGeos( simplified.get() );
2321 return simplifiedGeom;
2332 *errorMsg = u
"Input geometry was not set"_s;
2339 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( unioned.get() );
2350 *errorMsg = QObject::tr(
"QGIS geometry cannot be converted to a GEOS geometry",
"GEOS Error" );
2359 char res = GEOSisValidDetail_r( context, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
2360 const bool invalid = res != 1;
2365 error = QString( r );
2366 GEOSFree_r( context, r );
2369 if ( invalid && errorMsg )
2372 static const std::map< QString, QString > sTranslatedErrors {
2373 { u
"topology validation error"_s, QObject::tr(
"Topology validation error",
"GEOS Error" ) },
2374 { u
"repeated point"_s, QObject::tr(
"Repeated point",
"GEOS Error" ) },
2375 { u
"hole lies outside shell"_s, QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) },
2376 { u
"holes are nested"_s, QObject::tr(
"Holes are nested",
"GEOS Error" ) },
2377 { u
"interior is disconnected"_s, QObject::tr(
"Interior is disconnected",
"GEOS Error" ) },
2378 { u
"self-intersection"_s, QObject::tr(
"Self-intersection",
"GEOS Error" ) },
2379 { u
"ring self-intersection"_s, QObject::tr(
"Ring self-intersection",
"GEOS Error" ) },
2380 { u
"nested shells"_s, QObject::tr(
"Nested shells",
"GEOS Error" ) },
2381 { u
"duplicate rings"_s, QObject::tr(
"Duplicate rings",
"GEOS Error" ) },
2382 { u
"too few points in geometry component"_s, QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) },
2383 { u
"invalid coordinate"_s, QObject::tr(
"Invalid coordinate",
"GEOS Error" ) },
2384 { u
"ring is not closed"_s, QObject::tr(
"Ring is not closed",
"GEOS Error" ) },
2387 const auto translatedError = sTranslatedErrors.find( error.toLower() );
2388 if ( translatedError != sTranslatedErrors.end() )
2389 *errorMsg = translatedError->second;
2393 if ( g1 && errorLoc )
2399 GEOSGeom_destroy_r( context, g1 );
2409 if ( !mGeos || !geom )
2455GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
2459 std::unique_ptr< QgsLineString > segmentized;
2465 line = segmentized.get();
2472 GEOSCoordSequence *coordSeq =
nullptr;
2474 const int numPoints = line->
numPoints();
2476 const bool hasZ = line->
is3D();
2478#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10 )
2481 if ( !forceClose || ( line->
pointN( 0 ) == line->
pointN( numPoints - 1 ) ) )
2486 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, line->
xData(), line->
yData(), line->
zData(),
nullptr, numPoints );
2489 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points"_s.arg( numPoints ) );
2497 QVector< double > x = line->
xVector();
2498 if ( numPoints > 0 )
2499 x.append( x.at( 0 ) );
2500 QVector< double > y = line->
yVector();
2501 if ( numPoints > 0 )
2502 y.append( y.at( 0 ) );
2503 QVector< double > z = line->
zVector();
2504 if ( hasZ && numPoints > 0 )
2505 z.append( z.at( 0 ) );
2508 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, x.constData(), y.constData(), !hasZ ?
nullptr : z.constData(),
nullptr, numPoints + 1 );
2511 QgsDebugError( u
"GEOS Exception: Could not create closed coordinate sequence for %1 points"_s.arg( numPoints + 1 ) );
2522 const bool hasM =
false;
2533 int numOutPoints = numPoints;
2534 if ( forceClose && ( line->
pointN( 0 ) != line->
pointN( numPoints - 1 ) ) )
2541 coordSeq = GEOSCoordSeq_create_r( context, numOutPoints, coordDims );
2544 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions"_s.arg( numPoints ).arg( coordDims ) );
2548 const double *xData = line->
xData();
2549 const double *yData = line->
yData();
2550 const double *zData = hasZ ? line->
zData() :
nullptr;
2551 const double *mData = hasM ? line->
mData() :
nullptr;
2553 if ( precision > 0. )
2555 for (
int i = 0; i < numOutPoints; ++i )
2557 if ( i >= numPoints )
2560 xData = line->
xData();
2561 yData = line->
yData();
2562 zData = hasZ ? line->
zData() :
nullptr;
2563 mData = hasM ? line->
mData() :
nullptr;
2567 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
2571 GEOSCoordSeq_setXY_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
2575 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2581 for (
int i = 0; i < numOutPoints; ++i )
2583 if ( i >= numPoints )
2586 xData = line->
xData();
2587 yData = line->
yData();
2588 zData = hasZ ? line->
zData() :
nullptr;
2589 mData = hasM ? line->
mData() :
nullptr;
2593 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, *xData++, *yData++, *zData++ );
2597 GEOSCoordSeq_setXY_r( context, coordSeq, i, *xData++, *yData++ );
2601 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2611geos::unique_ptr
QgsGeos::createGeosPoint( const QgsAbstractGeometry *point,
int coordDims,
double precision, Qgis::GeosCreationFlags )
2617 return createGeosPointXY( pt->
x(), pt->
y(), pt->
is3D(), pt->
z(), pt->
isMeasure(), pt->
m(), coordDims, precision );
2629 if ( coordDims == 2 )
2632 if ( precision > 0. )
2633 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
2635 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, x, y ) );
2639 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( context, 1, coordDims );
2642 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions"_s.arg( coordDims ) );
2645 if ( precision > 0. )
2647 GEOSCoordSeq_setX_r( context, coordSeq, 0, std::round( x / precision ) * precision );
2648 GEOSCoordSeq_setY_r( context, coordSeq, 0, std::round( y / precision ) * precision );
2651 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, std::round( z / precision ) * precision );
2656 GEOSCoordSeq_setX_r( context, coordSeq, 0, x );
2657 GEOSCoordSeq_setY_r( context, coordSeq, 0, y );
2660 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, z );
2666 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 3, m );
2669 geosPoint.reset( GEOSGeom_createPoint_r( context, coordSeq ) );
2675geos::unique_ptr
QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve,
double precision, Qgis::GeosCreationFlags )
2681 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c, precision );
2694geos::unique_ptr
QgsGeos::createGeosPolygon( const QgsAbstractGeometry *poly,
double precision, Qgis::GeosCreationFlags flags )
2700 const QgsCurve *exteriorRing = polygon->
exteriorRing();
2701 if ( !exteriorRing )
2710 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( context, createCoordinateSequence( exteriorRing, precision,
true ) ) );
2713 QList< const QgsCurve * > holesToExport;
2714 holesToExport.reserve( nInteriorRings );
2715 for (
int i = 0; i < nInteriorRings; ++i )
2717 const QgsCurve *interiorRing = polygon->
interiorRing( i );
2720 holesToExport << interiorRing;
2725 if ( !holesToExport.empty() )
2728 for (
int i = 0; i < holesToExport.size(); ++i )
2730 holes[i] = GEOSGeom_createLinearRing_r( context, createCoordinateSequence( holesToExport[i], precision,
true ) );
2734 geosPolygon.reset( GEOSGeom_createPolygon_r( context, exteriorRingGeos.release(), holes, holesToExport.size() ) );
2754 offset.reset( GEOSOffsetCurve_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( joinStyle ), miterLimit ) );
2766 return fromGeos( res.get() ).release();
2781 GEOSBufferParams_setSingleSided_r( context, bp.get(), 1 );
2782 GEOSBufferParams_setQuadrantSegments_r( context, bp.get(), segments );
2783 GEOSBufferParams_setJoinStyle_r( context, bp.get(),
static_cast< int >( joinStyle ) );
2784 GEOSBufferParams_setMitreLimit_r( context, bp.get(), miterLimit );
2790 geos.reset( GEOSBufferWithParams_r( context, mGeos.get(), bp.get(),
distance ) );
2824 boundaryGeos =
asGeos( boundary );
2852 return std::numeric_limits< double >::quiet_NaN();
2860 return std::numeric_limits< double >::quiet_NaN();
2900 if ( !mGeos || !other )
2920 if ( !mGeos ||
mGeometry->dimension() == 0 )
2938 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
2942 int numGeoms = GEOSGetNumGeometries_r( context, mGeos.get() );
2943 if ( numGeoms == -1 )
2952 bool isMultiGeom =
false;
2953 int geosTypeId = GEOSGeomTypeId_r( context, mGeos.get() );
2954 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
2957 bool isLine = (
mGeometry->dimension() == 1 );
2964 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2968 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2973 if ( reshapedGeometry )
2979 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
2980 return reshapeResult;
2987 bool reshapeTookPlace =
false;
2992 for (
int i = 0; i < numGeoms; ++i )
2995 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2997 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2999 if ( currentReshapeGeometry )
3001 newGeoms[i] = currentReshapeGeometry.release();
3002 reshapeTookPlace =
true;
3006 newGeoms[i] = GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, mGeos.get(), i ) );
3013 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
3017 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
3021 if ( !newMultiGeom )
3030 if ( reshapeTookPlace )
3034 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
3035 return reshapedMultiGeom;
3058 if ( GEOSGeomTypeId_r( context, mGeos.get() ) != GEOS_MULTILINESTRING )
3064 double gridSize = parameters.
gridSize();
3067 geos::unique_ptr geosFixedSize( GEOSGeom_setPrecision_r( context, mGeos.get(), gridSize, 0 ) );
3068 geos.reset( GEOSLineMerge_r( context, geosFixedSize.get() ) );
3071 geos.reset( GEOSLineMerge_r( context, mGeos.get() ) );
3096 if ( mGeosPrepared )
3098 nearestCoord.reset( GEOSPreparedNearestPoints_r( context, mGeosPrepared.get(), otherGeom.get() ) );
3102 nearestCoord.reset( GEOSNearestPoints_r( context, mGeos.get(), otherGeom.get() ) );
3105 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx );
3106 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny );
3108 catch ( QgsGeosException &e )
3113 *errorMsg = e.what();
3118 return std::make_unique< QgsPoint >( nx, ny );
3123 if ( !mGeos || other.
isEmpty() )
3133 if ( !other || other->
isEmpty() )
3151 if ( !nearestCoord )
3154 *errorMsg = u
"GEOS returned no nearest points"_s;
3158 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx1 );
3159 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny1 );
3160 ( void ) GEOSCoordSeq_getX_r( context, nearestCoord.get(), 1, &nx2 );
3161 ( void ) GEOSCoordSeq_getY_r( context, nearestCoord.get(), 1, &ny2 );
3163 catch ( QgsGeosException &e )
3168 *errorMsg = e.what();
3173 auto line = std::make_unique< QgsLineString >();
3197 catch ( QgsGeosException &e )
3202 *errorMsg = e.what();
3217 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
3226 catch ( QgsGeosException &e )
3231 *errorMsg = e.what();
3248 lineGeosGeometries[validLines] = l.release();
3256 geos::unique_ptr result( GEOSPolygonize_r( context, lineGeosGeometries, validLines ) );
3257 for (
int i = 0; i < validLines; ++i )
3259 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3261 delete[] lineGeosGeometries;
3264 catch ( QgsGeosException &e )
3268 *errorMsg = e.what();
3270 for (
int i = 0; i < validLines; ++i )
3272 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3274 delete[] lineGeosGeometries;
3289 extentGeosGeom =
asGeos( extent, mPrecision );
3290 if ( !extentGeosGeom )
3300 geos.reset( GEOSVoronoiDiagram_r( context, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
3302 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3323 geos.reset( GEOSDelaunayTriangulation_r( context, mGeos.get(), tolerance, edgesOnly ) );
3325 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3337#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
3339 throw QgsNotSupportedException( QObject::tr(
"Calculating constrainedDelaunayTriangulation requires a QGIS build based on GEOS 3.11 or later" ) );
3350 geos.reset( GEOSConstrainedDelaunayTriangulation_r( context, mGeos.get() ) );
3352 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3357 std::unique_ptr< QgsAbstractGeometry > res =
fromGeos(
geos.get() );
3360 return std::unique_ptr< QgsAbstractGeometry >( collection->extractPartsByType(
Qgis::WkbType::Polygon,
true ) );
3372static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
3375 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( context, linestring );
3379 unsigned int coordSeqSize;
3380 if ( GEOSCoordSeq_getSize_r( context, coordSeq, &coordSeqSize ) == 0 )
3383 if ( coordSeqSize < 2 )
3386 GEOSCoordSeq_getX_r( context, coordSeq, 0, &x1 );
3387 GEOSCoordSeq_getY_r( context, coordSeq, 0, &y1 );
3388 GEOSCoordSeq_getX_r( context, coordSeq, coordSeqSize - 1, &x2 );
3389 GEOSCoordSeq_getY_r( context, coordSeq, coordSeqSize - 1, &y2 );
3397 double x1, y1, x2, y2;
3398 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
3401 double rx1, ry1, rx2, ry2;
3402 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
3405 bool intersectionAtOrigLineEndpoint = ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) != ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
3406 bool intersectionAtReshapeLineEndpoint = ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) || ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
3410 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
3414 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
3415 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, geoms, 2 ) );
3420 double x1res, y1res, x2res, y2res;
3421 if ( !_linestringEndpoints( res.get(), x1res, y1res, x2res, y2res ) )
3423 if ( ( x1res == x2 && y1res == y2 ) || ( x2res == x1 && y2res == y1 ) )
3424 res.reset( GEOSReverse_r( context, res.get() ) );
3435 if ( !line || !reshapeLineGeos )
3438 bool atLeastTwoIntersections =
false;
3439 bool oneIntersection =
false;
3440 QgsPointXY oneIntersectionPoint;
3446 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, line, reshapeLineGeos ) );
3447 if ( intersectGeom )
3449 const int geomType = GEOSGeomTypeId_r( context, intersectGeom.get() );
3450 atLeastTwoIntersections = ( geomType == GEOS_MULTIPOINT && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 1 )
3451 || ( geomType == GEOS_GEOMETRYCOLLECTION && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 )
3452 || ( geomType == GEOS_MULTILINESTRING && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 );
3454 if ( GEOSGeomTypeId_r( context, intersectGeom.get() ) == GEOS_POINT )
3456 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( context, intersectGeom.get() );
3458 GEOSCoordSeq_getX_r( context, intersectionCoordSeq, 0, &xi );
3459 GEOSCoordSeq_getY_r( context, intersectionCoordSeq, 0, &yi );
3460 oneIntersection =
true;
3461 oneIntersectionPoint = QgsPointXY( xi, yi );
3465 catch ( QgsGeosException & )
3467 atLeastTwoIntersections =
false;
3471 if ( oneIntersection )
3472 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
3474 if ( !atLeastTwoIntersections )
3478 double x1, y1, x2, y2;
3479 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
3482 geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1,
false, 0,
false, 0, 2, precision );
3483 geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2,
false, 0,
false, 0, 2, precision );
3485 bool isRing =
false;
3486 if ( GEOSGeomTypeId_r( context, line ) == GEOS_LINEARRING || GEOSEquals_r( context, beginLineVertex.get(), endLineVertex.get() ) == 1 )
3491 if ( !nodedGeometry )
3497 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, nodedGeometry.get() ) );
3503 int numMergedLines = GEOSGetNumGeometries_r( context, mergedLines.get() );
3504 if ( numMergedLines < 2 )
3506 if ( numMergedLines == 1 )
3515 QVector<GEOSGeometry *> resultLineParts;
3516 QVector<GEOSGeometry *> probableParts;
3518 for (
int i = 0; i < numMergedLines; ++i )
3520 const GEOSGeometry *currentGeom = GEOSGetGeometryN_r( context, mergedLines.get(), i );
3523 bool alreadyAdded =
false;
3525 double bufferDistance = std::pow( 10.0L, geomDigits( currentGeom ) - 11 );
3526 for (
const GEOSGeometry *other : std::as_const( resultLineParts ) )
3528 GEOSHausdorffDistance_r( context, currentGeom, other, &
distance );
3531 alreadyAdded =
true;
3538 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( context, currentGeom );
3539 unsigned int currentCoordSeqSize;
3540 GEOSCoordSeq_getSize_r( context, currentCoordSeq, ¤tCoordSeqSize );
3541 if ( currentCoordSeqSize < 2 )
3545 double xBegin, xEnd, yBegin, yEnd;
3546 GEOSCoordSeq_getX_r( context, currentCoordSeq, 0, &xBegin );
3547 GEOSCoordSeq_getY_r( context, currentCoordSeq, 0, &yBegin );
3548 GEOSCoordSeq_getX_r( context, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
3549 GEOSCoordSeq_getY_r( context, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
3550 geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin,
false, 0,
false, 0, 2, precision );
3551 geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd,
false, 0,
false, 0, 2, precision );
3554 int nEndpointsOnOriginalLine = 0;
3555 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
3556 nEndpointsOnOriginalLine += 1;
3558 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
3559 nEndpointsOnOriginalLine += 1;
3562 int nEndpointsSameAsOriginalLine = 0;
3563 if ( GEOSEquals_r( context, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1 || GEOSEquals_r( context, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3564 nEndpointsSameAsOriginalLine += 1;
3566 if ( GEOSEquals_r( context, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1 || GEOSEquals_r( context, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3567 nEndpointsSameAsOriginalLine += 1;
3570 bool currentGeomOverlapsOriginalGeom =
false;
3571 bool currentGeomOverlapsReshapeLine =
false;
3572 if ( lineContainedInLine( currentGeom, line ) == 1 )
3573 currentGeomOverlapsOriginalGeom =
true;
3575 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
3576 currentGeomOverlapsReshapeLine =
true;
3579 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3581 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3584 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3586 probableParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3588 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3590 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3592 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3594 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3596 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
3598 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3603 if ( isRing && !probableParts.isEmpty() )
3607 double maxLength = -std::numeric_limits<double>::max();
3608 double currentLength = 0;
3609 for (
int i = 0; i < probableParts.size(); ++i )
3611 currentGeom = probableParts.at( i );
3612 GEOSLength_r( context, currentGeom, ¤tLength );
3613 if ( currentLength > maxLength )
3615 maxLength = currentLength;
3616 maxGeom.reset( currentGeom );
3620 GEOSGeom_destroy_r( context, currentGeom );
3623 resultLineParts.push_back( maxGeom.release() );
3627 if ( resultLineParts.empty() )
3630 if ( resultLineParts.size() == 1 )
3632 result.reset( resultLineParts[0] );
3637 for (
int i = 0; i < resultLineParts.size(); ++i )
3639 lineArray[i] = resultLineParts[i];
3643 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
3647 result.reset( GEOSLineMerge_r( context, multiLineGeom.get() ) );
3651 if ( GEOSGeomTypeId_r( context, result.get() ) != GEOS_LINESTRING )
3657 bool reverseLine =
false;
3661 char isResultCCW = 0, isOriginCCW = 0;
3662 if ( GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, result.get() ), &isResultCCW ) && GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, line ), &isOriginCCW ) )
3665 reverseLine = ( isOriginCCW == 1 && isResultCCW == 0 ) || ( isOriginCCW == 0 && isResultCCW == 1 );
3671 double x1res, y1res, x2res, y2res;
3672 if ( !_linestringEndpoints( result.get(), x1res, y1res, x2res, y2res ) )
3674 geos::unique_ptr beginResultLineVertex = createGeosPointXY( x1res, y1res,
false, 0,
false, 0, 2, precision );
3675 geos::unique_ptr endResultLineVertex = createGeosPointXY( x2res, y2res,
false, 0,
false, 0, 2, precision );
3676 reverseLine = GEOSEquals_r( context, beginLineVertex.get(), endResultLineVertex.get() ) == 1 || GEOSEquals_r( context, endLineVertex.get(), beginResultLineVertex.get() ) == 1;
3679 result.reset( GEOSReverse_r( context, result.get() ) );
3687 int nIntersections = 0;
3688 int lastIntersectingRing = -2;
3692 int nRings = GEOSGetNumInteriorRings_r( context, polygon );
3697 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( context, polygon );
3698 if ( GEOSIntersects_r( context, outerRing, reshapeLineGeos ) == 1 )
3701 lastIntersectingRing = -1;
3702 lastIntersectingGeom = outerRing;
3710 for (
int i = 0; i < nRings; ++i )
3712 innerRings[i] = GEOSGetInteriorRingN_r( context, polygon, i );
3713 if ( GEOSIntersects_r( context, innerRings[i], reshapeLineGeos ) == 1 )
3716 lastIntersectingRing = i;
3717 lastIntersectingGeom = innerRings[i];
3721 catch ( QgsGeosException & )
3726 if ( nIntersections != 1 )
3728 delete[] innerRings;
3733 geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
3734 if ( !reshapeResult )
3736 delete[] innerRings;
3742 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( context, reshapeResult.get() );
3743 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( context, reshapeSequence );
3745 reshapeResult.reset();
3749 newRing = GEOSGeom_createLinearRing_r( context, newCoordSequence );
3751 catch ( QgsGeosException & )
3758 delete[] innerRings;
3763 if ( lastIntersectingRing == -1 )
3764 newOuterRing = newRing;
3766 newOuterRing = GEOSGeom_clone_r( context, outerRing );
3769 QVector<GEOSGeometry *> ringList;
3772 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( context, GEOSGeom_clone_r( context, newOuterRing ),
nullptr, 0 );
3773 if ( outerRingPoly )
3775 ringList.reserve( nRings );
3777 for (
int i = 0; i < nRings; ++i )
3779 if ( lastIntersectingRing == i )
3780 currentRing = newRing;
3782 currentRing = GEOSGeom_clone_r( context, innerRings[i] );
3785 if ( GEOSContains_r( context, outerRingPoly, currentRing ) == 1 )
3786 ringList.push_back( currentRing );
3788 GEOSGeom_destroy_r( context, currentRing );
3791 GEOSGeom_destroy_r( context, outerRingPoly );
3795 for (
int i = 0; i < ringList.size(); ++i )
3796 newInnerRings[i] = ringList.at( i );
3798 delete[] innerRings;
3800 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( context, newOuterRing, newInnerRings, ringList.size() ) );
3801 delete[] newInnerRings;
3803 return reshapedPolygon;
3808 if ( !line1 || !line2 )
3813 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
3820 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, bufferGeom.get(), line1 ) );
3823 double intersectGeomLength;
3826 GEOSLength_r( context, intersectionGeom.get(), &intersectGeomLength );
3827 GEOSLength_r( context, line1, &line1Length );
3829 double intersectRatio = line1Length / intersectGeomLength;
3830 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
3838 if ( !point || !line )
3841 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
3844 geos::unique_ptr lineBuffer( GEOSBuffer_r( context, line, bufferDistance, 8 ) );
3848 bool contained =
false;
3849 if ( GEOSContains_r( context, lineBuffer.get(), point ) == 1 )
3862 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( context, bbox.get() );
3866 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( context, bBoxRing );
3868 if ( !bBoxCoordSeq )
3871 unsigned int nCoords = 0;
3872 if ( !GEOSCoordSeq_getSize_r( context, bBoxCoordSeq, &nCoords ) )
3876 for (
unsigned int i = 0; i < nCoords - 1; ++i )
3879 GEOSCoordSeq_getX_r( context, bBoxCoordSeq, i, &t );
3882 digits = std::ceil( std::log10( std::fabs( t ) ) );
3883 if ( digits > maxDigits )
3886 GEOSCoordSeq_getY_r( context, bBoxCoordSeq, i, &t );
3887 digits = std::ceil( std::log10( std::fabs( t ) ) );
3888 if ( digits > maxDigits )
Provides global constants and enumerations for use throughout the application.
BufferSide
Side of line to buffer.
@ Right
Buffer to right of line.
GeometryOperationResult
Success or failure of a geometry operation.
@ AddPartNotMultiGeometry
The source geometry is not multi.
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
@ RejectOnInvalidSubGeometry
Don't allow geometries with invalid sub-geometries to be created.
@ SkipEmptyInteriorRings
Skip any empty polygon interior ring.
QFlags< GeosCreationFlag > GeosCreationFlags
Geos geometry creation behavior flags.
JoinStyle
Join styles for buffers.
EndCapStyle
End cap styles for buffers.
CoverageValidityResult
Coverage validity results.
@ Valid
Coverage is valid.
@ Invalid
Coverage is invalid. Invalidity includes polygons that overlap, that have gaps smaller than the gap w...
@ Error
An exception occurred while determining validity.
MakeValidMethod
Algorithms to use when repairing invalid geometries.
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
WkbType
The WKB type describes the number of dimensions a geometry has.
@ LineStringZM
LineStringZM.
@ GeometryCollection
GeometryCollection.
@ LineStringZ
LineStringZ.
@ PolyhedralSurface
PolyhedralSurface.
Abstract base class for all geometries.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual bool isEmpty() const
Returns true if the geometry is empty.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
Abstract base class for curved geometry type.
virtual QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
static Qgis::GeometryOperationResult addPart(QgsAbstractGeometry *geometry, std::unique_ptr< QgsAbstractGeometry > part)
Add a part to multi type geometry.
const QgsAbstractGeometry * mGeometry
EngineOperationResult
Success or failure of a geometry operation.
@ NothingHappened
Nothing happened, without any error.
@ InvalidBaseGeometry
The geometry on which the operation occurs is not valid.
@ InvalidInput
The input is not valid.
@ NodedGeometryError
Error occurred while creating a noded geometry.
@ EngineError
Error occurred in the geometry engine.
@ SplitCannotSplitPoint
Points cannot be split.
@ Success
Operation succeeded.
QgsGeometryEngine(const QgsAbstractGeometry *geometry)
void logError(const QString &engineName, const QString &message) const
Logs an error message encountered during an operation.
static std::unique_ptr< QgsGeometryCollection > createCollectionOfType(Qgis::WkbType type)
Returns a new geometry collection matching a specified WKB type.
Encapsulates parameters under which a geometry operation is performed.
double gridSize() const
Returns the grid size which will be used to snap vertices of a geometry.
static double sqrDistance2D(double x1, double y1, double x2, double y2)
Returns the squared 2D distance between (x1, y1) and (x2, y2).
A geometry is the spatial representation of a feature.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
Used to create and store a proj context object, correctly freeing the context upon destruction.
static GEOSContextHandle_t get()
Returns a thread local instance of a GEOS context, safe for use in the current thread.
double frechetDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
double hausdorffDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
bool touches(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom touches this.
static geos::unique_ptr offsetCurve(const GEOSGeometry *geometry, double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit, QString *errorMsg=nullptr)
Directly calculates the offset curve for a GEOS geometry object and returns a GEOS geometry result.
QgsAbstractGeometry * symDifference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const override
Calculate the symmetric difference of this and geom.
bool isValid(QString *errorMsg=nullptr, bool allowSelfTouchingHoles=false, QgsGeometry *errorLoc=nullptr) const override
Returns true if the geometry is valid.
std::unique_ptr< QgsAbstractGeometry > subdivide(int maxNodes, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const
Subdivides the geometry.
QgsAbstractGeometry * intersection(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const override
Calculate the intersection of this and geom.
std::unique_ptr< QgsAbstractGeometry > constrainedDelaunayTriangulation(QString *errorMsg=nullptr) const
Returns a constrained Delaunay triangulation for the vertices of the geometry.
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
std::unique_ptr< QgsAbstractGeometry > closestPoint(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the closest point on the geometry to the other geometry.
std::unique_ptr< QgsAbstractGeometry > reshapeGeometry(const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg=nullptr) const
Reshapes the geometry using a line.
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlags())
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
QgsAbstractGeometry * difference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const override
Calculate the difference of this and geom.
EngineOperationResult splitGeometry(const QgsLineString &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, QString *errorMsg=nullptr, bool skipIntersectionCheck=false) const override
Splits this geometry according to a given line.
std::unique_ptr< QgsAbstractGeometry > sharedPaths(const QgsAbstractGeometry *other, QString *errorMsg=nullptr) const
Find paths shared between the two given lineal geometries (this and other).
std::unique_ptr< QgsAbstractGeometry > clip(const QgsRectangle &rectangle, QString *errorMsg=nullptr) const
Performs a fast, non-robust intersection between the geometry and a rectangle.
std::unique_ptr< QgsAbstractGeometry > node(QString *errorMsg=nullptr) const
Returns a (Multi)LineString representing the fully noded version of a collection of linestrings.
double minimumClearance(QString *errorMsg=nullptr) const
Computes the minimum clearance of a geometry.
std::unique_ptr< QgsAbstractGeometry > singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit, QString *errorMsg=nullptr) const
Returns a single sided buffer for a geometry.
bool disjoint(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom is disjoint from this.
std::unique_ptr< QgsAbstractGeometry > mergeLines(QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
std::unique_ptr< QgsAbstractGeometry > makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false, QString *errorMsg=nullptr) const
Repairs the geometry using GEOS make valid routine.
std::unique_ptr< QgsAbstractGeometry > voronoiDiagram(const QgsAbstractGeometry *extent=nullptr, double tolerance=0.0, bool edgesOnly=false, QString *errorMsg=nullptr) const
Creates a Voronoi diagram for the nodes contained within the geometry.
QgsAbstractGeometry * simplify(double tolerance, QString *errorMsg=nullptr) const override
std::unique_ptr< QgsAbstractGeometry > unionCoverage(QString *errorMsg=nullptr) const
Optimized union algorithm for polygonal inputs that are correctly noded and do not overlap.
static std::unique_ptr< QgsPolygon > fromGeosPolygon(const GEOSGeometry *geos)
double hausdorffDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
std::unique_ptr< QgsAbstractGeometry > minimumClearanceLine(QString *errorMsg=nullptr) const
Returns a LineString whose endpoints define the minimum clearance of a geometry.
QgsAbstractGeometry * envelope(QString *errorMsg=nullptr) const override
QString relate(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Returns the Dimensional Extended 9 Intersection Model (DE-9IM) representation of the relationship bet...
bool crosses(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom crosses this.
QgsAbstractGeometry * convexHull(QString *errorMsg=nullptr) const override
Calculate the convex hull of this.
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
std::unique_ptr< QgsAbstractGeometry > minimumWidth(QString *errorMsg=nullptr) const
Returns a linestring geometry which represents the minimum diameter of the geometry.
bool isSimple(QString *errorMsg=nullptr) const override
Determines whether the geometry is simple (according to OGC definition).
QgsGeos(const QgsAbstractGeometry *geometry, double precision=0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
GEOS geometry engine constructor.
QgsAbstractGeometry * combine(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const override
Calculate the combination of this and geom.
bool within(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom is within this.
std::unique_ptr< QgsAbstractGeometry > shortestLine(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the shortest line joining this geometry to the other geometry.
std::unique_ptr< QgsAbstractGeometry > concaveHull(double targetPercent, bool allowHoles=false, QString *errorMsg=nullptr) const
Returns a possibly concave geometry that encloses the input geometry.
void prepareGeometry() override
Prepares the geometry, so that subsequent calls to spatial relation methods are much faster.
static std::unique_ptr< QgsAbstractGeometry > fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
QgsAbstractGeometry * interpolate(double distance, QString *errorMsg=nullptr) const override
bool distanceWithin(const QgsAbstractGeometry *geom, double maxdistance, QString *errorMsg=nullptr) const override
Checks if geom is within maxdistance distance from this geometry.
bool isEqual(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if this is equal to geom.
bool contains(double x, double y, QString *errorMsg=nullptr) const
Returns true if the geometry contains the point at (x, y).
static QgsGeometry polygonize(const QVector< const QgsAbstractGeometry * > &geometries, QString *errorMsg=nullptr)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
double frechetDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
bool relatePattern(const QgsAbstractGeometry *geom, const QString &pattern, QString *errorMsg=nullptr) const override
Tests whether two geometries are related by a specified Dimensional Extended 9 Intersection Model (DE...
QgsPoint * centroid(QString *errorMsg=nullptr) const override
Calculates the centroid of this.
double lineLocatePoint(const QgsPoint &point, QString *errorMsg=nullptr) const
Returns a distance representing the location along this linestring of the closest point on this lines...
std::unique_ptr< QgsAbstractGeometry > simplifyCoverageVW(double tolerance, bool preserveBoundary, QString *errorMsg=nullptr) const
Operates on a coverage (represented as a list of polygonal geometry with exactly matching edge geomet...
bool isEmpty(QString *errorMsg=nullptr) const override
static Qgis::GeometryOperationResult addPart(QgsGeometry &geometry, GEOSGeometry *newPart)
Adds a new island polygon to a multipolygon feature.
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom intersects this.
void geometryChanged() override
Should be called whenever the geometry associated with the engine has been modified and the engine mu...
bool overlaps(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom overlaps this.
double area(QString *errorMsg=nullptr) const override
std::unique_ptr< QgsAbstractGeometry > delaunayTriangulation(double tolerance=0.0, bool edgesOnly=false, QString *errorMsg=nullptr) const
Returns the Delaunay triangulation for the vertices of the geometry.
double length(QString *errorMsg=nullptr) const override
std::unique_ptr< QgsAbstractGeometry > largestEmptyCircle(double tolerance, const QgsAbstractGeometry *boundary=nullptr, QString *errorMsg=nullptr) const
Constructs the Largest Empty Circle for a set of obstacle geometries, up to a specified tolerance.
Qgis::CoverageValidityResult validateCoverage(double gapWidth, std::unique_ptr< QgsAbstractGeometry > *invalidEdges, QString *errorMsg=nullptr) const
Analyze a coverage (represented as a collection of polygonal geometry with exactly matching edge geom...
static QgsPoint coordSeqPoint(const GEOSCoordSequence *cs, int i, bool hasZ, bool hasM)
QgsPoint * pointOnSurface(QString *errorMsg=nullptr) const override
Calculate a point that is guaranteed to be on the surface of this.
static QgsGeometry geometryFromGeos(GEOSGeometry *geos)
Creates a new QgsGeometry object, feeding in a geometry in GEOS format.
Line string geometry type, with support for z-dimension and m-values.
const double * yData() const
Returns a const pointer to the y vertex data.
const double * xData() const
Returns a const pointer to the x vertex data.
QVector< double > xVector() const
Returns the x vertex values as a vector.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the linestring does not have z values.
int numPoints() const override
Returns the number of points in the curve.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
QVector< double > yVector() const
Returns the y vertex values as a vector.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
QVector< double > zVector() const
Returns the z vertex values as a vector.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the linestring does not have m values.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
Point geometry type, with support for z-dimension and m-values.
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
bool isEmpty() const override
Returns true if the geometry is empty.
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Polyhedral surface geometry type.
int numPatches() const
Returns the number of patches contained with the polyhedral surface.
const QgsPolygon * patchN(int i) const
Retrieves a patch from the polyhedral surface.
A rectangle specified with double values.
void setYMinimum(double y)
Set the minimum y value.
void setXMinimum(double x)
Set the minimum x value.
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
static Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
Contains geos related utilities and functions.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
std::unique_ptr< GEOSCoordSequence, GeosDeleter > coord_sequence_unique_ptr
Scoped GEOS coordinate sequence pointer.
std::unique_ptr< GEOSBufferParams, GeosDeleter > buffer_params_unique_ptr
Scoped GEOS buffer params pointer.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsPoint > QgsPointSequence
#define DEFAULT_QUADRANT_SEGMENTS
#define CATCH_GEOS_WITH_ERRMSG(r)
#define QgsDebugError(str)
QLineF segment(int index, QRectF rect, double radius)
Utility class for identifying a unique vertex within a geometry.
void CORE_EXPORT operator()(GEOSGeometry *geom) const
Destroys the GEOS geometry geom, using the static QGIS geos context.
struct GEOSGeom_t GEOSGeometry