21 #if ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR<8 )
40 const GEOSGeometry *geom =
nullptr;
41 GEOSGeometry *env =
nullptr;
43 struct Face_t *parent;
46 static Face *newFace(
const GEOSGeometry *g );
47 static void delFace( Face *f );
48 static unsigned int countParens(
const Face *f );
49 static void findFaceHoles( Face **faces,
int nfaces );
51 static Face *newFace(
const GEOSGeometry *g )
57 f->env = GEOSEnvelope_r( handle, f->geom );
58 GEOSArea_r( handle, f->env, &f->envarea );
63 static unsigned int countParens(
const Face *f )
65 unsigned int pcount = 0;
75 static void delFace( Face *f )
78 GEOSGeom_destroy_r( handle, f->env );
83 static int compare_by_envarea(
const void *g1,
const void *g2 )
85 Face *f1 = *( Face ** )g1;
86 Face *f2 = *( Face ** )g2;
87 double n1 = f1->envarea;
88 double n2 = f2->envarea;
90 if ( n1 < n2 )
return 1;
91 if ( n1 > n2 )
return -1;
96 static void findFaceHoles( Face **faces,
int nfaces )
102 qsort( faces, nfaces,
sizeof( Face * ), compare_by_envarea );
103 for (
int i = 0; i < nfaces; ++i )
106 int nholes = GEOSGetNumInteriorRings_r( handle, f->geom );
107 for (
int h = 0; h < nholes; ++h )
109 const GEOSGeometry *hole = GEOSGetInteriorRingN_r( handle, f->geom, h );
110 for (
int j = i + 1; j < nfaces; ++j )
120 if ( GEOSEquals_r( handle, GEOSGetExteriorRing_r( handle, f2->geom ), hole ) )
130 static GEOSGeometry *collectFacesWithEvenAncestors( Face **faces,
int nfaces )
134 unsigned int ngeoms = 0;
135 GEOSGeometry **geoms =
new GEOSGeometry*[nfaces];
136 for (
int i = 0; i < nfaces; ++i )
139 if ( countParens( f ) % 2 )
141 geoms[ngeoms++] = GEOSGeom_clone_r( handle, f->geom );
144 GEOSGeometry *ret = GEOSGeom_createCollection_r( handle, GEOS_MULTIPOLYGON, geoms, ngeoms );
150 static GEOSGeometry *LWGEOM_GEOS_buildArea(
const GEOSGeometry *geom_in, QString &errorMessage )
154 GEOSGeometry *tmp =
nullptr;
155 GEOSGeometry *shp =
nullptr;
156 GEOSGeometry *geos_result =
nullptr;
157 int srid = GEOSGetSRID_r( handle, geom_in );
159 GEOSGeometry
const *vgeoms[1];
163 geos_result = GEOSPolygonize_r( handle, vgeoms, 1 );
165 catch ( GEOSException &e )
167 errorMessage = QStringLiteral(
"GEOSPolygonize(): %1" ).arg( e.what() );
172 #if PARANOIA_LEVEL > 0
173 if ( GEOSGeometryTypeId_r( handle, geos_result ) != COLLECTIONTYPE )
175 GEOSGeom_destroy_r( handle, geos_result );
176 errorMessage =
"Unexpected return from GEOSpolygonize";
181 int ngeoms = GEOSGetNumGeometries_r( handle, geos_result );
186 GEOSSetSRID_r( handle, geos_result, srid );
194 tmp = ( GEOSGeometry * )GEOSGetGeometryN_r( handle, geos_result, 0 );
197 GEOSGeom_destroy_r( handle, geos_result );
200 shp = GEOSGeom_clone_r( handle, tmp );
201 GEOSGeom_destroy_r( handle, geos_result );
202 GEOSSetSRID_r( handle, shp, srid );
235 Face **geoms =
new Face*[ngeoms];
236 for (
int i = 0; i < ngeoms; ++i )
237 geoms[i] = newFace( GEOSGetGeometryN_r( handle, geos_result, i ) );
240 findFaceHoles( geoms, ngeoms );
244 tmp = collectFacesWithEvenAncestors( geoms, ngeoms );
247 for (
int i = 0; i < ngeoms; ++i )
253 GEOSGeom_destroy_r( handle, geos_result );
256 shp = GEOSUnionCascaded_r( handle, tmp );
259 GEOSGeom_destroy_r( handle, tmp );
263 GEOSGeom_destroy_r( handle, tmp );
265 GEOSSetSRID_r( handle, shp, srid );
273 static GEOSGeometry *LWGEOM_GEOS_getPointN(
const GEOSGeometry *g_in, uint32_t n )
278 const GEOSCoordSequence *seq_in =
nullptr;
279 GEOSCoordSeq seq_out;
283 GEOSGeometry *ret =
nullptr;
285 switch ( GEOSGeomTypeId_r( handle, g_in ) )
287 case GEOS_MULTIPOINT:
288 case GEOS_MULTILINESTRING:
289 case GEOS_MULTIPOLYGON:
290 case GEOS_GEOMETRYCOLLECTION:
292 for ( gn = 0; gn < GEOSGetNumGeometries_r( handle, g_in ); ++gn )
294 const GEOSGeometry *g = GEOSGetGeometryN_r( handle, g_in, gn );
295 ret = LWGEOM_GEOS_getPointN( g, n );
296 if ( ret )
return ret;
303 ret = LWGEOM_GEOS_getPointN( GEOSGetExteriorRing_r( handle, g_in ), n );
304 if ( ret )
return ret;
305 for ( gn = 0; gn < GEOSGetNumInteriorRings_r( handle, g_in ); ++gn )
307 const GEOSGeometry *g = GEOSGetInteriorRingN_r( handle, g_in, gn );
308 ret = LWGEOM_GEOS_getPointN( g, n );
309 if ( ret )
return ret;
315 case GEOS_LINESTRING:
316 case GEOS_LINEARRING:
321 seq_in = GEOSGeom_getCoordSeq_r( handle, g_in );
322 if ( ! seq_in )
return nullptr;
323 if ( ! GEOSCoordSeq_getSize_r( handle, seq_in, &sz ) )
return nullptr;
324 if ( ! sz )
return nullptr;
326 if ( ! GEOSCoordSeq_getDimensions_r( handle, seq_in, &dims ) )
return nullptr;
328 seq_out = GEOSCoordSeq_create_r( handle, 1, dims );
329 if ( ! seq_out )
return nullptr;
331 if ( ! GEOSCoordSeq_getX_r( handle, seq_in, n, &val ) )
return nullptr;
332 if ( ! GEOSCoordSeq_setX_r( handle, seq_out, n, val ) )
return nullptr;
333 if ( ! GEOSCoordSeq_getY_r( handle, seq_in, n, &val ) )
return nullptr;
334 if ( ! GEOSCoordSeq_setY_r( handle, seq_out, n, val ) )
return nullptr;
337 if ( ! GEOSCoordSeq_getZ_r( handle, seq_in, n, &val ) )
return nullptr;
338 if ( ! GEOSCoordSeq_setZ_r( handle, seq_out, n, val ) )
return nullptr;
341 return GEOSGeom_createPoint_r( handle, seq_out );
345 static bool lwline_make_geos_friendly(
QgsLineString *line );
346 static bool lwpoly_make_geos_friendly(
QgsPolygon *poly );
364 return lwline_make_geos_friendly( qgsgeometry_cast<QgsLineString *>( geom ) );
369 return lwpoly_make_geos_friendly( qgsgeometry_cast<QgsPolygon *>( geom ) );
375 return lwcollection_make_geos_friendly( qgsgeometry_cast<QgsGeometryCollection *>( geom ) );
379 QgsDebugMsg( QStringLiteral(
"lwgeom_make_geos_friendly: unsupported input geometry type: %1" ).arg( geom->
wkbType() ) );
386 static bool ring_make_geos_friendly(
QgsCurve *ring )
392 QgsLineString *linestring = qgsgeometry_cast<QgsLineString *>( ring );
393 Q_ASSERT_X( linestring,
"ring_make_geos_friendly",
"ring was not a linestring" );
398 if ( p1.
x() != p2.x() || p1.
y() != p2.y() )
410 static bool lwpoly_make_geos_friendly(
QgsPolygon *poly )
419 if ( !ring_make_geos_friendly( qgsgeometry_cast<QgsCurve *>( poly->
interiorRing( i ) ) ) )
440 if ( !lwgeom_make_geos_friendly( g->
geometryN( i ) ) )
449 static GEOSGeometry *LWGEOM_GEOS_nodeLines(
const GEOSGeometry *lines )
458 GEOSGeometry *point = LWGEOM_GEOS_getPointN( lines, 0 );
462 GEOSGeometry *noded =
nullptr;
465 noded = GEOSUnion_r( handle, lines, point );
467 catch ( GEOSException & )
471 GEOSGeom_destroy_r( handle, point );
477 static GEOSGeometry *LWGEOM_GEOS_makeValidPolygon(
const GEOSGeometry *gin, QString &errorMessage )
483 GEOSGeom geos_cut_edges, geos_area, collapse_points;
484 GEOSGeometry *vgeoms[3];
485 unsigned int nvgeoms = 0;
487 Q_ASSERT( GEOSGeomTypeId_r( handle, gin ) == GEOS_POLYGON ||
488 GEOSGeomTypeId_r( handle, gin ) == GEOS_MULTIPOLYGON );
490 geos_bound = GEOSBoundary_r( handle, gin );
496 geos_cut_edges = LWGEOM_GEOS_nodeLines( geos_bound );
497 if ( !geos_cut_edges )
499 GEOSGeom_destroy_r( handle, geos_bound );
500 errorMessage = QStringLiteral(
"LWGEOM_GEOS_nodeLines() failed" );
507 GEOSGeometry *pi =
nullptr;
508 GEOSGeometry *po =
nullptr;
512 pi = GEOSGeom_extractUniquePoints_r( handle, geos_bound );
514 catch ( GEOSException &e )
516 GEOSGeom_destroy_r( handle, geos_bound );
517 errorMessage = QStringLiteral(
"GEOSGeom_extractUniquePoints(): %1" ).arg( e.what() );
523 po = GEOSGeom_extractUniquePoints_r( handle, geos_cut_edges );
525 catch ( GEOSException &e )
527 GEOSGeom_destroy_r( handle, geos_bound );
528 GEOSGeom_destroy_r( handle, pi );
529 errorMessage = QStringLiteral(
"GEOSGeom_extractUniquePoints(): %1" ).arg( e.what() );
535 collapse_points = GEOSDifference_r( handle, pi, po );
537 catch ( GEOSException &e )
539 GEOSGeom_destroy_r( handle, geos_bound );
540 GEOSGeom_destroy_r( handle, pi );
541 GEOSGeom_destroy_r( handle, po );
542 errorMessage = QStringLiteral(
"GEOSDifference(): %1" ).arg( e.what() );
546 GEOSGeom_destroy_r( handle, pi );
547 GEOSGeom_destroy_r( handle, po );
549 GEOSGeom_destroy_r( handle, geos_bound );
554 geos_area = GEOSGeom_createEmptyPolygon_r( handle );
556 catch ( GEOSException &e )
558 errorMessage = QStringLiteral(
"GEOSGeom_createEmptyPolygon(): %1" ).arg( e.what() );
559 GEOSGeom_destroy_r( handle, geos_cut_edges );
567 while ( GEOSGetNumGeometries_r( handle, geos_cut_edges ) )
569 GEOSGeometry *new_area =
nullptr;
570 GEOSGeometry *new_area_bound =
nullptr;
571 GEOSGeometry *symdif =
nullptr;
572 GEOSGeometry *new_cut_edges =
nullptr;
578 new_area = LWGEOM_GEOS_buildArea( geos_cut_edges, errorMessage );
580 catch ( GEOSException &e )
582 GEOSGeom_destroy_r( handle, geos_cut_edges );
583 GEOSGeom_destroy_r( handle, geos_area );
584 errorMessage = QStringLiteral(
"LWGEOM_GEOS_buildArea() threw an error: %1" ).arg( e.what() );
588 if ( GEOSisEmpty_r( handle, new_area ) )
591 GEOSGeom_destroy_r( handle, new_area );
601 new_area_bound = GEOSBoundary_r( handle, new_area );
603 catch ( GEOSException &e )
607 errorMessage = QStringLiteral(
"GEOSBoundary() threw an error: %1" ).arg( e.what() );
608 GEOSGeom_destroy_r( handle, new_area );
609 GEOSGeom_destroy_r( handle, geos_area );
616 symdif = GEOSSymDifference_r( handle, geos_area, new_area );
618 catch ( GEOSException &e )
620 GEOSGeom_destroy_r( handle, geos_cut_edges );
621 GEOSGeom_destroy_r( handle, new_area );
622 GEOSGeom_destroy_r( handle, new_area_bound );
623 GEOSGeom_destroy_r( handle, geos_area );
624 errorMessage = QStringLiteral(
"GEOSSymDifference() threw an error: %1" ).arg( e.what() );
628 GEOSGeom_destroy_r( handle, geos_area );
629 GEOSGeom_destroy_r( handle, new_area );
643 new_cut_edges = GEOSDifference_r( handle, geos_cut_edges, new_area_bound );
645 catch ( GEOSException &e )
647 GEOSGeom_destroy_r( handle, geos_cut_edges );
648 GEOSGeom_destroy_r( handle, new_area_bound );
649 GEOSGeom_destroy_r( handle, geos_area );
650 errorMessage = QStringLiteral(
"GEOSDifference() threw an error: %1" ).arg( e.what() );
653 GEOSGeom_destroy_r( handle, geos_cut_edges );
654 GEOSGeom_destroy_r( handle, new_area_bound );
655 geos_cut_edges = new_cut_edges;
658 if ( !GEOSisEmpty_r( handle, geos_area ) )
660 vgeoms[nvgeoms++] = geos_area;
664 GEOSGeom_destroy_r( handle, geos_area );
667 if ( !GEOSisEmpty_r( handle, geos_cut_edges ) )
669 vgeoms[nvgeoms++] = geos_cut_edges;
673 GEOSGeom_destroy_r( handle, geos_cut_edges );
676 if ( !GEOSisEmpty_r( handle, collapse_points ) )
678 vgeoms[nvgeoms++] = collapse_points;
682 GEOSGeom_destroy_r( handle, collapse_points );
695 gout = GEOSGeom_createCollection_r( handle, GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms );
697 catch ( GEOSException &e )
699 errorMessage = QStringLiteral(
"GEOSGeom_createCollection() threw an error: %1" ).arg( e.what() );
710 static GEOSGeometry *LWGEOM_GEOS_makeValidLine(
const GEOSGeometry *gin, QString &errorMessage )
712 Q_UNUSED( errorMessage )
713 return LWGEOM_GEOS_nodeLines( gin );
716 static GEOSGeometry *LWGEOM_GEOS_makeValidMultiLine(
const GEOSGeometry *gin, QString &errorMessage )
720 int ngeoms = GEOSGetNumGeometries_r( handle, gin );
721 uint32_t nlines_alloc = ngeoms;
722 QVector<GEOSGeometry *> lines;
723 QVector<GEOSGeometry *> points;
724 lines.reserve( nlines_alloc );
725 points.reserve( ngeoms );
727 for (
int i = 0; i < ngeoms; ++i )
729 const GEOSGeometry *g = GEOSGetGeometryN_r( handle, gin, i );
730 GEOSGeometry *vg = LWGEOM_GEOS_makeValidLine( g, errorMessage );
731 if ( GEOSisEmpty_r( handle, vg ) )
734 GEOSGeom_destroy_r( handle, vg );
736 if ( GEOSGeomTypeId_r( handle, vg ) == GEOS_POINT )
740 else if ( GEOSGeomTypeId_r( handle, vg ) == GEOS_LINESTRING )
744 else if ( GEOSGeomTypeId_r( handle, vg ) == GEOS_MULTILINESTRING )
746 int nsubgeoms = GEOSGetNumGeometries_r( handle, vg );
747 nlines_alloc += nsubgeoms;
748 lines.reserve( nlines_alloc );
749 for (
int j = 0; j < nsubgeoms; ++j )
752 lines.append( GEOSGeom_clone_r( handle, GEOSGetGeometryN_r( handle, vg, j ) ) );
758 errorMessage = QStringLiteral(
"unexpected geom type returned by LWGEOM_GEOS_makeValid: %1" ).arg( GEOSGeomTypeId_r( handle, vg ) );
762 GEOSGeometry *mpoint_out =
nullptr;
763 if ( !points.isEmpty() )
765 if ( points.count() > 1 )
767 mpoint_out = GEOSGeom_createCollection_r( handle, GEOS_MULTIPOINT, points.data(), points.count() );
771 mpoint_out = points[0];
775 GEOSGeometry *mline_out =
nullptr;
776 if ( !lines.isEmpty() )
778 if ( lines.count() > 1 )
780 mline_out = GEOSGeom_createCollection_r( handle, GEOS_MULTILINESTRING, lines.data(), lines.count() );
784 mline_out = lines[0];
788 if ( mline_out && mpoint_out )
790 GEOSGeometry *collection[2];
791 collection[0] = mline_out;
792 collection[1] = mpoint_out;
793 return GEOSGeom_createCollection_r( handle, GEOS_GEOMETRYCOLLECTION, collection, 2 );
795 else if ( mline_out )
799 else if ( mpoint_out )
810 static GEOSGeometry *LWGEOM_GEOS_makeValid(
const GEOSGeometry *gin, QString &errorMessage );
813 static GEOSGeometry *LWGEOM_GEOS_makeValidCollection(
const GEOSGeometry *gin, QString &errorMessage )
817 int nvgeoms = GEOSGetNumGeometries_r( handle, gin );
820 errorMessage = QStringLiteral(
"GEOSGetNumGeometries: %1" ).arg( QLatin1String(
"?" ) );
824 QVector<GEOSGeometry *> vgeoms( nvgeoms );
825 for (
int i = 0; i < nvgeoms; ++i )
827 vgeoms[i] = LWGEOM_GEOS_makeValid( GEOSGetGeometryN_r( handle, gin, i ), errorMessage );
831 GEOSGeom_destroy_r( handle, vgeoms[i] );
840 return GEOSGeom_createCollection_r( handle, GEOS_GEOMETRYCOLLECTION, vgeoms.data(), nvgeoms );
842 catch ( GEOSException &e )
845 for (
int i = 0; i < nvgeoms; ++i )
846 GEOSGeom_destroy_r( handle, vgeoms[i] );
847 errorMessage = QStringLiteral(
"GEOSGeom_createCollection() threw an error: %1" ).arg( e.what() );
853 static GEOSGeometry *LWGEOM_GEOS_makeValid(
const GEOSGeometry *gin, QString &errorMessage )
861 if ( GEOSisValid_r( handle, gin ) )
864 return GEOSGeom_clone_r( handle, gin );
867 catch ( GEOSException &e )
870 errorMessage = QStringLiteral(
"GEOSisValid(): %1" ).arg( e.what() );
877 switch ( GEOSGeomTypeId_r( handle, gin ) )
879 case GEOS_MULTIPOINT:
882 QgsDebugMsg( QStringLiteral(
"PUNTUAL geometry resulted invalid to GEOS -- dunno how to clean that up" ) );
885 case GEOS_LINESTRING:
886 return LWGEOM_GEOS_makeValidLine( gin, errorMessage );
888 case GEOS_MULTILINESTRING:
889 return LWGEOM_GEOS_makeValidMultiLine( gin, errorMessage );
892 case GEOS_MULTIPOLYGON:
893 return LWGEOM_GEOS_makeValidPolygon( gin, errorMessage );
895 case GEOS_GEOMETRYCOLLECTION:
896 return LWGEOM_GEOS_makeValidCollection( gin, errorMessage );
899 errorMessage = QStringLiteral(
"ST_MakeValid: doesn't support geometry type: %1" ).arg( GEOSGeomTypeId_r( handle, gin ) );
905 std::unique_ptr< QgsAbstractGeometry > _qgis_lwgeom_make_valid(
const QgsAbstractGeometry *lwgeom_in, QString &errorMessage )
916 QgsDebugMsgLevel( QStringLiteral(
"Original geom can't be converted to GEOS - will try cleaning that up first" ), 3 );
918 std::unique_ptr<QgsAbstractGeometry> lwgeom_in_clone( lwgeom_in->
clone() );
919 if ( !lwgeom_make_geos_friendly( lwgeom_in_clone.get() ) )
921 QgsDebugMsg( QStringLiteral(
"Could not make a valid geometry out of input" ) );
930 errorMessage = QStringLiteral(
"Could not convert QGIS geom to GEOS" );
939 GEOSGeometry *geosout = LWGEOM_GEOS_makeValid( geosgeom.get(), errorMessage );
943 std::unique_ptr< QgsAbstractGeometry > lwgeom_out =
QgsGeos::fromGeos( geosout );
944 GEOSGeom_destroy_r( handle, geosout );
968 lwgeom_out.reset( collection );
Abstract base class for all geometries.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Abstract base class for curved geometry type.
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0)
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
static std::unique_ptr< QgsAbstractGeometry > fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
static GEOSContextHandle_t getGEOSHandler()
Line string geometry type, with support for z-dimension and m-values.
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
QgsPoint endPoint() const override SIP_HOLDGIL
Returns the end point of the curve.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
int nCoordinates() const override SIP_HOLDGIL
Returns the number of nodes contained in the geometry.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
Multi line string geometry collection.
Multi point geometry collection.
Multi polygon geometry collection.
Point geometry type, with support for z-dimension and m-values.
static bool isMultiType(Type type) SIP_HOLDGIL
Returns true if the WKB type is a multi type.
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
static Type multiType(Type type) SIP_HOLDGIL
Returns the multi type for a WKB type.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
#define Q_NOWARN_UNREACHABLE_PUSH
#define Q_NOWARN_UNREACHABLE_POP
#define QgsDebugMsgLevel(str, level)