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