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