1 /*
2 
3  gaia_auxnet.c -- implementation of the Topology-Network module methods
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/gaia_topology.h>
63 #include <spatialite/gaiaaux.h>
64 
65 #include <spatialite.h>
66 #include <spatialite_private.h>
67 
68 #include <librttopo.h>
69 
70 #include <lwn_network.h>
71 
72 #include "network_private.h"
73 #include "topology_private.h"
74 
75 #ifdef _WIN32
76 #define strcasecmp	_stricmp
77 #endif /* not WIN32 */
78 
79 #define GAIA_UNUSED() if (argc || argv) argc = argc;
80 
81 SPATIALITE_PRIVATE void
free_internal_cache_networks(void * firstNetwork)82 free_internal_cache_networks (void *firstNetwork)
83 {
84 /* destroying all Networks registered into the Internal Connection Cache */
85     struct gaia_network *p_net = (struct gaia_network *) firstNetwork;
86     struct gaia_network *p_net_n;
87 
88     while (p_net != NULL)
89       {
90 	  p_net_n = p_net->next;
91 	  gaiaNetworkDestroy ((GaiaNetworkAccessorPtr) p_net);
92 	  p_net = p_net_n;
93       }
94 }
95 
96 SPATIALITE_PRIVATE void
drop_networks_triggers(void * sqlite_handle)97 drop_networks_triggers (void *sqlite_handle)
98 {
99 /* dropping all "networks" triggers */
100     char *sql;
101     int ret;
102     char *err_msg = NULL;
103     char **results;
104     int rows;
105     int columns;
106     int i;
107     sqlite3 *sqlite = (sqlite3 *) sqlite_handle;
108 
109 /* checking for existing tables */
110     sql =
111 	"SELECT name FROM sqlite_master WHERE type = 'trigger' AND tbl_name = 'networks'";
112     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &err_msg);
113     if (ret != SQLITE_OK)
114       {
115 	  spatialite_e ("SQL error: %s\n", err_msg);
116 	  sqlite3_free (err_msg);
117 	  return;
118       }
119     for (i = 1; i <= rows; i++)
120       {
121 	  const char *name = results[(i * columns) + 0];
122 	  sql = sqlite3_mprintf ("DROP TRIGGER %s", name);
123 	  ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
124 	  if (ret != SQLITE_OK)
125 	    {
126 		spatialite_e ("SQL error: %s\n", err_msg);
127 		sqlite3_free (err_msg);
128 		return;
129 	    }
130 	  sqlite3_free (sql);
131       }
132     sqlite3_free_table (results);
133 }
134 
135 SPATIALITE_PRIVATE int
do_create_networks_triggers(void * sqlite_handle)136 do_create_networks_triggers (void *sqlite_handle)
137 {
138 /* attempting to create the Networks triggers */
139     const char *sql;
140     char *err_msg = NULL;
141     int ret;
142     char **results;
143     int rows;
144     int columns;
145     int i;
146     sqlite3 *handle = (sqlite3 *) sqlite_handle;
147     int ok_networks = 0;
148 
149 /* checking for existing networks */
150     sql =
151 	"SELECT tbl_name FROM sqlite_master WHERE type = 'table' AND tbl_name = 'networks'";
152     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, &err_msg);
153     if (ret != SQLITE_OK)
154       {
155 	  spatialite_e ("SQL error: %s\n", err_msg);
156 	  sqlite3_free (err_msg);
157 	  return 0;
158       }
159     for (i = 1; i <= rows; i++)
160       {
161 	  const char *name = results[(i * columns) + 0];
162 	  if (strcasecmp (name, "networks") == 0)
163 	      ok_networks = 1;
164       }
165     sqlite3_free_table (results);
166 
167     if (ok_networks)
168       {
169 	  /* creating Networks triggers */
170 	  sql = "CREATE TRIGGER IF NOT EXISTS network_name_insert\n"
171 	      "BEFORE INSERT ON 'networks'\nFOR EACH ROW BEGIN\n"
172 	      "SELECT RAISE(ABORT,'insert on networks violates constraint: "
173 	      "network_name value must not contain a single quote')\n"
174 	      "WHERE NEW.network_name LIKE ('%''%');\n"
175 	      "SELECT RAISE(ABORT,'insert on networks violates constraint: "
176 	      "network_name value must not contain a double quote')\n"
177 	      "WHERE NEW.network_name LIKE ('%\"%');\n"
178 	      "SELECT RAISE(ABORT,'insert on networks violates constraint: "
179 	      "network_name value must be lower case')\n"
180 	      "WHERE NEW.network_name <> lower(NEW.network_name);\nEND";
181 	  ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
182 	  if (ret != SQLITE_OK)
183 	    {
184 		spatialite_e ("SQL error: %s\n", err_msg);
185 		sqlite3_free (err_msg);
186 		return 0;
187 	    }
188 	  sql = "CREATE TRIGGER IF NOT EXISTS network_name_update\n"
189 	      "BEFORE UPDATE OF 'network_name' ON 'networks'\nFOR EACH ROW BEGIN\n"
190 	      "SELECT RAISE(ABORT,'update on networks violates constraint: "
191 	      "network_name value must not contain a single quote')\n"
192 	      "WHERE NEW.network_name LIKE ('%''%');\n"
193 	      "SELECT RAISE(ABORT,'update on networks violates constraint: "
194 	      "network_name value must not contain a double quote')\n"
195 	      "WHERE NEW.network_name LIKE ('%\"%');\n"
196 	      "SELECT RAISE(ABORT,'update on networks violates constraint: "
197 	      "network_name value must be lower case')\n"
198 	      "WHERE NEW.network_name <> lower(NEW.network_name);\nEND";
199 	  ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
200 	  if (ret != SQLITE_OK)
201 	    {
202 		spatialite_e ("SQL error: %s\n", err_msg);
203 		sqlite3_free (err_msg);
204 		return 0;
205 	    }
206       }
207 
208     return 1;
209 }
210 
211 SPATIALITE_PRIVATE int
do_create_networks(void * sqlite_handle)212 do_create_networks (void *sqlite_handle)
213 {
214 /* attempting to create the Networks table (if not already existing) */
215     const char *sql;
216     char *err_msg = NULL;
217     int ret;
218     sqlite3 *handle = (sqlite3 *) sqlite_handle;
219 
220     sql = "CREATE TABLE IF NOT EXISTS networks (\n"
221 	"\tnetwork_name TEXT NOT NULL PRIMARY KEY,\n"
222 	"\tspatial INTEGER NOT NULL,\n"
223 	"\tsrid INTEGER NOT NULL,\n"
224 	"\thas_z INTEGER NOT NULL,\n"
225 	"\tallow_coincident INTEGER NOT NULL,\n"
226 	"\tnext_node_id INTEGER NOT NULL DEFAULT 1,\n"
227 	"\tnext_link_id INTEGER NOT NULL DEFAULT 1,\n"
228 	"\tCONSTRAINT net_srid_fk FOREIGN KEY (srid) "
229 	"REFERENCES spatial_ref_sys (srid))";
230     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
231     if (ret != SQLITE_OK)
232       {
233 	  spatialite_e ("CREATE TABLE networks - error: %s\n", err_msg);
234 	  sqlite3_free (err_msg);
235 	  return 0;
236       }
237 
238     if (!do_create_networks_triggers (handle))
239 	return 0;
240     return 1;
241 }
242 
243 static int
check_new_network(sqlite3 * handle,const char * network_name)244 check_new_network (sqlite3 * handle, const char *network_name)
245 {
246 /* testing if some already defined DB object forbids creating the new Network */
247     char *sql;
248     char *prev;
249     char *table;
250     int ret;
251     int i;
252     char **results;
253     int rows;
254     int columns;
255     const char *value;
256     int error = 0;
257 
258 /* testing if the same Network is already defined */
259     sql = sqlite3_mprintf ("SELECT Count(*) FROM MAIN.networks WHERE "
260 			   "Lower(network_name) = Lower(%Q)", network_name);
261     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
262     sqlite3_free (sql);
263     if (ret != SQLITE_OK)
264 	return 0;
265     if (rows < 1)
266 	;
267     else
268       {
269 	  for (i = 1; i <= rows; i++)
270 	    {
271 		value = results[(i * columns) + 0];
272 		if (atoi (value) != 0)
273 		    error = 1;
274 	    }
275       }
276     sqlite3_free_table (results);
277     if (error)
278 	return 0;
279 
280 /* testing if some table/geom is already defined in geometry_columns */
281     sql = sqlite3_mprintf ("SELECT Count(*) FROM MAIN.geometry_columns WHERE");
282     prev = sql;
283     table = sqlite3_mprintf ("%s_node", network_name);
284     sql =
285 	sqlite3_mprintf
286 	("%s (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'geometry')",
287 	 prev, table);
288     sqlite3_free (table);
289     sqlite3_free (prev);
290     prev = sql;
291     table = sqlite3_mprintf ("%s_link", network_name);
292     sql =
293 	sqlite3_mprintf
294 	("%s OR (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'geometry')",
295 	 prev, table);
296     sqlite3_free (table);
297     sqlite3_free (prev);
298     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
299     sqlite3_free (sql);
300     if (ret != SQLITE_OK)
301 	return 0;
302     if (rows < 1)
303 	;
304     else
305       {
306 	  for (i = 1; i <= rows; i++)
307 	    {
308 		value = results[(i * columns) + 0];
309 		if (atoi (value) != 0)
310 		    error = 1;
311 	    }
312       }
313     sqlite3_free_table (results);
314     if (error)
315 	return 0;
316 
317 /* testing if some table is already defined */
318     sql = sqlite3_mprintf ("SELECT Count(*) FROM sqlite_master WHERE");
319     prev = sql;
320     table = sqlite3_mprintf ("%s_node", network_name);
321     sql = sqlite3_mprintf ("%s Lower(name) = Lower(%Q)", prev, table);
322     sqlite3_free (table);
323     sqlite3_free (prev);
324     prev = sql;
325     table = sqlite3_mprintf ("%s_link", network_name);
326     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
327     sqlite3_free (table);
328     sqlite3_free (prev);
329     prev = sql;
330     table = sqlite3_mprintf ("idx_%s_node_geometry", network_name);
331     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
332     sqlite3_free (table);
333     sqlite3_free (prev);
334     prev = sql;
335     table = sqlite3_mprintf ("idx_%s_link_geometry", network_name);
336     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
337     sqlite3_free (table);
338     sqlite3_free (prev);
339     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
340     sqlite3_free (sql);
341     if (ret != SQLITE_OK)
342 	return 0;
343     if (rows < 1)
344 	;
345     else
346       {
347 	  for (i = 1; i <= rows; i++)
348 	    {
349 		value = results[(i * columns) + 0];
350 		if (atoi (value) != 0)
351 		    error = 1;
352 	    }
353       }
354     sqlite3_free_table (results);
355     if (error)
356 	return 0;
357 
358     return 1;
359 }
360 
361 NETWORK_PRIVATE LWN_LINE *
gaianet_convert_linestring_to_lwnline(gaiaLinestringPtr ln,int srid,int has_z)362 gaianet_convert_linestring_to_lwnline (gaiaLinestringPtr ln, int srid,
363 				       int has_z)
364 {
365 /* converting a Linestring into an LWN_LINE */
366     int iv;
367     LWN_LINE *line = lwn_alloc_line (ln->Points, srid, has_z);
368     for (iv = 0; iv < ln->Points; iv++)
369       {
370 	  double x;
371 	  double y;
372 	  double z;
373 	  double m;
374 	  if (ln->DimensionModel == GAIA_XY_Z)
375 	    {
376 		gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z);
377 	    }
378 	  else if (ln->DimensionModel == GAIA_XY_M)
379 	    {
380 		gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m);
381 	    }
382 	  else if (ln->DimensionModel == GAIA_XY_Z_M)
383 	    {
384 		gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m);
385 	    }
386 	  else
387 	    {
388 		gaiaGetPoint (ln->Coords, iv, &x, &y);
389 	    }
390 	  line->x[iv] = x;
391 	  line->y[iv] = y;
392 	  if (has_z)
393 	      line->z[iv] = z;
394       }
395     return line;
396 }
397 
398 static int
do_create_node(sqlite3 * handle,const char * network_name,int srid,int has_z)399 do_create_node (sqlite3 * handle, const char *network_name, int srid, int has_z)
400 {
401 /* attempting to create the Network Node table */
402     char *sql;
403     char *table;
404     char *xtable;
405     char *trigger;
406     char *xtrigger;
407     char *err_msg = NULL;
408     int ret;
409 
410 /* creating the main table */
411     table = sqlite3_mprintf ("%s_node", network_name);
412     xtable = gaiaDoubleQuotedSql (table);
413     sqlite3_free (table);
414     sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (\n"
415 			   "\tnode_id INTEGER PRIMARY KEY AUTOINCREMENT)",
416 			   xtable);
417     free (xtable);
418     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
419     sqlite3_free (sql);
420     if (ret != SQLITE_OK)
421       {
422 	  spatialite_e ("CREATE TABLE network-NODE - error: %s\n", err_msg);
423 	  sqlite3_free (err_msg);
424 	  return 0;
425       }
426 
427 /* adding the "next_node_ins" trigger */
428     trigger = sqlite3_mprintf ("%s_node_next_ins", network_name);
429     xtrigger = gaiaDoubleQuotedSql (trigger);
430     sqlite3_free (trigger);
431     table = sqlite3_mprintf ("%s_node", network_name);
432     xtable = gaiaDoubleQuotedSql (table);
433     sqlite3_free (table);
434     sql = sqlite3_mprintf ("CREATE TRIGGER \"%s\" AFTER INSERT ON \"%s\"\n"
435 			   "FOR EACH ROW BEGIN\n"
436 			   "\tUPDATE networks SET next_node_id = NEW.node_id + 1 "
437 			   "WHERE Lower(network_name) = Lower(%Q) AND next_node_id < NEW.node_id + 1;\n"
438 			   "END", xtrigger, xtable, network_name);
439     free (xtrigger);
440     free (xtable);
441     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
442     sqlite3_free (sql);
443     if (ret != SQLITE_OK)
444       {
445 	  spatialite_e
446 	      ("CREATE TRIGGER network-NODE next INSERT - error: %s\n",
447 	       err_msg);
448 	  sqlite3_free (err_msg);
449 	  return 0;
450       }
451 
452 /* adding the "next_node_upd" trigger */
453     trigger = sqlite3_mprintf ("%s_node_next_upd", network_name);
454     xtrigger = gaiaDoubleQuotedSql (trigger);
455     sqlite3_free (trigger);
456     table = sqlite3_mprintf ("%s_node", network_name);
457     xtable = gaiaDoubleQuotedSql (table);
458     sqlite3_free (table);
459     sql =
460 	sqlite3_mprintf
461 	("CREATE TRIGGER \"%s\" AFTER UPDATE OF node_id ON \"%s\"\n"
462 	 "FOR EACH ROW BEGIN\n"
463 	 "\tUPDATE networks SET next_node_id = NEW.node_id + 1 "
464 	 "WHERE Lower(network_name) = Lower(%Q) AND next_node_id < NEW.node_id + 1;\n"
465 	 "END", xtrigger, xtable, network_name);
466     free (xtrigger);
467     free (xtable);
468     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
469     sqlite3_free (sql);
470     if (ret != SQLITE_OK)
471       {
472 	  spatialite_e
473 	      ("CREATE TRIGGER network-NODE next UPDATE - error: %s\n",
474 	       err_msg);
475 	  sqlite3_free (err_msg);
476 	  return 0;
477       }
478 
479 /* creating the Node Geometry */
480     table = sqlite3_mprintf ("%s_node", network_name);
481     sql =
482 	sqlite3_mprintf
483 	("SELECT AddGeometryColumn(%Q, 'geometry', %d, 'POINT', %Q)", table,
484 	 srid, has_z ? "XYZ" : "XY");
485     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
486     sqlite3_free (table);
487     sqlite3_free (sql);
488     if (ret != SQLITE_OK)
489       {
490 	  spatialite_e
491 	      ("AddGeometryColumn network-NODE - error: %s\n", err_msg);
492 	  sqlite3_free (err_msg);
493 	  return 0;
494       }
495 
496 /* creating a Spatial Index supporting Node Geometry */
497     table = sqlite3_mprintf ("%s_node", network_name);
498     sql = sqlite3_mprintf ("SELECT CreateSpatialIndex(%Q, 'geometry')", table);
499     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
500     sqlite3_free (table);
501     sqlite3_free (sql);
502     if (ret != SQLITE_OK)
503       {
504 	  spatialite_e
505 	      ("CreateSpatialIndex network-NODE - error: %s\n", err_msg);
506 	  sqlite3_free (err_msg);
507 	  return 0;
508       }
509 
510     return 1;
511 }
512 
513 static int
do_create_link(sqlite3 * handle,const char * network_name,int srid,int has_z)514 do_create_link (sqlite3 * handle, const char *network_name, int srid, int has_z)
515 {
516 /* attempting to create the Network Link table */
517     char *sql;
518     char *table;
519     char *xtable;
520     char *xconstraint1;
521     char *xconstraint2;
522     char *xnodes;
523     char *trigger;
524     char *xtrigger;
525     char *err_msg = NULL;
526     int ret;
527 
528 /* creating the main table */
529     table = sqlite3_mprintf ("%s_link", network_name);
530     xtable = gaiaDoubleQuotedSql (table);
531     sqlite3_free (table);
532     table = sqlite3_mprintf ("%s_link_node_start_fk", network_name);
533     xconstraint1 = gaiaDoubleQuotedSql (table);
534     sqlite3_free (table);
535     table = sqlite3_mprintf ("%s_link_node_end_fk", network_name);
536     xconstraint2 = gaiaDoubleQuotedSql (table);
537     sqlite3_free (table);
538     table = sqlite3_mprintf ("%s_node", network_name);
539     xnodes = gaiaDoubleQuotedSql (table);
540     sqlite3_free (table);
541     sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (\n"
542 			   "\tlink_id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
543 			   "\tstart_node INTEGER NOT NULL,\n"
544 			   "\tend_node INTEGER NOT NULL,\n"
545 			   "\ttimestamp DATETIME,\n"
546 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (start_node) "
547 			   "REFERENCES \"%s\" (node_id),\n"
548 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (end_node) "
549 			   "REFERENCES \"%s\" (node_id))",
550 			   xtable, xconstraint1, xnodes, xconstraint2, xnodes);
551     free (xtable);
552     free (xconstraint1);
553     free (xconstraint2);
554     free (xnodes);
555     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
556     sqlite3_free (sql);
557     if (ret != SQLITE_OK)
558       {
559 	  spatialite_e ("CREATE TABLE network-LINK - error: %s\n", err_msg);
560 	  sqlite3_free (err_msg);
561 	  return 0;
562       }
563 
564 /* adding the "next_link_ins" trigger */
565     trigger = sqlite3_mprintf ("%s_link_next_ins", network_name);
566     xtrigger = gaiaDoubleQuotedSql (trigger);
567     sqlite3_free (trigger);
568     table = sqlite3_mprintf ("%s_link", network_name);
569     xtable = gaiaDoubleQuotedSql (table);
570     sqlite3_free (table);
571     sql = sqlite3_mprintf ("CREATE TRIGGER \"%s\" AFTER INSERT ON \"%s\"\n"
572 			   "FOR EACH ROW BEGIN\n"
573 			   "\tUPDATE networks SET next_link_id = NEW.link_id + 1 "
574 			   "WHERE Lower(network_name) = Lower(%Q) AND next_link_id < NEW.link_id + 1;\n"
575 			   "\tUPDATE \"%s\" SET timestamp = strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ', 'now') "
576 			   "WHERE link_id = NEW.link_id;"
577 			   "END", xtrigger, xtable, network_name, xtable);
578     free (xtrigger);
579     free (xtable);
580     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
581     sqlite3_free (sql);
582     if (ret != SQLITE_OK)
583       {
584 	  spatialite_e
585 	      ("CREATE TRIGGER network-LINK next INSERT - error: %s\n",
586 	       err_msg);
587 	  sqlite3_free (err_msg);
588 	  return 0;
589       }
590 
591 /* adding the "link_update" trigger */
592     trigger = sqlite3_mprintf ("%s_link_update", network_name);
593     xtrigger = gaiaDoubleQuotedSql (trigger);
594     sqlite3_free (trigger);
595     table = sqlite3_mprintf ("%s_link", network_name);
596     xtable = gaiaDoubleQuotedSql (table);
597     sqlite3_free (table);
598     sql = sqlite3_mprintf ("CREATE TRIGGER \"%s\" AFTER UPDATE ON \"%s\"\n"
599 			   "FOR EACH ROW BEGIN\n"
600 			   "\tUPDATE \"%s\" SET timestamp = strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ', 'now') "
601 			   "WHERE link_id = NEW.link_id;"
602 			   "END", xtrigger, xtable, xtable);
603     free (xtrigger);
604     free (xtable);
605     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
606     sqlite3_free (sql);
607     if (ret != SQLITE_OK)
608       {
609 	  spatialite_e
610 	      ("CREATE TRIGGER topology-LINK next INSERT - error: %s\n",
611 	       err_msg);
612 	  sqlite3_free (err_msg);
613 	  return 0;
614       }
615 
616 /* adding the "next_link_upd" trigger */
617     trigger = sqlite3_mprintf ("%s_link_next_upd", network_name);
618     xtrigger = gaiaDoubleQuotedSql (trigger);
619     sqlite3_free (trigger);
620     table = sqlite3_mprintf ("%s_link", network_name);
621     xtable = gaiaDoubleQuotedSql (table);
622     sqlite3_free (table);
623     sql =
624 	sqlite3_mprintf
625 	("CREATE TRIGGER \"%s\" AFTER UPDATE OF link_id ON \"%s\"\n"
626 	 "FOR EACH ROW BEGIN\n"
627 	 "\tUPDATE networks SET next_link_id = NEW.link_id + 1 "
628 	 "WHERE Lower(network_name) = Lower(%Q) AND next_link_id < NEW.link_id + 1;\n"
629 	 "END", xtrigger, xtable, network_name);
630     free (xtrigger);
631     free (xtable);
632     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
633     sqlite3_free (sql);
634     if (ret != SQLITE_OK)
635       {
636 	  spatialite_e
637 	      ("CREATE TRIGGER network-LINK next UPDATE - error: %s\n",
638 	       err_msg);
639 	  sqlite3_free (err_msg);
640 	  return 0;
641       }
642 
643 /* creating the Link Geometry */
644     table = sqlite3_mprintf ("%s_link", network_name);
645     sql =
646 	sqlite3_mprintf
647 	("SELECT AddGeometryColumn(%Q, 'geometry', %d, 'LINESTRING', %Q)",
648 	 table, srid, has_z ? "XYZ" : "XY");
649     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
650     sqlite3_free (table);
651     sqlite3_free (sql);
652     if (ret != SQLITE_OK)
653       {
654 	  spatialite_e
655 	      ("AddGeometryColumn network-LINK - error: %s\n", err_msg);
656 	  sqlite3_free (err_msg);
657 	  return 0;
658       }
659 
660 /* creating a Spatial Index supporting Link Geometry */
661     table = sqlite3_mprintf ("%s_link", network_name);
662     sql = sqlite3_mprintf ("SELECT CreateSpatialIndex(%Q, 'geometry')", table);
663     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
664     sqlite3_free (table);
665     sqlite3_free (sql);
666     if (ret != SQLITE_OK)
667       {
668 	  spatialite_e
669 	      ("CreateSpatialIndex network-LINK - error: %s\n", err_msg);
670 	  sqlite3_free (err_msg);
671 	  return 0;
672       }
673 
674 /* creating an Index supporting "start_node" */
675     table = sqlite3_mprintf ("%s_link", network_name);
676     xtable = gaiaDoubleQuotedSql (table);
677     sqlite3_free (table);
678     table = sqlite3_mprintf ("idx_%s_start_node", network_name);
679     xconstraint1 = gaiaDoubleQuotedSql (table);
680     sqlite3_free (table);
681     sql =
682 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (start_node)",
683 			 xconstraint1, xtable);
684     free (xtable);
685     free (xconstraint1);
686     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
687     sqlite3_free (sql);
688     if (ret != SQLITE_OK)
689       {
690 	  spatialite_e ("CREATE INDEX link-startnode - error: %s\n", err_msg);
691 	  sqlite3_free (err_msg);
692 	  return 0;
693       }
694 
695 /* creating an Index supporting "end_node" */
696     table = sqlite3_mprintf ("%s_link", network_name);
697     xtable = gaiaDoubleQuotedSql (table);
698     sqlite3_free (table);
699     table = sqlite3_mprintf ("idx_%s_end_node", network_name);
700     xconstraint1 = gaiaDoubleQuotedSql (table);
701     sqlite3_free (table);
702     sql =
703 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (end_node)",
704 			 xconstraint1, xtable);
705     free (xtable);
706     free (xconstraint1);
707     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
708     sqlite3_free (sql);
709     if (ret != SQLITE_OK)
710       {
711 	  spatialite_e ("CREATE INDEX link-endnode - error: %s\n", err_msg);
712 	  sqlite3_free (err_msg);
713 	  return 0;
714       }
715 
716 /* creating an Index supporting "timestamp" */
717     table = sqlite3_mprintf ("%s_link", network_name);
718     xtable = gaiaDoubleQuotedSql (table);
719     sqlite3_free (table);
720     table = sqlite3_mprintf ("idx_%s_timestamp", network_name);
721     xconstraint1 = gaiaDoubleQuotedSql (table);
722     sqlite3_free (table);
723     sql =
724 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (timestamp)",
725 			 xconstraint1, xtable);
726     free (xtable);
727     free (xconstraint1);
728     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
729     sqlite3_free (sql);
730     if (ret != SQLITE_OK)
731       {
732 	  spatialite_e ("CREATE INDEX link-timestamps - error: %s\n", err_msg);
733 	  sqlite3_free (err_msg);
734 	  return 0;
735       }
736 
737     return 1;
738 }
739 
740 static int
do_create_seeds(sqlite3 * handle,const char * network_name,int srid,int has_z)741 do_create_seeds (sqlite3 * handle, const char *network_name, int srid,
742 		 int has_z)
743 {
744 /* attempting to create the Network Seeds table */
745     char *sql;
746     char *table;
747     char *xtable;
748     char *xconstraint;
749     char *xlinks;
750     char *trigger;
751     char *xtrigger;
752     char *err_msg = NULL;
753     int ret;
754 
755 /* creating the main table */
756     table = sqlite3_mprintf ("%s_seeds", network_name);
757     xtable = gaiaDoubleQuotedSql (table);
758     sqlite3_free (table);
759     table = sqlite3_mprintf ("%s_seeds_link_fk", network_name);
760     xconstraint = gaiaDoubleQuotedSql (table);
761     sqlite3_free (table);
762     table = sqlite3_mprintf ("%s_link", network_name);
763     xlinks = gaiaDoubleQuotedSql (table);
764     sqlite3_free (table);
765     sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (\n"
766 			   "\tseed_id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
767 			   "\tlink_id INTEGER NOT NULL,\n"
768 			   "\ttimestamp DATETIME,\n"
769 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (link_id) "
770 			   "REFERENCES \"%s\" (link_id) ON DELETE CASCADE)",
771 			   xtable, xconstraint, xlinks);
772     free (xtable);
773     free (xconstraint);
774     free (xlinks);
775     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
776     sqlite3_free (sql);
777     if (ret != SQLITE_OK)
778       {
779 	  spatialite_e ("CREATE TABLE network-SEEDS - error: %s\n", err_msg);
780 	  sqlite3_free (err_msg);
781 	  return 0;
782       }
783 
784 /* adding the "seeds_ins" trigger */
785     trigger = sqlite3_mprintf ("%s_seeds_ins", network_name);
786     xtrigger = gaiaDoubleQuotedSql (trigger);
787     sqlite3_free (trigger);
788     table = sqlite3_mprintf ("%s_seeds", network_name);
789     xtable = gaiaDoubleQuotedSql (table);
790     sqlite3_free (table);
791     sql = sqlite3_mprintf ("CREATE TRIGGER \"%s\" AFTER INSERT ON \"%s\"\n"
792 			   "FOR EACH ROW BEGIN\n"
793 			   "\tUPDATE \"%s\" SET timestamp = strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ', 'now') "
794 			   "WHERE seed_id = NEW.seed_id;"
795 			   "END", xtrigger, xtable, xtable);
796     free (xtrigger);
797     free (xtable);
798     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
799     sqlite3_free (sql);
800     if (ret != SQLITE_OK)
801       {
802 	  spatialite_e
803 	      ("CREATE TRIGGER network-SEEDS next INSERT - error: %s\n",
804 	       err_msg);
805 	  sqlite3_free (err_msg);
806 	  return 0;
807       }
808 
809 /* adding the "seeds_update" trigger */
810     trigger = sqlite3_mprintf ("%s_seeds_update", network_name);
811     xtrigger = gaiaDoubleQuotedSql (trigger);
812     sqlite3_free (trigger);
813     table = sqlite3_mprintf ("%s_seeds", network_name);
814     xtable = gaiaDoubleQuotedSql (table);
815     sqlite3_free (table);
816     sql = sqlite3_mprintf ("CREATE TRIGGER \"%s\" AFTER UPDATE ON \"%s\"\n"
817 			   "FOR EACH ROW BEGIN\n"
818 			   "\tUPDATE \"%s\" SET timestamp = strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ', 'now') "
819 			   "WHERE seed_id = NEW.seed_id;"
820 			   "END", xtrigger, xtable, xtable);
821     free (xtrigger);
822     free (xtable);
823     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
824     sqlite3_free (sql);
825     if (ret != SQLITE_OK)
826       {
827 	  spatialite_e
828 	      ("CREATE TRIGGER network-SEED next INSERT - error: %s\n",
829 	       err_msg);
830 	  sqlite3_free (err_msg);
831 	  return 0;
832       }
833 
834 /* creating the Seeds Geometry */
835     table = sqlite3_mprintf ("%s_seeds", network_name);
836     sql =
837 	sqlite3_mprintf
838 	("SELECT AddGeometryColumn(%Q, 'geometry', %d, 'POINT', %Q, 1)",
839 	 table, srid, has_z ? "XYZ" : "XY");
840     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
841     sqlite3_free (table);
842     sqlite3_free (sql);
843     if (ret != SQLITE_OK)
844       {
845 	  spatialite_e
846 	      ("AddGeometryColumn network-SEEDS - error: %s\n", err_msg);
847 	  sqlite3_free (err_msg);
848 	  return 0;
849       }
850 
851 /* creating a Spatial Index supporting Seeds Geometry */
852     table = sqlite3_mprintf ("%s_seeds", network_name);
853     sql = sqlite3_mprintf ("SELECT CreateSpatialIndex(%Q, 'geometry')", table);
854     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
855     sqlite3_free (table);
856     sqlite3_free (sql);
857     if (ret != SQLITE_OK)
858       {
859 	  spatialite_e
860 	      ("CreateSpatialIndex network-SEEDS - error: %s\n", err_msg);
861 	  sqlite3_free (err_msg);
862 	  return 0;
863       }
864 
865 /* creating an Index supporting "link_id" */
866     table = sqlite3_mprintf ("%s_seeds", network_name);
867     xtable = gaiaDoubleQuotedSql (table);
868     sqlite3_free (table);
869     table = sqlite3_mprintf ("idx_%s_link", network_name);
870     xconstraint = gaiaDoubleQuotedSql (table);
871     sqlite3_free (table);
872     sql =
873 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (link_id)",
874 			 xconstraint, xtable);
875     free (xtable);
876     free (xconstraint);
877     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
878     sqlite3_free (sql);
879     if (ret != SQLITE_OK)
880       {
881 	  spatialite_e ("CREATE INDEX seeds-link - error: %s\n", err_msg);
882 	  sqlite3_free (err_msg);
883 	  return 0;
884       }
885 
886 /* creating an Index supporting "timestamp" */
887     table = sqlite3_mprintf ("%s_seeds", network_name);
888     xtable = gaiaDoubleQuotedSql (table);
889     sqlite3_free (table);
890     table = sqlite3_mprintf ("idx_%s_seeds_timestamp", network_name);
891     xconstraint = gaiaDoubleQuotedSql (table);
892     sqlite3_free (table);
893     sql =
894 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (timestamp)",
895 			 xconstraint, xtable);
896     free (xtable);
897     free (xconstraint);
898     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
899     sqlite3_free (sql);
900     if (ret != SQLITE_OK)
901       {
902 	  spatialite_e ("CREATE INDEX seeds-timestamps - error: %s\n", err_msg);
903 	  sqlite3_free (err_msg);
904 	  return 0;
905       }
906 
907     return 1;
908 }
909 
910 GAIANET_DECLARE int
gaiaNetworkCreate(sqlite3 * handle,const char * network_name,int spatial,int srid,int has_z,int allow_coincident)911 gaiaNetworkCreate (sqlite3 * handle, const char *network_name, int spatial,
912 		   int srid, int has_z, int allow_coincident)
913 {
914 /* attempting to create a new Network */
915     int ret;
916     char *sql;
917 
918 /* creating the Networks table (just in case) */
919     if (!do_create_networks (handle))
920 	return 0;
921 
922 /* testing for forbidding objects */
923     if (!check_new_network (handle, network_name))
924 	return 0;
925 
926 /* creating the Network own Tables */
927     if (!do_create_node (handle, network_name, srid, has_z))
928 	goto error;
929     if (!do_create_link (handle, network_name, srid, has_z))
930 	goto error;
931     if (!do_create_seeds (handle, network_name, srid, has_z))
932 	goto error;
933 
934 /* registering the Network */
935     sql = sqlite3_mprintf ("INSERT INTO MAIN.networks (network_name, "
936 			   "spatial, srid, has_z, allow_coincident) VALUES (Lower(%Q), %d, %d, %d, %d)",
937 			   network_name, spatial, srid, has_z,
938 			   allow_coincident);
939     ret = sqlite3_exec (handle, sql, NULL, NULL, NULL);
940     sqlite3_free (sql);
941     if (ret != SQLITE_OK)
942 	goto error;
943 
944     return 1;
945 
946   error:
947     return 0;
948 }
949 
950 static int
check_existing_network(sqlite3 * handle,const char * network_name,int full_check)951 check_existing_network (sqlite3 * handle, const char *network_name,
952 			int full_check)
953 {
954 /* testing if a Network is already defined */
955     char *sql;
956     char *prev;
957     char *table;
958     int ret;
959     int i;
960     char **results;
961     int rows;
962     int columns;
963     const char *value;
964     int error = 0;
965 
966 /* testing if the Network is already defined */
967     sql = sqlite3_mprintf ("SELECT Count(*) FROM MAIN.networks WHERE "
968 			   "Lower(network_name) = Lower(%Q)", network_name);
969     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
970     sqlite3_free (sql);
971     if (ret != SQLITE_OK)
972 	return 0;
973     if (rows < 1)
974 	;
975     else
976       {
977 	  for (i = 1; i <= rows; i++)
978 	    {
979 		value = results[(i * columns) + 0];
980 		if (atoi (value) != 1)
981 		    error = 1;
982 	    }
983       }
984     sqlite3_free_table (results);
985     if (error)
986 	return 0;
987     if (!full_check)
988 	return 1;
989 
990 /* testing if all table/geom are correctly defined in geometry_columns */
991     sql = sqlite3_mprintf ("SELECT Count(*) FROM MAIN.geometry_columns WHERE");
992     prev = sql;
993     table = sqlite3_mprintf ("%s_node", network_name);
994     sql =
995 	sqlite3_mprintf
996 	("%s (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'geometry')",
997 	 prev, table);
998     sqlite3_free (table);
999     sqlite3_free (prev);
1000     prev = sql;
1001     table = sqlite3_mprintf ("%s_link", network_name);
1002     sql =
1003 	sqlite3_mprintf
1004 	("%s OR (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'geometry')",
1005 	 prev, table);
1006     sqlite3_free (table);
1007     sqlite3_free (prev);
1008     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
1009     sqlite3_free (sql);
1010     if (ret != SQLITE_OK)
1011 	return 0;
1012     if (rows < 1)
1013 	;
1014     else
1015       {
1016 	  for (i = 1; i <= rows; i++)
1017 	    {
1018 		value = results[(i * columns) + 0];
1019 		if (atoi (value) != 2)
1020 		    error = 1;
1021 	    }
1022       }
1023     sqlite3_free_table (results);
1024     if (error)
1025 	return 0;
1026 
1027 /* testing if all tables are already defined */
1028     sql =
1029 	sqlite3_mprintf
1030 	("SELECT Count(*) FROM sqlite_master WHERE type = 'table' AND (");
1031     prev = sql;
1032     table = sqlite3_mprintf ("%s_node", network_name);
1033     sql = sqlite3_mprintf ("%s Lower(name) = Lower(%Q)", prev, table);
1034     sqlite3_free (table);
1035     sqlite3_free (prev);
1036     prev = sql;
1037     table = sqlite3_mprintf ("%s_link", network_name);
1038     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
1039     sqlite3_free (table);
1040     sqlite3_free (prev);
1041     prev = sql;
1042     table = sqlite3_mprintf ("idx_%s_node_geometry", network_name);
1043     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
1044     sqlite3_free (table);
1045     sqlite3_free (prev);
1046     prev = sql;
1047     table = sqlite3_mprintf ("idx_%s_link_geometry", network_name);
1048     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q))", prev, table);
1049     sqlite3_free (table);
1050     sqlite3_free (prev);
1051     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
1052     sqlite3_free (sql);
1053     if (ret != SQLITE_OK)
1054 	return 0;
1055     if (rows < 1)
1056 	;
1057     else
1058       {
1059 	  for (i = 1; i <= rows; i++)
1060 	    {
1061 		value = results[(i * columns) + 0];
1062 		if (atoi (value) != 4)
1063 		    error = 1;
1064 	    }
1065       }
1066     sqlite3_free_table (results);
1067     if (error)
1068 	return 0;
1069 
1070     return 1;
1071 }
1072 
1073 static int
do_drop_network_table(sqlite3 * handle,const char * network_name,const char * which)1074 do_drop_network_table (sqlite3 * handle, const char *network_name,
1075 		       const char *which)
1076 {
1077 /* attempting to drop some Network table */
1078     char *sql;
1079     char *table;
1080     char *xtable;
1081     char *err_msg = NULL;
1082     int ret;
1083 
1084 /* disabling the corresponding Spatial Index */
1085     table = sqlite3_mprintf ("%s_%s", network_name, which);
1086     sql = sqlite3_mprintf ("SELECT DisableSpatialIndex(%Q, 'geometry')", table);
1087     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1088     sqlite3_free (table);
1089     sqlite3_free (sql);
1090     if (ret != SQLITE_OK)
1091       {
1092 	  spatialite_e
1093 	      ("DisableSpatialIndex network-%s - error: %s\n", which, err_msg);
1094 	  sqlite3_free (err_msg);
1095 	  return 0;
1096       }
1097 
1098 /* discarding the Geometry column */
1099     table = sqlite3_mprintf ("%s_%s", network_name, which);
1100     sql =
1101 	sqlite3_mprintf ("SELECT DiscardGeometryColumn(%Q, 'geometry')", table);
1102     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1103     sqlite3_free (table);
1104     sqlite3_free (sql);
1105     if (ret != SQLITE_OK)
1106       {
1107 	  spatialite_e
1108 	      ("DisableGeometryColumn network-%s - error: %s\n", which,
1109 	       err_msg);
1110 	  sqlite3_free (err_msg);
1111 	  return 0;
1112       }
1113 
1114 /* dropping the main table */
1115     table = sqlite3_mprintf ("%s_%s", network_name, which);
1116     xtable = gaiaDoubleQuotedSql (table);
1117     sqlite3_free (table);
1118     sql = sqlite3_mprintf ("DROP TABLE IF EXISTS \"%s\"", xtable);
1119     free (xtable);
1120     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1121     sqlite3_free (sql);
1122     if (ret != SQLITE_OK)
1123       {
1124 	  spatialite_e ("DROP network-%s - error: %s\n", which, err_msg);
1125 	  sqlite3_free (err_msg);
1126 	  return 0;
1127       }
1128 
1129 /* dropping the corresponding Spatial Index */
1130     table = sqlite3_mprintf ("idx_%s_%s_geometry", network_name, which);
1131     sql = sqlite3_mprintf ("DROP TABLE IF EXISTS \"%s\"", table);
1132     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1133     sqlite3_free (table);
1134     sqlite3_free (sql);
1135     if (ret != SQLITE_OK)
1136       {
1137 	  spatialite_e
1138 	      ("DROP SpatialIndex network-%s - error: %s\n", which, err_msg);
1139 	  sqlite3_free (err_msg);
1140 	  return 0;
1141       }
1142 
1143     return 1;
1144 }
1145 
1146 static int
do_get_network(sqlite3 * handle,const char * net_name,char ** network_name,int * spatial,int * srid,int * has_z,int * allow_coincident)1147 do_get_network (sqlite3 * handle, const char *net_name, char **network_name,
1148 		int *spatial, int *srid, int *has_z, int *allow_coincident)
1149 {
1150 /* retrieving a Network configuration */
1151     char *sql;
1152     int ret;
1153     sqlite3_stmt *stmt = NULL;
1154     int ok = 0;
1155     char *xnetwork_name = NULL;
1156     int xsrid;
1157     int xhas_z;
1158     int xspatial;
1159     int xallow_coincident;
1160 
1161 /* preparing the SQL query */
1162     sql =
1163 	sqlite3_mprintf
1164 	("SELECT network_name, spatial, srid, has_z, allow_coincident "
1165 	 "FROM MAIN.networks WHERE Lower(network_name) = Lower(%Q)", net_name);
1166     ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt, NULL);
1167     sqlite3_free (sql);
1168     if (ret != SQLITE_OK)
1169       {
1170 	  spatialite_e ("SELECT FROM networks error: \"%s\"\n",
1171 			sqlite3_errmsg (handle));
1172 	  return 0;
1173       }
1174 
1175     while (1)
1176       {
1177 	  /* scrolling the result set rows */
1178 	  ret = sqlite3_step (stmt);
1179 	  if (ret == SQLITE_DONE)
1180 	      break;		/* end of result set */
1181 	  if (ret == SQLITE_ROW)
1182 	    {
1183 		int ok_name = 0;
1184 		int ok_srid = 0;
1185 		int ok_z = 0;
1186 		int ok_spatial = 0;
1187 		int ok_allow_coincident = 0;
1188 		if (sqlite3_column_type (stmt, 0) == SQLITE_TEXT)
1189 		  {
1190 		      const char *str =
1191 			  (const char *) sqlite3_column_text (stmt, 0);
1192 		      if (xnetwork_name != NULL)
1193 			  free (xnetwork_name);
1194 		      xnetwork_name = malloc (strlen (str) + 1);
1195 		      strcpy (xnetwork_name, str);
1196 		      ok_name = 1;
1197 		  }
1198 		if (sqlite3_column_type (stmt, 1) == SQLITE_INTEGER)
1199 		  {
1200 		      xspatial = sqlite3_column_int (stmt, 1);
1201 		      ok_spatial = 1;
1202 		  }
1203 		if (sqlite3_column_type (stmt, 2) == SQLITE_INTEGER)
1204 		  {
1205 		      xsrid = sqlite3_column_int (stmt, 2);
1206 		      ok_srid = 1;
1207 		  }
1208 		if (sqlite3_column_type (stmt, 3) == SQLITE_INTEGER)
1209 		  {
1210 		      xhas_z = sqlite3_column_int (stmt, 3);
1211 		      ok_z = 1;
1212 		  }
1213 		if (sqlite3_column_type (stmt, 4) == SQLITE_INTEGER)
1214 		  {
1215 		      xallow_coincident = sqlite3_column_int (stmt, 4);
1216 		      ok_allow_coincident = 1;
1217 		  }
1218 		if (ok_name && ok_spatial && ok_srid && ok_z
1219 		    && ok_allow_coincident)
1220 		  {
1221 		      ok = 1;
1222 		      break;
1223 		  }
1224 	    }
1225 	  else
1226 	    {
1227 		spatialite_e
1228 		    ("step: SELECT FROM networks error: \"%s\"\n",
1229 		     sqlite3_errmsg (handle));
1230 		sqlite3_finalize (stmt);
1231 		return 0;
1232 	    }
1233       }
1234     sqlite3_finalize (stmt);
1235 
1236     if (ok)
1237       {
1238 	  *network_name = xnetwork_name;
1239 	  *srid = xsrid;
1240 	  *has_z = xhas_z;
1241 	  *spatial = xspatial;
1242 	  *allow_coincident = xallow_coincident;
1243 	  return 1;
1244       }
1245 
1246     if (xnetwork_name != NULL)
1247 	free (xnetwork_name);
1248     return 0;
1249 }
1250 
1251 GAIANET_DECLARE GaiaNetworkAccessorPtr
gaiaGetNetwork(sqlite3 * handle,const void * cache,const char * network_name)1252 gaiaGetNetwork (sqlite3 * handle, const void *cache, const char *network_name)
1253 {
1254 /* attempting to get a reference to some Network Accessor Object */
1255     GaiaNetworkAccessorPtr accessor;
1256 
1257 /* attempting to retrieve an alredy cached definition */
1258     accessor = gaiaNetworkFromCache (cache, network_name);
1259     if (accessor != NULL)
1260 	return accessor;
1261 
1262 /* attempting to create a new Network Accessor */
1263     accessor = gaiaNetworkFromDBMS (handle, cache, network_name);
1264     return accessor;
1265 }
1266 
1267 GAIANET_DECLARE GaiaNetworkAccessorPtr
gaiaNetworkFromCache(const void * p_cache,const char * network_name)1268 gaiaNetworkFromCache (const void *p_cache, const char *network_name)
1269 {
1270 /* attempting to retrieve an already defined Network Accessor Object from the Connection Cache */
1271     struct gaia_network *ptr;
1272     struct splite_internal_cache *cache =
1273 	(struct splite_internal_cache *) p_cache;
1274     if (cache == 0)
1275 	return NULL;
1276 
1277     ptr = (struct gaia_network *) (cache->firstNetwork);
1278     while (ptr != NULL)
1279       {
1280 	  /* checking for an already registered Network */
1281 	  if (strcasecmp (network_name, ptr->network_name) == 0)
1282 	      return (GaiaNetworkAccessorPtr) ptr;
1283 	  ptr = ptr->next;
1284       }
1285     return NULL;
1286 }
1287 
1288 GAIANET_DECLARE int
gaiaReadNetworkFromDBMS(sqlite3 * handle,const char * net_name,char ** network_name,int * spatial,int * srid,int * has_z,int * allow_coincident)1289 gaiaReadNetworkFromDBMS (sqlite3 *
1290 			 handle,
1291 			 const char
1292 			 *net_name, char **network_name, int *spatial,
1293 			 int *srid, int *has_z, int *allow_coincident)
1294 {
1295 /* testing for existing DBMS objects */
1296     if (!check_existing_network (handle, net_name, 1))
1297 	return 0;
1298 
1299 /* retrieving the Network configuration */
1300     if (!do_get_network
1301 	(handle, net_name, network_name, spatial, srid, has_z,
1302 	 allow_coincident))
1303 	return 0;
1304     return 1;
1305 }
1306 
1307 GAIANET_DECLARE GaiaNetworkAccessorPtr
gaiaNetworkFromDBMS(sqlite3 * handle,const void * p_cache,const char * network_name)1308 gaiaNetworkFromDBMS (sqlite3 * handle, const void *p_cache,
1309 		     const char *network_name)
1310 {
1311 /* attempting to create a Network Accessor Object into the Connection Cache */
1312     const RTCTX *ctx = NULL;
1313     LWN_BE_CALLBACKS *callbacks;
1314     struct gaia_network *ptr;
1315     struct splite_internal_cache *cache =
1316 	(struct splite_internal_cache *) p_cache;
1317     if (cache == 0)
1318 	return NULL;
1319     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1320 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1321 	return NULL;
1322     ctx = cache->RTTOPO_handle;
1323     if (ctx == NULL)
1324 	return NULL;
1325 
1326 /* allocating and initializing the opaque object */
1327     ptr = malloc (sizeof (struct gaia_network));
1328     ptr->db_handle = handle;
1329     ptr->cache = cache;
1330     ptr->network_name = NULL;
1331     ptr->srid = -1;
1332     ptr->has_z = 0;
1333     ptr->spatial = 0;
1334     ptr->allow_coincident = 0;
1335     ptr->last_error_message = NULL;
1336     ptr->lwn_iface = lwn_CreateBackendIface (ctx, (const LWN_BE_DATA *) ptr);
1337     ptr->prev = cache->lastNetwork;
1338     ptr->next = NULL;
1339 
1340     callbacks = malloc (sizeof (LWN_BE_CALLBACKS));
1341     callbacks->netGetSRID = netcallback_netGetSRID;
1342     callbacks->netHasZ = netcallback_netHasZ;
1343     callbacks->netIsSpatial = netcallback_netIsSpatial;
1344     callbacks->netAllowCoincident = netcallback_netAllowCoincident;
1345     callbacks->netGetGEOS = netcallback_netGetGEOS;
1346     callbacks->createNetwork = NULL;
1347     callbacks->loadNetworkByName = netcallback_loadNetworkByName;
1348     callbacks->freeNetwork = netcallback_freeNetwork;
1349     callbacks->getNetNodeWithinDistance2D =
1350 	netcallback_getNetNodeWithinDistance2D;
1351     callbacks->getLinkWithinDistance2D = netcallback_getLinkWithinDistance2D;
1352     callbacks->insertNetNodes = netcallback_insertNetNodes;
1353     callbacks->getNetNodeById = netcallback_getNetNodeById;
1354     callbacks->updateNetNodesById = netcallback_updateNetNodesById;
1355     callbacks->deleteNetNodesById = netcallback_deleteNetNodesById;
1356     callbacks->getLinkByNetNode = netcallback_getLinkByNetNode;
1357     callbacks->getNextLinkId = netcallback_getNextLinkId;
1358     callbacks->getNetNodeWithinBox2D = netcallback_getNetNodeWithinBox2D;
1359     callbacks->getNextLinkId = netcallback_getNextLinkId;
1360     callbacks->insertLinks = netcallback_insertLinks;
1361     callbacks->updateLinksById = netcallback_updateLinksById;
1362     callbacks->getLinkById = netcallback_getLinkById;
1363     callbacks->deleteLinksById = netcallback_deleteLinksById;
1364     ptr->callbacks = callbacks;
1365 
1366     lwn_BackendIfaceRegisterCallbacks (ptr->lwn_iface, callbacks);
1367     ptr->lwn_network = lwn_LoadNetwork (ptr->lwn_iface, network_name);
1368 
1369     ptr->stmt_getNetNodeWithinDistance2D = NULL;
1370     ptr->stmt_getLinkWithinDistance2D = NULL;
1371     ptr->stmt_insertNetNodes = NULL;
1372     ptr->stmt_deleteNetNodesById = NULL;
1373     ptr->stmt_getNetNodeWithinBox2D = NULL;
1374     ptr->stmt_getNextLinkId = NULL;
1375     ptr->stmt_setNextLinkId = NULL;
1376     ptr->stmt_insertLinks = NULL;
1377     ptr->stmt_deleteLinksById = NULL;
1378     if (ptr->lwn_network == NULL)
1379 	goto invalid;
1380 
1381 /* creating the SQL prepared statements */
1382     create_toponet_prepared_stmts ((GaiaNetworkAccessorPtr) ptr);
1383     return (GaiaNetworkAccessorPtr) ptr;
1384 
1385   invalid:
1386     gaiaNetworkDestroy ((GaiaNetworkAccessorPtr) ptr);
1387     return NULL;
1388 }
1389 
1390 GAIANET_DECLARE void
gaiaNetworkDestroy(GaiaNetworkAccessorPtr net_ptr)1391 gaiaNetworkDestroy (GaiaNetworkAccessorPtr net_ptr)
1392 {
1393 /* destroying a Network Accessor Object */
1394     struct gaia_network *prev;
1395     struct gaia_network *next;
1396     struct splite_internal_cache *cache;
1397     struct gaia_network *ptr = (struct gaia_network *) net_ptr;
1398     if (ptr == NULL)
1399 	return;
1400 
1401     prev = ptr->prev;
1402     next = ptr->next;
1403     cache = (struct splite_internal_cache *) (ptr->cache);
1404     if (ptr->lwn_network != NULL)
1405 	lwn_FreeNetwork ((LWN_NETWORK *) (ptr->lwn_network));
1406     if (ptr->lwn_iface != NULL)
1407 	lwn_FreeBackendIface ((LWN_BE_IFACE *) (ptr->lwn_iface));
1408     if (ptr->callbacks != NULL)
1409 	free (ptr->callbacks);
1410     if (ptr->network_name != NULL)
1411 	free (ptr->network_name);
1412     if (ptr->last_error_message != NULL)
1413 	free (ptr->last_error_message);
1414 
1415     finalize_toponet_prepared_stmts (net_ptr);
1416     free (ptr);
1417 
1418 /* unregistering from the Internal Cache double linked list */
1419     if (prev != NULL)
1420 	prev->next = next;
1421     if (next != NULL)
1422 	next->prev = prev;
1423     if (cache->firstNetwork == ptr)
1424 	cache->firstNetwork = next;
1425     if (cache->lastNetwork == ptr)
1426 	cache->lastNetwork = prev;
1427 }
1428 
1429 NETWORK_PRIVATE void
finalize_toponet_prepared_stmts(GaiaNetworkAccessorPtr accessor)1430 finalize_toponet_prepared_stmts (GaiaNetworkAccessorPtr accessor)
1431 {
1432 /* finalizing the SQL prepared statements */
1433     struct gaia_network *ptr = (struct gaia_network *) accessor;
1434     if (ptr->stmt_getNetNodeWithinDistance2D != NULL)
1435 	sqlite3_finalize (ptr->stmt_getNetNodeWithinDistance2D);
1436     if (ptr->stmt_getLinkWithinDistance2D != NULL)
1437 	sqlite3_finalize (ptr->stmt_getLinkWithinDistance2D);
1438     if (ptr->stmt_insertNetNodes != NULL)
1439 	sqlite3_finalize (ptr->stmt_insertNetNodes);
1440     if (ptr->stmt_deleteNetNodesById != NULL)
1441 	sqlite3_finalize (ptr->stmt_deleteNetNodesById);
1442     if (ptr->stmt_getNetNodeWithinBox2D != NULL)
1443 	sqlite3_finalize (ptr->stmt_getNetNodeWithinBox2D);
1444     if (ptr->stmt_getNextLinkId != NULL)
1445 	sqlite3_finalize (ptr->stmt_getNextLinkId);
1446     if (ptr->stmt_setNextLinkId != NULL)
1447 	sqlite3_finalize (ptr->stmt_setNextLinkId);
1448     if (ptr->stmt_insertLinks != NULL)
1449 	sqlite3_finalize (ptr->stmt_insertLinks);
1450     if (ptr->stmt_deleteLinksById != NULL)
1451 	sqlite3_finalize (ptr->stmt_deleteLinksById);
1452     ptr->stmt_getNetNodeWithinDistance2D = NULL;
1453     ptr->stmt_getLinkWithinDistance2D = NULL;
1454     ptr->stmt_insertNetNodes = NULL;
1455     ptr->stmt_deleteNetNodesById = NULL;
1456     ptr->stmt_getNetNodeWithinBox2D = NULL;
1457     ptr->stmt_getNextLinkId = NULL;
1458     ptr->stmt_setNextLinkId = NULL;
1459     ptr->stmt_insertLinks = NULL;
1460     ptr->stmt_deleteLinksById = NULL;
1461 }
1462 
1463 NETWORK_PRIVATE void
create_toponet_prepared_stmts(GaiaNetworkAccessorPtr accessor)1464 create_toponet_prepared_stmts (GaiaNetworkAccessorPtr accessor)
1465 {
1466 /* creating the SQL prepared statements */
1467     struct gaia_network *ptr = (struct gaia_network *) accessor;
1468     finalize_toponet_prepared_stmts (accessor);
1469     ptr->stmt_getNetNodeWithinDistance2D =
1470 	do_create_stmt_getNetNodeWithinDistance2D (accessor);
1471     ptr->stmt_getLinkWithinDistance2D =
1472 	do_create_stmt_getLinkWithinDistance2D (accessor);
1473     ptr->stmt_deleteNetNodesById = do_create_stmt_deleteNetNodesById (accessor);
1474     ptr->stmt_insertNetNodes = do_create_stmt_insertNetNodes (accessor);
1475     ptr->stmt_getNetNodeWithinBox2D =
1476 	do_create_stmt_getNetNodeWithinBox2D (accessor);
1477     ptr->stmt_getNextLinkId = do_create_stmt_getNextLinkId (accessor);
1478     ptr->stmt_setNextLinkId = do_create_stmt_setNextLinkId (accessor);
1479     ptr->stmt_insertLinks = do_create_stmt_insertLinks (accessor);
1480     ptr->stmt_deleteLinksById = do_create_stmt_deleteLinksById (accessor);
1481 }
1482 
1483 NETWORK_PRIVATE void
gaianet_reset_last_error_msg(GaiaNetworkAccessorPtr accessor)1484 gaianet_reset_last_error_msg (GaiaNetworkAccessorPtr accessor)
1485 {
1486 /* resets the last Network error message */
1487     struct gaia_network *net = (struct gaia_network *) accessor;
1488     if (net == NULL)
1489 	return;
1490 
1491     if (net->last_error_message != NULL)
1492 	free (net->last_error_message);
1493     net->last_error_message = NULL;
1494 }
1495 
1496 NETWORK_PRIVATE void
gaianet_set_last_error_msg(GaiaNetworkAccessorPtr accessor,const char * msg)1497 gaianet_set_last_error_msg (GaiaNetworkAccessorPtr accessor, const char *msg)
1498 {
1499 /* sets the last Network error message */
1500     int len;
1501     struct gaia_network *net = (struct gaia_network *) accessor;
1502     if (msg == NULL)
1503 	msg = "no message available";
1504 
1505     spatialite_e ("%s\n", msg);
1506     if (net == NULL)
1507 	return;
1508 
1509     if (net->last_error_message != NULL)
1510 	return;
1511 
1512     len = strlen (msg);
1513     net->last_error_message = malloc (len + 1);
1514     strcpy (net->last_error_message, msg);
1515 }
1516 
1517 NETWORK_PRIVATE const char *
gaianet_get_last_exception(GaiaNetworkAccessorPtr accessor)1518 gaianet_get_last_exception (GaiaNetworkAccessorPtr accessor)
1519 {
1520 /* returns the last Network error message */
1521     struct gaia_network *net = (struct gaia_network *) accessor;
1522     if (net == NULL)
1523 	return NULL;
1524 
1525     return net->last_error_message;
1526 }
1527 
1528 GAIANET_DECLARE int
gaiaNetworkDrop(sqlite3 * handle,const char * network_name)1529 gaiaNetworkDrop (sqlite3 * handle, const char *network_name)
1530 {
1531 /* attempting to drop an already existing Network */
1532     int ret;
1533     char *sql;
1534 
1535 /* creating the Networks table (just in case) */
1536     if (!do_create_networks (handle))
1537 	return 0;
1538 
1539 /* testing for existing DBMS objects */
1540     if (!check_existing_network (handle, network_name, 0))
1541 	return 0;
1542 
1543 /* dropping the Network own Tables */
1544     if (!do_drop_network_table (handle, network_name, "seeds"))
1545 	goto error;
1546     if (!do_drop_network_table (handle, network_name, "link"))
1547 	goto error;
1548     if (!do_drop_network_table (handle, network_name, "node"))
1549 	goto error;
1550 
1551 /* unregistering the Network */
1552     sql =
1553 	sqlite3_mprintf
1554 	("DELETE FROM MAIN.networks WHERE Lower(network_name) = Lower(%Q)",
1555 	 network_name);
1556     ret = sqlite3_exec (handle, sql, NULL, NULL, NULL);
1557     sqlite3_free (sql);
1558     if (ret != SQLITE_OK)
1559 	goto error;
1560 
1561     return 1;
1562 
1563   error:
1564     return 0;
1565 }
1566 
1567 GAIANET_DECLARE sqlite3_int64
gaiaAddIsoNetNode(GaiaNetworkAccessorPtr accessor,gaiaPointPtr pt)1568 gaiaAddIsoNetNode (GaiaNetworkAccessorPtr accessor, gaiaPointPtr pt)
1569 {
1570 /* LWN wrapper - AddIsoNetNode */
1571     sqlite3_int64 ret;
1572     LWN_POINT *point = NULL;
1573     struct gaia_network *network = (struct gaia_network *) accessor;
1574     if (network == NULL)
1575 	return 0;
1576 
1577     if (pt != NULL)
1578       {
1579 	  if (pt->DimensionModel == GAIA_XY_Z
1580 	      || pt->DimensionModel == GAIA_XY_Z_M)
1581 	      point = lwn_create_point3d (network->srid, pt->X, pt->Y, pt->Z);
1582 	  else
1583 	      point = lwn_create_point2d (network->srid, pt->X, pt->Y);
1584       }
1585     lwn_ResetErrorMsg (network->lwn_iface);
1586     ret = lwn_AddIsoNetNode ((LWN_NETWORK *) (network->lwn_network), point);
1587     lwn_free_point (point);
1588 
1589     return ret;
1590 }
1591 
1592 GAIANET_DECLARE int
gaiaMoveIsoNetNode(GaiaNetworkAccessorPtr accessor,sqlite3_int64 node,gaiaPointPtr pt)1593 gaiaMoveIsoNetNode (GaiaNetworkAccessorPtr accessor,
1594 		    sqlite3_int64 node, gaiaPointPtr pt)
1595 {
1596 /* LWN wrapper - MoveIsoNetNode */
1597     int ret;
1598     LWN_POINT *point = NULL;
1599     struct gaia_network *network = (struct gaia_network *) accessor;
1600     if (network == NULL)
1601 	return 0;
1602 
1603     if (pt != NULL)
1604       {
1605 	  if (pt->DimensionModel == GAIA_XY_Z
1606 	      || pt->DimensionModel == GAIA_XY_Z_M)
1607 	      point = lwn_create_point3d (network->srid, pt->X, pt->Y, pt->Z);
1608 	  else
1609 	      point = lwn_create_point2d (network->srid, pt->X, pt->Y);
1610       }
1611     lwn_ResetErrorMsg (network->lwn_iface);
1612     ret =
1613 	lwn_MoveIsoNetNode ((LWN_NETWORK *) (network->lwn_network), node,
1614 			    point);
1615     lwn_free_point (point);
1616 
1617     if (ret == 0)
1618 	return 1;
1619     return 0;
1620 }
1621 
1622 GAIANET_DECLARE int
gaiaRemIsoNetNode(GaiaNetworkAccessorPtr accessor,sqlite3_int64 node)1623 gaiaRemIsoNetNode (GaiaNetworkAccessorPtr accessor, sqlite3_int64 node)
1624 {
1625 /* LWN wrapper - RemIsoNetNode */
1626     int ret;
1627     struct gaia_network *network = (struct gaia_network *) accessor;
1628     if (network == NULL)
1629 	return 0;
1630 
1631     lwn_ResetErrorMsg (network->lwn_iface);
1632     ret = lwn_RemIsoNetNode ((LWN_NETWORK *) (network->lwn_network), node);
1633 
1634     if (ret == 0)
1635 	return 1;
1636     return 0;
1637 }
1638 
1639 GAIANET_DECLARE sqlite3_int64
gaiaAddLink(GaiaNetworkAccessorPtr accessor,sqlite3_int64 start_node,sqlite3_int64 end_node,gaiaLinestringPtr ln)1640 gaiaAddLink (GaiaNetworkAccessorPtr accessor,
1641 	     sqlite3_int64 start_node, sqlite3_int64 end_node,
1642 	     gaiaLinestringPtr ln)
1643 {
1644 /* LWN wrapper - AddLink */
1645     sqlite3_int64 ret;
1646     LWN_LINE *lwn_line = NULL;
1647     struct gaia_network *network = (struct gaia_network *) accessor;
1648     if (network == NULL)
1649 	return 0;
1650 
1651     if (ln != NULL)
1652       {
1653 	  lwn_line =
1654 	      gaianet_convert_linestring_to_lwnline (ln, network->srid,
1655 						     network->has_z);
1656       }
1657 
1658     lwn_ResetErrorMsg (network->lwn_iface);
1659     ret =
1660 	lwn_AddLink ((LWN_NETWORK *) (network->lwn_network), start_node,
1661 		     end_node, lwn_line);
1662 
1663     lwn_free_line (lwn_line);
1664     return ret;
1665 }
1666 
1667 GAIANET_DECLARE int
gaiaChangeLinkGeom(GaiaNetworkAccessorPtr accessor,sqlite3_int64 link_id,gaiaLinestringPtr ln)1668 gaiaChangeLinkGeom (GaiaNetworkAccessorPtr accessor,
1669 		    sqlite3_int64 link_id, gaiaLinestringPtr ln)
1670 {
1671 /* LWN wrapper - ChangeLinkGeom */
1672     int ret;
1673     LWN_LINE *lwn_line = NULL;
1674     struct gaia_network *network = (struct gaia_network *) accessor;
1675     if (network == NULL)
1676 	return 0;
1677 
1678     if (ln != NULL)
1679       {
1680 	  lwn_line =
1681 	      gaianet_convert_linestring_to_lwnline (ln, network->srid,
1682 						     network->has_z);
1683       }
1684 
1685     lwn_ResetErrorMsg (network->lwn_iface);
1686     ret =
1687 	lwn_ChangeLinkGeom ((LWN_NETWORK *) (network->lwn_network), link_id,
1688 			    lwn_line);
1689     lwn_free_line (lwn_line);
1690 
1691     if (ret == 0)
1692 	return 1;
1693     return 0;
1694 }
1695 
1696 GAIANET_DECLARE int
gaiaRemoveLink(GaiaNetworkAccessorPtr accessor,sqlite3_int64 link)1697 gaiaRemoveLink (GaiaNetworkAccessorPtr accessor, sqlite3_int64 link)
1698 {
1699 /* LWN wrapper - RemoveLink */
1700     int ret;
1701     struct gaia_network *network = (struct gaia_network *) accessor;
1702     if (network == NULL)
1703 	return 0;
1704 
1705     lwn_ResetErrorMsg (network->lwn_iface);
1706     ret = lwn_RemoveLink ((LWN_NETWORK *) (network->lwn_network), link);
1707 
1708     if (ret == 0)
1709 	return 1;
1710     return 0;
1711 }
1712 
1713 GAIANET_DECLARE sqlite3_int64
gaiaNewLogLinkSplit(GaiaNetworkAccessorPtr accessor,sqlite3_int64 link)1714 gaiaNewLogLinkSplit (GaiaNetworkAccessorPtr accessor, sqlite3_int64 link)
1715 {
1716 /* LWN wrapper - NewLogLinkSplit  */
1717     sqlite3_int64 ret;
1718     struct gaia_network *network = (struct gaia_network *) accessor;
1719     if (network == NULL)
1720 	return 0;
1721 
1722     lwn_ResetErrorMsg (network->lwn_iface);
1723     ret = lwn_NewLogLinkSplit ((LWN_NETWORK *) (network->lwn_network), link);
1724     return ret;
1725 }
1726 
1727 GAIANET_DECLARE sqlite3_int64
gaiaModLogLinkSplit(GaiaNetworkAccessorPtr accessor,sqlite3_int64 link)1728 gaiaModLogLinkSplit (GaiaNetworkAccessorPtr accessor, sqlite3_int64 link)
1729 {
1730 /* LWN wrapper - ModLogLinkSplit  */
1731     sqlite3_int64 ret;
1732     struct gaia_network *network = (struct gaia_network *) accessor;
1733     if (network == NULL)
1734 	return 0;
1735 
1736     lwn_ResetErrorMsg (network->lwn_iface);
1737     ret = lwn_ModLogLinkSplit ((LWN_NETWORK *) (network->lwn_network), link);
1738     return ret;
1739 }
1740 
1741 GAIANET_DECLARE sqlite3_int64
gaiaNewGeoLinkSplit(GaiaNetworkAccessorPtr accessor,sqlite3_int64 link,gaiaPointPtr pt)1742 gaiaNewGeoLinkSplit (GaiaNetworkAccessorPtr accessor, sqlite3_int64 link,
1743 		     gaiaPointPtr pt)
1744 {
1745 /* LWN wrapper - NewGeoLinkSplit  */
1746     sqlite3_int64 ret;
1747     LWN_POINT *point = NULL;
1748     struct gaia_network *network = (struct gaia_network *) accessor;
1749     if (network == NULL)
1750 	return 0;
1751 
1752     if (pt != NULL)
1753       {
1754 	  if (pt->DimensionModel == GAIA_XY_Z
1755 	      || pt->DimensionModel == GAIA_XY_Z_M)
1756 	      point = lwn_create_point3d (network->srid, pt->X, pt->Y, pt->Z);
1757 	  else
1758 	      point = lwn_create_point2d (network->srid, pt->X, pt->Y);
1759       }
1760     lwn_ResetErrorMsg (network->lwn_iface);
1761     ret =
1762 	lwn_NewGeoLinkSplit ((LWN_NETWORK *) (network->lwn_network), link,
1763 			     point);
1764     lwn_free_point (point);
1765     return ret;
1766 }
1767 
1768 GAIANET_DECLARE sqlite3_int64
gaiaModGeoLinkSplit(GaiaNetworkAccessorPtr accessor,sqlite3_int64 link,gaiaPointPtr pt)1769 gaiaModGeoLinkSplit (GaiaNetworkAccessorPtr accessor, sqlite3_int64 link,
1770 		     gaiaPointPtr pt)
1771 {
1772 /* LWN wrapper - ModGeoLinkSplit  */
1773     sqlite3_int64 ret;
1774     LWN_POINT *point = NULL;
1775     struct gaia_network *network = (struct gaia_network *) accessor;
1776     if (network == NULL)
1777 	return 0;
1778 
1779     if (pt != NULL)
1780       {
1781 	  if (pt->DimensionModel == GAIA_XY_Z
1782 	      || pt->DimensionModel == GAIA_XY_Z_M)
1783 	      point = lwn_create_point3d (network->srid, pt->X, pt->Y, pt->Z);
1784 	  else
1785 	      point = lwn_create_point2d (network->srid, pt->X, pt->Y);
1786       }
1787     lwn_ResetErrorMsg (network->lwn_iface);
1788     ret =
1789 	lwn_ModGeoLinkSplit ((LWN_NETWORK *) (network->lwn_network), link,
1790 			     point);
1791     lwn_free_point (point);
1792     return ret;
1793 }
1794 
1795 GAIANET_DECLARE sqlite3_int64
gaiaNewLinkHeal(GaiaNetworkAccessorPtr accessor,sqlite3_int64 link,sqlite3_int64 anotherlink)1796 gaiaNewLinkHeal (GaiaNetworkAccessorPtr accessor, sqlite3_int64 link,
1797 		 sqlite3_int64 anotherlink)
1798 {
1799 /* LWN wrapper - NewLinkHeal  */
1800     sqlite3_int64 ret;
1801     struct gaia_network *network = (struct gaia_network *) accessor;
1802     if (network == NULL)
1803 	return 0;
1804 
1805     lwn_ResetErrorMsg (network->lwn_iface);
1806     ret =
1807 	lwn_NewLinkHeal ((LWN_NETWORK *) (network->lwn_network), link,
1808 			 anotherlink);
1809 
1810     return ret;
1811 }
1812 
1813 GAIANET_DECLARE sqlite3_int64
gaiaModLinkHeal(GaiaNetworkAccessorPtr accessor,sqlite3_int64 link,sqlite3_int64 anotherlink)1814 gaiaModLinkHeal (GaiaNetworkAccessorPtr accessor, sqlite3_int64 link,
1815 		 sqlite3_int64 anotherlink)
1816 {
1817 /* LWN wrapper - ModLinkHeal  */
1818     sqlite3_int64 ret;
1819     struct gaia_network *network = (struct gaia_network *) accessor;
1820     if (network == NULL)
1821 	return 0;
1822 
1823     lwn_ResetErrorMsg (network->lwn_iface);
1824     ret =
1825 	lwn_ModLinkHeal ((LWN_NETWORK *) (network->lwn_network), link,
1826 			 anotherlink);
1827 
1828     return ret;
1829 }
1830 
1831 GAIANET_DECLARE sqlite3_int64
gaiaGetNetNodeByPoint(GaiaNetworkAccessorPtr accessor,gaiaPointPtr pt,double tolerance)1832 gaiaGetNetNodeByPoint (GaiaNetworkAccessorPtr accessor, gaiaPointPtr pt,
1833 		       double tolerance)
1834 {
1835 /* LWN wrapper - GetNetNodeByPoint */
1836     sqlite3_int64 ret;
1837     LWN_POINT *point = NULL;
1838     struct gaia_network *network = (struct gaia_network *) accessor;
1839     if (network == NULL)
1840 	return 0;
1841 
1842     if (pt != NULL)
1843       {
1844 	  if (pt->DimensionModel == GAIA_XY_Z
1845 	      || pt->DimensionModel == GAIA_XY_Z_M)
1846 	      point = lwn_create_point3d (network->srid, pt->X, pt->Y, pt->Z);
1847 	  else
1848 	      point = lwn_create_point2d (network->srid, pt->X, pt->Y);
1849       }
1850     lwn_ResetErrorMsg (network->lwn_iface);
1851 
1852     ret =
1853 	lwn_GetNetNodeByPoint ((LWN_NETWORK *) (network->lwn_network), point,
1854 			       tolerance);
1855 
1856     lwn_free_point (point);
1857     return ret;
1858 }
1859 
1860 GAIANET_DECLARE sqlite3_int64
gaiaGetLinkByPoint(GaiaNetworkAccessorPtr accessor,gaiaPointPtr pt,double tolerance)1861 gaiaGetLinkByPoint (GaiaNetworkAccessorPtr accessor, gaiaPointPtr pt,
1862 		    double tolerance)
1863 {
1864 /* LWN wrapper - GetLinkByPoint */
1865     sqlite3_int64 ret;
1866     LWN_POINT *point = NULL;
1867     struct gaia_network *network = (struct gaia_network *) accessor;
1868     if (network == NULL)
1869 	return 0;
1870 
1871     if (pt != NULL)
1872       {
1873 	  if (pt->DimensionModel == GAIA_XY_Z
1874 	      || pt->DimensionModel == GAIA_XY_Z_M)
1875 	      point = lwn_create_point3d (network->srid, pt->X, pt->Y, pt->Z);
1876 	  else
1877 	      point = lwn_create_point2d (network->srid, pt->X, pt->Y);
1878       }
1879     lwn_ResetErrorMsg (network->lwn_iface);
1880 
1881     ret =
1882 	lwn_GetLinkByPoint ((LWN_NETWORK *) (network->lwn_network), point,
1883 			    tolerance);
1884 
1885     lwn_free_point (point);
1886     return ret;
1887 }
1888 
1889 static int
do_check_create_valid_logicalnet_table(GaiaNetworkAccessorPtr accessor)1890 do_check_create_valid_logicalnet_table (GaiaNetworkAccessorPtr accessor)
1891 {
1892 /* attemtping to create or validate the target table */
1893     char *sql;
1894     char *table;
1895     char *xtable;
1896     int ret;
1897     char *errMsg = NULL;
1898     struct gaia_network *net = (struct gaia_network *) accessor;
1899 
1900 /* finalizing all prepared Statements */
1901     finalize_all_topo_prepared_stmts (net->cache);
1902 
1903 /* attempting to drop the table (just in case if it already exists) */
1904     table = sqlite3_mprintf ("%s_valid_logicalnet", net->network_name);
1905     xtable = gaiaDoubleQuotedSql (table);
1906     sqlite3_free (table);
1907     sql = sqlite3_mprintf ("DROP TABLE IF EXISTS TEMP.\"%s\"", xtable);
1908     free (xtable);
1909     ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
1910     sqlite3_free (sql);
1911     create_all_topo_prepared_stmts (net->cache);	/* recreating prepared stsms */
1912     if (ret != SQLITE_OK)
1913       {
1914 	  char *msg =
1915 	      sqlite3_mprintf ("ST_ValidLogicalNet exception: %s", errMsg);
1916 	  gaianet_set_last_error_msg (accessor, msg);
1917 	  sqlite3_free (msg);
1918 	  sqlite3_free (errMsg);
1919 	  return 0;
1920       }
1921 
1922 /* attempting to create the table */
1923     table = sqlite3_mprintf ("%s_valid_logicalnet", net->network_name);
1924     xtable = gaiaDoubleQuotedSql (table);
1925     sqlite3_free (table);
1926     sql =
1927 	sqlite3_mprintf
1928 	("CREATE TEMP TABLE \"%s\" (\n\terror TEXT,\n"
1929 	 "\tprimitive1 INTEGER,\n\tprimitive2 INTEGER)", xtable);
1930     free (xtable);
1931     ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
1932     sqlite3_free (sql);
1933     if (ret != SQLITE_OK)
1934       {
1935 	  char *msg =
1936 	      sqlite3_mprintf ("ST_ValidLogicalNet exception: %s", errMsg);
1937 	  gaianet_set_last_error_msg (accessor, msg);
1938 	  sqlite3_free (msg);
1939 	  sqlite3_free (errMsg);
1940 	  return 0;
1941       }
1942 
1943     return 1;
1944 }
1945 
1946 static int
do_loginet_check_nodes(GaiaNetworkAccessorPtr accessor,sqlite3_stmt * stmt)1947 do_loginet_check_nodes (GaiaNetworkAccessorPtr accessor, sqlite3_stmt * stmt)
1948 {
1949 /* checking for nodes with geometry */
1950     char *sql;
1951     char *table;
1952     char *xtable;
1953     int ret;
1954     sqlite3_stmt *stmt_in = NULL;
1955     struct gaia_network *net = (struct gaia_network *) accessor;
1956 
1957     table = sqlite3_mprintf ("%s_node", net->network_name);
1958     xtable = gaiaDoubleQuotedSql (table);
1959     sqlite3_free (table);
1960     sql =
1961 	sqlite3_mprintf
1962 	("SELECT node_id FROM MAIN.\"%s\" WHERE geometry IS NOT NULL", xtable);
1963     free (xtable);
1964     ret =
1965 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_in, NULL);
1966     sqlite3_free (sql);
1967     if (ret != SQLITE_OK)
1968       {
1969 	  char *msg =
1970 	      sqlite3_mprintf ("ST_ValidLogicalNet() - Nodes error: \"%s\"",
1971 			       sqlite3_errmsg (net->db_handle));
1972 	  gaianet_set_last_error_msg (accessor, msg);
1973 	  sqlite3_free (msg);
1974 	  goto error;
1975       }
1976 
1977     sqlite3_reset (stmt_in);
1978     sqlite3_clear_bindings (stmt_in);
1979     while (1)
1980       {
1981 	  /* scrolling the result set rows */
1982 	  ret = sqlite3_step (stmt_in);
1983 	  if (ret == SQLITE_DONE)
1984 	      break;		/* end of result set */
1985 	  if (ret == SQLITE_ROW)
1986 	    {
1987 		sqlite3_int64 node_id = sqlite3_column_int64 (stmt_in, 0);
1988 		/* reporting the error */
1989 		sqlite3_reset (stmt);
1990 		sqlite3_clear_bindings (stmt);
1991 		sqlite3_bind_text (stmt, 1, "node has geometry", -1,
1992 				   SQLITE_STATIC);
1993 		sqlite3_bind_int64 (stmt, 2, node_id);
1994 		sqlite3_bind_null (stmt, 3);
1995 		ret = sqlite3_step (stmt);
1996 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
1997 		    ;
1998 		else
1999 		  {
2000 		      char *msg =
2001 			  sqlite3_mprintf
2002 			  ("ST_ValidLogicalNet() insert error: \"%s\"",
2003 			   sqlite3_errmsg (net->db_handle));
2004 		      gaianet_set_last_error_msg (accessor, msg);
2005 		      sqlite3_free (msg);
2006 		      goto error;
2007 		  }
2008 	    }
2009 	  else
2010 	    {
2011 		char *msg =
2012 		    sqlite3_mprintf
2013 		    ("ST_ValidLogicalNet() - Nodes step error: %s",
2014 		     sqlite3_errmsg (net->db_handle));
2015 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
2016 		sqlite3_free (msg);
2017 		goto error;
2018 	    }
2019       }
2020     sqlite3_finalize (stmt_in);
2021 
2022     return 1;
2023 
2024   error:
2025     if (stmt_in == NULL)
2026 	sqlite3_finalize (stmt_in);
2027     return 0;
2028 }
2029 
2030 static int
do_loginet_check_links(GaiaNetworkAccessorPtr accessor,sqlite3_stmt * stmt)2031 do_loginet_check_links (GaiaNetworkAccessorPtr accessor, sqlite3_stmt * stmt)
2032 {
2033 /* checking for links with geometry */
2034     char *sql;
2035     char *table;
2036     char *xtable;
2037     int ret;
2038     sqlite3_stmt *stmt_in = NULL;
2039     struct gaia_network *net = (struct gaia_network *) accessor;
2040 
2041     table = sqlite3_mprintf ("%s_link", net->network_name);
2042     xtable = gaiaDoubleQuotedSql (table);
2043     sqlite3_free (table);
2044     sql =
2045 	sqlite3_mprintf
2046 	("SELECT link_id FROM MAIN.\"%s\" WHERE geometry IS NOT NULL", xtable);
2047     free (xtable);
2048     ret =
2049 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_in, NULL);
2050     sqlite3_free (sql);
2051     if (ret != SQLITE_OK)
2052       {
2053 	  char *msg =
2054 	      sqlite3_mprintf ("ST_ValidLogicalNet() - Links error: \"%s\"",
2055 			       sqlite3_errmsg (net->db_handle));
2056 	  gaianet_set_last_error_msg (accessor, msg);
2057 	  sqlite3_free (msg);
2058 	  goto error;
2059       }
2060 
2061     sqlite3_reset (stmt_in);
2062     sqlite3_clear_bindings (stmt_in);
2063     while (1)
2064       {
2065 	  /* scrolling the result set rows */
2066 	  ret = sqlite3_step (stmt_in);
2067 	  if (ret == SQLITE_DONE)
2068 	      break;		/* end of result set */
2069 	  if (ret == SQLITE_ROW)
2070 	    {
2071 		sqlite3_int64 link_id = sqlite3_column_int64 (stmt_in, 0);
2072 		/* reporting the error */
2073 		sqlite3_reset (stmt);
2074 		sqlite3_clear_bindings (stmt);
2075 		sqlite3_bind_text (stmt, 1, "link has geometry", -1,
2076 				   SQLITE_STATIC);
2077 		sqlite3_bind_int64 (stmt, 2, link_id);
2078 		sqlite3_bind_null (stmt, 3);
2079 		ret = sqlite3_step (stmt);
2080 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
2081 		    ;
2082 		else
2083 		  {
2084 		      char *msg =
2085 			  sqlite3_mprintf
2086 			  ("ST_ValidLogicalNet() insert error: \"%s\"",
2087 			   sqlite3_errmsg (net->db_handle));
2088 		      gaianet_set_last_error_msg (accessor, msg);
2089 		      sqlite3_free (msg);
2090 		      goto error;
2091 		  }
2092 	    }
2093 	  else
2094 	    {
2095 		char *msg =
2096 		    sqlite3_mprintf
2097 		    ("ST_ValidLogicalNet() - Links step error: %s",
2098 		     sqlite3_errmsg (net->db_handle));
2099 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
2100 		sqlite3_free (msg);
2101 		goto error;
2102 	    }
2103       }
2104     sqlite3_finalize (stmt_in);
2105 
2106     return 1;
2107 
2108   error:
2109     if (stmt_in == NULL)
2110 	sqlite3_finalize (stmt_in);
2111     return 0;
2112 }
2113 
2114 GAIANET_DECLARE int
gaiaValidLogicalNet(GaiaNetworkAccessorPtr accessor)2115 gaiaValidLogicalNet (GaiaNetworkAccessorPtr accessor)
2116 {
2117 /* generating a validity report for a given Logical Network */
2118     char *table;
2119     char *xtable;
2120     char *sql;
2121     int ret;
2122     sqlite3_stmt *stmt = NULL;
2123     struct gaia_network *net = (struct gaia_network *) accessor;
2124     if (net == NULL)
2125 	return 0;
2126 
2127     if (!do_check_create_valid_logicalnet_table (accessor))
2128 	return 0;
2129 
2130     table = sqlite3_mprintf ("%s_valid_logicalnet", net->network_name);
2131     xtable = gaiaDoubleQuotedSql (table);
2132     sqlite3_free (table);
2133     sql =
2134 	sqlite3_mprintf
2135 	("INSERT INTO TEMP.\"%s\" (error, primitive1, primitive2) VALUES (?, ?, ?)",
2136 	 xtable);
2137     free (xtable);
2138     ret = sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt, NULL);
2139     sqlite3_free (sql);
2140     if (ret != SQLITE_OK)
2141       {
2142 	  char *msg = sqlite3_mprintf ("ST_ValidLogicalNet error: \"%s\"",
2143 				       sqlite3_errmsg (net->db_handle));
2144 	  gaianet_set_last_error_msg (accessor, msg);
2145 	  sqlite3_free (msg);
2146 	  goto error;
2147       }
2148 
2149     if (!do_loginet_check_nodes (accessor, stmt))
2150 	goto error;
2151 
2152     if (!do_loginet_check_links (accessor, stmt))
2153 	goto error;
2154 
2155     sqlite3_finalize (stmt);
2156     return 1;
2157 
2158   error:
2159     if (stmt != NULL)
2160 	sqlite3_finalize (stmt);
2161     return 0;
2162 }
2163 
2164 static int
do_check_create_valid_spatialnet_table(GaiaNetworkAccessorPtr accessor)2165 do_check_create_valid_spatialnet_table (GaiaNetworkAccessorPtr accessor)
2166 {
2167 /* attemtping to create or validate the target table */
2168     char *sql;
2169     char *table;
2170     char *xtable;
2171     int ret;
2172     char *errMsg;
2173     struct gaia_network *net = (struct gaia_network *) accessor;
2174 
2175 /* attempting to drop the table (just in case if it already exists) */
2176     table = sqlite3_mprintf ("%s_valid_spatialnet", net->network_name);
2177     xtable = gaiaDoubleQuotedSql (table);
2178     sqlite3_free (table);
2179     sql = sqlite3_mprintf ("DROP TABLE IF EXISTS temp.\"%s\"", xtable);
2180     free (xtable);
2181     ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
2182     sqlite3_free (sql);
2183     if (ret != SQLITE_OK)
2184       {
2185 	  char *msg =
2186 	      sqlite3_mprintf ("ST_ValidSpatialNet exception: %s", errMsg);
2187 	  gaianet_set_last_error_msg (accessor, msg);
2188 	  sqlite3_free (msg);
2189 	  sqlite3_free (errMsg);
2190 	  return 0;
2191       }
2192 
2193 /* attempting to create the table */
2194     table = sqlite3_mprintf ("%s_valid_spatialnet", net->network_name);
2195     xtable = gaiaDoubleQuotedSql (table);
2196     sqlite3_free (table);
2197     sql =
2198 	sqlite3_mprintf
2199 	("CREATE TEMP TABLE \"%s\" (\n\terror TEXT,\n"
2200 	 "\tprimitive1 INTEGER,\n\tprimitive2 INTEGER)", xtable);
2201     free (xtable);
2202     ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
2203     sqlite3_free (sql);
2204     if (ret != SQLITE_OK)
2205       {
2206 	  char *msg =
2207 	      sqlite3_mprintf ("ST_ValidSpatialNet exception: %s", errMsg);
2208 	  gaianet_set_last_error_msg (accessor, msg);
2209 	  sqlite3_free (msg);
2210 	  sqlite3_free (errMsg);
2211 	  return 0;
2212       }
2213 
2214     return 1;
2215 }
2216 
2217 static int
do_spatnet_check_nodes(GaiaNetworkAccessorPtr accessor,sqlite3_stmt * stmt)2218 do_spatnet_check_nodes (GaiaNetworkAccessorPtr accessor, sqlite3_stmt * stmt)
2219 {
2220 /* checking for nodes without geometry */
2221     char *sql;
2222     char *table;
2223     char *xtable;
2224     int ret;
2225     sqlite3_stmt *stmt_in = NULL;
2226     struct gaia_network *net = (struct gaia_network *) accessor;
2227 
2228     table = sqlite3_mprintf ("%s_node", net->network_name);
2229     xtable = gaiaDoubleQuotedSql (table);
2230     sqlite3_free (table);
2231     sql =
2232 	sqlite3_mprintf
2233 	("SELECT node_id FROM MAIN.\"%s\" WHERE geometry IS NULL", xtable);
2234     free (xtable);
2235     ret =
2236 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_in, NULL);
2237     sqlite3_free (sql);
2238     if (ret != SQLITE_OK)
2239       {
2240 	  char *msg =
2241 	      sqlite3_mprintf ("ST_ValidSpatialNet() - Nodes error: \"%s\"",
2242 			       sqlite3_errmsg (net->db_handle));
2243 	  gaianet_set_last_error_msg (accessor, msg);
2244 	  sqlite3_free (msg);
2245 	  goto error;
2246       }
2247 
2248     sqlite3_reset (stmt_in);
2249     sqlite3_clear_bindings (stmt_in);
2250     while (1)
2251       {
2252 	  /* scrolling the result set rows */
2253 	  ret = sqlite3_step (stmt_in);
2254 	  if (ret == SQLITE_DONE)
2255 	      break;		/* end of result set */
2256 	  if (ret == SQLITE_ROW)
2257 	    {
2258 		sqlite3_int64 node_id = sqlite3_column_int64 (stmt_in, 0);
2259 		/* reporting the error */
2260 		sqlite3_reset (stmt);
2261 		sqlite3_clear_bindings (stmt);
2262 		sqlite3_bind_text (stmt, 1, "missing node geometry", -1,
2263 				   SQLITE_STATIC);
2264 		sqlite3_bind_int64 (stmt, 2, node_id);
2265 		sqlite3_bind_null (stmt, 3);
2266 		ret = sqlite3_step (stmt);
2267 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
2268 		    ;
2269 		else
2270 		  {
2271 		      char *msg =
2272 			  sqlite3_mprintf
2273 			  ("ST_ValidSpatialNet() insert error: \"%s\"",
2274 			   sqlite3_errmsg (net->db_handle));
2275 		      gaianet_set_last_error_msg (accessor, msg);
2276 		      sqlite3_free (msg);
2277 		      goto error;
2278 		  }
2279 	    }
2280 	  else
2281 	    {
2282 		char *msg =
2283 		    sqlite3_mprintf
2284 		    ("ST_ValidSpatialNet() - Nodes step error: %s",
2285 		     sqlite3_errmsg (net->db_handle));
2286 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
2287 		sqlite3_free (msg);
2288 		goto error;
2289 	    }
2290       }
2291     sqlite3_finalize (stmt_in);
2292 
2293     return 1;
2294 
2295   error:
2296     if (stmt_in == NULL)
2297 	sqlite3_finalize (stmt_in);
2298     return 0;
2299 }
2300 
2301 static int
do_spatnet_check_links(GaiaNetworkAccessorPtr accessor,sqlite3_stmt * stmt)2302 do_spatnet_check_links (GaiaNetworkAccessorPtr accessor, sqlite3_stmt * stmt)
2303 {
2304 /* checking for links without geometry */
2305     char *sql;
2306     char *table;
2307     char *xtable;
2308     int ret;
2309     sqlite3_stmt *stmt_in = NULL;
2310     struct gaia_network *net = (struct gaia_network *) accessor;
2311 
2312     table = sqlite3_mprintf ("%s_link", net->network_name);
2313     xtable = gaiaDoubleQuotedSql (table);
2314     sqlite3_free (table);
2315     sql =
2316 	sqlite3_mprintf
2317 	("SELECT link_id FROM MAIN.\"%s\" WHERE geometry IS NULL", xtable);
2318     free (xtable);
2319     ret =
2320 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_in, NULL);
2321     sqlite3_free (sql);
2322     if (ret != SQLITE_OK)
2323       {
2324 	  char *msg =
2325 	      sqlite3_mprintf ("ST_ValidSpatialNet() - Links error: \"%s\"",
2326 			       sqlite3_errmsg (net->db_handle));
2327 	  gaianet_set_last_error_msg (accessor, msg);
2328 	  sqlite3_free (msg);
2329 	  goto error;
2330       }
2331 
2332     sqlite3_reset (stmt_in);
2333     sqlite3_clear_bindings (stmt_in);
2334     while (1)
2335       {
2336 	  /* scrolling the result set rows */
2337 	  ret = sqlite3_step (stmt_in);
2338 	  if (ret == SQLITE_DONE)
2339 	      break;		/* end of result set */
2340 	  if (ret == SQLITE_ROW)
2341 	    {
2342 		sqlite3_int64 link_id = sqlite3_column_int64 (stmt_in, 0);
2343 		/* reporting the error */
2344 		sqlite3_reset (stmt);
2345 		sqlite3_clear_bindings (stmt);
2346 		sqlite3_bind_text (stmt, 1, "missing link geometry", -1,
2347 				   SQLITE_STATIC);
2348 		sqlite3_bind_int64 (stmt, 2, link_id);
2349 		sqlite3_bind_null (stmt, 3);
2350 		ret = sqlite3_step (stmt);
2351 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
2352 		    ;
2353 		else
2354 		  {
2355 		      char *msg =
2356 			  sqlite3_mprintf
2357 			  ("ST_ValidSpatialNet() insert error: \"%s\"",
2358 			   sqlite3_errmsg (net->db_handle));
2359 		      gaianet_set_last_error_msg (accessor, msg);
2360 		      sqlite3_free (msg);
2361 		      goto error;
2362 		  }
2363 	    }
2364 	  else
2365 	    {
2366 		char *msg =
2367 		    sqlite3_mprintf
2368 		    ("ST_ValidSpatialNet() - Links step error: %s",
2369 		     sqlite3_errmsg (net->db_handle));
2370 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
2371 		sqlite3_free (msg);
2372 		goto error;
2373 	    }
2374       }
2375     sqlite3_finalize (stmt_in);
2376 
2377     return 1;
2378 
2379   error:
2380     if (stmt_in == NULL)
2381 	sqlite3_finalize (stmt_in);
2382     return 0;
2383 }
2384 
2385 static int
do_spatnet_check_start_nodes(GaiaNetworkAccessorPtr accessor,sqlite3_stmt * stmt)2386 do_spatnet_check_start_nodes (GaiaNetworkAccessorPtr accessor,
2387 			      sqlite3_stmt * stmt)
2388 {
2389 /* checking for links mismatching start nodes */
2390     char *sql;
2391     char *table;
2392     char *xtable1;
2393     char *xtable2;
2394     int ret;
2395     sqlite3_stmt *stmt_in = NULL;
2396     struct gaia_network *net = (struct gaia_network *) accessor;
2397 
2398     table = sqlite3_mprintf ("%s_link", net->network_name);
2399     xtable1 = gaiaDoubleQuotedSql (table);
2400     sqlite3_free (table);
2401     table = sqlite3_mprintf ("%s_node", net->network_name);
2402     xtable2 = gaiaDoubleQuotedSql (table);
2403     sqlite3_free (table);
2404     sql =
2405 	sqlite3_mprintf ("SELECT l.link_id, l.start_node FROM MAIN.\"%s\" AS l "
2406 			 "JOIN MAIN.\"%s\" AS n ON (l.start_node = n.node_id) "
2407 			 "WHERE ST_Disjoint(ST_StartPoint(l.geometry), n.geometry) = 1",
2408 			 xtable1, xtable2);
2409     free (xtable1);
2410     free (xtable2);
2411     ret =
2412 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_in, NULL);
2413     sqlite3_free (sql);
2414     if (ret != SQLITE_OK)
2415       {
2416 	  char *msg =
2417 	      sqlite3_mprintf
2418 	      ("ST_ValidSpatialNet() - StartNodes error: \"%s\"",
2419 	       sqlite3_errmsg (net->db_handle));
2420 	  gaianet_set_last_error_msg (accessor, msg);
2421 	  sqlite3_free (msg);
2422 	  goto error;
2423       }
2424 
2425     sqlite3_reset (stmt_in);
2426     sqlite3_clear_bindings (stmt_in);
2427     while (1)
2428       {
2429 	  /* scrolling the result set rows */
2430 	  ret = sqlite3_step (stmt_in);
2431 	  if (ret == SQLITE_DONE)
2432 	      break;		/* end of result set */
2433 	  if (ret == SQLITE_ROW)
2434 	    {
2435 		sqlite3_int64 link_id = sqlite3_column_int64 (stmt_in, 0);
2436 		sqlite3_int64 node_id = sqlite3_column_int64 (stmt_in, 1);
2437 		/* reporting the error */
2438 		sqlite3_reset (stmt);
2439 		sqlite3_clear_bindings (stmt);
2440 		sqlite3_bind_text (stmt, 1, "geometry start mismatch", -1,
2441 				   SQLITE_STATIC);
2442 		sqlite3_bind_int64 (stmt, 2, link_id);
2443 		sqlite3_bind_int64 (stmt, 3, node_id);
2444 		ret = sqlite3_step (stmt);
2445 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
2446 		    ;
2447 		else
2448 		  {
2449 		      char *msg =
2450 			  sqlite3_mprintf
2451 			  ("ST_ValidSpatialNet() insert error: \"%s\"",
2452 			   sqlite3_errmsg (net->db_handle));
2453 		      gaianet_set_last_error_msg (accessor, msg);
2454 		      sqlite3_free (msg);
2455 		      goto error;
2456 		  }
2457 	    }
2458 	  else
2459 	    {
2460 		char *msg =
2461 		    sqlite3_mprintf
2462 		    ("ST_ValidSpatialNet() - StartNodes step error: %s",
2463 		     sqlite3_errmsg (net->db_handle));
2464 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
2465 		sqlite3_free (msg);
2466 		goto error;
2467 	    }
2468       }
2469     sqlite3_finalize (stmt_in);
2470 
2471     return 1;
2472 
2473   error:
2474     if (stmt_in == NULL)
2475 	sqlite3_finalize (stmt_in);
2476     return 0;
2477 }
2478 
2479 static int
do_spatnet_check_end_nodes(GaiaNetworkAccessorPtr accessor,sqlite3_stmt * stmt)2480 do_spatnet_check_end_nodes (GaiaNetworkAccessorPtr accessor,
2481 			    sqlite3_stmt * stmt)
2482 {
2483 /* checking for links mismatching end nodes */
2484     char *sql;
2485     char *table;
2486     char *xtable1;
2487     char *xtable2;
2488     int ret;
2489     sqlite3_stmt *stmt_in = NULL;
2490     struct gaia_network *net = (struct gaia_network *) accessor;
2491 
2492     table = sqlite3_mprintf ("%s_link", net->network_name);
2493     xtable1 = gaiaDoubleQuotedSql (table);
2494     sqlite3_free (table);
2495     table = sqlite3_mprintf ("%s_node", net->network_name);
2496     xtable2 = gaiaDoubleQuotedSql (table);
2497     sqlite3_free (table);
2498     sql = sqlite3_mprintf ("SELECT l.link_id, l.end_node FROM MAIN.\"%s\" AS l "
2499 			   "JOIN MAIN.\"%s\" AS n ON (l.end_node = n.node_id) "
2500 			   "WHERE ST_Disjoint(ST_EndPoint(l.geometry), n.geometry) = 1",
2501 			   xtable1, xtable2);
2502     free (xtable1);
2503     free (xtable2);
2504     ret =
2505 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_in, NULL);
2506     sqlite3_free (sql);
2507     if (ret != SQLITE_OK)
2508       {
2509 	  char *msg =
2510 	      sqlite3_mprintf ("ST_ValidSpatialNet() - EndNodes error: \"%s\"",
2511 			       sqlite3_errmsg (net->db_handle));
2512 	  gaianet_set_last_error_msg (accessor, msg);
2513 	  sqlite3_free (msg);
2514 	  goto error;
2515       }
2516 
2517     sqlite3_reset (stmt_in);
2518     sqlite3_clear_bindings (stmt_in);
2519     while (1)
2520       {
2521 	  /* scrolling the result set rows */
2522 	  ret = sqlite3_step (stmt_in);
2523 	  if (ret == SQLITE_DONE)
2524 	      break;		/* end of result set */
2525 	  if (ret == SQLITE_ROW)
2526 	    {
2527 		sqlite3_int64 link_id = sqlite3_column_int64 (stmt_in, 0);
2528 		sqlite3_int64 node_id = sqlite3_column_int64 (stmt_in, 1);
2529 		/* reporting the error */
2530 		sqlite3_reset (stmt);
2531 		sqlite3_clear_bindings (stmt);
2532 		sqlite3_bind_text (stmt, 1, "geometry end mismatch", -1,
2533 				   SQLITE_STATIC);
2534 		sqlite3_bind_int64 (stmt, 2, link_id);
2535 		sqlite3_bind_int64 (stmt, 3, node_id);
2536 		ret = sqlite3_step (stmt);
2537 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
2538 		    ;
2539 		else
2540 		  {
2541 		      char *msg =
2542 			  sqlite3_mprintf
2543 			  ("ST_ValidSpatialNet() insert error: \"%s\"",
2544 			   sqlite3_errmsg (net->db_handle));
2545 		      gaianet_set_last_error_msg (accessor, msg);
2546 		      sqlite3_free (msg);
2547 		      goto error;
2548 		  }
2549 	    }
2550 	  else
2551 	    {
2552 		char *msg =
2553 		    sqlite3_mprintf
2554 		    ("ST_ValidSpatialNet() - EndNodes step error: %s",
2555 		     sqlite3_errmsg (net->db_handle));
2556 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
2557 		sqlite3_free (msg);
2558 		goto error;
2559 	    }
2560       }
2561     sqlite3_finalize (stmt_in);
2562 
2563     return 1;
2564 
2565   error:
2566     if (stmt_in == NULL)
2567 	sqlite3_finalize (stmt_in);
2568     return 0;
2569 }
2570 
2571 GAIANET_DECLARE int
gaiaValidSpatialNet(GaiaNetworkAccessorPtr accessor)2572 gaiaValidSpatialNet (GaiaNetworkAccessorPtr accessor)
2573 {
2574 /* generating a validity report for a given Spatial Network */
2575     char *table;
2576     char *xtable;
2577     char *sql;
2578     int ret;
2579     sqlite3_stmt *stmt = NULL;
2580     struct gaia_network *net = (struct gaia_network *) accessor;
2581     if (net == NULL)
2582 	return 0;
2583 
2584     if (!do_check_create_valid_spatialnet_table (accessor))
2585 	return 0;
2586 
2587     table = sqlite3_mprintf ("%s_valid_spatialnet", net->network_name);
2588     xtable = gaiaDoubleQuotedSql (table);
2589     sqlite3_free (table);
2590     sql =
2591 	sqlite3_mprintf
2592 	("INSERT INTO TEMP.\"%s\" (error, primitive1, primitive2) VALUES (?, ?, ?)",
2593 	 xtable);
2594     free (xtable);
2595     ret = sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt, NULL);
2596     sqlite3_free (sql);
2597     if (ret != SQLITE_OK)
2598       {
2599 	  char *msg = sqlite3_mprintf ("ST_ValidSpatialNet error: \"%s\"",
2600 				       sqlite3_errmsg (net->db_handle));
2601 	  gaianet_set_last_error_msg (accessor, msg);
2602 	  sqlite3_free (msg);
2603 	  goto error;
2604       }
2605 
2606     if (!do_spatnet_check_nodes (accessor, stmt))
2607 	goto error;
2608 
2609     if (!do_spatnet_check_links (accessor, stmt))
2610 	goto error;
2611 
2612     if (!do_spatnet_check_start_nodes (accessor, stmt))
2613 	goto error;
2614 
2615     if (!do_spatnet_check_end_nodes (accessor, stmt))
2616 	goto error;
2617 
2618     sqlite3_finalize (stmt);
2619     return 1;
2620 
2621   error:
2622     if (stmt != NULL)
2623 	sqlite3_finalize (stmt);
2624     return 0;
2625 }
2626 
2627 NETWORK_PRIVATE int
auxnet_insert_into_network(GaiaNetworkAccessorPtr accessor,gaiaGeomCollPtr geom)2628 auxnet_insert_into_network (GaiaNetworkAccessorPtr accessor,
2629 			    gaiaGeomCollPtr geom)
2630 {
2631 /* processing all individual geometry items */
2632     sqlite3_int64 ret;
2633     gaiaLinestringPtr ln;
2634     gaiaPoint pt;
2635     struct gaia_network *network = (struct gaia_network *) accessor;
2636     if (network == NULL)
2637 	return 0;
2638 
2639     ln = geom->FirstLinestring;
2640     while (ln != NULL)
2641       {
2642 	  /* looping on Linestrings items */
2643 	  int last = ln->Points - 1;
2644 	  double x;
2645 	  double y;
2646 	  double z = 0.0;
2647 	  double m = 0.0;
2648 	  sqlite3_int64 start_node;
2649 	  sqlite3_int64 end_node;
2650 	  LWN_LINE *lwn_line;
2651 	  lwn_ResetErrorMsg (network->lwn_iface);
2652 
2653 	  /* attempting to retrieve or insert the Start Node */
2654 	  if (geom->DimensionModel == GAIA_XY_Z)
2655 	    {
2656 		gaiaGetPointXYZ (ln->Coords, 0, &x, &y, &z);
2657 	    }
2658 	  else if (geom->DimensionModel == GAIA_XY_Z_M)
2659 	    {
2660 		gaiaGetPointXYZM (ln->Coords, 0, &x, &y, &z, &m);
2661 	    }
2662 	  else if (geom->DimensionModel == GAIA_XY_M)
2663 	    {
2664 		gaiaGetPointXYM (ln->Coords, 0, &x, &y, &m);
2665 	    }
2666 	  else
2667 	    {
2668 		gaiaGetPoint (ln->Coords, 0, &x, &y);
2669 	    }
2670 	  if (network->has_z)
2671 	    {
2672 		pt.DimensionModel = GAIA_XY_Z;
2673 		pt.X = x;
2674 		pt.Y = y;
2675 		pt.Z = z;
2676 	    }
2677 	  else
2678 	    {
2679 		pt.DimensionModel = GAIA_XY;
2680 		pt.X = x;
2681 		pt.Y = y;
2682 	    }
2683 	  start_node = gaiaGetNetNodeByPoint (accessor, &pt, 0.0);
2684 	  if (start_node < 0)
2685 	      start_node = gaiaAddIsoNetNode (accessor, &pt);
2686 	  if (start_node < 0)
2687 	    {
2688 		const char *msg = lwn_GetErrorMsg (network->lwn_iface);
2689 		gaianet_set_last_error_msg (accessor, msg);
2690 		goto error;
2691 	    }
2692 
2693 	  /* attempting to retrieve or insert the End Node */
2694 	  if (geom->DimensionModel == GAIA_XY_Z)
2695 	    {
2696 		gaiaGetPointXYZ (ln->Coords, last, &x, &y, &z);
2697 	    }
2698 	  else if (geom->DimensionModel == GAIA_XY_Z_M)
2699 	    {
2700 		gaiaGetPointXYZM (ln->Coords, last, &x, &y, &z, &m);
2701 	    }
2702 	  else if (geom->DimensionModel == GAIA_XY_M)
2703 	    {
2704 		gaiaGetPointXYM (ln->Coords, last, &x, &y, &m);
2705 	    }
2706 	  else
2707 	    {
2708 		gaiaGetPoint (ln->Coords, last, &x, &y);
2709 	    }
2710 	  if (network->has_z)
2711 	    {
2712 		pt.DimensionModel = GAIA_XY_Z;
2713 		pt.X = x;
2714 		pt.Y = y;
2715 		pt.Z = z;
2716 	    }
2717 	  else
2718 	    {
2719 		pt.DimensionModel = GAIA_XY;
2720 		pt.X = x;
2721 		pt.Y = y;
2722 	    }
2723 	  end_node = gaiaGetNetNodeByPoint (accessor, &pt, 0.0);
2724 	  if (end_node < 0)
2725 	      end_node = gaiaAddIsoNetNode (accessor, &pt);
2726 	  if (end_node < 0)
2727 	    {
2728 		const char *msg = lwn_GetErrorMsg (network->lwn_iface);
2729 		gaianet_set_last_error_msg (accessor, msg);
2730 		goto error;
2731 	    }
2732 
2733 	  lwn_line = gaianet_convert_linestring_to_lwnline (ln, network->srid,
2734 							    network->has_z);
2735 	  ret =
2736 	      lwn_AddLink ((LWN_NETWORK *) (network->lwn_network), start_node,
2737 			   end_node, lwn_line);
2738 	  lwn_free_line (lwn_line);
2739 	  if (ret <= 0)
2740 	    {
2741 		const char *msg = lwn_GetErrorMsg (network->lwn_iface);
2742 		gaianet_set_last_error_msg (accessor, msg);
2743 		goto error;
2744 	    }
2745 
2746 	  ln = ln->Next;
2747       }
2748 
2749     return 1;
2750 
2751   error:
2752     return 0;
2753 }
2754 
2755 GAIANET_DECLARE int
gaiaTopoNet_FromGeoTable(GaiaNetworkAccessorPtr accessor,const char * db_prefix,const char * table,const char * column)2756 gaiaTopoNet_FromGeoTable (GaiaNetworkAccessorPtr accessor,
2757 			  const char *db_prefix, const char *table,
2758 			  const char *column)
2759 {
2760 /* attempting to import a whole GeoTable into a Topoology-Network */
2761     struct gaia_network *net = (struct gaia_network *) accessor;
2762     sqlite3_stmt *stmt = NULL;
2763     int ret;
2764     char *sql;
2765     char *xprefix;
2766     char *xtable;
2767     char *xcolumn;
2768     int gpkg_amphibious = 0;
2769     int gpkg_mode = 0;
2770 
2771     if (net == NULL)
2772 	return 0;
2773     if (net->cache != NULL)
2774       {
2775 	  struct splite_internal_cache *cache =
2776 	      (struct splite_internal_cache *) (net->cache);
2777 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
2778 	  gpkg_mode = cache->gpkg_mode;
2779       }
2780 
2781 /* building the SQL statement */
2782     xprefix = gaiaDoubleQuotedSql (db_prefix);
2783     xtable = gaiaDoubleQuotedSql (table);
2784     xcolumn = gaiaDoubleQuotedSql (column);
2785     sql =
2786 	sqlite3_mprintf ("SELECT \"%s\" FROM \"%s\".\"%s\"", xcolumn,
2787 			 xprefix, xtable);
2788     free (xprefix);
2789     free (xtable);
2790     free (xcolumn);
2791     ret = sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt, NULL);
2792     sqlite3_free (sql);
2793     if (ret != SQLITE_OK)
2794       {
2795 	  char *msg = sqlite3_mprintf ("TopoNet_FromGeoTable error: \"%s\"",
2796 				       sqlite3_errmsg (net->db_handle));
2797 	  gaianet_set_last_error_msg (accessor, msg);
2798 	  sqlite3_free (msg);
2799 	  goto error;
2800       }
2801 
2802 /* setting up the prepared statement */
2803     sqlite3_reset (stmt);
2804     sqlite3_clear_bindings (stmt);
2805 
2806     while (1)
2807       {
2808 	  /* scrolling the result set rows */
2809 	  ret = sqlite3_step (stmt);
2810 	  if (ret == SQLITE_DONE)
2811 	      break;		/* end of result set */
2812 	  if (ret == SQLITE_ROW)
2813 	    {
2814 		if (sqlite3_column_type (stmt, 0) == SQLITE_NULL)
2815 		    continue;
2816 		if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB)
2817 		  {
2818 		      const unsigned char *blob = sqlite3_column_blob (stmt, 0);
2819 		      int blob_sz = sqlite3_column_bytes (stmt, 0);
2820 		      gaiaGeomCollPtr geom =
2821 			  gaiaFromSpatiaLiteBlobWkbEx (blob, blob_sz, gpkg_mode,
2822 						       gpkg_amphibious);
2823 		      if (geom != NULL)
2824 			{
2825 			    if (!auxnet_insert_into_network (accessor, geom))
2826 			      {
2827 				  gaiaFreeGeomColl (geom);
2828 				  goto error;
2829 			      }
2830 			    gaiaFreeGeomColl (geom);
2831 			}
2832 		      else
2833 			{
2834 			    char *msg =
2835 				sqlite3_mprintf
2836 				("TopoNet_FromGeoTable error: Invalid Geometry");
2837 			    gaianet_set_last_error_msg (accessor, msg);
2838 			    sqlite3_free (msg);
2839 			    goto error;
2840 			}
2841 		  }
2842 		else
2843 		  {
2844 		      char *msg =
2845 			  sqlite3_mprintf
2846 			  ("TopoNet_FromGeoTable error: not a BLOB value");
2847 		      gaianet_set_last_error_msg (accessor, msg);
2848 		      sqlite3_free (msg);
2849 		      goto error;
2850 		  }
2851 	    }
2852 	  else
2853 	    {
2854 		char *msg =
2855 		    sqlite3_mprintf ("TopoNet_FromGeoTable error: \"%s\"",
2856 				     sqlite3_errmsg (net->db_handle));
2857 		gaianet_set_last_error_msg (accessor, msg);
2858 		sqlite3_free (msg);
2859 		goto error;
2860 	    }
2861       }
2862 
2863     sqlite3_finalize (stmt);
2864     return 1;
2865 
2866   error:
2867     if (stmt != NULL)
2868 	sqlite3_finalize (stmt);
2869     return 0;
2870 }
2871 
2872 GAIANET_DECLARE gaiaGeomCollPtr
gaiaGetLinkSeed(GaiaNetworkAccessorPtr accessor,sqlite3_int64 link)2873 gaiaGetLinkSeed (GaiaNetworkAccessorPtr accessor, sqlite3_int64 link)
2874 {
2875 /* attempting to get a Point (seed) identifying a Network Link */
2876     struct gaia_network *net = (struct gaia_network *) accessor;
2877     sqlite3_stmt *stmt = NULL;
2878     int ret;
2879     char *sql;
2880     char *table;
2881     char *xtable;
2882     gaiaGeomCollPtr point = NULL;
2883     if (net == NULL)
2884 	return NULL;
2885 
2886 /* building the SQL statement */
2887     table = sqlite3_mprintf ("%s_link", net->network_name);
2888     xtable = gaiaDoubleQuotedSql (table);
2889     sqlite3_free (table);
2890     sql =
2891 	sqlite3_mprintf ("SELECT geometry FROM MAIN.\"%s\" WHERE link_id = ?",
2892 			 xtable);
2893     free (xtable);
2894     ret = sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt, NULL);
2895     sqlite3_free (sql);
2896     if (ret != SQLITE_OK)
2897       {
2898 	  char *msg = sqlite3_mprintf ("GetLinkSeed error: \"%s\"",
2899 				       sqlite3_errmsg (net->db_handle));
2900 	  gaianet_set_last_error_msg (accessor, msg);
2901 	  sqlite3_free (msg);
2902 	  goto error;
2903       }
2904 
2905 /* setting up the prepared statement */
2906     sqlite3_reset (stmt);
2907     sqlite3_clear_bindings (stmt);
2908     sqlite3_bind_int64 (stmt, 1, link);
2909 
2910     while (1)
2911       {
2912 	  /* scrolling the result set rows */
2913 	  ret = sqlite3_step (stmt);
2914 	  if (ret == SQLITE_DONE)
2915 	      break;		/* end of result set */
2916 	  if (ret == SQLITE_ROW)
2917 	    {
2918 		if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB)
2919 		  {
2920 		      const unsigned char *blob = sqlite3_column_blob (stmt, 0);
2921 		      int blob_sz = sqlite3_column_bytes (stmt, 0);
2922 		      gaiaGeomCollPtr geom =
2923 			  gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
2924 		      if (geom != NULL)
2925 			{
2926 			    int iv;
2927 			    double x;
2928 			    double y;
2929 			    double z = 0.0;
2930 			    double m = 0.0;
2931 			    gaiaLinestringPtr ln = geom->FirstLinestring;
2932 			    if (ln == NULL)
2933 			      {
2934 				  char *msg =
2935 				      sqlite3_mprintf
2936 				      ("TopoNet_GetLinkSeed error: Invalid Geometry");
2937 				  gaianet_set_last_error_msg (accessor, msg);
2938 				  sqlite3_free (msg);
2939 				  gaiaFreeGeomColl (geom);
2940 				  goto error;
2941 			      }
2942 			    iv = ln->Points / 2;
2943 			    if (ln->DimensionModel == GAIA_XY_Z)
2944 			      {
2945 				  gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z);
2946 			      }
2947 			    else if (ln->DimensionModel == GAIA_XY_M)
2948 			      {
2949 				  gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m);
2950 			      }
2951 			    else if (ln->DimensionModel == GAIA_XY_Z_M)
2952 			      {
2953 				  gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z,
2954 						    &m);
2955 			      }
2956 			    else
2957 			      {
2958 				  gaiaGetPoint (ln->Coords, iv, &x, &y);
2959 			      }
2960 			    gaiaFreeGeomColl (geom);
2961 			    if (net->has_z)
2962 			      {
2963 				  point = gaiaAllocGeomCollXYZ ();
2964 				  gaiaAddPointToGeomCollXYZ (point, x, y, z);
2965 			      }
2966 			    else
2967 			      {
2968 				  point = gaiaAllocGeomColl ();
2969 				  gaiaAddPointToGeomColl (point, x, y);
2970 			      }
2971 			    point->Srid = net->srid;
2972 			}
2973 		      else
2974 			{
2975 			    char *msg =
2976 				sqlite3_mprintf
2977 				("TopoNet_GetLinkSeed error: Invalid Geometry");
2978 			    gaianet_set_last_error_msg (accessor, msg);
2979 			    sqlite3_free (msg);
2980 			    goto error;
2981 			}
2982 		  }
2983 		else
2984 		  {
2985 		      char *msg =
2986 			  sqlite3_mprintf
2987 			  ("TopoNet_GetLinkSeed error: not a BLOB value");
2988 		      gaianet_set_last_error_msg (accessor, msg);
2989 		      sqlite3_free (msg);
2990 		      goto error;
2991 		  }
2992 	    }
2993 	  else
2994 	    {
2995 		char *msg =
2996 		    sqlite3_mprintf ("TopoNet_GetLinkSeed error: \"%s\"",
2997 				     sqlite3_errmsg (net->db_handle));
2998 		gaianet_set_last_error_msg (accessor, msg);
2999 		sqlite3_free (msg);
3000 		goto error;
3001 	    }
3002       }
3003 
3004     sqlite3_finalize (stmt);
3005     return point;
3006 
3007   error:
3008     if (stmt != NULL)
3009 	sqlite3_finalize (stmt);
3010     return NULL;
3011 }
3012 
3013 static int
delete_all_seeds(struct gaia_network * net)3014 delete_all_seeds (struct gaia_network *net)
3015 {
3016 /* deleting all existing Seeds */
3017     char *table;
3018     char *xtable;
3019     char *sql;
3020     char *errMsg;
3021     int ret;
3022 
3023     table = sqlite3_mprintf ("%s_seeds", net->network_name);
3024     xtable = gaiaDoubleQuotedSql (table);
3025     sqlite3_free (table);
3026     sql = sqlite3_mprintf ("DELETE FROM MAIN.\"%s\"", xtable);
3027     free (xtable);
3028     ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
3029     sqlite3_free (sql);
3030     if (ret != SQLITE_OK)
3031       {
3032 	  char *msg =
3033 	      sqlite3_mprintf ("TopoNet_UpdateSeeds() error: \"%s\"", errMsg);
3034 	  sqlite3_free (errMsg);
3035 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3036 	  sqlite3_free (msg);
3037 	  return 0;
3038       }
3039     return 1;
3040 }
3041 
3042 static int
update_outdated_link_seeds(struct gaia_network * net)3043 update_outdated_link_seeds (struct gaia_network *net)
3044 {
3045 /* updating all outdated Link Seeds */
3046     char *table;
3047     char *xseeds;
3048     char *xlinks;
3049     char *sql;
3050     int ret;
3051     sqlite3_stmt *stmt_out;
3052     sqlite3_stmt *stmt_in;
3053 
3054 /* preparing the UPDATE statement */
3055     table = sqlite3_mprintf ("%s_seeds", net->network_name);
3056     xseeds = gaiaDoubleQuotedSql (table);
3057     sqlite3_free (table);
3058     sql = sqlite3_mprintf ("UPDATE MAIN.\"%s\" SET geometry = "
3059 			   "TopoNet_GetLinkSeed(%Q, link_id) WHERE link_id = ?",
3060 			   xseeds, net->network_name);
3061     free (xseeds);
3062     ret =
3063 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_out, NULL);
3064     sqlite3_free (sql);
3065     if (ret != SQLITE_OK)
3066       {
3067 	  char *msg = sqlite3_mprintf ("TopoNet_UpdateSeeds() error: \"%s\"",
3068 				       sqlite3_errmsg (net->db_handle));
3069 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3070 	  sqlite3_free (msg);
3071 	  goto error;
3072       }
3073 
3074 /* preparing the SELECT statement */
3075     table = sqlite3_mprintf ("%s_seeds", net->network_name);
3076     xseeds = gaiaDoubleQuotedSql (table);
3077     sqlite3_free (table);
3078     table = sqlite3_mprintf ("%s_link", net->network_name);
3079     xlinks = gaiaDoubleQuotedSql (table);
3080     sqlite3_free (table);
3081     sql = sqlite3_mprintf ("SELECT s.link_id FROM MAIN.\"%s\" AS s "
3082 			   "JOIN MAIN.\"%s\" AS l ON (l.link_id = s.link_id) "
3083 			   "WHERE s.link_id IS NOT NULL AND l.timestamp > s.timestamp",
3084 			   xseeds, xlinks);
3085     free (xseeds);
3086     free (xlinks);
3087     ret =
3088 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_in, NULL);
3089     sqlite3_free (sql);
3090     if (ret != SQLITE_OK)
3091       {
3092 	  char *msg = sqlite3_mprintf ("TopoNet_UpdateSeeds() error: \"%s\"",
3093 				       sqlite3_errmsg (net->db_handle));
3094 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3095 	  sqlite3_free (msg);
3096 	  goto error;
3097       }
3098 
3099     sqlite3_reset (stmt_in);
3100     sqlite3_clear_bindings (stmt_in);
3101     while (1)
3102       {
3103 	  /* scrolling the result set rows */
3104 	  ret = sqlite3_step (stmt_in);
3105 	  if (ret == SQLITE_DONE)
3106 	      break;		/* end of result set */
3107 	  if (ret == SQLITE_ROW)
3108 	    {
3109 		sqlite3_reset (stmt_out);
3110 		sqlite3_clear_bindings (stmt_out);
3111 		sqlite3_bind_int64 (stmt_out, 1,
3112 				    sqlite3_column_int64 (stmt_in, 0));
3113 		/* updating the Seeds table */
3114 		ret = sqlite3_step (stmt_out);
3115 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3116 		    ;
3117 		else
3118 		  {
3119 		      char *msg =
3120 			  sqlite3_mprintf
3121 			  ("TopoNet_UpdateSeeds() error: \"%s\"",
3122 			   sqlite3_errmsg (net->db_handle));
3123 		      gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr)
3124 						  net, msg);
3125 		      sqlite3_free (msg);
3126 		      goto error;
3127 		  }
3128 	    }
3129 	  else
3130 	    {
3131 		char *msg =
3132 		    sqlite3_mprintf ("TopoNet_UpdateSeeds() error: \"%s\"",
3133 				     sqlite3_errmsg (net->db_handle));
3134 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3135 		sqlite3_free (msg);
3136 		goto error;
3137 	    }
3138       }
3139 
3140     sqlite3_finalize (stmt_in);
3141     sqlite3_finalize (stmt_out);
3142     return 1;
3143 
3144   error:
3145     if (stmt_in != NULL)
3146 	sqlite3_finalize (stmt_in);
3147     if (stmt_out != NULL)
3148 	sqlite3_finalize (stmt_out);
3149     return 0;
3150 }
3151 
3152 GAIANET_DECLARE int
gaiaTopoNetUpdateSeeds(GaiaNetworkAccessorPtr accessor,int incremental_mode)3153 gaiaTopoNetUpdateSeeds (GaiaNetworkAccessorPtr accessor, int incremental_mode)
3154 {
3155 /* updating all TopoNet Seeds */
3156     char *table;
3157     char *xseeds;
3158     char *xlinks;
3159     char *sql;
3160     char *errMsg;
3161     int ret;
3162     struct gaia_network *net = (struct gaia_network *) accessor;
3163     if (net == NULL)
3164 	return 0;
3165 
3166     if (!incremental_mode)
3167       {
3168 	  /* deleting all existing Seeds */
3169 	  if (!delete_all_seeds (net))
3170 	      return 0;
3171       }
3172 
3173 /* paranoid precaution: deleting all orphan Link Seeds */
3174     table = sqlite3_mprintf ("%s_seeds", net->network_name);
3175     xseeds = gaiaDoubleQuotedSql (table);
3176     sqlite3_free (table);
3177     table = sqlite3_mprintf ("%s_link", net->network_name);
3178     xlinks = gaiaDoubleQuotedSql (table);
3179     sqlite3_free (table);
3180     sql = sqlite3_mprintf ("DELETE FROM MAIN.\"%s\" WHERE link_id IN ("
3181 			   "SELECT s.link_id FROM MAIN.\"%s\" AS s "
3182 			   "LEFT JOIN MAIN.\"%s\" AS l ON (s.link_id = l.link_id) "
3183 			   "WHERE l.link_id IS NULL)", xseeds, xseeds, xlinks);
3184     free (xseeds);
3185     free (xlinks);
3186     ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
3187     sqlite3_free (sql);
3188     if (ret != SQLITE_OK)
3189       {
3190 	  char *msg =
3191 	      sqlite3_mprintf ("TopoNet_UpdateSeeds() error: \"%s\"", errMsg);
3192 	  sqlite3_free (errMsg);
3193 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3194 	  sqlite3_free (msg);
3195 	  return 0;
3196       }
3197 
3198 /* updating all outdated Link Seeds */
3199     if (!update_outdated_link_seeds (net))
3200 	return 0;
3201 
3202 /* inserting all missing Link Seeds */
3203     table = sqlite3_mprintf ("%s_seeds", net->network_name);
3204     xseeds = gaiaDoubleQuotedSql (table);
3205     sqlite3_free (table);
3206     table = sqlite3_mprintf ("%s_link", net->network_name);
3207     xlinks = gaiaDoubleQuotedSql (table);
3208     sqlite3_free (table);
3209     sql =
3210 	sqlite3_mprintf
3211 	("INSERT INTO MAIN.\"%s\" (seed_id, link_id, geometry) "
3212 	 "SELECT NULL, l.link_id, TopoNet_GetLinkSeed(%Q, l.link_id) "
3213 	 "FROM MAIN.\"%s\" AS l "
3214 	 "LEFT JOIN MAIN.\"%s\" AS s ON (l.link_id = s.link_id) WHERE s.link_id IS NULL",
3215 	 xseeds, net->network_name, xlinks, xseeds);
3216     free (xseeds);
3217     free (xlinks);
3218     ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
3219     sqlite3_free (sql);
3220     if (ret != SQLITE_OK)
3221       {
3222 	  char *msg =
3223 	      sqlite3_mprintf ("TopoNet_UpdateSeeds() error: \"%s\"", errMsg);
3224 	  sqlite3_free (errMsg);
3225 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3226 	  sqlite3_free (msg);
3227 	  return 0;
3228       }
3229 
3230     return 1;
3231 }
3232 
3233 static gaiaGeomCollPtr
do_interpolate_middlepoint(gaiaGeomCollPtr geom)3234 do_interpolate_middlepoint (gaiaGeomCollPtr geom)
3235 {
3236 /* building a three-point segment */
3237     gaiaGeomCollPtr newg;
3238     gaiaLinestringPtr old_ln;
3239     gaiaLinestringPtr new_ln;
3240     double x0;
3241     double y0;
3242     double z0;
3243     double x1;
3244     double y1;
3245     double z1;
3246     double mx;
3247     double my;
3248     double mz;
3249 
3250     if (geom == NULL)
3251 	return NULL;
3252     if (geom->FirstPoint != NULL || geom->FirstPolygon != NULL)
3253 	return NULL;
3254     if (geom->FirstLinestring != geom->LastLinestring)
3255 	return NULL;
3256     old_ln = geom->FirstLinestring;
3257     if (old_ln == NULL)
3258 	return NULL;
3259     if (old_ln->Points != 2)
3260 	return NULL;
3261 
3262     if (geom->DimensionModel == GAIA_XY_Z)
3263       {
3264 	  gaiaGetPointXYZ (old_ln->Coords, 0, &x0, &y0, &z0);
3265 	  gaiaGetPointXYZ (old_ln->Coords, 1, &x1, &y1, &z1);
3266 	  newg = gaiaAllocGeomCollXYZ ();
3267       }
3268     else
3269       {
3270 	  gaiaGetPoint (old_ln->Coords, 0, &x0, &y0);
3271 	  gaiaGetPoint (old_ln->Coords, 1, &x1, &y1);
3272 	  newg = gaiaAllocGeomColl ();
3273       }
3274     newg->Srid = geom->Srid;
3275 
3276     if (x0 > x1)
3277 	mx = x1 + ((x0 - x1) / 2.0);
3278     else
3279 	mx = x0 + ((x1 - x0) / 2.0);
3280     if (y0 > y1)
3281 	my = y1 + ((y0 - y1) / 2.0);
3282     else
3283 	my = y0 + ((y1 - y0) / 2.0);
3284     if (geom->DimensionModel == GAIA_XY_Z)
3285       {
3286 	  if (z0 > z1)
3287 	      mz = z1 + ((z0 - z1) / 2.0);
3288 	  else
3289 	      mz = z0 + ((z1 - z0) / 2.0);
3290       }
3291 
3292     new_ln = gaiaAddLinestringToGeomColl (newg, 3);
3293     if (newg->DimensionModel == GAIA_XY_Z)
3294       {
3295 	  gaiaSetPointXYZ (new_ln->Coords, 0, x0, y0, z0);
3296 	  gaiaSetPointXYZ (new_ln->Coords, 1, mx, my, mz);
3297 	  gaiaSetPointXYZ (new_ln->Coords, 2, x1, y1, z1);
3298       }
3299     else
3300       {
3301 	  gaiaSetPoint (new_ln->Coords, 0, x0, y0);
3302 	  gaiaSetPoint (new_ln->Coords, 1, mx, my);
3303 	  gaiaSetPoint (new_ln->Coords, 2, x1, y1);
3304       }
3305 
3306     return newg;
3307 }
3308 
3309 GAIANET_DECLARE int
gaiaTopoNet_DisambiguateSegmentLinks(GaiaNetworkAccessorPtr accessor)3310 gaiaTopoNet_DisambiguateSegmentLinks (GaiaNetworkAccessorPtr accessor)
3311 {
3312 /*
3313 / Ensures that all Links on a Topology-Network will have not less
3314 / than three vertices; for all Links found beign simple two-points
3315 / segments a third intermediate point will be interpolated.
3316 */
3317     struct gaia_network *net = (struct gaia_network *) accessor;
3318     int ret;
3319     char *sql;
3320     char *link;
3321     char *xlink;
3322     sqlite3_stmt *stmt_in = NULL;
3323     sqlite3_stmt *stmt_out = NULL;
3324     int count = 0;
3325     if (net == NULL)
3326 	return -1;
3327 
3328 /* preparing the SQL query identifying all two-points Links */
3329     link = sqlite3_mprintf ("%s_link", net->network_name);
3330     xlink = gaiaDoubleQuotedSql (link);
3331     sqlite3_free (link);
3332     sql =
3333 	sqlite3_mprintf
3334 	("SELECT link_id, geometry FROM \"%s\" WHERE ST_NumPoints(geometry) = 2 "
3335 	 "ORDER BY link_id", xlink);
3336     free (xlink);
3337     ret =
3338 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_in, NULL);
3339     sqlite3_free (sql);
3340     if (ret != SQLITE_OK)
3341       {
3342 	  char *msg =
3343 	      sqlite3_mprintf ("TopoNet_DisambiguateSegmentLinks error: \"%s\"",
3344 			       sqlite3_errmsg (net->db_handle));
3345 	  gaianet_set_last_error_msg (accessor, msg);
3346 	  sqlite3_free (msg);
3347 	  goto error;
3348       }
3349 
3350 /* preparing the UPDATE SQL query */
3351     sql =
3352 	sqlite3_mprintf ("SELECT ST_ChangeLinkGeom(%Q, ?, ?)",
3353 			 net->network_name);
3354     ret =
3355 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_out, NULL);
3356     sqlite3_free (sql);
3357     if (ret != SQLITE_OK)
3358       {
3359 	  char *msg =
3360 	      sqlite3_mprintf ("TopoNet_DisambiguateSegmentLinks error: \"%s\"",
3361 			       sqlite3_errmsg (net->db_handle));
3362 	  gaianet_set_last_error_msg (accessor, msg);
3363 	  sqlite3_free (msg);
3364 	  goto error;
3365       }
3366 
3367     while (1)
3368       {
3369 	  /* scrolling the result set rows */
3370 	  ret = sqlite3_step (stmt_in);
3371 	  if (ret == SQLITE_DONE)
3372 	      break;		/* end of result set */
3373 	  if (ret == SQLITE_ROW)
3374 	    {
3375 		sqlite3_int64 link_id = sqlite3_column_int64 (stmt_in, 0);
3376 		if (sqlite3_column_type (stmt_in, 1) == SQLITE_BLOB)
3377 		  {
3378 		      const unsigned char *blob =
3379 			  sqlite3_column_blob (stmt_in, 1);
3380 		      int blob_sz = sqlite3_column_bytes (stmt_in, 1);
3381 		      gaiaGeomCollPtr geom =
3382 			  gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
3383 		      if (geom != NULL)
3384 			{
3385 			    gaiaGeomCollPtr newg =
3386 				do_interpolate_middlepoint (geom);
3387 			    gaiaFreeGeomColl (geom);
3388 			    if (newg != NULL)
3389 			      {
3390 				  unsigned char *outblob = NULL;
3391 				  int outblob_size = 0;
3392 				  sqlite3_reset (stmt_out);
3393 				  sqlite3_clear_bindings (stmt_out);
3394 				  sqlite3_bind_int64 (stmt_out, 1, link_id);
3395 				  gaiaToSpatiaLiteBlobWkb (newg, &outblob,
3396 							   &outblob_size);
3397 				  gaiaFreeGeomColl (newg);
3398 				  if (blob == NULL)
3399 				      continue;
3400 				  else
3401 				      sqlite3_bind_blob (stmt_out, 2, outblob,
3402 							 outblob_size, free);
3403 				  /* updating the Links table */
3404 				  ret = sqlite3_step (stmt_out);
3405 				  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3406 				      count++;
3407 				  else
3408 				    {
3409 					char *msg =
3410 					    sqlite3_mprintf
3411 					    ("TopoNet_DisambiguateSegmentLinks() error: \"%s\"",
3412 					     sqlite3_errmsg (net->db_handle));
3413 					gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3414 					sqlite3_free (msg);
3415 					goto error;
3416 				    }
3417 			      }
3418 			}
3419 		  }
3420 	    }
3421 	  else
3422 	    {
3423 		char *msg =
3424 		    sqlite3_mprintf
3425 		    ("TopoNet_DisambiguateSegmentLinks error: \"%s\"",
3426 		     sqlite3_errmsg (net->db_handle));
3427 		gaianet_set_last_error_msg (accessor, msg);
3428 		sqlite3_free (msg);
3429 		goto error;
3430 	    }
3431       }
3432 
3433     sqlite3_finalize (stmt_in);
3434     sqlite3_finalize (stmt_out);
3435     return count;
3436 
3437   error:
3438     if (stmt_out != NULL)
3439 	sqlite3_finalize (stmt_in);
3440     if (stmt_out != NULL)
3441 	sqlite3_finalize (stmt_out);
3442     return -1;
3443 }
3444 
3445 static void
do_eval_toponet_point(struct gaia_network * net,gaiaGeomCollPtr result,gaiaGeomCollPtr reference,sqlite3_stmt * stmt_node)3446 do_eval_toponet_point (struct gaia_network *net, gaiaGeomCollPtr result,
3447 		       gaiaGeomCollPtr reference, sqlite3_stmt * stmt_node)
3448 {
3449 /* retrieving Points from Topology */
3450     int ret;
3451     unsigned char *p_blob;
3452     int n_bytes;
3453 
3454 /* initializing the Topo-Node query */
3455     gaiaToSpatiaLiteBlobWkb (reference, &p_blob, &n_bytes);
3456     sqlite3_reset (stmt_node);
3457     sqlite3_clear_bindings (stmt_node);
3458     sqlite3_bind_blob (stmt_node, 1, p_blob, n_bytes, SQLITE_TRANSIENT);
3459     sqlite3_bind_blob (stmt_node, 2, p_blob, n_bytes, SQLITE_TRANSIENT);
3460     free (p_blob);
3461 
3462     while (1)
3463       {
3464 	  /* scrolling the result set rows */
3465 	  ret = sqlite3_step (stmt_node);
3466 	  if (ret == SQLITE_DONE)
3467 	      break;		/* end of result set */
3468 	  if (ret == SQLITE_ROW)
3469 	    {
3470 		const unsigned char *blob = sqlite3_column_blob (stmt_node, 0);
3471 		int blob_sz = sqlite3_column_bytes (stmt_node, 0);
3472 		gaiaGeomCollPtr geom =
3473 		    gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
3474 		if (geom != NULL)
3475 		  {
3476 		      gaiaPointPtr pt = geom->FirstPoint;
3477 		      while (pt != NULL)
3478 			{
3479 			    /* copying all Points into the result Geometry */
3480 			    if (net->has_z)
3481 				gaiaAddPointToGeomCollXYZ (result, pt->X, pt->Y,
3482 							   pt->Z);
3483 			    else
3484 				gaiaAddPointToGeomColl (result, pt->X, pt->Y);
3485 			    pt = pt->Next;
3486 			}
3487 		      gaiaFreeGeomColl (geom);
3488 		  }
3489 	    }
3490 	  else
3491 	    {
3492 		char *msg = sqlite3_mprintf ("TopoNet_ToGeoTable error: \"%s\"",
3493 					     sqlite3_errmsg (net->db_handle));
3494 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3495 		sqlite3_free (msg);
3496 		return;
3497 	    }
3498       }
3499 }
3500 
3501 static void
do_collect_topo_links(struct gaia_network * net,gaiaGeomCollPtr sparse,sqlite3_stmt * stmt_link,sqlite3_int64 link_id)3502 do_collect_topo_links (struct gaia_network *net, gaiaGeomCollPtr sparse,
3503 		       sqlite3_stmt * stmt_link, sqlite3_int64 link_id)
3504 {
3505 /* collecting Link Geometries one by one */
3506     int ret;
3507 
3508 /* initializing the Topo-Link query */
3509     sqlite3_reset (stmt_link);
3510     sqlite3_clear_bindings (stmt_link);
3511     sqlite3_bind_int64 (stmt_link, 1, link_id);
3512 
3513     while (1)
3514       {
3515 	  /* scrolling the result set rows */
3516 	  ret = sqlite3_step (stmt_link);
3517 	  if (ret == SQLITE_DONE)
3518 	      break;		/* end of result set */
3519 	  if (ret == SQLITE_ROW)
3520 	    {
3521 		const unsigned char *blob = sqlite3_column_blob (stmt_link, 0);
3522 		int blob_sz = sqlite3_column_bytes (stmt_link, 0);
3523 		gaiaGeomCollPtr geom =
3524 		    gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
3525 		if (geom != NULL)
3526 		  {
3527 		      gaiaLinestringPtr ln = geom->FirstLinestring;
3528 		      while (ln != NULL)
3529 			{
3530 			    if (net->has_z)
3531 				auxtopo_copy_linestring3d (ln, sparse);
3532 			    else
3533 				auxtopo_copy_linestring (ln, sparse);
3534 			    ln = ln->Next;
3535 			}
3536 		      gaiaFreeGeomColl (geom);
3537 		  }
3538 	    }
3539 	  else
3540 	    {
3541 		char *msg = sqlite3_mprintf ("TopoNet_ToGeoTable error: \"%s\"",
3542 					     sqlite3_errmsg (net->db_handle));
3543 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3544 		sqlite3_free (msg);
3545 		return;
3546 	    }
3547       }
3548 }
3549 
3550 static void
do_eval_toponet_line(struct gaia_network * net,gaiaGeomCollPtr result,gaiaGeomCollPtr reference,sqlite3_stmt * stmt_seed_link,sqlite3_stmt * stmt_link)3551 do_eval_toponet_line (struct gaia_network *net, gaiaGeomCollPtr result,
3552 		      gaiaGeomCollPtr reference, sqlite3_stmt * stmt_seed_link,
3553 		      sqlite3_stmt * stmt_link)
3554 {
3555 /* retrieving Linestrings from Topology */
3556     int ret;
3557     unsigned char *p_blob;
3558     int n_bytes;
3559     gaiaGeomCollPtr sparse;
3560     gaiaGeomCollPtr rearranged;
3561     gaiaLinestringPtr ln;
3562 
3563     if (net->has_z)
3564 	sparse = gaiaAllocGeomCollXYZ ();
3565     else
3566 	sparse = gaiaAllocGeomColl ();
3567     sparse->Srid = net->srid;
3568 
3569 /* initializing the Topo-Seed-Link query */
3570     gaiaToSpatiaLiteBlobWkb (reference, &p_blob, &n_bytes);
3571     sqlite3_reset (stmt_seed_link);
3572     sqlite3_clear_bindings (stmt_seed_link);
3573     sqlite3_bind_blob (stmt_seed_link, 1, p_blob, n_bytes, SQLITE_TRANSIENT);
3574     sqlite3_bind_blob (stmt_seed_link, 2, p_blob, n_bytes, SQLITE_TRANSIENT);
3575     free (p_blob);
3576 
3577     while (1)
3578       {
3579 	  /* scrolling the result set rows */
3580 	  ret = sqlite3_step (stmt_seed_link);
3581 	  if (ret == SQLITE_DONE)
3582 	      break;		/* end of result set */
3583 	  if (ret == SQLITE_ROW)
3584 	    {
3585 		sqlite3_int64 link_id =
3586 		    sqlite3_column_int64 (stmt_seed_link, 0);
3587 		do_collect_topo_links (net, sparse, stmt_link, link_id);
3588 	    }
3589 	  else
3590 	    {
3591 		char *msg = sqlite3_mprintf ("TopoNet_ToGeoTable error: \"%s\"",
3592 					     sqlite3_errmsg (net->db_handle));
3593 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3594 		sqlite3_free (msg);
3595 		gaiaFreeGeomColl (sparse);
3596 		return;
3597 	    }
3598       }
3599 
3600 /* attempting to rearrange sparse lines */
3601     rearranged = gaiaLineMerge_r (net->cache, sparse);
3602     gaiaFreeGeomColl (sparse);
3603     if (rearranged == NULL)
3604 	return;
3605     ln = rearranged->FirstLinestring;
3606     while (ln != NULL)
3607       {
3608 	  if (net->has_z)
3609 	      auxtopo_copy_linestring3d (ln, result);
3610 	  else
3611 	      auxtopo_copy_linestring (ln, result);
3612 	  ln = ln->Next;
3613       }
3614     gaiaFreeGeomColl (rearranged);
3615 }
3616 
3617 static gaiaGeomCollPtr
do_eval_toponet_geom(struct gaia_network * net,gaiaGeomCollPtr geom,sqlite3_stmt * stmt_seed_link,sqlite3_stmt * stmt_node,sqlite3_stmt * stmt_link,int out_type)3618 do_eval_toponet_geom (struct gaia_network *net, gaiaGeomCollPtr geom,
3619 		      sqlite3_stmt * stmt_seed_link,
3620 		      sqlite3_stmt * stmt_node, sqlite3_stmt * stmt_link,
3621 		      int out_type)
3622 {
3623 /* retrieving Topology-Network geometries via matching Seeds */
3624     gaiaGeomCollPtr result;
3625 
3626     if (net->has_z)
3627 	result = gaiaAllocGeomCollXYZ ();
3628     else
3629 	result = gaiaAllocGeomColl ();
3630     result->Srid = net->srid;
3631     result->DeclaredType = out_type;
3632 
3633     if (out_type == GAIA_POINT || out_type == GAIA_MULTIPOINT
3634 	|| out_type == GAIA_GEOMETRYCOLLECTION || out_type == GAIA_UNKNOWN)
3635       {
3636 	  /* processing all Points */
3637 	  gaiaPointPtr pt = geom->FirstPoint;
3638 	  while (pt != NULL)
3639 	    {
3640 		gaiaPointPtr next = pt->Next;
3641 		gaiaGeomCollPtr reference = (gaiaGeomCollPtr)
3642 		    auxtopo_make_geom_from_point (net->srid, net->has_z, pt);
3643 		do_eval_toponet_point (net, result, reference, stmt_node);
3644 		auxtopo_destroy_geom_from (reference);
3645 		pt->Next = next;
3646 		pt = pt->Next;
3647 	    }
3648       }
3649 
3650     if (out_type == GAIA_MULTILINESTRING || out_type == GAIA_GEOMETRYCOLLECTION
3651 	|| out_type == GAIA_UNKNOWN)
3652       {
3653 	  /* processing all Linestrings */
3654 	  gaiaLinestringPtr ln = geom->FirstLinestring;
3655 	  while (ln != NULL)
3656 	    {
3657 		gaiaLinestringPtr next = ln->Next;
3658 		gaiaGeomCollPtr reference = (gaiaGeomCollPtr)
3659 		    auxtopo_make_geom_from_line (net->srid, ln);
3660 		do_eval_toponet_line (net, result, reference, stmt_seed_link,
3661 				      stmt_link);
3662 		auxtopo_destroy_geom_from (reference);
3663 		ln->Next = next;
3664 		ln = ln->Next;
3665 	    }
3666       }
3667 
3668     if (out_type == GAIA_MULTIPOLYGON || out_type == GAIA_GEOMETRYCOLLECTION
3669 	|| out_type == GAIA_UNKNOWN)
3670       {
3671 	  /* processing all Polygons */
3672 	  if (geom->FirstPolygon != NULL)
3673 	      goto error;
3674       }
3675 
3676     if (result->FirstPoint == NULL && result->FirstLinestring == NULL
3677 	&& result->FirstPolygon == NULL)
3678 	goto error;
3679     return result;
3680 
3681   error:
3682     gaiaFreeGeomColl (result);
3683     return NULL;
3684 }
3685 
3686 static int
do_eval_toponet_seeds(struct gaia_network * net,sqlite3_stmt * stmt_ref,int ref_geom_col,sqlite3_stmt * stmt_ins,sqlite3_stmt * stmt_seed_link,sqlite3_stmt * stmt_node,sqlite3_stmt * stmt_link,int out_type)3687 do_eval_toponet_seeds (struct gaia_network *net, sqlite3_stmt * stmt_ref,
3688 		       int ref_geom_col, sqlite3_stmt * stmt_ins,
3689 		       sqlite3_stmt * stmt_seed_link, sqlite3_stmt * stmt_node,
3690 		       sqlite3_stmt * stmt_link, int out_type)
3691 {
3692 /* querying the ref-table */
3693     int ret;
3694 
3695     sqlite3_reset (stmt_ref);
3696     sqlite3_clear_bindings (stmt_ref);
3697     while (1)
3698       {
3699 	  /* scrolling the result set rows */
3700 	  ret = sqlite3_step (stmt_ref);
3701 	  if (ret == SQLITE_DONE)
3702 	      break;		/* end of result set */
3703 	  if (ret == SQLITE_ROW)
3704 	    {
3705 		int icol;
3706 		int ncol = sqlite3_column_count (stmt_ref);
3707 		sqlite3_reset (stmt_ins);
3708 		sqlite3_clear_bindings (stmt_ins);
3709 		for (icol = 0; icol < ncol; icol++)
3710 		  {
3711 		      int col_type = sqlite3_column_type (stmt_ref, icol);
3712 		      if (icol == ref_geom_col)
3713 			{
3714 			    /* the geometry column */
3715 			    const unsigned char *blob =
3716 				sqlite3_column_blob (stmt_ref, icol);
3717 			    int blob_sz = sqlite3_column_bytes (stmt_ref, icol);
3718 			    gaiaGeomCollPtr geom =
3719 				gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
3720 			    if (geom != NULL)
3721 			      {
3722 				  gaiaGeomCollPtr result;
3723 				  unsigned char *p_blob;
3724 				  int n_bytes;
3725 				  int gpkg_mode = 0;
3726 				  int tiny_point = 0;
3727 				  if (net->cache != NULL)
3728 				    {
3729 					struct splite_internal_cache *cache =
3730 					    (struct splite_internal_cache
3731 					     *) (net->cache);
3732 					gpkg_mode = cache->gpkg_mode;
3733 					tiny_point = cache->tinyPointEnabled;
3734 				    }
3735 				  result =
3736 				      do_eval_toponet_geom (net, geom,
3737 							    stmt_seed_link,
3738 							    stmt_node,
3739 							    stmt_link,
3740 							    out_type);
3741 				  gaiaFreeGeomColl (geom);
3742 				  if (result != NULL)
3743 				    {
3744 					gaiaToSpatiaLiteBlobWkbEx2 (result,
3745 								    &p_blob,
3746 								    &n_bytes,
3747 								    gpkg_mode,
3748 								    tiny_point);
3749 					gaiaFreeGeomColl (result);
3750 					sqlite3_bind_blob (stmt_ins, icol + 1,
3751 							   p_blob, n_bytes,
3752 							   free);
3753 				    }
3754 				  else
3755 				      sqlite3_bind_null (stmt_ins, icol + 1);
3756 			      }
3757 			    else
3758 				sqlite3_bind_null (stmt_ins, icol + 1);
3759 			    continue;
3760 			}
3761 		      switch (col_type)
3762 			{
3763 			case SQLITE_INTEGER:
3764 			    sqlite3_bind_int64 (stmt_ins, icol + 1,
3765 						sqlite3_column_int64 (stmt_ref,
3766 								      icol));
3767 			    break;
3768 			case SQLITE_FLOAT:
3769 			    sqlite3_bind_double (stmt_ins, icol + 1,
3770 						 sqlite3_column_double
3771 						 (stmt_ref, icol));
3772 			    break;
3773 			case SQLITE_TEXT:
3774 			    sqlite3_bind_text (stmt_ins, icol + 1,
3775 					       (const char *)
3776 					       sqlite3_column_text (stmt_ref,
3777 								    icol),
3778 					       sqlite3_column_bytes (stmt_ref,
3779 								     icol),
3780 					       SQLITE_STATIC);
3781 			    break;
3782 			case SQLITE_BLOB:
3783 			    sqlite3_bind_blob (stmt_ins, icol + 1,
3784 					       sqlite3_column_blob (stmt_ref,
3785 								    icol),
3786 					       sqlite3_column_bytes (stmt_ref,
3787 								     icol),
3788 					       SQLITE_STATIC);
3789 			    break;
3790 			default:
3791 			    sqlite3_bind_null (stmt_ins, icol + 1);
3792 			    break;
3793 			};
3794 		  }
3795 		ret = sqlite3_step (stmt_ins);
3796 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3797 		    ;
3798 		else
3799 		  {
3800 		      char *msg =
3801 			  sqlite3_mprintf ("TopoNet_ToGeoTable() error: \"%s\"",
3802 					   sqlite3_errmsg (net->db_handle));
3803 		      gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr)
3804 						  net, msg);
3805 		      sqlite3_free (msg);
3806 		      return 0;
3807 		  }
3808 	    }
3809 	  else
3810 	    {
3811 		char *msg =
3812 		    sqlite3_mprintf ("TopoNet_ToGeoTable() error: \"%s\"",
3813 				     sqlite3_errmsg (net->db_handle));
3814 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3815 		sqlite3_free (msg);
3816 		return 0;
3817 	    }
3818       }
3819     return 1;
3820 }
3821 
3822 GAIANET_DECLARE int
gaiaTopoNet_ToGeoTableGeneralize(GaiaNetworkAccessorPtr accessor,const char * db_prefix,const char * ref_table,const char * ref_column,const char * out_table,double tolerance,int with_spatial_index)3823 gaiaTopoNet_ToGeoTableGeneralize (GaiaNetworkAccessorPtr accessor,
3824 				  const char *db_prefix, const char *ref_table,
3825 				  const char *ref_column, const char *out_table,
3826 				  double tolerance, int with_spatial_index)
3827 {
3828 /*
3829 / attempting to create and populate a new GeoTable out from a Topology-Network
3830 / (simplified/generalized version)
3831 */
3832     struct gaia_network *net = (struct gaia_network *) accessor;
3833     sqlite3_stmt *stmt_ref = NULL;
3834     sqlite3_stmt *stmt_ins = NULL;
3835     sqlite3_stmt *stmt_seed_link = NULL;
3836     sqlite3_stmt *stmt_node = NULL;
3837     sqlite3_stmt *stmt_link = NULL;
3838     int ret;
3839     char *create;
3840     char *select;
3841     char *insert;
3842     char *sql;
3843     char *errMsg;
3844     char *xprefix;
3845     char *xtable;
3846     int ref_type;
3847     const char *type;
3848     int out_type;
3849     int ref_geom_col;
3850     if (net == NULL)
3851 	return 0;
3852 
3853 /* incrementally updating all Topology Seeds */
3854     if (!gaiaTopoNetUpdateSeeds (accessor, 1))
3855 	return 0;
3856 
3857 /* composing the CREATE TABLE output-table statement */
3858     if (!auxtopo_create_togeotable_sql
3859 	(net->db_handle, db_prefix, ref_table, ref_column, out_table, &create,
3860 	 &select, &insert, &ref_geom_col))
3861 	goto error;
3862 
3863 /* creating the output-table */
3864     ret = sqlite3_exec (net->db_handle, create, NULL, NULL, &errMsg);
3865     sqlite3_free (create);
3866     create = NULL;
3867     if (ret != SQLITE_OK)
3868       {
3869 	  char *msg =
3870 	      sqlite3_mprintf ("TopoNet_ToGeoTable() error: \"%s\"", errMsg);
3871 	  sqlite3_free (errMsg);
3872 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3873 	  sqlite3_free (msg);
3874 	  goto error;
3875       }
3876 
3877 /* checking the Geometry Type */
3878     if (!auxtopo_retrieve_geometry_type
3879 	(net->db_handle, db_prefix, ref_table, ref_column, &ref_type))
3880 	goto error;
3881     switch (ref_type)
3882       {
3883       case GAIA_POINT:
3884       case GAIA_POINTZ:
3885       case GAIA_POINTM:
3886       case GAIA_POINTZM:
3887 	  type = "POINT";
3888 	  out_type = GAIA_POINT;
3889 	  break;
3890       case GAIA_MULTIPOINT:
3891       case GAIA_MULTIPOINTZ:
3892       case GAIA_MULTIPOINTM:
3893       case GAIA_MULTIPOINTZM:
3894 	  type = "MULTIPOINT";
3895 	  out_type = GAIA_MULTIPOINT;
3896 	  break;
3897       case GAIA_LINESTRING:
3898       case GAIA_LINESTRINGZ:
3899       case GAIA_LINESTRINGM:
3900       case GAIA_LINESTRINGZM:
3901       case GAIA_MULTILINESTRING:
3902       case GAIA_MULTILINESTRINGZ:
3903       case GAIA_MULTILINESTRINGM:
3904       case GAIA_MULTILINESTRINGZM:
3905 	  type = "MULTILINESTRING";
3906 	  out_type = GAIA_MULTILINESTRING;
3907 	  break;
3908       case GAIA_GEOMETRYCOLLECTION:
3909       case GAIA_GEOMETRYCOLLECTIONZ:
3910       case GAIA_GEOMETRYCOLLECTIONM:
3911       case GAIA_GEOMETRYCOLLECTIONZM:
3912 	  type = "GEOMETRYCOLLECTION";
3913 	  out_type = GAIA_GEOMETRYCOLLECTION;
3914 	  break;
3915       default:
3916 	  type = "GEOMETRY";
3917 	  out_type = GAIA_UNKNOWN;
3918 	  break;
3919       };
3920 
3921 /* creating the output Geometry Column */
3922     sql =
3923 	sqlite3_mprintf
3924 	("SELECT AddGeometryColumn(Lower(%Q), Lower(%Q), %d, '%s', '%s')",
3925 	 out_table, ref_column, net->srid, type,
3926 	 (net->has_z == 0) ? "XY" : "XYZ");
3927     ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
3928     sqlite3_free (sql);
3929     if (ret != SQLITE_OK)
3930       {
3931 	  char *msg =
3932 	      sqlite3_mprintf ("TopoNet_ToGeoTable() error: \"%s\"", errMsg);
3933 	  sqlite3_free (errMsg);
3934 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3935 	  sqlite3_free (msg);
3936 	  goto error;
3937       }
3938 
3939     if (with_spatial_index)
3940       {
3941 	  /* adding a Spatial Index supporting the Geometry Column */
3942 	  sql =
3943 	      sqlite3_mprintf
3944 	      ("SELECT CreateSpatialIndex(Lower(%Q), Lower(%Q))",
3945 	       out_table, ref_column);
3946 	  ret = sqlite3_exec (net->db_handle, sql, NULL, NULL, &errMsg);
3947 	  sqlite3_free (sql);
3948 	  if (ret != SQLITE_OK)
3949 	    {
3950 		char *msg =
3951 		    sqlite3_mprintf ("TopoGeo_ToGeoTable() error: \"%s\"",
3952 				     errMsg);
3953 		sqlite3_free (errMsg);
3954 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3955 		sqlite3_free (msg);
3956 		goto error;
3957 	    }
3958       }
3959 
3960 /* preparing the "SELECT * FROM ref-table" query */
3961     ret =
3962 	sqlite3_prepare_v2 (net->db_handle, select, strlen (select), &stmt_ref,
3963 			    NULL);
3964     sqlite3_free (select);
3965     select = NULL;
3966     if (ret != SQLITE_OK)
3967       {
3968 	  char *msg = sqlite3_mprintf ("TopoNet_ToGeoTable() error: \"%s\"",
3969 				       sqlite3_errmsg (net->db_handle));
3970 	  sqlite3_free (errMsg);
3971 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3972 	  sqlite3_free (msg);
3973 	  goto error;
3974       }
3975 
3976 /* preparing the "INSERT INTO out-table" query */
3977     ret =
3978 	sqlite3_prepare_v2 (net->db_handle, insert, strlen (insert), &stmt_ins,
3979 			    NULL);
3980     sqlite3_free (insert);
3981     insert = NULL;
3982     if (ret != SQLITE_OK)
3983       {
3984 	  char *msg = sqlite3_mprintf ("TopoNet_ToGeoTable() error: \"%s\"",
3985 				       sqlite3_errmsg (net->db_handle));
3986 	  sqlite3_free (errMsg);
3987 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
3988 	  sqlite3_free (msg);
3989 	  goto error;
3990       }
3991 
3992 /* preparing the Topo-Seed-Links query */
3993     xprefix = sqlite3_mprintf ("%s_seeds", net->network_name);
3994     xtable = gaiaDoubleQuotedSql (xprefix);
3995     sql = sqlite3_mprintf ("SELECT link_id FROM MAIN.\"%s\" "
3996 			   "WHERE ST_Intersects(geometry, ?) = 1 AND ROWID IN ("
3997 			   "SELECT ROWID FROM SpatialIndex WHERE f_table_name = %Q AND search_frame = ?)",
3998 			   xtable, xprefix);
3999     free (xtable);
4000     sqlite3_free (xprefix);
4001     ret =
4002 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_seed_link,
4003 			    NULL);
4004     sqlite3_free (sql);
4005     if (ret != SQLITE_OK)
4006       {
4007 	  char *msg = sqlite3_mprintf ("TopoNet_ToGeoTable() error: \"%s\"",
4008 				       sqlite3_errmsg (net->db_handle));
4009 	  sqlite3_free (errMsg);
4010 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
4011 	  sqlite3_free (msg);
4012 	  goto error;
4013       }
4014 
4015 /* preparing the Topo-Nodes query */
4016     xprefix = sqlite3_mprintf ("%s_node", net->network_name);
4017     xtable = gaiaDoubleQuotedSql (xprefix);
4018     sql = sqlite3_mprintf ("SELECT geometry FROM MAIN.\"%s\" "
4019 			   "WHERE ST_Intersects(geometry, ?) = 1 AND ROWID IN ("
4020 			   "SELECT ROWID FROM SpatialIndex WHERE f_table_name = %Q AND search_frame = ?)",
4021 			   xtable, xprefix);
4022     free (xtable);
4023     sqlite3_free (xprefix);
4024     ret =
4025 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_node,
4026 			    NULL);
4027     sqlite3_free (sql);
4028     if (ret != SQLITE_OK)
4029       {
4030 	  char *msg = sqlite3_mprintf ("TopoNet_ToGeoTable() error: \"%s\"",
4031 				       sqlite3_errmsg (net->db_handle));
4032 	  sqlite3_free (errMsg);
4033 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
4034 	  sqlite3_free (msg);
4035 	  goto error;
4036       }
4037 
4038 /* preparing the Topo-Links query */
4039     xprefix = sqlite3_mprintf ("%s_link", net->network_name);
4040     xtable = gaiaDoubleQuotedSql (xprefix);
4041     if (tolerance > 0.0)
4042 	sql =
4043 	    sqlite3_mprintf
4044 	    ("SELECT ST_SimplifyPreserveTopology(geometry, %1.6f) FROM MAIN.\"%s\" WHERE link_id = ?",
4045 	     tolerance, xtable, xprefix);
4046     else
4047 	sql =
4048 	    sqlite3_mprintf
4049 	    ("SELECT geometry FROM MAIN.\"%s\" WHERE link_id = ?", xtable,
4050 	     xprefix);
4051     free (xtable);
4052     sqlite3_free (xprefix);
4053     ret =
4054 	sqlite3_prepare_v2 (net->db_handle, sql, strlen (sql), &stmt_link,
4055 			    NULL);
4056     sqlite3_free (sql);
4057     if (ret != SQLITE_OK)
4058       {
4059 	  char *msg = sqlite3_mprintf ("TopoNet_ToGeoTable() error: \"%s\"",
4060 				       sqlite3_errmsg (net->db_handle));
4061 	  sqlite3_free (errMsg);
4062 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) net, msg);
4063 	  sqlite3_free (msg);
4064 	  goto error;
4065       }
4066 
4067 /* evaluating feature/topology matching via coincident topo-seeds */
4068     if (!do_eval_toponet_seeds
4069 	(net, stmt_ref, ref_geom_col, stmt_ins, stmt_seed_link, stmt_node,
4070 	 stmt_link, out_type))
4071 	goto error;
4072 
4073     sqlite3_finalize (stmt_ref);
4074     sqlite3_finalize (stmt_ins);
4075     sqlite3_finalize (stmt_seed_link);
4076     sqlite3_finalize (stmt_node);
4077     sqlite3_finalize (stmt_link);
4078     return 1;
4079 
4080   error:
4081     if (create != NULL)
4082 	sqlite3_free (create);
4083     if (select != NULL)
4084 	sqlite3_free (select);
4085     if (insert != NULL)
4086 	sqlite3_free (insert);
4087     if (stmt_ref != NULL)
4088 	sqlite3_finalize (stmt_ref);
4089     if (stmt_ins != NULL)
4090 	sqlite3_finalize (stmt_ins);
4091     if (stmt_seed_link != NULL)
4092 	sqlite3_finalize (stmt_seed_link);
4093     if (stmt_node != NULL)
4094 	sqlite3_finalize (stmt_node);
4095     if (stmt_link != NULL)
4096 	sqlite3_finalize (stmt_link);
4097     return 0;
4098 }
4099 
4100 GAIANET_DECLARE int
gaiaTopoNet_ToGeoTable(GaiaNetworkAccessorPtr accessor,const char * db_prefix,const char * ref_table,const char * ref_column,const char * out_table,int with_spatial_index)4101 gaiaTopoNet_ToGeoTable (GaiaNetworkAccessorPtr accessor,
4102 			const char *db_prefix, const char *ref_table,
4103 			const char *ref_column, const char *out_table,
4104 			int with_spatial_index)
4105 {
4106 /* attempting to create and populate a new GeoTable out from a Topology-Network */
4107     return gaiaTopoNet_ToGeoTableGeneralize (accessor, db_prefix, ref_table,
4108 					     ref_column, out_table, -1.0,
4109 					     with_spatial_index);
4110 }
4111 
4112 static int
find_linelink_matches(struct gaia_network * network,sqlite3_stmt * stmt_ref,sqlite3_stmt * stmt_ins,sqlite3_int64 link_id,const unsigned char * blob,int blob_sz)4113 find_linelink_matches (struct gaia_network *network,
4114 		       sqlite3_stmt * stmt_ref, sqlite3_stmt * stmt_ins,
4115 		       sqlite3_int64 link_id, const unsigned char *blob,
4116 		       int blob_sz)
4117 {
4118 /* retrieving LineLink relationships */
4119     int ret;
4120     int count = 0;
4121     char direction[2];
4122     strcpy (direction, "?");
4123 
4124     sqlite3_reset (stmt_ref);
4125     sqlite3_clear_bindings (stmt_ref);
4126     sqlite3_bind_int64 (stmt_ref, 1, link_id);
4127 
4128     while (1)
4129       {
4130 	  /* scrolling the result set rows - Spatial Relationships */
4131 	  ret = sqlite3_step (stmt_ref);
4132 	  if (ret == SQLITE_DONE)
4133 	      break;		/* end of result set */
4134 	  if (ret == SQLITE_ROW)
4135 	    {
4136 		sqlite3_int64 rowid = sqlite3_column_int64 (stmt_ref, 0);
4137 
4138 		if (sqlite3_column_type (stmt_ref, 1) == SQLITE_BLOB)
4139 		  {
4140 		      /* testing directions */
4141 		      gaiaGeomCollPtr geom_link = NULL;
4142 		      gaiaGeomCollPtr geom_line = NULL;
4143 		      const unsigned char *blob2 =
4144 			  sqlite3_column_blob (stmt_ref, 1);
4145 		      int blob2_sz = sqlite3_column_bytes (stmt_ref, 1);
4146 		      geom_link = gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
4147 		      geom_line = gaiaFromSpatiaLiteBlobWkb (blob2, blob2_sz);
4148 		      if (geom_link != NULL && geom_line != NULL)
4149 			  gaia_do_check_direction (geom_link, geom_line,
4150 						   direction);
4151 		      if (geom_link != NULL)
4152 			  gaiaFreeGeomColl (geom_link);
4153 		      if (geom_line != NULL)
4154 			  gaiaFreeGeomColl (geom_line);
4155 		  }
4156 
4157 		sqlite3_reset (stmt_ins);
4158 		sqlite3_clear_bindings (stmt_ins);
4159 		sqlite3_bind_int64 (stmt_ins, 1, link_id);
4160 		sqlite3_bind_int64 (stmt_ins, 2, rowid);
4161 		sqlite3_bind_text (stmt_ins, 3, direction, 1, SQLITE_STATIC);
4162 		/* inserting a row into the output table */
4163 		ret = sqlite3_step (stmt_ins);
4164 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4165 		    count++;
4166 		else
4167 		  {
4168 		      char *msg =
4169 			  sqlite3_mprintf ("LineLinksList error: \"%s\"",
4170 					   sqlite3_errmsg (network->db_handle));
4171 		      gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr)
4172 						  network, msg);
4173 		      sqlite3_free (msg);
4174 		      return 0;
4175 		  }
4176 	    }
4177 	  else
4178 	    {
4179 		char *msg = sqlite3_mprintf ("LineLinksList error: \"%s\"",
4180 					     sqlite3_errmsg
4181 					     (network->db_handle));
4182 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) network,
4183 					    msg);
4184 		sqlite3_free (msg);
4185 		return 0;
4186 	    }
4187       }
4188 
4189     if (count == 0)
4190       {
4191 	  /* unrelated Link */
4192 	  sqlite3_reset (stmt_ins);
4193 	  sqlite3_clear_bindings (stmt_ins);
4194 	  sqlite3_bind_int64 (stmt_ins, 1, link_id);
4195 	  sqlite3_bind_null (stmt_ins, 2);
4196 	  sqlite3_bind_null (stmt_ins, 3);
4197 	  /* inserting a row into the output table */
4198 	  ret = sqlite3_step (stmt_ins);
4199 	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4200 	      ;
4201 	  else
4202 	    {
4203 		char *msg = sqlite3_mprintf ("LineLinksList error: \"%s\"",
4204 					     sqlite3_errmsg
4205 					     (network->db_handle));
4206 		gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) network,
4207 					    msg);
4208 		sqlite3_free (msg);
4209 		return 0;
4210 	    }
4211       }
4212     return 1;
4213 }
4214 
4215 static int
insert_linelink_reverse(struct gaia_network * network,sqlite3_stmt * stmt_ins,sqlite3_int64 polygon_id)4216 insert_linelink_reverse (struct gaia_network *network, sqlite3_stmt * stmt_ins,
4217 			 sqlite3_int64 polygon_id)
4218 {
4219 /* found a mismatching RefLinestring - inserting into the output table */
4220     int ret;
4221 
4222     sqlite3_reset (stmt_ins);
4223     sqlite3_clear_bindings (stmt_ins);
4224     sqlite3_bind_null (stmt_ins, 1);
4225     sqlite3_bind_int64 (stmt_ins, 2, polygon_id);
4226     sqlite3_bind_null (stmt_ins, 3);
4227     /* inserting a row into the output table */
4228     ret = sqlite3_step (stmt_ins);
4229     if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4230 	;
4231     else
4232       {
4233 	  char *msg = sqlite3_mprintf ("LineLinksList error: \"%s\"",
4234 				       sqlite3_errmsg (network->db_handle));
4235 	  gaianet_set_last_error_msg ((GaiaNetworkAccessorPtr) network, msg);
4236 	  sqlite3_free (msg);
4237 	  return 0;
4238       }
4239     return 1;
4240 }
4241 
4242 GAIANET_DECLARE int
gaiaTopoNet_LineLinksList(GaiaNetworkAccessorPtr accessor,const char * db_prefix,const char * ref_table,const char * ref_column,const char * out_table)4243 gaiaTopoNet_LineLinksList (GaiaNetworkAccessorPtr accessor,
4244 			   const char *db_prefix, const char *ref_table,
4245 			   const char *ref_column, const char *out_table)
4246 {
4247 /* creating and populating a new Table reporting about Links/Linestring correspondencies */
4248     struct gaia_network *network = (struct gaia_network *) accessor;
4249     sqlite3_stmt *stmt_links = NULL;
4250     sqlite3_stmt *stmt_ref = NULL;
4251     sqlite3_stmt *stmt_rev = NULL;
4252     sqlite3_stmt *stmt_ins = NULL;
4253     int ret;
4254     char *sql;
4255     char *table;
4256     char *idx_name;
4257     char *xtable;
4258     char *xprefix;
4259     char *xcolumn;
4260     char *xidx_name;
4261     char *rtree_name;
4262     char *seeds;
4263     char *xseeds;
4264     int ref_has_spatial_index = 0;
4265     if (network == NULL)
4266 	return 0;
4267 
4268 /* attempting to build the output table */
4269     xtable = gaiaDoubleQuotedSql (out_table);
4270     sql = sqlite3_mprintf ("CREATE TABLE main.\"%s\" (\n"
4271 			   "\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n"
4272 			   "\tlink_id INTEGER,\n"
4273 			   "\tref_rowid INTEGER,\n"
4274 			   "\tdirection TEXT)", xtable);
4275     free (xtable);
4276     ret = sqlite3_exec (network->db_handle, sql, NULL, NULL, NULL);
4277     sqlite3_free (sql);
4278     if (ret != SQLITE_OK)
4279       {
4280 	  char *msg = sqlite3_mprintf ("LineLinksList error: \"%s\"",
4281 				       sqlite3_errmsg (network->db_handle));
4282 	  gaianet_set_last_error_msg (accessor, msg);
4283 	  sqlite3_free (msg);
4284 	  goto error;
4285       }
4286     idx_name = sqlite3_mprintf ("idx_%s_link_id", out_table);
4287     xidx_name = gaiaDoubleQuotedSql (idx_name);
4288     sqlite3_free (idx_name);
4289     xtable = gaiaDoubleQuotedSql (out_table);
4290     sql =
4291 	sqlite3_mprintf
4292 	("CREATE INDEX main.\"%s\" ON \"%s\" (link_id, ref_rowid)", xidx_name,
4293 	 xtable);
4294     free (xidx_name);
4295     free (xtable);
4296     ret = sqlite3_exec (network->db_handle, sql, NULL, NULL, NULL);
4297     sqlite3_free (sql);
4298     if (ret != SQLITE_OK)
4299       {
4300 	  char *msg = sqlite3_mprintf ("LineLinksList error: \"%s\"",
4301 				       sqlite3_errmsg (network->db_handle));
4302 	  gaianet_set_last_error_msg (accessor, msg);
4303 	  sqlite3_free (msg);
4304 	  goto error;
4305       }
4306 
4307 /* building the Links SQL statement */
4308     table = sqlite3_mprintf ("%s_link", network->network_name);
4309     xtable = gaiaDoubleQuotedSql (table);
4310     sqlite3_free (table);
4311     sql = sqlite3_mprintf ("SELECT link_id, geometry FROM main.\"%s\"", xtable);
4312     free (xtable);
4313     ret =
4314 	sqlite3_prepare_v2 (network->db_handle, sql, strlen (sql), &stmt_links,
4315 			    NULL);
4316     sqlite3_free (sql);
4317     if (ret != SQLITE_OK)
4318       {
4319 	  char *msg = sqlite3_mprintf ("LineLinksList error: \"%s\"",
4320 				       sqlite3_errmsg (network->db_handle));
4321 	  gaianet_set_last_error_msg (accessor, msg);
4322 	  sqlite3_free (msg);
4323 	  goto error;
4324       }
4325 
4326 /* building the RefTable SQL statement */
4327     seeds = sqlite3_mprintf ("%s_seeds", network->network_name);
4328     rtree_name = sqlite3_mprintf ("DB=%s.%s", db_prefix, ref_table);
4329     ref_has_spatial_index =
4330 	gaia_check_spatial_index (network->db_handle, db_prefix, ref_table,
4331 				  ref_column);
4332     xprefix = gaiaDoubleQuotedSql (db_prefix);
4333     xtable = gaiaDoubleQuotedSql (ref_table);
4334     xcolumn = gaiaDoubleQuotedSql (ref_column);
4335     xseeds = gaiaDoubleQuotedSql (seeds);
4336     if (ref_has_spatial_index)
4337 	sql =
4338 	    sqlite3_mprintf
4339 	    ("SELECT r.rowid, r.\"%s\" FROM MAIN.\"%s\" AS s, \"%s\".\"%s\" AS r "
4340 	     "WHERE ST_Intersects(r.\"%s\", s.geometry) == 1 AND s.link_id = ? "
4341 	     "AND r.rowid IN (SELECT rowid FROM SpatialIndex WHERE f_table_name = %Q "
4342 	     "AND f_geometry_column = %Q AND search_frame = s.geometry)",
4343 	     xcolumn, xseeds, xprefix, xtable, xcolumn, rtree_name, xcolumn);
4344     else
4345 	sql =
4346 	    sqlite3_mprintf
4347 	    ("SELECT r.rowid, r.\"%s\" FROM MAIN.\"%s\" AS s, \"%s\".\"%s\" AS r "
4348 	     "WHERE  ST_Intersects(r.\"%s\", s.geometry) == 1 AND s.link_id = ?",
4349 	     xcolumn, xseeds, xprefix, xtable, xcolumn);
4350     free (xprefix);
4351     free (xtable);
4352     free (xcolumn);
4353     free (xseeds);
4354     sqlite3_free (rtree_name);
4355     sqlite3_free (seeds);
4356     ret =
4357 	sqlite3_prepare_v2 (network->db_handle, sql, strlen (sql), &stmt_ref,
4358 			    NULL);
4359     sqlite3_free (sql);
4360     if (ret != SQLITE_OK)
4361       {
4362 	  char *msg = sqlite3_mprintf ("LineLinksList error: \"%s\"",
4363 				       sqlite3_errmsg (network->db_handle));
4364 	  gaianet_set_last_error_msg (accessor, msg);
4365 	  sqlite3_free (msg);
4366 	  goto error;
4367       }
4368 
4369 /* building the Reverse RefTable SQL statement */
4370     seeds = sqlite3_mprintf ("%s_seeds", network->network_name);
4371     rtree_name = sqlite3_mprintf ("DB=%s.%s", db_prefix, ref_table);
4372     xprefix = gaiaDoubleQuotedSql (db_prefix);
4373     xtable = gaiaDoubleQuotedSql (ref_table);
4374     xcolumn = gaiaDoubleQuotedSql (ref_column);
4375     xseeds = gaiaDoubleQuotedSql (seeds);
4376     sql = sqlite3_mprintf ("SELECT r.rowid FROM \"%s\".\"%s\" AS r "
4377 			   "LEFT JOIN MAIN.\"%s\" AS s ON (ST_Intersects(r.\"%s\", s.geometry) = 1 "
4378 			   "AND s.link_id IS NOT NULL AND s.rowid IN (SELECT rowid FROM SpatialIndex "
4379 			   "WHERE f_table_name = %Q AND search_frame = r.\"%s\")) "
4380 			   "WHERE s.link_id IS NULL", xprefix, xtable, xseeds,
4381 			   xcolumn, rtree_name, xcolumn);
4382     free (xprefix);
4383     free (xtable);
4384     free (xcolumn);
4385     free (xseeds);
4386     sqlite3_free (rtree_name);
4387     sqlite3_free (seeds);
4388     ret =
4389 	sqlite3_prepare_v2 (network->db_handle, sql, strlen (sql), &stmt_rev,
4390 			    NULL);
4391     sqlite3_free (sql);
4392     if (ret != SQLITE_OK)
4393       {
4394 	  char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
4395 				       sqlite3_errmsg (network->db_handle));
4396 	  gaianet_set_last_error_msg (accessor, msg);
4397 	  sqlite3_free (msg);
4398 	  goto error;
4399       }
4400 
4401 /* building the Insert SQL statement */
4402     xtable = gaiaDoubleQuotedSql (out_table);
4403     sql = sqlite3_mprintf ("INSERT INTO main.\"%s\" (id, link_id, ref_rowid, "
4404 			   "direction) VALUES (NULL, ?, ?, ?)", xtable);
4405     free (xtable);
4406     ret =
4407 	sqlite3_prepare_v2 (network->db_handle, sql, strlen (sql), &stmt_ins,
4408 			    NULL);
4409     sqlite3_free (sql);
4410     if (ret != SQLITE_OK)
4411       {
4412 	  char *msg = sqlite3_mprintf ("LineLinksList error: \"%s\"",
4413 				       sqlite3_errmsg (network->db_handle));
4414 	  gaianet_set_last_error_msg (accessor, msg);
4415 	  sqlite3_free (msg);
4416 	  goto error;
4417       }
4418 
4419     while (1)
4420       {
4421 	  /* scrolling the result set rows - Links */
4422 	  ret = sqlite3_step (stmt_links);
4423 	  if (ret == SQLITE_DONE)
4424 	      break;		/* end of result set */
4425 	  if (ret == SQLITE_ROW)
4426 	    {
4427 		sqlite3_int64 link_id = sqlite3_column_int64 (stmt_links, 0);
4428 		if (sqlite3_column_type (stmt_links, 1) == SQLITE_BLOB)
4429 		  {
4430 		      if (!find_linelink_matches
4431 			  (network, stmt_ref, stmt_ins, link_id,
4432 			   sqlite3_column_blob (stmt_links, 1),
4433 			   sqlite3_column_bytes (stmt_links, 1)))
4434 			  goto error;
4435 		  }
4436 		else
4437 		  {
4438 		      char *msg =
4439 			  sqlite3_mprintf
4440 			  ("LineLinksList error: Link not a BLOB value");
4441 		      gaianet_set_last_error_msg (accessor, msg);
4442 		      sqlite3_free (msg);
4443 		      goto error;
4444 		  }
4445 	    }
4446 	  else
4447 	    {
4448 		char *msg = sqlite3_mprintf ("LineLinksList error: \"%s\"",
4449 					     sqlite3_errmsg
4450 					     (network->db_handle));
4451 		gaianet_set_last_error_msg (accessor, msg);
4452 		sqlite3_free (msg);
4453 		goto error;
4454 	    }
4455       }
4456 
4457     while (1)
4458       {
4459 	  /* scrolling the Reverse result set rows - Linestrings */
4460 	  ret = sqlite3_step (stmt_rev);
4461 	  if (ret == SQLITE_DONE)
4462 	      break;		/* end of result set */
4463 	  if (ret == SQLITE_ROW)
4464 	    {
4465 		sqlite3_int64 line_id = sqlite3_column_int64 (stmt_rev, 0);
4466 		if (!insert_linelink_reverse (network, stmt_ins, line_id))
4467 		    goto error;
4468 	    }
4469 	  else
4470 	    {
4471 		char *msg = sqlite3_mprintf ("LineLinksList error: \"%s\"",
4472 					     sqlite3_errmsg
4473 					     (network->db_handle));
4474 		gaianet_set_last_error_msg (accessor, msg);
4475 		sqlite3_free (msg);
4476 		goto error;
4477 	    }
4478       }
4479 
4480     sqlite3_finalize (stmt_links);
4481     sqlite3_finalize (stmt_ref);
4482     sqlite3_finalize (stmt_rev);
4483     sqlite3_finalize (stmt_ins);
4484     return 1;
4485 
4486   error:
4487     if (stmt_links != NULL)
4488 	sqlite3_finalize (stmt_links);
4489     if (stmt_ref != NULL)
4490 	sqlite3_finalize (stmt_ref);
4491     if (stmt_rev != NULL)
4492 	sqlite3_finalize (stmt_rev);
4493     if (stmt_ins != NULL)
4494 	sqlite3_finalize (stmt_ins);
4495     return 0;
4496 }
4497 
4498 #endif /* end RTTOPO conditionals */
4499