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 );
671 otherGeosGeom =
asGeos( &firstPoint );
675 otherGeosGeom =
asGeos( geom, mPrecision );
678 if ( !otherGeosGeom )
691 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
693#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
694 return GEOSPreparedDistanceWithin_r( context, mGeosPrepared.get(), otherGeosGeom.get(), maxdist );
696 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
701#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
702 return GEOSDistanceWithin_r( context, mGeos.get(), otherGeosGeom.get(), maxdist );
704 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
719#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
722 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
728#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
729 return GEOSPreparedContainsXY_r( context, mGeosPrepared.get(), x, y ) == 1;
731 return GEOSPreparedContains_r( context, mGeosPrepared.get(), point.get() ) == 1;
735#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
736 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
741 result = ( GEOSContains_r( context, mGeos.get(), point.get() ) == 1 );
743 catch ( QgsGeosException &e )
748 *errorMsg = e.what();
765 if ( !otherGeosGeom )
788 if ( !otherGeosGeom )
811 if ( !otherGeosGeom )
834 if ( !otherGeosGeom )
850 if ( !mGeos || !geom )
855#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
863 return GEOSPreparedIntersectsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
865 catch ( QgsGeosException &e )
870 *errorMsg = e.what();
878 return relation( geom, RelationIntersects, errorMsg );
883 return relation( geom, RelationTouches, errorMsg );
888 return relation( geom, RelationCrosses, errorMsg );
893 return relation( geom, RelationWithin, errorMsg );
898 return relation( geom, RelationOverlaps, errorMsg );
903 if ( !mGeos || !geom )
908#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
916 return GEOSPreparedContainsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
918 catch ( QgsGeosException &e )
923 *errorMsg = e.what();
931 return relation( geom, RelationContains, errorMsg );
936 return relation( geom, RelationDisjoint, errorMsg );
956 char *r = GEOSRelate_r( context, mGeos.get(), geosGeom.get() );
959 result = QString( r );
960 GEOSFree_r( context, r );
963 catch ( QgsGeosException &e )
968 *errorMsg = e.what();
977 if ( !mGeos || !geom )
992 result = ( GEOSRelatePattern_r( context, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
994 catch ( QgsGeosException &e )
999 *errorMsg = e.what();
1040 QVector<QgsGeometry> &newGeometries,
1043 QString *errorMsg,
bool skipIntersectionCheck )
const
1059 if ( !GEOSisValid_r( context, mGeos.get() ) )
1067 newGeometries.clear();
1074 splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
1078 splitLineGeos = createGeosPointXY( splitLine.
xAt( 0 ), splitLine.
yAt( 0 ),
false, 0,
false, 0, 2, mPrecision );
1085 if ( !GEOSisValid_r( context, splitLineGeos.get() ) || !GEOSisSimple_r( context, splitLineGeos.get() ) )
1093 if ( !topologicalTestPointsSplit( splitLineGeos.get(), topologyTestPoints ) )
1102 returnCode = splitLinearGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1106 returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1135 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, mGeos.get(), splitLine ) );
1136 if ( !intersectionGeom )
1139 bool simple =
false;
1140 int nIntersectGeoms = 1;
1141 if ( GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_LINESTRING
1142 || GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_POINT )
1146 nIntersectGeoms = GEOSGetNumGeometries_r( context, intersectionGeom.get() );
1148 for (
int i = 0; i < nIntersectGeoms; ++i )
1152 currentIntersectGeom = intersectionGeom.get();
1154 currentIntersectGeom = GEOSGetGeometryN_r( context, intersectionGeom.get(), i );
1156 const GEOSCoordSequence *lineSequence = GEOSGeom_getCoordSeq_r( context, currentIntersectGeom );
1157 unsigned int sequenceSize = 0;
1159 if ( GEOSCoordSeq_getSize_r( context, lineSequence, &sequenceSize ) != 0 )
1161 for (
unsigned int i = 0; i < sequenceSize; ++i )
1163 if ( GEOSCoordSeq_getXYZ_r( context, lineSequence, i, &x, &y, &z ) )
1165 testPoints.push_back( QgsPoint( x, y, z ) );
1179 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1181 std::unique_ptr< QgsMultiCurve > multiCurve;
1182 if ( type == GEOS_MULTILINESTRING )
1186 else if ( type == GEOS_LINESTRING )
1188 multiCurve = std::make_unique<QgsMultiCurve>( );
1189 multiCurve->addGeometry(
mGeometry->clone() );
1204 std::unique_ptr< QgsMultiPoint > splitPoints;
1206 std::unique_ptr< QgsAbstractGeometry > splitGeom(
fromGeos( GEOSsplitPoint ) );
1210 splitPoints.reset( qgis::down_cast<QgsMultiPoint *>( splitGeom.release() ) );
1214 splitPoints = std::make_unique< QgsMultiPoint >();
1217 splitPoints->addGeometry( qgis::down_cast<QgsPoint *>( splitGeom.release() ) );
1225 QgsMultiCurve lines;
1228 for (
int geometryIndex = 0; geometryIndex < multiCurve->numGeometries(); ++geometryIndex )
1241 QMap< int, QVector< QPair< double, QgsPoint > > >pointMap;
1242 for (
int splitPointIndex = 0; splitPointIndex < splitPoints->numGeometries(); ++splitPointIndex )
1244 const QgsPoint *intersectionPoint = splitPoints->pointN( splitPointIndex );
1246 QgsPoint segmentPoint2D;
1247 QgsVertexId nextVertex;
1250 line->
closestSegment( *intersectionPoint, segmentPoint2D, nextVertex );
1266 const QPair< double, QgsPoint > pair = qMakePair(
distance, *correctSegmentPoint.get() );
1267 if ( pointMap.contains( nextVertex.
vertex - 1 ) )
1268 pointMap[ nextVertex.
vertex - 1 ].append( pair );
1270 pointMap[ nextVertex.
vertex - 1 ] = QVector< QPair< double, QgsPoint > >() << pair;
1275 for (
auto &p : pointMap )
1277 std::sort( p.begin(), p.end(), [](
const QPair< double, QgsPoint > &a,
const QPair< double, QgsPoint > &b ) { return a.first < b.first; } );
1281 QgsLineString newLine;
1283 QgsPoint splitPoint;
1284 for (
int vertexIndex = 0; vertexIndex < nVertices; ++vertexIndex )
1286 QgsPoint currentPoint = line->
pointN( vertexIndex );
1288 if ( pointMap.contains( vertexIndex ) )
1291 for (
int k = 0; k < pointMap[ vertexIndex ].size(); ++k )
1293 splitPoint = pointMap[ vertexIndex ][k].second;
1294 if ( splitPoint == currentPoint )
1297 newLine = QgsLineString();
1300 else if ( splitPoint == line->
pointN( vertexIndex + 1 ) )
1304 newLine = QgsLineString();
1310 newLine = QgsLineString();
1319 return asGeos( &lines, mPrecision );
1324 Q_UNUSED( skipIntersectionCheck )
1333 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, splitLine, mGeos.get() ) );
1334 if ( !intersectGeom || GEOSisEmpty_r( context, intersectGeom.get() ) )
1338 const int linearIntersect = GEOSRelatePattern_r( context, mGeos.get(), splitLine,
"1********" );
1339 if ( linearIntersect > 0 )
1347 std::vector<geos::unique_ptr> lineGeoms;
1349 const int splitType = GEOSGeomTypeId_r( context, splitGeom.get() );
1350 if ( splitType == GEOS_MULTILINESTRING )
1352 const int nGeoms = GEOSGetNumGeometries_r( context, splitGeom.get() );
1353 lineGeoms.reserve( nGeoms );
1354 for (
int i = 0; i < nGeoms; ++i )
1355 lineGeoms.emplace_back( GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, splitGeom.get(), i ) ) );
1360 lineGeoms.emplace_back( GEOSGeom_clone_r( context, splitGeom.get() ) );
1363 mergeGeometriesMultiTypeSplit( lineGeoms );
1367 newGeometries << QgsGeometry(
fromGeos( lineGeom.get() ) );
1383 if ( !mGeosPrepared )
1389 if ( !skipIntersectionCheck && !GEOSPreparedIntersects_r( context, mGeosPrepared.get(), splitLine ) )
1394 if ( !nodedGeometry )
1403 const int numberOfGeometriesPolygon = numberOfGeometries( polygons.get() );
1404 if ( numberOfGeometriesPolygon == 0 )
1411 std::vector<geos::unique_ptr> testedGeometries;
1416 for (
int i = 0; i < numberOfGeometriesPolygon; i++ )
1418 const GEOSGeometry *polygon = GEOSGetGeometryN_r( context, polygons.get(), i );
1422 testedGeometries.emplace_back( GEOSGeom_clone_r( context, polygon ) );
1425 const size_t nGeometriesThis = numberOfGeometries( mGeos.get() );
1426 if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
1436 mergeGeometriesMultiTypeSplit( testedGeometries );
1439 for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( context, testedGeometries[i].get() ); ++i )
1442 if ( i < testedGeometries.size() )
1449 newGeometries << QgsGeometry(
fromGeos( testedGeometry.get() ) );
1457 if ( !splitLine || !geom )
1462 if ( GEOSGeom_getDimensions_r( context, geom ) == 2 )
1463 geometryBoundary.reset( GEOSBoundary_r( context, geom ) );
1465 geometryBoundary.reset( GEOSGeom_clone_r( context, geom ) );
1468 geos::unique_ptr unionGeometry( GEOSUnion_r( context, splitLineClone.get(), geometryBoundary.get() ) );
1470 return unionGeometry;
1473int QgsGeos::mergeGeometriesMultiTypeSplit( std::vector<geos::unique_ptr> &splitResult )
const
1480 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1481 if ( type != GEOS_GEOMETRYCOLLECTION &&
1482 type != GEOS_MULTILINESTRING &&
1483 type != GEOS_MULTIPOLYGON &&
1484 type != GEOS_MULTIPOINT )
1488 std::vector<geos::unique_ptr> unionGeom;
1490 std::vector<geos::unique_ptr> newSplitResult;
1492 for (
size_t i = 0; i < splitResult.size(); ++i )
1495 bool isPart =
false;
1496 for (
int j = 0; j < GEOSGetNumGeometries_r( context, mGeos.get() ); j++ )
1498 if ( GEOSEquals_r( context, splitResult[i].get(), GEOSGetGeometryN_r( context, mGeos.get(), j ) ) )
1507 unionGeom.emplace_back( std::move( splitResult[i] ) );
1511 std::vector<geos::unique_ptr> geomVector;
1512 geomVector.emplace_back( std::move( splitResult[i] ) );
1514 if ( type == GEOS_MULTILINESTRING )
1515 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, geomVector ) );
1516 else if ( type == GEOS_MULTIPOLYGON )
1517 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, geomVector ) );
1521 splitResult = std::move( newSplitResult );
1524 if ( !unionGeom.empty() )
1526 if ( type == GEOS_MULTILINESTRING )
1527 splitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, unionGeom ) );
1528 else if ( type == GEOS_MULTIPOLYGON )
1529 splitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ) );
1535geos::unique_ptr QgsGeos::createGeosCollection(
int typeId, std::vector<geos::unique_ptr> &geoms )
1537 std::vector<GEOSGeometry *> geomarr;
1538 geomarr.reserve( geoms.size() );
1543 if ( geomUniquePtr )
1545 if ( !GEOSisEmpty_r( context, geomUniquePtr.get() ) )
1549 geomarr.emplace_back( geomUniquePtr.release() );
1557 geomRes.reset( GEOSGeom_createCollection_r( context, typeId, geomarr.data(), geomarr.size() ) );
1559 catch ( QgsGeosException & )
1563 GEOSGeom_destroy_r( context, geom );
1578 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1579 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1580 bool hasZ = ( nCoordDims == 3 );
1581 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1583 switch ( GEOSGeomTypeId_r( context,
geos ) )
1587 if ( GEOSisEmpty_r( context,
geos ) )
1590 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1591 unsigned int nPoints = 0;
1592 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1601 return !point.
isEmpty() ? std::unique_ptr<QgsAbstractGeometry>( point.
clone() ) :
nullptr;
1603 case GEOS_LINESTRING:
1605 return sequenceToLinestring(
geos, hasZ, hasM );
1611 case GEOS_MULTIPOINT:
1613 auto multiPoint = std::make_unique<QgsMultiPoint>();
1614 int nParts = GEOSGetNumGeometries_r( context,
geos );
1615 multiPoint->reserve( nParts );
1616 for (
int i = 0; i < nParts; ++i )
1618 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context, GEOSGetGeometryN_r( context,
geos, i ) );
1621 unsigned int nPoints = 0;
1622 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1624 multiPoint->addGeometry(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1627 return std::move( multiPoint );
1629 case GEOS_MULTILINESTRING:
1631 auto multiLineString = std::make_unique<QgsMultiLineString>();
1632 int nParts = GEOSGetNumGeometries_r( context,
geos );
1633 multiLineString->reserve( nParts );
1634 for (
int i = 0; i < nParts; ++i )
1636 std::unique_ptr< QgsLineString >line( sequenceToLinestring( GEOSGetGeometryN_r( context,
geos, i ), hasZ, hasM ) );
1639 multiLineString->addGeometry( line.release() );
1642 return std::move( multiLineString );
1644 case GEOS_MULTIPOLYGON:
1646 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
1648 int nParts = GEOSGetNumGeometries_r( context,
geos );
1649 multiPolygon->reserve( nParts );
1650 for (
int i = 0; i < nParts; ++i )
1652 std::unique_ptr< QgsPolygon > poly =
fromGeosPolygon( GEOSGetGeometryN_r( context,
geos, i ) );
1655 multiPolygon->addGeometry( poly.release() );
1658 return std::move( multiPolygon );
1660 case GEOS_GEOMETRYCOLLECTION:
1662 auto geomCollection = std::make_unique<QgsGeometryCollection>();
1663 int nParts = GEOSGetNumGeometries_r( context,
geos );
1664 geomCollection->reserve( nParts );
1665 for (
int i = 0; i < nParts; ++i )
1667 std::unique_ptr< QgsAbstractGeometry > geom(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1670 geomCollection->addGeometry( geom.release() );
1673 return std::move( geomCollection );
1682 if ( GEOSGeomTypeId_r( context,
geos ) != GEOS_POLYGON )
1687 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1688 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1689 bool hasZ = ( nCoordDims == 3 );
1690 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1692 auto polygon = std::make_unique<QgsPolygon>();
1697 polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1700 QVector<QgsCurve *> interiorRings;
1701 const int ringCount = GEOSGetNumInteriorRings_r( context,
geos );
1702 interiorRings.reserve( ringCount );
1703 for (
int i = 0; i < ringCount; ++i )
1705 ring = GEOSGetInteriorRingN_r( context,
geos, i );
1708 interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1711 polygon->setInteriorRings( interiorRings );
1716std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring(
const GEOSGeometry *
geos,
bool hasZ,
bool hasM )
1719 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1721 unsigned int nPoints;
1722 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1724 QVector< double > xOut( nPoints );
1725 QVector< double > yOut( nPoints );
1726 QVector< double > zOut;
1728 zOut.resize( nPoints );
1729 QVector< double > mOut;
1731 mOut.resize( nPoints );
1733 double *x = xOut.data();
1734 double *y = yOut.data();
1735 double *z = zOut.data();
1736 double *m = mOut.data();
1738#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
1739 GEOSCoordSeq_copyToArrays_r( context, cs, x, y, hasZ ? z :
nullptr, hasM ? m :
nullptr );
1741 for (
unsigned int i = 0; i < nPoints; ++i )
1744 GEOSCoordSeq_getXYZ_r( context, cs, i, x++, y++, z++ );
1746 GEOSCoordSeq_getXY_r( context, cs, i, x++, y++ );
1749 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, m++ );
1753 auto line = std::make_unique<QgsLineString>( xOut, yOut, zOut, mOut );
1763 int geometryType = GEOSGeomTypeId_r( context, g );
1764 if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING
1765 || geometryType == GEOS_POLYGON )
1769 return GEOSGetNumGeometries_r( context, g );
1785 GEOSCoordSeq_getXYZ_r( context, cs, i, &x, &y, &z );
1787 GEOSCoordSeq_getXY_r( context, cs, i, &x, &y );
1790 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, &m );
1826 int geosType = GEOS_GEOMETRYCOLLECTION;
1833 geosType = GEOS_MULTIPOINT;
1837 geosType = GEOS_MULTILINESTRING;
1841 geosType = GEOS_MULTIPOLYGON;
1856 std::vector<geos::unique_ptr> geomVector;
1857 geomVector.reserve(
c->numGeometries() );
1858 for (
int i = 0; i <
c->numGeometries(); ++i )
1865 geomVector.emplace_back( std::move( geosGeom ) );
1867 return createGeosCollection( geosType, geomVector );
1875 if ( !polyhedralSurface )
1878 std::vector<geos::unique_ptr> geomVector;
1879 geomVector.reserve( polyhedralSurface->
numPatches() );
1880 for (
int i = 0; i < polyhedralSurface->
numPatches(); ++i )
1887 geomVector.emplace_back( std::move( geosPolygon ) );
1890 return createGeosCollection( GEOS_MULTIPOLYGON, geomVector );
1897 return createGeosPoint(
static_cast<const QgsPoint *
>( geom ), coordDims, precision, flags );
1900 return createGeosLinestring(
static_cast<const QgsLineString *
>( geom ), precision, flags );
1903 return createGeosPolygon(
static_cast<const QgsPolygon *
>( geom ), precision, flags );
1915 if ( !mGeos || !geom )
1926 const double gridSize = parameters.
gridSize();
1934 case OverlayIntersection:
1937 opGeom.reset( GEOSIntersectionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1941 opGeom.reset( GEOSIntersection_r( context, mGeos.get(), geosGeom.get() ) );
1945 case OverlayDifference:
1948 opGeom.reset( GEOSDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1952 opGeom.reset( GEOSDifference_r( context, mGeos.get(), geosGeom.get() ) );
1961 unionGeometry.reset( GEOSUnionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1965 unionGeometry.reset( GEOSUnion_r( context, mGeos.get(), geosGeom.get() ) );
1968 if ( unionGeometry && GEOSGeomTypeId_r( context, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1970 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, unionGeometry.get() ) );
1973 unionGeometry = std::move( mergedLines );
1977 opGeom = std::move( unionGeometry );
1981 case OverlaySymDifference:
1984 opGeom.reset( GEOSSymDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1988 opGeom.reset( GEOSSymDifference_r( context, mGeos.get(), geosGeom.get() ) );
1994 catch ( QgsGeosException &e )
1999 *errorMsg = e.what();
2005bool QgsGeos::relation(
const QgsAbstractGeometry *geom, Relation r, QString *errorMsg )
const
2007 if ( !mGeos || !geom )
2019 bool result =
false;
2022 if ( mGeosPrepared )
2026 case RelationIntersects:
2027 result = ( GEOSPreparedIntersects_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2029 case RelationTouches:
2030 result = ( GEOSPreparedTouches_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2032 case RelationCrosses:
2033 result = ( GEOSPreparedCrosses_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2035 case RelationWithin:
2036 result = ( GEOSPreparedWithin_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2038 case RelationContains:
2039 result = ( GEOSPreparedContains_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2041 case RelationDisjoint:
2042 result = ( GEOSPreparedDisjoint_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2044 case RelationOverlaps:
2045 result = ( GEOSPreparedOverlaps_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2053 case RelationIntersects:
2054 result = ( GEOSIntersects_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2056 case RelationTouches:
2057 result = ( GEOSTouches_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2059 case RelationCrosses:
2060 result = ( GEOSCrosses_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2062 case RelationWithin:
2063 result = ( GEOSWithin_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2065 case RelationContains:
2066 result = ( GEOSContains_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2068 case RelationDisjoint:
2069 result = ( GEOSDisjoint_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2071 case RelationOverlaps:
2072 result = ( GEOSOverlaps_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2076 catch ( QgsGeosException &e )
2081 *errorMsg = e.what();
2121 geos.reset( GEOSBufferWithStyle_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( endCapStyle ),
static_cast< int >( joinStyle ), miterLimit ) );
2171 geos.reset( GEOSGetCentroid_r( context, mGeos.get() ) );
2176 GEOSGeomGetX_r( context,
geos.get(), &x );
2177 GEOSGeomGetY_r( context,
geos.get(), &y );
2213 geos.reset( GEOSPointOnSurface_r( context, mGeos.get() ) );
2215 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
2220 GEOSGeomGetX_r( context,
geos.get(), &x );
2221 GEOSGeomGetY_r( context,
geos.get(), &y );
2238 std::unique_ptr< QgsAbstractGeometry > cHullGeom =
fromGeos( cHull.get() );
2239 return cHullGeom.release();
2244std::unique_ptr< QgsAbstractGeometry >
QgsGeos::concaveHull(
double targetPercent,
bool allowHoles, QString *errorMsg )
const
2246#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
2248 ( void )targetPercent;
2250 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHull requires a QGIS build based on GEOS 3.11 or later" ) );
2261 return concaveHullGeom;
2269#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<12
2271 ( void )invalidEdges;
2273 throw QgsNotSupportedException( QObject::tr(
"Validating coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2278 *errorMsg = u
"Input geometry was not set"_s;
2286 const int result = GEOSCoverageIsValid_r( context, mGeos.get(), gapWidth, invalidEdges ? &invalidEdgesGeos :
nullptr );
2287 if ( invalidEdges && invalidEdgesGeos )
2289 *invalidEdges =
fromGeos( invalidEdgesGeos );
2291 if ( invalidEdgesGeos )
2293 GEOSGeom_destroy_r( context, invalidEdgesGeos );
2294 invalidEdgesGeos =
nullptr;
2314#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<12
2316 ( void )preserveBoundary;
2318 throw QgsNotSupportedException( QObject::tr(
"Simplifying coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2323 *errorMsg = u
"Input geometry was not set"_s;
2330 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom =
fromGeos( simplified.get() );
2331 return simplifiedGeom;
2342 *errorMsg = u
"Input geometry was not set"_s;
2349 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( unioned.get() );
2360 *errorMsg = QObject::tr(
"QGIS geometry cannot be converted to a GEOS geometry",
"GEOS Error" );
2369 char res = GEOSisValidDetail_r( context, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
2370 const bool invalid = res != 1;
2375 error = QString( r );
2376 GEOSFree_r( context, r );
2379 if ( invalid && errorMsg )
2382 static const std::map< QString, QString > sTranslatedErrors
2384 { u
"topology validation error"_s, QObject::tr(
"Topology validation error",
"GEOS Error" ) },
2385 { u
"repeated point"_s, QObject::tr(
"Repeated point",
"GEOS Error" ) },
2386 { u
"hole lies outside shell"_s, QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) },
2387 { u
"holes are nested"_s, QObject::tr(
"Holes are nested",
"GEOS Error" ) },
2388 { u
"interior is disconnected"_s, QObject::tr(
"Interior is disconnected",
"GEOS Error" ) },
2389 { u
"self-intersection"_s, QObject::tr(
"Self-intersection",
"GEOS Error" ) },
2390 { u
"ring self-intersection"_s, QObject::tr(
"Ring self-intersection",
"GEOS Error" ) },
2391 { u
"nested shells"_s, QObject::tr(
"Nested shells",
"GEOS Error" ) },
2392 { u
"duplicate rings"_s, QObject::tr(
"Duplicate rings",
"GEOS Error" ) },
2393 { u
"too few points in geometry component"_s, QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) },
2394 { u
"invalid coordinate"_s, QObject::tr(
"Invalid coordinate",
"GEOS Error" ) },
2395 { u
"ring is not closed"_s, QObject::tr(
"Ring is not closed",
"GEOS Error" ) },
2398 const auto translatedError = sTranslatedErrors.find( error.toLower() );
2399 if ( translatedError != sTranslatedErrors.end() )
2400 *errorMsg = translatedError->second;
2404 if ( g1 && errorLoc )
2410 GEOSGeom_destroy_r( context, g1 );
2420 if ( !mGeos || !geom )
2466GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
2470 std::unique_ptr< QgsLineString > segmentized;
2476 line = segmentized.get();
2483 GEOSCoordSequence *coordSeq =
nullptr;
2485 const int numPoints = line->
numPoints();
2487 const bool hasZ = line->
is3D();
2489#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
2492 if ( !forceClose || ( line->
pointN( 0 ) == line->
pointN( numPoints - 1 ) ) )
2497 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, line->
xData(), line->
yData(), line->
zData(),
nullptr, numPoints );
2500 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points"_s.arg( numPoints ) );
2508 QVector< double > x = line->
xVector();
2509 if ( numPoints > 0 )
2510 x.append( x.at( 0 ) );
2511 QVector< double > y = line->
yVector();
2512 if ( numPoints > 0 )
2513 y.append( y.at( 0 ) );
2514 QVector< double > z = line->
zVector();
2515 if ( hasZ && numPoints > 0 )
2516 z.append( z.at( 0 ) );
2519 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, x.constData(), y.constData(), !hasZ ?
nullptr : z.constData(),
nullptr, numPoints + 1 );
2522 QgsDebugError( u
"GEOS Exception: Could not create closed coordinate sequence for %1 points"_s.arg( numPoints + 1 ) );
2533 const bool hasM =
false;
2544 int numOutPoints = numPoints;
2545 if ( forceClose && ( line->
pointN( 0 ) != line->
pointN( numPoints - 1 ) ) )
2552 coordSeq = GEOSCoordSeq_create_r( context, numOutPoints, coordDims );
2555 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions"_s.arg( numPoints ).arg( coordDims ) );
2559 const double *xData = line->
xData();
2560 const double *yData = line->
yData();
2561 const double *zData = hasZ ? line->
zData() :
nullptr;
2562 const double *mData = hasM ? line->
mData() :
nullptr;
2564 if ( precision > 0. )
2566 for (
int i = 0; i < numOutPoints; ++i )
2568 if ( i >= numPoints )
2571 xData = line->
xData();
2572 yData = line->
yData();
2573 zData = hasZ ? line->
zData() :
nullptr;
2574 mData = hasM ? line->
mData() :
nullptr;
2578 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
2582 GEOSCoordSeq_setXY_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
2586 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2592 for (
int i = 0; i < numOutPoints; ++i )
2594 if ( i >= numPoints )
2597 xData = line->
xData();
2598 yData = line->
yData();
2599 zData = hasZ ? line->
zData() :
nullptr;
2600 mData = hasM ? line->
mData() :
nullptr;
2604 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, *xData++, *yData++, *zData++ );
2608 GEOSCoordSeq_setXY_r( context, coordSeq, i, *xData++, *yData++ );
2612 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2622geos::unique_ptr
QgsGeos::createGeosPoint( const QgsAbstractGeometry *point,
int coordDims,
double precision, Qgis::GeosCreationFlags )
2628 return createGeosPointXY( pt->
x(), pt->
y(), pt->
is3D(), pt->
z(), pt->
isMeasure(), pt->
m(), coordDims, precision );
2640 if ( coordDims == 2 )
2643 if ( precision > 0. )
2644 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
2646 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, x, y ) );
2650 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( context, 1, coordDims );
2653 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions"_s.arg( coordDims ) );
2656 if ( precision > 0. )
2658 GEOSCoordSeq_setX_r( context, coordSeq, 0, std::round( x / precision ) * precision );
2659 GEOSCoordSeq_setY_r( context, coordSeq, 0, std::round( y / precision ) * precision );
2662 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, std::round( z / precision ) * precision );
2667 GEOSCoordSeq_setX_r( context, coordSeq, 0, x );
2668 GEOSCoordSeq_setY_r( context, coordSeq, 0, y );
2671 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, z );
2677 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 3, m );
2680 geosPoint.reset( GEOSGeom_createPoint_r( context, coordSeq ) );
2686geos::unique_ptr
QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve,
double precision, Qgis::GeosCreationFlags )
2692 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c, precision );
2705geos::unique_ptr
QgsGeos::createGeosPolygon( const QgsAbstractGeometry *poly,
double precision, Qgis::GeosCreationFlags flags )
2711 const QgsCurve *exteriorRing = polygon->
exteriorRing();
2712 if ( !exteriorRing )
2721 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( context, createCoordinateSequence( exteriorRing, precision,
true ) ) );
2724 QList< const QgsCurve * > holesToExport;
2725 holesToExport.reserve( nInteriorRings );
2726 for (
int i = 0; i < nInteriorRings; ++i )
2728 const QgsCurve *interiorRing = polygon->
interiorRing( i );
2731 holesToExport << interiorRing;
2736 if ( !holesToExport.empty() )
2739 for (
int i = 0; i < holesToExport.size(); ++i )
2741 holes[i] = GEOSGeom_createLinearRing_r( context, createCoordinateSequence( holesToExport[i], precision,
true ) );
2745 geosPolygon.reset( GEOSGeom_createPolygon_r( context, exteriorRingGeos.release(), holes, holesToExport.size() ) );
2765 offset.reset( GEOSOffsetCurve_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( joinStyle ), miterLimit ) );
2777 return fromGeos( res.get() ).release();
2792 GEOSBufferParams_setSingleSided_r( context, bp.get(), 1 );
2793 GEOSBufferParams_setQuadrantSegments_r( context, bp.get(), segments );
2794 GEOSBufferParams_setJoinStyle_r( context, bp.get(),
static_cast< int >( joinStyle ) );
2795 GEOSBufferParams_setMitreLimit_r( context, bp.get(), miterLimit );
2801 geos.reset( GEOSBufferWithParams_r( context, mGeos.get(), bp.get(),
distance ) );
2835 boundaryGeos =
asGeos( boundary );
2863 return std::numeric_limits< double >::quiet_NaN();
2871 return std::numeric_limits< double >::quiet_NaN();
2911 if ( !mGeos || !other )
2931 if ( !mGeos ||
mGeometry->dimension() == 0 )
2943 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
2947 int numGeoms = GEOSGetNumGeometries_r( context, mGeos.get() );
2948 if ( numGeoms == -1 )
2957 bool isMultiGeom =
false;
2958 int geosTypeId = GEOSGeomTypeId_r( context, mGeos.get() );
2959 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
2962 bool isLine = (
mGeometry->dimension() == 1 );
2969 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2973 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2978 if ( reshapedGeometry )
2984 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
2985 return reshapeResult;
2992 bool reshapeTookPlace =
false;
2997 for (
int i = 0; i < numGeoms; ++i )
3000 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3002 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3004 if ( currentReshapeGeometry )
3006 newGeoms[i] = currentReshapeGeometry.release();
3007 reshapeTookPlace =
true;
3011 newGeoms[i] = GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, mGeos.get(), i ) );
3018 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
3022 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
3026 if ( !newMultiGeom )
3032 if ( reshapeTookPlace )
3036 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
3037 return reshapedMultiGeom;
3060 if ( GEOSGeomTypeId_r( context, mGeos.get() ) != GEOS_MULTILINESTRING )
3066 double gridSize = parameters.
gridSize();
3069 geos::unique_ptr geosFixedSize( GEOSGeom_setPrecision_r( context, mGeos.get(), gridSize, 0 ) );
3070 geos.reset( GEOSLineMerge_r( context, geosFixedSize.get() ) );
3073 geos.reset( GEOSLineMerge_r( context, mGeos.get() ) );
3098 if ( mGeosPrepared )
3100 nearestCoord.reset( GEOSPreparedNearestPoints_r( context, mGeosPrepared.get(), otherGeom.get() ) );
3104 nearestCoord.reset( GEOSNearestPoints_r( context, mGeos.get(), otherGeom.get() ) );
3107 ( void )GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx );
3108 ( void )GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny );
3110 catch ( QgsGeosException &e )
3115 *errorMsg = e.what();
3120 return std::make_unique< QgsPoint >( nx, ny );
3125 if ( !mGeos || other.
isEmpty() )
3135 if ( !other || other->
isEmpty() )
3153 if ( !nearestCoord )
3156 *errorMsg = u
"GEOS returned no nearest points"_s;
3160 ( void )GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx1 );
3161 ( void )GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny1 );
3162 ( void )GEOSCoordSeq_getX_r( context, nearestCoord.get(), 1, &nx2 );
3163 ( void )GEOSCoordSeq_getY_r( context, nearestCoord.get(), 1, &ny2 );
3165 catch ( QgsGeosException &e )
3170 *errorMsg = e.what();
3175 auto line = std::make_unique< QgsLineString >();
3199 catch ( QgsGeosException &e )
3204 *errorMsg = e.what();
3219 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
3228 catch ( QgsGeosException &e )
3233 *errorMsg = e.what();
3250 lineGeosGeometries[validLines] = l.release();
3258 geos::unique_ptr result( GEOSPolygonize_r( context, lineGeosGeometries, validLines ) );
3259 for (
int i = 0; i < validLines; ++i )
3261 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3263 delete[] lineGeosGeometries;
3266 catch ( QgsGeosException &e )
3270 *errorMsg = e.what();
3272 for (
int i = 0; i < validLines; ++i )
3274 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3276 delete[] lineGeosGeometries;
3291 extentGeosGeom =
asGeos( extent, mPrecision );
3292 if ( !extentGeosGeom )
3302 geos.reset( GEOSVoronoiDiagram_r( context, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
3304 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3325 geos.reset( GEOSDelaunayTriangulation_r( context, mGeos.get(), tolerance, edgesOnly ) );
3327 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3339#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
3341 throw QgsNotSupportedException( QObject::tr(
"Calculating constrainedDelaunayTriangulation requires a QGIS build based on GEOS 3.11 or later" ) );
3352 geos.reset( GEOSConstrainedDelaunayTriangulation_r( context, mGeos.get() ) );
3354 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3359 std::unique_ptr< QgsAbstractGeometry > res =
fromGeos(
geos.get() );
3362 return std::unique_ptr< QgsAbstractGeometry >( collection->extractPartsByType(
Qgis::WkbType::Polygon,
true ) );
3374static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
3377 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( context, linestring );
3381 unsigned int coordSeqSize;
3382 if ( GEOSCoordSeq_getSize_r( context, coordSeq, &coordSeqSize ) == 0 )
3385 if ( coordSeqSize < 2 )
3388 GEOSCoordSeq_getX_r( context, coordSeq, 0, &x1 );
3389 GEOSCoordSeq_getY_r( context, coordSeq, 0, &y1 );
3390 GEOSCoordSeq_getX_r( context, coordSeq, coordSeqSize - 1, &x2 );
3391 GEOSCoordSeq_getY_r( context, coordSeq, coordSeqSize - 1, &y2 );
3399 double x1, y1, x2, y2;
3400 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
3403 double rx1, ry1, rx2, ry2;
3404 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
3407 bool intersectionAtOrigLineEndpoint =
3408 ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) !=
3409 ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
3410 bool intersectionAtReshapeLineEndpoint =
3411 ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) ||
3412 ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
3416 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
3420 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
3421 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, geoms, 2 ) );
3426 double x1res, y1res, x2res, y2res;
3427 if ( !_linestringEndpoints( res.get(), x1res, y1res, x2res, y2res ) )
3429 if ( ( x1res == x2 && y1res == y2 ) || ( x2res == x1 && y2res == y1 ) )
3430 res.reset( GEOSReverse_r( context, res.get() ) );
3441 if ( !line || !reshapeLineGeos )
3444 bool atLeastTwoIntersections =
false;
3445 bool oneIntersection =
false;
3446 QgsPointXY oneIntersectionPoint;
3452 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, line, reshapeLineGeos ) );
3453 if ( intersectGeom )
3455 const int geomType = GEOSGeomTypeId_r( context, intersectGeom.get() );
3456 atLeastTwoIntersections = ( geomType == GEOS_MULTIPOINT && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 1 )
3457 || ( geomType == GEOS_GEOMETRYCOLLECTION && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 )
3458 || ( geomType == GEOS_MULTILINESTRING && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 );
3460 if ( GEOSGeomTypeId_r( context, intersectGeom.get() ) == GEOS_POINT )
3462 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( context, intersectGeom.get() );
3464 GEOSCoordSeq_getX_r( context, intersectionCoordSeq, 0, &xi );
3465 GEOSCoordSeq_getY_r( context, intersectionCoordSeq, 0, &yi );
3466 oneIntersection =
true;
3467 oneIntersectionPoint = QgsPointXY( xi, yi );
3471 catch ( QgsGeosException & )
3473 atLeastTwoIntersections =
false;
3477 if ( oneIntersection )
3478 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
3480 if ( !atLeastTwoIntersections )
3484 double x1, y1, x2, y2;
3485 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
3488 geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1,
false, 0,
false, 0, 2, precision );
3489 geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2,
false, 0,
false, 0, 2, precision );
3491 bool isRing =
false;
3492 if ( GEOSGeomTypeId_r( context, line ) == GEOS_LINEARRING
3493 || GEOSEquals_r( context, beginLineVertex.get(), endLineVertex.get() ) == 1 )
3498 if ( !nodedGeometry )
3504 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, nodedGeometry.get() ) );
3510 int numMergedLines = GEOSGetNumGeometries_r( context, mergedLines.get() );
3511 if ( numMergedLines < 2 )
3513 if ( numMergedLines == 1 )
3522 QVector<GEOSGeometry *> resultLineParts;
3523 QVector<GEOSGeometry *> probableParts;
3525 for (
int i = 0; i < numMergedLines; ++i )
3527 const GEOSGeometry *currentGeom = GEOSGetGeometryN_r( context, mergedLines.get(), i );
3530 bool alreadyAdded =
false;
3532 double bufferDistance = std::pow( 10.0L, geomDigits( currentGeom ) - 11 );
3533 for (
const GEOSGeometry *other : std::as_const( resultLineParts ) )
3535 GEOSHausdorffDistance_r( context, currentGeom, other, &
distance );
3538 alreadyAdded =
true;
3545 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( context, currentGeom );
3546 unsigned int currentCoordSeqSize;
3547 GEOSCoordSeq_getSize_r( context, currentCoordSeq, ¤tCoordSeqSize );
3548 if ( currentCoordSeqSize < 2 )
3552 double xBegin, xEnd, yBegin, yEnd;
3553 GEOSCoordSeq_getX_r( context, currentCoordSeq, 0, &xBegin );
3554 GEOSCoordSeq_getY_r( context, currentCoordSeq, 0, &yBegin );
3555 GEOSCoordSeq_getX_r( context, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
3556 GEOSCoordSeq_getY_r( context, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
3557 geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin,
false, 0,
false, 0, 2, precision );
3558 geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd,
false, 0,
false, 0, 2, precision );
3561 int nEndpointsOnOriginalLine = 0;
3562 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
3563 nEndpointsOnOriginalLine += 1;
3565 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
3566 nEndpointsOnOriginalLine += 1;
3569 int nEndpointsSameAsOriginalLine = 0;
3570 if ( GEOSEquals_r( context, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
3571 || GEOSEquals_r( context, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3572 nEndpointsSameAsOriginalLine += 1;
3574 if ( GEOSEquals_r( context, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
3575 || GEOSEquals_r( context, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3576 nEndpointsSameAsOriginalLine += 1;
3579 bool currentGeomOverlapsOriginalGeom =
false;
3580 bool currentGeomOverlapsReshapeLine =
false;
3581 if ( lineContainedInLine( currentGeom, line ) == 1 )
3582 currentGeomOverlapsOriginalGeom =
true;
3584 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
3585 currentGeomOverlapsReshapeLine =
true;
3588 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3590 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3593 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3595 probableParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3597 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3599 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3601 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3603 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3605 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
3607 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3612 if ( isRing && !probableParts.isEmpty() )
3616 double maxLength = -std::numeric_limits<double>::max();
3617 double currentLength = 0;
3618 for (
int i = 0; i < probableParts.size(); ++i )
3620 currentGeom = probableParts.at( i );
3621 GEOSLength_r( context, currentGeom, ¤tLength );
3622 if ( currentLength > maxLength )
3624 maxLength = currentLength;
3625 maxGeom.reset( currentGeom );
3629 GEOSGeom_destroy_r( context, currentGeom );
3632 resultLineParts.push_back( maxGeom.release() );
3636 if ( resultLineParts.empty() )
3639 if ( resultLineParts.size() == 1 )
3641 result.reset( resultLineParts[0] );
3646 for (
int i = 0; i < resultLineParts.size(); ++i )
3648 lineArray[i] = resultLineParts[i];
3652 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
3653 delete [] lineArray;
3656 result.reset( GEOSLineMerge_r( context, multiLineGeom.get() ) );
3660 if ( GEOSGeomTypeId_r( context, result.get() ) != GEOS_LINESTRING )
3666 bool reverseLine =
false;
3670 char isResultCCW = 0, isOriginCCW = 0;
3671 if ( GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, result.get() ), &isResultCCW ) &&
3672 GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, line ), &isOriginCCW )
3676 reverseLine = ( isOriginCCW == 1 && isResultCCW == 0 ) || ( isOriginCCW == 0 && isResultCCW == 1 );
3682 double x1res, y1res, x2res, y2res;
3683 if ( !_linestringEndpoints( result.get(), x1res, y1res, x2res, y2res ) )
3685 geos::unique_ptr beginResultLineVertex = createGeosPointXY( x1res, y1res,
false, 0,
false, 0, 2, precision );
3686 geos::unique_ptr endResultLineVertex = createGeosPointXY( x2res, y2res,
false, 0,
false, 0, 2, precision );
3687 reverseLine = GEOSEquals_r( context, beginLineVertex.get(), endResultLineVertex.get() ) == 1 || GEOSEquals_r( context, endLineVertex.get(), beginResultLineVertex.get() ) == 1;
3690 result.reset( GEOSReverse_r( context, result.get() ) );
3698 int nIntersections = 0;
3699 int lastIntersectingRing = -2;
3703 int nRings = GEOSGetNumInteriorRings_r( context, polygon );
3708 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( context, polygon );
3709 if ( GEOSIntersects_r( context, outerRing, reshapeLineGeos ) == 1 )
3712 lastIntersectingRing = -1;
3713 lastIntersectingGeom = outerRing;
3721 for (
int i = 0; i < nRings; ++i )
3723 innerRings[i] = GEOSGetInteriorRingN_r( context, polygon, i );
3724 if ( GEOSIntersects_r( context, innerRings[i], reshapeLineGeos ) == 1 )
3727 lastIntersectingRing = i;
3728 lastIntersectingGeom = innerRings[i];
3732 catch ( QgsGeosException & )
3737 if ( nIntersections != 1 )
3739 delete [] innerRings;
3744 geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
3745 if ( !reshapeResult )
3747 delete [] innerRings;
3753 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( context, reshapeResult.get() );
3754 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( context, reshapeSequence );
3756 reshapeResult.reset();
3760 newRing = GEOSGeom_createLinearRing_r( context, newCoordSequence );
3762 catch ( QgsGeosException & )
3769 delete [] innerRings;
3774 if ( lastIntersectingRing == -1 )
3775 newOuterRing = newRing;
3777 newOuterRing = GEOSGeom_clone_r( context, outerRing );
3780 QVector<GEOSGeometry *> ringList;
3783 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( context, GEOSGeom_clone_r( context, newOuterRing ),
nullptr, 0 );
3784 if ( outerRingPoly )
3786 ringList.reserve( nRings );
3788 for (
int i = 0; i < nRings; ++i )
3790 if ( lastIntersectingRing == i )
3791 currentRing = newRing;
3793 currentRing = GEOSGeom_clone_r( context, innerRings[i] );
3796 if ( GEOSContains_r( context, outerRingPoly, currentRing ) == 1 )
3797 ringList.push_back( currentRing );
3799 GEOSGeom_destroy_r( context, currentRing );
3802 GEOSGeom_destroy_r( context, outerRingPoly );
3806 for (
int i = 0; i < ringList.size(); ++i )
3807 newInnerRings[i] = ringList.at( i );
3809 delete [] innerRings;
3811 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( context, newOuterRing, newInnerRings, ringList.size() ) );
3812 delete[] newInnerRings;
3814 return reshapedPolygon;
3819 if ( !line1 || !line2 )
3824 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
3831 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, bufferGeom.get(), line1 ) );
3834 double intersectGeomLength;
3837 GEOSLength_r( context, intersectionGeom.get(), &intersectGeomLength );
3838 GEOSLength_r( context, line1, &line1Length );
3840 double intersectRatio = line1Length / intersectGeomLength;
3841 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
3849 if ( !point || !line )
3852 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
3855 geos::unique_ptr lineBuffer( GEOSBuffer_r( context, line, bufferDistance, 8 ) );
3859 bool contained =
false;
3860 if ( GEOSContains_r( context, lineBuffer.get(), point ) == 1 )
3873 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( context, bbox.get() );
3877 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( context, bBoxRing );
3879 if ( !bBoxCoordSeq )
3882 unsigned int nCoords = 0;
3883 if ( !GEOSCoordSeq_getSize_r( context, bBoxCoordSeq, &nCoords ) )
3887 for (
unsigned int i = 0; i < nCoords - 1; ++i )
3890 GEOSCoordSeq_getX_r( context, bBoxCoordSeq, i, &t );
3893 digits = std::ceil( std::log10( std::fabs( t ) ) );
3894 if ( digits > maxDigits )
3897 GEOSCoordSeq_getY_r( context, bBoxCoordSeq, i, &t );
3898 digits = std::ceil( std::log10( std::fabs( t ) ) );
3899 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