Coverage Report

Created: 2026-03-31 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/lwiterator.c
Line
Count
Source
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
  const 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*
53
prepend_node(const void* g, LISTNODE* front)
54
0
{
55
0
  LISTNODE* n = lwalloc(sizeof(LISTNODE));
56
0
  n->item = g;
57
0
  n->next = front;
58
59
0
  return n;
60
0
}
61
62
static LISTNODE*
63
pop_node(LISTNODE* i)
64
0
{
65
0
  LISTNODE* next = i->next;
66
0
  lwfree(i);
67
0
  return next;
68
0
}
69
70
static int
71
add_lwgeom_to_stack(LWPOINTITERATOR* s, const LWGEOM* g)
72
0
{
73
0
  if (lwgeom_is_empty(g))
74
0
    return LW_FAILURE;
75
76
0
  s->geoms = prepend_node(g, s->geoms);
77
0
  return LW_SUCCESS;
78
0
}
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*
84
extract_pointarrays_from_lwgeom(const LWGEOM* g)
85
0
{
86
0
  switch(lwgeom_get_type(g))
87
0
  {
88
0
  case POINTTYPE:
89
0
    return prepend_node(lwgeom_as_lwpoint(g)->point, NULL);
90
0
  case LINETYPE:
91
0
    return prepend_node(lwgeom_as_lwline(g)->points, NULL);
92
0
  case TRIANGLETYPE:
93
0
    return prepend_node(lwgeom_as_lwtriangle(g)->points, NULL);
94
0
  case CIRCSTRINGTYPE:
95
0
    return prepend_node(lwgeom_as_lwcircstring(g)->points, NULL);
96
0
  case POLYGONTYPE:
97
0
  {
98
0
    LISTNODE* n = NULL;
99
100
0
    LWPOLY* p = lwgeom_as_lwpoly(g);
101
0
    int i;
102
0
    for (i = p->nrings - 1; i >= 0; i--)
103
0
      n = prepend_node(p->rings[i], n);
104
105
0
    return n;
106
0
  }
107
0
  default:
108
0
    lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(g->type));
109
0
  }
110
111
0
  return NULL;
112
0
}
113
114
/** Remove an LWCOLLECTION from the iterator stack, and add the components of the
115
 *  LWCOLLECTIONs to the stack.
116
 */
117
static void
118
unroll_collection(LWPOINTITERATOR* s)
119
0
{
120
0
  int i;
121
0
  LWCOLLECTION* c;
122
123
0
  if (!s->geoms)
124
0
  {
125
0
    return;
126
0
  }
127
128
0
  c = (LWCOLLECTION*) s->geoms->item;
129
0
  s->geoms = pop_node(s->geoms);
130
131
0
  for (i = c->ngeoms - 1; i >= 0; i--)
132
0
  {
133
0
    const LWGEOM* g = lwcollection_getsubgeom(c, i);
134
135
0
    add_lwgeom_to_stack(s, g);
136
0
  }
137
0
}
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
143
unroll_collections(LWPOINTITERATOR* s)
144
0
{
145
0
  while(s->geoms && lwgeom_is_collection(s->geoms->item))
146
0
  {
147
0
    unroll_collection(s);
148
0
  }
149
0
}
150
151
static int
152
lwpointiterator_advance(LWPOINTITERATOR* s)
153
0
{
154
0
  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
0
  if (s->pointarrays && s->i >= ((POINTARRAY*) s->pointarrays->item)->npoints)
159
0
  {
160
0
    s->pointarrays = pop_node(s->pointarrays);
161
0
    s->i = 0;
162
0
  }
163
164
  /* We don't have a current POINTARRAY.  Pull a geometry from the stack, and
165
   * decompose it into its POINTARRARYs. */
166
0
  if (!s->pointarrays)
167
0
  {
168
0
    const LWGEOM* g;
169
0
    unroll_collections(s);
170
171
0
    if (!s->geoms)
172
0
    {
173
0
      return LW_FAILURE;
174
0
    }
175
176
0
    s->i = 0;
177
0
    g = s->geoms->item;
178
0
    s->pointarrays = extract_pointarrays_from_lwgeom(g);
179
180
0
    s->geoms = pop_node(s->geoms);
181
0
  }
182
183
0
  if (!s->pointarrays)
184
0
  {
185
0
    return LW_FAILURE;
186
0
  }
187
0
  return LW_SUCCESS;
188
0
}
189
190
/* Public API implementation */
191
192
int
193
lwpointiterator_peek(LWPOINTITERATOR* s, POINT4D* p)
194
0
{
195
0
  if (!lwpointiterator_has_next(s))
196
0
    return LW_FAILURE;
197
198
0
  return getPoint4d_p(s->pointarrays->item, s->i, p);
199
0
}
200
201
int
202
lwpointiterator_has_next(LWPOINTITERATOR* s)
203
0
{
204
0
  if (s->pointarrays && s->i < ((POINTARRAY*) s->pointarrays->item)->npoints)
205
0
    return LW_TRUE;
206
0
  return LW_FALSE;
207
0
}
208
209
int
210
lwpointiterator_next(LWPOINTITERATOR* s, POINT4D* p)
211
0
{
212
0
  if (!lwpointiterator_has_next(s))
213
0
    return LW_FAILURE;
214
215
  /* If p is NULL, just advance without reading */
216
0
  if (p && !lwpointiterator_peek(s, p))
217
0
    return LW_FAILURE;
218
219
0
  lwpointiterator_advance(s);
220
0
  return LW_SUCCESS;
221
0
}
222
223
int
224
lwpointiterator_modify_next(LWPOINTITERATOR* s, const POINT4D* p)
225
0
{
226
0
  if (!lwpointiterator_has_next(s))
227
0
    return LW_FAILURE;
228
229
0
  if (!s->allow_modification)
230
0
  {
231
0
    lwerror("Cannot write to read-only iterator");
232
0
    return LW_FAILURE;
233
0
  }
234
235
0
  POINTARRAY *pa = (POINTARRAY *)(s->pointarrays->item);
236
0
  ptarray_set_point4d(pa, s->i, p);
237
238
0
  lwpointiterator_advance(s);
239
0
  return LW_SUCCESS;
240
0
}
241
242
LWPOINTITERATOR*
243
lwpointiterator_create(const LWGEOM* g)
244
0
{
245
0
  LWPOINTITERATOR* it = lwpointiterator_create_rw((LWGEOM*) g);
246
0
  it->allow_modification = LW_FALSE;
247
248
0
  return it;
249
0
}
250
251
LWPOINTITERATOR*
252
lwpointiterator_create_rw(LWGEOM* g)
253
0
{
254
0
  LWPOINTITERATOR* it = lwalloc(sizeof(LWPOINTITERATOR));
255
256
0
  it->geoms = NULL;
257
0
  it->pointarrays = NULL;
258
0
  it->i = 0;
259
0
  it->allow_modification = LW_TRUE;
260
261
0
  add_lwgeom_to_stack(it, g);
262
0
  lwpointiterator_advance(it);
263
264
0
  return it;
265
0
}
266
267
void
268
lwpointiterator_destroy(LWPOINTITERATOR* s)
269
0
{
270
0
  while (s->geoms != NULL)
271
0
  {
272
0
    s->geoms = pop_node(s->geoms);
273
0
  }
274
275
0
  while (s->pointarrays != NULL)
276
0
  {
277
0
    s->pointarrays = pop_node(s->pointarrays);
278
0
  }
279
280
0
  lwfree(s);
281
0
}