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