PostGIS 3.7.0dev-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-2024 Sandro Santilli <strk@kbt.io>
22 * Copyright (C) 2025 Darafei Praliaskouski <me@komzpa.net>
23 *
24 **********************************************************************/
25
26#include "../postgis_config.h"
27
28//#define POSTGIS_DEBUG_LEVEL 1
29//#define POSTGIS_DEBUG_GEOMETRY_WKB 1
30#include "lwgeom_log.h"
31
32#include "liblwgeom_internal.h"
34#include "lwt_node_edges.h"
35#include "lwt_edgeend_star.h"
36#include "lwgeom_geos.h"
37
38#include <inttypes.h>
39
40/*
41 * Report a human readable location along with topology errors so callers can
42 * narrow down robustness issues such as #5886/#5889 without further tracing.
43 * Using a point-on-surface keeps the message compact while remaining
44 * representative for any dimensionality of the offending intersection.
45 */
46static bool
47_lwt_describe_intersection_point(const GEOSGeometry *g1, const GEOSGeometry *g2, char *buf, size_t bufsize)
48{
49 unsigned int n = 0;
50 POINT2D pt;
51 GEOSGeometry *isect;
52 GEOSGeometry *surface_pt;
53 const GEOSCoordSequence *seq;
54
55 isect = GEOSIntersection(g1, g2);
56 if (!isect)
57 return false;
58
59 surface_pt = GEOSPointOnSurface(isect);
60 GEOSGeom_destroy(isect);
61 if (!surface_pt)
62 return false;
63
64 seq = GEOSGeom_getCoordSeq(surface_pt);
65 if (!seq)
66 {
67 GEOSGeom_destroy(surface_pt);
68 return false;
69 }
70
71 if (!GEOSCoordSeq_getSize(seq, &n) || !n)
72 {
73 GEOSGeom_destroy(surface_pt);
74 return false;
75 }
76
77 if (!GEOSCoordSeq_getX(seq, 0, &pt.x) || !GEOSCoordSeq_getY(seq, 0, &pt.y))
78 {
79 GEOSGeom_destroy(surface_pt);
80 return false;
81 }
82
83 GEOSGeom_destroy(surface_pt);
84 snprintf(buf, bufsize, " at POINT(%.15g %.15g)", pt.x, pt.y);
85 return true;
86}
87
88static bool
89_lwt_describe_point(const LWPOINT *pt, char *buf, size_t bufsize)
90{
91 POINT2D p;
92
93 if (!pt || !pt->point || !pt->point->npoints)
94 return false;
95
96 getPoint2d_p(pt->point, 0, &p);
97 snprintf(buf, bufsize, " at POINT(%.15g %.15g)", p.x, p.y);
98 return true;
99}
100
101/*********************************************************************
102 *
103 * Backend iface
104 *
105 ********************************************************************/
106
108{
109 LWT_BE_IFACE *iface = lwalloc(sizeof(LWT_BE_IFACE));
110 iface->data = data;
111 iface->cb = NULL;
112 return iface;
113}
114
116 const LWT_BE_CALLBACKS* cb)
117{
118 iface->cb = cb;
119}
120
122{
123 lwfree(iface);
124}
125
126/*********************************************************************
127 *
128 * Backend wrappers
129 *
130 ********************************************************************/
131
132#define CHECKCB(be, method) do { \
133 if ( ! (be)->cb || ! (be)->cb->method ) \
134 lwerror("Callback " # method " not registered by backend"); \
135} while (0)
136
137#define CB0(be, method) \
138 CHECKCB(be, method);\
139 return (be)->cb->method((be)->data)
140
141#define CB1(be, method, a1) \
142 CHECKCB(be, method);\
143 return (be)->cb->method((be)->data, a1)
144
145#define CBT0(to, method) \
146 CHECKCB((to)->be_iface, method);\
147 return (to)->be_iface->cb->method((to)->be_topo)
148
149#define CBT1(to, method, a1) \
150 CHECKCB((to)->be_iface, method);\
151 return (to)->be_iface->cb->method((to)->be_topo, a1)
152
153#define CBT2(to, method, a1, a2) \
154 CHECKCB((to)->be_iface, method);\
155 return (to)->be_iface->cb->method((to)->be_topo, a1, a2)
156
157#define CBT3(to, method, a1, a2, a3) \
158 CHECKCB((to)->be_iface, method);\
159 return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3)
160
161#define CBT4(to, method, a1, a2, a3, a4) \
162 CHECKCB((to)->be_iface, method);\
163 return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3, a4)
164
165#define CBT5(to, method, a1, a2, a3, a4, a5) \
166 CHECKCB((to)->be_iface, method);\
167 return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3, a4, a5)
168
169#define CBT6(to, method, a1, a2, a3, a4, a5, a6) \
170 CHECKCB((to)->be_iface, method);\
171 return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3, a4, a5, a6)
172
173const char *
175{
176 CB0(be, lastErrorMessage);
177}
178
181{
182 CB1(be, loadTopologyByName, name);
183}
184
185static int
187{
188 CBT0(topo, topoGetSRID);
189}
190
191static double
193{
194 CBT0(topo, topoGetPrecision);
195}
196
197static int
199{
200 CBT0(topo, topoHasZ);
201}
202
203int
205{
206 CBT0(topo, freeTopology);
207}
208
210lwt_be_getNodeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
211{
212 CBT3(topo, getNodeById, ids, numelems, fields);
213}
214
217 const LWPOINT *pt,
218 double dist,
219 uint64_t *numelems,
220 int fields,
221 int64_t limit)
222{
223 CBT5(topo, getNodeWithinDistance2D, pt, dist, numelems, fields, limit);
224}
225
226static LWT_ISO_NODE *
227lwt_be_getNodeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
228{
229 CBT4(topo, getNodeWithinBox2D, box, numelems, fields, limit);
230}
231
233lwt_be_getEdgeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
234{
235 CBT4(topo, getEdgeWithinBox2D, box, numelems, fields, limit);
236}
237
239lwt_be_getFaceWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
240{
241 CBT4(topo, getFaceWithinBox2D, box, numelems, fields, limit);
242}
243
244int
245lwt_be_insertNodes(LWT_TOPOLOGY *topo, LWT_ISO_NODE *node, uint64_t numelems)
246{
247 CBT2(topo, insertNodes, node, numelems);
248}
249
250int
251lwt_be_insertFaces(LWT_TOPOLOGY *topo, LWT_ISO_FACE *face, uint64_t numelems)
252{
253 CBT2(topo, insertFaces, face, numelems);
254}
255
256static int
257lwt_be_deleteFacesById(const LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems)
258{
259 CBT2(topo, deleteFacesById, ids, numelems);
260}
261
262static int
263lwt_be_deleteNodesById(const LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems)
264{
265 CBT2(topo, deleteNodesById, ids, numelems);
266}
267
270{
271 CBT0(topo, getNextEdgeId);
272}
273
275lwt_be_getEdgeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
276{
277 CBT3(topo, getEdgeById, ids, numelems, fields);
278}
279
280static LWT_ISO_FACE *
281lwt_be_getFaceById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
282{
283 CBT3(topo, getFaceById, ids, numelems, fields);
284}
285
287lwt_be_getEdgeByNode(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
288{
289 CBT3(topo, getEdgeByNode, ids, numelems, fields);
290}
291
292static LWT_ISO_EDGE *
293lwt_be_getEdgeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box)
294{
295 CBT4(topo, getEdgeByFace, ids, numelems, fields, box);
296}
297
298static LWT_ISO_NODE *
299lwt_be_getNodeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box)
300{
301 CBT4(topo, getNodeByFace, ids, numelems, fields, box);
302}
303
306 const LWPOINT *pt,
307 double dist,
308 uint64_t *numelems,
309 int fields,
310 int64_t limit)
311{
312 CBT5(topo, getEdgeWithinDistance2D, pt, dist, numelems, fields, limit);
313}
314
315int
316lwt_be_insertEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edge, uint64_t numelems)
317{
318 CBT2(topo, insertEdges, edge, numelems);
319}
320
321int
323 const LWT_ISO_EDGE* sel_edge, int sel_fields,
324 const LWT_ISO_EDGE* upd_edge, int upd_fields,
325 const LWT_ISO_EDGE* exc_edge, int exc_fields
326)
327{
328 CBT6(topo, updateEdges, sel_edge, sel_fields,
329 upd_edge, upd_fields,
330 exc_edge, exc_fields);
331}
332
333static int
335 const LWT_ISO_NODE* sel_node, int sel_fields,
336 const LWT_ISO_NODE* upd_node, int upd_fields,
337 const LWT_ISO_NODE* exc_node, int exc_fields
338)
339{
340 CBT6(topo, updateNodes, sel_node, sel_fields,
341 upd_node, upd_fields,
342 exc_node, exc_fields);
343}
344
345static uint64_t
347 const LWT_ISO_FACE* faces, uint64_t numfaces
348)
349{
350 CBT2(topo, updateFacesById, faces, numfaces);
351}
352
353int
355 const LWT_ISO_EDGE* edges, int numedges, int upd_fields
356)
357{
358 CBT3(topo, updateEdgesById, edges, numedges, upd_fields);
359}
360
361static int
363 const LWT_ISO_NODE* nodes, int numnodes, int upd_fields
364)
365{
366 CBT3(topo, updateNodesById, nodes, numnodes, upd_fields);
367}
368
369int
371 const LWT_ISO_EDGE* sel_edge, int sel_fields
372)
373{
374 CBT2(topo, deleteEdges, sel_edge, sel_fields);
375}
376
377int
379{
380 CBT3(topo, updateTopoGeomEdgeSplit, split_edge, new_edge1, new_edge2);
381}
382
383static int
385 LWT_ELEMID new_face1, LWT_ELEMID new_face2)
386{
387 CBT3(topo, updateTopoGeomFaceSplit, split_face, new_face1, new_face2);
388}
389
390static int
392 LWT_ELEMID face_left, LWT_ELEMID face_right)
393{
394 CBT3(topo, checkTopoGeomRemEdge, edge_id, face_left, face_right);
395}
396
397static int
399{
400 CBT1(topo, checkTopoGeomRemIsoEdge, edge_id);
401}
402
403static int
405 LWT_ELEMID eid1, LWT_ELEMID eid2)
406{
407 CBT3(topo, checkTopoGeomRemNode, node_id, eid1, eid2);
408}
409
410static int
412{
413 CBT1(topo, checkTopoGeomRemIsoNode, node_id);
414}
415
416static int
418 LWT_ELEMID face1, LWT_ELEMID face2,
419 LWT_ELEMID newface)
420{
421 CBT3(topo, updateTopoGeomFaceHeal, face1, face2, newface);
422}
423
424static int
426 LWT_ELEMID edge1, LWT_ELEMID edge2,
427 LWT_ELEMID newedge)
428{
429 CBT3(topo, updateTopoGeomEdgeHeal, edge1, edge2, newedge);
430}
431
432static LWT_ELEMID *
433lwt_be_getRingEdges(LWT_TOPOLOGY *topo, LWT_ELEMID edge, uint64_t *numedges, uint64_t limit)
434{
435 CBT3(topo, getRingEdges, edge, numedges, limit);
436}
437
440{
441 uint64_t exists = 0;
442 lwt_be_getNodeWithinDistance2D(topo, pt, 0, &exists, 0, -1);
443 if (exists == UINT64_MAX)
444 {
446 return 0;
447 }
448 return exists;
449}
450
453{
454 uint64_t exists = 0;
455 lwt_be_getEdgeWithinDistance2D(topo, pt, 0, &exists, 0, -1);
456 if (exists == UINT64_MAX)
457 {
459 return 0;
460 }
461 return exists;
462}
463
464static LWT_ISO_EDGE *
465lwt_be_getClosestEdge(const LWT_TOPOLOGY *topo, const LWPOINT *pt, uint64_t *numelems, int fields)
466{
467 CBT3(topo, getClosestEdge, pt, numelems, fields);
468}
469
470static GBOX *
472{
473 CBT1(topo, computeFaceMBR, face);
474}
475
476/************************************************************************
477 *
478 * Utility functions
479 *
480 ************************************************************************/
481
482static LWGEOM *
483_lwt_toposnap(LWGEOM *src, LWGEOM *tgt, double tol)
484{
485 LWGEOM *tmp = src;
486 LWGEOM *tmp2;
487 int changed;
488 int iterations = 0;
489
490 int maxiterations = lwgeom_count_vertices(tgt);
491
492 /* GEOS snapping can be unstable */
493 /* See https://trac.osgeo.org/geos/ticket/760 */
494 do {
495 tmp2 = lwgeom_snap(tmp, tgt, tol);
496 ++iterations;
497 changed = ( lwgeom_count_vertices(tmp2) != lwgeom_count_vertices(tmp) );
498 LWDEBUGF(2, "After iteration %d, geometry changed ? %d (%d vs %d vertices)", iterations, changed, lwgeom_count_vertices(tmp2), lwgeom_count_vertices(tmp));
499 if ( tmp != src ) lwgeom_free(tmp);
500 tmp = tmp2;
501 } while ( changed && iterations <= maxiterations );
502
503 LWDEBUGF(1, "It took %d/%d iterations to properly snap",
504 iterations, maxiterations);
505
506 return tmp;
507}
508
509void
510_lwt_release_faces(LWT_ISO_FACE *faces, int num_faces)
511{
512 int i;
513 for ( i=0; i<num_faces; ++i ) {
514 if ( faces[i].mbr ) lwfree(faces[i].mbr);
515 }
516 lwfree(faces);
517}
518
519void
520_lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
521{
522 int i;
523 for ( i=0; i<num_edges; ++i ) {
524 if ( edges[i].geom ) lwline_free(edges[i].geom);
525 }
526 lwfree(edges);
527}
528
529static void
530_lwt_release_nodes(LWT_ISO_NODE *nodes, int num_nodes)
531{
532 int i;
533 for ( i=0; i<num_nodes; ++i ) {
534 if ( nodes[i].geom ) lwpoint_free(nodes[i].geom);
535 }
536 lwfree(nodes);
537}
538
539/************************************************************************
540 *
541 * API implementation
542 *
543 ************************************************************************/
544
546lwt_LoadTopology( LWT_BE_IFACE *iface, const char *name )
547{
548 LWT_BE_TOPOLOGY* be_topo;
549 LWT_TOPOLOGY* topo;
550
551 be_topo = lwt_be_loadTopologyByName(iface, name);
552 if ( ! be_topo ) {
553 //lwerror("Could not load topology from backend: %s",
554 lwerror("%s", lwt_be_lastErrorMessage(iface));
555 return NULL;
556 }
557 topo = lwalloc(sizeof(LWT_TOPOLOGY));
558 topo->be_iface = iface;
559 topo->be_topo = be_topo;
560 topo->srid = lwt_be_topoGetSRID(topo);
561 topo->hasZ = lwt_be_topoHasZ(topo);
563
564 return topo;
565}
566
567void
569{
570 if ( ! lwt_be_freeTopology(topo) ) {
571 lwnotice("Could not release backend topology memory: %s",
573 }
574 lwfree(topo);
575}
576
585static LWT_ELEMID
587 LWPOINT* pt, int skipISOChecks, int checkFace )
588{
589 LWT_ELEMID foundInFace = -1;
590 char locinfo[128] = "";
591 const char *locsuffix = _lwt_describe_point(pt, locinfo, sizeof(locinfo)) ? locinfo : "";
592
593 if ( lwpoint_is_empty(pt) )
594 {
595 lwerror("Cannot add empty point as isolated node");
596 return -1;
597 }
598
599
600 if ( ! skipISOChecks )
601 {
602 if ( lwt_be_ExistsCoincidentNode(topo, pt) ) /*x*/
603 {
604 lwerror("SQL/MM Spatial exception - coincident node%s", locsuffix);
605 return -1;
606 }
607 if ( lwt_be_ExistsEdgeIntersectingPoint(topo, pt) ) /*x*/
608 {
609 lwerror("SQL/MM Spatial exception - edge crosses node.%s", locsuffix);
610 return -1;
611 }
612 }
613
614 if ( checkFace && ( face == -1 || ! skipISOChecks ) )
615 {
616 foundInFace = lwt_GetFaceContainingPoint(topo, pt); /*x*/
617 if ( foundInFace == -1 ) {
619 return -1;
620 }
621 if ( foundInFace == -1 ) foundInFace = 0;
622 }
623
624 if ( face == -1 ) {
625 face = foundInFace;
626 }
627 else if ( ! skipISOChecks && foundInFace != face ) {
628#if 0
629 lwerror("SQL/MM Spatial exception - within face %d (not %d)",
630 foundInFace, face);
631#else
632 lwerror("SQL/MM Spatial exception - not within face");
633#endif
634 return -1;
635 }
636
637 LWT_ISO_NODE node;
638 node.node_id = -1;
639 node.containing_face = face;
640 node.geom = pt;
641 if ( ! lwt_be_insertNodes(topo, &node, 1) )
642 {
644 return -1;
645 }
646
647 return node.node_id;
648}
649
652 LWPOINT* pt, int skipISOChecks )
653{
654 return _lwt_AddIsoNode( topo, face, pt, skipISOChecks, 1 );
655}
656
657/*
658 * Check that an edge does not cross an existing node and
659 * does not have non-boundary intersection with existing edge
660 *
661 * @param myself the id of an edge to skip, if any
662 * (for ChangeEdgeGeom). Can use 0 for none.
663 *
664 * Return -1 on cross or error, 0 if everything is fine.
665 * Note that before returning -1, lwerror is invoked...
666 */
667static int
669 LWT_ELEMID start_node, LWT_ELEMID end_node,
670 const LWLINE *geom, LWT_ELEMID myself )
671{
672 uint64_t i, num_nodes, num_edges;
673 LWT_ISO_EDGE *edges;
674 LWT_ISO_NODE *nodes;
675 const GBOX *edgebox;
676 GEOSGeometry *edgegg;
677
678 initGEOS(lwnotice, lwgeom_geos_error);
679
680 edgegg = LWGEOM2GEOS(lwline_as_lwgeom(geom), 0);
681 if (!edgegg)
682 {
683 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
684 return -1;
685 }
686 edgebox = lwgeom_get_bbox( lwline_as_lwgeom(geom) );
687
688 /* loop over each node within the edge's gbox */
689 nodes = lwt_be_getNodeWithinBox2D( topo, edgebox, &num_nodes,
690 LWT_COL_NODE_ALL, 0 );
691 LWDEBUGF(1, "lwt_be_getNodeWithinBox2D returned %llu nodes", num_nodes);
692 if (num_nodes == UINT64_MAX)
693 {
695 return -1;
696 }
697 for ( i=0; i<num_nodes; ++i )
698 {
699 POINT2D p;
700 LWT_ISO_NODE *node = &(nodes[i]);
701 if (node->node_id == start_node)
702 continue;
703 if (node->node_id == end_node)
704 continue;
705 /* check if the edge contains this node (not on boundary) */
706 /* ST_RelateMatch(rec.relate, 'T********') */
707 if (!node->geom || !node->geom->point || !node->geom->point->npoints)
708 {
709 GEOSGeom_destroy(edgegg);
710 _lwt_release_nodes(nodes, num_nodes);
711 lwerror("SQL/MM Spatial exception - geometry crosses a node");
712 return -1;
713 }
714
715 getPoint2d_p(node->geom->point, 0, &p);
717 if (contains)
718 {
719 char locinfo[128] = "";
720 snprintf(locinfo, sizeof(locinfo), " at POINT(%.15g %.15g)", p.x, p.y);
721 GEOSGeom_destroy(edgegg);
722 _lwt_release_nodes(nodes, num_nodes);
723 lwerror("SQL/MM Spatial exception - geometry crosses a node%s", locinfo);
724 return -1;
725 }
726 }
727 if ( nodes ) _lwt_release_nodes(nodes, num_nodes);
728 /* may be NULL if num_nodes == 0 */
729
730 /* loop over each edge within the edge's gbox */
731 edges = lwt_be_getEdgeWithinBox2D( topo, edgebox, &num_edges, LWT_COL_EDGE_ALL, 0 );
732 LWDEBUGF(1, "lwt_be_getEdgeWithinBox2D returned %llu edges", num_edges);
733 if (num_edges == UINT64_MAX)
734 {
735 GEOSGeom_destroy(edgegg);
737 return -1;
738 }
739 for ( i=0; i<num_edges; ++i )
740 {
741 LWT_ISO_EDGE* edge = &(edges[i]);
742 LWT_ELEMID edge_id = edge->edge_id;
743 GEOSGeometry *eegg;
744 char *relate;
745 int match;
746
747 if ( edge_id == myself ) continue;
748
749 if ( ! edge->geom ) {
750 _lwt_release_edges(edges, num_edges);
751 lwerror("Edge %" LWTFMT_ELEMID " has NULL geometry!", edge_id);
752 return -1;
753 }
754
755 eegg = LWGEOM2GEOS( lwline_as_lwgeom(edge->geom), 0 );
756 if ( ! eegg ) {
757 GEOSGeom_destroy(edgegg);
758 _lwt_release_edges(edges, num_edges);
759 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
760 return -1;
761 }
762
763 LWDEBUGF(2, "Edge %" LWTFMT_ELEMID " converted to GEOS", edge_id);
764
765 /* check if the edge has a non-boundary-boundary intersection with our edge */
766
767 relate = GEOSRelateBoundaryNodeRule(eegg, edgegg, 2);
768 if ( ! relate ) {
769 GEOSGeom_destroy(eegg);
770 GEOSGeom_destroy(edgegg);
771 _lwt_release_edges(edges, num_edges);
772 lwerror("GEOSRelateBoundaryNodeRule error: %s", lwgeom_geos_errmsg);
773 return -1;
774 }
775
776 LWDEBUGF(2, "Edge %" LWTFMT_ELEMID " relate pattern is %s", edge_id, relate);
777
778 match = GEOSRelatePatternMatch(relate, "FF*F*****");
779 if ( match ) {
780 /* error or no interior intersection */
781 GEOSFree(relate);
782 GEOSGeom_destroy(eegg);
783 if ( match == 2 ) {
784 _lwt_release_edges(edges, num_edges);
785 GEOSGeom_destroy(edgegg);
786 lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
787 return -1;
788 }
789 continue; /* no interior intersection */
790 }
791
792 match = GEOSRelatePatternMatch(relate, "1FFF*FFF2");
793 if ( match ) {
794 char locinfo[128] = "";
795 const char *locsuffix = match == 2 ? ""
796 : _lwt_describe_intersection_point(edgegg, eegg, locinfo, sizeof(locinfo)) ? locinfo
797 : "";
798 GEOSGeom_destroy(eegg);
799 GEOSGeom_destroy(edgegg);
800 _lwt_release_edges(edges, num_edges);
801 GEOSFree(relate);
802 if (match == 2)
803 {
804 lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
805 }
806 else
807 {
808 lwerror("SQL/MM Spatial exception - coincident edge %" LWTFMT_ELEMID "%s", edge_id, locsuffix);
809 }
810 return -1;
811 }
812
813 match = GEOSRelatePatternMatch(relate, "1********");
814 if ( match ) {
815 char locinfo[128] = "";
816 const char *locsuffix = match == 2 ? ""
817 : _lwt_describe_intersection_point(edgegg, eegg, locinfo, sizeof(locinfo)) ? locinfo
818 : "";
819 GEOSGeom_destroy(eegg);
820 GEOSGeom_destroy(edgegg);
821 _lwt_release_edges(edges, num_edges);
822 GEOSFree(relate);
823 if (match == 2)
824 {
825 lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
826 }
827 else
828 {
829 lwerror("Spatial exception - geometry intersects edge %" LWTFMT_ELEMID "%s", edge_id, locsuffix);
830 }
831 return -1;
832 }
833
834 match = GEOSRelatePatternMatch(relate, "T********");
835 if ( match ) {
836 char locinfo[128] = "";
837 const char *locsuffix = match == 2 ? ""
838 : _lwt_describe_intersection_point(edgegg, eegg, locinfo, sizeof(locinfo)) ? locinfo
839 : "";
840 GEOSGeom_destroy(eegg);
841 GEOSGeom_destroy(edgegg);
842 _lwt_release_edges(edges, num_edges);
843 GEOSFree(relate);
844 if (match == 2)
845 {
846 lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
847 }
848 else
849 {
850 lwerror(
851 "SQL/MM Spatial exception - geometry crosses edge %" LWTFMT_ELEMID "%s", edge_id, locsuffix);
852 }
853 return -1;
854 }
855
856 match = GEOSRelatePatternMatch(relate, "*T*******");
857 if ( match ) {
858 char locinfo[128] = "";
859 const char *locsuffix = match == 2 ? ""
860 : _lwt_describe_intersection_point(edgegg, eegg, locinfo, sizeof(locinfo)) ? locinfo
861 : "";
862 GEOSGeom_destroy(eegg);
863 GEOSGeom_destroy(edgegg);
864 _lwt_release_edges(edges, num_edges);
865 GEOSFree(relate);
866 if (match == 2)
867 {
868 lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
869 }
870 else
871 {
872 lwerror("Spatial exception - geometry boundary touches interior of edge %" LWTFMT_ELEMID "%s",
873 edge_id,
874 locsuffix);
875 }
876 return -1;
877 }
878
879 match = GEOSRelatePatternMatch(relate, "***T*****");
880 if ( match ) {
881 char locinfo[128] = "";
882 const char *locsuffix = match == 2 ? ""
883 : _lwt_describe_intersection_point(edgegg, eegg, locinfo, sizeof(locinfo)) ? locinfo
884 : "";
885 GEOSGeom_destroy(eegg);
886 GEOSGeom_destroy(edgegg);
887 _lwt_release_edges(edges, num_edges);
888 GEOSFree(relate);
889 if (match == 2)
890 {
891 lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
892 }
893 else
894 {
895 lwerror("Spatial exception - boundary of edge %" LWTFMT_ELEMID " touches interior of geometry%s",
896 edge_id,
897 locsuffix);
898 }
899 return -1;
900 }
901
902 LWDEBUGF(2, "Edge %" LWTFMT_ELEMID " analysis completed, it does no harm", edge_id);
903
904 GEOSFree(relate);
905 GEOSGeom_destroy(eegg);
906 }
907 LWDEBUGF(1, "No edge crossing detected among the %llu candidate edges", num_edges);
908 if ( edges ) _lwt_release_edges(edges, num_edges);
909 /* would be NULL if num_edges was 0 */
910
911 GEOSGeom_destroy(edgegg);
912
913 return 0;
914}
915
916
919 LWT_ELEMID endNode, const LWLINE* geom )
920{
921 uint64_t num_nodes;
922 uint64_t i;
923 LWT_ISO_EDGE newedge;
924 LWT_ISO_NODE *endpoints;
925 LWT_ELEMID containing_face = -1;
926 LWT_ELEMID node_ids[2];
927 LWT_ISO_NODE updated_nodes[2];
928 int skipISOChecks = 0;
929 POINT2D p1, p2;
930
931 /* NOT IN THE SPECS:
932 * A closed edge is never isolated (as it forms a face)
933 */
934 if (startNode == endNode)
935 {
936 lwerror("Closed edges would not be isolated, try lwt_AddEdgeNewFaces");
937 return -1;
938 }
939
940 if ( ! skipISOChecks )
941 {
942 /* Acurve must be simple */
943 if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) )
944 {
945 lwerror("SQL/MM Spatial exception - curve not simple");
946 return -1;
947 }
948 }
949
950 /*
951 * Check for:
952 * existence of nodes
953 * nodes faces match
954 * Extract:
955 * nodes face id
956 * nodes geoms
957 */
958 num_nodes = 2;
959 node_ids[0] = startNode;
960 node_ids[1] = endNode;
961 endpoints = lwt_be_getNodeById( topo, node_ids, &num_nodes,
963 if (num_nodes == UINT64_MAX)
964 {
966 return -1;
967 }
968 else if ( num_nodes < 2 )
969 {
970 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
971 lwerror("SQL/MM Spatial exception - non-existent node");
972 return -1;
973 }
974 for ( i=0; i<num_nodes; ++i )
975 {
976 const LWT_ISO_NODE *n = &(endpoints[i]);
977 if ( n->containing_face == -1 )
978 {
979 _lwt_release_nodes(endpoints, num_nodes);
980 lwerror("SQL/MM Spatial exception - not isolated node");
981 return -1;
982 }
983 if ( containing_face == -1 ) containing_face = n->containing_face;
984 else if ( containing_face != n->containing_face )
985 {
986 _lwt_release_nodes(endpoints, num_nodes);
987 lwerror("SQL/MM Spatial exception - nodes in different faces");
988 return -1;
989 }
990
991 if ( ! skipISOChecks )
992 {
993 if ( n->node_id == startNode )
994 {
995 /* l) Check that start point of acurve match start node geoms. */
996 getPoint2d_p(geom->points, 0, &p1);
997 getPoint2d_p(n->geom->point, 0, &p2);
998 if ( ! P2D_SAME_STRICT(&p1, &p2) )
999 {
1000 _lwt_release_nodes(endpoints, num_nodes);
1001 lwerror("SQL/MM Spatial exception - "
1002 "start node not geometry start point.");
1003 return -1;
1004 }
1005 }
1006 else
1007 {
1008 /* m) Check that end point of acurve match end node geoms. */
1009 getPoint2d_p(geom->points, geom->points->npoints-1, &p1);
1010 getPoint2d_p(n->geom->point, 0, &p2);
1011 if ( ! P2D_SAME_STRICT(&p1, &p2) )
1012 {
1013 _lwt_release_nodes(endpoints, num_nodes);
1014 lwerror("SQL/MM Spatial exception - "
1015 "end node not geometry end point.");
1016 return -1;
1017 }
1018 }
1019 }
1020 }
1021
1022 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
1023
1024 if ( ! skipISOChecks )
1025 {
1026 if ( _lwt_CheckEdgeCrossing( topo, startNode, endNode, geom, 0 ) )
1027 {
1028 /* would have called lwerror already, leaking :( */
1029 return -1;
1030 }
1031 }
1032
1033 /*
1034 * All checks passed, time to prepare the new edge
1035 */
1036
1037 newedge.edge_id = lwt_be_getNextEdgeId( topo );
1038 if ( newedge.edge_id == -1 ) {
1040 return -1;
1041 }
1042
1043 /* TODO: this should likely be an exception instead ! */
1044 if ( containing_face == -1 ) containing_face = 0;
1045
1046 newedge.start_node = startNode;
1047 newedge.end_node = endNode;
1048 newedge.face_left = newedge.face_right = containing_face;
1049 newedge.next_left = -newedge.edge_id;
1050 newedge.next_right = newedge.edge_id;
1051 newedge.geom = (LWLINE *)geom; /* const cast.. */
1052
1053 int ret = lwt_be_insertEdges(topo, &newedge, 1);
1054 if ( ret == -1 ) {
1056 return -1;
1057 } else if ( ret == 0 ) {
1058 lwerror("Insertion of split edge failed (no reason)");
1059 return -1;
1060 }
1061
1062 /*
1063 * Update Node containing_face values
1064 *
1065 * the nodes anode and anothernode are no more isolated
1066 * because now there is an edge connecting them
1067 */
1068 updated_nodes[0].node_id = startNode;
1069 updated_nodes[0].containing_face = -1;
1070 updated_nodes[1].node_id = endNode;
1071 updated_nodes[1].containing_face = -1;
1072 ret = lwt_be_updateNodesById(topo, updated_nodes, 2,
1074 if ( ret == -1 ) {
1076 return -1;
1077 }
1078
1079 return newedge.edge_id;
1080}
1081
1082static LWCOLLECTION *
1083_lwt_EdgeSplit( LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWPOINT* pt, int skipISOChecks, LWT_ISO_EDGE** oldedge )
1084{
1085 LWGEOM *split;
1086 LWCOLLECTION *split_col;
1087 uint64_t i;
1088
1089 /* Get edge */
1090 i = 1;
1091 LWDEBUG(1, "calling lwt_be_getEdgeById");
1092 *oldedge = lwt_be_getEdgeById(topo, &edge, &i, LWT_COL_EDGE_ALL);
1093 LWDEBUGF(1, "lwt_be_getEdgeById returned %p", *oldedge);
1094 LWDEBUGG(2, lwline_as_lwgeom(oldedge[0]->geom), "Edge to be split");
1095 if ( ! *oldedge )
1096 {
1097 LWDEBUGF(1, "lwt_be_getEdgeById returned NULL and set i=%llu", i);
1098 if (i == UINT64_MAX)
1099 {
1101 return NULL;
1102 }
1103 else if ( i == 0 )
1104 {
1105 lwerror("SQL/MM Spatial exception - non-existent edge");
1106 return NULL;
1107 }
1108 else
1109 {
1110 lwerror("Backend coding error: getEdgeById callback returned NULL "
1111 "but numelements output parameter has value %" PRIu64
1112 "(expected 0 or 1)", i);
1113 return NULL;
1114 }
1115 }
1116
1117
1118 /*
1119 * - check if a coincident node already exists
1120 */
1121 if ( ! skipISOChecks )
1122 {
1123 LWDEBUG(1, "calling lwt_be_ExistsCoincidentNode");
1124 if ( lwt_be_ExistsCoincidentNode(topo, pt) ) /*x*/
1125 {
1126 LWDEBUG(1, "lwt_be_ExistsCoincidentNode returned");
1127 _lwt_release_edges(*oldedge, 1);
1128 lwerror("SQL/MM Spatial exception - coincident node");
1129 return NULL;
1130 }
1131 LWDEBUG(1, "lwt_be_ExistsCoincidentNode returned");
1132 }
1133
1134 /* Split edge */
1135 split = lwgeom_split((LWGEOM*)(*oldedge)->geom, (LWGEOM*)pt);
1136 if ( ! split )
1137 {
1138 _lwt_release_edges(*oldedge, 1);
1139 lwerror("could not split edge by point ?");
1140 return NULL;
1141 }
1142 split_col = lwgeom_as_lwcollection(split);
1143 if ( ! split_col ) {
1144 _lwt_release_edges(*oldedge, 1);
1145 lwgeom_free(split);
1146 lwerror("lwgeom_as_lwcollection returned NULL");
1147 return NULL;
1148 }
1149 if (split_col->ngeoms < 2) {
1150 LWDEBUGG(1, lwpoint_as_lwgeom(pt), "Unable to split edge by point");
1151 _lwt_release_edges(*oldedge, 1);
1152 lwgeom_free(split);
1153 lwerror("SQL/MM Spatial exception - point not on edge");
1154 return NULL;
1155 }
1156
1157#if POSTGIS_DEBUG_LEVEL > 1
1158 {
1159 size_t sz;
1160 char *wkt = lwgeom_to_wkt((LWGEOM*)split_col, WKT_EXTENDED, 2, &sz);
1161 LWDEBUGF(1, "returning split col: %s", wkt);
1162 lwfree(wkt);
1163 }
1164#endif
1165 return split_col;
1166}
1167
1170 LWPOINT* pt, int skipISOChecks )
1171{
1172 LWT_ISO_NODE node;
1173 LWT_ISO_EDGE* oldedge = NULL;
1174 LWCOLLECTION *split_col;
1175 const LWGEOM *oldedge_geom;
1176 const LWGEOM *newedge_geom;
1177 LWT_ISO_EDGE newedge1;
1178 LWT_ISO_EDGE seledge, updedge, excedge;
1179 int ret;
1180
1181 split_col = _lwt_EdgeSplit( topo, edge, pt, skipISOChecks, &oldedge );
1182 if ( ! split_col ) return -1; /* should have raised an exception */
1183 oldedge_geom = split_col->geoms[0];
1184 newedge_geom = split_col->geoms[1];
1185 /* Make sure the SRID is set on the subgeom */
1186 ((LWGEOM*)oldedge_geom)->srid = split_col->srid;
1187 ((LWGEOM*)newedge_geom)->srid = split_col->srid;
1188
1189 /* Add new node, getting new id back */
1190 node.node_id = -1;
1191 node.containing_face = -1; /* means not-isolated */
1192 node.geom = pt;
1193 if ( ! lwt_be_insertNodes(topo, &node, 1) )
1194 {
1195 _lwt_release_edges(oldedge, 1);
1196 lwcollection_free(split_col);
1198 return -1;
1199 }
1200 if (node.node_id == -1) {
1201 /* should have been set by backend */
1202 _lwt_release_edges(oldedge, 1);
1203 lwcollection_free(split_col);
1204 lwerror("Backend coding error: "
1205 "insertNodes callback did not return node_id");
1206 return -1;
1207 }
1208
1209 /* Insert the new edge */
1210 newedge1.edge_id = lwt_be_getNextEdgeId(topo);
1211 if ( newedge1.edge_id == -1 ) {
1212 _lwt_release_edges(oldedge, 1);
1213 lwcollection_free(split_col);
1215 return -1;
1216 }
1217 newedge1.start_node = node.node_id;
1218 newedge1.end_node = oldedge->end_node;
1219 newedge1.face_left = oldedge->face_left;
1220 newedge1.face_right = oldedge->face_right;
1221 newedge1.next_left = oldedge->next_left == -oldedge->edge_id ?
1222 -newedge1.edge_id : oldedge->next_left;
1223 newedge1.next_right = -oldedge->edge_id;
1224 newedge1.geom = lwgeom_as_lwline(newedge_geom);
1225 /* lwgeom_split of a line should only return lines ... */
1226 if ( ! newedge1.geom ) {
1227 _lwt_release_edges(oldedge, 1);
1228 lwcollection_free(split_col);
1229 lwerror("first geometry in lwgeom_split output is not a line");
1230 return -1;
1231 }
1232 ret = lwt_be_insertEdges(topo, &newedge1, 1);
1233 if ( ret == -1 ) {
1234 _lwt_release_edges(oldedge, 1);
1235 lwcollection_free(split_col);
1237 return -1;
1238 } else if ( ret == 0 ) {
1239 _lwt_release_edges(oldedge, 1);
1240 lwcollection_free(split_col);
1241 lwerror("Insertion of split edge failed (no reason)");
1242 return -1;
1243 }
1244
1245 /* Update the old edge */
1246 updedge.geom = lwgeom_as_lwline(oldedge_geom);
1247 /* lwgeom_split of a line should only return lines ... */
1248 if ( ! updedge.geom ) {
1249 _lwt_release_edges(oldedge, 1);
1250 lwcollection_free(split_col);
1251 lwerror("second geometry in lwgeom_split output is not a line");
1252 return -1;
1253 }
1254 updedge.next_left = newedge1.edge_id;
1255 updedge.end_node = node.node_id;
1256 ret = lwt_be_updateEdges(topo,
1257 oldedge, LWT_COL_EDGE_EDGE_ID,
1259 NULL, 0);
1260 if ( ret == -1 ) {
1261 _lwt_release_edges(oldedge, 1);
1262 lwcollection_free(split_col);
1264 return -1;
1265 } else if ( ret == 0 ) {
1266 _lwt_release_edges(oldedge, 1);
1267 lwcollection_free(split_col);
1268 lwerror("Edge being split (%" LWTFMT_ELEMID ") disappeared during operations?", oldedge->edge_id);
1269 return -1;
1270 } else if ( ret > 1 ) {
1271 _lwt_release_edges(oldedge, 1);
1272 lwcollection_free(split_col);
1273 lwerror("More than a single edge found with id %" LWTFMT_ELEMID " !", oldedge->edge_id);
1274 return -1;
1275 }
1276
1277 /* Update all next edge references to match new layout (ST_ModEdgeSplit) */
1278
1279 updedge.next_right = -newedge1.edge_id;
1280 excedge.edge_id = newedge1.edge_id;
1281 seledge.next_right = -oldedge->edge_id;
1282 seledge.start_node = oldedge->end_node;
1283 ret = lwt_be_updateEdges(topo,
1285 &updedge, LWT_COL_EDGE_NEXT_RIGHT,
1286 &excedge, LWT_COL_EDGE_EDGE_ID);
1287 if ( ret == -1 ) {
1288 _lwt_release_edges(oldedge, 1);
1289 lwcollection_free(split_col);
1291 return -1;
1292 }
1293
1294 updedge.next_left = -newedge1.edge_id;
1295 excedge.edge_id = newedge1.edge_id;
1296 seledge.next_left = -oldedge->edge_id;
1297 seledge.end_node = oldedge->end_node;
1298 ret = lwt_be_updateEdges(topo,
1300 &updedge, LWT_COL_EDGE_NEXT_LEFT,
1301 &excedge, LWT_COL_EDGE_EDGE_ID);
1302 if ( ret == -1 ) {
1303 _lwt_release_edges(oldedge, 1);
1304 lwcollection_free(split_col);
1306 return -1;
1307 }
1308
1309 /* Update TopoGeometries composition */
1310 ret = lwt_be_updateTopoGeomEdgeSplit(topo, oldedge->edge_id, newedge1.edge_id, -1);
1311 if ( ! ret ) {
1312 _lwt_release_edges(oldedge, 1);
1313 lwcollection_free(split_col);
1315 return -1;
1316 }
1317
1318 _lwt_release_edges(oldedge, 1);
1319 lwcollection_free(split_col);
1320
1321 /* return new node id */
1322 return node.node_id;
1323}
1324
1327 LWPOINT* pt, int skipISOChecks )
1328{
1329 LWT_ISO_NODE node;
1330 LWT_ISO_EDGE* oldedge = NULL;
1331 LWCOLLECTION *split_col;
1332 const LWGEOM *oldedge_geom;
1333 const LWGEOM *newedge_geom;
1334 LWT_ISO_EDGE newedges[2];
1335 LWT_ISO_EDGE seledge, updedge;
1336 int ret;
1337
1338 split_col = _lwt_EdgeSplit( topo, edge, pt, skipISOChecks, &oldedge );
1339 if ( ! split_col ) return -1; /* should have raised an exception */
1340 oldedge_geom = split_col->geoms[0];
1341 newedge_geom = split_col->geoms[1];
1342 /* Make sure the SRID is set on the subgeom */
1343 ((LWGEOM*)oldedge_geom)->srid = split_col->srid;
1344 ((LWGEOM*)newedge_geom)->srid = split_col->srid;
1345
1346 /* Add new node, getting new id back */
1347 node.node_id = -1;
1348 node.containing_face = -1; /* means not-isolated */
1349 node.geom = pt;
1350 if ( ! lwt_be_insertNodes(topo, &node, 1) )
1351 {
1352 _lwt_release_edges(oldedge, 1);
1353 lwcollection_free(split_col);
1355 return -1;
1356 }
1357 if (node.node_id == -1) {
1358 _lwt_release_edges(oldedge, 1);
1359 lwcollection_free(split_col);
1360 /* should have been set by backend */
1361 lwerror("Backend coding error: "
1362 "insertNodes callback did not return node_id");
1363 return -1;
1364 }
1365
1366 /* Delete the old edge */
1367 seledge.edge_id = edge;
1368 ret = lwt_be_deleteEdges(topo, &seledge, LWT_COL_EDGE_EDGE_ID);
1369 if ( ret == -1 ) {
1370 _lwt_release_edges(oldedge, 1);
1371 lwcollection_free(split_col);
1373 return -1;
1374 }
1375
1376 /* Get new edges identifiers */
1377 newedges[0].edge_id = lwt_be_getNextEdgeId(topo);
1378 if ( newedges[0].edge_id == -1 ) {
1379 _lwt_release_edges(oldedge, 1);
1380 lwcollection_free(split_col);
1382 return -1;
1383 }
1384 newedges[1].edge_id = lwt_be_getNextEdgeId(topo);
1385 if ( newedges[1].edge_id == -1 ) {
1386 _lwt_release_edges(oldedge, 1);
1387 lwcollection_free(split_col);
1389 return -1;
1390 }
1391
1392 /* Define the first new edge (to new node) */
1393 newedges[0].start_node = oldedge->start_node;
1394 newedges[0].end_node = node.node_id;
1395 newedges[0].face_left = oldedge->face_left;
1396 newedges[0].face_right = oldedge->face_right;
1397 newedges[0].next_left = newedges[1].edge_id;
1398 if ( oldedge->next_right == edge )
1399 newedges[0].next_right = newedges[0].edge_id;
1400 else if ( oldedge->next_right == -edge )
1401 newedges[0].next_right = -newedges[1].edge_id;
1402 else
1403 newedges[0].next_right = oldedge->next_right;
1404 newedges[0].geom = lwgeom_as_lwline(oldedge_geom);
1405 /* lwgeom_split of a line should only return lines ... */
1406 if ( ! newedges[0].geom ) {
1407 _lwt_release_edges(oldedge, 1);
1408 lwcollection_free(split_col);
1409 lwerror("first geometry in lwgeom_split output is not a line");
1410 return -1;
1411 }
1412
1413 /* Define the second new edge (from new node) */
1414 newedges[1].start_node = node.node_id;
1415 newedges[1].end_node = oldedge->end_node;
1416 newedges[1].face_left = oldedge->face_left;
1417 newedges[1].face_right = oldedge->face_right;
1418 newedges[1].next_right = -newedges[0].edge_id;
1419 if ( oldedge->next_left == -edge )
1420 newedges[1].next_left = -newedges[1].edge_id;
1421 else if ( oldedge->next_left == edge )
1422 newedges[1].next_left = newedges[0].edge_id;
1423 else
1424 newedges[1].next_left = oldedge->next_left;
1425 newedges[1].geom = lwgeom_as_lwline(newedge_geom);
1426 /* lwgeom_split of a line should only return lines ... */
1427 if ( ! newedges[1].geom ) {
1428 _lwt_release_edges(oldedge, 1);
1429 lwcollection_free(split_col);
1430 lwerror("second geometry in lwgeom_split output is not a line");
1431 return -1;
1432 }
1433
1434 /* Insert both new edges */
1435 ret = lwt_be_insertEdges(topo, newedges, 2);
1436 if ( ret == -1 ) {
1437 _lwt_release_edges(oldedge, 1);
1439 return -1;
1440 } else if ( ret == 0 ) {
1441 _lwt_release_edges(oldedge, 1);
1442 lwcollection_free(split_col);
1443 lwerror("Insertion of split edge failed (no reason)");
1444 return -1;
1445 }
1446
1447 /* Update all next edge references pointing to old edge id */
1448
1449 updedge.next_right = newedges[0].edge_id;
1450 seledge.next_right = edge;
1451 seledge.start_node = oldedge->start_node;
1452 ret = lwt_be_updateEdges(topo,
1454 &updedge, LWT_COL_EDGE_NEXT_RIGHT,
1455 NULL, 0);
1456 if ( ret == -1 ) {
1457 _lwt_release_edges(oldedge, 1);
1458 lwcollection_free(split_col);
1460 return -1;
1461 }
1462
1463 updedge.next_right = -newedges[1].edge_id;
1464 seledge.next_right = -edge;
1465 seledge.start_node = oldedge->end_node;
1466 ret = lwt_be_updateEdges(topo,
1468 &updedge, LWT_COL_EDGE_NEXT_RIGHT,
1469 NULL, 0);
1470 if ( ret == -1 ) {
1471 _lwt_release_edges(oldedge, 1);
1472 lwcollection_free(split_col);
1474 return -1;
1475 }
1476
1477 updedge.next_left = newedges[0].edge_id;
1478 seledge.next_left = edge;
1479 seledge.end_node = oldedge->start_node;
1480 ret = lwt_be_updateEdges(topo,
1482 &updedge, LWT_COL_EDGE_NEXT_LEFT,
1483 NULL, 0);
1484 if ( ret == -1 ) {
1485 _lwt_release_edges(oldedge, 1);
1486 lwcollection_free(split_col);
1488 return -1;
1489 }
1490
1491 updedge.next_left = -newedges[1].edge_id;
1492 seledge.next_left = -edge;
1493 seledge.end_node = oldedge->end_node;
1494 ret = lwt_be_updateEdges(topo,
1496 &updedge, LWT_COL_EDGE_NEXT_LEFT,
1497 NULL, 0);
1498 if ( ret == -1 ) {
1499 _lwt_release_edges(oldedge, 1);
1500 lwcollection_release(split_col);
1502 return -1;
1503 }
1504
1505 /* Update TopoGeometries composition */
1506 ret = lwt_be_updateTopoGeomEdgeSplit(topo, oldedge->edge_id, newedges[0].edge_id, newedges[1].edge_id);
1507 if ( ! ret ) {
1508 _lwt_release_edges(oldedge, 1);
1509 lwcollection_free(split_col);
1511 return -1;
1512 }
1513
1514 _lwt_release_edges(oldedge, 1);
1515 lwcollection_free(split_col);
1516
1517 /* return new node id */
1518 return node.node_id;
1519}
1520
1521/* Data structure used by AddEdgeX functions */
1522typedef struct edgeend_t {
1523 /* Signed identifier of next clockwise edge (+outgoing,-incoming) */
1525 /* Identifier of face between myaz and next CW edge */
1527 /* Signed identifier of next counterclockwise edge (+outgoing,-incoming) */
1529 /* Identifier of face between myaz and next CCW edge */
1532 double myaz; /* azimuth of edgeend geometry */
1534
1535/*
1536 * Get first distinct vertex from endpoint
1537 * @param pa the pointarray to seek points in
1538 * @param ref the point we want to search a distinct one
1539 * @param from vertex index to start from (will really start from "from"+dir)
1540 * @param dir 1 to go forward
1541 * -1 to go backward
1542 * @return 1 on success, 0 if edge is collapsed (no distinct points)
1543 */
1544static int
1545_lwt_FirstDistinctVertex2D(const POINTARRAY* pa, const POINT2D *ref, int from, int dir, POINT2D *op)
1546{
1547 int i, toofar, inc;
1548 POINT2D fp;
1549
1550 if ( dir > 0 )
1551 {
1552 toofar = pa->npoints;
1553 inc = 1;
1554 }
1555 else
1556 {
1557 toofar = -1;
1558 inc = -1;
1559 }
1560
1561 LWDEBUGF(2, "first point is index %d", from);
1562 fp = *ref; /* getPoint2d_p(pa, from, &fp); */
1563 for ( i = from+inc; i != toofar; i += inc )
1564 {
1565 LWDEBUGF(2, "testing point %d", i);
1566 getPoint2d_p(pa, i, op); /* pick next point */
1567 if ( P2D_SAME_STRICT(op,&fp) ) continue; /* equal to startpoint */
1568 /* this is a good one, neither same of start nor of end point */
1569 return 1; /* found */
1570 }
1571
1572 /* no distinct vertices found */
1573 return 0;
1574}
1575
1576/*
1577 * Return non-zero on failure (lwerror is invoked in that case)
1578 * Possible failures:
1579 * -1 no two distinct vertices exist
1580 * -2 azimuth computation failed
1581 */
1582static int
1584 POINT2D *fp, POINT2D *lp)
1585{
1586 POINTARRAY *pa = edge->points;
1587 POINT2D pt;
1588
1589 fee->nextCW = fee->nextCCW =
1590 lee->nextCW = lee->nextCCW = 0;
1591 fee->cwFace = fee->ccwFace =
1592 lee->cwFace = lee->ccwFace = -1;
1593
1594 /* Compute azimuth of first edge end */
1595 LWDEBUG(1, "computing azimuth of first edge end");
1596 if ( ! _lwt_FirstDistinctVertex2D(pa, fp, 0, 1, &pt) )
1597 {
1598 lwerror("Invalid edge (no two distinct vertices exist)");
1599 return -1;
1600 }
1601 if ( ! azimuth_pt_pt(fp, &pt, &(fee->myaz)) ) {
1602 lwerror("error computing azimuth of first edgeend [%.15g %.15g,%.15g %.15g]",
1603 fp->x, fp->y, pt.x, pt.y);
1604 return -2;
1605 }
1606 LWDEBUGF(1, "azimuth of first edge end [%.15g %.15g,%.15g %.15g] is %g",
1607 fp->x, fp->y, pt.x, pt.y, fee->myaz);
1608
1609 /* Compute azimuth of second edge end */
1610 LWDEBUG(1, "computing azimuth of second edge end");
1611 if ( ! _lwt_FirstDistinctVertex2D(pa, lp, pa->npoints-1, -1, &pt) )
1612 {
1613 lwerror("Invalid edge (no two distinct vertices exist)");
1614 return -1;
1615 }
1616 if ( ! azimuth_pt_pt(lp, &pt, &(lee->myaz)) ) {
1617 lwerror("error computing azimuth of last edgeend [%.15g %.15g,%.15g %.15g]",
1618 lp->x, lp->y, pt.x, pt.y);
1619 return -2;
1620 }
1621 LWDEBUGF(1, "azimuth of last edge end [%.15g %.15g,%.15g %.15g] is %g",
1622 lp->x, lp->y, pt.x, pt.y, lee->myaz);
1623
1624 return 0;
1625}
1626
1627/*
1628 * Find the first edges encountered going clockwise and counterclockwise
1629 * around a node, starting from the given azimuth, and take
1630 * note of the face on both sides.
1631 *
1632 * @param topo the topology to load edges from
1633 * @param node the identifier of the node to analyze
1634 * @param data input (myaz) / output (nextCW, nextCCW) parameter
1635 * @param other edgeend, if also incident to given node (closed edge).
1636 * @param myedge_id identifier of the edge id that data->myaz belongs to
1637 * @return number of incident edges found
1638 *
1639 */
1640static int
1642 edgeend *other, LWT_ELEMID myedge_id )
1643{
1644 LWT_ISO_EDGE *edges;
1645 uint64_t numedges = 1;
1646 uint64_t i;
1647 double minaz, maxaz;
1648 double az, azdif;
1649
1650 data->nextCW = data->nextCCW = 0;
1651 data->cwFace = data->ccwFace = -1;
1652
1653 if ( other ) {
1654 azdif = other->myaz - data->myaz;
1655 if ( azdif < 0 ) azdif += 2 * M_PI;
1656 minaz = maxaz = azdif;
1657 /* TODO: set nextCW/nextCCW/cwFace/ccwFace to other->something ? */
1658 LWDEBUGF(1, "Other edge end has cwFace=%" LWTFMT_ELEMID
1659 " and ccwFace=%" LWTFMT_ELEMID,
1660 other->cwFace, other->ccwFace);
1661 } else {
1662 minaz = maxaz = -1;
1663 }
1664
1665 LWDEBUGF(1, "Looking for edges incident to node %" LWTFMT_ELEMID
1666 " and adjacent to azimuth %g", node, data->myaz);
1667
1668 /* Get incident edges */
1669 edges = lwt_be_getEdgeByNode( topo, &node, &numedges, LWT_COL_EDGE_ALL );
1670 if (numedges == UINT64_MAX)
1671 {
1673 return 0;
1674 }
1675
1676 LWDEBUGF(1, "getEdgeByNode returned %llu edges, minaz=%g, maxaz=%g",
1677 numedges, minaz, maxaz);
1678
1679 /* For each incident edge-end (1 or 2): */
1680 for ( i = 0; i < numedges; ++i )
1681 {
1682 LWT_ISO_EDGE *edge;
1683 LWGEOM *g;
1684 LWGEOM *cleangeom;
1685 POINT2D p1, p2;
1686 POINTARRAY *pa;
1687
1688 edge = &(edges[i]);
1689
1690 if ( edge->edge_id == myedge_id ) continue;
1691
1692 g = lwline_as_lwgeom(edge->geom);
1693 /* NOTE: remove_repeated_points call could be replaced by
1694 * some other mean to pick two distinct points for endpoints */
1695 cleangeom = lwgeom_remove_repeated_points( g, 0 );
1696 pa = lwgeom_as_lwline(cleangeom)->points;
1697
1698 if ( pa->npoints < 2 ) {{
1699 LWT_ELEMID id = edge->edge_id;
1700 _lwt_release_edges(edges, numedges);
1701 lwgeom_free(cleangeom);
1702 lwerror("corrupted topology: edge %" LWTFMT_ELEMID
1703 " does not have two distinct points", id);
1704 return -1;
1705 }}
1706
1707 if ( edge->start_node == node ) {
1708 getPoint2d_p(pa, 0, &p1);
1709 if ( ! _lwt_FirstDistinctVertex2D(pa, &p1, 0, 1, &p2) )
1710 {
1711 lwerror("Edge %" LWTFMT_ELEMID " has no distinct vertices: [%.15g %.15g,%.15g %.15g]: ",
1712 edge->edge_id, p1.x, p1.y, p2.x, p2.y);
1713 return -1;
1714 }
1715 LWDEBUGF(1, "edge %" LWTFMT_ELEMID
1716 " starts on node %" LWTFMT_ELEMID
1717 ", edgeend is [%.15g %.15g,%.15g %.15g]",
1718 edge->edge_id, node, p1.x, p1.y, p2.x, p2.y);
1719 if ( ! azimuth_pt_pt(&p1, &p2, &az) ) {{
1720 LWT_ELEMID id = edge->edge_id;
1721 _lwt_release_edges(edges, numedges);
1722 lwgeom_free(cleangeom);
1723 lwerror("error computing azimuth of edge %"
1724 LWTFMT_ELEMID " first edgeend [%.15g %.15g,%.15g %.15g]",
1725 id, p1.x, p1.y, p2.x, p2.y);
1726 return -1;
1727 }}
1728 azdif = az - data->myaz;
1729 LWDEBUGF(1, "azimuth of edge %" LWTFMT_ELEMID
1730 ": %.15g (diff: %.15g)", edge->edge_id, az, azdif);
1731
1732 if ( azdif < 0 ) azdif += 2 * M_PI;
1733 if ( minaz == -1 ) {
1734 minaz = maxaz = azdif;
1735 data->nextCW = data->nextCCW = edge->edge_id; /* outgoing */
1736 data->cwFace = edge->face_left;
1737 data->ccwFace = edge->face_right;
1738 LWDEBUGF(1, "new nextCW and nextCCW edge is %" LWTFMT_ELEMID
1739 ", outgoing, "
1740 "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID
1741 " (face_right is new ccwFace, face_left is new cwFace)",
1742 edge->edge_id, edge->face_left,
1743 edge->face_right);
1744 } else {
1745 if ( azdif < minaz ) {
1746 data->nextCW = edge->edge_id; /* outgoing */
1747 data->cwFace = edge->face_left;
1748 LWDEBUGF(1, "new nextCW edge is %" LWTFMT_ELEMID
1749 ", outgoing, "
1750 "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID
1751 " (previous had minaz=%g, face_left is new cwFace)",
1752 edge->edge_id, edge->face_left,
1753 edge->face_right, minaz);
1754 minaz = azdif;
1755 }
1756 else if ( azdif > maxaz ) {
1757 data->nextCCW = edge->edge_id; /* outgoing */
1758 data->ccwFace = edge->face_right;
1759 LWDEBUGF(1, "new nextCCW edge is %" LWTFMT_ELEMID
1760 ", outgoing, "
1761 "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID
1762 " (previous had maxaz=%g, face_right is new ccwFace)",
1763 edge->edge_id, edge->face_left,
1764 edge->face_right, maxaz);
1765 maxaz = azdif;
1766 }
1767 }
1768 }
1769
1770 if ( edge->end_node == node ) {
1771 getPoint2d_p(pa, pa->npoints-1, &p1);
1772 if ( ! _lwt_FirstDistinctVertex2D(pa, &p1, pa->npoints-1, -1, &p2) )
1773 {
1774 lwerror("Edge %" LWTFMT_ELEMID " has no distinct vertices: [%.15g %.15g,%.15g %.15g]: ",
1775 edge->edge_id, p1.x, p1.y, p2.x, p2.y);
1776 return -1;
1777 }
1778 LWDEBUGF(1, "edge %" LWTFMT_ELEMID " ends on node %" LWTFMT_ELEMID
1779 ", edgeend is [%.15g %.15g,%.15g %.15g]",
1780 edge->edge_id, node, p1.x, p1.y, p2.x, p2.y);
1781 if ( ! azimuth_pt_pt(&p1, &p2, &az) ) {{
1782 LWT_ELEMID id = edge->edge_id;
1783 _lwt_release_edges(edges, numedges);
1784 lwgeom_free(cleangeom);
1785 lwerror("error computing azimuth of edge %"
1786 LWTFMT_ELEMID " last edgeend [%.15g %.15g,%.15g %.15g]",
1787 id, p1.x, p1.y, p2.x, p2.y);
1788 return -1;
1789 }}
1790 azdif = az - data->myaz;
1791 LWDEBUGF(1, "azimuth of edge %" LWTFMT_ELEMID
1792 ": %.15g (diff: %.15g)", edge->edge_id, az, azdif);
1793 if ( azdif < 0 ) azdif += 2 * M_PI;
1794 if ( minaz == -1 ) {
1795 minaz = maxaz = azdif;
1796 data->nextCW = data->nextCCW = -edge->edge_id; /* incoming */
1797 data->cwFace = edge->face_right;
1798 data->ccwFace = edge->face_left;
1799 LWDEBUGF(1, "new nextCW and nextCCW edge is %" LWTFMT_ELEMID
1800 ", incoming, "
1801 "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID
1802 " (face_right is new cwFace, face_left is new ccwFace)",
1803 edge->edge_id, edge->face_left,
1804 edge->face_right);
1805 } else {
1806 if ( azdif < minaz ) {
1807 data->nextCW = -edge->edge_id; /* incoming */
1808 data->cwFace = edge->face_right;
1809 LWDEBUGF(1, "new nextCW edge is %" LWTFMT_ELEMID
1810 ", incoming, "
1811 "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID
1812 " (previous had minaz=%g, face_right is new cwFace)",
1813 edge->edge_id, edge->face_left,
1814 edge->face_right, minaz);
1815 minaz = azdif;
1816 }
1817 else if ( azdif > maxaz ) {
1818 data->nextCCW = -edge->edge_id; /* incoming */
1819 data->ccwFace = edge->face_left;
1820 LWDEBUGF(1, "new nextCCW edge is %" LWTFMT_ELEMID
1821 ", outgoing, from start point, "
1822 "with face_left %" LWTFMT_ELEMID " and face_right %" LWTFMT_ELEMID
1823 " (previous had maxaz=%g, face_left is new ccwFace)",
1824 edge->edge_id, edge->face_left,
1825 edge->face_right, maxaz);
1826 maxaz = azdif;
1827 }
1828 }
1829 }
1830
1831 lwgeom_free(cleangeom);
1832 }
1833 if ( numedges ) _lwt_release_edges(edges, numedges);
1834
1835 LWDEBUGF(1, "edges adjacent to azimuth %g"
1836 " (incident to node %" LWTFMT_ELEMID ")"
1837 ": CW:%" LWTFMT_ELEMID "(%g) CCW:%" LWTFMT_ELEMID "(%g)",
1838 data->myaz, node, data->nextCW, minaz,
1839 data->nextCCW, maxaz);
1840
1841 if ( myedge_id < 1 && numedges && data->cwFace != data->ccwFace )
1842 {
1843 if ( data->cwFace != -1 && data->ccwFace != -1 ) {
1844 lwerror("Corrupted topology: adjacent edges %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID
1845 " bind different face (%" LWTFMT_ELEMID " and %" LWTFMT_ELEMID ")",
1846 data->nextCW, data->nextCCW,
1847 data->cwFace, data->ccwFace);
1848 return -1;
1849 }
1850 }
1851
1852 /* Return number of incident edges found */
1853 return numedges;
1854}
1855
1856/*
1857 * Get a point internal to the line and write it into the "ip"
1858 * parameter
1859 *
1860 * return 0 on failure (line is empty or collapsed), 1 otherwise
1861 */
1862static int
1864{
1865 uint32_t i;
1866 POINT2D fp, lp, tp;
1867 POINTARRAY *pa = edge->points;
1868
1869 if ( pa->npoints < 2 ) return 0; /* empty or structurally collapsed */
1870
1871 getPoint2d_p(pa, 0, &fp); /* save first point */
1872 getPoint2d_p(pa, pa->npoints-1, &lp); /* save last point */
1873 for (i=1; i<pa->npoints-1; ++i)
1874 {
1875 getPoint2d_p(pa, i, &tp); /* pick next point */
1876 if ( P2D_SAME_STRICT(&tp, &fp) ) continue; /* equal to startpoint */
1877 if ( P2D_SAME_STRICT(&tp, &lp) ) continue; /* equal to endpoint */
1878 /* this is a good one, neither same of start nor of end point */
1879 *ip = tp;
1880 return 1; /* found */
1881 }
1882
1883 /* no distinct vertex found */
1884
1885 /* interpolate if start point != end point */
1886
1887 if ( P2D_SAME_STRICT(&fp, &lp) ) return 0; /* no distinct points in edge */
1888
1889 ip->x = fp.x + ( (lp.x - fp.x) * 0.5 );
1890 ip->y = fp.y + ( (lp.y - fp.y) * 0.5 );
1891
1892 return 1;
1893}
1894
1895static LWPOLY *
1896_lwt_MakeRingShell(LWT_TOPOLOGY *topo, LWT_ELEMID *signed_edge_ids, uint64_t num_signed_edge_ids)
1897{
1898 LWT_ELEMID *edge_ids;
1899 uint64_t numedges, i, j;
1900 LWT_ISO_EDGE *ring_edges;
1901
1902 /* Construct a polygon using edges of the ring */
1903 numedges = 0;
1904 edge_ids = lwalloc(sizeof(LWT_ELEMID)*num_signed_edge_ids);
1905 for (i=0; i<num_signed_edge_ids; ++i) {
1906 int absid = llabs(signed_edge_ids[i]);
1907 int found = 0;
1908 /* Do not add the same edge twice */
1909 for (j=0; j<numedges; ++j) {
1910 if ( edge_ids[j] == absid ) {
1911 found = 1;
1912 break;
1913 }
1914 }
1915 if ( ! found ) edge_ids[numedges++] = absid;
1916 }
1917 i = numedges;
1918 ring_edges = lwt_be_getEdgeById(topo, edge_ids, &i,
1920 lwfree( edge_ids );
1921 if (i == UINT64_MAX)
1922 {
1924 return NULL;
1925 }
1926 else if ( i != numedges )
1927 {
1928 lwfree( signed_edge_ids );
1929 _lwt_release_edges(ring_edges, i);
1930 lwerror("Unexpected error: %" LWTFMT_ELEMID
1931 " edges found when expecting %" PRIu64, i, numedges);
1932 return NULL;
1933 }
1934
1935 /* Should now build a polygon with those edges, in the order
1936 * given by GetRingEdges.
1937 */
1938 POINTARRAY *pa = NULL;
1939 for ( i=0; i<num_signed_edge_ids; ++i )
1940 {
1941 LWT_ELEMID eid = signed_edge_ids[i];
1942 LWDEBUGF(2, "Edge %llu in ring is edge %" LWTFMT_ELEMID, i, eid);
1943 LWT_ISO_EDGE *edge = NULL;
1944 POINTARRAY *epa;
1945 for ( j=0; j<numedges; ++j )
1946 {
1947 if ( ring_edges[j].edge_id == llabs(eid) )
1948 {
1949 edge = &(ring_edges[j]);
1950 break;
1951 }
1952 }
1953 if ( edge == NULL )
1954 {
1955 _lwt_release_edges(ring_edges, numedges);
1956 lwerror("missing edge that was found in ring edges loop");
1957 return NULL;
1958 }
1959
1960 if ( pa == NULL )
1961 {
1962 pa = ptarray_clone_deep(edge->geom->points);
1963 if ( eid < 0 ) ptarray_reverse_in_place(pa);
1964 }
1965 else
1966 {
1967 if ( eid < 0 )
1968 {
1969 epa = ptarray_clone_deep(edge->geom->points);
1971 ptarray_append_ptarray(pa, epa, 0);
1972 ptarray_free(epa);
1973 }
1974 else
1975 {
1976 /* avoid a clone here */
1977 ptarray_append_ptarray(pa, edge->geom->points, 0);
1978 }
1979 }
1980 }
1981 _lwt_release_edges(ring_edges, numedges);
1982 POINTARRAY **points = lwalloc(sizeof(POINTARRAY*));
1983 points[0] = pa;
1984
1985 /* NOTE: the ring may very well have collapsed components,
1986 * which would make it topologically invalid
1987 */
1988 LWPOLY* shell = lwpoly_construct(0, 0, 1, points);
1989 return shell;
1990}
1991
1992/*
1993 * Add a split face by walking on the edge side.
1994 *
1995 * @param topo the topology to act upon
1996 * @param sedge edge id and walking side and direction
1997 * (forward,left:positive backward,right:negative)
1998 * @param face the face in which the edge identifier is known to be
1999 * @param mbr_only do not create a new face but update MBR of the current
2000 *
2001 * @return:
2002 * -1: if mbr_only was requested
2003 * 0: if the edge does not form a ring
2004 * -1: if it is impossible to create a face on the requested side
2005 * ( new face on the side is the universe )
2006 * -2: error
2007 * >0 : id of newly added face
2008 */
2009static LWT_ELEMID
2011 LWT_ELEMID sedge, LWT_ELEMID face,
2012 int mbr_only )
2013{
2014 uint64_t numfaceedges, i, j;
2015 int newface_outside;
2016 uint64_t num_signed_edge_ids;
2017 LWT_ELEMID *signed_edge_ids;
2018 LWT_ISO_EDGE *edges;
2019 LWT_ISO_EDGE *forward_edges = NULL;
2020 int forward_edges_count = 0;
2021 LWT_ISO_EDGE *backward_edges = NULL;
2022 int backward_edges_count = 0;
2023
2024 signed_edge_ids = lwt_be_getRingEdges(topo, sedge, &num_signed_edge_ids, 0);
2025 if (!signed_edge_ids)
2026 {
2027 //PGTOPO_BE_ERRORF("no ring edges for edge %" LWTFMT_ELEMID, sedge);
2029 return -2;
2030 }
2031 LWDEBUGF(1, "getRingEdges returned %llu edges", num_signed_edge_ids);
2032
2033 /* You can't get to the other side of an edge forming a ring */
2034 for (i=0; i<num_signed_edge_ids; ++i) {
2035 if ( signed_edge_ids[i] == -sedge ) {
2036 /* No split here */
2037 LWDEBUG(1, "not a ring");
2038 lwfree( signed_edge_ids );
2039 return 0;
2040 }
2041 }
2042
2043 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " split face %" LWTFMT_ELEMID " (mbr_only:%d)",
2044 sedge, face, mbr_only);
2045
2046 /*
2047 * Construct a polygon using edges of the ring
2048 *
2049 * NOTE: this possibly includes dangling edges
2050 *
2051 */
2052 LWPOLY *shell = _lwt_MakeRingShell(topo, signed_edge_ids,
2053 num_signed_edge_ids);
2054 if ( ! shell ) {
2055 lwfree( signed_edge_ids );
2056 /* ring_edges should be NULL */
2057 lwerror("Could not create ring shell: %s", lwt_be_lastErrorMessage(topo->be_iface));
2058 return -2;
2059 }
2060 const POINTARRAY *pa = shell->rings[0];
2061 if ( ! ptarray_is_closed_2d(pa) )
2062 {
2063 lwpoly_free(shell);
2064 lwfree( signed_edge_ids );
2065 lwerror("Corrupted topology: ring of edge %" LWTFMT_ELEMID
2066 " is geometrically not-closed", sedge);
2067 return -2;
2068 }
2069
2070 int isccw = ptarray_isccw(pa);
2071 LWDEBUGF(1, "Ring of edge %" LWTFMT_ELEMID " is %sclockwise",
2072 sedge, isccw ? "counter" : "");
2073 const GBOX* shellbox = lwgeom_get_bbox(lwpoly_as_lwgeom(shell));
2074
2075 if ( face == 0 )
2076 {
2077 /* Edge split the universe face */
2078 if ( ! isccw )
2079 {
2080 lwpoly_free(shell);
2081 lwfree( signed_edge_ids );
2082 /* Face on the left side of this ring is the universe face.
2083 * Next call (for the other side) should create the split face
2084 */
2085 LWDEBUG(1, "The left face of this clockwise ring is the universe, "
2086 "won't create a new face there");
2087 return -1;
2088 }
2089 }
2090
2091 if ( mbr_only && face != 0 )
2092 {
2093 if ( isccw )
2094 {{
2095 LWT_ISO_FACE updface;
2096 updface.face_id = face;
2097 updface.mbr = (GBOX *)shellbox; /* const cast, we won't free it, later */
2098 int ret = lwt_be_updateFacesById( topo, &updface, 1 );
2099 if ( ret == -1 )
2100 {
2101 lwfree( signed_edge_ids );
2102 lwpoly_free(shell); /* NOTE: owns shellbox above */
2104 return -2;
2105 }
2106 if ( ret != 1 )
2107 {
2108 lwfree( signed_edge_ids );
2109 lwpoly_free(shell); /* NOTE: owns shellbox above */
2110 lwerror("Unexpected error: %d faces found when expecting 1", ret);
2111 return -2;
2112 }
2113 }}
2114 lwfree( signed_edge_ids );
2115 lwpoly_free(shell); /* NOTE: owns shellbox above */
2116 return -1; /* mbr only was requested */
2117 }
2118
2119 LWT_ISO_FACE *oldface = NULL;
2120 LWT_ISO_FACE newface;
2121 newface.face_id = -1;
2122 if ( face != 0 && ! isccw)
2123 {{
2124 /* Face created an hole in an outer face */
2125 uint64_t nfaces = 1;
2126 oldface = lwt_be_getFaceById(topo, &face, &nfaces, LWT_COL_FACE_ALL);
2127 if (nfaces == UINT64_MAX)
2128 {
2129 lwfree( signed_edge_ids );
2130 lwpoly_free(shell); /* NOTE: owns shellbox */
2132 return -2;
2133 }
2134 if ( nfaces != 1 )
2135 {
2136 lwfree( signed_edge_ids );
2137 lwpoly_free(shell); /* NOTE: owns shellbox */
2138 lwerror("Unexpected error: %" PRIu64 " faces found when expecting 1", nfaces);
2139 return -2;
2140 }
2141 newface.mbr = oldface->mbr;
2142 }}
2143 else
2144 {
2145 newface.mbr = (GBOX *)shellbox; /* const cast, we won't free it, later */
2146 }
2147
2148 /* Insert the new face */
2149 int ret = lwt_be_insertFaces( topo, &newface, 1 );
2150 if ( ret == -1 )
2151 {
2152 lwfree( signed_edge_ids );
2153 lwpoly_free(shell); /* NOTE: owns shellbox */
2155 return -2;
2156 }
2157 if ( ret != 1 )
2158 {
2159 lwfree( signed_edge_ids );
2160 lwpoly_free(shell); /* NOTE: owns shellbox */
2161 lwerror("Unexpected error: %d faces inserted when expecting 1", ret);
2162 return -2;
2163 }
2164 if ( oldface ) {
2165 newface.mbr = NULL; /* it is a reference to oldface mbr... */
2166 _lwt_release_faces(oldface, 1);
2167 }
2168
2169 /* Update side location of new face edges */
2170
2171 /* We want the new face to be on the left, if possible */
2172 if ( face != 0 && ! isccw ) { /* ring is clockwise in a real face */
2173 /* face shrunk, must update all non-contained edges and nodes */
2174 LWDEBUG(1, "New face is on the outside of the ring, updating rings in former shell");
2175 newface_outside = 1;
2176 /* newface is outside */
2177 } else {
2178 LWDEBUG(1, "New face is on the inside of the ring, updating forward edges in new ring");
2179 newface_outside = 0;
2180 /* newface is inside */
2181 }
2182
2183 /* Update edges bounding the old face */
2184 /* (1) fetch all edges where left_face or right_face is = oldface */
2185 int fields = LWT_COL_EDGE_EDGE_ID |
2189 ;
2190 numfaceedges = 1;
2191 edges = lwt_be_getEdgeByFace( topo, &face, &numfaceedges, fields, newface.mbr );
2192 if (numfaceedges == UINT64_MAX)
2193 {
2194 lwfree(signed_edge_ids);
2196 return -2;
2197 }
2198 LWDEBUGF(1, "_lwt_AddFaceSplit: lwt_be_getEdgeByFace(%" LWTFMT_ELEMID ") returned %llu edges", face, numfaceedges);
2199
2200 if ( numfaceedges )
2201 {
2202 forward_edges = lwalloc(sizeof(LWT_ISO_EDGE)*numfaceedges);
2203 forward_edges_count = 0;
2204 backward_edges = lwalloc(sizeof(LWT_ISO_EDGE)*numfaceedges);
2205 backward_edges_count = 0;
2206
2207 /* (2) loop over the results and: */
2208 for ( i=0; i<numfaceedges; ++i )
2209 {
2210 LWT_ISO_EDGE *e = &(edges[i]);
2211 int found = 0;
2212 int contains;
2213 POINT2D ep;
2214
2215 /* (2.1) skip edges whose ID is in the list of boundary edges */
2216 for ( j=0; j<num_signed_edge_ids; ++j )
2217 {
2218 int seid = signed_edge_ids[j]; /* signed ring edge id */
2219 if ( seid == e->edge_id )
2220 {
2221 /* IDEA: remove entry from signed_edge_ids, to speed next loop ? */
2222 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is a known forward edge of the new ring", e->edge_id);
2223 forward_edges[forward_edges_count].edge_id = e->edge_id;
2224 forward_edges[forward_edges_count++].face_left = newface.face_id;
2225 found++;
2226 if ( found == 2 ) break; /* both edge sides are found on the ring */
2227 }
2228 else if ( -seid == e->edge_id )
2229 {
2230 /* IDEA: remove entry from signed_edge_ids, to speed next loop ? */
2231 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is a known backward edge of the new ring", e->edge_id);
2232 backward_edges[backward_edges_count].edge_id = e->edge_id;
2233 backward_edges[backward_edges_count++].face_right = newface.face_id;
2234 found++;
2235 if ( found == 2 ) break; /* both edge sides are found on the ring */
2236 }
2237 }
2238 if ( found ) continue;
2239 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is not a known edge of the new ring", e->edge_id);
2240
2241 /* Check if the edge is now binding a different face */
2242
2243 if ( ! getPoint2d_p(e->geom->points, 0, &ep) )
2244 {
2245 lwfree(signed_edge_ids);
2246 lwpoly_free(shell);
2247 lwfree(forward_edges); /* contents owned by edges */
2248 lwfree(backward_edges); /* contents owned by edges */
2249 _lwt_release_edges(edges, numfaceedges);
2250 lwerror("Edge %" LWTFMT_ELEMID " is empty", e->edge_id);
2251 return -2;
2252 }
2253
2256
2257 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " first point %s new ring",
2258 e->edge_id, (contains == LW_INSIDE ? "inside" :
2259 contains == LW_OUTSIDE ? "outside" : "on boundary of"));
2260
2261 /* (2.2) skip edges (NOT, if newface_outside) contained in ring */
2262 if ( newface_outside )
2263 {
2264 if ( contains != LW_OUTSIDE )
2265 {
2266 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " not outside of the new ring, not updating it",
2267 e->edge_id);
2268 continue;
2269 }
2270 }
2271 else
2272 {
2273 if ( contains != LW_INSIDE )
2274 {
2275 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " not inside the new ring, not updating it",
2276 e->edge_id);
2277 continue;
2278 }
2279 }
2280
2281 /* (2.3) push to forward_edges if left_face = oface */
2282 if ( e->face_left == face )
2283 {
2284 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " has new face on the left side", e->edge_id);
2285 forward_edges[forward_edges_count].edge_id = e->edge_id;
2286 forward_edges[forward_edges_count++].face_left = newface.face_id;
2287 }
2288
2289 /* (2.4) push to backward_edges if right_face = oface */
2290 if ( e->face_right == face )
2291 {
2292 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " has new face on the right side", e->edge_id);
2293 backward_edges[backward_edges_count].edge_id = e->edge_id;
2294 backward_edges[backward_edges_count++].face_right = newface.face_id;
2295 }
2296 }
2297
2298 /* Update forward edges */
2299 if ( forward_edges_count )
2300 {
2301 ret = lwt_be_updateEdgesById(topo, forward_edges,
2302 forward_edges_count,
2304 if ( ret == -1 )
2305 {
2306 lwfree( signed_edge_ids );
2308 return -2;
2309 }
2310 if ( ret != forward_edges_count )
2311 {
2312 lwfree( signed_edge_ids );
2313 lwerror("Unexpected error: %d edges updated when expecting %d",
2314 ret, forward_edges_count);
2315 return -2;
2316 }
2317 }
2318
2319 /* Update backward edges */
2320 if ( backward_edges_count )
2321 {
2322 ret = lwt_be_updateEdgesById(topo, backward_edges,
2323 backward_edges_count,
2325 if ( ret == -1 )
2326 {
2327 lwfree( signed_edge_ids );
2329 return -2;
2330 }
2331 if ( ret != backward_edges_count )
2332 {
2333 lwfree( signed_edge_ids );
2334 lwerror("Unexpected error: %d edges updated when expecting %d",
2335 ret, backward_edges_count);
2336 return -2;
2337 }
2338 }
2339
2340 lwfree(forward_edges);
2341 lwfree(backward_edges);
2342
2343 _lwt_release_edges(edges, numfaceedges);
2344 }
2345
2346 /* Update isolated nodes which are now in new face */
2347 uint64_t numisonodes = 1;
2349 LWT_ISO_NODE *nodes = lwt_be_getNodeByFace(topo, &face,
2350 &numisonodes, fields, newface.mbr);
2351 if (numisonodes == UINT64_MAX)
2352 {
2353 lwfree(signed_edge_ids);
2355 return -2;
2356 }
2357 if ( numisonodes ) {
2358 LWT_ISO_NODE *updated_nodes = lwalloc(sizeof(LWT_ISO_NODE)*numisonodes);
2359 int nodes_to_update = 0;
2360 for (i=0; i<numisonodes; ++i)
2361 {
2362 LWT_ISO_NODE *n = &(nodes[i]);
2363 const POINT2D *pt = getPoint2d_cp(n->geom->point, 0);
2364 int contains = ptarray_contains_point(pa, pt) == LW_INSIDE;
2365 LWDEBUGF(1, "Node %" LWTFMT_ELEMID " is %scontained in new ring, newface is %s",
2366 n->node_id, contains ? "" : "not ",
2367 newface_outside ? "outside" : "inside" );
2368 if ( newface_outside )
2369 {
2370 if ( contains )
2371 {
2372 LWDEBUGF(1, "Node %" LWTFMT_ELEMID " contained in an hole of the new face",
2373 n->node_id);
2374 continue;
2375 }
2376 }
2377 else
2378 {
2379 if ( ! contains )
2380 {
2381 LWDEBUGF(1, "Node %" LWTFMT_ELEMID " not contained in the face shell",
2382 n->node_id);
2383 continue;
2384 }
2385 }
2386 updated_nodes[nodes_to_update].node_id = n->node_id;
2387 updated_nodes[nodes_to_update++].containing_face =
2388 newface.face_id;
2389 LWDEBUGF(1, "Node %" LWTFMT_ELEMID " will be updated", n->node_id);
2390 }
2391 _lwt_release_nodes(nodes, numisonodes);
2392 if ( nodes_to_update )
2393 {
2394 int ret = lwt_be_updateNodesById(topo, updated_nodes,
2395 nodes_to_update,
2397 if ( ret == -1 ) {
2398 lwfree( signed_edge_ids );
2400 return -2;
2401 }
2402 }
2403 lwfree(updated_nodes);
2404 }
2405
2406 lwfree(signed_edge_ids);
2407 lwpoly_free(shell);
2408
2409 return newface.face_id;
2410}
2411
2419static LWT_ELEMID
2421 LWT_ELEMID start_node, LWT_ELEMID end_node,
2422 LWLINE *geom, int skipChecks, int modFace )
2423{
2424 LWT_ISO_EDGE newedge;
2425 LWGEOM *cleangeom;
2426 edgeend span; /* start point analysis */
2427 edgeend epan; /* end point analysis */
2428 POINT2D p1, pn, p2;
2429 POINTARRAY *pa;
2430 LWT_ELEMID node_ids[2];
2431 const LWPOINT *start_node_geom = NULL;
2432 const LWPOINT *end_node_geom = NULL;
2433 uint64_t num_nodes;
2434 LWT_ISO_NODE *endpoints;
2435 uint64_t i;
2436 LWT_ELEMID prev_left;
2437 LWT_ELEMID prev_right;
2438 LWT_ISO_EDGE seledge;
2439 LWT_ISO_EDGE updedge;
2440
2441 if ( ! skipChecks )
2442 {
2443 /* curve must be simple */
2444 if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) )
2445 {
2446 lwerror("SQL/MM Spatial exception - curve not simple");
2447 return -1;
2448 }
2449 }
2450
2451 newedge.start_node = start_node;
2452 newedge.end_node = end_node;
2453 newedge.geom = geom;
2454 newedge.face_left = -1;
2455 newedge.face_right = -1;
2456 /* TODO: should do the repeated points removal in 2D space */
2457 cleangeom = lwgeom_remove_repeated_points( lwline_as_lwgeom(geom), 0 );
2458
2459 pa = lwgeom_as_lwline(cleangeom)->points;
2460 if ( pa->npoints < 2 ) {
2461 lwgeom_free(cleangeom);
2462 lwerror("Invalid edge (no two distinct vertices exist)");
2463 return -1;
2464 }
2465
2466 /* Initialize endpoint info (some of that ) */
2467 span.cwFace = span.ccwFace =
2468 epan.cwFace = epan.ccwFace = -1;
2469
2470 /* Compute azimuth of first edge end on start node */
2471 getPoint2d_p(pa, 0, &p1);
2472 if ( ! _lwt_FirstDistinctVertex2D(pa, &p1, 0, 1, &pn) )
2473 {
2474 lwgeom_free(cleangeom);
2475 lwerror("Invalid edge (no two distinct vertices exist)");
2476 return -1;
2477 }
2478 if ( ! azimuth_pt_pt(&p1, &pn, &span.myaz) ) {
2479 lwgeom_free(cleangeom);
2480 lwerror("error computing azimuth of first edgeend [%.15g %.15g,%.15g %.15g]",
2481 p1.x, p1.y, pn.x, pn.y);
2482 return -1;
2483 }
2484 LWDEBUGF(1, "edge's start node is %g,%g", p1.x, p1.y);
2485
2486 /* Compute azimuth of last edge end on end node */
2487 getPoint2d_p(pa, pa->npoints-1, &p2);
2488 if ( ! _lwt_FirstDistinctVertex2D(pa, &p2, pa->npoints-1, -1, &pn) )
2489 {
2490 lwgeom_free(cleangeom);
2491 /* This should never happen as we checked the edge while computing first edgend */
2492 lwerror("Invalid clean edge (no two distinct vertices exist) - should not happen");
2493 return -1;
2494 }
2495 lwgeom_free(cleangeom);
2496 if ( ! azimuth_pt_pt(&p2, &pn, &epan.myaz) ) {
2497 lwerror("error computing azimuth of last edgeend [%.15g %.15g,%.15g %.15g]",
2498 p2.x, p2.y, pn.x, pn.y);
2499 return -1;
2500 }
2501 LWDEBUGF(1, "edge's end node is %g,%g", p2.x, p2.y);
2502
2503 /*
2504 * Check endpoints existence, match with Curve geometry
2505 * and get face information (if any)
2506 */
2507
2508 if ( start_node != end_node ) {
2509 num_nodes = 2;
2510 node_ids[0] = start_node;
2511 node_ids[1] = end_node;
2512 } else {
2513 num_nodes = 1;
2514 node_ids[0] = start_node;
2515 }
2516
2517 endpoints = lwt_be_getNodeById( topo, node_ids, &num_nodes, LWT_COL_NODE_ALL );
2518 if (num_nodes == UINT64_MAX)
2519 {
2521 return -1;
2522 }
2523 for ( i=0; i<num_nodes; ++i )
2524 {
2525 LWT_ISO_NODE* node = &(endpoints[i]);
2526 if ( modFace != -1 && node->containing_face != -1 )
2527 {
2528 if ( newedge.face_left == -1 )
2529 {
2530 newedge.face_left = newedge.face_right = node->containing_face;
2531 }
2532 else if ( newedge.face_left != node->containing_face )
2533 {
2534 _lwt_release_nodes(endpoints, num_nodes);
2535 lwerror("SQL/MM Spatial exception - geometry crosses an edge"
2536 " (endnodes in faces %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID ")",
2537 newedge.face_left, node->containing_face);
2538 }
2539 }
2540
2541 LWDEBUGF(1, "Node %" LWTFMT_ELEMID ", with geom %p (looking for %"
2542 LWTFMT_ELEMID " and %" LWTFMT_ELEMID ")",
2543 node->node_id, node->geom, start_node, end_node);
2544 if ( node->node_id == start_node ) {
2545 start_node_geom = node->geom;
2546 }
2547 if ( node->node_id == end_node ) {
2548 end_node_geom = node->geom;
2549 }
2550 }
2551
2552 if ( ! skipChecks )
2553 {
2554 if ( ! start_node_geom )
2555 {
2556 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2557 lwerror("SQL/MM Spatial exception - non-existent node");
2558 return -1;
2559 }
2560 else
2561 {
2562 pa = start_node_geom->point;
2563 getPoint2d_p(pa, 0, &pn);
2564 if ( ! P2D_SAME_STRICT(&pn, &p1) )
2565 {
2566 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2567 lwerror("SQL/MM Spatial exception"
2568 " - start node not geometry start point."
2569 //" - start node not geometry start point (%g,%g != %g,%g).", pn.x, pn.y, p1.x, p1.y
2570 );
2571 return -1;
2572 }
2573 }
2574
2575 if ( ! end_node_geom )
2576 {
2577 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2578 lwerror("SQL/MM Spatial exception - non-existent node");
2579 return -1;
2580 }
2581 else
2582 {
2583 pa = end_node_geom->point;
2584 getPoint2d_p(pa, 0, &pn);
2585 if ( ! P2D_SAME_STRICT(&pn, &p2) )
2586 {
2587 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2588 lwerror("SQL/MM Spatial exception"
2589 " - end node not geometry end point."
2590 //" - end node not geometry end point (%g,%g != %g,%g).", pn.x, pn.y, p2.x, p2.y
2591 );
2592 return -1;
2593 }
2594 }
2595
2596 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2597
2598 if ( _lwt_CheckEdgeCrossing( topo, start_node, end_node, geom, 0 ) )
2599 return -1;
2600
2601 } /* ! skipChecks */
2602
2603 /*
2604 * All checks passed, time to prepare the new edge
2605 */
2606
2607 newedge.edge_id = lwt_be_getNextEdgeId( topo );
2608 if ( newedge.edge_id == -1 ) {
2610 return -1;
2611 }
2612
2613 /* Find adjacent edges to each endpoint */
2614 int isclosed = start_node == end_node;
2615 int found;
2616 found = _lwt_FindAdjacentEdges( topo, start_node, &span,
2617 isclosed ? &epan : NULL, -1 );
2618 if ( found ) {
2619 span.was_isolated = 0;
2620 newedge.next_right = span.nextCW ? span.nextCW : -newedge.edge_id;
2621 prev_left = span.nextCCW ? -span.nextCCW : newedge.edge_id;
2622 LWDEBUGF(1, "New edge %" LWTFMT_ELEMID " is connected on start node, "
2623 "next_right is %" LWTFMT_ELEMID
2624 ", prev_left is %" LWTFMT_ELEMID,
2625 newedge.edge_id, newedge.next_right, prev_left);
2626 if ( modFace != -1 )
2627 {
2628 if ( newedge.face_right == -1 ) {
2629 newedge.face_right = span.cwFace;
2630 }
2631 if ( newedge.face_left == -1 ) {
2632 newedge.face_left = span.ccwFace;
2633 }
2634 }
2635 } else {
2636 span.was_isolated = 1;
2637 newedge.next_right = isclosed ? -newedge.edge_id : newedge.edge_id;
2638 prev_left = isclosed ? newedge.edge_id : -newedge.edge_id;
2639 LWDEBUGF(1, "New edge %" LWTFMT_ELEMID " is isolated on start node, "
2640 "next_right is %" LWTFMT_ELEMID
2641 ", prev_left is %" LWTFMT_ELEMID,
2642 newedge.edge_id, newedge.next_right, prev_left);
2643 }
2644
2645 found = _lwt_FindAdjacentEdges( topo, end_node, &epan,
2646 isclosed ? &span : NULL, -1 );
2647 if ( found ) {
2648 epan.was_isolated = 0;
2649 newedge.next_left = epan.nextCW ? epan.nextCW : newedge.edge_id;
2650 prev_right = epan.nextCCW ? -epan.nextCCW : -newedge.edge_id;
2651 LWDEBUGF(1, "New edge %" LWTFMT_ELEMID " is connected on end node, "
2652 "next_left is %" LWTFMT_ELEMID
2653 ", prev_right is %" LWTFMT_ELEMID,
2654 newedge.edge_id, newedge.next_left, prev_right);
2655 if ( modFace != -1 )
2656 {
2657 if ( newedge.face_right == -1 ) {
2658 newedge.face_right = span.ccwFace;
2659 } else if ( newedge.face_right != epan.ccwFace ) {
2660 /* side-location conflict */
2661 lwerror("Side-location conflict: "
2662 "new edge starts in face"
2663 " %" LWTFMT_ELEMID " and ends in face"
2664 " %" LWTFMT_ELEMID,
2665 newedge.face_right, epan.ccwFace
2666 );
2667 return -1;
2668 }
2669 if ( newedge.face_left == -1 ) {
2670 newedge.face_left = span.cwFace;
2671 } else if ( newedge.face_left != epan.cwFace ) {
2672 /* side-location conflict */
2673 lwerror("Side-location conflict: "
2674 "new edge starts in face"
2675 " %" LWTFMT_ELEMID " and ends in face"
2676 " %" LWTFMT_ELEMID,
2677 newedge.face_left, epan.cwFace
2678 );
2679 return -1;
2680 }
2681 }
2682 } else {
2683 epan.was_isolated = 1;
2684 newedge.next_left = isclosed ? newedge.edge_id : -newedge.edge_id;
2685 prev_right = isclosed ? -newedge.edge_id : newedge.edge_id;
2686 LWDEBUGF(1, "New edge %" LWTFMT_ELEMID " is isolated on end node, "
2687 "next_left is %" LWTFMT_ELEMID
2688 ", prev_right is %" LWTFMT_ELEMID,
2689 newedge.edge_id, newedge.next_left, prev_right);
2690 }
2691
2692 /*
2693 * If we don't have faces setup by now we must have encountered
2694 * a malformed topology (no containing_face on isolated nodes, no
2695 * left/right faces on adjacent edges or mismatching values)
2696 */
2697 if ( modFace > -1 )
2698 {
2699 if ( newedge.face_left != newedge.face_right )
2700 {
2701 lwerror("Left(%" LWTFMT_ELEMID ")/right(%" LWTFMT_ELEMID ")"
2702 " faces mismatch: invalid topology ?",
2703 newedge.face_left, newedge.face_right);
2704 return -1;
2705 }
2706 else if ( newedge.face_left == -1 )
2707 {
2708 lwerror("Could not derive edge face from linked primitives:"
2709 " invalid topology ?");
2710 return -1;
2711 }
2712 }
2713
2714 /*
2715 * Insert the new edge, and update all linking
2716 */
2717
2718 int ret = lwt_be_insertEdges(topo, &newedge, 1);
2719 if ( ret == -1 ) {
2721 return -1;
2722 } else if ( ret == 0 ) {
2723 lwerror("Insertion of split edge failed (no reason)");
2724 return -1;
2725 }
2726
2727 int updfields;
2728
2729 /* Link prev_left to us
2730 * (if it's not us already) */
2731 if ( llabs(prev_left) != newedge.edge_id )
2732 {
2733 if ( prev_left > 0 )
2734 {
2735 /* its next_left_edge is us */
2736 updfields = LWT_COL_EDGE_NEXT_LEFT;
2737 updedge.next_left = newedge.edge_id;
2738 seledge.edge_id = prev_left;
2739 }
2740 else
2741 {
2742 /* its next_right_edge is us */
2743 updfields = LWT_COL_EDGE_NEXT_RIGHT;
2744 updedge.next_right = newedge.edge_id;
2745 seledge.edge_id = -prev_left;
2746 }
2747
2748 ret = lwt_be_updateEdges(topo,
2749 &seledge, LWT_COL_EDGE_EDGE_ID,
2750 &updedge, updfields,
2751 NULL, 0);
2752 if ( ret == -1 ) {
2754 return -1;
2755 }
2756 }
2757
2758 /* Link prev_right to us
2759 * (if it's not us already) */
2760 if ( llabs(prev_right) != newedge.edge_id )
2761 {
2762 if ( prev_right > 0 )
2763 {
2764 /* its next_left_edge is -us */
2765 updfields = LWT_COL_EDGE_NEXT_LEFT;
2766 updedge.next_left = -newedge.edge_id;
2767 seledge.edge_id = prev_right;
2768 }
2769 else
2770 {
2771 /* its next_right_edge is -us */
2772 updfields = LWT_COL_EDGE_NEXT_RIGHT;
2773 updedge.next_right = -newedge.edge_id;
2774 seledge.edge_id = -prev_right;
2775 }
2776
2777 ret = lwt_be_updateEdges(topo,
2778 &seledge, LWT_COL_EDGE_EDGE_ID,
2779 &updedge, updfields,
2780 NULL, 0);
2781 if ( ret == -1 ) {
2783 return -1;
2784 }
2785 }
2786
2787 /* NOT IN THE SPECS...
2788 * set containing_face = null for start_node and end_node
2789 * if they where isolated
2790 *
2791 */
2792 LWT_ISO_NODE updnode, selnode;
2793 updnode.containing_face = -1;
2794 if ( span.was_isolated )
2795 {
2796 selnode.node_id = start_node;
2797 ret = lwt_be_updateNodes(topo,
2798 &selnode, LWT_COL_NODE_NODE_ID,
2800 NULL, 0);
2801 if ( ret == -1 ) {
2803 return -1;
2804 }
2805 }
2806 if ( epan.was_isolated )
2807 {
2808 selnode.node_id = end_node;
2809 ret = lwt_be_updateNodes(topo,
2810 &selnode, LWT_COL_NODE_NODE_ID,
2812 NULL, 0);
2813 if ( ret == -1 ) {
2815 return -1;
2816 }
2817 }
2818
2819 /* Check face splitting, if required */
2820
2821 if ( modFace > -1 ) {
2822
2823 if ( ! isclosed && ( epan.was_isolated || span.was_isolated ) )
2824 {
2825 LWDEBUG(1, "New edge is dangling, so it cannot split any face");
2826 return newedge.edge_id; /* no split */
2827 }
2828
2829 int newface1 = -1;
2830
2831 /* IDEA: avoid building edge ring if input is closed, which means we
2832 * know in advance it splits a face */
2833
2834 if ( ! modFace )
2835 {
2836 newface1 = _lwt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 0 );
2837 if ( newface1 == 0 ) {
2838 LWDEBUG(1, "New edge does not split any face");
2839 return newedge.edge_id; /* no split */
2840 }
2841 }
2842
2843 int newface = _lwt_AddFaceSplit( topo, newedge.edge_id,
2844 newedge.face_left, 0 );
2845 if ( modFace )
2846 {
2847 if ( newface == 0 ) {
2848 LWDEBUG(1, "New edge does not split any face");
2849 return newedge.edge_id; /* no split */
2850 }
2851
2852 if ( newface < 0 )
2853 {
2854 /* face on the left is the universe face */
2855 /* must be forming a maximal ring in universal face */
2856 newface = _lwt_AddFaceSplit( topo, -newedge.edge_id,
2857 newedge.face_left, 0 );
2858 if ( newface < 0 ) return newedge.edge_id; /* no split */
2859 }
2860 else
2861 {
2862 _lwt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 1 );
2863 }
2864 }
2865
2866 /*
2867 * Update topogeometries, if needed
2868 */
2869 if ( newedge.face_left != 0 )
2870 {
2871 ret = lwt_be_updateTopoGeomFaceSplit(topo, newedge.face_left,
2872 newface, newface1);
2873 if ( ret == 0 ) {
2875 return -1;
2876 }
2877
2878 if ( ! modFace )
2879 {
2880 /* drop old face from the face table */
2881 ret = lwt_be_deleteFacesById(topo, &(newedge.face_left), 1);
2882 if ( ret == -1 ) {
2884 return -1;
2885 }
2886 }
2887 }
2888
2889 } // end of face split checking
2890
2891 return newedge.edge_id;
2892}
2893
2896 LWT_ELEMID start_node, LWT_ELEMID end_node,
2897 LWLINE *geom, int skipChecks )
2898{
2899 return _lwt_AddEdge( topo, start_node, end_node, geom, skipChecks, 1 );
2900}
2901
2904 LWT_ELEMID start_node, LWT_ELEMID end_node,
2905 LWLINE *geom, int skipChecks )
2906{
2907 return _lwt_AddEdge( topo, start_node, end_node, geom, skipChecks, 0 );
2908}
2909
2910static LWGEOM *
2911_lwt_FaceByEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edges, int numfaceedges)
2912{
2913 LWGEOM *outg;
2914 LWCOLLECTION *bounds;
2915 LWGEOM **geoms = lwalloc( sizeof(LWGEOM*) * numfaceedges );
2916 int i, validedges = 0;
2917
2918 for ( i=0; i<numfaceedges; ++i )
2919 {
2920 /* NOTE: skipping edges with same face on both sides, although
2921 * correct, results in a failure to build faces from
2922 * invalid topologies as expected by legacy tests.
2923 * TODO: update legacy tests expectances/unleash this skipping ?
2924 */
2925 /* if ( edges[i].face_left == edges[i].face_right ) continue; */
2926 geoms[validedges++] = lwline_as_lwgeom(edges[i].geom);
2927 }
2928 if ( ! validedges )
2929 {
2930 /* Face has no valid boundary edges, we'll return EMPTY, see
2931 * https://trac.osgeo.org/postgis/ticket/3221 */
2932 if ( numfaceedges ) lwfree(geoms);
2933 LWDEBUG(1, "_lwt_FaceByEdges returning empty polygon");
2934 return lwpoly_as_lwgeom(
2935 lwpoly_construct_empty(topo->srid, topo->hasZ, 0)
2936 );
2937 }
2939 topo->srid,
2940 NULL, /* gbox */
2941 validedges,
2942 geoms);
2943 outg = lwgeom_buildarea( lwcollection_as_lwgeom(bounds) );
2944 lwcollection_release(bounds);
2945 lwfree(geoms);
2946#if 0
2947 {
2948 size_t sz;
2949 char *wkt = lwgeom_to_wkt(outg, WKT_EXTENDED, 2, &sz);
2950 LWDEBUGF(1, "_lwt_FaceByEdges returning area: %s", wkt);
2951 lwfree(wkt);
2952 }
2953#endif
2954 return outg;
2955}
2956
2957LWGEOM*
2959{
2960 uint64_t numfaceedges;
2961 LWT_ISO_EDGE *edges;
2962 LWT_ISO_FACE *face;
2963 LWPOLY *out;
2964 LWGEOM *outg;
2965 uint64_t i, edgeid;
2966 int fields;
2967
2968 if (faceid == 0)
2969 {
2970 lwerror("SQL/MM Spatial exception - universal face has no geometry");
2971 return NULL;
2972 }
2973
2974 /* Construct the face geometry */
2975 numfaceedges = 1;
2976 fields = LWT_COL_EDGE_GEOM |
2980 ;
2981 edges = lwt_be_getEdgeByFace( topo, &faceid, &numfaceedges, fields, NULL );
2982 if (numfaceedges == UINT64_MAX)
2983 {
2985 return NULL;
2986 }
2987 LWDEBUGF(1, "lwt_GetFaceGeometry: lwt_be_getEdgeByFace returned %llu edges", numfaceedges);
2988
2989 if ( numfaceedges == 0 )
2990 {
2991 i = 1;
2992 face = lwt_be_getFaceById(topo, &faceid, &i, LWT_COL_FACE_FACE_ID);
2993 if (i == UINT64_MAX)
2994 {
2996 return NULL;
2997 }
2998 if ( i == 0 ) {
2999 lwerror("SQL/MM Spatial exception - non-existent face.");
3000 return NULL;
3001 }
3002 lwfree( face );
3003 if ( i > 1 ) {
3004 lwerror("Corrupted topology: multiple face records have face_id=%"
3005 LWTFMT_ELEMID, faceid);
3006 return NULL;
3007 }
3008 /* Face has no boundary edges, we'll return EMPTY, see
3009 * https://trac.osgeo.org/postgis/ticket/3221 */
3010 lwnotice("Corrupted topology: face %"
3011 LWTFMT_ELEMID " has no associated edges.", faceid);
3012 out = lwpoly_construct_empty(topo->srid, topo->hasZ, 0);
3013 return lwpoly_as_lwgeom(out);
3014 }
3015 edgeid = edges[0].edge_id;
3016
3017 outg = _lwt_FaceByEdges( topo, edges, numfaceedges );
3018 _lwt_release_edges(edges, numfaceedges);
3019
3020 if ( ! outg )
3021 {
3022 /* Face did have edges but no polygon could be constructed
3023 * with that material, sounds like a corrupted topology..
3024 *
3025 * We'll return EMPTY, see
3026 * https://trac.osgeo.org/postgis/ticket/3221 */
3027 lwnotice("Corrupted topology: face %"
3028 LWTFMT_ELEMID " could not be constructed only from edges "
3029 "knowing about it (like edge %" LWTFMT_ELEMID ").",
3030 faceid, edgeid);
3031 out = lwpoly_construct_empty(topo->srid, topo->hasZ, 0);
3032 return lwpoly_as_lwgeom(out);
3033 }
3034
3035 return outg;
3036}
3037
3038/* Find which edge from the "edges" set defines the next
3039 * portion of the given "ring".
3040 *
3041 * The edge might be either forward or backward.
3042 *
3043 * @param ring The ring to find definition of.
3044 * It is assumed it does not contain duplicated vertices.
3045 * @param from offset of the ring point to start looking from
3046 * @param edges array of edges to search into
3047 * @param numedges number of edges in the edges array
3048 *
3049 * @return index of the edge defining the next ring portion or
3050 * -1 if no edge was found to be part of the ring
3051 */
3052static int
3053_lwt_FindNextRingEdge(const POINTARRAY *ring, int from,
3054 const LWT_ISO_EDGE *edges, int numedges)
3055{
3056 int i;
3057 POINT2D p1;
3058
3059 /* Get starting ring point */
3060 getPoint2d_p(ring, from, &p1);
3061
3062 LWDEBUGF(1, "Ring's 'from' point (%d) is %g,%g", from, p1.x, p1.y);
3063
3064 /* find the edges defining the next portion of ring starting from
3065 * vertex "from" */
3066 for ( i=0; i<numedges; ++i )
3067 {
3068 const LWT_ISO_EDGE *isoe = &(edges[i]);
3069 LWLINE *edge = isoe->geom;
3070 POINTARRAY *epa = edge->points;
3071 POINT2D p2, pt;
3072 int match = 0;
3073 uint32_t j;
3074
3075 /* Skip if the edge is a dangling one */
3076 if ( isoe->face_left == isoe->face_right )
3077 {
3078 LWDEBUGF(3, "_lwt_FindNextRingEdge: edge %" LWTFMT_ELEMID
3079 " has same face (%" LWTFMT_ELEMID
3080 ") on both sides, skipping",
3081 isoe->edge_id, isoe->face_left);
3082 continue;
3083 }
3084
3085 if (epa->npoints < 2)
3086 {
3087 LWDEBUGF(3, "_lwt_FindNextRingEdge: edge %" LWTFMT_ELEMID
3088 " has only %"PRIu32" points",
3089 isoe->edge_id, epa->npoints);
3090 continue;
3091 }
3092
3093#if 0
3094 size_t sz;
3095 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is %s",
3096 isoe->edge_id,
3098#endif
3099
3100 /* ptarray_remove_repeated_points ? */
3101
3102 getPoint2d_p(epa, 0, &p2);
3103 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " 'first' point is %g,%g",
3104 isoe->edge_id, p2.x, p2.y);
3105 LWDEBUGF(1, "Rings's 'from' point is still %g,%g", p1.x, p1.y);
3106 if ( P2D_SAME_STRICT(&p1, &p2) )
3107 {
3108 LWDEBUG(1, "P2D_SAME_STRICT(p1,p2) returned true");
3109 LWDEBUGF(1, "First point of edge %" LWTFMT_ELEMID
3110 " matches ring vertex %d", isoe->edge_id, from);
3111 /* first point matches, let's check next non-equal one */
3112 for ( j=1; j<epa->npoints; ++j )
3113 {
3114 getPoint2d_p(epa, j, &p2);
3115 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " 'next' point %d is %g,%g",
3116 isoe->edge_id, j, p2.x, p2.y);
3117 /* we won't check duplicated edge points */
3118 if ( P2D_SAME_STRICT(&p1, &p2) ) continue;
3119 /* we assume there are no duplicated points in ring */
3120 getPoint2d_p(ring, from+1, &pt);
3121 LWDEBUGF(1, "Ring's point %d is %g,%g",
3122 from+1, pt.x, pt.y);
3123 match = P2D_SAME_STRICT(&pt, &p2);
3124 break; /* we want to check a single non-equal next vertex */
3125 }
3126#if POSTGIS_DEBUG_LEVEL > 0
3127 if ( match ) {
3128 LWDEBUGF(1, "Prev point of edge %" LWTFMT_ELEMID
3129 " matches ring vertex %d", isoe->edge_id, from+1);
3130 } else {
3131 LWDEBUGF(1, "Prev point of edge %" LWTFMT_ELEMID
3132 " does not match ring vertex %d", isoe->edge_id, from+1);
3133 }
3134#endif
3135 }
3136
3137 if ( ! match )
3138 {
3139 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " did not match as forward",
3140 isoe->edge_id);
3141 getPoint2d_p(epa, epa->npoints-1, &p2);
3142 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " 'last' point is %g,%g",
3143 isoe->edge_id, p2.x, p2.y);
3144 if ( P2D_SAME_STRICT(&p1, &p2) )
3145 {
3146 LWDEBUGF(1, "Last point of edge %" LWTFMT_ELEMID
3147 " matches ring vertex %d", isoe->edge_id, from);
3148 /* last point matches, let's check next non-equal one */
3149 for ( j=2; j<=epa->npoints; j++ )
3150 {
3151 getPoint2d_p(epa, epa->npoints - j, &p2);
3152 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " 'prev' point %d is %g,%g",
3153 isoe->edge_id, epa->npoints - j, p2.x, p2.y);
3154 /* we won't check duplicated edge points */
3155 if ( P2D_SAME_STRICT(&p1, &p2) ) continue;
3156 /* we assume there are no duplicated points in ring */
3157 getPoint2d_p(ring, from+1, &pt);
3158 LWDEBUGF(1, "Ring's point %d is %g,%g",
3159 from+1, pt.x, pt.y);
3160 match = P2D_SAME_STRICT(&pt, &p2);
3161 break; /* we want to check a single non-equal next vertex */
3162 }
3163 }
3164#if POSTGIS_DEBUG_LEVEL > 0
3165 if ( match ) {
3166 LWDEBUGF(1, "Prev point of edge %" LWTFMT_ELEMID
3167 " matches ring vertex %d", isoe->edge_id, from+1);
3168 } else {
3169 LWDEBUGF(1, "Prev point of edge %" LWTFMT_ELEMID
3170 " does not match ring vertex %d", isoe->edge_id, from+1);
3171 }
3172#endif
3173 }
3174
3175 if ( match ) return i;
3176
3177 }
3178
3179 return -1;
3180}
3181
3182/* Reverse values in array between "from" (inclusive)
3183 * and "to" (exclusive) indexes */
3184static void
3186{
3187 LWT_ELEMID t;
3188 while (from < to)
3189 {
3190 t = ary[from];
3191 ary[from++] = ary[to];
3192 ary[to--] = t;
3193 }
3194}
3195
3196/* Rotate values in array between "from" (inclusive)
3197 * and "to" (exclusive) indexes, so that "rotidx" is
3198 * the new value at "from" */
3199static void
3200_lwt_RotateElemidArray(LWT_ELEMID *ary, int from, int to, int rotidx)
3201{
3202 _lwt_ReverseElemidArray(ary, from, rotidx-1);
3203 _lwt_ReverseElemidArray(ary, rotidx, to-1);
3204 _lwt_ReverseElemidArray(ary, from, to-1);
3205}
3206
3207
3208int
3210{
3211 LWGEOM *face;
3212 LWPOLY *facepoly;
3213 LWT_ISO_EDGE *edges;
3214 uint64_t numfaceedges;
3215 int fields;
3216 uint32_t i;
3217 int nseid = 0; /* number of signed edge ids */
3218 int prevseid;
3219 LWT_ELEMID *seid; /* signed edge ids */
3220
3221 /* Get list of face edges */
3222 numfaceedges = 1;
3223 fields = LWT_COL_EDGE_EDGE_ID |
3227 ;
3228 edges = lwt_be_getEdgeByFace( topo, &face_id, &numfaceedges, fields, NULL );
3229 if (numfaceedges == UINT64_MAX)
3230 {
3232 return -1;
3233 }
3234 if ( ! numfaceedges ) return 0; /* no edges in output */
3235 LWDEBUGF(1, "lwt_GetFaceEdges: lwt_be_getEdgeByFace returned %llu edges", numfaceedges);
3236
3237 /* order edges by occurrence in face */
3238
3239 face = _lwt_FaceByEdges(topo, edges, numfaceedges);
3240 if ( ! face )
3241 {
3242 /* _lwt_FaceByEdges should have already invoked lwerror in this case */
3243 _lwt_release_edges(edges, numfaceedges);
3244 lwerror("Corrupted topology: unable to build geometry of face %"
3245 LWTFMT_ELEMID " from its %"PRIu64" edges", face_id, numfaceedges);
3246 return -1;
3247 }
3248
3249 if ( lwgeom_is_empty(face) )
3250 {
3251 /* no edges in output */
3252 _lwt_release_edges(edges, numfaceedges);
3253 lwgeom_free(face);
3254 return 0;
3255 }
3256
3257 /* force_lhr, if the face is not the universe */
3258 /* _lwt_FaceByEdges seems to guaranteed RHR */
3259 /* lwgeom_force_clockwise(face); */
3260 if ( face_id ) lwgeom_reverse_in_place(face);
3261
3262#if 0
3263 {
3264 size_t sz;
3265 char *wkt = lwgeom_to_wkt(face, WKT_EXTENDED, 6, &sz);
3266 LWDEBUGF(1, "Geometry of face %" LWTFMT_ELEMID " is: %s",
3267 face_id, wkt);
3268 lwfree(wkt);
3269 }
3270#endif
3271
3272 facepoly = lwgeom_as_lwpoly(face);
3273 if ( ! facepoly )
3274 {
3275 _lwt_release_edges(edges, numfaceedges);
3276 lwgeom_free(face);
3277 lwerror("Geometry of face %" LWTFMT_ELEMID " is not a polygon", face_id);
3278 return -1;
3279 }
3280
3281 nseid = prevseid = 0;
3282 seid = lwalloc( sizeof(LWT_ELEMID) * numfaceedges );
3283
3284 /* for each ring of the face polygon... */
3285 for ( i=0; i<facepoly->nrings; ++i )
3286 {
3287 const POINTARRAY *ring = facepoly->rings[i];
3288 int32_t j = 0;
3289 LWT_ISO_EDGE *nextedge;
3290 LWLINE *nextline;
3291
3292 LWDEBUGF(1, "Ring %d has %d points", i, ring->npoints);
3293
3294 while ( j < (int32_t) ring->npoints-1 )
3295 {
3296 LWDEBUGF(1, "Looking for edge covering ring %d from vertex %d",
3297 i, j);
3298
3299 int edgeno = _lwt_FindNextRingEdge(ring, j, edges, numfaceedges);
3300 if ( edgeno == -1 )
3301 {
3302 /* should never happen */
3303 _lwt_release_edges(edges, numfaceedges);
3304 lwgeom_free(face);
3305 lwfree(seid);
3306 lwerror("No edge (among %" PRIu64 ") found to be defining geometry of face %"
3307 LWTFMT_ELEMID, numfaceedges, face_id);
3308 return -1;
3309 }
3310
3311 nextedge = &(edges[edgeno]);
3312 nextline = nextedge->geom;
3313
3314 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID
3315 " covers ring %d from vertex %d to %d",
3316 nextedge->edge_id, i, j, j + nextline->points->npoints - 1);
3317
3318#if 0
3319 {
3320 size_t sz;
3321 char *wkt = lwgeom_to_wkt(lwline_as_lwgeom(nextline), WKT_EXTENDED, 6, &sz);
3322 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is %s",
3323 nextedge->edge_id, wkt);
3324 lwfree(wkt);
3325 }
3326#endif
3327
3328 j += nextline->points->npoints - 1;
3329
3330 /* Add next edge to the output array */
3331 seid[nseid++] = nextedge->face_left == face_id ?
3332 nextedge->edge_id :
3333 -nextedge->edge_id;
3334
3335 /* avoid checking again on next time turn */
3336 nextedge->face_left = nextedge->face_right = -1;
3337 }
3338
3339 /* now "scroll" the list of edges so that the one
3340 * with smaller absolute edge_id is first */
3341 /* Range is: [prevseid, nseid) -- [inclusive, exclusive) */
3342 if ( (nseid - prevseid) > 1 )
3343 {{
3344 LWT_ELEMID minid = 0;
3345 int minidx = 0;
3346 LWDEBUGF(1, "Looking for smallest id among the %d edges "
3347 "composing ring %d", (nseid-prevseid), i);
3348 for ( j=prevseid; j<nseid; ++j )
3349 {
3350 LWT_ELEMID id = llabs(seid[j]);
3351 LWDEBUGF(1, "Abs id of edge in pos %d is %" LWTFMT_ELEMID, j, id);
3352 if ( ! minid || id < minid )
3353 {
3354 minid = id;
3355 minidx = j;
3356 }
3357 }
3358 LWDEBUGF(1, "Smallest id is %" LWTFMT_ELEMID
3359 " at position %d", minid, minidx);
3360 if ( minidx != prevseid )
3361 {
3362 _lwt_RotateElemidArray(seid, prevseid, nseid, minidx);
3363 }
3364 }}
3365
3366 prevseid = nseid;
3367 }
3368
3369 lwgeom_free(face);
3370 _lwt_release_edges(edges, numfaceedges);
3371
3372 *out = seid;
3373 return nseid;
3374}
3375
3376int
3378{
3379 LWT_ISO_EDGE *oldedge;
3380 LWT_ISO_EDGE newedge;
3381 POINT2D p1, p2, pt;
3382 uint64_t i;
3383 int isclosed = 0;
3384 int leftRingIsCCW = -1;
3385
3386 /* curve must be simple */
3387 if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) )
3388 {
3389 lwerror("SQL/MM Spatial exception - curve not simple");
3390 return -1;
3391 }
3392
3393 i = 1;
3394 oldedge = lwt_be_getEdgeById(topo, &edge_id, &i, LWT_COL_EDGE_ALL);
3395 if ( ! oldedge )
3396 {
3397 LWDEBUGF(1, "lwt_ChangeEdgeGeom: "
3398 "lwt_be_getEdgeById returned NULL and set i=%llu", i);
3399 if (i == UINT64_MAX)
3400 {
3402 return -1;
3403 }
3404 else if ( i == 0 )
3405 {
3406 lwerror("SQL/MM Spatial exception - non-existent edge %"
3407 LWTFMT_ELEMID, edge_id);
3408 return -1;
3409 }
3410 else
3411 {
3412 lwerror("Backend coding error: getEdgeById callback returned NULL "
3413 "but numelements output parameter has value %" PRIu64 " "
3414 "(expected 0 or 1)", i);
3415 return -1;
3416 }
3417 }
3418
3419 LWDEBUGF(1, "lwt_ChangeEdgeGeom: "
3420 "old edge has %d points, new edge has %d points",
3421 oldedge->geom->points->npoints, geom->points->npoints);
3422
3423 /*
3424 * e) Check StartPoint consistency
3425 */
3426 getPoint2d_p(oldedge->geom->points, 0, &p1);
3427 getPoint2d_p(geom->points, 0, &pt);
3428 if ( ! P2D_SAME_STRICT(&p1, &pt) )
3429 {
3430 _lwt_release_edges(oldedge, 1);
3431 lwerror("SQL/MM Spatial exception - "
3432 "start node not geometry start point.");
3433 return -1;
3434 }
3435
3436 /*
3437 * f) Check EndPoint consistency
3438 */
3439 if ( oldedge->geom->points->npoints < 2 )
3440 {
3441 _lwt_release_edges(oldedge, 1);
3442 lwerror("Corrupted topology: edge %" LWTFMT_ELEMID
3443 " has less than 2 vertices", oldedge->edge_id);
3444 return -1;
3445 }
3446 getPoint2d_p(oldedge->geom->points, oldedge->geom->points->npoints-1, &p2);
3447 if ( geom->points->npoints < 2 )
3448 {
3449 _lwt_release_edges(oldedge, 1);
3450 lwerror("Invalid edge: less than 2 vertices");
3451 return -1;
3452 }
3453 getPoint2d_p(geom->points, geom->points->npoints-1, &pt);
3454 if ( ! P2D_SAME_STRICT(&pt, &p2) )
3455 {
3456 _lwt_release_edges(oldedge, 1);
3457 lwerror("SQL/MM Spatial exception - "
3458 "end node not geometry end point.");
3459 return -1;
3460 }
3461
3462 /* Not in the specs:
3463 * if the edge is closed, check we didn't change winding !
3464 * (should be part of isomorphism checking)
3465 */
3466 if ( oldedge->start_node == oldedge->end_node )
3467 {
3468 isclosed = 1;
3469#if 1 /* TODO: this is actually bogus as a test */
3470 /* check for valid edge (distinct vertices must exist) */
3471 if ( ! _lwt_GetInteriorEdgePoint(geom, &pt) )
3472 {
3473 _lwt_release_edges(oldedge, 1);
3474 lwerror("Invalid edge (no two distinct vertices exist)");
3475 return -1;
3476 }
3477#endif
3478
3479 if ( ptarray_isccw(oldedge->geom->points) !=
3480 ptarray_isccw(geom->points) )
3481 {
3482 _lwt_release_edges(oldedge, 1);
3483 lwerror("Edge twist at node POINT(%g %g)", p1.x, p1.y);
3484 return -1;
3485 }
3486 }
3487
3488 if ( _lwt_CheckEdgeCrossing(topo, oldedge->start_node,
3489 oldedge->end_node, geom, edge_id ) )
3490 {
3491 /* would have called lwerror already, leaking :( */
3492 _lwt_release_edges(oldedge, 1);
3493 return -1;
3494 }
3495
3496 LWDEBUG(1, "lwt_ChangeEdgeGeom: "
3497 "edge crossing check passed ");
3498
3499 /*
3500 * Not in the specs:
3501 * Check topological isomorphism
3502 */
3503
3504 /* Check that the "motion range" doesn't include any node */
3505 // 1. compute combined bbox of old and new edge
3506 GBOX mbox; /* motion box */
3507 lwgeom_add_bbox((LWGEOM*)oldedge->geom); /* just in case */
3508 lwgeom_add_bbox((LWGEOM*)geom); /* just in case */
3509 gbox_union(oldedge->geom->bbox, geom->bbox, &mbox);
3510 // 2. fetch all nodes in the combined box
3511 LWT_ISO_NODE *nodes;
3512 uint64_t numnodes;
3513 nodes = lwt_be_getNodeWithinBox2D(topo, &mbox, &numnodes,
3514 LWT_COL_NODE_ALL, 0);
3515 LWDEBUGF(1, "lwt_be_getNodeWithinBox2D returned %llu nodes", numnodes);
3516 if (numnodes == UINT64_MAX)
3517 {
3518 _lwt_release_edges(oldedge, 1);
3520 return -1;
3521 }
3522 // 3. if any node beside endnodes are found:
3523 if ( numnodes > ( 1 + isclosed ? 0 : 1 ) )
3524 {{
3525 // 3.2. bail out if any node is in one and not the other
3526 for (i=0; i<numnodes; ++i)
3527 {
3528 LWT_ISO_NODE *n = &(nodes[i]);
3529 if ( n->node_id == oldedge->start_node ) continue;
3530 if ( n->node_id == oldedge->end_node ) continue;
3531 const POINT2D *pt = getPoint2d_cp(n->geom->point, 0);
3532 int ocont = ptarray_contains_point_partial(oldedge->geom->points, pt, isclosed, NULL) == LW_INSIDE;
3533 int ncont = ptarray_contains_point_partial(geom->points, pt, isclosed, NULL) == LW_INSIDE;
3534 if (ocont != ncont)
3535 {
3536 size_t sz;
3537 char *wkt = lwgeom_to_wkt(lwpoint_as_lwgeom(n->geom), WKT_ISO, 15, &sz);
3538 _lwt_release_nodes(nodes, numnodes);
3539 lwerror("Edge motion collision at %s", wkt);
3540 lwfree(wkt); /* would not necessarely reach this point */
3541 return -1;
3542 }
3543 }
3544 }}
3545 if ( numnodes ) _lwt_release_nodes(nodes, numnodes);
3546
3547 LWDEBUG(1, "nodes containment check passed");
3548
3549 /*
3550 * Check edge adjacency before
3551 * TODO: can be optimized to gather azimuths of all edge ends once
3552 */
3553
3554 edgeend span_pre, epan_pre;
3555 /* initialize span_pre.myaz and epan_pre.myaz with existing edge */
3556 int res = _lwt_InitEdgeEndByLine(&span_pre, &epan_pre, oldedge->geom, &p1, &p2);
3557 if (res)
3558 return -1; /* lwerror should have been raised */
3559 _lwt_FindAdjacentEdges( topo, oldedge->start_node, &span_pre,
3560 isclosed ? &epan_pre : NULL, edge_id );
3561 _lwt_FindAdjacentEdges( topo, oldedge->end_node, &epan_pre,
3562 isclosed ? &span_pre : NULL, edge_id );
3563
3564 LWDEBUGF(1, "edges adjacent to old edge are %" LWTFMT_ELEMID
3565 " and %" LWTFMT_ELEMID " (first point), %" LWTFMT_ELEMID
3566 " and %" LWTFMT_ELEMID " (last point)",
3567 span_pre.nextCW, span_pre.nextCCW,
3568 epan_pre.nextCW, epan_pre.nextCCW);
3569
3570 /* If the same edge is both on CW and CCW direction on both start
3571 * and end points we need to verify winding of the left and right
3572 * rings to verify we didn't twist.
3573 * See https://trac.osgeo.org/postgis/ticket/5787
3574 *
3575 * NOTE: this could probably replace the "isclosed" test.
3576 *
3577 * NOTE: if either start or end node had different CW and CCW
3578 * edges a twist would be caught in the previous check.
3579 */
3580 if ( ! isclosed &&
3581 oldedge->face_left != oldedge->face_right &&
3582 span_pre.nextCW == span_pre.nextCCW &&
3583 epan_pre.nextCW == epan_pre.nextCCW )
3584 {{
3585 uint64_t num_signed_edge_ids;
3586 LWT_ELEMID *signed_edge_ids;
3587 LWPOLY *shell;
3588
3589 LWDEBUG(1, "Twist check before");
3590 signed_edge_ids = lwt_be_getRingEdges(topo, edge_id, &num_signed_edge_ids, 0);
3591 /* Get winding of left face ring */
3592 if (!signed_edge_ids)
3593 {
3594 //PGTOPO_BE_ERRORF("no ring edges for edge %" LWTFMT_ELEMID, sedge);
3596 return -1;
3597 }
3598 LWDEBUGF(1, "getRingEdges returned %llu edges", num_signed_edge_ids);
3599
3600 shell = _lwt_MakeRingShell(topo, signed_edge_ids, num_signed_edge_ids);
3601 if ( ! shell ) {
3602 lwfree( signed_edge_ids );
3603 /* ring_edges should be NULL */
3604 lwerror("Could not create ring shell: %s", lwt_be_lastErrorMessage(topo->be_iface));
3605 return -1;
3606 }
3607
3608 const POINTARRAY *pa = shell->rings[0];
3609 if ( ! ptarray_is_closed_2d(pa) )
3610 {
3611 lwpoly_free(shell);
3612 lwfree( signed_edge_ids );
3613 lwerror("Corrupted topology: ring of edge %" LWTFMT_ELEMID
3614 " is geometrically not-closed", edge_id);
3615 return -1;
3616 }
3617
3618 leftRingIsCCW = ptarray_isccw(pa);
3619 lwpoly_free(shell);
3620 lwfree( signed_edge_ids );
3621
3622 LWDEBUGF(1, "Ring of edge %" LWTFMT_ELEMID " is %sclockwise", edge_id, leftRingIsCCW ? "counter" : "");
3623 }}
3624
3625
3626 /* update edge geometry */
3627 newedge.edge_id = edge_id;
3628 newedge.geom = geom;
3629 res = lwt_be_updateEdgesById(topo, &newedge, 1, LWT_COL_EDGE_GEOM);
3630 if (res == -1)
3631 {
3632 _lwt_release_edges(oldedge, 1);
3634 return -1;
3635 }
3636 if (!res)
3637 {
3638 _lwt_release_edges(oldedge, 1);
3639 lwerror("Unexpected error: %" PRIu64 " edges updated when expecting 1", i);
3640 return -1;
3641 }
3642
3643 /*
3644 * Check edge adjacency after
3645 */
3646 edgeend span_post, epan_post;
3647 /* initialize epan_post.myaz and epan_post.myaz */
3648 res = _lwt_InitEdgeEndByLine(&span_post, &epan_post, geom, &p1, &p2);
3649 if (res)
3650 return -1; /* lwerror should have been raised */
3651 _lwt_FindAdjacentEdges( topo, oldedge->start_node, &span_post,
3652 isclosed ? &epan_post : NULL, edge_id );
3653 _lwt_FindAdjacentEdges( topo, oldedge->end_node, &epan_post,
3654 isclosed ? &span_post : NULL, edge_id );
3655
3656 LWDEBUGF(1, "edges adjacent to new edge are %" LWTFMT_ELEMID
3657 " and %" LWTFMT_ELEMID " (first point), %" LWTFMT_ELEMID
3658 " and %" LWTFMT_ELEMID " (last point)",
3659 span_pre.nextCW, span_pre.nextCCW,
3660 epan_pre.nextCW, epan_pre.nextCCW);
3661
3662
3663 /* Bail out if next CW or CCW edge on start node changed */
3664 if ( span_pre.nextCW != span_post.nextCW ||
3665 span_pre.nextCCW != span_post.nextCCW )
3666 {{
3667 LWT_ELEMID nid = oldedge->start_node;
3668 _lwt_release_edges(oldedge, 1);
3669 lwerror("Edge changed disposition around start node %"
3670 LWTFMT_ELEMID, nid);
3671 return -1;
3672 }}
3673
3674 /* Bail out if next CW or CCW edge on end node changed */
3675 if ( epan_pre.nextCW != epan_post.nextCW ||
3676 epan_pre.nextCCW != epan_post.nextCCW )
3677 {{
3678 LWT_ELEMID nid = oldedge->end_node;
3679 _lwt_release_edges(oldedge, 1);
3680 lwerror("Edge changed disposition around end node %"
3681 LWTFMT_ELEMID, nid);
3682 return -1;
3683 }}
3684
3685 /* Check winding of left face ring did not change */
3686 if ( leftRingIsCCW != -1 )
3687 {{
3688 uint64_t num_signed_edge_ids;
3689 LWT_ELEMID *signed_edge_ids;
3690 LWPOLY *shell;
3691 int isCCW;
3692
3693 LWDEBUG(1, "Twist check after");
3694 signed_edge_ids = lwt_be_getRingEdges(topo, edge_id, &num_signed_edge_ids, 0);
3695 /* Get winding of left face ring */
3696 if (!signed_edge_ids)
3697 {
3698 //PGTOPO_BE_ERRORF("no ring edges for edge %" LWTFMT_ELEMID, sedge);
3700 return -1;
3701 }
3702 LWDEBUGF(1, "getRingEdges returned %llu edges", num_signed_edge_ids);
3703
3704 shell = _lwt_MakeRingShell(topo, signed_edge_ids, num_signed_edge_ids);
3705 if ( ! shell ) {
3706 lwfree( signed_edge_ids );
3707 /* ring_edges should be NULL */
3708 lwerror("Could not create ring shell: %s", lwt_be_lastErrorMessage(topo->be_iface));
3709 return -1;
3710 }
3711
3712 const POINTARRAY *pa = shell->rings[0];
3713 if ( ! ptarray_is_closed_2d(pa) )
3714 {
3715 lwpoly_free(shell);
3716 lwfree( signed_edge_ids );
3717 lwerror("Corrupted topology: ring of edge %" LWTFMT_ELEMID
3718 " is geometrically not-closed", edge_id);
3719 return -1;
3720 }
3721
3722 isCCW = ptarray_isccw(pa);
3723 lwpoly_free(shell);
3724 lwfree( signed_edge_ids );
3725
3726 if ( isCCW != leftRingIsCCW )
3727 {
3728 _lwt_release_edges(oldedge, 1);
3729 lwerror("Edge ring changes winding");
3730 return -1;
3731 }
3732 }}
3733
3734 /*
3735 -- Update faces MBR of left and right faces
3736 -- TODO: think about ways to optimize this part, like see if
3737 -- the old edge geometry participated in the definition
3738 -- of the current MBR (for shrinking) or the new edge MBR
3739 -- would be larger than the old face MBR...
3740 --
3741 */
3742 LWGEOM *oldgeom = lwline_as_lwgeom(oldedge->geom);
3743 LWGEOM *newgeom = lwline_as_lwgeom(geom);
3744 lwgeom_refresh_bbox(oldgeom); /* Ensure we use a fit mbr, see #5709 -- TODO: fix this at lower level */
3745 lwgeom_refresh_bbox(newgeom); /* Ensure we use a fit mbr, see #5709 -- TODO: fix this at lower level */
3746 const GBOX* oldbox = lwgeom_get_bbox(oldgeom);
3747 const GBOX* newbox = lwgeom_get_bbox(newgeom);
3748 if ( ! gbox_same(oldbox, newbox) )
3749 {
3750 GBOX* updatedBox;
3751 uint64_t facestoupdate = 0;
3752 LWT_ISO_FACE faces[2];
3753 if ( oldedge->face_left > 0 )
3754 {
3755 updatedBox = lwt_be_computeFaceMBR(topo, oldedge->face_left);
3756 if ( ! updatedBox )
3757 {
3758 lwerror("Corrupted topology: face %" LWTFMT_ELEMID
3759 ", left of edge %" LWTFMT_ELEMID ", has no bbox",
3760 oldedge->face_left, edge_id);
3761 return -1;
3762 }
3763 faces[facestoupdate].face_id = oldedge->face_left;
3764 /* ownership transferred to faces[] */
3765 faces[facestoupdate++].mbr = updatedBox;
3766 }
3767 if ( oldedge->face_right > 0
3768 /* no need to update twice the same face.. */
3769 && oldedge->face_right != oldedge->face_left )
3770 {
3771 updatedBox = lwt_be_computeFaceMBR(topo, oldedge->face_right);
3772 if ( ! updatedBox )
3773 {
3774 lwerror("Corrupted topology: face %"
3775 LWTFMT_ELEMID ", right of edge %" LWTFMT_ELEMID ", has no bbox",
3776 oldedge->face_right, edge_id);
3777 return -1;
3778 }
3779 faces[facestoupdate].face_id = oldedge->face_right;
3780 /* ownership transferred to faces[] */
3781 faces[facestoupdate++].mbr = updatedBox;
3782 }
3783 LWDEBUGF(1, "%llu faces to update", facestoupdate);
3784 if ( facestoupdate )
3785 {
3786 uint64_t updatedFaces = lwt_be_updateFacesById(topo, &(faces[0]), facestoupdate);
3787 if (updatedFaces != facestoupdate)
3788 {
3789 while ( facestoupdate-- ) lwfree(faces[facestoupdate].mbr);
3790 _lwt_release_edges(oldedge, 1);
3791 if (updatedFaces == UINT64_MAX)
3793 else
3794 lwerror("Unexpected error: %" PRIu64 " faces updated when expecting 1", updatedFaces);
3795 return -1;
3796 }
3797 }
3798 while ( facestoupdate-- ) lwfree(faces[facestoupdate].mbr);
3799 }
3800 else
3801 {
3802 LWDEBUG(1, "BBOX of changed edge did not change");
3803 }
3804
3805 LWDEBUG(1, "all done, cleaning up edges");
3806
3807 _lwt_release_edges(oldedge, 1);
3808 return 0; /* success */
3809}
3810
3811/* Only return CONTAINING_FACE in the node object */
3812static LWT_ISO_NODE *
3814{
3815 LWT_ISO_NODE *node;
3816 uint64_t n = 1;
3817
3818 node = lwt_be_getNodeById( topo, &nid, &n, LWT_COL_NODE_CONTAINING_FACE );
3819 if (n == UINT64_MAX)
3820 {
3822 return 0;
3823 }
3824 if ( n < 1 ) {
3825 lwerror("SQL/MM Spatial exception - non-existent node");
3826 return 0;
3827 }
3828 if ( node->containing_face == -1 )
3829 {
3830 lwfree(node);
3831 lwerror("SQL/MM Spatial exception - not isolated node");
3832 return 0;
3833 }
3834
3835 return node;
3836}
3837
3838int
3840{
3841 LWT_ISO_NODE *node;
3842 int ret;
3843 int newPointFace;
3844
3845 node = _lwt_GetIsoNode( topo, nid );
3846 if ( ! node ) return -1;
3847
3848 if ( lwt_be_ExistsCoincidentNode(topo, pt) )
3849 {
3850 lwfree(node);
3851 lwerror("SQL/MM Spatial exception - coincident node");
3852 return -1;
3853 }
3854
3855 if ( lwt_be_ExistsEdgeIntersectingPoint(topo, pt) )
3856 {
3857 lwfree(node);
3858 lwerror("SQL/MM Spatial exception - edge crosses node.");
3859 return -1;
3860 }
3861
3862 /* Check that the new point is in the same containing face !
3863 * See https://trac.osgeo.org/postgis/ticket/3232 */
3864 newPointFace = lwt_GetFaceContainingPoint(topo, pt);
3865 if ( newPointFace == -1 ) {
3867 return -1;
3868 }
3869 if ( node->containing_face != newPointFace )
3870 {
3871 lwfree(node);
3872 lwerror("Cannot move isolated node across faces");
3873 return -1;
3874 }
3875
3876 node->node_id = nid;
3877 node->geom = pt;
3878 ret = lwt_be_updateNodesById(topo, node, 1,
3880 if ( ret == -1 ) {
3881 lwfree(node);
3883 return -1;
3884 }
3885
3886 lwfree(node);
3887 return 0;
3888}
3889
3890int
3892{
3893 LWT_ISO_NODE *node;
3894 int n = 1;
3895
3896 node = _lwt_GetIsoNode( topo, nid );
3897 if ( ! node ) return -1;
3898
3899 n = lwt_be_deleteNodesById( topo, &nid, n );
3900 if ( n == -1 )
3901 {
3902 lwfree(node);
3904 return -1;
3905 }
3906 if ( n != 1 )
3907 {
3908 lwfree(node);
3909 lwerror("Unexpected error: %d nodes deleted when expecting 1", n);
3910 return -1;
3911 }
3912
3913 if ( ! lwt_be_checkTopoGeomRemIsoNode(topo, nid) )
3914 {
3915 lwfree(node);
3917 return -1;
3918 }
3919
3920 lwfree(node);
3921 return 0; /* success */
3922}
3923
3924int
3926{
3927 LWT_ISO_EDGE deledge;
3928 LWT_ISO_EDGE *edge;
3929 LWT_ELEMID nid[2];
3930 LWT_ISO_NODE upd_node[2];
3931 LWT_ELEMID containing_face;
3932 uint64_t n = 1;
3933 uint64_t i;
3934
3935 edge = lwt_be_getEdgeById( topo, &id, &n, LWT_COL_EDGE_START_NODE|
3939 if ( ! edge )
3940 {
3942 return -1;
3943 }
3944 if ( ! n )
3945 {
3946 lwerror("SQL/MM Spatial exception - non-existent edge");
3947 return -1;
3948 }
3949 if ( n > 1 )
3950 {
3951 lwfree(edge);
3952 lwerror("Corrupted topology: more than a single edge have id %"
3953 LWTFMT_ELEMID, id);
3954 return -1;
3955 }
3956
3957 if ( edge[0].face_left != edge[0].face_right )
3958 {
3959 lwfree(edge);
3960 lwerror("SQL/MM Spatial exception - not isolated edge");
3961 return -1;
3962 }
3963 containing_face = edge[0].face_left;
3964
3965 nid[0] = edge[0].start_node;
3966 nid[1] = edge[0].end_node;
3967 lwfree(edge);
3968
3969 n = 2;
3970 edge = lwt_be_getEdgeByNode( topo, nid, &n, LWT_COL_EDGE_EDGE_ID );
3971 if ((n == UINT64_MAX) || (edge == NULL))
3972 {
3974 return -1;
3975 }
3976 for (i = 0; i < n; ++i)
3977 {
3978 if (edge[i].edge_id != id)
3979 {
3980 lwfree(edge);
3981 lwerror("SQL/MM Spatial exception - not isolated edge");
3982 return -1;
3983 }
3984 }
3985 lwfree(edge);
3986
3987 deledge.edge_id = id;
3988 n = lwt_be_deleteEdges( topo, &deledge, LWT_COL_EDGE_EDGE_ID );
3989 if (n == UINT64_MAX)
3990 {
3992 return -1;
3993 }
3994 if ( n != 1 )
3995 {
3996 lwerror("Unexpected error: %" PRIu64 " edges deleted when expecting 1", n);
3997 return -1;
3998 }
3999
4000 upd_node[0].node_id = nid[0];
4001 upd_node[0].containing_face = containing_face;
4002 n = 1;
4003 if ( nid[1] != nid[0] ) {
4004 upd_node[1].node_id = nid[1];
4005 upd_node[1].containing_face = containing_face;
4006 ++n;
4007 }
4008 n = lwt_be_updateNodesById(topo, upd_node, n,
4010 if (n == UINT64_MAX)
4011 {
4013 return -1;
4014 }
4015
4016 /* Check that the edge can be safely removed
4017 * See https://trac.osgeo.org/postgis/ticket/3248
4018 */
4019 if ( ! lwt_be_checkTopoGeomRemIsoEdge(topo, id) )
4020 {
4022 return -1;
4023 }
4024
4025 return 0; /* success */
4026}
4027
4028/* Used by _lwt_RemEdge to update edge face ref on healing
4029 *
4030 * @param of old face id (never 0 as you cannot remove face 0)
4031 * @param nf new face id
4032 * @return 0 on success, -1 on backend error
4033 */
4034static int
4036{
4037 LWT_ISO_EDGE sel_edge, upd_edge;
4038 int ret;
4039
4040 assert( of != 0 );
4041
4042 /* Update face_left for all edges still referencing old face */
4043 sel_edge.face_left = of;
4044 upd_edge.face_left = nf;
4045 ret = lwt_be_updateEdges(topo, &sel_edge, LWT_COL_EDGE_FACE_LEFT,
4046 &upd_edge, LWT_COL_EDGE_FACE_LEFT,
4047 NULL, 0);
4048 if ( ret == -1 ) return -1;
4049
4050 /* Update face_right for all edges still referencing old face */
4051 sel_edge.face_right = of;
4052 upd_edge.face_right = nf;
4053 ret = lwt_be_updateEdges(topo, &sel_edge, LWT_COL_EDGE_FACE_RIGHT,
4054 &upd_edge, LWT_COL_EDGE_FACE_RIGHT,
4055 NULL, 0);
4056 if ( ret == -1 ) return -1;
4057
4058 return 0;
4059}
4060
4061/* Used by _lwt_RemEdge to update node face ref on healing
4062 *
4063 * @param of old face id (never 0 as you cannot remove face 0)
4064 * @param nf new face id
4065 * @return 0 on success, -1 on backend error
4066 */
4067static int
4069{
4070 LWT_ISO_NODE sel, upd;
4071 int ret;
4072
4073 assert( of != 0 );
4074
4075 /* Update face_left for all edges still referencing old face */
4076 sel.containing_face = of;
4077 upd.containing_face = nf;
4080 NULL, 0);
4081 if ( ret == -1 ) return -1;
4082
4083 return 0;
4084}
4085
4086/* Used by lwt_RemEdgeModFace and lwt_RemEdgeNewFaces
4087 *
4088 * Returns -1 on error, identifier of the face that takes up the space
4089 * previously occupied by the removed edge if modFace is 1, identifier of
4090 * the created face (0 if none) if modFace is 0.
4091 */
4092static LWT_ELEMID
4093_lwt_RemEdge( LWT_TOPOLOGY* topo, LWT_ELEMID edge_id, int modFace )
4094{
4095 uint64_t i, nedges, nfaces, fields;
4096 LWT_ISO_EDGE *edge = NULL;
4097 LWT_ISO_EDGE *upd_edge = NULL;
4098 LWT_ISO_EDGE upd_edge_left[2];
4099 int nedge_left = 0;
4100 LWT_ISO_EDGE upd_edge_right[2];
4101 int nedge_right = 0;
4102 LWT_ISO_NODE upd_node[2];
4103 int nnode = 0;
4104 LWT_ISO_FACE *faces = NULL;
4105 LWT_ISO_FACE newface;
4106 LWT_ELEMID node_ids[2];
4107 LWT_ELEMID face_ids[2];
4108 int fnode_edges = 0; /* number of edges on the first node (excluded
4109 * the one being removed ) */
4110 int lnode_edges = 0; /* number of edges on the last node (excluded
4111 * the one being removed ) */
4112
4113
4114 newface.face_id = 0;
4115
4116 i = 1;
4117 edge = lwt_be_getEdgeById(topo, &edge_id, &i, LWT_COL_EDGE_ALL);
4118 if (!edge)
4119 {
4120 LWDEBUGF(1, "lwt_be_getEdgeById returned NULL and set i=%llu", i);
4121 if (i == UINT64_MAX)
4122 {
4124 return -1;
4125 }
4126 else if (i == 0)
4127 {
4128 lwerror("SQL/MM Spatial exception - non-existent edge %" LWTFMT_ELEMID, edge_id);
4129 return -1;
4130 }
4131 else
4132 {
4133 lwerror(
4134 "Backend coding error: getEdgeById callback returned NULL "
4135 "but numelements output parameter has value %" PRIu64 " "
4136 "(expected 0 or 1)",
4137 i);
4138 return -1;
4139 }
4140 }
4141
4142 if ( ! lwt_be_checkTopoGeomRemEdge(topo, edge_id,
4143 edge->face_left, edge->face_right) )
4144 {
4146 return -1;
4147 }
4148
4149 LWDEBUG(1, "Updating next_{right,left}_face of ring edges...");
4150
4151 /* Update edge linking */
4152
4153 nedges = 0;
4154 node_ids[nedges++] = edge->start_node;
4155 if ( edge->end_node != edge->start_node )
4156 {
4157 node_ids[nedges++] = edge->end_node;
4158 }
4162 upd_edge = lwt_be_getEdgeByNode( topo, &(node_ids[0]), &nedges, fields );
4163 if (nedges == UINT64_MAX)
4164 {
4166 return -1;
4167 }
4168 nedge_left = nedge_right = 0;
4169 for ( i=0; i<nedges; ++i )
4170 {
4171 LWT_ISO_EDGE *e = &(upd_edge[i]);
4172 if ( e->edge_id == edge_id ) continue;
4173 if ( e->start_node == edge->start_node || e->end_node == edge->start_node )
4174 {
4175 ++fnode_edges;
4176 }
4177 if ( e->start_node == edge->end_node || e->end_node == edge->end_node )
4178 {
4179 ++lnode_edges;
4180 }
4181 if ( e->next_left == -edge_id )
4182 {
4183 upd_edge_left[nedge_left].edge_id = e->edge_id;
4184 upd_edge_left[nedge_left++].next_left =
4185 edge->next_left != edge_id ? edge->next_left : edge->next_right;
4186 }
4187 else if ( e->next_left == edge_id )
4188 {
4189 upd_edge_left[nedge_left].edge_id = e->edge_id;
4190 upd_edge_left[nedge_left++].next_left =
4191 edge->next_right != -edge_id ? edge->next_right : edge->next_left;
4192 }
4193
4194 if ( e->next_right == -edge_id )
4195 {
4196 upd_edge_right[nedge_right].edge_id = e->edge_id;
4197 upd_edge_right[nedge_right++].next_right =
4198 edge->next_left != edge_id ? edge->next_left : edge->next_right;
4199 }
4200 else if ( e->next_right == edge_id )
4201 {
4202 upd_edge_right[nedge_right].edge_id = e->edge_id;
4203 upd_edge_right[nedge_right++].next_right =
4204 edge->next_right != -edge_id ? edge->next_right : edge->next_left;
4205 }
4206 }
4207
4208 if ( nedge_left )
4209 {
4210 LWDEBUGF(1, "updating %d 'next_left' edges", nedge_left);
4211 /* update edges in upd_edge_left set next_left */
4212 int result = lwt_be_updateEdgesById(topo, &(upd_edge_left[0]), nedge_left, LWT_COL_EDGE_NEXT_LEFT);
4213 if (result == -1)
4214 {
4215 _lwt_release_edges(edge, 1);
4216 lwfree(upd_edge);
4218 return -1;
4219 }
4220 }
4221 if ( nedge_right )
4222 {
4223 LWDEBUGF(1, "updating %d 'next_right' edges", nedge_right);
4224 /* update edges in upd_edge_right set next_right */
4225 int result = lwt_be_updateEdgesById(topo, &(upd_edge_right[0]), nedge_right, LWT_COL_EDGE_NEXT_RIGHT);
4226 if (result == -1)
4227 {
4228 _lwt_release_edges(edge, 1);
4229 lwfree(upd_edge);
4231 return -1;
4232 }
4233 }
4234 LWDEBUGF(1, "releasing %llu updateable edges in %p", nedges, upd_edge);
4235 lwfree(upd_edge);
4236
4237 /* Id of face that will take up all the space previously
4238 * taken by left and right faces of the edge */
4239 LWT_ELEMID floodface;
4240
4241 /* Find floodface, and update its mbr if != 0 */
4242 if ( edge->face_left == edge->face_right )
4243 {
4244 floodface = edge->face_right;
4245 }
4246 else
4247 {
4248 /* Two faces healed */
4249 if ( edge->face_left == 0 || edge->face_right == 0 )
4250 {
4251 floodface = 0;
4252 LWDEBUG(1, "floodface is universe");
4253 }
4254 else
4255 {
4256 /* we choose right face as the face that will remain
4257 * to be symmetric with ST_AddEdgeModFace */
4258 floodface = edge->face_right;
4259 LWDEBUGF(1, "floodface is %" LWTFMT_ELEMID, floodface);
4260 /* update mbr of floodface as union of mbr of both faces */
4261 face_ids[0] = edge->face_left;
4262 face_ids[1] = edge->face_right;
4263 nfaces = 2;
4264 fields = LWT_COL_FACE_ALL;
4265 faces = lwt_be_getFaceById(topo, face_ids, &nfaces, fields);
4266 if (nfaces == UINT64_MAX)
4267 {
4269 return -1;
4270 }
4271 GBOX *box1=NULL;
4272 GBOX *box2=NULL;
4273 for ( i=0; i<nfaces; ++i )
4274 {
4275 if ( faces[i].face_id == edge->face_left )
4276 {
4277 if ( ! box1 ) box1 = faces[i].mbr;
4278 else
4279 {
4280 i = edge->face_left;
4281 _lwt_release_edges(edge, 1);
4282 _lwt_release_faces(faces, nfaces);
4283 lwerror("corrupted topology: more than 1 face have face_id=%"
4284 LWTFMT_ELEMID, i);
4285 return -1;
4286 }
4287 }
4288 else if ( faces[i].face_id == edge->face_right )
4289 {
4290 if ( ! box2 ) box2 = faces[i].mbr;
4291 else
4292 {
4293 i = edge->face_right;
4294 _lwt_release_edges(edge, 1);
4295 _lwt_release_faces(faces, nfaces);
4296 lwerror("corrupted topology: more than 1 face have face_id=%"
4297 LWTFMT_ELEMID, i);
4298 return -1;
4299 }
4300 }
4301 else
4302 {
4303 i = faces[i].face_id;
4304 _lwt_release_edges(edge, 1);
4305 _lwt_release_faces(faces, nfaces);
4306 lwerror("Backend coding error: getFaceById returned face "
4307 "with non-requested id %" LWTFMT_ELEMID, i);
4308 return -1;
4309 }
4310 }
4311 if ( ! box1 ) {
4312 i = edge->face_left;
4313 _lwt_release_edges(edge, 1);
4314 if ( nfaces ) _lwt_release_faces(faces, nfaces);
4315 lwerror("corrupted topology: no face have face_id=%"
4316 LWTFMT_ELEMID " (left face for edge %"
4317 LWTFMT_ELEMID ")", i, edge_id);
4318 return -1;
4319 }
4320 if ( ! box2 ) {
4321 i = edge->face_right;
4322 _lwt_release_edges(edge, 1);
4323 if ( nfaces ) _lwt_release_faces(faces, nfaces);
4324 lwerror("corrupted topology: no face have face_id=%"
4325 LWTFMT_ELEMID " (right face for edge %"
4326 LWTFMT_ELEMID ")", i, edge_id);
4327 return -1;
4328 }
4329 gbox_merge(box2, box1); /* box1 is now the union of the two */
4330 newface.mbr = box1;
4331 if ( modFace )
4332 {
4333 newface.face_id = floodface;
4334 int result = lwt_be_updateFacesById(topo, &newface, 1);
4335 _lwt_release_faces(faces, 2);
4336 if (result == -1)
4337 {
4338 _lwt_release_edges(edge, 1);
4340 return -1;
4341 }
4342 if (result != 1)
4343 {
4344 _lwt_release_edges(edge, 1);
4345 lwerror("Unexpected error: %" PRIu64 " faces updated when expecting 1", i);
4346 return -1;
4347 }
4348 }
4349 else
4350 {
4351 /* New face replaces the old two faces */
4352 newface.face_id = -1;
4353 int result = lwt_be_insertFaces(topo, &newface, 1);
4354 _lwt_release_faces(faces, 2);
4355 if (result == -1)
4356 {
4357 _lwt_release_edges(edge, 1);
4359 return -1;
4360 }
4361 if (result != 1)
4362 {
4363 _lwt_release_edges(edge, 1);
4364 lwerror("Unexpected error: %d faces inserted when expecting 1", result);
4365 return -1;
4366 }
4367 floodface = newface.face_id;
4368 }
4369 }
4370
4371 /* Update face references for edges and nodes still referencing
4372 * the removed face(s) */
4373
4374 if ( edge->face_left != floodface )
4375 {
4376 if ( -1 == _lwt_UpdateEdgeFaceRef(topo, edge->face_left, floodface) )
4377 {
4378 _lwt_release_edges(edge, 1);
4380 return -1;
4381 }
4382 if ( -1 == _lwt_UpdateNodeFaceRef(topo, edge->face_left, floodface) )
4383 {
4384 _lwt_release_edges(edge, 1);
4386 return -1;
4387 }
4388 }
4389
4390 if ( edge->face_right != floodface )
4391 {
4392 if ( -1 == _lwt_UpdateEdgeFaceRef(topo, edge->face_right, floodface) )
4393 {
4394 _lwt_release_edges(edge, 1);
4396 return -1;
4397 }
4398 if ( -1 == _lwt_UpdateNodeFaceRef(topo, edge->face_right, floodface) )
4399 {
4400 _lwt_release_edges(edge, 1);
4402 return -1;
4403 }
4404 }
4405
4406 /* Update topogeoms on heal */
4408 edge->face_right, edge->face_left,
4409 floodface) )
4410 {
4411 _lwt_release_edges(edge, 1);
4413 return -1;
4414 }
4415 } /* two faces healed */
4416
4417 /* Delete the edge */
4419 if (result == -1)
4420 {
4421 _lwt_release_edges(edge, 1);
4423 return -1;
4424 }
4425
4426 /* If any of the edge nodes remained isolated, set
4427 * containing_face = floodface
4428 */
4429 if ( ! fnode_edges )
4430 {
4431 upd_node[nnode].node_id = edge->start_node;
4432 upd_node[nnode].containing_face = floodface;
4433 ++nnode;
4434 }
4435 if ( edge->end_node != edge->start_node && ! lnode_edges )
4436 {
4437 upd_node[nnode].node_id = edge->end_node;
4438 upd_node[nnode].containing_face = floodface;
4439 ++nnode;
4440 }
4441 if ( nnode )
4442 {
4443 int result = lwt_be_updateNodesById(topo, upd_node, nnode, LWT_COL_NODE_CONTAINING_FACE);
4444 if (result == -1)
4445 {
4446 _lwt_release_edges(edge, 1);
4448 return -1;
4449 }
4450 }
4451
4452 if ( edge->face_left != edge->face_right )
4453 /* or there'd be no face to remove */
4454 {
4455 LWT_ELEMID ids[2];
4456 int nids = 0;
4457 if ( edge->face_right != floodface )
4458 ids[nids++] = edge->face_right;
4459 if ( edge->face_left != floodface )
4460 ids[nids++] = edge->face_left;
4461 int result = lwt_be_deleteFacesById(topo, ids, nids);
4462 if (result == -1)
4463 {
4464 _lwt_release_edges(edge, 1);
4466 return -1;
4467 }
4468 }
4469
4470 _lwt_release_edges(edge, 1);
4471 return modFace ? floodface : newface.face_id;
4472}
4473
4476{
4477 return _lwt_RemEdge( topo, edge_id, 1 );
4478}
4479
4482{
4483 return _lwt_RemEdge( topo, edge_id, 0 );
4484}
4485
4486static LWT_ELEMID
4488 int modEdge )
4489{
4490 LWT_ELEMID ids[2];
4491 LWT_ELEMID commonnode = -1;
4492 int caseno = 0;
4493 LWT_ISO_EDGE *node_edges;
4494 uint64_t num_node_edges;
4495 LWT_ISO_EDGE *edges;
4496 LWT_ISO_EDGE *e1 = NULL;
4497 LWT_ISO_EDGE *e2 = NULL;
4498 LWT_ISO_EDGE newedge, updedge, seledge;
4499 uint64_t nedges, i;
4500 int e1freenode;
4501 int e2sign, e2freenode;
4502 POINTARRAY *pa;
4503 char buf[256];
4504 char *ptr;
4505 size_t bufleft = 256;
4506
4507 ptr = buf;
4508
4509 /* NOT IN THE SPECS: see if the same edge is given twice.. */
4510 if ( eid1 == eid2 )
4511 {
4512 lwerror("Cannot heal edge %" LWTFMT_ELEMID
4513 " with itself, try with another", eid1);
4514 return -1;
4515 }
4516 ids[0] = eid1;
4517 ids[1] = eid2;
4518 nedges = 2;
4519 edges = lwt_be_getEdgeById(topo, ids, &nedges, LWT_COL_EDGE_ALL);
4520 if ((nedges == UINT64_MAX) || (edges == NULL))
4521 {
4523 return -1;
4524 }
4525 for ( i=0; i<nedges; ++i )
4526 {
4527 if ( edges[i].edge_id == eid1 ) {
4528 if ( e1 ) {
4529 _lwt_release_edges(edges, nedges);
4530 lwerror("Corrupted topology: multiple edges have id %"
4531 LWTFMT_ELEMID, eid1);
4532 return -1;
4533 }
4534 e1 = &(edges[i]);
4535 }
4536 else if ( edges[i].edge_id == eid2 ) {
4537 if ( e2 ) {
4538 _lwt_release_edges(edges, nedges);
4539 lwerror("Corrupted topology: multiple edges have id %"
4540 LWTFMT_ELEMID, eid2);
4541 return -1;
4542 }
4543 e2 = &(edges[i]);
4544 }
4545 }
4546 if ( ! e1 )
4547 {
4548 _lwt_release_edges(edges, nedges);
4549 lwerror(
4550 "SQL/MM Spatial exception - non-existent edge %" LWTFMT_ELEMID,
4551 eid1);
4552 return -1;
4553 }
4554 if ( ! e2 )
4555 {
4556 _lwt_release_edges(edges, nedges);
4557 lwerror(
4558 "SQL/MM Spatial exception - non-existent edge %" LWTFMT_ELEMID,
4559 eid2);
4560 return -1;
4561 }
4562
4563 /* NOT IN THE SPECS: See if any of the two edges are closed. */
4564 if ( e1->start_node == e1->end_node )
4565 {
4566 _lwt_release_edges(edges, nedges);
4567 lwerror("Edge %" LWTFMT_ELEMID " is closed, cannot heal to edge %"
4568 LWTFMT_ELEMID, eid1, eid2);
4569 return -1;
4570 }
4571 if ( e2->start_node == e2->end_node )
4572 {
4573 _lwt_release_edges(edges, nedges);
4574 lwerror("Edge %" LWTFMT_ELEMID " is closed, cannot heal to edge %"
4575 LWTFMT_ELEMID, eid2, eid1);
4576 return -1;
4577 }
4578
4579 /* Find common node */
4580
4581 if ( e1->end_node == e2->start_node )
4582 {
4583 commonnode = e1->end_node;
4584 caseno = 1;
4585 }
4586 else if ( e1->end_node == e2->end_node )
4587 {
4588 commonnode = e1->end_node;
4589 caseno = 2;
4590 }
4591 /* Check if any other edge is connected to the common node, if found */
4592 if ( commonnode != -1 )
4593 {
4594 num_node_edges = 1;
4595 node_edges = lwt_be_getEdgeByNode( topo, &commonnode,
4596 &num_node_edges, LWT_COL_EDGE_EDGE_ID );
4597 if (num_node_edges == UINT64_MAX)
4598 {
4599 _lwt_release_edges(edges, nedges);
4601 return -1;
4602 }
4603 for (i=0; i<num_node_edges; ++i)
4604 {
4605 int r;
4606 if ( node_edges[i].edge_id == eid1 ) continue;
4607 if ( node_edges[i].edge_id == eid2 ) continue;
4608 commonnode = -1;
4609 /* append to string, for error message */
4610 if ( bufleft > 0 ) {
4611 r = snprintf(ptr, bufleft, "%s%" LWTFMT_ELEMID,
4612 ( ptr==buf ? "" : "," ), node_edges[i].edge_id);
4613 if ( r >= (int) bufleft )
4614 {
4615 bufleft = 0;
4616 buf[252] = '.';
4617 buf[253] = '.';
4618 buf[254] = '.';
4619 buf[255] = '\0';
4620 }
4621 else
4622 {
4623 bufleft -= r;
4624 ptr += r;
4625 }
4626 }
4627 }
4628 lwfree(node_edges);
4629 }
4630
4631 if ( commonnode == -1 )
4632 {
4633 if ( e1->start_node == e2->start_node )
4634 {
4635 commonnode = e1->start_node;
4636 caseno = 3;
4637 }
4638 else if ( e1->start_node == e2->end_node )
4639 {
4640 commonnode = e1->start_node;
4641 caseno = 4;
4642 }
4643 /* Check if any other edge is connected to the common node, if found */
4644 if ( commonnode != -1 )
4645 {
4646 num_node_edges = 1;
4647 node_edges = lwt_be_getEdgeByNode( topo, &commonnode,
4648 &num_node_edges, LWT_COL_EDGE_EDGE_ID );
4649 if (num_node_edges == UINT64_MAX)
4650 {
4651 _lwt_release_edges(edges, nedges);
4653 return -1;
4654 }
4655 for (i=0; i<num_node_edges; ++i)
4656 {
4657 int r;
4658 if ( node_edges[i].edge_id == eid1 ) continue;
4659 if ( node_edges[i].edge_id == eid2 ) continue;
4660 commonnode = -1;
4661 /* append to string, for error message */
4662 if ( bufleft > 0 ) {
4663 r = snprintf(ptr, bufleft, "%s%" LWTFMT_ELEMID,
4664 ( ptr==buf ? "" : "," ), node_edges[i].edge_id);
4665 if ( r >= (int) bufleft )
4666 {
4667 bufleft = 0;
4668 buf[252] = '.';
4669 buf[253] = '.';
4670 buf[254] = '.';
4671 buf[255] = '\0';
4672 }
4673 else
4674 {
4675 bufleft -= r;
4676 ptr += r;
4677 }
4678 }
4679 }
4680 if ( num_node_edges ) lwfree(node_edges);
4681 }
4682 }
4683
4684 if ( commonnode == -1 )
4685 {
4686 _lwt_release_edges(edges, nedges);
4687 if ( ptr != buf )
4688 {
4689 lwerror("SQL/MM Spatial exception - other edges connected (%s)",
4690 buf);
4691 }
4692 else
4693 {
4694 lwerror("SQL/MM Spatial exception - non-connected edges");
4695 }
4696 return -1;
4697 }
4698
4699 if ( ! lwt_be_checkTopoGeomRemNode(topo, commonnode,
4700 eid1, eid2 ) )
4701 {
4702 _lwt_release_edges(edges, nedges);
4704 return -1;
4705 }
4706
4707 /* Construct the geometry of the new edge */
4708 switch (caseno)
4709 {
4710 case 1: /* e1.end = e2.start */
4711 pa = ptarray_clone_deep(e1->geom->points);
4712 //pa = ptarray_merge(pa, e2->geom->points);
4713 ptarray_append_ptarray(pa, e2->geom->points, 0);
4714 newedge.start_node = e1->start_node;
4715 newedge.end_node = e2->end_node;
4716 newedge.next_left = e2->next_left;
4717 newedge.next_right = e1->next_right;
4718 e1freenode = 1;
4719 e2freenode = -1;
4720 e2sign = 1;
4721 break;
4722 case 2: /* e1.end = e2.end */
4723 {
4724 POINTARRAY *pa2;
4725 pa2 = ptarray_clone_deep(e2->geom->points);
4727 pa = ptarray_clone_deep(e1->geom->points);
4728 //pa = ptarray_merge(e1->geom->points, pa);
4729 ptarray_append_ptarray(pa, pa2, 0);
4730 ptarray_free(pa2);
4731 newedge.start_node = e1->start_node;
4732 newedge.end_node = e2->start_node;
4733 newedge.next_left = e2->next_right;
4734 newedge.next_right = e1->next_right;
4735 e1freenode = 1;
4736 e2freenode = 1;
4737 e2sign = -1;
4738 break;
4739 }
4740 case 3: /* e1.start = e2.start */
4741 pa = ptarray_clone_deep(e2->geom->points);
4743 //pa = ptarray_merge(pa, e1->geom->points);
4744 ptarray_append_ptarray(pa, e1->geom->points, 0);
4745 newedge.end_node = e1->end_node;
4746 newedge.start_node = e2->end_node;
4747 newedge.next_left = e1->next_left;
4748 newedge.next_right = e2->next_left;
4749 e1freenode = -1;
4750 e2freenode = -1;
4751 e2sign = -1;
4752 break;
4753 case 4: /* e1.start = e2.end */
4754 pa = ptarray_clone_deep(e2->geom->points);
4755 //pa = ptarray_merge(pa, e1->geom->points);
4756 ptarray_append_ptarray(pa, e1->geom->points, 0);
4757 newedge.end_node = e1->end_node;
4758 newedge.start_node = e2->start_node;
4759 newedge.next_left = e1->next_left;
4760 newedge.next_right = e2->next_right;
4761 e1freenode = -1;
4762 e2freenode = 1;
4763 e2sign = 1;
4764 break;
4765 default:
4766 pa = NULL;
4767 e1freenode = 0;
4768 e2freenode = 0;
4769 e2sign = 0;
4770 _lwt_release_edges(edges, nedges);
4771 lwerror("Coding error: caseno=%d should never happen", caseno);
4772 return -1;
4773 break;
4774 }
4775 newedge.geom = lwline_construct(topo->srid, NULL, pa);
4776
4777 if ( modEdge )
4778 {
4779 /* Update data of the first edge */
4780 newedge.edge_id = eid1;
4781 int result = lwt_be_updateEdgesById(topo,
4782 &newedge,
4783 1,
4786 if (result == -1)
4787 {
4788 lwline_free(newedge.geom);
4789 _lwt_release_edges(edges, nedges);
4791 return -1;
4792 }
4793 else if (result != 1)
4794 {
4795 lwline_free(newedge.geom);
4796 if ( edges ) _lwt_release_edges(edges, nedges);
4797 lwerror("Unexpected error: %" PRIu64 " edges updated when expecting 1", i);
4798 return -1;
4799 }
4800 }
4801 else
4802 {
4803 /* Add new edge */
4804 newedge.edge_id = -1;
4805 newedge.face_left = e1->face_left;
4806 newedge.face_right = e1->face_right;
4807 int result = lwt_be_insertEdges(topo, &newedge, 1);
4808 if (result == -1)
4809 {
4810 lwline_free(newedge.geom);
4811 _lwt_release_edges(edges, nedges);
4813 return -1;
4814 }
4815 else if (result == 0)
4816 {
4817 lwline_free(newedge.geom);
4818 _lwt_release_edges(edges, nedges);
4819 lwerror("Insertion of split edge failed (no reason)");
4820 return -1;
4821 }
4822 }
4823 lwline_free(newedge.geom);
4824
4825 /*
4826 -- Update next_left_edge/next_right_edge for
4827 -- any edge having them still pointing at the edge being removed
4828 -- (eid2 only when modEdge, or both otherwise)
4829 --
4830 -- NOTE:
4831 -- e#freenode is 1 when edge# end node was the common node
4832 -- and -1 otherwise. This gives the sign of possibly found references
4833 -- to its "free" (non connected to other edge) endnode.
4834 -- e2sign is -1 if edge1 direction is opposite to edge2 direction,
4835 -- or 1 otherwise.
4836 --
4837 */
4838
4839 /* update edges connected to e2's boundary from their end node */
4840 seledge.next_left = e2freenode * eid2;
4841 updedge.next_left = e2freenode * newedge.edge_id * e2sign;
4842 int result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_LEFT, &updedge, LWT_COL_EDGE_NEXT_LEFT, NULL, 0);
4843 if (result == -1)
4844 {
4845 _lwt_release_edges(edges, nedges);
4847 return -1;
4848 }
4849
4850 /* update edges connected to e2's boundary from their start node */
4851 seledge.next_right = e2freenode * eid2;
4852 updedge.next_right = e2freenode * newedge.edge_id * e2sign;
4853 result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_RIGHT, &updedge, LWT_COL_EDGE_NEXT_RIGHT, NULL, 0);
4854 if (result == -1)
4855 {
4856 _lwt_release_edges(edges, nedges);
4858 return -1;
4859 }
4860
4861 if ( ! modEdge )
4862 {
4863 /* update edges connected to e1's boundary from their end node */
4864 seledge.next_left = e1freenode * eid1;
4865 updedge.next_left = e1freenode * newedge.edge_id;
4866 result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_LEFT, &updedge, LWT_COL_EDGE_NEXT_LEFT, NULL, 0);
4867 if (result == -1)
4868 {
4869 _lwt_release_edges(edges, nedges);
4871 return -1;
4872 }
4873
4874 /* update edges connected to e1's boundary from their start node */
4875 seledge.next_right = e1freenode * eid1;
4876 updedge.next_right = e1freenode * newedge.edge_id;
4877 result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_RIGHT, &updedge, LWT_COL_EDGE_NEXT_RIGHT, NULL, 0);
4878 if (result == -1)
4879 {
4880 _lwt_release_edges(edges, nedges);
4882 return -1;
4883 }
4884 }
4885
4886 /* delete the edges (only second on modEdge or both) */
4888 if (result == -1)
4889 {
4890 _lwt_release_edges(edges, nedges);
4892 return -1;
4893 }
4894 if ( ! modEdge ) {
4896 if (result == -1)
4897 {
4898 _lwt_release_edges(edges, nedges);
4900 return -1;
4901 }
4902 }
4903
4904 _lwt_release_edges(edges, nedges);
4905
4906 /* delete the common node */
4907 result = lwt_be_deleteNodesById( topo, &commonnode, 1 );
4908 if (result == -1)
4909 {
4911 return -1;
4912 }
4913
4914 /*
4915 --
4916 -- NOT IN THE SPECS:
4917 -- Drop composition rows involving second
4918 -- edge, as the first edge took its space,
4919 -- and all affected TopoGeom have been previously checked
4920 -- for being composed by both edges.
4921 */
4923 eid1, eid2, newedge.edge_id) )
4924 {
4926 return -1;
4927 }
4928
4929 return modEdge ? commonnode : newedge.edge_id;
4930}
4931
4934{
4935 return _lwt_HealEdges( topo, e1, e2, 1 );
4936}
4937
4940{
4941 return _lwt_HealEdges( topo, e1, e2, 0 );
4942}
4943
4946{
4947 LWT_ISO_NODE *elem;
4948 uint64_t num;
4949 int flds = LWT_COL_NODE_NODE_ID|LWT_COL_NODE_GEOM; /* geom not needed */
4950 LWT_ELEMID id = 0;
4951 POINT2D qp; /* query point */
4952
4953 if ( ! getPoint2d_p(pt->point, 0, &qp) )
4954 {
4955 lwerror("Empty query point");
4956 return -1;
4957 }
4958 elem = lwt_be_getNodeWithinDistance2D(topo, pt, tol, &num, flds, 0);
4959 if (num == UINT64_MAX)
4960 {
4962 return -1;
4963 }
4964 else if ( num )
4965 {
4966 if ( num > 1 )
4967 {
4968 _lwt_release_nodes(elem, num);
4969 lwerror("Two or more nodes found");
4970 return -1;
4971 }
4972 id = elem[0].node_id;
4973 _lwt_release_nodes(elem, num);
4974 }
4975
4976 return id;
4977}
4978
4981{
4982 LWT_ISO_EDGE *elem;
4983 uint64_t num, i;
4984 int flds = LWT_COL_EDGE_EDGE_ID|LWT_COL_EDGE_GEOM; /* GEOM is not needed */
4985 LWT_ELEMID id = 0;
4986 LWGEOM *qp = lwpoint_as_lwgeom(pt); /* query point */
4987
4988 if ( lwgeom_is_empty(qp) )
4989 {
4990 lwerror("Empty query point");
4991 return -1;
4992 }
4993 elem = lwt_be_getEdgeWithinDistance2D(topo, pt, tol, &num, flds, 0);
4994 if (num == UINT64_MAX)
4995 {
4997 return -1;
4998 }
4999 for (i=0; i<num;++i)
5000 {
5001 LWT_ISO_EDGE *e = &(elem[i]);
5002#if 0
5003 LWGEOM* geom;
5004 double dist;
5005
5006 if ( ! e->geom )
5007 {
5008 _lwt_release_edges(elem, num);
5009 lwnotice("Corrupted topology: edge %" LWTFMT_ELEMID
5010 " has null geometry", e->edge_id);
5011 continue;
5012 }
5013
5014 /* Should we check for intersection not being on an endpoint
5015 * as documented ? */
5016 geom = lwline_as_lwgeom(e->geom);
5017 dist = lwgeom_mindistance2d_tolerance(geom, qp, tol);
5018 if ( dist > tol ) continue;
5019#endif
5020
5021 if ( id )
5022 {
5023 _lwt_release_edges(elem, num);
5024 lwerror("Two or more edges found");
5025 return -1;
5026 }
5027 else id = e->edge_id;
5028 }
5029
5030 if ( num ) _lwt_release_edges(elem, num);
5031
5032 return id;
5033}
5034
5036lwt_GetFaceByPoint(LWT_TOPOLOGY *topo, const LWPOINT *pt, double tol)
5037{
5038 LWT_ELEMID id = 0;
5039 LWT_ISO_EDGE *elem;
5040 uint64_t num, i;
5041 int flds = LWT_COL_EDGE_EDGE_ID |
5045 LWGEOM *qp = lwpoint_as_lwgeom(pt);
5046
5047 id = lwt_GetFaceContainingPoint(topo, pt);
5048 if ( id == -1 ) {
5050 return -1;
5051 }
5052
5053 if ( id > 0 )
5054 {
5055 return id;
5056 }
5057
5058 if ( tol == 0 )
5059 {
5060 return id;
5061 }
5062
5063 LWDEBUG(1, "No face properly contains query point,"
5064 " looking for edges");
5065
5066 /* Not in a face, may be in universe or on edge, let's check
5067 * for distance */
5068 /* NOTE: we never pass a tolerance of 0 to avoid ever using
5069 * ST_Within, which doesn't include endpoints matches */
5070 elem = lwt_be_getEdgeWithinDistance2D(topo, pt, tol?tol:1e-5, &num, flds, 0);
5071 if (num == UINT64_MAX)
5072 {
5074 return -1;
5075 }
5076 for (i=0; i<num; ++i)
5077 {
5078 LWT_ISO_EDGE *e = &(elem[i]);
5079 LWT_ELEMID eface = 0;
5080 LWGEOM* geom;
5081 double dist;
5082
5083 if ( ! e->geom )
5084 {
5085 _lwt_release_edges(elem, num);
5086 lwnotice("Corrupted topology: edge %" LWTFMT_ELEMID
5087 " has null geometry", e->edge_id);
5088 continue;
5089 }
5090
5091 /* don't consider dangling edges */
5092 if ( e->face_left == e->face_right )
5093 {
5094 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID
5095 " is dangling, won't consider it", e->edge_id);
5096 continue;
5097 }
5098
5099 geom = lwline_as_lwgeom(e->geom);
5100 dist = lwgeom_mindistance2d_tolerance(geom, qp, tol);
5101
5102 LWDEBUGF(1, "Distance from edge %" LWTFMT_ELEMID
5103 " is %g (tol=%g)", e->edge_id, dist, tol);
5104
5105 /* we won't consider edges too far */
5106 if ( dist > tol ) continue;
5107 if ( e->face_left == 0 ) {
5108 eface = e->face_right;
5109 }
5110 else if ( e->face_right == 0 ) {
5111 eface = e->face_left;
5112 }
5113 else {
5114 _lwt_release_edges(elem, num);
5115 lwerror("Two or more faces found");
5116 return -1;
5117 }
5118
5119 if ( id && id != eface )
5120 {
5121 _lwt_release_edges(elem, num);
5122 lwerror("Two or more faces found"
5123#if 0 /* debugging */
5124 " (%" LWTFMT_ELEMID
5125 " and %" LWTFMT_ELEMID ")", id, eface
5126#endif
5127 );
5128 return -1;
5129 }
5130 else id = eface;
5131 }
5132 if ( num ) _lwt_release_edges(elem, num);
5133
5134 return id;
5135}
5136
5137/* Return the smallest delta that can perturb
5138 * the given value */
5139static inline double
5141{
5142 double ret = 3.6 * pow(10, - ( 15 - log10(d?d:1.0) ) );
5143 return ret;
5144}
5145
5146/* Return the smallest delta that can perturb
5147 * the given point
5148static inline double
5149_lwt_minTolerancePoint2d( const POINT2D* p )
5150{
5151 double max = FP_ABS(p->x);
5152 if ( max < FP_ABS(p->y) ) max = FP_ABS(p->y);
5153 return _lwt_minToleranceDouble(max);
5154}
5155*/
5156
5157/* Return the smallest delta that can perturb
5158 * the maximum absolute value of a geometry ordinate
5159 */
5160static double
5162{
5163 const GBOX* gbox;
5164 double max;
5165 double ret;
5166
5167 gbox = lwgeom_get_bbox(g);
5168 if ( ! gbox ) return 0; /* empty */
5169 max = FP_ABS(gbox->xmin);
5170 if ( max < FP_ABS(gbox->xmax) ) max = FP_ABS(gbox->xmax);
5171 if ( max < FP_ABS(gbox->ymin) ) max = FP_ABS(gbox->ymin);
5172 if ( max < FP_ABS(gbox->ymax) ) max = FP_ABS(gbox->ymax);
5173
5174 ret = _lwt_minToleranceDouble(max);
5175
5176 return ret;
5177}
5178
5179#define _LWT_MINTOLERANCE( topo, geom ) ( \
5180 topo->precision ? topo->precision : _lwt_minTolerance(geom) )
5181
5186
5187static int
5188compare_scored_pointer(const void *si1, const void *si2)
5189{
5190 double a = ((scored_pointer *)si1)->score;
5191 double b = ((scored_pointer *)si2)->score;
5192 if ( a < b )
5193 return -1;
5194 else if ( a > b )
5195 return 1;
5196 else
5197 return 0;
5198}
5199
5200/* Return identifier of an equal edge, 0 if none or -1 on error
5201 * (and lwerror gets called on error)
5202 *
5203 * If an equal edge is found, specify, in "forward" variable whether
5204 * the edge is also equal direction-wise
5205 *
5206 */
5207static LWT_ELEMID
5208_lwt_GetEqualEdge( LWT_TOPOLOGY *topo, LWLINE *edge, int *forward )
5209{
5210 LWT_ELEMID id;
5211 LWT_ISO_EDGE *edges;
5212 uint64_t num, i;
5213 const GBOX *qbox = lwgeom_get_bbox( lwline_as_lwgeom(edge) );
5214 GEOSGeometry *edgeg;
5215 const int flds = LWT_COL_EDGE_EDGE_ID|LWT_COL_EDGE_GEOM;
5216
5217 edges = lwt_be_getEdgeWithinBox2D( topo, qbox, &num, flds, 0 );
5218 if (num == UINT64_MAX)
5219 {
5221 return -1;
5222 }
5223 if ( num )
5224 {
5225 initGEOS(lwnotice, lwgeom_geos_error);
5226
5227 edgeg = LWGEOM2GEOS( lwline_as_lwgeom(edge), 0 );
5228 if ( ! edgeg )
5229 {
5230 _lwt_release_edges(edges, num);
5231 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
5232 return -1;
5233 }
5234 for (i=0; i<num; ++i)
5235 {
5236 LWT_ISO_EDGE *e = &(edges[i]);
5237 LWGEOM *g = lwline_as_lwgeom(e->geom);
5238 GEOSGeometry *gg;
5239 int equals;
5240 gg = LWGEOM2GEOS( g, 0 );
5241 if ( ! gg )
5242 {
5243 GEOSGeom_destroy(edgeg);
5244 _lwt_release_edges(edges, num);
5245 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
5246 return -1;
5247 }
5248 equals = GEOSEquals(gg, edgeg);
5249 GEOSGeom_destroy(gg);
5250 if ( equals == 2 )
5251 {
5252 GEOSGeom_destroy(edgeg);
5253 _lwt_release_edges(edges, num);
5254 lwerror("GEOSEquals exception: %s", lwgeom_geos_errmsg);
5255 return -1;
5256 }
5257 if ( equals )
5258 {
5259 id = e->edge_id;
5260 /* Check if direction also matches */
5261 if ( forward )
5262 {
5263 /* If input line is closed, we use winding order */
5264 if ( lwline_is_closed(edge) )
5265 {
5266 if ( ptarray_isccw(edge->points) == ptarray_isccw(e->geom->points) )
5267 {
5268 *forward = 1;
5269 }
5270 else
5271 {
5272 *forward = 0;
5273 }
5274 }
5275 else
5276 {
5277 /* Input line is not closed, checking fist point is enough */
5278 if (
5279 memcmp(
5280 getPoint_internal(edge->points, 0),
5282 sizeof(POINT2D)
5283 ) == 0
5284 )
5285 {
5286 *forward = 1;
5287 }
5288 else
5289 {
5290 *forward = 0;
5291 }
5292 }
5293 }
5294 GEOSGeom_destroy(edgeg);
5295 _lwt_release_edges(edges, num);
5296 return id;
5297 }
5298 }
5299 GEOSGeom_destroy(edgeg);
5300 _lwt_release_edges(edges, num);
5301 }
5302
5303 return 0;
5304}
5305
5306
5321static int
5322_lwt_SnapEdge_checkMotion( LWT_TOPOLOGY* topo, const LWCOLLECTION *splitC, const LWT_ISO_EDGE *edge, LWT_ISO_EDGE *existingEdge, const LWT_NODE_EDGES *splitNodeEdges )
5323{
5324 // build the motion range shape: splitC->geoms[0] + splitC->geoms[1] - edge->geom
5325 POINTARRAY *motionRange = ptarray_clone_deep(lwgeom_as_lwline(splitC->geoms[0])->points);
5326 ptarray_append_ptarray(motionRange, lwgeom_as_lwline(splitC->geoms[1])->points, 0);
5327 POINTARRAY *reverseNewLine = ptarray_clone_deep(edge->geom->points);
5328 ptarray_reverse_in_place(reverseNewLine);
5329 ptarray_append_ptarray(motionRange, reverseNewLine, 0);
5330 ptarray_free(reverseNewLine);
5331
5332 // motionBounds takes ownership of motionRange
5333 LWLINE *motionBounds = lwline_construct(topo->srid, NULL, motionRange);
5334
5335 // motionPolyBare takes ownership of motionBounds
5336 LWGEOM *motionPolyBare = (LWGEOM *)lwpoly_from_lwlines(motionBounds, 0, NULL);
5337 LWGEOM *motionPoly = lwgeom_make_valid(motionPolyBare);
5338 lwgeom_free(motionPolyBare);
5339
5340 LWDEBUGG(1, motionPoly, "Motion range");
5341
5342 // check the Motion range doesn't cover any of
5343 // the edges incident to the split node other
5344 // than the existing edge
5345 GEOSGeometry *motionPolyG = NULL;
5346 for ( uint64_t t=0; t<splitNodeEdges->numEdges; t++ )
5347 {
5348 LWT_ISO_EDGE *e = &(splitNodeEdges->edges[t]);
5349 GEOSGeometry *eg;
5350 if ( e == existingEdge ) continue;
5351 if ( e == edge ) continue;
5352 if ( ! motionPolyG ) {
5353 motionPolyG = LWGEOM2GEOS( motionPoly, 0 );
5354 if ( ! motionPolyG )
5355 {
5356 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
5357 return -1;
5358 }
5359 }
5360 eg = LWGEOM2GEOS( lwline_as_lwgeom(e->geom), 0 );
5361 if ( ! eg )
5362 {
5363 GEOSGeom_destroy(motionPolyG);
5364 lwgeom_free(motionPoly);
5365 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
5366 return -1;
5367 }
5368
5369 char *relate = GEOSRelate( motionPolyG, eg );
5370 GEOSGeom_destroy(eg);
5371 if ( ! relate )
5372 {
5373 GEOSGeom_destroy(motionPolyG);
5374 lwgeom_free(motionPoly);
5375 lwerror("Relate error: %s", lwgeom_geos_errmsg);
5376 return -1;
5377 }
5378
5379 int match = GEOSRelatePatternMatch(relate, "FF*F*****");
5380 GEOSFree( relate );
5381 if (match == 2)
5382 {
5383 GEOSGeom_destroy(motionPolyG);
5384 lwgeom_free(motionPoly);
5385 lwerror("RelateMatch error: %s", lwgeom_geos_errmsg);
5386 return -1;
5387 }
5388 if ( ! match )
5389 {
5390 GEOSGeom_destroy(motionPolyG);
5391 lwgeom_free(motionPoly);
5392 lwerror("snapping edge %" LWTFMT_ELEMID
5393 " to new node moves it past edge %" LWTFMT_ELEMID,
5394 edge->edge_id, e->edge_id
5395 );
5396 return -1;
5397 }
5398 }
5399 if ( motionPolyG ) GEOSGeom_destroy(motionPolyG);
5400
5401 lwgeom_free(motionPoly);
5402
5403 return 0;
5404}
5405
5406static int
5408 LWT_TOPOLOGY* topo,
5409 LWT_ISO_NODE *node,
5410 LWT_ISO_EDGE *edge, /* known to require an update from DB for the edge linking and face labelling - geometry is fine */
5411 double tol
5412)
5413{
5414 LWGEOM *prj = lwpoint_as_lwgeom(node->geom);
5415 const POINT2D *pt = getPoint2d_cp(lwgeom_as_lwpoint(prj)->point, 0);
5416
5417 LWDEBUGF(1, "Experimental handling of within-tolerance edge %" LWTFMT_ELEMID, edge->edge_id);
5418 LWGEOM *snapE = lwgeom_snap(lwline_as_lwgeom(edge->geom), prj, tol);
5419
5420 /* TODO: check if an endpoint moved */
5421
5422 LWGEOM *splitE = lwgeom_split(snapE, prj);
5423 LWDEBUGG(1, splitE, "Split edge");
5424
5425 LWCOLLECTION *splitC = lwgeom_as_lwcollection(splitE);
5426 if ( splitC->ngeoms != 2 )
5427 {
5428 lwerror("Split of edge resulted in %d components", splitC->ngeoms);
5429 return -1;
5430 }
5431
5432 LWT_NODE_EDGES *firstNodeEdges = lwt_nodeEdges_loadFromDB(topo, edge->start_node, LWT_COL_EDGE_ALL );
5433 if ( ! firstNodeEdges ) {
5434 lwerror("No edges found in DB to be incident to split edge's first node");
5435 return -1;
5436 }
5437 LWT_NODE_EDGES *lastNodeEdges = lwt_nodeEdges_loadFromDB(topo, edge->end_node, LWT_COL_EDGE_ALL );
5438 if ( ! lastNodeEdges ) {
5439 lwerror("No edges found in DB to be incident to split edge's last node");
5440 return -1;
5441 }
5442 LWT_NODE_EDGES *splitNodeEdges = lwt_nodeEdges_loadFromDB(topo, node->node_id, LWT_COL_EDGE_ALL);
5443 if ( ! splitNodeEdges ) {
5444 lwerror("No edges found in DB to be incident to split node");
5445 return -1;
5446 }
5447
5448 /* Update split edge reference as it was possibly changed by previous call */
5449 for ( uint64_t t=0; t<firstNodeEdges->numEdges; t++ )
5450 {
5451 if ( firstNodeEdges->edges[t].edge_id == edge->edge_id )
5452 {
5453 edge = &(firstNodeEdges->edges[t]);
5454 break;
5455 }
5456 }
5457
5458 LWDEBUGF(1, "Split edge %"
5459 LWTFMT_ELEMID " start condition: next_right:%"
5460 LWTFMT_ELEMID ", next_left:%"
5461 LWTFMT_ELEMID ", face_right:%"
5462 LWTFMT_ELEMID ", face_left:%"
5463 LWTFMT_ELEMID ", start_node:%"
5464 LWTFMT_ELEMID ", end_node:%"
5466 edge->edge_id,
5467 edge->next_right,
5468 edge->next_left,
5469 edge->face_right,
5470 edge->face_left,
5471 edge->start_node,
5472 edge->end_node
5473 );
5474
5475 int ret;
5476
5477 uint64_t n;
5478 LWT_ELEMID replacedBy[2];
5479 LWT_ISO_EDGE *existingEdges[2];
5480 //int replacedByForward[2];
5481 edgeend splitNodeEdgeEnds[2];
5482 for ( n=0; n<2; ++n)
5483 {
5484 int forward;
5485 /* TODO: directly get full existing edge rather than just id * */
5486 LWT_ELEMID existingEdgeId = _lwt_GetEqualEdge( topo, lwgeom_as_lwline(splitC->geoms[n]), &forward );
5487 if ( existingEdgeId == -1 )
5488 {
5489 /* probably too late, due to internal lwerror */
5490 return -1;
5491 }
5492 existingEdges[n] = NULL;
5493 if ( existingEdgeId == 0 )
5494 {
5495 LWDEBUGF(1, "Split component %llu is a new edge, computing edgeEndInfo", n);
5496 {
5497 // Compute nextCW and nextCCW of the split edge now
5498 // incident to the split point
5499
5500 POINT2D op; /* other point */
5501 if ( n == 1 ) {
5503 lwgeom_as_lwline(splitC->geoms[1])->points, //const POINTARRAY* pa,
5504 pt, //POINT2D *ref,
5505 0, // int from,
5506 1, // int dir,
5507 &op // POINT2D *op
5508 );
5509 if (!ret)
5510 {
5511 lwerror("No distinct vertices found on second part of split edge");
5512 return -1; /* lwerror should have been raised */
5513 }
5514 } else {
5515 const POINTARRAY *pa = lwgeom_as_lwline(splitC->geoms[0])->points;
5517 pa, //const POINTARRAY* pa,
5518 pt, //POINT2D *ref,
5519 pa->npoints-1, // int from,
5520 -1, // int dir,
5521 &op // POINT2D *op
5522 );
5523 if (!ret)
5524 {
5525 lwerror("No distinct vertices found on first part of split edge");
5526 return -1; /* lwerror should have been raised */
5527 }
5528 }
5529 if ( ! azimuth_pt_pt(pt, &op, &(splitNodeEdgeEnds[n].myaz)) ) {
5530 lwerror("error computing azimuth of split endpoint [%.15g %.15g,%.15g %.15g]",
5531 op.x, op.y,
5532 pt->x, pt->y
5533 );
5534 return -1;
5535 }
5536 LWDEBUGF(1, "Azimuth of split component %llu edgeend [%.15g %.15g,%.15g %.15g] is %.15g",
5537 n, op.x, op.y, pt->x, pt->y, splitNodeEdgeEnds[n].myaz);
5538 ret = _lwt_FindAdjacentEdges( topo, node->node_id, &(splitNodeEdgeEnds[n]), NULL, -1 );
5539 if ( ! ret ) {
5540 lwerror("Unexpected backend return: _lwt_FindAdjacentEdges(%"
5541 LWTFMT_ELEMID ") found no edges when we previously split edge %"
5542 LWTFMT_ELEMID "d on it",
5543 node->node_id, edge->edge_id);
5544 return -1;
5545 }
5546 LWDEBUGF(1, "Component %llu of split edge %" LWTFMT_ELEMID
5547 " next CW is %" LWTFMT_ELEMID ", next CCW is %" LWTFMT_ELEMID,
5548 n, edge->edge_id, splitNodeEdgeEnds[n].nextCW, splitNodeEdgeEnds[n].nextCCW);
5549 }
5550 }
5551 else
5552 {
5553 LWDEBUGF(1, "Split component %llu matches existing edge %" LWTFMT_ELEMID
5554 " (%s)", n, existingEdgeId, forward ? "forward" : "backward" );
5555 /* Get existing edge */
5556 for ( uint64_t t=0; t<splitNodeEdges->numEdges; t++ )
5557 {
5558 if ( splitNodeEdges->edges[t].edge_id == existingEdgeId )
5559 {
5560 existingEdges[n] = &(splitNodeEdges->edges[t]);
5561 break;
5562 }
5563 }
5564 if (existingEdges[n] == NULL)
5565 {
5566 lwerror("could not find edge %" LWTFMT_ELEMID " in database, but _lwt_GetEqualEdges said it was there", existingEdgeId );
5567 return -1;
5568 }
5569 }
5570
5571 replacedBy[n] = existingEdgeId;
5572 }
5573
5574
5575 LWT_ISO_EDGE updatedEdge;
5576 int updateFlags;
5577 LWT_ISO_EDGE selEdge;
5578
5579 if ( ( replacedBy[0] != 0 && replacedBy[1] == 0 ) ||
5580 ( replacedBy[1] != 0 && replacedBy[0] == 0 ) )
5581 {
5582 /* One side of the snapped edge collapsed to an existing edge */
5583
5584 LWT_ISO_EDGE *existingEdge;
5585 edgeend *splitNodeEdgeEndInfo;
5586 LWLINE *newSplitEdgeLine;
5587 int splitNodeNewEdgeOutgoing; // The new split edge goes out of the split node
5588
5589 if ( replacedBy[1] == 0 )
5590 /* First part of edge is the existing edge */
5591 {
5592 existingEdge = existingEdges[0];
5593 newSplitEdgeLine = lwgeom_as_lwline(splitC->geoms[1]);
5594 splitNodeEdgeEndInfo = &(splitNodeEdgeEnds[1]);
5595 splitNodeNewEdgeOutgoing = 1;
5596 }
5597 else
5598 /* Second part of edge is the existing edge */
5599 {
5600 existingEdge = existingEdges[1];
5601 newSplitEdgeLine = lwgeom_as_lwline(splitC->geoms[0]);
5602 splitNodeEdgeEndInfo = &(splitNodeEdgeEnds[0]);
5603 splitNodeNewEdgeOutgoing = 0;
5604 }
5605
5606 _lwt_SnapEdge_checkMotion( topo, splitC, edge, existingEdge, splitNodeEdges );
5607
5608 LWDEBUGF(1, "Existing edge %"
5609 LWTFMT_ELEMID " (post-modEdgeSplit) next_right:%"
5610 LWTFMT_ELEMID ", next_left:%"
5611 LWTFMT_ELEMID ", face_right:%"
5612 LWTFMT_ELEMID ", face_left:%"
5614 existingEdge->edge_id,
5615 existingEdge->next_right,
5616 existingEdge->next_left,
5617 existingEdge->face_right,
5618 existingEdge->face_left
5619 );
5620
5621 /*****
5622 *
5623 * Update edge linking
5624 *
5625 */
5626
5627 /*
5628 * Update next CW / CCW edges on split node
5629 * (fix linking on split node)
5630 */
5631
5632 /* Update next CCW edge */
5633 {
5634 LWT_ELEMID nextEdge, sideFace;
5635 if ( splitNodeNewEdgeOutgoing ) {
5636 nextEdge = edge->edge_id;
5637 sideFace = edge->face_left;
5638 } else {
5639 nextEdge = -edge->edge_id;
5640 sideFace = edge->face_right;
5641 }
5642
5643 LWDEBUGF(1, "Setting nextEdge/sideFace of CCW edge %" LWTFMT_ELEMID
5644 " to %" LWTFMT_ELEMID "/%" LWTFMT_ELEMID,
5645 splitNodeEdgeEndInfo->nextCCW,
5646 nextEdge, sideFace
5647 );
5648
5649 if ( splitNodeEdgeEndInfo->nextCCW > 0 )
5650 {
5651 selEdge.edge_id = splitNodeEdgeEndInfo->nextCCW;
5652 updatedEdge.next_right = nextEdge;
5653 updatedEdge.face_right = sideFace;
5654 updateFlags = LWT_COL_EDGE_NEXT_RIGHT|
5656 LWDEBUGF(1, "Will update next_right/face_right of outgoing CCW edge %" LWTFMT_ELEMID
5657 " to %" LWTFMT_ELEMID "/%" LWTFMT_ELEMID,
5658 selEdge.edge_id,
5659 nextEdge, sideFace
5660 );
5661 }
5662 else
5663 {
5664 selEdge.edge_id = -splitNodeEdgeEndInfo->nextCCW;
5665 updatedEdge.next_left = nextEdge;
5666 updatedEdge.face_left = sideFace;
5667 updateFlags = LWT_COL_EDGE_NEXT_LEFT|
5669 LWDEBUGF(1, "Will update next_left/face_left of incoming CCW edge %" LWTFMT_ELEMID
5670 " to %" LWTFMT_ELEMID "/%" LWTFMT_ELEMID,
5671 selEdge.edge_id,
5672 nextEdge, sideFace
5673 );
5674 }
5675
5676 ret = lwt_be_updateEdges(topo,
5677 &selEdge, LWT_COL_EDGE_EDGE_ID,
5678 &updatedEdge, updateFlags,
5679 NULL, 0);
5680 if ( ret == -1 ) {
5682 return -1;
5683 } else if ( ret == 0 ) {
5684 lwerror("Edge %" LWTFMT_ELEMID " disappeared during operations", selEdge.edge_id);
5685 return -1;
5686 } else if ( ret > 1 ) {
5687 lwerror("More than a single edge found with id %"
5688 LWTFMT_ELEMID " ! (corrupted topology?)", selEdge.edge_id);
5689 return -1;
5690 }
5691 }
5692
5693 /* Update next CW edge on split node */
5694 {
5695 LWT_ELEMID sideFace;
5696 if ( splitNodeNewEdgeOutgoing ) {
5697 sideFace = edge->face_right;
5698 } else {
5699 sideFace = edge->face_left;
5700 }
5701
5702 LWDEBUGF(1, "Setting sideFace of CW edge %" LWTFMT_ELEMID
5703 " to %" LWTFMT_ELEMID,
5704 splitNodeEdgeEndInfo->nextCW, sideFace
5705 );
5706
5707 if ( splitNodeEdgeEndInfo->nextCW > 0 )
5708 {
5709 selEdge.edge_id = splitNodeEdgeEndInfo->nextCW;
5710 updatedEdge.face_left = sideFace;
5711 updateFlags = LWT_COL_EDGE_FACE_LEFT;
5712 LWDEBUGF(1, "Updating left_face of edge %" LWTFMT_ELEMID
5713 ", outgoing next CW of new split edge on split node",
5714 selEdge.edge_id);
5715 }
5716 else
5717 {
5718 selEdge.edge_id = -splitNodeEdgeEndInfo->nextCW;
5719 updatedEdge.face_right = sideFace;
5720 updateFlags = LWT_COL_EDGE_FACE_RIGHT;
5721 LWDEBUGF(1, "Updating right_face of edge %" LWTFMT_ELEMID
5722 ", incoming next CW of new split edge on split node",
5723 selEdge.edge_id);
5724 }
5725
5726 ret = lwt_be_updateEdges(topo,
5727 &selEdge, LWT_COL_EDGE_EDGE_ID,
5728 &updatedEdge, updateFlags,
5729 NULL, 0);
5730 if ( ret == -1 ) {
5732 return -1;
5733 } else if ( ret == 0 ) {
5734 lwerror("Edge %" LWTFMT_ELEMID " disappeared during operations", selEdge.edge_id);
5735 return -1;
5736 } else if ( ret > 1 ) {
5737 lwerror("More than a single edge found with edge_id %"
5738 LWTFMT_ELEMID " ! (corrupted topology?)", selEdge.edge_id);
5739 return -1;
5740 }
5741 }
5742
5743 /*
5744 * Update links to now-disappeared edgeend
5745 * on the node originally in common with
5746 * the existing node
5747 */
5748
5749 LWT_ISO_EDGE *nextCCWEdge = NULL;
5750 int nextCCWEdgeIsIncoming = 0;
5751 int collapsedEdgeIsIncoming;
5752
5753 LWT_ELEMID commonNodeID;
5754
5755 LWT_NODE_EDGES *commonNodeEdges;
5756 if ( replacedBy[0] ) {
5757 commonNodeEdges = firstNodeEdges;
5758 commonNodeID = edge->start_node;
5759 collapsedEdgeIsIncoming = 0;
5760 } else {
5761 commonNodeEdges = lastNodeEdges;
5762 commonNodeID = edge->end_node;
5763 collapsedEdgeIsIncoming = 1;
5764 }
5765
5766
5767 // Update next CCW edge
5768 {
5769 LWT_ELEMID signedCollapsedEdgeID = collapsedEdgeIsIncoming ? -edge->edge_id: edge->edge_id;
5770
5771 LWDEBUGF(1, "Looking for next CCW edge of split edge %"
5772 LWTFMT_ELEMID " on common node %" LWTFMT_ELEMID
5773 " having %llu attached edges",
5774 edge->edge_id,
5775 commonNodeID, commonNodeEdges->numEdges
5776 );
5777
5778 /* Find next CCW edge */
5779 for ( uint64_t t=0; t<commonNodeEdges->numEdges; t++ )
5780 {
5781 LWT_ISO_EDGE *et = &(commonNodeEdges->edges[t]);
5782
5783 LWDEBUGF(1, "common node edge %"
5784 LWTFMT_ELEMID " has next_left:%" LWTFMT_ELEMID
5785 " next_right:%" LWTFMT_ELEMID,
5786 et->edge_id,
5787 et->next_left,
5788 et->next_right
5789 );
5790
5791 if ( et->start_node == commonNodeID )
5792 {
5793 if ( et->next_right == signedCollapsedEdgeID )
5794 {
5795 nextCCWEdge = et;
5796 nextCCWEdgeIsIncoming = 0;
5797 break;
5798 }
5799 }
5800 else if ( et->end_node == commonNodeID )
5801 {
5802 if ( et->next_left == signedCollapsedEdgeID )
5803 {
5804 nextCCWEdge = et;
5805 nextCCWEdgeIsIncoming = 1;
5806 break;
5807 }
5808 }
5809 }
5810 if ( ! nextCCWEdge )
5811 {
5812 lwerror("Could not find nextCCW edge on common node %" LWTFMT_ELEMID,
5813 commonNodeID
5814 );
5815 return -1;
5816 }
5817
5818 LWDEBUGF(1, "Next CCW edge of split edge %"
5819 LWTFMT_ELEMID " on common node %" LWTFMT_ELEMID
5820 " is (%s) %" LWTFMT_ELEMID,
5821 edge->edge_id,
5822 commonNodeID,
5823 nextCCWEdgeIsIncoming ? "incoming" : "outgoing",
5824 nextCCWEdge->edge_id
5825 );
5826
5827 if ( nextCCWEdge->edge_id == existingEdge->edge_id )
5828 {
5829 LWDEBUG(1, "Next CCW edge is existing/collapse edge, will not update here");
5830 }
5831 else
5832 {
5833 /*
5834 * TODO: the nextCCW is only of interest IFF the collapse
5835 * happened clockwise!
5836 *
5837 * just update to the split edge id (positive or negative
5838 * depending on whether it was outgoing or incoming in the
5839 * common node)
5840 */
5841 LWT_ELEMID newNextEdge = existingEdge->start_node == commonNodeID ?
5842 existingEdge->edge_id :
5843 -existingEdge->edge_id;
5844
5845 selEdge.edge_id = nextCCWEdge->edge_id;
5846 if ( nextCCWEdgeIsIncoming ) {
5847 updatedEdge.next_left = newNextEdge;
5848 updateFlags = LWT_COL_EDGE_NEXT_LEFT;
5849 LWDEBUGF(1, "Updating next_left of incoming next CCW edge %"
5851 " to %" LWTFMT_ELEMID,
5852 selEdge.edge_id,
5853 newNextEdge
5854 );
5855 } else {
5856 updatedEdge.next_right = newNextEdge;
5857 updateFlags = LWT_COL_EDGE_NEXT_RIGHT;
5858 LWDEBUGF(1, "Updating next_right of outgoing next CCW edge %"
5860 " to %" LWTFMT_ELEMID,
5861 selEdge.edge_id,
5862 newNextEdge
5863 );
5864 }
5865 ret = lwt_be_updateEdges(topo,
5866 &selEdge, LWT_COL_EDGE_EDGE_ID,
5867 &updatedEdge, updateFlags,
5868 NULL, 0);
5869 if ( ret == -1 ) {
5871 return -1;
5872 } else if ( ret == 0 ) {
5873 lwerror("Edge %" LWTFMT_ELEMID " disappeared during operations", selEdge.edge_id);
5874 return -1;
5875 } else if ( ret > 1 ) {
5876 lwerror("More than a single edge found with next_left %"
5877 LWTFMT_ELEMID " ! (corrupted topology?)", selEdge.next_left);
5878 return -1;
5879 }
5880 }
5881 }
5882
5883 /*****
5884 *
5885 * Update the existing edge our split edge collapsed to
5886 *
5887 */
5888
5889 selEdge.edge_id = existingEdge->edge_id;
5890 updateFlags = 0;
5891 if ( existingEdge->next_left == edge->edge_id )
5892 {
5893 updatedEdge.next_left = edge->next_right;
5894 updateFlags |= LWT_COL_EDGE_NEXT_LEFT;
5895 LWDEBUGF(1, "Will update next_left of existing edge %"
5897 selEdge.edge_id,
5898 updatedEdge.next_left
5899 );
5900 }
5901 if ( existingEdge->next_right == edge->edge_id )
5902 {
5903 updatedEdge.next_right = edge->next_right;
5904 updateFlags |= LWT_COL_EDGE_NEXT_RIGHT;
5905 LWDEBUGF(1, "Will update next_right of existing edge %"
5907 selEdge.edge_id,
5908 updatedEdge.next_right
5909 );
5910 }
5911 if ( existingEdge->next_left == -edge->edge_id )
5912 {
5913 updatedEdge.next_left = edge->next_left;
5914 updateFlags |= LWT_COL_EDGE_NEXT_LEFT;
5915 LWDEBUGF(1, "Will update next_left of existing edge %"
5917 selEdge.edge_id,
5918 updatedEdge.next_left
5919 );
5920 }
5921 if ( existingEdge->next_right == -edge->edge_id )
5922 {
5923 updatedEdge.next_right = edge->next_left;
5924 updateFlags |= LWT_COL_EDGE_NEXT_RIGHT;
5925 LWDEBUGF(1, "Will update next_right of existing edge %"
5927 selEdge.edge_id,
5928 updatedEdge.next_right
5929 );
5930 }
5931 if ( updateFlags )
5932 {
5933 ret = lwt_be_updateEdges(topo,
5934 &selEdge, LWT_COL_EDGE_EDGE_ID,
5935 &updatedEdge, updateFlags,
5936 NULL, 0);
5937 if ( ret == -1 ) {
5939 return -1;
5940 } else if ( ret == 0 ) {
5941 lwerror("Edge being split (%" LWTFMT_ELEMID ") disappeared during operations?", selEdge.edge_id);
5942 return -1;
5943 } else if ( ret > 1 ) {
5944 lwerror("More than a single edge found with id %"
5945 LWTFMT_ELEMID " !", selEdge.edge_id);
5946 return -1;
5947 }
5948 }
5949
5950 /*****
5951 *
5952 * Update the split edge
5953 *
5954 */
5955
5956 selEdge.edge_id = edge->edge_id;
5957 updatedEdge.geom = newSplitEdgeLine;
5958 updateFlags = LWT_COL_EDGE_GEOM;
5959 if ( splitNodeNewEdgeOutgoing ) {
5960 updatedEdge.start_node = node->node_id;
5961 updatedEdge.next_right = splitNodeEdgeEndInfo->nextCW;
5962 updateFlags |= LWT_COL_EDGE_START_NODE;
5963 updateFlags |= LWT_COL_EDGE_NEXT_RIGHT;
5964 LWDEBUGF(1, "Updating split edge %" LWTFMT_ELEMID
5965 " start node to %" LWTFMT_ELEMID
5966 " and next_right to %" LWTFMT_ELEMID,
5967 selEdge.edge_id,
5968 updatedEdge.start_node,
5969 updatedEdge.next_right
5970 );
5971 } else {
5972 updatedEdge.end_node = node->node_id;
5973 updatedEdge.next_left = splitNodeEdgeEndInfo->nextCW;
5974 updateFlags |= LWT_COL_EDGE_END_NODE;
5975 updateFlags |= LWT_COL_EDGE_NEXT_LEFT;
5976 LWDEBUGF(1, "Updating split edge %" LWTFMT_ELEMID
5977 " end node to %" LWTFMT_ELEMID
5978 " and next_left to %" LWTFMT_ELEMID
5979 ,
5980 selEdge.edge_id,
5981 updatedEdge.end_node
5982 , updatedEdge.next_left
5983 );
5984 }
5985
5986 ret = lwt_be_updateEdges(topo,
5987 &selEdge, LWT_COL_EDGE_EDGE_ID,
5988 &updatedEdge, updateFlags,
5989 NULL, 0);
5990 if ( ret == -1 ) {
5992 return -1;
5993 } else if ( ret == 0 ) {
5994 lwerror("Edge being split (%" LWTFMT_ELEMID ") disappeared during operations?", selEdge.edge_id);
5995 return -1;
5996 } else if ( ret > 1 ) {
5997 lwerror("More than a single edge found with id %"
5998 LWTFMT_ELEMID " !", selEdge.edge_id);
5999 return -1;
6000 }
6001
6002 /************
6003 *
6004 * Update split-edge collapsed side face MBR, if needed
6005 *
6006 */
6007
6008 if ( edge->face_left != edge->face_right )
6009 {
6010 /* TODO: update a single face ? */
6011 LWT_ISO_FACE updface;
6012 updface.face_id = edge->face_right;
6013 if ( updface.face_id != 0 )
6014 {
6015 /* We only need to update the MBR if the shrunk face is
6016 * not the universe face */
6017 LWGEOM *fg = lwt_GetFaceGeometry(topo, updface.face_id);
6018 if ( ! fg )
6019 {
6020 lwerror("Could not get geometry of face %" LWTFMT_ELEMID, updface.face_id);
6021 return -1;
6022 }
6023 updface.mbr = (GBOX *)lwgeom_get_bbox(fg);
6024 if ( updface.mbr )
6025 {
6026 int ret = lwt_be_updateFacesById( topo, &updface, 1 );
6027 if ( ret == -1 )
6028 {
6030 return -1;
6031 }
6032 lwgeom_free(fg);
6033 }
6034 else
6035 {
6036 lwerror("Programmatic error ? Geometry of face %" LWTFMT_ELEMID " is empty!", updface.face_id);
6037 }
6038 }
6039 updface.face_id = edge->face_left;
6040 if ( updface.face_id != 0 )
6041 {
6042 /* We only need to update the MBR if the shrunk face is
6043 * not the universe face */
6044 LWGEOM *fg = lwt_GetFaceGeometry(topo, updface.face_id);
6045 if ( ! fg )
6046 {
6047 lwerror("Could not get geometry of face %" LWTFMT_ELEMID, updface.face_id);
6048 return -1;
6049 }
6050 updface.mbr = (GBOX *)lwgeom_get_bbox(fg);
6051 if ( updface.mbr )
6052 {
6053 int ret = lwt_be_updateFacesById( topo, &updface, 1 );
6054 if ( ret == -1 )
6055 {
6057 return -1;
6058 }
6059 lwgeom_free(fg);
6060 }
6061 else
6062 {
6063 lwerror("Programmatic error ? Geometry of face %" LWTFMT_ELEMID " is empty!", updface.face_id);
6064 }
6065 }
6066 }
6067
6068 /************
6069 *
6070 * Update TopoGeometries composition
6071 *
6072 */
6073
6074 LWDEBUGF(1, "Updating lineal TopoGeometry composition to add edge %" LWTFMT_ELEMID
6075 " to features composed by edge %" LWTFMT_ELEMID,
6076 existingEdge->edge_id, edge->edge_id
6077 );
6078 ret = lwt_be_updateTopoGeomEdgeSplit(topo, edge->edge_id, existingEdge->edge_id, -1);
6079 if ( ! ret ) {
6081 return -1;
6082 }
6083
6084 return 0;
6085 }
6086 else if ( replacedBy[0] == 0 && replacedBy[1] == 0 )
6087 {
6088
6089 /* Neither sides of the snapped edge collapsed to an existing edge */
6090
6091 /* New edge is the outgoing one, by design */
6092 LWT_ISO_EDGE newEdge;
6093 newEdge.edge_id = lwt_be_getNextEdgeId( topo );
6094 if ( newEdge.edge_id == -1 ) {
6096 return -1;
6097 }
6098 newEdge.geom = lwgeom_as_lwline(splitC->geoms[1]);
6099 newEdge.end_node = edge->end_node;
6100 newEdge.start_node = node->node_id;
6101 newEdge.next_right = -updatedEdge.edge_id; // to be assigned later, if needed
6102 if ( edge->next_left == -edge->edge_id ) {
6103 newEdge.next_left = -newEdge.edge_id;
6104 } else {
6105 newEdge.next_left = edge->next_left; // unchanged
6106 }
6107 newEdge.face_left = edge->face_left; // to be changed later
6108 newEdge.face_right = edge->face_right; // to be changed later
6109
6110 /* Updated edge is the incoming one, by design */
6111 updatedEdge.edge_id = edge->edge_id;
6112 updatedEdge.geom = lwgeom_as_lwline(splitC->geoms[0]);
6113 updatedEdge.end_node = node->node_id;
6114 updatedEdge.next_left = newEdge.edge_id; // to be re-assigned later, if needed
6115
6116
6117 LWT_EDGEEND_STAR *nodeStar = lwt_edgeEndStar_init( node->node_id );
6118 for ( uint64_t t=0; t<splitNodeEdges->numEdges; t++ )
6119 {
6120 lwt_edgeEndStar_addEdge( nodeStar, &(splitNodeEdges->edges[t]) );
6121 }
6122 lwt_edgeEndStar_addEdge( nodeStar, &updatedEdge );
6123 lwt_edgeEndStar_addEdge( nodeStar, &newEdge );
6124
6125 _lwt_SnapEdge_checkMotion( topo, splitC, edge, NULL, splitNodeEdges );
6126
6127 /* There cannot be anything in the middle of the two components,
6128 * so both sides will give the same nextCCW and same nextCW */
6129
6130 lwt_EdgeEndStar_debugPrint( nodeStar );
6131
6132 const LWT_EDGEEND *ee0 = lwt_edgeEndStar_getNextCW( nodeStar, &updatedEdge, 0); // incoming
6133 LWDEBUGF(1, "* NextCW of incoming snap-edge component is %s %" LWTFMT_ELEMID,
6134 ee0->outgoing ? "outgoing" : "incoming", ee0->edge->edge_id);
6135 if ( ee0->outgoing ) {
6136 updatedEdge.next_left = ee0->edge->edge_id;
6137 } else {
6138 updatedEdge.next_left = -ee0->edge->edge_id;
6139 }
6140 int splitFaceOnLeft = 1;
6141 if ( ee0->edge->edge_id == newEdge.edge_id )
6142 {
6143 splitFaceOnLeft = 0;
6144 }
6145
6146 const LWT_EDGEEND *ee1 = lwt_edgeEndStar_getNextCW( nodeStar, &newEdge, 1); // outgoing
6147 LWDEBUGF(1, "* NextCW of outgoing snap-edge component is %s %" LWTFMT_ELEMID,
6148 ee1->outgoing ? "outgoing" : "incoming", ee1->edge->edge_id);
6149 if ( ee1->outgoing ) {
6150 newEdge.next_right = ee1->edge->edge_id;
6151 } else {
6152 newEdge.next_right = -ee1->edge->edge_id;
6153 }
6154
6155 int ret = lwt_be_insertEdges(topo, &newEdge, 1);
6156 if (ret != 1)
6157 {
6159 return -1;
6160 }
6161
6162 /* Updating the first portion of the edge */
6163 selEdge.edge_id = edge->edge_id;
6164 ret = lwt_be_updateEdges(topo,
6165 &selEdge, LWT_COL_EDGE_EDGE_ID,
6167 NULL, 0);
6168 if ( ret == -1 ) {
6170 return -1;
6171 } else if ( ret == 0 ) {
6172 lwerror("Edge being split (%" LWTFMT_ELEMID ") disappeared during operations?", selEdge.edge_id);
6173 return -1;
6174 } else if ( ret > 1 ) {
6175 lwerror("More than a single edge found with id %"
6176 LWTFMT_ELEMID " !", selEdge.edge_id);
6177 return -1;
6178 }
6179
6180 /* Update CCW edges if needed */
6181 ee0 = lwt_edgeEndStar_getNextCCW( nodeStar, &updatedEdge, 0); // incoming
6182 LWDEBUGF(1, "* NextCCW of incoming snap-edge component is %s %" LWTFMT_ELEMID,
6183 ee0->outgoing ? "outgoing" : "incoming", ee0->edge->edge_id);
6184 if ( ee0->edge->edge_id != newEdge.edge_id )
6185 {
6186 selEdge.edge_id = ee0->edge->edge_id;
6187 if ( ee0->outgoing ) {
6188 updatedEdge.next_right = -edge->edge_id;
6189 updateFlags = LWT_COL_EDGE_NEXT_RIGHT;
6190 } else {
6191 updatedEdge.next_left = -edge->edge_id;
6192 updateFlags = LWT_COL_EDGE_NEXT_LEFT;
6193 }
6194
6195 LWDEBUGF(1, "Updating edge %" LWTFMT_ELEMID ", %s nextCCW of incoming snap-edge",
6196 selEdge.edge_id, ee0->outgoing ? "outgoing" : "incoming");
6197 ret = lwt_be_updateEdges(topo,
6198 &selEdge, LWT_COL_EDGE_EDGE_ID,
6199 &updatedEdge, updateFlags,
6200 NULL, 0);
6201 if ( ret == -1 ) {
6203 return -1;
6204 } else if ( ret == 0 ) {
6205 lwerror("Edge being split (%" LWTFMT_ELEMID ") disappeared during operations?", selEdge.edge_id);
6206 return -1;
6207 } else if ( ret > 1 ) {
6208 lwerror("More than a single edge found with id %"
6209 LWTFMT_ELEMID " !", selEdge.edge_id);
6210 return -1;
6211 }
6212 }
6213
6214 /* Update CCW edge of outgoing portion if needed */
6215 ee1 = lwt_edgeEndStar_getNextCCW( nodeStar, &newEdge, 1); // outgoing
6216 LWDEBUGF(1, "* NextCCW of outgoing snap-edge component is %s %" LWTFMT_ELEMID,
6217 ee1->outgoing ? "outgoing" : "incoming", ee1->edge->edge_id);
6218 if ( ee1->edge->edge_id != edge->edge_id )
6219 {
6220 selEdge.edge_id = ee1->edge->edge_id;
6221 if ( ee1->outgoing ) {
6222 updatedEdge.next_right = newEdge.edge_id;
6223 updateFlags = LWT_COL_EDGE_NEXT_RIGHT;
6224 } else {
6225 updatedEdge.next_left = newEdge.edge_id;
6226 updateFlags = LWT_COL_EDGE_NEXT_LEFT;
6227 }
6228
6229 LWDEBUGF(1, "Updating edge %" LWTFMT_ELEMID ", %s nextCCW of outgoing snap-edge",
6230 selEdge.edge_id, ee1->outgoing ? "outgoing" : "incoming");
6231 ret = lwt_be_updateEdges(topo,
6232 &selEdge, LWT_COL_EDGE_EDGE_ID,
6233 &updatedEdge, updateFlags,
6234 NULL, 0);
6235 if ( ret == -1 ) {
6237 return -1;
6238 } else if ( ret == 0 ) {
6239 lwerror("Edge being split (%" LWTFMT_ELEMID ") disappeared during operations?", selEdge.edge_id);
6240 return -1;
6241 } else if ( ret > 1 ) {
6242 lwerror("More than a single edge found with id %"
6243 LWTFMT_ELEMID " !", selEdge.edge_id);
6244 return -1;
6245 }
6246 }
6247
6248 /* If any edge was connected to the end node of the split edge, we
6249 * need to update its link to us */
6250 updateFlags = 0;
6251 for ( uint64_t t=0; t<lastNodeEdges->numEdges; t++ )
6252 {
6253 const LWT_ISO_EDGE *in = &(lastNodeEdges->edges[t]);
6254 if ( in->next_right == -edge->edge_id )
6255 {
6256 if ( in->edge_id != edge->edge_id ) {
6257 selEdge.edge_id = in->edge_id;
6258 updatedEdge.next_right = -newEdge.edge_id;
6259 updateFlags = LWT_COL_EDGE_NEXT_RIGHT;
6260 }
6261 break;
6262 }
6263 if ( in->next_left == -edge->edge_id )
6264 {
6265 if ( in->edge_id != edge->edge_id ) {
6266 selEdge.edge_id = in->edge_id;
6267 updatedEdge.next_left = -newEdge.edge_id;
6268 updateFlags = LWT_COL_EDGE_NEXT_LEFT;
6269 }
6270 break;
6271 }
6272 }
6273 if ( updateFlags != 0 )
6274 {
6275 LWDEBUGF(1, "Updating edge %" LWTFMT_ELEMID ", %s nextCCW of outgoing snap-edge",
6276 selEdge.edge_id,
6277 updateFlags & LWT_COL_EDGE_NEXT_LEFT ? "incoming" : "outgoing");
6278 ret = lwt_be_updateEdges(topo,
6279 &selEdge, LWT_COL_EDGE_EDGE_ID,
6280 &updatedEdge, updateFlags,
6281 NULL, 0);
6282 if ( ret == -1 ) {
6284 return -1;
6285 } else if ( ret == 0 ) {
6286 lwerror("Edge being split (%" LWTFMT_ELEMID ") disappeared during operations?", selEdge.edge_id);
6287 return -1;
6288 } else if ( ret > 1 ) {
6289 lwerror("More than a single edge found with id %"
6290 LWTFMT_ELEMID " !", selEdge.edge_id);
6291 return -1;
6292 }
6293 }
6294
6295 /* Handle face split */
6296 LWT_ELEMID newFaceId1 = 0;
6297 LWT_ELEMID newFaceId2 = 0;
6298 LWT_ELEMID oldFaceId;
6299 if ( splitFaceOnLeft ) {
6300
6301 oldFaceId = edge->face_left;
6302
6303 LWDEBUGF(1, "Adding face split for left side of new edge %" LWTFMT_ELEMID, newEdge.edge_id);
6304 newFaceId1 = _lwt_AddFaceSplit( topo, newEdge.edge_id, oldFaceId, 0 );
6305 LWDEBUGF(1, "_lwt_AddFaceSplit(%" LWTFMT_ELEMID ") returned %" LWTFMT_ELEMID,
6306 newEdge.edge_id, newFaceId1);
6307
6308 // TODO: retain oldFaceID rather than creating a new one, if a
6309 // new one was already created for the new edge ring !
6310 LWDEBUGF(1, "Adding face split for left side of updated edge %" LWTFMT_ELEMID, edge->edge_id);
6311 newFaceId2 = _lwt_AddFaceSplit( topo, edge->edge_id, oldFaceId, 0 );
6312 LWDEBUGF(1, "_lwt_AddFaceSplit(%" LWTFMT_ELEMID ") returned %" LWTFMT_ELEMID,
6313 edge->edge_id, newFaceId2);
6314
6315
6316 } else {
6317
6318 oldFaceId = edge->face_right;
6319
6320 LWDEBUGF(1, "Adding face split for right side of new edge %" LWTFMT_ELEMID, newEdge.edge_id);
6321 newFaceId1 = _lwt_AddFaceSplit( topo, -newEdge.edge_id, oldFaceId, 0 );
6322 LWDEBUGF(1, "_lwt_AddFaceSplit(%" LWTFMT_ELEMID ") returned %" LWTFMT_ELEMID,
6323 -newEdge.edge_id, newFaceId1);
6324
6325 // TODO: retain oldFaceID rather than creating a new one, if a
6326 // new one was already created on the new edge ring !
6327 LWDEBUGF(1, "Adding face split for right side of updated edge %" LWTFMT_ELEMID, edge->edge_id);
6328 newFaceId2 = _lwt_AddFaceSplit( topo, -edge->edge_id, oldFaceId, 0 );
6329 LWDEBUGF(1, "_lwt_AddFaceSplit(%" LWTFMT_ELEMID ") returned %" LWTFMT_ELEMID,
6330 -edge->edge_id, newFaceId2);
6331
6332 }
6333
6334 /************
6335 *
6336 * Update TopoGeometries composition
6337 *
6338 */
6339
6340 ret = lwt_be_updateTopoGeomEdgeSplit(topo, edge->edge_id, newEdge.edge_id, -1);
6341 if ( ! ret ) {
6343 return -1;
6344 }
6345
6346 LWDEBUGF(1, "New faces %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID
6347 "split from from old face %" LWTFMT_ELEMID,
6348 newFaceId1, newFaceId2, oldFaceId);
6349 if ( newFaceId1 || newFaceId2 )
6350 {
6352 topo, oldFaceId,
6353 newFaceId1 ? newFaceId1 : oldFaceId,
6354 newFaceId2 ? newFaceId2 : oldFaceId
6355 );
6356 if ( ! ret ) {
6358 return -1;
6359 }
6360
6361 if ( newFaceId1 && newFaceId2 )
6362 {
6363 LWDEBUGF(1, "Deleting fully replaced face %" LWTFMT_ELEMID, oldFaceId);
6364 int nids = 1;
6365 ret = lwt_be_deleteFacesById(topo, &oldFaceId, nids);
6366 if ( ret == -1 ) {
6368 return -1;
6369 }
6370 }
6371 }
6372
6373 return 0;
6374 }
6375 else if ( replacedBy[0] != 0 && replacedBy[1] != 0 )
6376 {
6377 // Edge was replaced with two existing edges
6378
6379 // Sanity check
6380 if ( edge->face_right == edge->face_left )
6381 {
6382 lwerror("Unexpectedly, dangling edge %" LWTFMT_ELEMID " was replaced by "
6383 "edges %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID
6384 " when snap to node %" LWTFMT_ELEMID,
6385 edge->edge_id, replacedBy[0], replacedBy[1], node->node_id);
6386 return -1;
6387 }
6388
6389 LWT_ELEMID collapsedFace = 0;
6390 LWT_ELEMID replacingFace = 0;
6391
6392 if ( FP_ABS(edge->next_right) == replacedBy[0] ) {
6393 /* Face on the right of the removed edge disappeared */
6394 // We need to update nextCW on first node and nextCCW on last node
6395 collapsedFace = edge->face_right;
6396 replacingFace = edge->face_left;
6397 } else {
6398 /* Face on the left of the removed edge disappeared */
6399 // We need to update nextCCW on first node and nextCW on last node
6400 collapsedFace = edge->face_left;
6401 replacingFace = edge->face_right;
6402 }
6403
6404 //const LWT_ISO_EDGE *sp_nextCCW = 0;
6405 //const LWT_ISO_EDGE *ep_nextCCW = 0;
6406
6407 // Update CCW edge on first node
6408 updateFlags = 0;
6409 for ( uint64_t t=0; t<firstNodeEdges->numEdges; t++ )
6410 {
6411 const LWT_ISO_EDGE *in = &(firstNodeEdges->edges[t]);
6412 if ( in->edge_id == edge->edge_id ) continue;
6413 if ( in->next_right == edge->edge_id )
6414 {
6415 selEdge.edge_id = in->edge_id;
6416 updatedEdge.next_right = edge->next_right;
6417 updateFlags = LWT_COL_EDGE_NEXT_RIGHT;
6418 if ( in->face_right == collapsedFace ) {
6419 updatedEdge.face_right = replacingFace;
6420 updateFlags |= LWT_COL_EDGE_FACE_RIGHT;
6421 }
6422 //sp_nextCCW = in;
6423 break;
6424 }
6425 if ( in->next_left == edge->edge_id )
6426 {
6427 selEdge.edge_id = in->edge_id;
6428 updatedEdge.next_left = edge->next_right;
6429 updateFlags = LWT_COL_EDGE_NEXT_LEFT;
6430 if ( in->face_left == collapsedFace ) {
6431 updatedEdge.face_left = replacingFace;
6432 updateFlags |= LWT_COL_EDGE_FACE_LEFT;
6433 }
6434 //sp_nextCCW = in;
6435 break;
6436 }
6437 }
6438 if ( updateFlags != 0 )
6439 {
6440 LWDEBUGF(1, "Updating edge %" LWTFMT_ELEMID ", %s nextCCW of outgoing snap-edge",
6441 selEdge.edge_id,
6442 updateFlags & LWT_COL_EDGE_NEXT_LEFT ? "incoming" : "outgoing");
6443 ret = lwt_be_updateEdges(topo,
6444 &selEdge, LWT_COL_EDGE_EDGE_ID,
6445 &updatedEdge, updateFlags,
6446 NULL, 0);
6447 if ( ret == -1 ) {
6449 return -1;
6450 } else if ( ret == 0 ) {
6451 lwerror("Edge being split (%" LWTFMT_ELEMID ") disappeared during operations?", selEdge.edge_id);
6452 return -1;
6453 } else if ( ret > 1 ) {
6454 lwerror("More than a single edge found with id %"
6455 LWTFMT_ELEMID " !", selEdge.edge_id);
6456 return -1;
6457 }
6458 }
6459
6460 // Update CCW edge on last node
6461 updateFlags = 0;
6462 for ( uint64_t t=0; t<lastNodeEdges->numEdges; t++ )
6463 {
6464 const LWT_ISO_EDGE *in = &(lastNodeEdges->edges[t]);
6465 if ( in->edge_id == edge->edge_id ) continue;
6466 if ( in->next_right == -edge->edge_id )
6467 {
6468 selEdge.edge_id = in->edge_id;
6469 updatedEdge.next_right = edge->next_left;
6470 updateFlags = LWT_COL_EDGE_NEXT_RIGHT;
6471 if ( in->face_right == collapsedFace ) {
6472 updatedEdge.face_right = replacingFace;
6473 updateFlags |= LWT_COL_EDGE_FACE_RIGHT;
6474 }
6475 break;
6476 }
6477 if ( in->next_left == -edge->edge_id )
6478 {
6479 selEdge.edge_id = in->edge_id;
6480 updatedEdge.next_left = edge->next_left;
6481 updateFlags = LWT_COL_EDGE_NEXT_LEFT;
6482 if ( in->face_left == collapsedFace ) {
6483 updatedEdge.face_left = replacingFace;
6484 updateFlags |= LWT_COL_EDGE_FACE_LEFT;
6485 }
6486 break;
6487 }
6488 }
6489 if ( updateFlags != 0 )
6490 {
6491 LWDEBUGF(1, "Updating edge %" LWTFMT_ELEMID ", %s nextCCW of incoming snap-edge",
6492 selEdge.edge_id,
6493 updateFlags & LWT_COL_EDGE_NEXT_LEFT ? "incoming" : "outgoing");
6494 ret = lwt_be_updateEdges(topo,
6495 &selEdge, LWT_COL_EDGE_EDGE_ID,
6496 &updatedEdge, updateFlags,
6497 NULL, 0);
6498 if ( ret == -1 ) {
6500 return -1;
6501 } else if ( ret == 0 ) {
6502 lwerror("Edge being split (%" LWTFMT_ELEMID ") disappeared during operations?", selEdge.edge_id);
6503 return -1;
6504 } else if ( ret > 1 ) {
6505 lwerror("More than a single edge found with id %"
6506 LWTFMT_ELEMID " !", selEdge.edge_id);
6507 return -1;
6508 }
6509 }
6510
6511 if ( collapsedFace == edge->face_right )
6512 {
6513 // We need to update side face of nextCW on first node and nextCCW on last node
6514 // nextCCW on last node was done already, supposedly so we need to do nextCW
6515 // on first
6516 if ( edge->next_right < 0 ) {
6517 selEdge.edge_id = -edge->next_right;
6518 updatedEdge.face_right = replacingFace;
6519 updateFlags = LWT_COL_EDGE_FACE_RIGHT;
6520 } else {
6521 selEdge.edge_id = edge->next_right;
6522 updatedEdge.face_left = replacingFace;
6523 updateFlags = LWT_COL_EDGE_FACE_LEFT;
6524 }
6525 LWDEBUGF(1, "Updating %s face of edge %" LWTFMT_ELEMID ", %s nextCW of outgoing snap-edge",
6526 updateFlags & LWT_COL_EDGE_FACE_LEFT ? "left" : "right",
6527 selEdge.edge_id,
6528 updateFlags & LWT_COL_EDGE_FACE_RIGHT ? "incoming" : "outgoing");
6529 ret = lwt_be_updateEdges(topo,
6530 &selEdge, LWT_COL_EDGE_EDGE_ID,
6531 &updatedEdge, updateFlags,
6532 NULL, 0);
6533 if ( ret == -1 ) {
6535 return -1;
6536 } else if ( ret == 0 ) {
6537 lwerror("Edge being split (%" LWTFMT_ELEMID ") disappeared during operations?", selEdge.edge_id);
6538 return -1;
6539 } else if ( ret > 1 ) {
6540 lwerror("More than a single edge found with id %"
6541 LWTFMT_ELEMID " !", selEdge.edge_id);
6542 return -1;
6543 }
6544 }
6545 else
6546 {
6547 // We need to update side face of nextCCW on first node and nextCW on last node
6548 // nextCCW on first node was done already, supposedly so we need to do nextCW
6549 // on last
6550 if ( edge->next_left < 0 ) {
6551 selEdge.edge_id = -edge->next_left;
6552 updatedEdge.face_right = replacingFace;
6553 updateFlags = LWT_COL_EDGE_FACE_RIGHT;
6554 } else {
6555 selEdge.edge_id = edge->next_left;
6556 updatedEdge.face_left = replacingFace;
6557 updateFlags = LWT_COL_EDGE_FACE_LEFT;
6558 }
6559 LWDEBUGF(1, "Updating %s face of edge %" LWTFMT_ELEMID ", %s nextCW of incoming snap-edge",
6560 updateFlags & LWT_COL_EDGE_FACE_LEFT ? "left" : "right",
6561 selEdge.edge_id,
6562 updateFlags & LWT_COL_EDGE_FACE_RIGHT ? "incoming" : "outgoing");
6563 ret = lwt_be_updateEdges(topo,
6564 &selEdge, LWT_COL_EDGE_EDGE_ID,
6565 &updatedEdge, updateFlags,
6566 NULL, 0);
6567 if ( ret == -1 ) {
6569 return -1;
6570 } else if ( ret == 0 ) {
6571 lwerror("Edge being split (%" LWTFMT_ELEMID ") disappeared during operations?", selEdge.edge_id);
6572 return -1;
6573 } else if ( ret > 1 ) {
6574 lwerror("More than a single edge found with id %"
6575 LWTFMT_ELEMID " !", selEdge.edge_id);
6576 return -1;
6577 }
6578 }
6579
6580 /* Delete the old edge */
6581 selEdge.edge_id = edge->edge_id;
6582 LWDEBUGF(1, "Deleting edge %" LWTFMT_ELEMID, selEdge.edge_id);
6583 ret = lwt_be_deleteEdges(topo, &selEdge, LWT_COL_EDGE_EDGE_ID);
6584 if ( ret == -1 ) {
6586 return -1;
6587 }
6588
6589 /* Delete collapsed face */
6590 LWDEBUGF(1, "Deleting collapsed face %" LWTFMT_ELEMID, collapsedFace);
6591 int nids = 1;
6592 ret = lwt_be_deleteFacesById(topo, &collapsedFace, nids);
6593 if ( ret == -1 ) {
6595 return -1;
6596 }
6597
6598 /* Delete the collapsed face from composition of TopoGeometries */
6599 if ( ! lwt_be_updateTopoGeomFaceHeal( topo, collapsedFace, -1, -1) )
6600 {
6602 return -1;
6603 }
6604
6605 return 0;
6606 }
6607
6608 //LWDEBUGF(1, "Not handling within-tolerance edge %" LWTFMT_ELEMID, edge->edge_id);
6609 lwerror("Don't know how to deal with edge %" LWTFMT_ELEMID
6610 " replaced by edges %" LWTFMT_ELEMID
6611 " and %" LWTFMT_ELEMID, edge->edge_id, replacedBy[0], replacedBy[1]);
6612
6613 return -1;
6614}
6615
6616/*
6617 *
6618 * Move the given point as close as possible to the
6619 * closest edge, snap all edges within tolerance to
6620 * it and split them accordingly.
6621 *
6622 * @param edges array of ISO edges with all attributes
6623 * @param num number of items in edges
6624 * @param point the point of the new node
6625 * @param moved if not-null will be set to 0 if the point was added
6626 * w/out any snapping or 1 otherwise.
6627 *
6628 * @return id of the new node, 0 if no edge was found below tolerance or -1 on error
6629 *
6630 */
6631static LWT_ELEMID
6632_lwt_SplitAllEdgesToNewNode(LWT_TOPOLOGY* topo, LWT_ISO_EDGE *edges, uint64_t num, LWPOINT *point, double tol, int *moved)
6633{
6634 uint64_t i, j;
6635 scored_pointer *sorted = lwalloc(sizeof(scored_pointer)*num);
6636 LWT_ISO_EDGE *edges2 = NULL;
6637 LWT_ISO_NODE node;
6638 node.node_id = 0; /* unneeded, but hushes a compiler warning */
6639 LWGEOM *prj; /* incoming point projected to nearest node or edge */
6640 const POINT2D *pt = NULL; /* projected 2d point */
6641 LWGEOM *lwpoint = lwpoint_as_lwgeom(point);
6642
6643 j = 0;
6644 for (i=0; i<num; ++i)
6645 {
6646 double dist;
6647 LWT_ISO_EDGE *e = edges+i;
6648 /* TODO: use square distance */
6649 dist = lwgeom_mindistance2d(lwline_as_lwgeom(edges[i].geom), lwpoint_as_lwgeom(point));
6650 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " distance: %.15g", e->edge_id, dist);
6651 if ( dist >= tol ) continue;
6652 sorted[j].ptr = e;
6653 sorted[j++].score = dist;
6654 }
6655 if ( ! j )
6656 {
6657 lwfree(sorted);
6658 return 0;
6659 }
6660
6661 num = j;
6662 qsort(sorted, num, sizeof(scored_pointer), compare_scored_pointer);
6663 edges2 = lwalloc(sizeof(LWT_ISO_EDGE)*num);
6664 for (j=0, i=0; i<num; ++i)
6665 {
6666 if ( sorted[i].score == sorted[0].score )
6667 {
6668 edges2[j++] = *((LWT_ISO_EDGE*)sorted[i].ptr);
6669 }
6670 else
6671 {
6672 edges2[j++] = *((LWT_ISO_EDGE*)sorted[i].ptr);
6673 //lwline_free(((LWT_ISO_EDGE*)sorted[i].ptr)->geom);
6674 }
6675 }
6676 num = j;
6677 lwfree(sorted);
6678 edges = edges2;
6679
6680 /* project point to closest edge */
6681 prj = lwgeom_closest_point(lwline_as_lwgeom(edges[0].geom), lwpoint);
6682 if ( moved ) *moved = lwgeom_same(prj, lwpoint) ? 0 : 1;
6683 if ( lwgeom_has_z(lwpoint) )
6684 {{
6685 /*
6686 -- This is a workaround for ClosestPoint lack of Z support:
6687 -- http://trac.osgeo.org/postgis/ticket/2033
6688 */
6689 LWGEOM *tmp;
6690 double z;
6691 POINT4D p4d;
6692 LWPOINT *prjpt;
6693 /* add Z to "prj" */
6694 tmp = lwgeom_force_3dz(prj, 0);
6695 prjpt = lwgeom_as_lwpoint(tmp);
6696 getPoint4d_p(point->point, 0, &p4d);
6697 z = p4d.z;
6698 getPoint4d_p(prjpt->point, 0, &p4d);
6699 p4d.z = z;
6700 ptarray_set_point4d(prjpt->point, 0, &p4d);
6701 lwgeom_free(prj);
6702 prj = tmp;
6703 }}
6704
6705 LWDEBUGG(1, prj, "Projected point");
6706 node.geom = lwgeom_as_lwpoint(prj);
6707 node.containing_face = -1; /* means not-isolated */
6708
6709 for (i=0; i<num; ++i)
6710 {
6711 /* The point is on or near one or more edges,
6712 * project it to closest edge and then split all
6713 * edges to it
6714 */
6715
6716 LWT_ISO_EDGE *e = &(edges[i]);
6717 LWGEOM *g = lwline_as_lwgeom(e->geom);
6718 int contains;
6719 LWT_ELEMID edge_id = e->edge_id;
6720
6721 if ( i )
6722 {{
6723 int ret = _lwt_SnapEdgeToExistingNode( topo, &node, e, tol );
6724 if ( ret != 0 ) {
6725 lwerror("Something went wrong with _lwt_SnapEdgeToExistingNode");
6726 return -1;
6727 }
6728 continue;
6729 }}
6730
6731 LWDEBUGF(1, "Splitting closest edge %" LWTFMT_ELEMID, edge_id);
6732
6733 pt = getPoint2d_cp(lwgeom_as_lwpoint(prj)->point, 0);
6735 if ( ! contains )
6736 {{
6737 LWGEOM *snapedge;
6738 LWLINE *snapline;
6739 POINT4D p1, p2;
6740
6741 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID
6742 " does not contain projected point to it",
6743 edge_id);
6744
6745#if 0
6746 /* In order to reduce the robustness issues, we'll pick
6747 * an edge that contains the projected point, if possible */
6748 if ( i+1 < num )
6749 {
6750 LWDEBUG(1, "But there's another to check");
6751 //lwgeom_free(prj);
6752 continue;
6753 }
6754#endif
6755
6756 snapedge = _lwt_toposnap(g, prj, tol);
6757 snapline = lwgeom_as_lwline(snapedge);
6758
6759 LWDEBUGF(1, "Edge snapped with tolerance %g", tol);
6760
6761 /* TODO: check if snapping did anything ? */
6762#if POSTGIS_DEBUG_LEVEL > 0
6763 {
6764 size_t sz;
6765 char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
6766 char *wkt2 = lwgeom_to_wkt(snapedge, WKT_EXTENDED, 15, &sz);
6767 LWDEBUGF(1, "Edge %s snapped became %s", wkt1, wkt2);
6768 lwfree(wkt1);
6769 lwfree(wkt2);
6770 }
6771#endif
6772
6773
6774 /*
6775 -- Snapping currently snaps the first point below tolerance
6776 -- so may possibly move first point. See ticket #1631
6777 */
6778 getPoint4d_p(e->geom->points, 0, &p1);
6779 getPoint4d_p(snapline->points, 0, &p2);
6780 LWDEBUGF(1, "Edge first point is %g %g, "
6781 "snapline first point is %g %g",
6782 p1.x, p1.y, p2.x, p2.y);
6783 if ( p1.x != p2.x || p1.y != p2.y )
6784 {
6785 LWDEBUG(1, "Snapping moved first point, re-adding it");
6786 if ( LW_SUCCESS != ptarray_insert_point(snapline->points, &p1, 0) )
6787 {
6788 lwgeom_free(prj);
6789 lwgeom_free(snapedge);
6790 lwerror("GEOS exception on Contains: %s", lwgeom_geos_errmsg);
6791 return -1;
6792 }
6793#if POSTGIS_DEBUG_LEVEL > 0
6794 {
6795 size_t sz;
6796 char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
6797 LWDEBUGF(1, "Tweaked snapline became %s", wkt1);
6798 lwfree(wkt1);
6799 }
6800#endif
6801 }
6802#if POSTGIS_DEBUG_LEVEL > 0
6803 else {
6804 LWDEBUG(1, "Snapping did not move first point");
6805 }
6806#endif
6807
6808 if ( -1 == lwt_ChangeEdgeGeom( topo, edge_id, snapline ) )
6809 {
6810 /* TODO: should have invoked lwerror already, leaking memory */
6811 lwgeom_free(prj);
6812 lwgeom_free(snapedge);
6813 lwerror("lwt_ChangeEdgeGeom failed");
6814 return -1;
6815 }
6816 lwgeom_free(snapedge);
6817 }}
6818#if POSTGIS_DEBUG_LEVEL > 0
6819 else
6820 {{
6821 size_t sz;
6822 char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
6823 char *wkt2 = lwgeom_to_wkt(prj, WKT_EXTENDED, 15, &sz);
6824 LWDEBUGF(1, "Edge %s contains projected point %s", wkt1, wkt2);
6825 lwfree(wkt1);
6826 lwfree(wkt2);
6827 }}
6828#endif
6829
6830 /* TODO: pass 1 as last argument (skipChecks) ? */
6831 node.node_id = lwt_ModEdgeSplit( topo, edge_id, node.geom, 0 );
6832 if ( -1 == node.node_id )
6833 {
6834 /* TODO: should have invoked lwerror already, leaking memory */
6835 lwgeom_free(prj);
6836 lwerror("lwt_ModEdgeSplit failed");
6837 return -1;
6838 }
6839
6840 /*
6841 * TODO: decimate the two new edges with the given tolerance ?
6842 *
6843 * the edge identifiers to decimate would be: edge_id and "id"
6844 * The problem here is that decimation of existing edges
6845 * may introduce intersections or topological inconsistencies,
6846 * for example:
6847 *
6848 * - A node may end up falling on the other side of the edge
6849 * - The decimated edge might intersect another existing edge
6850 *
6851 */
6852
6853 }
6854
6855 lwgeom_free(prj);
6856
6857 if ( edges2 ) lwfree(edges2);
6858 return node.node_id;
6859}
6860
6861/*
6862 * @param findFace if non-zero the code will determine which face
6863 * contains the given point (unless it is known to be NOT
6864 * isolated)
6865 * @param moved if not-null will be set to 0 if the point was added
6866 * w/out any snapping or 1 otherwise.
6867 * @param numSplitEdges if not-null will be set to the number of edges
6868 * split by this point.
6869 */
6870static LWT_ELEMID
6871_lwt_AddPoint(LWT_TOPOLOGY* topo, LWPOINT* point, double tol, int
6872 findFace, int *moved, int *numSplitEdges)
6873{
6874 uint64_t num, i;
6875 double mindist = FLT_MAX;
6876 LWT_ISO_NODE *nodes, *nodes2;
6877 LWT_ISO_EDGE *edges;
6878 LWGEOM *pt = lwpoint_as_lwgeom(point);
6879 scored_pointer *sorted;
6880 int flds;
6881 LWT_ELEMID id = 0;
6882
6883 /* Get tolerance, if 0 was given */
6884 if ( tol == -1 )
6885 tol = _LWT_MINTOLERANCE(topo, pt);
6886
6887 LWDEBUGG(1, pt, "Adding point");
6888
6889 /*
6890 -- 1. Check if any existing node is closer than the given precision
6891 -- and if so pick the closest
6892 TODO: use WithinBox2D
6893 */
6895 nodes = lwt_be_getNodeWithinDistance2D(topo, point, tol, &num, flds, 0);
6896 if (num == UINT64_MAX)
6897 {
6899 return -1;
6900 }
6901
6902 if ( numSplitEdges ) *numSplitEdges = 0;
6903
6904 if ( num )
6905 {
6906 LWDEBUGF(1, "New point is within %.15g units of %llu nodes", tol, num);
6907 /* Order by distance if there are more than a single return */
6908 if ( num > 1 )
6909 {{
6910 sorted = lwalloc(sizeof(scored_pointer)*num);
6911 for (i=0; i<num; ++i)
6912 {
6913 sorted[i].ptr = nodes+i;
6914 sorted[i].score = lwgeom_mindistance2d(lwpoint_as_lwgeom(nodes[i].geom), pt);
6915 LWDEBUGF(1, "Node %" LWTFMT_ELEMID " distance: %.15g",
6916 ((LWT_ISO_NODE*)(sorted[i].ptr))->node_id, sorted[i].score);
6917 }
6918 qsort(sorted, num, sizeof(scored_pointer), compare_scored_pointer);
6919 nodes2 = lwalloc(sizeof(LWT_ISO_NODE)*num);
6920 for (i=0; i<num; ++i)
6921 {
6922 nodes2[i] = *((LWT_ISO_NODE*)sorted[i].ptr);
6923 }
6924 lwfree(sorted);
6925 lwfree(nodes);
6926 nodes = nodes2;
6927 }}
6928
6929 for ( i=0; i<num; ++i )
6930 {
6931 LWT_ISO_NODE *n = &(nodes[i]);
6932 LWGEOM *g = lwpoint_as_lwgeom(n->geom);
6933 double dist = lwgeom_mindistance2d(g, pt);
6934 /* TODO: move this check in the previous sort scan ... */
6935 /* must be closer than tolerated, unless distance is zero */
6936 if ( dist && dist >= tol ) continue;
6937 if ( ! id || dist < mindist )
6938 {
6939 id = n->node_id;
6940 mindist = dist;
6941 }
6942 }
6943 if ( id )
6944 {
6945 /* found an existing node */
6946 if ( nodes ) _lwt_release_nodes(nodes, num);
6947 if ( moved ) *moved = mindist == 0 ? 0 : 1;
6948 return id;
6949 }
6950 }
6951
6952 initGEOS(lwnotice, lwgeom_geos_error);
6953
6954 /*
6955 -- 2. Check if any existing edge falls within tolerance
6956 -- and if so split it by a point projected on it
6957 TODO: use WithinBox2D
6958 */
6959 flds = LWT_COL_EDGE_ALL; /* EDGE_ID|LWT_COL_EDGE_GEOM;*/
6960 edges = lwt_be_getEdgeWithinDistance2D(topo, point, tol, &num, flds, 0);
6961 if (num == UINT64_MAX)
6962 {
6964 return -1;
6965 }
6966 LWDEBUGF(1, "New point is within %.15g units of %llu edges", tol, num);
6967 if ( num )
6968 {
6969 id = _lwt_SplitAllEdgesToNewNode(topo, edges, num, lwgeom_as_lwpoint(pt), tol, moved);
6970 _lwt_release_edges(edges, num);
6971 if ( numSplitEdges ) *numSplitEdges = num;
6972 }
6973
6974 if ( id == 0 )
6975 {
6976 /* The point is isolated, add it as such */
6977 /* TODO: pass 1 as last argument (skipChecks) ? */
6978 id = _lwt_AddIsoNode(topo, -1, point, 0, findFace);
6979 if ( moved ) *moved = 0;
6980 if ( -1 == id )
6981 {
6982 /* should have invoked lwerror already, leaking memory */
6983 _lwt_release_edges(edges, num);
6984 lwerror("lwt_AddIsoNode failed");
6985 return -1;
6986 }
6987 }
6988
6989 return id;
6990}
6991
6993lwt_AddPoint(LWT_TOPOLOGY* topo, LWPOINT* point, double tol)
6994{
6995 return _lwt_AddPoint(topo, point, tol, 1, NULL, NULL);
6996}
6997
6998/*
6999 * Add a pre-noded pre-split line edge. Used by lwt_AddLine
7000 * Return edge id, 0 if none added (empty edge), -1 on error
7001 *
7002 * @param handleFaceSplit if non-zero the code will check
7003 * if the newly added edge would split a face and if so
7004 * would create new faces accordingly. Otherwise it will
7005 * set left_face and right_face to null (-1)
7006 *
7007 * @param forward output parameter, will be set to 1
7008 * if the returned edge has the same direction
7009 * as the requested line and 0 if it goes the
7010 * opposite direction. For new edges this will
7011 * always be 1.
7012 *
7013 * @param numNewEdges output parameter, if not-null will be set to
7014 * the number of new edges resulting from this
7015 * incoming new edge, taking into account edges
7016 * created due to splitting of existing edges.
7017 *
7018 */
7019static LWT_ELEMID
7020_lwt_AddLineEdge( LWT_TOPOLOGY* topo, LWLINE* edge, double tol,
7021 int handleFaceSplit, int *forward, int *numNewEdges )
7022{
7023 LWCOLLECTION *col;
7024 LWPOINT *start_point, *end_point;
7025 LWGEOM *tmp = 0, *tmp2;
7026 LWT_ISO_NODE *node;
7027 LWT_ELEMID nid[2]; /* start_node, end_node */
7028 LWT_ELEMID id; /* edge id */
7029 POINT4D p4d;
7030 uint64_t nn, i;
7031 int moved=0, mm;
7032 int pointSplitEdges = -666;
7033
7034 if ( numNewEdges ) *numNewEdges = 0;
7035
7036 LWDEBUGG(1, lwline_as_lwgeom(edge), "_lwtAddLineEdge");
7037 LWDEBUGF(1, "_lwtAddLineEdge with tolerance %g", tol);
7038
7039 start_point = lwline_get_lwpoint(edge, 0);
7040 if ( ! start_point )
7041 {
7042 lwnotice("Empty component of noded line");
7043 return 0; /* must be empty */
7044 }
7045 nid[0] = _lwt_AddPoint( topo, start_point,
7047 handleFaceSplit, &mm, &pointSplitEdges );
7048 lwpoint_free(start_point); /* too late if lwt_AddPoint calls lwerror */
7049 if ( nid[0] == -1 ) return -1; /* lwerror should have been called */
7050 if ( numNewEdges ) *numNewEdges += pointSplitEdges;
7051 moved += mm;
7052 LWDEBUGF(1, "node for start point added or found to be %" LWTFMT_ELEMID " (moved ? %d; split %d edges)", nid[0], mm, pointSplitEdges);
7053
7054
7055 end_point = lwline_get_lwpoint(edge, edge->points->npoints-1);
7056 if ( ! end_point )
7057 {
7058 lwerror("could not get last point of line "
7059 "after successfully getting first point !?");
7060 return -1;
7061 }
7062 nid[1] = _lwt_AddPoint( topo, end_point,
7064 handleFaceSplit, &mm, &pointSplitEdges );
7065 lwpoint_free(end_point); /* too late if lwt_AddPoint calls lwerror */
7066 if ( nid[1] == -1 ) return -1; /* lwerror should have been called */
7067 if ( numNewEdges ) *numNewEdges += pointSplitEdges;
7068 moved += mm;
7069 LWDEBUGF(1, "node for end point added or found to be %" LWTFMT_ELEMID " (moved ? %d; split %d edges)", nid[1], mm, pointSplitEdges);
7070
7071 /*
7072 -- Added endpoints may have drifted due to tolerance, so
7073 -- we need to re-snap the edge to the new nodes before adding it
7074 */
7075 if ( moved )
7076 {
7077
7078 LWDEBUG(1, "One or both line endpoints moved by snap, updating line");
7079
7080 nn = nid[0] == nid[1] ? 1 : 2;
7081 node = lwt_be_getNodeById( topo, nid, &nn,
7083 if (nn == UINT64_MAX)
7084 {
7086 return -1;
7087 }
7088 start_point = NULL; end_point = NULL;
7089 for (i=0; i<nn; ++i)
7090 {
7091 if ( node[i].node_id == nid[0] ) start_point = node[i].geom;
7092 if ( node[i].node_id == nid[1] ) end_point = node[i].geom;
7093 }
7094 if ( ! start_point || ! end_point )
7095 {
7096 if ( nn ) _lwt_release_nodes(node, nn);
7097 lwerror("Could not find just-added nodes % " LWTFMT_ELEMID
7098 " and %" LWTFMT_ELEMID, nid[0], nid[1]);
7099 return -1;
7100 }
7101
7102 /* snap */
7103
7104 getPoint4d_p( start_point->point, 0, &p4d );
7105 lwline_setPoint4d(edge, 0, &p4d);
7106
7107 getPoint4d_p( end_point->point, 0, &p4d );
7108 lwline_setPoint4d(edge, edge->points->npoints-1, &p4d);
7109
7110 if ( nn ) _lwt_release_nodes(node, nn);
7111
7112 LWDEBUGG(2, lwline_as_lwgeom(edge), "Snapped after drifted endpoints snap");
7113
7114 /* make valid, after snap (to handle collapses) */
7116
7117 LWDEBUGG(2, tmp, "Made-valid after snap to drifted endpoints");
7118
7119 col = lwgeom_as_lwcollection(tmp);
7120 if ( col )
7121 {{
7122
7124
7125 /* Check if the so-snapped edge collapsed (see #1650) */
7126 if ( colex->ngeoms == 0 )
7127 {
7128 lwcollection_free(colex);
7129 lwgeom_free(tmp);
7130 LWDEBUG(1, "Made-valid snapped edge collapsed");
7131 return 0;
7132 }
7133
7134 tmp2 = lwgeom_clone_deep(colex->geoms[0]);
7135 lwgeom_free(tmp);
7136 tmp = tmp2;
7137 edge = lwgeom_as_lwline(tmp);
7138 lwcollection_free(colex);
7139 if ( ! edge )
7140 {
7141 /* should never happen */
7142 lwerror("lwcollection_extract(LINETYPE) returned a non-line?");
7143 return -1;
7144 }
7145 }}
7146 else
7147 {
7148 edge = lwgeom_as_lwline(tmp);
7149 if ( ! edge )
7150 {
7151 LWDEBUGF(1, "Made-valid snapped edge collapsed to %s",
7153 lwgeom_free(tmp);
7154 return 0;
7155 }
7156 }
7157
7158 }
7159
7160 /* check if the so-snapped edge _now_ exists */
7161 id = _lwt_GetEqualEdge ( topo, edge, forward );
7162 LWDEBUGF(1, "_lwt_GetEqualEdge returned %" LWTFMT_ELEMID, id);
7163 if ( id == -1 )
7164 {
7165 if ( tmp ) lwgeom_free(tmp); /* probably too late, due to internal lwerror */
7166 return -1;
7167 }
7168 if ( id )
7169 {
7170 if ( tmp ) lwgeom_free(tmp); /* possibly takes "edge" down with it */
7171 return id;
7172 }
7173
7174 /* No previously existing edge was found, we'll check after
7175 * decimating, if a tolerance was given */
7176
7177 /* Remove consecutive vertices below given tolerance
7178 * on edge addition */
7179 if ( tol )
7180 {{
7181 tmp2 = lwline_remove_repeated_points(edge, tol);
7182 LWDEBUGG(2, tmp2, "Repeated-point removed");
7183 edge = lwgeom_as_lwline(tmp2);
7184 if ( tmp ) lwgeom_free(tmp);
7185 tmp = tmp2;
7186
7187 /* check if the so-decimated edge collapsed to single-point */
7188 if ( nid[0] == nid[1] && edge->points->npoints == 2 )
7189 {
7190 lwgeom_free(tmp);
7191 LWDEBUG(1, "Repeated-point removed edge collapsed");
7192 return 0;
7193 }
7194
7195 /* check if the so-decimated edge _now_ exists */
7196 id = _lwt_GetEqualEdge ( topo, edge, forward );
7197 LWDEBUGF(1, "_lwt_GetEqualEdge returned %" LWTFMT_ELEMID, id);
7198 if ( id == -1 )
7199 {
7200 lwgeom_free(tmp); /* probably too late, due to internal lwerror */
7201 return -1;
7202 }
7203 if ( id )
7204 {
7205 lwgeom_free(tmp); /* takes "edge" down with it */
7206 return id;
7207 }
7208 }}
7209
7210 /* No previously existing edge was found, we'll add one */
7211
7212 /* TODO: skip checks ? */
7213 id = _lwt_AddEdge( topo, nid[0], nid[1], edge, 0, handleFaceSplit ? 1 : -1 );
7214 LWDEBUGF(1, "lwt_AddEdgeModFace returned %" LWTFMT_ELEMID, id);
7215 if ( id == -1 )
7216 {
7217 lwgeom_free(tmp); /* probably too late, due to internal lwerror */
7218 return -1;
7219 }
7220 lwgeom_free(tmp); /* possibly takes "edge" down with it */
7221
7222 if ( numNewEdges ) ++*numNewEdges;
7223 *forward = 1;
7224
7225 return id;
7226}
7227
7228/* Simulate split-loop as it was implemented in pl/pgsql version
7229 * of TopoGeo_addLinestring */
7230static LWGEOM *
7231_lwt_split_by_nodes(const LWGEOM *g, const LWGEOM *nodes)
7232{
7234 uint32_t i;
7235 LWGEOM *bg;
7236
7237 bg = lwgeom_clone_deep(g);
7238 if ( ! col->ngeoms ) return bg;
7239
7240 for (i=0; i<col->ngeoms; ++i)
7241 {
7242 LWGEOM *g2;
7243 g2 = lwgeom_split(bg, col->geoms[i]);
7244 lwgeom_free(bg);
7245 bg = g2;
7246 }
7247 bg->srid = nodes->srid;
7248
7249 return bg;
7250}
7251
7252static LWT_ELEMID*
7253_lwt_AddLine(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges,
7254 int handleFaceSplit, int maxNewEdges)
7255{
7256 LWGEOM *geomsbuf[1];
7257 LWGEOM **geoms;
7258 uint32_t ngeoms;
7259 LWGEOM *noded, *tmp;
7260 LWCOLLECTION *col;
7261 LWT_ELEMID *ids;
7262 LWT_ISO_EDGE *edges;
7263 LWT_ISO_NODE *nodes;
7264 uint64_t num, numedges = 0, numnodes = 0;
7265 int num_new_edges = 0;
7266 uint64_t i;
7267 GBOX qbox;
7268 int forward;
7269 int input_was_closed = 0;
7270 POINT4D originalStartPoint;
7271
7272 if ( lwline_is_empty(line) )
7273 {
7274 *nedges = 0;
7275 return NULL;
7276 }
7277
7278 if ( lwline_is_closed(line) )
7279 {
7280 input_was_closed = 1;
7281 getPoint4d_p( line->points, 0, &originalStartPoint);
7282 LWDEBUGF(1, "Input line is closed, original point is %g,%g", originalStartPoint.x, originalStartPoint.y);
7283 }
7284
7285 *nedges = -1; /* error condition, by default */
7286
7287 /* Get tolerance, if 0 was given */
7288 if ( tol == -1 ) tol = _LWT_MINTOLERANCE( topo, (LWGEOM*)line );
7289 LWDEBUGF(1, "Working tolerance:%.15g", tol);
7290 LWDEBUGF(1, "Input line has srid=%d", line->srid);
7291
7292 /* Remove consecutive vertices below given tolerance upfront */
7293 if ( tol )
7294 {{
7296 tmp = lwline_as_lwgeom(clean); /* NOTE: might collapse to non-simple */
7297 LWDEBUGG(1, tmp, "Repeated-point removed");
7298 }} else tmp=(LWGEOM*)line;
7299
7300 /* 1. Self-node */
7301 noded = lwgeom_node((LWGEOM*)tmp);
7302 if ( tmp != (LWGEOM*)line ) lwgeom_free(tmp);
7303 if ( ! noded ) return NULL; /* should have called lwerror already */
7304 LWDEBUGG(1, noded, "Noded");
7305
7306 qbox = *lwgeom_get_bbox( lwline_as_lwgeom(line) );
7307 LWDEBUGF(1, "Line BOX is %.15g %.15g, %.15g %.15g", qbox.xmin, qbox.ymin,
7308 qbox.xmax, qbox.ymax);
7309 gbox_expand(&qbox, tol);
7310 LWDEBUGF(1, "BOX expanded by %g is %.15g %.15g, %.15g %.15g",
7311 tol, qbox.xmin, qbox.ymin, qbox.xmax, qbox.ymax);
7312
7313 LWGEOM **nearby = 0;
7314 int nearbyindex = 0;
7315 int nearbycount = 0;
7316
7317 /* 2.0. Find edges falling within tol distance */
7318 edges = lwt_be_getEdgeWithinBox2D( topo, &qbox, &numedges, LWT_COL_EDGE_ALL, 0 );
7319 if (numedges == UINT64_MAX)
7320 {
7321 lwgeom_free(noded);
7323 return NULL;
7324 }
7325 LWDEBUGF(1, "Line has %u points, its bbox intersects %llu edges bboxes",
7326 line->points->npoints, numedges);
7327 if ( numedges )
7328 {{
7329 /* TODO: compute earlier for reuse later ? */
7330 GEOSGeometry *noded_g = LWGEOM2GEOS(noded, 0);
7331 /* collect those whose distance from us is < tol */
7332 nearbycount += numedges;
7333 nearby = lwalloc(numedges * sizeof(LWGEOM *));
7334 for (i=0; i<numedges; ++i)
7335 {{
7336 LW_ON_INTERRUPT(return NULL);
7337 LWT_ISO_EDGE *e = &(edges[i]);
7338 LWGEOM *g = lwline_as_lwgeom(e->geom);
7339 GEOSGeometry *edge_g = LWGEOM2GEOS(g, 0);
7340 LWDEBUGF(2, "Computing distance from edge %" LWTFMT_ELEMID " with %u points", i, e->geom->points->npoints);
7341 double dist;
7342 if ( 0 == GEOSDistanceIndexed(edge_g, noded_g, &dist) ) {
7343 GEOSGeom_destroy(edge_g);
7344 GEOSGeom_destroy(noded_g);
7345 lwgeom_free(noded);
7346 lwerror("GEOSDistanceIndexed error: %s", lwgeom_geos_errmsg);
7347 return NULL;
7348 }
7349 GEOSGeom_destroy(edge_g);
7350 if ( dist && dist >= tol ) continue;
7351 nearby[nearbyindex++] = g;
7352 }}
7353 LWDEBUGF(1, "Found %d edges closer than tolerance (%g)", nearbyindex, tol);
7354 GEOSGeom_destroy(noded_g);
7355 }}
7356 int nearbyedgecount = nearbyindex;
7357
7358 /* 2.1. Find isolated nodes falling within tol distance
7359 *
7360 * TODO: add backend-interface support for only getting isolated nodes
7361 */
7362 nodes = lwt_be_getNodeWithinBox2D( topo, &qbox, &numnodes, LWT_COL_NODE_ALL, 0 );
7363 if (numnodes == UINT64_MAX)
7364 {
7365 lwgeom_free(noded);
7367 return NULL;
7368 }
7369 LWDEBUGF(1, "Line bbox intersects %llu nodes bboxes", numnodes);
7370 if ( numnodes )
7371 {{
7372 /* collect those whose distance from us is < tol */
7373 nearbycount = nearbyedgecount + numnodes;
7374 nearby = nearby ?
7375 lwrealloc(nearby, nearbycount * sizeof(LWGEOM *))
7376 :
7377 lwalloc(nearbycount * sizeof(LWGEOM *))
7378 ;
7379 int nn = 0;
7380 for (i=0; i<numnodes; ++i)
7381 {
7382 LWT_ISO_NODE *n = &(nodes[i]);
7383 if ( n->containing_face == -1 ) continue; /* skip not-isolated nodes */
7384 LWGEOM *g = lwpoint_as_lwgeom(n->geom);
7385 double dist = lwgeom_mindistance2d(g, noded);
7386 /* must be closer than tolerated, unless distance is zero */
7387 if ( dist && dist >= tol )
7388 {
7389 LWDEBUGF(1, "Node %" LWTFMT_ELEMID " is %g units away, we tolerate only %g", n->node_id, dist, tol);
7390 continue;
7391 }
7392 nearby[nearbyindex++] = g;
7393 nn = nn + 1;
7394 }
7395 LWDEBUGF(1, "Found %d isolated nodes closer than tolerance (%g)", nn, tol);
7396 }}
7397 int nearbynodecount = nearbyindex - nearbyedgecount;
7398 nearbycount = nearbyindex;
7399
7400 LWDEBUGF(1, "Number of nearby elements is %d", nearbycount);
7401
7402 /* 2.2. Snap to nearby elements */
7403 if ( nearbycount )
7404 {{
7405 LWCOLLECTION *col;
7406 LWGEOM *elems;
7407
7409 NULL, nearbycount, nearby);
7410 elems = lwcollection_as_lwgeom(col);
7411
7412 LWDEBUGG(1, elems, "Collected nearby elements");
7413
7414 tmp = _lwt_toposnap(noded, elems, tol);
7415 lwgeom_free(noded);
7416 noded = tmp;
7417 LWDEBUGG(1, noded, "Elements-snapped");
7418 if ( input_was_closed )
7419 {{
7420 /* Recompute start point in case it moved */
7421 LWLINE *scrolled = lwgeom_as_lwline(noded);
7422 if (scrolled)
7423 {
7424 getPoint4d_p( scrolled->points, 0, &originalStartPoint);
7425 LWDEBUGF(1, "Closed input line start point after snap %g,%g", originalStartPoint.x, originalStartPoint.y);
7426 }
7427 }}
7428
7429 /* will not release the geoms array */
7431
7432 /*
7433 -- re-node to account for ST_Snap introduced self-intersections
7434 -- See http://trac.osgeo.org/postgis/ticket/1714
7435 -- TODO: consider running UnaryUnion once after all noding
7436 */
7437 tmp = lwgeom_unaryunion(noded);
7438 lwgeom_free(noded);
7439 noded = tmp;
7440 LWDEBUGG(1, noded, "Unary-unioned");
7441 }}
7442
7443 /* 2.3. Node with nearby edges */
7444 if ( nearbyedgecount )
7445 {{
7446 LWCOLLECTION *col;
7447 LWGEOM *iedges; /* just an alias for col */
7448 LWGEOM *diff, *xset;
7449
7450 LWDEBUGF(1, "Line intersects %d edges", nearbyedgecount);
7451
7453 NULL, nearbyedgecount, nearby);
7454 iedges = lwcollection_as_lwgeom(col);
7455 LWDEBUGG(1, iedges, "Collected edges");
7456
7457 LWDEBUGF(1, "Diffing noded, with srid=%d "
7458 "and intersecting edges, with srid=%d",
7459 noded->srid, iedges->srid);
7460 diff = lwgeom_difference(noded, iedges);
7461 LWDEBUGG(1, diff, "Differenced");
7462
7463 LWDEBUGF(1, "Intersecting noded, with srid=%d "
7464 "and intersecting edges, with srid=%d",
7465 noded->srid, iedges->srid);
7466 xset = lwgeom_intersection(noded, iedges);
7467 LWDEBUGG(1, xset, "Intersected");
7468 lwgeom_free(noded);
7469
7470 /* We linemerge here because INTERSECTION, as of GEOS 3.8,
7471 * will result in shared segments being output as multiple
7472 * lines rather than a single line. Example:
7473
7474 INTERSECTION(
7475 'LINESTRING(0 0, 5 0, 8 0, 10 0,12 0)',
7476 'LINESTRING(5 0, 8 0, 10 0)'
7477 )
7478 ==
7479 MULTILINESTRING((5 0,8 0),(8 0,10 0))
7480
7481 * We will re-split in a subsequent step, by splitting
7482 * the final line with pre-existing nodes
7483 */
7484 LWDEBUG(1, "Linemerging intersection");
7485 tmp = lwgeom_linemerge(xset);
7486 LWDEBUGG(1, tmp, "Linemerged");
7487 lwgeom_free(xset);
7488 xset = tmp;
7489
7490 /*
7491 * Here we union the (linemerged) intersection with
7492 * the difference (new lines)
7493 */
7494 LWDEBUG(1, "Unioning difference and (linemerged) intersection");
7495 noded = lwgeom_union(diff, xset);
7496 LWDEBUGG(1, noded, "Diff-Xset Unioned");
7497 lwgeom_free(xset);
7498 lwgeom_free(diff);
7499
7500 /* will not release the geoms array */
7502
7503 if ( input_was_closed )
7504 {{
7505 LWLINE *scrolled = lwgeom_as_lwline(noded);
7506 if (scrolled) {
7507 if ( lwline_is_closed(scrolled) ) {
7508 ptarray_scroll_in_place(scrolled->points, &originalStartPoint);
7509 }
7510 else {
7511 LWDEBUGG(1, lwline_as_lwgeom(scrolled), "Closed input became non closed");
7512 }
7513 }
7514 else {
7515 LWDEBUGG(1, noded, "Diff-Xset Unioned cannot be scrolled");
7516 }
7517 }}
7518
7519
7520 }}
7521
7522
7523 /* 2.4. Split by pre-existing nodes
7524 *
7525 * Pre-existing nodes are isolated nodes AND endpoints
7526 * of intersecting edges
7527 */
7528 if ( nearbyedgecount )
7529 {
7530 nearbycount += nearbyedgecount * 2; /* make space for endpoints */
7531 nearby = lwrealloc(nearby, nearbycount * sizeof(LWGEOM *));
7532 for (int i=0; i<nearbyedgecount; i++)
7533 {
7534 LWLINE *edge = lwgeom_as_lwline(nearby[i]);
7535 LWPOINT *startNode = lwline_get_lwpoint(edge, 0);
7536 LWPOINT *endNode = lwline_get_lwpoint(edge, edge->points->npoints-1);
7537 /* TODO: only add if within distance to noded AND if not duplicated */
7538 nearby[nearbyindex++] = lwpoint_as_lwgeom(startNode);
7539 nearbynodecount++;
7540 nearby[nearbyindex++] = lwpoint_as_lwgeom(endNode);
7541 nearbynodecount++;
7542 }
7543 }
7544 if ( nearbynodecount )
7545 {
7547 NULL, nearbynodecount,
7548 nearby + nearbyedgecount);
7549 LWGEOM *inodes = lwcollection_as_lwgeom(col);
7550 /* TODO: use lwgeom_split of lwgeom_union ... */
7551 tmp = _lwt_split_by_nodes(noded, inodes);
7552 lwgeom_free(noded);
7553 noded = tmp;
7554 LWDEBUGG(1, noded, "Node-split");
7555 /* will not release the geoms array */
7557 }
7558
7559
7560 LWDEBUG(1, "Freeing up nearby elements");
7561
7562 /* TODO: free up endpoints of nearbyedges */
7563 if ( nearby ) lwfree(nearby);
7564 if ( nodes ) _lwt_release_nodes(nodes, numnodes);
7565 if ( edges ) _lwt_release_edges(edges, numedges);
7566
7567 LWDEBUGG(2, noded, "Finally-noded");
7568
7569 /* 3. For each (now-noded) segment, insert an edge */
7570 col = lwgeom_as_lwcollection(noded);
7571 if ( col )
7572 {
7573 LWDEBUG(1, "Noded line was a collection");
7574 geoms = col->geoms;
7575 ngeoms = col->ngeoms;
7576 }
7577 else
7578 {
7579 LWDEBUG(1, "Noded line was a single geom");
7580 geomsbuf[0] = noded;
7581 geoms = geomsbuf;
7582 ngeoms = 1;
7583 }
7584
7585 LWDEBUGF(1, "Line was split into %d edges", ngeoms);
7586
7587 /* TODO: refactor to first add all nodes (re-snapping edges if
7588 * needed) and then check all edges for existing already
7589 * ( so to save a DB scan for each edge to be added )
7590 */
7591 ids = lwalloc(sizeof(LWT_ELEMID)*ngeoms);
7592 num = 0;
7593 for ( i=0; i<ngeoms; ++i )
7594 {
7595 int edgeNewEdges;
7596 LWT_ELEMID id;
7597 LWGEOM *g = geoms[i];
7598 g->srid = noded->srid;
7599
7600#if POSTGIS_DEBUG_LEVEL > 0
7601 {
7602 size_t sz;
7603 char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
7604 LWDEBUGF(1, "Component %llu of split line is: %s", i, wkt1);
7605 lwfree(wkt1);
7606 }
7607#endif
7608
7609 forward = -1; /* will be set to either 0 or 1 if the edge already existed */
7610 id = _lwt_AddLineEdge( topo, lwgeom_as_lwline(g), tol, handleFaceSplit, &forward, &edgeNewEdges );
7611 num_new_edges += edgeNewEdges;
7612 /* if forward is still == -1 this was NOT an existing edge ? */
7613 if ( forward == -1 )
7614 {
7615 ++num_new_edges;
7616 }
7617
7618 LWDEBUGF(1, "_lwt_AddLineEdge returned %" LWTFMT_ELEMID
7619 " (forward ? %d), reported to create %d new edges (total new edges: %d)",
7620 id, forward, edgeNewEdges, num_new_edges);
7621 if ( id < 0 )
7622 {
7623 lwgeom_free(noded);
7624 lwfree(ids);
7625 return NULL;
7626 }
7627
7628 if ( maxNewEdges >= 0 && num_new_edges > maxNewEdges )
7629 {
7630 lwgeom_free(noded);
7631 lwfree(ids);
7632 lwerror("Adding line to topology requires creating more edges than the requested limit of %d", maxNewEdges);
7633 return NULL;
7634 }
7635
7636 if ( ! id )
7637 {
7638 LWDEBUGF(1, "Component %llu of split line collapsed", i);
7639 continue;
7640 }
7641
7642 LWDEBUGF(1, "Component %llu of split line is %s edge %" LWTFMT_ELEMID,
7643 i, forward ? "forward" : "backward", id);
7644 ids[num++] = forward ? id : -id; /* TODO: skip duplicates */
7645 }
7646
7647 LWDEBUGG(2, noded, "Noded before free");
7648 lwgeom_free(noded);
7649
7650 /* TODO: XXX remove duplicated ids if not done before */
7651
7652 *nedges = num;
7653 return ids;
7654}
7655
7657lwt_AddLine(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges, int max_new_edges)
7658{
7659 return _lwt_AddLine(topo, line, tol, nedges, 1, max_new_edges);
7660}
7661
7663lwt_AddLineNoFace(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges)
7664{
7665 return _lwt_AddLine(topo, line, tol, nedges, 0, -1);
7666}
7667
7668static void
7669lwt_LoadPoint(LWT_TOPOLOGY* topo, LWPOINT* point, double tol)
7670{
7671 _lwt_AddPoint(topo, point, tol, 1, NULL, NULL);
7672}
7673
7674static void
7675lwt_LoadLine(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int max_new_edges)
7676{
7677 LWT_ELEMID* ids;
7678 int nedges;
7679
7680 /* TODO: avoid allocating edge ids */
7681 ids = lwt_AddLine(topo, line, tol, &nedges, max_new_edges);
7682 if ( nedges > 0 ) lwfree(ids);
7683}
7684
7685static void
7686lwt_LoadPolygon(LWT_TOPOLOGY* topo, const LWPOLY* poly, double tol)
7687{
7688 uint32_t i;
7689
7690 /* Add each ring as an edge */
7691 for ( i=0; i<poly->nrings; ++i )
7692 {
7693 LWLINE *line;
7694 POINTARRAY *pa;
7695
7696 /* TODO: avoid the clone here */
7697 pa = ptarray_clone(poly->rings[i]);
7698 line = lwline_construct(topo->srid, NULL, pa);
7699 lwt_LoadLine(topo, line, tol, -1);
7700 lwline_free(line);
7701 }
7702}
7703
7705lwt_AddPolygon(LWT_TOPOLOGY* topo, LWPOLY* poly, double tol, int* nfaces)
7706{
7707 *nfaces = -1; /* error condition, by default */
7708 int num;
7709 LWT_ISO_FACE *faces;
7710 uint64_t nfacesinbox;
7711 uint64_t j;
7712 LWT_ELEMID *ids = NULL;
7713 GBOX qbox;
7714 const GEOSPreparedGeometry *ppoly;
7715 GEOSGeometry *polyg;
7716
7717 /* Nothing to add, in an empty polygon */
7718 if ( lwpoly_is_empty(poly) )
7719 {
7720 *nfaces = 0;
7721 return NULL;
7722 }
7723
7724 /* Get tolerance, if 0 was given */
7725 if ( tol == -1 ) tol = _LWT_MINTOLERANCE( topo, (LWGEOM*)poly );
7726 LWDEBUGF(1, "Working tolerance:%.15g", tol);
7727
7728 lwt_LoadPolygon(topo, poly, tol);
7729
7730 /*
7731 -- Find faces covered by input polygon
7732 -- NOTE: potential snapping changed polygon edges
7733 */
7734 qbox = *lwgeom_get_bbox( lwpoly_as_lwgeom(poly) );
7735 gbox_expand(&qbox, tol);
7736 faces = lwt_be_getFaceWithinBox2D( topo, &qbox, &nfacesinbox,
7737 LWT_COL_FACE_ALL, 0 );
7738 if (nfacesinbox == UINT64_MAX)
7739 {
7740 lwfree(ids);
7742 return NULL;
7743 }
7744
7745 num = 0;
7746 if ( nfacesinbox )
7747 {
7748 polyg = LWGEOM2GEOS(lwpoly_as_lwgeom(poly), 0);
7749 if ( ! polyg )
7750 {
7751 _lwt_release_faces(faces, nfacesinbox);
7752 lwerror("Could not convert poly geometry to GEOS: %s", lwgeom_geos_errmsg);
7753 return NULL;
7754 }
7755 ppoly = GEOSPrepare(polyg);
7756 ids = lwalloc(sizeof(LWT_ELEMID)*nfacesinbox);
7757 for ( j=0; j<nfacesinbox; ++j )
7758 {
7759 LWT_ISO_FACE *f = &(faces[j]);
7760 LWGEOM *fg;
7761 GEOSGeometry *fgg, *sp;
7762 int covers;
7763
7764 /* check if a point on this face surface is covered by our polygon */
7765 fg = lwt_GetFaceGeometry( topo, f->face_id );
7766 if ( ! fg )
7767 {
7768 j = f->face_id; /* so we can destroy faces */
7769 GEOSPreparedGeom_destroy(ppoly);
7770 GEOSGeom_destroy(polyg);
7771 lwfree(ids);
7772 _lwt_release_faces(faces, nfacesinbox);
7773 lwerror("Could not get geometry of face %" LWTFMT_ELEMID, j);
7774 return NULL;
7775 }
7776 /* check if a point on this face's surface is covered by our polygon */
7777 fgg = LWGEOM2GEOS(fg, 0);
7778 lwgeom_free(fg);
7779 if ( ! fgg )
7780 {
7781 GEOSPreparedGeom_destroy(ppoly);
7782 GEOSGeom_destroy(polyg);
7783 _lwt_release_faces(faces, nfacesinbox);
7784 lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
7785 return NULL;
7786 }
7787 sp = GEOSPointOnSurface(fgg);
7788 GEOSGeom_destroy(fgg);
7789 if ( ! sp )
7790 {
7791 GEOSPreparedGeom_destroy(ppoly);
7792 GEOSGeom_destroy(polyg);
7793 _lwt_release_faces(faces, nfacesinbox);
7794 lwerror("Could not find point on face surface: %s", lwgeom_geos_errmsg);
7795 return NULL;
7796 }
7797 covers = GEOSPreparedCovers( ppoly, sp );
7798 GEOSGeom_destroy(sp);
7799 if (covers == 2)
7800 {
7801 GEOSPreparedGeom_destroy(ppoly);
7802 GEOSGeom_destroy(polyg);
7803 _lwt_release_faces(faces, nfacesinbox);
7804 lwerror("PreparedCovers error: %s", lwgeom_geos_errmsg);
7805 return NULL;
7806 }
7807 if ( ! covers )
7808 {
7809 continue; /* we're not composed by this face */
7810 }
7811
7812 /* TODO: avoid duplicates ? */
7813 ids[num++] = f->face_id;
7814 }
7815 GEOSPreparedGeom_destroy(ppoly);
7816 GEOSGeom_destroy(polyg);
7817 _lwt_release_faces(faces, nfacesinbox);
7818 }
7819
7820 /* possibly 0 if non face's surface point was found
7821 * to be covered by input polygon */
7822 *nfaces = num;
7823
7824 return ids;
7825}
7826
7829{
7830 LWT_ISO_EDGE* closestEdge;
7831 LWT_ISO_EDGE* edges;
7832 uint64_t numedges, i;
7833 const POINT2D *queryPoint;
7834 const POINT2D *closestPointOnEdge = NULL;
7835 uint32_t closestSegmentIndex;
7836 int closestSegmentSide;
7837 uint32_t closestPointVertex;
7838 const POINT2D *closestSegmentP0, *closestSegmentP1;
7839 LWT_ELEMID closestNode = 0;
7840 double dist;
7841 LWT_ELEMID containingFace = -1;
7842
7843 closestEdge = lwt_be_getClosestEdge( topo, pt, &numedges,
7850 );
7851 if (numedges == UINT64_MAX)
7852 {
7854 /* cberror(topo->be_data, "Error from cb_getClosestEdge"); */
7855 return -1;
7856 }
7857 if (numedges == 0)
7858 {
7859 /* If there are no edges the point is in the universal face */
7860 return 0;
7861 }
7862
7863 if ( closestEdge->face_left < 0 )
7864 {
7865 lwerror("Closest edge %" LWTFMT_ELEMID " has invalid face %" LWTFMT_ELEMID
7866 " on its left side", closestEdge->edge_id, closestEdge->face_left);
7867 _lwt_release_edges(closestEdge, 1);
7868 return -1;
7869 }
7870
7871 if ( closestEdge->face_right < 0 )
7872 {
7873 lwerror("Closest edge %" LWTFMT_ELEMID " has invalid face %" LWTFMT_ELEMID
7874 " on its right side", closestEdge->edge_id, closestEdge->face_right);
7875 _lwt_release_edges(closestEdge, 1);
7876 return -1;
7877 }
7878
7879 if ( closestEdge->geom->points->npoints < 2 )
7880 {
7881 lwerror("Corrupted topology: geometry of edge %" LWTFMT_ELEMID " is EMPTY",
7882 closestEdge->edge_id);
7883 _lwt_release_edges(closestEdge, 1);
7884 return -1;
7885 }
7886
7887 LWDEBUGGF(2, lwline_as_lwgeom(closestEdge->geom), "Closest edge %" LWTFMT_ELEMID, closestEdge->edge_id);
7888
7889 /* Find closest segment of edge to the point */
7890 queryPoint = getPoint2d_cp(pt->point, 0);
7891 closestSegmentIndex = ptarray_closest_segment_2d(closestEdge->geom->points, queryPoint, &dist);
7892 LWDEBUGF(1, "Closest segment on edge %" LWTFMT_ELEMID " is %d (dist %g)", closestEdge->edge_id, closestSegmentIndex, dist);
7893 closestSegmentP0 = getPoint2d_cp(closestEdge->geom->points, closestSegmentIndex);
7894 closestSegmentP1 = getPoint2d_cp(closestEdge->geom->points, closestSegmentIndex + 1);
7895 LWDEBUGF(1, "Closest segment on edge %" LWTFMT_ELEMID " is LINESTRING(%g %g, %g %g)",
7896 closestEdge->edge_id,
7897 closestSegmentP0->x,
7898 closestSegmentP0->y,
7899 closestSegmentP1->x,
7900 closestSegmentP1->y
7901 );
7902
7903 /*
7904 * We use comp.graphics.algorithms Frequently Asked Questions method
7905 *
7906 * (1) AC dot AB
7907 * r = ----------
7908 * ||AB||^2
7909 * r has the following meaning:
7910 * r=0 P = A
7911 * r=1 P = B
7912 * r<0 P is on the backward extension of AB
7913 * r>1 P is on the forward extension of AB
7914 * 0<r<1 P is interior to AB
7915 *
7916 */
7917 const POINT2D *p = queryPoint;
7918 const POINT2D *A = closestSegmentP0;
7919 const POINT2D *B = closestSegmentP1;
7920 double r = ( (p->x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) );
7921 if ( r <= 0 )
7922 {
7923 closestPointOnEdge = A;
7924 closestPointVertex = closestSegmentIndex;
7925 if ( closestSegmentIndex == 0 )
7926 {
7927 closestNode = closestEdge->start_node;
7928 }
7929 }
7930 else if (r >= 1 )
7931 {
7932 closestPointOnEdge = B;
7933 closestPointVertex = closestSegmentIndex + 1;
7934 if ( closestSegmentIndex + 2 == closestEdge->geom->points->npoints )
7935 {
7936 closestNode = closestEdge->end_node;
7937 }
7938 }
7939 else
7940 {
7941 closestPointVertex = closestEdge->geom->points->npoints;
7942 }
7943
7944 if ( closestNode != 0 )
7945 {
7946 LWDEBUGF(1, "Closest point is node %" LWTFMT_ELEMID, closestNode);
7947 if ( dist == 0 )
7948 {
7949 LWDEBUGF(1, "Query point is node %" LWTFMT_ELEMID, closestNode);
7950 /* Query point is the node
7951 *
7952 * If all edges incident to the node are
7953 * dangling, we can return their common
7954 * side face, otherwise the point will be
7955 * on multiple face boundaries
7956 */
7957 if ( closestEdge->face_left != closestEdge->face_right )
7958 {
7959 _lwt_release_edges(closestEdge, 1);
7960 lwerror("Two or more faces found");
7961 return -1;
7962 }
7963 containingFace = closestEdge->face_left;
7964
7965 /* Check other incident edges */
7966 numedges = 1;
7967 edges = lwt_be_getEdgeByNode( topo, &closestNode, &numedges, LWT_COL_EDGE_FACE_LEFT|LWT_COL_EDGE_FACE_RIGHT );
7968 if (numedges == UINT64_MAX)
7969 {
7971 /* cberror(topo->be_data, "Error from cb_getClosestEdge"); */
7972 _lwt_release_edges(closestEdge, 1);
7973 return -1;
7974 }
7975 for (i=0; i<numedges; ++i)
7976 {
7977 if ( edges[i].face_left != containingFace ||
7978 edges[i].face_right != containingFace )
7979 {
7980 _lwt_release_edges(edges, numedges);
7981 _lwt_release_edges(closestEdge, 1);
7982 lwerror("Two or more faces found");
7983 return -1;
7984 }
7985 }
7986 if (numedges < 1 )
7987 {
7988 lwerror("Unexpected backend return: getEdgeByNode(%" LWTFMT_ELEMID
7989 ") returns no edges when we previously found edge %"
7990 LWTFMT_ELEMID " ending on that node",
7991 closestNode, closestEdge->edge_id);
7992 _lwt_release_edges(edges, numedges);
7993 _lwt_release_edges(closestEdge, 1);
7994 return -1;
7995 }
7996 LWDEBUGF(1, "lwt_be_getEdgeByNode returned %" PRIu64 " edges", numedges);
7997 _lwt_release_edges(edges, numedges);
7998 _lwt_release_edges(closestEdge, 1);
7999 return containingFace;
8000 }
8001
8002 /* Closest point is a node, but query point is NOT on the node */
8003
8004 /* let's do azimuth computation */
8005 edgeend ee;
8006 if ( ! azimuth_pt_pt(closestPointOnEdge, queryPoint, &ee.myaz) ) {
8007 lwerror("error computing azimuth of query point [%.15g %.15g,%.15g %.15g]",
8008 closestPointOnEdge->x, closestPointOnEdge->y,
8009 queryPoint->x, queryPoint->y);
8010 _lwt_release_edges(closestEdge, 1);
8011 return -1;
8012 }
8013
8014 LWDEBUGF(1, "Query point azimuth is %g", ee.myaz);
8015
8016 int found = _lwt_FindAdjacentEdges( topo, closestNode, &ee, NULL, -1 );
8017 if ( ! found ) {
8018 lwerror("Unexpected backend return: _lwt_FindAdjacentEdges(%"
8020 ") found no edges when we previously found edge %"
8021 LWTFMT_ELEMID " ending on that node",
8022 closestNode, closestEdge->edge_id);
8023 _lwt_release_edges(closestEdge, 1);
8024 return -1;
8025 }
8026
8027 _lwt_release_edges(closestEdge, 1);
8028 return ee.cwFace;
8029
8030 }
8031
8032 LWDEBUG(1, "Closest point is NOT a node");
8033
8034 /* If this edge has the same face on the left and right sides
8035 * we found the face containing our query point */
8036 if ( closestEdge->face_left == closestEdge->face_right )
8037 {
8038 LWDEBUGF(1, "Closest point is on a edge with face %" LWTFMT_ELEMID
8039 " on both sides", closestEdge->face_left);
8040
8041 containingFace = closestEdge->face_left;
8042 _lwt_release_edges(closestEdge, 1);
8043 return containingFace;
8044 }
8045
8046 if ( dist == 0 )
8047 {
8048 /* We checked the dangling case above */
8049 _lwt_release_edges(closestEdge, 1);
8050 lwerror("Two or more faces found");
8051 return -1;
8052 }
8053
8054 /* Find on which side of the segment the query point lays */
8055 if ( closestPointVertex != closestEdge->geom->points->npoints )
8056 {
8057 /* Closest point is a vertex of the closest segment */
8058 LWDEBUGF(1, "Closest point is vertex %d of closest segment", closestPointVertex);
8059
8060 /*
8061 * We need to check if rotating clockwise the line
8062 * from previous vertex to closest vertex clockwise
8063 * around the closest vertex encounters
8064 * the line to query point first (which means it's on the left
8065 * of the closest edge) or the line to next vertex first (which
8066 * means the query point is on the right)
8067 */
8068
8069 uint32_t prevVertexIndex = closestPointVertex > 0 ?
8070 closestPointVertex - 1u :
8071 closestEdge->geom->points->npoints - 2u; /* last vertex would be == first vertex, this being a closed edge */
8072
8073 const POINT2D *prevVertex = getPoint2d_cp(
8074 closestEdge->geom->points,
8075 prevVertexIndex
8076 );
8077
8078 LWDEBUGF(1, "Previous vertex %u is POINT(%.15g %.15g)",
8079 prevVertexIndex,
8080 prevVertex->x,
8081 prevVertex->y
8082 );
8083
8084 uint32_t nextVertexIndex = closestPointVertex == closestEdge->geom->points->npoints - 1u ?
8085 1u : /* first point would be == last point, this being a closed edge */
8086 closestPointVertex + 1u;
8087
8088 const POINT2D *nextVertex = getPoint2d_cp(
8089 closestEdge->geom->points,
8090 nextVertexIndex
8091 );
8092
8093 LWDEBUGF(1, "Next vertex %u is POINT(%.15g %.15g)",
8094 nextVertexIndex,
8095 nextVertex->x,
8096 nextVertex->y
8097 );
8098
8099
8100 double azS0; /* azimuth from previous vertex to closestPointVertex */
8101 double azS1; /* azimuth from closestPointVertex to next vertex */
8102 double azSL; /* azimuth from closestPointVertex to query point */
8103
8104 if ( ! azimuth_pt_pt(closestPointOnEdge, prevVertex, &azS0)) {
8105 lwerror("error computing azimuth of segment to closest point [%.15g %.15g,%.15g %.15g]",
8106 closestPointOnEdge->x, closestPointOnEdge->y,
8107 prevVertex->x, prevVertex->y);
8108 _lwt_release_edges(closestEdge, 1);
8109 return -1;
8110 }
8111 if ( ! azimuth_pt_pt(closestPointOnEdge, nextVertex, &azS1)) {
8112 lwerror("error computing azimuth of segment from closest point [%.15g %.15g,%.15g %.15g]",
8113 closestPointOnEdge->x, closestPointOnEdge->y,
8114 nextVertex->x, nextVertex->y);
8115 _lwt_release_edges(closestEdge, 1);
8116 return -1;
8117 }
8118 if ( ! azimuth_pt_pt(closestPointOnEdge, queryPoint, &azSL) ) {
8119 lwerror("error computing azimuth of queryPoint [%.15g %.15g,%.15g %.15g]",
8120 closestPointOnEdge->x, closestPointOnEdge->y,
8121 queryPoint->x, queryPoint->y);
8122 _lwt_release_edges(closestEdge, 1);
8123 return -1;
8124 }
8125
8126 double angle_S0_S1 = azS1 - azS0;
8127 if ( angle_S0_S1 < 0 ) angle_S0_S1 += 2 * M_PI;
8128
8129 double angle_S0_SL = azSL - azS0;
8130 if ( angle_S0_SL < 0 ) angle_S0_SL += 2 * M_PI;
8131
8132 LWDEBUGF(1, "Azimuths from closest (vertex) point: P:%g, N:%g (+%g), Q:%g (+%g)",
8133 azS0,
8134 azS1, angle_S0_S1,
8135 azSL, angle_S0_SL
8136 );
8137 if ( angle_S0_SL < angle_S0_S1 )
8138 {
8139 /* line to query point was encountered first, is on the left */
8140 containingFace = closestEdge->face_left;
8141 }
8142 else
8143 {
8144 /* line to query point was NOT encountered first, is on the right */
8145 containingFace = closestEdge->face_right;
8146 }
8147 }
8148 else
8149 {
8150 /* Closest point is internal to closest segment, we can use
8151 * lw_segment_side */
8152
8153 LWDEBUGF(1, "Closest point is internal to closest segment, calling lw_segment_side((%g,%g),(%g,%g),(%g,%g)",
8154 closestSegmentP0->x,
8155 closestSegmentP0->y,
8156 closestSegmentP1->x,
8157 closestSegmentP1->y,
8158 queryPoint->x,
8159 queryPoint->y
8160 );
8161
8162 closestSegmentSide = lw_segment_side(closestSegmentP0, closestSegmentP1, queryPoint);
8163 LWDEBUGF(1, "Side of closest segment query point falls on: %d (%s)",
8164 closestSegmentSide, closestSegmentSide == -1 ? "left" : closestSegmentSide == 1 ? "right" : "collinear" );
8165
8166 if ( closestSegmentSide == -1 ) /* left */
8167 {
8168 containingFace = closestEdge->face_left;
8169 }
8170 else if ( closestSegmentSide == 1 ) /* right */
8171 {
8172 containingFace = closestEdge->face_right;
8173 }
8174 else
8175 {
8176 lwerror("Unexpected collinearity reported from lw_segment_side");
8177 _lwt_release_edges(closestEdge, 1);
8178 return -1;
8179 }
8180
8181 }
8182
8183 _lwt_release_edges(closestEdge, 1);
8184 return containingFace;
8185}
8186
8187void
8189{
8190 switch (geom->type)
8191 {
8192 case POINTTYPE:
8193 lwt_LoadPoint(topo, lwgeom_as_lwpoint(geom), tol);
8194 return;
8195
8196 case LINETYPE:
8197 lwt_LoadLine(topo, lwgeom_as_lwline(geom), tol, -1);
8198 return;
8199
8200 case POLYGONTYPE:
8201 lwt_LoadPolygon(topo, lwgeom_as_lwpoly(geom), tol);
8202 return;
8203
8204 case MULTILINETYPE:
8205 case MULTIPOLYGONTYPE:
8206 case MULTIPOINTTYPE:
8207 case COLLECTIONTYPE:
8208 {
8209 LWCOLLECTION *coll;
8210 uint32_t i;
8211 coll = (LWCOLLECTION *)geom;
8212 for (i=0; i<coll->ngeoms; i++)
8213 _lwt_LoadGeometryRecursive(topo, coll->geoms[i], tol);
8214 return;
8215 }
8216
8217 default:
8218 {
8219 lwerror("%s: Unsupported geometry type: %s", __func__,
8220 lwtype_name(geom->type));
8221 return;
8222 }
8223
8224 }
8225}
8226
8227void
8228lwt_LoadGeometry(LWT_TOPOLOGY* topo, LWGEOM* geom, double tol)
8229{
8230 _lwt_LoadGeometryRecursive(topo, geom, tol);
8231}
char * r
Definition cu_in_wkt.c:24
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition cu_print.c:267
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
int gbox_contains_point2d(const GBOX *g, const POINT2D *p)
Definition gbox.c:362
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
void lwgeom_refresh_bbox(LWGEOM *lwgeom)
Drop current bbox and calculate a fresh one.
Definition lwgeom.c:735
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:372
char lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
geom1 same as geom2 iff
Definition lwgeom.c:619
#define LW_FALSE
Definition liblwgeom.h:94
LWGEOM * lwgeom_unaryunion(const LWGEOM *geom1)
#define COLLECTIONTYPE
Definition liblwgeom.h:108
int ptarray_closest_segment_2d(const POINTARRAY *pa, const POINT2D *qp, double *dist)
Definition ptarray.c:1452
void * lwrealloc(void *mem, size_t size)
Definition lwutil.c:242
LWGEOM * lwgeom_split(const LWGEOM *lwgeom_in, const LWGEOM *blade_in)
LWGEOM * lwgeom_closest_point(const LWGEOM *lw1, const LWGEOM *lw2)
Definition measures.c:55
int azimuth_pt_pt(const POINT2D *p1, const POINT2D *p2, double *ret)
Compute the azimuth of segment AB in radians.
Definition measures.c:2408
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:1246
LWPOINT * lwline_get_lwpoint(const LWLINE *line, uint32_t where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition lwline.c:319
#define MULTILINETYPE
Definition liblwgeom.h:106
LWPOLY * lwpoly_from_lwlines(const LWLINE *shell, uint32_t nholes, const LWLINE **holes)
Definition lwpoly.c:359
#define LINETYPE
Definition liblwgeom.h:103
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition lwgeom.c:261
#define WKT_EXTENDED
Definition liblwgeom.h:2221
#define LW_SUCCESS
Definition liblwgeom.h:97
LWGEOM * lwgeom_force_3dz(const LWGEOM *geom, double zval)
Definition lwgeom.c:827
char * lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out)
WKT emitter function.
Definition lwout_wkt.c:708
#define MULTIPOINTTYPE
Definition liblwgeom.h:105
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:342
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition lwgeom.c:962
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:102
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:243
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
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:107
uint32_t lwgeom_count_vertices(const LWGEOM *geom)
Count the total number of vertices in any LWGEOM.
Definition lwgeom.c:1337
void lwfree(void *mem)
Definition lwutil.c:248
double lwgeom_mindistance2d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance)
Function handling min distance calculations and dwithin calculations.
Definition measures.c:222
#define POLYGONTYPE
Definition liblwgeom.h:104
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition lwgeom.c:367
LWGEOM * lwgeom_difference(const LWGEOM *geom1, const LWGEOM *geom2)
void lwcollection_release(LWCOLLECTION *lwcollection)
double lwgeom_mindistance2d(const LWGEOM *lw1, const LWGEOM *lw2)
Function initializing min distance calculation.
Definition measures.c:212
#define WKT_ISO
Definition liblwgeom.h:2219
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:207
void lwline_setPoint4d(LWLINE *line, uint32_t which, POINT4D *newpoint)
Definition lwline.c:374
LWGEOM * lwgeom_union(const LWGEOM *geom1, const LWGEOM *geom2)
int ptarray_is_closed_2d(const POINTARRAY *pa)
Definition ptarray.c:710
void lwpoly_free(LWPOLY *poly)
Definition lwpoly.c:175
#define LW_TRUE
Return types for functions with status returns.
Definition liblwgeom.h:93
const GBOX * lwgeom_get_bbox(const LWGEOM *lwgeom)
Get a non-empty geometry bounding box, computing and caching it if not already there.
Definition lwgeom.c:771
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:369
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition lwgeom.c:357
LWCOLLECTION * lwcollection_extract(const LWCOLLECTION *col, uint32_t type)
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition lwgeom.c:723
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:337
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition ptarray.c:643
LWGEOM * lwgeom_remove_repeated_points(const LWGEOM *in, double tolerance)
Definition lwgeom.c:1562
void lwgeom_reverse_in_place(LWGEOM *lwgeom)
Reverse vertex order of LWGEOM.
Definition lwgeom.c:131
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition lwgeom.c:557
int ptarray_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number)
Definition ptarray.c:1031
LWGEOM * lwline_remove_repeated_points(const LWLINE *in, double tolerance)
Definition lwline.c:449
#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 lwline_is_closed(const LWLINE *line)
Definition lwline.c:455
int ptarray_scroll_in_place(POINTARRAY *pa, const POINT4D *newbase)
Definition ptarray.c:2337
int lwpoint_is_empty(const LWPOINT *point)
int ptarray_contains_point(const POINTARRAY *pa, const POINT2D *pt)
The following is based on the "Fast Winding Number Inclusion of a Point in a Polygon" algorithm by Da...
Definition ptarray.c:755
#define FP_ABS(a)
int lwpoly_is_empty(const LWPOLY *poly)
#define LW_OUTSIDE
int ptarray_isccw(const POINTARRAY *pa)
Definition ptarray.c:1182
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
Definition lwalgorithm.c:70
POINTARRAY * ptarray_clone(const POINTARRAY *ptarray)
Clone a POINTARRAY object.
Definition ptarray.c:674
#define P2D_SAME_STRICT(a, b)
#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
#define PGTOPO_BE_ERROR()
#define LWTFMT_ELEMID
Datum contains(PG_FUNCTION_ARGS)
Datum covers(PG_FUNCTION_ARGS)
#define LWDEBUG(level, msg)
Definition lwgeom_log.h:101
#define LWDEBUGF(level, msg,...)
Definition lwgeom_log.h:106
void lwnotice(const char *fmt,...) __attribute__((format(printf
Write a notice out to the notice handler.
#define LWDEBUGGF(level, geom, fmt,...)
Definition lwgeom_log.h:116
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
#define LWDEBUGG(level, geom, msg)
Definition lwgeom_log.h:111
LWT_ELEMID lwt_AddPoint(LWT_TOPOLOGY *topo, LWPOINT *point, double tol)
Adds a point to the topology.
LWT_ELEMID lwt_be_ExistsCoincidentNode(LWT_TOPOLOGY *topo, const LWPOINT *pt)
LWT_ISO_FACE * lwt_be_getFaceWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
static LWGEOM * _lwt_toposnap(LWGEOM *src, LWGEOM *tgt, double tol)
LWT_ELEMID lwt_be_ExistsEdgeIntersectingPoint(LWT_TOPOLOGY *topo, const LWPOINT *pt)
struct edgeend_t edgeend
#define CBT2(to, method, a1, a2)
static LWT_ELEMID * _lwt_AddLine(LWT_TOPOLOGY *topo, LWLINE *line, double tol, int *nedges, int handleFaceSplit, int maxNewEdges)
LWT_ISO_EDGE * lwt_be_getEdgeWithinDistance2D(LWT_TOPOLOGY *topo, const LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit)
static GBOX * lwt_be_computeFaceMBR(const LWT_TOPOLOGY *topo, LWT_ELEMID face)
static uint64_t lwt_be_updateFacesById(LWT_TOPOLOGY *topo, const LWT_ISO_FACE *faces, uint64_t numfaces)
void lwt_BackendIfaceRegisterCallbacks(LWT_BE_IFACE *iface, const LWT_BE_CALLBACKS *cb)
Register backend callbacks into the opaque iface handler.
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.
#define CB1(be, method, a1)
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_ELEMID lwt_GetFaceContainingPoint(LWT_TOPOLOGY *topo, const LWPOINT *pt)
Find the face-id of the face properly containing a given point.
static int _lwt_InitEdgeEndByLine(edgeend *fee, edgeend *lee, LWLINE *edge, POINT2D *fp, POINT2D *lp)
static int _lwt_SnapEdgeToExistingNode(LWT_TOPOLOGY *topo, LWT_ISO_NODE *node, LWT_ISO_EDGE *edge, double tol)
static double lwt_be_topoGetPrecision(LWT_TOPOLOGY *topo)
void lwt_LoadGeometry(LWT_TOPOLOGY *topo, LWGEOM *geom, double tol)
Load a geometry into the topology.
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.
static int _lwt_GetInteriorEdgePoint(const LWLINE *edge, POINT2D *ip)
static LWT_ISO_EDGE * lwt_be_getClosestEdge(const LWT_TOPOLOGY *topo, const LWPOINT *pt, uint64_t *numelems, int fields)
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)
static int _lwt_SnapEdge_checkMotion(LWT_TOPOLOGY *topo, const LWCOLLECTION *splitC, const LWT_ISO_EDGE *edge, LWT_ISO_EDGE *existingEdge, const LWT_NODE_EDGES *splitNodeEdges)
Check the motion of a snapped edge, invoke lwerror if the movement hits any other edge or node.
static int lwt_be_checkTopoGeomRemIsoEdge(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id)
#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.
static void lwt_LoadLine(LWT_TOPOLOGY *topo, LWLINE *line, double tol, int max_new_edges)
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)
static void lwt_LoadPoint(LWT_TOPOLOGY *topo, LWPOINT *point, double tol)
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)
void _lwt_release_faces(LWT_ISO_FACE *faces, int num_faces)
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.
LWT_ELEMID lwt_GetFaceByPoint(LWT_TOPOLOGY *topo, const LWPOINT *pt, double tol)
Find the face-id of a face containing a given point.
static int lwt_be_updateTopoGeomFaceSplit(LWT_TOPOLOGY *topo, LWT_ELEMID split_face, LWT_ELEMID new_face1, LWT_ELEMID new_face2)
LWT_ELEMID * lwt_AddLine(LWT_TOPOLOGY *topo, LWLINE *line, double tol, int *nedges, int max_new_edges)
Adds a linestring to the topology.
static int lwt_be_updateNodesById(LWT_TOPOLOGY *topo, const LWT_ISO_NODE *nodes, int numnodes, int upd_fields)
LWT_ISO_NODE * lwt_be_getNodeWithinDistance2D(LWT_TOPOLOGY *topo, const LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit)
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_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)
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 LWT_ELEMID _lwt_AddPoint(LWT_TOPOLOGY *topo, LWPOINT *point, double tol, int findFace, int *moved, int *numSplitEdges)
int lwt_be_insertNodes(LWT_TOPOLOGY *topo, LWT_ISO_NODE *node, uint64_t numelems)
void _lwt_LoadGeometryRecursive(LWT_TOPOLOGY *topo, LWGEOM *geom, double tol)
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)
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)
static LWT_ELEMID _lwt_GetEqualEdge(LWT_TOPOLOGY *topo, LWLINE *edge, int *forward)
LWT_BE_IFACE * lwt_CreateBackendIface(const LWT_BE_DATA *data)
Create a new backend interface.
static void _lwt_RotateElemidArray(LWT_ELEMID *ary, int from, int to, int rotidx)
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)
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 LWGEOM * _lwt_split_by_nodes(const LWGEOM *g, const LWGEOM *nodes)
void _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
static int lwt_be_topoHasZ(LWT_TOPOLOGY *topo)
#define CB0(be, method)
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_LoadPolygon(LWT_TOPOLOGY *topo, const LWPOLY *poly, double tol)
#define CBT0(to, method)
static bool _lwt_describe_point(const LWPOINT *pt, char *buf, size_t bufsize)
Definition lwgeom_topo.c:89
static LWT_ELEMID _lwt_AddFaceSplit(LWT_TOPOLOGY *topo, LWT_ELEMID sedge, LWT_ELEMID face, int mbr_only)
int lwt_be_updateEdgesById(LWT_TOPOLOGY *topo, const LWT_ISO_EDGE *edges, int numedges, int upd_fields)
static int lwt_be_checkTopoGeomRemIsoNode(LWT_TOPOLOGY *topo, LWT_ELEMID node_id)
#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)
static LWT_ELEMID _lwt_HealEdges(LWT_TOPOLOGY *topo, LWT_ELEMID eid1, LWT_ELEMID eid2, int modEdge)
LWT_ISO_EDGE * lwt_be_getEdgeByNode(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
static LWT_ELEMID _lwt_SplitAllEdgesToNewNode(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edges, uint64_t num, LWPOINT *point, double tol, int *moved)
static LWT_ISO_NODE * lwt_be_getNodeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box)
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 int _lwt_FirstDistinctVertex2D(const POINTARRAY *pa, const POINT2D *ref, int from, int dir, POINT2D *op)
struct scored_pointer_t scored_pointer
LWT_ELEMID lwt_GetNodeByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol)
Retrieve the id of a node at a point location.
static int lwt_be_topoGetSRID(LWT_TOPOLOGY *topo)
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)
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 bool _lwt_describe_intersection_point(const GEOSGeometry *g1, const GEOSGeometry *g2, char *buf, size_t bufsize)
Definition lwgeom_topo.c:47
int lwt_be_insertFaces(LWT_TOPOLOGY *topo, LWT_ISO_FACE *face, uint64_t numelems)
static LWT_ELEMID _lwt_AddLineEdge(LWT_TOPOLOGY *topo, LWLINE *edge, double tol, int handleFaceSplit, int *forward, int *numNewEdges)
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)
int lwt_ChangeEdgeGeom(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id, LWLINE *geom)
Changes the shape of an edge without affecting the topology structure.
static LWT_ELEMID _lwt_RemEdge(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id, int modFace)
LWT_ISO_EDGE * lwt_be_getEdgeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
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 int _lwt_FindAdjacentEdges(LWT_TOPOLOGY *topo, LWT_ELEMID node, edgeend *data, edgeend *other, LWT_ELEMID myedge_id)
static uint8_t * getPoint_internal(const POINTARRAY *pa, uint32_t n)
Definition lwinline.h:75
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition lwinline.h:141
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition lwinline.h:127
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:199
static const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition lwinline.h:97
const LWT_EDGEEND * lwt_edgeEndStar_getNextCW(LWT_EDGEEND_STAR *star, LWT_ISO_EDGE *edge, int outgoing)
LWT_EDGEEND_STAR * lwt_edgeEndStar_init(LWT_ELEMID nodeID)
const LWT_EDGEEND * lwt_edgeEndStar_getNextCCW(LWT_EDGEEND_STAR *star, LWT_ISO_EDGE *edge, int outgoing)
void lwt_EdgeEndStar_debugPrint(const LWT_EDGEEND_STAR *star)
void lwt_edgeEndStar_addEdge(LWT_EDGEEND_STAR *star, const LWT_ISO_EDGE *edge)
LWT_NODE_EDGES * lwt_nodeEdges_loadFromDB(LWT_TOPOLOGY *topo, LWT_ELEMID node_id, int fields)
double ymax
Definition liblwgeom.h:357
double xmax
Definition liblwgeom.h:355
double ymin
Definition liblwgeom.h:356
double xmin
Definition liblwgeom.h:354
uint32_t ngeoms
Definition liblwgeom.h:580
LWGEOM ** geoms
Definition liblwgeom.h:575
int32_t srid
Definition liblwgeom.h:576
uint8_t type
Definition liblwgeom.h:462
int32_t srid
Definition liblwgeom.h:460
GBOX * bbox
Definition liblwgeom.h:482
POINTARRAY * points
Definition liblwgeom.h:483
int32_t srid
Definition liblwgeom.h:484
POINTARRAY * point
Definition liblwgeom.h:471
POINTARRAY ** rings
Definition liblwgeom.h:519
uint32_t nrings
Definition liblwgeom.h:524
Structure containing base backend callbacks.
const LWT_BE_DATA * data
const LWT_BE_CALLBACKS * cb
const LWT_ISO_EDGE * edge
Definition lwt_edgeend.h:33
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_ISO_EDGE * edges
LWT_BE_TOPOLOGY * be_topo
const LWT_BE_IFACE * be_iface
double y
Definition liblwgeom.h:390
double x
Definition liblwgeom.h:390
double x
Definition liblwgeom.h:414
double z
Definition liblwgeom.h:414
double y
Definition liblwgeom.h:414
uint32_t npoints
Definition liblwgeom.h:427
double myaz
LWT_ELEMID nextCCW
LWT_ELEMID ccwFace
LWT_ELEMID cwFace
LWT_ELEMID nextCW