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