1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * PostGIS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * PostGIS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with PostGIS.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  **********************************************************************
20  *
21  * Copyright 2015 Daniel Baston <dbaston@gmail.com>
22  *
23  **********************************************************************/
24 
25 
26 #include "liblwgeom.h"
27 #include "lwgeom_log.h"
28 
29 struct LISTNODE
30 {
31 	struct LISTNODE* next;
32 	void* item;
33 };
34 typedef struct LISTNODE LISTNODE;
35 
36 /* The LWPOINTITERATOR consists of two stacks of items to process: a stack
37  * of geometries, and a stack of POINTARRAYs extracted from those geometries.
38  * The index "i" refers to the "next" point, which is found at the top of the
39  * pointarrays stack.
40  *
41  * When the pointarrays stack is depleted, we pull a geometry from the geometry
42  * stack to replenish it.
43  */
44 struct LWPOINTITERATOR
45 {
46 	LISTNODE* geoms;
47 	LISTNODE* pointarrays;
48 	uint32_t i;
49 	char allow_modification;
50 };
51 
52 static LISTNODE*
prepend_node(void * g,LISTNODE * front)53 prepend_node(void* g, LISTNODE* front)
54 {
55 	LISTNODE* n = lwalloc(sizeof(LISTNODE));
56 	n->item = g;
57 	n->next = front;
58 
59 	return n;
60 }
61 
62 static LISTNODE*
pop_node(LISTNODE * i)63 pop_node(LISTNODE* i)
64 {
65 	LISTNODE* next = i->next;
66 	lwfree(i);
67 	return next;
68 }
69 
70 static int
add_lwgeom_to_stack(LWPOINTITERATOR * s,LWGEOM * g)71 add_lwgeom_to_stack(LWPOINTITERATOR* s, LWGEOM* g)
72 {
73 	if (lwgeom_is_empty(g))
74 		return LW_FAILURE;
75 
76 	s->geoms = prepend_node(g, s->geoms);
77 	return LW_SUCCESS;
78 }
79 
80 /** Return a pointer to the first of one or more LISTNODEs holding the POINTARRAYs
81  *  of a geometry.  Will not handle GeometryCollections.
82  */
83 static LISTNODE*
extract_pointarrays_from_lwgeom(LWGEOM * g)84 extract_pointarrays_from_lwgeom(LWGEOM* g)
85 {
86 	switch(lwgeom_get_type(g))
87 	{
88 	case POINTTYPE:
89 		return prepend_node(lwgeom_as_lwpoint(g)->point, NULL);
90 	case LINETYPE:
91 		return prepend_node(lwgeom_as_lwline(g)->points, NULL);
92 	case TRIANGLETYPE:
93 		return prepend_node(lwgeom_as_lwtriangle(g)->points, NULL);
94 	case CIRCSTRINGTYPE:
95 		return prepend_node(lwgeom_as_lwcircstring(g)->points, NULL);
96 	case POLYGONTYPE:
97 	{
98 		LISTNODE* n = NULL;
99 
100 		LWPOLY* p = lwgeom_as_lwpoly(g);
101 		int i;
102 		for (i = p->nrings - 1; i >= 0; i--)
103 			n = prepend_node(p->rings[i], n);
104 
105 		return n;
106 	}
107 	default:
108 		lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(g->type));
109 	}
110 
111 	return NULL;
112 }
113 
114 /** Remove an LWCOLLECTION from the iterator stack, and add the components of the
115  *  LWCOLLECTIONs to the stack.
116  */
117 static void
unroll_collection(LWPOINTITERATOR * s)118 unroll_collection(LWPOINTITERATOR* s)
119 {
120 	int i;
121 	LWCOLLECTION* c;
122 
123 	if (!s->geoms)
124 	{
125 		return;
126 	}
127 
128 	c = (LWCOLLECTION*) s->geoms->item;
129 	s->geoms = pop_node(s->geoms);
130 
131 	for (i = c->ngeoms - 1; i >= 0; i--)
132 	{
133 		LWGEOM* g = lwcollection_getsubgeom(c, i);
134 
135 		add_lwgeom_to_stack(s, g);
136 	}
137 }
138 
139 /** Unroll LWCOLLECTIONs from the top of the stack, as necessary, until the element at the
140  *  top of the stack is not a LWCOLLECTION.
141  */
142 static void
unroll_collections(LWPOINTITERATOR * s)143 unroll_collections(LWPOINTITERATOR* s)
144 {
145 	while(s->geoms && lwgeom_is_collection(s->geoms->item))
146 	{
147 		unroll_collection(s);
148 	}
149 }
150 
151 static int
lwpointiterator_advance(LWPOINTITERATOR * s)152 lwpointiterator_advance(LWPOINTITERATOR* s)
153 {
154 	s->i += 1;
155 
156 	/* We've reached the end of our current POINTARRAY.  Try to see if there
157 	 * are any more POINTARRAYS on the stack. */
158 	if (s->pointarrays && s->i >= ((POINTARRAY*) s->pointarrays->item)->npoints)
159 	{
160 		s->pointarrays = pop_node(s->pointarrays);
161 		s->i = 0;
162 	}
163 
164 	/* We don't have a current POINTARRAY.  Pull a geometry from the stack, and
165 	 * decompose it into its POINTARRARYs. */
166 	if (!s->pointarrays)
167 	{
168 		LWGEOM* g;
169 		unroll_collections(s);
170 
171 		if (!s->geoms)
172 		{
173 			return LW_FAILURE;
174 		}
175 
176 		s->i = 0;
177 		g = s->geoms->item;
178 		s->pointarrays = extract_pointarrays_from_lwgeom(g);
179 
180 		s->geoms = pop_node(s->geoms);
181 	}
182 
183 	if (!s->pointarrays)
184 	{
185 		return LW_FAILURE;
186 	}
187 	return LW_SUCCESS;
188 }
189 
190 /* Public API implementation */
191 
192 int
lwpointiterator_peek(LWPOINTITERATOR * s,POINT4D * p)193 lwpointiterator_peek(LWPOINTITERATOR* s, POINT4D* p)
194 {
195 	if (!lwpointiterator_has_next(s))
196 		return LW_FAILURE;
197 
198 	return getPoint4d_p(s->pointarrays->item, s->i, p);
199 }
200 
201 int
lwpointiterator_has_next(LWPOINTITERATOR * s)202 lwpointiterator_has_next(LWPOINTITERATOR* s)
203 {
204 	if (s->pointarrays && s->i < ((POINTARRAY*) s->pointarrays->item)->npoints)
205 		return LW_TRUE;
206 	return LW_FALSE;
207 }
208 
209 int
lwpointiterator_next(LWPOINTITERATOR * s,POINT4D * p)210 lwpointiterator_next(LWPOINTITERATOR* s, POINT4D* p)
211 {
212 	if (!lwpointiterator_has_next(s))
213 		return LW_FAILURE;
214 
215 	/* If p is NULL, just advance without reading */
216 	if (p && !lwpointiterator_peek(s, p))
217 		return LW_FAILURE;
218 
219 	lwpointiterator_advance(s);
220 	return LW_SUCCESS;
221 }
222 
223 int
lwpointiterator_modify_next(LWPOINTITERATOR * s,const POINT4D * p)224 lwpointiterator_modify_next(LWPOINTITERATOR* s, const POINT4D* p)
225 {
226 	if (!lwpointiterator_has_next(s))
227 		return LW_FAILURE;
228 
229 	if (!s->allow_modification)
230 	{
231 		lwerror("Cannot write to read-only iterator");
232 		return LW_FAILURE;
233 	}
234 
235 	ptarray_set_point4d(s->pointarrays->item, s->i, p);
236 
237 	lwpointiterator_advance(s);
238 	return LW_SUCCESS;
239 }
240 
241 LWPOINTITERATOR*
lwpointiterator_create(const LWGEOM * g)242 lwpointiterator_create(const LWGEOM* g)
243 {
244 	LWPOINTITERATOR* it = lwpointiterator_create_rw((LWGEOM*) g);
245 	it->allow_modification = LW_FALSE;
246 
247 	return it;
248 }
249 
250 LWPOINTITERATOR*
lwpointiterator_create_rw(LWGEOM * g)251 lwpointiterator_create_rw(LWGEOM* g)
252 {
253 	LWPOINTITERATOR* it = lwalloc(sizeof(LWPOINTITERATOR));
254 
255 	it->geoms = NULL;
256 	it->pointarrays = NULL;
257 	it->i = 0;
258 	it->allow_modification = LW_TRUE;
259 
260 	add_lwgeom_to_stack(it, g);
261 	lwpointiterator_advance(it);
262 
263 	return it;
264 }
265 
266 void
lwpointiterator_destroy(LWPOINTITERATOR * s)267 lwpointiterator_destroy(LWPOINTITERATOR* s)
268 {
269 	while (s->geoms != NULL)
270 	{
271 		s->geoms = pop_node(s->geoms);
272 	}
273 
274 	while (s->pointarrays != NULL)
275 	{
276 		s->pointarrays = pop_node(s->pointarrays);
277 	}
278 
279 	lwfree(s);
280 }
281