2010-03-10 15:33:33 +00:00
|
|
|
/**********************************************************************
|
|
|
|
* $Id: lwgeom_geos.c 5258 2010-02-17 21:02:49Z strk $
|
|
|
|
*
|
|
|
|
* PostGIS - Spatial Types for PostgreSQL
|
2014-01-29 17:49:35 +00:00
|
|
|
* http://postgis.net
|
2010-03-10 15:33:33 +00:00
|
|
|
*
|
|
|
|
* Copyright 2009-2010 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.
|
2010-03-11 20:34:01 +00:00
|
|
|
* 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
|
|
|
|
* part which has been cut out. We arbitrarely take the part
|
|
|
|
* on the *right* of cut lines as the part which has been cut out.
|
|
|
|
* For a line cut by a point the part which remains is the one
|
|
|
|
* from start of the line to the cut point.
|
|
|
|
*
|
2010-03-10 15:33:33 +00:00
|
|
|
*
|
|
|
|
* Author: Sandro Santilli <strk@keybit.net>
|
|
|
|
*
|
|
|
|
* Work done for Faunalia (http://www.faunalia.it) with fundings
|
|
|
|
* from Regione Toscana - Sistema Informativo per il Governo
|
|
|
|
* del Territorio e dell'Ambiente (RT-SIGTA).
|
|
|
|
*
|
|
|
|
* Thanks to the PostGIS community for sharing poly/line ideas [1]
|
|
|
|
*
|
|
|
|
* [1] http://trac.osgeo.org/postgis/wiki/UsersWikiSplitPolygonWithLineString
|
|
|
|
*
|
|
|
|
*
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
|
|
#include "lwgeom_geos.h"
|
2011-08-08 09:27:09 +00:00
|
|
|
#include "liblwgeom_internal.h"
|
2010-03-10 15:33:33 +00:00
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
2011-08-11 08:04:20 +00:00
|
|
|
static LWGEOM* lwline_split_by_line(const LWLINE* lwgeom_in, const LWLINE* 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);
|
|
|
|
static LWGEOM* lwcollection_split(const LWCOLLECTION* lwcoll_in, const LWGEOM* blade_in);
|
|
|
|
static LWGEOM* lwpoly_split(const LWPOLY* lwpoly_in, const LWGEOM* blade_in);
|
2010-03-16 22:18:59 +00:00
|
|
|
|
|
|
|
/* Initializes and uses GEOS internally */
|
2010-03-13 10:59:19 +00:00
|
|
|
static LWGEOM*
|
2011-08-11 08:04:20 +00:00
|
|
|
lwline_split_by_line(const LWLINE* lwline_in, const LWLINE* blade_in)
|
2010-03-13 10:59:19 +00:00
|
|
|
{
|
|
|
|
LWGEOM** components;
|
|
|
|
LWGEOM* diff;
|
|
|
|
LWCOLLECTION* out;
|
|
|
|
GEOSGeometry* gdiff; /* difference */
|
2010-08-15 08:30:08 +00:00
|
|
|
GEOSGeometry* g1;
|
|
|
|
GEOSGeometry* g2;
|
2010-03-13 12:55:00 +00:00
|
|
|
int ret;
|
2010-03-13 10:59:19 +00:00
|
|
|
|
|
|
|
/* Possible outcomes:
|
|
|
|
*
|
2010-08-15 08:30:08 +00:00
|
|
|
* 1. The lines do not cross or overlap
|
2010-03-13 10:59:19 +00:00
|
|
|
* -> Return a collection with single element
|
|
|
|
* 2. The lines cross
|
2010-03-15 18:00:12 +00:00
|
|
|
* -> Return a collection of all elements resulting from the split
|
2010-03-13 10:59:19 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
initGEOS(lwgeom_geos_error, lwgeom_geos_error);
|
|
|
|
|
2014-10-01 13:13:46 +00:00
|
|
|
g1 = LWGEOM2GEOS((LWGEOM*)lwline_in, 0);
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! g1 )
|
|
|
|
{
|
2010-03-13 10:59:19 +00:00
|
|
|
lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-10-01 13:13:46 +00:00
|
|
|
g2 = LWGEOM2GEOS((LWGEOM*)blade_in, 0);
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! g2 )
|
|
|
|
{
|
2010-03-13 10:59:19 +00:00
|
|
|
GEOSGeom_destroy(g1);
|
|
|
|
lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-03-13 12:55:00 +00:00
|
|
|
/* If interior intersecton is linear we can't split */
|
|
|
|
ret = GEOSRelatePattern(g1, g2, "1********");
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( 2 == ret )
|
|
|
|
{
|
2010-03-13 12:55:00 +00:00
|
|
|
lwerror("GEOSRelatePattern: %s", lwgeom_geos_errmsg);
|
2010-03-13 11:16:53 +00:00
|
|
|
GEOSGeom_destroy(g1);
|
2010-03-13 12:55:00 +00:00
|
|
|
GEOSGeom_destroy(g2);
|
2010-03-13 11:16:53 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ret )
|
|
|
|
{
|
2010-03-13 11:16:53 +00:00
|
|
|
GEOSGeom_destroy(g1);
|
|
|
|
GEOSGeom_destroy(g2);
|
2010-03-13 12:55:00 +00:00
|
|
|
lwerror("Splitter line has linear intersection with input");
|
2010-03-13 11:16:53 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-13 12:55:00 +00:00
|
|
|
gdiff = GEOSDifference(g1,g2);
|
2010-03-13 11:16:53 +00:00
|
|
|
GEOSGeom_destroy(g1);
|
|
|
|
GEOSGeom_destroy(g2);
|
2010-08-15 08:30:08 +00:00
|
|
|
if (gdiff == NULL)
|
|
|
|
{
|
2010-03-13 12:55:00 +00:00
|
|
|
lwerror("GEOSDifference: %s", lwgeom_geos_errmsg);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-03-13 11:16:53 +00:00
|
|
|
|
2010-11-21 19:02:23 +00:00
|
|
|
diff = GEOS2LWGEOM(gdiff, FLAGS_GET_Z(lwline_in->flags));
|
2010-03-13 10:59:19 +00:00
|
|
|
GEOSGeom_destroy(gdiff);
|
2010-08-15 08:30:08 +00:00
|
|
|
if (NULL == diff)
|
|
|
|
{
|
2010-03-13 10:59:19 +00:00
|
|
|
lwerror("GEOS2LWGEOM: %s", lwgeom_geos_errmsg);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-11-06 09:39:29 +00:00
|
|
|
out = lwgeom_as_lwcollection(diff);
|
|
|
|
if ( ! out )
|
2010-03-13 10:59:19 +00:00
|
|
|
{
|
|
|
|
components = lwalloc(sizeof(LWGEOM*)*1);
|
|
|
|
components[0] = diff;
|
2010-11-25 17:34:21 +00:00
|
|
|
out = lwcollection_construct(COLLECTIONTYPE, lwline_in->srid,
|
2010-08-15 08:30:08 +00:00
|
|
|
NULL, 1, components);
|
2010-03-13 10:59:19 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-11-06 09:39:29 +00:00
|
|
|
/* Set SRID */
|
|
|
|
lwgeom_set_srid((LWGEOM*)out, lwline_in->srid);
|
|
|
|
/* Force collection type */
|
|
|
|
out->type = COLLECTIONTYPE;
|
2010-03-13 10:59:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (LWGEOM*)out;
|
|
|
|
}
|
|
|
|
|
2010-03-10 15:33:33 +00:00
|
|
|
static LWGEOM*
|
2011-08-11 08:04:20 +00:00
|
|
|
lwline_split_by_point(const LWLINE* lwline_in, const LWPOINT* blade_in)
|
2011-11-03 16:20:39 +00:00
|
|
|
{
|
|
|
|
LWMLINE* out;
|
|
|
|
|
|
|
|
out = lwmline_construct_empty(lwline_in->srid,
|
|
|
|
FLAGS_GET_Z(lwline_in->flags),
|
|
|
|
FLAGS_GET_M(lwline_in->flags));
|
|
|
|
if ( lwline_split_by_point_to(lwline_in, blade_in, out) < 2 )
|
|
|
|
{
|
|
|
|
lwmline_add_lwline(out, lwline_clone(lwline_in));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Turn multiline into collection */
|
|
|
|
out->type = COLLECTIONTYPE;
|
|
|
|
|
|
|
|
return (LWGEOM*)out;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
lwline_split_by_point_to(const LWLINE* lwline_in, const LWPOINT* blade_in,
|
|
|
|
LWMLINE* v)
|
2010-03-10 15:33:33 +00:00
|
|
|
{
|
|
|
|
double loc, dist;
|
2012-01-14 01:03:37 +00:00
|
|
|
POINT4D pt, pt_projected;
|
2010-03-10 15:33:33 +00:00
|
|
|
POINTARRAY* pa1;
|
|
|
|
POINTARRAY* pa2;
|
2011-11-21 16:32:27 +00:00
|
|
|
double vstol; /* vertex snap tolerance */
|
2010-03-10 15:33:33 +00:00
|
|
|
|
|
|
|
/* Possible outcomes:
|
|
|
|
*
|
2010-03-11 20:50:40 +00:00
|
|
|
* 1. The point is not on the line or on the boundary
|
2011-11-03 16:20:39 +00:00
|
|
|
* -> Leave collection untouched, return 0
|
2012-04-12 07:21:23 +00:00
|
|
|
* 2. The point is on the boundary
|
|
|
|
* -> Push 1 element on the collection:
|
|
|
|
* o the original line
|
|
|
|
* -> Return 1
|
|
|
|
* 3. The point is in the line
|
2011-11-03 16:20:39 +00:00
|
|
|
* -> Push 2 elements on the collection:
|
2010-03-11 20:50:40 +00:00
|
|
|
* o start_point - cut_point
|
|
|
|
* o cut_point - last_point
|
2012-04-12 07:21:23 +00:00
|
|
|
* -> Return 2
|
2010-03-10 15:33:33 +00:00
|
|
|
*/
|
|
|
|
|
2012-01-14 01:03:37 +00:00
|
|
|
getPoint4d_p(blade_in->point, 0, &pt);
|
|
|
|
loc = ptarray_locate_point(lwline_in->points, &pt, &dist, &pt_projected);
|
2010-03-10 15:33:33 +00:00
|
|
|
|
|
|
|
/* lwnotice("Location: %g -- Distance: %g", loc, dist); */
|
|
|
|
|
2011-11-03 16:20:39 +00:00
|
|
|
if ( dist > 0 ) /* TODO: accept a tolerance ? */
|
|
|
|
{
|
|
|
|
/* No intersection */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( loc == 0 || loc == 1 )
|
2010-03-10 15:33:33 +00:00
|
|
|
{
|
2011-11-03 16:20:39 +00:00
|
|
|
/* Intersection is on the boundary */
|
|
|
|
return 1;
|
2010-08-15 08:30:08 +00:00
|
|
|
}
|
2010-03-10 15:33:33 +00:00
|
|
|
|
2011-11-03 16:20:39 +00:00
|
|
|
/* There is a real intersection, let's get two substrings */
|
2013-01-15 11:54:29 +00:00
|
|
|
|
|
|
|
/* Compute vertex snap tolerance based on line length
|
|
|
|
* TODO: take as parameter ? */
|
|
|
|
vstol = ptarray_length_2d(lwline_in->points) / 1e14;
|
|
|
|
|
2011-11-21 16:32:27 +00:00
|
|
|
pa1 = ptarray_substring(lwline_in->points, 0, loc, vstol);
|
|
|
|
pa2 = ptarray_substring(lwline_in->points, loc, 1, vstol);
|
2010-03-10 15:33:33 +00:00
|
|
|
|
2011-11-06 20:17:43 +00:00
|
|
|
/* NOTE: I've seen empty pointarrays with loc != 0 and loc != 1 */
|
|
|
|
if ( pa1->npoints == 0 || pa2->npoints == 0 ) {
|
|
|
|
ptarray_free(pa1);
|
|
|
|
ptarray_free(pa2);
|
|
|
|
/* Intersection is on the boundary */
|
|
|
|
return 1;
|
|
|
|
}
|
2011-11-03 16:20:39 +00:00
|
|
|
|
|
|
|
lwmline_add_lwline(v, lwline_construct(SRID_UNKNOWN, NULL, pa1));
|
|
|
|
lwmline_add_lwline(v, lwline_construct(SRID_UNKNOWN, NULL, pa2));
|
|
|
|
return 2;
|
2010-03-10 15:33:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static LWGEOM*
|
2011-08-11 08:04:20 +00:00
|
|
|
lwline_split(const LWLINE* lwline_in, const LWGEOM* blade_in)
|
2010-03-10 15:33:33 +00:00
|
|
|
{
|
2010-11-21 19:02:23 +00:00
|
|
|
switch (blade_in->type)
|
2010-03-10 15:33:33 +00:00
|
|
|
{
|
|
|
|
case POINTTYPE:
|
|
|
|
return lwline_split_by_point(lwline_in, (LWPOINT*)blade_in);
|
|
|
|
|
|
|
|
case LINETYPE:
|
2010-03-13 10:59:19 +00:00
|
|
|
return lwline_split_by_line(lwline_in, (LWLINE*)blade_in);
|
|
|
|
|
2010-03-10 15:33:33 +00:00
|
|
|
default:
|
|
|
|
lwerror("Splitting a Line by a %s is unsupported",
|
2010-11-21 19:02:23 +00:00
|
|
|
lwtype_name(blade_in->type));
|
2010-03-10 15:33:33 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-03-15 18:00:12 +00:00
|
|
|
/* Initializes and uses GEOS internally */
|
|
|
|
static LWGEOM*
|
2011-08-11 08:04:20 +00:00
|
|
|
lwpoly_split_by_line(const LWPOLY* lwpoly_in, const LWLINE* blade_in)
|
2010-03-15 18:00:12 +00:00
|
|
|
{
|
|
|
|
LWCOLLECTION* out;
|
2010-08-15 08:30:08 +00:00
|
|
|
GEOSGeometry* g1;
|
|
|
|
GEOSGeometry* g2;
|
|
|
|
GEOSGeometry* g1_bounds;
|
|
|
|
GEOSGeometry* polygons;
|
2010-03-15 18:00:12 +00:00
|
|
|
const GEOSGeometry *vgeoms[1];
|
|
|
|
int i,n;
|
2012-01-16 17:37:12 +00:00
|
|
|
int hasZ = FLAGS_GET_Z(lwpoly_in->flags);
|
|
|
|
|
2010-03-15 18:00:12 +00:00
|
|
|
|
|
|
|
/* Possible outcomes:
|
|
|
|
*
|
2010-08-15 08:30:08 +00:00
|
|
|
* 1. The line does not split the polygon
|
2010-03-15 18:00:12 +00:00
|
|
|
* -> Return a collection with single element
|
|
|
|
* 2. The line does split the polygon
|
|
|
|
* -> Return a collection of all elements resulting from the split
|
|
|
|
*/
|
|
|
|
|
|
|
|
initGEOS(lwgeom_geos_error, lwgeom_geos_error);
|
|
|
|
|
2014-10-01 13:13:46 +00:00
|
|
|
g1 = LWGEOM2GEOS((LWGEOM*)lwpoly_in, 0);
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( NULL == g1 )
|
|
|
|
{
|
2010-03-15 18:00:12 +00:00
|
|
|
lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
g1_bounds = GEOSBoundary(g1);
|
|
|
|
if ( NULL == g1_bounds )
|
|
|
|
{
|
|
|
|
GEOSGeom_destroy(g1);
|
|
|
|
lwerror("GEOSBoundary: %s", lwgeom_geos_errmsg);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-10-01 13:13:46 +00:00
|
|
|
g2 = LWGEOM2GEOS((LWGEOM*)blade_in, 0);
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( NULL == g2 )
|
|
|
|
{
|
2010-03-15 18:00:12 +00:00
|
|
|
GEOSGeom_destroy(g1);
|
|
|
|
GEOSGeom_destroy(g1_bounds);
|
|
|
|
lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
vgeoms[0] = GEOSUnion(g1_bounds, g2);
|
|
|
|
if ( NULL == vgeoms[0] )
|
|
|
|
{
|
|
|
|
GEOSGeom_destroy(g1);
|
|
|
|
GEOSGeom_destroy(g2);
|
|
|
|
GEOSGeom_destroy(g1_bounds);
|
|
|
|
lwerror("GEOSUnion: %s", lwgeom_geos_errmsg);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-08-15 08:30:08 +00:00
|
|
|
/* debugging..
|
|
|
|
lwnotice("Bounds poly: %s",
|
2012-01-16 17:37:12 +00:00
|
|
|
lwgeom_to_ewkt(GEOS2LWGEOM(g1_bounds, hasZ)));
|
2010-08-15 08:30:08 +00:00
|
|
|
lwnotice("Line: %s",
|
2012-01-16 17:37:12 +00:00
|
|
|
lwgeom_to_ewkt(GEOS2LWGEOM(g2, hasZ)));
|
2010-03-15 18:00:12 +00:00
|
|
|
|
2010-08-15 08:30:08 +00:00
|
|
|
lwnotice("Noded bounds: %s",
|
2012-01-16 17:37:12 +00:00
|
|
|
lwgeom_to_ewkt(GEOS2LWGEOM(vgeoms[0], hasZ)));
|
2010-08-15 08:30:08 +00:00
|
|
|
*/
|
2010-03-15 18:00:12 +00:00
|
|
|
|
|
|
|
polygons = GEOSPolygonize(vgeoms, 1);
|
|
|
|
if ( NULL == polygons )
|
|
|
|
{
|
|
|
|
GEOSGeom_destroy(g1);
|
|
|
|
GEOSGeom_destroy(g2);
|
|
|
|
GEOSGeom_destroy(g1_bounds);
|
|
|
|
GEOSGeom_destroy((GEOSGeometry*)vgeoms[0]);
|
|
|
|
lwerror("GEOSPolygonize: %s", lwgeom_geos_errmsg);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if PARANOIA_LEVEL > 0
|
|
|
|
if ( GEOSGeometryTypeId(polygons) != COLLECTIONTYPE )
|
|
|
|
{
|
|
|
|
GEOSGeom_destroy(g1);
|
|
|
|
GEOSGeom_destroy(g2);
|
|
|
|
GEOSGeom_destroy(g1_bounds);
|
|
|
|
GEOSGeom_destroy((GEOSGeometry*)vgeoms[0]);
|
|
|
|
GEOSGeom_destroy(polygons);
|
|
|
|
lwerror("Unexpected return from GEOSpolygonize");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* We should now have all polygons, just skip
|
|
|
|
* the ones which are in holes of the original
|
|
|
|
* geometries and return the rest in a collection
|
|
|
|
*/
|
|
|
|
n = GEOSGetNumGeometries(polygons);
|
2012-01-16 17:37:12 +00:00
|
|
|
out = lwcollection_construct_empty(COLLECTIONTYPE, lwpoly_in->srid,
|
|
|
|
hasZ, 0);
|
2010-03-15 18:00:12 +00:00
|
|
|
/* Allocate space for all polys */
|
2013-11-06 09:39:29 +00:00
|
|
|
out->geoms = lwrealloc(out->geoms, sizeof(LWGEOM*)*n);
|
2010-03-15 18:00:12 +00:00
|
|
|
assert(0 == out->ngeoms);
|
|
|
|
for (i=0; i<n; ++i)
|
|
|
|
{
|
|
|
|
GEOSGeometry* pos; /* point on surface */
|
2010-08-15 08:30:08 +00:00
|
|
|
const GEOSGeometry* p = GEOSGetGeometryN(polygons, i);
|
2010-03-15 18:00:12 +00:00
|
|
|
int contains;
|
|
|
|
|
|
|
|
pos = GEOSPointOnSurface(p);
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! pos )
|
|
|
|
{
|
2010-03-15 18:00:12 +00:00
|
|
|
GEOSGeom_destroy(g1);
|
|
|
|
GEOSGeom_destroy(g2);
|
|
|
|
GEOSGeom_destroy(g1_bounds);
|
|
|
|
GEOSGeom_destroy((GEOSGeometry*)vgeoms[0]);
|
|
|
|
GEOSGeom_destroy(polygons);
|
|
|
|
lwerror("GEOSPointOnSurface: %s", lwgeom_geos_errmsg);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
contains = GEOSContains(g1, pos);
|
|
|
|
if ( 2 == contains )
|
|
|
|
{
|
|
|
|
GEOSGeom_destroy(g1);
|
|
|
|
GEOSGeom_destroy(g2);
|
|
|
|
GEOSGeom_destroy(g1_bounds);
|
|
|
|
GEOSGeom_destroy((GEOSGeometry*)vgeoms[0]);
|
|
|
|
GEOSGeom_destroy(polygons);
|
|
|
|
GEOSGeom_destroy(pos);
|
|
|
|
lwerror("GEOSContains: %s", lwgeom_geos_errmsg);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GEOSGeom_destroy(pos);
|
|
|
|
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( 0 == contains )
|
|
|
|
{
|
2010-03-15 18:00:12 +00:00
|
|
|
/* Original geometry doesn't contain
|
|
|
|
* a point in this ring, must be an hole
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-01-16 17:37:12 +00:00
|
|
|
out->geoms[out->ngeoms++] = GEOS2LWGEOM(p, hasZ);
|
2010-03-15 18:00:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GEOSGeom_destroy(g1);
|
|
|
|
GEOSGeom_destroy(g2);
|
|
|
|
GEOSGeom_destroy(g1_bounds);
|
|
|
|
GEOSGeom_destroy((GEOSGeometry*)vgeoms[0]);
|
|
|
|
GEOSGeom_destroy(polygons);
|
|
|
|
|
|
|
|
return (LWGEOM*)out;
|
|
|
|
}
|
|
|
|
|
2010-03-16 22:18:59 +00:00
|
|
|
static LWGEOM*
|
2011-08-11 08:04:20 +00:00
|
|
|
lwcollection_split(const LWCOLLECTION* lwcoll_in, const LWGEOM* blade_in)
|
2010-03-16 22:18:59 +00:00
|
|
|
{
|
|
|
|
LWGEOM** split_vector=NULL;
|
|
|
|
LWCOLLECTION* out;
|
|
|
|
size_t split_vector_capacity;
|
|
|
|
size_t split_vector_size=0;
|
|
|
|
size_t i,j;
|
|
|
|
|
|
|
|
split_vector_capacity=8;
|
2010-05-13 08:39:49 +00:00
|
|
|
split_vector = lwalloc(split_vector_capacity * sizeof(LWGEOM*));
|
2010-08-15 08:30:08 +00:00
|
|
|
if ( ! split_vector )
|
|
|
|
{
|
2010-03-16 22:18:59 +00:00
|
|
|
lwerror("Out of virtual memory");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=0; i<lwcoll_in->ngeoms; ++i)
|
|
|
|
{
|
|
|
|
LWCOLLECTION* col;
|
|
|
|
LWGEOM* split = lwgeom_split(lwcoll_in->geoms[i], blade_in);
|
|
|
|
/* an exception should prevent this from ever returning NULL */
|
|
|
|
if ( ! split ) return NULL;
|
|
|
|
|
|
|
|
col = lwgeom_as_lwcollection(split);
|
|
|
|
/* Output, if any, will always be a collection */
|
|
|
|
assert(col);
|
|
|
|
|
|
|
|
/* Reallocate split_vector if needed */
|
|
|
|
if ( split_vector_size + col->ngeoms > split_vector_capacity )
|
|
|
|
{
|
2010-05-13 08:39:49 +00:00
|
|
|
/* NOTE: we could be smarter on reallocations here */
|
|
|
|
split_vector_capacity += col->ngeoms;
|
|
|
|
split_vector = lwrealloc(split_vector,
|
2010-08-15 08:30:08 +00:00
|
|
|
split_vector_capacity * sizeof(LWGEOM*));
|
|
|
|
if ( ! split_vector )
|
|
|
|
{
|
2010-03-16 22:18:59 +00:00
|
|
|
lwerror("Out of virtual memory");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j=0; j<col->ngeoms; ++j)
|
|
|
|
{
|
2010-12-13 19:25:15 +00:00
|
|
|
col->geoms[j]->srid = SRID_UNKNOWN; /* strip srid */
|
2010-03-16 22:18:59 +00:00
|
|
|
split_vector[split_vector_size++] = col->geoms[j];
|
|
|
|
}
|
2011-11-21 14:59:30 +00:00
|
|
|
lwfree(col->geoms);
|
|
|
|
lwfree(col);
|
2010-03-16 22:18:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Now split_vector has split_vector_size geometries */
|
2010-11-25 17:34:21 +00:00
|
|
|
out = lwcollection_construct(COLLECTIONTYPE, lwcoll_in->srid,
|
2010-08-15 08:30:08 +00:00
|
|
|
NULL, split_vector_size, split_vector);
|
2010-03-16 22:18:59 +00:00
|
|
|
|
|
|
|
return (LWGEOM*)out;
|
|
|
|
}
|
|
|
|
|
2010-03-10 15:33:33 +00:00
|
|
|
static LWGEOM*
|
2011-08-11 08:04:20 +00:00
|
|
|
lwpoly_split(const LWPOLY* lwpoly_in, const LWGEOM* blade_in)
|
2010-03-10 15:33:33 +00:00
|
|
|
{
|
2010-11-21 19:02:23 +00:00
|
|
|
switch (blade_in->type)
|
2010-03-10 15:33:33 +00:00
|
|
|
{
|
|
|
|
case LINETYPE:
|
2010-03-15 18:00:12 +00:00
|
|
|
return lwpoly_split_by_line(lwpoly_in, (LWLINE*)blade_in);
|
2010-03-10 15:33:33 +00:00
|
|
|
default:
|
|
|
|
lwerror("Splitting a Polygon by a %s is unsupported",
|
2010-11-21 19:02:23 +00:00
|
|
|
lwtype_name(blade_in->type));
|
2010-03-10 15:33:33 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-08-11 08:04:20 +00:00
|
|
|
/* exported */
|
|
|
|
LWGEOM*
|
|
|
|
lwgeom_split(const LWGEOM* lwgeom_in, const LWGEOM* blade_in)
|
2010-03-10 15:33:33 +00:00
|
|
|
{
|
2010-11-21 19:02:23 +00:00
|
|
|
switch (lwgeom_in->type)
|
2010-03-10 15:33:33 +00:00
|
|
|
{
|
|
|
|
case LINETYPE:
|
2011-08-11 08:04:20 +00:00
|
|
|
return lwline_split((const LWLINE*)lwgeom_in, blade_in);
|
2010-03-10 15:33:33 +00:00
|
|
|
|
|
|
|
case POLYGONTYPE:
|
2011-08-11 08:04:20 +00:00
|
|
|
return lwpoly_split((const LWPOLY*)lwgeom_in, blade_in);
|
2010-03-10 15:33:33 +00:00
|
|
|
|
2010-03-16 22:18:59 +00:00
|
|
|
case MULTIPOLYGONTYPE:
|
|
|
|
case MULTILINETYPE:
|
|
|
|
case COLLECTIONTYPE:
|
2011-08-11 08:04:20 +00:00
|
|
|
return lwcollection_split((const LWCOLLECTION*)lwgeom_in, blade_in);
|
2010-03-16 22:18:59 +00:00
|
|
|
|
2010-03-10 15:33:33 +00:00
|
|
|
default:
|
2010-03-13 11:23:22 +00:00
|
|
|
lwerror("Splitting of %s geometries is unsupported",
|
2010-11-21 19:02:23 +00:00
|
|
|
lwtype_name(lwgeom_in->type));
|
2010-03-10 15:33:33 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|