36#define DEFAULT_QUADRANT_SEGMENTS 8
38#define CATCH_GEOS(r) \
39 catch (QgsGeosException &) \
44#define CATCH_GEOS_WITH_ERRMSG(r) \
45 catch (QgsGeosException &e) \
49 *errorMsg = e.what(); \
56static void throwQgsGeosException(
const char *fmt, ... )
62 vsnprintf( buffer,
sizeof buffer, fmt, ap );
65 QString message = QString::fromUtf8( buffer );
75 throw QgsGeosException( message );
83 throw QgsGeosException( message );
88static void printGEOSNotice(
const char *fmt, ... )
95 vsnprintf( buffer,
sizeof buffer, fmt, ap );
106#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
109QThreadStorage< QgsGeosContext * > QgsGeosContext::sGeosContext;
115 mContext = GEOS_init_r();
116 GEOSContext_setNoticeHandler_r( mContext, printGEOSNotice );
117 GEOSContext_setErrorHandler_r( mContext, throwQgsGeosException );
122 GEOS_finish_r( mContext );
127#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
128 return sGeosContext.mContext;
130 GEOSContextHandle_t gContext =
nullptr;
131 if ( sGeosContext.hasLocalData() )
133 gContext = sGeosContext.localData()->mContext;
138 gContext = sGeosContext.localData()->mContext;
175 , mPrecision( precision )
202#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
204 throw QgsNotSupportedException( QObject::tr(
"The structured method to make geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
207 throw QgsNotSupportedException( QObject::tr(
"The keep collapsed option for making geometries valid requires a QGIS build based on GEOS 3.10 or later" ) );
211 geos.reset( GEOSMakeValid_r( context, mGeos.get() ) );
216 GEOSMakeValidParams *params = GEOSMakeValidParams_create_r( context );
220 GEOSMakeValidParams_setMethod_r( context, params, GEOS_MAKE_VALID_LINEWORK );
224 GEOSMakeValidParams_setMethod_r( context, params, GEOS_MAKE_VALID_STRUCTURE );
228 GEOSMakeValidParams_setKeepCollapsed_r( context,
230 keepCollapsed ? 1 : 0 );
235 geos.reset( GEOSMakeValidWithParams_r( context, mGeos.get(), params ) );
236 GEOSMakeValidParams_destroy_r( context, params );
238 catch ( QgsGeosException &e )
242 *errorMsg = e.what();
244 GEOSMakeValidParams_destroy_r( context, params );
273 std::unique_ptr< QgsAbstractGeometry > geom =
fromGeos( newPart );
280 mGeosPrepared.reset();
314 return overlay( geom, OverlayIntersection, errorMsg, parameters ).release();
319 return overlay( geom, OverlayDifference, errorMsg, parameters ).release();
334 catch ( QgsGeosException &e )
339 *errorMsg = e.what();
348 int partType = GEOSGeomTypeId_r( context, currentPart );
351 if ( partType == GEOS_POINT )
362 if ( partType == GEOS_MULTILINESTRING || partType == GEOS_MULTIPOLYGON || partType == GEOS_GEOMETRYCOLLECTION )
364 int partCount = GEOSGetNumGeometries_r( context, currentPart );
365 for (
int i = 0; i < partCount; ++i )
367 subdivideRecursive( GEOSGetGeometryN_r( context, currentPart, i ), maxNodes, depth, parts, clipRect, gridSize );
378 int vertexCount = GEOSGetNumCoordinates_r( context, currentPart );
379 if ( vertexCount == 0 )
383 else if ( vertexCount < maxNodes )
390 double width = clipRect.
width();
391 double height = clipRect.
height();
392 QgsRectangle halfClipRect1 = clipRect;
393 QgsRectangle halfClipRect2 = clipRect;
394 if ( width > height )
407 halfClipRect1.
setYMinimum( halfClipRect1.
yMinimum() - std::numeric_limits<double>::epsilon() );
408 halfClipRect2.
setYMinimum( halfClipRect2.
yMinimum() - std::numeric_limits<double>::epsilon() );
409 halfClipRect1.
setYMaximum( halfClipRect1.
yMaximum() + std::numeric_limits<double>::epsilon() );
410 halfClipRect2.
setYMaximum( halfClipRect2.
yMaximum() + std::numeric_limits<double>::epsilon() );
414 halfClipRect1.
setXMinimum( halfClipRect1.
xMinimum() - std::numeric_limits<double>::epsilon() );
415 halfClipRect2.
setXMinimum( halfClipRect2.
xMinimum() - std::numeric_limits<double>::epsilon() );
416 halfClipRect1.
setXMaximum( halfClipRect1.
xMaximum() + std::numeric_limits<double>::epsilon() );
417 halfClipRect2.
setXMaximum( halfClipRect2.
xMaximum() + std::numeric_limits<double>::epsilon() );
429 clipPart1.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart1.get(), gridSize ) );
431 subdivideRecursive( clipPart1.get(), maxNodes, depth, parts, halfClipRect1, gridSize );
437 clipPart2.reset( GEOSIntersectionPrec_r( context, mGeos.get(), clipPart2.get(), gridSize ) );
439 subdivideRecursive( clipPart2.get(), maxNodes, depth, parts, halfClipRect2, gridSize );
451 maxNodes = std::max( maxNodes, 8 );
456 subdivideRecursive( mGeos.get(), maxNodes, 0, parts.get(),
mGeometry->boundingBox(), parameters.
gridSize() );
460 return std::move( parts );
465 return overlay( geom, OverlayUnion, errorMsg, parameters ).release();
470 std::vector<geos::unique_ptr> geosGeometries;
471 geosGeometries.reserve( geomList.size() );
477 geosGeometries.emplace_back(
asGeos( g, mPrecision ) );
484 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
487 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
491 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
496 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
497 return result.release();
502 std::vector<geos::unique_ptr> geosGeometries;
503 geosGeometries.reserve( geomList.size() );
509 geosGeometries.emplace_back(
asGeos( g.constGet(), mPrecision ) );
516 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
520 geomUnion.reset( GEOSUnaryUnionPrec_r( context, geomCollection.get(), parameters.
gridSize() ) );
524 geomUnion.reset( GEOSUnaryUnion_r( context, geomCollection.get() ) );
530 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
531 return result.release();
536 return overlay( geom, OverlaySymDifference, errorMsg, parameters ).release();
539static bool isZVerticalLine(
const QgsAbstractGeometry *geom,
double tolerance = 4 * std::numeric_limits<double>::epsilon() )
549 bool isVertical =
true;
552 const int nrPoints = line->numPoints();
560 const double sqrTolerance = tolerance * tolerance;
561 const double *lineX = line->xData();
562 const double *lineY = line->yData();
563 for (
int iVert = nrPoints - 1, jVert = 0; jVert < nrPoints; iVert = jVert++ )
593 otherGeosGeom =
asGeos( &firstPoint, mPrecision );
597 otherGeosGeom =
asGeos( geom, mPrecision );
600 if ( !otherGeosGeom )
608 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
610 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
614 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
630 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
639 GEOSPreparedDistance_r( context, mGeosPrepared.get(), point.get(), &
distance );
643 GEOSDistance_r( context, mGeos.get(), point.get(), &
distance );
667 otherGeosGeom =
asGeos( &firstPoint );
671 otherGeosGeom =
asGeos( geom, mPrecision );
674 if ( !otherGeosGeom )
687 if ( mGeosPrepared && !isZVerticalLine(
mGeometry->simplifiedTypeRef() ) )
689#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
690 return GEOSPreparedDistanceWithin_r( context, mGeosPrepared.get(), otherGeosGeom.get(), maxdist );
692 GEOSPreparedDistance_r( context, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
697#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
698 return GEOSDistanceWithin_r( context, mGeos.get(), otherGeosGeom.get(), maxdist );
700 GEOSDistance_r( context, mGeos.get(), otherGeosGeom.get(), &
distance );
715#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
718 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
724#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
725 return GEOSPreparedContainsXY_r( context, mGeosPrepared.get(), x, y ) == 1;
727 return GEOSPreparedContains_r( context, mGeosPrepared.get(), point.get() ) == 1;
731#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
732 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
737 result = ( GEOSContains_r( context, mGeos.get(), point.get() ) == 1 );
739 catch ( QgsGeosException &e )
744 *errorMsg = e.what();
761 if ( !otherGeosGeom )
784 if ( !otherGeosGeom )
807 if ( !otherGeosGeom )
830 if ( !otherGeosGeom )
846 if ( !mGeos || !geom )
851#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
859 return GEOSPreparedIntersectsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
861 catch ( QgsGeosException &e )
866 *errorMsg = e.what();
874 return relation( geom, RelationIntersects, errorMsg );
879 return relation( geom, RelationTouches, errorMsg );
884 return relation( geom, RelationCrosses, errorMsg );
889 return relation( geom, RelationWithin, errorMsg );
894 return relation( geom, RelationOverlaps, errorMsg );
899 if ( !mGeos || !geom )
904#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
912 return GEOSPreparedContainsXY_r(
QgsGeosContext::get(), mGeosPrepared.get(), point->x(), point->y() ) == 1;
914 catch ( QgsGeosException &e )
919 *errorMsg = e.what();
927 return relation( geom, RelationContains, errorMsg );
932 return relation( geom, RelationDisjoint, errorMsg );
952 char *r = GEOSRelate_r( context, mGeos.get(), geosGeom.get() );
955 result = QString( r );
956 GEOSFree_r( context, r );
959 catch ( QgsGeosException &e )
964 *errorMsg = e.what();
973 if ( !mGeos || !geom )
988 result = ( GEOSRelatePattern_r( context, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
990 catch ( QgsGeosException &e )
995 *errorMsg = e.what();
1036 QVector<QgsGeometry> &newGeometries,
1039 QString *errorMsg,
bool skipIntersectionCheck )
const
1055 if ( !GEOSisValid_r( context, mGeos.get() ) )
1063 newGeometries.clear();
1070 splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
1074 splitLineGeos = createGeosPointXY( splitLine.
xAt( 0 ), splitLine.
yAt( 0 ),
false, 0,
false, 0, 2, mPrecision );
1081 if ( !GEOSisValid_r( context, splitLineGeos.get() ) || !GEOSisSimple_r( context, splitLineGeos.get() ) )
1089 if ( !topologicalTestPointsSplit( splitLineGeos.get(), topologyTestPoints ) )
1098 returnCode = splitLinearGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1102 returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
1131 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, mGeos.get(), splitLine ) );
1132 if ( !intersectionGeom )
1135 bool simple =
false;
1136 int nIntersectGeoms = 1;
1137 if ( GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_LINESTRING
1138 || GEOSGeomTypeId_r( context, intersectionGeom.get() ) == GEOS_POINT )
1142 nIntersectGeoms = GEOSGetNumGeometries_r( context, intersectionGeom.get() );
1144 for (
int i = 0; i < nIntersectGeoms; ++i )
1148 currentIntersectGeom = intersectionGeom.get();
1150 currentIntersectGeom = GEOSGetGeometryN_r( context, intersectionGeom.get(), i );
1152 const GEOSCoordSequence *lineSequence = GEOSGeom_getCoordSeq_r( context, currentIntersectGeom );
1153 unsigned int sequenceSize = 0;
1155 if ( GEOSCoordSeq_getSize_r( context, lineSequence, &sequenceSize ) != 0 )
1157 for (
unsigned int i = 0; i < sequenceSize; ++i )
1159 if ( GEOSCoordSeq_getXYZ_r( context, lineSequence, i, &x, &y, &z ) )
1161 testPoints.push_back( QgsPoint( x, y, z ) );
1175 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1177 std::unique_ptr< QgsMultiCurve > multiCurve;
1178 if ( type == GEOS_MULTILINESTRING )
1182 else if ( type == GEOS_LINESTRING )
1184 multiCurve = std::make_unique<QgsMultiCurve>( );
1185 multiCurve->addGeometry(
mGeometry->clone() );
1200 std::unique_ptr< QgsMultiPoint > splitPoints;
1202 std::unique_ptr< QgsAbstractGeometry > splitGeom(
fromGeos( GEOSsplitPoint ) );
1206 splitPoints.reset( qgis::down_cast<QgsMultiPoint *>( splitGeom.release() ) );
1210 splitPoints = std::make_unique< QgsMultiPoint >();
1213 splitPoints->addGeometry( qgis::down_cast<QgsPoint *>( splitGeom.release() ) );
1221 QgsMultiCurve lines;
1224 for (
int geometryIndex = 0; geometryIndex < multiCurve->numGeometries(); ++geometryIndex )
1237 QMap< int, QVector< QPair< double, QgsPoint > > >pointMap;
1238 for (
int splitPointIndex = 0; splitPointIndex < splitPoints->numGeometries(); ++splitPointIndex )
1240 const QgsPoint *intersectionPoint = splitPoints->pointN( splitPointIndex );
1242 QgsPoint segmentPoint2D;
1243 QgsVertexId nextVertex;
1246 line->
closestSegment( *intersectionPoint, segmentPoint2D, nextVertex );
1262 const QPair< double, QgsPoint > pair = qMakePair(
distance, *correctSegmentPoint.get() );
1263 if ( pointMap.contains( nextVertex.
vertex - 1 ) )
1264 pointMap[ nextVertex.
vertex - 1 ].append( pair );
1266 pointMap[ nextVertex.
vertex - 1 ] = QVector< QPair< double, QgsPoint > >() << pair;
1271 for (
auto &p : pointMap )
1273 std::sort( p.begin(), p.end(), [](
const QPair< double, QgsPoint > &a,
const QPair< double, QgsPoint > &b ) { return a.first < b.first; } );
1277 QgsLineString newLine;
1279 QgsPoint splitPoint;
1280 for (
int vertexIndex = 0; vertexIndex < nVertices; ++vertexIndex )
1282 QgsPoint currentPoint = line->
pointN( vertexIndex );
1284 if ( pointMap.contains( vertexIndex ) )
1287 for (
int k = 0; k < pointMap[ vertexIndex ].size(); ++k )
1289 splitPoint = pointMap[ vertexIndex ][k].second;
1290 if ( splitPoint == currentPoint )
1293 newLine = QgsLineString();
1296 else if ( splitPoint == line->
pointN( vertexIndex + 1 ) )
1300 newLine = QgsLineString();
1306 newLine = QgsLineString();
1315 return asGeos( &lines, mPrecision );
1320 Q_UNUSED( skipIntersectionCheck )
1329 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, splitLine, mGeos.get() ) );
1330 if ( !intersectGeom || GEOSisEmpty_r( context, intersectGeom.get() ) )
1334 const int linearIntersect = GEOSRelatePattern_r( context, mGeos.get(), splitLine,
"1********" );
1335 if ( linearIntersect > 0 )
1343 std::vector<geos::unique_ptr> lineGeoms;
1345 const int splitType = GEOSGeomTypeId_r( context, splitGeom.get() );
1346 if ( splitType == GEOS_MULTILINESTRING )
1348 const int nGeoms = GEOSGetNumGeometries_r( context, splitGeom.get() );
1349 lineGeoms.reserve( nGeoms );
1350 for (
int i = 0; i < nGeoms; ++i )
1351 lineGeoms.emplace_back( GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, splitGeom.get(), i ) ) );
1356 lineGeoms.emplace_back( GEOSGeom_clone_r( context, splitGeom.get() ) );
1359 mergeGeometriesMultiTypeSplit( lineGeoms );
1363 newGeometries << QgsGeometry(
fromGeos( lineGeom.get() ) );
1379 if ( !mGeosPrepared )
1385 if ( !skipIntersectionCheck && !GEOSPreparedIntersects_r( context, mGeosPrepared.get(), splitLine ) )
1390 if ( !nodedGeometry )
1399 const int numberOfGeometriesPolygon = numberOfGeometries( polygons.get() );
1400 if ( numberOfGeometriesPolygon == 0 )
1407 std::vector<geos::unique_ptr> testedGeometries;
1412 for (
int i = 0; i < numberOfGeometriesPolygon; i++ )
1414 const GEOSGeometry *polygon = GEOSGetGeometryN_r( context, polygons.get(), i );
1418 testedGeometries.emplace_back( GEOSGeom_clone_r( context, polygon ) );
1421 const size_t nGeometriesThis = numberOfGeometries( mGeos.get() );
1422 if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
1432 mergeGeometriesMultiTypeSplit( testedGeometries );
1435 for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( context, testedGeometries[i].get() ); ++i )
1438 if ( i < testedGeometries.size() )
1445 newGeometries << QgsGeometry(
fromGeos( testedGeometry.get() ) );
1453 if ( !splitLine || !geom )
1458 if ( GEOSGeom_getDimensions_r( context, geom ) == 2 )
1459 geometryBoundary.reset( GEOSBoundary_r( context, geom ) );
1461 geometryBoundary.reset( GEOSGeom_clone_r( context, geom ) );
1464 geos::unique_ptr unionGeometry( GEOSUnion_r( context, splitLineClone.get(), geometryBoundary.get() ) );
1466 return unionGeometry;
1469int QgsGeos::mergeGeometriesMultiTypeSplit( std::vector<geos::unique_ptr> &splitResult )
const
1476 int type = GEOSGeomTypeId_r( context, mGeos.get() );
1477 if ( type != GEOS_GEOMETRYCOLLECTION &&
1478 type != GEOS_MULTILINESTRING &&
1479 type != GEOS_MULTIPOLYGON &&
1480 type != GEOS_MULTIPOINT )
1484 std::vector<geos::unique_ptr> unionGeom;
1486 std::vector<geos::unique_ptr> newSplitResult;
1488 for (
size_t i = 0; i < splitResult.size(); ++i )
1491 bool isPart =
false;
1492 for (
int j = 0; j < GEOSGetNumGeometries_r( context, mGeos.get() ); j++ )
1494 if ( GEOSEquals_r( context, splitResult[i].get(), GEOSGetGeometryN_r( context, mGeos.get(), j ) ) )
1503 unionGeom.emplace_back( std::move( splitResult[i] ) );
1507 std::vector<geos::unique_ptr> geomVector;
1508 geomVector.emplace_back( std::move( splitResult[i] ) );
1510 if ( type == GEOS_MULTILINESTRING )
1511 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, geomVector ) );
1512 else if ( type == GEOS_MULTIPOLYGON )
1513 newSplitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, geomVector ) );
1517 splitResult = std::move( newSplitResult );
1520 if ( !unionGeom.empty() )
1522 if ( type == GEOS_MULTILINESTRING )
1523 splitResult.emplace_back( createGeosCollection( GEOS_MULTILINESTRING, unionGeom ) );
1524 else if ( type == GEOS_MULTIPOLYGON )
1525 splitResult.emplace_back( createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ) );
1531geos::unique_ptr QgsGeos::createGeosCollection(
int typeId, std::vector<geos::unique_ptr> &geoms )
1533 std::vector<GEOSGeometry *> geomarr;
1534 geomarr.reserve( geoms.size() );
1539 if ( geomUniquePtr )
1541 if ( !GEOSisEmpty_r( context, geomUniquePtr.get() ) )
1545 geomarr.emplace_back( geomUniquePtr.release() );
1553 geomRes.reset( GEOSGeom_createCollection_r( context, typeId, geomarr.data(), geomarr.size() ) );
1555 catch ( QgsGeosException & )
1559 GEOSGeom_destroy_r( context, geom );
1574 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1575 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1576 bool hasZ = ( nCoordDims == 3 );
1577 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1579 switch ( GEOSGeomTypeId_r( context,
geos ) )
1583 if ( GEOSisEmpty_r( context,
geos ) )
1586 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1587 unsigned int nPoints = 0;
1588 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1597 return !point.
isEmpty() ? std::unique_ptr<QgsAbstractGeometry>( point.
clone() ) :
nullptr;
1599 case GEOS_LINESTRING:
1601 return sequenceToLinestring(
geos, hasZ, hasM );
1607 case GEOS_MULTIPOINT:
1609 auto multiPoint = std::make_unique<QgsMultiPoint>();
1610 int nParts = GEOSGetNumGeometries_r( context,
geos );
1611 multiPoint->reserve( nParts );
1612 for (
int i = 0; i < nParts; ++i )
1614 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context, GEOSGetGeometryN_r( context,
geos, i ) );
1617 unsigned int nPoints = 0;
1618 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1620 multiPoint->addGeometry(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1623 return std::move( multiPoint );
1625 case GEOS_MULTILINESTRING:
1627 auto multiLineString = std::make_unique<QgsMultiLineString>();
1628 int nParts = GEOSGetNumGeometries_r( context,
geos );
1629 multiLineString->reserve( nParts );
1630 for (
int i = 0; i < nParts; ++i )
1632 std::unique_ptr< QgsLineString >line( sequenceToLinestring( GEOSGetGeometryN_r( context,
geos, i ), hasZ, hasM ) );
1635 multiLineString->addGeometry( line.release() );
1638 return std::move( multiLineString );
1640 case GEOS_MULTIPOLYGON:
1642 auto multiPolygon = std::make_unique<QgsMultiPolygon>();
1644 int nParts = GEOSGetNumGeometries_r( context,
geos );
1645 multiPolygon->reserve( nParts );
1646 for (
int i = 0; i < nParts; ++i )
1648 std::unique_ptr< QgsPolygon > poly =
fromGeosPolygon( GEOSGetGeometryN_r( context,
geos, i ) );
1651 multiPolygon->addGeometry( poly.release() );
1654 return std::move( multiPolygon );
1656 case GEOS_GEOMETRYCOLLECTION:
1658 auto geomCollection = std::make_unique<QgsGeometryCollection>();
1659 int nParts = GEOSGetNumGeometries_r( context,
geos );
1660 geomCollection->reserve( nParts );
1661 for (
int i = 0; i < nParts; ++i )
1663 std::unique_ptr< QgsAbstractGeometry > geom(
fromGeos( GEOSGetGeometryN_r( context,
geos, i ) ) );
1666 geomCollection->addGeometry( geom.release() );
1669 return std::move( geomCollection );
1678 if ( GEOSGeomTypeId_r( context,
geos ) != GEOS_POLYGON )
1683 int nCoordDims = GEOSGeom_getCoordinateDimension_r( context,
geos );
1684 int nDims = GEOSGeom_getDimensions_r( context,
geos );
1685 bool hasZ = ( nCoordDims == 3 );
1686 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1688 auto polygon = std::make_unique<QgsPolygon>();
1693 polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1696 QVector<QgsCurve *> interiorRings;
1697 const int ringCount = GEOSGetNumInteriorRings_r( context,
geos );
1698 interiorRings.reserve( ringCount );
1699 for (
int i = 0; i < ringCount; ++i )
1701 ring = GEOSGetInteriorRingN_r( context,
geos, i );
1704 interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1707 polygon->setInteriorRings( interiorRings );
1712std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring(
const GEOSGeometry *
geos,
bool hasZ,
bool hasM )
1715 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( context,
geos );
1717 unsigned int nPoints;
1718 GEOSCoordSeq_getSize_r( context, cs, &nPoints );
1720 QVector< double > xOut( nPoints );
1721 QVector< double > yOut( nPoints );
1722 QVector< double > zOut;
1724 zOut.resize( nPoints );
1725 QVector< double > mOut;
1727 mOut.resize( nPoints );
1729 double *x = xOut.data();
1730 double *y = yOut.data();
1731 double *z = zOut.data();
1732 double *m = mOut.data();
1734#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
1735 GEOSCoordSeq_copyToArrays_r( context, cs, x, y, hasZ ? z :
nullptr, hasM ? m :
nullptr );
1737 for (
unsigned int i = 0; i < nPoints; ++i )
1740 GEOSCoordSeq_getXYZ_r( context, cs, i, x++, y++, z++ );
1742 GEOSCoordSeq_getXY_r( context, cs, i, x++, y++ );
1745 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, m++ );
1749 auto line = std::make_unique<QgsLineString>( xOut, yOut, zOut, mOut );
1759 int geometryType = GEOSGeomTypeId_r( context, g );
1760 if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING
1761 || geometryType == GEOS_POLYGON )
1765 return GEOSGetNumGeometries_r( context, g );
1781 GEOSCoordSeq_getXYZ_r( context, cs, i, &x, &y, &z );
1783 GEOSCoordSeq_getXY_r( context, cs, i, &x, &y );
1786 GEOSCoordSeq_getOrdinate_r( context, cs, i, 3, &m );
1822 int geosType = GEOS_GEOMETRYCOLLECTION;
1829 geosType = GEOS_MULTIPOINT;
1833 geosType = GEOS_MULTILINESTRING;
1837 geosType = GEOS_MULTIPOLYGON;
1852 std::vector<geos::unique_ptr> geomVector;
1853 geomVector.reserve(
c->numGeometries() );
1854 for (
int i = 0; i <
c->numGeometries(); ++i )
1861 geomVector.emplace_back( std::move( geosGeom ) );
1863 return createGeosCollection( geosType, geomVector );
1871 if ( !polyhedralSurface )
1874 std::vector<geos::unique_ptr> geomVector;
1875 geomVector.reserve( polyhedralSurface->
numPatches() );
1876 for (
int i = 0; i < polyhedralSurface->
numPatches(); ++i )
1883 geomVector.emplace_back( std::move( geosPolygon ) );
1886 return createGeosCollection( GEOS_MULTIPOLYGON, geomVector );
1893 return createGeosPoint(
static_cast<const QgsPoint *
>( geom ), coordDims, precision, flags );
1896 return createGeosLinestring(
static_cast<const QgsLineString *
>( geom ), precision, flags );
1899 return createGeosPolygon(
static_cast<const QgsPolygon *
>( geom ), precision, flags );
1911 if ( !mGeos || !geom )
1922 const double gridSize = parameters.
gridSize();
1930 case OverlayIntersection:
1933 opGeom.reset( GEOSIntersectionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1937 opGeom.reset( GEOSIntersection_r( context, mGeos.get(), geosGeom.get() ) );
1941 case OverlayDifference:
1944 opGeom.reset( GEOSDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1948 opGeom.reset( GEOSDifference_r( context, mGeos.get(), geosGeom.get() ) );
1957 unionGeometry.reset( GEOSUnionPrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1961 unionGeometry.reset( GEOSUnion_r( context, mGeos.get(), geosGeom.get() ) );
1964 if ( unionGeometry && GEOSGeomTypeId_r( context, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1966 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, unionGeometry.get() ) );
1969 unionGeometry = std::move( mergedLines );
1973 opGeom = std::move( unionGeometry );
1977 case OverlaySymDifference:
1980 opGeom.reset( GEOSSymDifferencePrec_r( context, mGeos.get(), geosGeom.get(), gridSize ) );
1984 opGeom.reset( GEOSSymDifference_r( context, mGeos.get(), geosGeom.get() ) );
1990 catch ( QgsGeosException &e )
1995 *errorMsg = e.what();
2001bool QgsGeos::relation(
const QgsAbstractGeometry *geom, Relation r, QString *errorMsg )
const
2003 if ( !mGeos || !geom )
2015 bool result =
false;
2018 if ( mGeosPrepared )
2022 case RelationIntersects:
2023 result = ( GEOSPreparedIntersects_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2025 case RelationTouches:
2026 result = ( GEOSPreparedTouches_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2028 case RelationCrosses:
2029 result = ( GEOSPreparedCrosses_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2031 case RelationWithin:
2032 result = ( GEOSPreparedWithin_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2034 case RelationContains:
2035 result = ( GEOSPreparedContains_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2037 case RelationDisjoint:
2038 result = ( GEOSPreparedDisjoint_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2040 case RelationOverlaps:
2041 result = ( GEOSPreparedOverlaps_r( context, mGeosPrepared.get(), geosGeom.get() ) == 1 );
2049 case RelationIntersects:
2050 result = ( GEOSIntersects_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2052 case RelationTouches:
2053 result = ( GEOSTouches_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2055 case RelationCrosses:
2056 result = ( GEOSCrosses_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2058 case RelationWithin:
2059 result = ( GEOSWithin_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2061 case RelationContains:
2062 result = ( GEOSContains_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2064 case RelationDisjoint:
2065 result = ( GEOSDisjoint_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2067 case RelationOverlaps:
2068 result = ( GEOSOverlaps_r( context, mGeos.get(), geosGeom.get() ) == 1 );
2072 catch ( QgsGeosException &e )
2077 *errorMsg = e.what();
2117 geos.reset( GEOSBufferWithStyle_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( endCapStyle ),
static_cast< int >( joinStyle ), miterLimit ) );
2167 geos.reset( GEOSGetCentroid_r( context, mGeos.get() ) );
2172 GEOSGeomGetX_r( context,
geos.get(), &x );
2173 GEOSGeomGetY_r( context,
geos.get(), &y );
2209 geos.reset( GEOSPointOnSurface_r( context, mGeos.get() ) );
2211 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
2216 GEOSGeomGetX_r( context,
geos.get(), &x );
2217 GEOSGeomGetY_r( context,
geos.get(), &y );
2234 std::unique_ptr< QgsAbstractGeometry > cHullGeom =
fromGeos( cHull.get() );
2235 return cHullGeom.release();
2240std::unique_ptr< QgsAbstractGeometry >
QgsGeos::concaveHull(
double targetPercent,
bool allowHoles, QString *errorMsg )
const
2242#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
2244 ( void )targetPercent;
2246 throw QgsNotSupportedException( QObject::tr(
"Calculating concaveHull requires a QGIS build based on GEOS 3.11 or later" ) );
2257 return concaveHullGeom;
2265#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<12
2267 ( void )invalidEdges;
2269 throw QgsNotSupportedException( QObject::tr(
"Validating coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2274 *errorMsg = u
"Input geometry was not set"_s;
2282 const int result = GEOSCoverageIsValid_r( context, mGeos.get(), gapWidth, invalidEdges ? &invalidEdgesGeos :
nullptr );
2283 if ( invalidEdges && invalidEdgesGeos )
2285 *invalidEdges =
fromGeos( invalidEdgesGeos );
2287 if ( invalidEdgesGeos )
2289 GEOSGeom_destroy_r( context, invalidEdgesGeos );
2290 invalidEdgesGeos =
nullptr;
2310#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<12
2312 ( void )preserveBoundary;
2314 throw QgsNotSupportedException( QObject::tr(
"Simplifying coverages requires a QGIS build based on GEOS 3.12 or later" ) );
2319 *errorMsg = u
"Input geometry was not set"_s;
2326 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom =
fromGeos( simplified.get() );
2327 return simplifiedGeom;
2338 *errorMsg = u
"Input geometry was not set"_s;
2345 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( unioned.get() );
2356 *errorMsg = QObject::tr(
"QGIS geometry cannot be converted to a GEOS geometry",
"GEOS Error" );
2365 char res = GEOSisValidDetail_r( context, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
2366 const bool invalid = res != 1;
2371 error = QString( r );
2372 GEOSFree_r( context, r );
2375 if ( invalid && errorMsg )
2378 static const std::map< QString, QString > sTranslatedErrors
2380 { u
"topology validation error"_s, QObject::tr(
"Topology validation error",
"GEOS Error" ) },
2381 { u
"repeated point"_s, QObject::tr(
"Repeated point",
"GEOS Error" ) },
2382 { u
"hole lies outside shell"_s, QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) },
2383 { u
"holes are nested"_s, QObject::tr(
"Holes are nested",
"GEOS Error" ) },
2384 { u
"interior is disconnected"_s, QObject::tr(
"Interior is disconnected",
"GEOS Error" ) },
2385 { u
"self-intersection"_s, QObject::tr(
"Self-intersection",
"GEOS Error" ) },
2386 { u
"ring self-intersection"_s, QObject::tr(
"Ring self-intersection",
"GEOS Error" ) },
2387 { u
"nested shells"_s, QObject::tr(
"Nested shells",
"GEOS Error" ) },
2388 { u
"duplicate rings"_s, QObject::tr(
"Duplicate rings",
"GEOS Error" ) },
2389 { u
"too few points in geometry component"_s, QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) },
2390 { u
"invalid coordinate"_s, QObject::tr(
"Invalid coordinate",
"GEOS Error" ) },
2391 { u
"ring is not closed"_s, QObject::tr(
"Ring is not closed",
"GEOS Error" ) },
2394 const auto translatedError = sTranslatedErrors.find( error.toLower() );
2395 if ( translatedError != sTranslatedErrors.end() )
2396 *errorMsg = translatedError->second;
2400 if ( g1 && errorLoc )
2406 GEOSGeom_destroy_r( context, g1 );
2416 if ( !mGeos || !geom )
2462GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
2466 std::unique_ptr< QgsLineString > segmentized;
2472 line = segmentized.get();
2479 GEOSCoordSequence *coordSeq =
nullptr;
2481 const int numPoints = line->
numPoints();
2483 const bool hasZ = line->
is3D();
2485#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
2488 if ( !forceClose || ( line->
pointN( 0 ) == line->
pointN( numPoints - 1 ) ) )
2493 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, line->
xData(), line->
yData(), line->
zData(),
nullptr, numPoints );
2496 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points"_s.arg( numPoints ) );
2504 QVector< double > x = line->
xVector();
2505 if ( numPoints > 0 )
2506 x.append( x.at( 0 ) );
2507 QVector< double > y = line->
yVector();
2508 if ( numPoints > 0 )
2509 y.append( y.at( 0 ) );
2510 QVector< double > z = line->
zVector();
2511 if ( hasZ && numPoints > 0 )
2512 z.append( z.at( 0 ) );
2515 coordSeq = GEOSCoordSeq_copyFromArrays_r( context, x.constData(), y.constData(), !hasZ ?
nullptr : z.constData(),
nullptr, numPoints + 1 );
2518 QgsDebugError( u
"GEOS Exception: Could not create closed coordinate sequence for %1 points"_s.arg( numPoints + 1 ) );
2529 const bool hasM =
false;
2540 int numOutPoints = numPoints;
2541 if ( forceClose && ( line->
pointN( 0 ) != line->
pointN( numPoints - 1 ) ) )
2548 coordSeq = GEOSCoordSeq_create_r( context, numOutPoints, coordDims );
2551 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions"_s.arg( numPoints ).arg( coordDims ) );
2555 const double *xData = line->
xData();
2556 const double *yData = line->
yData();
2557 const double *zData = hasZ ? line->
zData() :
nullptr;
2558 const double *mData = hasM ? line->
mData() :
nullptr;
2560 if ( precision > 0. )
2562 for (
int i = 0; i < numOutPoints; ++i )
2564 if ( i >= numPoints )
2567 xData = line->
xData();
2568 yData = line->
yData();
2569 zData = hasZ ? line->
zData() :
nullptr;
2570 mData = hasM ? line->
mData() :
nullptr;
2574 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
2578 GEOSCoordSeq_setXY_r( context, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
2582 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2588 for (
int i = 0; i < numOutPoints; ++i )
2590 if ( i >= numPoints )
2593 xData = line->
xData();
2594 yData = line->
yData();
2595 zData = hasZ ? line->
zData() :
nullptr;
2596 mData = hasM ? line->
mData() :
nullptr;
2600 GEOSCoordSeq_setXYZ_r( context, coordSeq, i, *xData++, *yData++, *zData++ );
2604 GEOSCoordSeq_setXY_r( context, coordSeq, i, *xData++, *yData++ );
2608 GEOSCoordSeq_setOrdinate_r( context, coordSeq, i, 3, *mData++ );
2618geos::unique_ptr
QgsGeos::createGeosPoint( const QgsAbstractGeometry *point,
int coordDims,
double precision, Qgis::GeosCreationFlags )
2624 return createGeosPointXY( pt->
x(), pt->
y(), pt->
is3D(), pt->
z(), pt->
isMeasure(), pt->
m(), coordDims, precision );
2636 if ( coordDims == 2 )
2639 if ( precision > 0. )
2640 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
2642 geosPoint.reset( GEOSGeom_createPointFromXY_r( context, x, y ) );
2646 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( context, 1, coordDims );
2649 QgsDebugError( u
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions"_s.arg( coordDims ) );
2652 if ( precision > 0. )
2654 GEOSCoordSeq_setX_r( context, coordSeq, 0, std::round( x / precision ) * precision );
2655 GEOSCoordSeq_setY_r( context, coordSeq, 0, std::round( y / precision ) * precision );
2658 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, std::round( z / precision ) * precision );
2663 GEOSCoordSeq_setX_r( context, coordSeq, 0, x );
2664 GEOSCoordSeq_setY_r( context, coordSeq, 0, y );
2667 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 2, z );
2673 GEOSCoordSeq_setOrdinate_r( context, coordSeq, 0, 3, m );
2676 geosPoint.reset( GEOSGeom_createPoint_r( context, coordSeq ) );
2682geos::unique_ptr
QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve,
double precision, Qgis::GeosCreationFlags )
2688 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c, precision );
2701geos::unique_ptr
QgsGeos::createGeosPolygon( const QgsAbstractGeometry *poly,
double precision, Qgis::GeosCreationFlags flags )
2707 const QgsCurve *exteriorRing = polygon->
exteriorRing();
2708 if ( !exteriorRing )
2717 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( context, createCoordinateSequence( exteriorRing, precision,
true ) ) );
2720 QList< const QgsCurve * > holesToExport;
2721 holesToExport.reserve( nInteriorRings );
2722 for (
int i = 0; i < nInteriorRings; ++i )
2724 const QgsCurve *interiorRing = polygon->
interiorRing( i );
2727 holesToExport << interiorRing;
2732 if ( !holesToExport.empty() )
2735 for (
int i = 0; i < holesToExport.size(); ++i )
2737 holes[i] = GEOSGeom_createLinearRing_r( context, createCoordinateSequence( holesToExport[i], precision,
true ) );
2741 geosPolygon.reset( GEOSGeom_createPolygon_r( context, exteriorRingGeos.release(), holes, holesToExport.size() ) );
2761 offset.reset( GEOSOffsetCurve_r(
QgsGeosContext::get(), geometry,
distance, segments,
static_cast< int >( joinStyle ), miterLimit ) );
2773 return fromGeos( res.get() ).release();
2788 GEOSBufferParams_setSingleSided_r( context, bp.get(), 1 );
2789 GEOSBufferParams_setQuadrantSegments_r( context, bp.get(), segments );
2790 GEOSBufferParams_setJoinStyle_r( context, bp.get(),
static_cast< int >( joinStyle ) );
2791 GEOSBufferParams_setMitreLimit_r( context, bp.get(), miterLimit );
2797 geos.reset( GEOSBufferWithParams_r( context, mGeos.get(), bp.get(),
distance ) );
2831 boundaryGeos =
asGeos( boundary );
2859 return std::numeric_limits< double >::quiet_NaN();
2867 return std::numeric_limits< double >::quiet_NaN();
2907 if ( !mGeos || !other )
2927 if ( !mGeos ||
mGeometry->dimension() == 0 )
2939 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
2943 int numGeoms = GEOSGetNumGeometries_r( context, mGeos.get() );
2944 if ( numGeoms == -1 )
2953 bool isMultiGeom =
false;
2954 int geosTypeId = GEOSGeomTypeId_r( context, mGeos.get() );
2955 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
2958 bool isLine = (
mGeometry->dimension() == 1 );
2965 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2969 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2974 if ( reshapedGeometry )
2980 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
2981 return reshapeResult;
2988 bool reshapeTookPlace =
false;
2993 for (
int i = 0; i < numGeoms; ++i )
2996 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2998 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( context, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
3000 if ( currentReshapeGeometry )
3002 newGeoms[i] = currentReshapeGeometry.release();
3003 reshapeTookPlace =
true;
3007 newGeoms[i] = GEOSGeom_clone_r( context, GEOSGetGeometryN_r( context, mGeos.get(), i ) );
3014 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
3018 newMultiGeom.reset( GEOSGeom_createCollection_r( context, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
3022 if ( !newMultiGeom )
3028 if ( reshapeTookPlace )
3032 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
3033 return reshapedMultiGeom;
3056 if ( GEOSGeomTypeId_r( context, mGeos.get() ) != GEOS_MULTILINESTRING )
3062 double gridSize = parameters.
gridSize();
3065 geos::unique_ptr geosFixedSize( GEOSGeom_setPrecision_r( context, mGeos.get(), gridSize, 0 ) );
3066 geos.reset( GEOSLineMerge_r( context, geosFixedSize.get() ) );
3069 geos.reset( GEOSLineMerge_r( context, mGeos.get() ) );
3094 if ( mGeosPrepared )
3096 nearestCoord.reset( GEOSPreparedNearestPoints_r( context, mGeosPrepared.get(), otherGeom.get() ) );
3100 nearestCoord.reset( GEOSNearestPoints_r( context, mGeos.get(), otherGeom.get() ) );
3103 ( void )GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx );
3104 ( void )GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny );
3106 catch ( QgsGeosException &e )
3111 *errorMsg = e.what();
3116 return std::make_unique< QgsPoint >( nx, ny );
3121 if ( !mGeos || other.
isEmpty() )
3131 if ( !other || other->
isEmpty() )
3149 if ( !nearestCoord )
3152 *errorMsg = u
"GEOS returned no nearest points"_s;
3156 ( void )GEOSCoordSeq_getX_r( context, nearestCoord.get(), 0, &nx1 );
3157 ( void )GEOSCoordSeq_getY_r( context, nearestCoord.get(), 0, &ny1 );
3158 ( void )GEOSCoordSeq_getX_r( context, nearestCoord.get(), 1, &nx2 );
3159 ( void )GEOSCoordSeq_getY_r( context, nearestCoord.get(), 1, &ny2 );
3161 catch ( QgsGeosException &e )
3166 *errorMsg = e.what();
3171 auto line = std::make_unique< QgsLineString >();
3195 catch ( QgsGeosException &e )
3200 *errorMsg = e.what();
3215 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
3224 catch ( QgsGeosException &e )
3229 *errorMsg = e.what();
3246 lineGeosGeometries[validLines] = l.release();
3254 geos::unique_ptr result( GEOSPolygonize_r( context, lineGeosGeometries, validLines ) );
3255 for (
int i = 0; i < validLines; ++i )
3257 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3259 delete[] lineGeosGeometries;
3262 catch ( QgsGeosException &e )
3266 *errorMsg = e.what();
3268 for (
int i = 0; i < validLines; ++i )
3270 GEOSGeom_destroy_r( context, lineGeosGeometries[i] );
3272 delete[] lineGeosGeometries;
3287 extentGeosGeom =
asGeos( extent, mPrecision );
3288 if ( !extentGeosGeom )
3298 geos.reset( GEOSVoronoiDiagram_r( context, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
3300 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3321 geos.reset( GEOSDelaunayTriangulation_r( context, mGeos.get(), tolerance, edgesOnly ) );
3323 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3335#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
3337 throw QgsNotSupportedException( QObject::tr(
"Calculating constrainedDelaunayTriangulation requires a QGIS build based on GEOS 3.11 or later" ) );
3348 geos.reset( GEOSConstrainedDelaunayTriangulation_r( context, mGeos.get() ) );
3350 if ( !
geos || GEOSisEmpty_r( context,
geos.get() ) != 0 )
3355 std::unique_ptr< QgsAbstractGeometry > res =
fromGeos(
geos.get() );
3358 return std::unique_ptr< QgsAbstractGeometry >( collection->extractPartsByType(
Qgis::WkbType::Polygon,
true ) );
3370static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
3373 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( context, linestring );
3377 unsigned int coordSeqSize;
3378 if ( GEOSCoordSeq_getSize_r( context, coordSeq, &coordSeqSize ) == 0 )
3381 if ( coordSeqSize < 2 )
3384 GEOSCoordSeq_getX_r( context, coordSeq, 0, &x1 );
3385 GEOSCoordSeq_getY_r( context, coordSeq, 0, &y1 );
3386 GEOSCoordSeq_getX_r( context, coordSeq, coordSeqSize - 1, &x2 );
3387 GEOSCoordSeq_getY_r( context, coordSeq, coordSeqSize - 1, &y2 );
3395 double x1, y1, x2, y2;
3396 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
3399 double rx1, ry1, rx2, ry2;
3400 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
3403 bool intersectionAtOrigLineEndpoint =
3404 ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) !=
3405 ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
3406 bool intersectionAtReshapeLineEndpoint =
3407 ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) ||
3408 ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
3412 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
3416 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
3417 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, geoms, 2 ) );
3422 double x1res, y1res, x2res, y2res;
3423 if ( !_linestringEndpoints( res.get(), x1res, y1res, x2res, y2res ) )
3425 if ( ( x1res == x2 && y1res == y2 ) || ( x2res == x1 && y2res == y1 ) )
3426 res.reset( GEOSReverse_r( context, res.get() ) );
3437 if ( !line || !reshapeLineGeos )
3440 bool atLeastTwoIntersections =
false;
3441 bool oneIntersection =
false;
3442 QgsPointXY oneIntersectionPoint;
3448 geos::unique_ptr intersectGeom( GEOSIntersection_r( context, line, reshapeLineGeos ) );
3449 if ( intersectGeom )
3451 const int geomType = GEOSGeomTypeId_r( context, intersectGeom.get() );
3452 atLeastTwoIntersections = ( geomType == GEOS_MULTIPOINT && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 1 )
3453 || ( geomType == GEOS_GEOMETRYCOLLECTION && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 )
3454 || ( geomType == GEOS_MULTILINESTRING && GEOSGetNumGeometries_r( context, intersectGeom.get() ) > 0 );
3456 if ( GEOSGeomTypeId_r( context, intersectGeom.get() ) == GEOS_POINT )
3458 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( context, intersectGeom.get() );
3460 GEOSCoordSeq_getX_r( context, intersectionCoordSeq, 0, &xi );
3461 GEOSCoordSeq_getY_r( context, intersectionCoordSeq, 0, &yi );
3462 oneIntersection =
true;
3463 oneIntersectionPoint = QgsPointXY( xi, yi );
3467 catch ( QgsGeosException & )
3469 atLeastTwoIntersections =
false;
3473 if ( oneIntersection )
3474 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
3476 if ( !atLeastTwoIntersections )
3480 double x1, y1, x2, y2;
3481 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
3484 geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1,
false, 0,
false, 0, 2, precision );
3485 geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2,
false, 0,
false, 0, 2, precision );
3487 bool isRing =
false;
3488 if ( GEOSGeomTypeId_r( context, line ) == GEOS_LINEARRING
3489 || GEOSEquals_r( context, beginLineVertex.get(), endLineVertex.get() ) == 1 )
3494 if ( !nodedGeometry )
3500 geos::unique_ptr mergedLines( GEOSLineMerge_r( context, nodedGeometry.get() ) );
3506 int numMergedLines = GEOSGetNumGeometries_r( context, mergedLines.get() );
3507 if ( numMergedLines < 2 )
3509 if ( numMergedLines == 1 )
3518 QVector<GEOSGeometry *> resultLineParts;
3519 QVector<GEOSGeometry *> probableParts;
3521 for (
int i = 0; i < numMergedLines; ++i )
3523 const GEOSGeometry *currentGeom = GEOSGetGeometryN_r( context, mergedLines.get(), i );
3526 bool alreadyAdded =
false;
3528 double bufferDistance = std::pow( 10.0L, geomDigits( currentGeom ) - 11 );
3529 for (
const GEOSGeometry *other : std::as_const( resultLineParts ) )
3531 GEOSHausdorffDistance_r( context, currentGeom, other, &
distance );
3534 alreadyAdded =
true;
3541 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( context, currentGeom );
3542 unsigned int currentCoordSeqSize;
3543 GEOSCoordSeq_getSize_r( context, currentCoordSeq, ¤tCoordSeqSize );
3544 if ( currentCoordSeqSize < 2 )
3548 double xBegin, xEnd, yBegin, yEnd;
3549 GEOSCoordSeq_getX_r( context, currentCoordSeq, 0, &xBegin );
3550 GEOSCoordSeq_getY_r( context, currentCoordSeq, 0, &yBegin );
3551 GEOSCoordSeq_getX_r( context, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
3552 GEOSCoordSeq_getY_r( context, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
3553 geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin,
false, 0,
false, 0, 2, precision );
3554 geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd,
false, 0,
false, 0, 2, precision );
3557 int nEndpointsOnOriginalLine = 0;
3558 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
3559 nEndpointsOnOriginalLine += 1;
3561 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
3562 nEndpointsOnOriginalLine += 1;
3565 int nEndpointsSameAsOriginalLine = 0;
3566 if ( GEOSEquals_r( context, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
3567 || GEOSEquals_r( context, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3568 nEndpointsSameAsOriginalLine += 1;
3570 if ( GEOSEquals_r( context, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
3571 || GEOSEquals_r( context, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3572 nEndpointsSameAsOriginalLine += 1;
3575 bool currentGeomOverlapsOriginalGeom =
false;
3576 bool currentGeomOverlapsReshapeLine =
false;
3577 if ( lineContainedInLine( currentGeom, line ) == 1 )
3578 currentGeomOverlapsOriginalGeom =
true;
3580 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
3581 currentGeomOverlapsReshapeLine =
true;
3584 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3586 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3589 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3591 probableParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3593 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3595 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3597 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3599 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3601 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
3603 resultLineParts.push_back( GEOSGeom_clone_r( context, currentGeom ) );
3608 if ( isRing && !probableParts.isEmpty() )
3612 double maxLength = -std::numeric_limits<double>::max();
3613 double currentLength = 0;
3614 for (
int i = 0; i < probableParts.size(); ++i )
3616 currentGeom = probableParts.at( i );
3617 GEOSLength_r( context, currentGeom, ¤tLength );
3618 if ( currentLength > maxLength )
3620 maxLength = currentLength;
3621 maxGeom.reset( currentGeom );
3625 GEOSGeom_destroy_r( context, currentGeom );
3628 resultLineParts.push_back( maxGeom.release() );
3632 if ( resultLineParts.empty() )
3635 if ( resultLineParts.size() == 1 )
3637 result.reset( resultLineParts[0] );
3642 for (
int i = 0; i < resultLineParts.size(); ++i )
3644 lineArray[i] = resultLineParts[i];
3648 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( context, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
3649 delete [] lineArray;
3652 result.reset( GEOSLineMerge_r( context, multiLineGeom.get() ) );
3656 if ( GEOSGeomTypeId_r( context, result.get() ) != GEOS_LINESTRING )
3662 bool reverseLine =
false;
3666 char isResultCCW = 0, isOriginCCW = 0;
3667 if ( GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, result.get() ), &isResultCCW ) &&
3668 GEOSCoordSeq_isCCW_r( context, GEOSGeom_getCoordSeq_r( context, line ), &isOriginCCW )
3672 reverseLine = ( isOriginCCW == 1 && isResultCCW == 0 ) || ( isOriginCCW == 0 && isResultCCW == 1 );
3678 double x1res, y1res, x2res, y2res;
3679 if ( !_linestringEndpoints( result.get(), x1res, y1res, x2res, y2res ) )
3681 geos::unique_ptr beginResultLineVertex = createGeosPointXY( x1res, y1res,
false, 0,
false, 0, 2, precision );
3682 geos::unique_ptr endResultLineVertex = createGeosPointXY( x2res, y2res,
false, 0,
false, 0, 2, precision );
3683 reverseLine = GEOSEquals_r( context, beginLineVertex.get(), endResultLineVertex.get() ) == 1 || GEOSEquals_r( context, endLineVertex.get(), beginResultLineVertex.get() ) == 1;
3686 result.reset( GEOSReverse_r( context, result.get() ) );
3694 int nIntersections = 0;
3695 int lastIntersectingRing = -2;
3699 int nRings = GEOSGetNumInteriorRings_r( context, polygon );
3704 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( context, polygon );
3705 if ( GEOSIntersects_r( context, outerRing, reshapeLineGeos ) == 1 )
3708 lastIntersectingRing = -1;
3709 lastIntersectingGeom = outerRing;
3717 for (
int i = 0; i < nRings; ++i )
3719 innerRings[i] = GEOSGetInteriorRingN_r( context, polygon, i );
3720 if ( GEOSIntersects_r( context, innerRings[i], reshapeLineGeos ) == 1 )
3723 lastIntersectingRing = i;
3724 lastIntersectingGeom = innerRings[i];
3728 catch ( QgsGeosException & )
3733 if ( nIntersections != 1 )
3735 delete [] innerRings;
3740 geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
3741 if ( !reshapeResult )
3743 delete [] innerRings;
3749 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( context, reshapeResult.get() );
3750 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( context, reshapeSequence );
3752 reshapeResult.reset();
3756 newRing = GEOSGeom_createLinearRing_r( context, newCoordSequence );
3758 catch ( QgsGeosException & )
3765 delete [] innerRings;
3770 if ( lastIntersectingRing == -1 )
3771 newOuterRing = newRing;
3773 newOuterRing = GEOSGeom_clone_r( context, outerRing );
3776 QVector<GEOSGeometry *> ringList;
3779 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( context, GEOSGeom_clone_r( context, newOuterRing ),
nullptr, 0 );
3780 if ( outerRingPoly )
3782 ringList.reserve( nRings );
3784 for (
int i = 0; i < nRings; ++i )
3786 if ( lastIntersectingRing == i )
3787 currentRing = newRing;
3789 currentRing = GEOSGeom_clone_r( context, innerRings[i] );
3792 if ( GEOSContains_r( context, outerRingPoly, currentRing ) == 1 )
3793 ringList.push_back( currentRing );
3795 GEOSGeom_destroy_r( context, currentRing );
3798 GEOSGeom_destroy_r( context, outerRingPoly );
3802 for (
int i = 0; i < ringList.size(); ++i )
3803 newInnerRings[i] = ringList.at( i );
3805 delete [] innerRings;
3807 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( context, newOuterRing, newInnerRings, ringList.size() ) );
3808 delete[] newInnerRings;
3810 return reshapedPolygon;
3815 if ( !line1 || !line2 )
3820 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
3827 geos::unique_ptr intersectionGeom( GEOSIntersection_r( context, bufferGeom.get(), line1 ) );
3830 double intersectGeomLength;
3833 GEOSLength_r( context, intersectionGeom.get(), &intersectGeomLength );
3834 GEOSLength_r( context, line1, &line1Length );
3836 double intersectRatio = line1Length / intersectGeomLength;
3837 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
3845 if ( !point || !line )
3848 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
3851 geos::unique_ptr lineBuffer( GEOSBuffer_r( context, line, bufferDistance, 8 ) );
3855 bool contained =
false;
3856 if ( GEOSContains_r( context, lineBuffer.get(), point ) == 1 )
3869 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( context, bbox.get() );
3873 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( context, bBoxRing );
3875 if ( !bBoxCoordSeq )
3878 unsigned int nCoords = 0;
3879 if ( !GEOSCoordSeq_getSize_r( context, bBoxCoordSeq, &nCoords ) )
3883 for (
unsigned int i = 0; i < nCoords - 1; ++i )
3886 GEOSCoordSeq_getX_r( context, bBoxCoordSeq, i, &t );
3889 digits = std::ceil( std::log10( std::fabs( t ) ) );
3890 if ( digits > maxDigits )
3893 GEOSCoordSeq_getY_r( context, bBoxCoordSeq, i, &t );
3894 digits = std::ceil( std::log10( std::fabs( t ) ) );
3895 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