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