]> granicus.if.org Git - postgis/commitdiff
Allow splitting lines by multilines and (multi)polygon boundaries
authorSandro Santilli <strk@keybit.net>
Wed, 22 Apr 2015 15:25:32 +0000 (15:25 +0000)
committerSandro Santilli <strk@keybit.net>
Wed, 22 Apr 2015 15:25:32 +0000 (15:25 +0000)
Funded by Tuscany Region (Italy) - SITA (CIG: 6002233F59)

git-svn-id: http://svn.osgeo.org/postgis/trunk@13433 b70326c6-7e19-0410-871a-916f4a2858ee

NEWS
doc/reference_processing.xml
liblwgeom/cunit/cu_split.c
liblwgeom/lwgeom_geos_split.c
regress/split.sql
regress/split_expected

diff --git a/NEWS b/NEWS
index 19b1f4b7be3e20760a3ad2cf5a5a87f9b5ea9ee1..920c55eed58371c1c3d76255763b315a147fa214 100644 (file)
--- 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
index 8ded1792dca37b606228aae4815b6ab313ebe6b7..e6e60e6adf4701e9dd66d908e0fb01efe9231f6e 100644 (file)
@@ -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
         <refsection>
             <title>Description</title>
             <para>
-            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.
             </para>
             
             <para>
@@ -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
             </para>
             
             <para>Availability: 2.0.0</para>
+            <para>Changed: 2.2.0 support for splitting a line by a multiline or (multi)polygon boundary was introduced.</para>
+
             <note><para>To improve the robustness of ST_Split it may be convenient to <xref linkend="ST_Snap"/> 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 <ulink url="http://trac.osgeo.org/postgis/ticket/2192">#2192</ulink>).</para></note> 
+
+            <note><para>
+When a (multi)polygon is passed as as the blade, its linear component
+(the boundary) is used for cutting the input.
+            </para></note>
         
         </refsection>
         <refsection>
index 2f0646b7f6ca26ea5301158918bbbfac422c49b7..2a8fcdb1ac7f51aee64c295c501e9f809f28d3b3 100644 (file)
@@ -3,7 +3,7 @@
  * PostGIS - Spatial Types for PostgreSQL
  * http://postgis.net
  *
- * Copyright (C) 2011 Sandro Santilli <strk@keybit.net>
+ * Copyright (C) 2011-2015 Sandro Santilli <strk@keybit.net>
  *
  * 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);
 }
 
 
index 3687be637663e57411a3f1470171a1c1a300734a..89d66816d2725ef90124d601414aa5b9e0c9d394 100644 (file)
@@ -4,14 +4,15 @@
  * PostGIS - Spatial Types for PostgreSQL
  * http://postgis.net
  *
- * Copyright 2009-2010 Sandro Santilli <strk@keybit.net>
+ * Copyright 2011-2015 Sandro Santilli <strk@keybit.net>
  *
  * 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 <string.h>
 #include <assert.h>
 
-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",
index 6567bc3146823fa6d95982ba6c2d2972ab38d2de..1906fdc533da3d715d7f0d073ad77b2ac26ba330 100644 (file)
@@ -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 
index 94234e604073beb941171ad2d900b0f452e3c92d..e4ebfee102276af5e09897f6eab19a8ac343a4bd 100644 (file)
@@ -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))