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