PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
intervaltree.c
Go to the documentation of this file.
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 2023 Paul Ramsey <pramsey@cleverelephant.ca>
22 *
23 **********************************************************************/
24
25#include "intervaltree.h"
26
27void
29{
30 if (itree->nodes) lwfree(itree->nodes);
31 if (itree->ringCounts) lwfree(itree->ringCounts);
32 if (itree->indexArrays)
33 {
34 for (uint32_t i = 0; i < itree->numIndexes; i++)
35 {
36 if (itree->indexArrays[i])
37 ptarray_free(itree->indexArrays[i]);
38 }
39 }
40 if (itree->indexes) lwfree(itree->indexes);
41 if (itree->indexArrays) lwfree(itree->indexArrays);
42 lwfree(itree);
43}
44
45static uint32_t
47{
48 uint32_t num_nodes = 0;
49 uint32_t level_nodes = 0;
50
51 /* not a closed polygon */
52 if (!pa || pa->npoints < 4) return 0;
53
54 /* one leaf node per edge */
55 level_nodes = pa->npoints - 1;
56
57 while(level_nodes > 1)
58 {
59 uint32_t next_level_nodes = level_nodes / ITREE_MAX_NODES;
60 next_level_nodes += ((level_nodes % ITREE_MAX_NODES) > 0);
61 num_nodes += level_nodes;
62 level_nodes = next_level_nodes;
63 }
64 /* and the root node */
65 return num_nodes + 1;
66}
67
68static uint32_t
70{
71 uint32_t num_nodes = 0;
72 for (uint32_t i = 0; i < poly->nrings; i++)
73 {
74 const POINTARRAY *pa = poly->rings[i];
75 num_nodes += itree_num_nodes_pointarray(pa);
76 }
77 return num_nodes;
78}
79
80static uint32_t
82{
83 uint32_t num_nodes = 0;
84 for (uint32_t i = 0; i < mpoly->ngeoms; i++)
85 {
86 const LWPOLY *poly = mpoly->geoms[i];
87 num_nodes += itree_num_nodes_polygon(poly);
88 }
89 return num_nodes;
90}
91
92static uint32_t
94{
95 return lwgeom_count_rings((const LWGEOM*)mpoly);
96}
97
98static IntervalTreeNode *
100{
101 IntervalTreeNode *node = NULL;
102 if (itree->numNodes >= itree->maxNodes)
103 lwerror("%s ran out of node space", __func__);
104
105 node = &(itree->nodes[itree->numNodes++]);
106 node->min = FLT_MAX;
107 node->max = FLT_MIN;
108 node->edgeIndex = UINT32_MAX;
109 node->numChildren = 0;
110 memset(node->children, 0, sizeof(node->children));
111 return node;
112}
113
114
115static uint32_t // nodes_remaining for after latest merge, stop at 1
116itree_merge_nodes(IntervalTree *itree, uint32_t nodes_remaining)
117 {
118 /*
119 * store the starting state of the tree which gives
120 * us the array of nodes we are going to have to merge
121 */
122 uint32_t end_node = itree->numNodes;
123 uint32_t start_node = end_node - nodes_remaining;
124
125 /*
126 * one parent for every ITREE_MAX_NODES children
127 * plus one for the remainder
128 */
129 uint32_t num_parents = nodes_remaining / ITREE_MAX_NODES;
130 num_parents += ((nodes_remaining % ITREE_MAX_NODES) > 0);
131
132 /*
133 * each parent is composed by merging four adjacent children
134 * this generates a useful index structure in O(n) time
135 * because the edges are from a ring, and thus are
136 * spatially autocorrelated, so the pre-sorting step of
137 * building a packed index is already done for us before we even start
138 */
139 for (uint32_t i = 0; i < num_parents; i++)
140 {
141 uint32_t children_start = start_node + i * ITREE_MAX_NODES;
142 uint32_t children_end = start_node + (i+1) * ITREE_MAX_NODES;
143 children_end = children_end > end_node ? end_node : children_end;
144
145 /*
146 * put pointers to the children we are merging onto
147 * the new parent node
148 */
149 IntervalTreeNode *parent_node = itree_new_node(itree);
150 for (uint32_t j = children_start; j < children_end; j++)
151 {
152 IntervalTreeNode *child_node = &(itree->nodes[j]);
153 parent_node->min = FP_MIN(child_node->min, parent_node->min);
154 parent_node->max = FP_MAX(child_node->max, parent_node->max);
155 parent_node->edgeIndex = FP_MIN(child_node->edgeIndex, parent_node->edgeIndex);
156 parent_node->children[parent_node->numChildren++] = child_node;
157 }
158 }
159
160 /*
161 * keep going until num_parents gets down to one and
162 * we are at the root of the tree
163 */
164 return num_parents;
165}
166
167static int
168itree_edge_invalid(const POINT2D *pt1, const POINT2D *pt2)
169{
170 /* zero length */
171 if (pt1->x == pt2->x && pt1->y == pt2->y)
172 return 1;
173
174 /* nan/inf coordinates */
175 if (isfinite(pt1->x) &&
176 isfinite(pt1->y) &&
177 isfinite(pt2->x) &&
178 isfinite(pt2->y))
179 return 0;
180
181 return 1;
182}
183
184static void
186{
187 uint32_t nodes_remaining = 0;
188 uint32_t leaf_nodes = 0;
189 IntervalTreeNode *root = NULL;
190
191 /* EMPTY/unusable ring */
192 if (!pa || pa->npoints < 4)
193 lwerror("%s called with unusable ring", __func__);
194
195 /* fill in the leaf nodes */
196 for (uint32_t i = 0; i < pa->npoints-1; i++)
197 {
198 const POINT2D *pt1 = getPoint2d_cp(pa, i);
199 const POINT2D *pt2 = getPoint2d_cp(pa, i+1);
200
201 /* Do not add nodes for zero length segments */
202 if (itree_edge_invalid(pt1, pt2))
203 continue;
204
205 /* get a fresh node for each segment of the ring */
206 IntervalTreeNode *node = itree_new_node(itree);
207 node->min = FP_MIN(pt1->y, pt2->y);
208 node->max = FP_MAX(pt1->y, pt2->y);
209 node->edgeIndex = i;
210 leaf_nodes++;
211 }
212
213 /* merge leaf nodes up to parents */
214 nodes_remaining = leaf_nodes;
215 while (nodes_remaining > 1)
216 nodes_remaining = itree_merge_nodes(itree, nodes_remaining);
217
218 /* final parent is the root */
219 if (leaf_nodes > 0)
220 root = &(itree->nodes[itree->numNodes - 1]);
221 else
222 root = NULL;
223
224 /*
225 * take a copy of the point array we built this
226 * tree on top of so we can reference it to get
227 * segment information later
228 */
229 itree->indexes[itree->numIndexes] = root;
230 itree->indexArrays[itree->numIndexes] = ptarray_clone(pa);
231 itree->numIndexes += 1;
232
233 return;
234}
235
236
237static IntervalTree *
239{
240 IntervalTree *itree = lwalloc0(sizeof(IntervalTree));
241 if (poly->nrings == 0) return itree;
242
243 itree->maxNodes = itree_num_nodes_polygon(poly);
244 itree->nodes = lwalloc0(itree->maxNodes * sizeof(IntervalTreeNode));
245 itree->numNodes = 0;
246
247 itree->ringCounts = lwalloc0(sizeof(uint32_t));
248 itree->indexes = lwalloc0(poly->nrings * sizeof(IntervalTreeNode*));
249 itree->indexArrays = lwalloc0(poly->nrings * sizeof(POINTARRAY*));
250
251 for (uint32_t j = 0; j < poly->nrings; j++)
252 {
253 const POINTARRAY *pa = poly->rings[j];
254
255 /* skip empty/unclosed/invalid rings */
256 if (!pa || pa->npoints < 4)
257 continue;
258
259 itree_add_pointarray(itree, pa);
260
261 itree->ringCounts[itree->numPolys] += 1;
262 }
263 itree->numPolys = 1;
264 return itree;
265}
266
267
268static IntervalTree *
270{
271 IntervalTree *itree = lwalloc0(sizeof(IntervalTree));
272 if (mpoly->ngeoms == 0) return itree;
273
275 itree->nodes = lwalloc0(itree->maxNodes * sizeof(IntervalTreeNode));
276 itree->numNodes = 0;
277
278 itree->ringCounts = lwalloc0(mpoly->ngeoms * sizeof(uint32_t));
279 itree->indexes = lwalloc0(itree_num_rings(mpoly) * sizeof(IntervalTreeNode*));
280 itree->indexArrays = lwalloc0(itree_num_rings(mpoly) * sizeof(POINTARRAY*));
281
282 for (uint32_t i = 0; i < mpoly->ngeoms; i++)
283 {
284 const LWPOLY *poly = mpoly->geoms[i];
285
286 /* skip empty polygons */
287 if (! poly || lwpoly_is_empty(poly))
288 continue;
289
290 for (uint32_t j = 0; j < poly->nrings; j++)
291 {
292 const POINTARRAY *pa = poly->rings[j];
293
294 /* skip empty/unclosed/invalid rings */
295 if (!pa || pa->npoints < 4)
296 continue;
297
298 itree_add_pointarray(itree, pa);
299
300 itree->ringCounts[itree->numPolys] += 1;
301 }
302
303 itree->numPolys += 1;
304 }
305 return itree;
306}
307
308
311{
312 if (!geom) lwerror("%s called with null geometry", __func__);
313 switch(lwgeom_get_type(geom))
314 {
315 case MULTIPOLYGONTYPE:
317 case POLYGONTYPE:
319 default:
320 lwerror("%s got asked to build index on non-polygon", __func__);
321 }
322 return NULL;
323}
324
325
326/*******************************************************************************
327 * The following is based on the "Fast Winding Number Inclusion of a Point
328 * in a Polygon" algorithm by Dan Sunday.
329 * http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm#Winding%20Number
330 *
331 * returns: >0 for a point to the left of the segment,
332 * <0 for a point to the right of the segment,
333 * 0 for a point on the segment
334 */
335static inline double
336itree_segment_side(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point)
337{
338 return ((seg2->x - seg1->x) * (point->y - seg1->y) -
339 (point->x - seg1->x) * (seg2->y - seg1->y));
340}
341
342/*
343 * This function doesn't test that the point falls on the line defined by
344 * the two points. It assumes that that has already been determined
345 * by having itree_segment_side return within the tolerance. It simply checks
346 * that if the point is on the line, it is within the endpoints.
347 *
348 * returns: 1 if the point is inside the segment bounds
349 * 0 if the point is outside the segment bounds
350 */
351static int
352itree_point_on_segment(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point)
353{
354 double maxX = FP_MAX(seg1->x, seg2->x);
355 double maxY = FP_MAX(seg1->y, seg2->y);
356 double minX = FP_MIN(seg1->x, seg2->x);
357 double minY = FP_MIN(seg1->y, seg2->y);
358
359 return point->x >= minX && point->x <= maxX &&
360 point->y >= minY && point->y <= maxY;
361}
362
363
366 const IntervalTreeNode *node,
367 const POINTARRAY *pa,
368 const POINT2D *pt,
369 int *winding_number)
370{
371 if (!node) return ITREE_OUTSIDE;
372 /*
373 * If Y value is not within range of node, we can
374 * learn nothing from this node or its children, so
375 * we exit early.
376 */
377 uint8_t node_contains_value = FP_CONTAINS_INCL(node->min, pt->y, node->max) ? 1 : 0;
378 if (!node_contains_value)
379 return ITREE_OK;
380
381 /* This is a leaf node, so evaluate winding number */
382 if (node->numChildren == 0)
383 {
384 const POINT2D *seg1 = getPoint2d_cp(pa, node->edgeIndex);
385 const POINT2D *seg2 = getPoint2d_cp(pa, node->edgeIndex + 1);
386 double side = itree_segment_side(seg1, seg2, pt);
387
388 /* Zero length segments are ignored. */
389 // xxxx need a unit test, what about really really short segments?
390 // if (distance2d_sqr_pt_pt(seg1, seg2) < FP_EPS*FP_EPS)
391 // return ITREE_OK;
392
393 /* A point on the boundary of a ring is not contained. */
394 /* WAS: if (fabs(side) < 1e-12), see ticket #852 */
395 if (side == 0.0 && itree_point_on_segment(seg1, seg2, pt) == 1)
396 return ITREE_BOUNDARY;
397
398 /*
399 * If the point is to the left of the line, and it's rising,
400 * then the line is to the right of the point and
401 * circling counter-clockwise, so increment.
402 */
403 if ((seg1->y <= pt->y) && (pt->y < seg2->y) && (side > 0))
404 {
405 *winding_number = *winding_number + 1;
406 }
407
408 /*
409 * If the point is to the right of the line, and it's falling,
410 * then the line is to the right of the point and circling
411 * clockwise, so decrement.
412 */
413 else if ((seg2->y <= pt->y) && (pt->y < seg1->y) && (side < 0))
414 {
415 *winding_number = *winding_number - 1;
416 }
417
418 return ITREE_OK;
419 }
420 /* This is an interior node, so recurse downwards */
421 else
422 {
423 for (uint32_t i = 0; i < node->numChildren; i++)
424 {
425 IntervalTreeResult rslt = itree_point_in_ring_recursive(node->children[i], pa, pt, winding_number);
426 /* Short circuit and send back boundary result */
427 if (rslt == ITREE_BOUNDARY)
428 return rslt;
429 }
430 }
431 return ITREE_OK;
432}
433
435itree_point_in_ring(const IntervalTree *itree, uint32_t ringNumber, const POINT2D *pt)
436{
437 int winding_number = 0;
438 const IntervalTreeNode *node = itree->indexes[ringNumber];
439 const POINTARRAY *pa = itree->indexArrays[ringNumber];
440 IntervalTreeResult rslt = itree_point_in_ring_recursive(node, pa, pt, &winding_number);
441
442 /* Boundary case is separate from winding number */
443 if (rslt == ITREE_BOUNDARY) return rslt;
444
445 /* Not boundary, so evaluate winding number */
446 if (winding_number == 0)
447 return ITREE_OUTSIDE;
448 else
449 return ITREE_INSIDE;
450}
451
452
453/*
454 * Test if the given point falls within the given multipolygon.
455 * Assume bbox short-circuit has already been attempted.
456 * First check if point is within any of the outer rings.
457 * If not, it is outside. If so, check if the point is
458 * within the relevant inner rings. If not, it is inside.
459 */
462{
463 uint32_t i = 0;
464 const POINT2D *pt;
466
467 /* Empty is not within anything */
468 if (lwpoint_is_empty(point))
469 return ITREE_OUTSIDE;
470
471 /* Non-finite point is within anything */
472 pt = getPoint2d_cp(point->point, 0);
473 if (!pt || !(isfinite(pt->x) && isfinite(pt->y)))
474 return ITREE_OUTSIDE;
475
476 /* Is the point inside any of the exterior rings of the sub-polygons? */
477 for (uint32_t p = 0; p < itree->numPolys; p++)
478 {
479 uint32_t ringCount = itree->ringCounts[p];
480
481 /* Skip empty polygons */
482 if (ringCount == 0) continue;
483
484 /* Check result against exterior ring. */
485 result = itree_point_in_ring(itree, i, pt);
486
487 /* Boundary condition is a hard stop */
488 if (result == ITREE_BOUNDARY)
489 return ITREE_BOUNDARY;
490
491 /* We are inside an exterior ring! Are we outside all the holes? */
492 if (result == ITREE_INSIDE)
493 {
494 for(uint32_t r = 1; r < itree->ringCounts[p]; r++)
495 {
496 result = itree_point_in_ring(itree, i+r, pt);
497
498 /* Boundary condition is a hard stop */
499 if (result == ITREE_BOUNDARY)
500 return ITREE_BOUNDARY;
501
502 /*
503 * Inside a hole => Outside the polygon!
504 * But there might be other polygons lurking
505 * inside this hole so we have to continue
506 * and check all the exterior rings.
507 */
508 if (result == ITREE_INSIDE)
509 goto holes_done;
510 }
511 return ITREE_INSIDE;
512 }
513
514 holes_done:
515 /* Move to first ring of next polygon */
516 i += ringCount;
517 }
518
519 /* Not in any rings */
520 return ITREE_OUTSIDE;
521}
522
523
524
525
char * r
Definition cu_in_wkt.c:24
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition cu_print.c:267
static uint32_t itree_num_nodes_pointarray(const POINTARRAY *pa)
static IntervalTreeResult itree_point_in_ring(const IntervalTree *itree, uint32_t ringNumber, const POINT2D *pt)
static void itree_add_pointarray(IntervalTree *itree, const POINTARRAY *pa)
static uint32_t itree_num_rings(const LWMPOLY *mpoly)
static int itree_point_on_segment(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point)
static int itree_edge_invalid(const POINT2D *pt1, const POINT2D *pt2)
void itree_free(IntervalTree *itree)
static uint32_t itree_num_nodes_multipolygon(const LWMPOLY *mpoly)
static IntervalTreeResult itree_point_in_ring_recursive(const IntervalTreeNode *node, const POINTARRAY *pa, const POINT2D *pt, int *winding_number)
static uint32_t itree_merge_nodes(IntervalTree *itree, uint32_t nodes_remaining)
static IntervalTree * itree_from_multipolygon(const LWMPOLY *mpoly)
static double itree_segment_side(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point)
static IntervalTreeNode * itree_new_node(IntervalTree *itree)
static IntervalTree * itree_from_polygon(const LWPOLY *poly)
IntervalTreeResult itree_point_in_multipolygon(const IntervalTree *itree, const LWPOINT *point)
IntervalTree * itree_from_lwgeom(const LWGEOM *geom)
static uint32_t itree_num_nodes_polygon(const LWPOLY *poly)
#define ITREE_MAX_NODES
IntervalTreeResult
@ ITREE_BOUNDARY
@ ITREE_OK
@ ITREE_INSIDE
@ ITREE_OUTSIDE
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition lwgeom.c:243
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:107
void lwfree(void *mem)
Definition lwutil.c:248
#define POLYGONTYPE
Definition liblwgeom.h:104
LWMPOLY * lwgeom_as_lwmpoly(const LWGEOM *lwgeom)
Definition lwgeom.c:288
uint32_t lwgeom_count_rings(const LWGEOM *geom)
Count the total number of rings in any LWGEOM.
Definition lwgeom.c:1447
void ptarray_free(POINTARRAY *pa)
Definition ptarray.c:327
void * lwalloc0(size_t sz)
Definition lwutil.c:234
#define FP_MAX(A, B)
#define FP_MIN(A, B)
int lwpoint_is_empty(const LWPOINT *point)
#define FP_CONTAINS_INCL(A, X, B)
int lwpoly_is_empty(const LWPOLY *poly)
POINTARRAY * ptarray_clone(const POINTARRAY *ptarray)
Clone a POINTARRAY object.
Definition ptarray.c:674
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
#define UINT32_MAX
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition lwinline.h:141
static const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition lwinline.h:97
struct IntervalTreeNode * children[ITREE_MAX_NODES]
uint32_t numChildren
uint32_t maxNodes
struct IntervalTreeNode * nodes
uint32_t numNodes
struct IntervalTreeNode ** indexes
uint32_t * ringCounts
uint32_t numPolys
POINTARRAY ** indexArrays
uint32_t numIndexes
uint32_t ngeoms
Definition liblwgeom.h:566
LWPOLY ** geoms
Definition liblwgeom.h:561
POINTARRAY * point
Definition liblwgeom.h:471
POINTARRAY ** rings
Definition liblwgeom.h:519
uint32_t nrings
Definition liblwgeom.h:524
double y
Definition liblwgeom.h:390
double x
Definition liblwgeom.h:390
uint32_t npoints
Definition liblwgeom.h:427