From: Sandro Santilli Date: Wed, 22 Apr 2015 15:25:32 +0000 (+0000) Subject: Allow splitting lines by multilines and (multi)polygon boundaries X-Git-Tag: 2.2.0rc1~555 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fab1facae84b5e2f08a374c938a368936f4e54e0;p=postgis Allow splitting lines by multilines and (multi)polygon boundaries Funded by Tuscany Region (Italy) - SITA (CIG: 6002233F59) git-svn-id: http://svn.osgeo.org/postgis/trunk@13433 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/NEWS b/NEWS index 19b1f4b7b..920c55eed 100644 --- a/NEWS +++ b/NEWS @@ -63,6 +63,7 @@ PostGIS 2.2.0 * Enhancements * + - Allow splitting lines by multilines and (multi)polygon boundaries - #3070, Simplify geometry type constraint - #2839, Implement selectivity estimator for functional indexes, speeding up spatial queries on raster tables. @@ -832,7 +833,7 @@ PostGIS 1.5.3 - #457 ST_CollectionExtract returns non-requested type (Nicklas Avén, Paul Ramsey) - #441 ST_AsGeoJson Bbox on GeometryCollection error (Olivier Courtin) - - #411 Ability to backup invalid geometries (Sando Santilli) + - #411 Ability to backup invalid geometries (Sandro Santilli) Reported by Regione Toscana - #409 ST_AsSVG - degraded (Olivier Courtin) Reported by Sdikiy diff --git a/doc/reference_processing.xml b/doc/reference_processing.xml index 8ded1792d..e6e60e6ad 100644 --- a/doc/reference_processing.xml +++ b/doc/reference_processing.xml @@ -2868,7 +2868,7 @@ LINESTRING M (5 2 3.40282346638529e+38,3 8 29,6 20 1.5,7 25 49.5,10 10 3.4028234 Description - The function supports splitting a line by point, a line by line, a polygon by line. The returned geometry is always a collection. + The function supports splitting a line by point, a line by (multi)line or (multi)polygon boundary, a polygon by line. The returned geometry is always a collection. @@ -2878,7 +2878,14 @@ LINESTRING M (5 2 3.40282346638529e+38,3 8 29,6 20 1.5,7 25 49.5,10 10 3.4028234 Availability: 2.0.0 + Changed: 2.2.0 support for splitting a line by a multiline or (multi)polygon boundary was introduced. + To improve the robustness of ST_Split it may be convenient to the input to the blade in advance using a very low tolerance. Otherwise the internally used coordinate grid may cause tolerance problems, where coordinates of input and blade do not fall onto each other and the input is not being split correctly (see #2192). + + +When a (multi)polygon is passed as as the blade, its linear component +(the boundary) is used for cutting the input. + diff --git a/liblwgeom/cunit/cu_split.c b/liblwgeom/cunit/cu_split.c index 2f0646b7f..2a8fcdb1a 100644 --- a/liblwgeom/cunit/cu_split.c +++ b/liblwgeom/cunit/cu_split.c @@ -3,7 +3,7 @@ * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * - * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2011-2015 Sandro Santilli * * This is free software; you can redistribute and/or modify it under * the terms of the GNU General Public Licence. See the COPYING file. @@ -154,6 +154,83 @@ static void test_lwgeom_split(void) lwgeom_free(ret); lwgeom_free(geom); lwgeom_free(blade); + + /* Split line by multiline */ + geom = lwgeom_from_wkt("LINESTRING(0 0, 10 0)", LW_PARSER_CHECK_NONE); + CU_ASSERT_FATAL(geom != NULL); + blade = lwgeom_from_wkt("MULTILINESTRING((1 1,1 -1),(2 1,2 -1,3 -1,3 1))", + LW_PARSER_CHECK_NONE); + ret = lwgeom_split(geom, blade); + if ( ! ret ) printf("%s", cu_error_msg); + CU_ASSERT_FATAL(ret != NULL); + wkt = lwgeom_to_ewkt(ret); + CU_ASSERT_FATAL(wkt != NULL); + in_wkt = "GEOMETRYCOLLECTION(LINESTRING(0 0,1 0),LINESTRING(1 0,2 0),LINESTRING(2 0,3 0),LINESTRING(3 0,10 0))"; + if (strcmp(in_wkt, wkt)) + fprintf(stderr, "\nExp: %s\nObt: %s\n", in_wkt, wkt); + CU_ASSERT_STRING_EQUAL(wkt, in_wkt); + lwfree(wkt); + lwgeom_free(ret); + lwgeom_free(geom); + lwgeom_free(blade); + + /* Split line by polygon (boundary) */ + geom = lwgeom_from_wkt("LINESTRING(0 0, 10 0)", LW_PARSER_CHECK_NONE); + CU_ASSERT_FATAL(geom != NULL); + blade = lwgeom_from_wkt( +"POLYGON((1 -2,1 1,2 1,2 -1,3 -1,3 1,11 1,11 -2,1 -2))", + LW_PARSER_CHECK_NONE); + ret = lwgeom_split(geom, blade); + if ( ! ret ) printf("%s", cu_error_msg); + CU_ASSERT_FATAL(ret != NULL); + wkt = lwgeom_to_ewkt(ret); + CU_ASSERT_FATAL(wkt != NULL); + in_wkt = "GEOMETRYCOLLECTION(LINESTRING(0 0,1 0),LINESTRING(1 0,2 0),LINESTRING(2 0,3 0),LINESTRING(3 0,10 0))"; + if (strcmp(in_wkt, wkt)) + fprintf(stderr, "\nExp: %s\nObt: %s\n", in_wkt, wkt); + CU_ASSERT_STRING_EQUAL(wkt, in_wkt); + lwfree(wkt); + lwgeom_free(ret); + lwgeom_free(geom); + lwgeom_free(blade); + + /* Split line by EMPTY polygon (boundary) */ + geom = lwgeom_from_wkt("LINESTRING(0 0, 10 0)", LW_PARSER_CHECK_NONE); + CU_ASSERT_FATAL(geom != NULL); + blade = lwgeom_from_wkt("POLYGON EMPTY", LW_PARSER_CHECK_NONE); + ret = lwgeom_split(geom, blade); + if ( ! ret ) printf("%s", cu_error_msg); + CU_ASSERT_FATAL(ret != NULL); + wkt = lwgeom_to_ewkt(ret); + CU_ASSERT_FATAL(wkt != NULL); + in_wkt = "GEOMETRYCOLLECTION(LINESTRING(0 0,10 0))"; + if (strcmp(in_wkt, wkt)) + fprintf(stderr, "\nExp: %s\nObt: %s\n", in_wkt, wkt); + CU_ASSERT_STRING_EQUAL(wkt, in_wkt); + lwfree(wkt); + lwgeom_free(ret); + lwgeom_free(geom); + lwgeom_free(blade); + + /* Split line by multipolygon (boundary) */ + geom = lwgeom_from_wkt("LINESTRING(0 0, 10 0)", LW_PARSER_CHECK_NONE); + CU_ASSERT_FATAL(geom != NULL); + blade = lwgeom_from_wkt( +"MULTIPOLYGON(((1 -1,1 1,2 1,2 -1,1 -1)),((3 -1,3 1,11 1,11 -1,3 -1)))", + LW_PARSER_CHECK_NONE); + ret = lwgeom_split(geom, blade); + if ( ! ret ) printf("%s", cu_error_msg); + CU_ASSERT_FATAL(ret != NULL); + wkt = lwgeom_to_ewkt(ret); + CU_ASSERT_FATAL(wkt != NULL); + in_wkt = "GEOMETRYCOLLECTION(LINESTRING(0 0,1 0),LINESTRING(1 0,2 0),LINESTRING(2 0,3 0),LINESTRING(3 0,10 0))"; + if (strcmp(in_wkt, wkt)) + fprintf(stderr, "\nExp: %s\nObt: %s\n", in_wkt, wkt); + CU_ASSERT_STRING_EQUAL(wkt, in_wkt); + lwfree(wkt); + lwgeom_free(ret); + lwgeom_free(geom); + lwgeom_free(blade); } diff --git a/liblwgeom/lwgeom_geos_split.c b/liblwgeom/lwgeom_geos_split.c index 3687be637..89d66816d 100644 --- a/liblwgeom/lwgeom_geos_split.c +++ b/liblwgeom/lwgeom_geos_split.c @@ -4,14 +4,15 @@ * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * - * Copyright 2009-2010 Sandro Santilli + * Copyright 2011-2015 Sandro Santilli * * This is free software; you can redistribute and/or modify it under * the terms of the GNU General Public Licence. See the COPYING file. * ********************************************************************** * - * Split polygon by line, line by line, line by point. + * Split polygon by line, line by (multi)line or (multi)polygon boundary, + * line by point. * Returns at most components as a collection. * First element of the collection is always the part which * remains after the cut, while the second element is the @@ -40,7 +41,7 @@ #include #include -static LWGEOM* lwline_split_by_line(const LWLINE* lwgeom_in, const LWLINE* blade_in); +static LWGEOM* lwline_split_by_line(const LWLINE* lwgeom_in, const LWGEOM* blade_in); static LWGEOM* lwline_split_by_point(const LWLINE* lwgeom_in, const LWPOINT* blade_in); static LWGEOM* lwline_split(const LWLINE* lwgeom_in, const LWGEOM* blade_in); static LWGEOM* lwpoly_split_by_line(const LWPOLY* lwgeom_in, const LWLINE* blade_in); @@ -49,7 +50,7 @@ static LWGEOM* lwpoly_split(const LWPOLY* lwpoly_in, const LWGEOM* blade_in); /* Initializes and uses GEOS internally */ static LWGEOM* -lwline_split_by_line(const LWLINE* lwline_in, const LWLINE* blade_in) +lwline_split_by_line(const LWLINE* lwline_in, const LWGEOM* blade_in) { LWGEOM** components; LWGEOM* diff; @@ -59,6 +60,12 @@ lwline_split_by_line(const LWLINE* lwline_in, const LWLINE* blade_in) GEOSGeometry* g2; int ret; + /* ASSERT blade_in is LINE or MULTILINE */ + assert (blade_in->type == LINETYPE || + blade_in->type == MULTILINETYPE || + blade_in->type == POLYGONTYPE || + blade_in->type == MULTIPOLYGONTYPE ); + /* Possible outcomes: * * 1. The lines do not cross or overlap @@ -75,7 +82,7 @@ lwline_split_by_line(const LWLINE* lwline_in, const LWLINE* blade_in) lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg); return NULL; } - g2 = LWGEOM2GEOS((LWGEOM*)blade_in, 0); + g2 = LWGEOM2GEOS(blade_in, 0); if ( ! g2 ) { GEOSGeom_destroy(g1); @@ -83,6 +90,20 @@ lwline_split_by_line(const LWLINE* lwline_in, const LWLINE* blade_in) return NULL; } + /* If blade is a polygon, pick its boundary */ + if ( blade_in->type == POLYGONTYPE || blade_in->type == MULTIPOLYGONTYPE ) + { + gdiff = GEOSBoundary(g2); + GEOSGeom_destroy(g2); + if ( ! gdiff ) + { + GEOSGeom_destroy(g1); + lwerror("GEOSBoundary: %s", lwgeom_geos_errmsg); + return NULL; + } + g2 = gdiff; gdiff = NULL; + } + /* If interior intersecton is linear we can't split */ ret = GEOSRelatePattern(g1, g2, "1********"); if ( 2 == ret ) @@ -228,7 +249,10 @@ lwline_split(const LWLINE* lwline_in, const LWGEOM* blade_in) return lwline_split_by_point(lwline_in, (LWPOINT*)blade_in); case LINETYPE: - return lwline_split_by_line(lwline_in, (LWLINE*)blade_in); + case MULTILINETYPE: + case POLYGONTYPE: + case MULTIPOLYGONTYPE: + return lwline_split_by_line(lwline_in, blade_in); default: lwerror("Splitting a Line by a %s is unsupported", diff --git a/regress/split.sql b/regress/split.sql index 6567bc314..1906fdc53 100644 --- a/regress/split.sql +++ b/regress/split.sql @@ -78,4 +78,22 @@ select '82', st_equals(g, st_union( st_geometryn(st_split(g, st_pointn(g,p)), 1), st_geometryn(st_split(g, st_pointn(g,p)), 2))) from inp; +-- Split line by multiline +select '83', st_asewkt(ST_Split( + 'SRID=3;LINESTRING(1 -1,1 1)', + 'SRID=3;MULTILINESTRING((10 0, 10 4),(-4 0, 4 0))' +)); + +-- Split line by polygon (boundary) +select '84', st_asewkt(ST_Split( + 'SRID=3;LINESTRING(1 -1,1 1)', + 'SRID=3;POLYGON((-10 -10,-10 10,10 10,10 -10,-10 -10),(-4 2,-4 0,4 0,4 2,-4 2))' +)); + +-- Split line by multipolygon (boundary) +select '85', st_asewkt(ST_Split( + 'SRID=3;LINESTRING(1 -2,1 1,4 1)', + 'SRID=3;MULTIPOLYGON(((0 -1,0 -3,2 -3,2 -1,0 -1)),((3 0,3 2,5 2,5 0,3 0)))' +)); + -- TODO: split line by collapsed line diff --git a/regress/split_expected b/regress/split_expected index 94234e604..e4ebfee10 100644 --- a/regress/split_expected +++ b/regress/split_expected @@ -24,3 +24,6 @@ ERROR: Splitter line has linear intersection with input 80|GEOMETRYCOLLECTION(LINESTRING(0 1,0 1,0 1)) 81|GEOMETRYCOLLECTION(LINESTRING(0 1,0 1)) 82|t +83|SRID=3;GEOMETRYCOLLECTION(LINESTRING(1 -1,1 0),LINESTRING(1 0,1 1)) +84|SRID=3;GEOMETRYCOLLECTION(LINESTRING(1 -1,1 0),LINESTRING(1 0,1 1)) +85|SRID=3;GEOMETRYCOLLECTION(LINESTRING(1 -2,1 -1),LINESTRING(1 -1,1 1,3 1),LINESTRING(3 1,4 1))