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, ... )
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,
234 keepCollapsed ? 1 : 0 );
239 geos.reset( GEOSMakeValidWithParams_r( context, mGeos.get(), params ) );
240 GEOSMakeValidParams_destroy_r( context, params );
242 catch ( QgsGeosException &e )
246 *errorMsg = e.what();
248 GEOSMakeValidParams_destroy_r( context, params );
277 std::unique_ptr< QgsAbstractGeometry > geom =
fromGeos( newPart );
284 mGeosPrepared.reset();
318 return overlay( geom, OverlayIntersection, errorMsg, parameters ).release();
323 return overlay( geom, OverlayDifference, errorMsg, parameters ).release();
338 catch ( QgsGeosException &e )
343 *errorMsg = e.what();
352 int partType = GEOSGeomTypeId_r( context, currentPart );
355 if ( partType == GEOS_POINT )
366 if ( partType == GEOS_MULTILINESTRING || partType == GEOS_MULTIPOLYGON || partType == GEOS_GEOMETRYCOLLECTION )
368 int partCount = GEOSGetNumGeometries_r( context, currentPart );
369 for (
int i = 0; i < partCount; ++i )
371 subdivideRecursive( GEOSGetGeometryN_r( context, currentPart, i ), maxNodes, depth, parts, clipRect, gridSize );
382 int vertexCount = GEOSGetNumCoordinates_r( context, currentPart );
383 if ( vertexCount == 0 )
387 else if ( vertexCount < maxNodes )
394 double width = clipRect.
width();
395 double height = clipRect.
height();
396 QgsRectangle halfClipRect1 = clipRect;
397 QgsRectangle halfClipRect2 = clipRect;
398 if ( width > height )
411 halfClipRect1.
setYMinimum( halfClipRect1.
yMinimum() - std::numeric_limits<double>::epsilon() );
412 halfClipRect2.
setYMinimum( halfClipRect2.
yMinimum() - std::numeric_limits<double>::epsilon() );
413 halfClipRect1.
setYMaximum( halfClipRect1.
yMaximum() + std::numeric_limits<double>::epsilon() );
414 halfClipRect2.
setYMaximum( halfClipRect2.
yMaximum() + std::numeric_limits<double>::epsilon() );
418 halfClipRect1.
setXMinimum( halfClipRect1.
xMinimum() - std::numeric_limits<double>::epsilon() );
419 halfClipRect2.
setXMinimum( halfClipRect2.
xMinimum() - std::numeric_limits<double>::epsilon() );
420 halfClipRect1.
setXMaximum( halfClipRect1.
xMaximum() + std::numeric_limits<double>::epsilon() );
421 halfClipRect2.
setXMaximum( halfClipRect2.
xMaximum() + std::numeric_limits<double>::epsilon() );
433 clipPart1.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart1.get(), gridSize ) );
435 subdivideRecursive( clipPart1.get(), maxNodes, depth, parts, halfClipRect1, gridSize );
441 clipPart2.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart2.get(), gridSize ) );
443 subdivideRecursive( clipPart2.get(), maxNodes, depth, parts, halfClipRect2, gridSize );
455 maxNodes = std::max( maxNodes, 8 );
460 subdivideRecursive( mGeos.get(), maxNodes, 0, parts.get(),
mGeometry->boundingBox(), parameters.
gridSize() );
464 return std::move( parts );
469 return overlay( geom, OverlayUnion, errorMsg, parameters ).release();
474 std::vector<geos::unique_ptr> geosGeometries;
475 geosGeometries.reserve( geomList.size() );
481 geosGeometries.emplace_back(
asGeos( g, mPrecision ) );
488 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
491 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
495 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
500 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
501 return result.release();
506 std::vector<geos::unique_ptr> geosGeometries;
507 geosGeometries.reserve( geomList.size() );
513 geosGeometries.emplace_back(
asGeos( g.constGet(), mPrecision ) );
520 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
524 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
528 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
534 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
535 return result.release();
540 return overlay( geom, OverlaySymDifference, errorMsg, parameters ).release();
543static bool isZVerticalLine(
const QgsAbstractGeometry *geom,
double tolerance = 4 * std::numeric_limits<double>::epsilon() )
553 bool isVertical =
true;
556 const int nrPoints = line->numPoints();
564 const double sqrTolerance = tolerance * tolerance;
565 const double *lineX = line->xData();
566 const double *lineY = line->yData();
567 for (
int iVert = nrPoints - 1, jVert = 0; jVert < nrPoints; iVert = jVert++ )
597 otherGeosGeom =
asGeos( &firstPoint, mPrecision );
601 otherGeosGeom =
asGeos( geom, mPrecision );
604 if ( !otherGeosGeom )
612 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
614 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
618 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
634 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
643 GEOSPreparedDistance_r( context, mGeosPrepared.get(), point.get(), &
distance );
647 GEOSDistance_r( context, mGeos.get(), point.get(), &
distance );
676 otherGeosGeom =
asGeos( &firstPoint );
680 otherGeosGeom =
asGeos( geom, mPrecision );
683 if ( !otherGeosGeom )
696 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
698#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
699 return GEOSPreparedDistanceWithin_r( context, mGeosPrepared.get(), otherGeosGeom.get(), maxdist );
701 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
706#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
707 return GEOSDistanceWithin_r( context, mGeos.get(), otherGeosGeom.get(), maxdist );
709 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
724#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
727 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
733#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
734 return GEOSPreparedContainsXY_r( context, mGeosPrepared.get(), x, y ) == 1;
736 return GEOSPreparedContains_r( context, mGeosPrepared.get(), point.get() ) == 1;
740#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
741 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
746 result = ( GEOSContains_r( context, mGeos.get(), point.get() ) == 1 );
748 catch ( QgsGeosException &e )
753 *errorMsg = e.what();
770 if ( !otherGeosGeom )
793 if ( !otherGeosGeom )
816 if ( !otherGeosGeom )
839 if ( !otherGeosGeom )
855 if ( !mGeos || !geom )
860#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
868 return GEOSPreparedIntersectsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
870 catch ( QgsGeosException &e )
875 *errorMsg = e.what();
883 return relation( geom, RelationIntersects, errorMsg );
888 return relation( geom, RelationTouches, errorMsg );
893 return relation( geom, RelationCrosses, errorMsg );
898 return relation( geom, RelationWithin, errorMsg );
903 return relation( geom, RelationOverlaps, errorMsg );
908 if ( !mGeos || !geom )
913#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
921 return GEOSPreparedContainsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
923 catch ( QgsGeosException &e )
928 *errorMsg = e.what();
936 return relation( geom, RelationContains, errorMsg );
941 return relation( geom, RelationDisjoint, errorMsg );
961 char *r = GEOSRelate_r( context, mGeos.get(), geosGeom.get() );
964 result = QString( r );
965 GEOSFree_r( context, r );
968 catch ( QgsGeosException &e )
973 *errorMsg = e.what();
982 if ( !mGeos || !geom )
997 result = ( GEOSRelatePattern_r( context, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
999 catch ( QgsGeosException &e )
1004 *errorMsg = e.what();
1045 QVector<QgsGeometry> &newGeometries,
1048 QString *errorMsg,
bool skipIntersectionCheck )
const
1064 if ( !GEOSisValid_r( context, mGeos.get() ) )
1072 newGeometries.clear();
1079 splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
1083 splitLineGeos = createGeosPointXY( splitLine.
xAt( 0 ), splitLine.
yAt( 0 ),
false, 0,
false, 0, 2, mPrecision );
1090 if ( !GEOSisValid_r( context, splitLineGeos.get() ) || !GEOSisSimple_r( context, splitLineGeos.get() ) )
1098 if ( !topologicalTestPointsSplit( splitLineGeos.get(), topologyTestPoints ) )
1107 returnCode = splitLinearGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1111 returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1140 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, mGeos.get(), splitLine ) );
1141 if ( !intersectionGeom )
1144 bool simple =
false;
1145 int nIntersectGeoms = 1;
1146 if ( GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_LINESTRING
1147 || GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_POINT )
1151 nIntersectGeoms = GEOSGetNumGeometries_r( context, intersectionGeom.get() );
1153 for (
int i = 0; i < nIntersectGeoms; ++i )
1157 currentIntersectGeom = intersectionGeom.get();
1159 currentIntersectGeom = GEOSGetGeometryN_r( context, intersectionGeom.get(), i );
1161 const GEOSCoordSequence *lineSequence = GEOSGeom_getCoordSeq_r( context, currentIntersectGeom );
1162 unsigned int sequenceSize = 0;
1164 if ( GEOSCoordSeq_getSize_r( context, lineSequence, &sequenceSize ) != 0 )
1166 for (
unsigned int i = 0; i < sequenceSize; ++i )
1168 if ( GEOSCoordSeq_getXYZ_r( context, lineSequence, i, &x, &y, &z ) )
1170 testPoints.push_back( QgsPoint( x, y, z ) );
1184 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1186 std::unique_ptr< QgsMultiCurve > multiCurve;
1187 if ( type == GEOS_MULTILINESTRING )
1191 else if ( type == GEOS_LINESTRING )
1193 multiCurve = std::make_unique<QgsMultiCurve>( );
1194 multiCurve->addGeometry(
mGeometry->clone() );
1209 std::unique_ptr< QgsMultiPoint > splitPoints;
1211 std::unique_ptr< QgsAbstractGeometry > splitGeom(
fromGeos( GEOSsplitPoint ) );
1215 splitPoints.reset( qgis::down_cast<QgsMultiPoint *>( splitGeom.release() ) );
1219 splitPoints = std::make_unique< QgsMultiPoint >();
1222 splitPoints->addGeometry( qgis::down_cast<QgsPoint *>( splitGeom.release() ) );
1230 QgsMultiCurve lines;
1233 for (
int geometryIndex = 0; geometryIndex < multiCurve->numGeometries(); ++geometryIndex )
1246 QMap< int, QVector< QPair< double, QgsPoint > > >pointMap;
1247 for (
int splitPointIndex = 0; splitPointIndex < splitPoints->numGeometries(); ++splitPointIndex )
1249 const QgsPoint *intersectionPoint = splitPoints->pointN( splitPointIndex );
1251 QgsPoint segmentPoint2D;
1252 QgsVertexId nextVertex;
1255 line->
closestSegment( *intersectionPoint, segmentPoint2D, nextVertex );
1271 const QPair< double, QgsPoint > pair = qMakePair(
distance, *correctSegmentPoint.get() );
1272 if ( pointMap.contains( nextVertex.
vertex - 1 ) )
1273 pointMap[ nextVertex.
vertex - 1 ].append( pair );
1275 pointMap[ nextVertex.
vertex - 1 ] = QVector< QPair< double, QgsPoint > >() << pair;
1280 for (
auto &p : pointMap )
1282 std::sort( p.begin(), p.end(), [](
const QPair< double, QgsPoint > &a,
const QPair< double, QgsPoint > &b ) { return a.first < b.first; } );
1286 QgsLineString newLine;
1288 QgsPoint splitPoint;
1289 for (
int vertexIndex = 0; vertexIndex < nVertices; ++vertexIndex )
1291 QgsPoint currentPoint = line->
pointN( vertexIndex );
1293 if ( pointMap.contains( vertexIndex ) )
1296 for (
int k = 0; k < pointMap[ vertexIndex ].size(); ++k )
1298 splitPoint = pointMap[ vertexIndex ][k].second;
1299 if ( splitPoint == currentPoint )
1302 newLine = QgsLineString();
1305 else if ( splitPoint == line->
pointN( vertexIndex + 1 ) )
1309 newLine = QgsLineString();
1315 newLine = QgsLineString();
1324 return asGeos( &lines, mPrecision );
1329 Q_UNUSED( skipIntersectionCheck )
1338 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, splitLine, mGeos.get() ) );
1339 if ( !intersectGeom || GEOSisEmpty_r( context, intersectGeom.get() ) )
1343 const int linearIntersect = GEOSRelatePattern_r( context, mGeos.get(), splitLine,
"1********" );
1344 if ( linearIntersect > 0 )
1352 std::vector<geos::unique_ptr> lineGeoms;
1354 const int splitType = GEOSGeomTypeId_r( context, splitGeom.get() );
1355 if ( splitType == GEOS_MULTILINESTRING )
1357 const int nGeoms = GEOSGetNumGeometries_r( context, splitGeom.get() );
1358 lineGeoms.reserve( nGeoms );
1359 for (
int i = 0; i < nGeoms; ++i )
1360 lineGeoms.emplace_back( GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, splitGeom.get(), i ) ) );
1365 lineGeoms.emplace_back( GEOSGeom_clone_r( context, splitGeom.get() ) );
1368 mergeGeometriesMultiTypeSplit( lineGeoms );
1372 newGeometries << QgsGeometry(
fromGeos( lineGeom.get() ) );
1388 if ( !mGeosPrepared )
1394 if ( !skipIntersectionCheck && !GEOSPreparedIntersects_r( context, mGeosPrepared.get(), splitLine ) )
1399 if ( !nodedGeometry )
1408 const int numberOfGeometriesPolygon = numberOfGeometries( polygons.get() );
1409 if ( numberOfGeometriesPolygon == 0 )
1416 std::vector<geos::unique_ptr> testedGeometries;
1421 for (
int i = 0; i < numberOfGeometriesPolygon; i++ )
1423 const GEOSGeometry *polygon = GEOSGetGeometryN_r( context, polygons.get(), i );
1427 testedGeometries.emplace_back( GEOSGeom_clone_r( context, polygon ) );
1430 const size_t nGeometriesThis = numberOfGeometries( mGeos.get() );
1431 if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
1441 mergeGeometriesMultiTypeSplit( testedGeometries );
1444 for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( context, testedGeometries[i].get() ); ++i )
1447 if ( i < testedGeometries.size() )
1454 newGeometries << QgsGeometry(
fromGeos( testedGeometry.get() ) );
1462 if ( !splitLine || !geom )
1467 if ( GEOSGeom_getDimensions_r( context, geom ) == 2 )
1468 geometryBoundary.reset( GEOSBoundary_r( context, geom ) );
1470 geometryBoundary.reset( GEOSGeom_clone_r( context, geom ) );
1473 geos::unique_ptr unionGeometry( GEOSUnion_r( context, splitLineClone.get(), geometryBoundary.get() ) );
1475 return unionGeometry;
1478int QgsGeos::mergeGeometriesMultiTypeSplit( std::vector<geos::unique_ptr> &splitResult )
const
1485 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1486 if ( type != GEOS_GEOMETRYCOLLECTION &&
1487 type != GEOS_MULTILINESTRING &&
1488 type != GEOS_MULTIPOLYGON &&
1489 type != GEOS_MULTIPOINT )
1493 std::vector<geos::unique_ptr> unionGeom;
1495 std::vector<geos::unique_ptr> newSplitResult;
1497 for (
size_t i = 0; i < splitResult.size(); ++i )
1500 bool isPart =
false;
1501 for (
int j = 0; j < GEOSGetNumGeometries_r( context, mGeos.get() ); j++ )
1503 if ( GEOSEquals_r( context, splitResult[i].get(), GEOSGetGeometryN_r( context, mGeos.get(), j ) ) )
1512 unionGeom.emplace_back( std::move( splitResult[i] ) );
1516 std::vector<geos::unique_ptr> geomVector;
1517 geomVector.emplace_back( std::move( splitResult[i] ) );
1519 if ( type == GEOS_MULTILINESTRING )
1520 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, geomVector ) );
1521 else if ( type == GEOS_MULTIPOLYGON )
1522 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, geomVector ) );
1526 splitResult = std::move( newSplitResult );
1529 if ( !unionGeom.empty() )
1531 if ( type == GEOS_MULTILINESTRING )
1532 splitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, unionGeom ) );
1533 else if ( type == GEOS_MULTIPOLYGON )
1534 splitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ) );
1540geos::unique_ptr QgsGeos::createGeosCollection(
int typeId, std::vector<geos::unique_ptr> &geoms )
1542 std::vector<GEOSGeometry *> geomarr;
1543 geomarr.reserve( geoms.size() );
1548 if ( geomUniquePtr )
1550 if ( !GEOSisEmpty_r( context, geomUniquePtr.get() ) )
1554 geomarr.emplace_back( geomUniquePtr.release() );
1562 geomRes.reset( GEOSGeom_createCollection_r( context, typeId, geomarr.data(), geomarr.size() ) );
1564 catch ( QgsGeosException & )
1568 GEOSGeom_destroy_r( context, geom );
1583 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1584 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1585 bool hasZ = ( nCoordDims == 3 );
1586 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1588 switch ( GEOSGeomTypeId_r( context,
geos ) )
1592 if ( GEOSisEmpty_r( context,
geos ) )
1595 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1596 unsigned int nPoints = 0;
1597 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1606 return !point.
isEmpty() ? std::unique_ptr<QgsAbstractGeometry>( point.
clone() ) :
nullptr;
1608 case GEOS_LINESTRING:
1610 return sequenceToLinestring(
geos, hasZ, hasM );
1616 case GEOS_MULTIPOINT:
1618 auto multiPoint = std::make_unique<QgsMultiPoint>();
1619 int nParts = GEOSGetNumGeometries_r( context,
geos );
1620 multiPoint->reserve( nParts );
1621 for (
int i = 0; i < nParts; ++i )
1623 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context, GEOSGetGeometryN_r( context,
geos, i ) );
1626 unsigned int nPoints = 0;
1627 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1629 multiPoint->addGeometry(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1632 return std::move( multiPoint );
1634 case GEOS_MULTILINESTRING:
1636 auto multiLineString = std::make_unique<QgsMultiLineString>();
1637 int nParts = GEOSGetNumGeometries_r( context,
geos );
1638 multiLineString->reserve( nParts );
1639 for (
int i = 0; i < nParts; ++i )
1641 std::unique_ptr< QgsLineString >line( sequenceToLinestring( GEOSGetGeometryN_r( context,
geos, i ), hasZ, hasM ) );
1644 multiLineString->addGeometry( line.release() );
1647 return std::move( multiLineString );
1649 case GEOS_MULTIPOLYGON:
1651 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
1653 int nParts = GEOSGetNumGeometries_r( context,
geos );
1654 multiPolygon->reserve( nParts );
1655 for (
int i = 0; i < nParts; ++i )
1657 std::unique_ptr< QgsPolygon > poly =
fromGeosPolygon( GEOSGetGeometryN_r( context,
geos, i ) );
1660 multiPolygon->addGeometry( poly.release() );
1663 return std::move( multiPolygon );
1665 case GEOS_GEOMETRYCOLLECTION:
1667 auto geomCollection = std::make_unique<QgsGeometryCollection>();
1668 int nParts = GEOSGetNumGeometries_r( context,
geos );
1669 geomCollection->reserve( nParts );
1670 for (
int i = 0; i < nParts; ++i )
1672 std::unique_ptr< QgsAbstractGeometry > geom(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1675 geomCollection->addGeometry( geom.release() );
1678 return std::move( geomCollection );
1687 if ( GEOSGeomTypeId_r( context,
geos ) != GEOS_POLYGON )
1692 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1693 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1694 bool hasZ = ( nCoordDims == 3 );
1695 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1697 auto polygon = std::make_unique<QgsPolygon>();
1702 polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1705 QVector<QgsCurve *> interiorRings;
1706 const int ringCount = GEOSGetNumInteriorRings_r( context,
geos );
1707 interiorRings.reserve( ringCount );
1708 for (
int i = 0; i < ringCount; ++i )
1710 ring = GEOSGetInteriorRingN_r( context,
geos, i );
1713 interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1716 polygon->setInteriorRings( interiorRings );
1721std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring(
const GEOSGeometry *
geos,
bool hasZ,
bool hasM )
1724 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1726 unsigned int nPoints;
1727 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1729 QVector< double > xOut( nPoints );
1730 QVector< double > yOut( nPoints );
1731 QVector< double > zOut;
1733 zOut.resize( nPoints );
1734 QVector< double > mOut;
1736 mOut.resize( nPoints );
1738 double *x = xOut.data();
1739 double *y = yOut.data();
1740 double *z = zOut.data();
1741 double *m = mOut.data();
1743#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
1744 GEOSCoordSeq_copyToArrays_r( context, cs, x, y, hasZ ? z :
nullptr, hasM ? m :
nullptr );
1746 for (
unsigned int i = 0; i < nPoints; ++i )
1749 GEOSCoordSeq_getXYZ_r( context, cs, i, x++, y++, z++ );
1751 GEOSCoordSeq_getXY_r( context, cs, i, x++, y++ );
1754 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, m++ );
1758 auto line = std::make_unique<QgsLineString>( xOut, yOut, zOut, mOut );
1768 int geometryType = GEOSGeomTypeId_r( context, g );
1769 if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING
1770 || geometryType == GEOS_POLYGON )
1774 return GEOSGetNumGeometries_r( context, g );
1790 GEOSCoordSeq_getXYZ_r( context, cs, i, &x, &y, &z );
1792 GEOSCoordSeq_getXY_r( context, cs, i, &x, &y );
1795 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, &m );
1831 int geosType = GEOS_GEOMETRYCOLLECTION;
1838 geosType = GEOS_MULTIPOINT;
1842 geosType = GEOS_MULTILINESTRING;
1846 geosType = GEOS_MULTIPOLYGON;
1861 std::vector<geos::unique_ptr> geomVector;
1862 geomVector.reserve(
c->numGeometries() );
1863 for (
int i = 0; i <
c->numGeometries(); ++i )
1870 geomVector.emplace_back( std::move( geosGeom ) );
1872 return createGeosCollection( geosType, geomVector );
1880 if ( !polyhedralSurface )
1883 std::vector<geos::unique_ptr> geomVector;
1884 geomVector.reserve( polyhedralSurface->
numPatches() );
1885 for (
int i = 0; i < polyhedralSurface->
numPatches(); ++i )
1892 geomVector.emplace_back( std::move( geosPolygon ) );
1895 return createGeosCollection( GEOS_MULTIPOLYGON, geomVector );
1902 return createGeosPoint(
static_cast<const QgsPoint *
>( geom ), coordDims, precision, flags );
1905 return createGeosLinestring(
static_cast<const QgsLineString *
>( geom ), precision, flags );
1908 return createGeosPolygon(
static_cast<const QgsPolygon *
>( geom ), precision, flags );
1920 if ( !mGeos || !geom )
1931 const double gridSize = parameters.
gridSize();
1939 case OverlayIntersection:
1942 opGeom.reset( GEOSIntersectionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1946 opGeom.reset( GEOSIntersection_r( context, mGeos.get(), geosGeom.get() ) );
1950 case OverlayDifference:
1953 opGeom.reset( GEOSDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1957 opGeom.reset( GEOSDifference_r( context, mGeos.get(), geosGeom.get() ) );
1966 unionGeometry.reset( GEOSUnionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1970 unionGeometry.reset( GEOSUnion_r( context, mGeos.get(), geosGeom.get() ) );
1973 if ( unionGeometry && GEOSGeomTypeId_r( context, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1975 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, unionGeometry.get() ) );
1978 unionGeometry = std::move( mergedLines );
1982 opGeom = std::move( unionGeometry );
1986 case OverlaySymDifference:
1989 opGeom.reset( GEOSSymDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1993 opGeom.reset( GEOSSymDifference_r( context, mGeos.get(), geosGeom.get() ) );
1999 catch ( QgsGeosException &e )
2004 *errorMsg = e.what();
2010bool QgsGeos::relation(
const QgsAbstractGeometry *geom, Relation r, QString *errorMsg )
const
2012 if ( !mGeos || !geom )
2024 bool result =
false;
2027 if ( mGeosPrepared )
2031 case RelationIntersects:
2032 result = ( GEOSPreparedIntersects_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2034 case RelationTouches:
2035 result = ( GEOSPreparedTouches_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2037 case RelationCrosses:
2038 result = ( GEOSPreparedCrosses_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2040 case RelationWithin:
2041 result = ( GEOSPreparedWithin_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2043 case RelationContains:
2044 result = ( GEOSPreparedContains_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2046 case RelationDisjoint:
2047 result = ( GEOSPreparedDisjoint_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2049 case RelationOverlaps:
2050 result = ( GEOSPreparedOverlaps_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2058 case RelationIntersects:
2059 result = ( GEOSIntersects_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2061 case RelationTouches:
2062 result = ( GEOSTouches_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2064 case RelationCrosses:
2065 result = ( GEOSCrosses_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2067 case RelationWithin:
2068 result = ( GEOSWithin_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2070 case RelationContains:
2071 result = ( GEOSContains_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2073 case RelationDisjoint:
2074 result = ( GEOSDisjoint_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2076 case RelationOverlaps:
2077 result = ( GEOSOverlaps_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2081 catch ( QgsGeosException &e )
2086 *errorMsg = e.what();
2126 geos.reset( GEOSBufferWithStyle_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( endCapStyle ),
static_cast< int >( joinStyle ), miterLimit ) );
2176 geos.reset( GEOSGetCentroid_r( context, mGeos.get() ) );
2181 GEOSGeomGetX_r( context,
geos.get(), &x );
2182 GEOSGeomGetY_r( context,
geos.get(), &y );
2218 geos.reset( GEOSPointOnSurface_r( context, mGeos.get() ) );
2220 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
2225 GEOSGeomGetX_r( context,
geos.get(), &x );
2226 GEOSGeomGetY_r( context,
geos.get(), &y );
2243 std::unique_ptr< QgsAbstractGeometry > cHullGeom =
fromGeos( cHull.get() );
2244 return cHullGeom.release();
2249std::unique_ptr< QgsAbstractGeometry >
QgsGeos::concaveHull(
double targetPercent,
bool allowHoles, QString *errorMsg )
const
2251#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
2253 ( void )targetPercent;
2255 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHull requires a QGIS build based on GEOS 3.11 or later" ) );
2266 return concaveHullGeom;
2274#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<12
2276 ( void )invalidEdges;
2278 throw QgsNotSupportedException( QObject::tr(
"Validating coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2283 *errorMsg = u
"Input geometry was not set"_s;
2291 const int result = GEOSCoverageIsValid_r( context, mGeos.get(), gapWidth, invalidEdges ? &invalidEdgesGeos :
nullptr );
2292 if ( invalidEdges && invalidEdgesGeos )
2294 *invalidEdges =
fromGeos( invalidEdgesGeos );
2296 if ( invalidEdgesGeos )
2298 GEOSGeom_destroy_r( context, invalidEdgesGeos );
2299 invalidEdgesGeos =
nullptr;
2319#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<12
2321 ( void )preserveBoundary;
2323 throw QgsNotSupportedException( QObject::tr(
"Simplifying coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2328 *errorMsg = u
"Input geometry was not set"_s;
2335 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom =
fromGeos( simplified.get() );
2336 return simplifiedGeom;
2347 *errorMsg = u
"Input geometry was not set"_s;
2354 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( unioned.get() );
2365 *errorMsg = QObject::tr(
"QGIS geometry cannot be converted to a GEOS geometry",
"GEOS Error" );
2374 char res = GEOSisValidDetail_r( context, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
2375 const bool invalid = res != 1;
2380 error = QString( r );
2381 GEOSFree_r( context, r );
2384 if ( invalid && errorMsg )
2387 static const std::map< QString, QString > sTranslatedErrors
2389 { u
"topology validation error"_s, QObject::tr(
"Topology validation error",
"GEOS Error" ) },
2390 { u
"repeated point"_s, QObject::tr(
"Repeated point",
"GEOS Error" ) },
2391 { u
"hole lies outside shell"_s, QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) },
2392 { u
"holes are nested"_s, QObject::tr(
"Holes are nested",
"GEOS Error" ) },
2393 { u
"interior is disconnected"_s, QObject::tr(
"Interior is disconnected",
"GEOS Error" ) },
2394 { u
"self-intersection"_s, QObject::tr(
"Self-intersection",
"GEOS Error" ) },
2395 { u
"ring self-intersection"_s, QObject::tr(
"Ring self-intersection",
"GEOS Error" ) },
2396 { u
"nested shells"_s, QObject::tr(
"Nested shells",
"GEOS Error" ) },
2397 { u
"duplicate rings"_s, QObject::tr(
"Duplicate rings",
"GEOS Error" ) },
2398 { u
"too few points in geometry component"_s, QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) },
2399 { u
"invalid coordinate"_s, QObject::tr(
"Invalid coordinate",
"GEOS Error" ) },
2400 { u
"ring is not closed"_s, QObject::tr(
"Ring is not closed",
"GEOS Error" ) },
2403 const auto translatedError = sTranslatedErrors.find( error.toLower() );
2404 if ( translatedError != sTranslatedErrors.end() )
2405 *errorMsg = translatedError->second;
2409 if ( g1 && errorLoc )
2415 GEOSGeom_destroy_r( context, g1 );
2425 if ( !mGeos || !geom )
2471GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
2475 std::unique_ptr< QgsLineString > segmentized;
2481 line = segmentized.get();
2488 GEOSCoordSequence *coordSeq =
nullptr;
2490 const int numPoints = line->
numPoints();
2492 const bool hasZ = line->
is3D();
2494#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
2497 if ( !forceClose || ( line->
pointN( 0 ) == line->
pointN( numPoints - 1 ) ) )
2502 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, line->
xData(), line->
yData(), line->
zData(),
nullptr, numPoints );
2505 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points"_s.arg( numPoints ) );
2513 QVector< double > x = line->
xVector();
2514 if ( numPoints > 0 )
2515 x.append( x.at( 0 ) );
2516 QVector< double > y = line->
yVector();
2517 if ( numPoints > 0 )
2518 y.append( y.at( 0 ) );
2519 QVector< double > z = line->
zVector();
2520 if ( hasZ && numPoints > 0 )
2521 z.append( z.at( 0 ) );
2524 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, x.constData(), y.constData(), !hasZ ?
nullptr : z.constData(),
nullptr, numPoints + 1 );
2527 QgsDebugError( u
"GEOS Exception: Could not create closed coordinate sequence for %1 points"_s.arg( numPoints + 1 ) );
2538 const bool hasM =
false;
2549 int numOutPoints = numPoints;
2550 if ( forceClose && ( line->
pointN( 0 ) != line->
pointN( numPoints - 1 ) ) )
2557 coordSeq = GEOSCoordSeq_create_r( context, numOutPoints, coordDims );
2560 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions"_s.arg( numPoints ).arg( coordDims ) );
2564 const double *xData = line->
xData();
2565 const double *yData = line->
yData();
2566 const double *zData = hasZ ? line->
zData() :
nullptr;
2567 const double *mData = hasM ? line->
mData() :
nullptr;
2569 if ( precision > 0. )
2571 for (
int i = 0; i < numOutPoints; ++i )
2573 if ( i >= numPoints )
2576 xData = line->
xData();
2577 yData = line->
yData();
2578 zData = hasZ ? line->
zData() :
nullptr;
2579 mData = hasM ? line->
mData() :
nullptr;
2583 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
2587 GEOSCoordSeq_setXY_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
2591 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2597 for (
int i = 0; i < numOutPoints; ++i )
2599 if ( i >= numPoints )
2602 xData = line->
xData();
2603 yData = line->
yData();
2604 zData = hasZ ? line->
zData() :
nullptr;
2605 mData = hasM ? line->
mData() :
nullptr;
2609 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, *xData++, *yData++, *zData++ );
2613 GEOSCoordSeq_setXY_r( context, coordSeq, i, *xData++, *yData++ );
2617 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2627geos::unique_ptr
QgsGeos::createGeosPoint( const QgsAbstractGeometry *point,
int coordDims,
double precision, Qgis::GeosCreationFlags )
2633 return createGeosPointXY( pt->
x(), pt->
y(), pt->
is3D(), pt->
z(), pt->
isMeasure(), pt->
m(), coordDims, precision );
2645 if ( coordDims == 2 )
2648 if ( precision > 0. )
2649 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
2651 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, x, y ) );
2655 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( context, 1, coordDims );
2658 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions"_s.arg( coordDims ) );
2661 if ( precision > 0. )
2663 GEOSCoordSeq_setX_r( context, coordSeq, 0, std::round( x / precision ) * precision );
2664 GEOSCoordSeq_setY_r( context, coordSeq, 0, std::round( y / precision ) * precision );
2667 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, std::round( z / precision ) * precision );
2672 GEOSCoordSeq_setX_r( context, coordSeq, 0, x );
2673 GEOSCoordSeq_setY_r( context, coordSeq, 0, y );
2676 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, z );
2682 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 3, m );
2685 geosPoint.reset( GEOSGeom_createPoint_r( context, coordSeq ) );
2691geos::unique_ptr
QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve,
double precision, Qgis::GeosCreationFlags )
2697 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c, precision );
2710geos::unique_ptr
QgsGeos::createGeosPolygon( const QgsAbstractGeometry *poly,
double precision, Qgis::GeosCreationFlags flags )
2716 const QgsCurve *exteriorRing = polygon->
exteriorRing();
2717 if ( !exteriorRing )
2726 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( context, createCoordinateSequence( exteriorRing, precision,
true ) ) );
2729 QList< const QgsCurve * > holesToExport;
2730 holesToExport.reserve( nInteriorRings );
2731 for (
int i = 0; i < nInteriorRings; ++i )
2733 const QgsCurve *interiorRing = polygon->
interiorRing( i );
2736 holesToExport << interiorRing;
2741 if ( !holesToExport.empty() )
2744 for (
int i = 0; i < holesToExport.size(); ++i )
2746 holes[i] = GEOSGeom_createLinearRing_r( context, createCoordinateSequence( holesToExport[i], precision,
true ) );
2750 geosPolygon.reset( GEOSGeom_createPolygon_r( context, exteriorRingGeos.release(), holes, holesToExport.size() ) );
2770 offset.reset( GEOSOffsetCurve_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( joinStyle ), miterLimit ) );
2782 return fromGeos( res.get() ).release();
2797 GEOSBufferParams_setSingleSided_r( context, bp.get(), 1 );
2798 GEOSBufferParams_setQuadrantSegments_r( context, bp.get(), segments );
2799 GEOSBufferParams_setJoinStyle_r( context, bp.get(),
static_cast< int >( joinStyle ) );
2800 GEOSBufferParams_setMitreLimit_r( context, bp.get(), miterLimit );
2806 geos.reset( GEOSBufferWithParams_r( context, mGeos.get(), bp.get(),
distance ) );
2840 boundaryGeos =
asGeos( boundary );
2868 return std::numeric_limits< double >::quiet_NaN();
2876 return std::numeric_limits< double >::quiet_NaN();
2916 if ( !mGeos || !other )
2936 if ( !mGeos ||
mGeometry->dimension() == 0 )
2948 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
2952 int numGeoms = GEOSGetNumGeometries_r( context, mGeos.get() );
2953 if ( numGeoms == -1 )
2962 bool isMultiGeom =
false;
2963 int geosTypeId = GEOSGeomTypeId_r( context, mGeos.get() );
2964 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
2967 bool isLine = (
mGeometry->dimension() == 1 );
2974 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2978 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2983 if ( reshapedGeometry )
2989 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
2990 return reshapeResult;
2997 bool reshapeTookPlace =
false;
3002 for (
int i = 0; i < numGeoms; ++i )
3005 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3007 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3009 if ( currentReshapeGeometry )
3011 newGeoms[i] = currentReshapeGeometry.release();
3012 reshapeTookPlace =
true;
3016 newGeoms[i] = GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, mGeos.get(), i ) );
3023 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
3027 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
3031 if ( !newMultiGeom )
3037 if ( reshapeTookPlace )
3041 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
3042 return reshapedMultiGeom;
3065 if ( GEOSGeomTypeId_r( context, mGeos.get() ) != GEOS_MULTILINESTRING )
3071 double gridSize = parameters.
gridSize();
3074 geos::unique_ptr geosFixedSize( GEOSGeom_setPrecision_r( context, mGeos.get(), gridSize, 0 ) );
3075 geos.reset( GEOSLineMerge_r( context, geosFixedSize.get() ) );
3078 geos.reset( GEOSLineMerge_r( context, mGeos.get() ) );
3103 if ( mGeosPrepared )
3105 nearestCoord.reset( GEOSPreparedNearestPoints_r( context, mGeosPrepared.get(), otherGeom.get() ) );
3109 nearestCoord.reset( GEOSNearestPoints_r( context, mGeos.get(), otherGeom.get() ) );
3112 ( void )GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx );
3113 ( void )GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny );
3115 catch ( QgsGeosException &e )
3120 *errorMsg = e.what();
3125 return std::make_unique< QgsPoint >( nx, ny );
3130 if ( !mGeos || other.
isEmpty() )
3140 if ( !other || other->
isEmpty() )
3158 if ( !nearestCoord )
3161 *errorMsg = u
"GEOS returned no nearest points"_s;
3165 ( void )GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx1 );
3166 ( void )GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny1 );
3167 ( void )GEOSCoordSeq_getX_r( context, nearestCoord.get(), 1, &nx2 );
3168 ( void )GEOSCoordSeq_getY_r( context, nearestCoord.get(), 1, &ny2 );
3170 catch ( QgsGeosException &e )
3175 *errorMsg = e.what();
3180 auto line = std::make_unique< QgsLineString >();
3204 catch ( QgsGeosException &e )
3209 *errorMsg = e.what();
3224 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
3233 catch ( QgsGeosException &e )
3238 *errorMsg = e.what();
3255 lineGeosGeometries[validLines] = l.release();
3263 geos::unique_ptr result( GEOSPolygonize_r( context, lineGeosGeometries, validLines ) );
3264 for (
int i = 0; i < validLines; ++i )
3266 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3268 delete[] lineGeosGeometries;
3271 catch ( QgsGeosException &e )
3275 *errorMsg = e.what();
3277 for (
int i = 0; i < validLines; ++i )
3279 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3281 delete[] lineGeosGeometries;
3296 extentGeosGeom =
asGeos( extent, mPrecision );
3297 if ( !extentGeosGeom )
3307 geos.reset( GEOSVoronoiDiagram_r( context, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
3309 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3330 geos.reset( GEOSDelaunayTriangulation_r( context, mGeos.get(), tolerance, edgesOnly ) );
3332 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3344#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
3346 throw QgsNotSupportedException( QObject::tr(
"Calculating constrainedDelaunayTriangulation requires a QGIS build based on GEOS 3.11 or later" ) );
3357 geos.reset( GEOSConstrainedDelaunayTriangulation_r( context, mGeos.get() ) );
3359 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3364 std::unique_ptr< QgsAbstractGeometry > res =
fromGeos(
geos.get() );
3367 return std::unique_ptr< QgsAbstractGeometry >( collection->extractPartsByType(
Qgis::WkbType::Polygon,
true ) );
3379static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
3382 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( context, linestring );
3386 unsigned int coordSeqSize;
3387 if ( GEOSCoordSeq_getSize_r( context, coordSeq, &coordSeqSize ) == 0 )
3390 if ( coordSeqSize < 2 )
3393 GEOSCoordSeq_getX_r( context, coordSeq, 0, &x1 );
3394 GEOSCoordSeq_getY_r( context, coordSeq, 0, &y1 );
3395 GEOSCoordSeq_getX_r( context, coordSeq, coordSeqSize - 1, &x2 );
3396 GEOSCoordSeq_getY_r( context, coordSeq, coordSeqSize - 1, &y2 );
3404 double x1, y1, x2, y2;
3405 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
3408 double rx1, ry1, rx2, ry2;
3409 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
3412 bool intersectionAtOrigLineEndpoint =
3413 ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) !=
3414 ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
3415 bool intersectionAtReshapeLineEndpoint =
3416 ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) ||
3417 ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
3421 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
3425 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
3426 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, geoms, 2 ) );
3431 double x1res, y1res, x2res, y2res;
3432 if ( !_linestringEndpoints( res.get(), x1res, y1res, x2res, y2res ) )
3434 if ( ( x1res == x2 && y1res == y2 ) || ( x2res == x1 && y2res == y1 ) )
3435 res.reset( GEOSReverse_r( context, res.get() ) );
3446 if ( !line || !reshapeLineGeos )
3449 bool atLeastTwoIntersections =
false;
3450 bool oneIntersection =
false;
3451 QgsPointXY oneIntersectionPoint;
3457 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, line, reshapeLineGeos ) );
3458 if ( intersectGeom )
3460 const int geomType = GEOSGeomTypeId_r( context, intersectGeom.get() );
3461 atLeastTwoIntersections = ( geomType == GEOS_MULTIPOINT && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 1 )
3462 || ( geomType == GEOS_GEOMETRYCOLLECTION && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 )
3463 || ( geomType == GEOS_MULTILINESTRING && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 );
3465 if ( GEOSGeomTypeId_r( context, intersectGeom.get() ) == GEOS_POINT )
3467 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( context, intersectGeom.get() );
3469 GEOSCoordSeq_getX_r( context, intersectionCoordSeq, 0, &xi );
3470 GEOSCoordSeq_getY_r( context, intersectionCoordSeq, 0, &yi );
3471 oneIntersection =
true;
3472 oneIntersectionPoint = QgsPointXY( xi, yi );
3476 catch ( QgsGeosException & )
3478 atLeastTwoIntersections =
false;
3482 if ( oneIntersection )
3483 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
3485 if ( !atLeastTwoIntersections )
3489 double x1, y1, x2, y2;
3490 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
3493 geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1,
false, 0,
false, 0, 2, precision );
3494 geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2,
false, 0,
false, 0, 2, precision );
3496 bool isRing =
false;
3497 if ( GEOSGeomTypeId_r( context, line ) == GEOS_LINEARRING
3498 || GEOSEquals_r( context, beginLineVertex.get(), endLineVertex.get() ) == 1 )
3503 if ( !nodedGeometry )
3509 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, nodedGeometry.get() ) );
3515 int numMergedLines = GEOSGetNumGeometries_r( context, mergedLines.get() );
3516 if ( numMergedLines < 2 )
3518 if ( numMergedLines == 1 )
3527 QVector<GEOSGeometry *> resultLineParts;
3528 QVector<GEOSGeometry *> probableParts;
3530 for (
int i = 0; i < numMergedLines; ++i )
3532 const GEOSGeometry *currentGeom = GEOSGetGeometryN_r( context, mergedLines.get(), i );
3535 bool alreadyAdded =
false;
3537 double bufferDistance = std::pow( 10.0L, geomDigits( currentGeom ) - 11 );
3538 for (
const GEOSGeometry *other : std::as_const( resultLineParts ) )
3540 GEOSHausdorffDistance_r( context, currentGeom, other, &
distance );
3543 alreadyAdded =
true;
3550 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( context, currentGeom );
3551 unsigned int currentCoordSeqSize;
3552 GEOSCoordSeq_getSize_r( context, currentCoordSeq, ¤tCoordSeqSize );
3553 if ( currentCoordSeqSize < 2 )
3557 double xBegin, xEnd, yBegin, yEnd;
3558 GEOSCoordSeq_getX_r( context, currentCoordSeq, 0, &xBegin );
3559 GEOSCoordSeq_getY_r( context, currentCoordSeq, 0, &yBegin );
3560 GEOSCoordSeq_getX_r( context, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
3561 GEOSCoordSeq_getY_r( context, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
3562 geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin,
false, 0,
false, 0, 2, precision );
3563 geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd,
false, 0,
false, 0, 2, precision );
3566 int nEndpointsOnOriginalLine = 0;
3567 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
3568 nEndpointsOnOriginalLine += 1;
3570 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
3571 nEndpointsOnOriginalLine += 1;
3574 int nEndpointsSameAsOriginalLine = 0;
3575 if ( GEOSEquals_r( context, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
3576 || GEOSEquals_r( context, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3577 nEndpointsSameAsOriginalLine += 1;
3579 if ( GEOSEquals_r( context, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
3580 || GEOSEquals_r( context, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3581 nEndpointsSameAsOriginalLine += 1;
3584 bool currentGeomOverlapsOriginalGeom =
false;
3585 bool currentGeomOverlapsReshapeLine =
false;
3586 if ( lineContainedInLine( currentGeom, line ) == 1 )
3587 currentGeomOverlapsOriginalGeom =
true;
3589 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
3590 currentGeomOverlapsReshapeLine =
true;
3593 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3595 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3598 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3600 probableParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3602 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3604 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3606 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3608 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3610 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
3612 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3617 if ( isRing && !probableParts.isEmpty() )
3621 double maxLength = -std::numeric_limits<double>::max();
3622 double currentLength = 0;
3623 for (
int i = 0; i < probableParts.size(); ++i )
3625 currentGeom = probableParts.at( i );
3626 GEOSLength_r( context, currentGeom, ¤tLength );
3627 if ( currentLength > maxLength )
3629 maxLength = currentLength;
3630 maxGeom.reset( currentGeom );
3634 GEOSGeom_destroy_r( context, currentGeom );
3637 resultLineParts.push_back( maxGeom.release() );
3641 if ( resultLineParts.empty() )
3644 if ( resultLineParts.size() == 1 )
3646 result.reset( resultLineParts[0] );
3651 for (
int i = 0; i < resultLineParts.size(); ++i )
3653 lineArray[i] = resultLineParts[i];
3657 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
3658 delete [] lineArray;
3661 result.reset( GEOSLineMerge_r( context, multiLineGeom.get() ) );
3665 if ( GEOSGeomTypeId_r( context, result.get() ) != GEOS_LINESTRING )
3671 bool reverseLine =
false;
3675 char isResultCCW = 0, isOriginCCW = 0;
3676 if ( GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, result.get() ), &isResultCCW ) &&
3677 GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, line ), &isOriginCCW )
3681 reverseLine = ( isOriginCCW == 1 && isResultCCW == 0 ) || ( isOriginCCW == 0 && isResultCCW == 1 );
3687 double x1res, y1res, x2res, y2res;
3688 if ( !_linestringEndpoints( result.get(), x1res, y1res, x2res, y2res ) )
3690 geos::unique_ptr beginResultLineVertex = createGeosPointXY( x1res, y1res,
false, 0,
false, 0, 2, precision );
3691 geos::unique_ptr endResultLineVertex = createGeosPointXY( x2res, y2res,
false, 0,
false, 0, 2, precision );
3692 reverseLine = GEOSEquals_r( context, beginLineVertex.get(), endResultLineVertex.get() ) == 1 || GEOSEquals_r( context, endLineVertex.get(), beginResultLineVertex.get() ) == 1;
3695 result.reset( GEOSReverse_r( context, result.get() ) );
3703 int nIntersections = 0;
3704 int lastIntersectingRing = -2;
3708 int nRings = GEOSGetNumInteriorRings_r( context, polygon );
3713 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( context, polygon );
3714 if ( GEOSIntersects_r( context, outerRing, reshapeLineGeos ) == 1 )
3717 lastIntersectingRing = -1;
3718 lastIntersectingGeom = outerRing;
3726 for (
int i = 0; i < nRings; ++i )
3728 innerRings[i] = GEOSGetInteriorRingN_r( context, polygon, i );
3729 if ( GEOSIntersects_r( context, innerRings[i], reshapeLineGeos ) == 1 )
3732 lastIntersectingRing = i;
3733 lastIntersectingGeom = innerRings[i];
3737 catch ( QgsGeosException & )
3742 if ( nIntersections != 1 )
3744 delete [] innerRings;
3749 geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
3750 if ( !reshapeResult )
3752 delete [] innerRings;
3758 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( context, reshapeResult.get() );
3759 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( context, reshapeSequence );
3761 reshapeResult.reset();
3765 newRing = GEOSGeom_createLinearRing_r( context, newCoordSequence );
3767 catch ( QgsGeosException & )
3774 delete [] innerRings;
3779 if ( lastIntersectingRing == -1 )
3780 newOuterRing = newRing;
3782 newOuterRing = GEOSGeom_clone_r( context, outerRing );
3785 QVector<GEOSGeometry *> ringList;
3788 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( context, GEOSGeom_clone_r( context, newOuterRing ),
nullptr, 0 );
3789 if ( outerRingPoly )
3791 ringList.reserve( nRings );
3793 for (
int i = 0; i < nRings; ++i )
3795 if ( lastIntersectingRing == i )
3796 currentRing = newRing;
3798 currentRing = GEOSGeom_clone_r( context, innerRings[i] );
3801 if ( GEOSContains_r( context, outerRingPoly, currentRing ) == 1 )
3802 ringList.push_back( currentRing );
3804 GEOSGeom_destroy_r( context, currentRing );
3807 GEOSGeom_destroy_r( context, outerRingPoly );
3811 for (
int i = 0; i < ringList.size(); ++i )
3812 newInnerRings[i] = ringList.at( i );
3814 delete [] innerRings;
3816 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( context, newOuterRing, newInnerRings, ringList.size() ) );
3817 delete[] newInnerRings;
3819 return reshapedPolygon;
3824 if ( !line1 || !line2 )
3829 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
3836 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, bufferGeom.get(), line1 ) );
3839 double intersectGeomLength;
3842 GEOSLength_r( context, intersectionGeom.get(), &intersectGeomLength );
3843 GEOSLength_r( context, line1, &line1Length );
3845 double intersectRatio = line1Length / intersectGeomLength;
3846 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
3854 if ( !point || !line )
3857 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
3860 geos::unique_ptr lineBuffer( GEOSBuffer_r( context, line, bufferDistance, 8 ) );
3864 bool contained =
false;
3865 if ( GEOSContains_r( context, lineBuffer.get(), point ) == 1 )
3878 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( context, bbox.get() );
3882 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( context, bBoxRing );
3884 if ( !bBoxCoordSeq )
3887 unsigned int nCoords = 0;
3888 if ( !GEOSCoordSeq_getSize_r( context, bBoxCoordSeq, &nCoords ) )
3892 for (
unsigned int i = 0; i < nCoords - 1; ++i )
3895 GEOSCoordSeq_getX_r( context, bBoxCoordSeq, i, &t );
3898 digits = std::ceil( std::log10( std::fabs( t ) ) );
3899 if ( digits > maxDigits )
3902 GEOSCoordSeq_getY_r( context, bBoxCoordSeq, i, &t );
3903 digits = std::ceil( std::log10( std::fabs( t ) ) );
3904 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