PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
lwgeom_topo.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-2020 Sandro Santilli <strk@kbt.io>
22 *
23 **********************************************************************/
24
25
26
27#include "../postgis_config.h"
28
29/*#define POSTGIS_DEBUG_LEVEL 1*/
30#include "lwgeom_log.h"
31
32#include "liblwgeom_internal.h"
34#include "lwgeom_geos.h"
35
36#include <stdio.h>
37#include <inttypes.h> /* for PRId64 */
38#include <math.h>
39
40#ifdef WIN32
41# define LWTFMT_ELEMID "lld"
42#else
43# define LWTFMT_ELEMID PRId64
44#endif
45
46/*********************************************************************
47 *
48 * Backend iface
49 *
50 ********************************************************************/
51
53{
54 LWT_BE_IFACE *iface = lwalloc(sizeof(LWT_BE_IFACE));
55 iface->data = data;
56 iface->cb = NULL;
57 return iface;
58}
59
61 const LWT_BE_CALLBACKS* cb)
62{
63 iface->cb = cb;
64}
65
67{
68 lwfree(iface);
69}
70
71/*********************************************************************
72 *
73 * Backend wrappers
74 *
75 ********************************************************************/
76
77#define CHECKCB(be, method) do { \
78 if ( ! (be)->cb || ! (be)->cb->method ) \
79 lwerror("Callback " # method " not registered by backend"); \
80} while (0)
81
82#define CB0(be, method) \
83 CHECKCB(be, method);\
84 return (be)->cb->method((be)->data)
85
86#define CB1(be, method, a1) \
87 CHECKCB(be, method);\
88 return (be)->cb->method((be)->data, a1)
89
90#define CBT0(to, method) \
91 CHECKCB((to)->be_iface, method);\
92 return (to)->be_iface->cb->method((to)->be_topo)
93
94#define CBT1(to, method, a1) \
95 CHECKCB((to)->be_iface, method);\
96 return (to)->be_iface->cb->method((to)->be_topo, a1)
97
98#define CBT2(to, method, a1, a2) \
99 CHECKCB((to)->be_iface, method);\
100 return (to)->be_iface->cb->method((to)->be_topo, a1, a2)
101
102#define CBT3(to, method, a1, a2, a3) \
103 CHECKCB((to)->be_iface, method);\
104 return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3)
105
106#define CBT4(to, method, a1, a2, a3, a4) \
107 CHECKCB((to)->be_iface, method);\
108 return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3, a4)
109
110#define CBT5(to, method, a1, a2, a3, a4, a5) \
111 CHECKCB((to)->be_iface, method);\
112 return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3, a4, a5)
113
114#define CBT6(to, method, a1, a2, a3, a4, a5, a6) \
115 CHECKCB((to)->be_iface, method);\
116 return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3, a4, a5, a6)
117
118const char *
120{
121 CB0(be, lastErrorMessage);
122}
123
126{
127 CB1(be, loadTopologyByName, name);
128}
129
130static int
132{
133 CBT0(topo, topoGetSRID);
134}
135
136static double
138{
139 CBT0(topo, topoGetPrecision);
140}
141
142static int
144{
145 CBT0(topo, topoHasZ);
146}
147
148int
150{
151 CBT0(topo, freeTopology);
152}
153
155lwt_be_getNodeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
156{
157 CBT3(topo, getNodeById, ids, numelems, fields);
158}
159
162 LWPOINT *pt,
163 double dist,
164 uint64_t *numelems,
165 int fields,
166 int64_t limit)
167{
168 CBT5(topo, getNodeWithinDistance2D, pt, dist, numelems, fields, limit);
169}
170
171static LWT_ISO_NODE *
172lwt_be_getNodeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
173{
174 CBT4(topo, getNodeWithinBox2D, box, numelems, fields, limit);
175}
176
177static LWT_ISO_EDGE *
178lwt_be_getEdgeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
179{
180 CBT4(topo, getEdgeWithinBox2D, box, numelems, fields, limit);
181}
182
183static LWT_ISO_FACE *
184lwt_be_getFaceWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
185{
186 CBT4(topo, getFaceWithinBox2D, box, numelems, fields, limit);
187}
188
189int
190lwt_be_insertNodes(LWT_TOPOLOGY *topo, LWT_ISO_NODE *node, uint64_t numelems)
191{
192 CBT2(topo, insertNodes, node, numelems);
193}
194
195static int
196lwt_be_insertFaces(LWT_TOPOLOGY *topo, LWT_ISO_FACE *face, uint64_t numelems)
197{
198 CBT2(topo, insertFaces, face, numelems);
199}
200
201static int
202lwt_be_deleteFacesById(const LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems)
203{
204 CBT2(topo, deleteFacesById, ids, numelems);
205}
206
207static int
208lwt_be_deleteNodesById(const LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems)
209{
210 CBT2(topo, deleteNodesById, ids, numelems);
211}
212
215{
216 CBT0(topo, getNextEdgeId);
217}
218
220lwt_be_getEdgeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
221{
222 CBT3(topo, getEdgeById, ids, numelems, fields);
223}
224
225static LWT_ISO_FACE *
226lwt_be_getFaceById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
227{
228 CBT3(topo, getFaceById, ids, numelems, fields);
229}
230
231static LWT_ISO_EDGE *
232lwt_be_getEdgeByNode(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
233{
234 CBT3(topo, getEdgeByNode, ids, numelems, fields);
235}
236
237static LWT_ISO_EDGE *
238lwt_be_getEdgeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box)
239{
240 CBT4(topo, getEdgeByFace, ids, numelems, fields, box);
241}
242
243static LWT_ISO_NODE *
244lwt_be_getNodeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box)
245{
246 CBT4(topo, getNodeByFace, ids, numelems, fields, box);
247}
248
251 LWPOINT *pt,
252 double dist,
253 uint64_t *numelems,
254 int fields,
255 int64_t limit)
256{
257 CBT5(topo, getEdgeWithinDistance2D, pt, dist, numelems, fields, limit);
258}
259
260int
261lwt_be_insertEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edge, uint64_t numelems)
262{
263 CBT2(topo, insertEdges, edge, numelems);
264}
265
266int
268 const LWT_ISO_EDGE* sel_edge, int sel_fields,
269 const LWT_ISO_EDGE* upd_edge, int upd_fields,
270 const LWT_ISO_EDGE* exc_edge, int exc_fields
271)
272{
273 CBT6(topo, updateEdges, sel_edge, sel_fields,
274 upd_edge, upd_fields,
275 exc_edge, exc_fields);
276}
277
278static int
280 const LWT_ISO_NODE* sel_node, int sel_fields,
281 const LWT_ISO_NODE* upd_node, int upd_fields,
282 const LWT_ISO_NODE* exc_node, int exc_fields
283)
284{
285 CBT6(topo, updateNodes, sel_node, sel_fields,
286 upd_node, upd_fields,
287 exc_node, exc_fields);
288}
289
290static uint64_t
292 const LWT_ISO_FACE* faces, uint64_t numfaces
293)
294{
295 CBT2(topo, updateFacesById, faces, numfaces);
296}
297
298static int
300 const LWT_ISO_EDGE* edges, int numedges, int upd_fields
301)
302{
303 CBT3(topo, updateEdgesById, edges, numedges, upd_fields);
304}
305
306static int
308 const LWT_ISO_NODE* nodes, int numnodes, int upd_fields
309)
310{
311 CBT3(topo, updateNodesById, nodes, numnodes, upd_fields);
312}
313
314int
316 const LWT_ISO_EDGE* sel_edge, int sel_fields
317)
318{
319 CBT2(topo, deleteEdges, sel_edge, sel_fields);
320}
321
324{
325 CBT1(topo, getFaceContainingPoint, pt);
326}
327
328
329int
331{
332 CBT3(topo, updateTopoGeomEdgeSplit, split_edge, new_edge1, new_edge2);
333}
334
335static int
337 LWT_ELEMID new_face1, LWT_ELEMID new_face2)
338{
339 CBT3(topo, updateTopoGeomFaceSplit, split_face, new_face1, new_face2);
340}
341
342static int
344 LWT_ELEMID face_left, LWT_ELEMID face_right)
345{
346 CBT3(topo, checkTopoGeomRemEdge, edge_id, face_left, face_right);
347}
348
349static int
351 LWT_ELEMID eid1, LWT_ELEMID eid2)
352{
353 CBT3(topo, checkTopoGeomRemNode, node_id, eid1, eid2);
354}
355
356static int
358 LWT_ELEMID face1, LWT_ELEMID face2,
359 LWT_ELEMID newface)
360{
361 CBT3(topo, updateTopoGeomFaceHeal, face1, face2, newface);
362}
363
364static int
366 LWT_ELEMID edge1, LWT_ELEMID edge2,
367 LWT_ELEMID newedge)
368{
369 CBT3(topo, updateTopoGeomEdgeHeal, edge1, edge2, newedge);
370}
371
372static LWT_ELEMID *
373lwt_be_getRingEdges(LWT_TOPOLOGY *topo, LWT_ELEMID edge, uint64_t *numedges, uint64_t limit)
374{
375 CBT3(topo, getRingEdges, edge, numedges, limit);
376}
377
378
379/* wrappers of backend wrappers... */
380
381int
383{
384 uint64_t exists = 0;
385 lwt_be_getNodeWithinDistance2D(topo, pt, 0, &exists, 0, -1);
386 if (exists == UINT64_MAX)
387 {
388 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
389 return 0;
390 }
391 return exists;
392}
393
394int
396{
397 uint64_t exists = 0;
398 lwt_be_getEdgeWithinDistance2D(topo, pt, 0, &exists, 0, -1);
399 if (exists == UINT64_MAX)
400 {
401 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
402 return 0;
403 }
404 return exists;
405}
406
407/************************************************************************
408 *
409 * Utility functions
410 *
411 ************************************************************************/
412
413static LWGEOM *
414_lwt_toposnap(LWGEOM *src, LWGEOM *tgt, double tol)
415{
416 LWGEOM *tmp = src;
417 LWGEOM *tmp2;
418 int changed;
419 int iterations = 0;
420
421 int maxiterations = lwgeom_count_vertices(tgt);
422
423 /* GEOS snapping can be unstable */
424 /* See https://trac.osgeo.org/geos/ticket/760 */
425 do {
426 tmp2 = lwgeom_snap(tmp, tgt, tol);
427 ++iterations;
428 changed = ( lwgeom_count_vertices(tmp2) != lwgeom_count_vertices(tmp) );
429 LWDEBUGF(2, "After iteration %d, geometry changed ? %d (%d vs %d vertices)", iterations, changed, lwgeom_count_vertices(tmp2), lwgeom_count_vertices(tmp));
430 if ( tmp != src ) lwgeom_free(tmp);
431 tmp = tmp2;
432 } while ( changed && iterations <= maxiterations );
433
434 LWDEBUGF(1, "It took %d/%d iterations to properly snap",
435 iterations, maxiterations);
436
437 return tmp;
438}
439
440static void
441_lwt_release_faces(LWT_ISO_FACE *faces, int num_faces)
442{
443 int i;
444 for ( i=0; i<num_faces; ++i ) {
445 if ( faces[i].mbr ) lwfree(faces[i].mbr);
446 }
447 lwfree(faces);
448}
449
450static void
451_lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
452{
453 int i;
454 for ( i=0; i<num_edges; ++i ) {
455 if ( edges[i].geom ) lwline_free(edges[i].geom);
456 }
457 lwfree(edges);
458}
459
460static void
461_lwt_release_nodes(LWT_ISO_NODE *nodes, int num_nodes)
462{
463 int i;
464 for ( i=0; i<num_nodes; ++i ) {
465 if ( nodes[i].geom ) lwpoint_free(nodes[i].geom);
466 }
467 lwfree(nodes);
468}
469
470/************************************************************************
471 *
472 * API implementation
473 *
474 ************************************************************************/
475
477lwt_LoadTopology( LWT_BE_IFACE *iface, const char *name )
478{
479 LWT_BE_TOPOLOGY* be_topo;
480 LWT_TOPOLOGY* topo;
481
482 be_topo = lwt_be_loadTopologyByName(iface, name);
483 if ( ! be_topo ) {
484 //lwerror("Could not load topology from backend: %s",
485 lwerror("%s", lwt_be_lastErrorMessage(iface));
486 return NULL;
487 }
488 topo = lwalloc(sizeof(LWT_TOPOLOGY));
489 topo->be_iface = iface;
490 topo->be_topo = be_topo;
491 topo->srid = lwt_be_topoGetSRID(topo);
492 topo->hasZ = lwt_be_topoHasZ(topo);
494
495 return topo;
496}
497
498void
500{
501 if ( ! lwt_be_freeTopology(topo) ) {
502 lwnotice("Could not release backend topology memory: %s",
504 }
505 lwfree(topo);
506}
507
516static LWT_ELEMID
518 LWPOINT* pt, int skipISOChecks, int checkFace )
519{
520 LWT_ELEMID foundInFace = -1;
521
522 if ( lwpoint_is_empty(pt) )
523 {
524 lwerror("Cannot add empty point as isolated node");
525 return -1;
526 }
527
528
529 if ( ! skipISOChecks )
530 {
531 if ( lwt_be_ExistsCoincidentNode(topo, pt) ) /*x*/
532 {
533 lwerror("SQL/MM Spatial exception - coincident node");
534 return -1;
535 }
536 if ( lwt_be_ExistsEdgeIntersectingPoint(topo, pt) ) /*x*/
537 {
538 lwerror("SQL/MM Spatial exception - edge crosses node.");
539 return -1;
540 }
541 }
542
543 if ( checkFace && ( face == -1 || ! skipISOChecks ) )
544 {
545 foundInFace = lwt_be_getFaceContainingPoint(topo, pt); /*x*/
546 if ( foundInFace == -2 ) {
547 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
548 return -1;
549 }
550 if ( foundInFace == -1 ) foundInFace = 0;
551 }
552
553 if ( face == -1 ) {
554 face = foundInFace;
555 }
556 else if ( ! skipISOChecks && foundInFace != face ) {
557#if 0
558 lwerror("SQL/MM Spatial exception - within face %d (not %d)",
559 foundInFace, face);
560#else
561 lwerror("SQL/MM Spatial exception - not within face");
562#endif
563 return -1;
564 }
565
566 LWT_ISO_NODE node;
567 node.node_id = -1;
568 node.containing_face = face;
569 node.geom = pt;
570 if ( ! lwt_be_insertNodes(topo, &node, 1) )
571 {
572 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
573 return -1;
574 }
575
576 return node.node_id;
577}
578
581 LWPOINT* pt, int skipISOChecks )
582{
583 return _lwt_AddIsoNode( topo, face, pt, skipISOChecks, 1 );
584}
585
586/* Check that an edge does not cross an existing node or edge
587 *
588 * @param myself the id of an edge to skip, if any
589 * (for ChangeEdgeGeom). Can use 0 for none.
590 *
591 * Return -1 on cross or error, 0 if everything is fine.
592 * Note that before returning -1, lwerror is invoked...
593 */
594static int
596 LWT_ELEMID start_node, LWT_ELEMID end_node,
597 const LWLINE *geom, LWT_ELEMID myself )
598{
599 uint64_t i, num_nodes, num_edges;
600 LWT_ISO_EDGE *edges;
601 LWT_ISO_NODE *nodes;
602 const GBOX *edgebox;
603 GEOSGeometry *edgegg;
604
605 initGEOS(lwnotice, lwgeom_geos_error);
606
607 edgegg = LWGEOM2GEOS(lwline_as_lwgeom(geom), 0);
608 if (!edgegg)
609 {
610 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
611 return -1;
612 }
613 edgebox = lwgeom_get_bbox( lwline_as_lwgeom(geom) );
614
615 /* loop over each node within the edge's gbox */
616 nodes = lwt_be_getNodeWithinBox2D( topo, edgebox, &num_nodes,
617 LWT_COL_NODE_ALL, 0 );
618 LWDEBUGF(1, "lwt_be_getNodeWithinBox2D returned %d nodes", num_nodes);
619 if (num_nodes == UINT64_MAX)
620 {
621 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
622 return -1;
623 }
624 for ( i=0; i<num_nodes; ++i )
625 {
626 const POINT2D *pt;
627 LWT_ISO_NODE* node = &(nodes[i]);
628 if ( node->node_id == start_node ) continue;
629 if ( node->node_id == end_node ) continue;
630 /* check if the edge contains this node (not on boundary) */
631 /* ST_RelateMatch(rec.relate, 'T********') */
632 pt = getPoint2d_cp(node->geom->point, 0);
634 if ( contains )
635 {
636 GEOSGeom_destroy(edgegg);
637 _lwt_release_nodes(nodes, num_nodes);
638 lwerror("SQL/MM Spatial exception - geometry crosses a node");
639 return -1;
640 }
641 }
642 if ( nodes ) _lwt_release_nodes(nodes, num_nodes);
643 /* may be NULL if num_nodes == 0 */
644
645 /* loop over each edge within the edge's gbox */
646 edges = lwt_be_getEdgeWithinBox2D( topo, edgebox, &num_edges, LWT_COL_EDGE_ALL, 0 );
647 LWDEBUGF(1, "lwt_be_getEdgeWithinBox2D returned %d edges", num_edges);
648 if (num_edges == UINT64_MAX)
649 {
650 GEOSGeom_destroy(edgegg);
651 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
652 return -1;
653 }
654 for ( i=0; i<num_edges; ++i )
655 {
656 LWT_ISO_EDGE* edge = &(edges[i]);
657 LWT_ELEMID edge_id = edge->edge_id;
658 GEOSGeometry *eegg;
659 char *relate;
660 int match;
661
662 if ( edge_id == myself ) continue;
663
664 if ( ! edge->geom ) {
665 _lwt_release_edges(edges, num_edges);
666 lwerror("Edge %d has NULL geometry!", edge_id);
667 return -1;
668 }
669
670 eegg = LWGEOM2GEOS( lwline_as_lwgeom(edge->geom), 0 );
671 if ( ! eegg ) {
672 GEOSGeom_destroy(edgegg);
673 _lwt_release_edges(edges, num_edges);
674 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
675 return -1;
676 }
677
678 LWDEBUGF(2, "Edge %d converted to GEOS", edge_id);
679
680 /* check if the edge crosses our edge (not boundary-boundary) */
681
682 relate = GEOSRelateBoundaryNodeRule(eegg, edgegg, 2);
683 if ( ! relate ) {
684 GEOSGeom_destroy(eegg);
685 GEOSGeom_destroy(edgegg);
686 _lwt_release_edges(edges, num_edges);
687 lwerror("GEOSRelateBoundaryNodeRule error: %s", lwgeom_geos_errmsg);
688 return -1;
689 }
690
691 LWDEBUGF(2, "Edge %d relate pattern is %s", edge_id, relate);
692
693 match = GEOSRelatePatternMatch(relate, "F********");
694 if ( match ) {
695 /* error or no interior intersection */
696 GEOSGeom_destroy(eegg);
697 GEOSFree(relate);
698 if ( match == 2 ) {
699 _lwt_release_edges(edges, num_edges);
700 GEOSGeom_destroy(edgegg);
701 lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
702 return -1;
703 }
704 else continue; /* no interior intersection */
705 }
706
707 match = GEOSRelatePatternMatch(relate, "1FFF*FFF2");
708 if ( match ) {
709 _lwt_release_edges(edges, num_edges);
710 GEOSGeom_destroy(edgegg);
711 GEOSGeom_destroy(eegg);
712 GEOSFree(relate);
713 if ( match == 2 ) {
714 lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
715 } else {
716 lwerror("SQL/MM Spatial exception - coincident edge %" LWTFMT_ELEMID,
717 edge_id);
718 }
719 return -1;
720 }
721
722 match = GEOSRelatePatternMatch(relate, "1********");
723 if ( match ) {
724 _lwt_release_edges(edges, num_edges);
725 GEOSGeom_destroy(edgegg);
726 GEOSGeom_destroy(eegg);
727 GEOSFree(relate);
728 if ( match == 2 ) {
729 lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
730 } else {
731 lwerror("Spatial exception - geometry intersects edge %"
732 LWTFMT_ELEMID, edge_id);
733 }
734 return -1;
735 }
736
737 match = GEOSRelatePatternMatch(relate, "T********");
738 if ( match ) {
739 _lwt_release_edges(edges, num_edges);
740 GEOSGeom_destroy(edgegg);
741 GEOSGeom_destroy(eegg);
742 GEOSFree(relate);
743 if ( match == 2 ) {
744 lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
745 } else {
746 lwerror("SQL/MM Spatial exception - geometry crosses edge %"
747 LWTFMT_ELEMID, edge_id);
748 }
749 return -1;
750 }
751
752 LWDEBUGF(2, "Edge %d analisys completed, it does no harm", edge_id);
753
754 GEOSFree(relate);
755 GEOSGeom_destroy(eegg);
756 }
757 if ( edges ) _lwt_release_edges(edges, num_edges);
758 /* would be NULL if num_edges was 0 */
759
760 GEOSGeom_destroy(edgegg);
761
762 return 0;
763}
764
765
768 LWT_ELEMID endNode, const LWLINE* geom )
769{
770 uint64_t num_nodes;
771 uint64_t i;
772 LWT_ISO_EDGE newedge;
773 LWT_ISO_NODE *endpoints;
774 LWT_ELEMID containing_face = -1;
775 LWT_ELEMID node_ids[2];
776 LWT_ISO_NODE updated_nodes[2];
777 int skipISOChecks = 0;
778 POINT2D p1, p2;
779
780 /* NOT IN THE SPECS:
781 * A closed edge is never isolated (as it forms a face)
782 */
783 if (startNode == endNode)
784 {
785 lwerror("Closed edges would not be isolated, try lwt_AddEdgeNewFaces");
786 return -1;
787 }
788
789 if ( ! skipISOChecks )
790 {
791 /* Acurve must be simple */
792 if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) )
793 {
794 lwerror("SQL/MM Spatial exception - curve not simple");
795 return -1;
796 }
797 }
798
799 /*
800 * Check for:
801 * existence of nodes
802 * nodes faces match
803 * Extract:
804 * nodes face id
805 * nodes geoms
806 */
807 num_nodes = 2;
808 node_ids[0] = startNode;
809 node_ids[1] = endNode;
810 endpoints = lwt_be_getNodeById( topo, node_ids, &num_nodes,
812 if (num_nodes == UINT64_MAX)
813 {
814 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
815 return -1;
816 }
817 else if ( num_nodes < 2 )
818 {
819 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
820 lwerror("SQL/MM Spatial exception - non-existent node");
821 return -1;
822 }
823 for ( i=0; i<num_nodes; ++i )
824 {
825 const LWT_ISO_NODE *n = &(endpoints[i]);
826 if ( n->containing_face == -1 )
827 {
828 _lwt_release_nodes(endpoints, num_nodes);
829 lwerror("SQL/MM Spatial exception - not isolated node");
830 return -1;
831 }
832 if ( containing_face == -1 ) containing_face = n->containing_face;
833 else if ( containing_face != n->containing_face )
834 {
835 _lwt_release_nodes(endpoints, num_nodes);
836 lwerror("SQL/MM Spatial exception - nodes in different faces");
837 return -1;
838 }
839
840 if ( ! skipISOChecks )
841 {
842 if ( n->node_id == startNode )
843 {
844 /* l) Check that start point of acurve match start node geoms. */
845 getPoint2d_p(geom->points, 0, &p1);
846 getPoint2d_p(n->geom->point, 0, &p2);
847 if ( ! p2d_same(&p1, &p2) )
848 {
849 _lwt_release_nodes(endpoints, num_nodes);
850 lwerror("SQL/MM Spatial exception - "
851 "start node not geometry start point.");
852 return -1;
853 }
854 }
855 else
856 {
857 /* m) Check that end point of acurve match end node geoms. */
858 getPoint2d_p(geom->points, geom->points->npoints-1, &p1);
859 getPoint2d_p(n->geom->point, 0, &p2);
860 if ( ! p2d_same(&p1, &p2) )
861 {
862 _lwt_release_nodes(endpoints, num_nodes);
863 lwerror("SQL/MM Spatial exception - "
864 "end node not geometry end point.");
865 return -1;
866 }
867 }
868 }
869 }
870
871 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
872
873 if ( ! skipISOChecks )
874 {
875 if ( _lwt_CheckEdgeCrossing( topo, startNode, endNode, geom, 0 ) )
876 {
877 /* would have called lwerror already, leaking :( */
878 return -1;
879 }
880 }
881
882 /*
883 * All checks passed, time to prepare the new edge
884 */
885
886 newedge.edge_id = lwt_be_getNextEdgeId( topo );
887 if ( newedge.edge_id == -1 ) {
888 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
889 return -1;
890 }
891
892 /* TODO: this should likely be an exception instead ! */
893 if ( containing_face == -1 ) containing_face = 0;
894
895 newedge.start_node = startNode;
896 newedge.end_node = endNode;
897 newedge.face_left = newedge.face_right = containing_face;
898 newedge.next_left = -newedge.edge_id;
899 newedge.next_right = newedge.edge_id;
900 newedge.geom = (LWLINE *)geom; /* const cast.. */
901
902 int ret = lwt_be_insertEdges(topo, &newedge, 1);
903 if ( ret == -1 ) {
904 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
905 return -1;
906 } else if ( ret == 0 ) {
907 lwerror("Insertion of split edge failed (no reason)");
908 return -1;
909 }
910
911 /*
912 * Update Node containing_face values
913 *
914 * the nodes anode and anothernode are no more isolated
915 * because now there is an edge connecting them
916 */
917 updated_nodes[0].node_id = startNode;
918 updated_nodes[0].containing_face = -1;
919 updated_nodes[1].node_id = endNode;
920 updated_nodes[1].containing_face = -1;
921 ret = lwt_be_updateNodesById(topo, updated_nodes, 2,
923 if ( ret == -1 ) {
924 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
925 return -1;
926 }
927
928 return newedge.edge_id;
929}
930
931static LWCOLLECTION *
932_lwt_EdgeSplit( LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWPOINT* pt, int skipISOChecks, LWT_ISO_EDGE** oldedge )
933{
934 LWGEOM *split;
935 LWCOLLECTION *split_col;
936 uint64_t i;
937
938 /* Get edge */
939 i = 1;
940 LWDEBUG(1, "calling lwt_be_getEdgeById");
941 *oldedge = lwt_be_getEdgeById(topo, &edge, &i, LWT_COL_EDGE_ALL);
942 LWDEBUGF(1, "lwt_be_getEdgeById returned %p", *oldedge);
943 if ( ! *oldedge )
944 {
945 LWDEBUGF(1, "lwt_be_getEdgeById returned NULL and set i=%d", i);
946 if (i == UINT64_MAX)
947 {
948 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
949 return NULL;
950 }
951 else if ( i == 0 )
952 {
953 lwerror("SQL/MM Spatial exception - non-existent edge");
954 return NULL;
955 }
956 else
957 {
958 lwerror("Backend coding error: getEdgeById callback returned NULL "
959 "but numelements output parameter has value %d "
960 "(expected 0 or 1)", i);
961 return NULL;
962 }
963 }
964
965
966 /*
967 * - check if a coincident node already exists
968 */
969 if ( ! skipISOChecks )
970 {
971 LWDEBUG(1, "calling lwt_be_ExistsCoincidentNode");
972 if ( lwt_be_ExistsCoincidentNode(topo, pt) ) /*x*/
973 {
974 LWDEBUG(1, "lwt_be_ExistsCoincidentNode returned");
975 _lwt_release_edges(*oldedge, 1);
976 lwerror("SQL/MM Spatial exception - coincident node");
977 return NULL;
978 }
979 LWDEBUG(1, "lwt_be_ExistsCoincidentNode returned");
980 }
981
982 /* Split edge */
983 split = lwgeom_split((LWGEOM*)(*oldedge)->geom, (LWGEOM*)pt);
984 if ( ! split )
985 {
986 _lwt_release_edges(*oldedge, 1);
987 lwerror("could not split edge by point ?");
988 return NULL;
989 }
990 split_col = lwgeom_as_lwcollection(split);
991 if ( ! split_col ) {
992 _lwt_release_edges(*oldedge, 1);
993 lwgeom_free(split);
994 lwerror("lwgeom_as_lwcollection returned NULL");
995 return NULL;
996 }
997 if (split_col->ngeoms < 2) {
998 _lwt_release_edges(*oldedge, 1);
999 lwgeom_free(split);
1000 lwerror("SQL/MM Spatial exception - point not on edge");
1001 return NULL;
1002 }
1003
1004#if 0
1005 {
1006 size_t sz;
1007 char *wkt = lwgeom_to_wkt((LWGEOM*)split_col, WKT_EXTENDED, 2, &sz);
1008 LWDEBUGF(1, "returning split col: %s", wkt);
1009 lwfree(wkt);
1010 }
1011#endif
1012 return split_col;
1013}
1014
1017 LWPOINT* pt, int skipISOChecks )
1018{
1019 LWT_ISO_NODE node;
1020 LWT_ISO_EDGE* oldedge = NULL;
1021 LWCOLLECTION *split_col;
1022 const LWGEOM *oldedge_geom;
1023 const LWGEOM *newedge_geom;
1024 LWT_ISO_EDGE newedge1;
1025 LWT_ISO_EDGE seledge, updedge, excedge;
1026 int ret;
1027
1028 split_col = _lwt_EdgeSplit( topo, edge, pt, skipISOChecks, &oldedge );
1029 if ( ! split_col ) return -1; /* should have raised an exception */
1030 oldedge_geom = split_col->geoms[0];
1031 newedge_geom = split_col->geoms[1];
1032 /* Make sure the SRID is set on the subgeom */
1033 ((LWGEOM*)oldedge_geom)->srid = split_col->srid;
1034 ((LWGEOM*)newedge_geom)->srid = split_col->srid;
1035
1036 /* Add new node, getting new id back */
1037 node.node_id = -1;
1038 node.containing_face = -1; /* means not-isolated */
1039 node.geom = pt;
1040 if ( ! lwt_be_insertNodes(topo, &node, 1) )
1041 {
1042 _lwt_release_edges(oldedge, 1);
1043 lwcollection_free(split_col);
1044 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1045 return -1;
1046 }
1047 if (node.node_id == -1) {
1048 /* should have been set by backend */
1049 _lwt_release_edges(oldedge, 1);
1050 lwcollection_free(split_col);
1051 lwerror("Backend coding error: "
1052 "insertNodes callback did not return node_id");
1053 return -1;
1054 }
1055
1056 /* Insert the new edge */
1057 newedge1.edge_id = lwt_be_getNextEdgeId(topo);
1058 if ( newedge1.edge_id == -1 ) {
1059 _lwt_release_edges(oldedge, 1);
1060 lwcollection_free(split_col);
1061 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1062 return -1;
1063 }
1064 newedge1.start_node = node.node_id;
1065 newedge1.end_node = oldedge->end_node;
1066 newedge1.face_left = oldedge->face_left;
1067 newedge1.face_right = oldedge->face_right;
1068 newedge1.next_left = oldedge->next_left == -oldedge->edge_id ?
1069 -newedge1.edge_id : oldedge->next_left;
1070 newedge1.next_right = -oldedge->edge_id;
1071 newedge1.geom = lwgeom_as_lwline(newedge_geom);
1072 /* lwgeom_split of a line should only return lines ... */
1073 if ( ! newedge1.geom ) {
1074 _lwt_release_edges(oldedge, 1);
1075 lwcollection_free(split_col);
1076 lwerror("first geometry in lwgeom_split output is not a line");
1077 return -1;
1078 }
1079 ret = lwt_be_insertEdges(topo, &newedge1, 1);
1080 if ( ret == -1 ) {
1081 _lwt_release_edges(oldedge, 1);
1082 lwcollection_free(split_col);
1083 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1084 return -1;
1085 } else if ( ret == 0 ) {
1086 _lwt_release_edges(oldedge, 1);
1087 lwcollection_free(split_col);
1088 lwerror("Insertion of split edge failed (no reason)");
1089 return -1;
1090 }
1091
1092 /* Update the old edge */
1093 updedge.geom = lwgeom_as_lwline(oldedge_geom);
1094 /* lwgeom_split of a line should only return lines ... */
1095 if ( ! updedge.geom ) {
1096 _lwt_release_edges(oldedge, 1);
1097 lwcollection_free(split_col);
1098 lwerror("second geometry in lwgeom_split output is not a line");
1099 return -1;
1100 }
1101 updedge.next_left = newedge1.edge_id;
1102 updedge.end_node = node.node_id;
1103 ret = lwt_be_updateEdges(topo,
1104 oldedge, LWT_COL_EDGE_EDGE_ID,
1106 NULL, 0);
1107 if ( ret == -1 ) {
1108 _lwt_release_edges(oldedge, 1);
1109 lwcollection_free(split_col);
1110 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1111 return -1;
1112 } else if ( ret == 0 ) {
1113 _lwt_release_edges(oldedge, 1);
1114 lwcollection_free(split_col);
1115 lwerror("Edge being split (%d) disappeared during operations?", oldedge->edge_id);
1116 return -1;
1117 } else if ( ret > 1 ) {
1118 _lwt_release_edges(oldedge, 1);
1119 lwcollection_free(split_col);
1120 lwerror("More than a single edge found with id %d !", oldedge->edge_id);
1121 return -1;
1122 }
1123
1124 /* Update all next edge references to match new layout (ST_ModEdgeSplit) */
1125
1126 updedge.next_right = -newedge1.edge_id;
1127 excedge.edge_id = newedge1.edge_id;
1128 seledge.next_right = -oldedge->edge_id;
1129 seledge.start_node = oldedge->end_node;
1130 ret = lwt_be_updateEdges(topo,
1132 &updedge, LWT_COL_EDGE_NEXT_RIGHT,
1133 &excedge, LWT_COL_EDGE_EDGE_ID);
1134 if ( ret == -1 ) {
1135 _lwt_release_edges(oldedge, 1);
1136 lwcollection_free(split_col);
1137 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1138 return -1;
1139 }
1140
1141 updedge.next_left = -newedge1.edge_id;
1142 excedge.edge_id = newedge1.edge_id;
1143 seledge.next_left = -oldedge->edge_id;
1144 seledge.end_node = oldedge->end_node;
1145 ret = lwt_be_updateEdges(topo,
1147 &updedge, LWT_COL_EDGE_NEXT_LEFT,
1148 &excedge, LWT_COL_EDGE_EDGE_ID);
1149 if ( ret == -1 ) {
1150 _lwt_release_edges(oldedge, 1);
1151 lwcollection_free(split_col);
1152 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1153 return -1;
1154 }
1155
1156 /* Update TopoGeometries composition */
1157 ret = lwt_be_updateTopoGeomEdgeSplit(topo, oldedge->edge_id, newedge1.edge_id, -1);
1158 if ( ! ret ) {
1159 _lwt_release_edges(oldedge, 1);
1160 lwcollection_free(split_col);
1161 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1162 return -1;
1163 }
1164
1165 _lwt_release_edges(oldedge, 1);
1166 lwcollection_free(split_col);
1167
1168 /* return new node id */
1169 return node.node_id;
1170}
1171
1174 LWPOINT* pt, int skipISOChecks )
1175{
1176 LWT_ISO_NODE node;
1177 LWT_ISO_EDGE* oldedge = NULL;
1178 LWCOLLECTION *split_col;
1179 const LWGEOM *oldedge_geom;
1180 const LWGEOM *newedge_geom;
1181 LWT_ISO_EDGE newedges[2];
1182 LWT_ISO_EDGE seledge, updedge;
1183 int ret;
1184
1185 split_col = _lwt_EdgeSplit( topo, edge, pt, skipISOChecks, &oldedge );
1186 if ( ! split_col ) return -1; /* should have raised an exception */
1187 oldedge_geom = split_col->geoms[0];
1188 newedge_geom = split_col->geoms[1];
1189 /* Make sure the SRID is set on the subgeom */
1190 ((LWGEOM*)oldedge_geom)->srid = split_col->srid;
1191 ((LWGEOM*)newedge_geom)->srid = split_col->srid;
1192
1193 /* Add new node, getting new id back */
1194 node.node_id = -1;
1195 node.containing_face = -1; /* means not-isolated */
1196 node.geom = pt;
1197 if ( ! lwt_be_insertNodes(topo, &node, 1) )
1198 {
1199 _lwt_release_edges(oldedge, 1);
1200 lwcollection_free(split_col);
1201 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1202 return -1;
1203 }
1204 if (node.node_id == -1) {
1205 _lwt_release_edges(oldedge, 1);
1206 lwcollection_free(split_col);
1207 /* should have been set by backend */
1208 lwerror("Backend coding error: "
1209 "insertNodes callback did not return node_id");
1210 return -1;
1211 }
1212
1213 /* Delete the old edge */
1214 seledge.edge_id = edge;
1215 ret = lwt_be_deleteEdges(topo, &seledge, LWT_COL_EDGE_EDGE_ID);
1216 if ( ret == -1 ) {
1217 _lwt_release_edges(oldedge, 1);
1218 lwcollection_free(split_col);
1219 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1220 return -1;
1221 }
1222
1223 /* Get new edges identifiers */
1224 newedges[0].edge_id = lwt_be_getNextEdgeId(topo);
1225 if ( newedges[0].edge_id == -1 ) {
1226 _lwt_release_edges(oldedge, 1);
1227 lwcollection_free(split_col);
1228 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1229 return -1;
1230 }
1231 newedges[1].edge_id = lwt_be_getNextEdgeId(topo);
1232 if ( newedges[1].edge_id == -1 ) {
1233 _lwt_release_edges(oldedge, 1);
1234 lwcollection_free(split_col);
1235 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1236 return -1;
1237 }
1238
1239 /* Define the first new edge (to new node) */
1240 newedges[0].start_node = oldedge->start_node;
1241 newedges[0].end_node = node.node_id;
1242 newedges[0].face_left = oldedge->face_left;
1243 newedges[0].face_right = oldedge->face_right;
1244 newedges[0].next_left = newedges[1].edge_id;
1245 if ( oldedge->next_right == edge )
1246 newedges[0].next_right = newedges[0].edge_id;
1247 else if ( oldedge->next_right == -edge )
1248 newedges[0].next_right = -newedges[1].edge_id;
1249 else
1250 newedges[0].next_right = oldedge->next_right;
1251 newedges[0].geom = lwgeom_as_lwline(oldedge_geom);
1252 /* lwgeom_split of a line should only return lines ... */
1253 if ( ! newedges[0].geom ) {
1254 _lwt_release_edges(oldedge, 1);
1255 lwcollection_free(split_col);
1256 lwerror("first geometry in lwgeom_split output is not a line");
1257 return -1;
1258 }
1259
1260 /* Define the second new edge (from new node) */
1261 newedges[1].start_node = node.node_id;
1262 newedges[1].end_node = oldedge->end_node;
1263 newedges[1].face_left = oldedge->face_left;
1264 newedges[1].face_right = oldedge->face_right;
1265 newedges[1].next_right = -newedges[0].edge_id;
1266 if ( oldedge->next_left == -edge )
1267 newedges[1].next_left = -newedges[1].edge_id;
1268 else if ( oldedge->next_left == edge )
1269 newedges[1].next_left = newedges[0].edge_id;
1270 else
1271 newedges[1].next_left = oldedge->next_left;
1272 newedges[1].geom = lwgeom_as_lwline(newedge_geom);
1273 /* lwgeom_split of a line should only return lines ... */
1274 if ( ! newedges[1].geom ) {
1275 _lwt_release_edges(oldedge, 1);
1276 lwcollection_free(split_col);
1277 lwerror("second geometry in lwgeom_split output is not a line");
1278 return -1;
1279 }
1280
1281 /* Insert both new edges */
1282 ret = lwt_be_insertEdges(topo, newedges, 2);
1283 if ( ret == -1 ) {
1284 _lwt_release_edges(oldedge, 1);
1285 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1286 return -1;
1287 } else if ( ret == 0 ) {
1288 _lwt_release_edges(oldedge, 1);
1289 lwcollection_free(split_col);
1290 lwerror("Insertion of split edge failed (no reason)");
1291 return -1;
1292 }
1293
1294 /* Update all next edge references pointing to old edge id */
1295
1296 updedge.next_right = newedges[0].edge_id;
1297 seledge.next_right = edge;
1298 seledge.start_node = oldedge->start_node;
1299 ret = lwt_be_updateEdges(topo,
1301 &updedge, LWT_COL_EDGE_NEXT_RIGHT,
1302 NULL, 0);
1303 if ( ret == -1 ) {
1304 _lwt_release_edges(oldedge, 1);
1305 lwcollection_free(split_col);
1306 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1307 return -1;
1308 }
1309
1310 updedge.next_right = -newedges[1].edge_id;
1311 seledge.next_right = -edge;
1312 seledge.start_node = oldedge->end_node;
1313 ret = lwt_be_updateEdges(topo,
1315 &updedge, LWT_COL_EDGE_NEXT_RIGHT,
1316 NULL, 0);
1317 if ( ret == -1 ) {
1318 _lwt_release_edges(oldedge, 1);
1319 lwcollection_free(split_col);
1320 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1321 return -1;
1322 }
1323
1324 updedge.next_left = newedges[0].edge_id;
1325 seledge.next_left = edge;
1326 seledge.end_node = oldedge->start_node;
1327 ret = lwt_be_updateEdges(topo,
1329 &updedge, LWT_COL_EDGE_NEXT_LEFT,
1330 NULL, 0);
1331 if ( ret == -1 ) {
1332 _lwt_release_edges(oldedge, 1);
1333 lwcollection_free(split_col);
1334 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1335 return -1;
1336 }
1337
1338 updedge.next_left = -newedges[1].edge_id;
1339 seledge.next_left = -edge;
1340 seledge.end_node = oldedge->end_node;
1341 ret = lwt_be_updateEdges(topo,
1343 &updedge, LWT_COL_EDGE_NEXT_LEFT,
1344 NULL, 0);
1345 if ( ret == -1 ) {
1346 _lwt_release_edges(oldedge, 1);
1347 lwcollection_release(split_col);
1348 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1349 return -1;
1350 }
1351
1352 /* Update TopoGeometries composition */
1353 ret = lwt_be_updateTopoGeomEdgeSplit(topo, oldedge->edge_id, newedges[0].edge_id, newedges[1].edge_id);
1354 if ( ! ret ) {
1355 _lwt_release_edges(oldedge, 1);
1356 lwcollection_free(split_col);
1357 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1358 return -1;
1359 }
1360
1361 _lwt_release_edges(oldedge, 1);
1362 lwcollection_free(split_col);
1363
1364 /* return new node id */
1365 return node.node_id;
1366}
1367
1368/* Data structure used by AddEdgeX functions */
1369typedef struct edgeend_t {
1370 /* Signed identifier of next clockwise edge (+outgoing,-incoming) */
1372 /* Identifier of face between myaz and next CW edge */
1374 /* Signed identifier of next counterclockwise edge (+outgoing,-incoming) */
1376 /* Identifier of face between myaz and next CCW edge */
1379 double myaz; /* azimuth of edgeend geometry */
1381
1382/*
1383 * Get first distinct vertex from endpoint
1384 * @param pa the pointarray to seek points in
1385 * @param ref the point we want to search a distinct one
1386 * @param from vertex index to start from (will really start from "from"+dir)
1387 * @param dir 1 to go forward
1388 * -1 to go backward
1389 * @return 0 if edge is collapsed (no distinct points)
1390 */
1391static int
1392_lwt_FirstDistinctVertex2D(const POINTARRAY* pa, POINT2D *ref, int from, int dir, POINT2D *op)
1393{
1394 int i, toofar, inc;
1395 POINT2D fp;
1396
1397 if ( dir > 0 )
1398 {
1399 toofar = pa->npoints;
1400 inc = 1;
1401 }
1402 else
1403 {
1404 toofar = -1;
1405 inc = -1;
1406 }
1407
1408 LWDEBUGF(1, "first point is index %d", from);
1409 fp = *ref; /* getPoint2d_p(pa, from, &fp); */
1410 for ( i = from+inc; i != toofar; i += inc )
1411 {
1412 LWDEBUGF(1, "testing point %d", i);
1413 getPoint2d_p(pa, i, op); /* pick next point */
1414 if ( p2d_same(op, &fp) ) continue; /* equal to startpoint */
1415 /* this is a good one, neither same of start nor of end point */
1416 return 1; /* found */
1417 }
1418
1419 /* no distinct vertices found */
1420 return 0;
1421}
1422
1423
1424/*
1425 * Return non-zero on failure (lwerror is invoked in that case)
1426 * Possible failures:
1427 * -1 no two distinct vertices exist
1428 * -2 azimuth computation failed for first edge end
1429 */
1430static int
1432 POINT2D *fp, POINT2D *lp)
1433{
1434 POINTARRAY *pa = edge->points;
1435 POINT2D pt;
1436
1437 fee->nextCW = fee->nextCCW =
1438 lee->nextCW = lee->nextCCW = 0;
1439 fee->cwFace = fee->ccwFace =
1440 lee->cwFace = lee->ccwFace = -1;
1441
1442 /* Compute azimuth of first edge end */
1443 LWDEBUG(1, "computing azimuth of first edge end");
1444 if ( ! _lwt_FirstDistinctVertex2D(pa, fp, 0, 1, &pt) )
1445 {
1446 lwerror("Invalid edge (no two distinct vertices exist)");
1447 return -1;
1448 }
1449 if ( ! azimuth_pt_pt(fp, &pt, &(fee->myaz)) ) {
1450 lwerror("error computing azimuth of first edgeend [%.15g %.15g,%.15g %.15g]",
1451 fp->x, fp->y, pt.x, pt.y);
1452 return -2;
1453 }
1454 LWDEBUGF(1, "azimuth of first edge end [%.15g %.15g,%.15g %.15g] is %g",
1455 fp->x, fp->y, pt.x, pt.y, fee->myaz);
1456
1457 /* Compute azimuth of second edge end */
1458 LWDEBUG(1, "computing azimuth of second edge end");
1459 if ( ! _lwt_FirstDistinctVertex2D(pa, lp, pa->npoints-1, -1, &pt) )
1460 {
1461 lwerror("Invalid edge (no two distinct vertices exist)");
1462 return -1;
1463 }
1464 if ( ! azimuth_pt_pt(lp, &pt, &(lee->myaz)) ) {
1465 lwerror("error computing azimuth of last edgeend [%.15g %.15g,%.15g %.15g]",
1466 lp->x, lp->y, pt.x, pt.y);
1467 return -2;
1468 }
1469 LWDEBUGF(1, "azimuth of last edge end [%.15g %.15g,%.15g %.15g] is %g",
1470 lp->x, lp->y, pt.x, pt.y, lee->myaz);
1471
1472 return 0;
1473}
1474
1475/*
1476 * Find the first edges encountered going clockwise and counterclockwise
1477 * around a node, starting from the given azimuth, and take
1478 * note of the face on the both sides.
1479 *
1480 * @param topo the topology to act upon
1481 * @param node the identifier of the node to analyze
1482 * @param data input (myaz) / output (nextCW, nextCCW) parameter
1483 * @param other edgeend, if also incident to given node (closed edge).
1484 * @param myedge_id identifier of the edge id that data->myaz belongs to
1485 * @return number of incident edges found
1486 *
1487 */
1488static int
1490 edgeend *other, int myedge_id )
1491{
1492 LWT_ISO_EDGE *edges;
1493 uint64_t numedges = 1;
1494 uint64_t i;
1495 double minaz, maxaz;
1496 double az, azdif;
1497
1498 data->nextCW = data->nextCCW = 0;
1499 data->cwFace = data->ccwFace = -1;
1500
1501 if ( other ) {
1502 azdif = other->myaz - data->myaz;
1503 if ( azdif < 0 ) azdif += 2 * M_PI;
1504 minaz = maxaz = azdif;
1505 /* TODO: set nextCW/nextCCW/cwFace/ccwFace to other->something ? */
1506 LWDEBUGF(1, "Other edge end has cwFace=%d and ccwFace=%d",
1507 other->cwFace, other->ccwFace);
1508 } else {
1509 minaz = maxaz = -1;
1510 }
1511
1512 LWDEBUGF(1, "Looking for edges incident to node %" LWTFMT_ELEMID
1513 " and adjacent to azimuth %g", node, data->myaz);
1514
1515 /* Get incident edges */
1516 edges = lwt_be_getEdgeByNode( topo, &node, &numedges, LWT_COL_EDGE_ALL );
1517 if (numedges == UINT64_MAX)
1518 {
1519 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1520 return 0;
1521 }
1522
1523 LWDEBUGF(1, "getEdgeByNode returned %d edges, minaz=%g, maxaz=%g",
1524 numedges, minaz, maxaz);
1525
1526 /* For each incident edge-end (1 or 2): */
1527 for ( i = 0; i < numedges; ++i )
1528 {
1529 LWT_ISO_EDGE *edge;
1530 LWGEOM *g;
1531 LWGEOM *cleangeom;
1532 POINT2D p1, p2;
1533 POINTARRAY *pa;
1534
1535 edge = &(edges[i]);
1536
1537 if ( edge->edge_id == myedge_id ) continue;
1538
1539 g = lwline_as_lwgeom(edge->geom);
1540 /* NOTE: remove_repeated_points call could be replaced by
1541 * some other mean to pick two distinct points for endpoints */
1542 cleangeom = lwgeom_remove_repeated_points( g, 0 );
1543 pa = lwgeom_as_lwline(cleangeom)->points;
1544
1545 if ( pa->npoints < 2 ) {{
1546 LWT_ELEMID id = edge->edge_id;
1547 _lwt_release_edges(edges, numedges);
1548 lwgeom_free(cleangeom);
1549 lwerror("corrupted topology: edge %" LWTFMT_ELEMID
1550 " does not have two distinct points", id);
1551 return -1;
1552 }}
1553
1554 if ( edge->start_node == node ) {
1555 getPoint2d_p(pa, 0, &p1);
1556 if ( ! _lwt_FirstDistinctVertex2D(pa, &p1, 0, 1, &p2) )
1557 {
1558 lwerror("Edge %d has no distinct vertices: [%.15g %.15g,%.15g %.15g]: ",
1559 edge->edge_id, p1.x, p1.y, p2.x, p2.y);
1560 return -1;
1561 }
1562 LWDEBUGF(1, "edge %" LWTFMT_ELEMID
1563 " starts on node %" LWTFMT_ELEMID
1564 ", edgeend is %g,%g-%g,%g",
1565 edge->edge_id, node, p1.x, p1.y, p2.x, p2.y);
1566 if ( ! azimuth_pt_pt(&p1, &p2, &az) ) {{
1567 LWT_ELEMID id = edge->edge_id;
1568 _lwt_release_edges(edges, numedges);
1569 lwgeom_free(cleangeom);
1570 lwerror("error computing azimuth of edge %d first edgeend [%.15g %.15g,%.15g %.15g]",
1571 id, p1.x, p1.y, p2.x, p2.y);
1572 return -1;
1573 }}
1574 azdif = az - data->myaz;
1575 LWDEBUGF(1, "azimuth of edge %" LWTFMT_ELEMID
1576 ": %g (diff: %g)", edge->edge_id, az, azdif);
1577
1578 if ( azdif < 0 ) azdif += 2 * M_PI;
1579 if ( minaz == -1 ) {
1580 minaz = maxaz = azdif;
1581 data->nextCW = data->nextCCW = edge->edge_id; /* outgoing */
1582 data->cwFace = edge->face_left;
1583 data->ccwFace = edge->face_right;
1584 LWDEBUGF(1, "new nextCW and nextCCW edge is %" LWTFMT_ELEMID
1585 ", outgoing, "
1586 "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID
1587 " (face_right is new ccwFace, face_left is new cwFace)",
1588 edge->edge_id, edge->face_left,
1589 edge->face_right);
1590 } else {
1591 if ( azdif < minaz ) {
1592 data->nextCW = edge->edge_id; /* outgoing */
1593 data->cwFace = edge->face_left;
1594 LWDEBUGF(1, "new nextCW edge is %" LWTFMT_ELEMID
1595 ", outgoing, "
1596 "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID
1597 " (previous had minaz=%g, face_left is new cwFace)",
1598 edge->edge_id, edge->face_left,
1599 edge->face_right, minaz);
1600 minaz = azdif;
1601 }
1602 else if ( azdif > maxaz ) {
1603 data->nextCCW = edge->edge_id; /* outgoing */
1604 data->ccwFace = edge->face_right;
1605 LWDEBUGF(1, "new nextCCW edge is %" LWTFMT_ELEMID
1606 ", outgoing, "
1607 "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID
1608 " (previous had maxaz=%g, face_right is new ccwFace)",
1609 edge->edge_id, edge->face_left,
1610 edge->face_right, maxaz);
1611 maxaz = azdif;
1612 }
1613 }
1614 }
1615
1616 if ( edge->end_node == node ) {
1617 getPoint2d_p(pa, pa->npoints-1, &p1);
1618 if ( ! _lwt_FirstDistinctVertex2D(pa, &p1, pa->npoints-1, -1, &p2) )
1619 {
1620 lwerror("Edge %d has no distinct vertices: [%.15g %.15g,%.15g %.15g]: ",
1621 edge->edge_id, p1.x, p1.y, p2.x, p2.y);
1622 return -1;
1623 }
1624 LWDEBUGF(1, "edge %" LWTFMT_ELEMID " ends on node %" LWTFMT_ELEMID
1625 ", edgeend is %g,%g-%g,%g",
1626 edge->edge_id, node, p1.x, p1.y, p2.x, p2.y);
1627 if ( ! azimuth_pt_pt(&p1, &p2, &az) ) {{
1628 LWT_ELEMID id = edge->edge_id;
1629 _lwt_release_edges(edges, numedges);
1630 lwgeom_free(cleangeom);
1631 lwerror("error computing azimuth of edge %d last edgeend [%.15g %.15g,%.15g %.15g]",
1632 id, p1.x, p1.y, p2.x, p2.y);
1633 return -1;
1634 }}
1635 azdif = az - data->myaz;
1636 LWDEBUGF(1, "azimuth of edge %" LWTFMT_ELEMID
1637 ": %g (diff: %g)", edge->edge_id, az, azdif);
1638 if ( azdif < 0 ) azdif += 2 * M_PI;
1639 if ( minaz == -1 ) {
1640 minaz = maxaz = azdif;
1641 data->nextCW = data->nextCCW = -edge->edge_id; /* incoming */
1642 data->cwFace = edge->face_right;
1643 data->ccwFace = edge->face_left;
1644 LWDEBUGF(1, "new nextCW and nextCCW edge is %" LWTFMT_ELEMID
1645 ", incoming, "
1646 "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID
1647 " (face_right is new cwFace, face_left is new ccwFace)",
1648 edge->edge_id, edge->face_left,
1649 edge->face_right);
1650 } else {
1651 if ( azdif < minaz ) {
1652 data->nextCW = -edge->edge_id; /* incoming */
1653 data->cwFace = edge->face_right;
1654 LWDEBUGF(1, "new nextCW edge is %" LWTFMT_ELEMID
1655 ", incoming, "
1656 "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID
1657 " (previous had minaz=%g, face_right is new cwFace)",
1658 edge->edge_id, edge->face_left,
1659 edge->face_right, minaz);
1660 minaz = azdif;
1661 }
1662 else if ( azdif > maxaz ) {
1663 data->nextCCW = -edge->edge_id; /* incoming */
1664 data->ccwFace = edge->face_left;
1665 LWDEBUGF(1, "new nextCCW edge is %" LWTFMT_ELEMID
1666 ", outgoing, from start point, "
1667 "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID
1668 " (previous had maxaz=%g, face_left is new ccwFace)",
1669 edge->edge_id, edge->face_left,
1670 edge->face_right, maxaz);
1671 maxaz = azdif;
1672 }
1673 }
1674 }
1675
1676 lwgeom_free(cleangeom);
1677 }
1678 if ( numedges ) _lwt_release_edges(edges, numedges);
1679
1680 LWDEBUGF(1, "edges adjacent to azimuth %g"
1681 " (incident to node %" LWTFMT_ELEMID ")"
1682 ": CW:%" LWTFMT_ELEMID "(%g) CCW:%" LWTFMT_ELEMID "(%g)",
1683 data->myaz, node, data->nextCW, minaz,
1684 data->nextCCW, maxaz);
1685
1686 if ( myedge_id < 1 && numedges && data->cwFace != data->ccwFace )
1687 {
1688 if ( data->cwFace != -1 && data->ccwFace != -1 ) {
1689 lwerror("Corrupted topology: adjacent edges %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID
1690 " bind different face (%" LWTFMT_ELEMID " and %" LWTFMT_ELEMID ")",
1691 data->nextCW, data->nextCCW,
1692 data->cwFace, data->ccwFace);
1693 return -1;
1694 }
1695 }
1696
1697 /* Return number of incident edges found */
1698 return numedges;
1699}
1700
1701/*
1702 * Get a point internal to the line and write it into the "ip"
1703 * parameter
1704 *
1705 * return 0 on failure (line is empty or collapsed), 1 otherwise
1706 */
1707static int
1709{
1710 uint32_t i;
1711 POINT2D fp, lp, tp;
1712 POINTARRAY *pa = edge->points;
1713
1714 if ( pa->npoints < 2 ) return 0; /* empty or structurally collapsed */
1715
1716 getPoint2d_p(pa, 0, &fp); /* save first point */
1717 getPoint2d_p(pa, pa->npoints-1, &lp); /* save last point */
1718 for (i=1; i<pa->npoints-1; ++i)
1719 {
1720 getPoint2d_p(pa, i, &tp); /* pick next point */
1721 if ( p2d_same(&tp, &fp) ) continue; /* equal to startpoint */
1722 if ( p2d_same(&tp, &lp) ) continue; /* equal to endpoint */
1723 /* this is a good one, neither same of start nor of end point */
1724 *ip = tp;
1725 return 1; /* found */
1726 }
1727
1728 /* no distinct vertex found */
1729
1730 /* interpolate if start point != end point */
1731
1732 if ( p2d_same(&fp, &lp) ) return 0; /* no distinct points in edge */
1733
1734 ip->x = fp.x + ( (lp.x - fp.x) * 0.5 );
1735 ip->y = fp.y + ( (lp.y - fp.y) * 0.5 );
1736
1737 return 1;
1738}
1739
1740static LWPOLY *
1741_lwt_MakeRingShell(LWT_TOPOLOGY *topo, LWT_ELEMID *signed_edge_ids, uint64_t num_signed_edge_ids)
1742{
1743 LWT_ELEMID *edge_ids;
1744 uint64_t numedges, i, j;
1745 LWT_ISO_EDGE *ring_edges;
1746
1747 /* Construct a polygon using edges of the ring */
1748 numedges = 0;
1749 edge_ids = lwalloc(sizeof(LWT_ELEMID)*num_signed_edge_ids);
1750 for (i=0; i<num_signed_edge_ids; ++i) {
1751 int absid = llabs(signed_edge_ids[i]);
1752 int found = 0;
1753 /* Do not add the same edge twice */
1754 for (j=0; j<numedges; ++j) {
1755 if ( edge_ids[j] == absid ) {
1756 found = 1;
1757 break;
1758 }
1759 }
1760 if ( ! found ) edge_ids[numedges++] = absid;
1761 }
1762 i = numedges;
1763 ring_edges = lwt_be_getEdgeById(topo, edge_ids, &i,
1765 lwfree( edge_ids );
1766 if (i == UINT64_MAX)
1767 {
1768 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1769 return NULL;
1770 }
1771 else if ( i != numedges )
1772 {
1773 lwfree( signed_edge_ids );
1774 _lwt_release_edges(ring_edges, i);
1775 lwerror("Unexpected error: %d edges found when expecting %d", i, numedges);
1776 return NULL;
1777 }
1778
1779 /* Should now build a polygon with those edges, in the order
1780 * given by GetRingEdges.
1781 */
1782 POINTARRAY *pa = NULL;
1783 for ( i=0; i<num_signed_edge_ids; ++i )
1784 {
1785 LWT_ELEMID eid = signed_edge_ids[i];
1786 LWDEBUGF(2, "Edge %d in ring is edge %" LWTFMT_ELEMID, i, eid);
1787 LWT_ISO_EDGE *edge = NULL;
1788 POINTARRAY *epa;
1789 for ( j=0; j<numedges; ++j )
1790 {
1791 if ( ring_edges[j].edge_id == llabs(eid) )
1792 {
1793 edge = &(ring_edges[j]);
1794 break;
1795 }
1796 }
1797 if ( edge == NULL )
1798 {
1799 _lwt_release_edges(ring_edges, numedges);
1800 lwerror("missing edge that was found in ring edges loop");
1801 return NULL;
1802 }
1803
1804 if ( pa == NULL )
1805 {
1806 pa = ptarray_clone_deep(edge->geom->points);
1807 if ( eid < 0 ) ptarray_reverse_in_place(pa);
1808 }
1809 else
1810 {
1811 if ( eid < 0 )
1812 {
1813 epa = ptarray_clone_deep(edge->geom->points);
1815 ptarray_append_ptarray(pa, epa, 0);
1816 ptarray_free(epa);
1817 }
1818 else
1819 {
1820 /* avoid a clone here */
1821 ptarray_append_ptarray(pa, edge->geom->points, 0);
1822 }
1823 }
1824 }
1825 _lwt_release_edges(ring_edges, numedges);
1826 POINTARRAY **points = lwalloc(sizeof(POINTARRAY*));
1827 points[0] = pa;
1828
1829 /* NOTE: the ring may very well have collapsed components,
1830 * which would make it topologically invalid
1831 */
1832 LWPOLY* shell = lwpoly_construct(0, 0, 1, points);
1833 return shell;
1834}
1835
1836/*
1837 * Add a split face by walking on the edge side.
1838 *
1839 * @param topo the topology to act upon
1840 * @param sedge edge id and walking side and direction
1841 * (forward,left:positive backward,right:negative)
1842 * @param face the face in which the edge identifier is known to be
1843 * @param mbr_only do not create a new face but update MBR of the current
1844 *
1845 * @return:
1846 * -1: if mbr_only was requested
1847 * 0: if the edge does not form a ring
1848 * -1: if it is impossible to create a face on the requested side
1849 * ( new face on the side is the universe )
1850 * -2: error
1851 * >0 : id of newly added face
1852 */
1853static LWT_ELEMID
1855 LWT_ELEMID sedge, LWT_ELEMID face,
1856 int mbr_only )
1857{
1858 uint64_t numfaceedges, i, j;
1859 int newface_outside;
1860 uint64_t num_signed_edge_ids;
1861 LWT_ELEMID *signed_edge_ids;
1862 LWT_ISO_EDGE *edges;
1863 LWT_ISO_EDGE *forward_edges = NULL;
1864 int forward_edges_count = 0;
1865 LWT_ISO_EDGE *backward_edges = NULL;
1866 int backward_edges_count = 0;
1867
1868 signed_edge_ids = lwt_be_getRingEdges(topo, sedge, &num_signed_edge_ids, 0);
1869 if (!signed_edge_ids)
1870 {
1871 lwerror("Backend error (no ring edges for edge %" LWTFMT_ELEMID "): %s",
1872 sedge,
1874 return -2;
1875 }
1876 LWDEBUGF(1, "getRingEdges returned %d edges", num_signed_edge_ids);
1877
1878 /* You can't get to the other side of an edge forming a ring */
1879 for (i=0; i<num_signed_edge_ids; ++i) {
1880 if ( signed_edge_ids[i] == -sedge ) {
1881 /* No split here */
1882 LWDEBUG(1, "not a ring");
1883 lwfree( signed_edge_ids );
1884 return 0;
1885 }
1886 }
1887
1888 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " split face %" LWTFMT_ELEMID " (mbr_only:%d)",
1889 sedge, face, mbr_only);
1890
1891 /*
1892 * Construct a polygon using edges of the ring
1893 *
1894 * NOTE: this possibily includes dangling edges
1895 *
1896 */
1897 LWPOLY *shell = _lwt_MakeRingShell(topo, signed_edge_ids,
1898 num_signed_edge_ids);
1899 if ( ! shell ) {
1900 lwfree( signed_edge_ids );
1901 /* ring_edges should be NULL */
1902 lwerror("Could not create ring shell: %s", lwt_be_lastErrorMessage(topo->be_iface));
1903 return -2;
1904 }
1905 const POINTARRAY *pa = shell->rings[0];
1906 if ( ! ptarray_is_closed_2d(pa) )
1907 {
1908 lwpoly_free(shell);
1909 lwfree( signed_edge_ids );
1910 lwerror("Corrupted topology: ring of edge %" LWTFMT_ELEMID
1911 " is geometrically not-closed", sedge);
1912 return -2;
1913 }
1914
1915 int isccw = ptarray_isccw(pa);
1916 LWDEBUGF(1, "Ring of edge %" LWTFMT_ELEMID " is %sclockwise",
1917 sedge, isccw ? "counter" : "");
1918 const GBOX* shellbox = lwgeom_get_bbox(lwpoly_as_lwgeom(shell));
1919
1920 if ( face == 0 )
1921 {
1922 /* Edge split the universe face */
1923 if ( ! isccw )
1924 {
1925 lwpoly_free(shell);
1926 lwfree( signed_edge_ids );
1927 /* Face on the left side of this ring is the universe face.
1928 * Next call (for the other side) should create the split face
1929 */
1930 LWDEBUG(1, "The left face of this clockwise ring is the universe, "
1931 "won't create a new face there");
1932 return -1;
1933 }
1934 }
1935
1936 if ( mbr_only && face != 0 )
1937 {
1938 if ( isccw )
1939 {{
1940 LWT_ISO_FACE updface;
1941 updface.face_id = face;
1942 updface.mbr = (GBOX *)shellbox; /* const cast, we won't free it, later */
1943 int ret = lwt_be_updateFacesById( topo, &updface, 1 );
1944 if ( ret == -1 )
1945 {
1946 lwfree( signed_edge_ids );
1947 lwpoly_free(shell); /* NOTE: owns shellbox above */
1948 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1949 return -2;
1950 }
1951 if ( ret != 1 )
1952 {
1953 lwfree( signed_edge_ids );
1954 lwpoly_free(shell); /* NOTE: owns shellbox above */
1955 lwerror("Unexpected error: %d faces found when expecting 1", ret);
1956 return -2;
1957 }
1958 }}
1959 lwfree( signed_edge_ids );
1960 lwpoly_free(shell); /* NOTE: owns shellbox above */
1961 return -1; /* mbr only was requested */
1962 }
1963
1964 LWT_ISO_FACE *oldface = NULL;
1965 LWT_ISO_FACE newface;
1966 newface.face_id = -1;
1967 if ( face != 0 && ! isccw)
1968 {{
1969 /* Face created an hole in an outer face */
1970 uint64_t nfaces = 1;
1971 oldface = lwt_be_getFaceById(topo, &face, &nfaces, LWT_COL_FACE_ALL);
1972 if (nfaces == UINT64_MAX)
1973 {
1974 lwfree( signed_edge_ids );
1975 lwpoly_free(shell); /* NOTE: owns shellbox */
1976 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
1977 return -2;
1978 }
1979 if ( nfaces != 1 )
1980 {
1981 lwfree( signed_edge_ids );
1982 lwpoly_free(shell); /* NOTE: owns shellbox */
1983 lwerror("Unexpected error: %d faces found when expecting 1", nfaces);
1984 return -2;
1985 }
1986 newface.mbr = oldface->mbr;
1987 }}
1988 else
1989 {
1990 newface.mbr = (GBOX *)shellbox; /* const cast, we won't free it, later */
1991 }
1992
1993 /* Insert the new face */
1994 int ret = lwt_be_insertFaces( topo, &newface, 1 );
1995 if ( ret == -1 )
1996 {
1997 lwfree( signed_edge_ids );
1998 lwpoly_free(shell); /* NOTE: owns shellbox */
1999 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2000 return -2;
2001 }
2002 if ( ret != 1 )
2003 {
2004 lwfree( signed_edge_ids );
2005 lwpoly_free(shell); /* NOTE: owns shellbox */
2006 lwerror("Unexpected error: %d faces inserted when expecting 1", ret);
2007 return -2;
2008 }
2009 if ( oldface ) {
2010 newface.mbr = NULL; /* it is a reference to oldface mbr... */
2011 _lwt_release_faces(oldface, 1);
2012 }
2013
2014 /* Update side location of new face edges */
2015
2016 /* We want the new face to be on the left, if possible */
2017 if ( face != 0 && ! isccw ) { /* ring is clockwise in a real face */
2018 /* face shrinked, must update all non-contained edges and nodes */
2019 LWDEBUG(1, "New face is on the outside of the ring, updating rings in former shell");
2020 newface_outside = 1;
2021 /* newface is outside */
2022 } else {
2023 LWDEBUG(1, "New face is on the inside of the ring, updating forward edges in new ring");
2024 newface_outside = 0;
2025 /* newface is inside */
2026 }
2027
2028 /* Update edges bounding the old face */
2029 /* (1) fetch all edges where left_face or right_face is = oldface */
2030 int fields = LWT_COL_EDGE_EDGE_ID |
2034 ;
2035 numfaceedges = 1;
2036 edges = lwt_be_getEdgeByFace( topo, &face, &numfaceedges, fields, newface.mbr );
2037 if (numfaceedges == UINT64_MAX)
2038 {
2039 lwfree(signed_edge_ids);
2040 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2041 return -2;
2042 }
2043 LWDEBUGF(1, "_lwt_AddFaceSplit: lwt_be_getEdgeByFace(%d) returned %d edges", face, numfaceedges);
2044
2045 if ( numfaceedges )
2046 {
2047 forward_edges = lwalloc(sizeof(LWT_ISO_EDGE)*numfaceedges);
2048 forward_edges_count = 0;
2049 backward_edges = lwalloc(sizeof(LWT_ISO_EDGE)*numfaceedges);
2050 backward_edges_count = 0;
2051
2052 /* (2) loop over the results and: */
2053 for ( i=0; i<numfaceedges; ++i )
2054 {
2055 LWT_ISO_EDGE *e = &(edges[i]);
2056 int found = 0;
2057 int contains;
2058 POINT2D ep;
2059
2060 /* (2.1) skip edges whose ID is in the list of boundary edges */
2061 for ( j=0; j<num_signed_edge_ids; ++j )
2062 {
2063 int seid = signed_edge_ids[j]; /* signed ring edge id */
2064 if ( seid == e->edge_id )
2065 {
2066 /* IDEA: remove entry from signed_edge_ids, to speed next loop ? */
2067 LWDEBUGF(1, "Edge %d is a known forward edge of the new ring", e->edge_id);
2068 forward_edges[forward_edges_count].edge_id = e->edge_id;
2069 forward_edges[forward_edges_count++].face_left = newface.face_id;
2070 found++;
2071 if ( found == 2 ) break; /* both edge sides are found on the ring */
2072 }
2073 else if ( -seid == e->edge_id )
2074 {
2075 /* IDEA: remove entry from signed_edge_ids, to speed next loop ? */
2076 LWDEBUGF(1, "Edge %d is a known backward edge of the new ring", e->edge_id);
2077 backward_edges[backward_edges_count].edge_id = e->edge_id;
2078 backward_edges[backward_edges_count++].face_right = newface.face_id;
2079 found++;
2080 if ( found == 2 ) break; /* both edge sides are found on the ring */
2081 }
2082 }
2083 if ( found ) continue;
2084 LWDEBUGF(1, "Edge %d is not a known edge of the new ring", e->edge_id);
2085
2086 /* Check if the edge is now binding a different face */
2087
2088 if ( ! getPoint2d_p(e->geom->points, 0, &ep) )
2089 {
2090 lwfree(signed_edge_ids);
2091 lwpoly_free(shell);
2092 lwfree(forward_edges); /* contents owned by edges */
2093 lwfree(backward_edges); /* contents owned by edges */
2094 _lwt_release_edges(edges, numfaceedges);
2095 lwerror("Edge %d is empty", e->edge_id);
2096 return -2;
2097 }
2098
2099 /* IDEA: check that bounding box shortcut is taken, or use
2100 * shellbox to do it here */
2102
2103 LWDEBUGF(1, "Edge %d first point %s new ring",
2104 e->edge_id, (contains == LW_INSIDE ? "inside" :
2105 contains == LW_OUTSIDE ? "outside" : "on boundary of"));
2106
2107 /* (2.2) skip edges (NOT, if newface_outside) contained in ring */
2108 if ( newface_outside )
2109 {
2110 if ( contains != LW_OUTSIDE )
2111 {
2112 LWDEBUGF(1, "Edge %d not outside of the new ring, not updating it",
2113 e->edge_id);
2114 continue;
2115 }
2116 }
2117 else
2118 {
2119 if ( contains != LW_INSIDE )
2120 {
2121 LWDEBUGF(1, "Edge %d not inside the new ring, not updating it",
2122 e->edge_id);
2123 continue;
2124 }
2125 }
2126
2127 /* (2.3) push to forward_edges if left_face = oface */
2128 if ( e->face_left == face )
2129 {
2130 LWDEBUGF(1, "Edge %d has new face on the left side", e->edge_id);
2131 forward_edges[forward_edges_count].edge_id = e->edge_id;
2132 forward_edges[forward_edges_count++].face_left = newface.face_id;
2133 }
2134
2135 /* (2.4) push to backward_edges if right_face = oface */
2136 if ( e->face_right == face )
2137 {
2138 LWDEBUGF(1, "Edge %d has new face on the right side", e->edge_id);
2139 backward_edges[backward_edges_count].edge_id = e->edge_id;
2140 backward_edges[backward_edges_count++].face_right = newface.face_id;
2141 }
2142 }
2143
2144 /* Update forward edges */
2145 if ( forward_edges_count )
2146 {
2147 ret = lwt_be_updateEdgesById(topo, forward_edges,
2148 forward_edges_count,
2150 if ( ret == -1 )
2151 {
2152 lwfree( signed_edge_ids );
2153 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2154 return -2;
2155 }
2156 if ( ret != forward_edges_count )
2157 {
2158 lwfree( signed_edge_ids );
2159 lwerror("Unexpected error: %d edges updated when expecting %d",
2160 ret, forward_edges_count);
2161 return -2;
2162 }
2163 }
2164
2165 /* Update backward edges */
2166 if ( backward_edges_count )
2167 {
2168 ret = lwt_be_updateEdgesById(topo, backward_edges,
2169 backward_edges_count,
2171 if ( ret == -1 )
2172 {
2173 lwfree( signed_edge_ids );
2174 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2175 return -2;
2176 }
2177 if ( ret != backward_edges_count )
2178 {
2179 lwfree( signed_edge_ids );
2180 lwerror("Unexpected error: %d edges updated when expecting %d",
2181 ret, backward_edges_count);
2182 return -2;
2183 }
2184 }
2185
2186 lwfree(forward_edges);
2187 lwfree(backward_edges);
2188
2189 }
2190
2191 _lwt_release_edges(edges, numfaceedges);
2192
2193 /* Update isolated nodes which are now in new face */
2194 uint64_t numisonodes = 1;
2196 LWT_ISO_NODE *nodes = lwt_be_getNodeByFace(topo, &face,
2197 &numisonodes, fields, newface.mbr);
2198 if (numisonodes == UINT64_MAX)
2199 {
2200 lwfree(signed_edge_ids);
2201 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2202 return -2;
2203 }
2204 if ( numisonodes ) {
2205 LWT_ISO_NODE *updated_nodes = lwalloc(sizeof(LWT_ISO_NODE)*numisonodes);
2206 int nodes_to_update = 0;
2207 for (i=0; i<numisonodes; ++i)
2208 {
2209 LWT_ISO_NODE *n = &(nodes[i]);
2210 const POINT2D *pt = getPoint2d_cp(n->geom->point, 0);
2211 int contains = ptarray_contains_point(pa, pt) == LW_INSIDE;
2212 LWDEBUGF(1, "Node %d is %scontained in new ring, newface is %s",
2213 n->node_id, contains ? "" : "not ",
2214 newface_outside ? "outside" : "inside" );
2215 if ( newface_outside )
2216 {
2217 if ( contains )
2218 {
2219 LWDEBUGF(1, "Node %d contained in an hole of the new face",
2220 n->node_id);
2221 continue;
2222 }
2223 }
2224 else
2225 {
2226 if ( ! contains )
2227 {
2228 LWDEBUGF(1, "Node %d not contained in the face shell",
2229 n->node_id);
2230 continue;
2231 }
2232 }
2233 updated_nodes[nodes_to_update].node_id = n->node_id;
2234 updated_nodes[nodes_to_update++].containing_face =
2235 newface.face_id;
2236 LWDEBUGF(1, "Node %d will be updated", n->node_id);
2237 }
2238 _lwt_release_nodes(nodes, numisonodes);
2239 if ( nodes_to_update )
2240 {
2241 int ret = lwt_be_updateNodesById(topo, updated_nodes,
2242 nodes_to_update,
2244 if ( ret == -1 ) {
2245 lwfree( signed_edge_ids );
2246 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2247 return -2;
2248 }
2249 }
2250 lwfree(updated_nodes);
2251 }
2252
2253 lwfree(signed_edge_ids);
2254 lwpoly_free(shell);
2255
2256 return newface.face_id;
2257}
2258
2266static LWT_ELEMID
2268 LWT_ELEMID start_node, LWT_ELEMID end_node,
2269 LWLINE *geom, int skipChecks, int modFace )
2270{
2271 LWT_ISO_EDGE newedge;
2272 LWGEOM *cleangeom;
2273 edgeend span; /* start point analisys */
2274 edgeend epan; /* end point analisys */
2275 POINT2D p1, pn, p2;
2276 POINTARRAY *pa;
2277 LWT_ELEMID node_ids[2];
2278 const LWPOINT *start_node_geom = NULL;
2279 const LWPOINT *end_node_geom = NULL;
2280 uint64_t num_nodes;
2281 LWT_ISO_NODE *endpoints;
2282 uint64_t i;
2283 int prev_left;
2284 int prev_right;
2285 LWT_ISO_EDGE seledge;
2286 LWT_ISO_EDGE updedge;
2287
2288 if ( ! skipChecks )
2289 {
2290 /* curve must be simple */
2291 if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) )
2292 {
2293 lwerror("SQL/MM Spatial exception - curve not simple");
2294 return -1;
2295 }
2296 }
2297
2298 newedge.start_node = start_node;
2299 newedge.end_node = end_node;
2300 newedge.geom = geom;
2301 newedge.face_left = -1;
2302 newedge.face_right = -1;
2303 /* TODO: should do the repeated points removal in 2D space */
2304 cleangeom = lwgeom_remove_repeated_points( lwline_as_lwgeom(geom), 0 );
2305
2306 pa = lwgeom_as_lwline(cleangeom)->points;
2307 if ( pa->npoints < 2 ) {
2308 lwgeom_free(cleangeom);
2309 lwerror("Invalid edge (no two distinct vertices exist)");
2310 return -1;
2311 }
2312
2313 /* Initialize endpoint info (some of that ) */
2314 span.cwFace = span.ccwFace =
2315 epan.cwFace = epan.ccwFace = -1;
2316
2317 /* Compute azimuth of first edge end on start node */
2318 getPoint2d_p(pa, 0, &p1);
2319 if ( ! _lwt_FirstDistinctVertex2D(pa, &p1, 0, 1, &pn) )
2320 {
2321 lwgeom_free(cleangeom);
2322 lwerror("Invalid edge (no two distinct vertices exist)");
2323 return -1;
2324 }
2325 if ( ! azimuth_pt_pt(&p1, &pn, &span.myaz) ) {
2326 lwgeom_free(cleangeom);
2327 lwerror("error computing azimuth of first edgeend [%.15g %.15g,%.15g %.15g]",
2328 p1.x, p1.y, pn.x, pn.y);
2329 return -1;
2330 }
2331 LWDEBUGF(1, "edge's start node is %g,%g", p1.x, p1.y);
2332
2333 /* Compute azimuth of last edge end on end node */
2334 getPoint2d_p(pa, pa->npoints-1, &p2);
2335 if ( ! _lwt_FirstDistinctVertex2D(pa, &p2, pa->npoints-1, -1, &pn) )
2336 {
2337 lwgeom_free(cleangeom);
2338 /* This should never happen as we checked the edge while computing first edgend */
2339 lwerror("Invalid clean edge (no two distinct vertices exist) - should not happen");
2340 return -1;
2341 }
2342 lwgeom_free(cleangeom);
2343 if ( ! azimuth_pt_pt(&p2, &pn, &epan.myaz) ) {
2344 lwerror("error computing azimuth of last edgeend [%.15g %.15g,%.15g %.15g]",
2345 p2.x, p2.y, pn.x, pn.y);
2346 return -1;
2347 }
2348 LWDEBUGF(1, "edge's end node is %g,%g", p2.x, p2.y);
2349
2350 /*
2351 * Check endpoints existence, match with Curve geometry
2352 * and get face information (if any)
2353 */
2354
2355 if ( start_node != end_node ) {
2356 num_nodes = 2;
2357 node_ids[0] = start_node;
2358 node_ids[1] = end_node;
2359 } else {
2360 num_nodes = 1;
2361 node_ids[0] = start_node;
2362 }
2363
2364 endpoints = lwt_be_getNodeById( topo, node_ids, &num_nodes, LWT_COL_NODE_ALL );
2365 if (num_nodes == UINT64_MAX)
2366 {
2367 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2368 return -1;
2369 }
2370 for ( i=0; i<num_nodes; ++i )
2371 {
2372 LWT_ISO_NODE* node = &(endpoints[i]);
2373 if ( node->containing_face != -1 )
2374 {
2375 if ( newedge.face_left == -1 )
2376 {
2377 newedge.face_left = newedge.face_right = node->containing_face;
2378 }
2379 else if ( newedge.face_left != node->containing_face )
2380 {
2381 _lwt_release_nodes(endpoints, num_nodes);
2382 lwerror("SQL/MM Spatial exception - geometry crosses an edge"
2383 " (endnodes in faces %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID ")",
2384 newedge.face_left, node->containing_face);
2385 }
2386 }
2387
2388 LWDEBUGF(1, "Node %d, with geom %p (looking for %d and %d)",
2389 node->node_id, node->geom, start_node, end_node);
2390 if ( node->node_id == start_node ) {
2391 start_node_geom = node->geom;
2392 }
2393 if ( node->node_id == end_node ) {
2394 end_node_geom = node->geom;
2395 }
2396 }
2397
2398 if ( ! skipChecks )
2399 {
2400 if ( ! start_node_geom )
2401 {
2402 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2403 lwerror("SQL/MM Spatial exception - non-existent node");
2404 return -1;
2405 }
2406 else
2407 {
2408 pa = start_node_geom->point;
2409 getPoint2d_p(pa, 0, &pn);
2410 if ( ! p2d_same(&pn, &p1) )
2411 {
2412 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2413 lwerror("SQL/MM Spatial exception"
2414 " - start node not geometry start point."
2415 //" - start node not geometry start point (%g,%g != %g,%g).", pn.x, pn.y, p1.x, p1.y
2416 );
2417 return -1;
2418 }
2419 }
2420
2421 if ( ! end_node_geom )
2422 {
2423 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2424 lwerror("SQL/MM Spatial exception - non-existent node");
2425 return -1;
2426 }
2427 else
2428 {
2429 pa = end_node_geom->point;
2430 getPoint2d_p(pa, 0, &pn);
2431 if ( ! p2d_same(&pn, &p2) )
2432 {
2433 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2434 lwerror("SQL/MM Spatial exception"
2435 " - end node not geometry end point."
2436 //" - end node not geometry end point (%g,%g != %g,%g).", pn.x, pn.y, p2.x, p2.y
2437 );
2438 return -1;
2439 }
2440 }
2441
2442 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2443
2444 if ( _lwt_CheckEdgeCrossing( topo, start_node, end_node, geom, 0 ) )
2445 return -1;
2446
2447 } /* ! skipChecks */
2448
2449 /*
2450 * All checks passed, time to prepare the new edge
2451 */
2452
2453 newedge.edge_id = lwt_be_getNextEdgeId( topo );
2454 if ( newedge.edge_id == -1 ) {
2455 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2456 return -1;
2457 }
2458
2459 /* Find adjacent edges to each endpoint */
2460 int isclosed = start_node == end_node;
2461 int found;
2462 found = _lwt_FindAdjacentEdges( topo, start_node, &span,
2463 isclosed ? &epan : NULL, -1 );
2464 if ( found ) {
2465 span.was_isolated = 0;
2466 newedge.next_right = span.nextCW ? span.nextCW : -newedge.edge_id;
2467 prev_left = span.nextCCW ? -span.nextCCW : newedge.edge_id;
2468 LWDEBUGF(1, "New edge %d is connected on start node, "
2469 "next_right is %d, prev_left is %d",
2470 newedge.edge_id, newedge.next_right, prev_left);
2471 if ( newedge.face_right == -1 ) {
2472 newedge.face_right = span.cwFace;
2473 }
2474 if ( newedge.face_left == -1 ) {
2475 newedge.face_left = span.ccwFace;
2476 }
2477 } else {
2478 span.was_isolated = 1;
2479 newedge.next_right = isclosed ? -newedge.edge_id : newedge.edge_id;
2480 prev_left = isclosed ? newedge.edge_id : -newedge.edge_id;
2481 LWDEBUGF(1, "New edge %d is isolated on start node, "
2482 "next_right is %d, prev_left is %d",
2483 newedge.edge_id, newedge.next_right, prev_left);
2484 }
2485
2486 found = _lwt_FindAdjacentEdges( topo, end_node, &epan,
2487 isclosed ? &span : NULL, -1 );
2488 if ( found ) {
2489 epan.was_isolated = 0;
2490 newedge.next_left = epan.nextCW ? epan.nextCW : newedge.edge_id;
2491 prev_right = epan.nextCCW ? -epan.nextCCW : -newedge.edge_id;
2492 LWDEBUGF(1, "New edge %d is connected on end node, "
2493 "next_left is %d, prev_right is %d",
2494 newedge.edge_id, newedge.next_left, prev_right);
2495 if ( newedge.face_right == -1 ) {
2496 newedge.face_right = span.ccwFace;
2497 } else if ( modFace != -1 && newedge.face_right != epan.ccwFace ) {
2498 /* side-location conflict */
2499 lwerror("Side-location conflict: "
2500 "new edge starts in face"
2501 " %" LWTFMT_ELEMID " and ends in face"
2502 " %" LWTFMT_ELEMID,
2503 newedge.face_right, epan.ccwFace
2504 );
2505 return -1;
2506 }
2507 if ( newedge.face_left == -1 ) {
2508 newedge.face_left = span.cwFace;
2509 } else if ( modFace != -1 && newedge.face_left != epan.cwFace ) {
2510 /* side-location conflict */
2511 lwerror("Side-location conflict: "
2512 "new edge starts in face"
2513 " %" LWTFMT_ELEMID " and ends in face"
2514 " %" LWTFMT_ELEMID,
2515 newedge.face_left, epan.cwFace
2516 );
2517 return -1;
2518 }
2519 } else {
2520 epan.was_isolated = 1;
2521 newedge.next_left = isclosed ? newedge.edge_id : -newedge.edge_id;
2522 prev_right = isclosed ? -newedge.edge_id : newedge.edge_id;
2523 LWDEBUGF(1, "New edge %d is isolated on end node, "
2524 "next_left is %d, prev_right is %d",
2525 newedge.edge_id, newedge.next_left, prev_right);
2526 }
2527
2528 /*
2529 * If we don't have faces setup by now we must have encountered
2530 * a malformed topology (no containing_face on isolated nodes, no
2531 * left/right faces on adjacent edges or mismatching values)
2532 */
2533 if ( newedge.face_left != newedge.face_right )
2534 {
2535 lwerror("Left(%" LWTFMT_ELEMID ")/right(%" LWTFMT_ELEMID ")"
2536 "faces mismatch: invalid topology ?",
2537 newedge.face_left, newedge.face_right);
2538 return -1;
2539 }
2540 else if ( newedge.face_left == -1 && modFace > -1 )
2541 {
2542 lwerror("Could not derive edge face from linked primitives:"
2543 " invalid topology ?");
2544 return -1;
2545 }
2546
2547 /*
2548 * Insert the new edge, and update all linking
2549 */
2550
2551 int ret = lwt_be_insertEdges(topo, &newedge, 1);
2552 if ( ret == -1 ) {
2553 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2554 return -1;
2555 } else if ( ret == 0 ) {
2556 lwerror("Insertion of split edge failed (no reason)");
2557 return -1;
2558 }
2559
2560 int updfields;
2561
2562 /* Link prev_left to us
2563 * (if it's not us already) */
2564 if ( llabs(prev_left) != newedge.edge_id )
2565 {
2566 if ( prev_left > 0 )
2567 {
2568 /* its next_left_edge is us */
2569 updfields = LWT_COL_EDGE_NEXT_LEFT;
2570 updedge.next_left = newedge.edge_id;
2571 seledge.edge_id = prev_left;
2572 }
2573 else
2574 {
2575 /* its next_right_edge is us */
2576 updfields = LWT_COL_EDGE_NEXT_RIGHT;
2577 updedge.next_right = newedge.edge_id;
2578 seledge.edge_id = -prev_left;
2579 }
2580
2581 ret = lwt_be_updateEdges(topo,
2582 &seledge, LWT_COL_EDGE_EDGE_ID,
2583 &updedge, updfields,
2584 NULL, 0);
2585 if ( ret == -1 ) {
2586 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2587 return -1;
2588 }
2589 }
2590
2591 /* Link prev_right to us
2592 * (if it's not us already) */
2593 if ( llabs(prev_right) != newedge.edge_id )
2594 {
2595 if ( prev_right > 0 )
2596 {
2597 /* its next_left_edge is -us */
2598 updfields = LWT_COL_EDGE_NEXT_LEFT;
2599 updedge.next_left = -newedge.edge_id;
2600 seledge.edge_id = prev_right;
2601 }
2602 else
2603 {
2604 /* its next_right_edge is -us */
2605 updfields = LWT_COL_EDGE_NEXT_RIGHT;
2606 updedge.next_right = -newedge.edge_id;
2607 seledge.edge_id = -prev_right;
2608 }
2609
2610 ret = lwt_be_updateEdges(topo,
2611 &seledge, LWT_COL_EDGE_EDGE_ID,
2612 &updedge, updfields,
2613 NULL, 0);
2614 if ( ret == -1 ) {
2615 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2616 return -1;
2617 }
2618 }
2619
2620 /* NOT IN THE SPECS...
2621 * set containing_face = null for start_node and end_node
2622 * if they where isolated
2623 *
2624 */
2625 LWT_ISO_NODE updnode, selnode;
2626 updnode.containing_face = -1;
2627 if ( span.was_isolated )
2628 {
2629 selnode.node_id = start_node;
2630 ret = lwt_be_updateNodes(topo,
2631 &selnode, LWT_COL_NODE_NODE_ID,
2633 NULL, 0);
2634 if ( ret == -1 ) {
2635 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2636 return -1;
2637 }
2638 }
2639 if ( epan.was_isolated )
2640 {
2641 selnode.node_id = end_node;
2642 ret = lwt_be_updateNodes(topo,
2643 &selnode, LWT_COL_NODE_NODE_ID,
2645 NULL, 0);
2646 if ( ret == -1 ) {
2647 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2648 return -1;
2649 }
2650 }
2651
2652 /* Check face splitting, if required */
2653
2654 if ( modFace > -1 ) {
2655
2656 if ( ! isclosed && ( epan.was_isolated || span.was_isolated ) )
2657 {
2658 LWDEBUG(1, "New edge is dangling, so it cannot split any face");
2659 return newedge.edge_id; /* no split */
2660 }
2661
2662 int newface1 = -1;
2663
2664 /* IDEA: avoid building edge ring if input is closed, which means we
2665 * know in advance it splits a face */
2666
2667 if ( ! modFace )
2668 {
2669 newface1 = _lwt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 0 );
2670 if ( newface1 == 0 ) {
2671 LWDEBUG(1, "New edge does not split any face");
2672 return newedge.edge_id; /* no split */
2673 }
2674 }
2675
2676 int newface = _lwt_AddFaceSplit( topo, newedge.edge_id,
2677 newedge.face_left, 0 );
2678 if ( modFace )
2679 {
2680 if ( newface == 0 ) {
2681 LWDEBUG(1, "New edge does not split any face");
2682 return newedge.edge_id; /* no split */
2683 }
2684
2685 if ( newface < 0 )
2686 {
2687 /* face on the left is the universe face */
2688 /* must be forming a maximal ring in universal face */
2689 newface = _lwt_AddFaceSplit( topo, -newedge.edge_id,
2690 newedge.face_left, 0 );
2691 if ( newface < 0 ) return newedge.edge_id; /* no split */
2692 }
2693 else
2694 {
2695 _lwt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 1 );
2696 }
2697 }
2698
2699 /*
2700 * Update topogeometries, if needed
2701 */
2702 if ( newedge.face_left != 0 )
2703 {
2704 ret = lwt_be_updateTopoGeomFaceSplit(topo, newedge.face_left,
2705 newface, newface1);
2706 if ( ret == 0 ) {
2707 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2708 return -1;
2709 }
2710
2711 if ( ! modFace )
2712 {
2713 /* drop old face from the face table */
2714 ret = lwt_be_deleteFacesById(topo, &(newedge.face_left), 1);
2715 if ( ret == -1 ) {
2716 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2717 return -1;
2718 }
2719 }
2720 }
2721
2722 } // end of face split checking
2723
2724 return newedge.edge_id;
2725}
2726
2729 LWT_ELEMID start_node, LWT_ELEMID end_node,
2730 LWLINE *geom, int skipChecks )
2731{
2732 return _lwt_AddEdge( topo, start_node, end_node, geom, skipChecks, 1 );
2733}
2734
2737 LWT_ELEMID start_node, LWT_ELEMID end_node,
2738 LWLINE *geom, int skipChecks )
2739{
2740 return _lwt_AddEdge( topo, start_node, end_node, geom, skipChecks, 0 );
2741}
2742
2743static LWGEOM *
2744_lwt_FaceByEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edges, int numfaceedges)
2745{
2746 LWGEOM *outg;
2747 LWCOLLECTION *bounds;
2748 LWGEOM **geoms = lwalloc( sizeof(LWGEOM*) * numfaceedges );
2749 int i, validedges = 0;
2750
2751 for ( i=0; i<numfaceedges; ++i )
2752 {
2753 /* NOTE: skipping edges with same face on both sides, although
2754 * correct, results in a failure to build faces from
2755 * invalid topologies as expected by legacy tests.
2756 * TODO: update legacy tests expectances/unleash this skipping ?
2757 */
2758 /* if ( edges[i].face_left == edges[i].face_right ) continue; */
2759 geoms[validedges++] = lwline_as_lwgeom(edges[i].geom);
2760 }
2761 if ( ! validedges )
2762 {
2763 /* Face has no valid boundary edges, we'll return EMPTY, see
2764 * https://trac.osgeo.org/postgis/ticket/3221 */
2765 if ( numfaceedges ) lwfree(geoms);
2766 LWDEBUG(1, "_lwt_FaceByEdges returning empty polygon");
2767 return lwpoly_as_lwgeom(
2768 lwpoly_construct_empty(topo->srid, topo->hasZ, 0)
2769 );
2770 }
2772 topo->srid,
2773 NULL, /* gbox */
2774 validedges,
2775 geoms);
2776 outg = lwgeom_buildarea( lwcollection_as_lwgeom(bounds) );
2777 lwcollection_release(bounds);
2778 lwfree(geoms);
2779#if 0
2780 {
2781 size_t sz;
2782 char *wkt = lwgeom_to_wkt(outg, WKT_EXTENDED, 2, &sz);
2783 LWDEBUGF(1, "_lwt_FaceByEdges returning area: %s", wkt);
2784 lwfree(wkt);
2785 }
2786#endif
2787 return outg;
2788}
2789
2790LWGEOM*
2792{
2793 uint64_t numfaceedges;
2794 LWT_ISO_EDGE *edges;
2795 LWT_ISO_FACE *face;
2796 LWPOLY *out;
2797 LWGEOM *outg;
2798 uint64_t i;
2799 int fields;
2800
2801 if (faceid == 0)
2802 {
2803 lwerror("SQL/MM Spatial exception - universal face has no geometry");
2804 return NULL;
2805 }
2806
2807 /* Construct the face geometry */
2808 numfaceedges = 1;
2809 fields = LWT_COL_EDGE_GEOM |
2812 ;
2813 edges = lwt_be_getEdgeByFace( topo, &faceid, &numfaceedges, fields, NULL );
2814 if (numfaceedges == UINT64_MAX)
2815 {
2816 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2817 return NULL;
2818 }
2819
2820 if ( numfaceedges == 0 )
2821 {
2822 i = 1;
2823 face = lwt_be_getFaceById(topo, &faceid, &i, LWT_COL_FACE_FACE_ID);
2824 if (i == UINT64_MAX)
2825 {
2826 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
2827 return NULL;
2828 }
2829 if ( i == 0 ) {
2830 lwerror("SQL/MM Spatial exception - non-existent face.");
2831 return NULL;
2832 }
2833 lwfree( face );
2834 if ( i > 1 ) {
2835 lwerror("Corrupted topology: multiple face records have face_id=%"
2836 LWTFMT_ELEMID, faceid);
2837 return NULL;
2838 }
2839 /* Face has no boundary edges, we'll return EMPTY, see
2840 * https://trac.osgeo.org/postgis/ticket/3221 */
2841 out = lwpoly_construct_empty(topo->srid, topo->hasZ, 0);
2842 return lwpoly_as_lwgeom(out);
2843 }
2844
2845 outg = _lwt_FaceByEdges( topo, edges, numfaceedges );
2846 _lwt_release_edges(edges, numfaceedges);
2847
2848 return outg;
2849}
2850
2851/* Find which edge from the "edges" set defines the next
2852 * portion of the given "ring".
2853 *
2854 * The edge might be either forward or backward.
2855 *
2856 * @param ring The ring to find definition of.
2857 * It is assumed it does not contain duplicated vertices.
2858 * @param from offset of the ring point to start looking from
2859 * @param edges array of edges to search into
2860 * @param numedges number of edges in the edges array
2861 *
2862 * @return index of the edge defining the next ring portion or
2863 * -1 if no edge was found to be part of the ring
2864 */
2865static int
2866_lwt_FindNextRingEdge(const POINTARRAY *ring, int from,
2867 const LWT_ISO_EDGE *edges, int numedges)
2868{
2869 int i;
2870 POINT2D p1;
2871
2872 /* Get starting ring point */
2873 getPoint2d_p(ring, from, &p1);
2874
2875 LWDEBUGF(1, "Ring's 'from' point (%d) is %g,%g", from, p1.x, p1.y);
2876
2877 /* find the edges defining the next portion of ring starting from
2878 * vertex "from" */
2879 for ( i=0; i<numedges; ++i )
2880 {
2881 const LWT_ISO_EDGE *isoe = &(edges[i]);
2882 LWLINE *edge = isoe->geom;
2883 POINTARRAY *epa = edge->points;
2884 POINT2D p2, pt;
2885 int match = 0;
2886 uint32_t j;
2887
2888 /* Skip if the edge is a dangling one */
2889 if ( isoe->face_left == isoe->face_right )
2890 {
2891 LWDEBUGF(3, "_lwt_FindNextRingEdge: edge %" LWTFMT_ELEMID
2892 " has same face (%" LWTFMT_ELEMID
2893 ") on both sides, skipping",
2894 isoe->edge_id, isoe->face_left);
2895 continue;
2896 }
2897
2898 if (epa->npoints < 2)
2899 {
2900 LWDEBUGF(3, "_lwt_FindNextRingEdge: edge %" LWTFMT_ELEMID
2901 " has only %"PRIu32" points",
2902 isoe->edge_id, epa->npoints);
2903 continue;
2904 }
2905
2906#if 0
2907 size_t sz;
2908 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is %s",
2909 isoe->edge_id,
2911#endif
2912
2913 /* ptarray_remove_repeated_points ? */
2914
2915 getPoint2d_p(epa, 0, &p2);
2916 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " 'first' point is %g,%g",
2917 isoe->edge_id, p2.x, p2.y);
2918 LWDEBUGF(1, "Rings's 'from' point is still %g,%g", p1.x, p1.y);
2919 if ( p2d_same(&p1, &p2) )
2920 {
2921 LWDEBUG(1, "p2d_same(p1,p2) returned true");
2922 LWDEBUGF(1, "First point of edge %" LWTFMT_ELEMID
2923 " matches ring vertex %d", isoe->edge_id, from);
2924 /* first point matches, let's check next non-equal one */
2925 for ( j=1; j<epa->npoints; ++j )
2926 {
2927 getPoint2d_p(epa, j, &p2);
2928 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " 'next' point %d is %g,%g",
2929 isoe->edge_id, j, p2.x, p2.y);
2930 /* we won't check duplicated edge points */
2931 if ( p2d_same(&p1, &p2) ) continue;
2932 /* we assume there are no duplicated points in ring */
2933 getPoint2d_p(ring, from+1, &pt);
2934 LWDEBUGF(1, "Ring's point %d is %g,%g",
2935 from+1, pt.x, pt.y);
2936 match = p2d_same(&pt, &p2);
2937 break; /* we want to check a single non-equal next vertex */
2938 }
2939#if POSTGIS_DEBUG_LEVEL > 0
2940 if ( match ) {
2941 LWDEBUGF(1, "Prev point of edge %" LWTFMT_ELEMID
2942 " matches ring vertex %d", isoe->edge_id, from+1);
2943 } else {
2944 LWDEBUGF(1, "Prev point of edge %" LWTFMT_ELEMID
2945 " does not match ring vertex %d", isoe->edge_id, from+1);
2946 }
2947#endif
2948 }
2949
2950 if ( ! match )
2951 {
2952 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " did not match as forward",
2953 isoe->edge_id);
2954 getPoint2d_p(epa, epa->npoints-1, &p2);
2955 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " 'last' point is %g,%g",
2956 isoe->edge_id, p2.x, p2.y);
2957 if ( p2d_same(&p1, &p2) )
2958 {
2959 LWDEBUGF(1, "Last point of edge %" LWTFMT_ELEMID
2960 " matches ring vertex %d", isoe->edge_id, from);
2961 /* last point matches, let's check next non-equal one */
2962 for ( j=2; j<=epa->npoints; j++ )
2963 {
2964 getPoint2d_p(epa, epa->npoints - j, &p2);
2965 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " 'prev' point %d is %g,%g",
2966 isoe->edge_id, epa->npoints - j, p2.x, p2.y);
2967 /* we won't check duplicated edge points */
2968 if ( p2d_same(&p1, &p2) ) continue;
2969 /* we assume there are no duplicated points in ring */
2970 getPoint2d_p(ring, from+1, &pt);
2971 LWDEBUGF(1, "Ring's point %d is %g,%g",
2972 from+1, pt.x, pt.y);
2973 match = p2d_same(&pt, &p2);
2974 break; /* we want to check a single non-equal next vertex */
2975 }
2976 }
2977#if POSTGIS_DEBUG_LEVEL > 0
2978 if ( match ) {
2979 LWDEBUGF(1, "Prev point of edge %" LWTFMT_ELEMID
2980 " matches ring vertex %d", isoe->edge_id, from+1);
2981 } else {
2982 LWDEBUGF(1, "Prev point of edge %" LWTFMT_ELEMID
2983 " does not match ring vertex %d", isoe->edge_id, from+1);
2984 }
2985#endif
2986 }
2987
2988 if ( match ) return i;
2989
2990 }
2991
2992 return -1;
2993}
2994
2995/* Reverse values in array between "from" (inclusive)
2996 * and "to" (exclusive) indexes */
2997static void
2999{
3000 LWT_ELEMID t;
3001 while (from < to)
3002 {
3003 t = ary[from];
3004 ary[from++] = ary[to];
3005 ary[to--] = t;
3006 }
3007}
3008
3009/* Rotate values in array between "from" (inclusive)
3010 * and "to" (exclusive) indexes, so that "rotidx" is
3011 * the new value at "from" */
3012static void
3013_lwt_RotateElemidArray(LWT_ELEMID *ary, int from, int to, int rotidx)
3014{
3015 _lwt_ReverseElemidArray(ary, from, rotidx-1);
3016 _lwt_ReverseElemidArray(ary, rotidx, to-1);
3017 _lwt_ReverseElemidArray(ary, from, to-1);
3018}
3019
3020
3021int
3023{
3024 LWGEOM *face;
3025 LWPOLY *facepoly;
3026 LWT_ISO_EDGE *edges;
3027 uint64_t numfaceedges;
3028 int fields;
3029 uint32_t i;
3030 int nseid = 0; /* number of signed edge ids */
3031 int prevseid;
3032 LWT_ELEMID *seid; /* signed edge ids */
3033
3034 /* Get list of face edges */
3035 numfaceedges = 1;
3036 fields = LWT_COL_EDGE_EDGE_ID |
3040 ;
3041 edges = lwt_be_getEdgeByFace( topo, &face_id, &numfaceedges, fields, NULL );
3042 if (numfaceedges == UINT64_MAX)
3043 {
3044 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3045 return -1;
3046 }
3047 if ( ! numfaceedges ) return 0; /* no edges in output */
3048
3049 /* order edges by occurrence in face */
3050
3051 face = _lwt_FaceByEdges(topo, edges, numfaceedges);
3052 if ( ! face )
3053 {
3054 /* _lwt_FaceByEdges should have already invoked lwerror in this case */
3055 _lwt_release_edges(edges, numfaceedges);
3056 lwerror("Corrupted topology: unable to build geometry of face %"
3057 LWTFMT_ELEMID " from its %"PRIu64" edges", face_id, numfaceedges);
3058 return -1;
3059 }
3060
3061 if ( lwgeom_is_empty(face) )
3062 {
3063 /* no edges in output */
3064 _lwt_release_edges(edges, numfaceedges);
3065 lwgeom_free(face);
3066 return 0;
3067 }
3068
3069 /* force_lhr, if the face is not the universe */
3070 /* _lwt_FaceByEdges seems to guaranteed RHR */
3071 /* lwgeom_force_clockwise(face); */
3072 if ( face_id ) lwgeom_reverse_in_place(face);
3073
3074#if 0
3075 {
3076 size_t sz;
3077 char *wkt = lwgeom_to_wkt(face, WKT_EXTENDED, 6, &sz);
3078 LWDEBUGF(1, "Geometry of face %" LWTFMT_ELEMID " is: %s",
3079 face_id, wkt);
3080 lwfree(wkt);
3081 }
3082#endif
3083
3084 facepoly = lwgeom_as_lwpoly(face);
3085 if ( ! facepoly )
3086 {
3087 _lwt_release_edges(edges, numfaceedges);
3088 lwgeom_free(face);
3089 lwerror("Geometry of face %" LWTFMT_ELEMID " is not a polygon", face_id);
3090 return -1;
3091 }
3092
3093 nseid = prevseid = 0;
3094 seid = lwalloc( sizeof(LWT_ELEMID) * numfaceedges );
3095
3096 /* for each ring of the face polygon... */
3097 for ( i=0; i<facepoly->nrings; ++i )
3098 {
3099 const POINTARRAY *ring = facepoly->rings[i];
3100 int32_t j = 0;
3101 LWT_ISO_EDGE *nextedge;
3102 LWLINE *nextline;
3103
3104 LWDEBUGF(1, "Ring %d has %d points", i, ring->npoints);
3105
3106 while ( j < (int32_t) ring->npoints-1 )
3107 {
3108 LWDEBUGF(1, "Looking for edge covering ring %d from vertex %d",
3109 i, j);
3110
3111 int edgeno = _lwt_FindNextRingEdge(ring, j, edges, numfaceedges);
3112 if ( edgeno == -1 )
3113 {
3114 /* should never happen */
3115 _lwt_release_edges(edges, numfaceedges);
3116 lwgeom_free(face);
3117 lwfree(seid);
3118 lwerror("No edge (among %d) found to be defining geometry of face %"
3119 LWTFMT_ELEMID, numfaceedges, face_id);
3120 return -1;
3121 }
3122
3123 nextedge = &(edges[edgeno]);
3124 nextline = nextedge->geom;
3125
3126 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID
3127 " covers ring %d from vertex %d to %d",
3128 nextedge->edge_id, i, j, j + nextline->points->npoints - 1);
3129
3130#if 0
3131 {
3132 size_t sz;
3133 char *wkt = lwgeom_to_wkt(lwline_as_lwgeom(nextline), WKT_EXTENDED, 6, &sz);
3134 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is %s",
3135 nextedge->edge_id, wkt);
3136 lwfree(wkt);
3137 }
3138#endif
3139
3140 j += nextline->points->npoints - 1;
3141
3142 /* Add next edge to the output array */
3143 seid[nseid++] = nextedge->face_left == face_id ?
3144 nextedge->edge_id :
3145 -nextedge->edge_id;
3146
3147 /* avoid checking again on next time turn */
3148 nextedge->face_left = nextedge->face_right = -1;
3149 }
3150
3151 /* now "scroll" the list of edges so that the one
3152 * with smaller absolute edge_id is first */
3153 /* Range is: [prevseid, nseid) -- [inclusive, exclusive) */
3154 if ( (nseid - prevseid) > 1 )
3155 {{
3156 LWT_ELEMID minid = 0;
3157 int minidx = 0;
3158 LWDEBUGF(1, "Looking for smallest id among the %d edges "
3159 "composing ring %d", (nseid-prevseid), i);
3160 for ( j=prevseid; j<nseid; ++j )
3161 {
3162 LWT_ELEMID id = llabs(seid[j]);
3163 LWDEBUGF(1, "Abs id of edge in pos %d is %" LWTFMT_ELEMID, j, id);
3164 if ( ! minid || id < minid )
3165 {
3166 minid = id;
3167 minidx = j;
3168 }
3169 }
3170 LWDEBUGF(1, "Smallest id is %" LWTFMT_ELEMID
3171 " at position %d", minid, minidx);
3172 if ( minidx != prevseid )
3173 {
3174 _lwt_RotateElemidArray(seid, prevseid, nseid, minidx);
3175 }
3176 }}
3177
3178 prevseid = nseid;
3179 }
3180
3181 lwgeom_free(face);
3182 _lwt_release_edges(edges, numfaceedges);
3183
3184 *out = seid;
3185 return nseid;
3186}
3187
3188int
3190{
3191 LWT_ISO_EDGE *oldedge;
3192 LWT_ISO_EDGE newedge;
3193 POINT2D p1, p2, pt;
3194 uint64_t i;
3195 int isclosed = 0;
3196
3197 /* curve must be simple */
3198 if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) )
3199 {
3200 lwerror("SQL/MM Spatial exception - curve not simple");
3201 return -1;
3202 }
3203
3204 i = 1;
3205 oldedge = lwt_be_getEdgeById(topo, &edge_id, &i, LWT_COL_EDGE_ALL);
3206 if ( ! oldedge )
3207 {
3208 LWDEBUGF(1, "lwt_ChangeEdgeGeom: "
3209 "lwt_be_getEdgeById returned NULL and set i=%d", i);
3210 if (i == UINT64_MAX)
3211 {
3212 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3213 return -1;
3214 }
3215 else if ( i == 0 )
3216 {
3217 lwerror("SQL/MM Spatial exception - non-existent edge %"
3218 LWTFMT_ELEMID, edge_id);
3219 return -1;
3220 }
3221 else
3222 {
3223 lwerror("Backend coding error: getEdgeById callback returned NULL "
3224 "but numelements output parameter has value %d "
3225 "(expected 0 or 1)", i);
3226 return -1;
3227 }
3228 }
3229
3230 LWDEBUGF(1, "lwt_ChangeEdgeGeom: "
3231 "old edge has %d points, new edge has %d points",
3232 oldedge->geom->points->npoints, geom->points->npoints);
3233
3234 /*
3235 * e) Check StartPoint consistency
3236 */
3237 getPoint2d_p(oldedge->geom->points, 0, &p1);
3238 getPoint2d_p(geom->points, 0, &pt);
3239 if ( ! p2d_same(&p1, &pt) )
3240 {
3241 _lwt_release_edges(oldedge, 1);
3242 lwerror("SQL/MM Spatial exception - "
3243 "start node not geometry start point.");
3244 return -1;
3245 }
3246
3247 /*
3248 * f) Check EndPoint consistency
3249 */
3250 if ( oldedge->geom->points->npoints < 2 )
3251 {
3252 _lwt_release_edges(oldedge, 1);
3253 lwerror("Corrupted topology: edge %" LWTFMT_ELEMID
3254 " has less than 2 vertices", oldedge->edge_id);
3255 return -1;
3256 }
3257 getPoint2d_p(oldedge->geom->points, oldedge->geom->points->npoints-1, &p2);
3258 if ( geom->points->npoints < 2 )
3259 {
3260 _lwt_release_edges(oldedge, 1);
3261 lwerror("Invalid edge: less than 2 vertices");
3262 return -1;
3263 }
3264 getPoint2d_p(geom->points, geom->points->npoints-1, &pt);
3265 if ( ! p2d_same(&pt, &p2) )
3266 {
3267 _lwt_release_edges(oldedge, 1);
3268 lwerror("SQL/MM Spatial exception - "
3269 "end node not geometry end point.");
3270 return -1;
3271 }
3272
3273 /* Not in the specs:
3274 * if the edge is closed, check we didn't change winding !
3275 * (should be part of isomorphism checking)
3276 */
3277 if ( oldedge->start_node == oldedge->end_node )
3278 {
3279 isclosed = 1;
3280#if 1 /* TODO: this is actually bogus as a test */
3281 /* check for valid edge (distinct vertices must exist) */
3282 if ( ! _lwt_GetInteriorEdgePoint(geom, &pt) )
3283 {
3284 _lwt_release_edges(oldedge, 1);
3285 lwerror("Invalid edge (no two distinct vertices exist)");
3286 return -1;
3287 }
3288#endif
3289
3290 if ( ptarray_isccw(oldedge->geom->points) !=
3291 ptarray_isccw(geom->points) )
3292 {
3293 _lwt_release_edges(oldedge, 1);
3294 lwerror("Edge twist at node POINT(%g %g)", p1.x, p1.y);
3295 return -1;
3296 }
3297 }
3298
3299 if ( _lwt_CheckEdgeCrossing(topo, oldedge->start_node,
3300 oldedge->end_node, geom, edge_id ) )
3301 {
3302 /* would have called lwerror already, leaking :( */
3303 _lwt_release_edges(oldedge, 1);
3304 return -1;
3305 }
3306
3307 LWDEBUG(1, "lwt_ChangeEdgeGeom: "
3308 "edge crossing check passed ");
3309
3310 /*
3311 * Not in the specs:
3312 * Check topological isomorphism
3313 */
3314
3315 /* Check that the "motion range" doesn't include any node */
3316 // 1. compute combined bbox of old and new edge
3317 GBOX mbox; /* motion box */
3318 lwgeom_add_bbox((LWGEOM*)oldedge->geom); /* just in case */
3319 lwgeom_add_bbox((LWGEOM*)geom); /* just in case */
3320 gbox_union(oldedge->geom->bbox, geom->bbox, &mbox);
3321 // 2. fetch all nodes in the combined box
3322 LWT_ISO_NODE *nodes;
3323 uint64_t numnodes;
3324 nodes = lwt_be_getNodeWithinBox2D(topo, &mbox, &numnodes,
3325 LWT_COL_NODE_ALL, 0);
3326 LWDEBUGF(1, "lwt_be_getNodeWithinBox2D returned %d nodes", numnodes);
3327 if (numnodes == UINT64_MAX)
3328 {
3329 _lwt_release_edges(oldedge, 1);
3330 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3331 return -1;
3332 }
3333 // 3. if any node beside endnodes are found:
3334 if ( numnodes > ( 1 + isclosed ? 0 : 1 ) )
3335 {{
3336 // 3.2. bail out if any node is in one and not the other
3337 for (i=0; i<numnodes; ++i)
3338 {
3339 LWT_ISO_NODE *n = &(nodes[i]);
3340 if ( n->node_id == oldedge->start_node ) continue;
3341 if ( n->node_id == oldedge->end_node ) continue;
3342 const POINT2D *pt = getPoint2d_cp(n->geom->point, 0);
3343 int ocont = ptarray_contains_point_partial(oldedge->geom->points, pt, isclosed, NULL) == LW_INSIDE;
3344 int ncont = ptarray_contains_point_partial(geom->points, pt, isclosed, NULL) == LW_INSIDE;
3345 if (ocont != ncont)
3346 {
3347 size_t sz;
3348 char *wkt = lwgeom_to_wkt(lwpoint_as_lwgeom(n->geom), WKT_ISO, 15, &sz);
3349 _lwt_release_nodes(nodes, numnodes);
3350 lwerror("Edge motion collision at %s", wkt);
3351 lwfree(wkt); /* would not necessarely reach this point */
3352 return -1;
3353 }
3354 }
3355 }}
3356 if ( numnodes ) _lwt_release_nodes(nodes, numnodes);
3357
3358 LWDEBUG(1, "nodes containment check passed");
3359
3360 /*
3361 * Check edge adjacency before
3362 * TODO: can be optimized to gather azimuths of all edge ends once
3363 */
3364
3365 edgeend span_pre, epan_pre;
3366 /* initialize span_pre.myaz and epan_pre.myaz with existing edge */
3367 int res = _lwt_InitEdgeEndByLine(&span_pre, &epan_pre, oldedge->geom, &p1, &p2);
3368 if (res)
3369 return -1; /* lwerror should have been raised */
3370 _lwt_FindAdjacentEdges( topo, oldedge->start_node, &span_pre,
3371 isclosed ? &epan_pre : NULL, edge_id );
3372 _lwt_FindAdjacentEdges( topo, oldedge->end_node, &epan_pre,
3373 isclosed ? &span_pre : NULL, edge_id );
3374
3375 LWDEBUGF(1, "edges adjacent to old edge are %" LWTFMT_ELEMID
3376 " and %" LWTFMT_ELEMID " (first point), %" LWTFMT_ELEMID
3377 " and %" LWTFMT_ELEMID " (last point)",
3378 span_pre.nextCW, span_pre.nextCCW,
3379 epan_pre.nextCW, epan_pre.nextCCW);
3380
3381 /* update edge geometry */
3382 newedge.edge_id = edge_id;
3383 newedge.geom = geom;
3384 res = lwt_be_updateEdgesById(topo, &newedge, 1, LWT_COL_EDGE_GEOM);
3385 if (res == -1)
3386 {
3387 _lwt_release_edges(oldedge, 1);
3388 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3389 return -1;
3390 }
3391 if (!res)
3392 {
3393 _lwt_release_edges(oldedge, 1);
3394 lwerror("Unexpected error: %d edges updated when expecting 1", i);
3395 return -1;
3396 }
3397
3398 /*
3399 * Check edge adjacency after
3400 */
3401 edgeend span_post, epan_post;
3402 /* initialize epan_post.myaz and epan_post.myaz */
3403 res = _lwt_InitEdgeEndByLine(&span_post, &epan_post, geom, &p1, &p2);
3404 if (res)
3405 return -1; /* lwerror should have been raised */
3406 _lwt_FindAdjacentEdges( topo, oldedge->start_node, &span_post,
3407 isclosed ? &epan_post : NULL, edge_id );
3408 _lwt_FindAdjacentEdges( topo, oldedge->end_node, &epan_post,
3409 isclosed ? &span_post : NULL, edge_id );
3410
3411 LWDEBUGF(1, "edges adjacent to new edge are %" LWTFMT_ELEMID
3412 " and %" LWTFMT_ELEMID " (first point), %" LWTFMT_ELEMID
3413 " and %" LWTFMT_ELEMID " (last point)",
3414 span_pre.nextCW, span_pre.nextCCW,
3415 epan_pre.nextCW, epan_pre.nextCCW);
3416
3417
3418 /* Bail out if next CW or CCW edge on start node changed */
3419 if ( span_pre.nextCW != span_post.nextCW ||
3420 span_pre.nextCCW != span_post.nextCCW )
3421 {{
3422 LWT_ELEMID nid = oldedge->start_node;
3423 _lwt_release_edges(oldedge, 1);
3424 lwerror("Edge changed disposition around start node %"
3425 LWTFMT_ELEMID, nid);
3426 return -1;
3427 }}
3428
3429 /* Bail out if next CW or CCW edge on end node changed */
3430 if ( epan_pre.nextCW != epan_post.nextCW ||
3431 epan_pre.nextCCW != epan_post.nextCCW )
3432 {{
3433 LWT_ELEMID nid = oldedge->end_node;
3434 _lwt_release_edges(oldedge, 1);
3435 lwerror("Edge changed disposition around end node %"
3436 LWTFMT_ELEMID, nid);
3437 return -1;
3438 }}
3439
3440 /*
3441 -- Update faces MBR of left and right faces
3442 -- TODO: think about ways to optimize this part, like see if
3443 -- the old edge geometry participated in the definition
3444 -- of the current MBR (for shrinking) or the new edge MBR
3445 -- would be larger than the old face MBR...
3446 --
3447 */
3448 const GBOX* oldbox = lwgeom_get_bbox(lwline_as_lwgeom(oldedge->geom));
3449 const GBOX* newbox = lwgeom_get_bbox(lwline_as_lwgeom(geom));
3450 if ( ! gbox_same(oldbox, newbox) )
3451 {
3452 uint64_t facestoupdate = 0;
3453 LWT_ISO_FACE faces[2];
3454 LWGEOM *nface1 = NULL;
3455 LWGEOM *nface2 = NULL;
3456 if ( oldedge->face_left > 0 )
3457 {
3458 nface1 = lwt_GetFaceGeometry(topo, oldedge->face_left);
3459 if ( ! nface1 )
3460 {
3461 lwerror("lwt_ChangeEdgeGeom could not construct face %"
3462 LWTFMT_ELEMID ", on the left of edge %" LWTFMT_ELEMID,
3463 oldedge->face_left, edge_id);
3464 return -1;
3465 }
3466 #if 0
3467 {
3468 size_t sz;
3469 char *wkt = lwgeom_to_wkt(nface1, WKT_EXTENDED, 2, &sz);
3470 LWDEBUGF(1, "new geometry of face left (%d): %s", (int)oldedge->face_left, wkt);
3471 lwfree(wkt);
3472 }
3473 #endif
3474 lwgeom_add_bbox(nface1);
3475 if ( ! nface1->bbox )
3476 {
3477 lwerror("Corrupted topology: face %d, left of edge %d, has no bbox",
3478 oldedge->face_left, edge_id);
3479 return -1;
3480 }
3481 faces[facestoupdate].face_id = oldedge->face_left;
3482 /* ownership left to nface */
3483 faces[facestoupdate++].mbr = nface1->bbox;
3484 }
3485 if ( oldedge->face_right > 0
3486 /* no need to update twice the same face.. */
3487 && oldedge->face_right != oldedge->face_left )
3488 {
3489 nface2 = lwt_GetFaceGeometry(topo, oldedge->face_right);
3490 if ( ! nface2 )
3491 {
3492 lwerror("lwt_ChangeEdgeGeom could not construct face %"
3493 LWTFMT_ELEMID ", on the right of edge %" LWTFMT_ELEMID,
3494 oldedge->face_right, edge_id);
3495 return -1;
3496 }
3497 #if 0
3498 {
3499 size_t sz;
3500 char *wkt = lwgeom_to_wkt(nface2, WKT_EXTENDED, 2, &sz);
3501 LWDEBUGF(1, "new geometry of face right (%d): %s", (int)oldedge->face_right, wkt);
3502 lwfree(wkt);
3503 }
3504 #endif
3505 lwgeom_add_bbox(nface2);
3506 if ( ! nface2->bbox )
3507 {
3508 lwerror("Corrupted topology: face %d, right of edge %d, has no bbox",
3509 oldedge->face_right, edge_id);
3510 return -1;
3511 }
3512 faces[facestoupdate].face_id = oldedge->face_right;
3513 faces[facestoupdate++].mbr = nface2->bbox; /* ownership left to nface */
3514 }
3515 LWDEBUGF(1, "%d faces to update", facestoupdate);
3516 if ( facestoupdate )
3517 {
3518 uint64_t updatedFaces = lwt_be_updateFacesById(topo, &(faces[0]), facestoupdate);
3519 if (updatedFaces != facestoupdate)
3520 {
3521 if (nface1)
3522 lwgeom_free(nface1);
3523 if (nface2)
3524 lwgeom_free(nface2);
3525 _lwt_release_edges(oldedge, 1);
3526 if (updatedFaces == UINT64_MAX)
3527 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3528 else
3529 lwerror("Unexpected error: %d faces found when expecting 1", i);
3530 return -1;
3531 }
3532 }
3533 if ( nface1 ) lwgeom_free(nface1);
3534 if ( nface2 ) lwgeom_free(nface2);
3535 }
3536 else
3537 {
3538 lwnotice("BBOX of changed edge did not change");
3539 }
3540
3541 LWDEBUG(1, "all done, cleaning up edges");
3542
3543 _lwt_release_edges(oldedge, 1);
3544 return 0; /* success */
3545}
3546
3547/* Only return CONTAINING_FACE in the node object */
3548static LWT_ISO_NODE *
3550{
3551 LWT_ISO_NODE *node;
3552 uint64_t n = 1;
3553
3554 node = lwt_be_getNodeById( topo, &nid, &n, LWT_COL_NODE_CONTAINING_FACE );
3555 if (n == UINT64_MAX)
3556 {
3557 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3558 return 0;
3559 }
3560 if ( n < 1 ) {
3561 lwerror("SQL/MM Spatial exception - non-existent node");
3562 return 0;
3563 }
3564 if ( node->containing_face == -1 )
3565 {
3566 lwfree(node);
3567 lwerror("SQL/MM Spatial exception - not isolated node");
3568 return 0;
3569 }
3570
3571 return node;
3572}
3573
3574int
3576{
3577 LWT_ISO_NODE *node;
3578 int ret;
3579
3580 node = _lwt_GetIsoNode( topo, nid );
3581 if ( ! node ) return -1;
3582
3583 if ( lwt_be_ExistsCoincidentNode(topo, pt) )
3584 {
3585 lwfree(node);
3586 lwerror("SQL/MM Spatial exception - coincident node");
3587 return -1;
3588 }
3589
3590 if ( lwt_be_ExistsEdgeIntersectingPoint(topo, pt) )
3591 {
3592 lwfree(node);
3593 lwerror("SQL/MM Spatial exception - edge crosses node.");
3594 return -1;
3595 }
3596
3597 /* TODO: check that the new point is in the same containing face !
3598 * See https://trac.osgeo.org/postgis/ticket/3232
3599 */
3600
3601 node->node_id = nid;
3602 node->geom = pt;
3603 ret = lwt_be_updateNodesById(topo, node, 1,
3605 if ( ret == -1 ) {
3606 lwfree(node);
3607 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3608 return -1;
3609 }
3610
3611 lwfree(node);
3612 return 0;
3613}
3614
3615int
3617{
3618 LWT_ISO_NODE *node;
3619 int n = 1;
3620
3621 node = _lwt_GetIsoNode( topo, nid );
3622 if ( ! node ) return -1;
3623
3624 n = lwt_be_deleteNodesById( topo, &nid, n );
3625 if ( n == -1 )
3626 {
3627 lwfree(node);
3628 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3629 return -1;
3630 }
3631 if ( n != 1 )
3632 {
3633 lwfree(node);
3634 lwerror("Unexpected error: %d nodes deleted when expecting 1", n);
3635 return -1;
3636 }
3637
3638 /* TODO: notify to caller about node being removed ?
3639 * See https://trac.osgeo.org/postgis/ticket/3231
3640 */
3641
3642 lwfree(node);
3643 return 0; /* success */
3644}
3645
3646int
3648{
3649 LWT_ISO_EDGE deledge;
3650 LWT_ISO_EDGE *edge;
3651 LWT_ELEMID nid[2];
3652 LWT_ISO_NODE upd_node[2];
3653 LWT_ELEMID containing_face;
3654 uint64_t n = 1;
3655 uint64_t i;
3656
3657 edge = lwt_be_getEdgeById( topo, &id, &n, LWT_COL_EDGE_START_NODE|
3661 if ( ! edge )
3662 {
3663 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3664 return -1;
3665 }
3666 if ( ! n )
3667 {
3668 lwerror("SQL/MM Spatial exception - non-existent edge");
3669 return -1;
3670 }
3671 if ( n > 1 )
3672 {
3673 lwfree(edge);
3674 lwerror("Corrupted topology: more than a single edge have id %"
3675 LWTFMT_ELEMID, id);
3676 return -1;
3677 }
3678
3679 if ( edge[0].face_left != edge[0].face_right )
3680 {
3681 lwfree(edge);
3682 lwerror("SQL/MM Spatial exception - not isolated edge");
3683 return -1;
3684 }
3685 containing_face = edge[0].face_left;
3686
3687 nid[0] = edge[0].start_node;
3688 nid[1] = edge[0].end_node;
3689 lwfree(edge);
3690
3691 n = 2;
3692 edge = lwt_be_getEdgeByNode( topo, nid, &n, LWT_COL_EDGE_EDGE_ID );
3693 if ((n == UINT64_MAX) || (edge == NULL))
3694 {
3695 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3696 return -1;
3697 }
3698 for (i = 0; i < n; ++i)
3699 {
3700 if (edge[i].edge_id != id)
3701 {
3702 lwfree(edge);
3703 lwerror("SQL/MM Spatial exception - not isolated edge");
3704 return -1;
3705 }
3706 }
3707 lwfree(edge);
3708
3709 deledge.edge_id = id;
3710 n = lwt_be_deleteEdges( topo, &deledge, LWT_COL_EDGE_EDGE_ID );
3711 if (n == UINT64_MAX)
3712 {
3713 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3714 return -1;
3715 }
3716 if ( n != 1 )
3717 {
3718 lwerror("Unexpected error: %d edges deleted when expecting 1", n);
3719 return -1;
3720 }
3721
3722 upd_node[0].node_id = nid[0];
3723 upd_node[0].containing_face = containing_face;
3724 n = 1;
3725 if ( nid[1] != nid[0] ) {
3726 upd_node[1].node_id = nid[1];
3727 upd_node[1].containing_face = containing_face;
3728 ++n;
3729 }
3730 n = lwt_be_updateNodesById(topo, upd_node, n,
3732 if (n == UINT64_MAX)
3733 {
3734 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3735 return -1;
3736 }
3737
3738 /* TODO: notify to caller about edge being removed ?
3739 * See https://trac.osgeo.org/postgis/ticket/3248
3740 */
3741
3742 return 0; /* success */
3743}
3744
3745/* Used by _lwt_RemEdge to update edge face ref on healing
3746 *
3747 * @param of old face id (never 0 as you cannot remove face 0)
3748 * @param nf new face id
3749 * @return 0 on success, -1 on backend error
3750 */
3751static int
3753{
3754 LWT_ISO_EDGE sel_edge, upd_edge;
3755 int ret;
3756
3757 assert( of != 0 );
3758
3759 /* Update face_left for all edges still referencing old face */
3760 sel_edge.face_left = of;
3761 upd_edge.face_left = nf;
3762 ret = lwt_be_updateEdges(topo, &sel_edge, LWT_COL_EDGE_FACE_LEFT,
3763 &upd_edge, LWT_COL_EDGE_FACE_LEFT,
3764 NULL, 0);
3765 if ( ret == -1 ) return -1;
3766
3767 /* Update face_right for all edges still referencing old face */
3768 sel_edge.face_right = of;
3769 upd_edge.face_right = nf;
3770 ret = lwt_be_updateEdges(topo, &sel_edge, LWT_COL_EDGE_FACE_RIGHT,
3771 &upd_edge, LWT_COL_EDGE_FACE_RIGHT,
3772 NULL, 0);
3773 if ( ret == -1 ) return -1;
3774
3775 return 0;
3776}
3777
3778/* Used by _lwt_RemEdge to update node face ref on healing
3779 *
3780 * @param of old face id (never 0 as you cannot remove face 0)
3781 * @param nf new face id
3782 * @return 0 on success, -1 on backend error
3783 */
3784static int
3786{
3787 LWT_ISO_NODE sel, upd;
3788 int ret;
3789
3790 assert( of != 0 );
3791
3792 /* Update face_left for all edges still referencing old face */
3793 sel.containing_face = of;
3794 upd.containing_face = nf;
3797 NULL, 0);
3798 if ( ret == -1 ) return -1;
3799
3800 return 0;
3801}
3802
3803/* Used by lwt_RemEdgeModFace and lwt_RemEdgeNewFaces
3804 *
3805 * Returns -1 on error, identifier of the face that takes up the space
3806 * previously occupied by the removed edge if modFace is 1, identifier of
3807 * the created face (0 if none) if modFace is 0.
3808 */
3809static LWT_ELEMID
3810_lwt_RemEdge( LWT_TOPOLOGY* topo, LWT_ELEMID edge_id, int modFace )
3811{
3812 uint64_t i, nedges, nfaces, fields;
3813 LWT_ISO_EDGE *edge = NULL;
3814 LWT_ISO_EDGE *upd_edge = NULL;
3815 LWT_ISO_EDGE upd_edge_left[2];
3816 int nedge_left = 0;
3817 LWT_ISO_EDGE upd_edge_right[2];
3818 int nedge_right = 0;
3819 LWT_ISO_NODE upd_node[2];
3820 int nnode = 0;
3821 LWT_ISO_FACE *faces = NULL;
3822 LWT_ISO_FACE newface;
3823 LWT_ELEMID node_ids[2];
3824 LWT_ELEMID face_ids[2];
3825 int fnode_edges = 0; /* number of edges on the first node (excluded
3826 * the one being removed ) */
3827 int lnode_edges = 0; /* number of edges on the last node (excluded
3828 * the one being removed ) */
3829
3830 newface.face_id = 0;
3831
3832 i = 1;
3833 edge = lwt_be_getEdgeById(topo, &edge_id, &i, LWT_COL_EDGE_ALL);
3834 if (!edge)
3835 {
3836 LWDEBUGF(1, "lwt_be_getEdgeById returned NULL and set i=%d", i);
3837 if (i == UINT64_MAX)
3838 {
3839 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3840 return -1;
3841 }
3842 else if (i == 0)
3843 {
3844 lwerror("SQL/MM Spatial exception - non-existent edge %" LWTFMT_ELEMID, edge_id);
3845 return -1;
3846 }
3847 else
3848 {
3849 lwerror(
3850 "Backend coding error: getEdgeById callback returned NULL "
3851 "but numelements output parameter has value %d "
3852 "(expected 0 or 1)",
3853 i);
3854 return -1;
3855 }
3856 }
3857
3858 if ( ! lwt_be_checkTopoGeomRemEdge(topo, edge_id,
3859 edge->face_left, edge->face_right) )
3860 {
3862 return -1;
3863 }
3864
3865 LWDEBUG(1, "Updating next_{right,left}_face of ring edges...");
3866
3867 /* Update edge linking */
3868
3869 nedges = 0;
3870 node_ids[nedges++] = edge->start_node;
3871 if ( edge->end_node != edge->start_node )
3872 {
3873 node_ids[nedges++] = edge->end_node;
3874 }
3878 upd_edge = lwt_be_getEdgeByNode( topo, &(node_ids[0]), &nedges, fields );
3879 if (nedges == UINT64_MAX)
3880 {
3881 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3882 return -1;
3883 }
3884 nedge_left = nedge_right = 0;
3885 for ( i=0; i<nedges; ++i )
3886 {
3887 LWT_ISO_EDGE *e = &(upd_edge[i]);
3888 if ( e->edge_id == edge_id ) continue;
3889 if ( e->start_node == edge->start_node || e->end_node == edge->start_node )
3890 {
3891 ++fnode_edges;
3892 }
3893 if ( e->start_node == edge->end_node || e->end_node == edge->end_node )
3894 {
3895 ++lnode_edges;
3896 }
3897 if ( e->next_left == -edge_id )
3898 {
3899 upd_edge_left[nedge_left].edge_id = e->edge_id;
3900 upd_edge_left[nedge_left++].next_left =
3901 edge->next_left != edge_id ? edge->next_left : edge->next_right;
3902 }
3903 else if ( e->next_left == edge_id )
3904 {
3905 upd_edge_left[nedge_left].edge_id = e->edge_id;
3906 upd_edge_left[nedge_left++].next_left =
3907 edge->next_right != -edge_id ? edge->next_right : edge->next_left;
3908 }
3909
3910 if ( e->next_right == -edge_id )
3911 {
3912 upd_edge_right[nedge_right].edge_id = e->edge_id;
3913 upd_edge_right[nedge_right++].next_right =
3914 edge->next_left != edge_id ? edge->next_left : edge->next_right;
3915 }
3916 else if ( e->next_right == edge_id )
3917 {
3918 upd_edge_right[nedge_right].edge_id = e->edge_id;
3919 upd_edge_right[nedge_right++].next_right =
3920 edge->next_right != -edge_id ? edge->next_right : edge->next_left;
3921 }
3922 }
3923
3924 if ( nedge_left )
3925 {
3926 LWDEBUGF(1, "updating %d 'next_left' edges", nedge_left);
3927 /* update edges in upd_edge_left set next_left */
3928 int result = lwt_be_updateEdgesById(topo, &(upd_edge_left[0]), nedge_left, LWT_COL_EDGE_NEXT_LEFT);
3929 if (result == -1)
3930 {
3931 _lwt_release_edges(edge, 1);
3932 lwfree(upd_edge);
3933 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3934 return -1;
3935 }
3936 }
3937 if ( nedge_right )
3938 {
3939 LWDEBUGF(1, "updating %d 'next_right' edges", nedge_right);
3940 /* update edges in upd_edge_right set next_right */
3941 int result = lwt_be_updateEdgesById(topo, &(upd_edge_right[0]), nedge_right, LWT_COL_EDGE_NEXT_RIGHT);
3942 if (result == -1)
3943 {
3944 _lwt_release_edges(edge, 1);
3945 lwfree(upd_edge);
3946 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3947 return -1;
3948 }
3949 }
3950 LWDEBUGF(1, "releasing %d updateable edges in %p", nedges, upd_edge);
3951 lwfree(upd_edge);
3952
3953 /* Id of face that will take up all the space previously
3954 * taken by left and right faces of the edge */
3955 LWT_ELEMID floodface;
3956
3957 /* Find floodface, and update its mbr if != 0 */
3958 if ( edge->face_left == edge->face_right )
3959 {
3960 floodface = edge->face_right;
3961 }
3962 else
3963 {
3964 /* Two faces healed */
3965 if ( edge->face_left == 0 || edge->face_right == 0 )
3966 {
3967 floodface = 0;
3968 LWDEBUG(1, "floodface is universe");
3969 }
3970 else
3971 {
3972 /* we choose right face as the face that will remain
3973 * to be symmetric with ST_AddEdgeModFace */
3974 floodface = edge->face_right;
3975 LWDEBUGF(1, "floodface is %" LWTFMT_ELEMID, floodface);
3976 /* update mbr of floodface as union of mbr of both faces */
3977 face_ids[0] = edge->face_left;
3978 face_ids[1] = edge->face_right;
3979 nfaces = 2;
3980 fields = LWT_COL_FACE_ALL;
3981 faces = lwt_be_getFaceById(topo, face_ids, &nfaces, fields);
3982 if (nfaces == UINT64_MAX)
3983 {
3984 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3985 return -1;
3986 }
3987 GBOX *box1=NULL;
3988 GBOX *box2=NULL;
3989 for ( i=0; i<nfaces; ++i )
3990 {
3991 if ( faces[i].face_id == edge->face_left )
3992 {
3993 if ( ! box1 ) box1 = faces[i].mbr;
3994 else
3995 {
3996 i = edge->face_left;
3997 _lwt_release_edges(edge, 1);
3998 _lwt_release_faces(faces, nfaces);
3999 lwerror("corrupted topology: more than 1 face have face_id=%"
4000 LWTFMT_ELEMID, i);
4001 return -1;
4002 }
4003 }
4004 else if ( faces[i].face_id == edge->face_right )
4005 {
4006 if ( ! box2 ) box2 = faces[i].mbr;
4007 else
4008 {
4009 i = edge->face_right;
4010 _lwt_release_edges(edge, 1);
4011 _lwt_release_faces(faces, nfaces);
4012 lwerror("corrupted topology: more than 1 face have face_id=%"
4013 LWTFMT_ELEMID, i);
4014 return -1;
4015 }
4016 }
4017 else
4018 {
4019 i = faces[i].face_id;
4020 _lwt_release_edges(edge, 1);
4021 _lwt_release_faces(faces, nfaces);
4022 lwerror("Backend coding error: getFaceById returned face "
4023 "with non-requested id %" LWTFMT_ELEMID, i);
4024 return -1;
4025 }
4026 }
4027 if ( ! box1 ) {
4028 i = edge->face_left;
4029 _lwt_release_edges(edge, 1);
4030 _lwt_release_faces(faces, nfaces);
4031 lwerror("corrupted topology: no face have face_id=%"
4032 LWTFMT_ELEMID " (left face for edge %"
4033 LWTFMT_ELEMID ")", i, edge_id);
4034 return -1;
4035 }
4036 if ( ! box2 ) {
4037 i = edge->face_right;
4038 _lwt_release_edges(edge, 1);
4039 _lwt_release_faces(faces, nfaces);
4040 lwerror("corrupted topology: no face have face_id=%"
4041 LWTFMT_ELEMID " (right face for edge %"
4042 LWTFMT_ELEMID ")", i, edge_id);
4043 return -1;
4044 }
4045 gbox_merge(box2, box1); /* box1 is now the union of the two */
4046 newface.mbr = box1;
4047 if ( modFace )
4048 {
4049 newface.face_id = floodface;
4050 int result = lwt_be_updateFacesById(topo, &newface, 1);
4051 _lwt_release_faces(faces, 2);
4052 if (result == -1)
4053 {
4054 _lwt_release_edges(edge, 1);
4055 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4056 return -1;
4057 }
4058 if (result != 1)
4059 {
4060 _lwt_release_edges(edge, 1);
4061 lwerror("Unexpected error: %d faces updated when expecting 1", i);
4062 return -1;
4063 }
4064 }
4065 else
4066 {
4067 /* New face replaces the old two faces */
4068 newface.face_id = -1;
4069 int result = lwt_be_insertFaces(topo, &newface, 1);
4070 _lwt_release_faces(faces, 2);
4071 if (result == -1)
4072 {
4073 _lwt_release_edges(edge, 1);
4074 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4075 return -1;
4076 }
4077 if (result != 1)
4078 {
4079 _lwt_release_edges(edge, 1);
4080 lwerror("Unexpected error: %d faces inserted when expecting 1", result);
4081 return -1;
4082 }
4083 floodface = newface.face_id;
4084 }
4085 }
4086
4087 /* Update face references for edges and nodes still referencing
4088 * the removed face(s) */
4089
4090 if ( edge->face_left != floodface )
4091 {
4092 if ( -1 == _lwt_UpdateEdgeFaceRef(topo, edge->face_left, floodface) )
4093 {
4094 _lwt_release_edges(edge, 1);
4095 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4096 return -1;
4097 }
4098 if ( -1 == _lwt_UpdateNodeFaceRef(topo, edge->face_left, floodface) )
4099 {
4100 _lwt_release_edges(edge, 1);
4101 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4102 return -1;
4103 }
4104 }
4105
4106 if ( edge->face_right != floodface )
4107 {
4108 if ( -1 == _lwt_UpdateEdgeFaceRef(topo, edge->face_right, floodface) )
4109 {
4110 _lwt_release_edges(edge, 1);
4111 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4112 return -1;
4113 }
4114 if ( -1 == _lwt_UpdateNodeFaceRef(topo, edge->face_right, floodface) )
4115 {
4116 _lwt_release_edges(edge, 1);
4117 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4118 return -1;
4119 }
4120 }
4121
4122 /* Update topogeoms on heal */
4124 edge->face_right, edge->face_left,
4125 floodface) )
4126 {
4127 _lwt_release_edges(edge, 1);
4129 return -1;
4130 }
4131 } /* two faces healed */
4132
4133 /* Delete the edge */
4134 int result = lwt_be_deleteEdges(topo, edge, LWT_COL_EDGE_EDGE_ID);
4135 if (result == -1)
4136 {
4137 _lwt_release_edges(edge, 1);
4138 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4139 return -1;
4140 }
4141
4142 /* If any of the edge nodes remained isolated, set
4143 * containing_face = floodface
4144 */
4145 if ( ! fnode_edges )
4146 {
4147 upd_node[nnode].node_id = edge->start_node;
4148 upd_node[nnode].containing_face = floodface;
4149 ++nnode;
4150 }
4151 if ( edge->end_node != edge->start_node && ! lnode_edges )
4152 {
4153 upd_node[nnode].node_id = edge->end_node;
4154 upd_node[nnode].containing_face = floodface;
4155 ++nnode;
4156 }
4157 if ( nnode )
4158 {
4159 int result = lwt_be_updateNodesById(topo, upd_node, nnode, LWT_COL_NODE_CONTAINING_FACE);
4160 if (result == -1)
4161 {
4162 _lwt_release_edges(edge, 1);
4163 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4164 return -1;
4165 }
4166 }
4167
4168 if ( edge->face_left != edge->face_right )
4169 /* or there'd be no face to remove */
4170 {
4171 LWT_ELEMID ids[2];
4172 int nids = 0;
4173 if ( edge->face_right != floodface )
4174 ids[nids++] = edge->face_right;
4175 if ( edge->face_left != floodface )
4176 ids[nids++] = edge->face_left;
4177 int result = lwt_be_deleteFacesById(topo, ids, nids);
4178 if (result == -1)
4179 {
4180 _lwt_release_edges(edge, 1);
4181 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4182 return -1;
4183 }
4184 }
4185
4186 _lwt_release_edges(edge, 1);
4187 return modFace ? floodface : newface.face_id;
4188}
4189
4192{
4193 return _lwt_RemEdge( topo, edge_id, 1 );
4194}
4195
4198{
4199 return _lwt_RemEdge( topo, edge_id, 0 );
4200}
4201
4202static LWT_ELEMID
4204 int modEdge )
4205{
4206 LWT_ELEMID ids[2];
4207 LWT_ELEMID commonnode = -1;
4208 int caseno = 0;
4209 LWT_ISO_EDGE *node_edges;
4210 uint64_t num_node_edges;
4211 LWT_ISO_EDGE *edges;
4212 LWT_ISO_EDGE *e1 = NULL;
4213 LWT_ISO_EDGE *e2 = NULL;
4214 LWT_ISO_EDGE newedge, updedge, seledge;
4215 uint64_t nedges, i;
4216 int e1freenode;
4217 int e2sign, e2freenode;
4218 POINTARRAY *pa;
4219 char buf[256];
4220 char *ptr;
4221 size_t bufleft = 256;
4222
4223 ptr = buf;
4224
4225 /* NOT IN THE SPECS: see if the same edge is given twice.. */
4226 if ( eid1 == eid2 )
4227 {
4228 lwerror("Cannot heal edge %" LWTFMT_ELEMID
4229 " with itself, try with another", eid1);
4230 return -1;
4231 }
4232 ids[0] = eid1;
4233 ids[1] = eid2;
4234 nedges = 2;
4235 edges = lwt_be_getEdgeById(topo, ids, &nedges, LWT_COL_EDGE_ALL);
4236 if ((nedges == UINT64_MAX) || (edges == NULL))
4237 {
4238 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4239 return -1;
4240 }
4241 for ( i=0; i<nedges; ++i )
4242 {
4243 if ( edges[i].edge_id == eid1 ) {
4244 if ( e1 ) {
4245 _lwt_release_edges(edges, nedges);
4246 lwerror("Corrupted topology: multiple edges have id %"
4247 LWTFMT_ELEMID, eid1);
4248 return -1;
4249 }
4250 e1 = &(edges[i]);
4251 }
4252 else if ( edges[i].edge_id == eid2 ) {
4253 if ( e2 ) {
4254 _lwt_release_edges(edges, nedges);
4255 lwerror("Corrupted topology: multiple edges have id %"
4256 LWTFMT_ELEMID, eid2);
4257 return -1;
4258 }
4259 e2 = &(edges[i]);
4260 }
4261 }
4262 if ( ! e1 )
4263 {
4264 _lwt_release_edges(edges, nedges);
4265 lwerror(
4266 "SQL/MM Spatial exception - non-existent edge %" LWTFMT_ELEMID,
4267 eid1);
4268 return -1;
4269 }
4270 if ( ! e2 )
4271 {
4272 _lwt_release_edges(edges, nedges);
4273 lwerror(
4274 "SQL/MM Spatial exception - non-existent edge %" LWTFMT_ELEMID,
4275 eid2);
4276 return -1;
4277 }
4278
4279 /* NOT IN THE SPECS: See if any of the two edges are closed. */
4280 if ( e1->start_node == e1->end_node )
4281 {
4282 _lwt_release_edges(edges, nedges);
4283 lwerror("Edge %" LWTFMT_ELEMID " is closed, cannot heal to edge %"
4284 LWTFMT_ELEMID, eid1, eid2);
4285 return -1;
4286 }
4287 if ( e2->start_node == e2->end_node )
4288 {
4289 _lwt_release_edges(edges, nedges);
4290 lwerror("Edge %" LWTFMT_ELEMID " is closed, cannot heal to edge %"
4291 LWTFMT_ELEMID, eid2, eid1);
4292 return -1;
4293 }
4294
4295 /* Find common node */
4296
4297 if ( e1->end_node == e2->start_node )
4298 {
4299 commonnode = e1->end_node;
4300 caseno = 1;
4301 }
4302 else if ( e1->end_node == e2->end_node )
4303 {
4304 commonnode = e1->end_node;
4305 caseno = 2;
4306 }
4307 /* Check if any other edge is connected to the common node, if found */
4308 if ( commonnode != -1 )
4309 {
4310 num_node_edges = 1;
4311 node_edges = lwt_be_getEdgeByNode( topo, &commonnode,
4312 &num_node_edges, LWT_COL_EDGE_EDGE_ID );
4313 if (num_node_edges == UINT64_MAX)
4314 {
4315 _lwt_release_edges(edges, nedges);
4316 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4317 return -1;
4318 }
4319 for (i=0; i<num_node_edges; ++i)
4320 {
4321 int r;
4322 if ( node_edges[i].edge_id == eid1 ) continue;
4323 if ( node_edges[i].edge_id == eid2 ) continue;
4324 commonnode = -1;
4325 /* append to string, for error message */
4326 if ( bufleft > 0 ) {
4327 r = snprintf(ptr, bufleft, "%s%" LWTFMT_ELEMID,
4328 ( ptr==buf ? "" : "," ), node_edges[i].edge_id);
4329 if ( r >= (int) bufleft )
4330 {
4331 bufleft = 0;
4332 buf[252] = '.';
4333 buf[253] = '.';
4334 buf[254] = '.';
4335 buf[255] = '\0';
4336 }
4337 else
4338 {
4339 bufleft -= r;
4340 ptr += r;
4341 }
4342 }
4343 }
4344 lwfree(node_edges);
4345 }
4346
4347 if ( commonnode == -1 )
4348 {
4349 if ( e1->start_node == e2->start_node )
4350 {
4351 commonnode = e1->start_node;
4352 caseno = 3;
4353 }
4354 else if ( e1->start_node == e2->end_node )
4355 {
4356 commonnode = e1->start_node;
4357 caseno = 4;
4358 }
4359 /* Check if any other edge is connected to the common node, if found */
4360 if ( commonnode != -1 )
4361 {
4362 num_node_edges = 1;
4363 node_edges = lwt_be_getEdgeByNode( topo, &commonnode,
4364 &num_node_edges, LWT_COL_EDGE_EDGE_ID );
4365 if (num_node_edges == UINT64_MAX)
4366 {
4367 _lwt_release_edges(edges, nedges);
4368 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4369 return -1;
4370 }
4371 for (i=0; i<num_node_edges; ++i)
4372 {
4373 int r;
4374 if ( node_edges[i].edge_id == eid1 ) continue;
4375 if ( node_edges[i].edge_id == eid2 ) continue;
4376 commonnode = -1;
4377 /* append to string, for error message */
4378 if ( bufleft > 0 ) {
4379 r = snprintf(ptr, bufleft, "%s%" LWTFMT_ELEMID,
4380 ( ptr==buf ? "" : "," ), node_edges[i].edge_id);
4381 if ( r >= (int) bufleft )
4382 {
4383 bufleft = 0;
4384 buf[252] = '.';
4385 buf[253] = '.';
4386 buf[254] = '.';
4387 buf[255] = '\0';
4388 }
4389 else
4390 {
4391 bufleft -= r;
4392 ptr += r;
4393 }
4394 }
4395 }
4396 if ( num_node_edges ) lwfree(node_edges);
4397 }
4398 }
4399
4400 if ( commonnode == -1 )
4401 {
4402 _lwt_release_edges(edges, nedges);
4403 if ( ptr != buf )
4404 {
4405 lwerror("SQL/MM Spatial exception - other edges connected (%s)",
4406 buf);
4407 }
4408 else
4409 {
4410 lwerror("SQL/MM Spatial exception - non-connected edges");
4411 }
4412 return -1;
4413 }
4414
4415 if ( ! lwt_be_checkTopoGeomRemNode(topo, commonnode,
4416 eid1, eid2 ) )
4417 {
4418 _lwt_release_edges(edges, nedges);
4420 return -1;
4421 }
4422
4423 /* Construct the geometry of the new edge */
4424 switch (caseno)
4425 {
4426 case 1: /* e1.end = e2.start */
4427 pa = ptarray_clone_deep(e1->geom->points);
4428 //pa = ptarray_merge(pa, e2->geom->points);
4429 ptarray_append_ptarray(pa, e2->geom->points, 0);
4430 newedge.start_node = e1->start_node;
4431 newedge.end_node = e2->end_node;
4432 newedge.next_left = e2->next_left;
4433 newedge.next_right = e1->next_right;
4434 e1freenode = 1;
4435 e2freenode = -1;
4436 e2sign = 1;
4437 break;
4438 case 2: /* e1.end = e2.end */
4439 {
4440 POINTARRAY *pa2;
4441 pa2 = ptarray_clone_deep(e2->geom->points);
4443 pa = ptarray_clone_deep(e1->geom->points);
4444 //pa = ptarray_merge(e1->geom->points, pa);
4445 ptarray_append_ptarray(pa, pa2, 0);
4446 ptarray_free(pa2);
4447 newedge.start_node = e1->start_node;
4448 newedge.end_node = e2->start_node;
4449 newedge.next_left = e2->next_right;
4450 newedge.next_right = e1->next_right;
4451 e1freenode = 1;
4452 e2freenode = 1;
4453 e2sign = -1;
4454 break;
4455 }
4456 case 3: /* e1.start = e2.start */
4457 pa = ptarray_clone_deep(e2->geom->points);
4459 //pa = ptarray_merge(pa, e1->geom->points);
4460 ptarray_append_ptarray(pa, e1->geom->points, 0);
4461 newedge.end_node = e1->end_node;
4462 newedge.start_node = e2->end_node;
4463 newedge.next_left = e1->next_left;
4464 newedge.next_right = e2->next_left;
4465 e1freenode = -1;
4466 e2freenode = -1;
4467 e2sign = -1;
4468 break;
4469 case 4: /* e1.start = e2.end */
4470 pa = ptarray_clone_deep(e2->geom->points);
4471 //pa = ptarray_merge(pa, e1->geom->points);
4472 ptarray_append_ptarray(pa, e1->geom->points, 0);
4473 newedge.end_node = e1->end_node;
4474 newedge.start_node = e2->start_node;
4475 newedge.next_left = e1->next_left;
4476 newedge.next_right = e2->next_right;
4477 e1freenode = -1;
4478 e2freenode = 1;
4479 e2sign = 1;
4480 break;
4481 default:
4482 pa = NULL;
4483 e1freenode = 0;
4484 e2freenode = 0;
4485 e2sign = 0;
4486 _lwt_release_edges(edges, nedges);
4487 lwerror("Coding error: caseno=%d should never happen", caseno);
4488 break;
4489 }
4490 newedge.geom = lwline_construct(topo->srid, NULL, pa);
4491
4492 if ( modEdge )
4493 {
4494 /* Update data of the first edge */
4495 newedge.edge_id = eid1;
4496 int result = lwt_be_updateEdgesById(topo,
4497 &newedge,
4498 1,
4501 if (result == -1)
4502 {
4503 lwline_free(newedge.geom);
4504 _lwt_release_edges(edges, nedges);
4505 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4506 return -1;
4507 }
4508 else if (result != 1)
4509 {
4510 lwline_free(newedge.geom);
4511 if ( edges ) _lwt_release_edges(edges, nedges);
4512 lwerror("Unexpected error: %d edges updated when expecting 1", i);
4513 return -1;
4514 }
4515 }
4516 else
4517 {
4518 /* Add new edge */
4519 newedge.edge_id = -1;
4520 newedge.face_left = e1->face_left;
4521 newedge.face_right = e1->face_right;
4522 int result = lwt_be_insertEdges(topo, &newedge, 1);
4523 if (result == -1)
4524 {
4525 lwline_free(newedge.geom);
4526 _lwt_release_edges(edges, nedges);
4527 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4528 return -1;
4529 }
4530 else if (result == 0)
4531 {
4532 lwline_free(newedge.geom);
4533 _lwt_release_edges(edges, nedges);
4534 lwerror("Insertion of split edge failed (no reason)");
4535 return -1;
4536 }
4537 }
4538 lwline_free(newedge.geom);
4539
4540 /*
4541 -- Update next_left_edge/next_right_edge for
4542 -- any edge having them still pointing at the edge being removed
4543 -- (eid2 only when modEdge, or both otherwise)
4544 --
4545 -- NOTE:
4546 -- e#freenode is 1 when edge# end node was the common node
4547 -- and -1 otherwise. This gives the sign of possibly found references
4548 -- to its "free" (non connected to other edge) endnode.
4549 -- e2sign is -1 if edge1 direction is opposite to edge2 direction,
4550 -- or 1 otherwise.
4551 --
4552 */
4553
4554 /* update edges connected to e2's boundary from their end node */
4555 seledge.next_left = e2freenode * eid2;
4556 updedge.next_left = e2freenode * newedge.edge_id * e2sign;
4557 int result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_LEFT, &updedge, LWT_COL_EDGE_NEXT_LEFT, NULL, 0);
4558 if (result == -1)
4559 {
4560 _lwt_release_edges(edges, nedges);
4561 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4562 return -1;
4563 }
4564
4565 /* update edges connected to e2's boundary from their start node */
4566 seledge.next_right = e2freenode * eid2;
4567 updedge.next_right = e2freenode * newedge.edge_id * e2sign;
4568 result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_RIGHT, &updedge, LWT_COL_EDGE_NEXT_RIGHT, NULL, 0);
4569 if (result == -1)
4570 {
4571 _lwt_release_edges(edges, nedges);
4572 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4573 return -1;
4574 }
4575
4576 if ( ! modEdge )
4577 {
4578 /* update edges connected to e1's boundary from their end node */
4579 seledge.next_left = e1freenode * eid1;
4580 updedge.next_left = e1freenode * newedge.edge_id;
4581 result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_LEFT, &updedge, LWT_COL_EDGE_NEXT_LEFT, NULL, 0);
4582 if (result == -1)
4583 {
4584 _lwt_release_edges(edges, nedges);
4585 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4586 return -1;
4587 }
4588
4589 /* update edges connected to e1's boundary from their start node */
4590 seledge.next_right = e1freenode * eid1;
4591 updedge.next_right = e1freenode * newedge.edge_id;
4592 result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_RIGHT, &updedge, LWT_COL_EDGE_NEXT_RIGHT, NULL, 0);
4593 if (result == -1)
4594 {
4595 _lwt_release_edges(edges, nedges);
4596 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4597 return -1;
4598 }
4599 }
4600
4601 /* delete the edges (only second on modEdge or both) */
4602 result = lwt_be_deleteEdges(topo, e2, LWT_COL_EDGE_EDGE_ID);
4603 if (result == -1)
4604 {
4605 _lwt_release_edges(edges, nedges);
4606 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4607 return -1;
4608 }
4609 if ( ! modEdge ) {
4611 if (result == -1)
4612 {
4613 _lwt_release_edges(edges, nedges);
4614 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4615 return -1;
4616 }
4617 }
4618
4619 _lwt_release_edges(edges, nedges);
4620
4621 /* delete the common node */
4622 i = lwt_be_deleteNodesById( topo, &commonnode, 1 );
4623 if (result == -1)
4624 {
4625 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4626 return -1;
4627 }
4628
4629 /*
4630 --
4631 -- NOT IN THE SPECS:
4632 -- Drop composition rows involving second
4633 -- edge, as the first edge took its space,
4634 -- and all affected TopoGeom have been previously checked
4635 -- for being composed by both edges.
4636 */
4638 eid1, eid2, newedge.edge_id) )
4639 {
4641 return -1;
4642 }
4643
4644 return modEdge ? commonnode : newedge.edge_id;
4645}
4646
4649{
4650 return _lwt_HealEdges( topo, e1, e2, 1 );
4651}
4652
4655{
4656 return _lwt_HealEdges( topo, e1, e2, 0 );
4657}
4658
4661{
4662 LWT_ISO_NODE *elem;
4663 uint64_t num;
4664 int flds = LWT_COL_NODE_NODE_ID|LWT_COL_NODE_GEOM; /* geom not needed */
4665 LWT_ELEMID id = 0;
4666 POINT2D qp; /* query point */
4667
4668 if ( ! getPoint2d_p(pt->point, 0, &qp) )
4669 {
4670 lwerror("Empty query point");
4671 return -1;
4672 }
4673 elem = lwt_be_getNodeWithinDistance2D(topo, pt, tol, &num, flds, 0);
4674 if (num == UINT64_MAX)
4675 {
4676 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4677 return -1;
4678 }
4679 else if ( num )
4680 {
4681 if ( num > 1 )
4682 {
4683 _lwt_release_nodes(elem, num);
4684 lwerror("Two or more nodes found");
4685 return -1;
4686 }
4687 id = elem[0].node_id;
4688 _lwt_release_nodes(elem, num);
4689 }
4690
4691 return id;
4692}
4693
4696{
4697 LWT_ISO_EDGE *elem;
4698 uint64_t num, i;
4699 int flds = LWT_COL_EDGE_EDGE_ID|LWT_COL_EDGE_GEOM; /* GEOM is not needed */
4700 LWT_ELEMID id = 0;
4701 LWGEOM *qp = lwpoint_as_lwgeom(pt); /* query point */
4702
4703 if ( lwgeom_is_empty(qp) )
4704 {
4705 lwerror("Empty query point");
4706 return -1;
4707 }
4708 elem = lwt_be_getEdgeWithinDistance2D(topo, pt, tol, &num, flds, 0);
4709 if (num == UINT64_MAX)
4710 {
4711 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4712 return -1;
4713 }
4714 for (i=0; i<num;++i)
4715 {
4716 LWT_ISO_EDGE *e = &(elem[i]);
4717#if 0
4718 LWGEOM* geom;
4719 double dist;
4720
4721 if ( ! e->geom )
4722 {
4723 _lwt_release_edges(elem, num);
4724 lwnotice("Corrupted topology: edge %" LWTFMT_ELEMID
4725 " has null geometry", e->edge_id);
4726 continue;
4727 }
4728
4729 /* Should we check for intersection not being on an endpoint
4730 * as documented ? */
4731 geom = lwline_as_lwgeom(e->geom);
4732 dist = lwgeom_mindistance2d_tolerance(geom, qp, tol);
4733 if ( dist > tol ) continue;
4734#endif
4735
4736 if ( id )
4737 {
4738 _lwt_release_edges(elem, num);
4739 lwerror("Two or more edges found");
4740 return -1;
4741 }
4742 else id = e->edge_id;
4743 }
4744
4745 if ( num ) _lwt_release_edges(elem, num);
4746
4747 return id;
4748}
4749
4752{
4753 LWT_ELEMID id = 0;
4754 LWT_ISO_EDGE *elem;
4755 uint64_t num, i;
4756 int flds = LWT_COL_EDGE_EDGE_ID |
4760 LWGEOM *qp = lwpoint_as_lwgeom(pt);
4761
4762 id = lwt_be_getFaceContainingPoint(topo, pt);
4763 if ( id == -2 ) {
4764 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4765 return -1;
4766 }
4767
4768 if ( id > 0 )
4769 {
4770 return id;
4771 }
4772 id = 0; /* or it'll be -1 for not found */
4773
4774 LWDEBUG(1, "No face properly contains query point,"
4775 " looking for edges");
4776
4777 /* Not in a face, may be in universe or on edge, let's check
4778 * for distance */
4779 /* NOTE: we never pass a tolerance of 0 to avoid ever using
4780 * ST_Within, which doesn't include endpoints matches */
4781 elem = lwt_be_getEdgeWithinDistance2D(topo, pt, tol?tol:1e-5, &num, flds, 0);
4782 if (num == UINT64_MAX)
4783 {
4784 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4785 return -1;
4786 }
4787 for (i=0; i<num; ++i)
4788 {
4789 LWT_ISO_EDGE *e = &(elem[i]);
4790 LWT_ELEMID eface = 0;
4791 LWGEOM* geom;
4792 double dist;
4793
4794 if ( ! e->geom )
4795 {
4796 _lwt_release_edges(elem, num);
4797 lwnotice("Corrupted topology: edge %" LWTFMT_ELEMID
4798 " has null geometry", e->edge_id);
4799 continue;
4800 }
4801
4802 /* don't consider dangling edges */
4803 if ( e->face_left == e->face_right )
4804 {
4805 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID
4806 " is dangling, won't consider it", e->edge_id);
4807 continue;
4808 }
4809
4810 geom = lwline_as_lwgeom(e->geom);
4811 dist = lwgeom_mindistance2d_tolerance(geom, qp, tol);
4812
4813 LWDEBUGF(1, "Distance from edge %" LWTFMT_ELEMID
4814 " is %g (tol=%g)", e->edge_id, dist, tol);
4815
4816 /* we won't consider edges too far */
4817 if ( dist > tol ) continue;
4818 if ( e->face_left == 0 ) {
4819 eface = e->face_right;
4820 }
4821 else if ( e->face_right == 0 ) {
4822 eface = e->face_left;
4823 }
4824 else {
4825 _lwt_release_edges(elem, num);
4826 lwerror("Two or more faces found");
4827 return -1;
4828 }
4829
4830 if ( id && id != eface )
4831 {
4832 _lwt_release_edges(elem, num);
4833 lwerror("Two or more faces found"
4834#if 0 /* debugging */
4835 " (%" LWTFMT_ELEMID
4836 " and %" LWTFMT_ELEMID ")", id, eface
4837#endif
4838 );
4839 return -1;
4840 }
4841 else id = eface;
4842 }
4843 if ( num ) _lwt_release_edges(elem, num);
4844
4845 return id;
4846}
4847
4848/* Return the smallest delta that can perturbate
4849 * the given value */
4850static inline double
4852{
4853 double ret = 3.6 * pow(10, - ( 15 - log10(d?d:1.0) ) );
4854 return ret;
4855}
4856
4857/* Return the smallest delta that can perturbate
4858 * the given point
4859static inline double
4860_lwt_minTolerancePoint2d( const POINT2D* p )
4861{
4862 double max = FP_ABS(p->x);
4863 if ( max < FP_ABS(p->y) ) max = FP_ABS(p->y);
4864 return _lwt_minToleranceDouble(max);
4865}
4866*/
4867
4868/* Return the smallest delta that can perturbate
4869 * the maximum absolute value of a geometry ordinate
4870 */
4871static double
4873{
4874 const GBOX* gbox;
4875 double max;
4876 double ret;
4877
4878 gbox = lwgeom_get_bbox(g);
4879 if ( ! gbox ) return 0; /* empty */
4880 max = FP_ABS(gbox->xmin);
4881 if ( max < FP_ABS(gbox->xmax) ) max = FP_ABS(gbox->xmax);
4882 if ( max < FP_ABS(gbox->ymin) ) max = FP_ABS(gbox->ymin);
4883 if ( max < FP_ABS(gbox->ymax) ) max = FP_ABS(gbox->ymax);
4884
4885 ret = _lwt_minToleranceDouble(max);
4886
4887 return ret;
4888}
4889
4890#define _LWT_MINTOLERANCE( topo, geom ) ( \
4891 topo->precision ? topo->precision : _lwt_minTolerance(geom) )
4892
4897
4898static int
4899compare_scored_pointer(const void *si1, const void *si2)
4900{
4901 double a = ((scored_pointer *)si1)->score;
4902 double b = ((scored_pointer *)si2)->score;
4903 if ( a < b )
4904 return -1;
4905 else if ( a > b )
4906 return 1;
4907 else
4908 return 0;
4909}
4910
4911/*
4912 * @param findFace if non-zero the code will determine which face
4913 * contains the given point (unless it is known to be NOT
4914 * isolated)
4915 * @param moved if not-null will be set to 0 if the point was added
4916 * w/out any snapping or 1 otherwise.
4917 */
4918static LWT_ELEMID
4919_lwt_AddPoint(LWT_TOPOLOGY* topo, LWPOINT* point, double tol, int
4920 findFace, int *moved)
4921{
4922 uint64_t num, i;
4923 double mindist = FLT_MAX;
4924 LWT_ISO_NODE *nodes, *nodes2;
4925 LWT_ISO_EDGE *edges, *edges2;
4926 LWGEOM *pt = lwpoint_as_lwgeom(point);
4927 int flds;
4928 LWT_ELEMID id = 0;
4929 scored_pointer *sorted;
4930
4931 /* Get tolerance, if 0 was given */
4932 if (!tol)
4933 tol = _LWT_MINTOLERANCE(topo, pt);
4934
4935 LWDEBUGG(1, pt, "Adding point");
4936
4937 /*
4938 -- 1. Check if any existing node is closer than the given precision
4939 -- and if so pick the closest
4940 TODO: use WithinBox2D
4941 */
4943 nodes = lwt_be_getNodeWithinDistance2D(topo, point, tol, &num, flds, 0);
4944 if (num == UINT64_MAX)
4945 {
4946 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4947 return -1;
4948 }
4949 if ( num )
4950 {
4951 LWDEBUGF(1, "New point is within %.15g units of %d nodes", tol, num);
4952 /* Order by distance if there are more than a single return */
4953 if ( num > 1 )
4954 {{
4955 sorted= lwalloc(sizeof(scored_pointer)*num);
4956 for (i=0; i<num; ++i)
4957 {
4958 sorted[i].ptr = nodes+i;
4959 sorted[i].score = lwgeom_mindistance2d(lwpoint_as_lwgeom(nodes[i].geom), pt);
4960 LWDEBUGF(1, "Node %" LWTFMT_ELEMID " distance: %.15g",
4961 ((LWT_ISO_NODE*)(sorted[i].ptr))->node_id, sorted[i].score);
4962 }
4963 qsort(sorted, num, sizeof(scored_pointer), compare_scored_pointer);
4964 nodes2 = lwalloc(sizeof(LWT_ISO_NODE)*num);
4965 for (i=0; i<num; ++i)
4966 {
4967 nodes2[i] = *((LWT_ISO_NODE*)sorted[i].ptr);
4968 }
4969 lwfree(sorted);
4970 lwfree(nodes);
4971 nodes = nodes2;
4972 }}
4973
4974 for ( i=0; i<num; ++i )
4975 {
4976 LWT_ISO_NODE *n = &(nodes[i]);
4977 LWGEOM *g = lwpoint_as_lwgeom(n->geom);
4978 double dist = lwgeom_mindistance2d(g, pt);
4979 /* TODO: move this check in the previous sort scan ... */
4980 /* must be closer than tolerated, unless distance is zero */
4981 if ( dist && dist >= tol ) continue;
4982 if ( ! id || dist < mindist )
4983 {
4984 id = n->node_id;
4985 mindist = dist;
4986 }
4987 }
4988 if ( id )
4989 {
4990 /* found an existing node */
4991 if ( nodes ) _lwt_release_nodes(nodes, num);
4992 if ( moved ) *moved = mindist == 0 ? 0 : 1;
4993 return id;
4994 }
4995 }
4996
4997 initGEOS(lwnotice, lwgeom_geos_error);
4998
4999 /*
5000 -- 2. Check if any existing edge falls within tolerance
5001 -- and if so split it by a point projected on it
5002 TODO: use WithinBox2D
5003 */
5005 edges = lwt_be_getEdgeWithinDistance2D(topo, point, tol, &num, flds, 0);
5006 if (num == UINT64_MAX)
5007 {
5008 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5009 return -1;
5010 }
5011 if ( num )
5012 {
5013 LWDEBUGF(1, "New point is within %.15g units of %d edges", tol, num);
5014
5015 /* Order by distance if there are more than a single return */
5016 if ( num > 1 )
5017 {{
5018 int j;
5019 sorted = lwalloc(sizeof(scored_pointer)*num);
5020 for (i=0; i<num; ++i)
5021 {
5022 sorted[i].ptr = edges+i;
5023 sorted[i].score = lwgeom_mindistance2d(lwline_as_lwgeom(edges[i].geom), pt);
5024 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " distance: %.15g",
5025 ((LWT_ISO_EDGE*)(sorted[i].ptr))->edge_id, sorted[i].score);
5026 }
5027 qsort(sorted, num, sizeof(scored_pointer), compare_scored_pointer);
5028 edges2 = lwalloc(sizeof(LWT_ISO_EDGE)*num);
5029 for (j=0, i=0; i<num; ++i)
5030 {
5031 if ( sorted[i].score == sorted[0].score )
5032 {
5033 edges2[j++] = *((LWT_ISO_EDGE*)sorted[i].ptr);
5034 }
5035 else
5036 {
5037 lwline_free(((LWT_ISO_EDGE*)sorted[i].ptr)->geom);
5038 }
5039 }
5040 num = j;
5041 lwfree(sorted);
5042 lwfree(edges);
5043 edges = edges2;
5044 }}
5045
5046 for (i=0; i<num; ++i)
5047 {
5048 /* The point is on or near an edge, split the edge */
5049 LWT_ISO_EDGE *e = &(edges[i]);
5050 LWGEOM *g = lwline_as_lwgeom(e->geom);
5051 LWGEOM *prj;
5052 int contains;
5053 LWT_ELEMID edge_id = e->edge_id;
5054
5055 LWDEBUGF(1, "Splitting edge %" LWTFMT_ELEMID, edge_id);
5056
5057 /* project point to line, split edge by point */
5058 prj = lwgeom_closest_point(g, pt);
5059 if ( moved ) *moved = lwgeom_same(prj,pt) ? 0 : 1;
5060 if ( lwgeom_has_z(pt) )
5061 {{
5062 /*
5063 -- This is a workaround for ClosestPoint lack of Z support:
5064 -- http://trac.osgeo.org/postgis/ticket/2033
5065 */
5066 LWGEOM *tmp;
5067 double z;
5068 POINT4D p4d;
5069 LWPOINT *prjpt;
5070 /* add Z to "prj" */
5071 tmp = lwgeom_force_3dz(prj);
5072 prjpt = lwgeom_as_lwpoint(tmp);
5073 getPoint4d_p(point->point, 0, &p4d);
5074 z = p4d.z;
5075 getPoint4d_p(prjpt->point, 0, &p4d);
5076 p4d.z = z;
5077 ptarray_set_point4d(prjpt->point, 0, &p4d);
5078 lwgeom_free(prj);
5079 prj = tmp;
5080 }}
5081 const POINT2D *pt = getPoint2d_cp(lwgeom_as_lwpoint(prj)->point, 0);
5083 if ( ! contains )
5084 {{
5085 double snaptol;
5086 LWGEOM *snapedge;
5087 LWLINE *snapline;
5088 POINT4D p1, p2;
5089
5090 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID
5091 " does not contain projected point to it",
5092 edge_id);
5093
5094 /* In order to reduce the robustness issues, we'll pick
5095 * an edge that contains the projected point, if possible */
5096 if ( i+1 < num )
5097 {
5098 LWDEBUG(1, "But there's another to check");
5099 lwgeom_free(prj);
5100 continue;
5101 }
5102
5103 /*
5104 -- The tolerance must be big enough for snapping to happen
5105 -- and small enough to snap only to the projected point.
5106 -- Unfortunately ST_Distance returns 0 because it also uses
5107 -- a projected point internally, so we need another way.
5108 */
5109 snaptol = _lwt_minTolerance(prj);
5110 snapedge = _lwt_toposnap(g, prj, snaptol);
5111 snapline = lwgeom_as_lwline(snapedge);
5112
5113 LWDEBUGF(1, "Edge snapped with tolerance %g", snaptol);
5114
5115 /* TODO: check if snapping did anything ? */
5116#if POSTGIS_DEBUG_LEVEL > 0
5117 {
5118 size_t sz;
5119 char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
5120 char *wkt2 = lwgeom_to_wkt(snapedge, WKT_EXTENDED, 15, &sz);
5121 LWDEBUGF(1, "Edge %s snapped became %s", wkt1, wkt2);
5122 lwfree(wkt1);
5123 lwfree(wkt2);
5124 }
5125#endif
5126
5127
5128 /*
5129 -- Snapping currently snaps the first point below tolerance
5130 -- so may possibly move first point. See ticket #1631
5131 */
5132 getPoint4d_p(e->geom->points, 0, &p1);
5133 getPoint4d_p(snapline->points, 0, &p2);
5134 LWDEBUGF(1, "Edge first point is %g %g, "
5135 "snapline first point is %g %g",
5136 p1.x, p1.y, p2.x, p2.y);
5137 if ( p1.x != p2.x || p1.y != p2.y )
5138 {
5139 LWDEBUG(1, "Snapping moved first point, re-adding it");
5140 if ( LW_SUCCESS != ptarray_insert_point(snapline->points, &p1, 0) )
5141 {
5142 lwgeom_free(prj);
5143 lwgeom_free(snapedge);
5144 _lwt_release_edges(edges, num);
5145 lwerror("GEOS exception on Contains: %s", lwgeom_geos_errmsg);
5146 return -1;
5147 }
5148#if POSTGIS_DEBUG_LEVEL > 0
5149 {
5150 size_t sz;
5151 char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
5152 LWDEBUGF(1, "Tweaked snapline became %s", wkt1);
5153 lwfree(wkt1);
5154 }
5155#endif
5156 }
5157#if POSTGIS_DEBUG_LEVEL > 0
5158 else {
5159 LWDEBUG(1, "Snapping did not move first point");
5160 }
5161#endif
5162
5163 if ( -1 == lwt_ChangeEdgeGeom( topo, edge_id, snapline ) )
5164 {
5165 /* TODO: should have invoked lwerror already, leaking memory */
5166 lwgeom_free(prj);
5167 lwgeom_free(snapedge);
5168 _lwt_release_edges(edges, num);
5169 lwerror("lwt_ChangeEdgeGeom failed");
5170 return -1;
5171 }
5172 lwgeom_free(snapedge);
5173 }}
5174#if POSTGIS_DEBUG_LEVEL > 0
5175 else
5176 {{
5177 size_t sz;
5178 char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
5179 char *wkt2 = lwgeom_to_wkt(prj, WKT_EXTENDED, 15, &sz);
5180 LWDEBUGF(1, "Edge %s contains projected point %s", wkt1, wkt2);
5181 lwfree(wkt1);
5182 lwfree(wkt2);
5183 }}
5184#endif
5185
5186 /* TODO: pass 1 as last argument (skipChecks) ? */
5187 id = lwt_ModEdgeSplit( topo, edge_id, lwgeom_as_lwpoint(prj), 0 );
5188 if ( -1 == id )
5189 {
5190 /* TODO: should have invoked lwerror already, leaking memory */
5191 lwgeom_free(prj);
5192 _lwt_release_edges(edges, num);
5193 lwerror("lwt_ModEdgeSplit failed");
5194 return -1;
5195 }
5196
5197 lwgeom_free(prj);
5198
5199 /*
5200 * TODO: decimate the two new edges with the given tolerance ?
5201 *
5202 * the edge identifiers to decimate would be: edge_id and "id"
5203 * The problem here is that decimation of existing edges
5204 * may introduce intersections or topological inconsistencies,
5205 * for example:
5206 *
5207 * - A node may end up falling on the other side of the edge
5208 * - The decimated edge might intersect another existing edge
5209 *
5210 */
5211
5212 break; /* we only want to snap a single edge */
5213 }
5214 _lwt_release_edges(edges, num);
5215 }
5216 else
5217 {
5218 /* The point is isolated, add it as such */
5219 /* TODO: pass 1 as last argument (skipChecks) ? */
5220 id = _lwt_AddIsoNode(topo, -1, point, 0, findFace);
5221 if ( moved ) *moved = 0;
5222 if ( -1 == id )
5223 {
5224 /* should have invoked lwerror already, leaking memory */
5225 lwerror("lwt_AddIsoNode failed");
5226 return -1;
5227 }
5228 }
5229
5230 return id;
5231}
5232
5234lwt_AddPoint(LWT_TOPOLOGY* topo, LWPOINT* point, double tol)
5235{
5236 return _lwt_AddPoint(topo, point, tol, 1, NULL);
5237}
5238
5239/* Return identifier of an equal edge, 0 if none or -1 on error
5240 * (and lwerror gets called on error)
5241 */
5242static LWT_ELEMID
5244{
5245 LWT_ELEMID id;
5246 LWT_ISO_EDGE *edges;
5247 uint64_t num, i;
5248 const GBOX *qbox = lwgeom_get_bbox( lwline_as_lwgeom(edge) );
5249 GEOSGeometry *edgeg;
5250 const int flds = LWT_COL_EDGE_EDGE_ID|LWT_COL_EDGE_GEOM;
5251
5252 edges = lwt_be_getEdgeWithinBox2D( topo, qbox, &num, flds, 0 );
5253 if (num == UINT64_MAX)
5254 {
5255 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5256 return -1;
5257 }
5258 if ( num )
5259 {
5260 initGEOS(lwnotice, lwgeom_geos_error);
5261
5262 edgeg = LWGEOM2GEOS( lwline_as_lwgeom(edge), 0 );
5263 if ( ! edgeg )
5264 {
5265 _lwt_release_edges(edges, num);
5266 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
5267 return -1;
5268 }
5269 for (i=0; i<num; ++i)
5270 {
5271 LWT_ISO_EDGE *e = &(edges[i]);
5272 LWGEOM *g = lwline_as_lwgeom(e->geom);
5273 GEOSGeometry *gg;
5274 int equals;
5275 gg = LWGEOM2GEOS( g, 0 );
5276 if ( ! gg )
5277 {
5278 GEOSGeom_destroy(edgeg);
5279 _lwt_release_edges(edges, num);
5280 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
5281 return -1;
5282 }
5283 equals = GEOSEquals(gg, edgeg);
5284 GEOSGeom_destroy(gg);
5285 if ( equals == 2 )
5286 {
5287 GEOSGeom_destroy(edgeg);
5288 _lwt_release_edges(edges, num);
5289 lwerror("GEOSEquals exception: %s", lwgeom_geos_errmsg);
5290 return -1;
5291 }
5292 if ( equals )
5293 {
5294 id = e->edge_id;
5295 GEOSGeom_destroy(edgeg);
5296 _lwt_release_edges(edges, num);
5297 return id;
5298 }
5299 }
5300 GEOSGeom_destroy(edgeg);
5301 _lwt_release_edges(edges, num);
5302 }
5303
5304 return 0;
5305}
5306
5307/*
5308 * Add a pre-noded pre-split line edge. Used by lwt_AddLine
5309 * Return edge id, 0 if none added (empty edge), -1 on error
5310 *
5311 * @param handleFaceSplit if non-zero the code will check
5312 * if the newly added edge would split a face and if so
5313 * would create new faces accordingly. Otherwise it will
5314 * set left_face and right_face to null (-1)
5315 */
5316static LWT_ELEMID
5317_lwt_AddLineEdge( LWT_TOPOLOGY* topo, LWLINE* edge, double tol,
5318 int handleFaceSplit )
5319{
5320 LWCOLLECTION *col;
5321 LWPOINT *start_point, *end_point;
5322 LWGEOM *tmp = 0, *tmp2;
5323 LWT_ISO_NODE *node;
5324 LWT_ELEMID nid[2]; /* start_node, end_node */
5325 LWT_ELEMID id; /* edge id */
5326 POINT4D p4d;
5327 uint64_t nn, i;
5328 int moved=0, mm;
5329
5330 LWDEBUGG(1, lwline_as_lwgeom(edge), "_lwtAddLineEdge");
5331 LWDEBUGF(1, "_lwtAddLineEdge with tolerance %g", tol);
5332
5333 start_point = lwline_get_lwpoint(edge, 0);
5334 if ( ! start_point )
5335 {
5336 lwnotice("Empty component of noded line");
5337 return 0; /* must be empty */
5338 }
5339 nid[0] = _lwt_AddPoint( topo, start_point,
5341 handleFaceSplit, &mm );
5342 lwpoint_free(start_point); /* too late if lwt_AddPoint calls lwerror */
5343 if ( nid[0] == -1 ) return -1; /* lwerror should have been called */
5344 moved += mm;
5345
5346
5347 end_point = lwline_get_lwpoint(edge, edge->points->npoints-1);
5348 if ( ! end_point )
5349 {
5350 lwerror("could not get last point of line "
5351 "after successfully getting first point !?");
5352 return -1;
5353 }
5354 nid[1] = _lwt_AddPoint( topo, end_point,
5356 handleFaceSplit, &mm );
5357 moved += mm;
5358 lwpoint_free(end_point); /* too late if lwt_AddPoint calls lwerror */
5359 if ( nid[1] == -1 ) return -1; /* lwerror should have been called */
5360
5361 /*
5362 -- Added endpoints may have drifted due to tolerance, so
5363 -- we need to re-snap the edge to the new nodes before adding it
5364 */
5365 if ( moved )
5366 {
5367
5368 nn = nid[0] == nid[1] ? 1 : 2;
5369 node = lwt_be_getNodeById( topo, nid, &nn,
5371 if (nn == UINT64_MAX)
5372 {
5373 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5374 return -1;
5375 }
5376 start_point = NULL; end_point = NULL;
5377 for (i=0; i<nn; ++i)
5378 {
5379 if ( node[i].node_id == nid[0] ) start_point = node[i].geom;
5380 if ( node[i].node_id == nid[1] ) end_point = node[i].geom;
5381 }
5382 if ( ! start_point || ! end_point )
5383 {
5384 if ( nn ) _lwt_release_nodes(node, nn);
5385 lwerror("Could not find just-added nodes % " LWTFMT_ELEMID
5386 " and %" LWTFMT_ELEMID, nid[0], nid[1]);
5387 return -1;
5388 }
5389
5390 /* snap */
5391
5392 getPoint4d_p( start_point->point, 0, &p4d );
5393 lwline_setPoint4d(edge, 0, &p4d);
5394
5395 getPoint4d_p( end_point->point, 0, &p4d );
5396 lwline_setPoint4d(edge, edge->points->npoints-1, &p4d);
5397
5398 if ( nn ) _lwt_release_nodes(node, nn);
5399
5400 /* make valid, after snap (to handle collapses) */
5402
5403 col = lwgeom_as_lwcollection(tmp);
5404 if ( col )
5405 {{
5406
5407 col = lwcollection_extract(col, LINETYPE);
5408
5409 /* Check if the so-snapped edge collapsed (see #1650) */
5410 if ( col->ngeoms == 0 )
5411 {
5412 lwcollection_free(col);
5413 lwgeom_free(tmp);
5414 LWDEBUG(1, "Made-valid snapped edge collapsed");
5415 return 0;
5416 }
5417
5418 tmp2 = lwgeom_clone_deep( col->geoms[0] );
5419 lwgeom_free(tmp);
5420 tmp = tmp2;
5421 edge = lwgeom_as_lwline(tmp);
5422 lwcollection_free(col);
5423 if ( ! edge )
5424 {
5425 /* should never happen */
5426 lwerror("lwcollection_extract(LINETYPE) returned a non-line?");
5427 return -1;
5428 }
5429 }}
5430 else
5431 {
5432 edge = lwgeom_as_lwline(tmp);
5433 if ( ! edge )
5434 {
5435 LWDEBUGF(1, "Made-valid snapped edge collapsed to %s",
5437 lwgeom_free(tmp);
5438 return 0;
5439 }
5440 }
5441 }
5442
5443 /* check if the so-snapped edge _now_ exists */
5444 id = _lwt_GetEqualEdge ( topo, edge );
5445 LWDEBUGF(1, "_lwt_GetEqualEdge returned %" LWTFMT_ELEMID, id);
5446 if ( id == -1 )
5447 {
5448 if ( tmp ) lwgeom_free(tmp); /* probably too late, due to internal lwerror */
5449 return -1;
5450 }
5451 if ( id )
5452 {
5453 if ( tmp ) lwgeom_free(tmp); /* possibly takes "edge" down with it */
5454 return id;
5455 }
5456
5457 /* No previously existing edge was found, we'll add one */
5458
5459 /* Remove consecutive vertices below given tolerance
5460 * on edge addition */
5461 if ( tol )
5462 {{
5463 tmp2 = lwline_remove_repeated_points(edge, tol);
5464 LWDEBUGG(1, tmp2, "Repeated-point removed");
5465 edge = lwgeom_as_lwline(tmp2);
5466 if ( tmp ) lwgeom_free(tmp);
5467 tmp = tmp2;
5468
5469 /* check if the so-decimated edge collapsed to single-point */
5470 if ( nid[0] == nid[1] && edge->points->npoints == 2 )
5471 {
5472 lwgeom_free(tmp);
5473 LWDEBUG(1, "Repeated-point removed edge collapsed");
5474 return 0;
5475 }
5476
5477 /* check if the so-decimated edge _now_ exists */
5478 id = _lwt_GetEqualEdge ( topo, edge );
5479 LWDEBUGF(1, "_lwt_GetEqualEdge returned %" LWTFMT_ELEMID, id);
5480 if ( id == -1 )
5481 {
5482 lwgeom_free(tmp); /* probably too late, due to internal lwerror */
5483 return -1;
5484 }
5485 if ( id )
5486 {
5487 lwgeom_free(tmp); /* takes "edge" down with it */
5488 return id;
5489 }
5490 }}
5491
5492
5493 /* TODO: skip checks ? */
5494 id = _lwt_AddEdge( topo, nid[0], nid[1], edge, 0, handleFaceSplit ? 1 : -1 );
5495 LWDEBUGF(1, "lwt_AddEdgeModFace returned %" LWTFMT_ELEMID, id);
5496 if ( id == -1 )
5497 {
5498 lwgeom_free(tmp); /* probably too late, due to internal lwerror */
5499 return -1;
5500 }
5501 lwgeom_free(tmp); /* possibly takes "edge" down with it */
5502
5503 return id;
5504}
5505
5506/* Simulate split-loop as it was implemented in pl/pgsql version
5507 * of TopoGeo_addLinestring */
5508static LWGEOM *
5509_lwt_split_by_nodes(const LWGEOM *g, const LWGEOM *nodes)
5510{
5512 uint32_t i;
5513 LWGEOM *bg;
5514
5515 bg = lwgeom_clone_deep(g);
5516 if ( ! col->ngeoms ) return bg;
5517
5518 for (i=0; i<col->ngeoms; ++i)
5519 {
5520 LWGEOM *g2;
5521 g2 = lwgeom_split(bg, col->geoms[i]);
5522 lwgeom_free(bg);
5523 bg = g2;
5524 }
5525 bg->srid = nodes->srid;
5526
5527 return bg;
5528}
5529
5530static LWT_ELEMID*
5531_lwt_AddLine(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges,
5532 int handleFaceSplit)
5533{
5534 LWGEOM *geomsbuf[1];
5535 LWGEOM **geoms;
5536 uint32_t ngeoms;
5537 LWGEOM *noded, *tmp;
5538 LWCOLLECTION *col;
5539 LWT_ELEMID *ids;
5540 LWT_ISO_EDGE *edges;
5541 LWT_ISO_NODE *nodes;
5542 uint64_t num, numedges = 0, numnodes = 0;
5543 uint64_t i;
5544 GBOX qbox;
5545
5546 if ( lwline_is_empty(line) )
5547 {
5548 *nedges = 0;
5549 return NULL;
5550 }
5551
5552 *nedges = -1; /* error condition, by default */
5553
5554 /* Get tolerance, if 0 was given */
5555 if ( ! tol ) tol = _LWT_MINTOLERANCE( topo, (LWGEOM*)line );
5556 LWDEBUGF(1, "Working tolerance:%.15g", tol);
5557 LWDEBUGF(1, "Input line has srid=%d", line->srid);
5558
5559 /* Remove consecutive vertices below given tolerance upfront */
5560 if ( tol )
5561 {{
5563 tmp = lwline_as_lwgeom(clean); /* NOTE: might collapse to non-simple */
5564 LWDEBUGG(1, tmp, "Repeated-point removed");
5565 }} else tmp=(LWGEOM*)line;
5566
5567 /* 1. Self-node */
5568 noded = lwgeom_node((LWGEOM*)tmp);
5569 if ( tmp != (LWGEOM*)line ) lwgeom_free(tmp);
5570 if ( ! noded ) return NULL; /* should have called lwerror already */
5571 LWDEBUGG(1, noded, "Noded");
5572
5573 qbox = *lwgeom_get_bbox( lwline_as_lwgeom(line) );
5574 LWDEBUGF(1, "Line BOX is %.15g %.15g, %.15g %.15g", qbox.xmin, qbox.ymin,
5575 qbox.xmax, qbox.ymax);
5576 gbox_expand(&qbox, tol);
5577 LWDEBUGF(1, "BOX expanded by %g is %.15g %.15g, %.15g %.15g",
5578 tol, qbox.xmin, qbox.ymin, qbox.xmax, qbox.ymax);
5579
5580 LWGEOM **nearby = 0;
5581 int nearbyindex = 0;
5582 int nearbycount = 0;
5583
5584 /* 2.0. Find edges falling within tol distance */
5585 edges = lwt_be_getEdgeWithinBox2D( topo, &qbox, &numedges, LWT_COL_EDGE_ALL, 0 );
5586 if (numedges == UINT64_MAX)
5587 {
5588 lwgeom_free(noded);
5589 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5590 return NULL;
5591 }
5592 LWDEBUGF(1, "Line has %d points, its bbox intersects %d edges bboxes",
5593 line->points->npoints, numedges);
5594 if ( numedges )
5595 {{
5596 /* collect those whose distance from us is < tol */
5597 nearbycount += numedges;
5598 nearby = lwalloc(numedges * sizeof(LWGEOM *));
5599 for (i=0; i<numedges; ++i)
5600 {
5601 LW_ON_INTERRUPT(return NULL);
5602 LWT_ISO_EDGE *e = &(edges[i]);
5603 LWGEOM *g = lwline_as_lwgeom(e->geom);
5604 LWDEBUGF(2, "Computing distance from edge %d having %d points", i, e->geom->points->npoints);
5605 double dist = lwgeom_mindistance2d(g, noded);
5606 /* must be closer than tolerated, unless distance is zero */
5607 if ( dist && dist >= tol ) continue;
5608 nearby[nearbyindex++] = g;
5609 }
5610 LWDEBUGF(1, "Found %d edges closer than tolerance (%g)", nearbyindex, tol);
5611 }}
5612 int nearbyedgecount = nearbyindex;
5613
5614 /* 2.1. Find nodes falling within tol distance *
5615 * TODO: check if we should be only considering _isolated_ nodes! */
5616 nodes = lwt_be_getNodeWithinBox2D( topo, &qbox, &numnodes, LWT_COL_NODE_ALL, 0 );
5617 if (numnodes == UINT64_MAX)
5618 {
5619 lwgeom_free(noded);
5620 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5621 return NULL;
5622 }
5623 LWDEBUGF(1, "Line bbox intersects %d nodes bboxes", numnodes);
5624 if ( numnodes )
5625 {{
5626 /* collect those whose distance from us is < tol */
5627 nearbycount = nearbyedgecount + numnodes;
5628 nearby = nearby ?
5629 lwrealloc(nearby, nearbycount * sizeof(LWGEOM *))
5630 :
5631 lwalloc(nearbycount * sizeof(LWGEOM *))
5632 ;
5633 int nn = 0;
5634 for (i=0; i<numnodes; ++i)
5635 {
5636 LWT_ISO_NODE *n = &(nodes[i]);
5637 LWGEOM *g = lwpoint_as_lwgeom(n->geom);
5638 double dist = lwgeom_mindistance2d(g, noded);
5639 /* must be closer than tolerated, unless distance is zero */
5640 if ( dist && dist >= tol )
5641 {
5642 LWDEBUGF(1, "Node %d is %g units away, we tolerate only %g", n->node_id, dist, tol);
5643 continue;
5644 }
5645 nearby[nearbyindex++] = g;
5646 ++nn;
5647 }
5648 LWDEBUGF(1, "Found %d nodes closer than tolerance (%g)", nn, tol);
5649 }}
5650 int nearbynodecount = nearbyindex - nearbyedgecount;
5651 nearbycount = nearbyindex;
5652
5653 LWDEBUGF(1, "Number of nearby elements is %d", nearbycount);
5654
5655 /* 2.2. Snap to nearby elements */
5656 if ( nearbycount )
5657 {{
5658 LWCOLLECTION *col;
5659 LWGEOM *elems;
5660
5662 NULL, nearbycount, nearby);
5663 elems = lwcollection_as_lwgeom(col);
5664
5665 LWDEBUGG(1, elems, "Collected nearby elements");
5666
5667 tmp = _lwt_toposnap(noded, elems, tol);
5668 lwgeom_free(noded);
5669 noded = tmp;
5670 LWDEBUGG(1, noded, "Elements-snapped");
5671
5672 /* will not release the geoms array */
5674
5675 /*
5676 -- re-node to account for ST_Snap introduced self-intersections
5677 -- See http://trac.osgeo.org/postgis/ticket/1714
5678 -- TODO: consider running UnaryUnion once after all noding
5679 */
5680 tmp = lwgeom_unaryunion(noded);
5681 lwgeom_free(noded);
5682 noded = tmp;
5683 LWDEBUGG(1, noded, "Unary-unioned");
5684 }}
5685
5686 /* 2.3. Node with nearby edges */
5687 if ( nearbyedgecount )
5688 {{
5689 LWCOLLECTION *col;
5690 LWGEOM *iedges; /* just an alias for col */
5691 LWGEOM *diff, *xset;
5692
5693 LWDEBUGF(1, "Line intersects %d edges", nearbyedgecount);
5694
5696 NULL, nearbyedgecount, nearby);
5697 iedges = lwcollection_as_lwgeom(col);
5698 LWDEBUGG(1, iedges, "Collected edges");
5699
5700 LWDEBUGF(1, "Diffing noded, with srid=%d "
5701 "and interesecting edges, with srid=%d",
5702 noded->srid, iedges->srid);
5703 diff = lwgeom_difference(noded, iedges);
5704 LWDEBUGG(1, diff, "Differenced");
5705
5706 LWDEBUGF(1, "Intersecting noded, with srid=%d "
5707 "and interesecting edges, with srid=%d",
5708 noded->srid, iedges->srid);
5709 xset = lwgeom_intersection(noded, iedges);
5710 LWDEBUGG(1, xset, "Intersected");
5711 lwgeom_free(noded);
5712
5713 /* We linemerge here because INTERSECTION, as of GEOS 3.8,
5714 * will result in shared segments being output as multiple
5715 * lines rather than a single line. Example:
5716
5717 INTERSECTION(
5718 'LINESTRING(0 0, 5 0, 8 0, 10 0,12 0)',
5719 'LINESTRING(5 0, 8 0, 10 0)'
5720 )
5721 ==
5722 MULTILINESTRING((5 0,8 0),(8 0,10 0))
5723
5724 * We will re-split in a subsequent step, by splitting
5725 * the final line with pre-existing nodes
5726 */
5727 LWDEBUG(1, "Linemerging intersection");
5728 tmp = lwgeom_linemerge(xset);
5729 LWDEBUGG(1, tmp, "Linemerged");
5730 lwgeom_free(xset);
5731 xset = tmp;
5732
5733 /*
5734 * Here we union the (linemerged) intersection with
5735 * the difference (new lines)
5736 */
5737 LWDEBUG(1, "Unioning difference and (linemerged) intersection");
5738 noded = lwgeom_union(diff, xset);
5739 LWDEBUGG(1, noded, "Diff-Xset Unioned");
5740 lwgeom_free(xset);
5741 lwgeom_free(diff);
5742
5743 /* will not release the geoms array */
5745 }}
5746
5747
5748 /* 2.4. Split by pre-existing nodes
5749 *
5750 * Pre-existing nodes are isolated nodes AND endpoints
5751 * of intersecting edges
5752 */
5753 if ( nearbyedgecount )
5754 {
5755 nearbycount += nearbyedgecount * 2; /* make space for endpoints */
5756 nearby = lwrealloc(nearby, nearbycount * sizeof(LWGEOM *));
5757 for (int i=0; i<nearbyedgecount; i++)
5758 {
5759 LWLINE *edge = lwgeom_as_lwline(nearby[i]);
5760 LWPOINT *startNode = lwline_get_lwpoint(edge, 0);
5761 LWPOINT *endNode = lwline_get_lwpoint(edge, edge->points->npoints-1);
5762 /* TODO: only add if within distance to noded AND if not duplicated */
5763 nearby[nearbyindex++] = lwpoint_as_lwgeom(startNode);
5764 nearbynodecount++;
5765 nearby[nearbyindex++] = lwpoint_as_lwgeom(endNode);
5766 nearbynodecount++;
5767 }
5768 }
5769 if ( nearbynodecount )
5770 {
5772 NULL, nearbynodecount,
5773 nearby + nearbyedgecount);
5774 LWGEOM *inodes = lwcollection_as_lwgeom(col);
5775 /* TODO: use lwgeom_split of lwgeom_union ... */
5776 tmp = _lwt_split_by_nodes(noded, inodes);
5777 lwgeom_free(noded);
5778 noded = tmp;
5779 LWDEBUGG(1, noded, "Node-split");
5780 /* will not release the geoms array */
5782 }
5783
5784
5785 LWDEBUG(1, "Freeing up nearby elements");
5786
5787 /* TODO: free up endpoints of nearbyedges */
5788 if ( nearby ) lwfree(nearby);
5789 if ( nodes ) _lwt_release_nodes(nodes, numnodes);
5790 if ( edges ) _lwt_release_edges(edges, numedges);
5791
5792 LWDEBUGG(1, noded, "Finally-noded");
5793
5794 /* 3. For each (now-noded) segment, insert an edge */
5795 col = lwgeom_as_lwcollection(noded);
5796 if ( col )
5797 {
5798 LWDEBUG(1, "Noded line was a collection");
5799 geoms = col->geoms;
5800 ngeoms = col->ngeoms;
5801 }
5802 else
5803 {
5804 LWDEBUG(1, "Noded line was a single geom");
5805 geomsbuf[0] = noded;
5806 geoms = geomsbuf;
5807 ngeoms = 1;
5808 }
5809
5810 LWDEBUGF(1, "Line was split into %d edges", ngeoms);
5811
5812 /* TODO: refactor to first add all nodes (re-snapping edges if
5813 * needed) and then check all edges for existing already
5814 * ( so to save a DB scan for each edge to be added )
5815 */
5816 ids = lwalloc(sizeof(LWT_ELEMID)*ngeoms);
5817 num = 0;
5818 for ( i=0; i<ngeoms; ++i )
5819 {
5820 LWT_ELEMID id;
5821 LWGEOM *g = geoms[i];
5822 g->srid = noded->srid;
5823
5824#if POSTGIS_DEBUG_LEVEL > 0
5825 {
5826 size_t sz;
5827 char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
5828 LWDEBUGF(1, "Component %d of split line is: %s", i, wkt1);
5829 lwfree(wkt1);
5830 }
5831#endif
5832
5833 id = _lwt_AddLineEdge( topo, lwgeom_as_lwline(g), tol, handleFaceSplit );
5834 LWDEBUGF(1, "_lwt_AddLineEdge returned %" LWTFMT_ELEMID, id);
5835 if ( id < 0 )
5836 {
5837 lwgeom_free(noded);
5838 lwfree(ids);
5839 return NULL;
5840 }
5841 if ( ! id )
5842 {
5843 LWDEBUGF(1, "Component %d of split line collapsed", i);
5844 continue;
5845 }
5846
5847 LWDEBUGF(1, "Component %d of split line is edge %" LWTFMT_ELEMID,
5848 i, id);
5849 ids[num++] = id; /* TODO: skip duplicates */
5850 }
5851
5852 LWDEBUGG(1, noded, "Noded before free");
5853 lwgeom_free(noded);
5854
5855 /* TODO: XXX remove duplicated ids if not done before */
5856
5857 *nedges = num;
5858 return ids;
5859}
5860
5862lwt_AddLine(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges)
5863{
5864 return _lwt_AddLine(topo, line, tol, nedges, 1);
5865}
5866
5868lwt_AddLineNoFace(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges)
5869{
5870 return _lwt_AddLine(topo, line, tol, nedges, 0);
5871}
5872
5874lwt_AddPolygon(LWT_TOPOLOGY* topo, LWPOLY* poly, double tol, int* nfaces)
5875{
5876 uint32_t i;
5877 *nfaces = -1; /* error condition, by default */
5878 int num;
5879 LWT_ISO_FACE *faces;
5880 uint64_t nfacesinbox;
5881 uint64_t j;
5882 LWT_ELEMID *ids = NULL;
5883 GBOX qbox;
5884 const GEOSPreparedGeometry *ppoly;
5885 GEOSGeometry *polyg;
5886
5887 /* Nothing to add, in an empty polygon */
5888 if ( lwpoly_is_empty(poly) )
5889 {
5890 *nfaces = 0;
5891 return NULL;
5892 }
5893
5894 /* Get tolerance, if 0 was given */
5895 if ( ! tol ) tol = _LWT_MINTOLERANCE( topo, (LWGEOM*)poly );
5896 LWDEBUGF(1, "Working tolerance:%.15g", tol);
5897
5898 /* Add each ring as an edge */
5899 for ( i=0; i<poly->nrings; ++i )
5900 {
5901 LWLINE *line;
5902 POINTARRAY *pa;
5903 LWT_ELEMID *eids;
5904 int nedges;
5905
5906 pa = ptarray_clone(poly->rings[i]);
5907 line = lwline_construct(topo->srid, NULL, pa);
5908 eids = lwt_AddLine( topo, line, tol, &nedges );
5909 if ( nedges < 0 ) {
5910 /* probably too late as lwt_AddLine invoked lwerror */
5911 lwline_free(line);
5912 lwerror("Error adding ring %d of polygon", i);
5913 return NULL;
5914 }
5915 lwline_free(line);
5916 lwfree(eids);
5917 }
5918
5919 /*
5920 -- Find faces covered by input polygon
5921 -- NOTE: potential snapping changed polygon edges
5922 */
5923 qbox = *lwgeom_get_bbox( lwpoly_as_lwgeom(poly) );
5924 gbox_expand(&qbox, tol);
5925 faces = lwt_be_getFaceWithinBox2D( topo, &qbox, &nfacesinbox,
5926 LWT_COL_FACE_ALL, 0 );
5927 if (nfacesinbox == UINT64_MAX)
5928 {
5929 lwfree(ids);
5930 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5931 return NULL;
5932 }
5933
5934 num = 0;
5935 if ( nfacesinbox )
5936 {
5937 polyg = LWGEOM2GEOS(lwpoly_as_lwgeom(poly), 0);
5938 if ( ! polyg )
5939 {
5940 _lwt_release_faces(faces, nfacesinbox);
5941 lwerror("Could not convert poly geometry to GEOS: %s", lwgeom_geos_errmsg);
5942 return NULL;
5943 }
5944 ppoly = GEOSPrepare(polyg);
5945 ids = lwalloc(sizeof(LWT_ELEMID)*nfacesinbox);
5946 for ( j=0; j<nfacesinbox; ++j )
5947 {
5948 LWT_ISO_FACE *f = &(faces[j]);
5949 LWGEOM *fg;
5950 GEOSGeometry *fgg, *sp;
5951 int covers;
5952
5953 /* check if a point on this face surface is covered by our polygon */
5954 fg = lwt_GetFaceGeometry( topo, f->face_id );
5955 if ( ! fg )
5956 {
5957 j = f->face_id; /* so we can destroy faces */
5958 GEOSPreparedGeom_destroy(ppoly);
5959 GEOSGeom_destroy(polyg);
5960 lwfree(ids);
5961 _lwt_release_faces(faces, nfacesinbox);
5962 lwerror("Could not get geometry of face %" LWTFMT_ELEMID, j);
5963 return NULL;
5964 }
5965 /* check if a point on this face's surface is covered by our polygon */
5966 fgg = LWGEOM2GEOS(fg, 0);
5967 lwgeom_free(fg);
5968 if ( ! fgg )
5969 {
5970 GEOSPreparedGeom_destroy(ppoly);
5971 GEOSGeom_destroy(polyg);
5972 _lwt_release_faces(faces, nfacesinbox);
5973 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
5974 return NULL;
5975 }
5976 sp = GEOSPointOnSurface(fgg);
5977 GEOSGeom_destroy(fgg);
5978 if ( ! sp )
5979 {
5980 GEOSPreparedGeom_destroy(ppoly);
5981 GEOSGeom_destroy(polyg);
5982 _lwt_release_faces(faces, nfacesinbox);
5983 lwerror("Could not find point on face surface: %s", lwgeom_geos_errmsg);
5984 return NULL;
5985 }
5986 covers = GEOSPreparedCovers( ppoly, sp );
5987 GEOSGeom_destroy(sp);
5988 if (covers == 2)
5989 {
5990 GEOSPreparedGeom_destroy(ppoly);
5991 GEOSGeom_destroy(polyg);
5992 _lwt_release_faces(faces, nfacesinbox);
5993 lwerror("PreparedCovers error: %s", lwgeom_geos_errmsg);
5994 return NULL;
5995 }
5996 if ( ! covers )
5997 {
5998 continue; /* we're not composed by this face */
5999 }
6000
6001 /* TODO: avoid duplicates ? */
6002 ids[num++] = f->face_id;
6003 }
6004 GEOSPreparedGeom_destroy(ppoly);
6005 GEOSGeom_destroy(polyg);
6006 _lwt_release_faces(faces, nfacesinbox);
6007 }
6008
6009 /* possibly 0 if non face's surface point was found
6010 * to be covered by input polygon */
6011 *nfaces = num;
6012
6013 return ids;
6014}
6015
6016/*
6017 *---- polygonizer
6018 */
6019
6020/* An array of pointers to EDGERING structures */
6025
6026static int
6027compare_iso_edges_by_id(const void *si1, const void *si2)
6028{
6029 int a = ((LWT_ISO_EDGE *)si1)->edge_id;
6030 int b = ((LWT_ISO_EDGE *)si2)->edge_id;
6031 if ( a < b )
6032 return -1;
6033 else if ( a > b )
6034 return 1;
6035 else
6036 return 0;
6037}
6038
6039static LWT_ISO_EDGE *
6041{
6042 LWT_ISO_EDGE key;
6043 key.edge_id = id;
6044
6045 void *match = bsearch( &key, tab->edges, tab->size,
6046 sizeof(LWT_ISO_EDGE),
6048 return match;
6049}
6050
6051typedef struct LWT_EDGERING_ELEM_T {
6052 /* externally owned */
6054 /* 0 if false, 1 if true */
6055 int left;
6057
6058/* A ring of edges */
6059typedef struct LWT_EDGERING_T {
6060 /* Signed edge identifiers
6061 * positive ones are walked in their direction, negative ones
6062 * in the opposite direction */
6064 /* Number of edges in the ring */
6065 int size;
6067 /* Bounding box of the ring */
6069 /* Bounding box of the ring in GEOS format (for STRTree) */
6070 GEOSGeometry *genv;
6072
6073#define LWT_EDGERING_INIT(a) { \
6074 (a)->size = 0; \
6075 (a)->capacity = 1; \
6076 (a)->elems = lwalloc(sizeof(LWT_EDGERING_ELEM *) * (a)->capacity); \
6077 (a)->env = NULL; \
6078 (a)->genv = NULL; \
6079}
6080
6081#define LWT_EDGERING_PUSH(a, r) { \
6082 if ( (a)->size + 1 > (a)->capacity ) { \
6083 (a)->capacity *= 2; \
6084 (a)->elems = lwrealloc((a)->elems, sizeof(LWT_EDGERING_ELEM *) * (a)->capacity); \
6085 } \
6086 /* lwdebug(1, "adding elem %d (%p) of edgering %p", (a)->size, (r), (a)); */ \
6087 (a)->elems[(a)->size++] = (r); \
6088}
6089
6090#define LWT_EDGERING_CLEAN(a) { \
6091 int i; for (i=0; i<(a)->size; ++i) { \
6092 if ( (a)->elems[i] ) { \
6093 /* lwdebug(1, "freeing elem %d (%p) of edgering %p", i, (a)->elems[i], (a)); */ \
6094 lwfree((a)->elems[i]); \
6095 } \
6096 } \
6097 if ( (a)->elems ) { lwfree((a)->elems); (a)->elems = NULL; } \
6098 (a)->size = 0; \
6099 (a)->capacity = 0; \
6100 if ( (a)->env ) { lwfree((a)->env); (a)->env = NULL; } \
6101 if ( (a)->genv ) { GEOSGeom_destroy((a)->genv); (a)->genv = NULL; } \
6102}
6103
6104/* An array of pointers to EDGERING structures */
6111
6112#define LWT_EDGERING_ARRAY_INIT(a) { \
6113 (a)->size = 0; \
6114 (a)->capacity = 1; \
6115 (a)->rings = lwalloc(sizeof(LWT_EDGERING *) * (a)->capacity); \
6116 (a)->tree = NULL; \
6117}
6118
6119/* WARNING: use of 'j' is intentional, not to clash with
6120 * 'i' used in LWT_EDGERING_CLEAN */
6121#define LWT_EDGERING_ARRAY_CLEAN(a) { \
6122 int j; for (j=0; j<(a)->size; ++j) { \
6123 LWT_EDGERING_CLEAN((a)->rings[j]); \
6124 } \
6125 if ( (a)->capacity ) lwfree((a)->rings); \
6126 if ( (a)->tree ) { \
6127 GEOSSTRtree_destroy( (a)->tree ); \
6128 (a)->tree = NULL; \
6129 } \
6130}
6131
6132#define LWT_EDGERING_ARRAY_PUSH(a, r) { \
6133 if ( (a)->size + 1 > (a)->capacity ) { \
6134 (a)->capacity *= 2; \
6135 (a)->rings = lwrealloc((a)->rings, sizeof(LWT_EDGERING *) * (a)->capacity); \
6136 } \
6137 (a)->rings[(a)->size++] = (r); \
6138}
6139
6146
6147static int
6149{
6150 LWT_EDGERING_ELEM *el = it->curelem;
6151 POINTARRAY *pa;
6152
6153 if ( ! el ) return 0; /* finished */
6154
6155 pa = el->edge->geom->points;
6156
6157 int tonext = 0;
6158 LWDEBUGF(3, "iterator fetching idx %d from pa of %d points", it->curidx, pa->npoints);
6159 getPoint2d_p(pa, it->curidx, pt);
6160 if ( el->left ) {
6161 it->curidx++;
6162 if ( it->curidx >= (int) pa->npoints ) tonext = 1;
6163 } else {
6164 it->curidx--;
6165 if ( it->curidx < 0 ) tonext = 1;
6166 }
6167
6168 if ( tonext )
6169 {
6170 LWDEBUG(3, "iterator moving to next element");
6171 it->curelemidx++;
6172 if ( it->curelemidx < it->ring->size )
6173 {
6174 el = it->curelem = it->ring->elems[it->curelemidx];
6175 it->curidx = el->left ? 0 : el->edge->geom->points->npoints - 1;
6176 }
6177 else
6178 {
6179 it->curelem = NULL;
6180 }
6181 }
6182
6183 return 1;
6184}
6185
6186/* Release return with lwfree */
6189{
6191 ret->ring = er;
6192 if ( er->size ) ret->curelem = er->elems[0];
6193 else ret->curelem = NULL;
6194 ret->curelemidx = 0;
6195 ret->curidx = ret->curelem->left ? 0 : ret->curelem->edge->geom->points->npoints - 1;
6196 return ret;
6197}
6198
6199/* Identifier for a placeholder face that will be
6200 * used to mark hole rings */
6201#define LWT_HOLES_FACE_PLACEHOLDER INT32_MIN
6202
6203static int
6205{
6206 while (
6207 from < etab->size &&
6208 etab->edges[from].face_left != -1 &&
6209 etab->edges[from].face_right != -1
6210 ) from++;
6211 return from < etab->size ? from : -1;
6212}
6213
6214static LWT_ISO_EDGE *
6216{
6217 LWT_ISO_EDGE *edge;
6218 int fields = LWT_COL_EDGE_ALL;
6219 uint64_t nelems = 1;
6220
6221 edge = lwt_be_getEdgeWithinBox2D( topo, NULL, &nelems, fields, 0);
6222 *numedges = nelems;
6223 if (nelems == UINT64_MAX)
6224 {
6225 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
6226 return NULL;
6227 }
6228 return edge;
6229}
6230
6231/* Update the side face of given ring edges
6232 *
6233 * Edge identifiers are signed, those with negative identifier
6234 * need to be updated their right_face, those with positive
6235 * identifier need to be updated their left_face.
6236 *
6237 * @param face identifier of the face bound by the ring
6238 * @return 0 on success, -1 on error
6239 */
6240static int
6242 LWT_ELEMID face)
6243{
6244 LWT_ISO_EDGE *forward_edges = NULL;
6245 int forward_edges_count = 0;
6246 LWT_ISO_EDGE *backward_edges = NULL;
6247 int backward_edges_count = 0;
6248 int i, ret;
6249
6250 /* Make a list of forward_edges and backward_edges */
6251
6252 forward_edges = lwalloc(sizeof(LWT_ISO_EDGE) * ring->size);
6253 forward_edges_count = 0;
6254 backward_edges = lwalloc(sizeof(LWT_ISO_EDGE) * ring->size);
6255 backward_edges_count = 0;
6256
6257 for ( i=0; i<ring->size; ++i )
6258 {
6259 LWT_EDGERING_ELEM *elem = ring->elems[i];
6260 LWT_ISO_EDGE *edge = elem->edge;
6261 LWT_ELEMID id = edge->edge_id;
6262 if ( elem->left )
6263 {
6264 LWDEBUGF(3, "Forward edge %d is %d", forward_edges_count, id);
6265 forward_edges[forward_edges_count].edge_id = id;
6266 forward_edges[forward_edges_count++].face_left = face;
6267 edge->face_left = face;
6268 }
6269 else
6270 {
6271 LWDEBUGF(3, "Backward edge %d is %d", forward_edges_count, id);
6272 backward_edges[backward_edges_count].edge_id = id;
6273 backward_edges[backward_edges_count++].face_right = face;
6274 edge->face_right = face;
6275 }
6276 }
6277
6278 /* Update forward edges */
6279 if ( forward_edges_count )
6280 {
6281 ret = lwt_be_updateEdgesById(topo, forward_edges,
6282 forward_edges_count,
6284 if ( ret == -1 )
6285 {
6286 lwfree( forward_edges );
6287 lwfree( backward_edges );
6288 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
6289 return -1;
6290 }
6291 if ( ret != forward_edges_count )
6292 {
6293 lwfree( forward_edges );
6294 lwfree( backward_edges );
6295 lwerror("Unexpected error: %d edges updated when expecting %d (forward)",
6296 ret, forward_edges_count);
6297 return -1;
6298 }
6299 }
6300
6301 /* Update backward edges */
6302 if ( backward_edges_count )
6303 {
6304 ret = lwt_be_updateEdgesById(topo, backward_edges,
6305 backward_edges_count,
6307 if ( ret == -1 )
6308 {
6309 lwfree( forward_edges );
6310 lwfree( backward_edges );
6311 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
6312 return -1;
6313 }
6314 if ( ret != backward_edges_count )
6315 {
6316 lwfree( forward_edges );
6317 lwfree( backward_edges );
6318 lwerror("Unexpected error: %d edges updated when expecting %d (backward)",
6319 ret, backward_edges_count);
6320 return -1;
6321 }
6322 }
6323
6324 lwfree( forward_edges );
6325 lwfree( backward_edges );
6326
6327 return 0;
6328}
6329
6330/*
6331 * @param side 1 for left side, -1 for right side
6332 */
6333static LWT_EDGERING *
6335 LWT_ISO_EDGE *edge, int side)
6336{
6337 LWT_EDGERING *ring;
6338 LWT_EDGERING_ELEM *elem;
6339 LWT_ISO_EDGE *cur;
6340 int curside;
6341
6342 ring = lwalloc(sizeof(LWT_EDGERING));
6343 LWT_EDGERING_INIT(ring);
6344
6345 cur = edge;
6346 curside = side;
6347
6348 LWDEBUGF(2, "Building rings for edge %d (side %d)", cur->edge_id, side);
6349
6350 do {
6351 LWT_ELEMID next;
6352
6353 elem = lwalloc(sizeof(LWT_EDGERING_ELEM));
6354 elem->edge = cur;
6355 elem->left = ( curside == 1 );
6356
6357 /* Mark edge as "visited" */
6358 if ( elem->left ) cur->face_left = LWT_HOLES_FACE_PLACEHOLDER;
6359 else cur->face_right = LWT_HOLES_FACE_PLACEHOLDER;
6360
6361 LWT_EDGERING_PUSH(ring, elem);
6362 next = elem->left ? cur->next_left : cur->next_right;
6363
6364 LWDEBUGF(3, " next edge is %d", next);
6365
6366 if ( next > 0 ) curside = 1;
6367 else { curside = -1; next = -next; }
6368 cur = _lwt_getIsoEdgeById(edges, next);
6369 if ( ! cur )
6370 {
6371 lwerror("Could not find edge with id %d", next);
6372 break;
6373 }
6374 } while (cur != edge || curside != side);
6375
6376 LWDEBUGF(1, "Ring for edge %d has %d elems", edge->edge_id*side, ring->size);
6377
6378 return ring;
6379}
6380
6381static double
6383{
6384 POINT2D P1;
6385 POINT2D P2;
6386 POINT2D P3;
6387 double sum = 0.0;
6388 double x0, x, y1, y2;
6389
6390 if ( ! _lwt_EdgeRingIterator_next(it, &P1) ) return 0.0;
6391 if ( ! _lwt_EdgeRingIterator_next(it, &P2) ) return 0.0;
6392
6393 LWDEBUG(2, "_lwt_EdgeRingSignedArea");
6394
6395 x0 = P1.x;
6396 while ( _lwt_EdgeRingIterator_next(it, &P3) )
6397 {
6398 x = P2.x - x0;
6399 y1 = P3.y;
6400 y2 = P1.y;
6401 sum += x * (y2-y1);
6402
6403 /* Move forwards! */
6404 P1 = P2;
6405 P2 = P3;
6406 }
6407
6408 return sum / 2.0;
6409}
6410
6411
6412/* Return 1 for true, 0 for false */
6413static int
6415{
6416 double sa;
6417
6418 LWDEBUGF(2, "_lwt_EdgeRingIsCCW, ring has %d elems", ring->size);
6420 sa = _lwt_EdgeRingSignedArea(it);
6421 LWDEBUGF(2, "_lwt_EdgeRingIsCCW, signed area is %g", sa);
6422 lwfree(it);
6423 if ( sa >= 0 ) return 0;
6424 else return 1;
6425}
6426
6427static int
6429{
6430 int cn = 0; /* the crossing number counter */
6431 POINT2D v1, v2;
6432#ifndef RELAX
6433 POINT2D v0;
6434#endif
6435
6436 if ( ! _lwt_EdgeRingIterator_next(it, &v1) ) return cn;
6437 v0 = v1;
6438 while ( _lwt_EdgeRingIterator_next(it, &v2) )
6439 {
6440 double vt;
6441
6442 /* edge from vertex i to vertex i+1 */
6443 if
6444 (
6445 /* an upward crossing */
6446 ((v1.y <= p->y) && (v2.y > p->y))
6447 /* a downward crossing */
6448 || ((v1.y > p->y) && (v2.y <= p->y))
6449 )
6450 {
6451
6452 vt = (double)(p->y - v1.y) / (v2.y - v1.y);
6453
6454 /* P->x <intersect */
6455 if (p->x < v1.x + vt * (v2.x - v1.x))
6456 {
6457 /* a valid crossing of y=p->y right of p->x */
6458 ++cn;
6459 }
6460 }
6461 v1 = v2;
6462 }
6463
6464 LWDEBUGF(3, "_lwt_EdgeRingCrossingCount returning %d", cn);
6465
6466#ifndef RELAX
6467 if ( memcmp(&v1, &v0, sizeof(POINT2D)) )
6468 {
6469 lwerror("_lwt_EdgeRingCrossingCount: V[n] != V[0] (%g %g != %g %g)",
6470 v1.x, v1.y, v0.x, v0.y);
6471 return -1;
6472 }
6473#endif
6474
6475 return cn;
6476}
6477
6478/* Return 1 for true, 0 for false */
6479static int
6481{
6482 int cn = 0;
6483
6485 cn = _lwt_EdgeRingCrossingCount(p, it);
6486 lwfree(it);
6487 return (cn&1); /* 0 if even (out), and 1 if odd (in) */
6488}
6489
6490static GBOX *
6492{
6493 int i;
6494
6495 if ( ! ring->env )
6496 {
6497 LWDEBUGF(2, "Computing GBOX for ring %p", ring);
6498 for (i=0; i<ring->size; ++i)
6499 {
6500 LWT_EDGERING_ELEM *elem = ring->elems[i];
6501 LWLINE *g = elem->edge->geom;
6502 const GBOX *newbox = lwgeom_get_bbox(lwline_as_lwgeom(g));
6503 if ( ! i ) ring->env = gbox_clone( newbox );
6504 else gbox_merge( newbox, ring->env );
6505 }
6506 }
6507
6508 return ring->env;
6509}
6510
6511static LWT_ELEMID
6513{
6514 LWT_EDGERING_ELEM *el = ring->elems[0];
6515 return el->left ? el->edge->face_left : el->edge->face_right;
6516}
6517
6518
6519/*
6520 * Register a face on an edge side
6521 *
6522 * Create and register face to shell (CCW) walks,
6523 * register arbitrary negative face_id to CW rings.
6524 *
6525 * Push CCW rings to shells, CW rings to holes.
6526 *
6527 * The ownership of the "geom" and "ids" members of the
6528 * LWT_EDGERING pushed to the given LWT_EDGERING_ARRAYS
6529 * are transferred to caller.
6530 *
6531 * @param side 1 for left side, -1 for right side
6532 *
6533 * @param holes an array where holes will be pushed
6534 *
6535 * @param shells an array where shells will be pushed
6536 *
6537 * @param registered id of registered face. It will be a negative number
6538 * for holes or isolated edge strips (still registered in the face
6539 * table, but only temporary).
6540 *
6541 * @return 0 on success, -1 on error.
6542 *
6543 */
6544static int
6546 int side, LWT_ISO_EDGE_TABLE *edges,
6547 LWT_EDGERING_ARRAY *holes,
6548 LWT_EDGERING_ARRAY *shells,
6549 LWT_ELEMID *registered)
6550{
6551 const LWT_BE_IFACE *iface = topo->be_iface;
6552 /* this is arbitrary, could be taken as parameter */
6553 static const int placeholder_faceid = LWT_HOLES_FACE_PLACEHOLDER;
6554 LWT_EDGERING *ring;
6555
6556 /* Get edge ring */
6557 ring = _lwt_BuildEdgeRing(topo, edges, edge, side);
6558
6559 LWDEBUG(2, "Ring built, calling EdgeRingIsCCW");
6560
6561 /* Compute winding (CW or CCW?) */
6562 int isccw = _lwt_EdgeRingIsCCW(ring);
6563
6564 if ( isccw )
6565 {
6566 /* Create new face */
6567 LWT_ISO_FACE newface;
6568
6569 LWDEBUGF(1, "Ring of edge %d is a shell (shell %d)", edge->edge_id * side, shells->size);
6570
6571 newface.mbr = _lwt_EdgeRingGetBbox(ring);
6572
6573 newface.face_id = -1;
6574 /* Insert the new face */
6575 int ret = lwt_be_insertFaces( topo, &newface, 1 );
6576 newface.mbr = NULL;
6577 if ( ret == -1 )
6578 {
6579 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
6580 return -1;
6581 }
6582 if ( ret != 1 )
6583 {
6584 lwerror("Unexpected error: %d faces inserted when expecting 1", ret);
6585 return -1;
6586 }
6587 /* return new face_id */
6588 *registered = newface.face_id;
6589 LWT_EDGERING_ARRAY_PUSH(shells, ring);
6590
6591 /* update ring edges set new face_id on resp. side to *registered */
6592 ret = _lwt_UpdateEdgeRingSideFace(topo, ring, *registered);
6593 if ( ret )
6594 {
6595 lwerror("Errors updating edgering side face: %s",
6597 return -1;
6598 }
6599
6600 }
6601 else /* cw, so is an hole */
6602 {
6603 LWDEBUGF(1, "Ring of edge %d is a hole (hole %d)", edge->edge_id * side, holes->size);
6604 *registered = placeholder_faceid;
6605 LWT_EDGERING_ARRAY_PUSH(holes, ring);
6606 }
6607
6608 return 0;
6609}
6610
6611static void
6612_lwt_AccumulateCanditates(void* item, void* userdata)
6613{
6614 LWT_EDGERING_ARRAY *candidates = userdata;
6615 LWT_EDGERING *sring = item;
6616 LWT_EDGERING_ARRAY_PUSH(candidates, sring);
6617}
6618
6619static LWT_ELEMID
6621 LWT_EDGERING_ARRAY *shells)
6622{
6623 LWT_ELEMID foundInFace = -1;
6624 int i;
6625 const GBOX *minenv = NULL;
6626 POINT2D pt;
6627 const GBOX *testbox;
6628 GEOSGeometry *ghole;
6629
6630 getPoint2d_p( ring->elems[0]->edge->geom->points, 0, &pt );
6631
6632 testbox = _lwt_EdgeRingGetBbox(ring);
6633
6634 /* Create a GEOS Point from a vertex of the hole ring */
6635 {
6636 LWPOINT *point = lwpoint_make2d(topo->srid, pt.x, pt.y);
6637 ghole = LWGEOM2GEOS( lwpoint_as_lwgeom(point), 1 );
6638 lwpoint_free(point);
6639 if ( ! ghole ) {
6640 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
6641 return -1;
6642 }
6643 }
6644
6645 /* Build STRtree of shell envelopes */
6646 if ( ! shells->tree )
6647 {
6648 static const int STRTREE_NODE_CAPACITY = 10;
6649 LWDEBUG(1, "Building STRtree");
6650 shells->tree = GEOSSTRtree_create(STRTREE_NODE_CAPACITY);
6651 if (shells->tree == NULL)
6652 {
6653 lwerror("Could not create GEOS STRTree: %s", lwgeom_geos_errmsg);
6654 return -1;
6655 }
6656 for (i=0; i<shells->size; ++i)
6657 {
6658 LWT_EDGERING *sring = shells->rings[i];
6659 const GBOX* shellbox = _lwt_EdgeRingGetBbox(sring);
6660 LWDEBUGF(2, "GBOX of shell %p for edge %d is %g %g,%g %g",
6661 sring, sring->elems[0]->edge->edge_id, shellbox->xmin,
6662 shellbox->ymin, shellbox->xmax, shellbox->ymax);
6663 POINTARRAY *pa = ptarray_construct(0, 0, 2);
6664 POINT4D pt;
6665 LWLINE *diag;
6666 pt.x = shellbox->xmin;
6667 pt.y = shellbox->ymin;
6668 ptarray_set_point4d(pa, 0, &pt);
6669 pt.x = shellbox->xmax;
6670 pt.y = shellbox->ymax;
6671 ptarray_set_point4d(pa, 1, &pt);
6672 diag = lwline_construct(topo->srid, NULL, pa);
6673 /* Record just envelope in ggeom */
6674 /* making valid, probably not needed */
6675 sring->genv = LWGEOM2GEOS( lwline_as_lwgeom(diag), 1 );
6676 lwline_free(diag);
6677 GEOSSTRtree_insert(shells->tree, sring->genv, sring);
6678 }
6679 LWDEBUG(1, "STRtree build completed");
6680 }
6681
6682 LWT_EDGERING_ARRAY candidates;
6683 LWT_EDGERING_ARRAY_INIT(&candidates);
6684 GEOSSTRtree_query(shells->tree, ghole, &_lwt_AccumulateCanditates, &candidates);
6685 LWDEBUGF(1, "Found %d candidate shells containing first point of ring's originating edge %d",
6686 candidates.size, ring->elems[0]->edge->edge_id * ( ring->elems[0]->left ? 1 : -1 ) );
6687
6688 /* TODO: sort candidates by bounding box size */
6689
6690 for (i=0; i<candidates.size; ++i)
6691 {
6692 LWT_EDGERING *sring = candidates.rings[i];
6693 const GBOX* shellbox = _lwt_EdgeRingGetBbox(sring);
6694 int contains = 0;
6695
6696 if ( sring->elems[0]->edge->edge_id == ring->elems[0]->edge->edge_id )
6697 {
6698 LWDEBUGF(1, "Shell %d is on other side of ring",
6699 _lwt_EdgeRingGetFace(sring));
6700 continue;
6701 }
6702
6703 /* The hole envelope cannot equal the shell envelope */
6704 if ( gbox_same(shellbox, testbox) )
6705 {
6706 LWDEBUGF(1, "Bbox of shell %d equals that of hole ring",
6707 _lwt_EdgeRingGetFace(sring));
6708 continue;
6709 }
6710
6711 /* Skip if ring box is not in shell box */
6712 if ( ! gbox_contains_2d(shellbox, testbox) )
6713 {
6714 LWDEBUGF(1, "Bbox of shell %d does not contain bbox of ring point",
6715 _lwt_EdgeRingGetFace(sring));
6716 continue;
6717 }
6718
6719 /* Skip test if a containing shell was already found
6720 * and this shell's bbox is not contained in the other */
6721 if ( minenv && ! gbox_contains_2d(minenv, shellbox) )
6722 {
6723 LWDEBUGF(2, "Bbox of shell %d (face %d) not contained by bbox "
6724 "of last shell found to contain the point",
6725 i, _lwt_EdgeRingGetFace(sring));
6726 continue;
6727 }
6728
6730 if ( contains )
6731 {
6732 /* Continue until all shells are tested, as we want to
6733 * use the one with the smallest bounding box */
6734 /* IDEA: sort shells by bbox size, stopping on first match */
6735 LWDEBUGF(1, "Shell %d contains hole of edge %d",
6736 _lwt_EdgeRingGetFace(sring),
6737 ring->elems[0]->edge->edge_id);
6738 minenv = shellbox;
6739 foundInFace = _lwt_EdgeRingGetFace(sring);
6740 }
6741 }
6742 if ( foundInFace == -1 ) foundInFace = 0;
6743
6744 candidates.size = 0; /* Avoid destroying the actual shell rings */
6745 LWT_EDGERING_ARRAY_CLEAN(&candidates);
6746
6747 GEOSGeom_destroy(ghole);
6748
6749 return foundInFace;
6750}
6751
6752/*
6753 * @return -1 on error (and report error),
6754 * 1 if faces beside the universal one exist
6755 * 0 otherwise
6756 */
6757static int
6759{
6760 LWT_ISO_FACE *faces;
6761 int fields = LWT_COL_FACE_FACE_ID;
6762 uint64_t nelems = 1;
6763 GBOX qbox;
6764
6765 qbox.xmin = qbox.ymin = -DBL_MAX;
6766 qbox.xmax = qbox.ymax = DBL_MAX;
6767 faces = lwt_be_getFaceWithinBox2D( topo, &qbox, &nelems, fields, 1);
6768 if (nelems == UINT64_MAX)
6769 {
6770 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
6771 return -1;
6772 }
6773 if ( faces ) _lwt_release_faces(faces, nelems);
6774 return nelems;
6775}
6776
6777int
6779{
6780 /*
6781 Fetch all edges
6782 Sort edges by edge_id
6783 Mark all edges' left and right face as -1
6784 Iteratively:
6785 Fetch next edge with left or right face == -1
6786 For each side with face == -1:
6787 Find ring on its side
6788 If ring is CCW:
6789 create a new face, assign to the ring edges' appropriate side
6790 If ring is CW (face needs to be same of external):
6791 assign a negative face_id the ring edges' appropriate side
6792 Now for each edge with a negative face_id on the side:
6793 Find containing face (mbr cache and all)
6794 Update with id of containing face
6795 */
6796
6797 const LWT_BE_IFACE *iface = topo->be_iface;
6798 LWT_ISO_EDGE *edge;
6799 int numfaces = -1;
6800 LWT_ISO_EDGE_TABLE edgetable;
6801 LWT_EDGERING_ARRAY holes, shells;
6802 int i;
6803 int err = 0;
6804
6806 LWT_EDGERING_ARRAY_INIT(&shells);
6807
6808 initGEOS(lwnotice, lwgeom_geos_error);
6809
6810 /*
6811 Check if Topology already contains some Face
6812 (ignoring the Universal Face)
6813 */
6814 numfaces = _lwt_CheckFacesExist(topo);
6815 if ( numfaces != 0 ) {
6816 if ( numfaces > 0 ) {
6817 /* Faces exist */
6818 lwerror("Polygonize: face table is not empty.");
6819 }
6820 /* Backend error, message should have been printed already */
6821 return -1;
6822 }
6823
6824
6825 edgetable.edges = _lwt_FetchAllEdges(topo, &(edgetable.size));
6826 if ( ! edgetable.edges ) {
6827 if (edgetable.size == 0) {
6828 /* not an error: no Edges */
6829 return 0;
6830 }
6831 /* error should have been printed already */
6832 return -1;
6833 }
6834
6835 /* Sort edges by ID (to allow btree searches) */
6836 qsort(edgetable.edges, edgetable.size, sizeof(LWT_ISO_EDGE), compare_iso_edges_by_id);
6837
6838 /* Mark all edges as unvisited */
6839 for (i=0; i<edgetable.size; ++i)
6840 edgetable.edges[i].face_left = edgetable.edges[i].face_right = -1;
6841
6842 i = 0;
6843 while (1)
6844 {
6845 i = _lwt_FetchNextUnvisitedEdge(topo, &edgetable, i);
6846 if ( i < 0 ) break; /* end of unvisited */
6847 edge = &(edgetable.edges[i]);
6848
6849 LWT_ELEMID newface = -1;
6850
6851 LWDEBUGF(1, "Next face-missing edge has id:%d, face_left:%d, face_right:%d",
6852 edge->edge_id, edge->face_left, edge->face_right);
6853 if ( edge->face_left == -1 )
6854 {
6855 err = _lwt_RegisterFaceOnEdgeSide(topo, edge, 1, &edgetable,
6856 &holes, &shells, &newface);
6857 if ( err ) break;
6858 LWDEBUGF(1, "New face on the left of edge %d is %d",
6859 edge->edge_id, newface);
6860 edge->face_left = newface;
6861 }
6862 if ( edge->face_right == -1 )
6863 {
6864 err = _lwt_RegisterFaceOnEdgeSide(topo, edge, -1, &edgetable,
6865 &holes, &shells, &newface);
6866 if ( err ) break;
6867 LWDEBUGF(1, "New face on the right of edge %d is %d",
6868 edge->edge_id, newface);
6869 edge->face_right = newface;
6870 }
6871 }
6872
6873 if ( err )
6874 {
6875 _lwt_release_edges(edgetable.edges, edgetable.size);
6876 LWT_EDGERING_ARRAY_CLEAN( &holes );
6877 LWT_EDGERING_ARRAY_CLEAN( &shells );
6878 lwerror("Errors fetching or registering face-missing edges: %s",
6880 return -1;
6881 }
6882
6883 LWDEBUGF(1, "Found %d holes and %d shells", holes.size, shells.size);
6884
6885 /* TODO: sort holes by pt.x, sort shells by bbox.xmin */
6886
6887 /* Assign shells to holes */
6888 for (i=0; i<holes.size; ++i)
6889 {
6890 LWT_ELEMID containing_face;
6891 LWT_EDGERING *ring = holes.rings[i];
6892
6893 containing_face = _lwt_FindFaceContainingRing(topo, ring, &shells);
6894 LWDEBUGF(1, "Ring %d contained by face %" LWTFMT_ELEMID, i, containing_face);
6895 if ( containing_face == -1 )
6896 {
6897 _lwt_release_edges(edgetable.edges, edgetable.size);
6898 LWT_EDGERING_ARRAY_CLEAN( &holes );
6899 LWT_EDGERING_ARRAY_CLEAN( &shells );
6900 lwerror("Errors finding face containing ring: %s",
6902 return -1;
6903 }
6904 int ret = _lwt_UpdateEdgeRingSideFace(topo, holes.rings[i], containing_face);
6905 if ( ret )
6906 {
6907 _lwt_release_edges(edgetable.edges, edgetable.size);
6908 LWT_EDGERING_ARRAY_CLEAN( &holes );
6909 LWT_EDGERING_ARRAY_CLEAN( &shells );
6910 lwerror("Errors updating edgering side face: %s",
6912 return -1;
6913 }
6914 }
6915
6916 LWDEBUG(1, "All holes assigned, cleaning up");
6917
6918 _lwt_release_edges(edgetable.edges, edgetable.size);
6919
6920 /* delete all shell and hole EDGERINGS */
6921 LWT_EDGERING_ARRAY_CLEAN( &holes );
6922 LWT_EDGERING_ARRAY_CLEAN( &shells );
6923
6924 return 0;
6925}
char * r
Definition cu_in_wkt.c:24
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
int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout)
Update the output GBOX to be large enough to include both inputs.
Definition gbox.c:135
void gbox_expand(GBOX *g, double d)
Move the box minimums down and the maximums up by the distance provided.
Definition gbox.c:97
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,...)
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition lwutil.c:216
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition lwgeom.c:326
char lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
geom1 same as geom2 iff
Definition lwgeom.c:573
#define LW_FALSE
Definition liblwgeom.h:108
LWGEOM * lwgeom_unaryunion(const LWGEOM *geom1)
#define COLLECTIONTYPE
Definition liblwgeom.h:122
LWCOLLECTION * lwcollection_extract(LWCOLLECTION *col, int type)
Takes a potentially heterogeneous collection and returns a homogeneous collection consisting only of ...
void * lwrealloc(void *mem, size_t size)
Definition lwutil.c:235
LWGEOM * lwgeom_split(const LWGEOM *lwgeom_in, const LWGEOM *blade_in)
LWGEOM * lwgeom_closest_point(const LWGEOM *lw1, const LWGEOM *lw2)
Definition measures.c:52
int azimuth_pt_pt(const POINT2D *p1, const POINT2D *p2, double *ret)
Compute the azimuth of segment AB in radians.
Definition measures.c:2461
LWGEOM * lwgeom_node(const LWGEOM *lwgeom_in)
int ptarray_append_ptarray(POINTARRAY *pa1, POINTARRAY *pa2, double gap_tolerance)
Append a POINTARRAY, pa2 to the end of an existing POINTARRAY, pa1.
Definition ptarray.c:177
LWGEOM * lwgeom_linemerge(const LWGEOM *geom1)
void lwpoint_free(LWPOINT *pt)
Definition lwpoint.c:213
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1138
LWPOINT * lwline_get_lwpoint(const LWLINE *line, uint32_t where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition lwline.c:309
#define MULTILINETYPE
Definition liblwgeom.h:120
#define LINETYPE
Definition liblwgeom.h:117
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition lwgeom.c:215
#define WKT_EXTENDED
Definition liblwgeom.h:2132
#define LW_SUCCESS
Definition liblwgeom.h:111
char * lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out)
WKT emitter function.
Definition lwout_wkt.c:676
#define MULTIPOINTTYPE
Definition liblwgeom.h:119
LWGEOM * lwgeom_snap(const LWGEOM *geom1, const LWGEOM *geom2, double tolerance)
Snap vertices and segments of a geometry to another using a given tolerance.
int lwgeom_is_simple(const LWGEOM *lwgeom)
int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point)
Definition lwgeom_api.c:349
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition lwgeom.c:916
void * lwalloc(size_t size)
Definition lwutil.c:227
LWGEOM * lwgeom_intersection(const LWGEOM *geom1, const LWGEOM *geom2)
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition lwgeom.c:197
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition lwline.c:42
int ptarray_insert_point(POINTARRAY *pa, const POINT4D *p, uint32_t where)
Insert a point into an existing POINTARRAY.
Definition ptarray.c:85
uint32_t lwgeom_count_vertices(const LWGEOM *geom)
Count the total number of vertices in any LWGEOM.
Definition lwgeom.c:1229
void lwfree(void *mem)
Definition lwutil.c:242
double lwgeom_mindistance2d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance)
Function handling min distance calculations and dwithin calculations.
Definition measures.c:207
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition lwpoint.c:163
LWGEOM * lwgeom_force_3dz(const LWGEOM *geom)
Definition lwgeom.c:781
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition lwgeom.c:321
LWGEOM * lwgeom_difference(const LWGEOM *geom1, const LWGEOM *geom2)
#define __attribute__(x)
Definition liblwgeom.h:242
void lwcollection_release(LWCOLLECTION *lwcollection)
double lwgeom_mindistance2d(const LWGEOM *lw1, const LWGEOM *lw2)
Function initializing min distance calculation.
Definition measures.c:197
#define WKT_ISO
Definition liblwgeom.h:2130
void lwcollection_free(LWCOLLECTION *col)
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition lwgeom_api.c:125
void ptarray_free(POINTARRAY *pa)
Definition ptarray.c:327
LWPOLY * lwpoly_construct(int32_t srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
Definition lwpoly.c:43
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition lwgeom.c:161
void lwline_setPoint4d(LWLINE *line, uint32_t which, POINT4D *newpoint)
Definition lwline.c:364
LWGEOM * lwgeom_union(const LWGEOM *geom1, const LWGEOM *geom2)
int ptarray_is_closed_2d(const POINTARRAY *pa)
Definition ptarray.c:701
void lwpoly_free(LWPOLY *poly)
Definition lwpoly.c:175
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:725
LWPOLY * lwpoly_construct_empty(int32_t srid, char hasz, char hasm)
Definition lwpoly.c:161
LWGEOM * lwgeom_buildarea(const LWGEOM *geom)
Take a geometry and return an areal geometry (Polygon or MultiPolygon).
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition lwgeom_api.c:376
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition lwgeom.c:311
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition lwgeom.c:677
void lwline_free(LWLINE *line)
Definition lwline.c:67
LWGEOM * lwgeom_make_valid(LWGEOM *geom)
Attempts to make an invalid geometries valid w/out losing points.
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition lwgeom.c:291
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
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition ptarray.c:634
LWGEOM * lwgeom_remove_repeated_points(const LWGEOM *in, double tolerance)
Definition lwgeom.c:1454
void lwgeom_reverse_in_place(LWGEOM *lwgeom)
Reverse vertex order of LWGEOM.
Definition lwgeom.c:102
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition lwgeom.c:511
int ptarray_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number)
Definition ptarray.c:746
LWGEOM * lwline_remove_repeated_points(const LWLINE *in, double tolerance)
Definition lwline.c:439
#define LW_INSIDE
Constants for point-in-polygon return values.
#define LW_BOUNDARY
void ptarray_reverse_in_place(POINTARRAY *pa)
Definition ptarray.c:339
int lwline_is_empty(const LWLINE *line)
#define LW_ON_INTERRUPT(x)
int lwpoint_is_empty(const LWPOINT *point)
int ptarray_contains_point(const POINTARRAY *pa, const POINT2D *pt)
Return 1 if the point is inside the POINTARRAY, -1 if it is outside, and 0 if it is on the boundary.
Definition ptarray.c:740
#define FP_ABS(a)
int lwpoly_is_empty(const LWPOLY *poly)
#define LW_OUTSIDE
int ptarray_isccw(const POINTARRAY *pa)
Definition ptarray.c:1034
POINTARRAY * ptarray_clone(const POINTARRAY *ptarray)
Clone a POINTARRAY object.
Definition ptarray.c:665
int p2d_same(const POINT2D *p1, const POINT2D *p2)
Definition lwalgorithm.c:50
#define LWT_COL_EDGE_FACE_RIGHT
#define LWT_COL_FACE_FACE_ID
Face fields.
LWT_INT64 LWT_ELEMID
Identifier of topology element.
struct LWT_BE_TOPOLOGY_T LWT_BE_TOPOLOGY
Topology handler.
#define LWT_COL_FACE_ALL
#define LWT_COL_EDGE_START_NODE
#define LWT_COL_EDGE_FACE_LEFT
#define LWT_COL_EDGE_NEXT_RIGHT
#define LWT_COL_NODE_CONTAINING_FACE
#define LWT_COL_EDGE_EDGE_ID
Edge fields.
#define LWT_COL_EDGE_ALL
#define LWT_COL_NODE_GEOM
#define LWT_COL_EDGE_END_NODE
#define LWT_COL_EDGE_NEXT_LEFT
#define LWT_COL_NODE_NODE_ID
Node fields.
struct LWT_BE_DATA_T LWT_BE_DATA
Backend private data pointer.
#define LWT_COL_EDGE_GEOM
#define LWT_COL_NODE_ALL
static const int STRTREE_NODE_CAPACITY
#define LWDEBUG(level, msg)
Definition lwgeom_log.h:83
#define LWDEBUGF(level, msg,...)
Definition lwgeom_log.h:88
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition lwutil.c:190
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
Definition lwutil.c:177
#define LWDEBUGG(level, geom, msg)
Definition lwgeom_log.h:93
LWT_ELEMID lwt_AddPoint(LWT_TOPOLOGY *topo, LWPOINT *point, double tol)
Adds a point to the topology.
static LWT_EDGERING_POINT_ITERATOR * _lwt_EdgeRingIterator_begin(LWT_EDGERING *er)
static LWT_ISO_EDGE * lwt_be_getEdgeByNode(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
static LWGEOM * _lwt_toposnap(LWGEOM *src, LWGEOM *tgt, double tol)
struct edgeend_t edgeend
#define CBT2(to, method, a1, a2)
Definition lwgeom_topo.c:98
int lwt_be_ExistsEdgeIntersectingPoint(LWT_TOPOLOGY *topo, LWPOINT *pt)
struct LWT_ISO_EDGE_TABLE_T LWT_ISO_EDGE_TABLE
static uint64_t lwt_be_updateFacesById(LWT_TOPOLOGY *topo, const LWT_ISO_FACE *faces, uint64_t numfaces)
static double _lwt_EdgeRingSignedArea(LWT_EDGERING_POINT_ITERATOR *it)
void lwt_BackendIfaceRegisterCallbacks(LWT_BE_IFACE *iface, const LWT_BE_CALLBACKS *cb)
Register backend callbacks into the opaque iface handler.
Definition lwgeom_topo.c:60
static int _lwt_FindNextRingEdge(const POINTARRAY *ring, int from, const LWT_ISO_EDGE *edges, int numedges)
static int lwt_be_deleteNodesById(const LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems)
int lwt_RemoveIsoNode(LWT_TOPOLOGY *topo, LWT_ELEMID nid)
Remove an isolated node.
struct LWT_EDGERING_POINT_ITERATOR_T LWT_EDGERING_POINT_ITERATOR
#define CB1(be, method, a1)
Definition lwgeom_topo.c:86
static int _lwt_EdgeRingContainsPoint(LWT_EDGERING *ring, POINT2D *p)
static int lwt_be_deleteFacesById(const LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems)
static int lwt_be_checkTopoGeomRemEdge(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id, LWT_ELEMID face_left, LWT_ELEMID face_right)
static LWT_ELEMID _lwt_AddEdge(LWT_TOPOLOGY *topo, LWT_ELEMID start_node, LWT_ELEMID end_node, LWLINE *geom, int skipChecks, int modFace)
LWT_ISO_NODE * lwt_be_getNodeWithinDistance2D(LWT_TOPOLOGY *topo, LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit)
static int _lwt_InitEdgeEndByLine(edgeend *fee, edgeend *lee, LWLINE *edge, POINT2D *fp, POINT2D *lp)
static LWT_ISO_EDGE * lwt_be_getEdgeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
static double lwt_be_topoGetPrecision(LWT_TOPOLOGY *topo)
static int _lwt_UpdateEdgeFaceRef(LWT_TOPOLOGY *topo, LWT_ELEMID of, LWT_ELEMID nf)
void lwt_FreeBackendIface(LWT_BE_IFACE *iface)
Release memory associated with an LWT_BE_IFACE.
Definition lwgeom_topo.c:66
static int _lwt_GetInteriorEdgePoint(const LWLINE *edge, POINT2D *ip)
LWT_ELEMID lwt_GetEdgeByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol)
Find the edge-id of an edge that intersects a given point.
LWT_ELEMID lwt_AddEdgeModFace(LWT_TOPOLOGY *topo, LWT_ELEMID start_node, LWT_ELEMID end_node, LWLINE *geom, int skipChecks)
Add a new edge possibly splitting a face (modifying it)
struct LWT_EDGERING_ARRAY_T LWT_EDGERING_ARRAY
#define CBT3(to, method, a1, a2, a3)
LWT_ELEMID lwt_AddIsoNode(LWT_TOPOLOGY *topo, LWT_ELEMID face, LWPOINT *pt, int skipISOChecks)
Add an isolated node.
LWT_ISO_EDGE * lwt_be_getEdgeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
LWT_ELEMID * lwt_AddPolygon(LWT_TOPOLOGY *topo, LWPOLY *poly, double tol, int *nfaces)
Adds a polygon to the topology.
LWT_ELEMID lwt_ModEdgeSplit(LWT_TOPOLOGY *topo, LWT_ELEMID edge, LWPOINT *pt, int skipISOChecks)
Split an edge by a node, modifying the original edge and adding a new one.
static int lwt_be_updateTopoGeomEdgeHeal(LWT_TOPOLOGY *topo, LWT_ELEMID edge1, LWT_ELEMID edge2, LWT_ELEMID newedge)
#define CBT1(to, method, a1)
Definition lwgeom_topo.c:94
#define LWT_EDGERING_INIT(a)
static int _lwt_FirstDistinctVertex2D(const POINTARRAY *pa, POINT2D *ref, int from, int dir, POINT2D *op)
static GBOX * _lwt_EdgeRingGetBbox(LWT_EDGERING *ring)
LWT_ELEMID lwt_be_getNextEdgeId(LWT_TOPOLOGY *topo)
LWT_ELEMID * lwt_AddLineNoFace(LWT_TOPOLOGY *topo, LWLINE *line, double tol, int *nedges)
Adds a linestring to the topology without determining generated faces.
static LWT_ISO_NODE * _lwt_GetIsoNode(LWT_TOPOLOGY *topo, LWT_ELEMID nid)
static LWT_ELEMID _lwt_AddIsoNode(LWT_TOPOLOGY *topo, LWT_ELEMID face, LWPOINT *pt, int skipISOChecks, int checkFace)
static LWT_ELEMID _lwt_EdgeRingGetFace(LWT_EDGERING *ring)
LWT_ELEMID lwt_AddIsoEdge(LWT_TOPOLOGY *topo, LWT_ELEMID startNode, LWT_ELEMID endNode, const LWLINE *geom)
Add an isolated edge connecting two existing isolated nodes.
static LWT_EDGERING * _lwt_BuildEdgeRing(__attribute__((__unused__)) LWT_TOPOLOGY *topo, LWT_ISO_EDGE_TABLE *edges, LWT_ISO_EDGE *edge, int side)
static int lwt_be_updateTopoGeomFaceSplit(LWT_TOPOLOGY *topo, LWT_ELEMID split_face, LWT_ELEMID new_face1, LWT_ELEMID new_face2)
static int lwt_be_updateNodesById(LWT_TOPOLOGY *topo, const LWT_ISO_NODE *nodes, int numnodes, int upd_fields)
LWT_ELEMID * lwt_AddLine(LWT_TOPOLOGY *topo, LWLINE *line, double tol, int *nedges)
Adds a linestring to the topology.
LWT_ELEMID lwt_NewEdgesSplit(LWT_TOPOLOGY *topo, LWT_ELEMID edge, LWPOINT *pt, int skipISOChecks)
Split an edge by a node, replacing it with two new edges.
static int _lwt_CheckEdgeCrossing(LWT_TOPOLOGY *topo, LWT_ELEMID start_node, LWT_ELEMID end_node, const LWLINE *geom, LWT_ELEMID myself)
static void _lwt_release_nodes(LWT_ISO_NODE *nodes, int num_nodes)
LWT_ELEMID lwt_be_getFaceContainingPoint(LWT_TOPOLOGY *topo, LWPOINT *pt)
LWT_ELEMID lwt_RemEdgeNewFace(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id)
Remove an edge, possibly merging two faces (replacing both with a new one)
static double _lwt_minToleranceDouble(double d)
static int lwt_be_checkTopoGeomRemNode(LWT_TOPOLOGY *topo, LWT_ELEMID node_id, LWT_ELEMID eid1, LWT_ELEMID eid2)
static int lwt_be_updateTopoGeomFaceHeal(LWT_TOPOLOGY *topo, LWT_ELEMID face1, LWT_ELEMID face2, LWT_ELEMID newface)
static int lwt_be_insertFaces(LWT_TOPOLOGY *topo, LWT_ISO_FACE *face, uint64_t numelems)
int lwt_be_updateTopoGeomEdgeSplit(LWT_TOPOLOGY *topo, LWT_ELEMID split_edge, LWT_ELEMID new_edge1, LWT_ELEMID new_edge2)
LWT_TOPOLOGY * lwt_LoadTopology(LWT_BE_IFACE *iface, const char *name)
Loads an existing topology by name from the database.
static double _lwt_minTolerance(LWGEOM *g)
static int _lwt_UpdateEdgeRingSideFace(LWT_TOPOLOGY *topo, LWT_EDGERING *ring, LWT_ELEMID face)
static int _lwt_EdgeRingIsCCW(LWT_EDGERING *ring)
int lwt_be_insertNodes(LWT_TOPOLOGY *topo, LWT_ISO_NODE *node, uint64_t numelems)
int lwt_Polygonize(LWT_TOPOLOGY *topo)
LWT_ELEMID lwt_AddEdgeNewFaces(LWT_TOPOLOGY *topo, LWT_ELEMID start_node, LWT_ELEMID end_node, LWLINE *geom, int skipChecks)
Add a new edge possibly splitting a face (replacing with two new faces)
static LWT_ELEMID * lwt_be_getRingEdges(LWT_TOPOLOGY *topo, LWT_ELEMID edge, uint64_t *numedges, uint64_t limit)
static int _lwt_EdgeRingIterator_next(LWT_EDGERING_POINT_ITERATOR *it, POINT2D *pt)
#define LWT_HOLES_FACE_PLACEHOLDER
static int _lwt_CheckFacesExist(LWT_TOPOLOGY *topo)
LWT_BE_TOPOLOGY * lwt_be_loadTopologyByName(LWT_BE_IFACE *be, const char *name)
int lwt_be_updateEdges(LWT_TOPOLOGY *topo, const LWT_ISO_EDGE *sel_edge, int sel_fields, const LWT_ISO_EDGE *upd_edge, int upd_fields, const LWT_ISO_EDGE *exc_edge, int exc_fields)
LWT_BE_IFACE * lwt_CreateBackendIface(const LWT_BE_DATA *data)
Create a new backend interface.
Definition lwgeom_topo.c:52
static void _lwt_RotateElemidArray(LWT_ELEMID *ary, int from, int to, int rotidx)
static LWT_ELEMID _lwt_GetEqualEdge(LWT_TOPOLOGY *topo, LWLINE *edge)
#define LWT_EDGERING_ARRAY_INIT(a)
struct LWT_EDGERING_ELEM_T LWT_EDGERING_ELEM
static LWT_ELEMID _lwt_AddLineEdge(LWT_TOPOLOGY *topo, LWLINE *edge, double tol, int handleFaceSplit)
static LWT_ISO_FACE * lwt_be_getFaceById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
static int compare_scored_pointer(const void *si1, const void *si2)
LWT_ELEMID lwt_RemEdgeModFace(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id)
Remove an edge, possibly merging two faces (replacing one with the other)
static LWT_ISO_FACE * lwt_be_getFaceWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
int lwt_be_deleteEdges(LWT_TOPOLOGY *topo, const LWT_ISO_EDGE *sel_edge, int sel_fields)
static LWT_ISO_EDGE * lwt_be_getEdgeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box)
int lwt_GetFaceEdges(LWT_TOPOLOGY *topo, LWT_ELEMID face_id, LWT_ELEMID **out)
Return the list of directed edges bounding a face.
static int _lwt_UpdateNodeFaceRef(LWT_TOPOLOGY *topo, LWT_ELEMID of, LWT_ELEMID nf)
#define CBT4(to, method, a1, a2, a3, a4)
int lwt_MoveIsoNode(LWT_TOPOLOGY *topo, LWT_ELEMID nid, LWPOINT *pt)
Move an isolated node.
static LWT_ISO_EDGE * _lwt_getIsoEdgeById(LWT_ISO_EDGE_TABLE *tab, LWT_ELEMID id)
static LWGEOM * _lwt_split_by_nodes(const LWGEOM *g, const LWGEOM *nodes)
static int _lwt_FindAdjacentEdges(LWT_TOPOLOGY *topo, LWT_ELEMID node, edgeend *data, edgeend *other, int myedge_id)
static int lwt_be_topoHasZ(LWT_TOPOLOGY *topo)
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)
#define CB0(be, method)
Definition lwgeom_topo.c:82
LWT_ELEMID lwt_ModEdgeHeal(LWT_TOPOLOGY *topo, LWT_ELEMID e1, LWT_ELEMID e2)
Merge two edges, modifying the first and deleting the second.
static LWPOLY * _lwt_MakeRingShell(LWT_TOPOLOGY *topo, LWT_ELEMID *signed_edge_ids, uint64_t num_signed_edge_ids)
static void _lwt_AccumulateCanditates(void *item, void *userdata)
static void _lwt_release_faces(LWT_ISO_FACE *faces, int num_faces)
static LWT_ISO_EDGE * _lwt_FetchAllEdges(LWT_TOPOLOGY *topo, int *numedges)
#define CBT0(to, method)
Definition lwgeom_topo.c:90
#define LWT_EDGERING_PUSH(a, r)
static LWT_ELEMID _lwt_AddFaceSplit(LWT_TOPOLOGY *topo, LWT_ELEMID sedge, LWT_ELEMID face, int mbr_only)
static LWT_ELEMID _lwt_FindFaceContainingRing(LWT_TOPOLOGY *topo, LWT_EDGERING *ring, LWT_EDGERING_ARRAY *shells)
#define _LWT_MINTOLERANCE(topo, geom)
#define CBT6(to, method, a1, a2, a3, a4, a5, a6)
int lwt_be_freeTopology(LWT_TOPOLOGY *topo)
void lwt_FreeTopology(LWT_TOPOLOGY *topo)
Release memory associated with an LWT_TOPOLOGY.
LWT_ELEMID lwt_NewEdgeHeal(LWT_TOPOLOGY *topo, LWT_ELEMID e1, LWT_ELEMID e2)
Merge two edges, replacing both with a new one.
#define CBT5(to, method, a1, a2, a3, a4, a5)
int lwt_be_insertEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edge, uint64_t numelems)
#define LWT_EDGERING_ARRAY_CLEAN(a)
int lwt_be_ExistsCoincidentNode(LWT_TOPOLOGY *topo, LWPOINT *pt)
static LWT_ELEMID _lwt_HealEdges(LWT_TOPOLOGY *topo, LWT_ELEMID eid1, LWT_ELEMID eid2, int modEdge)
static LWT_ISO_NODE * lwt_be_getNodeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box)
static int compare_iso_edges_by_id(const void *si1, const void *si2)
const char * lwt_be_lastErrorMessage(const LWT_BE_IFACE *be)
static LWT_ISO_NODE * lwt_be_getNodeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
static LWT_ELEMID * _lwt_AddLine(LWT_TOPOLOGY *topo, LWLINE *line, double tol, int *nedges, int handleFaceSplit)
static int _lwt_FetchNextUnvisitedEdge(__attribute__((__unused__)) LWT_TOPOLOGY *topo, LWT_ISO_EDGE_TABLE *etab, int from)
struct scored_pointer_t scored_pointer
#define LWT_EDGERING_ARRAY_PUSH(a, r)
LWT_ELEMID lwt_GetNodeByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol)
Retrieve the id of a node at a point location.
LWT_ISO_EDGE * lwt_be_getEdgeWithinDistance2D(LWT_TOPOLOGY *topo, LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit)
static int lwt_be_topoGetSRID(LWT_TOPOLOGY *topo)
#define LWTFMT_ELEMID
Definition lwgeom_topo.c:43
LWGEOM * lwt_GetFaceGeometry(LWT_TOPOLOGY *topo, LWT_ELEMID faceid)
Return the geometry of a face.
static void _lwt_ReverseElemidArray(LWT_ELEMID *ary, int from, int to)
static LWT_ELEMID _lwt_AddPoint(LWT_TOPOLOGY *topo, LWPOINT *point, double tol, int findFace, int *moved)
LWT_ISO_NODE * lwt_be_getNodeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
int lwt_RemIsoEdge(LWT_TOPOLOGY *topo, LWT_ELEMID id)
Remove an isolated edge.
static int lwt_be_updateNodes(LWT_TOPOLOGY *topo, const LWT_ISO_NODE *sel_node, int sel_fields, const LWT_ISO_NODE *upd_node, int upd_fields, const LWT_ISO_NODE *exc_node, int exc_fields)
static void _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
static int _lwt_EdgeRingCrossingCount(const POINT2D *p, LWT_EDGERING_POINT_ITERATOR *it)
int lwt_ChangeEdgeGeom(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id, LWLINE *geom)
Changes the shape of an edge without affecting the topology structure.
static int lwt_be_updateEdgesById(LWT_TOPOLOGY *topo, const LWT_ISO_EDGE *edges, int numedges, int upd_fields)
struct LWT_EDGERING_T LWT_EDGERING
static LWT_ELEMID _lwt_RemEdge(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id, int modFace)
LWT_ELEMID lwt_GetFaceByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol)
Find the face-id of a face containing a given point.
static LWCOLLECTION * _lwt_EdgeSplit(LWT_TOPOLOGY *topo, LWT_ELEMID edge, LWPOINT *pt, int skipISOChecks, LWT_ISO_EDGE **oldedge)
static LWGEOM * _lwt_FaceByEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edges, int numfaceedges)
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition lwinline.h:135
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition lwinline.h:121
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition lwinline.h:193
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:91
Datum contains(PG_FUNCTION_ARGS)
Datum covers(PG_FUNCTION_ARGS)
double ymax
Definition liblwgeom.h:343
double xmax
Definition liblwgeom.h:341
double ymin
Definition liblwgeom.h:342
double xmin
Definition liblwgeom.h:340
uint32_t ngeoms
Definition liblwgeom.h:566
LWGEOM ** geoms
Definition liblwgeom.h:561
int32_t srid
Definition liblwgeom.h:562
GBOX * bbox
Definition liblwgeom.h:444
int32_t srid
Definition liblwgeom.h:446
GBOX * bbox
Definition liblwgeom.h:468
POINTARRAY * points
Definition liblwgeom.h:469
int32_t srid
Definition liblwgeom.h:470
POINTARRAY * point
Definition liblwgeom.h:457
POINTARRAY ** rings
Definition liblwgeom.h:505
uint32_t nrings
Definition liblwgeom.h:510
Structure containing base backend callbacks.
const LWT_BE_DATA * data
const LWT_BE_CALLBACKS * cb
LWT_EDGERING ** rings
LWT_ISO_EDGE * edge
LWT_EDGERING_ELEM * curelem
GEOSGeometry * genv
LWT_EDGERING_ELEM ** elems
LWT_ISO_EDGE * edges
LWT_ELEMID face_right
LWT_ELEMID next_right
LWT_ELEMID end_node
LWT_ELEMID face_left
LWT_ELEMID next_left
LWT_ELEMID edge_id
LWT_ELEMID start_node
LWT_ELEMID face_id
LWT_ELEMID node_id
LWT_ELEMID containing_face
LWPOINT * geom
LWT_BE_TOPOLOGY * be_topo
const LWT_BE_IFACE * be_iface
double y
Definition liblwgeom.h:376
double x
Definition liblwgeom.h:376
double x
Definition liblwgeom.h:400
double z
Definition liblwgeom.h:400
double y
Definition liblwgeom.h:400
uint32_t npoints
Definition liblwgeom.h:413
double myaz
LWT_ELEMID nextCCW
LWT_ELEMID ccwFace
LWT_ELEMID cwFace
LWT_ELEMID nextCW