* 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.
- #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
<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>
</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>
* 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.
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);
}
* 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
#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);
/* 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;
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
lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg);
return NULL;
}
- g2 = LWGEOM2GEOS((LWGEOM*)blade_in, 0);
+ g2 = LWGEOM2GEOS(blade_in, 0);
if ( ! g2 )
{
GEOSGeom_destroy(g1);
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 )
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",
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
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))