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