PostGIS  3.7.0dev-r@@SVN_REVISION@@
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 
113 const char *
115 {
116  CB0(be, lastErrorMessage);
117 }
118 
121 {
122  CB1(be, loadTopologyByName, name);
123 }
124 
125 static int
127 {
128  CBT0(topo, topoGetSRID);
129 }
130 
131 static double
133 {
134  CBT0(topo, topoGetPrecision);
135 }
136 
137 static int
139 {
140  CBT0(topo, topoHasZ);
141 }
142 
143 int
145 {
146  CBT0(topo, freeTopology);
147 }
148 
149 LWT_ISO_NODE *
150 lwt_be_getNodeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
151 {
152  CBT3(topo, getNodeById, ids, numelems, fields);
153 }
154 
155 LWT_ISO_NODE *
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 
166 static LWT_ISO_NODE *
167 lwt_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 
172 LWT_ISO_EDGE *
173 lwt_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 
178 LWT_ISO_FACE *
179 lwt_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 
184 int
185 lwt_be_insertNodes(LWT_TOPOLOGY *topo, LWT_ISO_NODE *node, uint64_t numelems)
186 {
187  CBT2(topo, insertNodes, node, numelems);
188 }
189 
190 int
191 lwt_be_insertFaces(LWT_TOPOLOGY *topo, LWT_ISO_FACE *face, uint64_t numelems)
192 {
193  CBT2(topo, insertFaces, face, numelems);
194 }
195 
196 static int
197 lwt_be_deleteFacesById(const LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems)
198 {
199  CBT2(topo, deleteFacesById, ids, numelems);
200 }
201 
202 static int
203 lwt_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 
214 LWT_ISO_EDGE *
215 lwt_be_getEdgeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
216 {
217  CBT3(topo, getEdgeById, ids, numelems, fields);
218 }
219 
220 static LWT_ISO_FACE *
221 lwt_be_getFaceById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
222 {
223  CBT3(topo, getFaceById, ids, numelems, fields);
224 }
225 
226 LWT_ISO_EDGE *
227 lwt_be_getEdgeByNode(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
228 {
229  CBT3(topo, getEdgeByNode, ids, numelems, fields);
230 }
231 
232 static LWT_ISO_EDGE *
233 lwt_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 
238 static LWT_ISO_NODE *
239 lwt_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 
244 LWT_ISO_EDGE *
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 
255 int
256 lwt_be_insertEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edge, uint64_t numelems)
257 {
258  CBT2(topo, insertEdges, edge, numelems);
259 }
260 
261 int
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 
273 static 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 
285 static uint64_t
287  const LWT_ISO_FACE* faces, uint64_t numfaces
288 )
289 {
290  CBT2(topo, updateFacesById, faces, numfaces);
291 }
292 
293 int
295  const LWT_ISO_EDGE* edges, int numedges, int upd_fields
296 )
297 {
298  CBT3(topo, updateEdgesById, edges, numedges, upd_fields);
299 }
300 
301 static int
303  const LWT_ISO_NODE* nodes, int numnodes, int upd_fields
304 )
305 {
306  CBT3(topo, updateNodesById, nodes, numnodes, upd_fields);
307 }
308 
309 int
311  const LWT_ISO_EDGE* sel_edge, int sel_fields
312 )
313 {
314  CBT2(topo, deleteEdges, sel_edge, sel_fields);
315 }
316 
317 int
319 {
320  CBT3(topo, updateTopoGeomEdgeSplit, split_edge, new_edge1, new_edge2);
321 }
322 
323 static int
325  LWT_ELEMID new_face1, LWT_ELEMID new_face2)
326 {
327  CBT3(topo, updateTopoGeomFaceSplit, split_face, new_face1, new_face2);
328 }
329 
330 static int
332  LWT_ELEMID face_left, LWT_ELEMID face_right)
333 {
334  CBT3(topo, checkTopoGeomRemEdge, edge_id, face_left, face_right);
335 }
336 
337 static int
339 {
340  CBT1(topo, checkTopoGeomRemIsoEdge, edge_id);
341 }
342 
343 static int
345  LWT_ELEMID eid1, LWT_ELEMID eid2)
346 {
347  CBT3(topo, checkTopoGeomRemNode, node_id, eid1, eid2);
348 }
349 
350 static int
352 {
353  CBT1(topo, checkTopoGeomRemIsoNode, node_id);
354 }
355 
356 static int
358  LWT_ELEMID face1, LWT_ELEMID face2,
359  LWT_ELEMID newface)
360 {
361  CBT3(topo, updateTopoGeomFaceHeal, face1, face2, newface);
362 }
363 
364 static int
366  LWT_ELEMID edge1, LWT_ELEMID edge2,
367  LWT_ELEMID newedge)
368 {
369  CBT3(topo, updateTopoGeomEdgeHeal, edge1, edge2, newedge);
370 }
371 
372 static LWT_ELEMID *
373 lwt_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  {
385  PGTOPO_BE_ERROR();
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  {
398  PGTOPO_BE_ERROR();
399  return 0;
400  }
401  return exists;
402 }
403 
404 static LWT_ISO_EDGE *
405 lwt_be_getClosestEdge(const LWT_TOPOLOGY *topo, const LWPOINT *pt, uint64_t *numelems, int fields)
406 {
407  CBT3(topo, getClosestEdge, pt, numelems, fields);
408 }
409 
410 static GBOX *
412 {
413  CBT1(topo, computeFaceMBR, face);
414 }
415 
416 /************************************************************************
417  *
418  * Utility functions
419  *
420  ************************************************************************/
421 
422 static 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 
449 void
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 
459 void
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 
469 static 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 
485 LWT_TOPOLOGY *
486 lwt_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);
502  topo->precision = lwt_be_topoGetPrecision(topo);
503 
504  return topo;
505 }
506 
507 void
509 {
510  if ( ! lwt_be_freeTopology(topo) ) {
511  lwnotice("Could not release backend topology memory: %s",
513  }
514  lwfree(topo);
515 }
516 
525 static 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 ) {
556  PGTOPO_BE_ERROR();
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  {
581  PGTOPO_BE_ERROR();
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  */
605 static 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  {
632  PGTOPO_BE_ERROR();
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);
662  PGTOPO_BE_ERROR();
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  {
848  PGTOPO_BE_ERROR();
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 ) {
922  PGTOPO_BE_ERROR();
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 ) {
938  PGTOPO_BE_ERROR();
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 ) {
958  PGTOPO_BE_ERROR();
959  return -1;
960  }
961 
962  return newedge.edge_id;
963 }
964 
965 static 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  {
983  PGTOPO_BE_ERROR();
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 
1051 LWT_ELEMID
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);
1080  PGTOPO_BE_ERROR();
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);
1097  PGTOPO_BE_ERROR();
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);
1119  PGTOPO_BE_ERROR();
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);
1146  PGTOPO_BE_ERROR();
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);
1173  PGTOPO_BE_ERROR();
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);
1188  PGTOPO_BE_ERROR();
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);
1197  PGTOPO_BE_ERROR();
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 
1208 LWT_ELEMID
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);
1237  PGTOPO_BE_ERROR();
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);
1255  PGTOPO_BE_ERROR();
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);
1264  PGTOPO_BE_ERROR();
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);
1271  PGTOPO_BE_ERROR();
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);
1321  PGTOPO_BE_ERROR();
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);
1342  PGTOPO_BE_ERROR();
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);
1356  PGTOPO_BE_ERROR();
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);
1370  PGTOPO_BE_ERROR();
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);
1384  PGTOPO_BE_ERROR();
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);
1393  PGTOPO_BE_ERROR();
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 */
1405 typedef 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  */
1427 static 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  */
1465 static 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  */
1523 static 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  {
1555  PGTOPO_BE_ERROR();
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  */
1745 static 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 
1778 static 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  {
1806  PGTOPO_BE_ERROR();
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  */
1892 static 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);
1911  PGTOPO_BE_ERROR();
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 */
1986  PGTOPO_BE_ERROR();
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 */
2014  PGTOPO_BE_ERROR();
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 */
2037  PGTOPO_BE_ERROR();
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);
2078  PGTOPO_BE_ERROR();
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 
2137  contains = gbox_contains_point2d(shellbox, &ep) == LW_TRUE ? LW_INSIDE : LW_OUTSIDE;
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 );
2190  PGTOPO_BE_ERROR();
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 );
2211  PGTOPO_BE_ERROR();
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);
2237  PGTOPO_BE_ERROR();
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 );
2282  PGTOPO_BE_ERROR();
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 
2302 static 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  {
2403  PGTOPO_BE_ERROR();
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 ) {
2492  PGTOPO_BE_ERROR();
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 ) {
2603  PGTOPO_BE_ERROR();
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 ) {
2636  PGTOPO_BE_ERROR();
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 ) {
2665  PGTOPO_BE_ERROR();
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,
2682  &updnode, LWT_COL_NODE_CONTAINING_FACE,
2683  NULL, 0);
2684  if ( ret == -1 ) {
2685  PGTOPO_BE_ERROR();
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,
2694  &updnode, LWT_COL_NODE_CONTAINING_FACE,
2695  NULL, 0);
2696  if ( ret == -1 ) {
2697  PGTOPO_BE_ERROR();
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 ) {
2757  PGTOPO_BE_ERROR();
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 ) {
2766  PGTOPO_BE_ERROR();
2767  return -1;
2768  }
2769  }
2770  }
2771 
2772  } // end of face split checking
2773 
2774  return newedge.edge_id;
2775 }
2776 
2777 LWT_ELEMID
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 
2785 LWT_ELEMID
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 
2793 static 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 
2840 LWGEOM*
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  {
2867  PGTOPO_BE_ERROR();
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  {
2878  PGTOPO_BE_ERROR();
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  */
2935 static 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,
2980  lwgeom_to_wkt(lwline_as_lwgeom(edge), WKT_EXTENDED, 2, &sz));
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 */
3067 static void
3068 _lwt_ReverseElemidArray(LWT_ELEMID *ary, int from, int to)
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" */
3082 static 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 
3091 int
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  {
3114  PGTOPO_BE_ERROR();
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 
3259 int
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  {
3284  PGTOPO_BE_ERROR();
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);
3402  PGTOPO_BE_ERROR();
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);
3478  PGTOPO_BE_ERROR();
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);
3516  PGTOPO_BE_ERROR();
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);
3582  PGTOPO_BE_ERROR();
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)
3675  PGTOPO_BE_ERROR();
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 */
3695 static 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  {
3704  PGTOPO_BE_ERROR();
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 
3721 int
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 ) {
3749  PGTOPO_BE_ERROR();
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);
3765  PGTOPO_BE_ERROR();
3766  return -1;
3767  }
3768 
3769  lwfree(node);
3770  return 0;
3771 }
3772 
3773 int
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);
3786  PGTOPO_BE_ERROR();
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 
3807 int
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  {
3824  PGTOPO_BE_ERROR();
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  {
3856  PGTOPO_BE_ERROR();
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  {
3874  PGTOPO_BE_ERROR();
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  {
3895  PGTOPO_BE_ERROR();
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  */
3917 static 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  */
3950 static 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  */
3975 static 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  {
4006  PGTOPO_BE_ERROR();
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  {
4048  PGTOPO_BE_ERROR();
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);
4100  PGTOPO_BE_ERROR();
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);
4113  PGTOPO_BE_ERROR();
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  {
4151  PGTOPO_BE_ERROR();
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);
4222  PGTOPO_BE_ERROR();
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);
4241  PGTOPO_BE_ERROR();
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);
4262  PGTOPO_BE_ERROR();
4263  return -1;
4264  }
4265  if ( -1 == _lwt_UpdateNodeFaceRef(topo, edge->face_left, floodface) )
4266  {
4267  _lwt_release_edges(edge, 1);
4268  PGTOPO_BE_ERROR();
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);
4278  PGTOPO_BE_ERROR();
4279  return -1;
4280  }
4281  if ( -1 == _lwt_UpdateNodeFaceRef(topo, edge->face_right, floodface) )
4282  {
4283  _lwt_release_edges(edge, 1);
4284  PGTOPO_BE_ERROR();
4285  return -1;
4286  }
4287  }
4288 
4289  /* Update topogeoms on heal */
4290  if ( ! lwt_be_updateTopoGeomFaceHeal(topo,
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 */
4301  int result = lwt_be_deleteEdges(topo, edge, LWT_COL_EDGE_EDGE_ID);
4302  if (result == -1)
4303  {
4304  _lwt_release_edges(edge, 1);
4305  PGTOPO_BE_ERROR();
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);
4330  PGTOPO_BE_ERROR();
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);
4348  PGTOPO_BE_ERROR();
4349  return -1;
4350  }
4351  }
4352 
4353  _lwt_release_edges(edge, 1);
4354  return modFace ? floodface : newface.face_id;
4355 }
4356 
4357 LWT_ELEMID
4359 {
4360  return _lwt_RemEdge( topo, edge_id, 1 );
4361 }
4362 
4363 LWT_ELEMID
4365 {
4366  return _lwt_RemEdge( topo, edge_id, 0 );
4367 }
4368 
4369 static 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  {
4405  PGTOPO_BE_ERROR();
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);
4483  PGTOPO_BE_ERROR();
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);
4535  PGTOPO_BE_ERROR();
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);
4673  PGTOPO_BE_ERROR();
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);
4695  PGTOPO_BE_ERROR();
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);
4729  PGTOPO_BE_ERROR();
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);
4740  PGTOPO_BE_ERROR();
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);
4753  PGTOPO_BE_ERROR();
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);
4764  PGTOPO_BE_ERROR();
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);
4774  PGTOPO_BE_ERROR();
4775  return -1;
4776  }
4777  if ( ! modEdge ) {
4779  if (result == -1)
4780  {
4781  _lwt_release_edges(edges, nedges);
4782  PGTOPO_BE_ERROR();
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  {
4793  PGTOPO_BE_ERROR();
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  */
4805  if ( ! lwt_be_updateTopoGeomEdgeHeal(topo,
4806  eid1, eid2, newedge.edge_id) )
4807  {
4809  return -1;
4810  }
4811 
4812  return modEdge ? commonnode : newedge.edge_id;
4813 }
4814 
4815 LWT_ELEMID
4817 {
4818  return _lwt_HealEdges( topo, e1, e2, 1 );
4819 }
4820 
4821 LWT_ELEMID
4823 {
4824  return _lwt_HealEdges( topo, e1, e2, 0 );
4825 }
4826 
4827 LWT_ELEMID
4828 lwt_GetNodeByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol)
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  {
4844  PGTOPO_BE_ERROR();
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 
4862 LWT_ELEMID
4863 lwt_GetEdgeByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol)
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  {
4879  PGTOPO_BE_ERROR();
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 
4918 LWT_ELEMID
4919 lwt_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 ) {
4932  PGTOPO_BE_ERROR();
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  {
4956  PGTOPO_BE_ERROR();
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 */
5022 static 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
5031 static 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  */
5043 static 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 
5065 typedef struct scored_pointer_t {
5066  void *ptr;
5067  double score;
5069 
5070 static int
5071 compare_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  */
5090 static 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  {
5103  PGTOPO_BE_ERROR();
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),
5164  getPoint_internal(e->geom->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 
5204 static 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 
5289 static 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:%"
5348  LWTFMT_ELEMID,
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:%"
5496  LWTFMT_ELEMID,
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 ) {
5564  PGTOPO_BE_ERROR();
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 ) {
5614  PGTOPO_BE_ERROR();
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 ) {
5753  PGTOPO_BE_ERROR();
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 %"
5779  LWTFMT_ELEMID " to %" LWTFMT_ELEMID,
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 %"
5789  LWTFMT_ELEMID " to %" LWTFMT_ELEMID,
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 %"
5799  LWTFMT_ELEMID " to %" LWTFMT_ELEMID,
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 %"
5809  LWTFMT_ELEMID " to %" LWTFMT_ELEMID,
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 ) {
5821  PGTOPO_BE_ERROR();
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 ) {
5874  PGTOPO_BE_ERROR();
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  {
5912  PGTOPO_BE_ERROR();
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  {
5939  PGTOPO_BE_ERROR();
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 ) {
5963  PGTOPO_BE_ERROR();
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 ) {
5978  PGTOPO_BE_ERROR();
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  {
6041  PGTOPO_BE_ERROR();
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 ) {
6052  PGTOPO_BE_ERROR();
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 ) {
6085  PGTOPO_BE_ERROR();
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 ) {
6119  PGTOPO_BE_ERROR();
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 ) {
6166  PGTOPO_BE_ERROR();
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 ) {
6225  PGTOPO_BE_ERROR();
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 ) {
6240  PGTOPO_BE_ERROR();
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 ) {
6250  PGTOPO_BE_ERROR();
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 ) {
6331  PGTOPO_BE_ERROR();
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 ) {
6382  PGTOPO_BE_ERROR();
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 ) {
6417  PGTOPO_BE_ERROR();
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 ) {
6451  PGTOPO_BE_ERROR();
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 ) {
6468  PGTOPO_BE_ERROR();
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 ) {
6477  PGTOPO_BE_ERROR();
6478  return -1;
6479  }
6480 
6481  /* Delete the collapsed face from composition of TopoGeometries */
6482  if ( ! lwt_be_updateTopoGeomFaceHeal( topo, collapsedFace, -1, -1) )
6483  {
6484  PGTOPO_BE_ERROR();
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  */
6514 static 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  */
6751 static 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  {
6779  PGTOPO_BE_ERROR();
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  {
6841  PGTOPO_BE_ERROR();
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 
6869 LWT_ELEMID
6870 lwt_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  */
6890 static 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,
6914  _lwt_minTolerance(lwpoint_as_lwgeom(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  {
6951  PGTOPO_BE_ERROR();
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) */
6981  tmp = lwgeom_make_valid(lwline_as_lwgeom(edge));
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 */
7092 static LWGEOM *
7093 _lwt_split_by_nodes(const LWGEOM *g, const LWGEOM *nodes)
7094 {
7095  LWCOLLECTION *col = lwgeom_as_lwcollection(nodes);
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 
7114 static 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);
7183  PGTOPO_BE_ERROR();
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);
7227  PGTOPO_BE_ERROR();
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 */
7291  lwcollection_release(col);
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 */
7362  lwcollection_release(col);
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 */
7417  lwcollection_release(col);
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 
7497 LWT_ELEMID*
7498 lwt_AddLine(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges)
7499 {
7500  return _lwt_AddLine(topo, line, tol, nedges, 1);
7501 }
7502 
7503 LWT_ELEMID*
7504 lwt_AddLineNoFace(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges)
7505 {
7506  return _lwt_AddLine(topo, line, tol, nedges, 0);
7507 }
7508 
7509 static void
7510 lwt_LoadPoint(LWT_TOPOLOGY* topo, LWPOINT* point, double tol)
7511 {
7512  _lwt_AddPoint(topo, point, tol, 1, NULL);
7513 }
7514 
7515 static void
7516 lwt_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 
7526 static void
7527 lwt_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 
7545 LWT_ELEMID*
7546 lwt_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);
7582  PGTOPO_BE_ERROR();
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 
7668 LWT_ELEMID
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  {
7694  PGTOPO_BE_ERROR();
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  {
7811  PGTOPO_BE_ERROR();
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 
8028 void
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 
8068 void
8069 lwt_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,...)
void lwgeom_refresh_bbox(LWGEOM *lwgeom)
Drop current bbox and calculate a fresh one.
Definition: lwgeom.c:707
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:179
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:339
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 * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:309
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
int ptarray_closest_segment_2d(const POINTARRAY *pa, const POINT2D *qp, double *dist)
Definition: ptarray.c:1324
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:2509
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
void lwpoint_free(LWPOINT *pt)
Definition: lwpoint.c:213
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1218
LWGEOM * lwgeom_split(const LWGEOM *lwgeom_in, const LWGEOM *blade_in)
#define MULTILINETYPE
Definition: liblwgeom.h:106
LWGEOM * lwgeom_intersection(const LWGEOM *geom1, const LWGEOM *geom2)
#define LINETYPE
Definition: liblwgeom.h:103
#define WKT_EXTENDED
Definition: liblwgeom.h:2218
#define LW_SUCCESS
Definition: liblwgeom.h:97
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:329
#define MULTIPOINTTYPE
Definition: liblwgeom.h:105
int lwgeom_is_simple(const LWGEOM *lwgeom)
LWGEOM * lwgeom_snap(const LWGEOM *geom1, const LWGEOM *geom2, double tolerance)
Snap vertices and segments of a geometry to another using a given tolerance.
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition: ptarray.c:643
LWGEOM * lwgeom_difference(const LWGEOM *geom1, const LWGEOM *geom2)
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:529
int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point)
Definition: lwgeom_api.c:342
LWGEOM * lwgeom_remove_repeated_points(const LWGEOM *in, double tolerance)
Definition: lwgeom.c:1534
LWGEOM * lwgeom_force_3dz(const LWGEOM *geom, double zval)
Definition: lwgeom.c:799
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
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 * lwrealloc(void *mem, size_t size)
Definition: lwutil.c:242
void lwfree(void *mem)
Definition: lwutil.c:248
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:344
double lwgeom_mindistance2d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance)
Function handling min distance calculations and dwithin calculations.
Definition: measures.c:210
LWGEOM * lwgeom_unaryunion(const LWGEOM *geom1)
LWCOLLECTION * lwcollection_extract(const LWCOLLECTION *col, uint32_t type)
Definition: lwcollection.c:432
#define POLYGONTYPE
Definition: liblwgeom.h:104
LWGEOM * lwgeom_buildarea(const LWGEOM *geom)
Take a geometry and return an areal geometry (Polygon or MultiPolygon).
void lwcollection_release(LWCOLLECTION *lwcollection)
Definition: lwcollection.c:36
double lwgeom_mindistance2d(const LWGEOM *lw1, const LWGEOM *lw2)
Function initializing min distance calculation.
Definition: measures.c:200
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
#define WKT_ISO
Definition: liblwgeom.h:2216
void lwcollection_free(LWCOLLECTION *col)
Definition: lwcollection.c:357
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
char * lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out)
WKT emitter function.
Definition: lwout_wkt.c:708
const GBOX * lwgeom_get_bbox(const LWGEOM *lwgeom)
Get a non-empty geometry bounding box, computing and caching it if not already there.
Definition: lwgeom.c:743
void lwline_setPoint4d(LWLINE *line, uint32_t which, POINT4D *newpoint)
Definition: lwline.c:364
LWGEOM * lwgeom_linemerge(const LWGEOM *geom1)
void * lwalloc(size_t size)
Definition: lwutil.c:227
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
Definition: lwcollection.c:42
int ptarray_is_closed_2d(const POINTARRAY *pa)
Definition: ptarray.c:710
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition: lwgeom.c:233
void lwpoly_free(LWPOLY *poly)
Definition: lwpoly.c:175
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:215
LWPOLY * lwpoly_from_lwlines(const LWLINE *shell, uint32_t nholes, const LWLINE **holes)
Definition: lwpoly.c:360
LWPOLY * lwpoly_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwpoly.c:161
LWPOLY * lwpoly_construct(int32_t srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
Definition: lwpoly.c:43
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition: lwgeom_api.c:369
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_union(const LWGEOM *geom1, const LWGEOM *geom2)
LWGEOM * lwgeom_make_valid(LWGEOM *geom)
Attempts to make an invalid geometries valid w/out losing points.
void lwgeom_reverse_in_place(LWGEOM *lwgeom)
Reverse vertex order of LWGEOM.
Definition: lwgeom.c:103
LWPOINT * lwline_get_lwpoint(const LWLINE *line, uint32_t where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition: lwline.c:309
int ptarray_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number)
Definition: ptarray.c:763
#define LW_INSIDE
Constants for point-in-polygon return values.
LWGEOM * lwline_remove_repeated_points(const LWLINE *in, double tolerance)
Definition: lwline.c:439
#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
POINTARRAY * ptarray_clone(const POINTARRAY *ptarray)
Clone a POINTARRAY object.
Definition: ptarray.c:674
int ptarray_scroll_in_place(POINTARRAY *pa, const POINT4D *newbase)
Definition: ptarray.c:2202
int lwpoint_is_empty(const LWPOINT *point)
int ptarray_contains_point(const POINTARRAY *pa, const POINT2D *pt)
Return LW_INSIDE if the point is inside the POINTARRAY, LW_OUTSIDE if it is outside,...
Definition: ptarray.c:751
#define FP_ABS(a)
int lwpoly_is_empty(const LWPOLY *poly)
#define LW_OUTSIDE
int ptarray_isccw(const POINTARRAY *pa)
Definition: ptarray.c:1051
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
Definition: lwalgorithm.c:70
#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
static LWT_ELEMID * lwt_be_getRingEdges(LWT_TOPOLOGY *topo, LWT_ELEMID edge, uint64_t *numedges, uint64_t limit)
Definition: lwgeom_topo.c:373
LWT_ELEMID lwt_AddPoint(LWT_TOPOLOGY *topo, LWPOINT *point, double tol)
Adds a point to the topology.
Definition: lwgeom_topo.c:6870
LWT_ELEMID lwt_be_ExistsCoincidentNode(LWT_TOPOLOGY *topo, const LWPOINT *pt)
Definition: lwgeom_topo.c:379
LWT_ELEMID lwt_be_ExistsEdgeIntersectingPoint(LWT_TOPOLOGY *topo, const LWPOINT *pt)
Definition: lwgeom_topo.c:392
struct edgeend_t edgeend
#define CBT2(to, method, a1, a2)
Definition: lwgeom_topo.c:93
static uint64_t lwt_be_updateFacesById(LWT_TOPOLOGY *topo, const LWT_ISO_FACE *faces, uint64_t numfaces)
Definition: lwgeom_topo.c:286
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
const char * lwt_be_lastErrorMessage(const LWT_BE_IFACE *be)
Definition: lwgeom_topo.c:114
static int _lwt_FindNextRingEdge(const POINTARRAY *ring, int from, const LWT_ISO_EDGE *edges, int numedges)
Definition: lwgeom_topo.c:2936
LWT_BE_IFACE * lwt_CreateBackendIface(const LWT_BE_DATA *data)
Create a new backend interface.
Definition: lwgeom_topo.c:47
LWT_ISO_EDGE * lwt_be_getEdgeByNode(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
Definition: lwgeom_topo.c:227
static int lwt_be_deleteNodesById(const LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems)
Definition: lwgeom_topo.c:203
int lwt_RemoveIsoNode(LWT_TOPOLOGY *topo, LWT_ELEMID nid)
Remove an isolated node.
Definition: lwgeom_topo.c:3774
#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)
Definition: lwgeom_topo.c:197
static int lwt_be_checkTopoGeomRemEdge(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id, LWT_ELEMID face_left, LWT_ELEMID face_right)
Definition: lwgeom_topo.c:331
static LWT_ELEMID _lwt_AddEdge(LWT_TOPOLOGY *topo, LWT_ELEMID start_node, LWT_ELEMID end_node, LWLINE *geom, int skipChecks, int modFace)
Definition: lwgeom_topo.c:2303
LWT_ELEMID lwt_GetFaceContainingPoint(LWT_TOPOLOGY *topo, const LWPOINT *pt)
Find the face-id of the face properly containing a given point.
Definition: lwgeom_topo.c:7669
static int _lwt_InitEdgeEndByLine(edgeend *fee, edgeend *lee, LWLINE *edge, POINT2D *fp, POINT2D *lp)
Definition: lwgeom_topo.c:1466
LWT_ISO_EDGE * lwt_be_getEdgeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
Definition: lwgeom_topo.c:215
static int _lwt_SnapEdgeToExistingNode(LWT_TOPOLOGY *topo, LWT_ISO_NODE *node, LWT_ISO_EDGE *edge, double tol)
Definition: lwgeom_topo.c:5290
static double lwt_be_topoGetPrecision(LWT_TOPOLOGY *topo)
Definition: lwgeom_topo.c:132
void lwt_LoadGeometry(LWT_TOPOLOGY *topo, LWGEOM *geom, double tol)
Load a geometry into the topology.
Definition: lwgeom_topo.c:8069
static int _lwt_UpdateEdgeFaceRef(LWT_TOPOLOGY *topo, LWT_ELEMID of, LWT_ELEMID nf)
Definition: lwgeom_topo.c:3918
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)
Definition: lwgeom_topo.c:1746
LWGEOM * lwt_GetFaceGeometry(LWT_TOPOLOGY *topo, LWT_ELEMID faceid)
Return the geometry of a face.
Definition: lwgeom_topo.c:2841
LWT_ELEMID lwt_GetEdgeByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol)
Find the edge-id of an edge that intersects a given point.
Definition: lwgeom_topo.c:4863
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)
Definition: lwgeom_topo.c:2778
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.
Definition: lwgeom_topo.c:5205
static int lwt_be_checkTopoGeomRemIsoEdge(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id)
Definition: lwgeom_topo.c:338
#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.
Definition: lwgeom_topo.c:589
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.
Definition: lwgeom_topo.c:1052
static int lwt_be_updateTopoGeomEdgeHeal(LWT_TOPOLOGY *topo, LWT_ELEMID edge1, LWT_ELEMID edge2, LWT_ELEMID newedge)
Definition: lwgeom_topo.c:365
#define CBT1(to, method, a1)
Definition: lwgeom_topo.c:89
static void lwt_LoadPoint(LWT_TOPOLOGY *topo, LWPOINT *point, double tol)
Definition: lwgeom_topo.c:7510
static LWGEOM * _lwt_split_by_nodes(const LWGEOM *g, const LWGEOM *nodes)
Definition: lwgeom_topo.c:7093
LWT_ELEMID lwt_be_getNextEdgeId(LWT_TOPOLOGY *topo)
Definition: lwgeom_topo.c:209
static LWT_ISO_NODE * _lwt_GetIsoNode(LWT_TOPOLOGY *topo, LWT_ELEMID nid)
Definition: lwgeom_topo.c:3696
static LWT_ELEMID _lwt_AddIsoNode(LWT_TOPOLOGY *topo, LWT_ELEMID face, LWPOINT *pt, int skipISOChecks, int checkFace)
Definition: lwgeom_topo.c:526
static void lwt_LoadLine(LWT_TOPOLOGY *topo, LWLINE *line, double tol)
Definition: lwgeom_topo.c:7516
void _lwt_release_faces(LWT_ISO_FACE *faces, int num_faces)
Definition: lwgeom_topo.c:450
LWT_ELEMID * lwt_AddLine(LWT_TOPOLOGY *topo, LWLINE *line, double tol, int *nedges)
Adds a linestring to the topology.
Definition: lwgeom_topo.c:7498
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.
Definition: lwgeom_topo.c:801
static LWPOLY * _lwt_MakeRingShell(LWT_TOPOLOGY *topo, LWT_ELEMID *signed_edge_ids, uint64_t num_signed_edge_ids)
Definition: lwgeom_topo.c:1779
LWT_ELEMID lwt_GetFaceByPoint(LWT_TOPOLOGY *topo, const LWPOINT *pt, double tol)
Find the face-id of a face containing a given point.
Definition: lwgeom_topo.c:4919
static int lwt_be_updateTopoGeomFaceSplit(LWT_TOPOLOGY *topo, LWT_ELEMID split_face, LWT_ELEMID new_face1, LWT_ELEMID new_face2)
Definition: lwgeom_topo.c:324
static GBOX * lwt_be_computeFaceMBR(const LWT_TOPOLOGY *topo, LWT_ELEMID face)
Definition: lwgeom_topo.c:411
static int lwt_be_updateNodesById(LWT_TOPOLOGY *topo, const LWT_ISO_NODE *nodes, int numnodes, int upd_fields)
Definition: lwgeom_topo.c:302
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.
Definition: lwgeom_topo.c:1209
static int _lwt_CheckEdgeCrossing(LWT_TOPOLOGY *topo, LWT_ELEMID start_node, LWT_ELEMID end_node, const LWLINE *geom, LWT_ELEMID myself)
Definition: lwgeom_topo.c:606
static void _lwt_release_nodes(LWT_ISO_NODE *nodes, int num_nodes)
Definition: lwgeom_topo.c:470
LWT_ELEMID lwt_RemEdgeNewFace(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id)
Remove an edge, possibly merging two faces (replacing both with a new one)
Definition: lwgeom_topo.c:4364
static double _lwt_minToleranceDouble(double d)
Definition: lwgeom_topo.c:5023
static int lwt_be_checkTopoGeomRemNode(LWT_TOPOLOGY *topo, LWT_ELEMID node_id, LWT_ELEMID eid1, LWT_ELEMID eid2)
Definition: lwgeom_topo.c:344
static int lwt_be_updateTopoGeomFaceHeal(LWT_TOPOLOGY *topo, LWT_ELEMID face1, LWT_ELEMID face2, LWT_ELEMID newface)
Definition: lwgeom_topo.c:357
int lwt_be_updateTopoGeomEdgeSplit(LWT_TOPOLOGY *topo, LWT_ELEMID split_edge, LWT_ELEMID new_edge1, LWT_ELEMID new_edge2)
Definition: lwgeom_topo.c:318
static double _lwt_minTolerance(LWGEOM *g)
Definition: lwgeom_topo.c:5044
static LWT_ISO_EDGE * lwt_be_getEdgeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box)
Definition: lwgeom_topo.c:233
LWT_ISO_EDGE * lwt_be_getEdgeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
Definition: lwgeom_topo.c:173
static LWT_ISO_EDGE * lwt_be_getClosestEdge(const LWT_TOPOLOGY *topo, const LWPOINT *pt, uint64_t *numelems, int fields)
Definition: lwgeom_topo.c:405
int lwt_be_insertNodes(LWT_TOPOLOGY *topo, LWT_ISO_NODE *node, uint64_t numelems)
Definition: lwgeom_topo.c:185
void _lwt_LoadGeometryRecursive(LWT_TOPOLOGY *topo, LWGEOM *geom, double tol)
Definition: lwgeom_topo.c:8029
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)
Definition: lwgeom_topo.c:2786
LWT_TOPOLOGY * lwt_LoadTopology(LWT_BE_IFACE *iface, const char *name)
Loads an existing topology by name from the database.
Definition: lwgeom_topo.c:486
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)
Definition: lwgeom_topo.c:262
static LWT_ELEMID _lwt_GetEqualEdge(LWT_TOPOLOGY *topo, LWLINE *edge, int *forward)
Definition: lwgeom_topo.c:5091
static void _lwt_RotateElemidArray(LWT_ELEMID *ary, int from, int to, int rotidx)
Definition: lwgeom_topo.c:3083
static int compare_scored_pointer(const void *si1, const void *si2)
Definition: lwgeom_topo.c:5071
LWT_ELEMID lwt_RemEdgeModFace(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id)
Remove an edge, possibly merging two faces (replacing one with the other)
Definition: lwgeom_topo.c:4358
LWT_ELEMID * lwt_AddPolygon(LWT_TOPOLOGY *topo, LWPOLY *poly, double tol, int *nfaces)
Adds a polygon to the topology.
Definition: lwgeom_topo.c:7546
int lwt_be_deleteEdges(LWT_TOPOLOGY *topo, const LWT_ISO_EDGE *sel_edge, int sel_fields)
Definition: lwgeom_topo.c:310
int lwt_GetFaceEdges(LWT_TOPOLOGY *topo, LWT_ELEMID face_id, LWT_ELEMID **out)
Return the list of directed edges bounding a face.
Definition: lwgeom_topo.c:3092
static int _lwt_UpdateNodeFaceRef(LWT_TOPOLOGY *topo, LWT_ELEMID of, LWT_ELEMID nf)
Definition: lwgeom_topo.c:3951
#define CBT4(to, method, a1, a2, a3, a4)
Definition: lwgeom_topo.c:101
int lwt_MoveIsoNode(LWT_TOPOLOGY *topo, LWT_ELEMID nid, LWPOINT *pt)
Move an isolated node.
Definition: lwgeom_topo.c:3722
LWT_ELEMID * lwt_AddLineNoFace(LWT_TOPOLOGY *topo, LWLINE *line, double tol, int *nedges)
Adds a linestring to the topology without determining generated faces.
Definition: lwgeom_topo.c:7504
LWT_ISO_NODE * lwt_be_getNodeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
Definition: lwgeom_topo.c:150
void _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
Definition: lwgeom_topo.c:460
static int lwt_be_topoHasZ(LWT_TOPOLOGY *topo)
Definition: lwgeom_topo.c:138
LWT_ISO_FACE * lwt_be_getFaceWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
Definition: lwgeom_topo.c:179
static LWT_ISO_NODE * lwt_be_getNodeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
Definition: lwgeom_topo.c:167
#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.
Definition: lwgeom_topo.c:4816
LWT_ISO_NODE * lwt_be_getNodeWithinDistance2D(LWT_TOPOLOGY *topo, const LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit)
Definition: lwgeom_topo.c:156
static void lwt_LoadPolygon(LWT_TOPOLOGY *topo, const LWPOLY *poly, double tol)
Definition: lwgeom_topo.c:7527
#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)
Definition: lwgeom_topo.c:1893
int lwt_be_updateEdgesById(LWT_TOPOLOGY *topo, const LWT_ISO_EDGE *edges, int numedges, int upd_fields)
Definition: lwgeom_topo.c:294
static int lwt_be_checkTopoGeomRemIsoNode(LWT_TOPOLOGY *topo, LWT_ELEMID node_id)
Definition: lwgeom_topo.c:351
#define _LWT_MINTOLERANCE(topo, geom)
Definition: lwgeom_topo.c:5062
#define CBT6(to, method, a1, a2, a3, a4, a5, a6)
Definition: lwgeom_topo.c:109
int lwt_be_freeTopology(LWT_TOPOLOGY *topo)
Definition: lwgeom_topo.c:144
void lwt_FreeTopology(LWT_TOPOLOGY *topo)
Release memory associated with an LWT_TOPOLOGY.
Definition: lwgeom_topo.c:508
LWT_ELEMID lwt_NewEdgeHeal(LWT_TOPOLOGY *topo, LWT_ELEMID e1, LWT_ELEMID e2)
Merge two edges, replacing both with a new one.
Definition: lwgeom_topo.c:4822
static LWCOLLECTION * _lwt_EdgeSplit(LWT_TOPOLOGY *topo, LWT_ELEMID edge, LWPOINT *pt, int skipISOChecks, LWT_ISO_EDGE **oldedge)
Definition: lwgeom_topo.c:966
static LWT_ELEMID _lwt_AddLineEdge(LWT_TOPOLOGY *topo, LWLINE *edge, double tol, int handleFaceSplit, int *forward)
Definition: lwgeom_topo.c:6891
#define CBT5(to, method, a1, a2, a3, a4, a5)
Definition: lwgeom_topo.c:105
int lwt_be_insertEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edge, uint64_t numelems)
Definition: lwgeom_topo.c:256
static LWT_ELEMID _lwt_HealEdges(LWT_TOPOLOGY *topo, LWT_ELEMID eid1, LWT_ELEMID eid2, int modEdge)
Definition: lwgeom_topo.c:4370
static LWT_ELEMID * _lwt_AddLine(LWT_TOPOLOGY *topo, LWLINE *line, double tol, int *nedges, int handleFaceSplit)
Definition: lwgeom_topo.c:7115
static LWT_ELEMID _lwt_SplitAllEdgesToNewNode(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edges, uint64_t num, LWPOINT *point, double tol, int *moved)
Definition: lwgeom_topo.c:6515
static LWGEOM * _lwt_FaceByEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edges, int numfaceedges)
Definition: lwgeom_topo.c:2794
LWT_BE_TOPOLOGY * lwt_be_loadTopologyByName(LWT_BE_IFACE *be, const char *name)
Definition: lwgeom_topo.c:120
static int _lwt_FirstDistinctVertex2D(const POINTARRAY *pa, const POINT2D *ref, int from, int dir, POINT2D *op)
Definition: lwgeom_topo.c:1428
static LWT_ISO_FACE * lwt_be_getFaceById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
Definition: lwgeom_topo.c:221
static LWT_ISO_NODE * lwt_be_getNodeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box)
Definition: lwgeom_topo.c:239
static LWGEOM * _lwt_toposnap(LWGEOM *src, LWGEOM *tgt, double tol)
Definition: lwgeom_topo.c:423
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.
Definition: lwgeom_topo.c:4828
static int lwt_be_topoGetSRID(LWT_TOPOLOGY *topo)
Definition: lwgeom_topo.c:126
static void _lwt_ReverseElemidArray(LWT_ELEMID *ary, int from, int to)
Definition: lwgeom_topo.c:3068
static LWT_ELEMID _lwt_AddPoint(LWT_TOPOLOGY *topo, LWPOINT *point, double tol, int findFace, int *moved)
Definition: lwgeom_topo.c:6752
int lwt_RemIsoEdge(LWT_TOPOLOGY *topo, LWT_ELEMID id)
Remove an isolated edge.
Definition: lwgeom_topo.c:3808
int lwt_be_insertFaces(LWT_TOPOLOGY *topo, LWT_ISO_FACE *face, uint64_t numelems)
Definition: lwgeom_topo.c:191
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)
Definition: lwgeom_topo.c:274
int lwt_ChangeEdgeGeom(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id, LWLINE *geom)
Changes the shape of an edge without affecting the topology structure.
Definition: lwgeom_topo.c:3260
static LWT_ELEMID _lwt_RemEdge(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id, int modFace)
Definition: lwgeom_topo.c:3976
LWT_ISO_EDGE * lwt_be_getEdgeWithinDistance2D(LWT_TOPOLOGY *topo, const LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit)
Definition: lwgeom_topo.c:245
static int _lwt_FindAdjacentEdges(LWT_TOPOLOGY *topo, LWT_ELEMID node, edgeend *data, edgeend *other, LWT_ELEMID myedge_id)
Definition: lwgeom_topo.c:1524
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
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwinline.h:141
static uint8_t * getPoint_internal(const POINTARRAY *pa, uint32_t n)
Definition: lwinline.h:75
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 LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwinline.h:127
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)
data
Definition: ovdump.py:104
tuple res
Definition: window.py:79
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
LWLINE * geom
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
Definition: lwgeom_topo.c:1415
LWT_ELEMID nextCCW
Definition: lwgeom_topo.c:1411
LWT_ELEMID ccwFace
Definition: lwgeom_topo.c:1413
int was_isolated
Definition: lwgeom_topo.c:1414
LWT_ELEMID cwFace
Definition: lwgeom_topo.c:1409
LWT_ELEMID nextCW
Definition: lwgeom_topo.c:1407