From: Olivier Courtin Date: Thu, 25 Jun 2015 20:44:47 +0000 (+0000) Subject: Add suport for SFCGAL 1.1. #3117. Apply patch written by Vincent Mora X-Git-Tag: 2.2.0rc1~327 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0863846f1ee3366cd45c74ede5aa3d2d7164d3bc;p=postgis Add suport for SFCGAL 1.1. #3117. Apply patch written by Vincent Mora git-svn-id: http://svn.osgeo.org/postgis/trunk@13709 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/doc/reference_sfcgal.xml b/doc/reference_sfcgal.xml index 830d88027..860dd214e 100644 --- a/doc/reference_sfcgal.xml +++ b/doc/reference_sfcgal.xml @@ -449,6 +449,63 @@ FROM ST_GeomFromText('LINESTRING Z (2 2 6,1.5 1.5 7,1 1 8,0.5 0.5 8,0 0 10)') A + + + ST_3DDifference + + Perform 3D difference + + + + + + geometry ST_3DDifference + geometry geom1 + geometry geom2 + + + + + + Description + + Availability: 2.2.0 + &sfcgal_required; + &Z_support; + &P_support; + &T_support; + + + + + + ST_3DUnion + + Perform 3D union + + + + + + geometry ST_3DUnion + geometry geom1 + geometry geom2 + + + + + + Description + + Availability: 2.2.0 + &sfcgal_required; + &Z_support; + &P_support; + &T_support; + + + + ST_3DArea @@ -583,4 +640,90 @@ FROM ST_GeomFromText('LINESTRING Z (2 2 6,1.5 1.5 7,1 1 8,0.5 0.5 8,0 0 10)') A + + + ST_Volume + + Computes the volume. + + + + + + floatST_Volume + geometry geom1 + + + + + + Description + + Availability: 2.2.0 + &sfcgal_required; + &Z_support; + &P_support; + &T_support; + + + + + + + ST_MakeSolid + + Cast the geometry into a solid. No check is performed. To obtain a valid solid, the input geometry must be a closed Polyhedral Surface or a closed TIN. + + + + + + geometryST_MakeSolid + geometry geom1 + + + + + + Description + + Availability: 2.2.0 + &sfcgal_required; + &Z_support; + &P_support; + &T_support; + + + + + + + ST_IsSolid + + Test if the geometry is a solid. No validity check is performed. + + + + + + booleanST_IsSolid + geometry geom1 + + + + + + Description + + Availability: 2.2.0 + &sfcgal_required; + &Z_support; + &P_support; + &T_support; + + + + + + diff --git a/postgis/lwgeom_backend_api.c b/postgis/lwgeom_backend_api.c index 621a25fb4..2259869fa 100644 --- a/postgis/lwgeom_backend_api.c +++ b/postgis/lwgeom_backend_api.c @@ -29,6 +29,8 @@ Datum intersects(PG_FUNCTION_ARGS); Datum intersects3d(PG_FUNCTION_ARGS); Datum intersection(PG_FUNCTION_ARGS); +Datum difference(PG_FUNCTION_ARGS); +Datum geomunion(PG_FUNCTION_ARGS); Datum area(PG_FUNCTION_ARGS); Datum distance(PG_FUNCTION_ARGS); Datum distance3d(PG_FUNCTION_ARGS); @@ -42,6 +44,8 @@ struct lwgeom_backend_definition Datum (*intersects_fn) (PG_FUNCTION_ARGS); Datum (*intersects3d_fn) (PG_FUNCTION_ARGS); Datum (*intersection_fn) (PG_FUNCTION_ARGS); + Datum (*difference_fn) (PG_FUNCTION_ARGS); + Datum (*union_fn) (PG_FUNCTION_ARGS); Datum (*area_fn) (PG_FUNCTION_ARGS); Datum (*distance_fn) (PG_FUNCTION_ARGS); Datum (*distance3d_fn) (PG_FUNCTION_ARGS); @@ -58,6 +62,8 @@ struct lwgeom_backend_definition lwgeom_backends[LWGEOM_NUM_BACKENDS] = { .intersects_fn = geos_intersects, .intersects3d_fn = intersects3d_dwithin, .intersection_fn = geos_intersection, + .difference_fn = geos_difference, + .union_fn = geos_geomunion, .area_fn = LWGEOM_area_polygon, .distance_fn = LWGEOM_mindistance2d, .distance3d_fn = LWGEOM_mindistance3d @@ -67,6 +73,8 @@ struct lwgeom_backend_definition lwgeom_backends[LWGEOM_NUM_BACKENDS] = { .intersects_fn = sfcgal_intersects, .intersects3d_fn = sfcgal_intersects3D, .intersection_fn = sfcgal_intersection, + .difference_fn = sfcgal_difference, + .union_fn = sfcgal_union, .area_fn = sfcgal_area, .distance_fn = sfcgal_distance, .distance3d_fn = sfcgal_distance3D @@ -123,6 +131,18 @@ Datum intersection(PG_FUNCTION_ARGS) return (*lwgeom_backend->intersection_fn)( fcinfo ); } +PG_FUNCTION_INFO_V1(difference); +Datum difference(PG_FUNCTION_ARGS) +{ + return (*lwgeom_backend->difference_fn)( fcinfo ); +} + +PG_FUNCTION_INFO_V1(geomunion); +Datum geomunion(PG_FUNCTION_ARGS) +{ + return (*lwgeom_backend->union_fn)( fcinfo ); +} + PG_FUNCTION_INFO_V1(area); Datum area(PG_FUNCTION_ARGS) { diff --git a/postgis/lwgeom_geos.c b/postgis/lwgeom_geos.c index 9e5865c50..789030b28 100644 --- a/postgis/lwgeom_geos.c +++ b/postgis/lwgeom_geos.c @@ -67,10 +67,10 @@ Datum buffer(PG_FUNCTION_ARGS); Datum geos_intersection(PG_FUNCTION_ARGS); Datum convexhull(PG_FUNCTION_ARGS); Datum topologypreservesimplify(PG_FUNCTION_ARGS); -Datum difference(PG_FUNCTION_ARGS); +Datum geos_difference(PG_FUNCTION_ARGS); Datum boundary(PG_FUNCTION_ARGS); Datum symdifference(PG_FUNCTION_ARGS); -Datum geomunion(PG_FUNCTION_ARGS); +Datum geos_geomunion(PG_FUNCTION_ARGS); Datum issimple(PG_FUNCTION_ARGS); Datum isring(PG_FUNCTION_ARGS); Datum pointonsurface(PG_FUNCTION_ARGS); @@ -812,8 +812,8 @@ Datum ST_UnaryUnion(PG_FUNCTION_ARGS) * ); * */ -PG_FUNCTION_INFO_V1(geomunion); -Datum geomunion(PG_FUNCTION_ARGS) +PG_FUNCTION_INFO_V1(geos_geomunion); +Datum geos_geomunion(PG_FUNCTION_ARGS) { GSERIALIZED *geom1; GSERIALIZED *geom2; @@ -1463,8 +1463,8 @@ Datum geos_intersection(PG_FUNCTION_ARGS) * 'POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', * 'POLYGON((5 5, 15 5, 15 7, 5 7, 5 5))'); */ -PG_FUNCTION_INFO_V1(difference); -Datum difference(PG_FUNCTION_ARGS) +PG_FUNCTION_INFO_V1(geos_difference); +Datum geos_difference(PG_FUNCTION_ARGS) { GSERIALIZED *geom1; GSERIALIZED *geom2; diff --git a/postgis/lwgeom_geos.h b/postgis/lwgeom_geos.h index 793b5ae6a..5f9b8e45d 100644 --- a/postgis/lwgeom_geos.h +++ b/postgis/lwgeom_geos.h @@ -25,6 +25,8 @@ GEOSGeometry * POSTGIS2GEOS(GSERIALIZED *g); Datum geos_intersects(PG_FUNCTION_ARGS); Datum geos_intersection(PG_FUNCTION_ARGS); +Datum geos_difference(PG_FUNCTION_ARGS); +Datum geos_geomunion(PG_FUNCTION_ARGS); Datum LWGEOM_area_polygon(PG_FUNCTION_ARGS); Datum LWGEOM_mindistance2d(PG_FUNCTION_ARGS); Datum LWGEOM_mindistance3d(PG_FUNCTION_ARGS); diff --git a/postgis/lwgeom_sfcgal.c b/postgis/lwgeom_sfcgal.c index 18243da2a..1900c7011 100644 --- a/postgis/lwgeom_sfcgal.c +++ b/postgis/lwgeom_sfcgal.c @@ -32,6 +32,11 @@ Datum sfcgal_intersects(PG_FUNCTION_ARGS); Datum sfcgal_intersects3D(PG_FUNCTION_ARGS); Datum sfcgal_intersection(PG_FUNCTION_ARGS); Datum sfcgal_intersection3D(PG_FUNCTION_ARGS); +Datum sfcgal_difference(PG_FUNCTION_ARGS); +Datum sfcgal_difference3D(PG_FUNCTION_ARGS); +Datum sfcgal_union(PG_FUNCTION_ARGS); +Datum sfcgal_union3D(PG_FUNCTION_ARGS); +Datum sfcgal_volume(PG_FUNCTION_ARGS); Datum sfcgal_extrude(PG_FUNCTION_ARGS); Datum sfcgal_straight_skeleton(PG_FUNCTION_ARGS); Datum sfcgal_approximate_medial_axis(PG_FUNCTION_ARGS); @@ -41,6 +46,8 @@ Datum sfcgal_force_lhr(PG_FUNCTION_ARGS); Datum sfcgal_triangulate(PG_FUNCTION_ARGS); Datum sfcgal_tesselate(PG_FUNCTION_ARGS); Datum sfcgal_minkowski_sum(PG_FUNCTION_ARGS); +Datum sfcgal_make_solid(PG_FUNCTION_ARGS); +Datum sfcgal_is_solid(PG_FUNCTION_ARGS); GSERIALIZED *geometry_serialize(LWGEOM *lwgeom); @@ -506,6 +513,139 @@ Datum sfcgal_intersection3D(PG_FUNCTION_ARGS) PG_RETURN_POINTER(output); } +PG_FUNCTION_INFO_V1(sfcgal_difference); +Datum sfcgal_difference(PG_FUNCTION_ARGS) +{ + GSERIALIZED *input0, *input1, *output; + sfcgal_geometry_t *geom0, *geom1; + sfcgal_geometry_t *result; + srid_t srid; + + sfcgal_postgis_init(); + + input0 = (GSERIALIZED*) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + srid = gserialized_get_srid(input0); + input1 = (GSERIALIZED*) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + geom0 = POSTGIS2SFCGALGeometry(input0); + PG_FREE_IF_COPY(input0, 0); + geom1 = POSTGIS2SFCGALGeometry(input1); + PG_FREE_IF_COPY(input1, 1); + + result = sfcgal_geometry_difference(geom0, geom1); + sfcgal_geometry_delete(geom0); + sfcgal_geometry_delete(geom1); + + output = SFCGALGeometry2POSTGIS(result, 0, srid); + sfcgal_geometry_delete(result); + + PG_RETURN_POINTER(output); +} + + +PG_FUNCTION_INFO_V1(sfcgal_difference3D); +Datum sfcgal_difference3D(PG_FUNCTION_ARGS) +{ + GSERIALIZED *input0, *input1, *output; + sfcgal_geometry_t *geom0, *geom1; + sfcgal_geometry_t *result; + srid_t srid; + + sfcgal_postgis_init(); + + input0 = (GSERIALIZED*) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + srid = gserialized_get_srid(input0); + input1 = (GSERIALIZED*) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + geom0 = POSTGIS2SFCGALGeometry(input0); + PG_FREE_IF_COPY(input0, 0); + geom1 = POSTGIS2SFCGALGeometry(input1); + PG_FREE_IF_COPY(input1, 1); + + result = sfcgal_geometry_difference_3d(geom0, geom1); + sfcgal_geometry_delete(geom0); + sfcgal_geometry_delete(geom1); + + output = SFCGALGeometry2POSTGIS(result, 0, srid); + sfcgal_geometry_delete(result); + + PG_RETURN_POINTER(output); +} + +PG_FUNCTION_INFO_V1(sfcgal_union); +Datum sfcgal_union(PG_FUNCTION_ARGS) +{ + GSERIALIZED *input0, *input1, *output; + sfcgal_geometry_t *geom0, *geom1; + sfcgal_geometry_t *result; + srid_t srid; + + sfcgal_postgis_init(); + + input0 = (GSERIALIZED*) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + srid = gserialized_get_srid(input0); + input1 = (GSERIALIZED*) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + geom0 = POSTGIS2SFCGALGeometry(input0); + PG_FREE_IF_COPY(input0, 0); + geom1 = POSTGIS2SFCGALGeometry(input1); + PG_FREE_IF_COPY(input1, 1); + + result = sfcgal_geometry_union(geom0, geom1); + sfcgal_geometry_delete(geom0); + sfcgal_geometry_delete(geom1); + + output = SFCGALGeometry2POSTGIS(result, 0, srid); + sfcgal_geometry_delete(result); + + PG_RETURN_POINTER(output); +} + + +PG_FUNCTION_INFO_V1(sfcgal_union3D); +Datum sfcgal_union3D(PG_FUNCTION_ARGS) +{ + GSERIALIZED *input0, *input1, *output; + sfcgal_geometry_t *geom0, *geom1; + sfcgal_geometry_t *result; + srid_t srid; + + sfcgal_postgis_init(); + + input0 = (GSERIALIZED*) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + srid = gserialized_get_srid(input0); + input1 = (GSERIALIZED*) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + geom0 = POSTGIS2SFCGALGeometry(input0); + PG_FREE_IF_COPY(input0, 0); + geom1 = POSTGIS2SFCGALGeometry(input1); + PG_FREE_IF_COPY(input1, 1); + + result = sfcgal_geometry_union_3d(geom0, geom1); + sfcgal_geometry_delete(geom0); + sfcgal_geometry_delete(geom1); + + output = SFCGALGeometry2POSTGIS(result, 0, srid); + sfcgal_geometry_delete(result); + + PG_RETURN_POINTER(output); +} + +PG_FUNCTION_INFO_V1(sfcgal_volume); +Datum sfcgal_volume(PG_FUNCTION_ARGS) +{ + GSERIALIZED *input; + sfcgal_geometry_t *geom; + double result; + + sfcgal_postgis_init(); + + input = (GSERIALIZED*) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + geom = POSTGIS2SFCGALGeometry(input); + + result = sfcgal_geometry_volume(geom); + sfcgal_geometry_delete(geom); + + PG_FREE_IF_COPY(input, 0); + + PG_RETURN_FLOAT8(result); +} PG_FUNCTION_INFO_V1(sfcgal_minkowski_sum); Datum sfcgal_minkowski_sum(PG_FUNCTION_ARGS) @@ -574,3 +714,41 @@ Datum postgis_sfcgal_version(PG_FUNCTION_ARGS) PG_RETURN_POINTER(result); } +PG_FUNCTION_INFO_V1(sfcgal_is_solid); +Datum sfcgal_is_solid(PG_FUNCTION_ARGS) +{ + int result; + GSERIALIZED *input = PG_GETARG_GSERIALIZED_P(0); + LWGEOM *lwgeom = lwgeom_from_gserialized(input); + PG_FREE_IF_COPY(input, 0); + if (! lwgeom) + { + lwerror("sfcgal_is_solid: Unable to deserialize input"); + } + result = FLAGS_GET_SOLID( lwgeom->flags ); + + lwgeom_free(lwgeom); + + PG_RETURN_BOOL(result); +} + +PG_FUNCTION_INFO_V1(sfcgal_make_solid); +Datum sfcgal_make_solid(PG_FUNCTION_ARGS) +{ + GSERIALIZED *output; + GSERIALIZED *input = PG_GETARG_GSERIALIZED_P(0); + LWGEOM *lwgeom = lwgeom_from_gserialized(input); + PG_FREE_IF_COPY(input, 0); + if (! lwgeom) + { + lwerror("sfcgal_make_solid: Unable to deserialize input"); + } + + FLAGS_SET_SOLID( lwgeom->flags, 1); + + output = geometry_serialize( lwgeom ); + lwgeom_free(lwgeom); + + PG_RETURN_POINTER(output); +} + diff --git a/postgis/lwgeom_sfcgal.h b/postgis/lwgeom_sfcgal.h index 52ad40a15..067a277d8 100644 --- a/postgis/lwgeom_sfcgal.h +++ b/postgis/lwgeom_sfcgal.h @@ -29,10 +29,17 @@ GSERIALIZED* SFCGALPreparedGeometry2POSTGIS( const sfcgal_prepared_geometry_t* g Datum sfcgal_intersects(PG_FUNCTION_ARGS); Datum sfcgal_intersects3D(PG_FUNCTION_ARGS); Datum sfcgal_intersection(PG_FUNCTION_ARGS); +Datum sfcgal_difference3D(PG_FUNCTION_ARGS); +Datum sfcgal_difference(PG_FUNCTION_ARGS); +Datum sfcgal_union3D(PG_FUNCTION_ARGS); +Datum sfcgal_union(PG_FUNCTION_ARGS); +Datum sfcgal_volume(PG_FUNCTION_ARGS); Datum sfcgal_triangulate(PG_FUNCTION_ARGS); Datum sfcgal_area(PG_FUNCTION_ARGS); Datum sfcgal_distance(PG_FUNCTION_ARGS); Datum sfcgal_distance3D(PG_FUNCTION_ARGS); +Datum sfcgal_make_solid(PG_FUNCTION_ARGS); +Datum sfcgal_is_solid(PG_FUNCTION_ARGS); /* Initialize sfcgal with PostGIS error handlers */ diff --git a/postgis/sfcgal.sql.in b/postgis/sfcgal.sql.in index 89e291388..7b40dc2eb 100644 --- a/postgis/sfcgal.sql.in +++ b/postgis/sfcgal.sql.in @@ -29,6 +29,20 @@ CREATE OR REPLACE FUNCTION ST_3DIntersection(geom1 geometry, geom2 geometry) AS 'MODULE_PATHNAME','sfcgal_intersection3D' LANGUAGE 'c' IMMUTABLE STRICT COST 100; + +-- Availability: 2.2 +CREATE OR REPLACE FUNCTION ST_3DDifference(geom1 geometry, geom2 geometry) + RETURNS geometry + AS 'MODULE_PATHNAME','sfcgal_difference3D' + LANGUAGE 'c' IMMUTABLE STRICT + COST 100; + +-- Availability: 2.2 +CREATE OR REPLACE FUNCTION ST_3DUnion(geom1 geometry, geom2 geometry) + RETURNS geometry + AS 'MODULE_PATHNAME','sfcgal_union3D' + LANGUAGE 'c' IMMUTABLE STRICT + COST 100; -- Availability: 2.1.0 CREATE OR REPLACE FUNCTION ST_Tesselate(geometry) @@ -116,4 +130,25 @@ CREATE OR REPLACE FUNCTION ST_IsPlanar(geometry) LANGUAGE 'c' IMMUTABLE STRICT COST 100; +-- Availability: 2.2 +CREATE OR REPLACE FUNCTION ST_Volume(geometry) + RETURNS FLOAT8 + AS 'MODULE_PATHNAME','sfcgal_volume' + LANGUAGE 'c' IMMUTABLE STRICT + COST 100; + +-- Availability: 2.2 +CREATE OR REPLACE FUNCTION ST_MakeSolid(geometry) + RETURNS geometry + AS 'MODULE_PATHNAME','sfcgal_make_solid' + LANGUAGE 'c' IMMUTABLE STRICT + COST 100; + +-- Availability: 2.2 +CREATE OR REPLACE FUNCTION ST_IsSolid(geometry) + RETURNS boolean + AS 'MODULE_PATHNAME','sfcgal_is_solid' + LANGUAGE 'c' IMMUTABLE STRICT + COST 100; + COMMIT; diff --git a/regress/sfcgal/concave_hull.sql b/regress/sfcgal/concave_hull.sql index 61992becf..2a4754834 100644 --- a/regress/sfcgal/concave_hull.sql +++ b/regress/sfcgal/concave_hull.sql @@ -2,9 +2,11 @@ SET postgis.backend = 'sfcgal'; -- Tests to confirm the concave hull area is <= convex hull and -- covers the original geometry (can't use covers because always gives topo errors with 3.3 +-- vmora: the small tolerance for area comes from the reordering of the geometry that +-- cause small errors on area to cummulate (difference of 5e-13 on 2224 in this case) SELECT 'ST_ConcaveHull MultiPolygon 0.95', ST_Area(ST_Intersection(geom,ST_ConcaveHull( - geom, 0.95) )) = ST_Area(geom) As encloses_geom, + geom, 0.95) )) - ST_Area(geom) < 1e-9 As encloses_geom, (ST_Area(ST_ConvexHull(geom)) - ST_Area(ST_ConcaveHull(geom, 0.95))) < (0.95 * ST_Area(ST_ConvexHull(geom) ) ) As reached_target FROM ST_Union(ST_GeomFromText('POLYGON((175 150, 20 40, diff --git a/regress/sfcgal/empty.sql b/regress/sfcgal/empty.sql index 0cb44f74e..758aaea34 100644 --- a/regress/sfcgal/empty.sql +++ b/regress/sfcgal/empty.sql @@ -42,7 +42,7 @@ WITH inp AS (SELECT WITH inp AS (SELECT 'POLYGON EMPTY'::geometry as empty, 'POLYGON((0 0, 10 0, 5 5, 0 0))'::geometry as geometry - ) SELECT 'ST_Union(geometry, empty) == geometry', ST_AsEWKT(ST_Union(geometry, empty)) FROM inp; + ) SELECT 'ST_Union(geometry, empty) == geometry', ST_Equals(ST_ExteriorRing(ST_Union(geometry, empty)), 'LINESTRING(0 0, 10 0, 5 5, 0 0)'::geometry) FROM inp; WITH inp AS (SELECT 'POLYGON EMPTY'::geometry as empty ) SELECT 'ST_Union(empty, empty) == empty', ST_IsEmpty(ST_Union(empty, empty)) FROM inp; @@ -56,7 +56,7 @@ WITH inp AS (SELECT WITH inp AS (SELECT 'POLYGON EMPTY'::geometry as empty, 'POLYGON((0 0, 10 0, 5 5, 0 0))'::geometry as geometry - ) SELECT 'ST_Difference(geometry, empty) == geometry', ST_AsEWKT(ST_Difference(geometry, empty)) FROM inp; + ) SELECT 'ST_Difference(geometry, empty) == geometry', ST_Equals(ST_ExteriorRing(ST_Difference(geometry, empty)), 'LINESTRING(0 0, 10 0, 5 5, 0 0)'::geometry) FROM inp; WITH inp AS (SELECT 'POLYGON EMPTY'::geometry as empty, 'POLYGON((0 0, 10 0, 5 5, 0 0))'::geometry as geometry diff --git a/regress/sfcgal/empty_expected b/regress/sfcgal/empty_expected index cde19e329..866824a23 100644 --- a/regress/sfcgal/empty_expected +++ b/regress/sfcgal/empty_expected @@ -24,11 +24,11 @@ T3.16| T3.17| T3.18| ST_Buffer(empty, tolerance) == empty|t -ST_Union(geometry, empty) == geometry|POLYGON((0 0,10 0,5 5,0 0)) +ST_Union(geometry, empty) == geometry|t ST_Union(empty, empty) == empty|t ST_Intersection(geometry, empty) == geometry|t ST_Intersection(empty, empty) == empty|t -ST_Difference(geometry, empty) == geometry|POLYGON((0 0,10 0,5 5,0 0)) +ST_Difference(geometry, empty) == geometry|t ST_Difference(empty, geometry) == empty|t ST_Distance(geometry, empty) == NULL|inf ST_DWithin(geometry, empty, tolerance) == FALSE|f diff --git a/regress/sfcgal/regress_ogc_expected b/regress/sfcgal/regress_ogc_expected index cd05535d0..01cf20ce5 100644 --- a/regress/sfcgal/regress_ogc_expected +++ b/regress/sfcgal/regress_ogc_expected @@ -63,7 +63,7 @@ NOTICE: Self-intersection isvalid|f isvalid|t intersection|POINT(-0 -0) -difference|MULTILINESTRING((0 10,0 2),(0 -2,0 -10)) +difference|MULTILINESTRING((0 -2,0 -10),(0 10,0 2)) boundary|MULTILINESTRING((0 0,0 10,10 10,10 0,0 0),(2 2,2 4,4 4,4 2,2 2)) symdifference|GEOMETRYCOLLECTION(LINESTRING(2 2,4 4),LINESTRING(10 10,20 20),POLYGON((0 0,0 10,10 10,10 0,0 0),(4 4,2 4,2 2,4 2,4 4))) issimple|t