[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