[postgis-tickets] r14400 - add LWPOINTITERATOR (closes #3366)

Daniel Baston dbaston at gmail.com
Wed Nov 18 05:05:32 PST 2015


Author: dbaston
Date: 2015-11-18 05:05:32 -0800 (Wed, 18 Nov 2015)
New Revision: 14400

Added:
   trunk/liblwgeom/cunit/cu_iterator.c
   trunk/liblwgeom/lwiterator.c
Modified:
   trunk/liblwgeom/Makefile.in
   trunk/liblwgeom/cunit/Makefile.in
   trunk/liblwgeom/cunit/cu_tester.c
   trunk/liblwgeom/liblwgeom.h.in
Log:
add LWPOINTITERATOR (closes #3366)

Modified: trunk/liblwgeom/Makefile.in
===================================================================
--- trunk/liblwgeom/Makefile.in	2015-11-18 05:14:52 UTC (rev 14399)
+++ trunk/liblwgeom/Makefile.in	2015-11-18 13:05:32 UTC (rev 14400)
@@ -64,6 +64,7 @@
 	lwin_geojson.o \
 	lwin_wkb.o \
 	lwin_twkb.o \
+	lwiterator.o \
 	lwout_wkt.o \
 	lwout_twkb.o \
 	lwin_wkt_parse.o \

Modified: trunk/liblwgeom/cunit/Makefile.in
===================================================================
--- trunk/liblwgeom/cunit/Makefile.in	2015-11-18 05:14:52 UTC (rev 14399)
+++ trunk/liblwgeom/cunit/Makefile.in	2015-11-18 13:05:32 UTC (rev 14400)
@@ -58,6 +58,7 @@
 	cu_in_wkb.o \
 	cu_in_wkt.o \
 	cu_in_encoded_polyline.o \
+	cu_iterator.o \
 	cu_varint.o \
 	cu_unionfind.o \
 	cu_tester.o

Added: trunk/liblwgeom/cunit/cu_iterator.c
===================================================================
--- trunk/liblwgeom/cunit/cu_iterator.c	                        (rev 0)
+++ trunk/liblwgeom/cunit/cu_iterator.c	2015-11-18 13:05:32 UTC (rev 14400)
@@ -0,0 +1,265 @@
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.net
+ * Copyright 2015 Daniel Baston
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************/
+
+#include "CUnit/Basic.h"
+#include "cu_tester.h"
+
+#include "../liblwgeom_internal.h"
+
+char* inputs[] =
+{
+	"POINT (17 253)",
+	"POINT Z (17 253 018)",
+	"TRIANGLE ((0 0, 10 0, 10 10, 0 0))",
+	"LINESTRING (17 253, -44 28, 33 11, 26 44)",
+	"LINESTRING M (17 253 0, -44 28 1, 33 11 2, 26 44 3)",
+	"POLYGON((26426 65078,26531 65242,26075 65136,26096 65427,26426 65078))",
+	"MULTIPOINT ((1 1), (1 1))",
+	"MULTILINESTRING Z ((1 1 0, 2 2 0), (3 3 1, 4 4 1))",
+	"MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1)), ((20 20, 20 30, 30 30, 20 20)))",
+	"POINT EMPTY",
+	"LINESTRING M EMPTY",
+	"POLYGON Z EMPTY",
+	"GEOMETRYCOLLECTION EMPTY",
+	"GEOMETRYCOLLECTION (MULTIPOINT ((14 80), (22 12)))",
+	"GEOMETRYCOLLECTION (POINT (3 7), LINESTRING (0 0, 14 3), GEOMETRYCOLLECTION(POINT (2 8)))",
+	"GEOMETRYCOLLECTION (POINT (3 7), GEOMETRYCOLLECTION(MULTIPOINT ((2 8))))",
+	"GEOMETRYCOLLECTION (POINT (3 7), GEOMETRYCOLLECTION(LINESTRING (2 8, 4 3), POLYGON EMPTY, MULTIPOINT ((2 8), (17 3), EMPTY)))",
+	"CIRCULARSTRING(0 0,2 0, 2 1, 2 3, 4 3)",
+	"COMPOUNDCURVE(CIRCULARSTRING(0 0,2 0, 2 1, 2 3, 4 3),(4 3, 4 5, 1 4, 0 0))",
+	"MULTICURVE((0 0, 5 5),CIRCULARSTRING(4 0, 4 4, 8 4))",
+	"CURVEPOLYGON(COMPOUNDCURVE(CIRCULARSTRING(0 0,2 0, 2 1, 2 3, 4 3),(4 3, 4 5, 1 4, 0 0)), LINESTRING (0.1 0.1, 0.3 0.1, 0.3 0.3, 0.1 0.1) )",
+	"MULTISURFACE(CURVEPOLYGON(CIRCULARSTRING(0 0, 4 0, 4 4, 0 4, 0 0),(1 1, 3 3, 3 1, 1 1)),((10 10, 14 12, 11 10, 10 10),(11 11, 11.5 11, 11 11.5, 11 11)))",
+	"POLYHEDRALSURFACE( ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)), ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)), ((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)), ((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)), ((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)) )",
+	"TIN(((80 130,50 160,80 70,80 130)),((50 160,10 190,10 70,50 160)), ((80 70,50 160,10 70,80 70)),((120 160,120 190,50 160,120 160)), ((120 190,10 190,50 160,120 190)))"
+};
+
+static uint32_t
+count_points_using_iterator(LWGEOM* g)
+{
+	POINT4D p;
+	uint32_t count = 0;
+	LWPOINTITERATOR* it = lwpointiterator_create(g);
+
+	while (lwpointiterator_has_next(it))
+	{
+		CU_ASSERT_TRUE(lwpointiterator_next(it, &p));
+		count++;
+	}
+
+	lwpointiterator_destroy(it);
+
+	return count;
+}
+
+static void
+test_point_count(void)
+{
+	char* types_visited = lwalloc(NUMTYPES * sizeof(char));
+	memset(types_visited, LW_FALSE, NUMTYPES * sizeof(char));
+
+	uint32_t i;
+	for (i = 0; i < sizeof(inputs)/sizeof(char*); i++)
+	{
+		LWGEOM* input = lwgeom_from_wkt(inputs[i], LW_PARSER_CHECK_NONE);
+		types_visited[lwgeom_get_type(input)] = LW_TRUE;
+
+		uint32_t itercount = count_points_using_iterator(input);
+
+		CU_ASSERT_EQUAL(lwgeom_count_vertices(input), itercount);
+
+		lwgeom_free(input);
+	}
+
+	/* Assert that every valid LWGEOM type has been tested */
+	for (i = 1; i < NUMTYPES; i++)
+	{
+		CU_ASSERT_TRUE(types_visited[i]);
+	}
+
+	lwfree(types_visited);
+}
+
+static void
+test_cannot_modify_read_only(void)
+{
+	LWGEOM* input = lwgeom_from_wkt(inputs[0], LW_PARSER_CHECK_NONE);
+	LWPOINTITERATOR* it = lwpointiterator_create(input);
+
+	POINT4D p;
+	p.x = 3.2;
+	p.y = 4.8;
+
+	CU_ASSERT_EQUAL(LW_FAILURE, lwpointiterator_modify_next(it, &p));
+
+	lwgeom_free(input);
+	lwpointiterator_destroy(it);
+}
+
+static void
+test_modification(void)
+{
+	uint32_t i;
+	uint32_t j = 0;
+
+	for (i = 0; i < sizeof(inputs)/sizeof(char*); i++)
+	{
+		LWGEOM* input = lwgeom_from_wkt(inputs[i], LW_PARSER_CHECK_NONE);
+		LWPOINTITERATOR* it1 = lwpointiterator_create_rw(input);
+		LWPOINTITERATOR* it2 = lwpointiterator_create(input);;
+
+		while (lwpointiterator_has_next(it1))
+		{
+			/* Make up a coordinate, assign it to the next spot in it1,
+			 * read it from it2 to verify that it was assigned correctly. */
+			POINT4D p1, p2;
+			p1.x = sqrt(j++);
+			p1.y = sqrt(j++);
+			p1.z = sqrt(j++);
+			p1.m = sqrt(j++);
+
+			CU_ASSERT_TRUE(lwpointiterator_modify_next(it1, &p1));
+			CU_ASSERT_TRUE(lwpointiterator_next(it2, &p2));
+
+			CU_ASSERT_EQUAL(p1.x, p2.x);
+			CU_ASSERT_EQUAL(p1.y, p2.y);
+
+			if (lwgeom_has_z(input))
+				CU_ASSERT_EQUAL(p1.z, p2.z);
+
+			if (lwgeom_has_m(input))
+				CU_ASSERT_EQUAL(p1.m, p2.m);
+		}
+
+		lwgeom_free(input);
+
+		lwpointiterator_destroy(it1);
+		lwpointiterator_destroy(it2);
+	}
+}
+
+static void
+test_no_memory_leaked_when_iterator_is_partially_used(void)
+{
+	LWGEOM* g = lwgeom_from_wkt("GEOMETRYCOLLECTION (POINT (3 7), GEOMETRYCOLLECTION(LINESTRING (2 8, 4 3), POLYGON EMPTY, MULTIPOINT ((2 8), (17 3), EMPTY)))", LW_PARSER_CHECK_NONE);
+
+	LWPOINTITERATOR* it = lwpointiterator_create(g);
+	lwpointiterator_next(it, NULL);
+	lwpointiterator_next(it, NULL);
+
+	lwpointiterator_destroy(it);
+	lwgeom_free(g);
+}
+
+static void
+test_mixed_rw_access(void)
+{
+	uint32_t i = 0;
+	LWGEOM* g = lwgeom_from_wkt("GEOMETRYCOLLECTION (POINT (3 7), GEOMETRYCOLLECTION(LINESTRING (2 8, 4 3), POLYGON EMPTY, MULTIPOINT ((2 8), (17 3), EMPTY)))", LW_PARSER_CHECK_NONE);
+	LWPOINTITERATOR* it1 = lwpointiterator_create_rw(g);
+	LWPOINTITERATOR* it2 = lwpointiterator_create(g);
+
+	/* Flip the coordinates of the 3rd point */
+	while(lwpointiterator_has_next(it1))
+	{
+		if (i == 2)
+		{
+			POINT4D p;
+			double tmp;
+
+			lwpointiterator_peek(it1, &p);
+			tmp = p.x;
+			p.x = p.y;
+			p.y = tmp;
+
+			lwpointiterator_modify_next(it1, &p);
+		}
+		else
+		{
+			lwpointiterator_next(it1, NULL);
+		}
+		i++;
+	}
+	CU_ASSERT_EQUAL(5, i); /* Every point was visited */
+	lwpointiterator_destroy(it1);
+
+	/* Verify that the points are as expected */
+	POINT2D points[] =
+	{
+		{ .x = 3, .y = 7 },
+		{ .x = 2, .y = 8 },
+		{ .x = 3, .y = 4 },
+		{ .x = 2, .y = 8 },
+		{ .x = 17, .y = 3}
+	};
+
+	for (i = 0; lwpointiterator_has_next(it2); i++)
+	{
+		POINT4D p;
+
+		lwpointiterator_next(it2, &p);
+
+		CU_ASSERT_EQUAL(p.x, points[i].x);
+		CU_ASSERT_EQUAL(p.y, points[i].y);
+	}
+
+	lwpointiterator_destroy(it2);
+	lwgeom_free(g);
+}
+
+static void
+test_ordering(void)
+{
+	uint32_t i = 0;
+	LWGEOM* g = lwgeom_from_wkt("GEOMETRYCOLLECTION (POLYGON ((0 0, 0 10, 10 10, 0 10, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1)), MULTIPOINT((4 4), (3 3)))", LW_PARSER_CHECK_NONE);
+
+	POINT2D points[] = { {.x = 0,  .y = 0},
+		{.x = 0,  .y = 10},
+		{.x = 10, .y = 10},
+		{.x = 0,  .y = 10},
+		{.x = 0,  .y = 0},
+		{.x = 1,  .y = 1},
+		{.x = 1,  .y = 2},
+		{.x = 2,  .y = 2},
+		{.x = 2,  .y = 1},
+		{.x = 1,  .y = 1},
+		{.x = 4,  .y = 4},
+		{.x = 3,  .y = 3}
+	};
+
+	LWPOINTITERATOR* it = lwpointiterator_create(g);
+	POINT4D p;
+
+	for (i = 0; lwpointiterator_has_next(it); i++)
+	{
+		CU_ASSERT_EQUAL(LW_SUCCESS, lwpointiterator_next(it, &p));
+		CU_ASSERT_EQUAL(p.x, points[i].x);
+		CU_ASSERT_EQUAL(p.y, points[i].y);
+	}
+
+	lwpointiterator_destroy(it);
+	lwgeom_free(g);
+}
+
+/*
+** Used by test harness to register the tests in this file.
+*/
+void iterator_suite_setup(void);
+void iterator_suite_setup(void)
+{
+	CU_pSuite suite = CU_add_suite("iterator", NULL, NULL);
+	PG_ADD_TEST(suite, test_point_count);
+	PG_ADD_TEST(suite, test_ordering);
+	PG_ADD_TEST(suite, test_modification);
+	PG_ADD_TEST(suite, test_mixed_rw_access);
+	PG_ADD_TEST(suite, test_cannot_modify_read_only);
+	PG_ADD_TEST(suite, test_no_memory_leaked_when_iterator_is_partially_used);
+}

Modified: trunk/liblwgeom/cunit/cu_tester.c
===================================================================
--- trunk/liblwgeom/cunit/cu_tester.c	2015-11-18 05:14:52 UTC (rev 14399)
+++ trunk/liblwgeom/cunit/cu_tester.c	2015-11-18 13:05:32 UTC (rev 14400)
@@ -38,6 +38,7 @@
 extern void homogenize_suite_setup(void);
 extern void in_encoded_polyline_suite_setup(void);
 extern void in_geojson_suite_setup(void);
+extern void iterator_suite_setup(void);
 extern void twkb_in_suite_setup(void);
 extern void libgeom_suite_setup(void);
 extern void measures_suite_setup(void);
@@ -82,6 +83,7 @@
 #if HAVE_LIBJSON
 	in_geojson_suite_setup,
 #endif
+    iterator_suite_setup,
 	twkb_in_suite_setup,
 	libgeom_suite_setup,
 	measures_suite_setup,

Modified: trunk/liblwgeom/liblwgeom.h.in
===================================================================
--- trunk/liblwgeom/liblwgeom.h.in	2015-11-18 05:14:52 UTC (rev 14399)
+++ trunk/liblwgeom/liblwgeom.h.in	2015-11-18 13:05:32 UTC (rev 14400)
@@ -1598,7 +1598,53 @@
 */
 extern LWGEOM* lwgeom_flip_coordinates(LWGEOM *in);
 
+struct LWPOINTITERATOR;
+typedef struct LWPOINTITERATOR LWPOINTITERATOR;
+
 /**
+ * Create a new LWPOINTITERATOR over supplied LWGEOM*
+ */
+extern LWPOINTITERATOR* lwpointiterator_create(const LWGEOM* g);
+
+/**
+ * Create a new LWPOINTITERATOR over supplied LWGEOM*
+ * Supports modification of coordinates during iteration.
+ */
+extern LWPOINTITERATOR* lwpointiterator_create_rw(LWGEOM* g);
+
+/** 
+ * Free all memory associated with the iterator
+ */
+extern void lwpointiterator_destroy(LWPOINTITERATOR* s);
+
+/**
+ * Returns LW_TRUE if there is another point available in the iterator.
+ */
+extern int lwpointiterator_has_next(LWPOINTITERATOR* s);
+
+/**
+ * Attempts to replace the next point int the iterator with p, and advances
+ * the iterator to the next point.
+ * Returns LW_SUCCESS if the assignment was successful, LW_FAILURE otherwise.
+ * */
+extern int lwpointiterator_modify_next(LWPOINTITERATOR* s, const POINT4D* p);
+
+/**
+ * Attempts to assign the next point in the iterator to p, and advances
+ * the iterator to the next point.  If p is NULL, the iterator will be
+ * advanced without reading a point.
+ * Returns LW_SUCCESS if the assignment was successful, LW_FAILURE otherwise.
+ * */
+extern int lwpointiterator_next(LWPOINTITERATOR* s, POINT4D* p);
+
+/**
+ * Attempts to assigns the next point in the iterator to p.  Does not advance.
+ * Returns LW_SUCCESS if the assignment was successful, LW_FAILURE otherwise.
+ */
+extern int lwpointiterator_peek(LWPOINTITERATOR* s, POINT4D* p);
+
+
+/**
 * Convert a single hex digit into the corresponding char
 */
 extern uint8_t parse_hex(char *str);

Added: trunk/liblwgeom/lwiterator.c
===================================================================
--- trunk/liblwgeom/lwiterator.c	                        (rev 0)
+++ trunk/liblwgeom/lwiterator.c	2015-11-18 13:05:32 UTC (rev 14400)
@@ -0,0 +1,269 @@
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.net
+ *
+ * Copyright 2015 Daniel Baston <dbaston at gmail.com>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************/
+
+#include "liblwgeom.h"
+#include "lwgeom_log.h"
+
+struct LISTNODE
+{
+	struct LISTNODE* next;
+	void* item;
+};
+typedef struct LISTNODE LISTNODE;
+
+/* The LWPOINTITERATOR consists of two stacks of items to process: a stack
+ * of geometries, and a stack of POINTARRAYs extracted from those geometries.
+ * The index "i" refers to the "next" point, which is found at the top of the
+ * pointarrays stack.
+ *
+ * When the pointarrays stack is depleted, we pull a geometry from the geometry
+ * stack to replenish it.
+ */
+struct LWPOINTITERATOR
+{
+	LISTNODE* geoms;
+	LISTNODE* pointarrays;
+	uint32_t i;
+	char allow_modification;
+};
+
+static LISTNODE*
+prepend_node(void* g, LISTNODE* front)
+{
+	LISTNODE* n = lwalloc(sizeof(LISTNODE));
+	n->item = g;
+	n->next = front;
+
+	return n;
+}
+
+static LISTNODE*
+pop_node(LISTNODE* i)
+{
+	LISTNODE* next = i->next;
+	lwfree(i);
+	return next;
+}
+
+static int
+add_lwgeom_to_stack(LWPOINTITERATOR* s, LWGEOM* g)
+{
+	if (lwgeom_is_empty(g))
+		return LW_FAILURE;
+
+	s->geoms = prepend_node(g, s->geoms);
+	return LW_SUCCESS;
+}
+
+/** Return a pointer to the first of one or more LISTNODEs holding the POINTARRAYs
+ *  of a geometry.  Will not handle GeometryCollections.
+ */
+static LISTNODE*
+extract_pointarrays_from_lwgeom(LWGEOM* g)
+{
+	switch(lwgeom_get_type(g))
+	{
+	case POINTTYPE:
+		return prepend_node(lwgeom_as_lwpoint(g)->point, NULL);
+	case LINETYPE:
+		return prepend_node(lwgeom_as_lwline(g)->points, NULL);
+	case TRIANGLETYPE:
+		return prepend_node(lwgeom_as_lwtriangle(g)->points, NULL);
+	case CIRCSTRINGTYPE:
+		return prepend_node(lwgeom_as_lwcircstring(g)->points, NULL);
+	case POLYGONTYPE:
+	{
+		LISTNODE* n = NULL;
+
+		LWPOLY* p = lwgeom_as_lwpoly(g);
+		int i;
+		for (i = p->nrings - 1; i >= 0; i--)
+		{
+			n = prepend_node(p->rings[i], n);
+		}
+
+		return n;
+	}
+	default:
+		lwerror("Unsupported geometry type for lwpointiterator");
+	}
+
+	return NULL;
+}
+
+/** Remove an LWCOLLECTION from the iterator stack, and add the components of the
+ *  LWCOLLECTIONs to the stack.
+ */
+static void
+unroll_collection(LWPOINTITERATOR* s)
+{
+	int i;
+	LWCOLLECTION* c;
+
+	if (!s->geoms)
+	{
+		return;
+	}
+
+	c = (LWCOLLECTION*) s->geoms->item;
+	s->geoms = pop_node(s->geoms);
+
+	for (i = c->ngeoms - 1; i >= 0; i--)
+	{
+		LWGEOM* g = lwcollection_getsubgeom(c, i);
+
+		add_lwgeom_to_stack(s, g);
+	}
+}
+
+/** Unroll LWCOLLECTIONs from the top of the stack, as necessary, until the element at the
+ *  top of the stack is not a LWCOLLECTION.
+ */
+static void
+unroll_collections(LWPOINTITERATOR* s)
+{
+	while(s->geoms && lwgeom_is_collection(s->geoms->item))
+	{
+		unroll_collection(s);
+	}
+}
+
+static int
+lwpointiterator_advance(LWPOINTITERATOR* s)
+{
+	s->i += 1;
+
+	/* We've reached the end of our current POINTARRAY.  Try to see if there
+	 * are any more POINTARRAYS on the stack. */
+	if (s->pointarrays && s->i >= ((POINTARRAY*) s->pointarrays->item)->npoints)
+	{
+		s->pointarrays = pop_node(s->pointarrays);
+		s->i = 0;
+	}
+
+	/* We don't have a current POINTARRAY.  Pull a geometry from the stack, and
+	 * decompose it into its POINTARRARYs. */
+	if (!s->pointarrays)
+	{
+		LWGEOM* g;
+		unroll_collections(s);
+
+		if (!s->geoms)
+		{
+			return LW_FAILURE;
+		}
+
+		s->i = 0;
+		g = s->geoms->item;
+		s->pointarrays = extract_pointarrays_from_lwgeom(g);
+
+		s->geoms = pop_node(s->geoms);
+	}
+
+	if (!s->pointarrays)
+	{
+		return LW_FAILURE;
+	}
+	return LW_SUCCESS;
+}
+
+/* Public API implementation */
+
+int
+lwpointiterator_peek(LWPOINTITERATOR* s, POINT4D* p)
+{
+	if (!lwpointiterator_has_next(s))
+		return LW_FAILURE;
+
+	return getPoint4d_p(s->pointarrays->item, s->i, p);
+}
+
+int
+lwpointiterator_has_next(LWPOINTITERATOR* s)
+{
+	if (s->pointarrays && s->i < ((POINTARRAY*) s->pointarrays->item)->npoints)
+		return LW_TRUE;
+	return LW_FALSE;
+}
+
+int
+lwpointiterator_next(LWPOINTITERATOR* s, POINT4D* p)
+{
+	if (!lwpointiterator_has_next(s))
+		return LW_FAILURE;
+
+	/* If p is NULL, just advance without reading */
+	if (p && !lwpointiterator_peek(s, p))
+		return LW_FAILURE;
+
+	lwpointiterator_advance(s);
+	return LW_SUCCESS;
+}
+
+int
+lwpointiterator_modify_next(LWPOINTITERATOR* s, const POINT4D* p)
+{
+	if (!lwpointiterator_has_next(s))
+		return LW_FAILURE;
+
+	if (!s->allow_modification)
+	{
+		lwerror("Cannot write to read-only iterator");
+		return LW_FAILURE;
+	}
+
+	ptarray_set_point4d(s->pointarrays->item, s->i, p);
+
+	lwpointiterator_advance(s);
+	return LW_SUCCESS;
+}
+
+LWPOINTITERATOR*
+lwpointiterator_create(const LWGEOM* g)
+{
+	LWPOINTITERATOR* it = lwpointiterator_create_rw((LWGEOM*) g);
+	it->allow_modification = LW_FALSE;
+
+	return it;
+}
+
+LWPOINTITERATOR*
+lwpointiterator_create_rw(LWGEOM* g)
+{
+	LWPOINTITERATOR* it = lwalloc(sizeof(LWPOINTITERATOR));
+
+	it->geoms = NULL;
+	it->pointarrays = NULL;
+	it->i = 0;
+	it->allow_modification = LW_TRUE;
+
+	add_lwgeom_to_stack(it, g);
+	lwpointiterator_advance(it);
+
+	return it;
+}
+
+void
+lwpointiterator_destroy(LWPOINTITERATOR* s)
+{
+	while (s->geoms != NULL)
+	{
+		s->geoms = pop_node(s->geoms);
+	}
+
+	while (s->pointarrays != NULL)
+	{
+		s->pointarrays = pop_node(s->pointarrays);
+	}
+
+	lwfree(s);
+}



More information about the postgis-tickets mailing list