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[1].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[0].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  return -1;
3057  }
3058 
3059  if ( lwgeom_is_empty(face) )
3060  {
3061  /* no edges in output */
3062  _lwt_release_edges(edges, numfaceedges);
3063  lwgeom_free(face);
3064  return 0;
3065  }
3066 
3067  /* force_lhr, if the face is not the universe */
3068  /* _lwt_FaceByEdges seems to guaranteed RHR */
3069  /* lwgeom_force_clockwise(face); */
3070  if ( face_id ) lwgeom_reverse_in_place(face);
3071 
3072 #if 0
3073  {
3074  size_t sz;
3075  char *wkt = lwgeom_to_wkt(face, WKT_EXTENDED, 6, &sz);
3076  LWDEBUGF(1, "Geometry of face %" LWTFMT_ELEMID " is: %s",
3077  face_id, wkt);
3078  lwfree(wkt);
3079  }
3080 #endif
3081 
3082  facepoly = lwgeom_as_lwpoly(face);
3083  if ( ! facepoly )
3084  {
3085  _lwt_release_edges(edges, numfaceedges);
3086  lwgeom_free(face);
3087  lwerror("Geometry of face %" LWTFMT_ELEMID " is not a polygon", face_id);
3088  return -1;
3089  }
3090 
3091  nseid = prevseid = 0;
3092  seid = lwalloc( sizeof(LWT_ELEMID) * numfaceedges );
3093 
3094  /* for each ring of the face polygon... */
3095  for ( i=0; i<facepoly->nrings; ++i )
3096  {
3097  const POINTARRAY *ring = facepoly->rings[i];
3098  int32_t j = 0;
3099  LWT_ISO_EDGE *nextedge;
3100  LWLINE *nextline;
3101 
3102  LWDEBUGF(1, "Ring %d has %d points", i, ring->npoints);
3103 
3104  while ( j < (int32_t) ring->npoints-1 )
3105  {
3106  LWDEBUGF(1, "Looking for edge covering ring %d from vertex %d",
3107  i, j);
3108 
3109  int edgeno = _lwt_FindNextRingEdge(ring, j, edges, numfaceedges);
3110  if ( edgeno == -1 )
3111  {
3112  /* should never happen */
3113  _lwt_release_edges(edges, numfaceedges);
3114  lwgeom_free(face);
3115  lwfree(seid);
3116  lwerror("No edge (among %d) found to be defining geometry of face %"
3117  LWTFMT_ELEMID, numfaceedges, face_id);
3118  return -1;
3119  }
3120 
3121  nextedge = &(edges[edgeno]);
3122  nextline = nextedge->geom;
3123 
3124  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID
3125  " covers ring %d from vertex %d to %d",
3126  nextedge->edge_id, i, j, j + nextline->points->npoints - 1);
3127 
3128 #if 0
3129  {
3130  size_t sz;
3131  char *wkt = lwgeom_to_wkt(lwline_as_lwgeom(nextline), WKT_EXTENDED, 6, &sz);
3132  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is %s",
3133  nextedge->edge_id, wkt);
3134  lwfree(wkt);
3135  }
3136 #endif
3137 
3138  j += nextline->points->npoints - 1;
3139 
3140  /* Add next edge to the output array */
3141  seid[nseid++] = nextedge->face_left == face_id ?
3142  nextedge->edge_id :
3143  -nextedge->edge_id;
3144 
3145  /* avoid checking again on next time turn */
3146  nextedge->face_left = nextedge->face_right = -1;
3147  }
3148 
3149  /* now "scroll" the list of edges so that the one
3150  * with smaller absolute edge_id is first */
3151  /* Range is: [prevseid, nseid) -- [inclusive, exclusive) */
3152  if ( (nseid - prevseid) > 1 )
3153  {{
3154  LWT_ELEMID minid = 0;
3155  int minidx = 0;
3156  LWDEBUGF(1, "Looking for smallest id among the %d edges "
3157  "composing ring %d", (nseid-prevseid), i);
3158  for ( j=prevseid; j<nseid; ++j )
3159  {
3160  LWT_ELEMID id = llabs(seid[j]);
3161  LWDEBUGF(1, "Abs id of edge in pos %d is %" LWTFMT_ELEMID, j, id);
3162  if ( ! minid || id < minid )
3163  {
3164  minid = id;
3165  minidx = j;
3166  }
3167  }
3168  LWDEBUGF(1, "Smallest id is %" LWTFMT_ELEMID
3169  " at position %d", minid, minidx);
3170  if ( minidx != prevseid )
3171  {
3172  _lwt_RotateElemidArray(seid, prevseid, nseid, minidx);
3173  }
3174  }}
3175 
3176  prevseid = nseid;
3177  }
3178 
3179  lwgeom_free(face);
3180  _lwt_release_edges(edges, numfaceedges);
3181 
3182  *out = seid;
3183  return nseid;
3184 }
3185 
3186 int
3188 {
3189  LWT_ISO_EDGE *oldedge;
3190  LWT_ISO_EDGE newedge;
3191  POINT2D p1, p2, pt;
3192  uint64_t i;
3193  int isclosed = 0;
3194 
3195  /* curve must be simple */
3196  if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) )
3197  {
3198  lwerror("SQL/MM Spatial exception - curve not simple");
3199  return -1;
3200  }
3201 
3202  i = 1;
3203  oldedge = lwt_be_getEdgeById(topo, &edge_id, &i, LWT_COL_EDGE_ALL);
3204  if ( ! oldedge )
3205  {
3206  LWDEBUGF(1, "lwt_ChangeEdgeGeom: "
3207  "lwt_be_getEdgeById returned NULL and set i=%d", i);
3208  if (i == UINT64_MAX)
3209  {
3210  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3211  return -1;
3212  }
3213  else if ( i == 0 )
3214  {
3215  lwerror("SQL/MM Spatial exception - non-existent edge %"
3216  LWTFMT_ELEMID, edge_id);
3217  return -1;
3218  }
3219  else
3220  {
3221  lwerror("Backend coding error: getEdgeById callback returned NULL "
3222  "but numelements output parameter has value %d "
3223  "(expected 0 or 1)", i);
3224  return -1;
3225  }
3226  }
3227 
3228  LWDEBUGF(1, "lwt_ChangeEdgeGeom: "
3229  "old edge has %d points, new edge has %d points",
3230  oldedge->geom->points->npoints, geom->points->npoints);
3231 
3232  /*
3233  * e) Check StartPoint consistency
3234  */
3235  getPoint2d_p(oldedge->geom->points, 0, &p1);
3236  getPoint2d_p(geom->points, 0, &pt);
3237  if ( ! p2d_same(&p1, &pt) )
3238  {
3239  _lwt_release_edges(oldedge, 1);
3240  lwerror("SQL/MM Spatial exception - "
3241  "start node not geometry start point.");
3242  return -1;
3243  }
3244 
3245  /*
3246  * f) Check EndPoint consistency
3247  */
3248  if ( oldedge->geom->points->npoints < 2 )
3249  {
3250  _lwt_release_edges(oldedge, 1);
3251  lwerror("Corrupted topology: edge %" LWTFMT_ELEMID
3252  " has less than 2 vertices", oldedge->edge_id);
3253  return -1;
3254  }
3255  getPoint2d_p(oldedge->geom->points, oldedge->geom->points->npoints-1, &p2);
3256  if ( geom->points->npoints < 2 )
3257  {
3258  _lwt_release_edges(oldedge, 1);
3259  lwerror("Invalid edge: less than 2 vertices");
3260  return -1;
3261  }
3262  getPoint2d_p(geom->points, geom->points->npoints-1, &pt);
3263  if ( ! p2d_same(&pt, &p2) )
3264  {
3265  _lwt_release_edges(oldedge, 1);
3266  lwerror("SQL/MM Spatial exception - "
3267  "end node not geometry end point.");
3268  return -1;
3269  }
3270 
3271  /* Not in the specs:
3272  * if the edge is closed, check we didn't change winding !
3273  * (should be part of isomorphism checking)
3274  */
3275  if ( oldedge->start_node == oldedge->end_node )
3276  {
3277  isclosed = 1;
3278 #if 1 /* TODO: this is actually bogus as a test */
3279  /* check for valid edge (distinct vertices must exist) */
3280  if ( ! _lwt_GetInteriorEdgePoint(geom, &pt) )
3281  {
3282  _lwt_release_edges(oldedge, 1);
3283  lwerror("Invalid edge (no two distinct vertices exist)");
3284  return -1;
3285  }
3286 #endif
3287 
3288  if ( ptarray_isccw(oldedge->geom->points) !=
3289  ptarray_isccw(geom->points) )
3290  {
3291  _lwt_release_edges(oldedge, 1);
3292  lwerror("Edge twist at node POINT(%g %g)", p1.x, p1.y);
3293  return -1;
3294  }
3295  }
3296 
3297  if ( _lwt_CheckEdgeCrossing(topo, oldedge->start_node,
3298  oldedge->end_node, geom, edge_id ) )
3299  {
3300  /* would have called lwerror already, leaking :( */
3301  _lwt_release_edges(oldedge, 1);
3302  return -1;
3303  }
3304 
3305  LWDEBUG(1, "lwt_ChangeEdgeGeom: "
3306  "edge crossing check passed ");
3307 
3308  /*
3309  * Not in the specs:
3310  * Check topological isomorphism
3311  */
3312 
3313  /* Check that the "motion range" doesn't include any node */
3314  // 1. compute combined bbox of old and new edge
3315  GBOX mbox; /* motion box */
3316  lwgeom_add_bbox((LWGEOM*)oldedge->geom); /* just in case */
3317  lwgeom_add_bbox((LWGEOM*)geom); /* just in case */
3318  gbox_union(oldedge->geom->bbox, geom->bbox, &mbox);
3319  // 2. fetch all nodes in the combined box
3320  LWT_ISO_NODE *nodes;
3321  uint64_t numnodes;
3322  nodes = lwt_be_getNodeWithinBox2D(topo, &mbox, &numnodes,
3323  LWT_COL_NODE_ALL, 0);
3324  LWDEBUGF(1, "lwt_be_getNodeWithinBox2D returned %d nodes", numnodes);
3325  if (numnodes == UINT64_MAX)
3326  {
3327  _lwt_release_edges(oldedge, 1);
3328  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3329  return -1;
3330  }
3331  // 3. if any node beside endnodes are found:
3332  if ( numnodes > ( 1 + isclosed ? 0 : 1 ) )
3333  {{
3334  // 3.2. bail out if any node is in one and not the other
3335  for (i=0; i<numnodes; ++i)
3336  {
3337  LWT_ISO_NODE *n = &(nodes[i]);
3338  if ( n->node_id == oldedge->start_node ) continue;
3339  if ( n->node_id == oldedge->end_node ) continue;
3340  const POINT2D *pt = getPoint2d_cp(n->geom->point, 0);
3341  int ocont = ptarray_contains_point_partial(oldedge->geom->points, pt, isclosed, NULL) == LW_INSIDE;
3342  int ncont = ptarray_contains_point_partial(geom->points, pt, isclosed, NULL) == LW_INSIDE;
3343  if (ocont != ncont)
3344  {
3345  size_t sz;
3346  char *wkt = lwgeom_to_wkt(lwpoint_as_lwgeom(n->geom), WKT_ISO, 15, &sz);
3347  _lwt_release_nodes(nodes, numnodes);
3348  lwerror("Edge motion collision at %s", wkt);
3349  lwfree(wkt); /* would not necessarely reach this point */
3350  return -1;
3351  }
3352  }
3353  }}
3354  if ( numnodes ) _lwt_release_nodes(nodes, numnodes);
3355 
3356  LWDEBUG(1, "nodes containment check passed");
3357 
3358  /*
3359  * Check edge adjacency before
3360  * TODO: can be optimized to gather azimuths of all edge ends once
3361  */
3362 
3363  edgeend span_pre, epan_pre;
3364  /* initialize span_pre.myaz and epan_pre.myaz with existing edge */
3365  int res = _lwt_InitEdgeEndByLine(&span_pre, &epan_pre, oldedge->geom, &p1, &p2);
3366  if (res)
3367  return -1; /* lwerror should have been raised */
3368  _lwt_FindAdjacentEdges( topo, oldedge->start_node, &span_pre,
3369  isclosed ? &epan_pre : NULL, edge_id );
3370  _lwt_FindAdjacentEdges( topo, oldedge->end_node, &epan_pre,
3371  isclosed ? &span_pre : NULL, edge_id );
3372 
3373  LWDEBUGF(1, "edges adjacent to old edge are %" LWTFMT_ELEMID
3374  " and %" LWTFMT_ELEMID " (first point), %" LWTFMT_ELEMID
3375  " and %" LWTFMT_ELEMID " (last point)",
3376  span_pre.nextCW, span_pre.nextCCW,
3377  epan_pre.nextCW, epan_pre.nextCCW);
3378 
3379  /* update edge geometry */
3380  newedge.edge_id = edge_id;
3381  newedge.geom = geom;
3382  res = lwt_be_updateEdgesById(topo, &newedge, 1, LWT_COL_EDGE_GEOM);
3383  if (res == -1)
3384  {
3385  _lwt_release_edges(oldedge, 1);
3386  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3387  return -1;
3388  }
3389  if (!res)
3390  {
3391  _lwt_release_edges(oldedge, 1);
3392  lwerror("Unexpected error: %d edges updated when expecting 1", i);
3393  return -1;
3394  }
3395 
3396  /*
3397  * Check edge adjacency after
3398  */
3399  edgeend span_post, epan_post;
3400  /* initialize epan_post.myaz and epan_post.myaz */
3401  res = _lwt_InitEdgeEndByLine(&span_post, &epan_post, geom, &p1, &p2);
3402  if (res)
3403  return -1; /* lwerror should have been raised */
3404  _lwt_FindAdjacentEdges( topo, oldedge->start_node, &span_post,
3405  isclosed ? &epan_post : NULL, edge_id );
3406  _lwt_FindAdjacentEdges( topo, oldedge->end_node, &epan_post,
3407  isclosed ? &span_post : NULL, edge_id );
3408 
3409  LWDEBUGF(1, "edges adjacent to new edge are %" LWTFMT_ELEMID
3410  " and %" LWTFMT_ELEMID " (first point), %" LWTFMT_ELEMID
3411  " and %" LWTFMT_ELEMID " (last point)",
3412  span_pre.nextCW, span_pre.nextCCW,
3413  epan_pre.nextCW, epan_pre.nextCCW);
3414 
3415 
3416  /* Bail out if next CW or CCW edge on start node changed */
3417  if ( span_pre.nextCW != span_post.nextCW ||
3418  span_pre.nextCCW != span_post.nextCCW )
3419  {{
3420  LWT_ELEMID nid = oldedge->start_node;
3421  _lwt_release_edges(oldedge, 1);
3422  lwerror("Edge changed disposition around start node %"
3423  LWTFMT_ELEMID, nid);
3424  return -1;
3425  }}
3426 
3427  /* Bail out if next CW or CCW edge on end node changed */
3428  if ( epan_pre.nextCW != epan_post.nextCW ||
3429  epan_pre.nextCCW != epan_post.nextCCW )
3430  {{
3431  LWT_ELEMID nid = oldedge->end_node;
3432  _lwt_release_edges(oldedge, 1);
3433  lwerror("Edge changed disposition around end node %"
3434  LWTFMT_ELEMID, nid);
3435  return -1;
3436  }}
3437 
3438  /*
3439  -- Update faces MBR of left and right faces
3440  -- TODO: think about ways to optimize this part, like see if
3441  -- the old edge geometry participated in the definition
3442  -- of the current MBR (for shrinking) or the new edge MBR
3443  -- would be larger than the old face MBR...
3444  --
3445  */
3446  const GBOX* oldbox = lwgeom_get_bbox(lwline_as_lwgeom(oldedge->geom));
3447  const GBOX* newbox = lwgeom_get_bbox(lwline_as_lwgeom(geom));
3448  if ( ! gbox_same(oldbox, newbox) )
3449  {
3450  uint64_t facestoupdate = 0;
3451  LWT_ISO_FACE faces[2];
3452  LWGEOM *nface1 = NULL;
3453  LWGEOM *nface2 = NULL;
3454  if ( oldedge->face_left > 0 )
3455  {
3456  nface1 = lwt_GetFaceGeometry(topo, oldedge->face_left);
3457  if ( ! nface1 )
3458  {
3459  lwerror("lwt_ChangeEdgeGeom could not construct face %"
3460  LWTFMT_ELEMID ", on the left of edge %" LWTFMT_ELEMID,
3461  oldedge->face_left, edge_id);
3462  return -1;
3463  }
3464  #if 0
3465  {
3466  size_t sz;
3467  char *wkt = lwgeom_to_wkt(nface1, WKT_EXTENDED, 2, &sz);
3468  LWDEBUGF(1, "new geometry of face left (%d): %s", (int)oldedge->face_left, wkt);
3469  lwfree(wkt);
3470  }
3471  #endif
3472  lwgeom_add_bbox(nface1);
3473  if ( ! nface1->bbox )
3474  {
3475  lwerror("Corrupted topology: face %d, left of edge %d, has no bbox",
3476  oldedge->face_left, edge_id);
3477  return -1;
3478  }
3479  faces[facestoupdate].face_id = oldedge->face_left;
3480  /* ownership left to nface */
3481  faces[facestoupdate++].mbr = nface1->bbox;
3482  }
3483  if ( oldedge->face_right > 0
3484  /* no need to update twice the same face.. */
3485  && oldedge->face_right != oldedge->face_left )
3486  {
3487  nface2 = lwt_GetFaceGeometry(topo, oldedge->face_right);
3488  if ( ! nface2 )
3489  {
3490  lwerror("lwt_ChangeEdgeGeom could not construct face %"
3491  LWTFMT_ELEMID ", on the right of edge %" LWTFMT_ELEMID,
3492  oldedge->face_right, edge_id);
3493  return -1;
3494  }
3495  #if 0
3496  {
3497  size_t sz;
3498  char *wkt = lwgeom_to_wkt(nface2, WKT_EXTENDED, 2, &sz);
3499  LWDEBUGF(1, "new geometry of face right (%d): %s", (int)oldedge->face_right, wkt);
3500  lwfree(wkt);
3501  }
3502  #endif
3503  lwgeom_add_bbox(nface2);
3504  if ( ! nface2->bbox )
3505  {
3506  lwerror("Corrupted topology: face %d, right of edge %d, has no bbox",
3507  oldedge->face_right, edge_id);
3508  return -1;
3509  }
3510  faces[facestoupdate].face_id = oldedge->face_right;
3511  faces[facestoupdate++].mbr = nface2->bbox; /* ownership left to nface */
3512  }
3513  LWDEBUGF(1, "%d faces to update", facestoupdate);
3514  if ( facestoupdate )
3515  {
3516  uint64_t updatedFaces = lwt_be_updateFacesById(topo, &(faces[0]), facestoupdate);
3517  if (updatedFaces != facestoupdate)
3518  {
3519  if (nface1)
3520  lwgeom_free(nface1);
3521  if (nface2)
3522  lwgeom_free(nface2);
3523  _lwt_release_edges(oldedge, 1);
3524  if (updatedFaces == UINT64_MAX)
3525  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3526  else
3527  lwerror("Unexpected error: %d faces found when expecting 1", i);
3528  return -1;
3529  }
3530  }
3531  if ( nface1 ) lwgeom_free(nface1);
3532  if ( nface2 ) lwgeom_free(nface2);
3533  }
3534  else
3535  {
3536  lwnotice("BBOX of changed edge did not change");
3537  }
3538 
3539  LWDEBUG(1, "all done, cleaning up edges");
3540 
3541  _lwt_release_edges(oldedge, 1);
3542  return 0; /* success */
3543 }
3544 
3545 /* Only return CONTAINING_FACE in the node object */
3546 static LWT_ISO_NODE *
3548 {
3549  LWT_ISO_NODE *node;
3550  uint64_t n = 1;
3551 
3552  node = lwt_be_getNodeById( topo, &nid, &n, LWT_COL_NODE_CONTAINING_FACE );
3553  if (n == UINT64_MAX)
3554  {
3555  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3556  return 0;
3557  }
3558  if ( n < 1 ) {
3559  lwerror("SQL/MM Spatial exception - non-existent node");
3560  return 0;
3561  }
3562  if ( node->containing_face == -1 )
3563  {
3564  lwfree(node);
3565  lwerror("SQL/MM Spatial exception - not isolated node");
3566  return 0;
3567  }
3568 
3569  return node;
3570 }
3571 
3572 int
3574 {
3575  LWT_ISO_NODE *node;
3576  int ret;
3577 
3578  node = _lwt_GetIsoNode( topo, nid );
3579  if ( ! node ) return -1;
3580 
3581  if ( lwt_be_ExistsCoincidentNode(topo, pt) )
3582  {
3583  lwfree(node);
3584  lwerror("SQL/MM Spatial exception - coincident node");
3585  return -1;
3586  }
3587 
3588  if ( lwt_be_ExistsEdgeIntersectingPoint(topo, pt) )
3589  {
3590  lwfree(node);
3591  lwerror("SQL/MM Spatial exception - edge crosses node.");
3592  return -1;
3593  }
3594 
3595  /* TODO: check that the new point is in the same containing face !
3596  * See https://trac.osgeo.org/postgis/ticket/3232
3597  */
3598 
3599  node->node_id = nid;
3600  node->geom = pt;
3601  ret = lwt_be_updateNodesById(topo, node, 1,
3603  if ( ret == -1 ) {
3604  lwfree(node);
3605  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3606  return -1;
3607  }
3608 
3609  lwfree(node);
3610  return 0;
3611 }
3612 
3613 int
3615 {
3616  LWT_ISO_NODE *node;
3617  int n = 1;
3618 
3619  node = _lwt_GetIsoNode( topo, nid );
3620  if ( ! node ) return -1;
3621 
3622  n = lwt_be_deleteNodesById( topo, &nid, n );
3623  if ( n == -1 )
3624  {
3625  lwfree(node);
3626  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3627  return -1;
3628  }
3629  if ( n != 1 )
3630  {
3631  lwfree(node);
3632  lwerror("Unexpected error: %d nodes deleted when expecting 1", n);
3633  return -1;
3634  }
3635 
3636  /* TODO: notify to caller about node being removed ?
3637  * See https://trac.osgeo.org/postgis/ticket/3231
3638  */
3639 
3640  lwfree(node);
3641  return 0; /* success */
3642 }
3643 
3644 int
3646 {
3647  LWT_ISO_EDGE deledge;
3648  LWT_ISO_EDGE *edge;
3649  LWT_ELEMID nid[2];
3650  LWT_ISO_NODE upd_node[2];
3651  LWT_ELEMID containing_face;
3652  uint64_t n = 1;
3653  uint64_t i;
3654 
3655  edge = lwt_be_getEdgeById( topo, &id, &n, LWT_COL_EDGE_START_NODE|
3659  if ( ! edge )
3660  {
3661  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3662  return -1;
3663  }
3664  if ( ! n )
3665  {
3666  lwerror("SQL/MM Spatial exception - non-existent edge");
3667  return -1;
3668  }
3669  if ( n > 1 )
3670  {
3671  lwfree(edge);
3672  lwerror("Corrupted topology: more than a single edge have id %"
3673  LWTFMT_ELEMID, id);
3674  return -1;
3675  }
3676 
3677  if ( edge[0].face_left != edge[0].face_right )
3678  {
3679  lwfree(edge);
3680  lwerror("SQL/MM Spatial exception - not isolated edge");
3681  return -1;
3682  }
3683  containing_face = edge[0].face_left;
3684 
3685  nid[0] = edge[0].start_node;
3686  nid[1] = edge[0].end_node;
3687  lwfree(edge);
3688 
3689  n = 2;
3690  edge = lwt_be_getEdgeByNode( topo, nid, &n, LWT_COL_EDGE_EDGE_ID );
3691  if ((n == UINT64_MAX) || (edge == NULL))
3692  {
3693  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3694  return -1;
3695  }
3696  for (i = 0; i < n; ++i)
3697  {
3698  if (edge[i].edge_id != id)
3699  {
3700  lwfree(edge);
3701  lwerror("SQL/MM Spatial exception - not isolated edge");
3702  return -1;
3703  }
3704  }
3705  lwfree(edge);
3706 
3707  deledge.edge_id = id;
3708  n = lwt_be_deleteEdges( topo, &deledge, LWT_COL_EDGE_EDGE_ID );
3709  if (n == UINT64_MAX)
3710  {
3711  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3712  return -1;
3713  }
3714  if ( n != 1 )
3715  {
3716  lwerror("Unexpected error: %d edges deleted when expecting 1", n);
3717  return -1;
3718  }
3719 
3720  upd_node[0].node_id = nid[0];
3721  upd_node[0].containing_face = containing_face;
3722  n = 1;
3723  if ( nid[1] != nid[0] ) {
3724  upd_node[1].node_id = nid[1];
3725  upd_node[1].containing_face = containing_face;
3726  ++n;
3727  }
3728  n = lwt_be_updateNodesById(topo, upd_node, n,
3730  if (n == UINT64_MAX)
3731  {
3732  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3733  return -1;
3734  }
3735 
3736  /* TODO: notify to caller about edge being removed ?
3737  * See https://trac.osgeo.org/postgis/ticket/3248
3738  */
3739 
3740  return 0; /* success */
3741 }
3742 
3743 /* Used by _lwt_RemEdge to update edge face ref on healing
3744  *
3745  * @param of old face id (never 0 as you cannot remove face 0)
3746  * @param nf new face id
3747  * @return 0 on success, -1 on backend error
3748  */
3749 static int
3751 {
3752  LWT_ISO_EDGE sel_edge, upd_edge;
3753  int ret;
3754 
3755  assert( of != 0 );
3756 
3757  /* Update face_left for all edges still referencing old face */
3758  sel_edge.face_left = of;
3759  upd_edge.face_left = nf;
3760  ret = lwt_be_updateEdges(topo, &sel_edge, LWT_COL_EDGE_FACE_LEFT,
3761  &upd_edge, LWT_COL_EDGE_FACE_LEFT,
3762  NULL, 0);
3763  if ( ret == -1 ) return -1;
3764 
3765  /* Update face_right for all edges still referencing old face */
3766  sel_edge.face_right = of;
3767  upd_edge.face_right = nf;
3768  ret = lwt_be_updateEdges(topo, &sel_edge, LWT_COL_EDGE_FACE_RIGHT,
3769  &upd_edge, LWT_COL_EDGE_FACE_RIGHT,
3770  NULL, 0);
3771  if ( ret == -1 ) return -1;
3772 
3773  return 0;
3774 }
3775 
3776 /* Used by _lwt_RemEdge to update node face ref on healing
3777  *
3778  * @param of old face id (never 0 as you cannot remove face 0)
3779  * @param nf new face id
3780  * @return 0 on success, -1 on backend error
3781  */
3782 static int
3784 {
3785  LWT_ISO_NODE sel, upd;
3786  int ret;
3787 
3788  assert( of != 0 );
3789 
3790  /* Update face_left for all edges still referencing old face */
3791  sel.containing_face = of;
3792  upd.containing_face = nf;
3795  NULL, 0);
3796  if ( ret == -1 ) return -1;
3797 
3798  return 0;
3799 }
3800 
3801 /* Used by lwt_RemEdgeModFace and lwt_RemEdgeNewFaces
3802  *
3803  * Returns -1 on error, identifier of the face that takes up the space
3804  * previously occupied by the removed edge if modFace is 1, identifier of
3805  * the created face (0 if none) if modFace is 0.
3806  */
3807 static LWT_ELEMID
3808 _lwt_RemEdge( LWT_TOPOLOGY* topo, LWT_ELEMID edge_id, int modFace )
3809 {
3810  uint64_t i, nedges, nfaces, fields;
3811  LWT_ISO_EDGE *edge = NULL;
3812  LWT_ISO_EDGE *upd_edge = NULL;
3813  LWT_ISO_EDGE upd_edge_left[2];
3814  int nedge_left = 0;
3815  LWT_ISO_EDGE upd_edge_right[2];
3816  int nedge_right = 0;
3817  LWT_ISO_NODE upd_node[2];
3818  int nnode = 0;
3819  LWT_ISO_FACE *faces = NULL;
3820  LWT_ISO_FACE newface;
3821  LWT_ELEMID node_ids[2];
3822  LWT_ELEMID face_ids[2];
3823  int fnode_edges = 0; /* number of edges on the first node (excluded
3824  * the one being removed ) */
3825  int lnode_edges = 0; /* number of edges on the last node (excluded
3826  * the one being removed ) */
3827 
3828  newface.face_id = 0;
3829 
3830  i = 1;
3831  edge = lwt_be_getEdgeById(topo, &edge_id, &i, LWT_COL_EDGE_ALL);
3832  if (!edge)
3833  {
3834  LWDEBUGF(1, "lwt_be_getEdgeById returned NULL and set i=%d", i);
3835  if (i == UINT64_MAX)
3836  {
3837  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3838  return -1;
3839  }
3840  else if (i == 0)
3841  {
3842  lwerror("SQL/MM Spatial exception - non-existent edge %" LWTFMT_ELEMID, edge_id);
3843  return -1;
3844  }
3845  else
3846  {
3847  lwerror(
3848  "Backend coding error: getEdgeById callback returned NULL "
3849  "but numelements output parameter has value %d "
3850  "(expected 0 or 1)",
3851  i);
3852  return -1;
3853  }
3854  }
3855 
3856  if ( ! lwt_be_checkTopoGeomRemEdge(topo, edge_id,
3857  edge->face_left, edge->face_right) )
3858  {
3860  return -1;
3861  }
3862 
3863  LWDEBUG(1, "Updating next_{right,left}_face of ring edges...");
3864 
3865  /* Update edge linking */
3866 
3867  nedges = 0;
3868  node_ids[nedges++] = edge->start_node;
3869  if ( edge->end_node != edge->start_node )
3870  {
3871  node_ids[nedges++] = edge->end_node;
3872  }
3876  upd_edge = lwt_be_getEdgeByNode( topo, &(node_ids[0]), &nedges, fields );
3877  if (nedges == UINT64_MAX)
3878  {
3879  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3880  return -1;
3881  }
3882  nedge_left = nedge_right = 0;
3883  for ( i=0; i<nedges; ++i )
3884  {
3885  LWT_ISO_EDGE *e = &(upd_edge[i]);
3886  if ( e->edge_id == edge_id ) continue;
3887  if ( e->start_node == edge->start_node || e->end_node == edge->start_node )
3888  {
3889  ++fnode_edges;
3890  }
3891  if ( e->start_node == edge->end_node || e->end_node == edge->end_node )
3892  {
3893  ++lnode_edges;
3894  }
3895  if ( e->next_left == -edge_id )
3896  {
3897  upd_edge_left[nedge_left].edge_id = e->edge_id;
3898  upd_edge_left[nedge_left++].next_left =
3899  edge->next_left != edge_id ? edge->next_left : edge->next_right;
3900  }
3901  else if ( e->next_left == edge_id )
3902  {
3903  upd_edge_left[nedge_left].edge_id = e->edge_id;
3904  upd_edge_left[nedge_left++].next_left =
3905  edge->next_right != -edge_id ? edge->next_right : edge->next_left;
3906  }
3907 
3908  if ( e->next_right == -edge_id )
3909  {
3910  upd_edge_right[nedge_right].edge_id = e->edge_id;
3911  upd_edge_right[nedge_right++].next_right =
3912  edge->next_left != edge_id ? edge->next_left : edge->next_right;
3913  }
3914  else if ( e->next_right == edge_id )
3915  {
3916  upd_edge_right[nedge_right].edge_id = e->edge_id;
3917  upd_edge_right[nedge_right++].next_right =
3918  edge->next_right != -edge_id ? edge->next_right : edge->next_left;
3919  }
3920  }
3921 
3922  if ( nedge_left )
3923  {
3924  LWDEBUGF(1, "updating %d 'next_left' edges", nedge_left);
3925  /* update edges in upd_edge_left set next_left */
3926  int result = lwt_be_updateEdgesById(topo, &(upd_edge_left[0]), nedge_left, LWT_COL_EDGE_NEXT_LEFT);
3927  if (result == -1)
3928  {
3929  _lwt_release_edges(edge, 1);
3930  lwfree(upd_edge);
3931  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3932  return -1;
3933  }
3934  }
3935  if ( nedge_right )
3936  {
3937  LWDEBUGF(1, "updating %d 'next_right' edges", nedge_right);
3938  /* update edges in upd_edge_right set next_right */
3939  int result = lwt_be_updateEdgesById(topo, &(upd_edge_right[0]), nedge_right, LWT_COL_EDGE_NEXT_RIGHT);
3940  if (result == -1)
3941  {
3942  _lwt_release_edges(edge, 1);
3943  lwfree(upd_edge);
3944  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3945  return -1;
3946  }
3947  }
3948  LWDEBUGF(1, "releasing %d updateable edges in %p", nedges, upd_edge);
3949  lwfree(upd_edge);
3950 
3951  /* Id of face that will take up all the space previously
3952  * taken by left and right faces of the edge */
3953  LWT_ELEMID floodface;
3954 
3955  /* Find floodface, and update its mbr if != 0 */
3956  if ( edge->face_left == edge->face_right )
3957  {
3958  floodface = edge->face_right;
3959  }
3960  else
3961  {
3962  /* Two faces healed */
3963  if ( edge->face_left == 0 || edge->face_right == 0 )
3964  {
3965  floodface = 0;
3966  LWDEBUG(1, "floodface is universe");
3967  }
3968  else
3969  {
3970  /* we choose right face as the face that will remain
3971  * to be symmetric with ST_AddEdgeModFace */
3972  floodface = edge->face_right;
3973  LWDEBUGF(1, "floodface is %" LWTFMT_ELEMID, floodface);
3974  /* update mbr of floodface as union of mbr of both faces */
3975  face_ids[0] = edge->face_left;
3976  face_ids[1] = edge->face_right;
3977  nfaces = 2;
3978  fields = LWT_COL_FACE_ALL;
3979  faces = lwt_be_getFaceById(topo, face_ids, &nfaces, fields);
3980  if (nfaces == UINT64_MAX)
3981  {
3982  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3983  return -1;
3984  }
3985  GBOX *box1=NULL;
3986  GBOX *box2=NULL;
3987  for ( i=0; i<nfaces; ++i )
3988  {
3989  if ( faces[i].face_id == edge->face_left )
3990  {
3991  if ( ! box1 ) box1 = faces[i].mbr;
3992  else
3993  {
3994  i = edge->face_left;
3995  _lwt_release_edges(edge, 1);
3996  _lwt_release_faces(faces, nfaces);
3997  lwerror("corrupted topology: more than 1 face have face_id=%"
3998  LWTFMT_ELEMID, i);
3999  return -1;
4000  }
4001  }
4002  else if ( faces[i].face_id == edge->face_right )
4003  {
4004  if ( ! box2 ) box2 = faces[i].mbr;
4005  else
4006  {
4007  i = edge->face_right;
4008  _lwt_release_edges(edge, 1);
4009  _lwt_release_faces(faces, nfaces);
4010  lwerror("corrupted topology: more than 1 face have face_id=%"
4011  LWTFMT_ELEMID, i);
4012  return -1;
4013  }
4014  }
4015  else
4016  {
4017  i = faces[i].face_id;
4018  _lwt_release_edges(edge, 1);
4019  _lwt_release_faces(faces, nfaces);
4020  lwerror("Backend coding error: getFaceById returned face "
4021  "with non-requested id %" LWTFMT_ELEMID, i);
4022  return -1;
4023  }
4024  }
4025  if ( ! box1 ) {
4026  i = edge->face_left;
4027  _lwt_release_edges(edge, 1);
4028  _lwt_release_faces(faces, nfaces);
4029  lwerror("corrupted topology: no face have face_id=%"
4030  LWTFMT_ELEMID " (left face for edge %"
4031  LWTFMT_ELEMID ")", i, edge_id);
4032  return -1;
4033  }
4034  if ( ! box2 ) {
4035  i = edge->face_right;
4036  _lwt_release_edges(edge, 1);
4037  _lwt_release_faces(faces, nfaces);
4038  lwerror("corrupted topology: no face have face_id=%"
4039  LWTFMT_ELEMID " (right face for edge %"
4040  LWTFMT_ELEMID ")", i, edge_id);
4041  return -1;
4042  }
4043  gbox_merge(box2, box1); /* box1 is now the union of the two */
4044  newface.mbr = box1;
4045  if ( modFace )
4046  {
4047  newface.face_id = floodface;
4048  int result = lwt_be_updateFacesById(topo, &newface, 1);
4049  _lwt_release_faces(faces, 2);
4050  if (result == -1)
4051  {
4052  _lwt_release_edges(edge, 1);
4053  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4054  return -1;
4055  }
4056  if (result != 1)
4057  {
4058  _lwt_release_edges(edge, 1);
4059  lwerror("Unexpected error: %d faces updated when expecting 1", i);
4060  return -1;
4061  }
4062  }
4063  else
4064  {
4065  /* New face replaces the old two faces */
4066  newface.face_id = -1;
4067  int result = lwt_be_insertFaces(topo, &newface, 1);
4068  _lwt_release_faces(faces, 2);
4069  if (result == -1)
4070  {
4071  _lwt_release_edges(edge, 1);
4072  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4073  return -1;
4074  }
4075  if (result != 1)
4076  {
4077  _lwt_release_edges(edge, 1);
4078  lwerror("Unexpected error: %d faces inserted when expecting 1", result);
4079  return -1;
4080  }
4081  floodface = newface.face_id;
4082  }
4083  }
4084 
4085  /* Update face references for edges and nodes still referencing
4086  * the removed face(s) */
4087 
4088  if ( edge->face_left != floodface )
4089  {
4090  if ( -1 == _lwt_UpdateEdgeFaceRef(topo, edge->face_left, floodface) )
4091  {
4092  _lwt_release_edges(edge, 1);
4093  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4094  return -1;
4095  }
4096  if ( -1 == _lwt_UpdateNodeFaceRef(topo, edge->face_left, floodface) )
4097  {
4098  _lwt_release_edges(edge, 1);
4099  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4100  return -1;
4101  }
4102  }
4103 
4104  if ( edge->face_right != floodface )
4105  {
4106  if ( -1 == _lwt_UpdateEdgeFaceRef(topo, edge->face_right, floodface) )
4107  {
4108  _lwt_release_edges(edge, 1);
4109  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4110  return -1;
4111  }
4112  if ( -1 == _lwt_UpdateNodeFaceRef(topo, edge->face_right, floodface) )
4113  {
4114  _lwt_release_edges(edge, 1);
4115  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4116  return -1;
4117  }
4118  }
4119 
4120  /* Update topogeoms on heal */
4121  if ( ! lwt_be_updateTopoGeomFaceHeal(topo,
4122  edge->face_right, edge->face_left,
4123  floodface) )
4124  {
4125  _lwt_release_edges(edge, 1);
4127  return -1;
4128  }
4129  } /* two faces healed */
4130 
4131  /* Delete the edge */
4132  int result = lwt_be_deleteEdges(topo, edge, LWT_COL_EDGE_EDGE_ID);
4133  if (result == -1)
4134  {
4135  _lwt_release_edges(edge, 1);
4136  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4137  return -1;
4138  }
4139 
4140  /* If any of the edge nodes remained isolated, set
4141  * containing_face = floodface
4142  */
4143  if ( ! fnode_edges )
4144  {
4145  upd_node[nnode].node_id = edge->start_node;
4146  upd_node[nnode].containing_face = floodface;
4147  ++nnode;
4148  }
4149  if ( edge->end_node != edge->start_node && ! lnode_edges )
4150  {
4151  upd_node[nnode].node_id = edge->end_node;
4152  upd_node[nnode].containing_face = floodface;
4153  ++nnode;
4154  }
4155  if ( nnode )
4156  {
4157  int result = lwt_be_updateNodesById(topo, upd_node, nnode, LWT_COL_NODE_CONTAINING_FACE);
4158  if (result == -1)
4159  {
4160  _lwt_release_edges(edge, 1);
4161  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4162  return -1;
4163  }
4164  }
4165 
4166  if ( edge->face_left != edge->face_right )
4167  /* or there'd be no face to remove */
4168  {
4169  LWT_ELEMID ids[2];
4170  int nids = 0;
4171  if ( edge->face_right != floodface )
4172  ids[nids++] = edge->face_right;
4173  if ( edge->face_left != floodface )
4174  ids[nids++] = edge->face_left;
4175  int result = lwt_be_deleteFacesById(topo, ids, nids);
4176  if (result == -1)
4177  {
4178  _lwt_release_edges(edge, 1);
4179  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4180  return -1;
4181  }
4182  }
4183 
4184  _lwt_release_edges(edge, 1);
4185  return modFace ? floodface : newface.face_id;
4186 }
4187 
4188 LWT_ELEMID
4190 {
4191  return _lwt_RemEdge( topo, edge_id, 1 );
4192 }
4193 
4194 LWT_ELEMID
4196 {
4197  return _lwt_RemEdge( topo, edge_id, 0 );
4198 }
4199 
4200 static LWT_ELEMID
4202  int modEdge )
4203 {
4204  LWT_ELEMID ids[2];
4205  LWT_ELEMID commonnode = -1;
4206  int caseno = 0;
4207  LWT_ISO_EDGE *node_edges;
4208  uint64_t num_node_edges;
4209  LWT_ISO_EDGE *edges;
4210  LWT_ISO_EDGE *e1 = NULL;
4211  LWT_ISO_EDGE *e2 = NULL;
4212  LWT_ISO_EDGE newedge, updedge, seledge;
4213  uint64_t nedges, i;
4214  int e1freenode;
4215  int e2sign, e2freenode;
4216  POINTARRAY *pa;
4217  char buf[256];
4218  char *ptr;
4219  size_t bufleft = 256;
4220 
4221  ptr = buf;
4222 
4223  /* NOT IN THE SPECS: see if the same edge is given twice.. */
4224  if ( eid1 == eid2 )
4225  {
4226  lwerror("Cannot heal edge %" LWTFMT_ELEMID
4227  " with itself, try with another", eid1);
4228  return -1;
4229  }
4230  ids[0] = eid1;
4231  ids[1] = eid2;
4232  nedges = 2;
4233  edges = lwt_be_getEdgeById(topo, ids, &nedges, LWT_COL_EDGE_ALL);
4234  if ((nedges == UINT64_MAX) || (edges == NULL))
4235  {
4236  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4237  return -1;
4238  }
4239  for ( i=0; i<nedges; ++i )
4240  {
4241  if ( edges[i].edge_id == eid1 ) {
4242  if ( e1 ) {
4243  _lwt_release_edges(edges, nedges);
4244  lwerror("Corrupted topology: multiple edges have id %"
4245  LWTFMT_ELEMID, eid1);
4246  return -1;
4247  }
4248  e1 = &(edges[i]);
4249  }
4250  else if ( edges[i].edge_id == eid2 ) {
4251  if ( e2 ) {
4252  _lwt_release_edges(edges, nedges);
4253  lwerror("Corrupted topology: multiple edges have id %"
4254  LWTFMT_ELEMID, eid2);
4255  return -1;
4256  }
4257  e2 = &(edges[i]);
4258  }
4259  }
4260  if ( ! e1 )
4261  {
4262  _lwt_release_edges(edges, nedges);
4263  lwerror(
4264  "SQL/MM Spatial exception - non-existent edge %" LWTFMT_ELEMID,
4265  eid1);
4266  return -1;
4267  }
4268  if ( ! e2 )
4269  {
4270  _lwt_release_edges(edges, nedges);
4271  lwerror(
4272  "SQL/MM Spatial exception - non-existent edge %" LWTFMT_ELEMID,
4273  eid2);
4274  return -1;
4275  }
4276 
4277  /* NOT IN THE SPECS: See if any of the two edges are closed. */
4278  if ( e1->start_node == e1->end_node )
4279  {
4280  _lwt_release_edges(edges, nedges);
4281  lwerror("Edge %" LWTFMT_ELEMID " is closed, cannot heal to edge %"
4282  LWTFMT_ELEMID, eid1, eid2);
4283  return -1;
4284  }
4285  if ( e2->start_node == e2->end_node )
4286  {
4287  _lwt_release_edges(edges, nedges);
4288  lwerror("Edge %" LWTFMT_ELEMID " is closed, cannot heal to edge %"
4289  LWTFMT_ELEMID, eid2, eid1);
4290  return -1;
4291  }
4292 
4293  /* Find common node */
4294 
4295  if ( e1->end_node == e2->start_node )
4296  {
4297  commonnode = e1->end_node;
4298  caseno = 1;
4299  }
4300  else if ( e1->end_node == e2->end_node )
4301  {
4302  commonnode = e1->end_node;
4303  caseno = 2;
4304  }
4305  /* Check if any other edge is connected to the common node, if found */
4306  if ( commonnode != -1 )
4307  {
4308  num_node_edges = 1;
4309  node_edges = lwt_be_getEdgeByNode( topo, &commonnode,
4310  &num_node_edges, LWT_COL_EDGE_EDGE_ID );
4311  if (num_node_edges == UINT64_MAX)
4312  {
4313  _lwt_release_edges(edges, nedges);
4314  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4315  return -1;
4316  }
4317  for (i=0; i<num_node_edges; ++i)
4318  {
4319  int r;
4320  if ( node_edges[i].edge_id == eid1 ) continue;
4321  if ( node_edges[i].edge_id == eid2 ) continue;
4322  commonnode = -1;
4323  /* append to string, for error message */
4324  if ( bufleft > 0 ) {
4325  r = snprintf(ptr, bufleft, "%s%" LWTFMT_ELEMID,
4326  ( ptr==buf ? "" : "," ), node_edges[i].edge_id);
4327  if ( r >= (int) bufleft )
4328  {
4329  bufleft = 0;
4330  buf[252] = '.';
4331  buf[253] = '.';
4332  buf[254] = '.';
4333  buf[255] = '\0';
4334  }
4335  else
4336  {
4337  bufleft -= r;
4338  ptr += r;
4339  }
4340  }
4341  }
4342  lwfree(node_edges);
4343  }
4344 
4345  if ( commonnode == -1 )
4346  {
4347  if ( e1->start_node == e2->start_node )
4348  {
4349  commonnode = e1->start_node;
4350  caseno = 3;
4351  }
4352  else if ( e1->start_node == e2->end_node )
4353  {
4354  commonnode = e1->start_node;
4355  caseno = 4;
4356  }
4357  /* Check if any other edge is connected to the common node, if found */
4358  if ( commonnode != -1 )
4359  {
4360  num_node_edges = 1;
4361  node_edges = lwt_be_getEdgeByNode( topo, &commonnode,
4362  &num_node_edges, LWT_COL_EDGE_EDGE_ID );
4363  if (num_node_edges == UINT64_MAX)
4364  {
4365  _lwt_release_edges(edges, nedges);
4366  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4367  return -1;
4368  }
4369  for (i=0; i<num_node_edges; ++i)
4370  {
4371  int r;
4372  if ( node_edges[i].edge_id == eid1 ) continue;
4373  if ( node_edges[i].edge_id == eid2 ) continue;
4374  commonnode = -1;
4375  /* append to string, for error message */
4376  if ( bufleft > 0 ) {
4377  r = snprintf(ptr, bufleft, "%s%" LWTFMT_ELEMID,
4378  ( ptr==buf ? "" : "," ), node_edges[i].edge_id);
4379  if ( r >= (int) bufleft )
4380  {
4381  bufleft = 0;
4382  buf[252] = '.';
4383  buf[253] = '.';
4384  buf[254] = '.';
4385  buf[255] = '\0';
4386  }
4387  else
4388  {
4389  bufleft -= r;
4390  ptr += r;
4391  }
4392  }
4393  }
4394  if ( num_node_edges ) lwfree(node_edges);
4395  }
4396  }
4397 
4398  if ( commonnode == -1 )
4399  {
4400  _lwt_release_edges(edges, nedges);
4401  if ( ptr != buf )
4402  {
4403  lwerror("SQL/MM Spatial exception - other edges connected (%s)",
4404  buf);
4405  }
4406  else
4407  {
4408  lwerror("SQL/MM Spatial exception - non-connected edges");
4409  }
4410  return -1;
4411  }
4412 
4413  if ( ! lwt_be_checkTopoGeomRemNode(topo, commonnode,
4414  eid1, eid2 ) )
4415  {
4416  _lwt_release_edges(edges, nedges);
4418  return -1;
4419  }
4420 
4421  /* Construct the geometry of the new edge */
4422  switch (caseno)
4423  {
4424  case 1: /* e1.end = e2.start */
4425  pa = ptarray_clone_deep(e1->geom->points);
4426  //pa = ptarray_merge(pa, e2->geom->points);
4427  ptarray_append_ptarray(pa, e2->geom->points, 0);
4428  newedge.start_node = e1->start_node;
4429  newedge.end_node = e2->end_node;
4430  newedge.next_left = e2->next_left;
4431  newedge.next_right = e1->next_right;
4432  e1freenode = 1;
4433  e2freenode = -1;
4434  e2sign = 1;
4435  break;
4436  case 2: /* e1.end = e2.end */
4437  {
4438  POINTARRAY *pa2;
4439  pa2 = ptarray_clone_deep(e2->geom->points);
4441  pa = ptarray_clone_deep(e1->geom->points);
4442  //pa = ptarray_merge(e1->geom->points, pa);
4443  ptarray_append_ptarray(pa, pa2, 0);
4444  ptarray_free(pa2);
4445  newedge.start_node = e1->start_node;
4446  newedge.end_node = e2->start_node;
4447  newedge.next_left = e2->next_right;
4448  newedge.next_right = e1->next_right;
4449  e1freenode = 1;
4450  e2freenode = 1;
4451  e2sign = -1;
4452  break;
4453  }
4454  case 3: /* e1.start = e2.start */
4455  pa = ptarray_clone_deep(e2->geom->points);
4457  //pa = ptarray_merge(pa, e1->geom->points);
4458  ptarray_append_ptarray(pa, e1->geom->points, 0);
4459  newedge.end_node = e1->end_node;
4460  newedge.start_node = e2->end_node;
4461  newedge.next_left = e1->next_left;
4462  newedge.next_right = e2->next_left;
4463  e1freenode = -1;
4464  e2freenode = -1;
4465  e2sign = -1;
4466  break;
4467  case 4: /* e1.start = e2.end */
4468  pa = ptarray_clone_deep(e2->geom->points);
4469  //pa = ptarray_merge(pa, e1->geom->points);
4470  ptarray_append_ptarray(pa, e1->geom->points, 0);
4471  newedge.end_node = e1->end_node;
4472  newedge.start_node = e2->start_node;
4473  newedge.next_left = e1->next_left;
4474  newedge.next_right = e2->next_right;
4475  e1freenode = -1;
4476  e2freenode = 1;
4477  e2sign = 1;
4478  break;
4479  default:
4480  pa = NULL;
4481  e1freenode = 0;
4482  e2freenode = 0;
4483  e2sign = 0;
4484  _lwt_release_edges(edges, nedges);
4485  lwerror("Coding error: caseno=%d should never happen", caseno);
4486  break;
4487  }
4488  newedge.geom = lwline_construct(topo->srid, NULL, pa);
4489 
4490  if ( modEdge )
4491  {
4492  /* Update data of the first edge */
4493  newedge.edge_id = eid1;
4494  int result = lwt_be_updateEdgesById(topo,
4495  &newedge,
4496  1,
4499  if (result == -1)
4500  {
4501  lwline_free(newedge.geom);
4502  _lwt_release_edges(edges, nedges);
4503  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4504  return -1;
4505  }
4506  else if (result != 1)
4507  {
4508  lwline_free(newedge.geom);
4509  if ( edges ) _lwt_release_edges(edges, nedges);
4510  lwerror("Unexpected error: %d edges updated when expecting 1", i);
4511  return -1;
4512  }
4513  }
4514  else
4515  {
4516  /* Add new edge */
4517  newedge.edge_id = -1;
4518  newedge.face_left = e1->face_left;
4519  newedge.face_right = e1->face_right;
4520  int result = lwt_be_insertEdges(topo, &newedge, 1);
4521  if (result == -1)
4522  {
4523  lwline_free(newedge.geom);
4524  _lwt_release_edges(edges, nedges);
4525  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4526  return -1;
4527  }
4528  else if (result == 0)
4529  {
4530  lwline_free(newedge.geom);
4531  _lwt_release_edges(edges, nedges);
4532  lwerror("Insertion of split edge failed (no reason)");
4533  return -1;
4534  }
4535  }
4536  lwline_free(newedge.geom);
4537 
4538  /*
4539  -- Update next_left_edge/next_right_edge for
4540  -- any edge having them still pointing at the edge being removed
4541  -- (eid2 only when modEdge, or both otherwise)
4542  --
4543  -- NOTE:
4544  -- e#freenode is 1 when edge# end node was the common node
4545  -- and -1 otherwise. This gives the sign of possibly found references
4546  -- to its "free" (non connected to other edge) endnode.
4547  -- e2sign is -1 if edge1 direction is opposite to edge2 direction,
4548  -- or 1 otherwise.
4549  --
4550  */
4551 
4552  /* update edges connected to e2's boundary from their end node */
4553  seledge.next_left = e2freenode * eid2;
4554  updedge.next_left = e2freenode * newedge.edge_id * e2sign;
4555  int result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_LEFT, &updedge, LWT_COL_EDGE_NEXT_LEFT, NULL, 0);
4556  if (result == -1)
4557  {
4558  _lwt_release_edges(edges, nedges);
4559  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4560  return -1;
4561  }
4562 
4563  /* update edges connected to e2's boundary from their start node */
4564  seledge.next_right = e2freenode * eid2;
4565  updedge.next_right = e2freenode * newedge.edge_id * e2sign;
4566  result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_RIGHT, &updedge, LWT_COL_EDGE_NEXT_RIGHT, NULL, 0);
4567  if (result == -1)
4568  {
4569  _lwt_release_edges(edges, nedges);
4570  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4571  return -1;
4572  }
4573 
4574  if ( ! modEdge )
4575  {
4576  /* update edges connected to e1's boundary from their end node */
4577  seledge.next_left = e1freenode * eid1;
4578  updedge.next_left = e1freenode * newedge.edge_id;
4579  result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_LEFT, &updedge, LWT_COL_EDGE_NEXT_LEFT, NULL, 0);
4580  if (result == -1)
4581  {
4582  _lwt_release_edges(edges, nedges);
4583  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4584  return -1;
4585  }
4586 
4587  /* update edges connected to e1's boundary from their start node */
4588  seledge.next_right = e1freenode * eid1;
4589  updedge.next_right = e1freenode * newedge.edge_id;
4590  result = lwt_be_updateEdges(topo, &seledge, LWT_COL_EDGE_NEXT_RIGHT, &updedge, LWT_COL_EDGE_NEXT_RIGHT, NULL, 0);
4591  if (result == -1)
4592  {
4593  _lwt_release_edges(edges, nedges);
4594  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4595  return -1;
4596  }
4597  }
4598 
4599  /* delete the edges (only second on modEdge or both) */
4600  result = lwt_be_deleteEdges(topo, e2, LWT_COL_EDGE_EDGE_ID);
4601  if (result == -1)
4602  {
4603  _lwt_release_edges(edges, nedges);
4604  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4605  return -1;
4606  }
4607  if ( ! modEdge ) {
4609  if (result == -1)
4610  {
4611  _lwt_release_edges(edges, nedges);
4612  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4613  return -1;
4614  }
4615  }
4616 
4617  _lwt_release_edges(edges, nedges);
4618 
4619  /* delete the common node */
4620  i = lwt_be_deleteNodesById( topo, &commonnode, 1 );
4621  if (result == -1)
4622  {
4623  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4624  return -1;
4625  }
4626 
4627  /*
4628  --
4629  -- NOT IN THE SPECS:
4630  -- Drop composition rows involving second
4631  -- edge, as the first edge took its space,
4632  -- and all affected TopoGeom have been previously checked
4633  -- for being composed by both edges.
4634  */
4635  if ( ! lwt_be_updateTopoGeomEdgeHeal(topo,
4636  eid1, eid2, newedge.edge_id) )
4637  {
4639  return -1;
4640  }
4641 
4642  return modEdge ? commonnode : newedge.edge_id;
4643 }
4644 
4645 LWT_ELEMID
4647 {
4648  return _lwt_HealEdges( topo, e1, e2, 1 );
4649 }
4650 
4651 LWT_ELEMID
4653 {
4654  return _lwt_HealEdges( topo, e1, e2, 0 );
4655 }
4656 
4657 LWT_ELEMID
4658 lwt_GetNodeByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol)
4659 {
4660  LWT_ISO_NODE *elem;
4661  uint64_t num;
4662  int flds = LWT_COL_NODE_NODE_ID|LWT_COL_NODE_GEOM; /* geom not needed */
4663  LWT_ELEMID id = 0;
4664  POINT2D qp; /* query point */
4665 
4666  if ( ! getPoint2d_p(pt->point, 0, &qp) )
4667  {
4668  lwerror("Empty query point");
4669  return -1;
4670  }
4671  elem = lwt_be_getNodeWithinDistance2D(topo, pt, tol, &num, flds, 0);
4672  if (num == UINT64_MAX)
4673  {
4674  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4675  return -1;
4676  }
4677  else if ( num )
4678  {
4679  if ( num > 1 )
4680  {
4681  _lwt_release_nodes(elem, num);
4682  lwerror("Two or more nodes found");
4683  return -1;
4684  }
4685  id = elem[0].node_id;
4686  _lwt_release_nodes(elem, num);
4687  }
4688 
4689  return id;
4690 }
4691 
4692 LWT_ELEMID
4693 lwt_GetEdgeByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol)
4694 {
4695  LWT_ISO_EDGE *elem;
4696  uint64_t num, i;
4697  int flds = LWT_COL_EDGE_EDGE_ID|LWT_COL_EDGE_GEOM; /* GEOM is not needed */
4698  LWT_ELEMID id = 0;
4699  LWGEOM *qp = lwpoint_as_lwgeom(pt); /* query point */
4700 
4701  if ( lwgeom_is_empty(qp) )
4702  {
4703  lwerror("Empty query point");
4704  return -1;
4705  }
4706  elem = lwt_be_getEdgeWithinDistance2D(topo, pt, tol, &num, flds, 0);
4707  if (num == UINT64_MAX)
4708  {
4709  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4710  return -1;
4711  }
4712  for (i=0; i<num;++i)
4713  {
4714  LWT_ISO_EDGE *e = &(elem[i]);
4715 #if 0
4716  LWGEOM* geom;
4717  double dist;
4718 
4719  if ( ! e->geom )
4720  {
4721  _lwt_release_edges(elem, num);
4722  lwnotice("Corrupted topology: edge %" LWTFMT_ELEMID
4723  " has null geometry", e->edge_id);
4724  continue;
4725  }
4726 
4727  /* Should we check for intersection not being on an endpoint
4728  * as documented ? */
4729  geom = lwline_as_lwgeom(e->geom);
4730  dist = lwgeom_mindistance2d_tolerance(geom, qp, tol);
4731  if ( dist > tol ) continue;
4732 #endif
4733 
4734  if ( id )
4735  {
4736  _lwt_release_edges(elem, num);
4737  lwerror("Two or more edges found");
4738  return -1;
4739  }
4740  else id = e->edge_id;
4741  }
4742 
4743  if ( num ) _lwt_release_edges(elem, num);
4744 
4745  return id;
4746 }
4747 
4748 LWT_ELEMID
4749 lwt_GetFaceByPoint(LWT_TOPOLOGY *topo, LWPOINT *pt, double tol)
4750 {
4751  LWT_ELEMID id = 0;
4752  LWT_ISO_EDGE *elem;
4753  uint64_t num, i;
4754  int flds = LWT_COL_EDGE_EDGE_ID |
4758  LWGEOM *qp = lwpoint_as_lwgeom(pt);
4759 
4760  id = lwt_be_getFaceContainingPoint(topo, pt);
4761  if ( id == -2 ) {
4762  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4763  return -1;
4764  }
4765 
4766  if ( id > 0 )
4767  {
4768  return id;
4769  }
4770  id = 0; /* or it'll be -1 for not found */
4771 
4772  LWDEBUG(1, "No face properly contains query point,"
4773  " looking for edges");
4774 
4775  /* Not in a face, may be in universe or on edge, let's check
4776  * for distance */
4777  /* NOTE: we never pass a tolerance of 0 to avoid ever using
4778  * ST_Within, which doesn't include endpoints matches */
4779  elem = lwt_be_getEdgeWithinDistance2D(topo, pt, tol?tol:1e-5, &num, flds, 0);
4780  if (num == UINT64_MAX)
4781  {
4782  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4783  return -1;
4784  }
4785  for (i=0; i<num; ++i)
4786  {
4787  LWT_ISO_EDGE *e = &(elem[i]);
4788  LWT_ELEMID eface = 0;
4789  LWGEOM* geom;
4790  double dist;
4791 
4792  if ( ! e->geom )
4793  {
4794  _lwt_release_edges(elem, num);
4795  lwnotice("Corrupted topology: edge %" LWTFMT_ELEMID
4796  " has null geometry", e->edge_id);
4797  continue;
4798  }
4799 
4800  /* don't consider dangling edges */
4801  if ( e->face_left == e->face_right )
4802  {
4803  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID
4804  " is dangling, won't consider it", e->edge_id);
4805  continue;
4806  }
4807 
4808  geom = lwline_as_lwgeom(e->geom);
4809  dist = lwgeom_mindistance2d_tolerance(geom, qp, tol);
4810 
4811  LWDEBUGF(1, "Distance from edge %" LWTFMT_ELEMID
4812  " is %g (tol=%g)", e->edge_id, dist, tol);
4813 
4814  /* we won't consider edges too far */
4815  if ( dist > tol ) continue;
4816  if ( e->face_left == 0 ) {
4817  eface = e->face_right;
4818  }
4819  else if ( e->face_right == 0 ) {
4820  eface = e->face_left;
4821  }
4822  else {
4823  _lwt_release_edges(elem, num);
4824  lwerror("Two or more faces found");
4825  return -1;
4826  }
4827 
4828  if ( id && id != eface )
4829  {
4830  _lwt_release_edges(elem, num);
4831  lwerror("Two or more faces found"
4832 #if 0 /* debugging */
4833  " (%" LWTFMT_ELEMID
4834  " and %" LWTFMT_ELEMID ")", id, eface
4835 #endif
4836  );
4837  return -1;
4838  }
4839  else id = eface;
4840  }
4841  if ( num ) _lwt_release_edges(elem, num);
4842 
4843  return id;
4844 }
4845 
4846 /* Return the smallest delta that can perturbate
4847  * the given value */
4848 static inline double
4850 {
4851  double ret = 3.6 * pow(10, - ( 15 - log10(d?d:1.0) ) );
4852  return ret;
4853 }
4854 
4855 /* Return the smallest delta that can perturbate
4856  * the given point
4857 static inline double
4858 _lwt_minTolerancePoint2d( const POINT2D* p )
4859 {
4860  double max = FP_ABS(p->x);
4861  if ( max < FP_ABS(p->y) ) max = FP_ABS(p->y);
4862  return _lwt_minToleranceDouble(max);
4863 }
4864 */
4865 
4866 /* Return the smallest delta that can perturbate
4867  * the maximum absolute value of a geometry ordinate
4868  */
4869 static double
4871 {
4872  const GBOX* gbox;
4873  double max;
4874  double ret;
4875 
4876  gbox = lwgeom_get_bbox(g);
4877  if ( ! gbox ) return 0; /* empty */
4878  max = FP_ABS(gbox->xmin);
4879  if ( max < FP_ABS(gbox->xmax) ) max = FP_ABS(gbox->xmax);
4880  if ( max < FP_ABS(gbox->ymin) ) max = FP_ABS(gbox->ymin);
4881  if ( max < FP_ABS(gbox->ymax) ) max = FP_ABS(gbox->ymax);
4882 
4883  ret = _lwt_minToleranceDouble(max);
4884 
4885  return ret;
4886 }
4887 
4888 #define _LWT_MINTOLERANCE( topo, geom ) ( \
4889  topo->precision ? topo->precision : _lwt_minTolerance(geom) )
4890 
4891 typedef struct scored_pointer_t {
4892  void *ptr;
4893  double score;
4895 
4896 static int
4897 compare_scored_pointer(const void *si1, const void *si2)
4898 {
4899  double a = ((scored_pointer *)si1)->score;
4900  double b = ((scored_pointer *)si2)->score;
4901  if ( a < b )
4902  return -1;
4903  else if ( a > b )
4904  return 1;
4905  else
4906  return 0;
4907 }
4908 
4909 /*
4910  * @param findFace if non-zero the code will determine which face
4911  * contains the given point (unless it is known to be NOT
4912  * isolated)
4913  * @param moved if not-null will be set to 0 if the point was added
4914  * w/out any snapping or 1 otherwise.
4915  */
4916 static LWT_ELEMID
4917 _lwt_AddPoint(LWT_TOPOLOGY* topo, LWPOINT* point, double tol, int
4918  findFace, int *moved)
4919 {
4920  uint64_t num, i;
4921  double mindist = FLT_MAX;
4922  LWT_ISO_NODE *nodes, *nodes2;
4923  LWT_ISO_EDGE *edges, *edges2;
4924  LWGEOM *pt = lwpoint_as_lwgeom(point);
4925  int flds;
4926  LWT_ELEMID id = 0;
4927  scored_pointer *sorted;
4928 
4929  /* Get tolerance, if 0 was given */
4930  if (!tol)
4931  tol = _LWT_MINTOLERANCE(topo, pt);
4932 
4933  LWDEBUGG(1, pt, "Adding point");
4934 
4935  /*
4936  -- 1. Check if any existing node is closer than the given precision
4937  -- and if so pick the closest
4938  TODO: use WithinBox2D
4939  */
4941  nodes = lwt_be_getNodeWithinDistance2D(topo, point, tol, &num, flds, 0);
4942  if (num == UINT64_MAX)
4943  {
4944  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4945  return -1;
4946  }
4947  if ( num )
4948  {
4949  LWDEBUGF(1, "New point is within %.15g units of %d nodes", tol, num);
4950  /* Order by distance if there are more than a single return */
4951  if ( num > 1 )
4952  {{
4953  sorted= lwalloc(sizeof(scored_pointer)*num);
4954  for (i=0; i<num; ++i)
4955  {
4956  sorted[i].ptr = nodes+i;
4957  sorted[i].score = lwgeom_mindistance2d(lwpoint_as_lwgeom(nodes[i].geom), pt);
4958  LWDEBUGF(1, "Node %" LWTFMT_ELEMID " distance: %.15g",
4959  ((LWT_ISO_NODE*)(sorted[i].ptr))->node_id, sorted[i].score);
4960  }
4961  qsort(sorted, num, sizeof(scored_pointer), compare_scored_pointer);
4962  nodes2 = lwalloc(sizeof(LWT_ISO_NODE)*num);
4963  for (i=0; i<num; ++i)
4964  {
4965  nodes2[i] = *((LWT_ISO_NODE*)sorted[i].ptr);
4966  }
4967  lwfree(sorted);
4968  lwfree(nodes);
4969  nodes = nodes2;
4970  }}
4971 
4972  for ( i=0; i<num; ++i )
4973  {
4974  LWT_ISO_NODE *n = &(nodes[i]);
4975  LWGEOM *g = lwpoint_as_lwgeom(n->geom);
4976  double dist = lwgeom_mindistance2d(g, pt);
4977  /* TODO: move this check in the previous sort scan ... */
4978  /* must be closer than tolerated, unless distance is zero */
4979  if ( dist && dist >= tol ) continue;
4980  if ( ! id || dist < mindist )
4981  {
4982  id = n->node_id;
4983  mindist = dist;
4984  }
4985  }
4986  if ( id )
4987  {
4988  /* found an existing node */
4989  if ( nodes ) _lwt_release_nodes(nodes, num);
4990  if ( moved ) *moved = mindist == 0 ? 0 : 1;
4991  return id;
4992  }
4993  }
4994 
4995  initGEOS(lwnotice, lwgeom_geos_error);
4996 
4997  /*
4998  -- 2. Check if any existing edge falls within tolerance
4999  -- and if so split it by a point projected on it
5000  TODO: use WithinBox2D
5001  */
5003  edges = lwt_be_getEdgeWithinDistance2D(topo, point, tol, &num, flds, 0);
5004  if (num == UINT64_MAX)
5005  {
5006  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5007  return -1;
5008  }
5009  if ( num )
5010  {
5011  LWDEBUGF(1, "New point is within %.15g units of %d edges", tol, num);
5012 
5013  /* Order by distance if there are more than a single return */
5014  if ( num > 1 )
5015  {{
5016  int j;
5017  sorted = lwalloc(sizeof(scored_pointer)*num);
5018  for (i=0; i<num; ++i)
5019  {
5020  sorted[i].ptr = edges+i;
5021  sorted[i].score = lwgeom_mindistance2d(lwline_as_lwgeom(edges[i].geom), pt);
5022  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " distance: %.15g",
5023  ((LWT_ISO_EDGE*)(sorted[i].ptr))->edge_id, sorted[i].score);
5024  }
5025  qsort(sorted, num, sizeof(scored_pointer), compare_scored_pointer);
5026  edges2 = lwalloc(sizeof(LWT_ISO_EDGE)*num);
5027  for (j=0, i=0; i<num; ++i)
5028  {
5029  if ( sorted[i].score == sorted[0].score )
5030  {
5031  edges2[j++] = *((LWT_ISO_EDGE*)sorted[i].ptr);
5032  }
5033  else
5034  {
5035  lwline_free(((LWT_ISO_EDGE*)sorted[i].ptr)->geom);
5036  }
5037  }
5038  num = j;
5039  lwfree(sorted);
5040  lwfree(edges);
5041  edges = edges2;
5042  }}
5043 
5044  for (i=0; i<num; ++i)
5045  {
5046  /* The point is on or near an edge, split the edge */
5047  LWT_ISO_EDGE *e = &(edges[i]);
5048  LWGEOM *g = lwline_as_lwgeom(e->geom);
5049  LWGEOM *prj;
5050  int contains;
5051  LWT_ELEMID edge_id = e->edge_id;
5052 
5053  LWDEBUGF(1, "Splitting edge %" LWTFMT_ELEMID, edge_id);
5054 
5055  /* project point to line, split edge by point */
5056  prj = lwgeom_closest_point(g, pt);
5057  if ( moved ) *moved = lwgeom_same(prj,pt) ? 0 : 1;
5058  if ( lwgeom_has_z(pt) )
5059  {{
5060  /*
5061  -- This is a workaround for ClosestPoint lack of Z support:
5062  -- http://trac.osgeo.org/postgis/ticket/2033
5063  */
5064  LWGEOM *tmp;
5065  double z;
5066  POINT4D p4d;
5067  LWPOINT *prjpt;
5068  /* add Z to "prj" */
5069  tmp = lwgeom_force_3dz(prj);
5070  prjpt = lwgeom_as_lwpoint(tmp);
5071  getPoint4d_p(point->point, 0, &p4d);
5072  z = p4d.z;
5073  getPoint4d_p(prjpt->point, 0, &p4d);
5074  p4d.z = z;
5075  ptarray_set_point4d(prjpt->point, 0, &p4d);
5076  lwgeom_free(prj);
5077  prj = tmp;
5078  }}
5079  const POINT2D *pt = getPoint2d_cp(lwgeom_as_lwpoint(prj)->point, 0);
5081  if ( ! contains )
5082  {{
5083  double snaptol;
5084  LWGEOM *snapedge;
5085  LWLINE *snapline;
5086  POINT4D p1, p2;
5087 
5088  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID
5089  " does not contain projected point to it",
5090  edge_id);
5091 
5092  /* In order to reduce the robustness issues, we'll pick
5093  * an edge that contains the projected point, if possible */
5094  if ( i+1 < num )
5095  {
5096  LWDEBUG(1, "But there's another to check");
5097  lwgeom_free(prj);
5098  continue;
5099  }
5100 
5101  /*
5102  -- The tolerance must be big enough for snapping to happen
5103  -- and small enough to snap only to the projected point.
5104  -- Unfortunately ST_Distance returns 0 because it also uses
5105  -- a projected point internally, so we need another way.
5106  */
5107  snaptol = _lwt_minTolerance(prj);
5108  snapedge = _lwt_toposnap(g, prj, snaptol);
5109  snapline = lwgeom_as_lwline(snapedge);
5110 
5111  LWDEBUGF(1, "Edge snapped with tolerance %g", snaptol);
5112 
5113  /* TODO: check if snapping did anything ? */
5114 #if POSTGIS_DEBUG_LEVEL > 0
5115  {
5116  size_t sz;
5117  char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
5118  char *wkt2 = lwgeom_to_wkt(snapedge, WKT_EXTENDED, 15, &sz);
5119  LWDEBUGF(1, "Edge %s snapped became %s", wkt1, wkt2);
5120  lwfree(wkt1);
5121  lwfree(wkt2);
5122  }
5123 #endif
5124 
5125 
5126  /*
5127  -- Snapping currently snaps the first point below tolerance
5128  -- so may possibly move first point. See ticket #1631
5129  */
5130  getPoint4d_p(e->geom->points, 0, &p1);
5131  getPoint4d_p(snapline->points, 0, &p2);
5132  LWDEBUGF(1, "Edge first point is %g %g, "
5133  "snapline first point is %g %g",
5134  p1.x, p1.y, p2.x, p2.y);
5135  if ( p1.x != p2.x || p1.y != p2.y )
5136  {
5137  LWDEBUG(1, "Snapping moved first point, re-adding it");
5138  if ( LW_SUCCESS != ptarray_insert_point(snapline->points, &p1, 0) )
5139  {
5140  lwgeom_free(prj);
5141  lwgeom_free(snapedge);
5142  _lwt_release_edges(edges, num);
5143  lwerror("GEOS exception on Contains: %s", lwgeom_geos_errmsg);
5144  return -1;
5145  }
5146 #if POSTGIS_DEBUG_LEVEL > 0
5147  {
5148  size_t sz;
5149  char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
5150  LWDEBUGF(1, "Tweaked snapline became %s", wkt1);
5151  lwfree(wkt1);
5152  }
5153 #endif
5154  }
5155 #if POSTGIS_DEBUG_LEVEL > 0
5156  else {
5157  LWDEBUG(1, "Snapping did not move first point");
5158  }
5159 #endif
5160 
5161  if ( -1 == lwt_ChangeEdgeGeom( topo, edge_id, snapline ) )
5162  {
5163  /* TODO: should have invoked lwerror already, leaking memory */
5164  lwgeom_free(prj);
5165  lwgeom_free(snapedge);
5166  _lwt_release_edges(edges, num);
5167  lwerror("lwt_ChangeEdgeGeom failed");
5168  return -1;
5169  }
5170  lwgeom_free(snapedge);
5171  }}
5172 #if POSTGIS_DEBUG_LEVEL > 0
5173  else
5174  {{
5175  size_t sz;
5176  char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
5177  char *wkt2 = lwgeom_to_wkt(prj, WKT_EXTENDED, 15, &sz);
5178  LWDEBUGF(1, "Edge %s contains projected point %s", wkt1, wkt2);
5179  lwfree(wkt1);
5180  lwfree(wkt2);
5181  }}
5182 #endif
5183 
5184  /* TODO: pass 1 as last argument (skipChecks) ? */
5185  id = lwt_ModEdgeSplit( topo, edge_id, lwgeom_as_lwpoint(prj), 0 );
5186  if ( -1 == id )
5187  {
5188  /* TODO: should have invoked lwerror already, leaking memory */
5189  lwgeom_free(prj);
5190  _lwt_release_edges(edges, num);
5191  lwerror("lwt_ModEdgeSplit failed");
5192  return -1;
5193  }
5194 
5195  lwgeom_free(prj);
5196 
5197  /*
5198  * TODO: decimate the two new edges with the given tolerance ?
5199  *
5200  * the edge identifiers to decimate would be: edge_id and "id"
5201  * The problem here is that decimation of existing edges
5202  * may introduce intersections or topological inconsistencies,
5203  * for example:
5204  *
5205  * - A node may end up falling on the other side of the edge
5206  * - The decimated edge might intersect another existing edge
5207  *
5208  */
5209 
5210  break; /* we only want to snap a single edge */
5211  }
5212  _lwt_release_edges(edges, num);
5213  }
5214  else
5215  {
5216  /* The point is isolated, add it as such */
5217  /* TODO: pass 1 as last argument (skipChecks) ? */
5218  id = _lwt_AddIsoNode(topo, -1, point, 0, findFace);
5219  if ( moved ) *moved = 0;
5220  if ( -1 == id )
5221  {
5222  /* should have invoked lwerror already, leaking memory */
5223  lwerror("lwt_AddIsoNode failed");
5224  return -1;
5225  }
5226  }
5227 
5228  return id;
5229 }
5230 
5231 LWT_ELEMID
5232 lwt_AddPoint(LWT_TOPOLOGY* topo, LWPOINT* point, double tol)
5233 {
5234  return _lwt_AddPoint(topo, point, tol, 1, NULL);
5235 }
5236 
5237 /* Return identifier of an equal edge, 0 if none or -1 on error
5238  * (and lwerror gets called on error)
5239  */
5240 static LWT_ELEMID
5242 {
5243  LWT_ELEMID id;
5244  LWT_ISO_EDGE *edges;
5245  uint64_t num, i;
5246  const GBOX *qbox = lwgeom_get_bbox( lwline_as_lwgeom(edge) );
5247  GEOSGeometry *edgeg;
5248  const int flds = LWT_COL_EDGE_EDGE_ID|LWT_COL_EDGE_GEOM;
5249 
5250  edges = lwt_be_getEdgeWithinBox2D( topo, qbox, &num, flds, 0 );
5251  if (num == UINT64_MAX)
5252  {
5253  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5254  return -1;
5255  }
5256  if ( num )
5257  {
5258  initGEOS(lwnotice, lwgeom_geos_error);
5259 
5260  edgeg = LWGEOM2GEOS( lwline_as_lwgeom(edge), 0 );
5261  if ( ! edgeg )
5262  {
5263  _lwt_release_edges(edges, num);
5264  lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
5265  return -1;
5266  }
5267  for (i=0; i<num; ++i)
5268  {
5269  LWT_ISO_EDGE *e = &(edges[i]);
5270  LWGEOM *g = lwline_as_lwgeom(e->geom);
5271  GEOSGeometry *gg;
5272  int equals;
5273  gg = LWGEOM2GEOS( g, 0 );
5274  if ( ! gg )
5275  {
5276  GEOSGeom_destroy(edgeg);
5277  _lwt_release_edges(edges, num);
5278  lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
5279  return -1;
5280  }
5281  equals = GEOSEquals(gg, edgeg);
5282  GEOSGeom_destroy(gg);
5283  if ( equals == 2 )
5284  {
5285  GEOSGeom_destroy(edgeg);
5286  _lwt_release_edges(edges, num);
5287  lwerror("GEOSEquals exception: %s", lwgeom_geos_errmsg);
5288  return -1;
5289  }
5290  if ( equals )
5291  {
5292  id = e->edge_id;
5293  GEOSGeom_destroy(edgeg);
5294  _lwt_release_edges(edges, num);
5295  return id;
5296  }
5297  }
5298  GEOSGeom_destroy(edgeg);
5299  _lwt_release_edges(edges, num);
5300  }
5301 
5302  return 0;
5303 }
5304 
5305 /*
5306  * Add a pre-noded pre-split line edge. Used by lwt_AddLine
5307  * Return edge id, 0 if none added (empty edge), -1 on error
5308  *
5309  * @param handleFaceSplit if non-zero the code will check
5310  * if the newly added edge would split a face and if so
5311  * would create new faces accordingly. Otherwise it will
5312  * set left_face and right_face to null (-1)
5313  */
5314 static LWT_ELEMID
5315 _lwt_AddLineEdge( LWT_TOPOLOGY* topo, LWLINE* edge, double tol,
5316  int handleFaceSplit )
5317 {
5318  LWCOLLECTION *col;
5319  LWPOINT *start_point, *end_point;
5320  LWGEOM *tmp = 0, *tmp2;
5321  LWT_ISO_NODE *node;
5322  LWT_ELEMID nid[2]; /* start_node, end_node */
5323  LWT_ELEMID id; /* edge id */
5324  POINT4D p4d;
5325  uint64_t nn, i;
5326  int moved=0, mm;
5327 
5328  LWDEBUGG(1, lwline_as_lwgeom(edge), "_lwtAddLineEdge");
5329  LWDEBUGF(1, "_lwtAddLineEdge with tolerance %g", tol);
5330 
5331  start_point = lwline_get_lwpoint(edge, 0);
5332  if ( ! start_point )
5333  {
5334  lwnotice("Empty component of noded line");
5335  return 0; /* must be empty */
5336  }
5337  nid[0] = _lwt_AddPoint( topo, start_point,
5338  _lwt_minTolerance(lwpoint_as_lwgeom(start_point)),
5339  handleFaceSplit, &mm );
5340  lwpoint_free(start_point); /* too late if lwt_AddPoint calls lwerror */
5341  if ( nid[0] == -1 ) return -1; /* lwerror should have been called */
5342  moved += mm;
5343 
5344 
5345  end_point = lwline_get_lwpoint(edge, edge->points->npoints-1);
5346  if ( ! end_point )
5347  {
5348  lwerror("could not get last point of line "
5349  "after successfully getting first point !?");
5350  return -1;
5351  }
5352  nid[1] = _lwt_AddPoint( topo, end_point,
5354  handleFaceSplit, &mm );
5355  moved += mm;
5356  lwpoint_free(end_point); /* too late if lwt_AddPoint calls lwerror */
5357  if ( nid[1] == -1 ) return -1; /* lwerror should have been called */
5358 
5359  /*
5360  -- Added endpoints may have drifted due to tolerance, so
5361  -- we need to re-snap the edge to the new nodes before adding it
5362  */
5363  if ( moved )
5364  {
5365 
5366  nn = nid[0] == nid[1] ? 1 : 2;
5367  node = lwt_be_getNodeById( topo, nid, &nn,
5369  if (nn == UINT64_MAX)
5370  {
5371  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5372  return -1;
5373  }
5374  start_point = NULL; end_point = NULL;
5375  for (i=0; i<nn; ++i)
5376  {
5377  if ( node[i].node_id == nid[0] ) start_point = node[i].geom;
5378  if ( node[i].node_id == nid[1] ) end_point = node[i].geom;
5379  }
5380  if ( ! start_point || ! end_point )
5381  {
5382  if ( nn ) _lwt_release_nodes(node, nn);
5383  lwerror("Could not find just-added nodes % " LWTFMT_ELEMID
5384  " and %" LWTFMT_ELEMID, nid[0], nid[1]);
5385  return -1;
5386  }
5387 
5388  /* snap */
5389 
5390  getPoint4d_p( start_point->point, 0, &p4d );
5391  lwline_setPoint4d(edge, 0, &p4d);
5392 
5393  getPoint4d_p( end_point->point, 0, &p4d );
5394  lwline_setPoint4d(edge, edge->points->npoints-1, &p4d);
5395 
5396  if ( nn ) _lwt_release_nodes(node, nn);
5397 
5398  /* make valid, after snap (to handle collapses) */
5399  tmp = lwgeom_make_valid(lwline_as_lwgeom(edge));
5400 
5401  col = lwgeom_as_lwcollection(tmp);
5402  if ( col )
5403  {{
5404 
5405  col = lwcollection_extract(col, LINETYPE);
5406 
5407  /* Check if the so-snapped edge collapsed (see #1650) */
5408  if ( col->ngeoms == 0 )
5409  {
5410  lwcollection_free(col);
5411  lwgeom_free(tmp);
5412  LWDEBUG(1, "Made-valid snapped edge collapsed");
5413  return 0;
5414  }
5415 
5416  tmp2 = lwgeom_clone_deep( col->geoms[0] );
5417  lwgeom_free(tmp);
5418  tmp = tmp2;
5419  edge = lwgeom_as_lwline(tmp);
5420  lwcollection_free(col);
5421  if ( ! edge )
5422  {
5423  /* should never happen */
5424  lwerror("lwcollection_extract(LINETYPE) returned a non-line?");
5425  return -1;
5426  }
5427  }}
5428  else
5429  {
5430  edge = lwgeom_as_lwline(tmp);
5431  if ( ! edge )
5432  {
5433  LWDEBUGF(1, "Made-valid snapped edge collapsed to %s",
5435  lwgeom_free(tmp);
5436  return 0;
5437  }
5438  }
5439  }
5440 
5441  /* check if the so-snapped edge _now_ exists */
5442  id = _lwt_GetEqualEdge ( topo, edge );
5443  LWDEBUGF(1, "_lwt_GetEqualEdge returned %" LWTFMT_ELEMID, id);
5444  if ( id == -1 )
5445  {
5446  if ( tmp ) lwgeom_free(tmp); /* probably too late, due to internal lwerror */
5447  return -1;
5448  }
5449  if ( id )
5450  {
5451  if ( tmp ) lwgeom_free(tmp); /* possibly takes "edge" down with it */
5452  return id;
5453  }
5454 
5455  /* No previously existing edge was found, we'll add one */
5456 
5457  /* Remove consecutive vertices below given tolerance
5458  * on edge addition */
5459  if ( tol )
5460  {{
5461  tmp2 = lwline_remove_repeated_points(edge, tol);
5462  LWDEBUGG(1, tmp2, "Repeated-point removed");
5463  edge = lwgeom_as_lwline(tmp2);
5464  if ( tmp ) lwgeom_free(tmp);
5465  tmp = tmp2;
5466 
5467  /* check if the so-decimated edge collapsed to single-point */
5468  if ( nid[0] == nid[1] && edge->points->npoints == 2 )
5469  {
5470  lwgeom_free(tmp);
5471  LWDEBUG(1, "Repeated-point removed edge collapsed");
5472  return 0;
5473  }
5474 
5475  /* check if the so-decimated edge _now_ exists */
5476  id = _lwt_GetEqualEdge ( topo, edge );
5477  LWDEBUGF(1, "_lwt_GetEqualEdge returned %" LWTFMT_ELEMID, id);
5478  if ( id == -1 )
5479  {
5480  lwgeom_free(tmp); /* probably too late, due to internal lwerror */
5481  return -1;
5482  }
5483  if ( id )
5484  {
5485  lwgeom_free(tmp); /* takes "edge" down with it */
5486  return id;
5487  }
5488  }}
5489 
5490 
5491  /* TODO: skip checks ? */
5492  id = _lwt_AddEdge( topo, nid[0], nid[1], edge, 0, handleFaceSplit ? 1 : -1 );
5493  LWDEBUGF(1, "lwt_AddEdgeModFace returned %" LWTFMT_ELEMID, id);
5494  if ( id == -1 )
5495  {
5496  lwgeom_free(tmp); /* probably too late, due to internal lwerror */
5497  return -1;
5498  }
5499  lwgeom_free(tmp); /* possibly takes "edge" down with it */
5500 
5501  return id;
5502 }
5503 
5504 /* Simulate split-loop as it was implemented in pl/pgsql version
5505  * of TopoGeo_addLinestring */
5506 static LWGEOM *
5507 _lwt_split_by_nodes(const LWGEOM *g, const LWGEOM *nodes)
5508 {
5509  LWCOLLECTION *col = lwgeom_as_lwcollection(nodes);
5510  uint32_t i;
5511  LWGEOM *bg;
5512 
5513  bg = lwgeom_clone_deep(g);
5514  if ( ! col->ngeoms ) return bg;
5515 
5516  for (i=0; i<col->ngeoms; ++i)
5517  {
5518  LWGEOM *g2;
5519  g2 = lwgeom_split(bg, col->geoms[i]);
5520  lwgeom_free(bg);
5521  bg = g2;
5522  }
5523  bg->srid = nodes->srid;
5524 
5525  return bg;
5526 }
5527 
5528 static LWT_ELEMID*
5529 _lwt_AddLine(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges,
5530  int handleFaceSplit)
5531 {
5532  LWGEOM *geomsbuf[1];
5533  LWGEOM **geoms;
5534  uint32_t ngeoms;
5535  LWGEOM *noded, *tmp;
5536  LWCOLLECTION *col;
5537  LWT_ELEMID *ids;
5538  LWT_ISO_EDGE *edges;
5539  LWT_ISO_NODE *nodes;
5540  uint64_t num, numedges = 0, numnodes = 0;
5541  uint64_t i;
5542  GBOX qbox;
5543 
5544  *nedges = -1; /* error condition, by default */
5545 
5546  /* Get tolerance, if 0 was given */
5547  if ( ! tol ) tol = _LWT_MINTOLERANCE( topo, (LWGEOM*)line );
5548  LWDEBUGF(1, "Working tolerance:%.15g", tol);
5549  LWDEBUGF(1, "Input line has srid=%d", line->srid);
5550 
5551  /* Remove consecutive vertices below given tolerance upfront */
5552  if ( tol )
5553  {{
5555  tmp = lwline_as_lwgeom(clean); /* NOTE: might collapse to non-simple */
5556  LWDEBUGG(1, tmp, "Repeated-point removed");
5557  }} else tmp=(LWGEOM*)line;
5558 
5559  /* 1. Self-node */
5560  noded = lwgeom_node((LWGEOM*)tmp);
5561  if ( tmp != (LWGEOM*)line ) lwgeom_free(tmp);
5562  if ( ! noded ) return NULL; /* should have called lwerror already */
5563  LWDEBUGG(1, noded, "Noded");
5564 
5565  qbox = *lwgeom_get_bbox( lwline_as_lwgeom(line) );
5566  LWDEBUGF(1, "Line BOX is %.15g %.15g, %.15g %.15g", qbox.xmin, qbox.ymin,
5567  qbox.xmax, qbox.ymax);
5568  gbox_expand(&qbox, tol);
5569  LWDEBUGF(1, "BOX expanded by %g is %.15g %.15g, %.15g %.15g",
5570  tol, qbox.xmin, qbox.ymin, qbox.xmax, qbox.ymax);
5571 
5572  LWGEOM **nearby = 0;
5573  int nearbyindex = 0;
5574  int nearbycount = 0;
5575 
5576  /* 2.0. Find edges falling within tol distance */
5577  edges = lwt_be_getEdgeWithinBox2D( topo, &qbox, &numedges, LWT_COL_EDGE_ALL, 0 );
5578  if (numedges == UINT64_MAX)
5579  {
5580  lwgeom_free(noded);
5581  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5582  return NULL;
5583  }
5584  LWDEBUGF(1, "Line has %d points, its bbox intersects %d edges bboxes",
5585  line->points->npoints, numedges);
5586  if ( numedges )
5587  {{
5588  /* collect those whose distance from us is < tol */
5589  nearbycount += numedges;
5590  nearby = lwalloc(numedges * sizeof(LWGEOM *));
5591  for (i=0; i<numedges; ++i)
5592  {
5593  LW_ON_INTERRUPT(return NULL);
5594  LWT_ISO_EDGE *e = &(edges[i]);
5595  LWGEOM *g = lwline_as_lwgeom(e->geom);
5596  LWDEBUGF(2, "Computing distance from edge %d having %d points", i, e->geom->points->npoints);
5597  double dist = lwgeom_mindistance2d(g, noded);
5598  /* must be closer than tolerated, unless distance is zero */
5599  if ( dist && dist >= tol ) continue;
5600  nearby[nearbyindex++] = g;
5601  }
5602  LWDEBUGF(1, "Found %d edges closer than tolerance (%g)", nearbyindex, tol);
5603  }}
5604  int nearbyedgecount = nearbyindex;
5605 
5606  /* 2.1. Find nodes falling within tol distance *
5607  * TODO: check if we should be only considering _isolated_ nodes! */
5608  nodes = lwt_be_getNodeWithinBox2D( topo, &qbox, &numnodes, LWT_COL_NODE_ALL, 0 );
5609  if (numnodes == UINT64_MAX)
5610  {
5611  lwgeom_free(noded);
5612  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5613  return NULL;
5614  }
5615  LWDEBUGF(1, "Line bbox intersects %d nodes bboxes", numnodes);
5616  if ( numnodes )
5617  {{
5618  /* collect those whose distance from us is < tol */
5619  nearbycount = nearbyedgecount + numnodes;
5620  nearby = nearby ?
5621  lwrealloc(nearby, nearbycount * sizeof(LWGEOM *))
5622  :
5623  lwalloc(nearbycount * sizeof(LWGEOM *))
5624  ;
5625  int nn = 0;
5626  for (i=0; i<numnodes; ++i)
5627  {
5628  LWT_ISO_NODE *n = &(nodes[i]);
5629  LWGEOM *g = lwpoint_as_lwgeom(n->geom);
5630  double dist = lwgeom_mindistance2d(g, noded);
5631  /* must be closer than tolerated, unless distance is zero */
5632  if ( dist && dist >= tol )
5633  {
5634  LWDEBUGF(1, "Node %d is %g units away, we tolerate only %g", n->node_id, dist, tol);
5635  continue;
5636  }
5637  nearby[nearbyindex++] = g;
5638  ++nn;
5639  }
5640  LWDEBUGF(1, "Found %d nodes closer than tolerance (%g)", nn, tol);
5641  }}
5642  int nearbynodecount = nearbyindex - nearbyedgecount;
5643  nearbycount = nearbyindex;
5644 
5645  LWDEBUGF(1, "Number of nearby elements is %d", nearbycount);
5646 
5647  /* 2.2. Snap to nearby elements */
5648  if ( nearbycount )
5649  {{
5650  LWCOLLECTION *col;
5651  LWGEOM *elems;
5652 
5654  NULL, nearbycount, nearby);
5655  elems = lwcollection_as_lwgeom(col);
5656 
5657  LWDEBUGG(1, elems, "Collected nearby elements");
5658 
5659  tmp = _lwt_toposnap(noded, elems, tol);
5660  lwgeom_free(noded);
5661  noded = tmp;
5662  LWDEBUGG(1, noded, "Elements-snapped");
5663 
5664  /* will not release the geoms array */
5665  lwcollection_release(col);
5666 
5667  /*
5668  -- re-node to account for ST_Snap introduced self-intersections
5669  -- See http://trac.osgeo.org/postgis/ticket/1714
5670  -- TODO: consider running UnaryUnion once after all noding
5671  */
5672  tmp = lwgeom_unaryunion(noded);
5673  lwgeom_free(noded);
5674  noded = tmp;
5675  LWDEBUGG(1, noded, "Unary-unioned");
5676  }}
5677 
5678  /* 2.3. Node with nearby edges */
5679  if ( nearbyedgecount )
5680  {{
5681  LWCOLLECTION *col;
5682  LWGEOM *iedges; /* just an alias for col */
5683  LWGEOM *diff, *xset;
5684 
5685  LWDEBUGF(1, "Line intersects %d edges", nearbyedgecount);
5686 
5688  NULL, nearbyedgecount, nearby);
5689  iedges = lwcollection_as_lwgeom(col);
5690  LWDEBUGG(1, iedges, "Collected edges");
5691 
5692  LWDEBUGF(1, "Diffing noded, with srid=%d "
5693  "and interesecting edges, with srid=%d",
5694  noded->srid, iedges->srid);
5695  diff = lwgeom_difference(noded, iedges);
5696  LWDEBUGG(1, diff, "Differenced");
5697 
5698  LWDEBUGF(1, "Intersecting noded, with srid=%d "
5699  "and interesecting edges, with srid=%d",
5700  noded->srid, iedges->srid);
5701  xset = lwgeom_intersection(noded, iedges);
5702  LWDEBUGG(1, xset, "Intersected");
5703  lwgeom_free(noded);
5704 
5705  /* We linemerge here because INTERSECTION, as of GEOS 3.8,
5706  * will result in shared segments being output as multiple
5707  * lines rather than a single line. Example:
5708 
5709  INTERSECTION(
5710  'LINESTRING(0 0, 5 0, 8 0, 10 0,12 0)',
5711  'LINESTRING(5 0, 8 0, 10 0)'
5712  )
5713  ==
5714  MULTILINESTRING((5 0,8 0),(8 0,10 0))
5715 
5716  * We will re-split in a subsequent step, by splitting
5717  * the final line with pre-existing nodes
5718  */
5719  LWDEBUG(1, "Linemerging intersection");
5720  tmp = lwgeom_linemerge(xset);
5721  LWDEBUGG(1, tmp, "Linemerged");
5722  lwgeom_free(xset);
5723  xset = tmp;
5724 
5725  /*
5726  * Here we union the (linemerged) intersection with
5727  * the difference (new lines)
5728  */
5729  LWDEBUG(1, "Unioning difference and (linemerged) intersection");
5730  noded = lwgeom_union(diff, xset);
5731  LWDEBUGG(1, noded, "Diff-Xset Unioned");
5732  lwgeom_free(xset);
5733  lwgeom_free(diff);
5734 
5735  /* will not release the geoms array */
5736  lwcollection_release(col);
5737  }}
5738 
5739 
5740  /* 2.4. Split by pre-existing nodes
5741  *
5742  * Pre-existing nodes are isolated nodes AND endpoints
5743  * of intersecting edges
5744  */
5745  if ( nearbyedgecount )
5746  {
5747  nearbycount += nearbyedgecount * 2; /* make space for endpoints */
5748  nearby = lwrealloc(nearby, nearbycount * sizeof(LWGEOM *));
5749  for (int i=0; i<nearbyedgecount; i++)
5750  {
5751  LWLINE *edge = lwgeom_as_lwline(nearby[i]);
5752  LWPOINT *startNode = lwline_get_lwpoint(edge, 0);
5753  LWPOINT *endNode = lwline_get_lwpoint(edge, edge->points->npoints-1);
5754  /* TODO: only add if within distance to noded AND if not duplicated */
5755  nearby[nearbyindex++] = lwpoint_as_lwgeom(startNode);
5756  nearbynodecount++;
5757  nearby[nearbyindex++] = lwpoint_as_lwgeom(endNode);
5758  nearbynodecount++;
5759  }
5760  }
5761  if ( nearbynodecount )
5762  {
5764  NULL, nearbynodecount,
5765  nearby + nearbyedgecount);
5766  LWGEOM *inodes = lwcollection_as_lwgeom(col);
5767  /* TODO: use lwgeom_split of lwgeom_union ... */
5768  tmp = _lwt_split_by_nodes(noded, inodes);
5769  lwgeom_free(noded);
5770  noded = tmp;
5771  LWDEBUGG(1, noded, "Node-split");
5772  /* will not release the geoms array */
5773  lwcollection_release(col);
5774  }
5775 
5776 
5777  LWDEBUG(1, "Freeing up nearby elements");
5778 
5779  /* TODO: free up endpoints of nearbyedges */
5780  if ( nearby ) lwfree(nearby);
5781  if ( nodes ) _lwt_release_nodes(nodes, numnodes);
5782  if ( edges ) _lwt_release_edges(edges, numedges);
5783 
5784  LWDEBUGG(1, noded, "Finally-noded");
5785 
5786  /* 3. For each (now-noded) segment, insert an edge */
5787  col = lwgeom_as_lwcollection(noded);
5788  if ( col )
5789  {
5790  LWDEBUG(1, "Noded line was a collection");
5791  geoms = col->geoms;
5792  ngeoms = col->ngeoms;
5793  }
5794  else
5795  {
5796  LWDEBUG(1, "Noded line was a single geom");
5797  geomsbuf[0] = noded;
5798  geoms = geomsbuf;
5799  ngeoms = 1;
5800  }
5801 
5802  LWDEBUGF(1, "Line was split into %d edges", ngeoms);
5803 
5804  /* TODO: refactor to first add all nodes (re-snapping edges if
5805  * needed) and then check all edges for existing already
5806  * ( so to save a DB scan for each edge to be added )
5807  */
5808  ids = lwalloc(sizeof(LWT_ELEMID)*ngeoms);
5809  num = 0;
5810  for ( i=0; i<ngeoms; ++i )
5811  {
5812  LWT_ELEMID id;
5813  LWGEOM *g = geoms[i];
5814  g->srid = noded->srid;
5815 
5816 #if POSTGIS_DEBUG_LEVEL > 0
5817  {
5818  size_t sz;
5819  char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
5820  LWDEBUGF(1, "Component %d of split line is: %s", i, wkt1);
5821  lwfree(wkt1);
5822  }
5823 #endif
5824 
5825  id = _lwt_AddLineEdge( topo, lwgeom_as_lwline(g), tol, handleFaceSplit );
5826  LWDEBUGF(1, "_lwt_AddLineEdge returned %" LWTFMT_ELEMID, id);
5827  if ( id < 0 )
5828  {
5829  lwgeom_free(noded);
5830  lwfree(ids);
5831  return NULL;
5832  }
5833  if ( ! id )
5834  {
5835  LWDEBUGF(1, "Component %d of split line collapsed", i);
5836  continue;
5837  }
5838 
5839  LWDEBUGF(1, "Component %d of split line is edge %" LWTFMT_ELEMID,
5840  i, id);
5841  ids[num++] = id; /* TODO: skip duplicates */
5842  }
5843 
5844  LWDEBUGG(1, noded, "Noded before free");
5845  lwgeom_free(noded);
5846 
5847  /* TODO: XXX remove duplicated ids if not done before */
5848 
5849  *nedges = num;
5850  return ids;
5851 }
5852 
5853 LWT_ELEMID*
5854 lwt_AddLine(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges)
5855 {
5856  return _lwt_AddLine(topo, line, tol, nedges, 1);
5857 }
5858 
5859 LWT_ELEMID*
5860 lwt_AddLineNoFace(LWT_TOPOLOGY* topo, LWLINE* line, double tol, int* nedges)
5861 {
5862  return _lwt_AddLine(topo, line, tol, nedges, 0);
5863 }
5864 
5865 LWT_ELEMID*
5866 lwt_AddPolygon(LWT_TOPOLOGY* topo, LWPOLY* poly, double tol, int* nfaces)
5867 {
5868  uint32_t i;
5869  *nfaces = -1; /* error condition, by default */
5870  int num;
5871  LWT_ISO_FACE *faces;
5872  uint64_t nfacesinbox;
5873  uint64_t j;
5874  LWT_ELEMID *ids = NULL;
5875  GBOX qbox;
5876  const GEOSPreparedGeometry *ppoly;
5877  GEOSGeometry *polyg;
5878 
5879  /* Get tolerance, if 0 was given */
5880  if ( ! tol ) tol = _LWT_MINTOLERANCE( topo, (LWGEOM*)poly );
5881  LWDEBUGF(1, "Working tolerance:%.15g", tol);
5882 
5883  /* Add each ring as an edge */
5884  for ( i=0; i<poly->nrings; ++i )
5885  {
5886  LWLINE *line;
5887  POINTARRAY *pa;
5888  LWT_ELEMID *eids;
5889  int nedges;
5890 
5891  pa = ptarray_clone(poly->rings[i]);
5892  line = lwline_construct(topo->srid, NULL, pa);
5893  eids = lwt_AddLine( topo, line, tol, &nedges );
5894  if ( nedges < 0 ) {
5895  /* probably too late as lwt_AddLine invoked lwerror */
5896  lwline_free(line);
5897  lwerror("Error adding ring %d of polygon", i);
5898  return NULL;
5899  }
5900  lwline_free(line);
5901  lwfree(eids);
5902  }
5903 
5904  /*
5905  -- Find faces covered by input polygon
5906  -- NOTE: potential snapping changed polygon edges
5907  */
5908  qbox = *lwgeom_get_bbox( lwpoly_as_lwgeom(poly) );
5909  gbox_expand(&qbox, tol);
5910  faces = lwt_be_getFaceWithinBox2D( topo, &qbox, &nfacesinbox,
5911  LWT_COL_FACE_ALL, 0 );
5912  if (nfacesinbox == UINT64_MAX)
5913  {
5914  lwfree(ids);
5915  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5916  return NULL;
5917  }
5918 
5919  num = 0;
5920  if ( nfacesinbox )
5921  {
5922  polyg = LWGEOM2GEOS(lwpoly_as_lwgeom(poly), 0);
5923  if ( ! polyg )
5924  {
5925  _lwt_release_faces(faces, nfacesinbox);
5926  lwerror("Could not convert poly geometry to GEOS: %s", lwgeom_geos_errmsg);
5927  return NULL;
5928  }
5929  ppoly = GEOSPrepare(polyg);
5930  ids = lwalloc(sizeof(LWT_ELEMID)*nfacesinbox);
5931  for ( j=0; j<nfacesinbox; ++j )
5932  {
5933  LWT_ISO_FACE *f = &(faces[j]);
5934  LWGEOM *fg;
5935  GEOSGeometry *fgg, *sp;
5936  int covers;
5937 
5938  /* check if a point on this face surface is covered by our polygon */
5939  fg = lwt_GetFaceGeometry( topo, f->face_id );
5940  if ( ! fg )
5941  {
5942  j = f->face_id; /* so we can destroy faces */
5943  GEOSPreparedGeom_destroy(ppoly);
5944  GEOSGeom_destroy(polyg);
5945  lwfree(ids);
5946  _lwt_release_faces(faces, nfacesinbox);
5947  lwerror("Could not get geometry of face %" LWTFMT_ELEMID, j);
5948  return NULL;
5949  }
5950  /* check if a point on this face's surface is covered by our polygon */
5951  fgg = LWGEOM2GEOS(fg, 0);
5952  lwgeom_free(fg);
5953  if ( ! fgg )
5954  {
5955  GEOSPreparedGeom_destroy(ppoly);
5956  GEOSGeom_destroy(polyg);
5957  _lwt_release_faces(faces, nfacesinbox);
5958  lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
5959  return NULL;
5960  }
5961  sp = GEOSPointOnSurface(fgg);
5962  GEOSGeom_destroy(fgg);
5963  if ( ! sp )
5964  {
5965  GEOSPreparedGeom_destroy(ppoly);
5966  GEOSGeom_destroy(polyg);
5967  _lwt_release_faces(faces, nfacesinbox);
5968  lwerror("Could not find point on face surface: %s", lwgeom_geos_errmsg);
5969  return NULL;
5970  }
5971  covers = GEOSPreparedCovers( ppoly, sp );
5972  GEOSGeom_destroy(sp);
5973  if (covers == 2)
5974  {
5975  GEOSPreparedGeom_destroy(ppoly);
5976  GEOSGeom_destroy(polyg);
5977  _lwt_release_faces(faces, nfacesinbox);
5978  lwerror("PreparedCovers error: %s", lwgeom_geos_errmsg);
5979  return NULL;
5980  }
5981  if ( ! covers )
5982  {
5983  continue; /* we're not composed by this face */
5984  }
5985 
5986  /* TODO: avoid duplicates ? */
5987  ids[num++] = f->face_id;
5988  }
5989  GEOSPreparedGeom_destroy(ppoly);
5990  GEOSGeom_destroy(polyg);
5991  _lwt_release_faces(faces, nfacesinbox);
5992  }
5993 
5994  /* possibly 0 if non face's surface point was found
5995  * to be covered by input polygon */
5996  *nfaces = num;
5997 
5998  return ids;
5999 }
6000 
6001 /*
6002  *---- polygonizer
6003  */
6004 
6005 /* An array of pointers to EDGERING structures */
6006 typedef struct LWT_ISO_EDGE_TABLE_T {
6008  int size;
6010 
6011 static int
6012 compare_iso_edges_by_id(const void *si1, const void *si2)
6013 {
6014  int a = ((LWT_ISO_EDGE *)si1)->edge_id;
6015  int b = ((LWT_ISO_EDGE *)si2)->edge_id;
6016  if ( a < b )
6017  return -1;
6018  else if ( a > b )
6019  return 1;
6020  else
6021  return 0;
6022 }
6023 
6024 static LWT_ISO_EDGE *
6026 {
6027  LWT_ISO_EDGE key;
6028  key.edge_id = id;
6029 
6030  void *match = bsearch( &key, tab->edges, tab->size,
6031  sizeof(LWT_ISO_EDGE),
6033  return match;
6034 }
6035 
6036 typedef struct LWT_EDGERING_ELEM_T {
6037  /* externally owned */
6039  /* 0 if false, 1 if true */
6040  int left;
6042 
6043 /* A ring of edges */
6044 typedef struct LWT_EDGERING_T {
6045  /* Signed edge identifiers
6046  * positive ones are walked in their direction, negative ones
6047  * in the opposite direction */
6049  /* Number of edges in the ring */
6050  int size;
6052  /* Bounding box of the ring */
6054  /* Bounding box of the ring in GEOS format (for STRTree) */
6055  GEOSGeometry *genv;
6057 
6058 #define LWT_EDGERING_INIT(a) { \
6059  (a)->size = 0; \
6060  (a)->capacity = 1; \
6061  (a)->elems = lwalloc(sizeof(LWT_EDGERING_ELEM *) * (a)->capacity); \
6062  (a)->env = NULL; \
6063  (a)->genv = NULL; \
6064 }
6065 
6066 #define LWT_EDGERING_PUSH(a, r) { \
6067  if ( (a)->size + 1 > (a)->capacity ) { \
6068  (a)->capacity *= 2; \
6069  (a)->elems = lwrealloc((a)->elems, sizeof(LWT_EDGERING_ELEM *) * (a)->capacity); \
6070  } \
6071  /* lwdebug(1, "adding elem %d (%p) of edgering %p", (a)->size, (r), (a)); */ \
6072  (a)->elems[(a)->size++] = (r); \
6073 }
6074 
6075 #define LWT_EDGERING_CLEAN(a) { \
6076  int i; for (i=0; i<(a)->size; ++i) { \
6077  if ( (a)->elems[i] ) { \
6078  /* lwdebug(1, "freeing elem %d (%p) of edgering %p", i, (a)->elems[i], (a)); */ \
6079  lwfree((a)->elems[i]); \
6080  } \
6081  } \
6082  if ( (a)->elems ) { lwfree((a)->elems); (a)->elems = NULL; } \
6083  (a)->size = 0; \
6084  (a)->capacity = 0; \
6085  if ( (a)->env ) { lwfree((a)->env); (a)->env = NULL; } \
6086  if ( (a)->genv ) { GEOSGeom_destroy((a)->genv); (a)->genv = NULL; } \
6087 }
6088 
6089 /* An array of pointers to EDGERING structures */
6090 typedef struct LWT_EDGERING_ARRAY_T {
6092  int size;
6094  GEOSSTRtree* tree;
6096 
6097 #define LWT_EDGERING_ARRAY_INIT(a) { \
6098  (a)->size = 0; \
6099  (a)->capacity = 1; \
6100  (a)->rings = lwalloc(sizeof(LWT_EDGERING *) * (a)->capacity); \
6101  (a)->tree = NULL; \
6102 }
6103 
6104 /* WARNING: use of 'j' is intentional, not to clash with
6105  * 'i' used in LWT_EDGERING_CLEAN */
6106 #define LWT_EDGERING_ARRAY_CLEAN(a) { \
6107  int j; for (j=0; j<(a)->size; ++j) { \
6108  LWT_EDGERING_CLEAN((a)->rings[j]); \
6109  } \
6110  if ( (a)->capacity ) lwfree((a)->rings); \
6111  if ( (a)->tree ) { \
6112  GEOSSTRtree_destroy( (a)->tree ); \
6113  (a)->tree = NULL; \
6114  } \
6115 }
6116 
6117 #define LWT_EDGERING_ARRAY_PUSH(a, r) { \
6118  if ( (a)->size + 1 > (a)->capacity ) { \
6119  (a)->capacity *= 2; \
6120  (a)->rings = lwrealloc((a)->rings, sizeof(LWT_EDGERING *) * (a)->capacity); \
6121  } \
6122  (a)->rings[(a)->size++] = (r); \
6123 }
6124 
6129  int curidx;
6131 
6132 static int
6134 {
6135  LWT_EDGERING_ELEM *el = it->curelem;
6136  POINTARRAY *pa;
6137 
6138  if ( ! el ) return 0; /* finished */
6139 
6140  pa = el->edge->geom->points;
6141 
6142  int tonext = 0;
6143  LWDEBUGF(3, "iterator fetching idx %d from pa of %d points", it->curidx, pa->npoints);
6144  getPoint2d_p(pa, it->curidx, pt);
6145  if ( el->left ) {
6146  it->curidx++;
6147  if ( it->curidx >= (int) pa->npoints ) tonext = 1;
6148  } else {
6149  it->curidx--;
6150  if ( it->curidx < 0 ) tonext = 1;
6151  }
6152 
6153  if ( tonext )
6154  {
6155  LWDEBUG(3, "iterator moving to next element");
6156  it->curelemidx++;
6157  if ( it->curelemidx < it->ring->size )
6158  {
6159  el = it->curelem = it->ring->elems[it->curelemidx];
6160  it->curidx = el->left ? 0 : el->edge->geom->points->npoints - 1;
6161  }
6162  else
6163  {
6164  it->curelem = NULL;
6165  }
6166  }
6167 
6168  return 1;
6169 }
6170 
6171 /* Release return with lwfree */
6174 {
6176  ret->ring = er;
6177  if ( er->size ) ret->curelem = er->elems[0];
6178  else ret->curelem = NULL;
6179  ret->curelemidx = 0;
6180  ret->curidx = ret->curelem->left ? 0 : ret->curelem->edge->geom->points->npoints - 1;
6181  return ret;
6182 }
6183 
6184 /* Identifier for a placeholder face that will be
6185  * used to mark hole rings */
6186 #define LWT_HOLES_FACE_PLACEHOLDER INT32_MIN
6187 
6188 static int
6190 {
6191  while (
6192  from < etab->size &&
6193  etab->edges[from].face_left != -1 &&
6194  etab->edges[from].face_right != -1
6195  ) from++;
6196  return from < etab->size ? from : -1;
6197 }
6198 
6199 static LWT_ISO_EDGE *
6200 _lwt_FetchAllEdges(LWT_TOPOLOGY *topo, int *numedges)
6201 {
6202  LWT_ISO_EDGE *edge;
6203  int fields = LWT_COL_EDGE_ALL;
6204  uint64_t nelems = 1;
6205 
6206  edge = lwt_be_getEdgeWithinBox2D( topo, NULL, &nelems, fields, 0);
6207  *numedges = nelems;
6208  if (nelems == UINT64_MAX)
6209  {
6210  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
6211  return NULL;
6212  }
6213  return edge;
6214 }
6215 
6216 /* Update the side face of given ring edges
6217  *
6218  * Edge identifiers are signed, those with negative identifier
6219  * need to be updated their right_face, those with positive
6220  * identifier need to be updated their left_face.
6221  *
6222  * @param face identifier of the face bound by the ring
6223  * @return 0 on success, -1 on error
6224  */
6225 static int
6227  LWT_ELEMID face)
6228 {
6229  LWT_ISO_EDGE *forward_edges = NULL;
6230  int forward_edges_count = 0;
6231  LWT_ISO_EDGE *backward_edges = NULL;
6232  int backward_edges_count = 0;
6233  int i, ret;
6234 
6235  /* Make a list of forward_edges and backward_edges */
6236 
6237  forward_edges = lwalloc(sizeof(LWT_ISO_EDGE) * ring->size);
6238  forward_edges_count = 0;
6239  backward_edges = lwalloc(sizeof(LWT_ISO_EDGE) * ring->size);
6240  backward_edges_count = 0;
6241 
6242  for ( i=0; i<ring->size; ++i )
6243  {
6244  LWT_EDGERING_ELEM *elem = ring->elems[i];
6245  LWT_ISO_EDGE *edge = elem->edge;
6246  LWT_ELEMID id = edge->edge_id;
6247  if ( elem->left )
6248  {
6249  LWDEBUGF(3, "Forward edge %d is %d", forward_edges_count, id);
6250  forward_edges[forward_edges_count].edge_id = id;
6251  forward_edges[forward_edges_count++].face_left = face;
6252  edge->face_left = face;
6253  }
6254  else
6255  {
6256  LWDEBUGF(3, "Backward edge %d is %d", forward_edges_count, id);
6257  backward_edges[backward_edges_count].edge_id = id;
6258  backward_edges[backward_edges_count++].face_right = face;
6259  edge->face_right = face;
6260  }
6261  }
6262 
6263  /* Update forward edges */
6264  if ( forward_edges_count )
6265  {
6266  ret = lwt_be_updateEdgesById(topo, forward_edges,
6267  forward_edges_count,
6269  if ( ret == -1 )
6270  {
6271  lwfree( forward_edges );
6272  lwfree( backward_edges );
6273  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
6274  return -1;
6275  }
6276  if ( ret != forward_edges_count )
6277  {
6278  lwfree( forward_edges );
6279  lwfree( backward_edges );
6280  lwerror("Unexpected error: %d edges updated when expecting %d (forward)",
6281  ret, forward_edges_count);
6282  return -1;
6283  }
6284  }
6285 
6286  /* Update backward edges */
6287  if ( backward_edges_count )
6288  {
6289  ret = lwt_be_updateEdgesById(topo, backward_edges,
6290  backward_edges_count,
6292  if ( ret == -1 )
6293  {
6294  lwfree( forward_edges );
6295  lwfree( backward_edges );
6296  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
6297  return -1;
6298  }
6299  if ( ret != backward_edges_count )
6300  {
6301  lwfree( forward_edges );
6302  lwfree( backward_edges );
6303  lwerror("Unexpected error: %d edges updated when expecting %d (backward)",
6304  ret, backward_edges_count);
6305  return -1;
6306  }
6307  }
6308 
6309  lwfree( forward_edges );
6310  lwfree( backward_edges );
6311 
6312  return 0;
6313 }
6314 
6315 /*
6316  * @param side 1 for left side, -1 for right side
6317  */
6318 static LWT_EDGERING *
6320  LWT_ISO_EDGE *edge, int side)
6321 {
6322  LWT_EDGERING *ring;
6323  LWT_EDGERING_ELEM *elem;
6324  LWT_ISO_EDGE *cur;
6325  int curside;
6326 
6327  ring = lwalloc(sizeof(LWT_EDGERING));
6328  LWT_EDGERING_INIT(ring);
6329 
6330  cur = edge;
6331  curside = side;
6332 
6333  LWDEBUGF(2, "Building rings for edge %d (side %d)", cur->edge_id, side);
6334 
6335  do {
6336  LWT_ELEMID next;
6337 
6338  elem = lwalloc(sizeof(LWT_EDGERING_ELEM));
6339  elem->edge = cur;
6340  elem->left = ( curside == 1 );
6341 
6342  /* Mark edge as "visited" */
6343  if ( elem->left ) cur->face_left = LWT_HOLES_FACE_PLACEHOLDER;
6344  else cur->face_right = LWT_HOLES_FACE_PLACEHOLDER;
6345 
6346  LWT_EDGERING_PUSH(ring, elem);
6347  next = elem->left ? cur->next_left : cur->next_right;
6348 
6349  LWDEBUGF(3, " next edge is %d", next);
6350 
6351  if ( next > 0 ) curside = 1;
6352  else { curside = -1; next = -next; }
6353  cur = _lwt_getIsoEdgeById(edges, next);
6354  if ( ! cur )
6355  {
6356  lwerror("Could not find edge with id %d", next);
6357  break;
6358  }
6359  } while (cur != edge || curside != side);
6360 
6361  LWDEBUGF(1, "Ring for edge %d has %d elems", edge->edge_id*side, ring->size);
6362 
6363  return ring;
6364 }
6365 
6366 static double
6368 {
6369  POINT2D P1;
6370  POINT2D P2;
6371  POINT2D P3;
6372  double sum = 0.0;
6373  double x0, x, y1, y2;
6374 
6375  if ( ! _lwt_EdgeRingIterator_next(it, &P1) ) return 0.0;
6376  if ( ! _lwt_EdgeRingIterator_next(it, &P2) ) return 0.0;
6377 
6378  LWDEBUG(2, "_lwt_EdgeRingSignedArea");
6379 
6380  x0 = P1.x;
6381  while ( _lwt_EdgeRingIterator_next(it, &P3) )
6382  {
6383  x = P2.x - x0;
6384  y1 = P3.y;
6385  y2 = P1.y;
6386  sum += x * (y2-y1);
6387 
6388  /* Move forwards! */
6389  P1 = P2;
6390  P2 = P3;
6391  }
6392 
6393  return sum / 2.0;
6394 }
6395 
6396 
6397 /* Return 1 for true, 0 for false */
6398 static int
6400 {
6401  double sa;
6402 
6403  LWDEBUGF(2, "_lwt_EdgeRingIsCCW, ring has %d elems", ring->size);
6405  sa = _lwt_EdgeRingSignedArea(it);
6406  LWDEBUGF(2, "_lwt_EdgeRingIsCCW, signed area is %g", sa);
6407  lwfree(it);
6408  if ( sa >= 0 ) return 0;
6409  else return 1;
6410 }
6411 
6412 static int
6414 {
6415  int cn = 0; /* the crossing number counter */
6416  POINT2D v1, v2;
6417 #ifndef RELAX
6418  POINT2D v0;
6419 #endif
6420 
6421  if ( ! _lwt_EdgeRingIterator_next(it, &v1) ) return cn;
6422  v0 = v1;
6423  while ( _lwt_EdgeRingIterator_next(it, &v2) )
6424  {
6425  double vt;
6426 
6427  /* edge from vertex i to vertex i+1 */
6428  if
6429  (
6430  /* an upward crossing */
6431  ((v1.y <= p->y) && (v2.y > p->y))
6432  /* a downward crossing */
6433  || ((v1.y > p->y) && (v2.y <= p->y))
6434  )
6435  {
6436 
6437  vt = (double)(p->y - v1.y) / (v2.y - v1.y);
6438 
6439  /* P->x <intersect */
6440  if (p->x < v1.x + vt * (v2.x - v1.x))
6441  {
6442  /* a valid crossing of y=p->y right of p->x */
6443  ++cn;
6444  }
6445  }
6446  v1 = v2;
6447  }
6448 
6449  LWDEBUGF(3, "_lwt_EdgeRingCrossingCount returning %d", cn);
6450 
6451 #ifndef RELAX
6452  if ( memcmp(&v1, &v0, sizeof(POINT2D)) )
6453  {
6454  lwerror("_lwt_EdgeRingCrossingCount: V[n] != V[0] (%g %g != %g %g)",
6455  v1.x, v1.y, v0.x, v0.y);
6456  return -1;
6457  }
6458 #endif
6459 
6460  return cn;
6461 }
6462 
6463 /* Return 1 for true, 0 for false */
6464 static int
6466 {
6467  int cn = 0;
6468 
6470  cn = _lwt_EdgeRingCrossingCount(p, it);
6471  lwfree(it);
6472  return (cn&1); /* 0 if even (out), and 1 if odd (in) */
6473 }
6474 
6475 static GBOX *
6477 {
6478  int i;
6479 
6480  if ( ! ring->env )
6481  {
6482  LWDEBUGF(2, "Computing GBOX for ring %p", ring);
6483  for (i=0; i<ring->size; ++i)
6484  {
6485  LWT_EDGERING_ELEM *elem = ring->elems[i];
6486  LWLINE *g = elem->edge->geom;
6487  const GBOX *newbox = lwgeom_get_bbox(lwline_as_lwgeom(g));
6488  if ( ! i ) ring->env = gbox_clone( newbox );
6489  else gbox_merge( newbox, ring->env );
6490  }
6491  }
6492 
6493  return ring->env;
6494 }
6495 
6496 static LWT_ELEMID
6498 {
6499  LWT_EDGERING_ELEM *el = ring->elems[0];
6500  return el->left ? el->edge->face_left : el->edge->face_right;
6501 }
6502 
6503 
6504 /*
6505  * Register a face on an edge side
6506  *
6507  * Create and register face to shell (CCW) walks,
6508  * register arbitrary negative face_id to CW rings.
6509  *
6510  * Push CCW rings to shells, CW rings to holes.
6511  *
6512  * The ownership of the "geom" and "ids" members of the
6513  * LWT_EDGERING pushed to the given LWT_EDGERING_ARRAYS
6514  * are transferred to caller.
6515  *
6516  * @param side 1 for left side, -1 for right side
6517  *
6518  * @param holes an array where holes will be pushed
6519  *
6520  * @param shells an array where shells will be pushed
6521  *
6522  * @param registered id of registered face. It will be a negative number
6523  * for holes or isolated edge strips (still registered in the face
6524  * table, but only temporary).
6525  *
6526  * @return 0 on success, -1 on error.
6527  *
6528  */
6529 static int
6531  int side, LWT_ISO_EDGE_TABLE *edges,
6532  LWT_EDGERING_ARRAY *holes,
6533  LWT_EDGERING_ARRAY *shells,
6534  LWT_ELEMID *registered)
6535 {
6536  const LWT_BE_IFACE *iface = topo->be_iface;
6537  /* this is arbitrary, could be taken as parameter */
6538  static const int placeholder_faceid = LWT_HOLES_FACE_PLACEHOLDER;
6539  LWT_EDGERING *ring;
6540 
6541  /* Get edge ring */
6542  ring = _lwt_BuildEdgeRing(topo, edges, edge, side);
6543 
6544  LWDEBUG(2, "Ring built, calling EdgeRingIsCCW");
6545 
6546  /* Compute winding (CW or CCW?) */
6547  int isccw = _lwt_EdgeRingIsCCW(ring);
6548 
6549  if ( isccw )
6550  {
6551  /* Create new face */
6552  LWT_ISO_FACE newface;
6553 
6554  LWDEBUGF(1, "Ring of edge %d is a shell (shell %d)", edge->edge_id * side, shells->size);
6555 
6556  newface.mbr = _lwt_EdgeRingGetBbox(ring);
6557 
6558  newface.face_id = -1;
6559  /* Insert the new face */
6560  int ret = lwt_be_insertFaces( topo, &newface, 1 );
6561  newface.mbr = NULL;
6562  if ( ret == -1 )
6563  {
6564  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
6565  return -1;
6566  }
6567  if ( ret != 1 )
6568  {
6569  lwerror("Unexpected error: %d faces inserted when expecting 1", ret);
6570  return -1;
6571  }
6572  /* return new face_id */
6573  *registered = newface.face_id;
6574  LWT_EDGERING_ARRAY_PUSH(shells, ring);
6575 
6576  /* update ring edges set new face_id on resp. side to *registered */
6577  ret = _lwt_UpdateEdgeRingSideFace(topo, ring, *registered);
6578  if ( ret )
6579  {
6580  lwerror("Errors updating edgering side face: %s",
6581  lwt_be_lastErrorMessage(iface));
6582  return -1;
6583  }
6584 
6585  }
6586  else /* cw, so is an hole */
6587  {
6588  LWDEBUGF(1, "Ring of edge %d is a hole (hole %d)", edge->edge_id * side, holes->size);
6589  *registered = placeholder_faceid;
6590  LWT_EDGERING_ARRAY_PUSH(holes, ring);
6591  }
6592 
6593  return 0;
6594 }
6595 
6596 static void
6597 _lwt_AccumulateCanditates(void* item, void* userdata)
6598 {
6599  LWT_EDGERING_ARRAY *candidates = userdata;
6600  LWT_EDGERING *sring = item;
6601  LWT_EDGERING_ARRAY_PUSH(candidates, sring);
6602 }
6603 
6604 static LWT_ELEMID
6606  LWT_EDGERING_ARRAY *shells)
6607 {
6608  LWT_ELEMID foundInFace = -1;
6609  int i;
6610  const GBOX *minenv = NULL;
6611  POINT2D pt;
6612  const GBOX *testbox;
6613  GEOSGeometry *ghole;
6614 
6615  getPoint2d_p( ring->elems[0]->edge->geom->points, 0, &pt );
6616 
6617  testbox = _lwt_EdgeRingGetBbox(ring);
6618 
6619  /* Create a GEOS Point from a vertex of the hole ring */
6620  {
6621  LWPOINT *point = lwpoint_make2d(topo->srid, pt.x, pt.y);
6622  ghole = LWGEOM2GEOS( lwpoint_as_lwgeom(point), 1 );
6623  lwpoint_free(point);
6624  if ( ! ghole ) {
6625  lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
6626  return -1;
6627  }
6628  }
6629 
6630  /* Build STRtree of shell envelopes */
6631  if ( ! shells->tree )
6632  {
6633  static const int STRTREE_NODE_CAPACITY = 10;
6634  LWDEBUG(1, "Building STRtree");
6635  shells->tree = GEOSSTRtree_create(STRTREE_NODE_CAPACITY);
6636  if (shells->tree == NULL)
6637  {
6638  lwerror("Could not create GEOS STRTree: %s", lwgeom_geos_errmsg);
6639  return -1;
6640  }
6641  for (i=0; i<shells->size; ++i)
6642  {
6643  LWT_EDGERING *sring = shells->rings[i];
6644  const GBOX* shellbox = _lwt_EdgeRingGetBbox(sring);
6645  LWDEBUGF(2, "GBOX of shell %p for edge %d is %g %g,%g %g",
6646  sring, sring->elems[0]->edge->edge_id, shellbox->xmin,
6647  shellbox->ymin, shellbox->xmax, shellbox->ymax);
6648  POINTARRAY *pa = ptarray_construct(0, 0, 2);
6649  POINT4D pt;
6650  LWLINE *diag;
6651  pt.x = shellbox->xmin;
6652  pt.y = shellbox->ymin;
6653  ptarray_set_point4d(pa, 0, &pt);
6654  pt.x = shellbox->xmax;
6655  pt.y = shellbox->ymax;
6656  ptarray_set_point4d(pa, 1, &pt);
6657  diag = lwline_construct(topo->srid, NULL, pa);
6658  /* Record just envelope in ggeom */
6659  /* making valid, probably not needed */
6660  sring->genv = LWGEOM2GEOS( lwline_as_lwgeom(diag), 1 );
6661  lwline_free(diag);
6662  GEOSSTRtree_insert(shells->tree, sring->genv, sring);
6663  }
6664  LWDEBUG(1, "STRtree build completed");
6665  }
6666 
6667  LWT_EDGERING_ARRAY candidates;
6668  LWT_EDGERING_ARRAY_INIT(&candidates);
6669  GEOSSTRtree_query(shells->tree, ghole, &_lwt_AccumulateCanditates, &candidates);
6670  LWDEBUGF(1, "Found %d candidate shells containing first point of ring's originating edge %d",
6671  candidates.size, ring->elems[0]->edge->edge_id * ( ring->elems[0]->left ? 1 : -1 ) );
6672 
6673  /* TODO: sort candidates by bounding box size */
6674 
6675  for (i=0; i<candidates.size; ++i)
6676  {
6677  LWT_EDGERING *sring = candidates.rings[i];
6678  const GBOX* shellbox = _lwt_EdgeRingGetBbox(sring);
6679  int contains = 0;
6680 
6681  if ( sring->elems[0]->edge->edge_id == ring->elems[0]->edge->edge_id )
6682  {
6683  LWDEBUGF(1, "Shell %d is on other side of ring",
6684  _lwt_EdgeRingGetFace(sring));
6685  continue;
6686  }
6687 
6688  /* The hole envelope cannot equal the shell envelope */
6689  if ( gbox_same(shellbox, testbox) )
6690  {
6691  LWDEBUGF(1, "Bbox of shell %d equals that of hole ring",
6692  _lwt_EdgeRingGetFace(sring));
6693  continue;
6694  }
6695 
6696  /* Skip if ring box is not in shell box */
6697  if ( ! gbox_contains_2d(shellbox, testbox) )
6698  {
6699  LWDEBUGF(1, "Bbox of shell %d does not contain bbox of ring point",
6700  _lwt_EdgeRingGetFace(sring));
6701  continue;
6702  }
6703 
6704  /* Skip test if a containing shell was already found
6705  * and this shell's bbox is not contained in the other */
6706  if ( minenv && ! gbox_contains_2d(minenv, shellbox) )
6707  {
6708  LWDEBUGF(2, "Bbox of shell %d (face %d) not contained by bbox "
6709  "of last shell found to contain the point",
6710  i, _lwt_EdgeRingGetFace(sring));
6711  continue;
6712  }
6713 
6714  contains = _lwt_EdgeRingContainsPoint(sring, &pt);
6715  if ( contains )
6716  {
6717  /* Continue until all shells are tested, as we want to
6718  * use the one with the smallest bounding box */
6719  /* IDEA: sort shells by bbox size, stopping on first match */
6720  LWDEBUGF(1, "Shell %d contains hole of edge %d",
6721  _lwt_EdgeRingGetFace(sring),
6722  ring->elems[0]->edge->edge_id);
6723  minenv = shellbox;
6724  foundInFace = _lwt_EdgeRingGetFace(sring);
6725  }
6726  }
6727  if ( foundInFace == -1 ) foundInFace = 0;
6728 
6729  candidates.size = 0; /* Avoid destroying the actual shell rings */
6730  LWT_EDGERING_ARRAY_CLEAN(&candidates);
6731 
6732  GEOSGeom_destroy(ghole);
6733 
6734  return foundInFace;
6735 }
6736 
6737 /*
6738  * @return -1 on error (and report error),
6739  * 1 if faces beside the universal one exist
6740  * 0 otherwise
6741  */
6742 static int
6744 {
6745  LWT_ISO_FACE *faces;
6746  int fields = LWT_COL_FACE_FACE_ID;
6747  uint64_t nelems = 1;
6748  GBOX qbox;
6749 
6750  qbox.xmin = qbox.ymin = -DBL_MAX;
6751  qbox.xmax = qbox.ymax = DBL_MAX;
6752  faces = lwt_be_getFaceWithinBox2D( topo, &qbox, &nelems, fields, 1);
6753  if (nelems == UINT64_MAX)
6754  {
6755  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
6756  return -1;
6757  }
6758  if ( faces ) _lwt_release_faces(faces, nelems);
6759  return nelems;
6760 }
6761 
6762 int
6764 {
6765  /*
6766  Fetch all edges
6767  Sort edges by edge_id
6768  Mark all edges' left and right face as -1
6769  Iteratively:
6770  Fetch next edge with left or right face == -1
6771  For each side with face == -1:
6772  Find ring on its side
6773  If ring is CCW:
6774  create a new face, assign to the ring edges' appropriate side
6775  If ring is CW (face needs to be same of external):
6776  assign a negative face_id the ring edges' appropriate side
6777  Now for each edge with a negative face_id on the side:
6778  Find containing face (mbr cache and all)
6779  Update with id of containing face
6780  */
6781 
6782  const LWT_BE_IFACE *iface = topo->be_iface;
6783  LWT_ISO_EDGE *edge;
6784  int numfaces = -1;
6785  LWT_ISO_EDGE_TABLE edgetable;
6786  LWT_EDGERING_ARRAY holes, shells;
6787  int i;
6788  int err = 0;
6789 
6790  LWT_EDGERING_ARRAY_INIT(&holes);
6791  LWT_EDGERING_ARRAY_INIT(&shells);
6792 
6793  initGEOS(lwnotice, lwgeom_geos_error);
6794 
6795  /*
6796  Check if Topology already contains some Face
6797  (ignoring the Universal Face)
6798  */
6799  numfaces = _lwt_CheckFacesExist(topo);
6800  if ( numfaces != 0 ) {
6801  if ( numfaces > 0 ) {
6802  /* Faces exist */
6803  lwerror("Polygonize: face table is not empty.");
6804  }
6805  /* Backend error, message should have been printed already */
6806  return -1;
6807  }
6808 
6809 
6810  edgetable.edges = _lwt_FetchAllEdges(topo, &(edgetable.size));
6811  if ( ! edgetable.edges ) {
6812  if (edgetable.size == 0) {
6813  /* not an error: no Edges */
6814  return 0;
6815  }
6816  /* error should have been printed already */
6817  return -1;
6818  }
6819 
6820  /* Sort edges by ID (to allow btree searches) */
6821  qsort(edgetable.edges, edgetable.size, sizeof(LWT_ISO_EDGE), compare_iso_edges_by_id);
6822 
6823  /* Mark all edges as unvisited */
6824  for (i=0; i<edgetable.size; ++i)
6825  edgetable.edges[i].face_left = edgetable.edges[i].face_right = -1;
6826 
6827  i = 0;
6828  while (1)
6829  {
6830  i = _lwt_FetchNextUnvisitedEdge(topo, &edgetable, i);
6831  if ( i < 0 ) break; /* end of unvisited */
6832  edge = &(edgetable.edges[i]);
6833 
6834  LWT_ELEMID newface = -1;
6835 
6836  LWDEBUGF(1, "Next face-missing edge has id:%d, face_left:%d, face_right:%d",
6837  edge->edge_id, edge->face_left, edge->face_right);
6838  if ( edge->face_left == -1 )
6839  {
6840  err = _lwt_RegisterFaceOnEdgeSide(topo, edge, 1, &edgetable,
6841  &holes, &shells, &newface);
6842  if ( err ) break;
6843  LWDEBUGF(1, "New face on the left of edge %d is %d",
6844  edge->edge_id, newface);
6845  edge->face_left = newface;
6846  }
6847  if ( edge->face_right == -1 )
6848  {
6849  err = _lwt_RegisterFaceOnEdgeSide(topo, edge, -1, &edgetable,
6850  &holes, &shells, &newface);
6851  if ( err ) break;
6852  LWDEBUGF(1, "New face on the right of edge %d is %d",
6853  edge->edge_id, newface);
6854  edge->face_right = newface;
6855  }
6856  }
6857 
6858  if ( err )
6859  {
6860  _lwt_release_edges(edgetable.edges, edgetable.size);
6861  LWT_EDGERING_ARRAY_CLEAN( &holes );
6862  LWT_EDGERING_ARRAY_CLEAN( &shells );
6863  lwerror("Errors fetching or registering face-missing edges: %s",
6864  lwt_be_lastErrorMessage(iface));
6865  return -1;
6866  }
6867 
6868  LWDEBUGF(1, "Found %d holes and %d shells", holes.size, shells.size);
6869 
6870  /* TODO: sort holes by pt.x, sort shells by bbox.xmin */
6871 
6872  /* Assign shells to holes */
6873  for (i=0; i<holes.size; ++i)
6874  {
6875  LWT_ELEMID containing_face;
6876  LWT_EDGERING *ring = holes.rings[i];
6877 
6878  containing_face = _lwt_FindFaceContainingRing(topo, ring, &shells);
6879  LWDEBUGF(1, "Ring %d contained by face %" LWTFMT_ELEMID, i, containing_face);
6880  if ( containing_face == -1 )
6881  {
6882  _lwt_release_edges(edgetable.edges, edgetable.size);
6883  LWT_EDGERING_ARRAY_CLEAN( &holes );
6884  LWT_EDGERING_ARRAY_CLEAN( &shells );
6885  lwerror("Errors finding face containing ring: %s",
6886  lwt_be_lastErrorMessage(iface));
6887  return -1;
6888  }
6889  int ret = _lwt_UpdateEdgeRingSideFace(topo, holes.rings[i], containing_face);
6890  if ( ret )
6891  {
6892  _lwt_release_edges(edgetable.edges, edgetable.size);
6893  LWT_EDGERING_ARRAY_CLEAN( &holes );
6894  LWT_EDGERING_ARRAY_CLEAN( &shells );
6895  lwerror("Errors updating edgering side face: %s",
6896  lwt_be_lastErrorMessage(iface));
6897  return -1;
6898  }
6899  }
6900 
6901  LWDEBUG(1, "All holes assigned, cleaning up");
6902 
6903  _lwt_release_edges(edgetable.edges, edgetable.size);
6904 
6905  /* delete all shell and hole EDGERINGS */
6906  LWT_EDGERING_ARRAY_CLEAN( &holes );
6907  LWT_EDGERING_ARRAY_CLEAN( &shells );
6908 
6909  return 0;
6910 }
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:626
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:319
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:693
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:738
#define LW_INSIDE
Constants for point-in-polygon return values.
LWGEOM * lwline_remove_repeated_points(const LWLINE *in, double tolerance)
Definition: lwline.c:439
#define LW_BOUNDARY
void ptarray_reverse_in_place(POINTARRAY *pa)
Definition: ptarray.c:331
#define LW_ON_INTERRUPT(x)
POINTARRAY * ptarray_clone(const POINTARRAY *ptarray)
Clone a POINTARRAY object.
Definition: ptarray.c:657
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:732
#define FP_ABS(a)
#define LW_OUTSIDE
int ptarray_isccw(const POINTARRAY *pa)
Definition: ptarray.c:1026
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:5232
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:6367
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:3614
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:6465
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:3750
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:4693
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:6058
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:5507
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:3547
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:6319
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:6497
LWT_ELEMID * lwt_AddLine(LWT_TOPOLOGY *topo, LWLINE *line, double tol, int *nedges)
Adds a linestring to the topology.
Definition: lwgeom_topo.c:5854
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:4195
static double _lwt_minToleranceDouble(double d)
Definition: lwgeom_topo.c:4849
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:4870
static int _lwt_UpdateEdgeRingSideFace(LWT_TOPOLOGY *topo, LWT_EDGERING *ring, LWT_ELEMID face)
Definition: lwgeom_topo.c:6226
static int _lwt_EdgeRingIsCCW(LWT_EDGERING *ring)
Definition: lwgeom_topo.c:6399
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:6763
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:6173
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:6133
#define LWT_HOLES_FACE_PLACEHOLDER
Definition: lwgeom_topo.c:6186
static int _lwt_CheckFacesExist(LWT_TOPOLOGY *topo)
Definition: lwgeom_topo.c:6743
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:6476
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:5241
#define LWT_EDGERING_ARRAY_INIT(a)
Definition: lwgeom_topo.c:6097
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:5315
static int compare_scored_pointer(const void *si1, const void *si2)
Definition: lwgeom_topo.c:4897
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:4189
LWT_ELEMID * lwt_AddPolygon(LWT_TOPOLOGY *topo, LWPOLY *poly, double tol, int *nfaces)
Adds a polygon to the topology.
Definition: lwgeom_topo.c:5866
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:6025
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:3783
#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:3573
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:5860
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:6530
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:4646
static void _lwt_AccumulateCanditates(void *item, void *userdata)
Definition: lwgeom_topo.c:6597
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:6066
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:6605
#define _LWT_MINTOLERANCE(topo, geom)
Definition: lwgeom_topo.c:4888
#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:4652
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:6106
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:6200
static LWT_ELEMID _lwt_HealEdges(LWT_TOPOLOGY *topo, LWT_ELEMID eid1, LWT_ELEMID eid2, int modEdge)
Definition: lwgeom_topo.c:4201
static LWT_ELEMID * _lwt_AddLine(LWT_TOPOLOGY *topo, LWLINE *line, double tol, int *nedges, int handleFaceSplit)
Definition: lwgeom_topo.c:5529
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:6012
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:6189
struct scored_pointer_t scored_pointer
#define LWT_EDGERING_ARRAY_PUSH(a, r)
Definition: lwgeom_topo.c:6117
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:4658
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:4917
int lwt_RemIsoEdge(LWT_TOPOLOGY *topo, LWT_ELEMID id)
Remove an isolated edge.
Definition: lwgeom_topo.c:3645
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:6413
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:3187
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:3808
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:4749
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:6094
LWT_EDGERING ** rings
Definition: lwgeom_topo.c:6091
LWT_ISO_EDGE * edge
Definition: lwgeom_topo.c:6038
LWT_EDGERING_ELEM * curelem
Definition: lwgeom_topo.c:6127
GEOSGeometry * genv
Definition: lwgeom_topo.c:6055
LWT_EDGERING_ELEM ** elems
Definition: lwgeom_topo.c:6048
LWT_ISO_EDGE * edges
Definition: lwgeom_topo.c:6007
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