PostGIS  3.7.0dev-r@@SVN_REVISION@@
lwgeom_topo_polygonizer.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 (C) 2015-2024 Sandro Santilli <strk@kbt.io>
22  *
23  **********************************************************************
24  *
25  * Topology Polygonizer
26  *
27  **********************************************************************/
28 
29 #include "../postgis_config.h"
30 
31 /*#define POSTGIS_DEBUG_LEVEL 1*/
32 #include "lwgeom_log.h"
33 
34 #include "liblwgeom_internal.h"
36 
37 #include "lwgeom_geos.h"
38 
39 /* An array of pointers to EDGERING structures */
40 typedef struct LWT_ISO_EDGE_TABLE_T {
42  int size;
44 
45 static int
46 compare_iso_edges_by_id(const void *si1, const void *si2)
47 {
48  int a = ((LWT_ISO_EDGE *)si1)->edge_id;
49  int b = ((LWT_ISO_EDGE *)si2)->edge_id;
50  if ( a < b )
51  return -1;
52  else if ( a > b )
53  return 1;
54  else
55  return 0;
56 }
57 
58 static LWT_ISO_EDGE *
60 {
61  LWT_ISO_EDGE key;
62  key.edge_id = id;
63 
64  void *match = bsearch( &key, tab->edges, tab->size,
65  sizeof(LWT_ISO_EDGE),
67  return match;
68 }
69 
70 typedef struct LWT_EDGERING_ELEM_T {
71  /* externally owned */
73  /* 0 if false, 1 if true */
74  int left;
76 
77 /* A ring of edges */
78 typedef struct LWT_EDGERING_T {
79  /* Signed edge identifiers
80  * positive ones are walked in their direction, negative ones
81  * in the opposite direction */
83  /* Number of edges in the ring */
84  int size;
85  int capacity;
86  /* Bounding box of the ring */
88  /* Bounding box of the ring in GEOS format (for STRTree) */
89  GEOSGeometry *genv;
91 
92 #define LWT_EDGERING_INIT(a) { \
93  (a)->size = 0; \
94  (a)->capacity = 1; \
95  (a)->elems = lwalloc(sizeof(LWT_EDGERING_ELEM *) * (a)->capacity); \
96  (a)->env = NULL; \
97  (a)->genv = NULL; \
98 }
99 
100 #define LWT_EDGERING_PUSH(a, r) { \
101  if ( (a)->size + 1 > (a)->capacity ) { \
102  (a)->capacity *= 2; \
103  (a)->elems = lwrealloc((a)->elems, sizeof(LWT_EDGERING_ELEM *) * (a)->capacity); \
104  } \
105  /* lwdebug(1, "adding elem %d (%p) of edgering %p", (a)->size, (r), (a)); */ \
106  (a)->elems[(a)->size++] = (r); \
107 }
108 
109 #define LWT_EDGERING_CLEAN(a) { \
110  int i; for (i=0; i<(a)->size; ++i) { \
111  if ( (a)->elems[i] ) { \
112  /* lwdebug(1, "freeing elem %d (%p) of edgering %p", i, (a)->elems[i], (a)); */ \
113  lwfree((a)->elems[i]); \
114  } \
115  } \
116  if ( (a)->elems ) { lwfree((a)->elems); (a)->elems = NULL; } \
117  (a)->size = 0; \
118  (a)->capacity = 0; \
119  if ( (a)->env ) { lwfree((a)->env); (a)->env = NULL; } \
120  if ( (a)->genv ) { GEOSGeom_destroy((a)->genv); (a)->genv = NULL; } \
121 }
122 
123 /* An array of pointers to EDGERING structures */
124 typedef struct LWT_EDGERING_ARRAY_T {
126  int size;
127  int capacity;
128  GEOSSTRtree* tree;
130 
131 #define LWT_EDGERING_ARRAY_INIT(a) { \
132  (a)->size = 0; \
133  (a)->capacity = 1; \
134  (a)->rings = lwalloc(sizeof(LWT_EDGERING *) * (a)->capacity); \
135  (a)->tree = NULL; \
136 }
137 
138 /* WARNING: use of 'j' is intentional, not to clash with
139  * 'i' used in LWT_EDGERING_CLEAN */
140 #define LWT_EDGERING_ARRAY_CLEAN(a) { \
141  int j; for (j=0; j<(a)->size; ++j) { \
142  LWT_EDGERING_CLEAN((a)->rings[j]); \
143  } \
144  if ( (a)->capacity ) lwfree((a)->rings); \
145  if ( (a)->tree ) { \
146  GEOSSTRtree_destroy( (a)->tree ); \
147  (a)->tree = NULL; \
148  } \
149 }
150 
151 #define LWT_EDGERING_ARRAY_PUSH(a, r) { \
152  if ( (a)->size + 1 > (a)->capacity ) { \
153  (a)->capacity *= 2; \
154  (a)->rings = lwrealloc((a)->rings, sizeof(LWT_EDGERING *) * (a)->capacity); \
155  } \
156  (a)->rings[(a)->size++] = (r); \
157 }
158 
163  int curidx;
165 
166 static int
168 {
169  LWT_EDGERING_ELEM *el = it->curelem;
170  POINTARRAY *pa;
171 
172  if ( ! el ) return 0; /* finished */
173 
174  pa = el->edge->geom->points;
175 
176  int tonext = 0;
177  LWDEBUGF(3, "iterator fetching idx %d from pa of %d points", it->curidx, pa->npoints);
178  getPoint2d_p(pa, it->curidx, pt);
179  if ( el->left ) {
180  it->curidx++;
181  if ( it->curidx >= (int) pa->npoints ) tonext = 1;
182  } else {
183  it->curidx--;
184  if ( it->curidx < 0 ) tonext = 1;
185  }
186 
187  if ( tonext )
188  {
189  LWDEBUG(3, "iterator moving to next element");
190  it->curelemidx++;
191  if ( it->curelemidx < it->ring->size )
192  {
193  el = it->curelem = it->ring->elems[it->curelemidx];
194  it->curidx = el->left ? 0 : el->edge->geom->points->npoints - 1;
195  }
196  else
197  {
198  it->curelem = NULL;
199  }
200  }
201 
202  return 1;
203 }
204 
205 /* Release return with lwfree */
208 {
210  ret->ring = er;
211  if ( er->size ) ret->curelem = er->elems[0];
212  else ret->curelem = NULL;
213  ret->curelemidx = 0;
214  ret->curidx = (ret->curelem == NULL || ret->curelem->left) ? 0 : ret->curelem->edge->geom->points->npoints - 1;
215  return ret;
216 }
217 
218 /* Identifier for a placeholder face that will be
219  * used to mark hole rings */
220 #define LWT_HOLES_FACE_PLACEHOLDER INT32_MIN
221 
222 static int
224 {
225  while (
226  from < etab->size &&
227  etab->edges[from].face_left != -1 &&
228  etab->edges[from].face_right != -1
229  ) from++;
230  return from < etab->size ? from : -1;
231 }
232 
233 static LWT_ISO_EDGE *
234 _lwt_FetchAllEdges(LWT_TOPOLOGY *topo, int *numedges)
235 {
236  LWT_ISO_EDGE *edge;
237  int fields = LWT_COL_EDGE_ALL;
238  uint64_t nelems = 1;
239 
240  edge = lwt_be_getEdgeWithinBox2D( topo, NULL, &nelems, fields, 0);
241  *numedges = nelems;
242  if (nelems == UINT64_MAX)
243  {
244  PGTOPO_BE_ERROR();
245  return NULL;
246  }
247  return edge;
248 }
249 
250 /* Update the side face of given ring edges
251  *
252  * Edge identifiers are signed, those with negative identifier
253  * need to be updated their right_face, those with positive
254  * identifier need to be updated their left_face.
255  *
256  * @param face identifier of the face bound by the ring
257  * @return 0 on success, -1 on error
258  */
259 static int
261  LWT_ELEMID face)
262 {
263  LWT_ISO_EDGE *forward_edges = NULL;
264  int forward_edges_count = 0;
265  LWT_ISO_EDGE *backward_edges = NULL;
266  int backward_edges_count = 0;
267  int i, ret;
268 
269  /* Make a list of forward_edges and backward_edges */
270 
271  forward_edges = lwalloc(sizeof(LWT_ISO_EDGE) * ring->size);
272  forward_edges_count = 0;
273  backward_edges = lwalloc(sizeof(LWT_ISO_EDGE) * ring->size);
274  backward_edges_count = 0;
275 
276  for ( i=0; i<ring->size; ++i )
277  {
278  LWT_EDGERING_ELEM *elem = ring->elems[i];
279  LWT_ISO_EDGE *edge = elem->edge;
280  LWT_ELEMID id = edge->edge_id;
281  if ( elem->left )
282  {
283  LWDEBUGF(3, "Forward edge %d is %lld", forward_edges_count, id);
284  forward_edges[forward_edges_count].edge_id = id;
285  forward_edges[forward_edges_count++].face_left = face;
286  edge->face_left = face;
287  }
288  else
289  {
290  LWDEBUGF(3, "Backward edge %d is %lld", forward_edges_count, id);
291  backward_edges[backward_edges_count].edge_id = id;
292  backward_edges[backward_edges_count++].face_right = face;
293  edge->face_right = face;
294  }
295  }
296 
297  /* Update forward edges */
298  if ( forward_edges_count )
299  {
300  ret = lwt_be_updateEdgesById(topo, forward_edges,
301  forward_edges_count,
303  if ( ret == -1 )
304  {
305  lwfree( forward_edges );
306  lwfree( backward_edges );
307  PGTOPO_BE_ERROR();
308  return -1;
309  }
310  if ( ret != forward_edges_count )
311  {
312  lwfree( forward_edges );
313  lwfree( backward_edges );
314  lwerror("Unexpected error: %d edges updated when expecting %d (forward)",
315  ret, forward_edges_count);
316  return -1;
317  }
318  }
319 
320  /* Update backward edges */
321  if ( backward_edges_count )
322  {
323  ret = lwt_be_updateEdgesById(topo, backward_edges,
324  backward_edges_count,
326  if ( ret == -1 )
327  {
328  lwfree( forward_edges );
329  lwfree( backward_edges );
330  PGTOPO_BE_ERROR();
331  return -1;
332  }
333  if ( ret != backward_edges_count )
334  {
335  lwfree( forward_edges );
336  lwfree( backward_edges );
337  lwerror("Unexpected error: %d edges updated when expecting %d (backward)",
338  ret, backward_edges_count);
339  return -1;
340  }
341  }
342 
343  lwfree( forward_edges );
344  lwfree( backward_edges );
345 
346  return 0;
347 }
348 
349 /*
350  * @param side 1 for left side, -1 for right side
351  */
352 static LWT_EDGERING *
354  LWT_ISO_EDGE *edge, int side)
355 {
356  LWT_EDGERING *ring;
357  LWT_EDGERING_ELEM *elem;
358  LWT_ISO_EDGE *cur;
359  int curside;
360 
361  ring = lwalloc(sizeof(LWT_EDGERING));
362  LWT_EDGERING_INIT(ring);
363 
364  cur = edge;
365  curside = side;
366 
367  LWDEBUGF(2, "Building rings for edge %lld (side %d)", cur->edge_id, side);
368 
369  do {
370  LWT_ELEMID next;
371 
372  elem = lwalloc(sizeof(LWT_EDGERING_ELEM));
373  elem->edge = cur;
374  elem->left = ( curside == 1 );
375 
376  /* Mark edge as "visited" */
377  if ( elem->left ) cur->face_left = LWT_HOLES_FACE_PLACEHOLDER;
378  else cur->face_right = LWT_HOLES_FACE_PLACEHOLDER;
379 
380  LWT_EDGERING_PUSH(ring, elem);
381  next = elem->left ? cur->next_left : cur->next_right;
382 
383  LWDEBUGF(3, " next edge is %lld", next);
384 
385  if ( next > 0 ) curside = 1;
386  else { curside = -1; next = -next; }
387  cur = _lwt_getIsoEdgeById(edges, next);
388  if ( ! cur )
389  {
390  lwerror("Could not find edge with id %" LWTFMT_ELEMID, next);
391  break;
392  }
393  } while (cur != edge || curside != side);
394 
395  LWDEBUGF(1, "Ring for edge %lld has %d elems", edge->edge_id*side, ring->size);
396 
397  return ring;
398 }
399 
400 static double
402 {
403  POINT2D P1;
404  POINT2D P2;
405  POINT2D P3;
406  double sum = 0.0;
407  double x0, x, y1, y2;
408 
409  if ( ! _lwt_EdgeRingIterator_next(it, &P1) ) return 0.0;
410  if ( ! _lwt_EdgeRingIterator_next(it, &P2) ) return 0.0;
411 
412  LWDEBUG(2, "_lwt_EdgeRingSignedArea");
413 
414  x0 = P1.x;
415  while ( _lwt_EdgeRingIterator_next(it, &P3) )
416  {
417  x = P2.x - x0;
418  y1 = P3.y;
419  y2 = P1.y;
420  sum += x * (y2-y1);
421 
422  /* Move forwards! */
423  P1 = P2;
424  P2 = P3;
425  }
426 
427  return sum / 2.0;
428 }
429 
430 
431 /* Return 1 for true, 0 for false */
432 static int
434 {
435  double sa;
436 
437  LWDEBUGF(2, "_lwt_EdgeRingIsCCW, ring has %d elems", ring->size);
439  sa = _lwt_EdgeRingSignedArea(it);
440  LWDEBUGF(2, "_lwt_EdgeRingIsCCW, signed area is %g", sa);
441  lwfree(it);
442  if ( sa >= 0 ) return 0;
443  else return 1;
444 }
445 
446 static int
448 {
449  int cn = 0; /* the crossing number counter */
450  POINT2D v1, v2;
451 #ifndef RELAX
452  POINT2D v0;
453 #endif
454 
455  if ( ! _lwt_EdgeRingIterator_next(it, &v1) ) return cn;
456  v0 = v1;
457  while ( _lwt_EdgeRingIterator_next(it, &v2) )
458  {
459  double vt;
460 
461  /* edge from vertex i to vertex i+1 */
462  if
463  (
464  /* an upward crossing */
465  ((v1.y <= p->y) && (v2.y > p->y))
466  /* a downward crossing */
467  || ((v1.y > p->y) && (v2.y <= p->y))
468  )
469  {
470 
471  vt = (double)(p->y - v1.y) / (v2.y - v1.y);
472 
473  /* P->x <intersect */
474  if (p->x < v1.x + vt * (v2.x - v1.x))
475  {
476  /* a valid crossing of y=p->y right of p->x */
477  ++cn;
478  }
479  }
480  v1 = v2;
481  }
482 
483  LWDEBUGF(3, "_lwt_EdgeRingCrossingCount returning %d", cn);
484 
485 #ifndef RELAX
486  if ( memcmp(&v1, &v0, sizeof(POINT2D)) )
487  {
488  lwerror("_lwt_EdgeRingCrossingCount: V[n] != V[0] (%g %g != %g %g)",
489  v1.x, v1.y, v0.x, v0.y);
490  return -1;
491  }
492 #endif
493 
494  return cn;
495 }
496 
497 /* Return 1 for true, 0 for false */
498 static int
500 {
501  int cn = 0;
502 
504  cn = _lwt_EdgeRingCrossingCount(p, it);
505  lwfree(it);
506  return (cn&1); /* 0 if even (out), and 1 if odd (in) */
507 }
508 
509 static GBOX *
511 {
512  int i;
513 
514  if ( ! ring->env )
515  {
516  LWDEBUGF(2, "Computing GBOX for ring %p", ring);
517  for (i=0; i<ring->size; ++i)
518  {
519  LWT_EDGERING_ELEM *elem = ring->elems[i];
520  LWLINE *g = elem->edge->geom;
521  const GBOX *newbox = lwgeom_get_bbox(lwline_as_lwgeom(g));
522  if ( ! i ) ring->env = gbox_clone( newbox );
523  else gbox_merge( newbox, ring->env );
524  }
525  }
526 
527  return ring->env;
528 }
529 
530 static LWT_ELEMID
532 {
533  LWT_EDGERING_ELEM *el = ring->elems[0];
534  return el->left ? el->edge->face_left : el->edge->face_right;
535 }
536 
537 
538 /*
539  * Register a face on an edge side
540  *
541  * Create and register face to shell (CCW) walks,
542  * register arbitrary negative face_id to CW rings.
543  *
544  * Push CCW rings to shells, CW rings to holes.
545  *
546  * The ownership of the "geom" and "ids" members of the
547  * LWT_EDGERING pushed to the given LWT_EDGERING_ARRAYS
548  * are transferred to caller.
549  *
550  * @param side 1 for left side, -1 for right side
551  *
552  * @param holes an array where holes will be pushed
553  *
554  * @param shells an array where shells will be pushed
555  *
556  * @param registered id of registered face. It will be a negative number
557  * for holes or isolated edge strips (still registered in the face
558  * table, but only temporary).
559  *
560  * @return 0 on success, -1 on error.
561  *
562  */
563 static int
565  int side, LWT_ISO_EDGE_TABLE *edges,
566  LWT_EDGERING_ARRAY *holes,
567  LWT_EDGERING_ARRAY *shells,
568  LWT_ELEMID *registered)
569 {
570  const LWT_BE_IFACE *iface = topo->be_iface;
571  /* this is arbitrary, could be taken as parameter */
572  static const LWT_ELEMID placeholder_faceid = LWT_HOLES_FACE_PLACEHOLDER;
573  LWT_EDGERING *ring;
574 
575  /* Get edge ring */
576  ring = _lwt_BuildEdgeRing(topo, edges, edge, side);
577 
578  LWDEBUG(2, "Ring built, calling EdgeRingIsCCW");
579 
580  /* Compute winding (CW or CCW?) */
581  int isccw = _lwt_EdgeRingIsCCW(ring);
582 
583  if ( isccw )
584  {
585  /* Create new face */
586  LWT_ISO_FACE newface;
587 
588  LWDEBUGF(1, "Ring of edge %lld is a shell (shell %d)", edge->edge_id * side, shells->size);
589 
590  newface.mbr = _lwt_EdgeRingGetBbox(ring);
591 
592  newface.face_id = -1;
593  /* Insert the new face */
594  int ret = lwt_be_insertFaces( topo, &newface, 1 );
595  newface.mbr = NULL;
596  if ( ret == -1 )
597  {
598  PGTOPO_BE_ERROR();
599  return -1;
600  }
601  if ( ret != 1 )
602  {
603  lwerror("Unexpected error: %d faces inserted when expecting 1", ret);
604  return -1;
605  }
606  /* return new face_id */
607  *registered = newface.face_id;
608  LWT_EDGERING_ARRAY_PUSH(shells, ring);
609 
610  /* update ring edges set new face_id on resp. side to *registered */
611  ret = _lwt_UpdateEdgeRingSideFace(topo, ring, *registered);
612  if ( ret )
613  {
614  lwerror("Errors updating edgering side face: %s",
615  lwt_be_lastErrorMessage(iface));
616  return -1;
617  }
618 
619  }
620  else /* cw, so is an hole */
621  {
622  LWDEBUGF(1, "Ring of edge %lld is a hole (hole %d)", edge->edge_id * side, holes->size);
623  *registered = placeholder_faceid;
624  LWT_EDGERING_ARRAY_PUSH(holes, ring);
625  }
626 
627  return 0;
628 }
629 
630 static void
631 _lwt_AccumulateCanditates(void* item, void* userdata)
632 {
633  LWT_EDGERING_ARRAY *candidates = userdata;
634  LWT_EDGERING *sring = item;
635  LWT_EDGERING_ARRAY_PUSH(candidates, sring);
636 }
637 
638 static LWT_ELEMID
640  LWT_EDGERING_ARRAY *shells)
641 {
642  LWT_ELEMID foundInFace = -1;
643  int i;
644  const GBOX *minenv = NULL;
645  POINT2D pt;
646  const GBOX *testbox;
647  GEOSGeometry *ghole;
648 
649  getPoint2d_p( ring->elems[0]->edge->geom->points, 0, &pt );
650 
651  testbox = _lwt_EdgeRingGetBbox(ring);
652 
653  /* Create a GEOS Point from a vertex of the hole ring */
654  {
655  LWPOINT *point = lwpoint_make2d(topo->srid, pt.x, pt.y);
656  ghole = LWGEOM2GEOS( lwpoint_as_lwgeom(point), 1 );
657  lwpoint_free(point);
658  if ( ! ghole ) {
659  lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
660  return -1;
661  }
662  }
663 
664  /* Build STRtree of shell envelopes */
665  if ( ! shells->tree )
666  {
667  static const int STRTREE_NODE_CAPACITY = 10;
668  LWDEBUG(1, "Building STRtree");
669  shells->tree = GEOSSTRtree_create(STRTREE_NODE_CAPACITY);
670  if (shells->tree == NULL)
671  {
672  lwerror("Could not create GEOS STRTree: %s", lwgeom_geos_errmsg);
673  return -1;
674  }
675  for (i=0; i<shells->size; ++i)
676  {
677  LWT_EDGERING *sring = shells->rings[i];
678  const GBOX* shellbox = _lwt_EdgeRingGetBbox(sring);
679  LWDEBUGF(2, "GBOX of shell %p for edge %lld is %g %g,%g %g",
680  sring, sring->elems[0]->edge->edge_id, shellbox->xmin,
681  shellbox->ymin, shellbox->xmax, shellbox->ymax);
682  POINTARRAY *pa = ptarray_construct(0, 0, 2);
683  POINT4D pt;
684  LWLINE *diag;
685  pt.x = shellbox->xmin;
686  pt.y = shellbox->ymin;
687  ptarray_set_point4d(pa, 0, &pt);
688  pt.x = shellbox->xmax;
689  pt.y = shellbox->ymax;
690  ptarray_set_point4d(pa, 1, &pt);
691  diag = lwline_construct(topo->srid, NULL, pa);
692  /* Record just envelope in ggeom */
693  /* making valid, probably not needed */
694  sring->genv = LWGEOM2GEOS( lwline_as_lwgeom(diag), 1 );
695  lwline_free(diag);
696  GEOSSTRtree_insert(shells->tree, sring->genv, sring);
697  }
698  LWDEBUG(1, "STRtree build completed");
699  }
700 
701  LWT_EDGERING_ARRAY candidates;
702  LWT_EDGERING_ARRAY_INIT(&candidates);
703  GEOSSTRtree_query(shells->tree, ghole, &_lwt_AccumulateCanditates, &candidates);
704  LWDEBUGF(1, "Found %d candidate shells containing first point of ring's originating edge %lld",
705  candidates.size, ring->elems[0]->edge->edge_id * ( ring->elems[0]->left ? 1 : -1 ) );
706 
707  /* TODO: sort candidates by bounding box size */
708 
709  for (i=0; i<candidates.size; ++i)
710  {
711  LWT_EDGERING *sring = candidates.rings[i];
712  const GBOX* shellbox = _lwt_EdgeRingGetBbox(sring);
713  int contains = 0;
714 
715  if ( sring->elems[0]->edge->edge_id == ring->elems[0]->edge->edge_id )
716  {
717  LWDEBUGF(1, "Shell %lld is on other side of ring",
718  _lwt_EdgeRingGetFace(sring));
719  continue;
720  }
721 
722  /* The hole envelope cannot equal the shell envelope */
723  if ( gbox_same(shellbox, testbox) )
724  {
725  LWDEBUGF(1, "Bbox of shell %lld equals that of hole ring",
726  _lwt_EdgeRingGetFace(sring));
727  continue;
728  }
729 
730  /* Skip if ring box is not in shell box */
731  if ( ! gbox_contains_2d(shellbox, testbox) )
732  {
733  LWDEBUGF(1, "Bbox of shell %lld does not contain bbox of ring point",
734  _lwt_EdgeRingGetFace(sring));
735  continue;
736  }
737 
738  /* Skip test if a containing shell was already found
739  * and this shell's bbox is not contained in the other */
740  if ( minenv && ! gbox_contains_2d(minenv, shellbox) )
741  {
742  LWDEBUGF(2, "Bbox of shell %d (face %lld) not contained by bbox "
743  "of last shell found to contain the point",
744  i, _lwt_EdgeRingGetFace(sring));
745  continue;
746  }
747 
748  contains = _lwt_EdgeRingContainsPoint(sring, &pt);
749  if ( contains )
750  {
751  /* Continue until all shells are tested, as we want to
752  * use the one with the smallest bounding box */
753  /* IDEA: sort shells by bbox size, stopping on first match */
754  LWDEBUGF(1, "Shell %lld contains hole of edge %lld",
755  _lwt_EdgeRingGetFace(sring),
756  ring->elems[0]->edge->edge_id);
757  minenv = shellbox;
758  foundInFace = _lwt_EdgeRingGetFace(sring);
759  }
760  }
761  if ( foundInFace == -1 ) foundInFace = 0;
762 
763  candidates.size = 0; /* Avoid destroying the actual shell rings */
764  LWT_EDGERING_ARRAY_CLEAN(&candidates);
765 
766  GEOSGeom_destroy(ghole);
767 
768  return foundInFace;
769 }
770 
771 /*
772  * @return -1 on error (and report error),
773  * 1 if faces beside the universal one exist
774  * 0 otherwise
775  */
776 static int
778 {
779  LWT_ISO_FACE *faces;
780  int fields = LWT_COL_FACE_FACE_ID;
781  uint64_t nelems = 1;
782  GBOX qbox;
783 
784  qbox.xmin = qbox.ymin = -DBL_MAX;
785  qbox.xmax = qbox.ymax = DBL_MAX;
786  faces = lwt_be_getFaceWithinBox2D( topo, &qbox, &nelems, fields, 1);
787  if (nelems == UINT64_MAX)
788  {
789  PGTOPO_BE_ERROR();
790  return -1;
791  }
792  if ( faces )
793  {
794  /* we do not call _lwt_release_faces because we are not asking
795  * for the MBR so there are no nested objects to release */
796  lwfree(faces);
797  }
798  return nelems;
799 }
800 
801 int
803 {
804  /*
805  Fetch all edges
806  Sort edges by edge_id
807  Mark all edges' left and right face as -1
808  Iteratively:
809  Fetch next edge with left or right face == -1
810  For each side with face == -1:
811  Find ring on its side
812  If ring is CCW:
813  create a new face, assign to the ring edges' appropriate side
814  If ring is CW (face needs to be same of external):
815  assign a negative face_id the ring edges' appropriate side
816  Now for each edge with a negative face_id on the side:
817  Find containing face (mbr cache and all)
818  Update with id of containing face
819  */
820 
821  const LWT_BE_IFACE *iface = topo->be_iface;
822  LWT_ISO_EDGE *edge;
823  int numfaces = -1;
824  LWT_ISO_EDGE_TABLE edgetable;
825  LWT_EDGERING_ARRAY holes, shells;
826  int i;
827  int err = 0;
828 
829  LWT_EDGERING_ARRAY_INIT(&holes);
830  LWT_EDGERING_ARRAY_INIT(&shells);
831 
832  initGEOS(lwnotice, lwgeom_geos_error);
833 
834  /*
835  Check if Topology already contains some Face
836  (ignoring the Universal Face)
837  */
838  numfaces = _lwt_CheckFacesExist(topo);
839  if ( numfaces != 0 ) {
840  if ( numfaces > 0 ) {
841  /* Faces exist */
842  lwerror("Polygonize: face table is not empty.");
843  }
844  /* Backend error, message should have been printed already */
845  return -1;
846  }
847 
848 
849  edgetable.edges = _lwt_FetchAllEdges(topo, &(edgetable.size));
850  if ( ! edgetable.edges ) {
851  if (edgetable.size == 0) {
852  /* not an error: no Edges */
853  return 0;
854  }
855  /* error should have been printed already */
856  return -1;
857  }
858 
859  /* Sort edges by ID (to allow btree searches) */
860  qsort(edgetable.edges, edgetable.size, sizeof(LWT_ISO_EDGE), compare_iso_edges_by_id);
861 
862  /* Mark all edges as unvisited */
863  for (i=0; i<edgetable.size; ++i)
864  edgetable.edges[i].face_left = edgetable.edges[i].face_right = -1;
865 
866  i = 0;
867  while (1)
868  {
869  i = _lwt_FetchNextUnvisitedEdge(topo, &edgetable, i);
870  if ( i < 0 ) break; /* end of unvisited */
871  edge = &(edgetable.edges[i]);
872 
873  LWT_ELEMID newface = -1;
874 
875  LWDEBUGF(1, "Next face-missing edge has id:%lld, face_left:%lld, face_right:%lld",
876  edge->edge_id, edge->face_left, edge->face_right);
877  if ( edge->face_left == -1 )
878  {
879  err = _lwt_RegisterFaceOnEdgeSide(topo, edge, 1, &edgetable,
880  &holes, &shells, &newface);
881  if ( err ) break;
882  LWDEBUGF(1, "New face on the left of edge %lld is %lld",
883  edge->edge_id, newface);
884  edge->face_left = newface;
885  }
886  if ( edge->face_right == -1 )
887  {
888  err = _lwt_RegisterFaceOnEdgeSide(topo, edge, -1, &edgetable,
889  &holes, &shells, &newface);
890  if ( err ) break;
891  LWDEBUGF(1, "New face on the right of edge %lld is %lld",
892  edge->edge_id, newface);
893  edge->face_right = newface;
894  }
895  }
896 
897  if ( err )
898  {
899  _lwt_release_edges(edgetable.edges, edgetable.size);
900  LWT_EDGERING_ARRAY_CLEAN( &holes );
901  LWT_EDGERING_ARRAY_CLEAN( &shells );
902  lwerror("Errors fetching or registering face-missing edges: %s",
903  lwt_be_lastErrorMessage(iface));
904  return -1;
905  }
906 
907  LWDEBUGF(1, "Found %d holes and %d shells", holes.size, shells.size);
908 
909  /* TODO: sort holes by pt.x, sort shells by bbox.xmin */
910 
911  /* Assign shells to holes */
912  for (i=0; i<holes.size; ++i)
913  {
914  LWT_ELEMID containing_face;
915  LWT_EDGERING *ring = holes.rings[i];
916 
917  containing_face = _lwt_FindFaceContainingRing(topo, ring, &shells);
918  LWDEBUGF(1, "Ring %d contained by face %" LWTFMT_ELEMID, i, containing_face);
919  if ( containing_face == -1 )
920  {
921  _lwt_release_edges(edgetable.edges, edgetable.size);
922  LWT_EDGERING_ARRAY_CLEAN( &holes );
923  LWT_EDGERING_ARRAY_CLEAN( &shells );
924  lwerror("Errors finding face containing ring: %s",
925  lwt_be_lastErrorMessage(iface));
926  return -1;
927  }
928  int ret = _lwt_UpdateEdgeRingSideFace(topo, holes.rings[i], containing_face);
929  if ( ret )
930  {
931  _lwt_release_edges(edgetable.edges, edgetable.size);
932  LWT_EDGERING_ARRAY_CLEAN( &holes );
933  LWT_EDGERING_ARRAY_CLEAN( &shells );
934  lwerror("Errors updating edgering side face: %s",
935  lwt_be_lastErrorMessage(iface));
936  return -1;
937  }
938  }
939 
940  LWDEBUG(1, "All holes assigned, cleaning up");
941 
942  _lwt_release_edges(edgetable.edges, edgetable.size);
943 
944  /* delete all shell and hole EDGERINGS */
945  LWT_EDGERING_ARRAY_CLEAN( &holes );
946  LWT_EDGERING_ARRAY_CLEAN( &shells );
947 
948  return 0;
949 }
950 
951 
int gbox_merge(const GBOX *new_box, GBOX *merge_box)
Update the merged GBOX to be large enough to include itself and the new box.
Definition: gbox.c:257
int gbox_same(const GBOX *g1, const GBOX *g2)
Check if 2 given Gbox are the same.
Definition: gbox.c:164
GBOX * gbox_clone(const GBOX *gbox)
Definition: gbox.c:45
int gbox_contains_2d(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the first GBOX contains the second on the 2d plane, LW_FALSE otherwise.
Definition: gbox.c:339
char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, uint8_t autofix)
void lwgeom_geos_error(const char *fmt,...)
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:339
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition: lwpoint.c:163
void lwpoint_free(LWPOINT *pt)
Definition: lwpoint.c:213
int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point)
Definition: lwgeom_api.c:342
POINTARRAY * ptarray_construct(char hasz, char hasm, uint32_t npoints)
Construct an empty pointarray, allocating storage and setting the npoints, but not filling in any inf...
Definition: ptarray.c:51
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
void lwfree(void *mem)
Definition: lwutil.c:248
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:344
#define __attribute__(x)
Definition: liblwgeom.h:228
const GBOX * lwgeom_get_bbox(const LWGEOM *lwgeom)
Get a non-empty geometry bounding box, computing and caching it if not already there.
Definition: lwgeom.c:743
void * lwalloc(size_t size)
Definition: lwutil.c:227
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition: lwgeom_api.c:369
void lwline_free(LWLINE *line)
Definition: lwline.c:67
#define LWT_COL_EDGE_FACE_RIGHT
#define LWT_COL_FACE_FACE_ID
Face fields.
LWT_INT64 LWT_ELEMID
Identifier of topology element.
#define LWT_COL_EDGE_FACE_LEFT
#define LWT_COL_EDGE_ALL
const char * lwt_be_lastErrorMessage(const LWT_BE_IFACE *be)
Definition: lwgeom_topo.c:114
LWT_ISO_EDGE * lwt_be_getEdgeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
Definition: lwgeom_topo.c:173
void _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
Definition: lwgeom_topo.c:460
LWT_ISO_FACE * lwt_be_getFaceWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
Definition: lwgeom_topo.c:179
int lwt_be_updateEdgesById(LWT_TOPOLOGY *topo, const LWT_ISO_EDGE *edges, int numedges, int upd_fields)
Definition: lwgeom_topo.c:294
#define PGTOPO_BE_ERROR()
#define LWTFMT_ELEMID
int lwt_be_insertFaces(LWT_TOPOLOGY *topo, LWT_ISO_FACE *face, uint64_t numelems)
Definition: lwgeom_topo.c:191
static const int STRTREE_NODE_CAPACITY
Datum contains(PG_FUNCTION_ARGS)
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:101
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:106
void lwnotice(const char *fmt,...) __attribute__((format(printf
Write a notice out to the notice handler.
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
struct LWT_ISO_EDGE_TABLE_T LWT_ISO_EDGE_TABLE
static double _lwt_EdgeRingSignedArea(LWT_EDGERING_POINT_ITERATOR *it)
struct LWT_EDGERING_POINT_ITERATOR_T LWT_EDGERING_POINT_ITERATOR
static int _lwt_EdgeRingContainsPoint(LWT_EDGERING *ring, POINT2D *p)
struct LWT_EDGERING_ARRAY_T LWT_EDGERING_ARRAY
#define LWT_EDGERING_INIT(a)
static LWT_EDGERING * _lwt_BuildEdgeRing(__attribute__((__unused__)) LWT_TOPOLOGY *topo, LWT_ISO_EDGE_TABLE *edges, LWT_ISO_EDGE *edge, int side)
static LWT_ELEMID _lwt_EdgeRingGetFace(LWT_EDGERING *ring)
static int _lwt_UpdateEdgeRingSideFace(LWT_TOPOLOGY *topo, LWT_EDGERING *ring, LWT_ELEMID face)
static int _lwt_EdgeRingIsCCW(LWT_EDGERING *ring)
int lwt_Polygonize(LWT_TOPOLOGY *topo)
static LWT_EDGERING_POINT_ITERATOR * _lwt_EdgeRingIterator_begin(LWT_EDGERING *er)
static int _lwt_EdgeRingIterator_next(LWT_EDGERING_POINT_ITERATOR *it, POINT2D *pt)
#define LWT_HOLES_FACE_PLACEHOLDER
static int _lwt_CheckFacesExist(LWT_TOPOLOGY *topo)
static GBOX * _lwt_EdgeRingGetBbox(LWT_EDGERING *ring)
#define LWT_EDGERING_ARRAY_INIT(a)
struct LWT_EDGERING_ELEM_T LWT_EDGERING_ELEM
static LWT_ISO_EDGE * _lwt_getIsoEdgeById(LWT_ISO_EDGE_TABLE *tab, LWT_ELEMID id)
static int _lwt_RegisterFaceOnEdgeSide(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edge, int side, LWT_ISO_EDGE_TABLE *edges, LWT_EDGERING_ARRAY *holes, LWT_EDGERING_ARRAY *shells, LWT_ELEMID *registered)
static void _lwt_AccumulateCanditates(void *item, void *userdata)
#define LWT_EDGERING_PUSH(a, r)
static LWT_ELEMID _lwt_FindFaceContainingRing(LWT_TOPOLOGY *topo, LWT_EDGERING *ring, LWT_EDGERING_ARRAY *shells)
#define LWT_EDGERING_ARRAY_CLEAN(a)
static LWT_ISO_EDGE * _lwt_FetchAllEdges(LWT_TOPOLOGY *topo, int *numedges)
static int compare_iso_edges_by_id(const void *si1, const void *si2)
static int _lwt_FetchNextUnvisitedEdge(__attribute__((__unused__)) LWT_TOPOLOGY *topo, LWT_ISO_EDGE_TABLE *etab, int from)
#define LWT_EDGERING_ARRAY_PUSH(a, r)
static int _lwt_EdgeRingCrossingCount(const POINT2D *p, LWT_EDGERING_POINT_ITERATOR *it)
struct LWT_EDGERING_T LWT_EDGERING
double ymax
Definition: liblwgeom.h:357
double xmax
Definition: liblwgeom.h:355
double ymin
Definition: liblwgeom.h:356
double xmin
Definition: liblwgeom.h:354
POINTARRAY * points
Definition: liblwgeom.h:483
LWT_EDGERING_ELEM ** elems
LWT_ELEMID face_right
LWT_ELEMID face_left
LWLINE * geom
LWT_ELEMID edge_id
LWT_ELEMID face_id
const LWT_BE_IFACE * be_iface
double y
Definition: liblwgeom.h:390
double x
Definition: liblwgeom.h:390
double x
Definition: liblwgeom.h:414
double y
Definition: liblwgeom.h:414
uint32_t npoints
Definition: liblwgeom.h:427