31 #define DEFAULT_QUADRANT_SEGMENTS 8
33 #define CATCH_GEOS(r) \
34 catch (GEOSException &) \
39 #define CATCH_GEOS_WITH_ERRMSG(r) \
40 catch (GEOSException &e) \
44 *errorMsg = e.what(); \
51 static void throwGEOSException(
const char *fmt, ... )
57 vsnprintf( buffer,
sizeof buffer, fmt, ap );
60 QString message = QString::fromUtf8( buffer );
70 throw GEOSException( message );
78 throw GEOSException( message );
83 static void printGEOSNotice(
const char *fmt, ... )
85 #if defined(QGISDEBUG)
90 vsnprintf( buffer,
sizeof buffer, fmt, ap );
100 GEOSContextHandle_t ctxt;
104 ctxt = initGEOS_r( printGEOSNotice, throwGEOSException );
109 finishGEOS_r( ctxt );
112 GEOSInit(
const GEOSInit &rh ) =
delete;
113 GEOSInit &operator=(
const GEOSInit &rh ) =
delete;
120 GEOSGeom_destroy_r( geosinit()->ctxt, geom );
125 GEOSPreparedGeom_destroy_r( geosinit()->ctxt, geom );
130 GEOSBufferParams_destroy_r( geosinit()->ctxt, params );
135 GEOSCoordSeq_destroy_r( geosinit()->ctxt, sequence );
163 #if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=8 )
164 std::unique_ptr<QgsAbstractGeometry> QgsGeos::makeValid( QString *errorMsg )
const
174 geos.reset( GEOSMakeValid_r( geosinit()->ctxt, mGeos.get() ) );
202 std::unique_ptr< QgsAbstractGeometry > geom =
fromGeos( newPart );
209 mGeosPrepared.reset();
222 mGeosPrepared.reset( GEOSPrepare_r( geosinit()->ctxt, mGeos.get() ) );
226 void QgsGeos::cacheGeos()
const
243 return overlay( geom, OverlayIntersection, errorMsg ).release();
248 return overlay( geom, OverlayDifference, errorMsg ).release();
263 catch ( GEOSException &e )
265 logError( QStringLiteral(
"GEOS" ), e.what() );
268 *errorMsg = e.what();
279 int partType = GEOSGeomTypeId_r( geosinit()->ctxt, currentPart );
282 if ( partType == GEOS_POINT )
293 if ( partType == GEOS_MULTILINESTRING || partType == GEOS_MULTIPOLYGON || partType == GEOS_GEOMETRYCOLLECTION )
295 int partCount = GEOSGetNumGeometries_r( geosinit()->ctxt, currentPart );
296 for (
int i = 0; i < partCount; ++i )
298 subdivideRecursive( GEOSGetGeometryN_r( geosinit()->ctxt, currentPart, i ), maxNodes, depth, parts, clipRect );
309 int vertexCount = GEOSGetNumCoordinates_r( geosinit()->ctxt, currentPart );
310 if ( vertexCount == 0 )
314 else if ( vertexCount < maxNodes )
321 double width = clipRect.
width();
322 double height = clipRect.
height();
325 if ( width > height )
338 halfClipRect1.
setYMinimum( halfClipRect1.
yMinimum() - std::numeric_limits<double>::epsilon() );
339 halfClipRect2.
setYMinimum( halfClipRect2.
yMinimum() - std::numeric_limits<double>::epsilon() );
340 halfClipRect1.
setYMaximum( halfClipRect1.
yMaximum() + std::numeric_limits<double>::epsilon() );
341 halfClipRect2.
setYMaximum( halfClipRect2.
yMaximum() + std::numeric_limits<double>::epsilon() );
345 halfClipRect1.
setXMinimum( halfClipRect1.
xMinimum() - std::numeric_limits<double>::epsilon() );
346 halfClipRect2.
setXMinimum( halfClipRect2.
xMinimum() - std::numeric_limits<double>::epsilon() );
347 halfClipRect1.
setXMaximum( halfClipRect1.
xMaximum() + std::numeric_limits<double>::epsilon() );
348 halfClipRect2.
setXMaximum( halfClipRect2.
xMaximum() + std::numeric_limits<double>::epsilon() );
358 subdivideRecursive( clipPart1.get(), maxNodes, depth, parts, halfClipRect1 );
362 subdivideRecursive( clipPart2.get(), maxNodes, depth, parts, halfClipRect2 );
374 maxNodes = std::max( maxNodes, 8 );
383 return std::move( parts );
388 return overlay( geom, OverlayUnion, errorMsg ).release();
393 QVector< GEOSGeometry * > geosGeometries;
394 geosGeometries.reserve( geomList.size() );
400 geosGeometries <<
asGeos( g, mPrecision ).release();
406 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
407 geomUnion.reset( GEOSUnaryUnion_r( geosinit()->ctxt, geomCollection.get() ) );
411 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
412 return result.release();
417 QVector< GEOSGeometry * > geosGeometries;
418 geosGeometries.reserve( geomList.size() );
424 geosGeometries <<
asGeos( g.constGet(), mPrecision ).release();
430 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
431 geomUnion.reset( GEOSUnaryUnion_r( geosinit()->ctxt, geomCollection.get() ) );
435 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
436 return result.release();
441 return overlay( geom, OverlaySymDifference, errorMsg ).release();
453 if ( !otherGeosGeom )
460 #if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=9 )
463 GEOSPreparedDistance_r( geosinit()->ctxt, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
467 GEOSDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &
distance );
470 GEOSDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &
distance );
486 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
492 #if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=9 )
495 GEOSPreparedDistance_r( geosinit()->ctxt, mGeosPrepared.get(), point.get(), &
distance );
499 GEOSDistance_r( geosinit()->ctxt, mGeos.get(), point.get(), &
distance );
502 GEOSDistance_r( geosinit()->ctxt, mGeos.get(), point.get(), &
distance );
518 if ( !otherGeosGeom )
531 #if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=9 )
534 #if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
535 return GEOSPreparedDistanceWithin_r( geosinit()->ctxt, mGeosPrepared.get(), otherGeosGeom.get(), maxdist );
537 GEOSPreparedDistance_r( geosinit()->ctxt, mGeosPrepared.get(), otherGeosGeom.get(), &
distance );
542 #if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
543 return GEOSDistanceWithin_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), maxdist );
545 GEOSDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &
distance );
548 #else // GEOS < 3.3.9
549 GEOSDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &
distance );
559 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
568 return GEOSPreparedContains_r( geosinit()->ctxt, mGeosPrepared.get(), point.get() ) == 1;
571 result = ( GEOSContains_r( geosinit()->ctxt, mGeos.get(), point.get() ) == 1 );
573 catch ( GEOSException &e )
575 logError( QStringLiteral(
"GEOS" ), e.what() );
578 *errorMsg = e.what();
595 if ( !otherGeosGeom )
602 GEOSHausdorffDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &
distance );
618 if ( !otherGeosGeom )
625 GEOSHausdorffDistanceDensify_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), densifyFraction, &
distance );
634 #if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<7
637 throw QgsNotSupportedException( QStringLiteral(
"Calculating frechetDistance requires a QGIS build based on GEOS 3.7 or later" ) );
646 if ( !otherGeosGeom )
653 GEOSFrechetDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &
distance );
663 #if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<7
665 ( void )densifyFraction;
667 throw QgsNotSupportedException( QStringLiteral(
"Calculating frechetDistanceDensify requires a QGIS build based on GEOS 3.7 or later" ) );
676 if ( !otherGeosGeom )
683 GEOSFrechetDistanceDensify_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), densifyFraction, &
distance );
693 return relation( geom, RelationIntersects, errorMsg );
698 return relation( geom, RelationTouches, errorMsg );
703 return relation( geom, RelationCrosses, errorMsg );
708 return relation( geom, RelationWithin, errorMsg );
713 return relation( geom, RelationOverlaps, errorMsg );
718 return relation( geom, RelationContains, errorMsg );
723 return relation( geom, RelationDisjoint, errorMsg );
742 char *r = GEOSRelate_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() );
745 result = QString( r );
746 GEOSFree_r( geosinit()->ctxt, r );
749 catch ( GEOSException &e )
751 logError( QStringLiteral(
"GEOS" ), e.what() );
754 *errorMsg = e.what();
763 if ( !mGeos || !geom )
777 result = ( GEOSRelatePattern_r( geosinit()->ctxt, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
779 catch ( GEOSException &e )
781 logError( QStringLiteral(
"GEOS" ), e.what() );
784 *errorMsg = e.what();
801 if ( GEOSArea_r( geosinit()->ctxt, mGeos.get(), &
area ) != 1 )
817 if ( GEOSLength_r( geosinit()->ctxt, mGeos.get(), &
length ) != 1 )
825 QVector<QgsGeometry> &newGeometries,
828 QString *errorMsg,
bool skipIntersectionCheck )
const
843 if ( !GEOSisValid_r( geosinit()->ctxt, mGeos.get() ) )
851 newGeometries.clear();
858 splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
862 splitLineGeos = createGeosPointXY( splitLine.
xAt( 0 ), splitLine.
yAt( 0 ),
false, 0,
false, 0, 2, mPrecision );
869 if ( !GEOSisValid_r( geosinit()->ctxt, splitLineGeos.get() ) || !GEOSisSimple_r( geosinit()->ctxt, splitLineGeos.get() ) )
877 if ( !topologicalTestPointsSplit( splitLineGeos.get(), topologyTestPoints ) )
886 returnCode = splitLinearGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
890 returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
904 bool QgsGeos::topologicalTestPointsSplit(
const GEOSGeometry *splitLine,
QgsPointSequence &testPoints, QString *errorMsg )
const
918 geos::unique_ptr intersectionGeom( GEOSIntersection_r( geosinit()->ctxt, mGeos.get(), splitLine ) );
919 if ( !intersectionGeom )
923 int nIntersectGeoms = 1;
924 if ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectionGeom.get() ) == GEOS_LINESTRING
925 || GEOSGeomTypeId_r( geosinit()->ctxt, intersectionGeom.get() ) == GEOS_POINT )
929 nIntersectGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, intersectionGeom.get() );
931 for (
int i = 0; i < nIntersectGeoms; ++i )
933 const GEOSGeometry *currentIntersectGeom =
nullptr;
935 currentIntersectGeom = intersectionGeom.get();
937 currentIntersectGeom = GEOSGetGeometryN_r( geosinit()->ctxt, intersectionGeom.get(), i );
939 const GEOSCoordSequence *lineSequence = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, currentIntersectGeom );
940 unsigned int sequenceSize = 0;
942 if ( GEOSCoordSeq_getSize_r( geosinit()->ctxt, lineSequence, &sequenceSize ) != 0 )
944 for (
unsigned int i = 0; i < sequenceSize; ++i )
946 if ( GEOSCoordSeq_getX_r( geosinit()->ctxt, lineSequence, i, &x ) != 0 )
948 if ( GEOSCoordSeq_getY_r( geosinit()->ctxt, lineSequence, i, &y ) != 0 )
950 testPoints.push_back(
QgsPoint( x, y ) );
964 int type = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
966 std::unique_ptr< QgsMultiCurve > multiCurve;
967 if ( type == GEOS_MULTILINESTRING )
969 multiCurve.reset( qgsgeometry_cast<QgsMultiCurve *>(
mGeometry->
clone() ) );
971 else if ( type == GEOS_LINESTRING )
989 std::unique_ptr< QgsAbstractGeometry > splitGeom(
fromGeos( GEOSsplitPoint ) );
990 std::unique_ptr< QgsMultiPoint > splitPoints( qgsgeometry_cast<QgsMultiPoint *>( splitGeom->clone() ) );
993 QgsPoint *splitPoint = qgsgeometry_cast<QgsPoint *>( splitGeom->clone() );
999 splitPoints->addGeometry( splitPoint );
1005 for (
int i = 0; i < multiCurve->numGeometries(); ++i )
1007 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( multiCurve->geometryN( i ) );
1010 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( multiCurve->geometryN( i ) );
1018 QMap< int, QVector< QPair< double, QgsPoint > > >pointMap;
1019 for (
int p = 0; p < splitPoints->numGeometries(); ++p )
1021 const QgsPoint *intersectionPoint = splitPoints->pointN( p );
1026 line->
closestSegment( *intersectionPoint, segmentPoint2D, nextVertex );
1029 std::unique_ptr< QgsPoint > correctSegmentPoint(
segment.interpolatePoint(
distance ) );
1030 const QPair< double, QgsPoint > pair = qMakePair(
distance, *correctSegmentPoint.get() );
1031 if ( pointMap.contains( nextVertex.
vertex - 1 ) )
1032 pointMap[ nextVertex.
vertex - 1 ].append( pair );
1034 pointMap[ nextVertex.
vertex - 1 ] = QVector< QPair< double, QgsPoint > >() << pair;
1039 for (
auto &p : pointMap )
1041 std::sort( p.begin(), p.end(), [](
const QPair< double, QgsPoint > &a,
const QPair< double, QgsPoint > &b ) { return a.first < b.first; } );
1048 for (
int j = 0; j < nVertices; ++j )
1052 if ( pointMap.contains( j ) )
1055 for (
int k = 0; k < pointMap[ j ].size(); ++k )
1057 splitPoint = pointMap[ j ][k].second;
1058 if ( splitPoint == currentPoint )
1064 else if ( splitPoint == line->
pointN( j + 1 ) )
1083 return asGeos( &lines, mPrecision );
1088 Q_UNUSED( skipIntersectionCheck )
1095 geos::unique_ptr intersectGeom( GEOSIntersection_r( geosinit()->ctxt, splitLine, mGeos.get() ) );
1096 if ( !intersectGeom )
1100 int linearIntersect = GEOSRelatePattern_r( geosinit()->ctxt, mGeos.get(), splitLine,
"1********" );
1101 if ( linearIntersect > 0 )
1105 splitGeom = linePointDifference( intersectGeom.get() );
1110 QVector<GEOSGeometry *> lineGeoms;
1112 int splitType = GEOSGeomTypeId_r( geosinit()->ctxt, splitGeom.get() );
1113 if ( splitType == GEOS_MULTILINESTRING )
1115 int nGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, splitGeom.get() );
1116 lineGeoms.reserve( nGeoms );
1117 for (
int i = 0; i < nGeoms; ++i )
1118 lineGeoms << GEOSGeom_clone_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt, splitGeom.get(), i ) );
1123 lineGeoms << GEOSGeom_clone_r( geosinit()->ctxt, splitGeom.get() );
1126 mergeGeometriesMultiTypeSplit( lineGeoms );
1128 for (
int i = 0; i < lineGeoms.size(); ++i )
1131 GEOSGeom_destroy_r( geosinit()->ctxt, lineGeoms[i] );
1147 if ( !mGeosPrepared )
1151 if ( !skipIntersectionCheck && !GEOSPreparedIntersects_r( geosinit()->ctxt, mGeosPrepared.get(), splitLine ) )
1156 if ( !nodedGeometry )
1159 const GEOSGeometry *noded = nodedGeometry.get();
1160 geos::unique_ptr polygons( GEOSPolygonize_r( geosinit()->ctxt, &noded, 1 ) );
1161 if ( !polygons || numberOfGeometries( polygons.get() ) == 0 )
1168 QVector<GEOSGeometry *> testedGeometries;
1173 for (
int i = 0; i < numberOfGeometries( polygons.get() ); i++ )
1175 const GEOSGeometry *polygon = GEOSGetGeometryN_r( geosinit()->ctxt, polygons.get(), i );
1179 testedGeometries << GEOSGeom_clone_r( geosinit()->ctxt, polygon );
1182 int nGeometriesThis = numberOfGeometries( mGeos.get() );
1183 if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
1186 for (
int i = 0; i < testedGeometries.size(); ++i )
1188 GEOSGeom_destroy_r( geosinit()->ctxt, testedGeometries[i] );
1197 mergeGeometriesMultiTypeSplit( testedGeometries );
1200 for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( geosinit()->ctxt, testedGeometries[i] ); ++i )
1203 if ( i < testedGeometries.size() )
1205 for ( i = 0; i < testedGeometries.size(); ++i )
1206 GEOSGeom_destroy_r( geosinit()->ctxt, testedGeometries[i] );
1211 for ( i = 0; i < testedGeometries.size(); ++i )
1214 GEOSGeom_destroy_r( geosinit()->ctxt, testedGeometries[i] );
1220 geos::unique_ptr QgsGeos::nodeGeometries(
const GEOSGeometry *splitLine,
const GEOSGeometry *geom )
1222 if ( !splitLine || !geom )
1226 if ( GEOSGeomTypeId_r( geosinit()->ctxt, geom ) == GEOS_POLYGON || GEOSGeomTypeId_r( geosinit()->ctxt, geom ) == GEOS_MULTIPOLYGON )
1227 geometryBoundary.reset( GEOSBoundary_r( geosinit()->ctxt, geom ) );
1229 geometryBoundary.reset( GEOSGeom_clone_r( geosinit()->ctxt, geom ) );
1231 geos::unique_ptr splitLineClone( GEOSGeom_clone_r( geosinit()->ctxt, splitLine ) );
1232 geos::unique_ptr unionGeometry( GEOSUnion_r( geosinit()->ctxt, splitLineClone.get(), geometryBoundary.get() ) );
1234 return unionGeometry;
1237 int QgsGeos::mergeGeometriesMultiTypeSplit( QVector<GEOSGeometry *> &splitResult )
const
1243 int type = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
1244 if ( type != GEOS_GEOMETRYCOLLECTION &&
1245 type != GEOS_MULTILINESTRING &&
1246 type != GEOS_MULTIPOLYGON &&
1247 type != GEOS_MULTIPOINT )
1250 QVector<GEOSGeometry *> copyList = splitResult;
1251 splitResult.clear();
1254 QVector<GEOSGeometry *> unionGeom;
1256 for (
int i = 0; i < copyList.size(); ++i )
1259 bool isPart =
false;
1260 for (
int j = 0; j < GEOSGetNumGeometries_r( geosinit()->ctxt, mGeos.get() ); j++ )
1262 if ( GEOSEquals_r( geosinit()->ctxt, copyList[i], GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), j ) ) )
1271 unionGeom << copyList[i];
1275 QVector<GEOSGeometry *> geomVector;
1276 geomVector << copyList[i];
1278 if ( type == GEOS_MULTILINESTRING )
1279 splitResult << createGeosCollection( GEOS_MULTILINESTRING, geomVector ).release();
1280 else if ( type == GEOS_MULTIPOLYGON )
1281 splitResult << createGeosCollection( GEOS_MULTIPOLYGON, geomVector ).release();
1283 GEOSGeom_destroy_r( geosinit()->ctxt, copyList[i] );
1288 if ( !unionGeom.isEmpty() )
1290 if ( type == GEOS_MULTILINESTRING )
1291 splitResult << createGeosCollection( GEOS_MULTILINESTRING, unionGeom ).release();
1292 else if ( type == GEOS_MULTIPOLYGON )
1293 splitResult << createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ).release();
1303 geos::unique_ptr QgsGeos::createGeosCollection(
int typeId,
const QVector<GEOSGeometry *> &geoms )
1305 int nNullGeoms = geoms.count(
nullptr );
1306 int nNotNullGeoms = geoms.size() - nNullGeoms;
1308 GEOSGeometry **geomarr =
new GEOSGeometry*[ nNotNullGeoms ];
1315 QVector<GEOSGeometry *>::const_iterator geomIt = geoms.constBegin();
1316 for ( ; geomIt != geoms.constEnd(); ++geomIt )
1320 if ( GEOSisEmpty_r( geosinit()->ctxt, *geomIt ) )
1325 GEOSGeom_destroy_r( geosinit()->ctxt, *geomIt );
1329 geomarr[i] = *geomIt;
1338 geom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, typeId, geomarr, nNotNullGeoms ) );
1340 catch ( GEOSException & )
1356 int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit()->ctxt,
geos );
1357 int nDims = GEOSGeom_getDimensions_r( geosinit()->ctxt,
geos );
1358 bool hasZ = ( nCoordDims == 3 );
1359 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1361 switch ( GEOSGeomTypeId_r( geosinit()->ctxt,
geos ) )
1365 if ( GEOSisEmpty_r( geosinit()->ctxt,
geos ) )
1368 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt,
geos );
1369 unsigned int nPoints = 0;
1370 GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1371 return nPoints > 0 ? std::unique_ptr<QgsAbstractGeometry>(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() ) :
nullptr;
1373 case GEOS_LINESTRING:
1375 return sequenceToLinestring(
geos, hasZ, hasM );
1381 case GEOS_MULTIPOINT:
1383 std::unique_ptr< QgsMultiPoint > multiPoint(
new QgsMultiPoint() );
1384 int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt,
geos );
1385 multiPoint->reserve( nParts );
1386 for (
int i = 0; i < nParts; ++i )
1388 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt,
geos, i ) );
1391 unsigned int nPoints = 0;
1392 GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1394 multiPoint->addGeometry(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1397 return std::move( multiPoint );
1399 case GEOS_MULTILINESTRING:
1402 int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt,
geos );
1403 multiLineString->reserve( nParts );
1404 for (
int i = 0; i < nParts; ++i )
1406 std::unique_ptr< QgsLineString >line( sequenceToLinestring( GEOSGetGeometryN_r( geosinit()->ctxt,
geos, i ), hasZ, hasM ) );
1409 multiLineString->addGeometry( line.release() );
1412 return std::move( multiLineString );
1414 case GEOS_MULTIPOLYGON:
1416 std::unique_ptr< QgsMultiPolygon > multiPolygon(
new QgsMultiPolygon() );
1418 int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt,
geos );
1419 multiPolygon->reserve( nParts );
1420 for (
int i = 0; i < nParts; ++i )
1422 std::unique_ptr< QgsPolygon > poly =
fromGeosPolygon( GEOSGetGeometryN_r( geosinit()->ctxt,
geos, i ) );
1425 multiPolygon->addGeometry( poly.release() );
1428 return std::move( multiPolygon );
1430 case GEOS_GEOMETRYCOLLECTION:
1433 int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt,
geos );
1434 geomCollection->reserve( nParts );
1435 for (
int i = 0; i < nParts; ++i )
1437 std::unique_ptr< QgsAbstractGeometry > geom(
fromGeos( GEOSGetGeometryN_r( geosinit()->ctxt,
geos, i ) ) );
1440 geomCollection->addGeometry( geom.release() );
1443 return std::move( geomCollection );
1451 if ( GEOSGeomTypeId_r( geosinit()->ctxt,
geos ) != GEOS_POLYGON )
1456 int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit()->ctxt,
geos );
1457 int nDims = GEOSGeom_getDimensions_r( geosinit()->ctxt,
geos );
1458 bool hasZ = ( nCoordDims == 3 );
1459 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1461 std::unique_ptr< QgsPolygon > polygon(
new QgsPolygon() );
1463 const GEOSGeometry *ring = GEOSGetExteriorRing_r( geosinit()->ctxt,
geos );
1466 polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1469 QVector<QgsCurve *> interiorRings;
1470 const int ringCount = GEOSGetNumInteriorRings_r( geosinit()->ctxt,
geos );
1471 interiorRings.reserve( ringCount );
1472 for (
int i = 0; i < ringCount; ++i )
1474 ring = GEOSGetInteriorRingN_r( geosinit()->ctxt,
geos, i );
1477 interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1480 polygon->setInteriorRings( interiorRings );
1485 std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring(
const GEOSGeometry *
geos,
bool hasZ,
bool hasM )
1487 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt,
geos );
1488 unsigned int nPoints;
1489 GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1490 QVector< double > xOut( nPoints );
1491 QVector< double > yOut( nPoints );
1492 QVector< double > zOut;
1494 zOut.resize( nPoints );
1495 QVector< double > mOut;
1497 mOut.resize( nPoints );
1498 double *x = xOut.data();
1499 double *y = yOut.data();
1500 double *z = zOut.data();
1501 double *m = mOut.data();
1502 for (
unsigned int i = 0; i < nPoints; ++i )
1504 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1506 GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, cs, i, x++, y++, z++ );
1508 GEOSCoordSeq_getXY_r( geosinit()->ctxt, cs, i, x++, y++ );
1510 GEOSCoordSeq_getX_r( geosinit()->ctxt, cs, i, x++ );
1511 GEOSCoordSeq_getY_r( geosinit()->ctxt, cs, i, y++ );
1514 GEOSCoordSeq_getZ_r( geosinit()->ctxt, cs, i, z++ );
1519 GEOSCoordSeq_getOrdinate_r( geosinit()->ctxt, cs, i, 3, m++ );
1522 std::unique_ptr< QgsLineString > line(
new QgsLineString( xOut, yOut, zOut, mOut ) );
1526 int QgsGeos::numberOfGeometries( GEOSGeometry *g )
1531 int geometryType = GEOSGeomTypeId_r( geosinit()->ctxt, g );
1532 if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING
1533 || geometryType == GEOS_POLYGON )
1537 return GEOSGetNumGeometries_r( geosinit()->ctxt, g );
1550 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1552 GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, cs, i, &x, &y, &z );
1554 GEOSCoordSeq_getXY_r( geosinit()->ctxt, cs, i, &x, &y );
1556 GEOSCoordSeq_getX_r( geosinit()->ctxt, cs, i, &x );
1557 GEOSCoordSeq_getY_r( geosinit()->ctxt, cs, i, &y );
1560 GEOSCoordSeq_getZ_r( geosinit()->ctxt, cs, i, &z );
1565 GEOSCoordSeq_getOrdinate_r( geosinit()->ctxt, cs, i, 3, &m );
1601 int geosType = GEOS_GEOMETRYCOLLECTION;
1608 geosType = GEOS_MULTIPOINT;
1612 geosType = GEOS_MULTILINESTRING;
1616 geosType = GEOS_MULTIPOLYGON;
1631 QVector< GEOSGeometry * > geomVector(
c->numGeometries() );
1632 for (
int i = 0; i <
c->numGeometries(); ++i )
1636 return createGeosCollection( geosType, geomVector );
1643 return createGeosPoint(
static_cast<const QgsPoint *
>( geom ), coordDims,
precision );
1659 std::unique_ptr<QgsAbstractGeometry> QgsGeos::overlay(
const QgsAbstractGeometry *geom, Overlay op, QString *errorMsg )
const
1661 if ( !mGeos || !geom )
1677 case OverlayIntersection:
1678 opGeom.reset( GEOSIntersection_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1681 case OverlayDifference:
1682 opGeom.reset( GEOSDifference_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1687 geos::unique_ptr unionGeometry( GEOSUnion_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1689 if ( unionGeometry && GEOSGeomTypeId_r( geosinit()->ctxt, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1691 geos::unique_ptr mergedLines( GEOSLineMerge_r( geosinit()->ctxt, unionGeometry.get() ) );
1694 unionGeometry = std::move( mergedLines );
1698 opGeom = std::move( unionGeometry );
1702 case OverlaySymDifference:
1703 opGeom.reset( GEOSSymDifference_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1708 catch ( GEOSException &e )
1710 logError( QStringLiteral(
"GEOS" ), e.what() );
1713 *errorMsg = e.what();
1719 bool QgsGeos::relation(
const QgsAbstractGeometry *geom, Relation r, QString *errorMsg )
const
1721 if ( !mGeos || !geom )
1732 bool result =
false;
1735 if ( mGeosPrepared )
1739 case RelationIntersects:
1740 result = ( GEOSPreparedIntersects_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1742 case RelationTouches:
1743 result = ( GEOSPreparedTouches_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1745 case RelationCrosses:
1746 result = ( GEOSPreparedCrosses_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1748 case RelationWithin:
1749 result = ( GEOSPreparedWithin_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1751 case RelationContains:
1752 result = ( GEOSPreparedContains_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1754 case RelationDisjoint:
1755 result = ( GEOSPreparedDisjoint_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1757 case RelationOverlaps:
1758 result = ( GEOSPreparedOverlaps_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1766 case RelationIntersects:
1767 result = ( GEOSIntersects_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1769 case RelationTouches:
1770 result = ( GEOSTouches_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1772 case RelationCrosses:
1773 result = ( GEOSCrosses_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1775 case RelationWithin:
1776 result = ( GEOSWithin_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1778 case RelationContains:
1779 result = ( GEOSContains_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1781 case RelationDisjoint:
1782 result = ( GEOSDisjoint_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1784 case RelationOverlaps:
1785 result = ( GEOSOverlaps_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1789 catch ( GEOSException &e )
1791 logError( QStringLiteral(
"GEOS" ), e.what() );
1794 *errorMsg = e.what();
1812 geos.reset( GEOSBuffer_r( geosinit()->ctxt, mGeos.get(),
distance, segments ) );
1828 geos.reset( GEOSBufferWithStyle_r( geosinit()->ctxt, mGeos.get(),
distance, segments,
static_cast< int >( endCapStyle ),
static_cast< int >( joinStyle ), miterLimit ) );
1843 geos.reset( GEOSTopologyPreserveSimplify_r( geosinit()->ctxt, mGeos.get(), tolerance ) );
1858 geos.reset( GEOSInterpolate_r( geosinit()->ctxt, mGeos.get(),
distance ) );
1877 geos.reset( GEOSGetCentroid_r( geosinit()->ctxt, mGeos.get() ) );
1882 GEOSGeomGetX_r( geosinit()->ctxt,
geos.get(), &x );
1883 GEOSGeomGetY_r( geosinit()->ctxt,
geos.get(), &y );
1899 geos.reset( GEOSEnvelope_r( geosinit()->ctxt, mGeos.get() ) );
1918 geos.reset( GEOSPointOnSurface_r( geosinit()->ctxt, mGeos.get() ) );
1920 if ( !
geos || GEOSisEmpty_r( geosinit()->ctxt,
geos.get() ) != 0 )
1925 GEOSGeomGetX_r( geosinit()->ctxt,
geos.get(), &x );
1926 GEOSGeomGetY_r( geosinit()->ctxt,
geos.get(), &y );
1942 geos::unique_ptr cHull( GEOSConvexHull_r( geosinit()->ctxt, mGeos.get() ) );
1943 std::unique_ptr< QgsAbstractGeometry > cHullGeom =
fromGeos( cHull.get() );
1944 return cHullGeom.release();
1958 GEOSGeometry *g1 =
nullptr;
1960 char res = GEOSisValidDetail_r( geosinit()->ctxt, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
1961 const bool invalid = res != 1;
1966 error = QString( r );
1967 GEOSFree_r( geosinit()->ctxt, r );
1970 if ( invalid && errorMsg )
1974 if ( translatedErrors.empty() )
1977 translatedErrors.insert( QStringLiteral(
"topology validation error" ), QObject::tr(
"Topology validation error",
"GEOS Error" ) );
1978 translatedErrors.insert( QStringLiteral(
"repeated point" ), QObject::tr(
"Repeated point",
"GEOS Error" ) );
1979 translatedErrors.insert( QStringLiteral(
"hole lies outside shell" ), QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) );
1980 translatedErrors.insert( QStringLiteral(
"holes are nested" ), QObject::tr(
"Holes are nested",
"GEOS Error" ) );
1981 translatedErrors.insert( QStringLiteral(
"interior is disconnected" ), QObject::tr(
"Interior is disconnected",
"GEOS Error" ) );
1982 translatedErrors.insert( QStringLiteral(
"self-intersection" ), QObject::tr(
"Self-intersection",
"GEOS Error" ) );
1983 translatedErrors.insert( QStringLiteral(
"ring self-intersection" ), QObject::tr(
"Ring self-intersection",
"GEOS Error" ) );
1984 translatedErrors.insert( QStringLiteral(
"nested shells" ), QObject::tr(
"Nested shells",
"GEOS Error" ) );
1985 translatedErrors.insert( QStringLiteral(
"duplicate rings" ), QObject::tr(
"Duplicate rings",
"GEOS Error" ) );
1986 translatedErrors.insert( QStringLiteral(
"too few points in geometry component" ), QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) );
1987 translatedErrors.insert( QStringLiteral(
"invalid coordinate" ), QObject::tr(
"Invalid coordinate",
"GEOS Error" ) );
1988 translatedErrors.insert( QStringLiteral(
"ring is not closed" ), QObject::tr(
"Ring is not closed",
"GEOS Error" ) );
1991 *errorMsg = translatedErrors.value( error.toLower(), error );
1993 if ( g1 && errorLoc )
1999 GEOSGeom_destroy_r( geosinit()->ctxt, g1 );
2009 if ( !mGeos || !geom )
2021 bool equal = GEOSEquals_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() );
2036 return GEOSisEmpty_r( geosinit()->ctxt, mGeos.get() );
2050 return GEOSisSimple_r( geosinit()->ctxt, mGeos.get() );
2055 GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
2057 std::unique_ptr< QgsLineString > segmentized;
2058 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( curve );
2063 line = segmentized.get();
2071 bool hasZ = line->
is3D();
2085 int numOutPoints = numPoints;
2086 if ( forceClose && ( line->
pointN( 0 ) != line->
pointN( numPoints - 1 ) ) )
2091 GEOSCoordSequence *coordSeq =
nullptr;
2094 coordSeq = GEOSCoordSeq_create_r( geosinit()->ctxt, numOutPoints, coordDims );
2097 QgsDebugMsg( QStringLiteral(
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions" ).arg( numPoints ).arg( coordDims ) );
2101 const double *xData = line->
xData();
2102 const double *yData = line->
yData();
2103 const double *zData = hasZ ? line->
zData() :
nullptr;
2104 const double *mData = hasM ? line->
mData() :
nullptr;
2108 for (
int i = 0; i < numOutPoints; ++i )
2110 if ( i >= numPoints )
2113 xData = line->
xData();
2114 yData = line->
yData();
2115 zData = hasZ ? line->
zData() :
nullptr;
2116 mData = hasM ? line->
mData() :
nullptr;
2118 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
2128 GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, i, std::round( *xData++ /
precision ) *
precision );
2129 GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, i, std::round( *yData++ /
precision ) *
precision );
2132 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 2, std::round( *zData++ /
precision ) *
precision );
2137 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 3, line->
mAt( *mData++ ) );
2143 for (
int i = 0; i < numOutPoints; ++i )
2145 if ( i >= numPoints )
2148 xData = line->
xData();
2149 yData = line->
yData();
2150 zData = hasZ ? line->
zData() :
nullptr;
2151 mData = hasM ? line->
mData() :
nullptr;
2153 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
2156 GEOSCoordSeq_setXYZ_r( geosinit()->ctxt, coordSeq, i, *xData++, *yData++, *zData++ );
2160 GEOSCoordSeq_setXY_r( geosinit()->ctxt, coordSeq, i, *xData++, *yData++ );
2163 GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, i, *xData++ );
2164 GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, i, *yData++ );
2167 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 2, *zData++ );
2172 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 3, *mData++ );
2184 const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( point );
2191 geos::unique_ptr QgsGeos::createGeosPointXY(
double x,
double y,
bool hasZ,
double z,
bool hasM,
double m,
int coordDims,
double precision )
2199 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
2200 if ( coordDims == 2 )
2206 geosPoint.reset( GEOSGeom_createPointFromXY_r( geosinit()->ctxt, x, y ) );
2211 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( geosinit()->ctxt, 1, coordDims );
2214 QgsDebugMsg( QStringLiteral(
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions" ).arg( coordDims ) );
2219 GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, 0, std::round( x /
precision ) *
precision );
2220 GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, 0, std::round( y /
precision ) *
precision );
2223 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 2, std::round( z /
precision ) *
precision );
2228 GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, 0, x );
2229 GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, 0, y );
2232 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 2, z );
2235 #if 0 //disabled until geos supports m-coordinates
2238 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 3, m );
2241 geosPoint.reset( GEOSGeom_createPoint_r( geosinit()->ctxt, coordSeq ) );
2249 const QgsCurve *
c = qgsgeometry_cast<const QgsCurve *>( curve );
2253 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c,
precision );
2260 geosGeom.reset( GEOSGeom_createLineString_r( geosinit()->ctxt, coordSeq ) );
2268 const QgsCurvePolygon *polygon = qgsgeometry_cast<const QgsCurvePolygon *>( poly );
2273 if ( !exteriorRing )
2281 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( geosinit()->ctxt, createCoordinateSequence( exteriorRing,
precision,
true ) ) );
2284 GEOSGeometry **holes =
nullptr;
2287 holes =
new GEOSGeometry*[ nHoles ];
2290 for (
int i = 0; i < nHoles; ++i )
2293 holes[i] = GEOSGeom_createLinearRing_r( geosinit()->ctxt, createCoordinateSequence( interiorRing,
precision,
true ) );
2295 geosPolygon.reset( GEOSGeom_createPolygon_r( geosinit()->ctxt, exteriorRingGeos.release(), holes, nHoles ) );
2311 offset.reset( GEOSOffsetCurve_r( geosinit()->ctxt, mGeos.get(),
distance, segments,
static_cast< int >( joinStyle ), miterLimit ) );
2314 std::unique_ptr< QgsAbstractGeometry > offsetGeom =
fromGeos( offset.get() );
2315 return offsetGeom.release();
2329 GEOSBufferParams_setSingleSided_r( geosinit()->ctxt, bp.get(), 1 );
2330 GEOSBufferParams_setQuadrantSegments_r( geosinit()->ctxt, bp.get(), segments );
2331 GEOSBufferParams_setJoinStyle_r( geosinit()->ctxt, bp.get(),
static_cast< int >( joinStyle ) );
2332 GEOSBufferParams_setMitreLimit_r( geosinit()->ctxt, bp.get(), miterLimit );
2334 if ( side == Qgis::BufferSide::Right )
2338 geos.reset( GEOSBufferWithParams_r( geosinit()->ctxt, mGeos.get(), bp.get(),
distance ) );
2346 #if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<9
2349 throw QgsNotSupportedException( QStringLiteral(
"Calculating maximumInscribedCircle requires a QGIS build based on GEOS 3.9 or later" ) );
2359 geos.reset( GEOSMaximumInscribedCircle_r( geosinit()->ctxt, mGeos.get(), tolerance ) );
2368 #if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<9
2372 throw QgsNotSupportedException( QStringLiteral(
"Calculating largestEmptyCircle requires a QGIS build based on GEOS 3.9 or later" ) );
2384 boundaryGeos =
asGeos( boundary );
2386 geos.reset( GEOSLargestEmptyCircle_r( geosinit()->ctxt, mGeos.get(), boundaryGeos.get(), tolerance ) );
2395 #if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<6
2397 throw QgsNotSupportedException( QStringLiteral(
"Calculating minimumWidth requires a QGIS build based on GEOS 3.6 or later" ) );
2407 geos.reset( GEOSMinimumWidth_r( geosinit()->ctxt, mGeos.get() ) );
2416 #if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<6
2418 throw QgsNotSupportedException( QStringLiteral(
"Calculating minimumClearance requires a QGIS build based on GEOS 3.6 or later" ) );
2422 return std::numeric_limits< double >::quiet_NaN();
2429 if ( GEOSMinimumClearance_r( geosinit()->ctxt, mGeos.get(), &res ) != 0 )
2430 return std::numeric_limits< double >::quiet_NaN();
2439 #if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<6
2441 throw QgsNotSupportedException( QStringLiteral(
"Calculating minimumClearanceLine requires a QGIS build based on GEOS 3.6 or later" ) );
2451 geos.reset( GEOSMinimumClearanceLine_r( geosinit()->ctxt, mGeos.get() ) );
2468 geos.reset( GEOSNode_r( geosinit()->ctxt, mGeos.get() ) );
2476 if ( !mGeos || !other )
2488 geos.reset( GEOSSharedPaths_r( geosinit()->ctxt, mGeos.get(), otherGeos.get() ) );
2508 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
2511 int numGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, mGeos.get() );
2512 if ( numGeoms == -1 )
2521 bool isMultiGeom =
false;
2522 int geosTypeId = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
2523 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
2533 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2537 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2542 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
2543 return reshapeResult;
2550 bool reshapeTookPlace =
false;
2553 GEOSGeometry **newGeoms =
new GEOSGeometry*[numGeoms];
2555 for (
int i = 0; i < numGeoms; ++i )
2558 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2560 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2562 if ( currentReshapeGeometry )
2564 newGeoms[i] = currentReshapeGeometry.release();
2565 reshapeTookPlace =
true;
2569 newGeoms[i] = GEOSGeom_clone_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ) );
2576 newMultiGeom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
2580 newMultiGeom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
2584 if ( !newMultiGeom )
2590 if ( reshapeTookPlace )
2594 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
2595 return reshapedMultiGeom;
2617 if ( GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() ) != GEOS_MULTILINESTRING )
2623 geos.reset( GEOSLineMerge_r( geosinit()->ctxt, mGeos.get() ) );
2646 #if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=9 )
2648 if ( mGeosPrepared )
2650 nearestCoord.reset( GEOSPreparedNearestPoints_r( geosinit()->ctxt, mGeosPrepared.get(), otherGeom.get() ) );
2654 nearestCoord.reset( GEOSNearestPoints_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() ) );
2660 ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 0, &nx );
2661 ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 0, &ny );
2663 catch ( GEOSException &e )
2665 logError( QStringLiteral(
"GEOS" ), e.what() );
2668 *errorMsg = e.what();
2678 if ( !mGeos || other.
isEmpty() )
2688 if ( !other || other->
isEmpty() )
2705 if ( !nearestCoord )
2708 *errorMsg = QStringLiteral(
"GEOS returned no nearest points" );
2712 ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 0, &nx1 );
2713 ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 0, &ny1 );
2714 ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 1, &nx2 );
2715 ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 1, &ny2 );
2717 catch ( GEOSException &e )
2719 logError( QStringLiteral(
"GEOS" ), e.what() );
2722 *errorMsg = e.what();
2749 distance = GEOSProject_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() );
2751 catch ( GEOSException &e )
2753 logError( QStringLiteral(
"GEOS" ), e.what() );
2756 *errorMsg = e.what();
2771 geos::unique_ptr point = createGeosPointXY( x, y,
false, 0,
false, 0, 2, 0 );
2778 distance = GEOSProject_r( geosinit()->ctxt, mGeos.get(), point.get() );
2780 catch ( GEOSException &e )
2782 logError( QStringLiteral(
"GEOS" ), e.what() );
2785 *errorMsg = e.what();
2795 GEOSGeometry **
const lineGeosGeometries =
new GEOSGeometry*[ geometries.size()];
2802 lineGeosGeometries[validLines] = l.release();
2809 geos::unique_ptr result( GEOSPolygonize_r( geosinit()->ctxt, lineGeosGeometries, validLines ) );
2810 for (
int i = 0; i < validLines; ++i )
2812 GEOSGeom_destroy_r( geosinit()->ctxt, lineGeosGeometries[i] );
2814 delete[] lineGeosGeometries;
2817 catch ( GEOSException &e )
2821 *errorMsg = e.what();
2823 for (
int i = 0; i < validLines; ++i )
2825 GEOSGeom_destroy_r( geosinit()->ctxt, lineGeosGeometries[i] );
2827 delete[] lineGeosGeometries;
2842 extentGeosGeom =
asGeos( extent, mPrecision );
2843 if ( !extentGeosGeom )
2852 geos.reset( GEOSVoronoiDiagram_r( geosinit()->ctxt, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
2854 if ( !
geos || GEOSisEmpty_r( geosinit()->ctxt,
geos.get() ) != 0 )
2874 geos.reset( GEOSDelaunayTriangulation_r( geosinit()->ctxt, mGeos.get(), tolerance, edgesOnly ) );
2876 if ( !
geos || GEOSisEmpty_r( geosinit()->ctxt,
geos.get() ) != 0 )
2888 static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
2890 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, linestring );
2894 unsigned int coordSeqSize;
2895 if ( GEOSCoordSeq_getSize_r( geosinit()->ctxt, coordSeq, &coordSeqSize ) == 0 )
2898 if ( coordSeqSize < 2 )
2901 GEOSCoordSeq_getX_r( geosinit()->ctxt, coordSeq, 0, &x1 );
2902 GEOSCoordSeq_getY_r( geosinit()->ctxt, coordSeq, 0, &y1 );
2903 GEOSCoordSeq_getX_r( geosinit()->ctxt, coordSeq, coordSeqSize - 1, &x2 );
2904 GEOSCoordSeq_getY_r( geosinit()->ctxt, coordSeq, coordSeqSize - 1, &y2 );
2910 static geos::unique_ptr _mergeLinestrings(
const GEOSGeometry *line1,
const GEOSGeometry *line2,
const QgsPointXY &intersectionPoint )
2912 double x1, y1, x2, y2;
2913 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
2916 double rx1, ry1, rx2, ry2;
2917 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
2920 bool intersectionAtOrigLineEndpoint =
2921 ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) !=
2922 ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
2923 bool intersectionAtReshapeLineEndpoint =
2924 ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) ||
2925 ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
2928 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
2932 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
2933 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2934 geos::unique_ptr res( GEOSLineMerge_r( geosinit()->ctxt, multiGeom.get() ) );
2942 geos::unique_ptr QgsGeos::reshapeLine(
const GEOSGeometry *line,
const GEOSGeometry *reshapeLineGeos,
double precision )
2944 if ( !line || !reshapeLineGeos )
2947 bool atLeastTwoIntersections =
false;
2948 bool oneIntersection =
false;
2954 geos::unique_ptr intersectGeom( GEOSIntersection_r( geosinit()->ctxt, line, reshapeLineGeos ) );
2955 if ( intersectGeom )
2957 const int geomType = GEOSGeomTypeId_r( geosinit()->ctxt, intersectGeom.get() );
2958 atLeastTwoIntersections = ( geomType == GEOS_MULTIPOINT && GEOSGetNumGeometries_r( geosinit()->ctxt, intersectGeom.get() ) > 1 )
2959 || ( geomType == GEOS_GEOMETRYCOLLECTION && GEOSGetNumGeometries_r( geosinit()->ctxt, intersectGeom.get() ) > 0 )
2960 || ( geomType == GEOS_MULTILINESTRING && GEOSGetNumGeometries_r( geosinit()->ctxt, intersectGeom.get() ) > 0 );
2962 if ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectGeom.get() ) == GEOS_POINT )
2964 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, intersectGeom.get() );
2966 GEOSCoordSeq_getX_r( geosinit()->ctxt, intersectionCoordSeq, 0, &xi );
2967 GEOSCoordSeq_getY_r( geosinit()->ctxt, intersectionCoordSeq, 0, &yi );
2968 oneIntersection =
true;
2973 catch ( GEOSException & )
2975 atLeastTwoIntersections =
false;
2979 if ( oneIntersection )
2980 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
2982 if ( !atLeastTwoIntersections )
2986 double x1, y1, x2, y2;
2987 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
2993 bool isRing =
false;
2994 if ( GEOSGeomTypeId_r( geosinit()->ctxt, line ) == GEOS_LINEARRING
2995 || GEOSEquals_r( geosinit()->ctxt, beginLineVertex.get(), endLineVertex.get() ) == 1 )
3000 if ( !nodedGeometry )
3006 geos::unique_ptr mergedLines( GEOSLineMerge_r( geosinit()->ctxt, nodedGeometry.get() ) );
3012 int numMergedLines = GEOSGetNumGeometries_r( geosinit()->ctxt, mergedLines.get() );
3013 if ( numMergedLines < 2 )
3015 if ( numMergedLines == 1 )
3017 geos::unique_ptr result( GEOSGeom_clone_r( geosinit()->ctxt, reshapeLineGeos ) );
3024 QVector<GEOSGeometry *> resultLineParts;
3025 QVector<GEOSGeometry *> probableParts;
3027 for (
int i = 0; i < numMergedLines; ++i )
3029 const GEOSGeometry *currentGeom = GEOSGetGeometryN_r( geosinit()->ctxt, mergedLines.get(), i );
3032 bool alreadyAdded =
false;
3034 double bufferDistance = std::pow( 10.0L, geomDigits( currentGeom ) - 11 );
3035 for (
const GEOSGeometry *other : std::as_const( resultLineParts ) )
3037 GEOSHausdorffDistance_r( geosinit()->ctxt, currentGeom, other, &
distance );
3040 alreadyAdded =
true;
3047 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, currentGeom );
3048 unsigned int currentCoordSeqSize;
3049 GEOSCoordSeq_getSize_r( geosinit()->ctxt, currentCoordSeq, ¤tCoordSeqSize );
3050 if ( currentCoordSeqSize < 2 )
3054 double xBegin, xEnd, yBegin, yEnd;
3055 GEOSCoordSeq_getX_r( geosinit()->ctxt, currentCoordSeq, 0, &xBegin );
3056 GEOSCoordSeq_getY_r( geosinit()->ctxt, currentCoordSeq, 0, &yBegin );
3057 GEOSCoordSeq_getX_r( geosinit()->ctxt, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
3058 GEOSCoordSeq_getY_r( geosinit()->ctxt, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
3063 int nEndpointsOnOriginalLine = 0;
3064 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
3065 nEndpointsOnOriginalLine += 1;
3067 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
3068 nEndpointsOnOriginalLine += 1;
3071 int nEndpointsSameAsOriginalLine = 0;
3072 if ( GEOSEquals_r( geosinit()->ctxt, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
3073 || GEOSEquals_r( geosinit()->ctxt, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3074 nEndpointsSameAsOriginalLine += 1;
3076 if ( GEOSEquals_r( geosinit()->ctxt, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
3077 || GEOSEquals_r( geosinit()->ctxt, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
3078 nEndpointsSameAsOriginalLine += 1;
3081 bool currentGeomOverlapsOriginalGeom =
false;
3082 bool currentGeomOverlapsReshapeLine =
false;
3083 if ( lineContainedInLine( currentGeom, line ) == 1 )
3084 currentGeomOverlapsOriginalGeom =
true;
3086 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
3087 currentGeomOverlapsReshapeLine =
true;
3090 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3092 resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
3095 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
3097 probableParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
3099 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3101 resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
3103 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
3105 resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
3107 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
3109 resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
3114 if ( isRing && !probableParts.isEmpty() )
3117 GEOSGeometry *currentGeom =
nullptr;
3118 double maxLength = -std::numeric_limits<double>::max();
3119 double currentLength = 0;
3120 for (
int i = 0; i < probableParts.size(); ++i )
3122 currentGeom = probableParts.at( i );
3123 GEOSLength_r( geosinit()->ctxt, currentGeom, ¤tLength );
3124 if ( currentLength > maxLength )
3126 maxLength = currentLength;
3127 maxGeom.reset( currentGeom );
3131 GEOSGeom_destroy_r( geosinit()->ctxt, currentGeom );
3134 resultLineParts.push_back( maxGeom.release() );
3138 if ( resultLineParts.empty() )
3141 if ( resultLineParts.size() == 1 )
3143 result.reset( resultLineParts[0] );
3147 GEOSGeometry **lineArray =
new GEOSGeometry*[resultLineParts.size()];
3148 for (
int i = 0; i < resultLineParts.size(); ++i )
3150 lineArray[i] = resultLineParts[i];
3154 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
3155 delete [] lineArray;
3158 result.reset( GEOSLineMerge_r( geosinit()->ctxt, multiLineGeom.get() ) );
3162 if ( GEOSGeomTypeId_r( geosinit()->ctxt, result.get() ) != GEOS_LINESTRING )
3170 geos::unique_ptr QgsGeos::reshapePolygon(
const GEOSGeometry *polygon,
const GEOSGeometry *reshapeLineGeos,
double precision )
3173 int nIntersections = 0;
3174 int lastIntersectingRing = -2;
3175 const GEOSGeometry *lastIntersectingGeom =
nullptr;
3177 int nRings = GEOSGetNumInteriorRings_r( geosinit()->ctxt, polygon );
3182 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( geosinit()->ctxt, polygon );
3183 if ( GEOSIntersects_r( geosinit()->ctxt, outerRing, reshapeLineGeos ) == 1 )
3186 lastIntersectingRing = -1;
3187 lastIntersectingGeom = outerRing;
3191 const GEOSGeometry **innerRings =
new const GEOSGeometry*[nRings];
3195 for (
int i = 0; i < nRings; ++i )
3197 innerRings[i] = GEOSGetInteriorRingN_r( geosinit()->ctxt, polygon, i );
3198 if ( GEOSIntersects_r( geosinit()->ctxt, innerRings[i], reshapeLineGeos ) == 1 )
3201 lastIntersectingRing = i;
3202 lastIntersectingGeom = innerRings[i];
3206 catch ( GEOSException & )
3211 if ( nIntersections != 1 )
3213 delete [] innerRings;
3219 if ( !reshapeResult )
3221 delete [] innerRings;
3226 GEOSGeometry *newRing =
nullptr;
3227 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, reshapeResult.get() );
3228 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( geosinit()->ctxt, reshapeSequence );
3230 reshapeResult.reset();
3232 newRing = GEOSGeom_createLinearRing_r( geosinit()->ctxt, newCoordSequence );
3235 delete [] innerRings;
3239 GEOSGeometry *newOuterRing =
nullptr;
3240 if ( lastIntersectingRing == -1 )
3241 newOuterRing = newRing;
3243 newOuterRing = GEOSGeom_clone_r( geosinit()->ctxt, outerRing );
3246 QVector<GEOSGeometry *> ringList;
3249 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( geosinit()->ctxt, GEOSGeom_clone_r( geosinit()->ctxt, newOuterRing ),
nullptr, 0 );
3250 if ( outerRingPoly )
3252 ringList.reserve( nRings );
3253 GEOSGeometry *currentRing =
nullptr;
3254 for (
int i = 0; i < nRings; ++i )
3256 if ( lastIntersectingRing == i )
3257 currentRing = newRing;
3259 currentRing = GEOSGeom_clone_r( geosinit()->ctxt, innerRings[i] );
3262 if ( GEOSContains_r( geosinit()->ctxt, outerRingPoly, currentRing ) == 1 )
3263 ringList.push_back( currentRing );
3265 GEOSGeom_destroy_r( geosinit()->ctxt, currentRing );
3268 GEOSGeom_destroy_r( geosinit()->ctxt, outerRingPoly );
3271 GEOSGeometry **newInnerRings =
new GEOSGeometry*[ringList.size()];
3272 for (
int i = 0; i < ringList.size(); ++i )
3273 newInnerRings[i] = ringList.at( i );
3275 delete [] innerRings;
3277 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( geosinit()->ctxt, newOuterRing, newInnerRings, ringList.size() ) );
3278 delete[] newInnerRings;
3280 return reshapedPolygon;
3283 int QgsGeos::lineContainedInLine(
const GEOSGeometry *line1,
const GEOSGeometry *line2 )
3285 if ( !line1 || !line2 )
3290 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
3296 geos::unique_ptr intersectionGeom( GEOSIntersection_r( geosinit()->ctxt, bufferGeom.get(), line1 ) );
3299 double intersectGeomLength;
3302 GEOSLength_r( geosinit()->ctxt, intersectionGeom.get(), &intersectGeomLength );
3303 GEOSLength_r( geosinit()->ctxt, line1, &line1Length );
3305 double intersectRatio = line1Length / intersectGeomLength;
3306 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
3312 int QgsGeos::pointContainedInLine(
const GEOSGeometry *point,
const GEOSGeometry *line )
3314 if ( !point || !line )
3317 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
3319 geos::unique_ptr lineBuffer( GEOSBuffer_r( geosinit()->ctxt, line, bufferDistance, 8 ) );
3323 bool contained =
false;
3324 if ( GEOSContains_r( geosinit()->ctxt, lineBuffer.get(), point ) == 1 )
3330 int QgsGeos::geomDigits(
const GEOSGeometry *geom )
3336 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( geosinit()->ctxt, bbox.get() );
3340 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, bBoxRing );
3342 if ( !bBoxCoordSeq )
3345 unsigned int nCoords = 0;
3346 if ( !GEOSCoordSeq_getSize_r( geosinit()->ctxt, bBoxCoordSeq, &nCoords ) )
3350 for (
unsigned int i = 0; i < nCoords - 1; ++i )
3353 GEOSCoordSeq_getX_r( geosinit()->ctxt, bBoxCoordSeq, i, &t );
3356 digits = std::ceil( std::log10( std::fabs( t ) ) );
3357 if ( digits > maxDigits )
3360 GEOSCoordSeq_getY_r( geosinit()->ctxt, bBoxCoordSeq, i, &t );
3361 digits = std::ceil( std::log10( std::fabs( t ) ) );
3362 if ( digits > maxDigits )
3371 return geosinit()->ctxt;