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