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