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