Example #1
0
static const LWGEOM*
lwgeom_subgeom(const LWGEOM* g, int n)
{
	const LWCOLLECTION* c = lwgeom_as_lwcollection(g);
	if ( c ) return lwcollection_getsubgeom((LWCOLLECTION*)c, n);
	else return g;
}
Example #2
0
static int
lwgeom_ngeoms(const LWGEOM* n)
{
	const LWCOLLECTION* c = lwgeom_as_lwcollection(n);
	if ( c ) return c->ngeoms;
	else return 1;
}
static LWGEOM*
lwcollection_split(const LWCOLLECTION* lwcoll_in, const LWGEOM* blade_in)
{
	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;
	split_vector = lwalloc(split_vector_capacity * sizeof(LWGEOM*));
	if ( ! split_vector )
	{
		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 )
		{
			/* NOTE: we could be smarter on reallocations here */
			split_vector_capacity += col->ngeoms;
			split_vector = lwrealloc(split_vector,
			                         split_vector_capacity * sizeof(LWGEOM*));
			if ( ! split_vector )
			{
				lwerror("Out of virtual memory");
				return NULL;
			}
		}

		for (j=0; j<col->ngeoms; ++j)
		{
			col->geoms[j]->srid = SRID_UNKNOWN; /* strip srid */
			split_vector[split_vector_size++] = col->geoms[j];
		}
		lwfree(col->geoms);
		lwfree(col);
	}

	/* Now split_vector has split_vector_size geometries */
	out = lwcollection_construct(COLLECTIONTYPE, lwcoll_in->srid,
	                             NULL, split_vector_size, split_vector);

	return (LWGEOM*)out;
}
Example #4
0
Datum LWGEOM_geometryn_collection(PG_FUNCTION_ARGS)
{
	GSERIALIZED *geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
	GSERIALIZED *result;
	int type = gserialized_get_type(geom);
	int32 idx;
	LWCOLLECTION *coll;
	LWGEOM *subgeom;

	POSTGIS_DEBUG(2, "LWGEOM_geometryn_collection called.");

	/* elog(NOTICE, "GeometryN called"); */

	idx = PG_GETARG_INT32(1);
	idx -= 1; /* index is 1-based */

	/* call is valid on multi* geoms only */
	if (type==POINTTYPE || type==LINETYPE || type==CIRCSTRINGTYPE ||
	        type==COMPOUNDTYPE || type==POLYGONTYPE ||
		type==CURVEPOLYTYPE || type==TRIANGLETYPE)
	{
		if ( idx == 0 ) PG_RETURN_POINTER(geom);
		PG_RETURN_NULL();
	}

	coll = lwgeom_as_lwcollection(lwgeom_from_gserialized(geom));

	if ( idx < 0 ) PG_RETURN_NULL();
	if ( idx >= coll->ngeoms ) PG_RETURN_NULL();

	subgeom = coll->geoms[idx];
	subgeom->srid = coll->srid;

	/* COMPUTE_BBOX==TAINTING */
	if ( coll->bbox ) lwgeom_add_bbox(subgeom);

	result = geometry_serialize(subgeom);

	lwcollection_free(coll);
	PG_FREE_IF_COPY(geom, 0);

	PG_RETURN_POINTER(result);

}
Example #5
0
int 
lwgeom_is_closed(const LWGEOM *geom)
{
	int type = geom->type;
	
	if( lwgeom_is_empty(geom) )
		return LW_FALSE;
	
	/* Test linear types for closure */
	switch (type)
	{
	case LINETYPE:
		return lwline_is_closed((LWLINE*)geom);
	case POLYGONTYPE:
		return lwpoly_is_closed((LWPOLY*)geom);
	case CIRCSTRINGTYPE:
		return lwcircstring_is_closed((LWCIRCSTRING*)geom);
	case COMPOUNDTYPE:
		return lwcompound_is_closed((LWCOMPOUND*)geom);
	case TINTYPE:
		return lwtin_is_closed((LWTIN*)geom);
	case POLYHEDRALSURFACETYPE:
		return lwpsurface_is_closed((LWPSURFACE*)geom);
	}
	
	/* Recurse into collections and see if anything is not closed */
	if ( lwgeom_is_collection(geom) )
	{
		LWCOLLECTION *col = lwgeom_as_lwcollection(geom);
		int i;
		int closed;
		for ( i = 0; i < col->ngeoms; i++ ) 
		{
			closed = lwgeom_is_closed(col->geoms[i]);
			if ( ! closed ) 
				return LW_FALSE;
		}
		return LW_TRUE;
	}
	
	/* All non-linear non-collection types we will call closed */
	return LW_TRUE;
}
Example #6
0
void lwgeom_set_srid(LWGEOM *geom, int32_t srid)
{
	int i;

	LWDEBUGF(4,"entered with srid=%d",srid);

	geom->srid = srid;

	if ( lwgeom_is_collection(geom) )
	{
		/* All the children are set to the unknown SRID value 
		   TODO: change this so the children have a known SRID? */
		LWCOLLECTION *col = lwgeom_as_lwcollection(geom);
		for ( i = 0; i < col->ngeoms; i++ )
		{
			lwgeom_set_srid(col->geoms[i], SRID_UNKNOWN);
		}
	}
}
Example #7
0
Datum LWGEOM_numgeometries_collection(PG_FUNCTION_ARGS)
{
	GSERIALIZED *geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
	LWGEOM *lwgeom;
	int32 ret = 1;

	lwgeom = lwgeom_from_gserialized(geom);
	if ( lwgeom_is_empty(lwgeom) )
	{
		ret = 0;
	}
	else if ( lwgeom_is_collection(lwgeom) )
	{
		LWCOLLECTION *col = lwgeom_as_lwcollection(lwgeom);
		ret = col->ngeoms;
	}
	lwgeom_free(lwgeom);
	PG_FREE_IF_COPY(geom, 0);
	PG_RETURN_INT32(ret);
}
Example #8
0
/*
** Given a generic collection, return the "simplest" form.
**
** eg: GEOMETRYCOLLECTION(MULTILINESTRING()) => MULTILINESTRING()
**
**     GEOMETRYCOLLECTION(MULTILINESTRING(), MULTILINESTRING(), POINT())
**      => GEOMETRYCOLLECTION(MULTILINESTRING(), POINT())
**
** In general, if the subcomponents are homogeneous, return a properly
** typed collection.
** Otherwise, return a generic collection, with the subtypes in minimal
** typed collections.
*/
static void
lwcollection_build_buffer(const LWCOLLECTION *col, HomogenizeBuffer *buffer)
{
	int i;
	
	if ( ! col ) return;
	if ( lwgeom_is_empty(lwcollection_as_lwgeom(col)) ) return;
	for ( i = 0; i < col->ngeoms; i++ )
	{
		LWGEOM *geom = col->geoms[i];
		switch(geom->type)
		{
			case POINTTYPE:
			case LINETYPE:
			case CIRCSTRINGTYPE:
			case COMPOUNDTYPE:
			case TRIANGLETYPE:
			case CURVEPOLYTYPE:
			case POLYGONTYPE:
			{
				/* Init if necessary */
				if ( ! buffer->buf[geom->type] )
				{
					LWCOLLECTION *bufcol = lwcollection_construct_empty(COLLECTIONTYPE, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags));
					bufcol->type = lwtype_get_collectiontype(geom->type);
					buffer->buf[geom->type] = bufcol;
				}
				/* Add sub-geom to buffer */
				lwcollection_add_lwgeom(buffer->buf[geom->type], lwgeom_clone(geom));
				/* Increment count for this singleton type */
				buffer->cnt[geom->type] = buffer->cnt[geom->type] + 1;
			}
			default:
			{
				lwcollection_build_buffer(lwgeom_as_lwcollection(geom), buffer);
			}
		}
	}
	return;
}
Example #9
0
LWGEOM* wkt_parser_collection_add_geom(LWGEOM *col, LWGEOM *geom)
{
	LWDEBUG(4,"entered");

	/* Toss error on null geometry input */
	if( ! (geom && col) )
	{
		SET_PARSER_ERROR(PARSER_ERROR_OTHER);
		return NULL;
	}
		
	/* All the elements must agree on dimensionality */
	if( FLAGS_NDIMS(col->flags) != FLAGS_NDIMS(geom->flags) )
	{
		lwgeom_free(col);
		lwgeom_free(geom);
		SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
		return NULL;
	}
	
	return lwcollection_as_lwgeom(lwcollection_add_lwgeom(lwgeom_as_lwcollection(col), geom));
}
/* Initializes and uses GEOS internally */
static LWGEOM*
lwline_split_by_line(const LWLINE* lwline_in, const LWGEOM* blade_in)
{
	LWGEOM** components;
	LWGEOM* diff;
	LWCOLLECTION* out;
	GEOSGeometry* gdiff; /* difference */
	GEOSGeometry* g1;
	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
	 *      -> Return a collection with single element
	 *  2. The lines cross
	 *      -> Return a collection of all elements resulting from the split
	 */

	initGEOS(lwgeom_geos_error, lwgeom_geos_error);

	g1 = LWGEOM2GEOS((LWGEOM*)lwline_in, 0);
	if ( ! g1 )
	{
		lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg);
		return NULL;
	}
	g2 = LWGEOM2GEOS(blade_in, 0);
	if ( ! g2 )
	{
		GEOSGeom_destroy(g1);
		lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg);
		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 )
	{
		lwerror("GEOSRelatePattern: %s", lwgeom_geos_errmsg);
		GEOSGeom_destroy(g1);
		GEOSGeom_destroy(g2);
		return NULL;
	}
	if ( ret )
	{
		GEOSGeom_destroy(g1);
		GEOSGeom_destroy(g2);
		lwerror("Splitter line has linear intersection with input");
		return NULL;
	}


	gdiff = GEOSDifference(g1,g2);
	GEOSGeom_destroy(g1);
	GEOSGeom_destroy(g2);
	if (gdiff == NULL)
	{
		lwerror("GEOSDifference: %s", lwgeom_geos_errmsg);
		return NULL;
	}

	diff = GEOS2LWGEOM(gdiff, FLAGS_GET_Z(lwline_in->flags));
	GEOSGeom_destroy(gdiff);
	if (NULL == diff)
	{
		lwerror("GEOS2LWGEOM: %s", lwgeom_geos_errmsg);
		return NULL;
	}

	out = lwgeom_as_lwcollection(diff);
	if ( ! out )
	{
		components = lwalloc(sizeof(LWGEOM*)*1);
		components[0] = diff;
		out = lwcollection_construct(COLLECTIONTYPE, lwline_in->srid,
		                             NULL, 1, components);
	}
	else
	{
	  /* Set SRID */
		lwgeom_set_srid((LWGEOM*)out, lwline_in->srid);
	  /* Force collection type */
	  out->type = COLLECTIONTYPE;
	}


	return (LWGEOM*)out;
}
Example #11
0
/**
This is a recursive function delivering every possible combination of subgeometries
*/
int lw_dist3d_recursive(const LWGEOM *lwg1,const LWGEOM *lwg2, DISTPTS3D *dl)
{
	int i, j;
	int n1=1;
	int n2=1;
	LWGEOM *g1 = NULL;
	LWGEOM *g2 = NULL;
	LWCOLLECTION *c1 = NULL;
	LWCOLLECTION *c2 = NULL;

	LWDEBUGF(2, "lw_dist3d_recursive is called with type1=%d, type2=%d", lwg1->type, lwg2->type);

	if (lwgeom_is_collection(lwg1))
	{
		LWDEBUG(3, "First geometry is collection");
		c1 = lwgeom_as_lwcollection(lwg1);
		n1 = c1->ngeoms;
	}
	if (lwgeom_is_collection(lwg2))
	{
		LWDEBUG(3, "Second geometry is collection");
		c2 = lwgeom_as_lwcollection(lwg2);
		n2 = c2->ngeoms;
	}

	for ( i = 0; i < n1; i++ )
	{

		if (lwgeom_is_collection(lwg1))
		{
			g1 = c1->geoms[i];
		}
		else
		{
			g1 = (LWGEOM*)lwg1;
		}

		if (lwgeom_is_empty(g1)) return LW_TRUE;

		if (lwgeom_is_collection(g1))
		{
			LWDEBUG(3, "Found collection inside first geometry collection, recursing");
			if (!lw_dist3d_recursive(g1, lwg2, dl)) return LW_FALSE;
			continue;
		}
		for ( j = 0; j < n2; j++ )
		{
			if (lwgeom_is_collection(lwg2))
			{
				g2 = c2->geoms[j];
			}
			else
			{
				g2 = (LWGEOM*)lwg2;
			}
			if (lwgeom_is_collection(g2))
			{
				LWDEBUG(3, "Found collection inside second geometry collection, recursing");
				if (!lw_dist3d_recursive(g1, g2, dl)) return LW_FALSE;
				continue;
			}


			/*If one of geometries is empty, return. True here only means continue searching. False would have stoped the process*/
			if (lwgeom_is_empty(g1)||lwgeom_is_empty(g2)) return LW_TRUE;


			if (!lw_dist3d_distribute_bruteforce(g1, g2, dl)) return LW_FALSE;
			if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; /*just a check if  the answer is already given*/
		}
	}
	return LW_TRUE;
}
Example #12
0
static void test_lwgeom_split(void)
{
	LWGEOM *geom, *blade, *ret;
	char *wkt, *in_wkt;

	geom = lwgeom_from_wkt(
"MULTILINESTRING((-5 -2,0 0),(0 0,10 10))",
	LW_PARSER_CHECK_NONE);
	CU_ASSERT(geom != NULL);
	blade = lwgeom_from_wkt(
		"POINT(0 0)",
		LW_PARSER_CHECK_NONE);
	CU_ASSERT(blade != NULL);
	ret = lwgeom_split(geom, blade);
	CU_ASSERT(ret != NULL);
	wkt = lwgeom_to_ewkt(ret);
	in_wkt = "GEOMETRYCOLLECTION(LINESTRING(-5 -2,0 0),LINESTRING(0 0,10 10))";
        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);

        /* See #1311 */
        geom = lwgeom_from_wkt(
                "LINESTRING(0 0,10 0,20 4,0 3)",
                LW_PARSER_CHECK_NONE);
        CU_ASSERT(geom != NULL);
        blade = lwgeom_from_wkt("POINT(10 0)", LW_PARSER_CHECK_NONE);
        ret = lwgeom_split(geom, blade);
        CU_ASSERT(ret != NULL);
        wkt = lwgeom_to_ewkt(ret);
	in_wkt = "GEOMETRYCOLLECTION(LINESTRING(0 0,10 0),LINESTRING(10 0,20 4,0 3))";
        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);

  /* See #2528 (1) -- memory leak test, needs valgrind to check */
  geom = lwgeom_from_wkt("SRID=1;LINESTRING(0 1,10 1)", LW_PARSER_CHECK_NONE);
  CU_ASSERT(geom != NULL);
  blade = lwgeom_from_wkt("LINESTRING(7 0,7 3)", LW_PARSER_CHECK_NONE);
  ret = lwgeom_split(geom, blade);
  CU_ASSERT(ret != NULL);
  wkt = lwgeom_to_ewkt(ret);
	in_wkt = "SRID=1;GEOMETRYCOLLECTION(LINESTRING(0 1,7 1),LINESTRING(7 1,10 1))";
  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);

  /* See #2528 (2) -- memory leak test, needs valgrind to check */
  geom = lwgeom_from_wkt("SRID=1;POLYGON((0 1, 10 1, 10 10, 0 10, 0 1))", LW_PARSER_CHECK_NONE);
  CU_ASSERT(geom != NULL);
  blade = lwgeom_from_wkt("LINESTRING(7 0,7 20)", LW_PARSER_CHECK_NONE);
  ret = lwgeom_split(geom, blade);
  CU_ASSERT(ret != NULL);
  wkt = lwgeom_to_ewkt(ret);
	in_wkt = "SRID=1;GEOMETRYCOLLECTION(POLYGON((7 1,0 1,0 10,7 10,7 1)),POLYGON((7 10,10 10,10 1,7 1,7 10)))";
  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 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);

  /* Split line by multipoint */
  geom = lwgeom_from_wkt("LINESTRING(0 0, 10 0)", LW_PARSER_CHECK_NONE);
  CU_ASSERT_FATAL(geom != NULL);
  blade = lwgeom_from_wkt("MULTIPOINT(2 0,8 0,4 0)", 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(8 0,10 0),LINESTRING(0 0,2 0),LINESTRING(4 0,8 0),LINESTRING(2 0,4 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);

  /* See #3401 -- robustness issue */
  geom = lwgeom_from_wkt("LINESTRING(-180 0,0 0)", LW_PARSER_CHECK_NONE);
  CU_ASSERT(geom != NULL);
  blade = lwgeom_from_wkt("POINT(-20 0)", LW_PARSER_CHECK_NONE);
  ret = lwgeom_split(geom, blade);
  CU_ASSERT(ret != NULL);
	{
		LWCOLLECTION *split = lwgeom_as_lwcollection(ret);
		LWLINE *l1, *l2;
		POINT2D pt;
		CU_ASSERT(split != NULL);
		l1 = lwgeom_as_lwline(split->geoms[0]);
		CU_ASSERT(l1 != NULL);
		getPoint2d_p(l1->points, 1, &pt);
		ASSERT_DOUBLE_EQUAL(pt.x, -20);
		ASSERT_DOUBLE_EQUAL(pt.y, 0);
		l2 = lwgeom_as_lwline(split->geoms[1]);
		CU_ASSERT(l2 != NULL);
		getPoint2d_p(l2->points, 0, &pt);
		ASSERT_DOUBLE_EQUAL(pt.x, -20);
		ASSERT_DOUBLE_EQUAL(pt.y, 0);
	}
	lwgeom_free(ret);
	lwgeom_free(geom);
	lwgeom_free(blade);
}