Coverage Report

Created: 2026-10-17 07:17

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 NURBSCURVETYPE:
97
0
    return prepend_node(lwnurbscurve_get_control_points(lwgeom_as_lwnurbscurve(g)), NULL);
98
0
  case POLYGONTYPE:
99
0
  {
100
0
    LISTNODE* n = NULL;
101
102
0
    LWPOLY* p = lwgeom_as_lwpoly(g);
103
0
    int i;
104
0
    for (i = p->nrings - 1; i >= 0; i--)
105
0
      n = prepend_node(p->rings[i], n);
106
107
0
    return n;
108
0
  }
109
0
  default:
110
0
    lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(g->type));
111
0
  }
112
113
0
  return NULL;
114
0
}
115
116
/** Remove an LWCOLLECTION from the iterator stack, and add the components of the
117
 *  LWCOLLECTIONs to the stack.
118
 */
119
static void
120
unroll_collection(LWPOINTITERATOR* s)
121
0
{
122
0
  int i;
123
0
  LWCOLLECTION* c;
124
125
0
  if (!s->geoms)
126
0
  {
127
0
    return;
128
0
  }
129
130
0
  c = (LWCOLLECTION*) s->geoms->item;
131
0
  s->geoms = pop_node(s->geoms);
132
133
0
  for (i = c->ngeoms - 1; i >= 0; i--)
134
0
  {
135
0
    const LWGEOM* g = lwcollection_getsubgeom(c, i);
136
137
0
    add_lwgeom_to_stack(s, g);
138
0
  }
139
0
}
140
141
/** Unroll LWCOLLECTIONs from the top of the stack, as necessary, until the element at the
142
 *  top of the stack is not a LWCOLLECTION.
143
 */
144
static void
145
unroll_collections(LWPOINTITERATOR* s)
146
0
{
147
0
  while(s->geoms && lwgeom_is_collection(s->geoms->item))
148
0
  {
149
0
    unroll_collection(s);
150
0
  }
151
0
}
152
153
static int
154
lwpointiterator_advance(LWPOINTITERATOR* s)
155
0
{
156
0
  s->i += 1;
157
158
  /* We've reached the end of our current POINTARRAY.  Try to see if there
159
   * are any more POINTARRAYS on the stack. */
160
0
  if (s->pointarrays && s->i >= ((POINTARRAY*) s->pointarrays->item)->npoints)
161
0
  {
162
0
    s->pointarrays = pop_node(s->pointarrays);
163
0
    s->i = 0;
164
0
  }
165
166
  /* We don't have a current POINTARRAY.  Pull a geometry from the stack, and
167
   * decompose it into its POINTARRARYs. */
168
0
  if (!s->pointarrays)
169
0
  {
170
0
    const LWGEOM* g;
171
0
    unroll_collections(s);
172
173
0
    if (!s->geoms)
174
0
    {
175
0
      return LW_FAILURE;
176
0
    }
177
178
0
    s->i = 0;
179
0
    g = s->geoms->item;
180
0
    s->pointarrays = extract_pointarrays_from_lwgeom(g);
181
182
0
    s->geoms = pop_node(s->geoms);
183
0
  }
184
185
0
  if (!s->pointarrays)
186
0
  {
187
0
    return LW_FAILURE;
188
0
  }
189
0
  return LW_SUCCESS;
190
0
}
191
192
/* Public API implementation */
193
194
int
195
lwpointiterator_peek(LWPOINTITERATOR* s, POINT4D* p)
196
0
{
197
0
  if (!lwpointiterator_has_next(s))
198
0
    return LW_FAILURE;
199
200
0
  return getPoint4d_p(s->pointarrays->item, s->i, p);
201
0
}
202
203
int
204
lwpointiterator_has_next(LWPOINTITERATOR* s)
205
0
{
206
0
  if (s->pointarrays && s->i < ((POINTARRAY*) s->pointarrays->item)->npoints)
207
0
    return LW_TRUE;
208
0
  return LW_FALSE;
209
0
}
210
211
int
212
lwpointiterator_next(LWPOINTITERATOR* s, POINT4D* p)
213
0
{
214
0
  if (!lwpointiterator_has_next(s))
215
0
    return LW_FAILURE;
216
217
  /* If p is NULL, just advance without reading */
218
0
  if (p && !lwpointiterator_peek(s, p))
219
0
    return LW_FAILURE;
220
221
0
  lwpointiterator_advance(s);
222
0
  return LW_SUCCESS;
223
0
}
224
225
int
226
lwpointiterator_modify_next(LWPOINTITERATOR* s, const POINT4D* p)
227
0
{
228
0
  if (!lwpointiterator_has_next(s))
229
0
    return LW_FAILURE;
230
231
0
  if (!s->allow_modification)
232
0
  {
233
0
    lwerror("Cannot write to read-only iterator");
234
0
    return LW_FAILURE;
235
0
  }
236
237
0
  POINTARRAY *pa = (POINTARRAY *)(s->pointarrays->item);
238
0
  ptarray_set_point4d(pa, s->i, p);
239
240
0
  lwpointiterator_advance(s);
241
0
  return LW_SUCCESS;
242
0
}
243
244
LWPOINTITERATOR*
245
lwpointiterator_create(const LWGEOM* g)
246
0
{
247
0
  LWPOINTITERATOR* it = lwpointiterator_create_rw((LWGEOM*) g);
248
0
  it->allow_modification = LW_FALSE;
249
250
0
  return it;
251
0
}
252
253
LWPOINTITERATOR*
254
lwpointiterator_create_rw(LWGEOM* g)
255
0
{
256
0
  LWPOINTITERATOR* it = lwalloc(sizeof(LWPOINTITERATOR));
257
258
0
  it->geoms = NULL;
259
0
  it->pointarrays = NULL;
260
0
  it->i = 0;
261
0
  it->allow_modification = LW_TRUE;
262
263
0
  add_lwgeom_to_stack(it, g);
264
0
  lwpointiterator_advance(it);
265
266
0
  return it;
267
0
}
268
269
void
270
lwpointiterator_destroy(LWPOINTITERATOR* s)
271
0
{
272
0
  while (s->geoms != NULL)
273
0
  {
274
0
    s->geoms = pop_node(s->geoms);
275
0
  }
276
277
0
  while (s->pointarrays != NULL)
278
0
  {
279
0
    s->pointarrays = pop_node(s->pointarrays);
280
0
  }
281
282
0
  lwfree(s);
283
0
}