From: Darafei Praliaskouski Date: Wed, 7 Mar 2018 13:21:45 +0000 (+0000) Subject: Fix invalid inputs for GEOS if it cannot process them X-Git-Tag: 2.5.0alpha~77 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=68061bf81bb4090f8c16b1c867a39826336a7b8b;p=postgis Fix invalid inputs for GEOS if it cannot process them Invalid input geometry is fixed with MakeValid for GEOS exceptions in ST_Intersection, ST_Union, ST_Difference, ST_SymDifference. Closes #4037 Closes https://github.com/postgis/postgis/pull/228 git-svn-id: http://svn.osgeo.org/postgis/trunk@16450 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/NEWS b/NEWS index 24066b564..66d4c7ccb 100644 --- a/NEWS +++ b/NEWS @@ -14,7 +14,7 @@ PostGIS 2.5.0 - #3885, version number removed from address_standardize lib file - #3893, raster support functions can only be loaded in the same schema with core PostGIS functions. - - #4035, remove dummy pgis_abs type from aggregate/collect routines. + - #4035, remove dummy pgis_abs type from aggregate/collect routines. * Enhancements and Fixes* - #3944, Update to EPSG register v9.2 (Even Rouault) @@ -43,6 +43,9 @@ PostGIS 2.5.0 - #4020, Casting from box3d to geometry now returns correctly connected PolyhedralSurface (Matthias Bay) - #2508, ST_OffsetCurve now works with collections (Darafei Praliaskouski) + - #4037, Invalid input geometry is fixed with MakeValid for GEOS exceptions in + ST_Intersection, ST_Union, ST_Difference, ST_SymDifference (Darafei + Praliaskouski) PostGIS 2.4.0 2017/09/30 diff --git a/liblwgeom/lwgeom_geos.c b/liblwgeom/lwgeom_geos.c index 254be6c4a..963af7467 100644 --- a/liblwgeom/lwgeom_geos.c +++ b/liblwgeom/lwgeom_geos.c @@ -603,6 +603,24 @@ lwgeom_intersection(const LWGEOM* geom1, const LWGEOM* geom2) g3 = GEOSIntersection(g1, g2); + if (!g3) + { + GEOSGeometry *g1v, *g2v; + lwnotice("%s: GEOS Error: %s", __func__, lwgeom_geos_errmsg); + + if (!GEOSisValid(g1) || !GEOSisValid(g2)) + { + lwnotice( + "Your geometry dataset is not valid per OGC Specification. " + "Please fix it with manual review of entries that are not ST_IsValid(geom). " + "Retrying GEOS operation with ST_MakeValid of your input."); + g1v = LWGEOM_GEOS_makeValid(g1); + g2v = LWGEOM_GEOS_makeValid(g2); + g3 = GEOSIntersection(g1v, g2v); + geos_clean(g1v, g2v, NULL); + } + } + if (!g3) return geos_clean_and_fail(g1, g2, NULL, __func__); if (!output_geos_as_lwgeom(&g3, &result, srid, is3d, __func__)) @@ -695,6 +713,24 @@ lwgeom_difference(const LWGEOM* geom1, const LWGEOM* geom2) g3 = GEOSDifference(g1, g2); + if (!g3) + { + GEOSGeometry *g1v, *g2v; + lwnotice("%s: GEOS Error: %s", __func__, lwgeom_geos_errmsg); + + if (!GEOSisValid(g1) || !GEOSisValid(g2)) + { + lwnotice( + "Your geometry dataset is not valid per OGC Specification. " + "Please fix it with manual review of entries that are not ST_IsValid(geom). " + "Retrying GEOS operation with ST_MakeValid of your input."); + g1v = LWGEOM_GEOS_makeValid(g1); + g2v = LWGEOM_GEOS_makeValid(g2); + g3 = GEOSDifference(g1v, g2v); + geos_clean(g1v, g2v, NULL); + } + } + if (!g3) return geos_clean_and_fail(g1, g2, NULL, __func__); if (!output_geos_as_lwgeom(&g3, &result, srid, is3d, __func__)) @@ -727,6 +763,24 @@ lwgeom_symdifference(const LWGEOM* geom1, const LWGEOM* geom2) g3 = GEOSSymDifference(g1, g2); + if (!g3) + { + GEOSGeometry *g1v, *g2v; + lwnotice("%s: GEOS Error: %s", __func__, lwgeom_geos_errmsg); + + if (!GEOSisValid(g1) || !GEOSisValid(g2)) + { + lwnotice( + "Your geometry dataset is not valid per OGC Specification. " + "Please fix it with manual review of entries that are not ST_IsValid(geom). " + "Retrying GEOS operation with ST_MakeValid of your input."); + g1v = LWGEOM_GEOS_makeValid(g1); + g2v = LWGEOM_GEOS_makeValid(g2); + g3 = GEOSSymDifference(g1v, g2v); + geos_clean(g1v, g2v, NULL); + } + } + if (!g3) return geos_clean_and_fail(g1, g2, NULL, __func__); if (!output_geos_as_lwgeom(&g3, &result, srid, is3d, __func__)) @@ -791,6 +845,24 @@ lwgeom_union(const LWGEOM* geom1, const LWGEOM* geom2) g3 = GEOSUnion(g1, g2); + if (!g3) + { + GEOSGeometry *g1v, *g2v; + lwnotice("%s: GEOS Error: %s", __func__, lwgeom_geos_errmsg); + + if (!GEOSisValid(g1) || !GEOSisValid(g2)) + { + lwnotice( + "Your geometry dataset is not valid per OGC Specification. " + "Please fix it with manual review of entries that are not ST_IsValid(geom). " + "Retrying GEOS operation with ST_MakeValid of your input."); + g1v = LWGEOM_GEOS_makeValid(g1); + g2v = LWGEOM_GEOS_makeValid(g2); + g3 = GEOSUnion(g1v, g2v); + geos_clean(g1v, g2v, NULL); + } + } + if (!g3) return geos_clean_and_fail(g1, g2, NULL, __func__); if (!output_geos_as_lwgeom(&g3, &result, srid, is3d, __func__)) diff --git a/regress/tickets.sql b/regress/tickets.sql index e65b0acbd..231d2a709 100644 --- a/regress/tickets.sql +++ b/regress/tickets.sql @@ -1075,5 +1075,11 @@ select '#4025', ST_DistanceCPA ( 2996311.81673704 5844916.90864168 1180471541.171)'::geometry ); +-- a butterfly polygon in all cases +SELECT '#4037.1', ST_AsText(ST_Intersection('POLYGON((0 0, 10 10, 0 10, 10 0, 0 0))', ST_MakeEnvelope(2,2,8,8))); +SELECT '#4037.2', ST_AsText(ST_Difference('POLYGON((0 0, 10 10, 0 10, 10 0, 0 0))', ST_MakeEnvelope(2,2,8,8))); +SELECT '#4037.3', ST_AsText(ST_SymDifference('POLYGON((0 0, 10 10, 0 10, 10 0, 0 0))', ST_MakeEnvelope(2,2,8,8))); +SELECT '#4037.4', ST_AsText(ST_Union('POLYGON((0 0, 10 10, 0 10, 10 0, 0 0))', ST_MakeEnvelope(2,2,8,8))); + -- Clean up DELETE FROM spatial_ref_sys; diff --git a/regress/tickets_expected b/regress/tickets_expected index e57d0900d..2e4084352 100644 --- a/regress/tickets_expected +++ b/regress/tickets_expected @@ -326,3 +326,23 @@ NOTICE: Too few points in geometry component at or near point 0 0 #4011|ST_MultiLineString|MULTILINESTRING EMPTY|t|t #4011|ST_GeometryCollection|MULTILINESTRING((0 0,0 0))|f|f #4025| +NOTICE: lwgeom_intersection: GEOS Error: TopologyException: Input geom 0 is invalid: Self-intersection +NOTICE: Self-intersection +NOTICE: Your geometry dataset is not valid per OGC Specification. Please fix it with manual review of entries that are not ST_IsValid(geom). Retrying GEOS operation with ST_MakeValid of your input. +NOTICE: Self-intersection +#4037.1|MULTIPOLYGON(((2 2,5 5,8 2,2 2)),((5 5,2 8,8 8,5 5))) +NOTICE: lwgeom_difference: GEOS Error: TopologyException: Input geom 0 is invalid: Self-intersection +NOTICE: Self-intersection +NOTICE: Your geometry dataset is not valid per OGC Specification. Please fix it with manual review of entries that are not ST_IsValid(geom). Retrying GEOS operation with ST_MakeValid of your input. +NOTICE: Self-intersection +#4037.2|MULTIPOLYGON(((0 0,2 2,8 2,10 0,0 0)),((2 8,0 10,10 10,8 8,2 8))) +NOTICE: lwgeom_symdifference: GEOS Error: TopologyException: Input geom 0 is invalid: Self-intersection +NOTICE: Self-intersection +NOTICE: Your geometry dataset is not valid per OGC Specification. Please fix it with manual review of entries that are not ST_IsValid(geom). Retrying GEOS operation with ST_MakeValid of your input. +NOTICE: Self-intersection +#4037.3|MULTIPOLYGON(((0 0,2 2,8 2,10 0,0 0)),((5 5,2 2,2 8,5 5)),((8 2,5 5,8 8,8 2)),((2 8,0 10,10 10,8 8,2 8))) +NOTICE: lwgeom_union: GEOS Error: TopologyException: Input geom 0 is invalid: Self-intersection +NOTICE: Self-intersection +NOTICE: Your geometry dataset is not valid per OGC Specification. Please fix it with manual review of entries that are not ST_IsValid(geom). Retrying GEOS operation with ST_MakeValid of your input. +NOTICE: Self-intersection +#4037.4|POLYGON((0 0,2 2,2 8,0 10,10 10,8 8,8 2,10 0,0 0))