1 /*
2
3 lwn_network.c -- Topology-Network abstract multi-backend interface
4
5 version 5.0, 2020 August 1
6
7 Author: Sandro Furieri a.furieri@lqt.it
8
9 -----------------------------------------------------------------------------
10
11 Version: MPL 1.1/GPL 2.0/LGPL 2.1
12
13 The contents of this file are subject to the Mozilla Public License Version
14 1.1 (the "License"); you may not use this file except in compliance with
15 the License. You may obtain a copy of the License at
16 http://www.mozilla.org/MPL/
17
18 Software distributed under the License is distributed on an "AS IS" basis,
19 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
20 for the specific language governing rights and limitations under the
21 License.
22
23 The Original Code is the SpatiaLite library
24
25 The Initial Developer of the Original Code is Alessandro Furieri
26
27 Portions created by the Initial Developer are Copyright (C) 2015-2021
28 the Initial Developer. All Rights Reserved.
29
30 Contributor(s):
31
32 Alternatively, the contents of this file may be used under the terms of
33 either the GNU General Public License Version 2 or later (the "GPL"), or
34 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
35 in which case the provisions of the GPL or the LGPL are applicable instead
36 of those above. If you wish to allow use of your version of this file only
37 under the terms of either the GPL or the LGPL, and not to allow others to
38 use your version of this file under the terms of the MPL, indicate your
39 decision by deleting the provisions above and replace them with the notice
40 and other provisions required by the GPL or the LGPL. If you do not delete
41 the provisions above, a recipient may use your version of this file under
42 the terms of any one of the MPL, the GPL or the LGPL.
43
44 */
45
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <float.h>
50
51 #if defined(_WIN32) && !defined(__MINGW32__)
52 #include "config-msvc.h"
53 #else
54 #include "config.h"
55 #endif
56
57 #ifdef ENABLE_RTTOPO /* only if RTTOPO is enabled */
58
59 #include <librttopo_geom.h>
60
61 #include "lwn_network.h"
62 #include "lwn_network_private.h"
63
64 #ifdef GEOS_REENTRANT
65 #ifdef GEOS_ONLY_REENTRANT
66 #define GEOS_USE_ONLY_R_API /* only fully thread-safe GEOS API */
67 #endif
68 #endif
69 #include <geos_c.h>
70
71 #include <librttopo_geom.h>
72
73 #include <spatialite_private.h>
74
75 /*********************************************************************
76 *
77 * Backend iface
78 *
79 ********************************************************************/
80
81 LWN_BE_IFACE *
lwn_CreateBackendIface(const RTCTX * ctx,const LWN_BE_DATA * data)82 lwn_CreateBackendIface (const RTCTX * ctx, const LWN_BE_DATA * data)
83 {
84 LWN_BE_IFACE *iface = malloc (sizeof (LWN_BE_IFACE));
85 iface->ctx = ctx;
86 iface->data = data;
87 iface->cb = NULL;
88 iface->errorMsg = NULL;
89 return iface;
90 }
91
92 void
lwn_BackendIfaceRegisterCallbacks(LWN_BE_IFACE * iface,const LWN_BE_CALLBACKS * cb)93 lwn_BackendIfaceRegisterCallbacks (LWN_BE_IFACE * iface,
94 const LWN_BE_CALLBACKS * cb)
95 {
96 iface->cb = cb;
97 }
98
99 void
lwn_FreeBackendIface(LWN_BE_IFACE * iface)100 lwn_FreeBackendIface (LWN_BE_IFACE * iface)
101 {
102 if (iface == NULL)
103 return;
104 if (iface->errorMsg != NULL)
105 free (iface->errorMsg);
106 free (iface);
107 }
108
109 /*********************************************************************
110 *
111 * Backend wrappers
112 *
113 ********************************************************************/
114
115 #define CHECKCB(be, method) do { \
116 if ( ! (be)->cb || ! (be)->cb->method ) \
117 lwn_SetErrorMsg(be, "Callback " # method " not registered by backend"); \
118 } while (0)
119
120 #define NETCB0(be, method) \
121 CHECKCB(be, method);\
122 return (be)->cb->method((be)->data)
123
124 #define NETCB1(be, method, a1) \
125 CHECKCB(be, method);\
126 return (be)->cb->method((be)->data, a1)
127
128 #define NETCBT0(to, method) \
129 CHECKCB((to)->be_iface, method);\
130 return (to)->be_iface->cb->method((to)->be_net)
131
132 #define NETCBT1(to, method, a1) \
133 CHECKCB((to)->be_iface, method);\
134 return (to)->be_iface->cb->method((to)->be_net, a1)
135
136 #define NETCBT2(to, method, a1, a2) \
137 CHECKCB((to)->be_iface, method);\
138 return (to)->be_iface->cb->method((to)->be_net, a1, a2)
139
140 #define NETCBT3(to, method, a1, a2, a3) \
141 CHECKCB((to)->be_iface, method);\
142 return (to)->be_iface->cb->method((to)->be_net, a1, a2, a3)
143
144 #define NETCBT4(to, method, a1, a2, a3, a4) \
145 CHECKCB((to)->be_iface, method);\
146 return (to)->be_iface->cb->method((to)->be_net, a1, a2, a3, a4)
147
148 #define NETCBT5(to, method, a1, a2, a3, a4, a5) \
149 CHECKCB((to)->be_iface, method);\
150 return (to)->be_iface->cb->method((to)->be_net, a1, a2, a3, a4, a5)
151
152
153 static LWN_BE_NETWORK *
lwn_be_loadNetworkByName(LWN_BE_IFACE * be,const char * name)154 lwn_be_loadNetworkByName (LWN_BE_IFACE * be, const char *name)
155 {
156 NETCB1 (be, loadNetworkByName, name);
157 }
158
159 static int
lwn_be_netGetSRID(LWN_NETWORK * net)160 lwn_be_netGetSRID (LWN_NETWORK * net)
161 {
162 NETCBT0 (net, netGetSRID);
163 }
164
165 static int
lwn_be_netHasZ(LWN_NETWORK * net)166 lwn_be_netHasZ (LWN_NETWORK * net)
167 {
168 NETCBT0 (net, netHasZ);
169 }
170
171 static int
lwn_be_netIsSpatial(LWN_NETWORK * net)172 lwn_be_netIsSpatial (LWN_NETWORK * net)
173 {
174 NETCBT0 (net, netIsSpatial);
175 }
176
177 static int
lwn_be_netAllowCoincident(LWN_NETWORK * net)178 lwn_be_netAllowCoincident (LWN_NETWORK * net)
179 {
180 NETCBT0 (net, netAllowCoincident);
181 }
182
183 static const void *
lwn_be_netGetGEOS(LWN_NETWORK * net)184 lwn_be_netGetGEOS (LWN_NETWORK * net)
185 {
186 NETCBT0 (net, netGetGEOS);
187 }
188
189 static int
lwn_be_freeNetwork(LWN_NETWORK * net)190 lwn_be_freeNetwork (LWN_NETWORK * net)
191 {
192 NETCBT0 (net, freeNetwork);
193 }
194
195 static LWN_NET_NODE *
lwn_be_getNetNodeWithinDistance2D(const LWN_NETWORK * net,const LWN_POINT * pt,double dist,int * numelems,int fields,int limit)196 lwn_be_getNetNodeWithinDistance2D (const LWN_NETWORK * net,
197 const LWN_POINT * pt, double dist,
198 int *numelems, int fields, int limit)
199 {
200 NETCBT5 (net, getNetNodeWithinDistance2D, pt, dist, numelems, fields,
201 limit);
202 }
203
204 static LWN_LINK *
lwn_be_getLinkWithinDistance2D(const LWN_NETWORK * net,const LWN_POINT * pt,double dist,int * numelems,int fields,int limit)205 lwn_be_getLinkWithinDistance2D (const LWN_NETWORK * net, const LWN_POINT * pt,
206 double dist, int *numelems, int fields,
207 int limit)
208 {
209 NETCBT5 (net, getLinkWithinDistance2D, pt, dist, numelems, fields, limit);
210 }
211
212 static int
lwn_be_updateNetNodesById(const LWN_NETWORK * net,const LWN_NET_NODE * nodes,int numnodes,int upd_fields)213 lwn_be_updateNetNodesById (const LWN_NETWORK * net,
214 const LWN_NET_NODE * nodes, int numnodes,
215 int upd_fields)
216 {
217 NETCBT3 (net, updateNetNodesById, nodes, numnodes, upd_fields);
218 }
219
220 static int
lwn_be_insertNetNodes(const LWN_NETWORK * net,LWN_NET_NODE * node,int numelems)221 lwn_be_insertNetNodes (const LWN_NETWORK * net, LWN_NET_NODE * node,
222 int numelems)
223 {
224 NETCBT2 (net, insertNetNodes, node, numelems);
225 }
226
227 static LWN_NET_NODE *
lwn_be_getNetNodeById(const LWN_NETWORK * net,const LWN_ELEMID * ids,int * numelems,int fields)228 lwn_be_getNetNodeById (const LWN_NETWORK * net, const LWN_ELEMID * ids,
229 int *numelems, int fields)
230 {
231 NETCBT3 (net, getNetNodeById, ids, numelems, fields);
232 }
233
234 static LWN_LINK *
lwn_be_getLinkByNetNode(const LWN_NETWORK * net,const LWN_ELEMID * ids,int * numelems,int fields)235 lwn_be_getLinkByNetNode (const LWN_NETWORK * net, const LWN_ELEMID * ids,
236 int *numelems, int fields)
237 {
238 NETCBT3 (net, getLinkByNetNode, ids, numelems, fields);
239 }
240
241 static int
lwn_be_deleteNetNodesById(const LWN_NETWORK * net,const LWN_ELEMID * ids,int numelems)242 lwn_be_deleteNetNodesById (const LWN_NETWORK * net, const LWN_ELEMID * ids,
243 int numelems)
244 {
245 NETCBT2 (net, deleteNetNodesById, ids, numelems);
246 }
247
248 static LWN_ELEMID
lwn_be_getNextLinkId(const LWN_NETWORK * net)249 lwn_be_getNextLinkId (const LWN_NETWORK * net)
250 {
251 NETCBT0 (net, getNextLinkId);
252 }
253
254 static LWN_NET_NODE *
lwn_be_getNetNodeWithinBox2D(const LWN_NETWORK * net,const LWN_BBOX * box,int * numelems,int fields,int limit)255 lwn_be_getNetNodeWithinBox2D (const LWN_NETWORK * net,
256 const LWN_BBOX * box, int *numelems, int fields,
257 int limit)
258 {
259 NETCBT4 (net, getNetNodeWithinBox2D, box, numelems, fields, limit);
260 }
261
262 static int
lwn_be_insertLinks(const LWN_NETWORK * net,LWN_LINK * link,int numelems)263 lwn_be_insertLinks (const LWN_NETWORK * net, LWN_LINK * link, int numelems)
264 {
265 NETCBT2 (net, insertLinks, link, numelems);
266 }
267
268 static int
lwn_be_updateLinksById(const LWN_NETWORK * net,LWN_LINK * links,int numlinks,int upd_fields)269 lwn_be_updateLinksById (const LWN_NETWORK * net, LWN_LINK * links, int numlinks,
270 int upd_fields)
271 {
272 NETCBT3 (net, updateLinksById, links, numlinks, upd_fields);
273 }
274
275 static LWN_LINK *
lwn_be_getLinkById(const LWN_NETWORK * net,const LWN_ELEMID * ids,int * numelems,int fields)276 lwn_be_getLinkById (const LWN_NETWORK * net, const LWN_ELEMID * ids,
277 int *numelems, int fields)
278 {
279 NETCBT3 (net, getLinkById, ids, numelems, fields);
280 }
281
282 static int
lwn_be_deleteLinksById(const LWN_NETWORK * net,const LWN_ELEMID * ids,int numelems)283 lwn_be_deleteLinksById (const LWN_NETWORK * net, const LWN_ELEMID * ids,
284 int numelems)
285 {
286 NETCBT2 (net, deleteLinksById, ids, numelems);
287 }
288
289
290 /************************************************************************
291 *
292 * API implementation
293 *
294 ************************************************************************/
295
296 LWN_NETWORK *
lwn_LoadNetwork(LWN_BE_IFACE * iface,const char * name)297 lwn_LoadNetwork (LWN_BE_IFACE * iface, const char *name)
298 {
299 LWN_BE_NETWORK *be_net;
300 LWN_NETWORK *net;
301
302 be_net = lwn_be_loadNetworkByName (iface, name);
303 if (!be_net)
304 {
305 lwn_SetErrorMsg (iface, "Could not load network from backend");
306 return NULL;
307 }
308 net = malloc (sizeof (LWN_NETWORK));
309 net->be_iface = iface;
310 net->be_net = be_net;
311 net->srid = lwn_be_netGetSRID (net);
312 net->hasZ = lwn_be_netHasZ (net);
313 net->spatial = lwn_be_netIsSpatial (net);
314 net->allowCoincident = lwn_be_netAllowCoincident (net);
315 net->geos_handle = lwn_be_netGetGEOS (net);
316 return net;
317 }
318
319 void
lwn_FreeNetwork(LWN_NETWORK * net)320 lwn_FreeNetwork (LWN_NETWORK * net)
321 {
322 if (!lwn_be_freeNetwork (net))
323 {
324 lwn_SetErrorMsg (net->be_iface,
325 "Could not release backend topology memory");
326 }
327 free (net);
328 }
329
330 LWN_POINT *
lwn_create_point2d(int srid,double x,double y)331 lwn_create_point2d (int srid, double x, double y)
332 {
333 LWN_POINT *point = malloc (sizeof (LWN_POINT));
334 point->srid = srid;
335 point->has_z = 0;
336 point->x = x;
337 point->y = y;
338 return point;
339 }
340
341 LWN_POINT *
lwn_create_point3d(int srid,double x,double y,double z)342 lwn_create_point3d (int srid, double x, double y, double z)
343 {
344 LWN_POINT *point = malloc (sizeof (LWN_POINT));
345 point->srid = srid;
346 point->has_z = 1;
347 point->x = x;
348 point->y = y;
349 point->z = z;
350 return point;
351 }
352
353 void
lwn_free_point(LWN_POINT * point)354 lwn_free_point (LWN_POINT * point)
355 {
356 if (point == NULL)
357 return;
358 free (point);
359 }
360
361 LWN_LINE *
lwn_alloc_line(int points,int srid,int hasz)362 lwn_alloc_line (int points, int srid, int hasz)
363 {
364 LWN_LINE *line = malloc (sizeof (LWN_LINE));
365 line->points = points;
366 line->srid = srid;
367 line->has_z = hasz;
368 line->x = malloc (sizeof (double) * points);
369 line->y = malloc (sizeof (double) * points);
370 if (hasz)
371 line->z = malloc (sizeof (double) * points);
372 else
373 line->z = NULL;
374 return line;
375 }
376
377 void
lwn_free_line(LWN_LINE * line)378 lwn_free_line (LWN_LINE * line)
379 {
380 if (line == NULL)
381 return;
382 if (line->x != NULL)
383 free (line->x);
384 if (line->y != NULL)
385 free (line->y);
386 if (line->z != NULL && line->has_z)
387 free (line->z);
388 free (line);
389 }
390
391 void
lwn_ResetErrorMsg(LWN_BE_IFACE * iface)392 lwn_ResetErrorMsg (LWN_BE_IFACE * iface)
393 {
394 if (iface == NULL)
395 return;
396 if (iface->errorMsg != NULL)
397 free (iface->errorMsg);
398 iface->errorMsg = NULL;
399 }
400
401 void
lwn_SetErrorMsg(LWN_BE_IFACE * iface,const char * message)402 lwn_SetErrorMsg (LWN_BE_IFACE * iface, const char *message)
403 {
404 int len;
405 if (iface == NULL)
406 return;
407 if (iface->errorMsg != NULL)
408 free (iface->errorMsg);
409 iface->errorMsg = NULL;
410 if (message == NULL)
411 return;
412 len = strlen (message);
413 iface->errorMsg = malloc (len + 1);
414 strcpy (iface->errorMsg, message);
415 }
416
417 const char *
lwn_GetErrorMsg(LWN_BE_IFACE * iface)418 lwn_GetErrorMsg (LWN_BE_IFACE * iface)
419 {
420 if (iface == NULL)
421 return NULL;
422 return iface->errorMsg;
423 }
424
425 static void
_lwn_release_nodes(LWN_NET_NODE * nodes,int num_nodes)426 _lwn_release_nodes (LWN_NET_NODE * nodes, int num_nodes)
427 {
428 int i;
429 for (i = 0; i < num_nodes; ++i)
430 {
431 if (nodes[i].geom != NULL)
432 lwn_free_point (nodes[i].geom);
433 }
434 free (nodes);
435 }
436
437 static void
_lwn_release_links(LWN_LINK * links,int num_links)438 _lwn_release_links (LWN_LINK * links, int num_links)
439 {
440 int i;
441 for (i = 0; i < num_links; ++i)
442 {
443 if (links[i].geom != NULL)
444 lwn_free_line (links[i].geom);
445 }
446 free (links);
447 }
448
449 LWN_ELEMID
lwn_AddIsoNetNode(LWN_NETWORK * net,LWN_POINT * pt)450 lwn_AddIsoNetNode (LWN_NETWORK * net, LWN_POINT * pt)
451 {
452 LWN_NET_NODE node;
453
454 if (net->spatial && net->allowCoincident == 0)
455 {
456 if (lwn_be_existsCoincidentNode (net, pt))
457 {
458 lwn_SetErrorMsg (net->be_iface,
459 "SQL/MM Spatial exception - coincident node.");
460 return -1;
461 }
462 if (lwn_be_existsLinkIntersectingPoint (net, pt))
463 {
464 lwn_SetErrorMsg (net->be_iface,
465 "SQL/MM Spatial exception - link crosses node.");
466 return -1;
467 }
468 }
469
470 node.node_id = -1;
471 node.geom = pt;
472 if (!lwn_be_insertNetNodes (net, &node, 1))
473 return -1;
474
475 return node.node_id;
476 }
477
478 static LWN_NET_NODE *
_lwn_GetIsoNetNode(LWN_NETWORK * net,LWN_ELEMID nid)479 _lwn_GetIsoNetNode (LWN_NETWORK * net, LWN_ELEMID nid)
480 {
481 LWN_NET_NODE *node;
482 int n = 1;
483 LWN_LINK *links;
484 int nlinks;
485
486 node = lwn_be_getNetNodeById (net, &nid, &n, LWN_COL_NODE_NODE_ID);
487 if (n < 0)
488 return 0;
489 if (n < 1)
490 {
491 lwn_SetErrorMsg (net->be_iface,
492 "SQL/MM Spatial exception - non-existent node.");
493 return 0;
494 }
495
496 nlinks = 1;
497 links = lwn_be_getLinkByNetNode (net, &nid, &nlinks, LWN_COL_LINK_LINK_ID);
498 if (nlinks < 0)
499 return 0;
500 if (nlinks != 0)
501 {
502 free (node);
503 _lwn_release_links (links, nlinks);
504 lwn_SetErrorMsg (net->be_iface,
505 "SQL/MM Spatial exception - not isolated node.");
506 return 0;
507 }
508
509 return node;
510 }
511
512 static LWN_LINK *
_lwn_GetLink(LWN_NETWORK * net,LWN_ELEMID link_id)513 _lwn_GetLink (LWN_NETWORK * net, LWN_ELEMID link_id)
514 {
515 LWN_LINK *link;
516 int n = 1;
517
518 link = lwn_be_getLinkById (net, &link_id, &n, LWN_COL_LINK_LINK_ID);
519 if (n < 0)
520 return 0;
521 if (n < 1)
522 {
523 lwn_SetErrorMsg (net->be_iface,
524 "SQL/MM Spatial exception - non-existent link.");
525 return 0;
526 }
527 return link;
528 }
529
530 static int
line2bbox(const LWN_LINE * line,LWN_BBOX * bbox)531 line2bbox (const LWN_LINE * line, LWN_BBOX * bbox)
532 {
533 int iv;
534 if (line == NULL)
535 return 0;
536
537 bbox->min_x = DBL_MAX;
538 bbox->min_y = DBL_MAX;
539 bbox->max_x = -DBL_MAX;
540 bbox->max_y = -DBL_MAX;
541
542 for (iv = 0; iv < line->points; iv++)
543 {
544 double x = line->x[iv];
545 double y = line->y[iv];
546 if (x < bbox->min_x)
547 bbox->min_x = x;
548 if (y < bbox->min_y)
549 bbox->min_y = y;
550 if (x > bbox->max_x)
551 bbox->max_x = x;
552 if (y > bbox->max_y)
553 bbox->max_y = y;
554 }
555
556 return 1;
557 }
558
559
560 static GEOSGeometry *
point2geos(GEOSContextHandle_t handle,const LWN_POINT * point)561 point2geos (GEOSContextHandle_t handle, const LWN_POINT * point)
562 {
563 GEOSGeometry *geos = NULL;
564 GEOSCoordSequence *cs;
565 cs = GEOSCoordSeq_create_r (handle, 1, 2);
566 GEOSCoordSeq_setX_r (handle, cs, 0, point->x);
567 GEOSCoordSeq_setY_r (handle, cs, 0, point->y);
568 geos = GEOSGeom_createPoint_r (handle, cs);
569 return geos;
570 }
571
572 static GEOSGeometry *
line2geos(GEOSContextHandle_t handle,const LWN_LINE * line)573 line2geos (GEOSContextHandle_t handle, const LWN_LINE * line)
574 {
575 int iv;
576 GEOSGeometry *geos = NULL;
577 GEOSCoordSequence *cs;
578 cs = GEOSCoordSeq_create_r (handle, line->points, 2);
579 for (iv = 0; iv < line->points; iv++)
580 {
581 GEOSCoordSeq_setX_r (handle, cs, iv, line->x[iv]);
582 GEOSCoordSeq_setY_r (handle, cs, iv, line->y[iv]);
583 }
584 geos = GEOSGeom_createLineString_r (handle, cs);
585 return geos;
586 }
587
588 /* Check that a link does not cross an existing node
589 *
590 * Return -1 on cross or error, 0 if everything is fine.
591 * Note that before returning -1, lwerror is invoked...
592 */
593 static int
_lwn_CheckLinkCrossing(LWN_NETWORK * net,LWN_ELEMID start_node,LWN_ELEMID end_node,const LWN_LINE * geom)594 _lwn_CheckLinkCrossing (LWN_NETWORK * net,
595 LWN_ELEMID start_node, LWN_ELEMID end_node,
596 const LWN_LINE * geom)
597 {
598 int i, num_nodes;
599 LWN_NET_NODE *nodes;
600 LWN_BBOX linkbbox;
601 GEOSContextHandle_t handle = (GEOSContextHandle_t) (net->geos_handle);
602 GEOSGeometry *linkgg;
603 const GEOSPreparedGeometry *prepared_link;
604
605 linkgg = line2geos (handle, geom);
606 if (!linkgg)
607 return -1;
608 prepared_link = GEOSPrepare_r (handle, linkgg);
609 if (!prepared_link)
610 return -1;
611 if (!line2bbox (geom, &linkbbox))
612 {
613 GEOSPreparedGeom_destroy_r (handle, prepared_link);
614 GEOSGeom_destroy_r (handle, linkgg);
615 return -1;
616 }
617
618 /* loop over each node within the edge's gbox */
619 nodes = lwn_be_getNetNodeWithinBox2D (net, &linkbbox, &num_nodes,
620 LWN_COL_NODE_ALL, 0);
621 if (num_nodes == -1)
622 {
623 GEOSPreparedGeom_destroy_r (handle, prepared_link);
624 GEOSGeom_destroy_r (handle, linkgg);
625 return -1;
626 }
627 for (i = 0; i < num_nodes; ++i)
628 {
629 LWN_NET_NODE *node = &(nodes[i]);
630 GEOSGeometry *nodegg;
631 int contains;
632 if (node->node_id == start_node)
633 continue;
634 if (node->node_id == end_node)
635 continue;
636 /* check if the link contains this node (not on boundary) */
637 nodegg = point2geos (handle, node->geom);
638 /* ST_RelateMatch(rec.relate, 'T********') */
639 contains = GEOSPreparedContains_r (handle, prepared_link, nodegg);
640 GEOSGeom_destroy_r (handle, nodegg);
641 if (contains == 2)
642 {
643 GEOSPreparedGeom_destroy_r (handle, prepared_link);
644 GEOSGeom_destroy_r (handle, linkgg);
645 _lwn_release_nodes (nodes, num_nodes);
646 lwn_SetErrorMsg (net->be_iface,
647 "GEOS exception on PreparedContains");
648 return -1;
649 }
650 if (contains)
651 {
652 GEOSPreparedGeom_destroy_r (handle, prepared_link);
653 GEOSGeom_destroy_r (handle, linkgg);
654 _lwn_release_nodes (nodes, num_nodes);
655 lwn_SetErrorMsg (net->be_iface,
656 "SQL/MM Spatial exception - geometry crosses a node.");
657 return -1;
658 }
659 }
660 if (nodes)
661 _lwn_release_nodes (nodes, num_nodes);
662 GEOSPreparedGeom_destroy_r (handle, prepared_link);
663 GEOSGeom_destroy_r (handle, linkgg);
664
665 return 0;
666 }
667
668 int
lwn_MoveIsoNetNode(LWN_NETWORK * net,LWN_ELEMID nid,const LWN_POINT * pt)669 lwn_MoveIsoNetNode (LWN_NETWORK * net, LWN_ELEMID nid, const LWN_POINT * pt)
670 {
671 LWN_NET_NODE *node;
672 int ret;
673
674 node = _lwn_GetIsoNetNode (net, nid);
675 if (!node)
676 return -1;
677
678 if (net->spatial && net->allowCoincident == 0)
679 {
680 if (lwn_be_existsCoincidentNode (net, pt))
681 {
682 _lwn_release_nodes (node, 1);
683 lwn_SetErrorMsg (net->be_iface,
684 "SQL/MM Spatial exception - coincident node.");
685 return -1;
686 }
687
688 if (lwn_be_existsLinkIntersectingPoint (net, pt))
689 {
690 _lwn_release_nodes (node, 1);
691 lwn_SetErrorMsg (net->be_iface,
692 "SQL/MM Spatial exception - link crosses node.");
693 return -1;
694 }
695 }
696
697 node->node_id = nid;
698 if (node->geom)
699 lwn_free_point (node->geom);
700 node->geom = (LWN_POINT *) pt;
701 ret = lwn_be_updateNetNodesById (net, node, 1, LWN_COL_NODE_GEOM);
702 node->geom = NULL;
703 _lwn_release_nodes (node, 1);
704 if (ret == -1)
705 return -1;
706
707 return 0;
708 }
709
710 int
lwn_RemIsoNetNode(LWN_NETWORK * net,LWN_ELEMID nid)711 lwn_RemIsoNetNode (LWN_NETWORK * net, LWN_ELEMID nid)
712 {
713 LWN_NET_NODE *node;
714 int n = 1;
715
716 node = _lwn_GetIsoNetNode (net, nid);
717 if (!node)
718 return -1;
719
720 n = lwn_be_deleteNetNodesById (net, &nid, n);
721 if (n == -1)
722 {
723 lwn_SetErrorMsg (net->be_iface,
724 "SQL/MM Spatial exception - not isolated node.");
725 return -1;
726 }
727 if (n != 1)
728 return -1;
729
730 free (node);
731 return 0;
732 }
733
734 static int
getLineFirstPoint(const LWN_LINE * geom,LWN_POINT * pt)735 getLineFirstPoint (const LWN_LINE * geom, LWN_POINT * pt)
736 {
737 if (geom == NULL)
738 return 0;
739 pt->srid = geom->srid;
740 pt->has_z = geom->has_z;
741 pt->x = geom->x[0];
742 pt->y = geom->y[0];
743 if (geom->has_z)
744 pt->z = geom->z[0];
745 return 1;
746 }
747
748 static int
getLineLastPoint(const LWN_LINE * geom,LWN_POINT * pt)749 getLineLastPoint (const LWN_LINE * geom, LWN_POINT * pt)
750 {
751 int iv;
752 if (geom == NULL)
753 return 0;
754 iv = geom->points - 1;
755 pt->srid = geom->srid;
756 pt->has_z = geom->has_z;
757 pt->x = geom->x[iv];
758 pt->y = geom->y[iv];
759 if (geom->has_z)
760 pt->z = geom->z[iv];
761 return 1;
762 }
763
764 static int
point_same_2d(LWN_POINT * pt1,LWN_POINT * pt2)765 point_same_2d (LWN_POINT * pt1, LWN_POINT * pt2)
766 {
767 if (pt1->x == pt2->x && pt1->y == pt2->y)
768 return 1;
769 return 0;
770 }
771
772 LWN_ELEMID
lwn_AddLink(LWN_NETWORK * net,LWN_ELEMID startNode,LWN_ELEMID endNode,LWN_LINE * geom)773 lwn_AddLink (LWN_NETWORK * net,
774 LWN_ELEMID startNode, LWN_ELEMID endNode, LWN_LINE * geom)
775 {
776 int num_nodes;
777 int i;
778 LWN_LINK newlink;
779 LWN_NET_NODE *endpoints;
780 LWN_ELEMID *node_ids;
781 LWN_POINT pt;
782
783 /* NOT IN THE SPECS:
784 * A closed link makes no sense and should always be forbidden
785 */
786 if (startNode == endNode)
787 {
788 lwn_SetErrorMsg (net->be_iface,
789 "SQL/MM Spatial exception - self-closed links are forbidden.");
790 return -1;
791 }
792
793 /*
794 * Check for:
795 * existence of nodes
796 * Extract:
797 * nodes geoms
798 */
799 num_nodes = 2;
800 node_ids = malloc (sizeof (LWN_ELEMID) * num_nodes);
801 node_ids[0] = startNode;
802 node_ids[1] = endNode;
803 endpoints = lwn_be_getNetNodeById (net, node_ids, &num_nodes,
804 LWN_COL_NODE_ALL);
805 if (num_nodes < 0)
806 return -1;
807 else if (num_nodes < 2)
808 {
809 if (num_nodes)
810 _lwn_release_nodes (endpoints, num_nodes);
811 free (node_ids);
812 lwn_SetErrorMsg (net->be_iface,
813 "SQL/MM Spatial exception - non-existent node.");
814 return -1;
815 }
816 for (i = 0; i < num_nodes; ++i)
817 {
818 const LWN_NET_NODE *n = &(endpoints[i]);
819 if (net->spatial)
820 {
821 if (n->geom == NULL)
822 return -1;
823 if (n->node_id == startNode)
824 {
825 /* l) Check that start point of acurve match start node geoms. */
826 if (!getLineFirstPoint (geom, &pt))
827 return -1;
828 if (!point_same_2d (&pt, n->geom))
829 {
830 _lwn_release_nodes (endpoints, num_nodes);
831 free (node_ids);
832 lwn_SetErrorMsg (net->be_iface,
833 "SQL/MM Spatial exception - "
834 "start node not geometry start point.");
835 return -1;
836 }
837 }
838 else
839 {
840 /* m) Check that end point of acurve match end node geoms. */
841 if (!getLineLastPoint (geom, &pt))
842 return -1;
843 if (!point_same_2d (&pt, n->geom))
844 {
845 _lwn_release_nodes (endpoints, num_nodes);
846 free (node_ids);
847 lwn_SetErrorMsg (net->be_iface,
848 "SQL/MM Spatial exception - "
849 "end node not geometry end point.");
850 return -1;
851 }
852 }
853 }
854 }
855 _lwn_release_nodes (endpoints, num_nodes);
856 free (node_ids);
857
858 if (net->spatial && net->allowCoincident == 0)
859 {
860 if (_lwn_CheckLinkCrossing (net, startNode, endNode, geom))
861 return -1;
862 }
863
864 /*
865 * All checks passed, time to prepare the new link
866 */
867
868 newlink.link_id = lwn_be_getNextLinkId (net);
869 if (newlink.link_id == -1)
870 return -1;
871
872 newlink.start_node = startNode;
873 newlink.end_node = endNode;
874 newlink.geom = geom;
875
876 if (!lwn_be_insertLinks (net, &newlink, 1))
877 return -1;
878
879 return newlink.link_id;
880 }
881
882 int
lwn_ChangeLinkGeom(LWN_NETWORK * net,LWN_ELEMID link,const LWN_LINE * geom)883 lwn_ChangeLinkGeom (LWN_NETWORK * net, LWN_ELEMID link, const LWN_LINE * geom)
884 {
885 int num_nodes;
886 int i;
887 LWN_LINK newlink;
888 LWN_LINK *oldlink;
889 LWN_NET_NODE *endpoints;
890 LWN_ELEMID *node_ids;
891 LWN_POINT pt;
892 LWN_ELEMID startNode;
893 LWN_ELEMID endNode;
894 int ret;
895
896 i = 1;
897 oldlink =
898 lwn_be_getLinkById (net, &link, &i,
899 LWN_COL_LINK_START_NODE | LWN_COL_LINK_END_NODE);
900 if (!oldlink)
901 {
902 if (i == -1)
903 return -1;
904 else if (i == 0)
905 {
906 lwn_SetErrorMsg (net->be_iface,
907 "SQL/MM Spatial exception - non-existent link.");
908 return -1;
909 }
910 }
911 startNode = oldlink->start_node;
912 endNode = oldlink->end_node;
913 _lwn_release_links (oldlink, 1);
914
915 /*
916 * Check for:
917 * existence of nodes
918 * Extract:
919 * nodes geoms
920 */
921 num_nodes = 2;
922 node_ids = malloc (sizeof (LWN_ELEMID) * num_nodes);
923 node_ids[0] = startNode;
924 node_ids[1] = endNode;
925 endpoints = lwn_be_getNetNodeById (net, node_ids, &num_nodes,
926 LWN_COL_NODE_ALL);
927 if (num_nodes < 0)
928 return -1;
929 else if (num_nodes < 2)
930 {
931 if (num_nodes)
932 _lwn_release_nodes (endpoints, num_nodes);
933 free (node_ids);
934 lwn_SetErrorMsg (net->be_iface,
935 "SQL/MM Spatial exception - non-existent node.");
936 return -1;
937 }
938 for (i = 0; i < num_nodes; ++i)
939 {
940 const LWN_NET_NODE *n = &(endpoints[i]);
941 if (net->spatial)
942 {
943 if (n->geom == NULL)
944 return -1;
945 if (n->node_id == startNode)
946 {
947 /* l) Check that start point of acurve match start node geoms. */
948 if (!getLineFirstPoint (geom, &pt))
949 return -1;
950 if (!point_same_2d (&pt, n->geom))
951 {
952 _lwn_release_nodes (endpoints, num_nodes);
953 free (node_ids);
954 lwn_SetErrorMsg (net->be_iface,
955 "SQL/MM Spatial exception - "
956 "start node not geometry start point.");
957 return -1;
958 }
959 }
960 else
961 {
962 /* m) Check that end point of acurve match end node geoms. */
963 if (!getLineLastPoint (geom, &pt))
964 return -1;
965 if (!point_same_2d (&pt, n->geom))
966 {
967 _lwn_release_nodes (endpoints, num_nodes);
968 free (node_ids);
969 lwn_SetErrorMsg (net->be_iface,
970 "SQL/MM Spatial exception - "
971 "end node not geometry end point.");
972 return -1;
973 }
974 }
975 }
976 }
977 _lwn_release_nodes (endpoints, num_nodes);
978 free (node_ids);
979
980 if (net->spatial && net->allowCoincident == 0)
981 {
982 if (_lwn_CheckLinkCrossing (net, startNode, endNode, geom))
983 return -1;
984 }
985
986 /*
987 * All checks passed, time to prepare the new link
988 */
989
990 newlink.link_id = link;
991 newlink.start_node = startNode;
992 newlink.end_node = endNode;
993 newlink.geom = (LWN_LINE *) geom;
994
995 ret = lwn_be_updateLinksById (net, &newlink, 1, LWN_COL_LINK_GEOM);
996 if (ret == -1)
997 return -1;
998 else if (ret == 0)
999 return -1;
1000
1001 return 0;
1002 }
1003
1004 int
lwn_RemoveLink(LWN_NETWORK * net,LWN_ELEMID link_id)1005 lwn_RemoveLink (LWN_NETWORK * net, LWN_ELEMID link_id)
1006 {
1007 LWN_LINK *link;
1008 int n = 1;
1009
1010 link = _lwn_GetLink (net, link_id);
1011 if (!link)
1012 return -1;
1013
1014 n = lwn_be_deleteLinksById (net, &link_id, n);
1015 if (n != 1)
1016 return -1;
1017
1018 free (link);
1019 return 0;
1020 }
1021
1022 LWN_INT64
lwn_NewLogLinkSplit(LWN_NETWORK * net,LWN_ELEMID link)1023 lwn_NewLogLinkSplit (LWN_NETWORK * net, LWN_ELEMID link)
1024 {
1025 int i;
1026 LWN_LINK *oldlink;
1027 LWN_LINK newlink[2];
1028 LWN_ELEMID startNode;
1029 LWN_ELEMID endNode;
1030 LWN_NET_NODE newnode;
1031
1032 i = 1;
1033 oldlink =
1034 lwn_be_getLinkById (net, &link, &i,
1035 LWN_COL_LINK_START_NODE | LWN_COL_LINK_END_NODE);
1036 if (!oldlink)
1037 {
1038 if (i == -1)
1039 return -1;
1040 else if (i == 0)
1041 {
1042 lwn_SetErrorMsg (net->be_iface,
1043 "SQL/MM Spatial exception - non-existent link.");
1044 return -1;
1045 }
1046 }
1047 startNode = oldlink->start_node;
1048 endNode = oldlink->end_node;
1049 _lwn_release_links (oldlink, 1);
1050
1051 /* inserting a new NetNode */
1052 newnode.node_id = -1;
1053 newnode.geom = NULL;
1054 if (!lwn_be_insertNetNodes (net, &newnode, 1))
1055 return -1;
1056
1057 /* deleting the original Link */
1058 i = lwn_be_deleteLinksById (net, &link, 1);
1059 if (i != 1)
1060 return -1;
1061
1062 /* inserting two new Links */
1063 newlink[0].link_id = lwn_be_getNextLinkId (net);
1064 if (newlink[0].link_id == -1)
1065 return -1;
1066 newlink[1].link_id = lwn_be_getNextLinkId (net);
1067 if (newlink[1].link_id == -1)
1068 return -1;
1069
1070 newlink[0].start_node = startNode;
1071 newlink[0].end_node = newnode.node_id;
1072 newlink[0].geom = NULL;
1073 newlink[1].start_node = newnode.node_id;
1074 newlink[1].end_node = endNode;
1075 newlink[1].geom = NULL;
1076
1077 if (!lwn_be_insertLinks (net, newlink, 2))
1078 return -1;
1079
1080 return newnode.node_id;
1081 }
1082
1083 LWN_INT64
lwn_ModLogLinkSplit(LWN_NETWORK * net,LWN_ELEMID link)1084 lwn_ModLogLinkSplit (LWN_NETWORK * net, LWN_ELEMID link)
1085 {
1086 int i;
1087 LWN_LINK *oldlink;
1088 LWN_LINK newlink;
1089 LWN_ELEMID startNode;
1090 LWN_ELEMID endNode;
1091 LWN_NET_NODE newnode;
1092
1093 i = 1;
1094 oldlink =
1095 lwn_be_getLinkById (net, &link, &i,
1096 LWN_COL_LINK_START_NODE | LWN_COL_LINK_END_NODE);
1097 if (!oldlink)
1098 {
1099 if (i == -1)
1100 return -1;
1101 else if (i == 0)
1102 {
1103 lwn_SetErrorMsg (net->be_iface,
1104 "SQL/MM Spatial exception - non-existent link.");
1105 return -1;
1106 }
1107 }
1108 startNode = oldlink->start_node;
1109 endNode = oldlink->end_node;
1110 _lwn_release_links (oldlink, 1);
1111
1112 /* inserting a new NetNode */
1113 newnode.node_id = -1;
1114 newnode.geom = NULL;
1115 if (!lwn_be_insertNetNodes (net, &newnode, 1))
1116 return -1;
1117
1118 /* update the original Link */
1119 newlink.link_id = link;
1120 newlink.start_node = startNode;
1121 newlink.end_node = newnode.node_id;
1122 newlink.geom = NULL;
1123 if (!lwn_be_updateLinksById (net, &newlink, 1, LWN_COL_LINK_END_NODE))
1124 return -1;
1125
1126 /* inserting a new Link */
1127 newlink.link_id = lwn_be_getNextLinkId (net);
1128 if (newlink.link_id == -1)
1129 return -1;
1130 newlink.start_node = newnode.node_id;
1131 newlink.end_node = endNode;
1132 newlink.geom = NULL;
1133
1134 if (!lwn_be_insertLinks (net, &newlink, 1))
1135 return -1;
1136
1137 return newnode.node_id;
1138 }
1139
1140 static int
geo_link_split(LWN_NETWORK * net,const LWN_LINE * oldline,const LWN_POINT * pt,LWN_LINE * newline1,LWN_LINE * newline2)1141 geo_link_split (LWN_NETWORK * net, const LWN_LINE * oldline,
1142 const LWN_POINT * pt, LWN_LINE * newline1, LWN_LINE * newline2)
1143 {
1144 const RTCTX *ctx = NULL;
1145 RTPOINTARRAY *pa;
1146 RTPOINT4D point;
1147 int iv;
1148 RTGEOM *rtg_ln;
1149 RTGEOM *rtg_pt;
1150 RTGEOM *split;
1151 RTCOLLECTION *split_col;
1152 RTGEOM *rtg = NULL;
1153 RTLINE *rtl = NULL;
1154 RTPOINT4D pt4d;
1155 int ret = 0;
1156
1157 if (net == NULL)
1158 return 0;
1159 if (net->be_iface == NULL)
1160 return 0;
1161 ctx = net->be_iface->ctx;
1162 if (ctx == NULL)
1163 return 0;
1164
1165 /* creating an RTGEOM Linestring from oldline */
1166 pa = ptarray_construct (ctx, oldline->has_z, 0, oldline->points);
1167 for (iv = 0; iv < oldline->points; iv++)
1168 {
1169 /* copying vertices */
1170 point.x = oldline->x[iv];
1171 point.y = oldline->y[iv];
1172 if (oldline->has_z)
1173 point.z = oldline->z[iv];
1174 ptarray_set_point4d (ctx, pa, iv, &point);
1175 }
1176 rtg_ln = (RTGEOM *) rtline_construct (ctx, oldline->srid, NULL, pa);
1177
1178 /* creating an RTGEOM Point from pt */
1179 pa = ptarray_construct (ctx, pt->has_z, 0, 1);
1180 point.x = pt->x;
1181 point.y = pt->y;
1182 if (pt->has_z)
1183 point.z = pt->z;
1184 ptarray_set_point4d (ctx, pa, 0, &point);
1185 rtg_pt = (RTGEOM *) rtpoint_construct (ctx, oldline->srid, NULL, pa);
1186
1187 /* Split link */
1188 split = rtgeom_split (ctx, rtg_ln, rtg_pt);
1189 rtgeom_free (ctx, rtg_ln);
1190 rtgeom_free (ctx, rtg_pt);
1191 if (!split)
1192 {
1193 lwn_SetErrorMsg (net->be_iface, "could not split link by point ?");
1194 goto end;
1195 }
1196 split_col = rtgeom_as_rtcollection (ctx, split);
1197 if (!split_col)
1198 {
1199 lwn_SetErrorMsg (net->be_iface,
1200 "lwgeom_as_lwcollection returned NULL");
1201 goto end;
1202 }
1203 if (split_col->ngeoms != 2)
1204 {
1205 lwn_SetErrorMsg (net->be_iface,
1206 "SQL/MM Spatial exception - point not on link.");
1207 goto end;
1208 }
1209
1210 /* retrieving the first half of the split link */
1211 rtg = split_col->geoms[0];
1212 if (rtg->type == RTLINETYPE)
1213 {
1214 rtl = (RTLINE *) rtg;
1215 pa = rtl->points;
1216 newline1->points = pa->npoints;
1217 newline1->x = malloc (sizeof (double) * newline1->points);
1218 newline1->y = malloc (sizeof (double) * newline1->points);
1219 if (newline1->has_z)
1220 newline1->z = malloc (sizeof (double) * newline1->points);
1221 for (iv = 0; iv < newline1->points; iv++)
1222 {
1223 /* copying LINESTRING vertices */
1224 rt_getPoint4d_p (ctx, pa, iv, &pt4d);
1225 newline1->x[iv] = pt4d.x;
1226 newline1->y[iv] = pt4d.y;
1227 if (newline1->has_z)
1228 newline1->z[iv] = pt4d.z;
1229 }
1230 }
1231 else
1232 goto end;
1233
1234 /* retrieving the second half of the split link */
1235 rtg = split_col->geoms[1];
1236 if (rtg->type == RTLINETYPE)
1237 {
1238 rtl = (RTLINE *) rtg;
1239 pa = rtl->points;
1240 newline2->points = pa->npoints;
1241 newline2->x = malloc (sizeof (double) * newline2->points);
1242 newline2->y = malloc (sizeof (double) * newline2->points);
1243 if (newline2->has_z)
1244 newline2->z = malloc (sizeof (double) * newline2->points);
1245 for (iv = 0; iv < newline2->points; iv++)
1246 {
1247 /* copying LINESTRING vertices */
1248 rt_getPoint4d_p (ctx, pa, iv, &pt4d);
1249 newline2->x[iv] = pt4d.x;
1250 newline2->y[iv] = pt4d.y;
1251 if (newline2->has_z)
1252 newline2->z[iv] = pt4d.z;
1253 }
1254 }
1255 else
1256 goto end;
1257
1258 ret = 1;
1259
1260 end:
1261 if (split != NULL)
1262 rtgeom_free (ctx, split);
1263 return ret;
1264 }
1265
1266 static void
cleanup_line(LWN_LINE * line)1267 cleanup_line (LWN_LINE * line)
1268 {
1269 if (line->x != NULL)
1270 free (line->x);
1271 if (line->y != NULL)
1272 free (line->y);
1273 if (line->z != NULL)
1274 free (line->z);
1275 }
1276
1277 LWN_INT64
lwn_NewGeoLinkSplit(LWN_NETWORK * net,LWN_ELEMID link,const LWN_POINT * pt)1278 lwn_NewGeoLinkSplit (LWN_NETWORK * net, LWN_ELEMID link, const LWN_POINT * pt)
1279 {
1280 int i;
1281 LWN_LINK *oldlink;
1282 LWN_LINK newlink[2];
1283 LWN_ELEMID startNode;
1284 LWN_ELEMID endNode;
1285 LWN_NET_NODE newnode;
1286 LWN_LINE newline1;
1287 LWN_LINE newline2;
1288
1289 i = 1;
1290 oldlink = lwn_be_getLinkById (net, &link, &i, LWN_COL_LINK_ALL);
1291 if (!oldlink)
1292 {
1293 if (i == -1)
1294 return -1;
1295 else if (i == 0)
1296 {
1297 lwn_SetErrorMsg (net->be_iface,
1298 "SQL/MM Spatial exception - non-existent link.");
1299 return -1;
1300 }
1301 }
1302 startNode = oldlink->start_node;
1303 endNode = oldlink->end_node;
1304
1305 newline1.srid = oldlink->geom->srid;
1306 newline1.has_z = oldlink->geom->has_z;
1307 newline1.points = 0;
1308 newline1.x = NULL;
1309 newline1.y = NULL;
1310 newline1.z = NULL;
1311 newline2.srid = oldlink->geom->srid;
1312 newline2.has_z = oldlink->geom->has_z;
1313 newline2.points = 0;
1314 newline2.x = NULL;
1315 newline2.y = NULL;
1316 newline2.z = NULL;
1317 if (!geo_link_split (net, oldlink->geom, pt, &newline1, &newline2))
1318 {
1319 _lwn_release_links (oldlink, 1);
1320 cleanup_line (&newline1);
1321 cleanup_line (&newline2);
1322 return -1;
1323 }
1324 _lwn_release_links (oldlink, 1);
1325
1326 if (net->spatial && net->allowCoincident == 0)
1327 {
1328 /* check if a coincident node already exists */
1329 if (lwn_be_existsCoincidentNode (net, pt))
1330 {
1331 lwn_SetErrorMsg (net->be_iface,
1332 "SQL/MM Spatial exception - coincident node");
1333 cleanup_line (&newline1);
1334 cleanup_line (&newline2);
1335 return -1;
1336 }
1337 }
1338
1339 /* inserting a new NetNode */
1340 newnode.node_id = -1;
1341 newnode.geom = (LWN_POINT *) pt;
1342 if (!lwn_be_insertNetNodes (net, &newnode, 1))
1343 {
1344 cleanup_line (&newline1);
1345 cleanup_line (&newline2);
1346 return -1;
1347 }
1348
1349 /* deleting the original Link */
1350 i = lwn_be_deleteLinksById (net, &link, 1);
1351 if (i != 1)
1352 {
1353 cleanup_line (&newline1);
1354 cleanup_line (&newline2);
1355 return -1;
1356 }
1357
1358 /* inserting two new Links */
1359 newlink[0].link_id = lwn_be_getNextLinkId (net);
1360 if (newlink[0].link_id == -1)
1361 {
1362 cleanup_line (&newline1);
1363 cleanup_line (&newline2);
1364 return -1;
1365 }
1366 newlink[1].link_id = lwn_be_getNextLinkId (net);
1367 if (newlink[1].link_id == -1)
1368 {
1369 cleanup_line (&newline1);
1370 cleanup_line (&newline2);
1371 return -1;
1372 }
1373
1374 newlink[0].start_node = startNode;
1375 newlink[0].end_node = newnode.node_id;
1376 newlink[0].geom = &newline1;
1377 newlink[1].start_node = newnode.node_id;
1378 newlink[1].end_node = endNode;
1379 newlink[1].geom = &newline2;
1380
1381 if (!lwn_be_insertLinks (net, newlink, 2))
1382 {
1383 cleanup_line (&newline1);
1384 cleanup_line (&newline2);
1385 return -1;
1386 }
1387
1388 cleanup_line (&newline1);
1389 cleanup_line (&newline2);
1390 return newnode.node_id;
1391 }
1392
1393 LWN_INT64
lwn_ModGeoLinkSplit(LWN_NETWORK * net,LWN_ELEMID link,const LWN_POINT * pt)1394 lwn_ModGeoLinkSplit (LWN_NETWORK * net, LWN_ELEMID link, const LWN_POINT * pt)
1395 {
1396 int i;
1397 LWN_LINK *oldlink;
1398 LWN_LINK newlink;
1399 LWN_ELEMID startNode;
1400 LWN_ELEMID endNode;
1401 LWN_NET_NODE newnode;
1402 LWN_LINE newline1;
1403 LWN_LINE newline2;
1404
1405 i = 1;
1406 oldlink = lwn_be_getLinkById (net, &link, &i, LWN_COL_LINK_ALL);
1407 if (!oldlink)
1408 {
1409 if (i == -1)
1410 return -1;
1411 else if (i == 0)
1412 {
1413 lwn_SetErrorMsg (net->be_iface,
1414 "SQL/MM Spatial exception - non-existent link.");
1415 return -1;
1416 }
1417 }
1418 startNode = oldlink->start_node;
1419 endNode = oldlink->end_node;
1420
1421 newline1.srid = oldlink->geom->srid;
1422 newline1.has_z = oldlink->geom->has_z;
1423 newline1.points = 0;
1424 newline1.x = NULL;
1425 newline1.y = NULL;
1426 newline1.z = NULL;
1427 newline2.srid = oldlink->geom->srid;
1428 newline2.has_z = oldlink->geom->has_z;
1429 newline2.points = 0;
1430 newline2.x = NULL;
1431 newline2.y = NULL;
1432 newline2.z = NULL;
1433 if (!geo_link_split (net, oldlink->geom, pt, &newline1, &newline2))
1434 {
1435 _lwn_release_links (oldlink, 1);
1436 cleanup_line (&newline1);
1437 cleanup_line (&newline2);
1438 return -1;
1439 }
1440 _lwn_release_links (oldlink, 1);
1441
1442 if (net->spatial && net->allowCoincident == 0)
1443 {
1444 /* check if a coincident node already exists */
1445 if (lwn_be_existsCoincidentNode (net, pt))
1446 {
1447 lwn_SetErrorMsg (net->be_iface,
1448 "SQL/MM Spatial exception - coincident node");
1449 cleanup_line (&newline1);
1450 cleanup_line (&newline2);
1451 return -1;
1452 }
1453 }
1454
1455 /* inserting a new NetNode */
1456 newnode.node_id = -1;
1457 newnode.geom = (LWN_POINT *) pt;
1458 if (!lwn_be_insertNetNodes (net, &newnode, 1))
1459 {
1460 cleanup_line (&newline1);
1461 cleanup_line (&newline2);
1462 return -1;
1463 }
1464
1465 /* update the original Link */
1466 newlink.link_id = link;
1467 newlink.start_node = startNode;
1468 newlink.end_node = newnode.node_id;
1469 newlink.geom = &newline1;
1470 if (!lwn_be_updateLinksById
1471 (net, &newlink, 1, LWN_COL_LINK_END_NODE | LWN_COL_LINK_GEOM))
1472 return -1;
1473
1474 /* inserting a new Link */
1475 newlink.link_id = lwn_be_getNextLinkId (net);
1476 if (newlink.link_id == -1)
1477 {
1478 cleanup_line (&newline1);
1479 cleanup_line (&newline2);
1480 return -1;
1481 }
1482
1483 newlink.start_node = newnode.node_id;
1484 newlink.end_node = endNode;
1485 newlink.geom = &newline2;
1486
1487 if (!lwn_be_insertLinks (net, &newlink, 1))
1488 {
1489 cleanup_line (&newline1);
1490 cleanup_line (&newline2);
1491 return -1;
1492 }
1493
1494 cleanup_line (&newline1);
1495 cleanup_line (&newline2);
1496 return newnode.node_id;
1497 }
1498
1499 static int
_lwn_LinkHeal(LWN_NETWORK * net,LWN_ELEMID eid1,LWN_ELEMID eid2,LWN_ELEMID * node_id,LWN_ELEMID * start_node,LWN_ELEMID * end_node,LWN_LINE * newline)1500 _lwn_LinkHeal (LWN_NETWORK * net, LWN_ELEMID eid1, LWN_ELEMID eid2,
1501 LWN_ELEMID * node_id, LWN_ELEMID * start_node,
1502 LWN_ELEMID * end_node, LWN_LINE * newline)
1503 {
1504 int caseno = 0;
1505 LWN_ELEMID ids[2];
1506 LWN_ELEMID commonnode = -1;
1507 LWN_LINK *node_links;
1508 int num_node_links;
1509 LWN_LINK *links;
1510 LWN_LINK *e1 = NULL;
1511 LWN_LINK *e2 = NULL;
1512 int nlinks, i;
1513 int otherlinks = 0;
1514
1515 /* NOT IN THE SPECS: see if the same link is given twice.. */
1516 if (eid1 == eid2)
1517 {
1518 lwn_SetErrorMsg (net->be_iface,
1519 "SQL/MM Spatial exception - Cannot heal link with itself.");
1520 return 0;
1521 }
1522 ids[0] = eid1;
1523 ids[1] = eid2;
1524 nlinks = 2;
1525 links = lwn_be_getLinkById (net, ids, &nlinks, LWN_COL_LINK_ALL);
1526 if (nlinks == -1)
1527 return -0;
1528 for (i = 0; i < nlinks; ++i)
1529 {
1530 if (links[i].link_id == eid1)
1531 e1 = &(links[i]);
1532 else if (links[i].link_id == eid2)
1533 e2 = &(links[i]);
1534 }
1535 if (!e1)
1536 {
1537 _lwn_release_links (links, nlinks);
1538 lwn_SetErrorMsg (net->be_iface,
1539 "SQL/MM Spatial exception - non-existent first link.");
1540 return 0;
1541 }
1542 if (!e2)
1543 {
1544 _lwn_release_links (links, nlinks);
1545 lwn_SetErrorMsg (net->be_iface,
1546 "SQL/MM Spatial exception - non-existent second link.");
1547 return 0;
1548 }
1549
1550 /* Find common node */
1551
1552 if (e1->end_node == e2->start_node)
1553 {
1554 commonnode = e1->end_node;
1555 *start_node = e1->start_node;
1556 *end_node = e2->end_node;
1557 caseno = 1;
1558 }
1559 else if (e1->end_node == e2->end_node)
1560 {
1561 commonnode = e1->end_node;
1562 *start_node = e1->start_node;
1563 *end_node = e2->start_node;
1564 caseno = 2;
1565 }
1566 else if (e1->start_node == e2->start_node)
1567 {
1568 commonnode = e1->start_node;
1569 *start_node = e2->end_node;
1570 *end_node = e1->end_node;
1571 caseno = 3;
1572 }
1573 else if (e1->start_node == e2->end_node)
1574 {
1575 commonnode = e1->start_node;
1576 *start_node = e2->start_node;
1577 *end_node = e1->start_node;
1578 caseno = 4;
1579 }
1580
1581 if (e1->geom != NULL && e2->geom != NULL)
1582 {
1583 /* reassembling the healed Link geometry */
1584 int iv;
1585 int iv2 = 0;
1586 newline->srid = e1->geom->srid;
1587 newline->has_z = e1->geom->has_z;
1588 newline->points = e1->geom->points + e2->geom->points - 1;
1589 newline->x = malloc (sizeof (double) * newline->points);
1590 newline->y = malloc (sizeof (double) * newline->points);
1591 if (newline->has_z)
1592 newline->z = malloc (sizeof (double) * newline->points);
1593 switch (caseno)
1594 {
1595 case 1:
1596 for (iv = 0; iv < e1->geom->points; iv++)
1597 {
1598 newline->x[iv2] = e1->geom->x[iv];
1599 newline->y[iv2] = e1->geom->y[iv];
1600 if (newline->has_z)
1601 newline->z[iv2] = e1->geom->z[iv];
1602 iv2++;
1603 }
1604 for (iv = 1; iv < e2->geom->points; iv++)
1605 {
1606 newline->x[iv2] = e2->geom->x[iv];
1607 newline->y[iv2] = e2->geom->y[iv];
1608 if (newline->has_z)
1609 newline->z[iv2] = e2->geom->z[iv];
1610 iv2++;
1611 }
1612 break;
1613 case 2:
1614 for (iv = 0; iv < e1->geom->points - 1; iv++)
1615 {
1616 newline->x[iv2] = e1->geom->x[iv];
1617 newline->y[iv2] = e1->geom->y[iv];
1618 if (newline->has_z)
1619 newline->z[iv2] = e1->geom->z[iv];
1620 iv2++;
1621 }
1622 for (iv = e2->geom->points - 1; iv >= 0; iv--)
1623 {
1624 newline->x[iv2] = e2->geom->x[iv];
1625 newline->y[iv2] = e2->geom->y[iv];
1626 if (newline->has_z)
1627 newline->z[iv2] = e2->geom->z[iv];
1628 iv2++;
1629 }
1630 break;
1631 case 3:
1632 for (iv = e2->geom->points - 1; iv >= 0; iv--)
1633 {
1634 newline->x[iv2] = e2->geom->x[iv];
1635 newline->y[iv2] = e2->geom->y[iv];
1636 if (newline->has_z)
1637 newline->z[iv2] = e2->geom->z[iv];
1638 iv2++;
1639 }
1640 for (iv = 1; iv < e1->geom->points; iv++)
1641 {
1642 newline->x[iv2] = e1->geom->x[iv];
1643 newline->y[iv2] = e1->geom->y[iv];
1644 if (newline->has_z)
1645 newline->z[iv2] = e1->geom->z[iv];
1646 iv2++;
1647 }
1648 break;
1649 case 4:
1650 for (iv = 0; iv < e2->geom->points; iv++)
1651 {
1652 newline->x[iv2] = e2->geom->x[iv];
1653 newline->y[iv2] = e2->geom->y[iv];
1654 if (newline->has_z)
1655 newline->z[iv2] = e2->geom->z[iv];
1656 iv2++;
1657 }
1658 for (iv = 1; iv < e1->geom->points; iv++)
1659 {
1660 newline->x[iv2] = e1->geom->x[iv];
1661 newline->y[iv2] = e1->geom->y[iv];
1662 if (newline->has_z)
1663 newline->z[iv2] = e1->geom->z[iv];
1664 iv2++;
1665 }
1666 break;
1667 };
1668 }
1669
1670
1671 /* Check if any other link is connected to the common node, if found */
1672 if (commonnode != -1)
1673 {
1674 num_node_links = 1;
1675 node_links = lwn_be_getLinkByNetNode (net, &commonnode,
1676 &num_node_links,
1677 LWN_COL_LINK_LINK_ID);
1678 if (num_node_links == -1)
1679 {
1680 _lwn_release_links (links, nlinks);
1681 return 0;
1682 }
1683 for (i = 0; i < num_node_links; ++i)
1684 {
1685 if (node_links[i].link_id == eid1)
1686 continue;
1687 if (node_links[i].link_id == eid2)
1688 continue;
1689 commonnode = -1;
1690 otherlinks++;
1691 }
1692 free (node_links);
1693 }
1694
1695 if (commonnode == -1)
1696 {
1697 _lwn_release_links (links, nlinks);
1698 if (otherlinks)
1699 {
1700 lwn_SetErrorMsg (net->be_iface,
1701 "SQL/MM Spatial exception - other links connected.");
1702 }
1703 else
1704 {
1705 lwn_SetErrorMsg (net->be_iface,
1706 "SQL/MM Spatial exception - non-connected links.");
1707 }
1708 return 0;
1709 }
1710 _lwn_release_links (links, nlinks);
1711
1712 *node_id = commonnode;
1713 return 1;
1714 }
1715
1716 LWN_INT64
lwn_NewLinkHeal(LWN_NETWORK * net,LWN_ELEMID link,LWN_ELEMID anotherlink)1717 lwn_NewLinkHeal (LWN_NETWORK * net, LWN_ELEMID link, LWN_ELEMID anotherlink)
1718 {
1719 LWN_ELEMID node_id;
1720 LWN_ELEMID start_node;
1721 LWN_ELEMID end_node;
1722 LWN_ELEMID linkids[2];
1723 LWN_LINK newlink;
1724 LWN_LINE newline;
1725 int n;
1726
1727 newline.points = 0;
1728 newline.x = NULL;
1729 newline.y = NULL;
1730 newline.z = NULL;
1731 if (!_lwn_LinkHeal
1732 (net, link, anotherlink, &node_id, &start_node, &end_node, &newline))
1733 {
1734 cleanup_line (&newline);
1735 return -1;
1736 }
1737
1738 /* removing both Links */
1739 linkids[0] = link;
1740 linkids[1] = anotherlink;
1741 n = lwn_be_deleteLinksById (net, linkids, 2);
1742 if (n != 2)
1743 {
1744 cleanup_line (&newline);
1745 return -1;
1746 }
1747
1748 /* removing the common NetNode */
1749 n = lwn_be_deleteNetNodesById (net, &node_id, 1);
1750 if (n == -1)
1751 {
1752 cleanup_line (&newline);
1753 return -1;
1754 }
1755
1756 /* inserting a new healed Link */
1757 newlink.link_id = -1;
1758 newlink.start_node = start_node;
1759 newlink.end_node = end_node;
1760 if (newline.points == 0)
1761 newlink.geom = NULL;
1762 else
1763 newlink.geom = &newline;
1764
1765 if (!lwn_be_insertLinks (net, &newlink, 1))
1766 {
1767 cleanup_line (&newline);
1768 return -1;
1769 }
1770
1771 cleanup_line (&newline);
1772 return node_id;
1773 }
1774
1775 LWN_INT64
lwn_ModLinkHeal(LWN_NETWORK * net,LWN_ELEMID link,LWN_ELEMID anotherlink)1776 lwn_ModLinkHeal (LWN_NETWORK * net, LWN_ELEMID link, LWN_ELEMID anotherlink)
1777 {
1778 LWN_ELEMID node_id;
1779 LWN_ELEMID start_node;
1780 LWN_ELEMID end_node;
1781 LWN_LINK newlink;
1782 LWN_LINE newline;
1783 int n;
1784
1785 newline.points = 0;
1786 newline.x = NULL;
1787 newline.y = NULL;
1788 newline.z = NULL;
1789 if (!_lwn_LinkHeal
1790 (net, link, anotherlink, &node_id, &start_node, &end_node, &newline))
1791 {
1792 cleanup_line (&newline);
1793 return -1;
1794 }
1795
1796 /* removing the second Link */
1797 n = lwn_be_deleteLinksById (net, &anotherlink, 1);
1798 if (n != 1)
1799 {
1800 cleanup_line (&newline);
1801 return -1;
1802 }
1803
1804 /* updating the healed link */
1805 newlink.link_id = link;
1806 newlink.start_node = start_node;
1807 newlink.end_node = end_node;
1808 if (newline.points == 0)
1809 newlink.geom = NULL;
1810 else
1811 newlink.geom = &newline;
1812 if (!lwn_be_updateLinksById
1813 (net, &newlink, 1,
1814 LWN_COL_LINK_START_NODE | LWN_COL_LINK_END_NODE | LWN_COL_LINK_GEOM))
1815 {
1816 cleanup_line (&newline);
1817 return -1;
1818 }
1819
1820 /* removing the common NetNode */
1821 n = lwn_be_deleteNetNodesById (net, &node_id, 1);
1822 if (n == -1)
1823 {
1824 cleanup_line (&newline);
1825 return -1;
1826 }
1827
1828 cleanup_line (&newline);
1829 return node_id;
1830 }
1831
1832 LWN_ELEMID
lwn_GetNetNodeByPoint(LWN_NETWORK * net,const LWN_POINT * pt,double tol)1833 lwn_GetNetNodeByPoint (LWN_NETWORK * net, const LWN_POINT * pt, double tol)
1834 {
1835 LWN_NET_NODE *elem;
1836 int num;
1837 int flds = LWN_COL_NODE_NODE_ID;
1838 LWN_ELEMID id = 0;
1839
1840 elem = lwn_be_getNetNodeWithinDistance2D (net, pt, tol, &num, flds, 0);
1841 if (num <= 0)
1842 return -1;
1843 else if (num > 1)
1844 {
1845 _lwn_release_nodes (elem, num);
1846 lwn_SetErrorMsg (net->be_iface, "Two or more net-nodes found");
1847 return -1;
1848 }
1849 id = elem[0].node_id;
1850 _lwn_release_nodes (elem, num);
1851
1852 return id;
1853 }
1854
1855 LWN_ELEMID
lwn_GetLinkByPoint(LWN_NETWORK * net,const LWN_POINT * pt,double tol)1856 lwn_GetLinkByPoint (LWN_NETWORK * net, const LWN_POINT * pt, double tol)
1857 {
1858 LWN_LINK *elem;
1859 int num, i;
1860 int flds = LWN_COL_LINK_LINK_ID;
1861 LWN_ELEMID id = 0;
1862
1863 elem = lwn_be_getLinkWithinDistance2D (net, pt, tol, &num, flds, 0);
1864 if (num <= 0)
1865 return -1;
1866 for (i = 0; i < num; ++i)
1867 {
1868 LWN_LINK *e = &(elem[i]);
1869
1870 if (id)
1871 {
1872 _lwn_release_links (elem, num);
1873 lwn_SetErrorMsg (net->be_iface, "Two or more links found");
1874 return -1;
1875 }
1876 else
1877 id = e->link_id;
1878 }
1879 _lwn_release_links (elem, num);
1880
1881 return id;
1882 }
1883
1884 /* wrappers of backend wrappers... */
1885
1886 int
lwn_be_existsCoincidentNode(const LWN_NETWORK * net,const LWN_POINT * pt)1887 lwn_be_existsCoincidentNode (const LWN_NETWORK * net, const LWN_POINT * pt)
1888 {
1889 int exists = 0;
1890 lwn_be_getNetNodeWithinDistance2D (net, pt, 0, &exists, 0, -1);
1891 if (exists == -1)
1892 return 0;
1893 return exists;
1894 }
1895
1896 int
lwn_be_existsLinkIntersectingPoint(const LWN_NETWORK * net,const LWN_POINT * pt)1897 lwn_be_existsLinkIntersectingPoint (const LWN_NETWORK * net,
1898 const LWN_POINT * pt)
1899 {
1900 int exists = 0;
1901 lwn_be_getLinkWithinDistance2D (net, pt, 0, &exists, 0, -1);
1902 if (exists == -1)
1903 return 0;
1904 return exists;
1905 }
1906
1907 #endif /* end RTTOPO conditionals */
1908