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 qWarning(
"GEOS exception: %s", buffer );
61 QString message = QString::fromUtf8( buffer );
71 throw GEOSException( message );
79 throw GEOSException( message );
84 static void printGEOSNotice(
const char *fmt, ... )
86 #if defined(QGISDEBUG)
91 vsnprintf( buffer,
sizeof buffer, fmt, ap );
94 QgsDebugMsg( QStringLiteral(
"GEOS notice: %1" ).arg( QString::fromUtf8( buffer ) ) );
103 GEOSContextHandle_t ctxt;
107 ctxt = initGEOS_r( printGEOSNotice, throwGEOSException );
112 finishGEOS_r( ctxt );
115 GEOSInit(
const GEOSInit &rh ) =
delete;
116 GEOSInit &operator=(
const GEOSInit &rh ) =
delete;
123 GEOSGeom_destroy_r( geosinit()->ctxt, geom );
128 GEOSPreparedGeom_destroy_r( geosinit()->ctxt, geom );
133 GEOSBufferParams_destroy_r( geosinit()->ctxt, params );
138 GEOSCoordSeq_destroy_r( geosinit()->ctxt, sequence );
187 std::unique_ptr< QgsAbstractGeometry > geom =
fromGeos( newPart );
194 mGeosPrepared.reset();
200 mGeosPrepared.reset();
203 mGeosPrepared.reset( GEOSPrepare_r( geosinit()->ctxt, mGeos.get() ) );
207 void QgsGeos::cacheGeos()
const
219 return overlay( geom, OverlayIntersection, errorMsg ).release();
224 return overlay( geom, OverlayDifference, errorMsg ).release();
239 catch ( GEOSException &e )
243 *errorMsg = e.what();
254 int partType = GEOSGeomTypeId_r( geosinit()->ctxt, currentPart );
257 if ( partType == GEOS_POINT )
268 if ( partType == GEOS_MULTILINESTRING || partType == GEOS_MULTIPOLYGON || partType == GEOS_GEOMETRYCOLLECTION )
270 int partCount = GEOSGetNumGeometries_r( geosinit()->ctxt, currentPart );
271 for (
int i = 0; i < partCount; ++i )
273 subdivideRecursive( GEOSGetGeometryN_r( geosinit()->ctxt, currentPart, i ), maxNodes, depth, parts, clipRect );
284 int vertexCount = GEOSGetNumCoordinates_r( geosinit()->ctxt, currentPart );
285 if ( vertexCount == 0 )
289 else if ( vertexCount < maxNodes )
296 double width = clipRect.
width();
297 double height = clipRect.
height();
300 if ( width > height )
313 halfClipRect1.
setYMinimum( halfClipRect1.
yMinimum() - std::numeric_limits<double>::epsilon() );
314 halfClipRect2.
setYMinimum( halfClipRect2.
yMinimum() - std::numeric_limits<double>::epsilon() );
315 halfClipRect1.
setYMaximum( halfClipRect1.
yMaximum() + std::numeric_limits<double>::epsilon() );
316 halfClipRect2.
setYMaximum( halfClipRect2.
yMaximum() + std::numeric_limits<double>::epsilon() );
320 halfClipRect1.
setXMinimum( halfClipRect1.
xMinimum() - std::numeric_limits<double>::epsilon() );
321 halfClipRect2.
setXMinimum( halfClipRect2.
xMinimum() - std::numeric_limits<double>::epsilon() );
322 halfClipRect1.
setXMaximum( halfClipRect1.
xMaximum() + std::numeric_limits<double>::epsilon() );
323 halfClipRect2.
setXMaximum( halfClipRect2.
xMaximum() + std::numeric_limits<double>::epsilon() );
333 subdivideRecursive( clipPart1.get(), maxNodes, depth, parts, halfClipRect1 );
337 subdivideRecursive( clipPart2.get(), maxNodes, depth, parts, halfClipRect2 );
349 maxNodes = std::max( maxNodes, 8 );
358 return std::move( parts );
363 return overlay( geom, OverlayUnion, errorMsg ).release();
368 QVector< GEOSGeometry * > geosGeometries;
369 geosGeometries.reserve( geomList.size() );
375 geosGeometries <<
asGeos( g, mPrecision ).release();
381 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
382 geomUnion.reset( GEOSUnaryUnion_r( geosinit()->ctxt, geomCollection.get() ) );
386 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
387 return result.release();
392 QVector< GEOSGeometry * > geosGeometries;
393 geosGeometries.reserve( geomList.size() );
399 geosGeometries <<
asGeos( g.constGet(), mPrecision ).release();
405 geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
406 geomUnion.reset( GEOSUnaryUnion_r( geosinit()->ctxt, geomCollection.get() ) );
410 std::unique_ptr< QgsAbstractGeometry > result =
fromGeos( geomUnion.get() );
411 return result.release();
416 return overlay( geom, OverlaySymDifference, errorMsg ).release();
428 if ( !otherGeosGeom )
435 GEOSDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &
distance );
451 if ( !otherGeosGeom )
458 GEOSHausdorffDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &
distance );
474 if ( !otherGeosGeom )
481 GEOSHausdorffDistanceDensify_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), densifyFraction, &
distance );
490 return relation( geom, RelationIntersects, errorMsg );
495 return relation( geom, RelationTouches, errorMsg );
500 return relation( geom, RelationCrosses, errorMsg );
505 return relation( geom, RelationWithin, errorMsg );
510 return relation( geom, RelationOverlaps, errorMsg );
515 return relation( geom, RelationContains, errorMsg );
520 return relation( geom, RelationDisjoint, errorMsg );
539 char *r = GEOSRelate_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() );
542 result = QString( r );
543 GEOSFree_r( geosinit()->ctxt, r );
546 catch ( GEOSException &e )
550 *errorMsg = e.what();
559 if ( !mGeos || !geom )
573 result = ( GEOSRelatePattern_r( geosinit()->ctxt, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
575 catch ( GEOSException &e )
579 *errorMsg = e.what();
596 if ( GEOSArea_r( geosinit()->ctxt, mGeos.get(), &
area ) != 1 )
612 if ( GEOSLength_r( geosinit()->ctxt, mGeos.get(), &
length ) != 1 )
620 QVector<QgsGeometry> &newGeometries,
623 QString *errorMsg )
const
638 if ( !GEOSisValid_r( geosinit()->ctxt, mGeos.get() ) )
646 newGeometries.clear();
653 splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
657 splitLineGeos = createGeosPointXY( splitLine.
xAt( 0 ), splitLine.
yAt( 0 ),
false, 0,
false, 0, 2, mPrecision );
664 if ( !GEOSisValid_r( geosinit()->ctxt, splitLineGeos.get() ) || !GEOSisSimple_r( geosinit()->ctxt, splitLineGeos.get() ) )
672 if ( !topologicalTestPointsSplit( splitLineGeos.get(), topologyTestPoints ) )
681 returnCode = splitLinearGeometry( splitLineGeos.get(), newGeometries );
685 returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries );
699 bool QgsGeos::topologicalTestPointsSplit(
const GEOSGeometry *splitLine,
QgsPointSequence &testPoints, QString *errorMsg )
const
713 geos::unique_ptr intersectionGeom( GEOSIntersection_r( geosinit()->ctxt, mGeos.get(), splitLine ) );
714 if ( !intersectionGeom )
718 int nIntersectGeoms = 1;
719 if ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectionGeom.get() ) == GEOS_LINESTRING
720 || GEOSGeomTypeId_r( geosinit()->ctxt, intersectionGeom.get() ) == GEOS_POINT )
724 nIntersectGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, intersectionGeom.get() );
726 for (
int i = 0; i < nIntersectGeoms; ++i )
728 const GEOSGeometry *currentIntersectGeom =
nullptr;
730 currentIntersectGeom = intersectionGeom.get();
732 currentIntersectGeom = GEOSGetGeometryN_r( geosinit()->ctxt, intersectionGeom.get(), i );
734 const GEOSCoordSequence *lineSequence = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, currentIntersectGeom );
735 unsigned int sequenceSize = 0;
737 if ( GEOSCoordSeq_getSize_r( geosinit()->ctxt, lineSequence, &sequenceSize ) != 0 )
739 for (
unsigned int i = 0; i < sequenceSize; ++i )
741 if ( GEOSCoordSeq_getX_r( geosinit()->ctxt, lineSequence, i, &x ) != 0 )
743 if ( GEOSCoordSeq_getY_r( geosinit()->ctxt, lineSequence, i, &y ) != 0 )
745 testPoints.push_back(
QgsPoint( x, y ) );
759 int type = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
761 std::unique_ptr< QgsMultiCurve > multiCurve;
762 if ( type == GEOS_MULTILINESTRING )
764 multiCurve.reset( qgsgeometry_cast<QgsMultiCurve *>(
mGeometry->
clone() ) );
766 else if ( type == GEOS_LINESTRING )
782 std::unique_ptr< QgsAbstractGeometry > splitGeom(
fromGeos( GEOSsplitPoint ) );
783 QgsPoint *splitPoint = qgsgeometry_cast<QgsPoint *>( splitGeom.get() );
792 for (
int i = 0; i < multiCurve->numGeometries(); ++i )
794 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( multiCurve->geometryN( i ) );
801 for (
int j = 1; j < ( nVertices - 1 ); ++j )
805 if ( currentPoint == *splitPoint )
817 return asGeos( &lines, mPrecision );
829 if ( !GEOSIntersects_r( geosinit()->ctxt, splitLine, mGeos.get() ) )
833 int linearIntersect = GEOSRelatePattern_r( geosinit()->ctxt, mGeos.get(), splitLine,
"1********" );
834 if ( linearIntersect > 0 )
837 int splitGeomType = GEOSGeomTypeId_r( geosinit()->ctxt, splitLine );
840 if ( splitGeomType == GEOS_POINT )
842 splitGeom = linePointDifference( splitLine );
846 splitGeom.reset( GEOSDifference_r( geosinit()->ctxt, mGeos.get(), splitLine ) );
848 QVector<GEOSGeometry *> lineGeoms;
850 int splitType = GEOSGeomTypeId_r( geosinit()->ctxt, splitGeom.get() );
851 if ( splitType == GEOS_MULTILINESTRING )
853 int nGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, splitGeom.get() );
854 lineGeoms.reserve( nGeoms );
855 for (
int i = 0; i < nGeoms; ++i )
856 lineGeoms << GEOSGeom_clone_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt, splitGeom.get(), i ) );
861 lineGeoms << GEOSGeom_clone_r( geosinit()->ctxt, splitGeom.get() );
864 mergeGeometriesMultiTypeSplit( lineGeoms );
866 for (
int i = 0; i < lineGeoms.size(); ++i )
869 GEOSGeom_destroy_r( geosinit()->ctxt, lineGeoms[i] );
885 if ( !mGeosPrepared )
889 if ( !GEOSPreparedIntersects_r( geosinit()->ctxt, mGeosPrepared.get(), splitLine ) )
894 if ( !nodedGeometry )
897 const GEOSGeometry *noded = nodedGeometry.get();
898 geos::unique_ptr polygons( GEOSPolygonize_r( geosinit()->ctxt, &noded, 1 ) );
899 if ( !polygons || numberOfGeometries( polygons.get() ) == 0 )
906 QVector<GEOSGeometry *> testedGeometries;
911 for (
int i = 0; i < numberOfGeometries( polygons.get() ); i++ )
913 const GEOSGeometry *polygon = GEOSGetGeometryN_r( geosinit()->ctxt, polygons.get(), i );
917 testedGeometries << GEOSGeom_clone_r( geosinit()->ctxt, polygon );
920 int nGeometriesThis = numberOfGeometries( mGeos.get() );
921 if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
924 for (
int i = 0; i < testedGeometries.size(); ++i )
926 GEOSGeom_destroy_r( geosinit()->ctxt, testedGeometries[i] );
935 mergeGeometriesMultiTypeSplit( testedGeometries );
938 for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( geosinit()->ctxt, testedGeometries[i] ); ++i )
941 if ( i < testedGeometries.size() )
943 for ( i = 0; i < testedGeometries.size(); ++i )
944 GEOSGeom_destroy_r( geosinit()->ctxt, testedGeometries[i] );
949 for ( i = 0; i < testedGeometries.size(); ++i )
952 GEOSGeom_destroy_r( geosinit()->ctxt, testedGeometries[i] );
958 geos::unique_ptr QgsGeos::nodeGeometries(
const GEOSGeometry *splitLine,
const GEOSGeometry *geom )
960 if ( !splitLine || !geom )
964 if ( GEOSGeomTypeId_r( geosinit()->ctxt, geom ) == GEOS_POLYGON || GEOSGeomTypeId_r( geosinit()->ctxt, geom ) == GEOS_MULTIPOLYGON )
965 geometryBoundary.reset( GEOSBoundary_r( geosinit()->ctxt, geom ) );
967 geometryBoundary.reset( GEOSGeom_clone_r( geosinit()->ctxt, geom ) );
969 geos::unique_ptr splitLineClone( GEOSGeom_clone_r( geosinit()->ctxt, splitLine ) );
970 geos::unique_ptr unionGeometry( GEOSUnion_r( geosinit()->ctxt, splitLineClone.get(), geometryBoundary.get() ) );
972 return unionGeometry;
975 int QgsGeos::mergeGeometriesMultiTypeSplit( QVector<GEOSGeometry *> &splitResult )
const
981 int type = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
982 if ( type != GEOS_GEOMETRYCOLLECTION &&
983 type != GEOS_MULTILINESTRING &&
984 type != GEOS_MULTIPOLYGON &&
985 type != GEOS_MULTIPOINT )
988 QVector<GEOSGeometry *> copyList = splitResult;
992 QVector<GEOSGeometry *> unionGeom;
994 for (
int i = 0; i < copyList.size(); ++i )
998 for (
int j = 0; j < GEOSGetNumGeometries_r( geosinit()->ctxt, mGeos.get() ); j++ )
1000 if ( GEOSEquals_r( geosinit()->ctxt, copyList[i], GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), j ) ) )
1009 unionGeom << copyList[i];
1013 QVector<GEOSGeometry *> geomVector;
1014 geomVector << copyList[i];
1016 if ( type == GEOS_MULTILINESTRING )
1017 splitResult << createGeosCollection( GEOS_MULTILINESTRING, geomVector ).release();
1018 else if ( type == GEOS_MULTIPOLYGON )
1019 splitResult << createGeosCollection( GEOS_MULTIPOLYGON, geomVector ).release();
1021 GEOSGeom_destroy_r( geosinit()->ctxt, copyList[i] );
1026 if ( !unionGeom.isEmpty() )
1028 if ( type == GEOS_MULTILINESTRING )
1029 splitResult << createGeosCollection( GEOS_MULTILINESTRING, unionGeom ).release();
1030 else if ( type == GEOS_MULTIPOLYGON )
1031 splitResult << createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ).release();
1041 geos::unique_ptr QgsGeos::createGeosCollection(
int typeId,
const QVector<GEOSGeometry *> &geoms )
1043 int nNullGeoms = geoms.count(
nullptr );
1044 int nNotNullGeoms = geoms.size() - nNullGeoms;
1046 GEOSGeometry **geomarr =
new GEOSGeometry*[ nNotNullGeoms ];
1053 QVector<GEOSGeometry *>::const_iterator geomIt = geoms.constBegin();
1054 for ( ; geomIt != geoms.constEnd(); ++geomIt )
1058 geomarr[i] = *geomIt;
1066 geom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, typeId, geomarr, nNotNullGeoms ) );
1068 catch ( GEOSException & )
1084 int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit()->ctxt,
geos );
1085 int nDims = GEOSGeom_getDimensions_r( geosinit()->ctxt,
geos );
1086 bool hasZ = ( nCoordDims == 3 );
1087 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1089 switch ( GEOSGeomTypeId_r( geosinit()->ctxt,
geos ) )
1093 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt,
geos );
1094 unsigned int nPoints = 0;
1095 GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1096 return nPoints > 0 ? std::unique_ptr<QgsAbstractGeometry>(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() ) : qgis::make_unique< QgsPoint >();
1098 case GEOS_LINESTRING:
1100 return sequenceToLinestring(
geos, hasZ, hasM );
1106 case GEOS_MULTIPOINT:
1108 std::unique_ptr< QgsMultiPoint > multiPoint(
new QgsMultiPoint() );
1109 int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt,
geos );
1110 multiPoint->reserve( nParts );
1111 for (
int i = 0; i < nParts; ++i )
1113 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt,
geos, i ) );
1116 unsigned int nPoints = 0;
1117 GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1119 multiPoint->addGeometry(
coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1122 return std::move( multiPoint );
1124 case GEOS_MULTILINESTRING:
1127 int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt,
geos );
1128 multiLineString->reserve( nParts );
1129 for (
int i = 0; i < nParts; ++i )
1131 std::unique_ptr< QgsLineString >line( sequenceToLinestring( GEOSGetGeometryN_r( geosinit()->ctxt,
geos, i ), hasZ, hasM ) );
1134 multiLineString->addGeometry( line.release() );
1137 return std::move( multiLineString );
1139 case GEOS_MULTIPOLYGON:
1141 std::unique_ptr< QgsMultiPolygon > multiPolygon(
new QgsMultiPolygon() );
1143 int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt,
geos );
1144 multiPolygon->reserve( nParts );
1145 for (
int i = 0; i < nParts; ++i )
1147 std::unique_ptr< QgsPolygon > poly =
fromGeosPolygon( GEOSGetGeometryN_r( geosinit()->ctxt,
geos, i ) );
1150 multiPolygon->addGeometry( poly.release() );
1153 return std::move( multiPolygon );
1155 case GEOS_GEOMETRYCOLLECTION:
1158 int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt,
geos );
1159 geomCollection->reserve( nParts );
1160 for (
int i = 0; i < nParts; ++i )
1162 std::unique_ptr< QgsAbstractGeometry > geom(
fromGeos( GEOSGetGeometryN_r( geosinit()->ctxt,
geos, i ) ) );
1165 geomCollection->addGeometry( geom.release() );
1168 return std::move( geomCollection );
1176 if ( GEOSGeomTypeId_r( geosinit()->ctxt,
geos ) != GEOS_POLYGON )
1181 int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit()->ctxt,
geos );
1182 int nDims = GEOSGeom_getDimensions_r( geosinit()->ctxt,
geos );
1183 bool hasZ = ( nCoordDims == 3 );
1184 bool hasM = ( ( nDims - nCoordDims ) == 1 );
1186 std::unique_ptr< QgsPolygon > polygon(
new QgsPolygon() );
1188 const GEOSGeometry *ring = GEOSGetExteriorRing_r( geosinit()->ctxt,
geos );
1191 polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1194 QVector<QgsCurve *> interiorRings;
1195 const int ringCount = GEOSGetNumInteriorRings_r( geosinit()->ctxt,
geos );
1196 interiorRings.reserve( ringCount );
1197 for (
int i = 0; i < ringCount; ++i )
1199 ring = GEOSGetInteriorRingN_r( geosinit()->ctxt,
geos, i );
1202 interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1205 polygon->setInteriorRings( interiorRings );
1210 std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring(
const GEOSGeometry *
geos,
bool hasZ,
bool hasM )
1212 const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt,
geos );
1213 unsigned int nPoints;
1214 GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1215 QVector< double > xOut( nPoints );
1216 QVector< double > yOut( nPoints );
1217 QVector< double > zOut;
1219 zOut.resize( nPoints );
1220 QVector< double > mOut;
1222 mOut.resize( nPoints );
1223 double *x = xOut.data();
1224 double *y = yOut.data();
1225 double *z = zOut.data();
1226 double *m = mOut.data();
1227 for (
unsigned int i = 0; i < nPoints; ++i )
1229 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1231 GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, cs, i, x++, y++, z++ );
1233 GEOSCoordSeq_getXY_r( geosinit()->ctxt, cs, i, x++, y++ );
1235 GEOSCoordSeq_getX_r( geosinit()->ctxt, cs, i, x++ );
1236 GEOSCoordSeq_getY_r( geosinit()->ctxt, cs, i, y++ );
1239 GEOSCoordSeq_getZ_r( geosinit()->ctxt, cs, i, z++ );
1244 GEOSCoordSeq_getOrdinate_r( geosinit()->ctxt, cs, i, 3, m++ );
1247 std::unique_ptr< QgsLineString > line(
new QgsLineString( xOut, yOut, zOut, mOut ) );
1251 int QgsGeos::numberOfGeometries( GEOSGeometry *g )
1256 int geometryType = GEOSGeomTypeId_r( geosinit()->ctxt, g );
1257 if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING
1258 || geometryType == GEOS_POLYGON )
1262 return GEOSGetNumGeometries_r( geosinit()->ctxt, g );
1275 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1277 GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, cs, i, &x, &y, &z );
1279 GEOSCoordSeq_getXY_r( geosinit()->ctxt, cs, i, &x, &y );
1281 GEOSCoordSeq_getX_r( geosinit()->ctxt, cs, i, &x );
1282 GEOSCoordSeq_getY_r( geosinit()->ctxt, cs, i, &y );
1285 GEOSCoordSeq_getZ_r( geosinit()->ctxt, cs, i, &z );
1290 GEOSCoordSeq_getOrdinate_r( geosinit()->ctxt, cs, i, 3, &m );
1326 int geosType = GEOS_GEOMETRYCOLLECTION;
1333 geosType = GEOS_MULTIPOINT;
1337 geosType = GEOS_MULTILINESTRING;
1341 geosType = GEOS_MULTIPOLYGON;
1357 QVector< GEOSGeometry * > geomVector(
c->numGeometries() );
1358 for (
int i = 0; i <
c->numGeometries(); ++i )
1362 return createGeosCollection( geosType, geomVector );
1369 return createGeosPoint(
static_cast<const QgsPoint *
>( geom ), coordDims,
precision );
1389 std::unique_ptr<QgsAbstractGeometry> QgsGeos::overlay(
const QgsAbstractGeometry *geom, Overlay op, QString *errorMsg )
const
1391 if ( !mGeos || !geom )
1407 case OverlayIntersection:
1408 opGeom.reset( GEOSIntersection_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1410 case OverlayDifference:
1411 opGeom.reset( GEOSDifference_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1415 geos::unique_ptr unionGeometry( GEOSUnion_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1417 if ( unionGeometry && GEOSGeomTypeId_r( geosinit()->ctxt, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1419 geos::unique_ptr mergedLines( GEOSLineMerge_r( geosinit()->ctxt, unionGeometry.get() ) );
1422 unionGeometry = std::move( mergedLines );
1426 opGeom = std::move( unionGeometry );
1429 case OverlaySymDifference:
1430 opGeom.reset( GEOSSymDifference_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1437 catch ( GEOSException &e )
1441 *errorMsg = e.what();
1447 bool QgsGeos::relation(
const QgsAbstractGeometry *geom, Relation r, QString *errorMsg )
const
1449 if ( !mGeos || !geom )
1460 bool result =
false;
1463 if ( mGeosPrepared )
1467 case RelationIntersects:
1468 result = ( GEOSPreparedIntersects_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1470 case RelationTouches:
1471 result = ( GEOSPreparedTouches_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1473 case RelationCrosses:
1474 result = ( GEOSPreparedCrosses_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1476 case RelationWithin:
1477 result = ( GEOSPreparedWithin_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1479 case RelationContains:
1480 result = ( GEOSPreparedContains_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1482 case RelationDisjoint:
1483 result = ( GEOSPreparedDisjoint_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1485 case RelationOverlaps:
1486 result = ( GEOSPreparedOverlaps_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1496 case RelationIntersects:
1497 result = ( GEOSIntersects_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1499 case RelationTouches:
1500 result = ( GEOSTouches_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1502 case RelationCrosses:
1503 result = ( GEOSCrosses_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1505 case RelationWithin:
1506 result = ( GEOSWithin_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1508 case RelationContains:
1509 result = ( GEOSContains_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1511 case RelationDisjoint:
1512 result = ( GEOSDisjoint_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1514 case RelationOverlaps:
1515 result = ( GEOSOverlaps_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1521 catch ( GEOSException &e )
1525 *errorMsg = e.what();
1543 geos.reset( GEOSBuffer_r( geosinit()->ctxt, mGeos.get(),
distance, segments ) );
1559 geos.reset( GEOSBufferWithStyle_r( geosinit()->ctxt, mGeos.get(),
distance, segments, endCapStyle, joinStyle, miterLimit ) );
1574 geos.reset( GEOSTopologyPreserveSimplify_r( geosinit()->ctxt, mGeos.get(), tolerance ) );
1589 geos.reset( GEOSInterpolate_r( geosinit()->ctxt, mGeos.get(),
distance ) );
1608 geos.reset( GEOSGetCentroid_r( geosinit()->ctxt, mGeos.get() ) );
1613 GEOSGeomGetX_r( geosinit()->ctxt,
geos.get(), &x );
1614 GEOSGeomGetY_r( geosinit()->ctxt,
geos.get(), &y );
1630 geos.reset( GEOSEnvelope_r( geosinit()->ctxt, mGeos.get() ) );
1649 geos.reset( GEOSPointOnSurface_r( geosinit()->ctxt, mGeos.get() ) );
1651 if ( !
geos || GEOSisEmpty_r( geosinit()->ctxt,
geos.get() ) != 0 )
1656 GEOSGeomGetX_r( geosinit()->ctxt,
geos.get(), &x );
1657 GEOSGeomGetY_r( geosinit()->ctxt,
geos.get(), &y );
1673 geos::unique_ptr cHull( GEOSConvexHull_r( geosinit()->ctxt, mGeos.get() ) );
1674 std::unique_ptr< QgsAbstractGeometry > cHullGeom =
fromGeos( cHull.get() );
1675 return cHullGeom.release();
1689 GEOSGeometry *g1 =
nullptr;
1691 char res = GEOSisValidDetail_r( geosinit()->ctxt, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
1692 const bool invalid = res != 1;
1697 error = QString( r );
1698 GEOSFree_r( geosinit()->ctxt, r );
1701 if ( invalid && errorMsg )
1705 if ( translatedErrors.empty() )
1708 translatedErrors.insert( QStringLiteral(
"topology validation error" ), QObject::tr(
"Topology validation error",
"GEOS Error" ) );
1709 translatedErrors.insert( QStringLiteral(
"repeated point" ), QObject::tr(
"Repeated point",
"GEOS Error" ) );
1710 translatedErrors.insert( QStringLiteral(
"hole lies outside shell" ), QObject::tr(
"Hole lies outside shell",
"GEOS Error" ) );
1711 translatedErrors.insert( QStringLiteral(
"holes are nested" ), QObject::tr(
"Holes are nested",
"GEOS Error" ) );
1712 translatedErrors.insert( QStringLiteral(
"interior is disconnected" ), QObject::tr(
"Interior is disconnected",
"GEOS Error" ) );
1713 translatedErrors.insert( QStringLiteral(
"self-intersection" ), QObject::tr(
"Self-intersection",
"GEOS Error" ) );
1714 translatedErrors.insert( QStringLiteral(
"ring self-intersection" ), QObject::tr(
"Ring self-intersection",
"GEOS Error" ) );
1715 translatedErrors.insert( QStringLiteral(
"nested shells" ), QObject::tr(
"Nested shells",
"GEOS Error" ) );
1716 translatedErrors.insert( QStringLiteral(
"duplicate rings" ), QObject::tr(
"Duplicate rings",
"GEOS Error" ) );
1717 translatedErrors.insert( QStringLiteral(
"too few points in geometry component" ), QObject::tr(
"Too few points in geometry component",
"GEOS Error" ) );
1718 translatedErrors.insert( QStringLiteral(
"invalid coordinate" ), QObject::tr(
"Invalid coordinate",
"GEOS Error" ) );
1719 translatedErrors.insert( QStringLiteral(
"ring is not closed" ), QObject::tr(
"Ring is not closed",
"GEOS Error" ) );
1722 *errorMsg = translatedErrors.value( error.toLower(), error );
1724 if ( g1 && errorLoc )
1730 GEOSGeom_destroy_r( geosinit()->ctxt, g1 );
1740 if ( !mGeos || !geom )
1752 bool equal = GEOSEquals_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() );
1767 return GEOSisEmpty_r( geosinit()->ctxt, mGeos.get() );
1781 return GEOSisSimple_r( geosinit()->ctxt, mGeos.get() );
1786 GEOSCoordSequence *QgsGeos::createCoordinateSequence(
const QgsCurve *curve,
double precision,
bool forceClose )
1788 std::unique_ptr< QgsLineString > segmentized;
1789 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( curve );
1794 line = segmentized.get();
1802 bool hasZ = line->
is3D();
1816 int numOutPoints = numPoints;
1817 if ( forceClose && ( line->
pointN( 0 ) != line->
pointN( numPoints - 1 ) ) )
1822 GEOSCoordSequence *coordSeq =
nullptr;
1825 coordSeq = GEOSCoordSeq_create_r( geosinit()->ctxt, numOutPoints, coordDims );
1828 QgsDebugMsg( QStringLiteral(
"GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions" ).arg( numPoints ).arg( coordDims ) );
1832 const double *xData = line->
xData();
1833 const double *yData = line->
yData();
1834 const double *zData = hasZ ? line->
zData() :
nullptr;
1835 const double *mData = hasM ? line->
mData() :
nullptr;
1839 for (
int i = 0; i < numOutPoints; ++i )
1841 if ( i >= numPoints )
1844 xData = line->
xData();
1845 yData = line->
yData();
1846 zData = hasZ ? line->
zData() :
nullptr;
1847 mData = hasM ? line->
mData() :
nullptr;
1849 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1859 GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, i, std::round( *xData++ /
precision ) *
precision );
1860 GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, i, std::round( *yData++ /
precision ) *
precision );
1863 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 2, std::round( *zData++ /
precision ) *
precision );
1868 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 3, line->
mAt( *mData++ ) );
1874 for (
int i = 0; i < numOutPoints; ++i )
1876 if ( i >= numPoints )
1879 xData = line->
xData();
1880 yData = line->
yData();
1881 zData = hasZ ? line->
zData() :
nullptr;
1882 mData = hasM ? line->
mData() :
nullptr;
1884 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1887 GEOSCoordSeq_setXYZ_r( geosinit()->ctxt, coordSeq, i, *xData++, *yData++, *zData++ );
1891 GEOSCoordSeq_setXY_r( geosinit()->ctxt, coordSeq, i, *xData++, *yData++ );
1894 GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, i, *xData++ );
1895 GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, i, *yData++ );
1898 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 2, *zData++ );
1903 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 3, *mData++ );
1915 const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( point );
1922 geos::unique_ptr QgsGeos::createGeosPointXY(
double x,
double y,
bool hasZ,
double z,
bool hasM,
double m,
int coordDims,
double precision )
1930 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1931 if ( coordDims == 2 )
1937 geosPoint.reset( GEOSGeom_createPointFromXY_r( geosinit()->ctxt, x, y ) );
1942 GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( geosinit()->ctxt, 1, coordDims );
1945 QgsDebugMsg( QStringLiteral(
"GEOS Exception: Could not create coordinate sequence for point with %1 dimensions" ).arg( coordDims ) );
1950 GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, 0, std::round( x /
precision ) *
precision );
1951 GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, 0, std::round( y /
precision ) *
precision );
1954 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 2, std::round( z /
precision ) *
precision );
1959 GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, 0, x );
1960 GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, 0, y );
1963 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 2, z );
1966 #if 0 //disabled until geos supports m-coordinates
1969 GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 3, m );
1972 geosPoint.reset( GEOSGeom_createPoint_r( geosinit()->ctxt, coordSeq ) );
1980 const QgsCurve *
c = qgsgeometry_cast<const QgsCurve *>( curve );
1984 GEOSCoordSequence *coordSeq = createCoordinateSequence(
c,
precision );
1991 geosGeom.reset( GEOSGeom_createLineString_r( geosinit()->ctxt, coordSeq ) );
1999 const QgsCurvePolygon *polygon = qgsgeometry_cast<const QgsCurvePolygon *>( poly );
2004 if ( !exteriorRing )
2012 geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( geosinit()->ctxt, createCoordinateSequence( exteriorRing,
precision,
true ) ) );
2015 GEOSGeometry **holes =
nullptr;
2018 holes =
new GEOSGeometry*[ nHoles ];
2021 for (
int i = 0; i < nHoles; ++i )
2024 holes[i] = GEOSGeom_createLinearRing_r( geosinit()->ctxt, createCoordinateSequence( interiorRing,
precision,
true ) );
2026 geosPolygon.reset( GEOSGeom_createPolygon_r( geosinit()->ctxt, exteriorRingGeos.release(), holes, nHoles ) );
2042 offset.reset( GEOSOffsetCurve_r( geosinit()->ctxt, mGeos.get(),
distance, segments, joinStyle, miterLimit ) );
2045 std::unique_ptr< QgsAbstractGeometry > offsetGeom =
fromGeos( offset.get() );
2046 return offsetGeom.release();
2060 GEOSBufferParams_setSingleSided_r( geosinit()->ctxt, bp.get(), 1 );
2061 GEOSBufferParams_setQuadrantSegments_r( geosinit()->ctxt, bp.get(), segments );
2062 GEOSBufferParams_setJoinStyle_r( geosinit()->ctxt, bp.get(), joinStyle );
2063 GEOSBufferParams_setMitreLimit_r( geosinit()->ctxt, bp.get(), miterLimit );
2069 geos.reset( GEOSBufferWithParams_r( geosinit()->ctxt, mGeos.get(), bp.get(),
distance ) );
2089 geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
2092 int numGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, mGeos.get() );
2093 if ( numGeoms == -1 )
2102 bool isMultiGeom =
false;
2103 int geosTypeId = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
2104 if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
2114 reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2118 reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2123 std::unique_ptr< QgsAbstractGeometry > reshapeResult =
fromGeos( reshapedGeometry.get() );
2124 return reshapeResult;
2131 bool reshapeTookPlace =
false;
2134 GEOSGeometry **newGeoms =
new GEOSGeometry*[numGeoms];
2136 for (
int i = 0; i < numGeoms; ++i )
2139 currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2141 currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2143 if ( currentReshapeGeometry )
2145 newGeoms[i] = currentReshapeGeometry.release();
2146 reshapeTookPlace =
true;
2150 newGeoms[i] = GEOSGeom_clone_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ) );
2157 newMultiGeom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
2161 newMultiGeom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
2165 if ( !newMultiGeom )
2171 if ( reshapeTookPlace )
2175 std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom =
fromGeos( newMultiGeom.get() );
2176 return reshapedMultiGeom;
2198 if ( GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() ) != GEOS_MULTILINESTRING )
2204 geos.reset( GEOSLineMerge_r( geosinit()->ctxt, mGeos.get() ) );
2229 ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 0, &nx );
2230 ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 0, &ny );
2232 catch ( GEOSException &e )
2236 *errorMsg = e.what();
2246 if ( !mGeos || other.
isNull() )
2265 ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 0, &nx1 );
2266 ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 0, &ny1 );
2267 ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 1, &nx2 );
2268 ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 1, &ny2 );
2270 catch ( GEOSException &e )
2274 *errorMsg = e.what();
2301 distance = GEOSProject_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() );
2303 catch ( GEOSException &e )
2307 *errorMsg = e.what();
2317 GEOSGeometry **
const lineGeosGeometries =
new GEOSGeometry*[ geometries.size()];
2324 lineGeosGeometries[validLines] = l.release();
2331 geos::unique_ptr result( GEOSPolygonize_r( geosinit()->ctxt, lineGeosGeometries, validLines ) );
2332 for (
int i = 0; i < validLines; ++i )
2334 GEOSGeom_destroy_r( geosinit()->ctxt, lineGeosGeometries[i] );
2336 delete[] lineGeosGeometries;
2339 catch ( GEOSException &e )
2343 *errorMsg = e.what();
2345 for (
int i = 0; i < validLines; ++i )
2347 GEOSGeom_destroy_r( geosinit()->ctxt, lineGeosGeometries[i] );
2349 delete[] lineGeosGeometries;
2364 extentGeosGeom =
asGeos( extent, mPrecision );
2365 if ( !extentGeosGeom )
2374 geos.reset( GEOSVoronoiDiagram_r( geosinit()->ctxt, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
2376 if ( !
geos || GEOSisEmpty_r( geosinit()->ctxt,
geos.get() ) != 0 )
2396 geos.reset( GEOSDelaunayTriangulation_r( geosinit()->ctxt, mGeos.get(), tolerance, edgesOnly ) );
2398 if ( !
geos || GEOSisEmpty_r( geosinit()->ctxt,
geos.get() ) != 0 )
2410 static bool _linestringEndpoints(
const GEOSGeometry *linestring,
double &x1,
double &y1,
double &x2,
double &y2 )
2412 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, linestring );
2416 unsigned int coordSeqSize;
2417 if ( GEOSCoordSeq_getSize_r( geosinit()->ctxt, coordSeq, &coordSeqSize ) == 0 )
2420 if ( coordSeqSize < 2 )
2423 GEOSCoordSeq_getX_r( geosinit()->ctxt, coordSeq, 0, &x1 );
2424 GEOSCoordSeq_getY_r( geosinit()->ctxt, coordSeq, 0, &y1 );
2425 GEOSCoordSeq_getX_r( geosinit()->ctxt, coordSeq, coordSeqSize - 1, &x2 );
2426 GEOSCoordSeq_getY_r( geosinit()->ctxt, coordSeq, coordSeqSize - 1, &y2 );
2432 static geos::unique_ptr _mergeLinestrings(
const GEOSGeometry *line1,
const GEOSGeometry *line2,
const QgsPointXY &intersectionPoint )
2434 double x1, y1, x2, y2;
2435 if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
2438 double rx1, ry1, rx2, ry2;
2439 if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
2442 bool intersectionAtOrigLineEndpoint =
2443 ( intersectionPoint.
x() == x1 && intersectionPoint.
y() == y1 ) !=
2444 ( intersectionPoint.
x() == x2 && intersectionPoint.
y() == y2 );
2445 bool intersectionAtReshapeLineEndpoint =
2446 ( intersectionPoint.
x() == rx1 && intersectionPoint.
y() == ry1 ) ||
2447 ( intersectionPoint.
x() == rx2 && intersectionPoint.
y() == ry2 );
2450 if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
2454 GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
2455 geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2456 geos::unique_ptr res( GEOSLineMerge_r( geosinit()->ctxt, multiGeom.get() ) );
2464 geos::unique_ptr QgsGeos::reshapeLine(
const GEOSGeometry *line,
const GEOSGeometry *reshapeLineGeos,
double precision )
2466 if ( !line || !reshapeLineGeos )
2469 bool atLeastTwoIntersections =
false;
2470 bool oneIntersection =
false;
2476 geos::unique_ptr intersectGeom( GEOSIntersection_r( geosinit()->ctxt, line, reshapeLineGeos ) );
2477 if ( intersectGeom )
2479 atLeastTwoIntersections = ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectGeom.get() ) == GEOS_MULTIPOINT
2480 && GEOSGetNumGeometries_r( geosinit()->ctxt, intersectGeom.get() ) > 1 );
2482 if ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectGeom.get() ) == GEOS_POINT )
2484 const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, intersectGeom.get() );
2486 GEOSCoordSeq_getX_r( geosinit()->ctxt, intersectionCoordSeq, 0, &xi );
2487 GEOSCoordSeq_getY_r( geosinit()->ctxt, intersectionCoordSeq, 0, &yi );
2488 oneIntersection =
true;
2493 catch ( GEOSException & )
2495 atLeastTwoIntersections =
false;
2499 if ( oneIntersection )
2500 return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
2502 if ( !atLeastTwoIntersections )
2506 double x1, y1, x2, y2;
2507 if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
2513 bool isRing =
false;
2514 if ( GEOSGeomTypeId_r( geosinit()->ctxt, line ) == GEOS_LINEARRING
2515 || GEOSEquals_r( geosinit()->ctxt, beginLineVertex.get(), endLineVertex.get() ) == 1 )
2520 if ( !nodedGeometry )
2526 geos::unique_ptr mergedLines( GEOSLineMerge_r( geosinit()->ctxt, nodedGeometry.get() ) );
2532 int numMergedLines = GEOSGetNumGeometries_r( geosinit()->ctxt, mergedLines.get() );
2533 if ( numMergedLines < 2 )
2535 if ( numMergedLines == 1 )
2537 geos::unique_ptr result( GEOSGeom_clone_r( geosinit()->ctxt, reshapeLineGeos ) );
2544 QVector<GEOSGeometry *> resultLineParts;
2545 QVector<GEOSGeometry *> probableParts;
2547 for (
int i = 0; i < numMergedLines; ++i )
2549 const GEOSGeometry *currentGeom =
nullptr;
2551 currentGeom = GEOSGetGeometryN_r( geosinit()->ctxt, mergedLines.get(), i );
2552 const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, currentGeom );
2553 unsigned int currentCoordSeqSize;
2554 GEOSCoordSeq_getSize_r( geosinit()->ctxt, currentCoordSeq, ¤tCoordSeqSize );
2555 if ( currentCoordSeqSize < 2 )
2559 double xBegin, xEnd, yBegin, yEnd;
2560 GEOSCoordSeq_getX_r( geosinit()->ctxt, currentCoordSeq, 0, &xBegin );
2561 GEOSCoordSeq_getY_r( geosinit()->ctxt, currentCoordSeq, 0, &yBegin );
2562 GEOSCoordSeq_getX_r( geosinit()->ctxt, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
2563 GEOSCoordSeq_getY_r( geosinit()->ctxt, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
2568 int nEndpointsOnOriginalLine = 0;
2569 if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
2570 nEndpointsOnOriginalLine += 1;
2572 if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
2573 nEndpointsOnOriginalLine += 1;
2576 int nEndpointsSameAsOriginalLine = 0;
2577 if ( GEOSEquals_r( geosinit()->ctxt, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
2578 || GEOSEquals_r( geosinit()->ctxt, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
2579 nEndpointsSameAsOriginalLine += 1;
2581 if ( GEOSEquals_r( geosinit()->ctxt, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
2582 || GEOSEquals_r( geosinit()->ctxt, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
2583 nEndpointsSameAsOriginalLine += 1;
2586 bool currentGeomOverlapsOriginalGeom =
false;
2587 bool currentGeomOverlapsReshapeLine =
false;
2588 if ( lineContainedInLine( currentGeom, line ) == 1 )
2589 currentGeomOverlapsOriginalGeom =
true;
2591 if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
2592 currentGeomOverlapsReshapeLine =
true;
2595 if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
2597 resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2600 else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
2602 probableParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2604 else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
2606 resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2608 else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
2610 resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2612 else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
2614 resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2619 if ( isRing && !probableParts.isEmpty() )
2622 GEOSGeometry *currentGeom =
nullptr;
2623 double maxLength = -std::numeric_limits<double>::max();
2624 double currentLength = 0;
2625 for (
int i = 0; i < probableParts.size(); ++i )
2627 currentGeom = probableParts.at( i );
2628 GEOSLength_r( geosinit()->ctxt, currentGeom, ¤tLength );
2629 if ( currentLength > maxLength )
2631 maxLength = currentLength;
2632 maxGeom.reset( currentGeom );
2636 GEOSGeom_destroy_r( geosinit()->ctxt, currentGeom );
2639 resultLineParts.push_back( maxGeom.release() );
2643 if ( resultLineParts.empty() )
2646 if ( resultLineParts.size() == 1 )
2648 result.reset( resultLineParts[0] );
2652 GEOSGeometry **lineArray =
new GEOSGeometry*[resultLineParts.size()];
2653 for (
int i = 0; i < resultLineParts.size(); ++i )
2655 lineArray[i] = resultLineParts[i];
2659 geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
2660 delete [] lineArray;
2663 result.reset( GEOSLineMerge_r( geosinit()->ctxt, multiLineGeom.get() ) );
2667 if ( GEOSGeomTypeId_r( geosinit()->ctxt, result.get() ) != GEOS_LINESTRING )
2675 geos::unique_ptr QgsGeos::reshapePolygon(
const GEOSGeometry *polygon,
const GEOSGeometry *reshapeLineGeos,
double precision )
2678 int nIntersections = 0;
2679 int lastIntersectingRing = -2;
2680 const GEOSGeometry *lastIntersectingGeom =
nullptr;
2682 int nRings = GEOSGetNumInteriorRings_r( geosinit()->ctxt, polygon );
2687 const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( geosinit()->ctxt, polygon );
2688 if ( GEOSIntersects_r( geosinit()->ctxt, outerRing, reshapeLineGeos ) == 1 )
2691 lastIntersectingRing = -1;
2692 lastIntersectingGeom = outerRing;
2696 const GEOSGeometry **innerRings =
new const GEOSGeometry*[nRings];
2700 for (
int i = 0; i < nRings; ++i )
2702 innerRings[i] = GEOSGetInteriorRingN_r( geosinit()->ctxt, polygon, i );
2703 if ( GEOSIntersects_r( geosinit()->ctxt, innerRings[i], reshapeLineGeos ) == 1 )
2706 lastIntersectingRing = i;
2707 lastIntersectingGeom = innerRings[i];
2711 catch ( GEOSException & )
2716 if ( nIntersections != 1 )
2718 delete [] innerRings;
2724 if ( !reshapeResult )
2726 delete [] innerRings;
2731 GEOSGeometry *newRing =
nullptr;
2732 const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, reshapeResult.get() );
2733 GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( geosinit()->ctxt, reshapeSequence );
2735 reshapeResult.reset();
2737 newRing = GEOSGeom_createLinearRing_r( geosinit()->ctxt, newCoordSequence );
2740 delete [] innerRings;
2744 GEOSGeometry *newOuterRing =
nullptr;
2745 if ( lastIntersectingRing == -1 )
2746 newOuterRing = newRing;
2748 newOuterRing = GEOSGeom_clone_r( geosinit()->ctxt, outerRing );
2751 QVector<GEOSGeometry *> ringList;
2754 GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( geosinit()->ctxt, GEOSGeom_clone_r( geosinit()->ctxt, newOuterRing ),
nullptr, 0 );
2755 if ( outerRingPoly )
2757 GEOSGeometry *currentRing =
nullptr;
2758 for (
int i = 0; i < nRings; ++i )
2760 if ( lastIntersectingRing == i )
2761 currentRing = newRing;
2763 currentRing = GEOSGeom_clone_r( geosinit()->ctxt, innerRings[i] );
2766 if ( GEOSContains_r( geosinit()->ctxt, outerRingPoly, currentRing ) == 1 )
2767 ringList.push_back( currentRing );
2769 GEOSGeom_destroy_r( geosinit()->ctxt, currentRing );
2772 GEOSGeom_destroy_r( geosinit()->ctxt, outerRingPoly );
2775 GEOSGeometry **newInnerRings =
new GEOSGeometry*[ringList.size()];
2776 for (
int i = 0; i < ringList.size(); ++i )
2777 newInnerRings[i] = ringList.at( i );
2779 delete [] innerRings;
2781 geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( geosinit()->ctxt, newOuterRing, newInnerRings, ringList.size() ) );
2782 delete[] newInnerRings;
2784 return reshapedPolygon;
2787 int QgsGeos::lineContainedInLine(
const GEOSGeometry *line1,
const GEOSGeometry *line2 )
2789 if ( !line1 || !line2 )
2794 double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
2800 geos::unique_ptr intersectionGeom( GEOSIntersection_r( geosinit()->ctxt, bufferGeom.get(), line1 ) );
2803 double intersectGeomLength;
2806 GEOSLength_r( geosinit()->ctxt, intersectionGeom.get(), &intersectGeomLength );
2807 GEOSLength_r( geosinit()->ctxt, line1, &line1Length );
2809 double intersectRatio = line1Length / intersectGeomLength;
2810 if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
2816 int QgsGeos::pointContainedInLine(
const GEOSGeometry *point,
const GEOSGeometry *line )
2818 if ( !point || !line )
2821 double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
2823 geos::unique_ptr lineBuffer( GEOSBuffer_r( geosinit()->ctxt, line, bufferDistance, 8 ) );
2827 bool contained =
false;
2828 if ( GEOSContains_r( geosinit()->ctxt, lineBuffer.get(), point ) == 1 )
2834 int QgsGeos::geomDigits(
const GEOSGeometry *geom )
2840 const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( geosinit()->ctxt, bbox.get() );
2844 const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, bBoxRing );
2846 if ( !bBoxCoordSeq )
2849 unsigned int nCoords = 0;
2850 if ( !GEOSCoordSeq_getSize_r( geosinit()->ctxt, bBoxCoordSeq, &nCoords ) )
2854 for (
unsigned int i = 0; i < nCoords - 1; ++i )
2857 GEOSCoordSeq_getX_r( geosinit()->ctxt, bBoxCoordSeq, i, &t );
2860 digits = std::ceil( std::log10( std::fabs( t ) ) );
2861 if ( digits > maxDigits )
2864 GEOSCoordSeq_getY_r( geosinit()->ctxt, bBoxCoordSeq, i, &t );
2865 digits = std::ceil( std::log10( std::fabs( t ) ) );
2866 if ( digits > maxDigits )
2875 return geosinit()->ctxt;