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