PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
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 */
44
45static int
46compare_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
58static 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
70typedef struct LWT_EDGERING_ELEM_T {
71 /* externally owned */
73 /* 0 if false, 1 if true */
74 int left;
76
77/* A ring of edges */
78typedef 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;
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 */
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
165
166static 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
222static 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
233static 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 {
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 */
259static 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 );
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 );
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 */
352static 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
400static 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 */
432static int
434{
435 double sa;
436
437 LWDEBUGF(2, "_lwt_EdgeRingIsCCW, ring has %d elems", ring->size);
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
446static 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 */
498static 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
509static 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
530static 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 */
563static 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 {
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",
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
630static 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
638static 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
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",
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 */
776static 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 {
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
801int
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
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",
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",
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",
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 * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition lwgeom.c:372
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
void * lwalloc(size_t size)
Definition lwutil.c:227
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition lwline.c:42
void lwfree(void *mem)
Definition lwutil.c:248
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition lwpoint.c:163
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition lwgeom.c:367
#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:771
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
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
#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
LWT_ISO_FACE * lwt_be_getFaceWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
void _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
int lwt_be_updateEdgesById(LWT_TOPOLOGY *topo, const LWT_ISO_EDGE *edges, int numedges, int upd_fields)
const char * lwt_be_lastErrorMessage(const LWT_BE_IFACE *be)
#define PGTOPO_BE_ERROR()
#define LWTFMT_ELEMID
int lwt_be_insertFaces(LWT_TOPOLOGY *topo, LWT_ISO_FACE *face, uint64_t numelems)
LWT_ISO_EDGE * lwt_be_getEdgeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
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.
static LWT_EDGERING_POINT_ITERATOR * _lwt_EdgeRingIterator_begin(LWT_EDGERING *er)
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 GBOX * _lwt_EdgeRingGetBbox(LWT_EDGERING *ring)
static LWT_ELEMID _lwt_EdgeRingGetFace(LWT_EDGERING *ring)
static LWT_EDGERING * _lwt_BuildEdgeRing(__attribute__((__unused__)) LWT_TOPOLOGY *topo, LWT_ISO_EDGE_TABLE *edges, LWT_ISO_EDGE *edge, int side)
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 int _lwt_EdgeRingIterator_next(LWT_EDGERING_POINT_ITERATOR *it, POINT2D *pt)
#define LWT_HOLES_FACE_PLACEHOLDER
static int _lwt_CheckFacesExist(LWT_TOPOLOGY *topo)
#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)
static LWT_ISO_EDGE * _lwt_FetchAllEdges(LWT_TOPOLOGY *topo, int *numedges)
#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 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
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