PostGIS  3.7.0dev-r@@SVN_REVISION@@
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 
27 void
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 
45 static 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 
68 static 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 
80 static 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 
92 static uint32_t
93 itree_num_rings(const LWMPOLY *mpoly)
94 {
95  return lwgeom_count_rings((const LWGEOM*)mpoly);
96 }
97 
98 static 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 
115 static uint32_t // nodes_remaining for after latest merge, stop at 1
116 itree_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 
167 static int
168 itree_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 
184 static 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 
237 static 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 
268 static IntervalTree *
270 {
271  IntervalTree *itree = lwalloc0(sizeof(IntervalTree));
272  if (mpoly->ngeoms == 0) return itree;
273 
274  itree->maxNodes = itree_num_nodes_multipolygon(mpoly);
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 
309 IntervalTree *
311 {
312  if (!geom) lwerror("%s called with null geometry", __func__);
313  switch(lwgeom_get_type(geom))
314  {
315  case MULTIPOLYGONTYPE:
317  case POLYGONTYPE:
318  return itree_from_polygon(lwgeom_as_lwpoly(geom));
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  */
335 static inline double
336 itree_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  */
351 static int
352 itree_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 
364 static IntervalTreeResult
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 
434 static IntervalTreeResult
435 itree_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)
Definition: intervaltree.c:46
static IntervalTreeResult itree_point_in_ring(const IntervalTree *itree, uint32_t ringNumber, const POINT2D *pt)
Definition: intervaltree.c:435
static void itree_add_pointarray(IntervalTree *itree, const POINTARRAY *pa)
Definition: intervaltree.c:185
static uint32_t itree_num_rings(const LWMPOLY *mpoly)
Definition: intervaltree.c:93
IntervalTree * itree_from_lwgeom(const LWGEOM *geom)
Definition: intervaltree.c:310
static int itree_point_on_segment(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point)
Definition: intervaltree.c:352
static int itree_edge_invalid(const POINT2D *pt1, const POINT2D *pt2)
Definition: intervaltree.c:168
void itree_free(IntervalTree *itree)
Definition: intervaltree.c:28
static uint32_t itree_num_nodes_multipolygon(const LWMPOLY *mpoly)
Definition: intervaltree.c:81
static IntervalTreeResult itree_point_in_ring_recursive(const IntervalTreeNode *node, const POINTARRAY *pa, const POINT2D *pt, int *winding_number)
Definition: intervaltree.c:365
static uint32_t itree_merge_nodes(IntervalTree *itree, uint32_t nodes_remaining)
Definition: intervaltree.c:116
static IntervalTree * itree_from_multipolygon(const LWMPOLY *mpoly)
Definition: intervaltree.c:269
static double itree_segment_side(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point)
Definition: intervaltree.c:336
IntervalTreeResult itree_point_in_multipolygon(const IntervalTree *itree, const LWPOINT *point)
Definition: intervaltree.c:461
static IntervalTreeNode * itree_new_node(IntervalTree *itree)
Definition: intervaltree.c:99
static IntervalTree * itree_from_polygon(const LWPOLY *poly)
Definition: intervaltree.c:238
static uint32_t itree_num_nodes_polygon(const LWPOLY *poly)
Definition: intervaltree.c:69
#define ITREE_MAX_NODES
Definition: intervaltree.h:29
IntervalTreeResult
Definition: intervaltree.h:32
@ ITREE_BOUNDARY
Definition: intervaltree.h:34
@ ITREE_OK
Definition: intervaltree.h:36
@ ITREE_INSIDE
Definition: intervaltree.h:33
@ ITREE_OUTSIDE
Definition: intervaltree.h:35
LWMPOLY * lwgeom_as_lwmpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:260
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
void lwfree(void *mem)
Definition: lwutil.c:248
#define POLYGONTYPE
Definition: liblwgeom.h:104
uint32_t lwgeom_count_rings(const LWGEOM *geom)
Count the total number of rings in any LWGEOM.
Definition: lwgeom.c:1419
void ptarray_free(POINTARRAY *pa)
Definition: ptarray.c:327
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:215
POINTARRAY * ptarray_clone(const POINTARRAY *ptarray)
Clone a POINTARRAY object.
Definition: ptarray.c:674
#define FP_MAX(A, B)
#define FP_MIN(A, B)
int lwpoint_is_empty(const LWPOINT *point)
void * lwalloc0(size_t sz)
Definition: lwutil.c:234
#define FP_CONTAINS_INCL(A, X, B)
int lwpoly_is_empty(const LWPOLY *poly)
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
#define UINT32_MAX
Definition: lwin_wkt_lex.c:343
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
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwinline.h:141
struct IntervalTreeNode * children[ITREE_MAX_NODES]
Definition: intervaltree.h:43
uint32_t edgeIndex
Definition: intervaltree.h:44
uint32_t numChildren
Definition: intervaltree.h:45
uint32_t maxNodes
Definition: intervaltree.h:65
struct IntervalTreeNode * nodes
Definition: intervaltree.h:58
uint32_t numNodes
Definition: intervaltree.h:64
struct IntervalTreeNode ** indexes
Definition: intervaltree.h:59
uint32_t * ringCounts
Definition: intervaltree.h:62
uint32_t numPolys
Definition: intervaltree.h:63
POINTARRAY ** indexArrays
Definition: intervaltree.h:60
uint32_t numIndexes
Definition: intervaltree.h:61
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