1 /*
2 
3  net_callbacks.c -- implementation of Topology-Network callback functions
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 
50 #if defined(_WIN32) && !defined(__MINGW32__)
51 #include "config-msvc.h"
52 #else
53 #include "config.h"
54 #endif
55 
56 #ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
57 
58 #include <spatialite/sqlite.h>
59 #include <spatialite/debug.h>
60 #include <spatialite/gaiageo.h>
61 #include <spatialite/gaia_network.h>
62 #include <spatialite/gaiaaux.h>
63 
64 #include <spatialite.h>
65 #include <spatialite_private.h>
66 
67 #include <librttopo.h>
68 #include <lwn_network.h>
69 
70 #include "network_private.h"
71 
72 struct net_node
73 {
74 /* a struct wrapping a Network Node */
75     sqlite3_int64 node_id;
76     double x;
77     double y;
78     double z;
79     int has_z;
80     int is_null;
81     struct net_node *next;
82 };
83 
84 struct net_nodes_list
85 {
86 /* a struct wrapping a list of Network Nodes */
87     struct net_node *first;
88     struct net_node *last;
89     int count;
90 };
91 
92 struct net_link
93 {
94 /* a struct wrapping a Network Link */
95     sqlite3_int64 link_id;
96     sqlite3_int64 start_node;
97     sqlite3_int64 end_node;
98     gaiaLinestringPtr geom;
99     struct net_link *next;
100 };
101 
102 struct net_links_list
103 {
104 /* a struct wrapping a list of Network Links */
105     struct net_link *first;
106     struct net_link *last;
107     int count;
108 };
109 
110 static struct net_node *
create_net_node(sqlite3_int64 node_id,double x,double y,double z,int has_z)111 create_net_node (sqlite3_int64 node_id, double x, double y, double z, int has_z)
112 {
113 /* creating a Network Node */
114     struct net_node *ptr = malloc (sizeof (struct net_node));
115     ptr->node_id = node_id;
116     ptr->x = x;
117     ptr->y = y;
118     ptr->z = z;
119     ptr->has_z = has_z;
120     ptr->is_null = 0;
121     ptr->next = NULL;
122     return ptr;
123 }
124 
125 static struct net_node *
create_net_node_null(sqlite3_int64 node_id)126 create_net_node_null (sqlite3_int64 node_id)
127 {
128 /* creating a Network Node - NULL */
129     struct net_node *ptr = malloc (sizeof (struct net_node));
130     ptr->node_id = node_id;
131     ptr->is_null = 1;
132     ptr->next = NULL;
133     return ptr;
134 }
135 
136 static void
destroy_net_node(struct net_node * ptr)137 destroy_net_node (struct net_node *ptr)
138 {
139 /* destroying a Network Node */
140     if (ptr == NULL)
141 	return;
142     free (ptr);
143 }
144 
145 static struct net_link *
create_net_link(sqlite3_int64 link_id,sqlite3_int64 start_node,sqlite3_int64 end_node,gaiaLinestringPtr ln)146 create_net_link (sqlite3_int64 link_id, sqlite3_int64 start_node,
147 		 sqlite3_int64 end_node, gaiaLinestringPtr ln)
148 {
149 /* creating a Network Link */
150     struct net_link *ptr = malloc (sizeof (struct net_link));
151     ptr->link_id = link_id;
152     ptr->start_node = start_node;
153     ptr->end_node = end_node;
154     ptr->geom = ln;
155     ptr->next = NULL;
156     return ptr;
157 }
158 
159 static void
destroy_net_link(struct net_link * ptr)160 destroy_net_link (struct net_link *ptr)
161 {
162 /* destroying a Network Link */
163     if (ptr == NULL)
164 	return;
165     if (ptr->geom != NULL)
166 	gaiaFreeLinestring (ptr->geom);
167     free (ptr);
168 }
169 
170 static struct net_nodes_list *
create_nodes_list(void)171 create_nodes_list (void)
172 {
173 /* creating an empty list of Network Nodes */
174     struct net_nodes_list *ptr = malloc (sizeof (struct net_nodes_list));
175     ptr->first = NULL;
176     ptr->last = NULL;
177     ptr->count = 0;
178     return ptr;
179 }
180 
181 static void
destroy_net_nodes_list(struct net_nodes_list * ptr)182 destroy_net_nodes_list (struct net_nodes_list *ptr)
183 {
184 /* destroying a list of Network Nodes */
185     struct net_node *p;
186     struct net_node *pn;
187     if (ptr == NULL)
188 	return;
189 
190     p = ptr->first;
191     while (p != NULL)
192       {
193 	  pn = p->next;
194 	  destroy_net_node (p);
195 	  p = pn;
196       }
197     free (ptr);
198 }
199 
200 static void
add_node_2D(struct net_nodes_list * list,sqlite3_int64 node_id,double x,double y)201 add_node_2D (struct net_nodes_list *list, sqlite3_int64 node_id,
202 	     double x, double y)
203 {
204 /* inserting a Network Node 2D into the list */
205     struct net_node *ptr;
206     if (list == NULL)
207 	return;
208 
209     ptr = create_net_node (node_id, x, y, 0.0, 0);
210     if (list->first == NULL)
211 	list->first = ptr;
212     if (list->last != NULL)
213 	list->last->next = ptr;
214     list->last = ptr;
215     list->count++;
216 }
217 
218 static void
add_node_3D(struct net_nodes_list * list,sqlite3_int64 node_id,double x,double y,double z)219 add_node_3D (struct net_nodes_list *list, sqlite3_int64 node_id,
220 	     double x, double y, double z)
221 {
222 /* inserting a Network Node 3D into the list */
223     struct net_node *ptr;
224     if (list == NULL)
225 	return;
226 
227     ptr = create_net_node (node_id, x, y, z, 1);
228     if (list->first == NULL)
229 	list->first = ptr;
230     if (list->last != NULL)
231 	list->last->next = ptr;
232     list->last = ptr;
233     list->count++;
234 }
235 
236 static void
add_node_null(struct net_nodes_list * list,sqlite3_int64 node_id)237 add_node_null (struct net_nodes_list *list, sqlite3_int64 node_id)
238 {
239 /* inserting a Network Node (NULL) into the list */
240     struct net_node *ptr;
241     if (list == NULL)
242 	return;
243 
244     ptr = create_net_node_null (node_id);
245     if (list->first == NULL)
246 	list->first = ptr;
247     if (list->last != NULL)
248 	list->last->next = ptr;
249     list->last = ptr;
250     list->count++;
251 }
252 
253 static struct net_links_list *
create_links_list(void)254 create_links_list (void)
255 {
256 /* creating an empty list of Network Links */
257     struct net_links_list *ptr = malloc (sizeof (struct net_links_list));
258     ptr->first = NULL;
259     ptr->last = NULL;
260     ptr->count = 0;
261     return ptr;
262 }
263 
264 static void
destroy_links_list(struct net_links_list * ptr)265 destroy_links_list (struct net_links_list *ptr)
266 {
267 /* destroying a list of Network Links */
268     struct net_link *p;
269     struct net_link *pn;
270     if (ptr == NULL)
271 	return;
272 
273     p = ptr->first;
274     while (p != NULL)
275       {
276 	  pn = p->next;
277 	  destroy_net_link (p);
278 	  p = pn;
279       }
280     free (ptr);
281 }
282 
283 static void
add_link(struct net_links_list * list,sqlite3_int64 link_id,sqlite3_int64 start_node,sqlite3_int64 end_node,gaiaLinestringPtr ln)284 add_link (struct net_links_list *list, sqlite3_int64 link_id,
285 	  sqlite3_int64 start_node, sqlite3_int64 end_node,
286 	  gaiaLinestringPtr ln)
287 {
288 /* inserting a Network Link into the list */
289     struct net_link *ptr;
290     if (list == NULL)
291 	return;
292 
293     ptr = create_net_link (link_id, start_node, end_node, ln);
294     if (list->first == NULL)
295 	list->first = ptr;
296     if (list->last != NULL)
297 	list->last->next = ptr;
298     list->last = ptr;
299     list->count++;
300 }
301 
302 static char *
do_prepare_read_net_node(const char * network_name,int fields,int spatial,int has_z)303 do_prepare_read_net_node (const char *network_name, int fields, int spatial,
304 			  int has_z)
305 {
306 /* preparing the auxiliary "read_node" SQL statement */
307     char *sql;
308     char *prev;
309     char *table;
310     char *xtable;
311     int comma = 0;
312 
313     sql = sqlite3_mprintf ("SELECT ");
314     prev = sql;
315     if (fields & LWN_COL_NODE_NODE_ID)
316       {
317 	  if (comma)
318 	      sql = sqlite3_mprintf ("%s, node_id", prev);
319 	  else
320 	      sql = sqlite3_mprintf ("%s node_id", prev);
321 	  comma = 1;
322 	  sqlite3_free (prev);
323 	  prev = sql;
324       }
325     if (fields & LWN_COL_NODE_GEOM && spatial)
326       {
327 	  if (comma)
328 	      sql =
329 		  sqlite3_mprintf ("%s, ST_X(geometry), ST_Y(geometry)", prev);
330 	  else
331 	      sql = sqlite3_mprintf ("%s ST_X(geometry), ST_Y(geometry)", prev);
332 	  comma = 1;
333 	  sqlite3_free (prev);
334 	  prev = sql;
335 	  if (has_z)
336 	    {
337 		sql = sqlite3_mprintf ("%s, ST_Z(geometry)", prev);
338 		sqlite3_free (prev);
339 		prev = sql;
340 	    }
341       }
342     table = sqlite3_mprintf ("%s_node", network_name);
343     xtable = gaiaDoubleQuotedSql (table);
344     sqlite3_free (table);
345     sql =
346 	sqlite3_mprintf ("%s FROM MAIN.\"%s\" WHERE node_id = ?", prev, xtable);
347     sqlite3_free (prev);
348     free (xtable);
349     return sql;
350 }
351 
352 static int
do_read_net_node(sqlite3_stmt * stmt,struct net_nodes_list * list,sqlite3_int64 id,int fields,int spatial,int has_z,const char * callback_name,char ** errmsg)353 do_read_net_node (sqlite3_stmt * stmt, struct net_nodes_list *list,
354 		  sqlite3_int64 id, int fields, int spatial, int has_z,
355 		  const char *callback_name, char **errmsg)
356 {
357 /* reading Nodes out from the DBMS */
358     int icol = 0;
359     int ret;
360 
361 /* setting up the prepared statement */
362     sqlite3_reset (stmt);
363     sqlite3_clear_bindings (stmt);
364     sqlite3_bind_int64 (stmt, 1, id);
365 
366     while (1)
367       {
368 	  /* scrolling the result set rows */
369 	  ret = sqlite3_step (stmt);
370 	  if (ret == SQLITE_DONE)
371 	      break;		/* end of result set */
372 	  if (ret == SQLITE_ROW)
373 	    {
374 		int ok_id = 0;
375 		int ok_x = 0;
376 		int ok_y = 0;
377 		int ok_z = 0;
378 		sqlite3_int64 node_id = -1;
379 		double x = 0.0;
380 		double y = 0.0;
381 		double z = 0.0;
382 		if (fields & LWN_COL_NODE_NODE_ID)
383 		  {
384 		      if (sqlite3_column_type (stmt, icol) == SQLITE_INTEGER)
385 			{
386 			    node_id = sqlite3_column_int64 (stmt, icol);
387 			    ok_id = 1;
388 			}
389 		      icol++;
390 		  }
391 		else
392 		    ok_id = 1;
393 		if (fields & LWN_COL_NODE_GEOM && spatial)
394 		  {
395 		      if (sqlite3_column_type (stmt, icol) == SQLITE_FLOAT)
396 			{
397 			    x = sqlite3_column_double (stmt, icol);
398 			    ok_x = 1;
399 			}
400 		      icol++;
401 		      if (sqlite3_column_type (stmt, icol) == SQLITE_FLOAT)
402 			{
403 			    y = sqlite3_column_double (stmt, icol);
404 			    ok_y = 1;
405 			}
406 		      icol++;
407 		      if (has_z)
408 			{
409 			    if (sqlite3_column_type (stmt, icol) ==
410 				SQLITE_FLOAT)
411 			      {
412 				  z = sqlite3_column_double (stmt, icol);
413 				  ok_z = 1;
414 			      }
415 			}
416 		  }
417 		else
418 		  {
419 		      ok_x = 1;
420 		      ok_y = 1;
421 		      ok_z = 1;
422 		  }
423 		if (!spatial)
424 		  {
425 		      add_node_null (list, node_id);
426 		      *errmsg = NULL;
427 		      sqlite3_reset (stmt);
428 		      return 1;
429 		  }
430 		else
431 		  {
432 		      if (has_z)
433 			{
434 			    if (ok_id && ok_x && ok_y && ok_z)
435 			      {
436 				  add_node_3D (list, node_id, x, y, z);
437 				  *errmsg = NULL;
438 				  sqlite3_reset (stmt);
439 				  return 1;
440 			      }
441 			}
442 		      else
443 			{
444 			    if (ok_id && ok_x && ok_y)
445 			      {
446 				  add_node_2D (list, node_id, x, y);
447 				  *errmsg = NULL;
448 				  sqlite3_reset (stmt);
449 				  return 1;
450 			      }
451 			}
452 		  }
453 		/* an invalid Node has been found */
454 		*errmsg =
455 		    sqlite3_mprintf
456 		    ("%s: found an invalid Node \"%lld\"", callback_name,
457 		     node_id);
458 		sqlite3_reset (stmt);
459 		return 0;
460 	    }
461       }
462     *errmsg = NULL;
463     sqlite3_reset (stmt);
464     return 1;
465 }
466 
467 static char *
do_prepare_read_link(const char * network_name,int fields)468 do_prepare_read_link (const char *network_name, int fields)
469 {
470 /* preparing the auxiliary "read_link" SQL statement */
471     char *sql;
472     char *prev;
473     char *table;
474     char *xtable;
475     int comma = 0;
476 
477     sql = sqlite3_mprintf ("SELECT ");
478     prev = sql;
479     if (fields & LWN_COL_LINK_LINK_ID)
480       {
481 	  if (comma)
482 	      sql = sqlite3_mprintf ("%s, link_id", prev);
483 	  else
484 	      sql = sqlite3_mprintf ("%s link_id", prev);
485 	  comma = 1;
486 	  sqlite3_free (prev);
487 	  prev = sql;
488       }
489     if (fields & LWN_COL_LINK_START_NODE)
490       {
491 	  if (comma)
492 	      sql = sqlite3_mprintf ("%s, start_node", prev);
493 	  else
494 	      sql = sqlite3_mprintf ("%s start_node", prev);
495 	  comma = 1;
496 	  sqlite3_free (prev);
497 	  prev = sql;
498       }
499     if (fields & LWN_COL_LINK_END_NODE)
500       {
501 	  if (comma)
502 	      sql = sqlite3_mprintf ("%s, end_node", prev);
503 	  else
504 	      sql = sqlite3_mprintf ("%s end_node", prev);
505 	  comma = 1;
506 	  sqlite3_free (prev);
507 	  prev = sql;
508       }
509     if (fields & LWN_COL_LINK_GEOM)
510       {
511 	  if (comma)
512 	      sql = sqlite3_mprintf ("%s, geometry", prev);
513 	  else
514 	      sql = sqlite3_mprintf ("%s geometry", prev);
515 	  comma = 1;
516 	  sqlite3_free (prev);
517 	  prev = sql;
518       }
519     table = sqlite3_mprintf ("%s_link", network_name);
520     xtable = gaiaDoubleQuotedSql (table);
521     sqlite3_free (table);
522     sql =
523 	sqlite3_mprintf ("%s FROM MAIN.\"%s\" WHERE link_id = ?", prev, xtable);
524     free (xtable);
525     sqlite3_free (prev);
526     return sql;
527 }
528 
529 static int
do_read_link_row(sqlite3_stmt * stmt,struct net_links_list * list,int fields,const char * callback_name,char ** errmsg)530 do_read_link_row (sqlite3_stmt * stmt, struct net_links_list *list, int fields,
531 		  const char *callback_name, char **errmsg)
532 {
533 /* reading a Link Row out from the resultset */
534     int icol = 0;
535 
536     int ok_id = 0;
537     int ok_start = 0;
538     int ok_end = 0;
539     int ok_geom = 0;
540     sqlite3_int64 link_id = -1;
541     sqlite3_int64 start_node = -1;
542     sqlite3_int64 end_node = -1;
543     gaiaGeomCollPtr geom = NULL;
544     gaiaLinestringPtr ln = NULL;
545     if (fields & LWN_COL_LINK_LINK_ID)
546       {
547 	  if (sqlite3_column_type (stmt, icol) == SQLITE_INTEGER)
548 	    {
549 		link_id = sqlite3_column_int64 (stmt, icol);
550 		ok_id = 1;
551 	    }
552 	  icol++;
553       }
554     else
555 	ok_id = 1;
556     if (fields & LWN_COL_LINK_START_NODE)
557       {
558 	  if (sqlite3_column_type (stmt, icol) == SQLITE_INTEGER)
559 	    {
560 		start_node = sqlite3_column_int64 (stmt, icol);
561 		ok_start = 1;
562 	    }
563 	  icol++;
564       }
565     else
566 	ok_start = 1;
567     if (fields & LWN_COL_LINK_END_NODE)
568       {
569 	  if (sqlite3_column_type (stmt, icol) == SQLITE_INTEGER)
570 	    {
571 		end_node = sqlite3_column_int64 (stmt, icol);
572 		ok_end = 1;
573 	    }
574 	  icol++;
575       }
576     else
577 	ok_end = 1;
578     if (fields & LWN_COL_LINK_GEOM)
579       {
580 	  if (sqlite3_column_type (stmt, icol) == SQLITE_NULL)
581 	    {
582 		ln = NULL;
583 		ok_geom = 1;
584 	    }
585 	  if (sqlite3_column_type (stmt, icol) == SQLITE_BLOB)
586 	    {
587 		const unsigned char *blob = sqlite3_column_blob (stmt, icol);
588 		int blob_sz = sqlite3_column_bytes (stmt, icol);
589 		geom = gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
590 		if (geom != NULL)
591 		  {
592 		      if (geom->FirstPoint == NULL
593 			  && geom->FirstPolygon == NULL
594 			  && geom->FirstLinestring ==
595 			  geom->LastLinestring && geom->FirstLinestring != NULL)
596 			{
597 			    ln = geom->FirstLinestring;
598 			    ok_geom = 1;
599 			    /* releasing ownership on Linestring */
600 			    geom->FirstLinestring = NULL;
601 			    geom->LastLinestring = NULL;
602 			}
603 		      gaiaFreeGeomColl (geom);
604 		  }
605 	    }
606 	  icol++;
607       }
608     else
609 	ok_geom = 1;
610     if (ok_id && ok_start && ok_end && ok_geom)
611       {
612 	  add_link (list, link_id, start_node, end_node, ln);
613 	  *errmsg = NULL;
614 	  return 1;
615       }
616 /* an invalid Link has been found */
617     if (geom != NULL)
618 	gaiaFreeGeomColl (geom);
619     *errmsg =
620 	sqlite3_mprintf
621 	("%s: found an invalid Link \"%lld\"", callback_name, link_id);
622     return 0;
623 }
624 
625 static int
do_read_link(sqlite3_stmt * stmt,struct net_links_list * list,sqlite3_int64 link_id,int fields,const char * callback_name,char ** errmsg)626 do_read_link (sqlite3_stmt * stmt, struct net_links_list *list,
627 	      sqlite3_int64 link_id, int fields, const char *callback_name,
628 	      char **errmsg)
629 {
630 /* reading a single Link out from the DBMS */
631     int ret;
632 
633 /* setting up the prepared statement */
634     sqlite3_reset (stmt);
635     sqlite3_clear_bindings (stmt);
636     sqlite3_bind_int64 (stmt, 1, link_id);
637 
638     while (1)
639       {
640 	  /* scrolling the result set rows */
641 	  ret = sqlite3_step (stmt);
642 	  if (ret == SQLITE_DONE)
643 	      break;		/* end of result set */
644 	  if (ret == SQLITE_ROW)
645 	    {
646 		if (!do_read_link_row
647 		    (stmt, list, fields, callback_name, errmsg))
648 		  {
649 		      sqlite3_reset (stmt);
650 		      return 0;
651 		  }
652 	    }
653       }
654     sqlite3_reset (stmt);
655     return 1;
656 }
657 
658 static int
do_read_link_by_net_node(sqlite3_stmt * stmt,struct net_links_list * list,sqlite3_int64 node_id,int fields,const char * callback_name,char ** errmsg)659 do_read_link_by_net_node (sqlite3_stmt * stmt, struct net_links_list *list,
660 			  sqlite3_int64 node_id, int fields,
661 			  const char *callback_name, char **errmsg)
662 {
663 /* reading a single Link out from the DBMS */
664     int ret;
665 
666 /* setting up the prepared statement */
667     sqlite3_reset (stmt);
668     sqlite3_clear_bindings (stmt);
669     sqlite3_bind_int64 (stmt, 1, node_id);
670     sqlite3_bind_int64 (stmt, 2, node_id);
671 
672     while (1)
673       {
674 	  /* scrolling the result set rows */
675 	  ret = sqlite3_step (stmt);
676 	  if (ret == SQLITE_DONE)
677 	      break;		/* end of result set */
678 	  if (ret == SQLITE_ROW)
679 	    {
680 		if (!do_read_link_row
681 		    (stmt, list, fields, callback_name, errmsg))
682 		  {
683 		      sqlite3_reset (stmt);
684 		      return 0;
685 		  }
686 	    }
687       }
688     sqlite3_reset (stmt);
689     return 1;
690 }
691 
692 const char *
netcallback_lastErrorMessage(const LWN_BE_DATA * be)693 netcallback_lastErrorMessage (const LWN_BE_DATA * be)
694 {
695     return gaianet_get_last_exception ((GaiaNetworkAccessorPtr) be);
696 }
697 
698 LWN_BE_NETWORK *
netcallback_loadNetworkByName(const LWN_BE_DATA * be,const char * name)699 netcallback_loadNetworkByName (const LWN_BE_DATA * be, const char *name)
700 {
701 /* callback function: loadNetworkByName */
702     struct gaia_network *ptr = (struct gaia_network *) be;
703     char *network_name;
704     int spatial;
705     int srid;
706     int has_z;
707     int allow_coincident;
708     struct splite_internal_cache *cache =
709 	(struct splite_internal_cache *) ptr->cache;
710 
711     if (gaiaReadNetworkFromDBMS
712 	(ptr->db_handle, name, &network_name, &spatial, &srid, &has_z,
713 	 &allow_coincident))
714       {
715 	  ptr->network_name = network_name;
716 	  ptr->srid = srid;
717 	  ptr->has_z = has_z;
718 	  ptr->spatial = spatial;
719 	  ptr->allow_coincident = allow_coincident;
720 	  /* registering into the Internal Cache double linked list */
721 	  if (cache->firstNetwork == NULL)
722 	      cache->firstNetwork = ptr;
723 	  if (cache->lastNetwork != NULL)
724 	    {
725 		struct gaia_network *p2 =
726 		    (struct gaia_network *) (cache->lastNetwork);
727 		p2->next = ptr;
728 	    }
729 	  cache->lastNetwork = ptr;
730 	  return (LWN_BE_NETWORK *) ptr;
731       }
732     else
733 	return NULL;
734 }
735 
736 int
netcallback_freeNetwork(LWN_BE_NETWORK * lwn_net)737 netcallback_freeNetwork (LWN_BE_NETWORK * lwn_net)
738 {
739 /* callback function: freeNetwork - does nothing */
740     if (lwn_net != NULL)
741 	lwn_net = NULL;		/* silencing stupid compiler warnings on unuse args */
742     return 1;
743 }
744 
745 static gaiaGeomCollPtr
do_convert_lwnline_to_geom(LWN_LINE * line,int srid)746 do_convert_lwnline_to_geom (LWN_LINE * line, int srid)
747 {
748 /* converting an LWN_LINE into a Linestring  */
749     int iv;
750     double ox;
751     double oy;
752     int normalized_points = 0;
753     gaiaLinestringPtr ln;
754     gaiaGeomCollPtr geom;
755     if (line->has_z)
756 	geom = gaiaAllocGeomCollXYZ ();
757     else
758 	geom = gaiaAllocGeomColl ();
759     for (iv = 0; iv < line->points; iv++)
760       {
761 	  /* counting how many duplicate points are there */
762 	  double x = line->x[iv];
763 	  double y = line->y[iv];
764 	  if (iv == 0)
765 	      normalized_points++;
766 	  else
767 	    {
768 		if (x == ox && y == oy)
769 		    ;
770 		else
771 		    normalized_points++;
772 	    }
773 	  ox = x;
774 	  oy = y;
775       }
776     ln = gaiaAddLinestringToGeomColl (geom, normalized_points);
777     normalized_points = 0;
778     for (iv = 0; iv < line->points; iv++)
779       {
780 	  double x = line->x[iv];
781 	  double y = line->y[iv];
782 	  double z;
783 	  if (iv == 0)
784 	      ;
785 	  else
786 	    {
787 		if (x == ox && y == oy)
788 		    continue;	/* discarding duplicate points */
789 	    }
790 	  ox = x;
791 	  oy = y;
792 	  if (line->has_z)
793 	      z = line->z[iv];
794 	  if (line->has_z)
795 	    {
796 		gaiaSetPointXYZ (ln->Coords, normalized_points, x, y, z);
797 	    }
798 	  else
799 	    {
800 		gaiaSetPoint (ln->Coords, normalized_points, x, y);
801 	    }
802 	  normalized_points++;
803       }
804     geom->DeclaredType = GAIA_LINESTRING;
805     geom->Srid = srid;
806 
807     return geom;
808 }
809 
810 LWN_NET_NODE *
netcallback_getNetNodeWithinDistance2D(const LWN_BE_NETWORK * lwn_net,const LWN_POINT * pt,double dist,int * numelems,int fields,int limit)811 netcallback_getNetNodeWithinDistance2D (const LWN_BE_NETWORK * lwn_net,
812 					const LWN_POINT * pt, double dist,
813 					int *numelems, int fields, int limit)
814 {
815 /* callback function: getNodeWithinDistance2D */
816     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
817     struct gaia_network *accessor = (struct gaia_network *) net;
818     sqlite3_stmt *stmt;
819     int ret;
820     int count = 0;
821     sqlite3_stmt *stmt_aux = NULL;
822     char *sql;
823     struct net_nodes_list *list = NULL;
824     LWN_NET_NODE *result = NULL;
825     if (accessor == NULL)
826       {
827 	  *numelems = -1;
828 	  return NULL;
829       }
830     if (pt == NULL)
831       {
832 	  *numelems = 0;
833 	  return NULL;
834       }
835 
836     stmt = accessor->stmt_getNetNodeWithinDistance2D;
837     if (stmt == NULL)
838       {
839 	  *numelems = -1;
840 	  return NULL;
841       }
842 
843     if (limit >= 0)
844       {
845 	  /* preparing the auxiliary SQL statement */
846 	  sql =
847 	      do_prepare_read_net_node (accessor->network_name, fields,
848 					accessor->spatial, accessor->has_z);
849 	  ret =
850 	      sqlite3_prepare_v2 (accessor->db_handle, sql, strlen (sql),
851 				  &stmt_aux, NULL);
852 	  sqlite3_free (sql);
853 	  if (ret != SQLITE_OK)
854 	    {
855 		char *msg =
856 		    sqlite3_mprintf
857 		    ("Prepare_getNetNodeWithinDistance2D AUX error: \"%s\"",
858 		     sqlite3_errmsg (accessor->db_handle));
859 		gaianet_set_last_error_msg (net, msg);
860 		sqlite3_free (msg);
861 		*numelems = -1;
862 		return NULL;
863 	    }
864       }
865 
866 /* setting up the prepared statement */
867     sqlite3_reset (stmt);
868     sqlite3_clear_bindings (stmt);
869     sqlite3_bind_double (stmt, 1, pt->x);
870     sqlite3_bind_double (stmt, 2, pt->y);
871     sqlite3_bind_double (stmt, 3, dist);
872     sqlite3_bind_double (stmt, 4, pt->x);
873     sqlite3_bind_double (stmt, 5, pt->y);
874     sqlite3_bind_double (stmt, 6, dist);
875     list = create_nodes_list ();
876 
877     while (1)
878       {
879 	  /* scrolling the result set rows */
880 	  ret = sqlite3_step (stmt);
881 	  if (ret == SQLITE_DONE)
882 	      break;		/* end of result set */
883 	  if (ret == SQLITE_ROW)
884 	    {
885 		sqlite3_int64 node_id = sqlite3_column_int64 (stmt, 0);
886 		if (stmt_aux != NULL)
887 		  {
888 		      char *msg;
889 		      if (!do_read_net_node
890 			  (stmt_aux, list, node_id, fields, accessor->spatial,
891 			   accessor->has_z,
892 			   "netcallback_getNetNodeWithinDistance2D", &msg))
893 			{
894 			    gaianet_set_last_error_msg (net, msg);
895 			    sqlite3_free (msg);
896 			    goto error;
897 			}
898 		  }
899 		count++;
900 		if (limit > 0)
901 		  {
902 		      if (count > limit)
903 			  break;
904 		  }
905 		if (limit < 0)
906 		    break;
907 	    }
908 	  else
909 	    {
910 		char *msg =
911 		    sqlite3_mprintf ("netcallback_getNodeWithinDistance2D: %s",
912 				     sqlite3_errmsg (accessor->db_handle));
913 		gaianet_set_last_error_msg (net, msg);
914 		sqlite3_free (msg);
915 		goto error;
916 	    }
917       }
918 
919     if (limit < 0)
920       {
921 	  result = NULL;
922 	  *numelems = count;
923       }
924     else
925       {
926 	  if (list->count <= 0)
927 	    {
928 		result = NULL;
929 		*numelems = 0;
930 	    }
931 	  else
932 	    {
933 		int i = 0;
934 		struct net_node *p_nd;
935 		result = malloc (sizeof (LWN_NET_NODE) * list->count);
936 		p_nd = list->first;
937 		while (p_nd != NULL)
938 		  {
939 		      LWN_NET_NODE *nd = result + i;
940 		      nd->geom = NULL;
941 		      if (fields & LWN_COL_NODE_NODE_ID)
942 			  nd->node_id = p_nd->node_id;
943 		      if (fields & LWN_COL_NODE_GEOM)
944 			{
945 			    if (p_nd->is_null)
946 				;
947 			    else
948 			      {
949 				  if (accessor->has_z)
950 				      nd->geom =
951 					  lwn_create_point3d (accessor->srid,
952 							      p_nd->x, p_nd->y,
953 							      p_nd->z);
954 				  else
955 				      nd->geom =
956 					  lwn_create_point2d (accessor->srid,
957 							      p_nd->x, p_nd->y);
958 			      }
959 			}
960 		      i++;
961 		      p_nd = p_nd->next;
962 		  }
963 		*numelems = list->count;
964 	    }
965       }
966 
967     if (stmt_aux != NULL)
968 	sqlite3_finalize (stmt_aux);
969     destroy_net_nodes_list (list);
970     sqlite3_reset (stmt);
971     return result;
972 
973   error:
974     if (stmt_aux != NULL)
975 	sqlite3_finalize (stmt_aux);
976     if (list != NULL)
977 	destroy_net_nodes_list (list);
978     *numelems = -1;
979     sqlite3_reset (stmt);
980     return NULL;
981 }
982 
983 LWN_NET_NODE *
netcallback_getNetNodeById(const LWN_BE_NETWORK * lwn_net,const LWN_ELEMID * ids,int * numelems,int fields)984 netcallback_getNetNodeById (const LWN_BE_NETWORK * lwn_net,
985 			    const LWN_ELEMID * ids, int *numelems, int fields)
986 {
987 /* callback function: getNetNodeById */
988     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
989     struct gaia_network *accessor = (struct gaia_network *) net;
990     sqlite3_stmt *stmt_aux = NULL;
991     int ret;
992     int i;
993     char *sql;
994     struct net_nodes_list *list = NULL;
995     LWN_NET_NODE *result = NULL;
996     if (accessor == NULL)
997       {
998 	  *numelems = -1;
999 	  return NULL;
1000       }
1001 
1002     /* preparing the SQL statement */
1003     sql =
1004 	do_prepare_read_net_node (accessor->network_name, fields,
1005 				  accessor->spatial, accessor->has_z);
1006     ret =
1007 	sqlite3_prepare_v2 (accessor->db_handle, sql, strlen (sql), &stmt_aux,
1008 			    NULL);
1009     sqlite3_free (sql);
1010     if (ret != SQLITE_OK)
1011       {
1012 	  char *msg =
1013 	      sqlite3_mprintf ("Prepare_getNetNodeById AUX error: \"%s\"",
1014 			       sqlite3_errmsg (accessor->db_handle));
1015 	  gaianet_set_last_error_msg (net, msg);
1016 	  sqlite3_free (msg);
1017 	  *numelems = -1;
1018 	  return NULL;
1019       }
1020 
1021     list = create_nodes_list ();
1022     for (i = 0; i < *numelems; i++)
1023       {
1024 	  char *msg;
1025 	  if (!do_read_net_node
1026 	      (stmt_aux, list, *(ids + i), fields, accessor->spatial,
1027 	       accessor->has_z, "netcallback_getNetNodeById", &msg))
1028 	    {
1029 		gaianet_set_last_error_msg (net, msg);
1030 		sqlite3_free (msg);
1031 		goto error;
1032 	    }
1033       }
1034 
1035     if (list->count == 0)
1036       {
1037 	  /* no node was found */
1038 	  *numelems = list->count;
1039       }
1040     else
1041       {
1042 	  struct net_node *p_nd;
1043 	  result = malloc (sizeof (LWN_NET_NODE) * list->count);
1044 	  p_nd = list->first;
1045 	  i = 0;
1046 	  while (p_nd != NULL)
1047 	    {
1048 		LWN_NET_NODE *nd = result + i;
1049 		nd->geom = NULL;
1050 		if (fields & LWN_COL_NODE_NODE_ID)
1051 		    nd->node_id = p_nd->node_id;
1052 		if (fields & LWN_COL_NODE_GEOM)
1053 		  {
1054 		      if (p_nd->is_null)
1055 			  ;
1056 		      else
1057 			{
1058 			    if (accessor->has_z)
1059 				nd->geom =
1060 				    lwn_create_point3d (accessor->srid, p_nd->x,
1061 							p_nd->y, p_nd->z);
1062 			    else
1063 				nd->geom =
1064 				    lwn_create_point2d (accessor->srid, p_nd->x,
1065 							p_nd->y);
1066 			}
1067 		  }
1068 		i++;
1069 		p_nd = p_nd->next;
1070 	    }
1071 	  *numelems = list->count;
1072       }
1073     sqlite3_finalize (stmt_aux);
1074     destroy_net_nodes_list (list);
1075     return result;
1076 
1077   error:
1078     if (stmt_aux != NULL)
1079 	sqlite3_finalize (stmt_aux);
1080     if (list != NULL)
1081 	destroy_net_nodes_list (list);
1082     *numelems = -1;
1083     return NULL;
1084 }
1085 
1086 LWN_LINK *
netcallback_getLinkWithinDistance2D(const LWN_BE_NETWORK * lwn_net,const LWN_POINT * pt,double dist,int * numelems,int fields,int limit)1087 netcallback_getLinkWithinDistance2D (const LWN_BE_NETWORK * lwn_net,
1088 				     const LWN_POINT * pt, double dist,
1089 				     int *numelems, int fields, int limit)
1090 {
1091 /* callback function: getLinkWithinDistance2D */
1092     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
1093     struct gaia_network *accessor = (struct gaia_network *) net;
1094     sqlite3_stmt *stmt;
1095     int ret;
1096     int count = 0;
1097     sqlite3_stmt *stmt_aux = NULL;
1098     char *sql;
1099     struct net_links_list *list = NULL;
1100     LWN_LINK *result = NULL;
1101     if (accessor == NULL)
1102       {
1103 	  *numelems = -1;
1104 	  return NULL;
1105       }
1106     if (pt == NULL)
1107       {
1108 	  *numelems = 0;
1109 	  return NULL;
1110       }
1111 
1112     stmt = accessor->stmt_getLinkWithinDistance2D;
1113     if (stmt == NULL)
1114       {
1115 	  *numelems = -1;
1116 	  return NULL;
1117       }
1118 
1119     if (limit >= 0)
1120       {
1121 	  /* preparing the auxiliary SQL statement */
1122 	  sql = do_prepare_read_link (accessor->network_name, fields);
1123 	  ret =
1124 	      sqlite3_prepare_v2 (accessor->db_handle, sql, strlen (sql),
1125 				  &stmt_aux, NULL);
1126 	  sqlite3_free (sql);
1127 	  if (ret != SQLITE_OK)
1128 	    {
1129 		char *msg =
1130 		    sqlite3_mprintf ("Prepare_getLinkById AUX error: \"%s\"",
1131 				     sqlite3_errmsg (accessor->db_handle));
1132 		gaianet_set_last_error_msg (net, msg);
1133 		sqlite3_free (msg);
1134 		*numelems = -1;
1135 		return NULL;
1136 	    }
1137       }
1138 
1139 /* setting up the prepared statement */
1140     sqlite3_reset (stmt);
1141     sqlite3_clear_bindings (stmt);
1142     sqlite3_bind_double (stmt, 1, pt->x);
1143     sqlite3_bind_double (stmt, 2, pt->y);
1144     sqlite3_bind_double (stmt, 3, dist);
1145     sqlite3_bind_double (stmt, 4, pt->x);
1146     sqlite3_bind_double (stmt, 5, pt->y);
1147     sqlite3_bind_double (stmt, 6, dist);
1148     list = create_links_list ();
1149 
1150     while (1)
1151       {
1152 	  /* scrolling the result set rows */
1153 	  ret = sqlite3_step (stmt);
1154 	  if (ret == SQLITE_DONE)
1155 	      break;		/* end of result set */
1156 	  if (ret == SQLITE_ROW)
1157 	    {
1158 		sqlite3_int64 link_id = sqlite3_column_int64 (stmt, 0);
1159 		if (stmt_aux != NULL)
1160 		  {
1161 		      char *msg;
1162 		      if (!do_read_link
1163 			  (stmt_aux, list, link_id, fields,
1164 			   "netcallback_getLinkWithinDistance2D", &msg))
1165 			{
1166 			    gaianet_set_last_error_msg (net, msg);
1167 			    sqlite3_free (msg);
1168 			    goto error;
1169 			}
1170 		  }
1171 		count++;
1172 		if (limit > 0)
1173 		  {
1174 		      if (count > limit)
1175 			  break;
1176 		  }
1177 		if (limit < 0)
1178 		    break;
1179 	    }
1180 	  else
1181 	    {
1182 		char *msg =
1183 		    sqlite3_mprintf ("netcallback_getLinkWithinDistance2D: %s",
1184 				     sqlite3_errmsg (accessor->db_handle));
1185 		gaianet_set_last_error_msg (net, msg);
1186 		sqlite3_free (msg);
1187 		goto error;
1188 	    }
1189       }
1190 
1191     if (limit < 0)
1192       {
1193 	  result = NULL;
1194 	  *numelems = count;
1195       }
1196     else
1197       {
1198 	  if (list->count <= 0)
1199 	    {
1200 		result = NULL;
1201 		*numelems = 0;
1202 	    }
1203 	  else
1204 	    {
1205 		int i = 0;
1206 		struct net_link *p_lnk;
1207 		result = malloc (sizeof (LWN_LINK) * list->count);
1208 		p_lnk = list->first;
1209 		while (p_lnk != NULL)
1210 		  {
1211 		      LWN_LINK *lnk = result + i;
1212 		      if (fields & LWN_COL_LINK_LINK_ID)
1213 			  lnk->link_id = p_lnk->link_id;
1214 		      if (fields & LWN_COL_LINK_START_NODE)
1215 			  lnk->start_node = p_lnk->start_node;
1216 		      if (fields & LWN_COL_LINK_END_NODE)
1217 			  lnk->end_node = p_lnk->end_node;
1218 		      if (fields & LWN_COL_LINK_GEOM)
1219 			  lnk->geom =
1220 			      gaianet_convert_linestring_to_lwnline
1221 			      (p_lnk->geom, accessor->srid, accessor->has_z);
1222 		      else
1223 			  lnk->geom = NULL;
1224 		      i++;
1225 		      p_lnk = p_lnk->next;
1226 		  }
1227 		*numelems = list->count;
1228 	    }
1229       }
1230     if (stmt_aux != NULL)
1231 	sqlite3_finalize (stmt_aux);
1232     destroy_links_list (list);
1233     sqlite3_reset (stmt);
1234     return result;
1235 
1236   error:
1237     if (stmt_aux != NULL)
1238 	sqlite3_finalize (stmt_aux);
1239     if (list != NULL)
1240 	destroy_links_list (list);
1241     *numelems = -1;
1242     sqlite3_reset (stmt);
1243     return NULL;
1244 }
1245 
1246 int
netcallback_insertNetNodes(const LWN_BE_NETWORK * lwn_net,LWN_NET_NODE * nodes,int numelems)1247 netcallback_insertNetNodes (const LWN_BE_NETWORK * lwn_net,
1248 			    LWN_NET_NODE * nodes, int numelems)
1249 {
1250 /* callback function: insertNetNodes */
1251     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
1252     struct gaia_network *accessor = (struct gaia_network *) net;
1253     sqlite3_stmt *stmt;
1254     int ret;
1255     int i;
1256     unsigned char *p_blob;
1257     int n_bytes;
1258     gaiaGeomCollPtr geom = NULL;
1259     int gpkg_mode = 0;
1260     int tiny_point = 0;
1261     if (accessor == NULL)
1262 	return 0;
1263 
1264     stmt = accessor->stmt_insertNetNodes;
1265     if (stmt == NULL)
1266 	return 0;
1267 
1268     if (accessor->cache != NULL)
1269       {
1270 	  struct splite_internal_cache *cache =
1271 	      (struct splite_internal_cache *) (accessor->cache);
1272 	  gpkg_mode = cache->gpkg_mode;
1273 	  tiny_point = cache->tinyPointEnabled;
1274       }
1275 
1276     for (i = 0; i < numelems; i++)
1277       {
1278 	  LWN_NET_NODE *nd = nodes + i;
1279 	  /* setting up the prepared statement */
1280 	  sqlite3_reset (stmt);
1281 	  sqlite3_clear_bindings (stmt);
1282 	  if (nd->node_id <= 0)
1283 	      sqlite3_bind_null (stmt, 1);
1284 	  else
1285 	      sqlite3_bind_int64 (stmt, 1, nd->node_id);
1286 	  if (nd->geom == NULL)
1287 	      sqlite3_bind_null (stmt, 2);
1288 	  else
1289 	    {
1290 		if (accessor->has_z)
1291 		    geom = gaiaAllocGeomCollXYZ ();
1292 		else
1293 		    geom = gaiaAllocGeomColl ();
1294 		if (accessor->has_z)
1295 		    gaiaAddPointToGeomCollXYZ (geom, nd->geom->x, nd->geom->y,
1296 					       nd->geom->z);
1297 		else
1298 		    gaiaAddPointToGeomColl (geom, nd->geom->x, nd->geom->y);
1299 		geom->Srid = accessor->srid;
1300 		geom->DeclaredType = GAIA_POINT;
1301 		gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_blob, &n_bytes, gpkg_mode,
1302 					    tiny_point);
1303 		gaiaFreeGeomColl (geom);
1304 		sqlite3_bind_blob (stmt, 2, p_blob, n_bytes, free);
1305 	    }
1306 	  ret = sqlite3_step (stmt);
1307 	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
1308 	    {
1309 		/* retrieving the PK value */
1310 		nd->node_id = sqlite3_last_insert_rowid (accessor->db_handle);
1311 	    }
1312 	  else
1313 	    {
1314 		char *msg =
1315 		    sqlite3_mprintf ("netcallback_insertNetNodes: \"%s\"",
1316 				     sqlite3_errmsg (accessor->db_handle));
1317 		gaianet_set_last_error_msg (net, msg);
1318 		sqlite3_free (msg);
1319 		goto error;
1320 	    }
1321       }
1322     sqlite3_reset (stmt);
1323     return 1;
1324 
1325   error:
1326     sqlite3_reset (stmt);
1327     return 0;
1328 }
1329 
1330 int
netcallback_updateNetNodesById(const LWN_BE_NETWORK * lwn_net,const LWN_NET_NODE * nodes,int numnodes,int upd_fields)1331 netcallback_updateNetNodesById (const LWN_BE_NETWORK * lwn_net,
1332 				const LWN_NET_NODE * nodes, int numnodes,
1333 				int upd_fields)
1334 {
1335 /* callback function: updateNetNodesById */
1336     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
1337     struct gaia_network *accessor = (struct gaia_network *) net;
1338     sqlite3_stmt *stmt = NULL;
1339     int ret;
1340     char *sql;
1341     char *prev;
1342     int comma = 0;
1343     char *table;
1344     char *xtable;
1345     int icol = 1;
1346     int i;
1347     int changed = 0;
1348     if (accessor == NULL)
1349 	return -1;
1350 
1351 /* composing the SQL prepared statement */
1352     table = sqlite3_mprintf ("%s_node", accessor->network_name);
1353     xtable = gaiaDoubleQuotedSql (table);
1354     sqlite3_free (table);
1355     sql = sqlite3_mprintf ("UPDATE MAIN.\"%s\" SET", xtable);
1356     free (xtable);
1357     prev = sql;
1358     if (upd_fields & LWN_COL_NODE_NODE_ID)
1359       {
1360 	  if (comma)
1361 	      sql = sqlite3_mprintf ("%s, node_id = ?", prev);
1362 	  else
1363 	      sql = sqlite3_mprintf ("%s node_id = ?", prev);
1364 	  comma = 1;
1365 	  sqlite3_free (prev);
1366 	  prev = sql;
1367       }
1368     if (upd_fields & LWN_COL_NODE_GEOM)
1369       {
1370 	  if (accessor->has_z)
1371 	    {
1372 		if (comma)
1373 		    sql =
1374 			sqlite3_mprintf
1375 			("%s, geometry = MakePointZ(?, ?. ?, %d)", prev,
1376 			 accessor->srid);
1377 		else
1378 		    sql =
1379 			sqlite3_mprintf
1380 			("%s geometry = MakePointZ(?, ?, ?, %d)", prev,
1381 			 accessor->srid);
1382 	    }
1383 	  else
1384 	    {
1385 		if (comma)
1386 		    sql =
1387 			sqlite3_mprintf ("%s, geometry = MakePoint(?, ?, %d)",
1388 					 prev, accessor->srid);
1389 		else
1390 		    sql =
1391 			sqlite3_mprintf ("%s geometry = MakePoint(?, ?, %d)",
1392 					 prev, accessor->srid);
1393 	    }
1394 	  comma = 1;
1395 	  sqlite3_free (prev);
1396 	  prev = sql;
1397       }
1398     sql = sqlite3_mprintf ("%s WHERE node_id = ?", prev);
1399     sqlite3_free (prev);
1400     ret =
1401 	sqlite3_prepare_v2 (accessor->db_handle, sql, strlen (sql), &stmt,
1402 			    NULL);
1403     sqlite3_free (sql);
1404     if (ret != SQLITE_OK)
1405       {
1406 	  char *msg =
1407 	      sqlite3_mprintf ("Prepare_updateNetNodesById error: \"%s\"",
1408 			       sqlite3_errmsg (accessor->db_handle));
1409 	  gaianet_set_last_error_msg (net, msg);
1410 	  sqlite3_free (msg);
1411 	  return -1;
1412       }
1413 
1414     for (i = 0; i < numnodes; i++)
1415       {
1416 	  /* parameter binding */
1417 	  const LWN_NET_NODE *nd = nodes + i;
1418 	  icol = 1;
1419 	  sqlite3_reset (stmt);
1420 	  sqlite3_clear_bindings (stmt);
1421 	  if (upd_fields & LWN_COL_NODE_NODE_ID)
1422 	    {
1423 		sqlite3_bind_int64 (stmt, icol, nd->node_id);
1424 		icol++;
1425 	    }
1426 	  if (upd_fields & LWN_COL_NODE_GEOM)
1427 	    {
1428 		if (accessor->spatial)
1429 		  {
1430 		      sqlite3_bind_double (stmt, icol, nd->geom->x);
1431 		      icol++;
1432 		      sqlite3_bind_double (stmt, icol, nd->geom->y);
1433 		      icol++;
1434 		      if (accessor->has_z)
1435 			{
1436 			    sqlite3_bind_double (stmt, icol, nd->geom->z);
1437 			    icol++;
1438 			}
1439 		  }
1440 		else
1441 		  {
1442 		      icol += 2;
1443 		      if (accessor->has_z)
1444 			  icol++;
1445 		  }
1446 	    }
1447 	  sqlite3_bind_int64 (stmt, icol, nd->node_id);
1448 	  ret = sqlite3_step (stmt);
1449 	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
1450 	      changed += sqlite3_changes (accessor->db_handle);
1451 	  else
1452 	    {
1453 		char *msg =
1454 		    sqlite3_mprintf ("netcallback_updateNetNodesById: \"%s\"",
1455 				     sqlite3_errmsg (accessor->db_handle));
1456 		gaianet_set_last_error_msg (net, msg);
1457 		sqlite3_free (msg);
1458 		goto error;
1459 	    }
1460       }
1461     sqlite3_finalize (stmt);
1462     return changed;
1463 
1464   error:
1465     sqlite3_finalize (stmt);
1466     return -1;
1467 }
1468 
1469 int
netcallback_insertLinks(const LWN_BE_NETWORK * lwn_net,LWN_LINK * links,int numelems)1470 netcallback_insertLinks (const LWN_BE_NETWORK * lwn_net, LWN_LINK * links,
1471 			 int numelems)
1472 {
1473 /* callback function: insertLinks */
1474     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
1475     struct gaia_network *accessor = (struct gaia_network *) net;
1476     sqlite3_stmt *stmt;
1477     int ret;
1478     int i;
1479     gaiaGeomCollPtr geom;
1480     unsigned char *p_blob;
1481     int n_bytes;
1482     int gpkg_mode = 0;
1483     int tiny_point = 0;
1484     if (accessor == NULL)
1485 	return 0;
1486 
1487     stmt = accessor->stmt_insertLinks;
1488     if (stmt == NULL)
1489 	return 0;
1490 
1491     if (accessor->cache != NULL)
1492       {
1493 	  struct splite_internal_cache *cache =
1494 	      (struct splite_internal_cache *) (accessor->cache);
1495 	  gpkg_mode = cache->gpkg_mode;
1496 	  tiny_point = cache->tinyPointEnabled;
1497       }
1498 
1499     for (i = 0; i < numelems; i++)
1500       {
1501 	  LWN_LINK *lnk = links + i;
1502 	  /* setting up the prepared statement */
1503 	  sqlite3_reset (stmt);
1504 	  sqlite3_clear_bindings (stmt);
1505 	  if (lnk->link_id <= 0)
1506 	      sqlite3_bind_null (stmt, 1);
1507 	  else
1508 	      sqlite3_bind_int64 (stmt, 1, lnk->link_id);
1509 	  sqlite3_bind_int64 (stmt, 2, lnk->start_node);
1510 	  sqlite3_bind_int64 (stmt, 3, lnk->end_node);
1511 	  if (lnk->geom == NULL)
1512 	      sqlite3_bind_null (stmt, 4);
1513 	  else
1514 	    {
1515 		/* transforming the LWN_LINE into a Geometry-Linestring */
1516 		geom = do_convert_lwnline_to_geom (lnk->geom, accessor->srid);
1517 		gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_blob, &n_bytes, gpkg_mode,
1518 					    tiny_point);
1519 		gaiaFreeGeomColl (geom);
1520 		sqlite3_bind_blob (stmt, 4, p_blob, n_bytes, free);
1521 	    }
1522 	  ret = sqlite3_step (stmt);
1523 	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
1524 	    {
1525 		/* retrieving the PK value */
1526 		lnk->link_id = sqlite3_last_insert_rowid (accessor->db_handle);
1527 	    }
1528 	  else
1529 	    {
1530 		char *msg = sqlite3_mprintf ("netcallback_inserLinks: \"%s\"",
1531 					     sqlite3_errmsg
1532 					     (accessor->db_handle));
1533 		gaianet_set_last_error_msg (net, msg);
1534 		sqlite3_free (msg);
1535 		goto error;
1536 	    }
1537       }
1538     sqlite3_reset (stmt);
1539     return 1;
1540 
1541   error:
1542     sqlite3_reset (stmt);
1543     return 0;
1544 }
1545 
1546 LWN_LINK *
netcallback_getLinkByNetNode(const LWN_BE_NETWORK * lwn_net,const LWN_ELEMID * ids,int * numelems,int fields)1547 netcallback_getLinkByNetNode (const LWN_BE_NETWORK * lwn_net,
1548 			      const LWN_ELEMID * ids, int *numelems, int fields)
1549 {
1550 /* callback function: getLinkByNetNode */
1551     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
1552     struct gaia_network *accessor = (struct gaia_network *) net;
1553     int ret;
1554     char *sql;
1555     char *prev;
1556     char *table;
1557     char *xtable;
1558     int comma = 0;
1559     int i;
1560     sqlite3_stmt *stmt_aux = NULL;
1561     struct net_links_list *list = NULL;
1562     LWN_LINK *result = NULL;
1563     if (accessor == NULL)
1564       {
1565 	  *numelems = -1;
1566 	  return NULL;
1567       }
1568 
1569     /* preparing the SQL statement */
1570     sql = sqlite3_mprintf ("SELECT ");
1571     prev = sql;
1572     if (fields & LWN_COL_LINK_LINK_ID)
1573       {
1574 	  if (comma)
1575 	      sql = sqlite3_mprintf ("%s, link_id", prev);
1576 	  else
1577 	      sql = sqlite3_mprintf ("%s link_id", prev);
1578 	  comma = 1;
1579 	  sqlite3_free (prev);
1580 	  prev = sql;
1581       }
1582     if (fields & LWN_COL_LINK_START_NODE)
1583       {
1584 	  if (comma)
1585 	      sql = sqlite3_mprintf ("%s, start_node", prev);
1586 	  else
1587 	      sql = sqlite3_mprintf ("%s start_node", prev);
1588 	  comma = 1;
1589 	  sqlite3_free (prev);
1590 	  prev = sql;
1591       }
1592     if (fields & LWN_COL_LINK_END_NODE)
1593       {
1594 	  if (comma)
1595 	      sql = sqlite3_mprintf ("%s, end_node", prev);
1596 	  else
1597 	      sql = sqlite3_mprintf ("%s end_node", prev);
1598 	  comma = 1;
1599 	  sqlite3_free (prev);
1600 	  prev = sql;
1601       }
1602     if (fields & LWN_COL_LINK_GEOM)
1603       {
1604 	  if (comma)
1605 	      sql = sqlite3_mprintf ("%s, geometry", prev);
1606 	  else
1607 	      sql = sqlite3_mprintf ("%s geometry", prev);
1608 	  comma = 1;
1609 	  sqlite3_free (prev);
1610 	  prev = sql;
1611       }
1612     table = sqlite3_mprintf ("%s_link", accessor->network_name);
1613     xtable = gaiaDoubleQuotedSql (table);
1614     sqlite3_free (table);
1615     sql =
1616 	sqlite3_mprintf
1617 	("%s FROM MAIN.\"%s\" WHERE start_node = ? OR end_node = ?", prev,
1618 	 xtable);
1619     free (xtable);
1620     sqlite3_free (prev);
1621     ret =
1622 	sqlite3_prepare_v2 (accessor->db_handle, sql, strlen (sql),
1623 			    &stmt_aux, NULL);
1624     sqlite3_free (sql);
1625     if (ret != SQLITE_OK)
1626       {
1627 	  char *msg =
1628 	      sqlite3_mprintf ("Prepare_getLinkByNetNode AUX error: \"%s\"",
1629 			       sqlite3_errmsg (accessor->db_handle));
1630 	  gaianet_set_last_error_msg (net, msg);
1631 	  sqlite3_free (msg);
1632 	  *numelems = -1;
1633 	  return NULL;
1634       }
1635 
1636     list = create_links_list ();
1637     for (i = 0; i < *numelems; i++)
1638       {
1639 	  char *msg;
1640 	  if (!do_read_link_by_net_node
1641 	      (stmt_aux, list, *(ids + i), fields,
1642 	       "netcallback_getLinkByNetNode", &msg))
1643 	    {
1644 		gaianet_set_last_error_msg (net, msg);
1645 		sqlite3_free (msg);
1646 		goto error;
1647 	    }
1648       }
1649 
1650     if (list->count == 0)
1651       {
1652 	  /* no link was found */
1653 	  *numelems = list->count;
1654       }
1655     else
1656       {
1657 	  struct net_link *p_lnk;
1658 	  result = malloc (sizeof (LWN_LINK) * list->count);
1659 	  p_lnk = list->first;
1660 	  i = 0;
1661 	  while (p_lnk != NULL)
1662 	    {
1663 		LWN_LINK *lnk = result + i;
1664 		lnk->geom = NULL;
1665 		if (fields & LWN_COL_LINK_LINK_ID)
1666 		    lnk->link_id = p_lnk->link_id;
1667 		if (fields & LWN_COL_LINK_START_NODE)
1668 		    lnk->start_node = p_lnk->start_node;
1669 		if (fields & LWN_COL_LINK_END_NODE)
1670 		    lnk->end_node = p_lnk->end_node;
1671 		if (fields & LWN_COL_LINK_GEOM)
1672 		    lnk->geom =
1673 			gaianet_convert_linestring_to_lwnline (p_lnk->geom,
1674 							       accessor->srid,
1675 							       accessor->has_z);
1676 		i++;
1677 		p_lnk = p_lnk->next;
1678 	    }
1679 	  *numelems = list->count;
1680       }
1681     sqlite3_finalize (stmt_aux);
1682     destroy_links_list (list);
1683     return result;
1684 
1685   error:
1686     if (stmt_aux != NULL)
1687 	sqlite3_finalize (stmt_aux);
1688     if (list != NULL)
1689 	destroy_links_list (list);
1690     *numelems = -1;
1691     return NULL;
1692 }
1693 
1694 int
netcallback_deleteNetNodesById(const LWN_BE_NETWORK * lwn_net,const LWN_ELEMID * ids,int numelems)1695 netcallback_deleteNetNodesById (const LWN_BE_NETWORK * lwn_net,
1696 				const LWN_ELEMID * ids, int numelems)
1697 {
1698 /* callback function: deleteNetNodesById */
1699     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
1700     struct gaia_network *accessor = (struct gaia_network *) net;
1701     sqlite3_stmt *stmt = NULL;
1702     int ret;
1703     int i;
1704     int changed = 0;
1705     if (accessor == NULL)
1706 	return -1;
1707 
1708     stmt = accessor->stmt_deleteNetNodesById;
1709     if (stmt == NULL)
1710 	return -1;
1711 
1712     for (i = 0; i < numelems; i++)
1713       {
1714 	  /* parameter binding */
1715 	  sqlite3_int64 id = *(ids + i);
1716 	  sqlite3_reset (stmt);
1717 	  sqlite3_clear_bindings (stmt);
1718 	  sqlite3_bind_int64 (stmt, 1, id);
1719 	  ret = sqlite3_step (stmt);
1720 	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
1721 	    {
1722 		changed += sqlite3_changes (accessor->db_handle);
1723 	    }
1724 	  else
1725 	    {
1726 		char *msg =
1727 		    sqlite3_mprintf ("netcallback_deleteNetNodesById: \"%s\"",
1728 				     sqlite3_errmsg (accessor->db_handle));
1729 		gaianet_set_last_error_msg (net, msg);
1730 		sqlite3_free (msg);
1731 		goto error;
1732 	    }
1733       }
1734     sqlite3_reset (stmt);
1735     return changed;
1736 
1737   error:
1738     sqlite3_reset (stmt);
1739     return -1;
1740 }
1741 
1742 LWN_NET_NODE *
netcallback_getNetNodeWithinBox2D(const LWN_BE_NETWORK * lwn_net,const LWN_BBOX * box,int * numelems,int fields,int limit)1743 netcallback_getNetNodeWithinBox2D (const LWN_BE_NETWORK * lwn_net,
1744 				   const LWN_BBOX * box, int *numelems,
1745 				   int fields, int limit)
1746 {
1747 /* callback function: getNetNodeWithinBox2D */
1748     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
1749     struct gaia_network *accessor = (struct gaia_network *) net;
1750     sqlite3_stmt *stmt;
1751     int ret;
1752     int count = 0;
1753     sqlite3_stmt *stmt_aux = NULL;
1754     char *sql;
1755     struct net_nodes_list *list = NULL;
1756     LWN_NET_NODE *result = NULL;
1757     if (accessor == NULL)
1758       {
1759 	  *numelems = -1;
1760 	  return NULL;
1761       }
1762 
1763     stmt = accessor->stmt_getNetNodeWithinBox2D;
1764     if (stmt == NULL)
1765       {
1766 	  *numelems = -1;
1767 	  return NULL;
1768       }
1769 
1770     if (limit >= 0)
1771       {
1772 	  /* preparing the auxiliary SQL statement */
1773 	  sql =
1774 	      do_prepare_read_net_node (accessor->network_name, fields,
1775 					accessor->spatial, accessor->has_z);
1776 	  ret =
1777 	      sqlite3_prepare_v2 (accessor->db_handle, sql, strlen (sql),
1778 				  &stmt_aux, NULL);
1779 	  sqlite3_free (sql);
1780 	  if (ret != SQLITE_OK)
1781 	    {
1782 		char *msg =
1783 		    sqlite3_mprintf
1784 		    ("Prepare_getNetNodeWithinBox2D AUX error: \"%s\"",
1785 		     sqlite3_errmsg (accessor->db_handle));
1786 		gaianet_set_last_error_msg (net, msg);
1787 		sqlite3_free (msg);
1788 		*numelems = -1;
1789 		return NULL;
1790 	    }
1791       }
1792 
1793 /* setting up the prepared statement */
1794     sqlite3_reset (stmt);
1795     sqlite3_clear_bindings (stmt);
1796     sqlite3_bind_double (stmt, 1, box->min_x);
1797     sqlite3_bind_double (stmt, 2, box->min_y);
1798     sqlite3_bind_double (stmt, 3, box->max_x);
1799     sqlite3_bind_double (stmt, 4, box->max_y);
1800     list = create_nodes_list ();
1801 
1802     while (1)
1803       {
1804 	  /* scrolling the result set rows */
1805 	  ret = sqlite3_step (stmt);
1806 	  if (ret == SQLITE_DONE)
1807 	      break;		/* end of result set */
1808 	  if (ret == SQLITE_ROW)
1809 	    {
1810 		sqlite3_int64 node_id = sqlite3_column_int64 (stmt, 0);
1811 		if (stmt_aux != NULL)
1812 		  {
1813 		      char *msg;
1814 		      if (!do_read_net_node
1815 			  (stmt_aux, list, node_id, fields, accessor->spatial,
1816 			   accessor->has_z, "netcallback_getNetNodeWithinBox2D",
1817 			   &msg))
1818 			{
1819 			    gaianet_set_last_error_msg (net, msg);
1820 			    sqlite3_free (msg);
1821 			    goto error;
1822 			}
1823 		  }
1824 		count++;
1825 		if (limit > 0)
1826 		  {
1827 		      if (count > limit)
1828 			  break;
1829 		  }
1830 		if (limit < 0)
1831 		    break;
1832 	    }
1833 	  else
1834 	    {
1835 		char *msg =
1836 		    sqlite3_mprintf ("netcallback_getNetNodeWithinBox2D: %s",
1837 				     sqlite3_errmsg (accessor->db_handle));
1838 		gaianet_set_last_error_msg (net, msg);
1839 		sqlite3_free (msg);
1840 		goto error;
1841 	    }
1842       }
1843 
1844     if (limit < 0)
1845       {
1846 	  result = NULL;
1847 	  *numelems = count;
1848       }
1849     else
1850       {
1851 	  if (list->count <= 0)
1852 	    {
1853 		result = NULL;
1854 		*numelems = 0;
1855 	    }
1856 	  else
1857 	    {
1858 		int i = 0;
1859 		struct net_node *p_nd;
1860 		result = malloc (sizeof (LWN_NET_NODE) * list->count);
1861 		p_nd = list->first;
1862 		while (p_nd != NULL)
1863 		  {
1864 		      LWN_NET_NODE *nd = result + i;
1865 		      nd->geom = NULL;
1866 		      if (fields & LWN_COL_NODE_NODE_ID)
1867 			  nd->node_id = p_nd->node_id;
1868 		      if (fields & LWN_COL_NODE_GEOM)
1869 			{
1870 			    if (p_nd->is_null)
1871 				;
1872 			    else
1873 			      {
1874 				  if (accessor->has_z)
1875 				      nd->geom =
1876 					  lwn_create_point3d (accessor->srid,
1877 							      p_nd->x, p_nd->y,
1878 							      p_nd->z);
1879 				  else
1880 				      nd->geom =
1881 					  lwn_create_point2d (accessor->srid,
1882 							      p_nd->x, p_nd->y);
1883 			      }
1884 			}
1885 		      i++;
1886 		      p_nd = p_nd->next;
1887 		  }
1888 		*numelems = list->count;
1889 	    }
1890       }
1891 
1892     if (stmt_aux != NULL)
1893 	sqlite3_finalize (stmt_aux);
1894     destroy_net_nodes_list (list);
1895     sqlite3_reset (stmt);
1896     return result;
1897 
1898   error:
1899     if (stmt_aux != NULL)
1900 	sqlite3_finalize (stmt_aux);
1901     if (list != NULL)
1902 	destroy_net_nodes_list (list);
1903     *numelems = 1;
1904     sqlite3_reset (stmt);
1905     return NULL;
1906 }
1907 
1908 LWN_ELEMID
netcallback_getNextLinkId(const LWN_BE_NETWORK * lwn_net)1909 netcallback_getNextLinkId (const LWN_BE_NETWORK * lwn_net)
1910 {
1911 /* callback function: getNextLinkId */
1912     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
1913     struct gaia_network *accessor = (struct gaia_network *) net;
1914     sqlite3_stmt *stmt_in;
1915     sqlite3_stmt *stmt_out;
1916     int ret;
1917     sqlite3_int64 link_id = -1;
1918     if (accessor == NULL)
1919 	return -1;
1920 
1921     stmt_in = accessor->stmt_getNextLinkId;
1922     if (stmt_in == NULL)
1923 	return -1;
1924     stmt_out = accessor->stmt_setNextLinkId;
1925     if (stmt_out == NULL)
1926 	return -1;
1927 
1928 /* setting up the prepared statement */
1929     sqlite3_reset (stmt_in);
1930     sqlite3_clear_bindings (stmt_in);
1931 
1932     while (1)
1933       {
1934 	  /* scrolling the result set rows */
1935 	  ret = sqlite3_step (stmt_in);
1936 	  if (ret == SQLITE_DONE)
1937 	      break;		/* end of result set */
1938 	  if (ret == SQLITE_ROW)
1939 	      link_id = sqlite3_column_int64 (stmt_in, 0);
1940 	  else
1941 	    {
1942 		char *msg = sqlite3_mprintf ("netcallback_getNextLinkId: %s",
1943 					     sqlite3_errmsg
1944 					     (accessor->db_handle));
1945 		gaianet_set_last_error_msg (net, msg);
1946 		sqlite3_free (msg);
1947 		goto stop;
1948 	    }
1949       }
1950 
1951 /* updating next_link_id */
1952     sqlite3_reset (stmt_out);
1953     sqlite3_clear_bindings (stmt_out);
1954     ret = sqlite3_step (stmt_out);
1955     if (ret == SQLITE_DONE || ret == SQLITE_ROW)
1956       {
1957 	  sqlite3_reset (stmt_in);
1958 	  sqlite3_reset (stmt_out);
1959 	  return link_id;
1960       }
1961     else
1962       {
1963 	  char *msg = sqlite3_mprintf ("netcallback_setNextLinkId: \"%s\"",
1964 				       sqlite3_errmsg (accessor->db_handle));
1965 	  gaianet_set_last_error_msg (net, msg);
1966 	  sqlite3_free (msg);
1967 	  link_id = -1;
1968       }
1969   stop:
1970     sqlite3_reset (stmt_in);
1971     sqlite3_reset (stmt_out);
1972     if (link_id >= 0)
1973 	link_id++;
1974     return link_id;
1975 }
1976 
1977 int
netcallback_updateLinksById(const LWN_BE_NETWORK * lwn_net,const LWN_LINK * links,int numlinks,int upd_fields)1978 netcallback_updateLinksById (const LWN_BE_NETWORK * lwn_net,
1979 			     const LWN_LINK * links, int numlinks,
1980 			     int upd_fields)
1981 {
1982 /* callback function: updateLinksById */
1983     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
1984     struct gaia_network *accessor = (struct gaia_network *) net;
1985     sqlite3_stmt *stmt = NULL;
1986     gaiaGeomCollPtr geom;
1987     int ret;
1988     char *sql;
1989     char *prev;
1990     int comma = 0;
1991     char *table;
1992     char *xtable;
1993     int i;
1994     int changed = 0;
1995     unsigned char *p_blob;
1996     int n_bytes;
1997     int gpkg_mode = 0;
1998     int tiny_point = 0;
1999     if (accessor == NULL)
2000 	return -1;
2001 
2002     if (accessor->cache != NULL)
2003       {
2004 	  struct splite_internal_cache *cache =
2005 	      (struct splite_internal_cache *) (accessor->cache);
2006 	  gpkg_mode = cache->gpkg_mode;
2007 	  tiny_point = cache->tinyPointEnabled;
2008       }
2009 
2010 /* composing the SQL prepared statement */
2011     table = sqlite3_mprintf ("%s_link", accessor->network_name);
2012     xtable = gaiaDoubleQuotedSql (table);
2013     sqlite3_free (table);
2014     sql = sqlite3_mprintf ("UPDATE MAIN.\"%s\" SET", xtable);
2015     free (xtable);
2016     prev = sql;
2017     if (upd_fields & LWN_COL_LINK_LINK_ID)
2018       {
2019 	  if (comma)
2020 	      sql = sqlite3_mprintf ("%s, link_id = ?", prev);
2021 	  else
2022 	      sql = sqlite3_mprintf ("%s link_id = ?", prev);
2023 	  comma = 1;
2024 	  sqlite3_free (prev);
2025 	  prev = sql;
2026       }
2027     if (upd_fields & LWN_COL_LINK_START_NODE)
2028       {
2029 	  if (comma)
2030 	      sql = sqlite3_mprintf ("%s, start_node = ?", prev);
2031 	  else
2032 	      sql = sqlite3_mprintf ("%s start_node = ?", prev);
2033 	  comma = 1;
2034 	  sqlite3_free (prev);
2035 	  prev = sql;
2036       }
2037     if (upd_fields & LWN_COL_LINK_END_NODE)
2038       {
2039 	  if (comma)
2040 	      sql = sqlite3_mprintf ("%s, end_node = ?", prev);
2041 	  else
2042 	      sql = sqlite3_mprintf ("%s end_node = ?", prev);
2043 	  comma = 1;
2044 	  sqlite3_free (prev);
2045 	  prev = sql;
2046       }
2047     if (upd_fields & LWN_COL_LINK_GEOM)
2048       {
2049 	  if (comma)
2050 	      sql = sqlite3_mprintf ("%s, geometry = ?", prev);
2051 	  else
2052 	      sql = sqlite3_mprintf ("%s geometry = ?", prev);
2053 	  comma = 1;
2054 	  sqlite3_free (prev);
2055 	  prev = sql;
2056       }
2057     sql = sqlite3_mprintf ("%s WHERE link_id = ?", prev);
2058     sqlite3_free (prev);
2059     ret =
2060 	sqlite3_prepare_v2 (accessor->db_handle, sql, strlen (sql), &stmt,
2061 			    NULL);
2062     sqlite3_free (sql);
2063     if (ret != SQLITE_OK)
2064       {
2065 	  char *msg = sqlite3_mprintf ("Prepare_updateLinksById error: \"%s\"",
2066 				       sqlite3_errmsg (accessor->db_handle));
2067 	  gaianet_set_last_error_msg (net, msg);
2068 	  sqlite3_free (msg);
2069 	  return -1;
2070       }
2071 
2072     for (i = 0; i < numlinks; i++)
2073       {
2074 	  /* parameter binding */
2075 	  int icol = 1;
2076 	  const LWN_LINK *upd_link = links + i;
2077 	  sqlite3_reset (stmt);
2078 	  sqlite3_clear_bindings (stmt);
2079 	  if (upd_fields & LWN_COL_LINK_LINK_ID)
2080 	    {
2081 		sqlite3_bind_int64 (stmt, icol, upd_link->link_id);
2082 		icol++;
2083 	    }
2084 	  if (upd_fields & LWN_COL_LINK_START_NODE)
2085 	    {
2086 		sqlite3_bind_int64 (stmt, icol, upd_link->start_node);
2087 		icol++;
2088 	    }
2089 	  if (upd_fields & LWN_COL_LINK_END_NODE)
2090 	    {
2091 		sqlite3_bind_int64 (stmt, icol, upd_link->end_node);
2092 		icol++;
2093 	    }
2094 	  if (upd_fields & LWN_COL_LINK_GEOM)
2095 	    {
2096 		if (upd_link->geom == NULL)
2097 		    sqlite3_bind_null (stmt, icol);
2098 		else
2099 		  {
2100 		      /* transforming the LWN_LINE into a Geometry-Linestring */
2101 		      geom =
2102 			  do_convert_lwnline_to_geom (upd_link->geom,
2103 						      accessor->srid);
2104 		      gaiaToSpatiaLiteBlobWkbEx2 (geom, &p_blob, &n_bytes,
2105 						  gpkg_mode, tiny_point);
2106 		      gaiaFreeGeomColl (geom);
2107 		      sqlite3_bind_blob (stmt, icol, p_blob, n_bytes, free);
2108 		  }
2109 		icol++;
2110 	    }
2111 	  sqlite3_bind_int64 (stmt, icol, upd_link->link_id);
2112 	  ret = sqlite3_step (stmt);
2113 	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
2114 	      changed += sqlite3_changes (accessor->db_handle);
2115 	  else
2116 	    {
2117 		char *msg =
2118 		    sqlite3_mprintf ("netcallback_updateLinksById: \"%s\"",
2119 				     sqlite3_errmsg (accessor->db_handle));
2120 		gaianet_set_last_error_msg (net, msg);
2121 		sqlite3_free (msg);
2122 		goto error;
2123 	    }
2124       }
2125     sqlite3_finalize (stmt);
2126     return changed;
2127 
2128   error:
2129     sqlite3_finalize (stmt);
2130     return -1;
2131 }
2132 
2133 LWN_LINK *
netcallback_getLinkById(const LWN_BE_NETWORK * lwn_net,const LWN_ELEMID * ids,int * numelems,int fields)2134 netcallback_getLinkById (const LWN_BE_NETWORK * lwn_net,
2135 			 const LWN_ELEMID * ids, int *numelems, int fields)
2136 {
2137 /* callback function: getLinkById */
2138     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
2139     struct gaia_network *accessor = (struct gaia_network *) net;
2140     sqlite3_stmt *stmt_aux = NULL;
2141     int ret;
2142     int i;
2143     char *sql;
2144     struct net_links_list *list = NULL;
2145     LWN_LINK *result = NULL;
2146     if (accessor == NULL)
2147       {
2148 	  *numelems = -1;
2149 	  return NULL;
2150       }
2151 
2152     /* preparing the SQL statement */
2153     sql = do_prepare_read_link (accessor->network_name, fields);
2154     ret =
2155 	sqlite3_prepare_v2 (accessor->db_handle, sql, strlen (sql), &stmt_aux,
2156 			    NULL);
2157     sqlite3_free (sql);
2158     if (ret != SQLITE_OK)
2159       {
2160 	  char *msg = sqlite3_mprintf ("Prepare_getLinkById AUX error: \"%s\"",
2161 				       sqlite3_errmsg (accessor->db_handle));
2162 	  gaianet_set_last_error_msg (net, msg);
2163 	  sqlite3_free (msg);
2164 	  *numelems = -1;
2165 	  return NULL;
2166       }
2167 
2168     list = create_links_list ();
2169     for (i = 0; i < *numelems; i++)
2170       {
2171 	  char *msg;
2172 	  if (!do_read_link
2173 	      (stmt_aux, list, *(ids + i), fields, "netcallback_getLinkById",
2174 	       &msg))
2175 	    {
2176 		gaianet_set_last_error_msg (net, msg);
2177 		sqlite3_free (msg);
2178 		goto error;
2179 	    }
2180       }
2181 
2182     if (list->count == 0)
2183       {
2184 	  /* no link was found */
2185 	  *numelems = list->count;
2186       }
2187     else
2188       {
2189 	  struct net_link *p_lnk;
2190 	  result = malloc (sizeof (LWN_LINK) * list->count);
2191 	  p_lnk = list->first;
2192 	  i = 0;
2193 	  while (p_lnk != NULL)
2194 	    {
2195 		LWN_LINK *lnk = result + i;
2196 		lnk->geom = NULL;
2197 		if (fields & LWN_COL_LINK_LINK_ID)
2198 		    lnk->link_id = p_lnk->link_id;
2199 		if (fields & LWN_COL_LINK_START_NODE)
2200 		    lnk->start_node = p_lnk->start_node;
2201 		if (fields & LWN_COL_LINK_END_NODE)
2202 		    lnk->end_node = p_lnk->end_node;
2203 		if (fields & LWN_COL_LINK_GEOM)
2204 		  {
2205 		      if (p_lnk->geom == NULL)
2206 			  lnk->geom = NULL;
2207 		      else
2208 			  lnk->geom =
2209 			      gaianet_convert_linestring_to_lwnline
2210 			      (p_lnk->geom, accessor->srid, accessor->has_z);
2211 		  }
2212 		i++;
2213 		p_lnk = p_lnk->next;
2214 	    }
2215 	  *numelems = list->count;
2216       }
2217     sqlite3_finalize (stmt_aux);
2218     destroy_links_list (list);
2219     return result;
2220 
2221   error:
2222     if (stmt_aux != NULL)
2223 	sqlite3_finalize (stmt_aux);
2224     if (list != NULL)
2225 	destroy_links_list (list);
2226     *numelems = -1;
2227     return NULL;
2228 }
2229 
2230 int
netcallback_deleteLinksById(const LWN_BE_NETWORK * lwn_net,const LWN_ELEMID * ids,int numelems)2231 netcallback_deleteLinksById (const LWN_BE_NETWORK * lwn_net,
2232 			     const LWN_ELEMID * ids, int numelems)
2233 {
2234 /* callback function: deleteLinksById */
2235     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
2236     struct gaia_network *accessor = (struct gaia_network *) net;
2237     sqlite3_stmt *stmt = NULL;
2238     int ret;
2239     int i;
2240     int changed = 0;
2241     if (accessor == NULL)
2242 	return -1;
2243 
2244     stmt = accessor->stmt_deleteLinksById;
2245     if (stmt == NULL)
2246 	return -1;
2247 
2248     for (i = 0; i < numelems; i++)
2249       {
2250 	  /* parameter binding */
2251 	  sqlite3_int64 id = *(ids + i);
2252 	  sqlite3_reset (stmt);
2253 	  sqlite3_clear_bindings (stmt);
2254 	  sqlite3_bind_int64 (stmt, 1, id);
2255 	  ret = sqlite3_step (stmt);
2256 	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
2257 	    {
2258 		changed += sqlite3_changes (accessor->db_handle);
2259 	    }
2260 	  else
2261 	    {
2262 		char *msg =
2263 		    sqlite3_mprintf ("netcallback_deleteLinksById: \"%s\"",
2264 				     sqlite3_errmsg (accessor->db_handle));
2265 		gaianet_set_last_error_msg (net, msg);
2266 		sqlite3_free (msg);
2267 		goto error;
2268 	    }
2269       }
2270     sqlite3_reset (stmt);
2271     return changed;
2272 
2273   error:
2274     sqlite3_reset (stmt);
2275     return -1;
2276 }
2277 
2278 int
netcallback_netGetSRID(const LWN_BE_NETWORK * lwn_net)2279 netcallback_netGetSRID (const LWN_BE_NETWORK * lwn_net)
2280 {
2281 /* callback function: netGetSRID */
2282     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
2283     struct gaia_network *accessor = (struct gaia_network *) net;
2284     if (accessor == NULL)
2285 	return -1;
2286 
2287     return accessor->srid;
2288 }
2289 
2290 int
netcallback_netHasZ(const LWN_BE_NETWORK * lwn_net)2291 netcallback_netHasZ (const LWN_BE_NETWORK * lwn_net)
2292 {
2293 /* callback function: netHasZ */
2294     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
2295     struct gaia_network *accessor = (struct gaia_network *) net;
2296     if (accessor == NULL)
2297 	return 0;
2298 
2299     return accessor->has_z;
2300 }
2301 
2302 int
netcallback_netIsSpatial(const LWN_BE_NETWORK * lwn_net)2303 netcallback_netIsSpatial (const LWN_BE_NETWORK * lwn_net)
2304 {
2305 /* callback function: netIsSpatial */
2306     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
2307     struct gaia_network *accessor = (struct gaia_network *) net;
2308     if (accessor == NULL)
2309 	return 0;
2310 
2311     return accessor->spatial;
2312 }
2313 
2314 int
netcallback_netAllowCoincident(const LWN_BE_NETWORK * lwn_net)2315 netcallback_netAllowCoincident (const LWN_BE_NETWORK * lwn_net)
2316 {
2317 /* callback function: netAllowCoincident */
2318     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
2319     struct gaia_network *accessor = (struct gaia_network *) net;
2320     if (accessor == NULL)
2321 	return 0;
2322 
2323     return accessor->allow_coincident;
2324 }
2325 
2326 const void *
netcallback_netGetGEOS(const LWN_BE_NETWORK * lwn_net)2327 netcallback_netGetGEOS (const LWN_BE_NETWORK * lwn_net)
2328 {
2329 /* callback function: netGetGEOS */
2330     const struct splite_internal_cache *cache;
2331     GaiaNetworkAccessorPtr net = (GaiaNetworkAccessorPtr) lwn_net;
2332     struct gaia_network *accessor = (struct gaia_network *) net;
2333     if (accessor == NULL)
2334 	return NULL;
2335     if (accessor->cache == NULL)
2336 	return NULL;
2337 
2338     cache = (const struct splite_internal_cache *) (accessor->cache);
2339     return cache->GEOS_handle;
2340 }
2341 
2342 #endif /* end RTTOPO conditionals */
2343