33 #if ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR<8 )
39 const GEOSGeometry *geom =
nullptr;
40 GEOSGeometry *env =
nullptr;
42 struct Face_t *parent;
45 static Face *newFace(
const GEOSGeometry *g );
46 static void delFace( Face *f );
47 static unsigned int countParens(
const Face *f );
48 static void findFaceHoles( Face **faces,
int nfaces );
50 static Face *newFace(
const GEOSGeometry *g )
56 f->env = GEOSEnvelope_r( handle, f->geom );
57 GEOSArea_r( handle, f->env, &f->envarea );
62 static unsigned int countParens(
const Face *f )
64 unsigned int pcount = 0;
74 static void delFace( Face *f )
77 GEOSGeom_destroy_r( handle, f->env );
82 static int compare_by_envarea(
const void *g1,
const void *g2 )
84 Face *f1 = *( Face ** )g1;
85 Face *f2 = *( Face ** )g2;
86 double n1 = f1->envarea;
87 double n2 = f2->envarea;
89 if ( n1 < n2 )
return 1;
90 if ( n1 > n2 )
return -1;
95 static void findFaceHoles( Face **faces,
int nfaces )
101 qsort( faces, nfaces,
sizeof( Face * ), compare_by_envarea );
102 for (
int i = 0; i < nfaces; ++i )
105 int nholes = GEOSGetNumInteriorRings_r( handle, f->geom );
106 for (
int h = 0; h < nholes; ++h )
108 const GEOSGeometry *hole = GEOSGetInteriorRingN_r( handle, f->geom, h );
109 for (
int j = i + 1; j < nfaces; ++j )
119 if ( GEOSEquals_r( handle, GEOSGetExteriorRing_r( handle, f2->geom ), hole ) )
129 static GEOSGeometry *collectFacesWithEvenAncestors( Face **faces,
int nfaces )
133 unsigned int ngeoms = 0;
134 GEOSGeometry **geoms =
new GEOSGeometry*[nfaces];
135 for (
int i = 0; i < nfaces; ++i )
138 if ( countParens( f ) % 2 )
140 geoms[ngeoms++] = GEOSGeom_clone_r( handle, f->geom );
143 GEOSGeometry *ret = GEOSGeom_createCollection_r( handle, GEOS_MULTIPOLYGON, geoms, ngeoms );
149 static GEOSGeometry *LWGEOM_GEOS_buildArea(
const GEOSGeometry *geom_in, QString &errorMessage )
153 GEOSGeometry *tmp =
nullptr;
154 GEOSGeometry *shp =
nullptr;
155 GEOSGeometry *geos_result =
nullptr;
156 int srid = GEOSGetSRID_r( handle, geom_in );
158 GEOSGeometry
const *vgeoms[1];
162 geos_result = GEOSPolygonize_r( handle, vgeoms, 1 );
164 catch ( GEOSException &e )
166 errorMessage = QStringLiteral(
"GEOSPolygonize(): %1" ).arg( e.what() );
171 #if PARANOIA_LEVEL > 0
172 if ( GEOSGeometryTypeId_r( handle, geos_result ) != COLLECTIONTYPE )
174 GEOSGeom_destroy_r( handle, geos_result );
175 errorMessage =
"Unexpected return from GEOSpolygonize";
180 int ngeoms = GEOSGetNumGeometries_r( handle, geos_result );
185 GEOSSetSRID_r( handle, geos_result, srid );
193 tmp = ( GEOSGeometry * )GEOSGetGeometryN_r( handle, geos_result, 0 );
196 GEOSGeom_destroy_r( handle, geos_result );
199 shp = GEOSGeom_clone_r( handle, tmp );
200 GEOSGeom_destroy_r( handle, geos_result );
201 GEOSSetSRID_r( handle, shp, srid );
234 Face **geoms =
new Face*[ngeoms];
235 for (
int i = 0; i < ngeoms; ++i )
236 geoms[i] = newFace( GEOSGetGeometryN_r( handle, geos_result, i ) );
239 findFaceHoles( geoms, ngeoms );
243 tmp = collectFacesWithEvenAncestors( geoms, ngeoms );
246 for (
int i = 0; i < ngeoms; ++i )
252 GEOSGeom_destroy_r( handle, geos_result );
255 shp = GEOSUnionCascaded_r( handle, tmp );
258 GEOSGeom_destroy_r( handle, tmp );
262 GEOSGeom_destroy_r( handle, tmp );
264 GEOSSetSRID_r( handle, shp, srid );
272 static GEOSGeometry *LWGEOM_GEOS_getPointN(
const GEOSGeometry *g_in, uint32_t n )
277 const GEOSCoordSequence *seq_in =
nullptr;
278 GEOSCoordSeq seq_out;
282 GEOSGeometry *ret =
nullptr;
284 switch ( GEOSGeomTypeId_r( handle, g_in ) )
286 case GEOS_MULTIPOINT:
287 case GEOS_MULTILINESTRING:
288 case GEOS_MULTIPOLYGON:
289 case GEOS_GEOMETRYCOLLECTION:
291 for ( gn = 0; gn < GEOSGetNumGeometries_r( handle, g_in ); ++gn )
293 const GEOSGeometry *g = GEOSGetGeometryN_r( handle, g_in, gn );
294 ret = LWGEOM_GEOS_getPointN( g, n );
295 if ( ret )
return ret;
302 ret = LWGEOM_GEOS_getPointN( GEOSGetExteriorRing_r( handle, g_in ), n );
303 if ( ret )
return ret;
304 for ( gn = 0; gn < GEOSGetNumInteriorRings_r( handle, g_in ); ++gn )
306 const GEOSGeometry *g = GEOSGetInteriorRingN_r( handle, g_in, gn );
307 ret = LWGEOM_GEOS_getPointN( g, n );
308 if ( ret )
return ret;
314 case GEOS_LINESTRING:
315 case GEOS_LINEARRING:
320 seq_in = GEOSGeom_getCoordSeq_r( handle, g_in );
321 if ( ! seq_in )
return nullptr;
322 if ( ! GEOSCoordSeq_getSize_r( handle, seq_in, &sz ) )
return nullptr;
323 if ( ! sz )
return nullptr;
325 if ( ! GEOSCoordSeq_getDimensions_r( handle, seq_in, &dims ) )
return nullptr;
327 seq_out = GEOSCoordSeq_create_r( handle, 1, dims );
328 if ( ! seq_out )
return nullptr;
330 if ( ! GEOSCoordSeq_getX_r( handle, seq_in, n, &val ) )
return nullptr;
331 if ( ! GEOSCoordSeq_setX_r( handle, seq_out, n, val ) )
return nullptr;
332 if ( ! GEOSCoordSeq_getY_r( handle, seq_in, n, &val ) )
return nullptr;
333 if ( ! GEOSCoordSeq_setY_r( handle, seq_out, n, val ) )
return nullptr;
336 if ( ! GEOSCoordSeq_getZ_r( handle, seq_in, n, &val ) )
return nullptr;
337 if ( ! GEOSCoordSeq_setZ_r( handle, seq_out, n, val ) )
return nullptr;
340 return GEOSGeom_createPoint_r( handle, seq_out );
344 static bool lwline_make_geos_friendly(
QgsLineString *line );
345 static bool lwpoly_make_geos_friendly(
QgsPolygon *poly );
363 return lwline_make_geos_friendly( qgsgeometry_cast<QgsLineString *>( geom ) );
368 return lwpoly_make_geos_friendly( qgsgeometry_cast<QgsPolygon *>( geom ) );
374 return lwcollection_make_geos_friendly( qgsgeometry_cast<QgsGeometryCollection *>( geom ) );
378 QgsDebugMsg( QStringLiteral(
"lwgeom_make_geos_friendly: unsupported input geometry type: %1" ).arg( geom->
wkbType() ) );
385 static bool ring_make_geos_friendly(
QgsCurve *ring )
391 QgsLineString *linestring = qgsgeometry_cast<QgsLineString *>( ring );
392 Q_ASSERT_X( linestring,
"ring_make_geos_friendly",
"ring was not a linestring" );
397 if ( p1.
x() != p2.x() || p1.
y() != p2.y() )
409 static bool lwpoly_make_geos_friendly(
QgsPolygon *poly )
418 if ( !ring_make_geos_friendly( qgsgeometry_cast<QgsCurve *>( poly->
interiorRing( i ) ) ) )
439 if ( !lwgeom_make_geos_friendly( g->
geometryN( i ) ) )
448 static GEOSGeometry *LWGEOM_GEOS_nodeLines(
const GEOSGeometry *lines )
457 GEOSGeometry *point = LWGEOM_GEOS_getPointN( lines, 0 );
461 GEOSGeometry *noded =
nullptr;
464 noded = GEOSUnion_r( handle, lines, point );
466 catch ( GEOSException & )
470 GEOSGeom_destroy_r( handle, point );
476 static GEOSGeometry *LWGEOM_GEOS_makeValidPolygon(
const GEOSGeometry *gin, QString &errorMessage )
482 GEOSGeom geos_cut_edges, geos_area, collapse_points;
483 GEOSGeometry *vgeoms[3];
484 unsigned int nvgeoms = 0;
486 Q_ASSERT( GEOSGeomTypeId_r( handle, gin ) == GEOS_POLYGON ||
487 GEOSGeomTypeId_r( handle, gin ) == GEOS_MULTIPOLYGON );
489 geos_bound = GEOSBoundary_r( handle, gin );
495 geos_cut_edges = LWGEOM_GEOS_nodeLines( geos_bound );
496 if ( !geos_cut_edges )
498 GEOSGeom_destroy_r( handle, geos_bound );
499 errorMessage = QStringLiteral(
"LWGEOM_GEOS_nodeLines() failed" );
506 GEOSGeometry *pi =
nullptr;
507 GEOSGeometry *po =
nullptr;
511 pi = GEOSGeom_extractUniquePoints_r( handle, geos_bound );
513 catch ( GEOSException &e )
515 GEOSGeom_destroy_r( handle, geos_bound );
516 errorMessage = QStringLiteral(
"GEOSGeom_extractUniquePoints(): %1" ).arg( e.what() );
522 po = GEOSGeom_extractUniquePoints_r( handle, geos_cut_edges );
524 catch ( GEOSException &e )
526 GEOSGeom_destroy_r( handle, geos_bound );
527 GEOSGeom_destroy_r( handle, pi );
528 errorMessage = QStringLiteral(
"GEOSGeom_extractUniquePoints(): %1" ).arg( e.what() );
534 collapse_points = GEOSDifference_r( handle, pi, po );
536 catch ( GEOSException &e )
538 GEOSGeom_destroy_r( handle, geos_bound );
539 GEOSGeom_destroy_r( handle, pi );
540 GEOSGeom_destroy_r( handle, po );
541 errorMessage = QStringLiteral(
"GEOSDifference(): %1" ).arg( e.what() );
545 GEOSGeom_destroy_r( handle, pi );
546 GEOSGeom_destroy_r( handle, po );
548 GEOSGeom_destroy_r( handle, geos_bound );
553 geos_area = GEOSGeom_createEmptyPolygon_r( handle );
555 catch ( GEOSException &e )
557 errorMessage = QStringLiteral(
"GEOSGeom_createEmptyPolygon(): %1" ).arg( e.what() );
558 GEOSGeom_destroy_r( handle, geos_cut_edges );
566 while ( GEOSGetNumGeometries_r( handle, geos_cut_edges ) )
568 GEOSGeometry *new_area =
nullptr;
569 GEOSGeometry *new_area_bound =
nullptr;
570 GEOSGeometry *symdif =
nullptr;
571 GEOSGeometry *new_cut_edges =
nullptr;
577 new_area = LWGEOM_GEOS_buildArea( geos_cut_edges, errorMessage );
579 catch ( GEOSException &e )
581 GEOSGeom_destroy_r( handle, geos_cut_edges );
582 GEOSGeom_destroy_r( handle, geos_area );
583 errorMessage = QStringLiteral(
"LWGEOM_GEOS_buildArea() threw an error: %1" ).arg( e.what() );
587 if ( GEOSisEmpty_r( handle, new_area ) )
590 GEOSGeom_destroy_r( handle, new_area );
600 new_area_bound = GEOSBoundary_r( handle, new_area );
602 catch ( GEOSException &e )
606 errorMessage = QStringLiteral(
"GEOSBoundary() threw an error: %1" ).arg( e.what() );
607 GEOSGeom_destroy_r( handle, new_area );
608 GEOSGeom_destroy_r( handle, geos_area );
615 symdif = GEOSSymDifference_r( handle, geos_area, new_area );
617 catch ( GEOSException &e )
619 GEOSGeom_destroy_r( handle, geos_cut_edges );
620 GEOSGeom_destroy_r( handle, new_area );
621 GEOSGeom_destroy_r( handle, new_area_bound );
622 GEOSGeom_destroy_r( handle, geos_area );
623 errorMessage = QStringLiteral(
"GEOSSymDifference() threw an error: %1" ).arg( e.what() );
627 GEOSGeom_destroy_r( handle, geos_area );
628 GEOSGeom_destroy_r( handle, new_area );
642 new_cut_edges = GEOSDifference_r( handle, geos_cut_edges, new_area_bound );
644 catch ( GEOSException &e )
646 GEOSGeom_destroy_r( handle, geos_cut_edges );
647 GEOSGeom_destroy_r( handle, new_area_bound );
648 GEOSGeom_destroy_r( handle, geos_area );
649 errorMessage = QStringLiteral(
"GEOSDifference() threw an error: %1" ).arg( e.what() );
652 GEOSGeom_destroy_r( handle, geos_cut_edges );
653 GEOSGeom_destroy_r( handle, new_area_bound );
654 geos_cut_edges = new_cut_edges;
657 if ( !GEOSisEmpty_r( handle, geos_area ) )
659 vgeoms[nvgeoms++] = geos_area;
663 GEOSGeom_destroy_r( handle, geos_area );
666 if ( !GEOSisEmpty_r( handle, geos_cut_edges ) )
668 vgeoms[nvgeoms++] = geos_cut_edges;
672 GEOSGeom_destroy_r( handle, geos_cut_edges );
675 if ( !GEOSisEmpty_r( handle, collapse_points ) )
677 vgeoms[nvgeoms++] = collapse_points;
681 GEOSGeom_destroy_r( handle, collapse_points );
694 gout = GEOSGeom_createCollection_r( handle, GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms );
696 catch ( GEOSException &e )
698 errorMessage = QStringLiteral(
"GEOSGeom_createCollection() threw an error: %1" ).arg( e.what() );
709 static GEOSGeometry *LWGEOM_GEOS_makeValidLine(
const GEOSGeometry *gin, QString &errorMessage )
711 Q_UNUSED( errorMessage )
712 return LWGEOM_GEOS_nodeLines( gin );
715 static GEOSGeometry *LWGEOM_GEOS_makeValidMultiLine(
const GEOSGeometry *gin, QString &errorMessage )
719 int ngeoms = GEOSGetNumGeometries_r( handle, gin );
720 uint32_t nlines_alloc = ngeoms;
721 QVector<GEOSGeometry *> lines;
722 QVector<GEOSGeometry *> points;
723 lines.reserve( nlines_alloc );
724 points.reserve( ngeoms );
726 for (
int i = 0; i < ngeoms; ++i )
728 const GEOSGeometry *g = GEOSGetGeometryN_r( handle, gin, i );
729 GEOSGeometry *vg = LWGEOM_GEOS_makeValidLine( g, errorMessage );
730 if ( GEOSisEmpty_r( handle, vg ) )
733 GEOSGeom_destroy_r( handle, vg );
735 if ( GEOSGeomTypeId_r( handle, vg ) == GEOS_POINT )
739 else if ( GEOSGeomTypeId_r( handle, vg ) == GEOS_LINESTRING )
743 else if ( GEOSGeomTypeId_r( handle, vg ) == GEOS_MULTILINESTRING )
745 int nsubgeoms = GEOSGetNumGeometries_r( handle, vg );
746 nlines_alloc += nsubgeoms;
747 lines.reserve( nlines_alloc );
748 for (
int j = 0; j < nsubgeoms; ++j )
751 lines.append( GEOSGeom_clone_r( handle, GEOSGetGeometryN_r( handle, vg, j ) ) );
757 errorMessage = QStringLiteral(
"unexpected geom type returned by LWGEOM_GEOS_makeValid: %1" ).arg( GEOSGeomTypeId_r( handle, vg ) );
761 GEOSGeometry *mpoint_out =
nullptr;
762 if ( !points.isEmpty() )
764 if ( points.count() > 1 )
766 mpoint_out = GEOSGeom_createCollection_r( handle, GEOS_MULTIPOINT, points.data(), points.count() );
770 mpoint_out = points[0];
774 GEOSGeometry *mline_out =
nullptr;
775 if ( !lines.isEmpty() )
777 if ( lines.count() > 1 )
779 mline_out = GEOSGeom_createCollection_r( handle, GEOS_MULTILINESTRING, lines.data(), lines.count() );
783 mline_out = lines[0];
787 if ( mline_out && mpoint_out )
789 GEOSGeometry *collection[2];
790 collection[0] = mline_out;
791 collection[1] = mpoint_out;
792 return GEOSGeom_createCollection_r( handle, GEOS_GEOMETRYCOLLECTION, collection, 2 );
794 else if ( mline_out )
798 else if ( mpoint_out )
809 static GEOSGeometry *LWGEOM_GEOS_makeValid(
const GEOSGeometry *gin, QString &errorMessage );
812 static GEOSGeometry *LWGEOM_GEOS_makeValidCollection(
const GEOSGeometry *gin, QString &errorMessage )
816 int nvgeoms = GEOSGetNumGeometries_r( handle, gin );
819 errorMessage = QStringLiteral(
"GEOSGetNumGeometries: %1" ).arg( QLatin1String(
"?" ) );
823 QVector<GEOSGeometry *> vgeoms( nvgeoms );
824 for (
int i = 0; i < nvgeoms; ++i )
826 vgeoms[i] = LWGEOM_GEOS_makeValid( GEOSGetGeometryN_r( handle, gin, i ), errorMessage );
830 GEOSGeom_destroy_r( handle, vgeoms[i] );
839 return GEOSGeom_createCollection_r( handle, GEOS_GEOMETRYCOLLECTION, vgeoms.data(), nvgeoms );
841 catch ( GEOSException &e )
844 for (
int i = 0; i < nvgeoms; ++i )
845 GEOSGeom_destroy_r( handle, vgeoms[i] );
846 errorMessage = QStringLiteral(
"GEOSGeom_createCollection() threw an error: %1" ).arg( e.what() );
852 static GEOSGeometry *LWGEOM_GEOS_makeValid(
const GEOSGeometry *gin, QString &errorMessage )
860 if ( GEOSisValid_r( handle, gin ) )
863 return GEOSGeom_clone_r( handle, gin );
866 catch ( GEOSException &e )
869 errorMessage = QStringLiteral(
"GEOSisValid(): %1" ).arg( e.what() );
876 switch ( GEOSGeomTypeId_r( handle, gin ) )
878 case GEOS_MULTIPOINT:
881 QgsDebugMsg( QStringLiteral(
"PUNTUAL geometry resulted invalid to GEOS -- dunno how to clean that up" ) );
884 case GEOS_LINESTRING:
885 return LWGEOM_GEOS_makeValidLine( gin, errorMessage );
887 case GEOS_MULTILINESTRING:
888 return LWGEOM_GEOS_makeValidMultiLine( gin, errorMessage );
891 case GEOS_MULTIPOLYGON:
892 return LWGEOM_GEOS_makeValidPolygon( gin, errorMessage );
894 case GEOS_GEOMETRYCOLLECTION:
895 return LWGEOM_GEOS_makeValidCollection( gin, errorMessage );
898 errorMessage = QStringLiteral(
"ST_MakeValid: doesn't support geometry type: %1" ).arg( GEOSGeomTypeId_r( handle, gin ) );
904 std::unique_ptr< QgsAbstractGeometry > _qgis_lwgeom_make_valid(
const QgsAbstractGeometry *lwgeom_in, QString &errorMessage )
915 QgsDebugMsgLevel( QStringLiteral(
"Original geom can't be converted to GEOS - will try cleaning that up first" ), 3 );
917 std::unique_ptr<QgsAbstractGeometry> lwgeom_in_clone( lwgeom_in->
clone() );
918 if ( !lwgeom_make_geos_friendly( lwgeom_in_clone.get() ) )
920 QgsDebugMsg( QStringLiteral(
"Could not make a valid geometry out of input" ) );
929 errorMessage = QStringLiteral(
"Could not convert QGIS geom to GEOS" );
938 GEOSGeometry *geosout = LWGEOM_GEOS_makeValid( geosgeom.get(), errorMessage );
942 std::unique_ptr< QgsAbstractGeometry > lwgeom_out =
QgsGeos::fromGeos( geosout );
943 GEOSGeom_destroy_r( handle, geosout );
967 lwgeom_out.reset( collection );