1 /*
2 
3  gaia_auxtopo.c -- implementation of the Topology 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 /*
47 
48 CREDITS:
49 
50 this module has been completely funded by:
51 Regione Toscana - Settore Sistema Informativo Territoriale ed Ambientale
52 (Topology support)
53 
54 CIG: 6038019AE5
55 
56 */
57 
58 #include <stdlib.h>
59 #include <stdio.h>
60 #include <string.h>
61 #include <float.h>
62 #include <math.h>
63 
64 #if defined(_WIN32) && !defined(__MINGW32__)
65 #include "process.h"
66 #else
67 #include "unistd.h"
68 #endif
69 
70 #if defined(_WIN32) && !defined(__MINGW32__)
71 #include "config-msvc.h"
72 #else
73 #include "config.h"
74 #endif
75 
76 #include <spatialite_private.h>
77 
78 #ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
79 
80 #include <spatialite/sqlite.h>
81 #include <spatialite/debug.h>
82 #include <spatialite/gaiageo.h>
83 #include <spatialite/gaia_topology.h>
84 #include <spatialite/gaia_network.h>
85 #include <spatialite/gaiaaux.h>
86 
87 #include <spatialite.h>
88 
89 #include <librttopo.h>
90 
91 #include <lwn_network.h>
92 
93 #include "topology_private.h"
94 #include "network_private.h"
95 
96 #ifdef _WIN32
97 #define strcasecmp	_stricmp
98 #endif /* not WIN32 */
99 
100 #define GAIA_UNUSED() if (argc || argv) argc = argc;
101 
102 SPATIALITE_PRIVATE void
free_internal_cache_topologies(void * firstTopology)103 free_internal_cache_topologies (void *firstTopology)
104 {
105 /* destroying all Topologies registered into the Internal Connection Cache */
106     struct gaia_topology *p_topo = (struct gaia_topology *) firstTopology;
107     struct gaia_topology *p_topo_n;
108 
109     while (p_topo != NULL)
110       {
111 	  p_topo_n = p_topo->next;
112 	  gaiaTopologyDestroy ((GaiaTopologyAccessorPtr) p_topo);
113 	  p_topo = p_topo_n;
114       }
115 }
116 
117 SPATIALITE_PRIVATE void
drop_topologies_triggers(void * sqlite_handle)118 drop_topologies_triggers (void *sqlite_handle)
119 {
120 /* dropping all "topologies" triggers */
121     char *sql;
122     int ret;
123     char *err_msg = NULL;
124     char **results;
125     int rows;
126     int columns;
127     int i;
128     sqlite3 *sqlite = (sqlite3 *) sqlite_handle;
129 
130 /* checking for existing tables */
131     sql =
132 	"SELECT name FROM sqlite_master WHERE type = 'trigger' AND tbl_name = 'topologies'";
133     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &err_msg);
134     if (ret != SQLITE_OK)
135       {
136 	  spatialite_e ("SQL error: %s\n", err_msg);
137 	  sqlite3_free (err_msg);
138 	  return;
139       }
140     for (i = 1; i <= rows; i++)
141       {
142 	  const char *name = results[(i * columns) + 0];
143 	  sql = sqlite3_mprintf ("DROP TRIGGER %s", name);
144 	  ret = sqlite3_exec (sqlite, sql, NULL, NULL, &err_msg);
145 	  if (ret != SQLITE_OK)
146 	    {
147 		spatialite_e ("SQL error: %s\n", err_msg);
148 		sqlite3_free (err_msg);
149 		return;
150 	    }
151 	  sqlite3_free (sql);
152       }
153     sqlite3_free_table (results);
154 }
155 
156 SPATIALITE_PRIVATE int
do_create_topologies_triggers(void * sqlite_handle)157 do_create_topologies_triggers (void *sqlite_handle)
158 {
159 /* attempting to create the Topologies triggers */
160     const char *sql;
161     char *err_msg = NULL;
162     int ret;
163     char **results;
164     int rows;
165     int columns;
166     int i;
167     sqlite3 *handle = (sqlite3 *) sqlite_handle;
168     int ok_topologies = 0;
169 
170 /* checking for existing tables */
171     sql =
172 	"SELECT tbl_name FROM sqlite_master WHERE type = 'table' AND tbl_name = 'topologies'";
173     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, &err_msg);
174     if (ret != SQLITE_OK)
175       {
176 	  spatialite_e ("SQL error: %s\n", err_msg);
177 	  sqlite3_free (err_msg);
178 	  return 0;
179       }
180     for (i = 1; i <= rows; i++)
181       {
182 	  const char *name = results[(i * columns) + 0];
183 	  if (strcasecmp (name, "topologies") == 0)
184 	      ok_topologies = 1;
185       }
186     sqlite3_free_table (results);
187 
188     if (ok_topologies)
189       {
190 	  /* creating Topologies triggers */
191 	  sql = "CREATE TRIGGER IF NOT EXISTS topology_name_insert\n"
192 	      "BEFORE INSERT ON 'topologies'\nFOR EACH ROW BEGIN\n"
193 	      "SELECT RAISE(ABORT,'insert on topologies violates constraint: "
194 	      "topology_name value must not contain a single quote')\n"
195 	      "WHERE NEW.topology_name LIKE ('%''%');\n"
196 	      "SELECT RAISE(ABORT,'insert on topologies violates constraint: "
197 	      "topology_name value must not contain a double quote')\n"
198 	      "WHERE NEW.topology_name LIKE ('%\"%');\n"
199 	      "SELECT RAISE(ABORT,'insert on topologies violates constraint: "
200 	      "topology_name value must be lower case')\n"
201 	      "WHERE NEW.topology_name <> lower(NEW.topology_name);\nEND";
202 	  ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
203 	  if (ret != SQLITE_OK)
204 	    {
205 		spatialite_e ("SQL error: %s\n", err_msg);
206 		sqlite3_free (err_msg);
207 		return 0;
208 	    }
209 	  sql = "CREATE TRIGGER IF NOT EXISTS topology_name_update\n"
210 	      "BEFORE UPDATE OF 'topology_name' ON 'topologies'\nFOR EACH ROW BEGIN\n"
211 	      "SELECT RAISE(ABORT,'update on topologies violates constraint: "
212 	      "topology_name value must not contain a single quote')\n"
213 	      "WHERE NEW.topology_name LIKE ('%''%');\n"
214 	      "SELECT RAISE(ABORT,'update on topologies violates constraint: "
215 	      "topology_name value must not contain a double quote')\n"
216 	      "WHERE NEW.topology_name LIKE ('%\"%');\n"
217 	      "SELECT RAISE(ABORT,'update on topologies violates constraint: "
218 	      "topology_name value must be lower case')\n"
219 	      "WHERE NEW.topology_name <> lower(NEW.topology_name);\nEND";
220 	  ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
221 	  if (ret != SQLITE_OK)
222 	    {
223 		spatialite_e ("SQL error: %s\n", err_msg);
224 		sqlite3_free (err_msg);
225 		return 0;
226 	    }
227       }
228 
229     return 1;
230 }
231 
232 SPATIALITE_PRIVATE int
do_create_topologies(void * sqlite_handle)233 do_create_topologies (void *sqlite_handle)
234 {
235 /* attempting to create the Topologies table (if not already existing) */
236     const char *sql;
237     char *err_msg = NULL;
238     int ret;
239     sqlite3 *handle = (sqlite3 *) sqlite_handle;
240 
241     sql = "CREATE TABLE IF NOT EXISTS topologies (\n"
242 	"\ttopology_name TEXT NOT NULL PRIMARY KEY,\n"
243 	"\tsrid INTEGER NOT NULL,\n"
244 	"\ttolerance DOUBLE NOT NULL,\n"
245 	"\thas_z INTEGER NOT NULL,\n"
246 	"\tnext_edge_id INTEGER NOT NULL DEFAULT 1,\n"
247 	"\tCONSTRAINT topo_srid_fk FOREIGN KEY (srid) "
248 	"REFERENCES spatial_ref_sys (srid))";
249     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
250     if (ret != SQLITE_OK)
251       {
252 	  spatialite_e ("CREATE TABLE topologies - error: %s\n", err_msg);
253 	  sqlite3_free (err_msg);
254 	  return 0;
255       }
256 
257     if (!do_create_topologies_triggers (handle))
258 	return 0;
259     return 1;
260 }
261 
262 static int
check_new_topology(sqlite3 * handle,const char * topo_name)263 check_new_topology (sqlite3 * handle, const char *topo_name)
264 {
265 /* testing if some already defined DB object forbids creating the new Topology */
266     char *sql;
267     char *prev;
268     char *table;
269     int ret;
270     int i;
271     char **results;
272     int rows;
273     int columns;
274     const char *value;
275     int error = 0;
276 
277 /* testing if the same Topology is already defined */
278     sql = sqlite3_mprintf ("SELECT Count(*) FROM MAIN.topologies WHERE "
279 			   "Lower(topology_name) = Lower(%Q)", topo_name);
280     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
281     sqlite3_free (sql);
282     if (ret != SQLITE_OK)
283 	return 0;
284     if (rows < 1)
285 	;
286     else
287       {
288 	  for (i = 1; i <= rows; i++)
289 	    {
290 		value = results[(i * columns) + 0];
291 		if (atoi (value) != 0)
292 		    error = 1;
293 	    }
294       }
295     sqlite3_free_table (results);
296     if (error)
297 	return 0;
298 
299 /* testing if some table/geom is already defined in geometry_columns */
300     sql = sqlite3_mprintf ("SELECT Count(*) FROM geometry_columns WHERE");
301     prev = sql;
302     table = sqlite3_mprintf ("%s_face", topo_name);
303     sql =
304 	sqlite3_mprintf
305 	("%s (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'mbr')",
306 	 prev, table);
307     sqlite3_free (table);
308     sqlite3_free (prev);
309     prev = sql;
310     table = sqlite3_mprintf ("%s_node", topo_name);
311     sql =
312 	sqlite3_mprintf
313 	("%s OR (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'geom')",
314 	 prev, table);
315     sqlite3_free (table);
316     sqlite3_free (prev);
317     prev = sql;
318     table = sqlite3_mprintf ("%s_edge", topo_name);
319     sql =
320 	sqlite3_mprintf
321 	("%s OR (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'geom')",
322 	 prev, table);
323     sqlite3_free (table);
324     sqlite3_free (prev);
325     prev = sql;
326     table = sqlite3_mprintf ("%s_seeds", topo_name);
327     sql =
328 	sqlite3_mprintf
329 	("%s OR (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'geom')",
330 	 prev, table);
331     sqlite3_free (table);
332     sqlite3_free (prev);
333     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
334     sqlite3_free (sql);
335     if (ret != SQLITE_OK)
336 	return 0;
337     if (rows < 1)
338 	;
339     else
340       {
341 	  for (i = 1; i <= rows; i++)
342 	    {
343 		value = results[(i * columns) + 0];
344 		if (atoi (value) != 0)
345 		    error = 1;
346 	    }
347       }
348     sqlite3_free_table (results);
349     if (error)
350 	return 0;
351 
352 /* testing if some Spatial View is already defined in views_geometry_columns */
353     sql = sqlite3_mprintf ("SELECT Count(*) FROM views_geometry_columns WHERE");
354     prev = sql;
355     table = sqlite3_mprintf ("%s_face_geoms", topo_name);
356     sql =
357 	sqlite3_mprintf
358 	("%s (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'mbr')",
359 	 prev, table);
360     sqlite3_free (table);
361     sqlite3_free (prev);
362     prev = sql;
363     table = sqlite3_mprintf ("%s_face_seeds", topo_name);
364     sql =
365 	sqlite3_mprintf
366 	("%s OR (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'geom')",
367 	 prev, table);
368     sqlite3_free (table);
369     sqlite3_free (prev);
370     prev = sql;
371     table = sqlite3_mprintf ("%s_edge_seeds", topo_name);
372     sql =
373 	sqlite3_mprintf
374 	("%s OR (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'geom')",
375 	 prev, table);
376     sqlite3_free (table);
377     sqlite3_free (prev);
378     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
379     sqlite3_free (sql);
380     if (ret != SQLITE_OK)
381 	return 0;
382     if (rows < 1)
383 	;
384     else
385       {
386 	  for (i = 1; i <= rows; i++)
387 	    {
388 		value = results[(i * columns) + 0];
389 		if (atoi (value) != 0)
390 		    error = 1;
391 	    }
392       }
393     sqlite3_free_table (results);
394     if (error)
395 	return 0;
396 
397 /* testing if some table is already defined */
398     sql = sqlite3_mprintf ("SELECT Count(*) FROM sqlite_master WHERE");
399     prev = sql;
400     table = sqlite3_mprintf ("%s_node", topo_name);
401     sql = sqlite3_mprintf ("%s Lower(name) = Lower(%Q)", prev, table);
402     sqlite3_free (table);
403     sqlite3_free (prev);
404     prev = sql;
405     table = sqlite3_mprintf ("%s_edge", topo_name);
406     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
407     sqlite3_free (table);
408     sqlite3_free (prev);
409     prev = sql;
410     table = sqlite3_mprintf ("%s_face", topo_name);
411     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
412     sqlite3_free (table);
413     sqlite3_free (prev);
414     prev = sql;
415     table = sqlite3_mprintf ("%s_seeds", topo_name);
416     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
417     sqlite3_free (table);
418     sqlite3_free (prev);
419     prev = sql;
420     table = sqlite3_mprintf ("%s_topolayers", topo_name);
421     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
422     sqlite3_free (table);
423     sqlite3_free (prev);
424     prev = sql;
425     table = sqlite3_mprintf ("%s_topofeatures", topo_name);
426     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
427     sqlite3_free (table);
428     sqlite3_free (prev);
429     prev = sql;
430     table = sqlite3_mprintf ("idx_%s_node_geom", topo_name);
431     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
432     sqlite3_free (table);
433     sqlite3_free (prev);
434     prev = sql;
435     table = sqlite3_mprintf ("idx_%s_edge_geom", topo_name);
436     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
437     sqlite3_free (table);
438     sqlite3_free (prev);
439     prev = sql;
440     table = sqlite3_mprintf ("idx_%s_face_mbr", topo_name);
441     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
442     sqlite3_free (table);
443     sqlite3_free (prev);
444     prev = sql;
445     table = sqlite3_mprintf ("idx_%s_seeds_geom", topo_name);
446     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
447     sqlite3_free (table);
448     sqlite3_free (prev);
449     prev = sql;
450     table = sqlite3_mprintf ("%s_face_geoms", topo_name);
451     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
452     sqlite3_free (table);
453     sqlite3_free (prev);
454     prev = sql;
455     table = sqlite3_mprintf ("%s_face_seeds", topo_name);
456     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
457     sqlite3_free (table);
458     sqlite3_free (prev);
459     prev = sql;
460     table = sqlite3_mprintf ("%s_edge_seeds", topo_name);
461     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
462     sqlite3_free (table);
463     sqlite3_free (prev);
464     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
465     sqlite3_free (sql);
466     if (ret != SQLITE_OK)
467 	return 0;
468     if (rows < 1)
469 	;
470     else
471       {
472 	  for (i = 1; i <= rows; i++)
473 	    {
474 		value = results[(i * columns) + 0];
475 		if (atoi (value) != 0)
476 		    error = 1;
477 	    }
478       }
479     sqlite3_free_table (results);
480     if (error)
481 	return 0;
482 
483     return 1;
484 }
485 
486 static int
do_create_face(sqlite3 * handle,const char * topo_name,int srid)487 do_create_face (sqlite3 * handle, const char *topo_name, int srid)
488 {
489 /* attempting to create the Topology Face table */
490     char *sql;
491     char *table;
492     char *xtable;
493     char *err_msg = NULL;
494     int ret;
495 
496 /* creating the main table */
497     table = sqlite3_mprintf ("%s_face", topo_name);
498     xtable = gaiaDoubleQuotedSql (table);
499     sqlite3_free (table);
500     sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (\n"
501 			   "\tface_id INTEGER PRIMARY KEY AUTOINCREMENT)",
502 			   xtable);
503     free (xtable);
504     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
505     sqlite3_free (sql);
506     if (ret != SQLITE_OK)
507       {
508 	  spatialite_e ("CREATE TABLE topology-FACE - error: %s\n", err_msg);
509 	  sqlite3_free (err_msg);
510 	  return 0;
511       }
512 
513 /* creating the Face BBOX Geometry */
514     table = sqlite3_mprintf ("%s_face", topo_name);
515     sql =
516 	sqlite3_mprintf
517 	("SELECT AddGeometryColumn(%Q, 'mbr', %d, 'POLYGON', 'XY')",
518 	 table, srid);
519     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
520     sqlite3_free (table);
521     sqlite3_free (sql);
522     if (ret != SQLITE_OK)
523       {
524 	  spatialite_e
525 	      ("AddGeometryColumn topology-FACE - error: %s\n", err_msg);
526 	  sqlite3_free (err_msg);
527 	  return 0;
528       }
529 
530 /* creating a Spatial Index supporting Face Geometry */
531     table = sqlite3_mprintf ("%s_face", topo_name);
532     sql = sqlite3_mprintf ("SELECT CreateSpatialIndex(%Q, 'mbr')", table);
533     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
534     sqlite3_free (table);
535     sqlite3_free (sql);
536     if (ret != SQLITE_OK)
537       {
538 	  spatialite_e
539 	      ("CreateSpatialIndex topology-FACE - error: %s\n", err_msg);
540 	  sqlite3_free (err_msg);
541 	  return 0;
542       }
543 
544 /* inserting the default World Face */
545     table = sqlite3_mprintf ("%s_face", topo_name);
546     xtable = gaiaDoubleQuotedSql (table);
547     sqlite3_free (table);
548     sql = sqlite3_mprintf ("INSERT INTO MAIN.\"%s\" VALUES (0, NULL)", xtable);
549     free (xtable);
550     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
551     sqlite3_free (sql);
552     if (ret != SQLITE_OK)
553       {
554 	  spatialite_e ("INSERT WorldFACE - error: %s\n", err_msg);
555 	  sqlite3_free (err_msg);
556 	  return 0;
557       }
558 
559     return 1;
560 }
561 
562 static int
do_create_node(sqlite3 * handle,const char * topo_name,int srid,int has_z)563 do_create_node (sqlite3 * handle, const char *topo_name, int srid, int has_z)
564 {
565 /* attempting to create the Topology Node table */
566     char *sql;
567     char *table;
568     char *xtable;
569     char *xconstraint;
570     char *xmother;
571     char *err_msg = NULL;
572     int ret;
573 
574 /* creating the main table */
575     table = sqlite3_mprintf ("%s_node", topo_name);
576     xtable = gaiaDoubleQuotedSql (table);
577     sqlite3_free (table);
578     table = sqlite3_mprintf ("%s_node_face_fk", topo_name);
579     xconstraint = gaiaDoubleQuotedSql (table);
580     sqlite3_free (table);
581     table = sqlite3_mprintf ("%s_face", topo_name);
582     xmother = gaiaDoubleQuotedSql (table);
583     sqlite3_free (table);
584     sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (\n"
585 			   "\tnode_id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
586 			   "\tcontaining_face INTEGER,\n"
587 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (containing_face) "
588 			   "REFERENCES \"%s\" (face_id))", xtable, xconstraint,
589 			   xmother);
590     free (xtable);
591     free (xconstraint);
592     free (xmother);
593     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
594     sqlite3_free (sql);
595     if (ret != SQLITE_OK)
596       {
597 	  spatialite_e ("CREATE TABLE topology-NODE - error: %s\n", err_msg);
598 	  sqlite3_free (err_msg);
599 	  return 0;
600       }
601 
602 /* creating the Node Geometry */
603     table = sqlite3_mprintf ("%s_node", topo_name);
604     sql =
605 	sqlite3_mprintf
606 	("SELECT AddGeometryColumn(%Q, 'geom', %d, 'POINT', %Q, 1)", table,
607 	 srid, has_z ? "XYZ" : "XY");
608     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
609     sqlite3_free (table);
610     sqlite3_free (sql);
611     if (ret != SQLITE_OK)
612       {
613 	  spatialite_e
614 	      ("AddGeometryColumn topology-NODE - error: %s\n", err_msg);
615 	  sqlite3_free (err_msg);
616 	  return 0;
617       }
618 
619 /* creating a Spatial Index supporting Node Geometry */
620     table = sqlite3_mprintf ("%s_node", topo_name);
621     sql = sqlite3_mprintf ("SELECT CreateSpatialIndex(%Q, 'geom')", table);
622     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
623     sqlite3_free (table);
624     sqlite3_free (sql);
625     if (ret != SQLITE_OK)
626       {
627 	  spatialite_e
628 	      ("CreateSpatialIndex topology-NODE - error: %s\n", err_msg);
629 	  sqlite3_free (err_msg);
630 	  return 0;
631       }
632 
633 /* creating an Index supporting "containing_face" */
634     table = sqlite3_mprintf ("%s_node", topo_name);
635     xtable = gaiaDoubleQuotedSql (table);
636     sqlite3_free (table);
637     table = sqlite3_mprintf ("idx_%s_node_contface", topo_name);
638     xconstraint = gaiaDoubleQuotedSql (table);
639     sqlite3_free (table);
640     sql =
641 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (containing_face)",
642 			 xconstraint, xtable);
643     free (xtable);
644     free (xconstraint);
645     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
646     sqlite3_free (sql);
647     if (ret != SQLITE_OK)
648       {
649 	  spatialite_e ("CREATE INDEX node-contface - error: %s\n", err_msg);
650 	  sqlite3_free (err_msg);
651 	  return 0;
652       }
653 
654     return 1;
655 }
656 
657 static int
do_create_edge(sqlite3 * handle,const char * topo_name,int srid,int has_z)658 do_create_edge (sqlite3 * handle, const char *topo_name, int srid, int has_z)
659 {
660 /* attempting to create the Topology Edge table */
661     char *sql;
662     char *table;
663     char *xtable;
664     char *xconstraint1;
665     char *xconstraint2;
666     char *xconstraint3;
667     char *xconstraint4;
668     char *xnodes;
669     char *xfaces;
670     char *trigger;
671     char *xtrigger;
672     char *err_msg = NULL;
673     int ret;
674 
675 /* creating the main table */
676     table = sqlite3_mprintf ("%s_edge", topo_name);
677     xtable = gaiaDoubleQuotedSql (table);
678     sqlite3_free (table);
679     table = sqlite3_mprintf ("%s_edge_node_start_fk", topo_name);
680     xconstraint1 = gaiaDoubleQuotedSql (table);
681     sqlite3_free (table);
682     table = sqlite3_mprintf ("%s_edge_node_end_fk", topo_name);
683     xconstraint2 = gaiaDoubleQuotedSql (table);
684     sqlite3_free (table);
685     table = sqlite3_mprintf ("%s_edge_face_left_fk", topo_name);
686     xconstraint3 = gaiaDoubleQuotedSql (table);
687     sqlite3_free (table);
688     table = sqlite3_mprintf ("%s_edge_face_right_fk", topo_name);
689     xconstraint4 = gaiaDoubleQuotedSql (table);
690     sqlite3_free (table);
691     table = sqlite3_mprintf ("%s_node", topo_name);
692     xnodes = gaiaDoubleQuotedSql (table);
693     sqlite3_free (table);
694     table = sqlite3_mprintf ("%s_face", topo_name);
695     xfaces = gaiaDoubleQuotedSql (table);
696     sqlite3_free (table);
697     sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (\n"
698 			   "\tedge_id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
699 			   "\tstart_node INTEGER NOT NULL,\n"
700 			   "\tend_node INTEGER NOT NULL,\n"
701 			   "\tnext_left_edge INTEGER NOT NULL,\n"
702 			   "\tnext_right_edge INTEGER NOT NULL,\n"
703 			   "\tleft_face INTEGER,\n"
704 			   "\tright_face INTEGER,\n"
705 			   "\ttimestamp DATETIME,\n"
706 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (start_node) "
707 			   "REFERENCES \"%s\" (node_id),\n"
708 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (end_node) "
709 			   "REFERENCES \"%s\" (node_id),\n"
710 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (left_face) "
711 			   "REFERENCES \"%s\" (face_id),\n"
712 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (right_face) "
713 			   "REFERENCES \"%s\" (face_id))",
714 			   xtable, xconstraint1, xnodes, xconstraint2, xnodes,
715 			   xconstraint3, xfaces, xconstraint4, xfaces);
716     free (xtable);
717     free (xconstraint1);
718     free (xconstraint2);
719     free (xconstraint3);
720     free (xconstraint4);
721     free (xnodes);
722     free (xfaces);
723     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
724     sqlite3_free (sql);
725     if (ret != SQLITE_OK)
726       {
727 	  spatialite_e ("CREATE TABLE topology-EDGE - error: %s\n", err_msg);
728 	  sqlite3_free (err_msg);
729 	  return 0;
730       }
731 
732 /* adding the "next_edge_ins" trigger */
733     trigger = sqlite3_mprintf ("%s_edge_next_ins", topo_name);
734     xtrigger = gaiaDoubleQuotedSql (trigger);
735     sqlite3_free (trigger);
736     table = sqlite3_mprintf ("%s_edge", topo_name);
737     xtable = gaiaDoubleQuotedSql (table);
738     sqlite3_free (table);
739     sql = sqlite3_mprintf ("CREATE TRIGGER \"%s\" AFTER INSERT ON \"%s\"\n"
740 			   "FOR EACH ROW BEGIN\n"
741 			   "\tUPDATE topologies SET next_edge_id = NEW.edge_id + 1 "
742 			   "WHERE Lower(topology_name) = Lower(%Q) AND next_edge_id < NEW.edge_id + 1;\n"
743 			   "\tUPDATE \"%s\" SET timestamp = strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ', 'now') "
744 			   "WHERE edge_id = NEW.edge_id;"
745 			   "END", xtrigger, xtable, topo_name, xtable);
746     free (xtrigger);
747     free (xtable);
748     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
749     sqlite3_free (sql);
750     if (ret != SQLITE_OK)
751       {
752 	  spatialite_e
753 	      ("CREATE TRIGGER topology-EDGE next INSERT - error: %s\n",
754 	       err_msg);
755 	  sqlite3_free (err_msg);
756 	  return 0;
757       }
758 
759 /* adding the "edge_update" trigger */
760     trigger = sqlite3_mprintf ("%s_edge_update", topo_name);
761     xtrigger = gaiaDoubleQuotedSql (trigger);
762     sqlite3_free (trigger);
763     table = sqlite3_mprintf ("%s_edge", topo_name);
764     xtable = gaiaDoubleQuotedSql (table);
765     sqlite3_free (table);
766     sql = sqlite3_mprintf ("CREATE TRIGGER \"%s\" AFTER UPDATE ON \"%s\"\n"
767 			   "FOR EACH ROW BEGIN\n"
768 			   "\tUPDATE \"%s\" SET timestamp = strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ', 'now') "
769 			   "WHERE edge_id = NEW.edge_id;"
770 			   "END", xtrigger, xtable, xtable);
771     free (xtrigger);
772     free (xtable);
773     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
774     sqlite3_free (sql);
775     if (ret != SQLITE_OK)
776       {
777 	  spatialite_e
778 	      ("CREATE TRIGGER topology-EDGE next INSERT - error: %s\n",
779 	       err_msg);
780 	  sqlite3_free (err_msg);
781 	  return 0;
782       }
783 
784 /* adding the "next_edge_upd" trigger */
785     trigger = sqlite3_mprintf ("%s_edge_next_upd", topo_name);
786     xtrigger = gaiaDoubleQuotedSql (trigger);
787     sqlite3_free (trigger);
788     table = sqlite3_mprintf ("%s_edge", topo_name);
789     xtable = gaiaDoubleQuotedSql (table);
790     sqlite3_free (table);
791     sql =
792 	sqlite3_mprintf
793 	("CREATE TRIGGER \"%s\" AFTER UPDATE OF edge_id ON \"%s\"\n"
794 	 "FOR EACH ROW BEGIN\n"
795 	 "\tUPDATE topologies SET next_edge_id = NEW.edge_id + 1 "
796 	 "WHERE Lower(topology_name) = Lower(%Q) AND next_edge_id < NEW.edge_id + 1;\n"
797 	 "END", xtrigger, xtable, topo_name);
798     free (xtrigger);
799     free (xtable);
800     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
801     sqlite3_free (sql);
802     if (ret != SQLITE_OK)
803       {
804 	  spatialite_e
805 	      ("CREATE TRIGGER topology-EDGE next UPDATE - error: %s\n",
806 	       err_msg);
807 	  sqlite3_free (err_msg);
808 	  return 0;
809       }
810 
811 /* creating the Edge Geometry */
812     table = sqlite3_mprintf ("%s_edge", topo_name);
813     sql =
814 	sqlite3_mprintf
815 	("SELECT AddGeometryColumn(%Q, 'geom', %d, 'LINESTRING', %Q, 1)",
816 	 table, srid, has_z ? "XYZ" : "XY");
817     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
818     sqlite3_free (table);
819     sqlite3_free (sql);
820     if (ret != SQLITE_OK)
821       {
822 	  spatialite_e
823 	      ("AddGeometryColumn topology-EDGE - error: %s\n", err_msg);
824 	  sqlite3_free (err_msg);
825 	  return 0;
826       }
827 
828 /* creating a Spatial Index supporting Edge Geometry */
829     table = sqlite3_mprintf ("%s_edge", topo_name);
830     sql = sqlite3_mprintf ("SELECT CreateSpatialIndex(%Q, 'geom')", table);
831     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
832     sqlite3_free (table);
833     sqlite3_free (sql);
834     if (ret != SQLITE_OK)
835       {
836 	  spatialite_e
837 	      ("CreateSpatialIndex topology-EDGE - error: %s\n", err_msg);
838 	  sqlite3_free (err_msg);
839 	  return 0;
840       }
841 
842 /* creating an Index supporting "start_node" */
843     table = sqlite3_mprintf ("%s_edge", topo_name);
844     xtable = gaiaDoubleQuotedSql (table);
845     sqlite3_free (table);
846     table = sqlite3_mprintf ("idx_%s_start_node", topo_name);
847     xconstraint1 = gaiaDoubleQuotedSql (table);
848     sqlite3_free (table);
849     sql =
850 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (start_node)",
851 			 xconstraint1, xtable);
852     free (xtable);
853     free (xconstraint1);
854     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
855     sqlite3_free (sql);
856     if (ret != SQLITE_OK)
857       {
858 	  spatialite_e ("CREATE INDEX edge-startnode - error: %s\n", err_msg);
859 	  sqlite3_free (err_msg);
860 	  return 0;
861       }
862 
863 /* creating an Index supporting "end_node" */
864     table = sqlite3_mprintf ("%s_edge", topo_name);
865     xtable = gaiaDoubleQuotedSql (table);
866     sqlite3_free (table);
867     table = sqlite3_mprintf ("idx_%s_end_node", topo_name);
868     xconstraint1 = gaiaDoubleQuotedSql (table);
869     sqlite3_free (table);
870     sql =
871 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (end_node)",
872 			 xconstraint1, xtable);
873     free (xtable);
874     free (xconstraint1);
875     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
876     sqlite3_free (sql);
877     if (ret != SQLITE_OK)
878       {
879 	  spatialite_e ("CREATE INDEX edge-endnode - error: %s\n", err_msg);
880 	  sqlite3_free (err_msg);
881 	  return 0;
882       }
883 
884 /* creating an Index supporting "left_face" */
885     table = sqlite3_mprintf ("%s_edge", topo_name);
886     xtable = gaiaDoubleQuotedSql (table);
887     sqlite3_free (table);
888     table = sqlite3_mprintf ("idx_%s_edge_leftface", topo_name);
889     xconstraint1 = gaiaDoubleQuotedSql (table);
890     sqlite3_free (table);
891     sql =
892 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (left_face)",
893 			 xconstraint1, xtable);
894     free (xtable);
895     free (xconstraint1);
896     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
897     sqlite3_free (sql);
898     if (ret != SQLITE_OK)
899       {
900 	  spatialite_e ("CREATE INDEX edge-leftface - error: %s\n", err_msg);
901 	  sqlite3_free (err_msg);
902 	  return 0;
903       }
904 
905 /* creating an Index supporting "right_face" */
906     table = sqlite3_mprintf ("%s_edge", topo_name);
907     xtable = gaiaDoubleQuotedSql (table);
908     sqlite3_free (table);
909     table = sqlite3_mprintf ("idx_%s_edge_rightface", topo_name);
910     xconstraint1 = gaiaDoubleQuotedSql (table);
911     sqlite3_free (table);
912     sql =
913 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (right_face)",
914 			 xconstraint1, xtable);
915     free (xtable);
916     free (xconstraint1);
917     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
918     sqlite3_free (sql);
919     if (ret != SQLITE_OK)
920       {
921 	  spatialite_e ("CREATE INDEX edge-rightface - error: %s\n", err_msg);
922 	  sqlite3_free (err_msg);
923 	  return 0;
924       }
925 
926 /* creating an Index supporting "timestamp" */
927     table = sqlite3_mprintf ("%s_edge", topo_name);
928     xtable = gaiaDoubleQuotedSql (table);
929     sqlite3_free (table);
930     table = sqlite3_mprintf ("idx_%s_timestamp", topo_name);
931     xconstraint1 = gaiaDoubleQuotedSql (table);
932     sqlite3_free (table);
933     sql =
934 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (timestamp)",
935 			 xconstraint1, xtable);
936     free (xtable);
937     free (xconstraint1);
938     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
939     sqlite3_free (sql);
940     if (ret != SQLITE_OK)
941       {
942 	  spatialite_e ("CREATE INDEX edge-timestamps - error: %s\n", err_msg);
943 	  sqlite3_free (err_msg);
944 	  return 0;
945       }
946 
947     return 1;
948 }
949 
950 static int
do_create_seeds(sqlite3 * handle,const char * topo_name,int srid,int has_z)951 do_create_seeds (sqlite3 * handle, const char *topo_name, int srid, int has_z)
952 {
953 /* attempting to create the Topology Seeds table */
954     char *sql;
955     char *table;
956     char *xtable;
957     char *xconstraint1;
958     char *xconstraint2;
959     char *xedges;
960     char *xfaces;
961     char *trigger;
962     char *xtrigger;
963     char *err_msg = NULL;
964     int ret;
965 
966 /* creating the main table */
967     table = sqlite3_mprintf ("%s_seeds", topo_name);
968     xtable = gaiaDoubleQuotedSql (table);
969     sqlite3_free (table);
970     table = sqlite3_mprintf ("%s_seeds_edge_fk", topo_name);
971     xconstraint1 = gaiaDoubleQuotedSql (table);
972     sqlite3_free (table);
973     table = sqlite3_mprintf ("%s_seeds_face_fk", topo_name);
974     xconstraint2 = gaiaDoubleQuotedSql (table);
975     sqlite3_free (table);
976     table = sqlite3_mprintf ("%s_edge", topo_name);
977     xedges = gaiaDoubleQuotedSql (table);
978     sqlite3_free (table);
979     table = sqlite3_mprintf ("%s_face", topo_name);
980     xfaces = gaiaDoubleQuotedSql (table);
981     sqlite3_free (table);
982     sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (\n"
983 			   "\tseed_id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
984 			   "\tedge_id INTEGER,\n"
985 			   "\tface_id INTEGER,\n"
986 			   "\ttimestamp DATETIME,\n"
987 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (edge_id) "
988 			   "REFERENCES \"%s\" (edge_id) ON DELETE CASCADE,\n"
989 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (face_id) "
990 			   "REFERENCES \"%s\" (face_id) ON DELETE CASCADE)",
991 			   xtable, xconstraint1, xedges, xconstraint2, xfaces);
992     free (xtable);
993     free (xconstraint1);
994     free (xconstraint2);
995     free (xedges);
996     free (xfaces);
997     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
998     sqlite3_free (sql);
999     if (ret != SQLITE_OK)
1000       {
1001 	  spatialite_e ("CREATE TABLE topology-SEEDS - error: %s\n", err_msg);
1002 	  sqlite3_free (err_msg);
1003 	  return 0;
1004       }
1005 
1006 /* adding the "seeds_ins" trigger */
1007     trigger = sqlite3_mprintf ("%s_seeds_ins", topo_name);
1008     xtrigger = gaiaDoubleQuotedSql (trigger);
1009     sqlite3_free (trigger);
1010     table = sqlite3_mprintf ("%s_seeds", topo_name);
1011     xtable = gaiaDoubleQuotedSql (table);
1012     sqlite3_free (table);
1013     sql = sqlite3_mprintf ("CREATE TRIGGER \"%s\" AFTER INSERT ON \"%s\"\n"
1014 			   "FOR EACH ROW BEGIN\n"
1015 			   "\tUPDATE \"%s\" SET timestamp = strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ', 'now') "
1016 			   "WHERE seed_id = NEW.seed_id;"
1017 			   "END", xtrigger, xtable, xtable);
1018     free (xtrigger);
1019     free (xtable);
1020     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1021     sqlite3_free (sql);
1022     if (ret != SQLITE_OK)
1023       {
1024 	  spatialite_e
1025 	      ("CREATE TRIGGER topology-SEEDS next INSERT - error: %s\n",
1026 	       err_msg);
1027 	  sqlite3_free (err_msg);
1028 	  return 0;
1029       }
1030 
1031 /* adding the "seeds_update" trigger */
1032     trigger = sqlite3_mprintf ("%s_seeds_update", topo_name);
1033     xtrigger = gaiaDoubleQuotedSql (trigger);
1034     sqlite3_free (trigger);
1035     table = sqlite3_mprintf ("%s_seeds", topo_name);
1036     xtable = gaiaDoubleQuotedSql (table);
1037     sqlite3_free (table);
1038     sql = sqlite3_mprintf ("CREATE TRIGGER \"%s\" AFTER UPDATE ON \"%s\"\n"
1039 			   "FOR EACH ROW BEGIN\n"
1040 			   "\tUPDATE \"%s\" SET timestamp = strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ', 'now') "
1041 			   "WHERE seed_id = NEW.seed_id;"
1042 			   "END", xtrigger, xtable, xtable);
1043     free (xtrigger);
1044     free (xtable);
1045     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1046     sqlite3_free (sql);
1047     if (ret != SQLITE_OK)
1048       {
1049 	  spatialite_e
1050 	      ("CREATE TRIGGER topology-SEED next INSERT - error: %s\n",
1051 	       err_msg);
1052 	  sqlite3_free (err_msg);
1053 	  return 0;
1054       }
1055 
1056 /* creating the Seeds Geometry */
1057     table = sqlite3_mprintf ("%s_seeds", topo_name);
1058     sql =
1059 	sqlite3_mprintf
1060 	("SELECT AddGeometryColumn(%Q, 'geom', %d, 'POINT', %Q, 1)",
1061 	 table, srid, has_z ? "XYZ" : "XY");
1062     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1063     sqlite3_free (table);
1064     sqlite3_free (sql);
1065     if (ret != SQLITE_OK)
1066       {
1067 	  spatialite_e
1068 	      ("AddGeometryColumn topology-SEEDS - error: %s\n", err_msg);
1069 	  sqlite3_free (err_msg);
1070 	  return 0;
1071       }
1072 
1073 /* creating a Spatial Index supporting Seeds Geometry */
1074     table = sqlite3_mprintf ("%s_seeds", topo_name);
1075     sql = sqlite3_mprintf ("SELECT CreateSpatialIndex(%Q, 'geom')", table);
1076     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1077     sqlite3_free (table);
1078     sqlite3_free (sql);
1079     if (ret != SQLITE_OK)
1080       {
1081 	  spatialite_e
1082 	      ("CreateSpatialIndex topology-SEEDS - error: %s\n", err_msg);
1083 	  sqlite3_free (err_msg);
1084 	  return 0;
1085       }
1086 
1087 /* creating an Index supporting "edge_id" */
1088     table = sqlite3_mprintf ("%s_seeds", topo_name);
1089     xtable = gaiaDoubleQuotedSql (table);
1090     sqlite3_free (table);
1091     table = sqlite3_mprintf ("idx_%s_sdedge", topo_name);
1092     xconstraint1 = gaiaDoubleQuotedSql (table);
1093     sqlite3_free (table);
1094     sql =
1095 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (edge_id)",
1096 			 xconstraint1, xtable);
1097     free (xtable);
1098     free (xconstraint1);
1099     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1100     sqlite3_free (sql);
1101     if (ret != SQLITE_OK)
1102       {
1103 	  spatialite_e ("CREATE INDEX seeds-edge - error: %s\n", err_msg);
1104 	  sqlite3_free (err_msg);
1105 	  return 0;
1106       }
1107 
1108 /* creating an Index supporting "face_id" */
1109     table = sqlite3_mprintf ("%s_seeds", topo_name);
1110     xtable = gaiaDoubleQuotedSql (table);
1111     sqlite3_free (table);
1112     table = sqlite3_mprintf ("idx_%s_sdface", topo_name);
1113     xconstraint1 = gaiaDoubleQuotedSql (table);
1114     sqlite3_free (table);
1115     sql =
1116 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (face_id)",
1117 			 xconstraint1, xtable);
1118     free (xtable);
1119     free (xconstraint1);
1120     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1121     sqlite3_free (sql);
1122     if (ret != SQLITE_OK)
1123       {
1124 	  spatialite_e ("CREATE INDEX seeds-face - error: %s\n", err_msg);
1125 	  sqlite3_free (err_msg);
1126 	  return 0;
1127       }
1128 
1129 /* creating an Index supporting "timestamp" */
1130     table = sqlite3_mprintf ("%s_seeds", topo_name);
1131     xtable = gaiaDoubleQuotedSql (table);
1132     sqlite3_free (table);
1133     table = sqlite3_mprintf ("idx_%s_seeds_timestamp", topo_name);
1134     xconstraint1 = gaiaDoubleQuotedSql (table);
1135     sqlite3_free (table);
1136     sql =
1137 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (timestamp)",
1138 			 xconstraint1, xtable);
1139     free (xtable);
1140     free (xconstraint1);
1141     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1142     sqlite3_free (sql);
1143     if (ret != SQLITE_OK)
1144       {
1145 	  spatialite_e ("CREATE INDEX seeds-timestamps - error: %s\n", err_msg);
1146 	  sqlite3_free (err_msg);
1147 	  return 0;
1148       }
1149 
1150     return 1;
1151 }
1152 
1153 static int
do_create_edge_seeds(sqlite3 * handle,const char * topo_name)1154 do_create_edge_seeds (sqlite3 * handle, const char *topo_name)
1155 {
1156 /* attempting to create the Edge Seeds view */
1157     char *sql;
1158     char *table;
1159     char *xtable;
1160     char *xview;
1161     char *err_msg = NULL;
1162     int ret;
1163 
1164 /* creating the view */
1165     table = sqlite3_mprintf ("%s_edge_seeds", topo_name);
1166     xview = gaiaDoubleQuotedSql (table);
1167     sqlite3_free (table);
1168     table = sqlite3_mprintf ("%s_seeds", topo_name);
1169     xtable = gaiaDoubleQuotedSql (table);
1170     sqlite3_free (table);
1171     sql = sqlite3_mprintf ("CREATE VIEW \"%s\" AS\n"
1172 			   "SELECT seed_id AS rowid, edge_id AS edge_id, geom AS geom\n"
1173 			   "FROM \"%s\"\n"
1174 			   "WHERE edge_id IS NOT NULL", xview, xtable);
1175     free (xtable);
1176     free (xview);
1177     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1178     sqlite3_free (sql);
1179     if (ret != SQLITE_OK)
1180       {
1181 	  spatialite_e ("CREATE VIEW topology-EDGE-SEEDS - error: %s\n",
1182 			err_msg);
1183 	  sqlite3_free (err_msg);
1184 	  return 0;
1185       }
1186 
1187 /* registering a Spatial View */
1188     xview = sqlite3_mprintf ("%s_edge_seeds", topo_name);
1189     xtable = sqlite3_mprintf ("%s_seeds", topo_name);
1190     sql = sqlite3_mprintf ("INSERT INTO views_geometry_columns (view_name, "
1191 			   "view_geometry, view_rowid, f_table_name, f_geometry_column, read_only) "
1192 			   "VALUES (Lower(%Q), 'geom', 'rowid', Lower(%Q), 'geom', 1)",
1193 			   xview, xtable);
1194     sqlite3_free (xview);
1195     sqlite3_free (xtable);
1196     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1197     sqlite3_free (sql);
1198     if (ret != SQLITE_OK)
1199       {
1200 	  spatialite_e
1201 	      ("Registering Spatial VIEW topology-EDGE-SEEDS - error: %s\n",
1202 	       err_msg);
1203 	  sqlite3_free (err_msg);
1204 	  return 0;
1205       }
1206 
1207     return 1;
1208 }
1209 
1210 static int
do_create_face_seeds(sqlite3 * handle,const char * topo_name)1211 do_create_face_seeds (sqlite3 * handle, const char *topo_name)
1212 {
1213 /* attempting to create the Face Seeds view */
1214     char *sql;
1215     char *table;
1216     char *xtable;
1217     char *xview;
1218     char *err_msg = NULL;
1219     int ret;
1220 
1221 /* creating the view */
1222     table = sqlite3_mprintf ("%s_face_seeds", topo_name);
1223     xview = gaiaDoubleQuotedSql (table);
1224     sqlite3_free (table);
1225     table = sqlite3_mprintf ("%s_seeds", topo_name);
1226     xtable = gaiaDoubleQuotedSql (table);
1227     sqlite3_free (table);
1228     sql = sqlite3_mprintf ("CREATE VIEW \"%s\" AS\n"
1229 			   "SELECT seed_id AS rowid, face_id AS face_id, geom AS geom\n"
1230 			   "FROM \"%s\"\n"
1231 			   "WHERE face_id IS NOT NULL", xview, xtable);
1232     free (xtable);
1233     free (xview);
1234     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1235     sqlite3_free (sql);
1236     if (ret != SQLITE_OK)
1237       {
1238 	  spatialite_e ("CREATE VIEW topology-FACE-SEEDS - error: %s\n",
1239 			err_msg);
1240 	  sqlite3_free (err_msg);
1241 	  return 0;
1242       }
1243 
1244 /* registering a Spatial View */
1245     xview = sqlite3_mprintf ("%s_face_seeds", topo_name);
1246     xtable = sqlite3_mprintf ("%s_seeds", topo_name);
1247     sql = sqlite3_mprintf ("INSERT INTO views_geometry_columns (view_name, "
1248 			   "view_geometry, view_rowid, f_table_name, f_geometry_column, read_only) "
1249 			   "VALUES (Lower(%Q), 'geom', 'rowid', Lower(%Q), 'geom', 1)",
1250 			   xview, xtable);
1251     sqlite3_free (xview);
1252     sqlite3_free (xtable);
1253     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1254     sqlite3_free (sql);
1255     if (ret != SQLITE_OK)
1256       {
1257 	  spatialite_e
1258 	      ("Registering Spatial VIEW topology-FACE-SEEDS - error: %s\n",
1259 	       err_msg);
1260 	  sqlite3_free (err_msg);
1261 	  return 0;
1262       }
1263 
1264     return 1;
1265 }
1266 
1267 static int
do_create_face_geoms(sqlite3 * handle,const char * topo_name)1268 do_create_face_geoms (sqlite3 * handle, const char *topo_name)
1269 {
1270 /* attempting to create the Face Geoms view */
1271     char *sql;
1272     char *table;
1273     char *xtable;
1274     char *xview;
1275     char *err_msg = NULL;
1276     int ret;
1277 
1278 /* creating the view */
1279     table = sqlite3_mprintf ("%s_face_geoms", topo_name);
1280     xview = gaiaDoubleQuotedSql (table);
1281     sqlite3_free (table);
1282     table = sqlite3_mprintf ("%s_face", topo_name);
1283     xtable = gaiaDoubleQuotedSql (table);
1284     sqlite3_free (table);
1285     sql = sqlite3_mprintf ("CREATE VIEW \"%s\" AS\n"
1286 			   "SELECT face_id AS rowid, ST_GetFaceGeometry(%Q, face_id) AS geom\n"
1287 			   "FROM \"%s\"\n"
1288 			   "WHERE face_id <> 0", xview, topo_name, xtable);
1289     free (xtable);
1290     free (xview);
1291     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1292     sqlite3_free (sql);
1293     if (ret != SQLITE_OK)
1294       {
1295 	  spatialite_e ("CREATE VIEW topology-FACE-GEOMS - error: %s\n",
1296 			err_msg);
1297 	  sqlite3_free (err_msg);
1298 	  return 0;
1299       }
1300 
1301 /* registering a Spatial View */
1302     xview = sqlite3_mprintf ("%s_face_geoms", topo_name);
1303     xtable = sqlite3_mprintf ("%s_face", topo_name);
1304     sql = sqlite3_mprintf ("INSERT INTO views_geometry_columns (view_name, "
1305 			   "view_geometry, view_rowid, f_table_name, f_geometry_column, read_only) "
1306 			   "VALUES (Lower(%Q), 'geom', 'rowid', Lower(%Q), 'mbr', 1)",
1307 			   xview, xtable);
1308     sqlite3_free (xview);
1309     sqlite3_free (xtable);
1310     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1311     sqlite3_free (sql);
1312     if (ret != SQLITE_OK)
1313       {
1314 	  spatialite_e
1315 	      ("Registering Spatial VIEW topology-FACE-GEOMS - error: %s\n",
1316 	       err_msg);
1317 	  sqlite3_free (err_msg);
1318 	  return 0;
1319       }
1320 
1321     return 1;
1322 }
1323 
1324 static int
do_create_topolayers(sqlite3 * handle,const char * topo_name)1325 do_create_topolayers (sqlite3 * handle, const char *topo_name)
1326 {
1327 /* attempting to create the TopoLayers table */
1328     char *sql;
1329     char *table;
1330     char *xtable;
1331     char *xtrigger;
1332     char *err_msg = NULL;
1333     int ret;
1334 
1335 /* creating the main table */
1336     table = sqlite3_mprintf ("%s_topolayers", topo_name);
1337     xtable = gaiaDoubleQuotedSql (table);
1338     sqlite3_free (table);
1339     sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (\n"
1340 			   "\ttopolayer_id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
1341 			   "\ttopolayer_name NOT NULL UNIQUE)", xtable);
1342     free (xtable);
1343     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1344     sqlite3_free (sql);
1345     if (ret != SQLITE_OK)
1346       {
1347 	  spatialite_e ("CREATE TABLE topology-TOPOLAYERS - error: %s\n",
1348 			err_msg);
1349 	  sqlite3_free (err_msg);
1350 	  return 0;
1351       }
1352 
1353 /* creating TopoLayers triggers */
1354     table = sqlite3_mprintf ("%s_topolayers", topo_name);
1355     xtable = gaiaDoubleQuotedSql (table);
1356     sqlite3_free (table);
1357     table = sqlite3_mprintf ("%s_topolayer_name_insert", topo_name);
1358     xtrigger = gaiaDoubleQuotedSql (table);
1359     sqlite3_free (table);
1360     sql = sqlite3_mprintf ("CREATE TRIGGER IF NOT EXISTS \"%s\"\n"
1361 			   "BEFORE INSERT ON \"%s\"\nFOR EACH ROW BEGIN\n"
1362 			   "SELECT RAISE(ABORT,'insert on topolayers violates constraint: "
1363 			   "topolayer_name value must not contain a single quote')\n"
1364 			   "WHERE NEW.topolayer_name LIKE ('%%''%%');\n"
1365 			   "SELECT RAISE(ABORT,'insert on topolayers violates constraint: "
1366 			   "topolayers_name value must not contain a double quote')\n"
1367 			   "WHERE NEW.topolayer_name LIKE ('%%\"%%');\n"
1368 			   "SELECT RAISE(ABORT,'insert on topolayers violates constraint: "
1369 			   "topolayer_name value must be lower case')\n"
1370 			   "WHERE NEW.topolayer_name <> lower(NEW.topolayer_name);\nEND",
1371 			   xtrigger, xtable);
1372     free (xtable);
1373     free (xtrigger);
1374     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1375     sqlite3_free (sql);
1376     if (ret != SQLITE_OK)
1377       {
1378 	  spatialite_e ("SQL error: %s\n", err_msg);
1379 	  sqlite3_free (err_msg);
1380 	  return 0;
1381       }
1382     table = sqlite3_mprintf ("%s_topolayers", topo_name);
1383     xtable = gaiaDoubleQuotedSql (table);
1384     sqlite3_free (table);
1385     table = sqlite3_mprintf ("%s_topolayer_name_update", topo_name);
1386     xtrigger = gaiaDoubleQuotedSql (table);
1387     sqlite3_free (table);
1388     sql = sqlite3_mprintf ("CREATE TRIGGER IF NOT EXISTS \"%s\"\n"
1389 			   "BEFORE UPDATE OF 'topolayer_name' ON \"%s\"\nFOR EACH ROW BEGIN\n"
1390 			   "SELECT RAISE(ABORT,'update on topolayers violates constraint: "
1391 			   "topolayer_name value must not contain a single quote')\n"
1392 			   "WHERE NEW.topolayer_name LIKE ('%%''%%');\n"
1393 			   "SELECT RAISE(ABORT,'update on topolayers violates constraint: "
1394 			   "topolayer_name value must not contain a double quote')\n"
1395 			   "WHERE NEW.topolayer_name LIKE ('%%\"%%');\n"
1396 			   "SELECT RAISE(ABORT,'update on topolayers violates constraint: "
1397 			   "topolayer_name value must be lower case')\n"
1398 			   "WHERE NEW.topolayer_name <> lower(NEW.topolayer_name);\nEND",
1399 			   xtrigger, xtable);
1400     free (xtable);
1401     free (xtrigger);
1402     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1403     sqlite3_free (sql);
1404     if (ret != SQLITE_OK)
1405       {
1406 	  spatialite_e ("SQL error: %s\n", err_msg);
1407 	  sqlite3_free (err_msg);
1408 	  return 0;
1409       }
1410 
1411     return 1;
1412 }
1413 
1414 static int
do_create_topofeatures(sqlite3 * handle,const char * topo_name)1415 do_create_topofeatures (sqlite3 * handle, const char *topo_name)
1416 {
1417 /* attempting to create the TopoFeatures table */
1418     char *sql;
1419     char *table;
1420     char *xtable;
1421     char *xtable1;
1422     char *xtable2;
1423     char *xtable3;
1424     char *xtable4;
1425     char *xconstraint1;
1426     char *xconstraint2;
1427     char *xconstraint3;
1428     char *xconstraint4;
1429     char *err_msg = NULL;
1430     int ret;
1431 
1432 /* creating the main table */
1433     table = sqlite3_mprintf ("%s_topofeatures", topo_name);
1434     xtable = gaiaDoubleQuotedSql (table);
1435     sqlite3_free (table);
1436     table = sqlite3_mprintf ("%s_node", topo_name);
1437     xtable1 = gaiaDoubleQuotedSql (table);
1438     sqlite3_free (table);
1439     table = sqlite3_mprintf ("%s_edge", topo_name);
1440     xtable2 = gaiaDoubleQuotedSql (table);
1441     sqlite3_free (table);
1442     table = sqlite3_mprintf ("%s_face", topo_name);
1443     xtable3 = gaiaDoubleQuotedSql (table);
1444     sqlite3_free (table);
1445     table = sqlite3_mprintf ("%s_topolayers", topo_name);
1446     xtable4 = gaiaDoubleQuotedSql (table);
1447     sqlite3_free (table);
1448     table = sqlite3_mprintf ("fk_%s_ftnode", topo_name);
1449     xconstraint1 = gaiaDoubleQuotedSql (table);
1450     sqlite3_free (table);
1451     table = sqlite3_mprintf ("fk_%s_ftedge", topo_name);
1452     xconstraint2 = gaiaDoubleQuotedSql (table);
1453     sqlite3_free (table);
1454     table = sqlite3_mprintf ("fk_%s_ftface", topo_name);
1455     xconstraint3 = gaiaDoubleQuotedSql (table);
1456     sqlite3_free (table);
1457     table = sqlite3_mprintf ("fk_%s_topolayer", topo_name);
1458     xconstraint4 = gaiaDoubleQuotedSql (table);
1459     sqlite3_free (table);
1460     sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (\n"
1461 			   "\tuid INTEGER PRIMARY KEY AUTOINCREMENT,\n"
1462 			   "\tnode_id INTEGER,\n\tedge_id INTEGER,\n"
1463 			   "\tface_id INTEGER,\n\ttopolayer_id INTEGER NOT NULL,\n"
1464 			   "\tfid INTEGER NOT NULL,\n"
1465 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (node_id) "
1466 			   "REFERENCES \"%s\" (node_id) ON DELETE CASCADE,\n"
1467 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (edge_id) "
1468 			   "REFERENCES \"%s\" (edge_id) ON DELETE CASCADE,\n"
1469 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (face_id) "
1470 			   "REFERENCES \"%s\" (face_id) ON DELETE CASCADE,\n"
1471 			   "\tCONSTRAINT \"%s\" FOREIGN KEY (topolayer_id) "
1472 			   "REFERENCES \"%s\" (topolayer_id) ON DELETE CASCADE)",
1473 			   xtable, xconstraint1, xtable1, xconstraint2, xtable2,
1474 			   xconstraint3, xtable3, xconstraint4, xtable4);
1475     free (xtable);
1476     free (xtable1);
1477     free (xtable2);
1478     free (xtable3);
1479     free (xtable4);
1480     free (xconstraint1);
1481     free (xconstraint2);
1482     free (xconstraint3);
1483     free (xconstraint4);
1484     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1485     sqlite3_free (sql);
1486     if (ret != SQLITE_OK)
1487       {
1488 	  spatialite_e ("CREATE TABLE topology-TOPOFEATURES - error: %s\n",
1489 			err_msg);
1490 	  sqlite3_free (err_msg);
1491 	  return 0;
1492       }
1493 
1494 /* creating an Index supporting "node_id" */
1495     table = sqlite3_mprintf ("%s_topofeatures", topo_name);
1496     xtable = gaiaDoubleQuotedSql (table);
1497     sqlite3_free (table);
1498     table = sqlite3_mprintf ("idx_%s_ftnode", topo_name);
1499     xconstraint1 = gaiaDoubleQuotedSql (table);
1500     sqlite3_free (table);
1501     sql =
1502 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (node_id)",
1503 			 xconstraint1, xtable);
1504     free (xtable);
1505     free (xconstraint1);
1506     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1507     sqlite3_free (sql);
1508     if (ret != SQLITE_OK)
1509       {
1510 	  spatialite_e ("CREATE INDEX topofeatures-node - error: %s\n",
1511 			err_msg);
1512 	  sqlite3_free (err_msg);
1513 	  return 0;
1514       }
1515 
1516 /* creating an Index supporting "edge_id" */
1517     table = sqlite3_mprintf ("%s_topofeatures", topo_name);
1518     xtable = gaiaDoubleQuotedSql (table);
1519     sqlite3_free (table);
1520     table = sqlite3_mprintf ("idx_%s_ftedge", topo_name);
1521     xconstraint1 = gaiaDoubleQuotedSql (table);
1522     sqlite3_free (table);
1523     sql =
1524 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (edge_id)",
1525 			 xconstraint1, xtable);
1526     free (xtable);
1527     free (xconstraint1);
1528     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1529     sqlite3_free (sql);
1530     if (ret != SQLITE_OK)
1531       {
1532 	  spatialite_e ("CREATE INDEX topofeatures-edge - error: %s\n",
1533 			err_msg);
1534 	  sqlite3_free (err_msg);
1535 	  return 0;
1536       }
1537 
1538 /* creating an Index supporting "face_id" */
1539     table = sqlite3_mprintf ("%s_topofeatures", topo_name);
1540     xtable = gaiaDoubleQuotedSql (table);
1541     sqlite3_free (table);
1542     table = sqlite3_mprintf ("idx_%s_ftface", topo_name);
1543     xconstraint1 = gaiaDoubleQuotedSql (table);
1544     sqlite3_free (table);
1545     sql =
1546 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (face_id)",
1547 			 xconstraint1, xtable);
1548     free (xtable);
1549     free (xconstraint1);
1550     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1551     sqlite3_free (sql);
1552     if (ret != SQLITE_OK)
1553       {
1554 	  spatialite_e ("CREATE INDEX topofeatures-face - error: %s\n",
1555 			err_msg);
1556 	  sqlite3_free (err_msg);
1557 	  return 0;
1558       }
1559 
1560 /* creating an Index supporting "topolayers_id" */
1561     table = sqlite3_mprintf ("%s_topofeatures", topo_name);
1562     xtable = gaiaDoubleQuotedSql (table);
1563     sqlite3_free (table);
1564     table = sqlite3_mprintf ("idx_%s_fttopolayers", topo_name);
1565     xconstraint1 = gaiaDoubleQuotedSql (table);
1566     sqlite3_free (table);
1567     sql =
1568 	sqlite3_mprintf ("CREATE INDEX \"%s\" ON \"%s\" (topolayer_id, fid)",
1569 			 xconstraint1, xtable);
1570     free (xtable);
1571     free (xconstraint1);
1572     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1573     sqlite3_free (sql);
1574     if (ret != SQLITE_OK)
1575       {
1576 	  spatialite_e ("CREATE INDEX topofeatures-topolayers - error: %s\n",
1577 			err_msg);
1578 	  sqlite3_free (err_msg);
1579 	  return 0;
1580       }
1581 
1582     return 1;
1583 }
1584 
1585 GAIATOPO_DECLARE int
gaiaTopologyCreate(sqlite3 * handle,const char * topo_name,int srid,double tolerance,int has_z)1586 gaiaTopologyCreate (sqlite3 * handle, const char *topo_name, int srid,
1587 		    double tolerance, int has_z)
1588 {
1589 /* attempting to create a new Topology */
1590     int ret;
1591     char *sql;
1592 
1593 /* creating the Topologies table (just in case) */
1594     if (!do_create_topologies (handle))
1595 	return 0;
1596 
1597 /* testing for forbidding objects */
1598     if (!check_new_topology (handle, topo_name))
1599 	return 0;
1600 
1601 /* creating the Topology own Tables */
1602     if (!do_create_face (handle, topo_name, srid))
1603 	goto error;
1604     if (!do_create_node (handle, topo_name, srid, has_z))
1605 	goto error;
1606     if (!do_create_edge (handle, topo_name, srid, has_z))
1607 	goto error;
1608     if (!do_create_seeds (handle, topo_name, srid, has_z))
1609 	goto error;
1610     if (!do_create_edge_seeds (handle, topo_name))
1611 	goto error;
1612     if (!do_create_face_seeds (handle, topo_name))
1613 	goto error;
1614     if (!do_create_face_geoms (handle, topo_name))
1615 	goto error;
1616     if (!do_create_topolayers (handle, topo_name))
1617 	goto error;
1618     if (!do_create_topofeatures (handle, topo_name))
1619 	goto error;
1620 
1621 /* registering the Topology */
1622     sql = sqlite3_mprintf ("INSERT INTO MAIN.topologies (topology_name, "
1623 			   "srid, tolerance, has_z) VALUES (Lower(%Q), %d, %f, %d)",
1624 			   topo_name, srid, tolerance, has_z);
1625     ret = sqlite3_exec (handle, sql, NULL, NULL, NULL);
1626     sqlite3_free (sql);
1627     if (ret != SQLITE_OK)
1628 	goto error;
1629 
1630     return 1;
1631 
1632   error:
1633     return 0;
1634 }
1635 
1636 static int
check_existing_topology(sqlite3 * handle,const char * topo_name,int full_check)1637 check_existing_topology (sqlite3 * handle, const char *topo_name,
1638 			 int full_check)
1639 {
1640 /* testing if a Topology is already defined */
1641     char *sql;
1642     char *prev;
1643     char *table;
1644     int ret;
1645     int i;
1646     char **results;
1647     int rows;
1648     int columns;
1649     const char *value;
1650     int error = 0;
1651 
1652 /* testing if the Topology is already defined */
1653     sql = sqlite3_mprintf ("SELECT Count(*) FROM MAIN.topologies WHERE "
1654 			   "Lower(topology_name) = Lower(%Q)", topo_name);
1655     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
1656     sqlite3_free (sql);
1657     if (ret != SQLITE_OK)
1658 	return 0;
1659     if (rows < 1)
1660 	;
1661     else
1662       {
1663 	  for (i = 1; i <= rows; i++)
1664 	    {
1665 		value = results[(i * columns) + 0];
1666 		if (atoi (value) != 1)
1667 		    error = 1;
1668 	    }
1669       }
1670     sqlite3_free_table (results);
1671     if (error)
1672 	return 0;
1673     if (!full_check)
1674 	return 1;
1675 
1676 /* testing if all table/geom are correctly defined in geometry_columns */
1677     sql = sqlite3_mprintf ("SELECT Count(*) FROM geometry_columns WHERE");
1678     prev = sql;
1679     table = sqlite3_mprintf ("%s_node", topo_name);
1680     sql =
1681 	sqlite3_mprintf
1682 	("%s (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'geom')",
1683 	 prev, table);
1684     sqlite3_free (table);
1685     sqlite3_free (prev);
1686     prev = sql;
1687     table = sqlite3_mprintf ("%s_edge", topo_name);
1688     sql =
1689 	sqlite3_mprintf
1690 	("%s OR (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'geom')",
1691 	 prev, table);
1692     sqlite3_free (table);
1693     sqlite3_free (prev);
1694     prev = sql;
1695     table = sqlite3_mprintf ("%s_face", topo_name);
1696     sql =
1697 	sqlite3_mprintf
1698 	("%s OR (Lower(f_table_name) = Lower(%Q) AND f_geometry_column = 'mbr')",
1699 	 prev, table);
1700     sqlite3_free (table);
1701     sqlite3_free (prev);
1702     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
1703     sqlite3_free (sql);
1704     if (ret != SQLITE_OK)
1705 	return 0;
1706     if (rows < 1)
1707 	;
1708     else
1709       {
1710 	  for (i = 1; i <= rows; i++)
1711 	    {
1712 		value = results[(i * columns) + 0];
1713 		if (atoi (value) != 3)
1714 		    error = 1;
1715 	    }
1716       }
1717     sqlite3_free_table (results);
1718     if (error)
1719 	return 0;
1720 
1721 /* testing if all Spatial Views are correctly defined in geometry_columns */
1722     sql = sqlite3_mprintf ("SELECT Count(*) FROM views_geometry_columns WHERE");
1723     prev = sql;
1724     table = sqlite3_mprintf ("%s_edge_seeds", topo_name);
1725     sql =
1726 	sqlite3_mprintf
1727 	("%s (Lower(view_name) = Lower(%Q) AND view_geometry = 'geom')",
1728 	 prev, table);
1729     sqlite3_free (table);
1730     sqlite3_free (prev);
1731     prev = sql;
1732     table = sqlite3_mprintf ("%s_face_seeds", topo_name);
1733     sql =
1734 	sqlite3_mprintf
1735 	("%s OR (Lower(view_name) = Lower(%Q) AND view_geometry = 'geom')",
1736 	 prev, table);
1737     sqlite3_free (table);
1738     sqlite3_free (prev);
1739     prev = sql;
1740     table = sqlite3_mprintf ("%s_face_geoms", topo_name);
1741     sql =
1742 	sqlite3_mprintf
1743 	("%s OR (Lower(view_name) = Lower(%Q) AND view_geometry = 'geom')",
1744 	 prev, table);
1745     sqlite3_free (table);
1746     sqlite3_free (prev);
1747     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
1748     sqlite3_free (sql);
1749     if (ret != SQLITE_OK)
1750 	return 0;
1751     if (rows < 1)
1752 	;
1753     else
1754       {
1755 	  for (i = 1; i <= rows; i++)
1756 	    {
1757 		value = results[(i * columns) + 0];
1758 		if (atoi (value) != 3)
1759 		    error = 1;
1760 	    }
1761       }
1762     sqlite3_free_table (results);
1763     if (error)
1764 	return 0;
1765 
1766 
1767 /* testing if all tables are already defined */
1768     sql =
1769 	sqlite3_mprintf
1770 	("SELECT Count(*) FROM sqlite_master WHERE (type = 'table' AND (");
1771     prev = sql;
1772     table = sqlite3_mprintf ("%s_node", topo_name);
1773     sql = sqlite3_mprintf ("%s Lower(name) = Lower(%Q)", prev, table);
1774     sqlite3_free (table);
1775     sqlite3_free (prev);
1776     prev = sql;
1777     table = sqlite3_mprintf ("%s_edge", topo_name);
1778     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
1779     sqlite3_free (table);
1780     sqlite3_free (prev);
1781     prev = sql;
1782     table = sqlite3_mprintf ("%s_face", topo_name);
1783     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
1784     sqlite3_free (table);
1785     sqlite3_free (prev);
1786     prev = sql;
1787     table = sqlite3_mprintf ("idx_%s_node_geom", topo_name);
1788     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
1789     sqlite3_free (table);
1790     sqlite3_free (prev);
1791     prev = sql;
1792     table = sqlite3_mprintf ("idx_%s_edge_geom", topo_name);
1793     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
1794     sqlite3_free (table);
1795     sqlite3_free (prev);
1796     prev = sql;
1797     table = sqlite3_mprintf ("idx_%s_face_mbr", topo_name);
1798     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)))", prev, table);
1799     sqlite3_free (table);
1800     sqlite3_free (prev);
1801     prev = sql;
1802     table = sqlite3_mprintf ("%s_edge_seeds", topo_name);
1803     sql =
1804 	sqlite3_mprintf ("%s OR (type = 'view' AND (Lower(name) = Lower(%Q)",
1805 			 prev, table);
1806     sqlite3_free (table);
1807     sqlite3_free (prev);
1808     prev = sql;
1809     table = sqlite3_mprintf ("%s_face_seeds", topo_name);
1810     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)", prev, table);
1811     sqlite3_free (table);
1812     sqlite3_free (prev);
1813     prev = sql;
1814     table = sqlite3_mprintf ("%s_face_geoms", topo_name);
1815     sql = sqlite3_mprintf ("%s OR Lower(name) = Lower(%Q)))", prev, table);
1816     sqlite3_free (table);
1817     sqlite3_free (prev);
1818     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
1819     sqlite3_free (sql);
1820     if (ret != SQLITE_OK)
1821 	return 0;
1822     if (rows < 1)
1823 	;
1824     else
1825       {
1826 	  for (i = 1; i <= rows; i++)
1827 	    {
1828 		value = results[(i * columns) + 0];
1829 		if (atoi (value) != 9)
1830 		    error = 1;
1831 	    }
1832       }
1833     sqlite3_free_table (results);
1834     if (error)
1835 	return 0;
1836 
1837     return 1;
1838 }
1839 
1840 static int
do_drop_topo_face(sqlite3 * handle,const char * topo_name)1841 do_drop_topo_face (sqlite3 * handle, const char *topo_name)
1842 {
1843 /* attempting to drop the Topology-Face table */
1844     char *sql;
1845     char *table;
1846     char *xtable;
1847     char *err_msg = NULL;
1848     int ret;
1849 
1850 /* disabling the corresponding Spatial Index */
1851     table = sqlite3_mprintf ("%s_face", topo_name);
1852     sql = sqlite3_mprintf ("SELECT DisableSpatialIndex(%Q, 'mbr')", table);
1853     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1854     sqlite3_free (table);
1855     sqlite3_free (sql);
1856     if (ret != SQLITE_OK)
1857       {
1858 	  spatialite_e
1859 	      ("DisableSpatialIndex topology-face - error: %s\n", err_msg);
1860 	  sqlite3_free (err_msg);
1861 	  return 0;
1862       }
1863 
1864 /* discarding the Geometry column */
1865     table = sqlite3_mprintf ("%s_face", topo_name);
1866     sql = sqlite3_mprintf ("SELECT DiscardGeometryColumn(%Q, 'mbr')", table);
1867     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1868     sqlite3_free (table);
1869     sqlite3_free (sql);
1870     if (ret != SQLITE_OK)
1871       {
1872 	  spatialite_e
1873 	      ("DisableGeometryColumn topology-face - error: %s\n", err_msg);
1874 	  sqlite3_free (err_msg);
1875 	  return 0;
1876       }
1877 
1878 /* dropping the main table */
1879     table = sqlite3_mprintf ("%s_face", topo_name);
1880     xtable = gaiaDoubleQuotedSql (table);
1881     sqlite3_free (table);
1882     sql = sqlite3_mprintf ("DROP TABLE IF EXISTS \"%s\"", xtable);
1883     free (xtable);
1884     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1885     sqlite3_free (sql);
1886     if (ret != SQLITE_OK)
1887       {
1888 	  spatialite_e ("DROP topology-face - error: %s\n", err_msg);
1889 	  sqlite3_free (err_msg);
1890 	  return 0;
1891       }
1892 
1893 /* dropping the corresponding Spatial Index */
1894     table = sqlite3_mprintf ("idx_%s_face_mbr", topo_name);
1895     sql = sqlite3_mprintf ("DROP TABLE IF EXISTS \"%s\"", table);
1896     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1897     sqlite3_free (table);
1898     sqlite3_free (sql);
1899     if (ret != SQLITE_OK)
1900       {
1901 	  spatialite_e
1902 	      ("DROP SpatialIndex topology-face - error: %s\n", err_msg);
1903 	  sqlite3_free (err_msg);
1904 	  return 0;
1905       }
1906 
1907     return 1;
1908 }
1909 
1910 static int
do_drop_topo_table(sqlite3 * handle,const char * topo_name,const char * which,int spatial)1911 do_drop_topo_table (sqlite3 * handle, const char *topo_name, const char *which,
1912 		    int spatial)
1913 {
1914 /* attempting to drop some Topology table */
1915     char *sql;
1916     char *table;
1917     char *xtable;
1918     char *err_msg = NULL;
1919     int ret;
1920 
1921     if (strcmp (which, "face") == 0)
1922 	return do_drop_topo_face (handle, topo_name);
1923 
1924     if (spatial)
1925       {
1926 	  /* disabling the corresponding Spatial Index */
1927 	  table = sqlite3_mprintf ("%s_%s", topo_name, which);
1928 	  sql =
1929 	      sqlite3_mprintf ("SELECT DisableSpatialIndex(%Q, 'geom')", table);
1930 	  ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1931 	  sqlite3_free (table);
1932 	  sqlite3_free (sql);
1933 	  if (ret != SQLITE_OK)
1934 	    {
1935 		spatialite_e
1936 		    ("DisableSpatialIndex topology-%s - error: %s\n", which,
1937 		     err_msg);
1938 		sqlite3_free (err_msg);
1939 		return 0;
1940 	    }
1941 	  /* discarding the Geometry column */
1942 	  table = sqlite3_mprintf ("%s_%s", topo_name, which);
1943 	  sql =
1944 	      sqlite3_mprintf ("SELECT DiscardGeometryColumn(%Q, 'geom')",
1945 			       table);
1946 	  ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1947 	  sqlite3_free (table);
1948 	  sqlite3_free (sql);
1949 	  if (ret != SQLITE_OK)
1950 	    {
1951 		spatialite_e
1952 		    ("DisableGeometryColumn topology-%s - error: %s\n", which,
1953 		     err_msg);
1954 		sqlite3_free (err_msg);
1955 		return 0;
1956 	    }
1957       }
1958 
1959 /* dropping the main table */
1960     table = sqlite3_mprintf ("%s_%s", topo_name, which);
1961     xtable = gaiaDoubleQuotedSql (table);
1962     sqlite3_free (table);
1963     sql = sqlite3_mprintf ("DROP TABLE IF EXISTS MAIN.\"%s\"", xtable);
1964     free (xtable);
1965     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1966     sqlite3_free (sql);
1967     if (ret != SQLITE_OK)
1968       {
1969 	  spatialite_e ("DROP topology-%s - error: %s\n", which, err_msg);
1970 	  sqlite3_free (err_msg);
1971 	  return 0;
1972       }
1973 
1974     if (spatial)
1975       {
1976 	  /* dropping the corresponding Spatial Index */
1977 	  table = sqlite3_mprintf ("idx_%s_%s_geom", topo_name, which);
1978 	  sql = sqlite3_mprintf ("DROP TABLE IF EXISTS MAIN.\"%s\"", table);
1979 	  ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
1980 	  sqlite3_free (table);
1981 	  sqlite3_free (sql);
1982 	  if (ret != SQLITE_OK)
1983 	    {
1984 		spatialite_e
1985 		    ("DROP SpatialIndex topology-%s - error: %s\n", which,
1986 		     err_msg);
1987 		sqlite3_free (err_msg);
1988 		return 0;
1989 	    }
1990       }
1991 
1992     return 1;
1993 }
1994 
1995 static int
do_drop_topo_view(sqlite3 * handle,const char * topo_name,const char * which)1996 do_drop_topo_view (sqlite3 * handle, const char *topo_name, const char *which)
1997 {
1998 /* attempting to drop some Topology view */
1999     char *sql;
2000     char *table;
2001     char *xtable;
2002     char *err_msg = NULL;
2003     int ret;
2004 
2005 /* unregistering the Spatial View */
2006     table = sqlite3_mprintf ("%s_%s", topo_name, which);
2007     sql =
2008 	sqlite3_mprintf
2009 	("DELETE FROM views_geometry_columns WHERE view_name = Lower(%Q)",
2010 	 table);
2011     sqlite3_free (table);
2012     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
2013     sqlite3_free (sql);
2014     if (ret != SQLITE_OK)
2015       {
2016 	  spatialite_e ("Unregister Spatial View -%s - error: %s\n", which,
2017 			err_msg);
2018 	  sqlite3_free (err_msg);
2019       }
2020 
2021 /* dropping the view */
2022     table = sqlite3_mprintf ("%s_%s", topo_name, which);
2023     xtable = gaiaDoubleQuotedSql (table);
2024     sqlite3_free (table);
2025     sql = sqlite3_mprintf ("DROP VIEW IF EXISTS MAIN.\"%s\"", xtable);
2026     free (xtable);
2027     ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
2028     sqlite3_free (sql);
2029     if (ret != SQLITE_OK)
2030       {
2031 	  spatialite_e ("DROP topology-%s - error: %s\n", which, err_msg);
2032 	  sqlite3_free (err_msg);
2033 	  return 0;
2034       }
2035 
2036     return 1;
2037 }
2038 
2039 static int
do_drop_topofeature_tables(sqlite3 * handle,const char * topo_name)2040 do_drop_topofeature_tables (sqlite3 * handle, const char *topo_name)
2041 {
2042 /* dropping any eventual topofeatures table */
2043     char *table;
2044     char *xtable;
2045     char *sql;
2046     char *err_msg = NULL;
2047     int ret;
2048     int i;
2049     char **results;
2050     int rows;
2051     int columns;
2052 
2053     table = sqlite3_mprintf ("%s_topolayers", topo_name);
2054     xtable = gaiaDoubleQuotedSql (table);
2055     sqlite3_free (table);
2056     sql = sqlite3_mprintf ("SELECT topolayer_id FROM MAIN.\"%s\"", xtable);
2057     free (xtable);
2058     ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL);
2059     sqlite3_free (sql);
2060     if (ret != SQLITE_OK)
2061 	return 1;
2062     if (rows < 1)
2063 	;
2064     else
2065       {
2066 	  for (i = 1; i <= rows; i++)
2067 	    {
2068 		const char *id = results[(i * columns) + 0];
2069 		table = sqlite3_mprintf ("%s_topofeatures_%s", topo_name, id);
2070 		xtable = gaiaDoubleQuotedSql (table);
2071 		sqlite3_free (table);
2072 		sql =
2073 		    sqlite3_mprintf ("DROP TABLE IF EXISTS MAIN.\"%s\"",
2074 				     xtable);
2075 		free (xtable);
2076 		ret = sqlite3_exec (handle, sql, NULL, NULL, &err_msg);
2077 		sqlite3_free (sql);
2078 		if (ret != SQLITE_OK)
2079 		  {
2080 		      spatialite_e ("DROP topology-features (%s) - error: %s\n",
2081 				    id, err_msg);
2082 		      sqlite3_free (err_msg);
2083 		      return 0;
2084 		  }
2085 	    }
2086       }
2087     sqlite3_free_table (results);
2088     return 1;
2089 }
2090 
2091 static int
do_get_topology(sqlite3 * handle,const char * topo_name,char ** topology_name,int * srid,double * tolerance,int * has_z)2092 do_get_topology (sqlite3 * handle, const char *topo_name, char **topology_name,
2093 		 int *srid, double *tolerance, int *has_z)
2094 {
2095 /* retrieving a Topology configuration */
2096     char *sql;
2097     int ret;
2098     sqlite3_stmt *stmt = NULL;
2099     int ok = 0;
2100     char *xtopology_name = NULL;
2101     int xsrid;
2102     double xtolerance;
2103     int xhas_z;
2104 
2105 /* preparing the SQL query */
2106     sql =
2107 	sqlite3_mprintf
2108 	("SELECT topology_name, srid, tolerance, has_z FROM MAIN.topologies WHERE "
2109 	 "Lower(topology_name) = Lower(%Q)", topo_name);
2110     ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt, NULL);
2111     sqlite3_free (sql);
2112     if (ret != SQLITE_OK)
2113       {
2114 	  spatialite_e ("SELECT FROM topologys error: \"%s\"\n",
2115 			sqlite3_errmsg (handle));
2116 	  return 0;
2117       }
2118 
2119     while (1)
2120       {
2121 	  /* scrolling the result set rows */
2122 	  ret = sqlite3_step (stmt);
2123 	  if (ret == SQLITE_DONE)
2124 	      break;		/* end of result set */
2125 	  if (ret == SQLITE_ROW)
2126 	    {
2127 		int ok_name = 0;
2128 		int ok_srid = 0;
2129 		int ok_tolerance = 0;
2130 		int ok_z = 0;
2131 		if (sqlite3_column_type (stmt, 0) == SQLITE_TEXT)
2132 		  {
2133 		      const char *str =
2134 			  (const char *) sqlite3_column_text (stmt, 0);
2135 		      if (xtopology_name != NULL)
2136 			  free (xtopology_name);
2137 		      xtopology_name = malloc (strlen (str) + 1);
2138 		      strcpy (xtopology_name, str);
2139 		      ok_name = 1;
2140 		  }
2141 		if (sqlite3_column_type (stmt, 1) == SQLITE_INTEGER)
2142 		  {
2143 		      xsrid = sqlite3_column_int (stmt, 1);
2144 		      ok_srid = 1;
2145 		  }
2146 		if (sqlite3_column_type (stmt, 2) == SQLITE_FLOAT)
2147 		  {
2148 		      xtolerance = sqlite3_column_double (stmt, 2);
2149 		      ok_tolerance = 1;
2150 		  }
2151 		if (sqlite3_column_type (stmt, 3) == SQLITE_INTEGER)
2152 		  {
2153 		      xhas_z = sqlite3_column_int (stmt, 3);
2154 		      ok_z = 1;
2155 		  }
2156 		if (ok_name && ok_srid && ok_tolerance && ok_z)
2157 		  {
2158 		      ok = 1;
2159 		      break;
2160 		  }
2161 	    }
2162 	  else
2163 	    {
2164 		spatialite_e
2165 		    ("step: SELECT FROM topologies error: \"%s\"\n",
2166 		     sqlite3_errmsg (handle));
2167 		sqlite3_finalize (stmt);
2168 		return 0;
2169 	    }
2170       }
2171     sqlite3_finalize (stmt);
2172 
2173     if (ok)
2174       {
2175 	  *topology_name = xtopology_name;
2176 	  *srid = xsrid;
2177 	  *tolerance = xtolerance;
2178 	  *has_z = xhas_z;
2179 	  return 1;
2180       }
2181 
2182     if (xtopology_name != NULL)
2183 	free (xtopology_name);
2184     return 0;
2185 }
2186 
2187 GAIATOPO_DECLARE GaiaTopologyAccessorPtr
gaiaGetTopology(sqlite3 * handle,const void * cache,const char * topo_name)2188 gaiaGetTopology (sqlite3 * handle, const void *cache, const char *topo_name)
2189 {
2190 /* attempting to get a reference to some Topology Accessor Object */
2191     GaiaTopologyAccessorPtr accessor;
2192 
2193 /* attempting to retrieve an alredy cached definition */
2194     accessor = gaiaTopologyFromCache (cache, topo_name);
2195     if (accessor != NULL)
2196 	return accessor;
2197 
2198 /* attempting to create a new Topology Accessor */
2199     accessor = gaiaTopologyFromDBMS (handle, cache, topo_name);
2200     return accessor;
2201 }
2202 
2203 GAIATOPO_DECLARE GaiaTopologyAccessorPtr
gaiaTopologyFromCache(const void * p_cache,const char * topo_name)2204 gaiaTopologyFromCache (const void *p_cache, const char *topo_name)
2205 {
2206 /* attempting to retrieve an already defined Topology Accessor Object from the Connection Cache */
2207     struct gaia_topology *ptr;
2208     struct splite_internal_cache *cache =
2209 	(struct splite_internal_cache *) p_cache;
2210     if (cache == NULL)
2211 	return NULL;
2212 
2213     ptr = (struct gaia_topology *) (cache->firstTopology);
2214     while (ptr != NULL)
2215       {
2216 	  /* checking for an already registered Topology */
2217 	  if (strcasecmp (topo_name, ptr->topology_name) == 0)
2218 	      return (GaiaTopologyAccessorPtr) ptr;
2219 	  ptr = ptr->next;
2220       }
2221     return NULL;
2222 }
2223 
2224 GAIATOPO_DECLARE int
gaiaReadTopologyFromDBMS(sqlite3 * handle,const char * topo_name,char ** topology_name,int * srid,double * tolerance,int * has_z)2225 gaiaReadTopologyFromDBMS (sqlite3 *
2226 			  handle,
2227 			  const char
2228 			  *topo_name, char **topology_name, int *srid,
2229 			  double *tolerance, int *has_z)
2230 {
2231 /* testing for existing DBMS objects */
2232     if (!check_existing_topology (handle, topo_name, 1))
2233 	return 0;
2234 
2235 /* retrieving the Topology configuration */
2236     if (!do_get_topology
2237 	(handle, topo_name, topology_name, srid, tolerance, has_z))
2238 	return 0;
2239     return 1;
2240 }
2241 
2242 GAIATOPO_DECLARE GaiaTopologyAccessorPtr
gaiaTopologyFromDBMS(sqlite3 * handle,const void * p_cache,const char * topo_name)2243 gaiaTopologyFromDBMS (sqlite3 * handle, const void *p_cache,
2244 		      const char *topo_name)
2245 {
2246 /* attempting to create a Topology Accessor Object into the Connection Cache */
2247     const RTCTX *ctx = NULL;
2248     RTT_BE_CALLBACKS *callbacks;
2249     struct gaia_topology *ptr;
2250     struct splite_internal_cache *cache =
2251 	(struct splite_internal_cache *) p_cache;
2252     if (cache == NULL)
2253 	return NULL;
2254     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2255 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2256 	return NULL;
2257     ctx = cache->RTTOPO_handle;
2258     if (ctx == NULL)
2259 	return NULL;
2260 
2261 /* allocating and initializing the opaque object */
2262     ptr = malloc (sizeof (struct gaia_topology));
2263     ptr->db_handle = handle;
2264     ptr->cache = cache;
2265     ptr->topology_name = NULL;
2266     ptr->srid = -1;
2267     ptr->tolerance = 0;
2268     ptr->has_z = 0;
2269     ptr->last_error_message = NULL;
2270     ptr->rtt_iface = rtt_CreateBackendIface (ctx, (const RTT_BE_DATA *) ptr);
2271     ptr->prev = cache->lastTopology;
2272     ptr->next = NULL;
2273 
2274     callbacks = malloc (sizeof (RTT_BE_CALLBACKS));
2275     callbacks->lastErrorMessage = callback_lastErrorMessage;
2276     callbacks->topoGetSRID = callback_topoGetSRID;
2277     callbacks->topoGetPrecision = callback_topoGetPrecision;
2278     callbacks->topoHasZ = callback_topoHasZ;
2279     callbacks->createTopology = NULL;
2280     callbacks->loadTopologyByName = callback_loadTopologyByName;
2281     callbacks->freeTopology = callback_freeTopology;
2282     callbacks->getNodeById = callback_getNodeById;
2283     callbacks->getNodeWithinDistance2D = callback_getNodeWithinDistance2D;
2284     callbacks->insertNodes = callback_insertNodes;
2285     callbacks->getEdgeById = callback_getEdgeById;
2286     callbacks->getEdgeWithinDistance2D = callback_getEdgeWithinDistance2D;
2287     callbacks->getNextEdgeId = callback_getNextEdgeId;
2288     callbacks->insertEdges = callback_insertEdges;
2289     callbacks->updateEdges = callback_updateEdges;
2290     callbacks->getFaceById = callback_getFaceById;
2291     callbacks->getFaceContainingPoint = callback_getFaceContainingPoint;
2292     callbacks->deleteEdges = callback_deleteEdges;
2293     callbacks->getNodeWithinBox2D = callback_getNodeWithinBox2D;
2294     callbacks->getEdgeWithinBox2D = callback_getEdgeWithinBox2D;
2295     callbacks->getEdgeByNode = callback_getEdgeByNode;
2296     callbacks->updateNodes = callback_updateNodes;
2297     callbacks->insertFaces = callback_insertFaces;
2298     callbacks->updateFacesById = callback_updateFacesById;
2299     callbacks->deleteFacesById = callback_deleteFacesById;
2300     callbacks->getRingEdges = callback_getRingEdges;
2301     callbacks->updateEdgesById = callback_updateEdgesById;
2302     callbacks->getEdgeByFace = callback_getEdgeByFace;
2303     callbacks->getNodeByFace = callback_getNodeByFace;
2304     callbacks->updateNodesById = callback_updateNodesById;
2305     callbacks->deleteNodesById = callback_deleteNodesById;
2306     callbacks->updateTopoGeomEdgeSplit = callback_updateTopoGeomEdgeSplit;
2307     callbacks->updateTopoGeomFaceSplit = callback_updateTopoGeomFaceSplit;
2308     callbacks->checkTopoGeomRemEdge = callback_checkTopoGeomRemEdge;
2309     callbacks->updateTopoGeomFaceHeal = callback_updateTopoGeomFaceHeal;
2310     callbacks->checkTopoGeomRemNode = callback_checkTopoGeomRemNode;
2311     callbacks->updateTopoGeomEdgeHeal = callback_updateTopoGeomEdgeHeal;
2312     callbacks->getFaceWithinBox2D = callback_getFaceWithinBox2D;
2313     ptr->callbacks = callbacks;
2314 
2315     rtt_BackendIfaceRegisterCallbacks (ptr->rtt_iface, callbacks);
2316     ptr->rtt_topology = rtt_LoadTopology (ptr->rtt_iface, topo_name);
2317 
2318     ptr->stmt_getNodeWithinDistance2D = NULL;
2319     ptr->stmt_insertNodes = NULL;
2320     ptr->stmt_getEdgeWithinDistance2D = NULL;
2321     ptr->stmt_getNextEdgeId = NULL;
2322     ptr->stmt_setNextEdgeId = NULL;
2323     ptr->stmt_insertEdges = NULL;
2324     ptr->stmt_getFaceContainingPoint_1 = NULL;
2325     ptr->stmt_getFaceContainingPoint_2 = NULL;
2326     ptr->stmt_deleteEdges = NULL;
2327     ptr->stmt_getNodeWithinBox2D = NULL;
2328     ptr->stmt_getEdgeWithinBox2D = NULL;
2329     ptr->stmt_getFaceWithinBox2D = NULL;
2330     ptr->stmt_getAllEdges = NULL;
2331     ptr->stmt_updateNodes = NULL;
2332     ptr->stmt_insertFaces = NULL;
2333     ptr->stmt_updateFacesById = NULL;
2334     ptr->stmt_deleteFacesById = NULL;
2335     ptr->stmt_deleteNodesById = NULL;
2336     ptr->stmt_getRingEdges = NULL;
2337     if (ptr->rtt_topology == NULL)
2338       {
2339 	  char *msg =
2340 	      sqlite3_mprintf ("Topology \"%s\" is undefined !!!", topo_name);
2341 	  gaiaSetRtTopoErrorMsg (p_cache, msg);
2342 	  sqlite3_free (msg);
2343 	  goto invalid;
2344       }
2345 
2346 /* creating the SQL prepared statements */
2347     create_topogeo_prepared_stmts ((GaiaTopologyAccessorPtr) ptr);
2348 
2349     return (GaiaTopologyAccessorPtr) ptr;
2350 
2351   invalid:
2352     gaiaTopologyDestroy ((GaiaTopologyAccessorPtr) ptr);
2353     return NULL;
2354 }
2355 
2356 GAIATOPO_DECLARE void
gaiaTopologyDestroy(GaiaTopologyAccessorPtr topo_ptr)2357 gaiaTopologyDestroy (GaiaTopologyAccessorPtr topo_ptr)
2358 {
2359 /* destroying a Topology Accessor Object */
2360     struct gaia_topology *prev;
2361     struct gaia_topology *next;
2362     struct splite_internal_cache *cache;
2363     struct gaia_topology *ptr = (struct gaia_topology *) topo_ptr;
2364     if (ptr == NULL)
2365 	return;
2366 
2367     prev = ptr->prev;
2368     next = ptr->next;
2369     cache = (struct splite_internal_cache *) (ptr->cache);
2370     if (ptr->rtt_topology != NULL)
2371 	rtt_FreeTopology ((RTT_TOPOLOGY *) (ptr->rtt_topology));
2372     if (ptr->rtt_iface != NULL)
2373 	rtt_FreeBackendIface ((RTT_BE_IFACE *) (ptr->rtt_iface));
2374     if (ptr->callbacks != NULL)
2375 	free (ptr->callbacks);
2376     if (ptr->topology_name != NULL)
2377 	free (ptr->topology_name);
2378     if (ptr->last_error_message != NULL)
2379 	free (ptr->last_error_message);
2380 
2381     finalize_topogeo_prepared_stmts (topo_ptr);
2382     free (ptr);
2383 
2384 /* unregistering from the Internal Cache double linked list */
2385     if (prev != NULL)
2386 	prev->next = next;
2387     if (next != NULL)
2388 	next->prev = prev;
2389     if (cache->firstTopology == ptr)
2390 	cache->firstTopology = next;
2391     if (cache->lastTopology == ptr)
2392 	cache->lastTopology = prev;
2393 }
2394 
2395 TOPOLOGY_PRIVATE void
finalize_topogeo_prepared_stmts(GaiaTopologyAccessorPtr accessor)2396 finalize_topogeo_prepared_stmts (GaiaTopologyAccessorPtr accessor)
2397 {
2398 /* finalizing the SQL prepared statements */
2399     struct gaia_topology *ptr = (struct gaia_topology *) accessor;
2400     if (ptr->stmt_getNodeWithinDistance2D != NULL)
2401 	sqlite3_finalize (ptr->stmt_getNodeWithinDistance2D);
2402     if (ptr->stmt_insertNodes != NULL)
2403 	sqlite3_finalize (ptr->stmt_insertNodes);
2404     if (ptr->stmt_getEdgeWithinDistance2D != NULL)
2405 	sqlite3_finalize (ptr->stmt_getEdgeWithinDistance2D);
2406     if (ptr->stmt_getNextEdgeId != NULL)
2407 	sqlite3_finalize (ptr->stmt_getNextEdgeId);
2408     if (ptr->stmt_setNextEdgeId != NULL)
2409 	sqlite3_finalize (ptr->stmt_setNextEdgeId);
2410     if (ptr->stmt_insertEdges != NULL)
2411 	sqlite3_finalize (ptr->stmt_insertEdges);
2412     if (ptr->stmt_getFaceContainingPoint_1 != NULL)
2413 	sqlite3_finalize (ptr->stmt_getFaceContainingPoint_1);
2414     if (ptr->stmt_getFaceContainingPoint_2 != NULL)
2415 	sqlite3_finalize (ptr->stmt_getFaceContainingPoint_2);
2416     if (ptr->stmt_deleteEdges != NULL)
2417 	sqlite3_finalize (ptr->stmt_deleteEdges);
2418     if (ptr->stmt_getNodeWithinBox2D != NULL)
2419 	sqlite3_finalize (ptr->stmt_getNodeWithinBox2D);
2420     if (ptr->stmt_getEdgeWithinBox2D != NULL)
2421 	sqlite3_finalize (ptr->stmt_getEdgeWithinBox2D);
2422     if (ptr->stmt_getFaceWithinBox2D != NULL)
2423 	sqlite3_finalize (ptr->stmt_getFaceWithinBox2D);
2424     if (ptr->stmt_getAllEdges != NULL)
2425 	sqlite3_finalize (ptr->stmt_getAllEdges);
2426     if (ptr->stmt_updateNodes != NULL)
2427 	sqlite3_finalize (ptr->stmt_updateNodes);
2428     if (ptr->stmt_insertFaces != NULL)
2429 	sqlite3_finalize (ptr->stmt_insertFaces);
2430     if (ptr->stmt_updateFacesById != NULL)
2431 	sqlite3_finalize (ptr->stmt_updateFacesById);
2432     if (ptr->stmt_deleteFacesById != NULL)
2433 	sqlite3_finalize (ptr->stmt_deleteFacesById);
2434     if (ptr->stmt_deleteNodesById != NULL)
2435 	sqlite3_finalize (ptr->stmt_deleteNodesById);
2436     if (ptr->stmt_getRingEdges != NULL)
2437 	sqlite3_finalize (ptr->stmt_getRingEdges);
2438     ptr->stmt_getNodeWithinDistance2D = NULL;
2439     ptr->stmt_insertNodes = NULL;
2440     ptr->stmt_getEdgeWithinDistance2D = NULL;
2441     ptr->stmt_getNextEdgeId = NULL;
2442     ptr->stmt_setNextEdgeId = NULL;
2443     ptr->stmt_insertEdges = NULL;
2444     ptr->stmt_getFaceContainingPoint_1 = NULL;
2445     ptr->stmt_getFaceContainingPoint_2 = NULL;
2446     ptr->stmt_deleteEdges = NULL;
2447     ptr->stmt_getNodeWithinBox2D = NULL;
2448     ptr->stmt_getEdgeWithinBox2D = NULL;
2449     ptr->stmt_getFaceWithinBox2D = NULL;
2450     ptr->stmt_getAllEdges = NULL;
2451     ptr->stmt_updateNodes = NULL;
2452     ptr->stmt_insertFaces = NULL;
2453     ptr->stmt_updateFacesById = NULL;
2454     ptr->stmt_deleteFacesById = NULL;
2455     ptr->stmt_deleteNodesById = NULL;
2456     ptr->stmt_getRingEdges = NULL;
2457 }
2458 
2459 TOPOLOGY_PRIVATE void
create_topogeo_prepared_stmts(GaiaTopologyAccessorPtr accessor)2460 create_topogeo_prepared_stmts (GaiaTopologyAccessorPtr accessor)
2461 {
2462 /* creating the SQL prepared statements */
2463     struct gaia_topology *ptr = (struct gaia_topology *) accessor;
2464     finalize_topogeo_prepared_stmts (accessor);
2465     ptr->stmt_getNodeWithinDistance2D =
2466 	do_create_stmt_getNodeWithinDistance2D (accessor);
2467     ptr->stmt_insertNodes = do_create_stmt_insertNodes (accessor);
2468     ptr->stmt_getEdgeWithinDistance2D =
2469 	do_create_stmt_getEdgeWithinDistance2D (accessor);
2470     ptr->stmt_getNextEdgeId = do_create_stmt_getNextEdgeId (accessor);
2471     ptr->stmt_setNextEdgeId = do_create_stmt_setNextEdgeId (accessor);
2472     ptr->stmt_insertEdges = do_create_stmt_insertEdges (accessor);
2473     ptr->stmt_getFaceContainingPoint_1 =
2474 	do_create_stmt_getFaceContainingPoint_1 (accessor);
2475     ptr->stmt_getFaceContainingPoint_2 =
2476 	do_create_stmt_getFaceContainingPoint_2 (accessor);
2477     ptr->stmt_deleteEdges = NULL;
2478     ptr->stmt_getNodeWithinBox2D = do_create_stmt_getNodeWithinBox2D (accessor);
2479     ptr->stmt_getEdgeWithinBox2D = do_create_stmt_getEdgeWithinBox2D (accessor);
2480     ptr->stmt_getFaceWithinBox2D = do_create_stmt_getFaceWithinBox2D (accessor);
2481     ptr->stmt_getAllEdges = do_create_stmt_getAllEdges (accessor);
2482     ptr->stmt_updateNodes = NULL;
2483     ptr->stmt_insertFaces = do_create_stmt_insertFaces (accessor);
2484     ptr->stmt_updateFacesById = do_create_stmt_updateFacesById (accessor);
2485     ptr->stmt_deleteFacesById = do_create_stmt_deleteFacesById (accessor);
2486     ptr->stmt_deleteNodesById = do_create_stmt_deleteNodesById (accessor);
2487     ptr->stmt_getRingEdges = do_create_stmt_getRingEdges (accessor);
2488 }
2489 
2490 TOPOLOGY_PRIVATE void
finalize_all_topo_prepared_stmts(const void * p_cache)2491 finalize_all_topo_prepared_stmts (const void *p_cache)
2492 {
2493 /* finalizing all Topology-related prepared Stms */
2494     struct gaia_topology *p_topo;
2495     struct gaia_network *p_network;
2496     struct splite_internal_cache *cache =
2497 	(struct splite_internal_cache *) p_cache;
2498     if (cache == NULL)
2499 	return;
2500     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2501 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2502 	return;
2503 
2504     p_topo = (struct gaia_topology *) cache->firstTopology;
2505     while (p_topo != NULL)
2506       {
2507 	  finalize_topogeo_prepared_stmts ((GaiaTopologyAccessorPtr) p_topo);
2508 	  p_topo = p_topo->next;
2509       }
2510 
2511     p_network = (struct gaia_network *) cache->firstNetwork;
2512     while (p_network != NULL)
2513       {
2514 	  finalize_toponet_prepared_stmts ((GaiaNetworkAccessorPtr) p_network);
2515 	  p_network = p_network->next;
2516       }
2517 }
2518 
2519 TOPOLOGY_PRIVATE void
create_all_topo_prepared_stmts(const void * p_cache)2520 create_all_topo_prepared_stmts (const void *p_cache)
2521 {
2522 /* (re)creating all Topology-related prepared Stms */
2523     struct gaia_topology *p_topo;
2524     struct gaia_network *p_network;
2525     struct splite_internal_cache *cache =
2526 	(struct splite_internal_cache *) p_cache;
2527     if (cache == NULL)
2528 	return;
2529     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2530 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2531 	return;
2532 
2533     p_topo = (struct gaia_topology *) cache->firstTopology;
2534     while (p_topo != NULL)
2535       {
2536 	  create_topogeo_prepared_stmts ((GaiaTopologyAccessorPtr) p_topo);
2537 	  p_topo = p_topo->next;
2538       }
2539 
2540     p_network = (struct gaia_network *) cache->firstNetwork;
2541     while (p_network != NULL)
2542       {
2543 	  create_toponet_prepared_stmts ((GaiaNetworkAccessorPtr) p_network);
2544 	  p_network = p_network->next;
2545       }
2546 }
2547 
2548 TOPOLOGY_PRIVATE void
gaiatopo_reset_last_error_msg(GaiaTopologyAccessorPtr accessor)2549 gaiatopo_reset_last_error_msg (GaiaTopologyAccessorPtr accessor)
2550 {
2551 /* resets the last Topology error message */
2552     struct gaia_topology *topo = (struct gaia_topology *) accessor;
2553     if (topo == NULL)
2554 	return;
2555 
2556     if (topo->cache != NULL)
2557 	gaiaResetRtTopoMsg (topo->cache);
2558     if (topo->last_error_message != NULL)
2559 	free (topo->last_error_message);
2560     topo->last_error_message = NULL;
2561 }
2562 
2563 TOPOLOGY_PRIVATE void
gaiatopo_set_last_error_msg(GaiaTopologyAccessorPtr accessor,const char * msg)2564 gaiatopo_set_last_error_msg (GaiaTopologyAccessorPtr accessor, const char *msg)
2565 {
2566 /* sets the last Topology error message */
2567     int len;
2568     struct gaia_topology *topo = (struct gaia_topology *) accessor;
2569     if (msg == NULL)
2570 	msg = "no message available";
2571 
2572     spatialite_e ("%s\n", msg);
2573     if (topo == NULL)
2574 	return;
2575 
2576     if (topo->last_error_message != NULL)
2577 	return;
2578 
2579     len = strlen (msg);
2580     topo->last_error_message = malloc (len + 1);
2581     strcpy (topo->last_error_message, msg);
2582 }
2583 
2584 TOPOLOGY_PRIVATE const char *
gaiatopo_get_last_exception(GaiaTopologyAccessorPtr accessor)2585 gaiatopo_get_last_exception (GaiaTopologyAccessorPtr accessor)
2586 {
2587 /* returns the last Topology error message */
2588     struct gaia_topology *topo = (struct gaia_topology *) accessor;
2589     if (topo == NULL)
2590 	return NULL;
2591 
2592     return topo->last_error_message;
2593 }
2594 
2595 GAIATOPO_DECLARE int
gaiaTopologyDrop(sqlite3 * handle,const char * topo_name)2596 gaiaTopologyDrop (sqlite3 * handle, const char *topo_name)
2597 {
2598 /* attempting to drop an already existing Topology */
2599     int ret;
2600     char *sql;
2601 
2602 /* creating the Topologies table (just in case) */
2603     if (!do_create_topologies (handle))
2604 	return 0;
2605 
2606 /* testing for existing DBMS objects */
2607     if (!check_existing_topology (handle, topo_name, 0))
2608 	return 0;
2609 
2610 /* dropping all topofeature tables (if any) */
2611     if (!do_drop_topofeature_tables (handle, topo_name))
2612 	goto error;
2613 
2614 /* dropping the Topology own Tables */
2615     if (!do_drop_topo_view (handle, topo_name, "edge_seeds"))
2616 	goto error;
2617     if (!do_drop_topo_view (handle, topo_name, "face_seeds"))
2618 	goto error;
2619     if (!do_drop_topo_view (handle, topo_name, "face_geoms"))
2620 	goto error;
2621     if (!do_drop_topo_table (handle, topo_name, "topofeatures", 0))
2622 	goto error;
2623     if (!do_drop_topo_table (handle, topo_name, "topolayers", 0))
2624 	goto error;
2625     if (!do_drop_topo_table (handle, topo_name, "seeds", 1))
2626 	goto error;
2627     if (!do_drop_topo_table (handle, topo_name, "edge", 1))
2628 	goto error;
2629     if (!do_drop_topo_table (handle, topo_name, "node", 1))
2630 	goto error;
2631     if (!do_drop_topo_table (handle, topo_name, "face", 1))
2632 	goto error;
2633 
2634 /* unregistering the Topology */
2635     sql =
2636 	sqlite3_mprintf
2637 	("DELETE FROM MAIN.topologies WHERE Lower(topology_name) = Lower(%Q)",
2638 	 topo_name);
2639     ret = sqlite3_exec (handle, sql, NULL, NULL, NULL);
2640     sqlite3_free (sql);
2641     if (ret != SQLITE_OK)
2642 	goto error;
2643 
2644     return 1;
2645 
2646   error:
2647     return 0;
2648 }
2649 
2650 GAIATOPO_DECLARE sqlite3_int64
gaiaAddIsoNode(GaiaTopologyAccessorPtr accessor,sqlite3_int64 face,gaiaPointPtr pt,int skip_checks)2651 gaiaAddIsoNode (GaiaTopologyAccessorPtr accessor,
2652 		sqlite3_int64 face, gaiaPointPtr pt, int skip_checks)
2653 {
2654 /* RTT wrapper - AddIsoNode */
2655     const RTCTX *ctx = NULL;
2656     struct splite_internal_cache *cache = NULL;
2657     sqlite3_int64 ret;
2658     int has_z = 0;
2659     RTPOINT *rt_pt;
2660     RTPOINTARRAY *pa;
2661     RTPOINT4D point;
2662     struct gaia_topology *topo = (struct gaia_topology *) accessor;
2663     if (topo == NULL)
2664 	return 0;
2665 
2666     cache = (struct splite_internal_cache *) topo->cache;
2667     if (cache == NULL)
2668 	return 0;
2669     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2670 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2671 	return 0;
2672     ctx = cache->RTTOPO_handle;
2673     if (ctx == NULL)
2674 	return 0;
2675 
2676     if (pt->DimensionModel == GAIA_XY_Z || pt->DimensionModel == GAIA_XY_Z_M)
2677 	has_z = 1;
2678     pa = ptarray_construct (ctx, has_z, 0, 1);
2679     point.x = pt->X;
2680     point.y = pt->Y;
2681     if (has_z)
2682 	point.z = pt->Z;
2683     ptarray_set_point4d (ctx, pa, 0, &point);
2684     rt_pt = rtpoint_construct (ctx, topo->srid, NULL, pa);
2685 
2686     gaiaResetRtTopoMsg (cache);
2687     ret =
2688 	rtt_AddIsoNode ((RTT_TOPOLOGY *) (topo->rtt_topology), face, rt_pt,
2689 			skip_checks);
2690 
2691     rtpoint_free (ctx, rt_pt);
2692     return ret;
2693 }
2694 
2695 GAIATOPO_DECLARE int
gaiaMoveIsoNode(GaiaTopologyAccessorPtr accessor,sqlite3_int64 node,gaiaPointPtr pt)2696 gaiaMoveIsoNode (GaiaTopologyAccessorPtr accessor,
2697 		 sqlite3_int64 node, gaiaPointPtr pt)
2698 {
2699 /* RTT wrapper - MoveIsoNode */
2700     const RTCTX *ctx = NULL;
2701     struct splite_internal_cache *cache = NULL;
2702     int ret;
2703     int has_z = 0;
2704     RTPOINT *rt_pt;
2705     RTPOINTARRAY *pa;
2706     RTPOINT4D point;
2707     struct gaia_topology *topo = (struct gaia_topology *) accessor;
2708     if (topo == NULL)
2709 	return 0;
2710 
2711     cache = (struct splite_internal_cache *) topo->cache;
2712     if (cache == NULL)
2713 	return 0;
2714     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2715 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2716 	return 0;
2717     ctx = cache->RTTOPO_handle;
2718     if (ctx == NULL)
2719 	return 0;
2720 
2721     if (pt->DimensionModel == GAIA_XY_Z || pt->DimensionModel == GAIA_XY_Z_M)
2722 	has_z = 1;
2723     pa = ptarray_construct (ctx, has_z, 0, 1);
2724     point.x = pt->X;
2725     point.y = pt->Y;
2726     if (has_z)
2727 	point.z = pt->Z;
2728     ptarray_set_point4d (ctx, pa, 0, &point);
2729     rt_pt = rtpoint_construct (ctx, topo->srid, NULL, pa);
2730 
2731     gaiaResetRtTopoMsg (cache);
2732     ret = rtt_MoveIsoNode ((RTT_TOPOLOGY *) (topo->rtt_topology), node, rt_pt);
2733 
2734     rtpoint_free (ctx, rt_pt);
2735     if (ret == 0)
2736 	return 1;
2737     return 0;
2738 }
2739 
2740 GAIATOPO_DECLARE int
gaiaRemIsoNode(GaiaTopologyAccessorPtr accessor,sqlite3_int64 node)2741 gaiaRemIsoNode (GaiaTopologyAccessorPtr accessor, sqlite3_int64 node)
2742 {
2743 /* RTT wrapper - RemIsoNode */
2744     struct splite_internal_cache *cache = NULL;
2745     int ret;
2746     struct gaia_topology *topo = (struct gaia_topology *) accessor;
2747     if (topo == NULL)
2748 	return 0;
2749 
2750     cache = (struct splite_internal_cache *) topo->cache;
2751     if (cache == NULL)
2752 	return 0;
2753     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2754 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2755 	return 0;
2756 
2757     gaiaResetRtTopoMsg (cache);
2758     ret = rtt_RemoveIsoNode ((RTT_TOPOLOGY *) (topo->rtt_topology), node);
2759 
2760     if (ret == 0)
2761 	return 1;
2762     return 0;
2763 }
2764 
2765 GAIATOPO_DECLARE sqlite3_int64
gaiaAddIsoEdge(GaiaTopologyAccessorPtr accessor,sqlite3_int64 start_node,sqlite3_int64 end_node,gaiaLinestringPtr ln)2766 gaiaAddIsoEdge (GaiaTopologyAccessorPtr accessor,
2767 		sqlite3_int64 start_node, sqlite3_int64 end_node,
2768 		gaiaLinestringPtr ln)
2769 {
2770 /* RTT wrapper - AddIsoEdge */
2771     const RTCTX *ctx = NULL;
2772     struct splite_internal_cache *cache = NULL;
2773     sqlite3_int64 ret;
2774     RTLINE *rt_line;
2775     struct gaia_topology *topo = (struct gaia_topology *) accessor;
2776     if (topo == NULL)
2777 	return 0;
2778 
2779     cache = (struct splite_internal_cache *) topo->cache;
2780     if (cache == NULL)
2781 	return 0;
2782     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2783 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2784 	return 0;
2785     ctx = cache->RTTOPO_handle;
2786     if (ctx == NULL)
2787 	return 0;
2788 
2789     rt_line =
2790 	gaia_convert_linestring_to_rtline (ctx, ln, topo->srid, topo->has_z);
2791 
2792     gaiaResetRtTopoMsg (cache);
2793     ret =
2794 	rtt_AddIsoEdge ((RTT_TOPOLOGY *) (topo->rtt_topology), start_node,
2795 			end_node, rt_line);
2796 
2797     rtline_free (ctx, rt_line);
2798     return ret;
2799 }
2800 
2801 GAIATOPO_DECLARE int
gaiaRemIsoEdge(GaiaTopologyAccessorPtr accessor,sqlite3_int64 edge)2802 gaiaRemIsoEdge (GaiaTopologyAccessorPtr accessor, sqlite3_int64 edge)
2803 {
2804 /* RTT wrapper - RemIsoEdge */
2805     struct splite_internal_cache *cache = NULL;
2806     int ret;
2807     struct gaia_topology *topo = (struct gaia_topology *) accessor;
2808     if (topo == NULL)
2809 	return 0;
2810 
2811     cache = (struct splite_internal_cache *) topo->cache;
2812     if (cache == NULL)
2813 	return 0;
2814     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2815 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2816 	return 0;
2817 
2818     gaiaResetRtTopoMsg (cache);
2819     ret = rtt_RemIsoEdge ((RTT_TOPOLOGY *) (topo->rtt_topology), edge);
2820 
2821     if (ret == 0)
2822 	return 1;
2823     return 0;
2824 }
2825 
2826 GAIATOPO_DECLARE int
gaiaChangeEdgeGeom(GaiaTopologyAccessorPtr accessor,sqlite3_int64 edge_id,gaiaLinestringPtr ln)2827 gaiaChangeEdgeGeom (GaiaTopologyAccessorPtr accessor,
2828 		    sqlite3_int64 edge_id, gaiaLinestringPtr ln)
2829 {
2830 /* RTT wrapper - ChangeEdgeGeom  */
2831     const RTCTX *ctx = NULL;
2832     struct splite_internal_cache *cache = NULL;
2833     int ret;
2834     RTLINE *rt_line;
2835     struct gaia_topology *topo = (struct gaia_topology *) accessor;
2836     if (topo == NULL)
2837 	return 0;
2838 
2839     cache = (struct splite_internal_cache *) topo->cache;
2840     if (cache == NULL)
2841 	return 0;
2842     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2843 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2844 	return 0;
2845     ctx = cache->RTTOPO_handle;
2846     if (ctx == NULL)
2847 	return 0;
2848 
2849     rt_line =
2850 	gaia_convert_linestring_to_rtline (ctx, ln, topo->srid, topo->has_z);
2851 
2852     gaiaResetRtTopoMsg (cache);
2853     ret =
2854 	rtt_ChangeEdgeGeom ((RTT_TOPOLOGY *) (topo->rtt_topology), edge_id,
2855 			    rt_line);
2856 
2857     rtline_free (ctx, rt_line);
2858     if (ret == 0)
2859 	return 1;
2860     return 0;
2861 }
2862 
2863 static void
do_check_mod_split_edge3d(struct gaia_topology * topo,gaiaPointPtr pt,sqlite3_int64 old_edge)2864 do_check_mod_split_edge3d (struct gaia_topology *topo, gaiaPointPtr pt,
2865 			   sqlite3_int64 old_edge)
2866 {
2867 /*
2868 / defensive programming: carefully ensuring that lwgeom could
2869 / never shift Edges' start/end points after computing ModSplit
2870 / 3D topology
2871 */
2872     sqlite3_stmt *stmt = NULL;
2873     int ret;
2874     char *sql;
2875     char *table;
2876     char *xtable;
2877     double x1s;
2878     double y1s;
2879     double z1s;
2880     double x1e;
2881     double y1e;
2882     double z1e;
2883     double x2s;
2884     double y2s;
2885     double z2s;
2886     double x2e;
2887     double y2e;
2888     double z2e;
2889     sqlite3_int64 new_edge = sqlite3_last_insert_rowid (topo->db_handle);
2890     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
2891     xtable = gaiaDoubleQuotedSql (table);
2892     sql =
2893 	sqlite3_mprintf
2894 	("SELECT ST_X(ST_StartPoint(geom)), ST_Y(ST_StartPoint(geom)), "
2895 	 "ST_Z(ST_StartPoint(geom)), ST_X(ST_EndPoint(geom)), ST_Y(ST_EndPoint(geom)), "
2896 	 "ST_Z(ST_EndPoint(geom)) FROM \"%s\" WHERE edge_id = ?", xtable);
2897     free (xtable);
2898     sqlite3_free (table);
2899     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
2900     sqlite3_free (sql);
2901     if (ret != SQLITE_OK)
2902 	return;
2903 
2904     sqlite3_reset (stmt);
2905     sqlite3_clear_bindings (stmt);
2906     sqlite3_bind_int64 (stmt, 1, old_edge);
2907     while (1)
2908       {
2909 	  /* scrolling the result set rows */
2910 	  ret = sqlite3_step (stmt);
2911 	  if (ret == SQLITE_DONE)
2912 	      break;		/* end of result set */
2913 	  if (ret == SQLITE_ROW)
2914 	    {
2915 		x1s = sqlite3_column_double (stmt, 0);
2916 		y1s = sqlite3_column_double (stmt, 1);
2917 		z1s = sqlite3_column_double (stmt, 2);
2918 		x1e = sqlite3_column_double (stmt, 3);
2919 		y1e = sqlite3_column_double (stmt, 4);
2920 		z1e = sqlite3_column_double (stmt, 5);
2921 	    }
2922 	  else
2923 	      goto error;
2924       }
2925 
2926     sqlite3_reset (stmt);
2927     sqlite3_clear_bindings (stmt);
2928     sqlite3_bind_int64 (stmt, 1, new_edge);
2929     while (1)
2930       {
2931 	  /* scrolling the result set rows */
2932 	  ret = sqlite3_step (stmt);
2933 	  if (ret == SQLITE_DONE)
2934 	      break;		/* end of result set */
2935 	  if (ret == SQLITE_ROW)
2936 	    {
2937 		x2s = sqlite3_column_double (stmt, 0);
2938 		y2s = sqlite3_column_double (stmt, 1);
2939 		z2s = sqlite3_column_double (stmt, 2);
2940 		x2e = sqlite3_column_double (stmt, 3);
2941 		y2e = sqlite3_column_double (stmt, 4);
2942 		z2e = sqlite3_column_double (stmt, 5);
2943 	    }
2944 	  else
2945 	      goto error;
2946       }
2947     if (x1s == x2e && y1s == y2e && z1s == z2e)
2948       {
2949 	  /* just silencing stupid compiler warnings */
2950 	  ;
2951       }
2952     if (x1e == x2s && y1e == y2s && z1e == z2s)
2953       {
2954 	  if (x1e != pt->X || y1e != pt->Y || z1e != pt->Z)
2955 	      goto fixme;
2956       }
2957   error:
2958     sqlite3_finalize (stmt);
2959     return;
2960 
2961   fixme:
2962     sqlite3_finalize (stmt);
2963     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
2964     xtable = gaiaDoubleQuotedSql (table);
2965     sql =
2966 	sqlite3_mprintf
2967 	("UPDATE \"%s\" SET geom = ST_SetEndPoint(geom, MakePointZ(?, ?, ?)) WHERE edge_id = ?",
2968 	 xtable);
2969     free (xtable);
2970     sqlite3_free (table);
2971     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
2972     sqlite3_free (sql);
2973     if (ret != SQLITE_OK)
2974 	return;
2975     sqlite3_reset (stmt);
2976     sqlite3_clear_bindings (stmt);
2977     sqlite3_bind_double (stmt, 1, pt->X);
2978     sqlite3_bind_double (stmt, 2, pt->Y);
2979     sqlite3_bind_double (stmt, 3, pt->Z);
2980     sqlite3_bind_int64 (stmt, 4, old_edge);
2981     ret = sqlite3_step (stmt);
2982     if (ret == SQLITE_DONE || ret == SQLITE_ROW)
2983 	;
2984     else
2985 	goto error2;
2986 
2987     sqlite3_finalize (stmt);
2988     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
2989     xtable = gaiaDoubleQuotedSql (table);
2990     sql =
2991 	sqlite3_mprintf
2992 	("UPDATE \"%s\" SET geom = ST_SetStartPoint(geom, MakePointZ(?, ?, ?)) WHERE edge_id = ?",
2993 	 xtable);
2994     free (xtable);
2995     sqlite3_free (table);
2996     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
2997     sqlite3_free (sql);
2998     if (ret != SQLITE_OK)
2999 	return;
3000     sqlite3_reset (stmt);
3001     sqlite3_clear_bindings (stmt);
3002     sqlite3_bind_double (stmt, 1, pt->X);
3003     sqlite3_bind_double (stmt, 2, pt->Y);
3004     sqlite3_bind_double (stmt, 3, pt->Z);
3005     sqlite3_bind_int64 (stmt, 4, new_edge);
3006     ret = sqlite3_step (stmt);
3007     if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3008 	;
3009     else
3010 	goto error2;
3011 
3012   error2:
3013     sqlite3_finalize (stmt);
3014     return;
3015 }
3016 
3017 static void
do_check_mod_split_edge(struct gaia_topology * topo,gaiaPointPtr pt,sqlite3_int64 old_edge)3018 do_check_mod_split_edge (struct gaia_topology *topo, gaiaPointPtr pt,
3019 			 sqlite3_int64 old_edge)
3020 {
3021 /*
3022 / defensive programming: carefully ensuring that lwgeom could
3023 / never shift Edges' start/end points after computing ModSplit
3024 / 2D topology
3025 */
3026     sqlite3_stmt *stmt = NULL;
3027     int ret;
3028     char *sql;
3029     char *table;
3030     char *xtable;
3031     double x1s;
3032     double y1s;
3033     double x1e;
3034     double y1e;
3035     double x2s;
3036     double y2s;
3037     double x2e;
3038     double y2e;
3039     sqlite3_int64 new_edge;
3040     if (topo->has_z)
3041       {
3042 	  do_check_mod_split_edge3d (topo, pt, old_edge);
3043 	  return;
3044       }
3045 
3046     new_edge = sqlite3_last_insert_rowid (topo->db_handle);
3047     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
3048     xtable = gaiaDoubleQuotedSql (table);
3049     sql =
3050 	sqlite3_mprintf
3051 	("SELECT ST_X(ST_StartPoint(geom)), ST_Y(ST_StartPoint(geom)), "
3052 	 "ST_X(ST_EndPoint(geom)), ST_Y(ST_EndPoint(geom)) FROM \"%s\" WHERE edge_id = ?",
3053 	 xtable);
3054     free (xtable);
3055     sqlite3_free (table);
3056     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
3057     sqlite3_free (sql);
3058     if (ret != SQLITE_OK)
3059 	return;
3060 
3061     sqlite3_reset (stmt);
3062     sqlite3_clear_bindings (stmt);
3063     sqlite3_bind_int64 (stmt, 1, old_edge);
3064     while (1)
3065       {
3066 	  /* scrolling the result set rows */
3067 	  ret = sqlite3_step (stmt);
3068 	  if (ret == SQLITE_DONE)
3069 	      break;		/* end of result set */
3070 	  if (ret == SQLITE_ROW)
3071 	    {
3072 		x1s = sqlite3_column_double (stmt, 0);
3073 		y1s = sqlite3_column_double (stmt, 1);
3074 		x1e = sqlite3_column_double (stmt, 2);
3075 		y1e = sqlite3_column_double (stmt, 3);
3076 	    }
3077 	  else
3078 	      goto error;
3079       }
3080 
3081     sqlite3_reset (stmt);
3082     sqlite3_clear_bindings (stmt);
3083     sqlite3_bind_int64 (stmt, 1, new_edge);
3084     while (1)
3085       {
3086 	  /* scrolling the result set rows */
3087 	  ret = sqlite3_step (stmt);
3088 	  if (ret == SQLITE_DONE)
3089 	      break;		/* end of result set */
3090 	  if (ret == SQLITE_ROW)
3091 	    {
3092 		x2s = sqlite3_column_double (stmt, 0);
3093 		y2s = sqlite3_column_double (stmt, 1);
3094 		x2e = sqlite3_column_double (stmt, 2);
3095 		y2e = sqlite3_column_double (stmt, 3);
3096 	    }
3097 	  else
3098 	      goto error;
3099       }
3100     if (x1s == x2e && y1s == y2e)
3101       {
3102 	  /* just silencing stupid compiler warnings */
3103 	  ;
3104       }
3105     if (x1e == x2s && y1e == y2s)
3106       {
3107 	  if (x1e != pt->X || y1e != pt->Y)
3108 	      goto fixme;
3109       }
3110   error:
3111     sqlite3_finalize (stmt);
3112     return;
3113 
3114   fixme:
3115     sqlite3_finalize (stmt);
3116     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
3117     xtable = gaiaDoubleQuotedSql (table);
3118     sql =
3119 	sqlite3_mprintf
3120 	("UPDATE \"%s\" SET geom = ST_SetEndPoint(geom, MakePoint(?, ?)) WHERE edge_id = ?",
3121 	 xtable);
3122     free (xtable);
3123     sqlite3_free (table);
3124     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
3125     sqlite3_free (sql);
3126     if (ret != SQLITE_OK)
3127 	return;
3128     sqlite3_reset (stmt);
3129     sqlite3_clear_bindings (stmt);
3130     sqlite3_bind_double (stmt, 1, pt->X);
3131     sqlite3_bind_double (stmt, 2, pt->Y);
3132     sqlite3_bind_int64 (stmt, 3, old_edge);
3133     ret = sqlite3_step (stmt);
3134     if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3135 	;
3136     else
3137 	goto error2;
3138 
3139     sqlite3_finalize (stmt);
3140     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
3141     xtable = gaiaDoubleQuotedSql (table);
3142     sql =
3143 	sqlite3_mprintf
3144 	("UPDATE \"%s\" SET geom = ST_SetStartPoint(geom, MakePoint(?, ?)) WHERE edge_id = ?",
3145 	 xtable);
3146     free (xtable);
3147     sqlite3_free (table);
3148     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
3149     sqlite3_free (sql);
3150     if (ret != SQLITE_OK)
3151 	return;
3152     sqlite3_reset (stmt);
3153     sqlite3_clear_bindings (stmt);
3154     sqlite3_bind_double (stmt, 1, pt->X);
3155     sqlite3_bind_double (stmt, 2, pt->Y);
3156     sqlite3_bind_int64 (stmt, 3, new_edge);
3157     ret = sqlite3_step (stmt);
3158     if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3159 	;
3160     else
3161 	goto error2;
3162 
3163   error2:
3164     sqlite3_finalize (stmt);
3165     return;
3166 }
3167 
3168 GAIATOPO_DECLARE sqlite3_int64
gaiaModEdgeSplit(GaiaTopologyAccessorPtr accessor,sqlite3_int64 edge,gaiaPointPtr pt,int skip_checks)3169 gaiaModEdgeSplit (GaiaTopologyAccessorPtr accessor,
3170 		  sqlite3_int64 edge, gaiaPointPtr pt, int skip_checks)
3171 {
3172 /* RTT wrapper - ModEdgeSplit */
3173     const RTCTX *ctx = NULL;
3174     struct splite_internal_cache *cache = NULL;
3175     sqlite3_int64 ret;
3176     int has_z = 0;
3177     RTPOINT *rt_pt;
3178     RTPOINTARRAY *pa;
3179     RTPOINT4D point;
3180     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3181     if (topo == NULL)
3182 	return 0;
3183 
3184     cache = (struct splite_internal_cache *) topo->cache;
3185     if (cache == NULL)
3186 	return 0;
3187     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3188 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3189 	return 0;
3190     ctx = cache->RTTOPO_handle;
3191     if (ctx == NULL)
3192 	return 0;
3193 
3194     if (pt->DimensionModel == GAIA_XY_Z || pt->DimensionModel == GAIA_XY_Z_M)
3195 	has_z = 1;
3196     pa = ptarray_construct (ctx, has_z, 0, 1);
3197     point.x = pt->X;
3198     point.y = pt->Y;
3199     if (has_z)
3200 	point.z = pt->Z;
3201     ptarray_set_point4d (ctx, pa, 0, &point);
3202     rt_pt = rtpoint_construct (ctx, topo->srid, NULL, pa);
3203 
3204     gaiaResetRtTopoMsg (cache);
3205     ret =
3206 	rtt_ModEdgeSplit ((RTT_TOPOLOGY *) (topo->rtt_topology), edge, rt_pt,
3207 			  skip_checks);
3208 
3209     rtpoint_free (ctx, rt_pt);
3210 
3211     if (ret > 0)
3212 	do_check_mod_split_edge (topo, pt, edge);
3213 
3214     return ret;
3215 }
3216 
3217 GAIATOPO_DECLARE sqlite3_int64
gaiaNewEdgesSplit(GaiaTopologyAccessorPtr accessor,sqlite3_int64 edge,gaiaPointPtr pt,int skip_checks)3218 gaiaNewEdgesSplit (GaiaTopologyAccessorPtr accessor,
3219 		   sqlite3_int64 edge, gaiaPointPtr pt, int skip_checks)
3220 {
3221 /* RTT wrapper - NewEdgesSplit */
3222     const RTCTX *ctx = NULL;
3223     struct splite_internal_cache *cache = NULL;
3224     sqlite3_int64 ret;
3225     int has_z = 0;
3226     RTPOINT *rt_pt;
3227     RTPOINTARRAY *pa;
3228     RTPOINT4D point;
3229     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3230     if (topo == NULL)
3231 	return 0;
3232 
3233     cache = (struct splite_internal_cache *) topo->cache;
3234     if (cache == NULL)
3235 	return 0;
3236     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3237 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3238 	return 0;
3239     ctx = cache->RTTOPO_handle;
3240     if (ctx == NULL)
3241 	return 0;
3242 
3243     if (pt->DimensionModel == GAIA_XY_Z || pt->DimensionModel == GAIA_XY_Z_M)
3244 	has_z = 1;
3245     pa = ptarray_construct (ctx, has_z, 0, 1);
3246     point.x = pt->X;
3247     point.y = pt->Y;
3248     if (has_z)
3249 	point.z = pt->Z;
3250     ptarray_set_point4d (ctx, pa, 0, &point);
3251     rt_pt = rtpoint_construct (ctx, topo->srid, NULL, pa);
3252 
3253     gaiaResetRtTopoMsg (cache);
3254     ret =
3255 	rtt_NewEdgesSplit ((RTT_TOPOLOGY *) (topo->rtt_topology), edge, rt_pt,
3256 			   skip_checks);
3257 
3258     rtpoint_free (ctx, rt_pt);
3259     return ret;
3260 }
3261 
3262 GAIATOPO_DECLARE sqlite3_int64
gaiaAddEdgeModFace(GaiaTopologyAccessorPtr accessor,sqlite3_int64 start_node,sqlite3_int64 end_node,gaiaLinestringPtr ln,int skip_checks)3263 gaiaAddEdgeModFace (GaiaTopologyAccessorPtr accessor,
3264 		    sqlite3_int64 start_node, sqlite3_int64 end_node,
3265 		    gaiaLinestringPtr ln, int skip_checks)
3266 {
3267 /* RTT wrapper - AddEdgeModFace */
3268     const RTCTX *ctx = NULL;
3269     struct splite_internal_cache *cache = NULL;
3270     sqlite3_int64 ret;
3271     RTLINE *rt_line;
3272     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3273     if (topo == NULL)
3274 	return 0;
3275 
3276     cache = (struct splite_internal_cache *) topo->cache;
3277     if (cache == NULL)
3278 	return 0;
3279     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3280 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3281 	return 0;
3282     ctx = cache->RTTOPO_handle;
3283     if (ctx == NULL)
3284 	return 0;
3285 
3286     rt_line =
3287 	gaia_convert_linestring_to_rtline (ctx, ln, topo->srid, topo->has_z);
3288 
3289     gaiaResetRtTopoMsg (cache);
3290     ret =
3291 	rtt_AddEdgeModFace ((RTT_TOPOLOGY *) (topo->rtt_topology), start_node,
3292 			    end_node, rt_line, skip_checks);
3293 
3294     rtline_free (ctx, rt_line);
3295     return ret;
3296 }
3297 
3298 GAIATOPO_DECLARE sqlite3_int64
gaiaAddEdgeNewFaces(GaiaTopologyAccessorPtr accessor,sqlite3_int64 start_node,sqlite3_int64 end_node,gaiaLinestringPtr ln,int skip_checks)3299 gaiaAddEdgeNewFaces (GaiaTopologyAccessorPtr accessor,
3300 		     sqlite3_int64 start_node, sqlite3_int64 end_node,
3301 		     gaiaLinestringPtr ln, int skip_checks)
3302 {
3303 /* RTT wrapper - AddEdgeNewFaces */
3304     const RTCTX *ctx = NULL;
3305     struct splite_internal_cache *cache = NULL;
3306     sqlite3_int64 ret;
3307     RTLINE *rt_line;
3308     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3309     if (topo == NULL)
3310 	return 0;
3311 
3312     cache = (struct splite_internal_cache *) topo->cache;
3313     if (cache == NULL)
3314 	return 0;
3315     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3316 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3317 	return 0;
3318     ctx = cache->RTTOPO_handle;
3319     if (ctx == NULL)
3320 	return 0;
3321 
3322     rt_line =
3323 	gaia_convert_linestring_to_rtline (ctx, ln, topo->srid, topo->has_z);
3324 
3325     gaiaResetRtTopoMsg (cache);
3326     ret =
3327 	rtt_AddEdgeNewFaces ((RTT_TOPOLOGY *) (topo->rtt_topology), start_node,
3328 			     end_node, rt_line, skip_checks);
3329 
3330     rtline_free (ctx, rt_line);
3331     return ret;
3332 }
3333 
3334 GAIATOPO_DECLARE sqlite3_int64
gaiaRemEdgeNewFace(GaiaTopologyAccessorPtr accessor,sqlite3_int64 edge_id)3335 gaiaRemEdgeNewFace (GaiaTopologyAccessorPtr accessor, sqlite3_int64 edge_id)
3336 {
3337 /* RTT wrapper - RemEdgeNewFace */
3338     struct splite_internal_cache *cache = NULL;
3339     sqlite3_int64 ret;
3340     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3341     if (topo == NULL)
3342 	return 0;
3343 
3344     cache = (struct splite_internal_cache *) topo->cache;
3345     if (cache == NULL)
3346 	return 0;
3347     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3348 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3349 	return 0;
3350 
3351     gaiaResetRtTopoMsg (cache);
3352     ret = rtt_RemEdgeNewFace ((RTT_TOPOLOGY *) (topo->rtt_topology), edge_id);
3353 
3354     return ret;
3355 }
3356 
3357 GAIATOPO_DECLARE sqlite3_int64
gaiaRemEdgeModFace(GaiaTopologyAccessorPtr accessor,sqlite3_int64 edge_id)3358 gaiaRemEdgeModFace (GaiaTopologyAccessorPtr accessor, sqlite3_int64 edge_id)
3359 {
3360 /* RTT wrapper - RemEdgeModFace */
3361     struct splite_internal_cache *cache = NULL;
3362     sqlite3_int64 ret;
3363     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3364     if (topo == NULL)
3365 	return 0;
3366 
3367     cache = (struct splite_internal_cache *) topo->cache;
3368     if (cache == NULL)
3369 	return 0;
3370     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3371 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3372 	return 0;
3373 
3374     gaiaResetRtTopoMsg (cache);
3375     ret = rtt_RemEdgeModFace ((RTT_TOPOLOGY *) (topo->rtt_topology), edge_id);
3376 
3377     return ret;
3378 }
3379 
3380 GAIATOPO_DECLARE sqlite3_int64
gaiaNewEdgeHeal(GaiaTopologyAccessorPtr accessor,sqlite3_int64 edge_id1,sqlite3_int64 edge_id2)3381 gaiaNewEdgeHeal (GaiaTopologyAccessorPtr accessor, sqlite3_int64 edge_id1,
3382 		 sqlite3_int64 edge_id2)
3383 {
3384 /* RTT wrapper - NewEdgeHeal */
3385     struct splite_internal_cache *cache = NULL;
3386     sqlite3_int64 ret;
3387     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3388     if (topo == NULL)
3389 	return 0;
3390 
3391     cache = (struct splite_internal_cache *) topo->cache;
3392     if (cache == NULL)
3393 	return 0;
3394     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3395 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3396 	return 0;
3397 
3398     gaiaResetRtTopoMsg (cache);
3399     ret =
3400 	rtt_NewEdgeHeal ((RTT_TOPOLOGY *) (topo->rtt_topology), edge_id1,
3401 			 edge_id2);
3402 
3403     return ret;
3404 }
3405 
3406 GAIATOPO_DECLARE sqlite3_int64
gaiaModEdgeHeal(GaiaTopologyAccessorPtr accessor,sqlite3_int64 edge_id1,sqlite3_int64 edge_id2)3407 gaiaModEdgeHeal (GaiaTopologyAccessorPtr accessor, sqlite3_int64 edge_id1,
3408 		 sqlite3_int64 edge_id2)
3409 {
3410 /* RTT wrapper - ModEdgeHeal */
3411     struct splite_internal_cache *cache = NULL;
3412     sqlite3_int64 ret;
3413     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3414     if (topo == NULL)
3415 	return 0;
3416 
3417     cache = (struct splite_internal_cache *) topo->cache;
3418     if (cache == NULL)
3419 	return 0;
3420     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3421 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3422 	return 0;
3423 
3424     gaiaResetRtTopoMsg (cache);
3425     ret =
3426 	rtt_ModEdgeHeal ((RTT_TOPOLOGY *) (topo->rtt_topology), edge_id1,
3427 			 edge_id2);
3428 
3429     return ret;
3430 }
3431 
3432 GAIATOPO_DECLARE gaiaGeomCollPtr
gaiaGetFaceGeometry(GaiaTopologyAccessorPtr accessor,sqlite3_int64 face)3433 gaiaGetFaceGeometry (GaiaTopologyAccessorPtr accessor, sqlite3_int64 face)
3434 {
3435 /* RTT wrapper - GetFaceGeometry */
3436     const RTCTX *ctx = NULL;
3437     struct splite_internal_cache *cache = NULL;
3438     RTGEOM *result = NULL;
3439     RTPOLY *rtpoly;
3440     int has_z = 0;
3441     RTPOINTARRAY *pa;
3442     RTPOINT4D pt4d;
3443     int iv;
3444     int ib;
3445     double x;
3446     double y;
3447     double z;
3448     gaiaGeomCollPtr geom;
3449     gaiaPolygonPtr pg;
3450     gaiaRingPtr rng;
3451     int dimension_model;
3452     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3453     if (topo == NULL)
3454 	return 0;
3455 
3456     cache = (struct splite_internal_cache *) topo->cache;
3457     if (cache == NULL)
3458 	return 0;
3459     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3460 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3461 	return 0;
3462     ctx = cache->RTTOPO_handle;
3463     if (ctx == NULL)
3464 	return 0;
3465 
3466     result = rtt_GetFaceGeometry ((RTT_TOPOLOGY *) (topo->rtt_topology), face);
3467     if (result == NULL)
3468 	return NULL;
3469 
3470 /* converting the result as a Gaia Geometry */
3471     rtpoly = (RTPOLY *) result;
3472     if (rtpoly->nrings <= 0)
3473       {
3474 	  /* empty geometry */
3475 	  rtgeom_free (ctx, result);
3476 	  return NULL;
3477       }
3478     pa = rtpoly->rings[0];
3479     if (pa->npoints <= 0)
3480       {
3481 	  /* empty geometry */
3482 	  rtgeom_free (ctx, result);
3483 	  return NULL;
3484       }
3485     if (RTFLAGS_GET_Z (pa->flags))
3486 	has_z = 1;
3487     if (has_z)
3488       {
3489 	  dimension_model = GAIA_XY_Z;
3490 	  geom = gaiaAllocGeomCollXYZ ();
3491       }
3492     else
3493       {
3494 	  dimension_model = GAIA_XY;
3495 	  geom = gaiaAllocGeomColl ();
3496       }
3497     pg = gaiaAddPolygonToGeomColl (geom, pa->npoints, rtpoly->nrings - 1);
3498     rng = pg->Exterior;
3499     for (iv = 0; iv < pa->npoints; iv++)
3500       {
3501 	  /* copying Exterior Ring vertices */
3502 	  rt_getPoint4d_p (ctx, pa, iv, &pt4d);
3503 	  x = pt4d.x;
3504 	  y = pt4d.y;
3505 	  if (has_z)
3506 	      z = pt4d.z;
3507 	  else
3508 	      z = 0.0;
3509 	  if (dimension_model == GAIA_XY_Z)
3510 	    {
3511 		gaiaSetPointXYZ (rng->Coords, iv, x, y, z);
3512 	    }
3513 	  else
3514 	    {
3515 		gaiaSetPoint (rng->Coords, iv, x, y);
3516 	    }
3517       }
3518     for (ib = 1; ib < rtpoly->nrings; ib++)
3519       {
3520 	  has_z = 0;
3521 	  pa = rtpoly->rings[ib];
3522 	  if (RTFLAGS_GET_Z (pa->flags))
3523 	      has_z = 1;
3524 	  rng = gaiaAddInteriorRing (pg, ib - 1, pa->npoints);
3525 	  for (iv = 0; iv < pa->npoints; iv++)
3526 	    {
3527 		/* copying Exterior Ring vertices */
3528 		rt_getPoint4d_p (ctx, pa, iv, &pt4d);
3529 		x = pt4d.x;
3530 		y = pt4d.y;
3531 		if (has_z)
3532 		    z = pt4d.z;
3533 		else
3534 		    z = 0.0;
3535 		if (dimension_model == GAIA_XY_Z)
3536 		  {
3537 		      gaiaSetPointXYZ (rng->Coords, iv, x, y, z);
3538 		  }
3539 		else
3540 		  {
3541 		      gaiaSetPoint (rng->Coords, iv, x, y);
3542 		  }
3543 	    }
3544       }
3545     rtgeom_free (ctx, result);
3546     geom->DeclaredType = GAIA_POLYGON;
3547     geom->Srid = topo->srid;
3548     return geom;
3549 }
3550 
3551 static int
do_check_create_faceedges_table(GaiaTopologyAccessorPtr accessor)3552 do_check_create_faceedges_table (GaiaTopologyAccessorPtr accessor)
3553 {
3554 /* attemtping to create or validate the target table */
3555     char *sql;
3556     char *table;
3557     char *xtable;
3558     int ret;
3559     int i;
3560     char **results;
3561     int rows;
3562     int columns;
3563     char *errMsg = NULL;
3564     int exists = 0;
3565     int ok_face_id = 0;
3566     int ok_sequence = 0;
3567     int ok_edge_id = 0;
3568     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3569 
3570 /* testing for an already existing table */
3571     table = sqlite3_mprintf ("%s_face_edges_temp", topo->topology_name);
3572     xtable = gaiaDoubleQuotedSql (table);
3573     sqlite3_free (table);
3574     sql = sqlite3_mprintf ("PRAGMA TEMP.table_info(\"%s\")", xtable);
3575     free (xtable);
3576     ret =
3577 	sqlite3_get_table (topo->db_handle, sql, &results, &rows, &columns,
3578 			   &errMsg);
3579     sqlite3_free (sql);
3580     if (ret != SQLITE_OK)
3581       {
3582 	  char *msg = sqlite3_mprintf ("ST_GetFaceEdges exception: %s", errMsg);
3583 	  gaiatopo_set_last_error_msg (accessor, msg);
3584 	  sqlite3_free (msg);
3585 	  sqlite3_free (errMsg);
3586 	  return 0;
3587       }
3588     for (i = 1; i <= rows; i++)
3589       {
3590 	  const char *name = results[(i * columns) + 1];
3591 	  const char *type = results[(i * columns) + 2];
3592 	  const char *notnull = results[(i * columns) + 3];
3593 	  const char *dflt_value = results[(i * columns) + 4];
3594 	  const char *pk = results[(i * columns) + 5];
3595 	  if (strcmp (name, "face_id") == 0 && strcmp (type, "INTEGER") == 0
3596 	      && strcmp (notnull, "1") == 0 && dflt_value == NULL
3597 	      && strcmp (pk, "1") == 0)
3598 	      ok_face_id = 1;
3599 	  if (strcmp (name, "sequence") == 0 && strcmp (type, "INTEGER") == 0
3600 	      && strcmp (notnull, "1") == 0 && dflt_value == NULL
3601 	      && strcmp (pk, "2") == 0)
3602 	      ok_sequence = 1;
3603 	  if (strcmp (name, "edge_id") == 0 && strcmp (type, "INTEGER") == 0
3604 	      && strcmp (notnull, "1") == 0 && dflt_value == NULL
3605 	      && strcmp (pk, "0") == 0)
3606 	      ok_edge_id = 1;
3607 	  exists = 1;
3608       }
3609     sqlite3_free_table (results);
3610     if (ok_face_id && ok_sequence && ok_edge_id)
3611 	return 1;		/* already existing and valid */
3612 
3613     if (exists)
3614 	return 0;		/* already existing but invalid */
3615 
3616 /* attempting to create the table */
3617     table = sqlite3_mprintf ("%s_face_edges_temp", topo->topology_name);
3618     xtable = gaiaDoubleQuotedSql (table);
3619     sqlite3_free (table);
3620     sql =
3621 	sqlite3_mprintf
3622 	("CREATE TEMP TABLE \"%s\" (\n\tface_id INTEGER NOT NULL,\n"
3623 	 "\tsequence INTEGER NOT NULL,\n\tedge_id INTEGER NOT NULL,\n"
3624 	 "\tCONSTRAINT pk_topo_facee_edges PRIMARY KEY (face_id, sequence))",
3625 	 xtable);
3626     free (xtable);
3627     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
3628     sqlite3_free (sql);
3629     if (ret != SQLITE_OK)
3630       {
3631 	  char *msg = sqlite3_mprintf ("ST_GetFaceEdges exception: %s", errMsg);
3632 	  gaiatopo_set_last_error_msg (accessor, msg);
3633 	  sqlite3_free (msg);
3634 	  sqlite3_free (errMsg);
3635 	  return 0;
3636       }
3637 
3638     return 1;
3639 }
3640 
3641 static int
do_populate_faceedges_table(GaiaTopologyAccessorPtr accessor,sqlite3_int64 face,RTT_ELEMID * edges,int num_edges)3642 do_populate_faceedges_table (GaiaTopologyAccessorPtr accessor,
3643 			     sqlite3_int64 face, RTT_ELEMID * edges,
3644 			     int num_edges)
3645 {
3646 /* populating the target table */
3647     char *sql;
3648     char *table;
3649     char *xtable;
3650     int ret;
3651     int i;
3652     sqlite3_stmt *stmt = NULL;
3653     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3654 
3655 /* deleting all rows belonging to Face (if any) */
3656     table = sqlite3_mprintf ("%s_face_edges_temp", topo->topology_name);
3657     xtable = gaiaDoubleQuotedSql (table);
3658     sqlite3_free (table);
3659     sql = sqlite3_mprintf ("DELETE FROM TEMP.\"%s\" WHERE face_id = ?", xtable);
3660     free (xtable);
3661     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
3662     sqlite3_free (sql);
3663     if (ret != SQLITE_OK)
3664       {
3665 	  char *msg = sqlite3_mprintf ("ST_GetFaceEdges exception: %s",
3666 				       sqlite3_errmsg (topo->db_handle));
3667 	  gaiatopo_set_last_error_msg (accessor, msg);
3668 	  sqlite3_free (msg);
3669 	  goto error;
3670       }
3671     sqlite3_reset (stmt);
3672     sqlite3_clear_bindings (stmt);
3673     sqlite3_bind_int64 (stmt, 1, face);
3674     ret = sqlite3_step (stmt);
3675     if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3676 	;
3677     else
3678       {
3679 	  char *msg = sqlite3_mprintf ("ST_GetFaceEdges exception: %s",
3680 				       sqlite3_errmsg (topo->db_handle));
3681 	  gaiatopo_set_last_error_msg (accessor, msg);
3682 	  sqlite3_free (msg);
3683 	  goto error;
3684       }
3685     sqlite3_finalize (stmt);
3686     stmt = NULL;
3687 
3688 /* preparing the INSERT statement */
3689     table = sqlite3_mprintf ("%s_face_edges_temp", topo->topology_name);
3690     xtable = gaiaDoubleQuotedSql (table);
3691     sqlite3_free (table);
3692     sql =
3693 	sqlite3_mprintf
3694 	("INSERT INTO TEMP.\"%s\" (face_id, sequence, edge_id) VALUES (?, ?, ?)",
3695 	 xtable);
3696     free (xtable);
3697     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
3698     sqlite3_free (sql);
3699     if (ret != SQLITE_OK)
3700       {
3701 	  char *msg = sqlite3_mprintf ("ST_GetFaceEdges exception: %s",
3702 				       sqlite3_errmsg (topo->db_handle));
3703 	  gaiatopo_set_last_error_msg (accessor, msg);
3704 	  sqlite3_free (msg);
3705 	  goto error;
3706       }
3707     for (i = 0; i < num_edges; i++)
3708       {
3709 	  /* inserting all Face/Edges */
3710 	  sqlite3_reset (stmt);
3711 	  sqlite3_clear_bindings (stmt);
3712 	  sqlite3_bind_int64 (stmt, 1, face);
3713 	  sqlite3_bind_int (stmt, 2, i + 1);
3714 	  sqlite3_bind_int64 (stmt, 3, *(edges + i));
3715 	  ret = sqlite3_step (stmt);
3716 	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3717 	      ;
3718 	  else
3719 	    {
3720 		char *msg = sqlite3_mprintf ("ST_GetFaceEdges exception: %s",
3721 					     sqlite3_errmsg (topo->db_handle));
3722 		gaiatopo_set_last_error_msg (accessor, msg);
3723 		sqlite3_free (msg);
3724 		goto error;
3725 	    }
3726       }
3727     sqlite3_finalize (stmt);
3728     return 1;
3729 
3730   error:
3731     if (stmt != NULL)
3732 	sqlite3_finalize (stmt);
3733     return 0;
3734 }
3735 
3736 GAIATOPO_DECLARE int
gaiaGetFaceEdges(GaiaTopologyAccessorPtr accessor,sqlite3_int64 face)3737 gaiaGetFaceEdges (GaiaTopologyAccessorPtr accessor, sqlite3_int64 face)
3738 {
3739 /* RTT wrapper - GetFaceEdges */
3740     const RTCTX *ctx = NULL;
3741     struct splite_internal_cache *cache = NULL;
3742     RTT_ELEMID *edges = NULL;
3743     int num_edges;
3744     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3745     if (topo == NULL)
3746 	return 0;
3747 
3748     cache = (struct splite_internal_cache *) topo->cache;
3749     if (cache == NULL)
3750 	return 0;
3751     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3752 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3753 	return 0;
3754     ctx = cache->RTTOPO_handle;
3755     if (ctx == NULL)
3756 	return 0;
3757 
3758     gaiaResetRtTopoMsg (cache);
3759 
3760     num_edges =
3761 	rtt_GetFaceEdges ((RTT_TOPOLOGY *) (topo->rtt_topology), face, &edges);
3762 
3763     if (num_edges < 0)
3764 	return 0;
3765 
3766     if (num_edges > 0)
3767       {
3768 	  /* attemtping to create or validate the target table */
3769 	  if (!do_check_create_faceedges_table (accessor))
3770 	    {
3771 		rtfree (ctx, edges);
3772 		return 0;
3773 	    }
3774 
3775 	  /* populating the target table */
3776 	  if (!do_populate_faceedges_table (accessor, face, edges, num_edges))
3777 	    {
3778 		rtfree (ctx, edges);
3779 		return 0;
3780 	    }
3781       }
3782     rtfree (ctx, edges);
3783     return 1;
3784 }
3785 
3786 static int
do_check_create_validate_topogeo_table(GaiaTopologyAccessorPtr accessor)3787 do_check_create_validate_topogeo_table (GaiaTopologyAccessorPtr accessor)
3788 {
3789 /* attemtping to create or validate the target table */
3790     char *sql;
3791     char *table;
3792     char *xtable;
3793     int ret;
3794     char *errMsg = NULL;
3795     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3796 
3797 /* finalizing all prepared Statements */
3798     finalize_all_topo_prepared_stmts (topo->cache);
3799 
3800 /* attempting to drop the table (just in case if it already exists) */
3801     table = sqlite3_mprintf ("%s_validate_topogeo", topo->topology_name);
3802     xtable = gaiaDoubleQuotedSql (table);
3803     sqlite3_free (table);
3804     sql = sqlite3_mprintf ("DROP TABLE IF EXISTS temp.\"%s\"", xtable);
3805     free (xtable);
3806     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
3807     create_all_topo_prepared_stmts (topo->cache);	/* recreating prepared stsms */
3808     sqlite3_free (sql);
3809     if (ret != SQLITE_OK)
3810       {
3811 	  char *msg =
3812 	      sqlite3_mprintf ("ST_ValidSpatialNet exception: %s", errMsg);
3813 	  gaiatopo_set_last_error_msg (accessor, msg);
3814 	  sqlite3_free (msg);
3815 	  sqlite3_free (errMsg);
3816 	  return 0;
3817       }
3818 /* attempting to create the table */
3819     table = sqlite3_mprintf ("%s_validate_topogeo", topo->topology_name);
3820     xtable = gaiaDoubleQuotedSql (table);
3821     sqlite3_free (table);
3822     sql =
3823 	sqlite3_mprintf
3824 	("CREATE TEMP TABLE \"%s\" (\n\terror TEXT,\n"
3825 	 "\tprimitive1 INTEGER,\n\tprimitive2 INTEGER)", xtable);
3826     free (xtable);
3827     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
3828     sqlite3_free (sql);
3829     if (ret != SQLITE_OK)
3830       {
3831 	  char *msg =
3832 	      sqlite3_mprintf ("ST_ValidateTopoGeo exception: %s", errMsg);
3833 	  gaiatopo_set_last_error_msg (accessor, msg);
3834 	  sqlite3_free (msg);
3835 	  sqlite3_free (errMsg);
3836 	  return 0;
3837       }
3838 
3839     return 1;
3840 }
3841 
3842 static int
do_topo_check_coincident_nodes(GaiaTopologyAccessorPtr accessor,sqlite3_stmt * stmt)3843 do_topo_check_coincident_nodes (GaiaTopologyAccessorPtr accessor,
3844 				sqlite3_stmt * stmt)
3845 {
3846 /* checking for coincident nodes */
3847     char *sql;
3848     char *table;
3849     char *xtable;
3850     int ret;
3851     sqlite3_stmt *stmt_in = NULL;
3852     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3853 
3854     table = sqlite3_mprintf ("%s_node", topo->topology_name);
3855     xtable = gaiaDoubleQuotedSql (table);
3856     sql =
3857 	sqlite3_mprintf ("SELECT n1.node_id, n2.node_id FROM MAIN.\"%s\" AS n1 "
3858 			 "JOIN MAIN.\"%s\" AS n2 ON (n1.node_id <> n2.node_id AND "
3859 			 "ST_Equals(n1.geom, n2.geom) = 1 AND n2.node_id IN "
3860 			 "(SELECT rowid FROM SpatialIndex WHERE f_table_name = %Q AND "
3861 			 "f_geometry_column = 'geom' AND search_frame = n1.geom))",
3862 			 xtable, xtable, table);
3863     sqlite3_free (table);
3864     free (xtable);
3865     ret =
3866 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
3867     sqlite3_free (sql);
3868     if (ret != SQLITE_OK)
3869       {
3870 	  char *msg =
3871 	      sqlite3_mprintf
3872 	      ("ST_ValidateTopoGeo() - CoicidentNodes error: \"%s\"",
3873 	       sqlite3_errmsg (topo->db_handle));
3874 	  gaiatopo_set_last_error_msg (accessor, msg);
3875 	  sqlite3_free (msg);
3876 	  goto error;
3877       }
3878 
3879     sqlite3_reset (stmt_in);
3880     sqlite3_clear_bindings (stmt_in);
3881     while (1)
3882       {
3883 	  /* scrolling the result set rows */
3884 	  ret = sqlite3_step (stmt_in);
3885 	  if (ret == SQLITE_DONE)
3886 	      break;		/* end of result set */
3887 	  if (ret == SQLITE_ROW)
3888 	    {
3889 		sqlite3_int64 node_id1 = sqlite3_column_int64 (stmt_in, 0);
3890 		sqlite3_int64 node_id2 = sqlite3_column_int64 (stmt_in, 1);
3891 		/* reporting the error */
3892 		sqlite3_reset (stmt);
3893 		sqlite3_clear_bindings (stmt);
3894 		sqlite3_bind_text (stmt, 1, "coincident nodes", -1,
3895 				   SQLITE_STATIC);
3896 		sqlite3_bind_int64 (stmt, 2, node_id1);
3897 		sqlite3_bind_int64 (stmt, 3, node_id2);
3898 		ret = sqlite3_step (stmt);
3899 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3900 		    ;
3901 		else
3902 		  {
3903 		      char *msg =
3904 			  sqlite3_mprintf
3905 			  ("ST_ValidateTopoGeo() insert #1 error: \"%s\"",
3906 			   sqlite3_errmsg (topo->db_handle));
3907 		      gaiatopo_set_last_error_msg (accessor, msg);
3908 		      sqlite3_free (msg);
3909 		      goto error;
3910 		  }
3911 	    }
3912 	  else
3913 	    {
3914 		char *msg =
3915 		    sqlite3_mprintf
3916 		    ("ST_ValidateTopoGeo() - CoicidentNodes step error: %s",
3917 		     sqlite3_errmsg (topo->db_handle));
3918 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
3919 					     msg);
3920 		sqlite3_free (msg);
3921 		goto error;
3922 	    }
3923       }
3924     sqlite3_finalize (stmt_in);
3925 
3926     return 1;
3927 
3928   error:
3929     if (stmt_in == NULL)
3930 	sqlite3_finalize (stmt_in);
3931     return 0;
3932 }
3933 
3934 static int
do_topo_check_edge_node(GaiaTopologyAccessorPtr accessor,sqlite3_stmt * stmt)3935 do_topo_check_edge_node (GaiaTopologyAccessorPtr accessor, sqlite3_stmt * stmt)
3936 {
3937 /* checking for edge-node crossing */
3938     char *sql;
3939     char *table;
3940     char *xtable1;
3941     char *xtable2;
3942     int ret;
3943     sqlite3_stmt *stmt_in = NULL;
3944     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3945 
3946     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
3947     xtable1 = gaiaDoubleQuotedSql (table);
3948     sqlite3_free (table);
3949     table = sqlite3_mprintf ("%s_node", topo->topology_name);
3950     xtable2 = gaiaDoubleQuotedSql (table);
3951     sql = sqlite3_mprintf ("SELECT e.edge_id, n.node_id FROM MAIN.\"%s\" AS e "
3952 			   "JOIN MAIN.\"%s\" AS n ON (ST_Distance(e.geom, n.geom) <= 0 "
3953 			   "AND ST_Disjoint(ST_StartPoint(e.geom), n.geom) = 1 AND "
3954 			   "ST_Disjoint(ST_EndPoint(e.geom), n.geom) = 1 AND n.node_id IN "
3955 			   "(SELECT rowid FROM SpatialIndex WHERE f_table_name = %Q AND "
3956 			   "f_geometry_column = 'geom' AND search_frame = e.geom))",
3957 			   xtable1, xtable2, table);
3958     sqlite3_free (table);
3959     free (xtable1);
3960     free (xtable2);
3961     ret =
3962 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
3963     sqlite3_free (sql);
3964     if (ret != SQLITE_OK)
3965       {
3966 	  char *msg =
3967 	      sqlite3_mprintf
3968 	      ("ST_ValidateTopoGeo() - EdgeCrossedNode error: \"%s\"",
3969 	       sqlite3_errmsg (topo->db_handle));
3970 	  gaiatopo_set_last_error_msg (accessor, msg);
3971 	  sqlite3_free (msg);
3972 	  goto error;
3973       }
3974 
3975     sqlite3_reset (stmt_in);
3976     sqlite3_clear_bindings (stmt_in);
3977     while (1)
3978       {
3979 	  /* scrolling the result set rows */
3980 	  ret = sqlite3_step (stmt_in);
3981 	  if (ret == SQLITE_DONE)
3982 	      break;		/* end of result set */
3983 	  if (ret == SQLITE_ROW)
3984 	    {
3985 		sqlite3_int64 edge_id = sqlite3_column_int64 (stmt_in, 0);
3986 		sqlite3_int64 node_id = sqlite3_column_int64 (stmt_in, 1);
3987 		/* reporting the error */
3988 		sqlite3_reset (stmt);
3989 		sqlite3_clear_bindings (stmt);
3990 		sqlite3_bind_text (stmt, 1, "edge crosses node", -1,
3991 				   SQLITE_STATIC);
3992 		sqlite3_bind_int64 (stmt, 2, node_id);
3993 		sqlite3_bind_int64 (stmt, 3, edge_id);
3994 		ret = sqlite3_step (stmt);
3995 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3996 		    ;
3997 		else
3998 		  {
3999 		      char *msg =
4000 			  sqlite3_mprintf
4001 			  ("ST_ValidateTopoGeo() insert #2 error: \"%s\"",
4002 			   sqlite3_errmsg (topo->db_handle));
4003 		      gaiatopo_set_last_error_msg (accessor, msg);
4004 		      sqlite3_free (msg);
4005 		      goto error;
4006 		  }
4007 	    }
4008 	  else
4009 	    {
4010 		char *msg =
4011 		    sqlite3_mprintf
4012 		    ("ST_ValidateTopoGeo() - EdgeCrossedNode step error: %s",
4013 		     sqlite3_errmsg (topo->db_handle));
4014 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
4015 					     msg);
4016 		sqlite3_free (msg);
4017 		goto error;
4018 	    }
4019       }
4020     sqlite3_finalize (stmt_in);
4021 
4022     return 1;
4023 
4024   error:
4025     if (stmt_in == NULL)
4026 	sqlite3_finalize (stmt_in);
4027     return 0;
4028 }
4029 
4030 static int
do_topo_check_non_simple(GaiaTopologyAccessorPtr accessor,sqlite3_stmt * stmt)4031 do_topo_check_non_simple (GaiaTopologyAccessorPtr accessor, sqlite3_stmt * stmt)
4032 {
4033 /* checking for non-simple edges */
4034     char *sql;
4035     char *table;
4036     char *xtable;
4037     int ret;
4038     sqlite3_stmt *stmt_in = NULL;
4039     struct gaia_topology *topo = (struct gaia_topology *) accessor;
4040 
4041     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
4042     xtable = gaiaDoubleQuotedSql (table);
4043     sqlite3_free (table);
4044     sql =
4045 	sqlite3_mprintf
4046 	("SELECT edge_id FROM MAIN.\"%s\" WHERE ST_IsSimple(geom) = 0", xtable);
4047     free (xtable);
4048     ret =
4049 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
4050     sqlite3_free (sql);
4051     if (ret != SQLITE_OK)
4052       {
4053 	  char *msg =
4054 	      sqlite3_mprintf
4055 	      ("ST_ValidateTopoGeo() - NonSimpleEdge error: \"%s\"",
4056 	       sqlite3_errmsg (topo->db_handle));
4057 	  gaiatopo_set_last_error_msg (accessor, msg);
4058 	  sqlite3_free (msg);
4059 	  goto error;
4060       }
4061 
4062     sqlite3_reset (stmt_in);
4063     sqlite3_clear_bindings (stmt_in);
4064     while (1)
4065       {
4066 	  /* scrolling the result set rows */
4067 	  ret = sqlite3_step (stmt_in);
4068 	  if (ret == SQLITE_DONE)
4069 	      break;		/* end of result set */
4070 	  if (ret == SQLITE_ROW)
4071 	    {
4072 		sqlite3_int64 edge_id = sqlite3_column_int64 (stmt_in, 0);
4073 		/* reporting the error */
4074 		sqlite3_reset (stmt);
4075 		sqlite3_clear_bindings (stmt);
4076 		sqlite3_bind_text (stmt, 1, "edge not simple", -1,
4077 				   SQLITE_STATIC);
4078 		sqlite3_bind_int64 (stmt, 2, edge_id);
4079 		sqlite3_bind_null (stmt, 3);
4080 		ret = sqlite3_step (stmt);
4081 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4082 		    ;
4083 		else
4084 		  {
4085 		      char *msg =
4086 			  sqlite3_mprintf
4087 			  ("ST_ValidateTopoGeo() insert #3 error: \"%s\"",
4088 			   sqlite3_errmsg (topo->db_handle));
4089 		      gaiatopo_set_last_error_msg (accessor, msg);
4090 		      sqlite3_free (msg);
4091 		      goto error;
4092 		  }
4093 	    }
4094 	  else
4095 	    {
4096 		char *msg =
4097 		    sqlite3_mprintf
4098 		    ("ST_ValidateTopoGeo() - NonSimpleEdge step error: %s",
4099 		     sqlite3_errmsg (topo->db_handle));
4100 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
4101 					     msg);
4102 		sqlite3_free (msg);
4103 		goto error;
4104 	    }
4105       }
4106     sqlite3_finalize (stmt_in);
4107 
4108     return 1;
4109 
4110   error:
4111     if (stmt_in == NULL)
4112 	sqlite3_finalize (stmt_in);
4113     return 0;
4114 }
4115 
4116 static int
do_topo_check_edge_edge(GaiaTopologyAccessorPtr accessor,sqlite3_stmt * stmt)4117 do_topo_check_edge_edge (GaiaTopologyAccessorPtr accessor, sqlite3_stmt * stmt)
4118 {
4119 /* checking for edge-edge crossing */
4120     char *sql;
4121     char *table;
4122     char *xtable;
4123     int ret;
4124     sqlite3_stmt *stmt_in = NULL;
4125     struct gaia_topology *topo = (struct gaia_topology *) accessor;
4126 
4127     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
4128     xtable = gaiaDoubleQuotedSql (table);
4129 /*
4130     sql =
4131 	sqlite3_mprintf ("SELECT e1.edge_id, e2.edge_id FROM MAIN.\"%s\" AS e1 "
4132 			 "JOIN MAIN.\"%s\" AS e2 ON (e1.edge_id <> e2.edge_id AND "
4133 			 "ST_Crosses(e1.geom, e2.geom) = 1 AND e2.edge_id IN "
4134 			 "(SELECT rowid FROM SpatialIndex WHERE f_table_name = %Q AND "
4135 			 "f_geometry_column = 'geom' AND search_frame = e1.geom))",
4136 			 xtable, xtable, table);
4137 */
4138     sql =
4139 	sqlite3_mprintf ("SELECT e1.edge_id, e2.edge_id FROM MAIN.\"%s\" AS e1 "
4140 			 "JOIN MAIN.\"%s\" AS e2 ON (e1.edge_id <> e2.edge_id AND "
4141 			 "ST_RelateMatch(ST_Relate(e1.geom, e2.geom), '0******0*') = 1 AND e2.edge_id IN "
4142 			 "(SELECT rowid FROM SpatialIndex WHERE f_table_name = %Q AND "
4143 			 "f_geometry_column = 'geom' AND search_frame = e1.geom))",
4144 			 xtable, xtable, table);
4145     sqlite3_free (table);
4146     free (xtable);
4147     ret =
4148 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
4149     sqlite3_free (sql);
4150     if (ret != SQLITE_OK)
4151       {
4152 	  char *msg =
4153 	      sqlite3_mprintf
4154 	      ("ST_ValidateTopoGeo() - EdgeCrossesEdge error: \"%s\"",
4155 	       sqlite3_errmsg (topo->db_handle));
4156 	  gaiatopo_set_last_error_msg (accessor, msg);
4157 	  sqlite3_free (msg);
4158 	  goto error;
4159       }
4160 
4161     sqlite3_reset (stmt_in);
4162     sqlite3_clear_bindings (stmt_in);
4163     while (1)
4164       {
4165 	  /* scrolling the result set rows */
4166 	  ret = sqlite3_step (stmt_in);
4167 	  if (ret == SQLITE_DONE)
4168 	      break;		/* end of result set */
4169 	  if (ret == SQLITE_ROW)
4170 	    {
4171 		sqlite3_int64 edge_id1 = sqlite3_column_int64 (stmt_in, 0);
4172 		sqlite3_int64 edge_id2 = sqlite3_column_int64 (stmt_in, 1);
4173 		/* reporting the error */
4174 		sqlite3_reset (stmt);
4175 		sqlite3_clear_bindings (stmt);
4176 		sqlite3_bind_text (stmt, 1, "edge crosses edge", -1,
4177 				   SQLITE_STATIC);
4178 		sqlite3_bind_int64 (stmt, 2, edge_id1);
4179 		sqlite3_bind_int64 (stmt, 3, edge_id2);
4180 		ret = sqlite3_step (stmt);
4181 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4182 		    ;
4183 		else
4184 		  {
4185 		      char *msg =
4186 			  sqlite3_mprintf
4187 			  ("ST_ValidateTopoGeo() insert #4 error: \"%s\"",
4188 			   sqlite3_errmsg (topo->db_handle));
4189 		      gaiatopo_set_last_error_msg (accessor, msg);
4190 		      sqlite3_free (msg);
4191 		      goto error;
4192 		  }
4193 	    }
4194 	  else
4195 	    {
4196 		char *msg =
4197 		    sqlite3_mprintf
4198 		    ("ST_ValidateTopoGeo() - EdgeCrossesEdge step error: %s",
4199 		     sqlite3_errmsg (topo->db_handle));
4200 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
4201 					     msg);
4202 		sqlite3_free (msg);
4203 		goto error;
4204 	    }
4205       }
4206     sqlite3_finalize (stmt_in);
4207 
4208     return 1;
4209 
4210   error:
4211     if (stmt_in == NULL)
4212 	sqlite3_finalize (stmt_in);
4213     return 0;
4214 }
4215 
4216 static int
do_topo_check_start_nodes(GaiaTopologyAccessorPtr accessor,sqlite3_stmt * stmt)4217 do_topo_check_start_nodes (GaiaTopologyAccessorPtr accessor,
4218 			   sqlite3_stmt * stmt)
4219 {
4220 /* checking for edges mismatching start nodes */
4221     char *sql;
4222     char *table;
4223     char *xtable1;
4224     char *xtable2;
4225     int ret;
4226     sqlite3_stmt *stmt_in = NULL;
4227     struct gaia_topology *topo = (struct gaia_topology *) accessor;
4228 
4229     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
4230     xtable1 = gaiaDoubleQuotedSql (table);
4231     sqlite3_free (table);
4232     table = sqlite3_mprintf ("%s_node", topo->topology_name);
4233     xtable2 = gaiaDoubleQuotedSql (table);
4234     sqlite3_free (table);
4235     sql =
4236 	sqlite3_mprintf ("SELECT e.edge_id, e.start_node FROM MAIN.\"%s\" AS e "
4237 			 "JOIN MAIN.\"%s\" AS n ON (e.start_node = n.node_id) "
4238 			 "WHERE ST_Disjoint(ST_StartPoint(e.geom), n.geom) = 1",
4239 			 xtable1, xtable2);
4240     free (xtable1);
4241     free (xtable2);
4242     ret =
4243 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
4244     sqlite3_free (sql);
4245     if (ret != SQLITE_OK)
4246       {
4247 	  char *msg =
4248 	      sqlite3_mprintf
4249 	      ("ST_ValidateTopoGeo() - StartNodes error: \"%s\"",
4250 	       sqlite3_errmsg (topo->db_handle));
4251 	  gaiatopo_set_last_error_msg (accessor, msg);
4252 	  sqlite3_free (msg);
4253 	  goto error;
4254       }
4255 
4256     sqlite3_reset (stmt_in);
4257     sqlite3_clear_bindings (stmt_in);
4258     while (1)
4259       {
4260 	  /* scrolling the result set rows */
4261 	  ret = sqlite3_step (stmt_in);
4262 	  if (ret == SQLITE_DONE)
4263 	      break;		/* end of result set */
4264 	  if (ret == SQLITE_ROW)
4265 	    {
4266 		sqlite3_int64 edge_id = sqlite3_column_int64 (stmt_in, 0);
4267 		sqlite3_int64 node_id = sqlite3_column_int64 (stmt_in, 1);
4268 		/* reporting the error */
4269 		sqlite3_reset (stmt);
4270 		sqlite3_clear_bindings (stmt);
4271 		sqlite3_bind_text (stmt, 1, "geometry start mismatch", -1,
4272 				   SQLITE_STATIC);
4273 		sqlite3_bind_int64 (stmt, 2, edge_id);
4274 		sqlite3_bind_int64 (stmt, 3, node_id);
4275 		ret = sqlite3_step (stmt);
4276 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4277 		    ;
4278 		else
4279 		  {
4280 		      char *msg =
4281 			  sqlite3_mprintf
4282 			  ("ST_ValidateTopoGeo() insert #5 error: \"%s\"",
4283 			   sqlite3_errmsg (topo->db_handle));
4284 		      gaiatopo_set_last_error_msg (accessor, msg);
4285 		      sqlite3_free (msg);
4286 		      goto error;
4287 		  }
4288 	    }
4289 	  else
4290 	    {
4291 		char *msg =
4292 		    sqlite3_mprintf
4293 		    ("ST_ValidateTopoGeo() - StartNodes step error: %s",
4294 		     sqlite3_errmsg (topo->db_handle));
4295 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
4296 					     msg);
4297 		sqlite3_free (msg);
4298 		goto error;
4299 	    }
4300       }
4301     sqlite3_finalize (stmt_in);
4302 
4303     return 1;
4304 
4305   error:
4306     if (stmt_in == NULL)
4307 	sqlite3_finalize (stmt_in);
4308     return 0;
4309 }
4310 
4311 static int
do_topo_check_end_nodes(GaiaTopologyAccessorPtr accessor,sqlite3_stmt * stmt)4312 do_topo_check_end_nodes (GaiaTopologyAccessorPtr accessor, sqlite3_stmt * stmt)
4313 {
4314 /* checking for edges mismatching end nodes */
4315     char *sql;
4316     char *table;
4317     char *xtable1;
4318     char *xtable2;
4319     int ret;
4320     sqlite3_stmt *stmt_in = NULL;
4321     struct gaia_topology *topo = (struct gaia_topology *) accessor;
4322 
4323     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
4324     xtable1 = gaiaDoubleQuotedSql (table);
4325     sqlite3_free (table);
4326     table = sqlite3_mprintf ("%s_node", topo->topology_name);
4327     xtable2 = gaiaDoubleQuotedSql (table);
4328     sqlite3_free (table);
4329     sql = sqlite3_mprintf ("SELECT e.edge_id, e.end_node FROM MAIN.\"%s\" AS e "
4330 			   "JOIN MAIN.\"%s\" AS n ON (e.end_node = n.node_id) "
4331 			   "WHERE ST_Disjoint(ST_EndPoint(e.geom), n.geom) = 1",
4332 			   xtable1, xtable2);
4333     free (xtable1);
4334     free (xtable2);
4335     ret =
4336 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
4337     sqlite3_free (sql);
4338     if (ret != SQLITE_OK)
4339       {
4340 	  char *msg =
4341 	      sqlite3_mprintf ("ST_ValidateTopoGeo() - EndNodes error: \"%s\"",
4342 			       sqlite3_errmsg (topo->db_handle));
4343 	  gaiatopo_set_last_error_msg (accessor, msg);
4344 	  sqlite3_free (msg);
4345 	  goto error;
4346       }
4347 
4348     sqlite3_reset (stmt_in);
4349     sqlite3_clear_bindings (stmt_in);
4350     while (1)
4351       {
4352 	  /* scrolling the result set rows */
4353 	  ret = sqlite3_step (stmt_in);
4354 	  if (ret == SQLITE_DONE)
4355 	      break;		/* end of result set */
4356 	  if (ret == SQLITE_ROW)
4357 	    {
4358 		sqlite3_int64 edge_id = sqlite3_column_int64 (stmt_in, 0);
4359 		sqlite3_int64 node_id = sqlite3_column_int64 (stmt_in, 1);
4360 		/* reporting the error */
4361 		sqlite3_reset (stmt);
4362 		sqlite3_clear_bindings (stmt);
4363 		sqlite3_bind_text (stmt, 1, "geometry end mismatch", -1,
4364 				   SQLITE_STATIC);
4365 		sqlite3_bind_int64 (stmt, 2, edge_id);
4366 		sqlite3_bind_int64 (stmt, 3, node_id);
4367 		ret = sqlite3_step (stmt);
4368 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4369 		    ;
4370 		else
4371 		  {
4372 		      char *msg =
4373 			  sqlite3_mprintf
4374 			  ("ST_ValidateTopoGeo() insert #6 error: \"%s\"",
4375 			   sqlite3_errmsg (topo->db_handle));
4376 		      gaiatopo_set_last_error_msg (accessor, msg);
4377 		      sqlite3_free (msg);
4378 		      goto error;
4379 		  }
4380 	    }
4381 	  else
4382 	    {
4383 		char *msg =
4384 		    sqlite3_mprintf
4385 		    ("ST_ValidateTopoGeo() - EndNodes step error: %s",
4386 		     sqlite3_errmsg (topo->db_handle));
4387 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
4388 					     msg);
4389 		sqlite3_free (msg);
4390 		goto error;
4391 	    }
4392       }
4393     sqlite3_finalize (stmt_in);
4394 
4395     return 1;
4396 
4397   error:
4398     if (stmt_in == NULL)
4399 	sqlite3_finalize (stmt_in);
4400     return 0;
4401 }
4402 
4403 static int
do_topo_check_face_no_edges(GaiaTopologyAccessorPtr accessor,sqlite3_stmt * stmt)4404 do_topo_check_face_no_edges (GaiaTopologyAccessorPtr accessor,
4405 			     sqlite3_stmt * stmt)
4406 {
4407 /* checking for faces with no edges */
4408     char *sql;
4409     char *table;
4410     char *xtable1;
4411     char *xtable2;
4412     int ret;
4413     sqlite3_stmt *stmt_in = NULL;
4414     struct gaia_topology *topo = (struct gaia_topology *) accessor;
4415 
4416     table = sqlite3_mprintf ("%s_face", topo->topology_name);
4417     xtable1 = gaiaDoubleQuotedSql (table);
4418     sqlite3_free (table);
4419     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
4420     xtable2 = gaiaDoubleQuotedSql (table);
4421     sqlite3_free (table);
4422     sql = sqlite3_mprintf ("SELECT f.face_id, Count(e1.edge_id) AS cnt1, "
4423 			   "Count(e2.edge_id) AS cnt2 FROM MAIN.\"%s\" AS f "
4424 			   "LEFT JOIN MAIN.\"%s\" AS e1 ON (f.face_id = e1.left_face) "
4425 			   "LEFT JOIN MAIN.\"%s\" AS e2 ON (f.face_id = e2.right_face) "
4426 			   "GROUP BY f.face_id HAVING cnt1 = 0 AND cnt2 = 0",
4427 			   xtable1, xtable2, xtable2);
4428     free (xtable1);
4429     free (xtable2);
4430     ret =
4431 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
4432     sqlite3_free (sql);
4433     if (ret != SQLITE_OK)
4434       {
4435 	  char *msg =
4436 	      sqlite3_mprintf
4437 	      ("ST_ValidateTopoGeo() - FaceNoEdges error: \"%s\"",
4438 	       sqlite3_errmsg (topo->db_handle));
4439 	  gaiatopo_set_last_error_msg (accessor, msg);
4440 	  sqlite3_free (msg);
4441 	  goto error;
4442       }
4443 
4444     sqlite3_reset (stmt_in);
4445     sqlite3_clear_bindings (stmt_in);
4446     while (1)
4447       {
4448 	  /* scrolling the result set rows */
4449 	  ret = sqlite3_step (stmt_in);
4450 	  if (ret == SQLITE_DONE)
4451 	      break;		/* end of result set */
4452 	  if (ret == SQLITE_ROW)
4453 	    {
4454 		sqlite3_int64 face_id = sqlite3_column_int64 (stmt_in, 0);
4455 		/* reporting the error */
4456 		sqlite3_reset (stmt);
4457 		sqlite3_clear_bindings (stmt);
4458 		sqlite3_bind_text (stmt, 1, "face without edges", -1,
4459 				   SQLITE_STATIC);
4460 		sqlite3_bind_int64 (stmt, 2, face_id);
4461 		sqlite3_bind_null (stmt, 3);
4462 		ret = sqlite3_step (stmt);
4463 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4464 		    ;
4465 		else
4466 		  {
4467 		      char *msg =
4468 			  sqlite3_mprintf
4469 			  ("ST_ValidateTopoGeo() insert #7 error: \"%s\"",
4470 			   sqlite3_errmsg (topo->db_handle));
4471 		      gaiatopo_set_last_error_msg (accessor, msg);
4472 		      sqlite3_free (msg);
4473 		      goto error;
4474 		  }
4475 	    }
4476 	  else
4477 	    {
4478 		char *msg =
4479 		    sqlite3_mprintf
4480 		    ("ST_ValidateTopoGeo() - FaceNoEdges step error: %s",
4481 		     sqlite3_errmsg (topo->db_handle));
4482 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
4483 					     msg);
4484 		sqlite3_free (msg);
4485 		goto error;
4486 	    }
4487       }
4488     sqlite3_finalize (stmt_in);
4489 
4490     return 1;
4491 
4492   error:
4493     if (stmt_in == NULL)
4494 	sqlite3_finalize (stmt_in);
4495     return 0;
4496 }
4497 
4498 static int
do_topo_check_no_universal_face(GaiaTopologyAccessorPtr accessor,sqlite3_stmt * stmt)4499 do_topo_check_no_universal_face (GaiaTopologyAccessorPtr accessor,
4500 				 sqlite3_stmt * stmt)
4501 {
4502 /* checking for missing universal face */
4503     char *sql;
4504     char *table;
4505     char *xtable;
4506     int ret;
4507     int i;
4508     char **results;
4509     int rows;
4510     int columns;
4511     char *errMsg = NULL;
4512     int count = 0;
4513     struct gaia_topology *topo = (struct gaia_topology *) accessor;
4514 
4515     table = sqlite3_mprintf ("%s_face", topo->topology_name);
4516     xtable = gaiaDoubleQuotedSql (table);
4517     sqlite3_free (table);
4518     sql =
4519 	sqlite3_mprintf ("SELECT Count(*) FROM MAIN.\"%s\" WHERE face_id = 0",
4520 			 xtable);
4521     free (xtable);
4522     ret =
4523 	sqlite3_get_table (topo->db_handle, sql, &results, &rows, &columns,
4524 			   &errMsg);
4525     sqlite3_free (sql);
4526     if (ret != SQLITE_OK)
4527       {
4528 	  sqlite3_free (errMsg);
4529 	  return 0;
4530       }
4531     for (i = 1; i <= rows; i++)
4532       {
4533 	  count = atoi (results[(i * columns) + 0]);
4534       }
4535     sqlite3_free_table (results);
4536 
4537     if (count <= 0)
4538       {
4539 	  /* reporting the error */
4540 	  sqlite3_reset (stmt);
4541 	  sqlite3_clear_bindings (stmt);
4542 	  sqlite3_bind_text (stmt, 1, "no universal face", -1, SQLITE_STATIC);
4543 	  sqlite3_bind_null (stmt, 2);
4544 	  sqlite3_bind_null (stmt, 3);
4545 	  ret = sqlite3_step (stmt);
4546 	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4547 	      ;
4548 	  else
4549 	    {
4550 		char *msg =
4551 		    sqlite3_mprintf
4552 		    ("ST_ValidateTopoGeo() insert #8 error: \"%s\"",
4553 		     sqlite3_errmsg (topo->db_handle));
4554 		gaiatopo_set_last_error_msg (accessor, msg);
4555 		sqlite3_free (msg);
4556 		return 0;
4557 	    }
4558       }
4559 
4560     return 1;
4561 }
4562 
4563 static int
do_topo_check_create_aux_faces(GaiaTopologyAccessorPtr accessor)4564 do_topo_check_create_aux_faces (GaiaTopologyAccessorPtr accessor)
4565 {
4566 /* creating the aux-Face temp table */
4567     char *table;
4568     char *xtable;
4569     char *sql;
4570     char *errMsg;
4571     int ret;
4572 #if defined(_WIN32) && !defined(__MINGW32__)
4573     int pid;
4574 #else
4575     pid_t pid;
4576 #endif
4577     struct gaia_topology *topo = (struct gaia_topology *) accessor;
4578 
4579 /* creating the aux-face Temp Table */
4580 #if defined(_WIN32) && !defined(__MINGW32__)
4581     pid = _getpid ();
4582 #else
4583     pid = getpid ();
4584 #endif
4585     table = sqlite3_mprintf ("%s_aux_face_%d", topo->topology_name, pid);
4586     xtable = gaiaDoubleQuotedSql (table);
4587     sqlite3_free (table);
4588     sql = sqlite3_mprintf ("CREATE TEMPORARY TABLE \"%s\" (\n"
4589 			   "\tface_id INTEGER PRIMARY KEY,\n\tgeom BLOB)",
4590 			   xtable);
4591     free (xtable);
4592     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
4593     sqlite3_free (sql);
4594     if (ret != SQLITE_OK)
4595       {
4596 	  char *msg =
4597 	      sqlite3_mprintf ("CREATE TEMPORARY TABLE aux_face - error: %s\n",
4598 			       errMsg);
4599 	  sqlite3_free (errMsg);
4600 	  gaiatopo_set_last_error_msg (accessor, msg);
4601 	  sqlite3_free (msg);
4602 	  return 0;
4603       }
4604 
4605 /* creating the exotic spatial index */
4606     table = sqlite3_mprintf ("%s_aux_face_%d_rtree", topo->topology_name, pid);
4607     xtable = gaiaDoubleQuotedSql (table);
4608     sqlite3_free (table);
4609     sql = sqlite3_mprintf ("CREATE VIRTUAL TABLE temp.\"%s\" USING RTree "
4610 			   "(id_face, x_min, x_max, y_min, y_max)", xtable);
4611     free (xtable);
4612     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
4613     sqlite3_free (sql);
4614     if (ret != SQLITE_OK)
4615       {
4616 	  char *msg =
4617 	      sqlite3_mprintf ("CREATE TEMPORARY TABLE aux_face - error: %s\n",
4618 			       errMsg);
4619 	  sqlite3_free (errMsg);
4620 	  gaiatopo_set_last_error_msg (accessor, msg);
4621 	  sqlite3_free (msg);
4622 	  return 0;
4623       }
4624 
4625     return 1;
4626 }
4627 
4628 static int
do_topo_check_build_aux_faces(GaiaTopologyAccessorPtr accessor,sqlite3_stmt * stmt)4629 do_topo_check_build_aux_faces (GaiaTopologyAccessorPtr accessor,
4630 			       sqlite3_stmt * stmt)
4631 {
4632 /* populating the aux-face Temp Table */
4633     char *sql;
4634     char *table;
4635     char *xtable;
4636     int ret;
4637     sqlite3_stmt *stmt_in = NULL;
4638     sqlite3_stmt *stmt_out = NULL;
4639     sqlite3_stmt *stmt_rtree = NULL;
4640 #if defined(_WIN32) && !defined(__MINGW32__)
4641     int pid;
4642 #else
4643     pid_t pid;
4644 #endif
4645     struct gaia_topology *topo = (struct gaia_topology *) accessor;
4646 
4647 /* preparing the input SQL statement */
4648 #if defined(_WIN32) && !defined(__MINGW32__)
4649     pid = _getpid ();
4650 #else
4651     pid = getpid ();
4652 #endif
4653     table = sqlite3_mprintf ("%s_face", topo->topology_name);
4654     xtable = gaiaDoubleQuotedSql (table);
4655     sqlite3_free (table);
4656     sql = sqlite3_mprintf ("SELECT face_id, ST_GetFaceGeometry(%Q, face_id) "
4657 			   "FROM MAIN.\"%s\" WHERE face_id <> 0",
4658 			   topo->topology_name, xtable);
4659     free (xtable);
4660     ret =
4661 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
4662     sqlite3_free (sql);
4663     if (ret != SQLITE_OK)
4664       {
4665 	  char *msg =
4666 	      sqlite3_mprintf
4667 	      ("ST_ValidateTopoGeo() - GetFaceGeometry error: \"%s\"",
4668 	       sqlite3_errmsg (topo->db_handle));
4669 	  gaiatopo_set_last_error_msg (accessor, msg);
4670 	  sqlite3_free (msg);
4671 	  goto error;
4672       }
4673 
4674 /* preparing the output SQL statement */
4675     table = sqlite3_mprintf ("%s_aux_face_%d", topo->topology_name, pid);
4676     xtable = gaiaDoubleQuotedSql (table);
4677     sqlite3_free (table);
4678     sql =
4679 	sqlite3_mprintf
4680 	("INSERT INTO TEMP.\"%s\" (face_id, geom) VALUES (?, ?)", xtable);
4681     free (xtable);
4682     ret =
4683 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_out,
4684 			    NULL);
4685     sqlite3_free (sql);
4686     if (ret != SQLITE_OK)
4687       {
4688 	  char *msg =
4689 	      sqlite3_mprintf ("ST_ValidateTopoGeo() - AuxFace error: \"%s\"",
4690 			       sqlite3_errmsg (topo->db_handle));
4691 	  gaiatopo_set_last_error_msg (accessor, msg);
4692 	  sqlite3_free (msg);
4693 	  goto error;
4694       }
4695 
4696 /* preparing the RTree SQL statement */
4697     table = sqlite3_mprintf ("%s_aux_face_%d_rtree", topo->topology_name, pid);
4698     xtable = gaiaDoubleQuotedSql (table);
4699     sqlite3_free (table);
4700     sql = sqlite3_mprintf ("INSERT INTO TEMP.\"%s\" "
4701 			   "(id_face, x_min, x_max, y_min, y_max) VALUES (?, ?, ?, ?, ?)",
4702 			   xtable);
4703     free (xtable);
4704     ret =
4705 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_rtree,
4706 			    NULL);
4707     sqlite3_free (sql);
4708     if (ret != SQLITE_OK)
4709       {
4710 	  char *msg =
4711 	      sqlite3_mprintf
4712 	      ("ST_ValidateTopoGeo() - AuxFaceRTree error: \"%s\"",
4713 	       sqlite3_errmsg (topo->db_handle));
4714 	  gaiatopo_set_last_error_msg (accessor, msg);
4715 	  sqlite3_free (msg);
4716 	  goto error;
4717       }
4718 
4719     sqlite3_reset (stmt_in);
4720     sqlite3_clear_bindings (stmt_in);
4721     while (1)
4722       {
4723 	  /* scrolling the result set rows */
4724 	  ret = sqlite3_step (stmt_in);
4725 	  if (ret == SQLITE_DONE)
4726 	      break;		/* end of result set */
4727 	  if (ret == SQLITE_ROW)
4728 	    {
4729 		gaiaGeomCollPtr geom = NULL;
4730 		const unsigned char *blob;
4731 		int blob_sz;
4732 		sqlite3_int64 face_id = sqlite3_column_int64 (stmt_in, 0);
4733 		if (sqlite3_column_type (stmt_in, 1) == SQLITE_BLOB)
4734 		  {
4735 		      blob = sqlite3_column_blob (stmt_in, 1);
4736 		      blob_sz = sqlite3_column_bytes (stmt_in, 1);
4737 		      geom = gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
4738 		  }
4739 		if (geom == NULL)
4740 		  {
4741 		      /* reporting the error */
4742 		      sqlite3_reset (stmt);
4743 		      sqlite3_clear_bindings (stmt);
4744 		      sqlite3_bind_text (stmt, 1, "invalid face geometry", -1,
4745 					 SQLITE_STATIC);
4746 		      sqlite3_bind_int64 (stmt, 2, face_id);
4747 		      sqlite3_bind_null (stmt, 3);
4748 		      ret = sqlite3_step (stmt);
4749 		      if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4750 			  ;
4751 		      else
4752 			{
4753 			    char *msg =
4754 				sqlite3_mprintf
4755 				("ST_ValidateTopoGeo() insert #9 error: \"%s\"",
4756 				 sqlite3_errmsg (topo->db_handle));
4757 			    gaiatopo_set_last_error_msg (accessor, msg);
4758 			    sqlite3_free (msg);
4759 			    goto error;
4760 			}
4761 		  }
4762 		else
4763 		  {
4764 		      double xmin = geom->MinX;
4765 		      double xmax = geom->MaxX;
4766 		      double ymin = geom->MinY;
4767 		      double ymax = geom->MaxY;
4768 		      gaiaFreeGeomColl (geom);
4769 		      /* inserting into AuxFace */
4770 		      sqlite3_reset (stmt_out);
4771 		      sqlite3_clear_bindings (stmt_out);
4772 		      sqlite3_bind_int64 (stmt_out, 1, face_id);
4773 		      sqlite3_bind_blob (stmt_out, 2, blob, blob_sz,
4774 					 SQLITE_STATIC);
4775 		      ret = sqlite3_step (stmt_out);
4776 		      if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4777 			  ;
4778 		      else
4779 			{
4780 			    char *msg =
4781 				sqlite3_mprintf
4782 				("ST_ValidateTopoGeo() insert #10 error: \"%s\"",
4783 				 sqlite3_errmsg (topo->db_handle));
4784 			    gaiatopo_set_last_error_msg (accessor, msg);
4785 			    sqlite3_free (msg);
4786 			    goto error;
4787 			}
4788 		      /* updating the AuxFaceRTree */
4789 		      sqlite3_reset (stmt_rtree);
4790 		      sqlite3_clear_bindings (stmt_rtree);
4791 		      sqlite3_bind_int64 (stmt_rtree, 1, face_id);
4792 		      sqlite3_bind_double (stmt_rtree, 2, xmin);
4793 		      sqlite3_bind_double (stmt_rtree, 3, xmax);
4794 		      sqlite3_bind_double (stmt_rtree, 4, ymin);
4795 		      sqlite3_bind_double (stmt_rtree, 5, ymax);
4796 		      ret = sqlite3_step (stmt_rtree);
4797 		      if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4798 			  ;
4799 		      else
4800 			{
4801 			    char *msg =
4802 				sqlite3_mprintf
4803 				("ST_ValidateTopoGeo() insert #11 error: \"%s\"",
4804 				 sqlite3_errmsg (topo->db_handle));
4805 			    gaiatopo_set_last_error_msg (accessor, msg);
4806 			    sqlite3_free (msg);
4807 			    goto error;
4808 			}
4809 		  }
4810 	    }
4811 	  else
4812 	    {
4813 		char *msg =
4814 		    sqlite3_mprintf
4815 		    ("ST_ValidateTopoGeo() - GetFaceGeometry step error: %s",
4816 		     sqlite3_errmsg (topo->db_handle));
4817 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
4818 					     msg);
4819 		sqlite3_free (msg);
4820 		goto error;
4821 	    }
4822       }
4823     sqlite3_finalize (stmt_in);
4824     sqlite3_finalize (stmt_out);
4825     sqlite3_finalize (stmt_rtree);
4826 
4827     return 1;
4828 
4829   error:
4830     if (stmt_in == NULL)
4831 	sqlite3_finalize (stmt_in);
4832     if (stmt_out == NULL)
4833 	sqlite3_finalize (stmt_out);
4834     if (stmt_rtree == NULL)
4835 	sqlite3_finalize (stmt_rtree);
4836     return 0;
4837 }
4838 
4839 static int
do_topo_check_overlapping_faces(GaiaTopologyAccessorPtr accessor,sqlite3_stmt * stmt)4840 do_topo_check_overlapping_faces (GaiaTopologyAccessorPtr accessor,
4841 				 sqlite3_stmt * stmt)
4842 {
4843 /* checking for overlapping faces */
4844     char *sql;
4845     char *table;
4846     char *xtable;
4847     char *rtree;
4848     int ret;
4849 #if defined(_WIN32) && !defined(__MINGW32__)
4850     int pid;
4851 #else
4852     pid_t pid;
4853 #endif
4854     sqlite3_stmt *stmt_in = NULL;
4855     struct gaia_topology *topo = (struct gaia_topology *) accessor;
4856 
4857 
4858 #if defined(_WIN32) && !defined(__MINGW32__)
4859     pid = _getpid ();
4860 #else
4861     pid = getpid ();
4862 #endif
4863     table = sqlite3_mprintf ("%s_aux_face_%d", topo->topology_name, pid);
4864     xtable = gaiaDoubleQuotedSql (table);
4865     sqlite3_free (table);
4866     table = sqlite3_mprintf ("%s_aux_face_%d_rtree", topo->topology_name, pid);
4867     rtree = gaiaDoubleQuotedSql (table);
4868     sqlite3_free (table);
4869     sql =
4870 	sqlite3_mprintf
4871 	("SELECT a.face_id, b.face_id FROM TEMP.\"%s\" AS a, TEMP.\"%s\" AS b "
4872 	 "WHERE a.geom IS NOT NULL AND a.face_id <> b.face_id AND ST_Overlaps(a.geom, b.geom) = 1 "
4873 	 "AND b.face_id IN (SELECT id_face FROM TEMP.\"%s\" WHERE x_min <= MbrMaxX(a.geom) "
4874 	 "AND x_max >= MbrMinX(a.geom) AND y_min <= MbrMaxY(a.geom) AND y_max >= MbrMinY(a.geom))",
4875 	 xtable, xtable, rtree);
4876     free (xtable);
4877     free (rtree);
4878     ret =
4879 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
4880     sqlite3_free (sql);
4881     if (ret != SQLITE_OK)
4882       {
4883 	  char *msg =
4884 	      sqlite3_mprintf
4885 	      ("ST_ValidateTopoGeo() - OverlappingFaces error: \"%s\"",
4886 	       sqlite3_errmsg (topo->db_handle));
4887 	  gaiatopo_set_last_error_msg (accessor, msg);
4888 	  sqlite3_free (msg);
4889 	  goto error;
4890       }
4891 
4892     sqlite3_reset (stmt_in);
4893     sqlite3_clear_bindings (stmt_in);
4894     while (1)
4895       {
4896 	  /* scrolling the result set rows */
4897 	  ret = sqlite3_step (stmt_in);
4898 	  if (ret == SQLITE_DONE)
4899 	      break;		/* end of result set */
4900 	  if (ret == SQLITE_ROW)
4901 	    {
4902 		sqlite3_int64 face_id1 = sqlite3_column_int64 (stmt_in, 0);
4903 		sqlite3_int64 face_id2 = sqlite3_column_int64 (stmt_in, 1);
4904 		/* reporting the error */
4905 		sqlite3_reset (stmt);
4906 		sqlite3_clear_bindings (stmt);
4907 		sqlite3_bind_text (stmt, 1, "face overlaps face", -1,
4908 				   SQLITE_STATIC);
4909 		sqlite3_bind_int64 (stmt, 2, face_id1);
4910 		sqlite3_bind_int64 (stmt, 3, face_id2);
4911 		ret = sqlite3_step (stmt);
4912 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4913 		    ;
4914 		else
4915 		  {
4916 		      char *msg =
4917 			  sqlite3_mprintf
4918 			  ("ST_ValidateTopoGeo() insert #12 error: \"%s\"",
4919 			   sqlite3_errmsg (topo->db_handle));
4920 		      gaiatopo_set_last_error_msg (accessor, msg);
4921 		      sqlite3_free (msg);
4922 		      goto error;
4923 		  }
4924 	    }
4925 	  else
4926 	    {
4927 		char *msg =
4928 		    sqlite3_mprintf
4929 		    ("ST_ValidateTopoGeo() - OverlappingFaces step error: %s",
4930 		     sqlite3_errmsg (topo->db_handle));
4931 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
4932 					     msg);
4933 		sqlite3_free (msg);
4934 		goto error;
4935 	    }
4936       }
4937     sqlite3_finalize (stmt_in);
4938 
4939     return 1;
4940 
4941   error:
4942     if (stmt_in == NULL)
4943 	sqlite3_finalize (stmt_in);
4944     return 0;
4945 }
4946 
4947 static int
do_topo_check_face_within_face(GaiaTopologyAccessorPtr accessor,sqlite3_stmt * stmt)4948 do_topo_check_face_within_face (GaiaTopologyAccessorPtr accessor,
4949 				sqlite3_stmt * stmt)
4950 {
4951 /* checking for face-within-face */
4952     char *sql;
4953     char *table;
4954     char *xtable;
4955     char *rtree;
4956     int ret;
4957 #if defined(_WIN32) && !defined(__MINGW32__)
4958     int pid;
4959 #else
4960     pid_t pid;
4961 #endif
4962     sqlite3_stmt *stmt_in = NULL;
4963     struct gaia_topology *topo = (struct gaia_topology *) accessor;
4964 
4965 
4966 #if defined(_WIN32) && !defined(__MINGW32__)
4967     pid = _getpid ();
4968 #else
4969     pid = getpid ();
4970 #endif
4971     table = sqlite3_mprintf ("%s_aux_face_%d", topo->topology_name, pid);
4972     xtable = gaiaDoubleQuotedSql (table);
4973     sqlite3_free (table);
4974     table = sqlite3_mprintf ("%s_aux_face_%d_rtree", topo->topology_name, pid);
4975     rtree = gaiaDoubleQuotedSql (table);
4976     sqlite3_free (table);
4977     sql =
4978 	sqlite3_mprintf
4979 	("SELECT a.face_id, b.face_id FROM TEMP.\"%s\" AS a, TEMP.\"%s\" AS b "
4980 	 "WHERE a.geom IS NOT NULL AND a.face_id <> b.face_id AND ST_Within(a.geom, b.geom) = 1 "
4981 	 "AND b.face_id IN (SELECT id_face FROM TEMP.\"%s\" WHERE x_min <= MbrMaxX(a.geom) "
4982 	 "AND x_max >= MbrMinX(a.geom) AND y_min <= MbrMaxY(a.geom) AND y_max >= MbrMinY(a.geom))",
4983 	 xtable, xtable, rtree);
4984     free (xtable);
4985     free (rtree);
4986     ret =
4987 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
4988     sqlite3_free (sql);
4989     if (ret != SQLITE_OK)
4990       {
4991 	  char *msg =
4992 	      sqlite3_mprintf
4993 	      ("ST_ValidateTopoGeo() - FaceWithinFace error: \"%s\"",
4994 	       sqlite3_errmsg (topo->db_handle));
4995 	  gaiatopo_set_last_error_msg (accessor, msg);
4996 	  sqlite3_free (msg);
4997 	  goto error;
4998       }
4999 
5000     sqlite3_reset (stmt_in);
5001     sqlite3_clear_bindings (stmt_in);
5002     while (1)
5003       {
5004 	  /* scrolling the result set rows */
5005 	  ret = sqlite3_step (stmt_in);
5006 	  if (ret == SQLITE_DONE)
5007 	      break;		/* end of result set */
5008 	  if (ret == SQLITE_ROW)
5009 	    {
5010 		sqlite3_int64 face_id1 = sqlite3_column_int64 (stmt_in, 0);
5011 		sqlite3_int64 face_id2 = sqlite3_column_int64 (stmt_in, 1);
5012 		/* reporting the error */
5013 		sqlite3_reset (stmt);
5014 		sqlite3_clear_bindings (stmt);
5015 		sqlite3_bind_text (stmt, 1, "face within face", -1,
5016 				   SQLITE_STATIC);
5017 		sqlite3_bind_int64 (stmt, 2, face_id1);
5018 		sqlite3_bind_int64 (stmt, 3, face_id2);
5019 		ret = sqlite3_step (stmt);
5020 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
5021 		    ;
5022 		else
5023 		  {
5024 		      char *msg =
5025 			  sqlite3_mprintf
5026 			  ("ST_ValidateTopoGeo() insert #13 error: \"%s\"",
5027 			   sqlite3_errmsg (topo->db_handle));
5028 		      gaiatopo_set_last_error_msg (accessor, msg);
5029 		      sqlite3_free (msg);
5030 		      goto error;
5031 		  }
5032 	    }
5033 	  else
5034 	    {
5035 		char *msg =
5036 		    sqlite3_mprintf
5037 		    ("ST_ValidateTopoGeo() - FaceWithinFace step error: %s",
5038 		     sqlite3_errmsg (topo->db_handle));
5039 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
5040 					     msg);
5041 		sqlite3_free (msg);
5042 		goto error;
5043 	    }
5044       }
5045     sqlite3_finalize (stmt_in);
5046 
5047     return 1;
5048 
5049   error:
5050     if (stmt_in == NULL)
5051 	sqlite3_finalize (stmt_in);
5052     return 0;
5053 }
5054 
5055 static int
do_topo_check_drop_aux_faces(GaiaTopologyAccessorPtr accessor)5056 do_topo_check_drop_aux_faces (GaiaTopologyAccessorPtr accessor)
5057 {
5058 /* dropping the aux-Face temp table */
5059     char *table;
5060     char *xtable;
5061     char *sql;
5062     char *errMsg;
5063     int ret;
5064 #if defined(_WIN32) && !defined(__MINGW32__)
5065     int pid;
5066 #else
5067     pid_t pid;
5068 #endif
5069     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5070 
5071 /* finalizing all prepared Statements */
5072     finalize_all_topo_prepared_stmts (topo->cache);
5073 
5074 /* dropping the aux-face Temp Table */
5075 #if defined(_WIN32) && !defined(__MINGW32__)
5076     pid = _getpid ();
5077 #else
5078     pid = getpid ();
5079 #endif
5080     table = sqlite3_mprintf ("%s_aux_face_%d", topo->topology_name, pid);
5081     xtable = gaiaDoubleQuotedSql (table);
5082     sqlite3_free (table);
5083     sql = sqlite3_mprintf ("DROP TABLE TEMP.\"%s\"", xtable);
5084     free (xtable);
5085     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
5086     create_all_topo_prepared_stmts (topo->cache);	/* recreating prepared stsms */
5087     sqlite3_free (sql);
5088     if (ret != SQLITE_OK)
5089       {
5090 	  char *msg = sqlite3_mprintf ("DROP TABLE temp.aux_face - error: %s\n",
5091 				       errMsg);
5092 	  sqlite3_free (errMsg);
5093 	  gaiatopo_set_last_error_msg (accessor, msg);
5094 	  sqlite3_free (msg);
5095 	  return 0;
5096       }
5097 
5098 /* dropping the aux-face Temp RTree */
5099     table = sqlite3_mprintf ("%s_aux_face_%d_rtree", topo->topology_name, pid);
5100     xtable = gaiaDoubleQuotedSql (table);
5101     sqlite3_free (table);
5102     sql = sqlite3_mprintf ("DROP TABLE TEMP.\"%s\"", xtable);
5103     free (xtable);
5104     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
5105     sqlite3_free (sql);
5106     if (ret != SQLITE_OK)
5107       {
5108 	  char *msg =
5109 	      sqlite3_mprintf ("DROP TABLE temp.aux_face_rtree - error: %s\n",
5110 			       errMsg);
5111 	  sqlite3_free (errMsg);
5112 	  gaiatopo_set_last_error_msg (accessor, msg);
5113 	  sqlite3_free (msg);
5114 	  return 0;
5115       }
5116 
5117     return 1;
5118 }
5119 
5120 GAIATOPO_DECLARE int
gaiaValidateTopoGeo(GaiaTopologyAccessorPtr accessor)5121 gaiaValidateTopoGeo (GaiaTopologyAccessorPtr accessor)
5122 {
5123 /* generating a validity report for a given Topology */
5124     char *table;
5125     char *xtable;
5126     char *sql;
5127     int ret;
5128     sqlite3_stmt *stmt = NULL;
5129     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5130     if (topo == NULL)
5131 	return 0;
5132 
5133     if (!do_check_create_validate_topogeo_table (accessor))
5134 	return 0;
5135 
5136     table = sqlite3_mprintf ("%s_validate_topogeo", topo->topology_name);
5137     xtable = gaiaDoubleQuotedSql (table);
5138     sqlite3_free (table);
5139     sql =
5140 	sqlite3_mprintf
5141 	("INSERT INTO TEMP.\"%s\" (error, primitive1, primitive2) VALUES (?, ?, ?)",
5142 	 xtable);
5143     free (xtable);
5144     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
5145     sqlite3_free (sql);
5146     if (ret != SQLITE_OK)
5147       {
5148 	  char *msg = sqlite3_mprintf ("ST_ValidateTopoGeo error: \"%s\"",
5149 				       sqlite3_errmsg (topo->db_handle));
5150 	  gaiatopo_set_last_error_msg (accessor, msg);
5151 	  sqlite3_free (msg);
5152 	  goto error;
5153       }
5154 
5155     if (!do_topo_check_coincident_nodes (accessor, stmt))
5156 	goto error;
5157 
5158     if (!do_topo_check_edge_node (accessor, stmt))
5159 	goto error;
5160 
5161     if (!do_topo_check_non_simple (accessor, stmt))
5162 	goto error;
5163 
5164     if (!do_topo_check_edge_edge (accessor, stmt))
5165 	goto error;
5166 
5167     if (!do_topo_check_start_nodes (accessor, stmt))
5168 	goto error;
5169 
5170     if (!do_topo_check_end_nodes (accessor, stmt))
5171 	goto error;
5172 
5173     if (!do_topo_check_face_no_edges (accessor, stmt))
5174 	goto error;
5175 
5176     if (!do_topo_check_no_universal_face (accessor, stmt))
5177 	goto error;
5178 
5179     if (!do_topo_check_create_aux_faces (accessor))
5180 	goto error;
5181 
5182     if (!do_topo_check_build_aux_faces (accessor, stmt))
5183 	goto error;
5184 
5185     if (!do_topo_check_overlapping_faces (accessor, stmt))
5186 	goto error;
5187 
5188     if (!do_topo_check_face_within_face (accessor, stmt))
5189 	goto error;
5190 
5191     if (!do_topo_check_drop_aux_faces (accessor))
5192 	goto error;
5193 
5194     sqlite3_finalize (stmt);
5195     return 1;
5196 
5197   error:
5198     if (stmt != NULL)
5199 	sqlite3_finalize (stmt);
5200     return 0;
5201 }
5202 
5203 GAIATOPO_DECLARE sqlite3_int64
gaiaGetNodeByPoint(GaiaTopologyAccessorPtr accessor,gaiaPointPtr pt,double tolerance)5204 gaiaGetNodeByPoint (GaiaTopologyAccessorPtr accessor, gaiaPointPtr pt,
5205 		    double tolerance)
5206 {
5207 /* RTT wrapper - GetNodeByPoint */
5208     const RTCTX *ctx = NULL;
5209     struct splite_internal_cache *cache = NULL;
5210     sqlite3_int64 ret;
5211     int has_z = 0;
5212     RTPOINT *rt_pt;
5213     RTPOINTARRAY *pa;
5214     RTPOINT4D point;
5215     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5216     if (topo == NULL)
5217 	return 0;
5218 
5219     cache = (struct splite_internal_cache *) topo->cache;
5220     if (cache == NULL)
5221 	return 0;
5222     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
5223 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
5224 	return 0;
5225     ctx = cache->RTTOPO_handle;
5226     if (ctx == NULL)
5227 	return 0;
5228 
5229     if (pt->DimensionModel == GAIA_XY_Z || pt->DimensionModel == GAIA_XY_Z_M)
5230 	has_z = 1;
5231     pa = ptarray_construct (ctx, has_z, 0, 1);
5232     point.x = pt->X;
5233     point.y = pt->Y;
5234     if (has_z)
5235 	point.z = pt->Z;
5236     ptarray_set_point4d (ctx, pa, 0, &point);
5237     rt_pt = rtpoint_construct (ctx, topo->srid, NULL, pa);
5238 
5239     if (tolerance < 0.0)
5240 	tolerance = topo->tolerance;	/* using the standard tolerance */
5241 
5242     gaiaResetRtTopoMsg (cache);
5243     ret =
5244 	rtt_GetNodeByPoint ((RTT_TOPOLOGY *) (topo->rtt_topology), rt_pt,
5245 			    tolerance);
5246     rtpoint_free (ctx, rt_pt);
5247 
5248     return ret;
5249 }
5250 
5251 GAIATOPO_DECLARE sqlite3_int64
gaiaGetEdgeByPoint(GaiaTopologyAccessorPtr accessor,gaiaPointPtr pt,double tolerance)5252 gaiaGetEdgeByPoint (GaiaTopologyAccessorPtr accessor, gaiaPointPtr pt,
5253 		    double tolerance)
5254 {
5255 /* RTT wrapper - GetEdgeByPoint */
5256     const RTCTX *ctx = NULL;
5257     struct splite_internal_cache *cache = NULL;
5258     sqlite3_int64 ret;
5259     int has_z = 0;
5260     RTPOINT *rt_pt;
5261     RTPOINTARRAY *pa;
5262     RTPOINT4D point;
5263     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5264     if (topo == NULL)
5265 	return 0;
5266 
5267     cache = (struct splite_internal_cache *) topo->cache;
5268     if (cache == NULL)
5269 	return 0;
5270     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
5271 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
5272 	return 0;
5273     ctx = cache->RTTOPO_handle;
5274     if (ctx == NULL)
5275 	return 0;
5276 
5277     if (pt->DimensionModel == GAIA_XY_Z || pt->DimensionModel == GAIA_XY_Z_M)
5278 	has_z = 1;
5279     pa = ptarray_construct (ctx, has_z, 0, 1);
5280     point.x = pt->X;
5281     point.y = pt->Y;
5282     if (has_z)
5283 	point.z = pt->Z;
5284     ptarray_set_point4d (ctx, pa, 0, &point);
5285     rt_pt = rtpoint_construct (ctx, topo->srid, NULL, pa);
5286 
5287     if (tolerance < 0.0)
5288 	tolerance = topo->tolerance;	/* using the standard tolerance */
5289 
5290     gaiaResetRtTopoMsg (cache);
5291     ret =
5292 	rtt_GetEdgeByPoint ((RTT_TOPOLOGY *) (topo->rtt_topology), rt_pt,
5293 			    tolerance);
5294     rtpoint_free (ctx, rt_pt);
5295 
5296     return ret;
5297 }
5298 
5299 GAIATOPO_DECLARE sqlite3_int64
gaiaGetFaceByPoint(GaiaTopologyAccessorPtr accessor,gaiaPointPtr pt,double tolerance)5300 gaiaGetFaceByPoint (GaiaTopologyAccessorPtr accessor, gaiaPointPtr pt,
5301 		    double tolerance)
5302 {
5303 /* RTT wrapper - GetFaceByPoint */
5304     const RTCTX *ctx = NULL;
5305     struct splite_internal_cache *cache = NULL;
5306     sqlite3_int64 ret;
5307     int has_z = 0;
5308     RTPOINT *rt_pt;
5309     RTPOINTARRAY *pa;
5310     RTPOINT4D point;
5311     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5312     if (topo == NULL)
5313 	return 0;
5314 
5315     cache = (struct splite_internal_cache *) topo->cache;
5316     if (cache == NULL)
5317 	return 0;
5318     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
5319 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
5320 	return 0;
5321     ctx = cache->RTTOPO_handle;
5322     if (ctx == NULL)
5323 	return 0;
5324 
5325     if (pt->DimensionModel == GAIA_XY_Z || pt->DimensionModel == GAIA_XY_Z_M)
5326 	has_z = 1;
5327     pa = ptarray_construct (ctx, has_z, 0, 1);
5328     point.x = pt->X;
5329     point.y = pt->Y;
5330     if (has_z)
5331 	point.z = pt->Z;
5332     ptarray_set_point4d (ctx, pa, 0, &point);
5333     rt_pt = rtpoint_construct (ctx, topo->srid, NULL, pa);
5334 
5335     if (tolerance < 0.0)
5336 	tolerance = topo->tolerance;	/* using the standard tolerance */
5337 
5338     gaiaResetRtTopoMsg (cache);
5339     ret =
5340 	rtt_GetFaceByPoint ((RTT_TOPOLOGY *) (topo->rtt_topology), rt_pt,
5341 			    tolerance);
5342     rtpoint_free (ctx, rt_pt);
5343 
5344     return ret;
5345 }
5346 
5347 GAIATOPO_DECLARE sqlite3_int64
gaiaTopoGeo_AddPoint(GaiaTopologyAccessorPtr accessor,gaiaPointPtr pt,double tolerance)5348 gaiaTopoGeo_AddPoint (GaiaTopologyAccessorPtr accessor, gaiaPointPtr pt,
5349 		      double tolerance)
5350 {
5351 /* RTT wrapper - AddPoint */
5352     const RTCTX *ctx = NULL;
5353     struct splite_internal_cache *cache = NULL;
5354     sqlite3_int64 ret;
5355     int has_z = 0;
5356     RTPOINT *rt_pt;
5357     RTPOINTARRAY *pa;
5358     RTPOINT4D point;
5359     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5360     if (topo == NULL)
5361 	return 0;
5362 
5363     cache = (struct splite_internal_cache *) topo->cache;
5364     if (cache == NULL)
5365 	return 0;
5366     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
5367 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
5368 	return 0;
5369     ctx = cache->RTTOPO_handle;
5370     if (ctx == NULL)
5371 	return 0;
5372 
5373     if (pt->DimensionModel == GAIA_XY_Z || pt->DimensionModel == GAIA_XY_Z_M)
5374 	has_z = 1;
5375     pa = ptarray_construct (ctx, has_z, 0, 1);
5376     point.x = pt->X;
5377     point.y = pt->Y;
5378     if (has_z)
5379 	point.z = pt->Z;
5380     ptarray_set_point4d (ctx, pa, 0, &point);
5381     rt_pt = rtpoint_construct (ctx, topo->srid, NULL, pa);
5382 
5383     if (tolerance < 0.0)
5384 	tolerance = topo->tolerance;	/* using the standard tolerance */
5385 
5386     gaiaResetRtTopoMsg (cache);
5387     ret =
5388 	rtt_AddPoint ((RTT_TOPOLOGY *) (topo->rtt_topology), rt_pt, tolerance);
5389     rtpoint_free (ctx, rt_pt);
5390 
5391     return ret;
5392 }
5393 
5394 GAIATOPO_DECLARE int
gaiaTopoGeo_AddLineString(GaiaTopologyAccessorPtr accessor,gaiaLinestringPtr ln,double tolerance,sqlite3_int64 ** edge_ids,int * ids_count)5395 gaiaTopoGeo_AddLineString (GaiaTopologyAccessorPtr accessor,
5396 			   gaiaLinestringPtr ln, double tolerance,
5397 			   sqlite3_int64 ** edge_ids, int *ids_count)
5398 {
5399 /* RTT wrapper - AddLinestring */
5400     const RTCTX *ctx = NULL;
5401     struct splite_internal_cache *cache = NULL;
5402     int ret = 0;
5403     RTT_ELEMID *edgeids;
5404     int nedges;
5405     int i;
5406     sqlite3_int64 *ids;
5407     RTLINE *rt_line;
5408     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5409     *edge_ids = NULL;
5410     *ids_count = 0;
5411     if (topo == NULL)
5412 	return 0;
5413 
5414     cache = (struct splite_internal_cache *) topo->cache;
5415     if (cache == NULL)
5416 	return 0;
5417     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
5418 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
5419 	return 0;
5420     ctx = cache->RTTOPO_handle;
5421     if (ctx == NULL)
5422 	return 0;
5423 
5424     rt_line =
5425 	gaia_convert_linestring_to_rtline (ctx, ln, topo->srid, topo->has_z);
5426 
5427     if (tolerance < 0.0)
5428 	tolerance = topo->tolerance;	/* using the standard tolerance */
5429 
5430     gaiaResetRtTopoMsg (cache);
5431     edgeids =
5432 	rtt_AddLine ((RTT_TOPOLOGY *) (topo->rtt_topology), rt_line, tolerance,
5433 		     &nedges);
5434 
5435     rtline_free (ctx, rt_line);
5436     if (edgeids != NULL)
5437       {
5438 	  ids = malloc (sizeof (sqlite3_int64) * nedges);
5439 	  for (i = 0; i < nedges; i++)
5440 	      ids[i] = edgeids[i];
5441 	  *edge_ids = ids;
5442 	  *ids_count = nedges;
5443 	  ret = 1;
5444 	  rtfree (ctx, edgeids);
5445       }
5446     return ret;
5447 }
5448 
5449 GAIATOPO_DECLARE int
gaiaTopoGeo_AddLineStringNoFace(GaiaTopologyAccessorPtr accessor,gaiaLinestringPtr ln,double tolerance,sqlite3_int64 ** edge_ids,int * ids_count)5450 gaiaTopoGeo_AddLineStringNoFace (GaiaTopologyAccessorPtr accessor,
5451 				 gaiaLinestringPtr ln, double tolerance,
5452 				 sqlite3_int64 ** edge_ids, int *ids_count)
5453 {
5454 /* RTT wrapper - AddLinestring NO FACE */
5455     const RTCTX *ctx = NULL;
5456     struct splite_internal_cache *cache = NULL;
5457     int ret = 0;
5458     RTT_ELEMID *edgeids;
5459     int nedges;
5460     int i;
5461     sqlite3_int64 *ids;
5462     RTLINE *rt_line;
5463     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5464     *edge_ids = NULL;
5465     *ids_count = 0;
5466     if (topo == NULL)
5467 	return 0;
5468 
5469     cache = (struct splite_internal_cache *) topo->cache;
5470     if (cache == NULL)
5471 	return 0;
5472     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
5473 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
5474 	return 0;
5475     ctx = cache->RTTOPO_handle;
5476     if (ctx == NULL)
5477 	return 0;
5478 
5479     rt_line =
5480 	gaia_convert_linestring_to_rtline (ctx, ln, topo->srid, topo->has_z);
5481 
5482     if (tolerance < 0.0)
5483 	tolerance = topo->tolerance;	/* using the standard tolerance */
5484 
5485     gaiaResetRtTopoMsg (cache);
5486     edgeids =
5487 	rtt_AddLineNoFace ((RTT_TOPOLOGY *) (topo->rtt_topology), rt_line,
5488 			   tolerance, &nedges);
5489 
5490     rtline_free (ctx, rt_line);
5491     if (edgeids != NULL)
5492       {
5493 	  ids = malloc (sizeof (sqlite3_int64) * nedges);
5494 	  for (i = 0; i < nedges; i++)
5495 	      ids[i] = edgeids[i];
5496 	  *edge_ids = ids;
5497 	  *ids_count = nedges;
5498 	  ret = 1;
5499 	  rtfree (ctx, edgeids);
5500       }
5501     return ret;
5502 }
5503 
5504 GAIATOPO_DECLARE int
gaiaTopoGeo_Polygonize(GaiaTopologyAccessorPtr accessor)5505 gaiaTopoGeo_Polygonize (GaiaTopologyAccessorPtr accessor)
5506 {
5507 /* RTT wrapper - Determine and register all topology faces */
5508     const RTCTX *ctx = NULL;
5509     struct splite_internal_cache *cache = NULL;
5510     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5511     if (topo == NULL)
5512 	return 0;
5513 
5514     cache = (struct splite_internal_cache *) topo->cache;
5515     if (cache == NULL)
5516 	return 0;
5517     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
5518 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
5519 	return 0;
5520     ctx = cache->RTTOPO_handle;
5521     if (ctx == NULL)
5522 	return 0;
5523 
5524     gaiaResetRtTopoMsg (cache);
5525     if (rtt_Polygonize ((RTT_TOPOLOGY *) (topo->rtt_topology)) == 0)
5526 	return 1;
5527     return 0;
5528 }
5529 
5530 GAIATOPO_DECLARE gaiaGeomCollPtr
gaiaTopoSnap(GaiaTopologyAccessorPtr accessor,gaiaGeomCollPtr geom,double tolerance_snap,double tolerance_removal,int iterate)5531 gaiaTopoSnap (GaiaTopologyAccessorPtr accessor, gaiaGeomCollPtr geom,
5532 	      double tolerance_snap, double tolerance_removal, int iterate)
5533 {
5534 /* RTT wrapper - TopoSnap */
5535     const RTCTX *ctx = NULL;
5536     struct splite_internal_cache *cache = NULL;
5537     RTGEOM *input = NULL;
5538     RTGEOM *result = NULL;
5539     gaiaGeomCollPtr output;
5540     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5541     if (topo == NULL)
5542 	return 0;
5543 
5544     cache = (struct splite_internal_cache *) topo->cache;
5545     if (cache == NULL)
5546 	return NULL;
5547     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
5548 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
5549 	return NULL;
5550     ctx = cache->RTTOPO_handle;
5551     if (ctx == NULL)
5552 	return NULL;
5553     if (!geom)
5554 	return NULL;
5555 
5556     input = toRTGeom (ctx, geom);
5557     if (!input)
5558 	return NULL;
5559 
5560     if (tolerance_snap < 0.0)
5561 	tolerance_snap = topo->tolerance;
5562 
5563     result =
5564 	rtt_tpsnap ((RTT_TOPOLOGY *) (topo->rtt_topology), input,
5565 		    tolerance_snap, tolerance_removal, iterate);
5566     rtgeom_free (ctx, input);
5567     if (result == NULL)
5568 	return NULL;
5569 
5570 /* converting the result as a Gaia Geometry */
5571     output = fromRTGeom (ctx, result, geom->DimensionModel, geom->DeclaredType);
5572     output->Srid = geom->Srid;
5573     rtgeom_free (ctx, result);
5574     return output;
5575 }
5576 
5577 GAIATOPO_DECLARE int
gaiaTopoGeo_AddPolygon(GaiaTopologyAccessorPtr accessor,gaiaPolygonPtr pg,double tolerance,sqlite3_int64 ** face_ids,int * ids_count)5578 gaiaTopoGeo_AddPolygon (GaiaTopologyAccessorPtr accessor, gaiaPolygonPtr pg,
5579 			double tolerance, sqlite3_int64 ** face_ids,
5580 			int *ids_count)
5581 {
5582 /* RTT wrapper - AddPolygon */
5583     const RTCTX *ctx = NULL;
5584     struct splite_internal_cache *cache = NULL;
5585     int ret = 0;
5586     RTT_ELEMID *faceids;
5587     int nfaces;
5588     int i;
5589     sqlite3_int64 *ids;
5590     RTPOLY *rt_polyg;
5591     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5592     if (topo == NULL)
5593 	return 0;
5594 
5595     cache = (struct splite_internal_cache *) topo->cache;
5596     if (cache == NULL)
5597 	return 0;
5598     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
5599 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
5600 	return 0;
5601     ctx = cache->RTTOPO_handle;
5602     if (ctx == NULL)
5603 	return 0;
5604 
5605     rt_polyg =
5606 	gaia_convert_polygon_to_rtpoly (ctx, pg, topo->srid, topo->has_z);
5607 
5608     gaiaResetRtTopoMsg (cache);
5609     faceids =
5610 	rtt_AddPolygon ((RTT_TOPOLOGY *) (topo->rtt_topology), rt_polyg,
5611 			tolerance, &nfaces);
5612 
5613     rtpoly_free (ctx, rt_polyg);
5614     if (faceids != NULL)
5615       {
5616 	  ids = malloc (sizeof (sqlite3_int64) * nfaces);
5617 	  for (i = 0; i < nfaces; i++)
5618 	      ids[i] = faceids[i];
5619 	  *face_ids = ids;
5620 	  *ids_count = nfaces;
5621 	  ret = 1;
5622 	  rtfree (ctx, faceids);
5623       }
5624     return ret;
5625 }
5626 
5627 static void
do_split_line(gaiaGeomCollPtr geom,gaiaDynamicLinePtr dyn)5628 do_split_line (gaiaGeomCollPtr geom, gaiaDynamicLinePtr dyn)
5629 {
5630 /* inserting a new linestring into the collection of split lines */
5631     int points = 0;
5632     int iv = 0;
5633     gaiaPointPtr pt;
5634     gaiaLinestringPtr ln;
5635 
5636     pt = dyn->First;
5637     while (pt != NULL)
5638       {
5639 	  /* counting how many points */
5640 	  points++;
5641 	  pt = pt->Next;
5642       }
5643 
5644     ln = gaiaAddLinestringToGeomColl (geom, points);
5645     pt = dyn->First;
5646     while (pt != NULL)
5647       {
5648 	  /* copying all points */
5649 	  if (ln->DimensionModel == GAIA_XY_Z)
5650 	    {
5651 		gaiaSetPointXYZ (ln->Coords, iv, pt->X, pt->Y, pt->Z);
5652 	    }
5653 	  else if (ln->DimensionModel == GAIA_XY_M)
5654 	    {
5655 		gaiaSetPointXYM (ln->Coords, iv, pt->X, pt->Y, pt->M);
5656 	    }
5657 	  else if (ln->DimensionModel == GAIA_XY_Z_M)
5658 	    {
5659 		gaiaSetPointXYZM (ln->Coords, iv, pt->X, pt->Y, pt->Z, pt->M);
5660 	    }
5661 	  else
5662 	    {
5663 		gaiaSetPoint (ln->Coords, iv, pt->X, pt->Y);
5664 	    }
5665 	  iv++;
5666 	  pt = pt->Next;
5667       }
5668 }
5669 
5670 static void
do_geom_split_line(gaiaGeomCollPtr geom,gaiaLinestringPtr in,int line_max_points,double max_length)5671 do_geom_split_line (gaiaGeomCollPtr geom, gaiaLinestringPtr in,
5672 		    int line_max_points, double max_length)
5673 {
5674 /* splitting a Linestring into a collection of shorter Linestrings */
5675     int iv;
5676     int count = 0;
5677     int split = 0;
5678     double tot_length = 0.0;
5679     gaiaDynamicLinePtr dyn = gaiaAllocDynamicLine ();
5680 
5681     for (iv = 0; iv < in->Points; iv++)
5682       {
5683 	  /* consuming all Points from the input Linestring */
5684 	  double ox;
5685 	  double oy;
5686 	  double x;
5687 	  double y;
5688 	  double z = 0.0;
5689 	  double m = 0.0;
5690 	  if (in->DimensionModel == GAIA_XY_Z)
5691 	    {
5692 		gaiaGetPointXYZ (in->Coords, iv, &x, &y, &z);
5693 	    }
5694 	  else if (in->DimensionModel == GAIA_XY_M)
5695 	    {
5696 		gaiaGetPointXYM (in->Coords, iv, &x, &y, &m);
5697 	    }
5698 	  else if (in->DimensionModel == GAIA_XY_Z_M)
5699 	    {
5700 		gaiaGetPointXYZM (in->Coords, iv, &x, &y, &z, &m);
5701 	    }
5702 	  else
5703 	    {
5704 		gaiaGetPoint (in->Coords, iv, &x, &y);
5705 	    }
5706 
5707 	  split = 0;
5708 	  if (max_length > 0.0)
5709 	    {
5710 		if (tot_length > max_length)
5711 		    split = 1;
5712 	    }
5713 	  if (line_max_points > 0)
5714 	    {
5715 		if (count == line_max_points)
5716 		    split = 1;
5717 	    }
5718 	  if (split && count >= 2)
5719 	    {
5720 		/* line break */
5721 		double oz;
5722 		double om;
5723 		ox = dyn->Last->X;
5724 		oy = dyn->Last->Y;
5725 		if (in->DimensionModel == GAIA_XY_Z
5726 		    || in->DimensionModel == GAIA_XY_Z_M)
5727 		    oz = dyn->Last->Z;
5728 		if (in->DimensionModel == GAIA_XY_M
5729 		    || in->DimensionModel == GAIA_XY_Z_M)
5730 		    om = dyn->Last->M;
5731 		do_split_line (geom, dyn);
5732 		gaiaFreeDynamicLine (dyn);
5733 		dyn = gaiaAllocDynamicLine ();
5734 		/* reinserting the last point */
5735 		if (in->DimensionModel == GAIA_XY_Z)
5736 		    gaiaAppendPointZToDynamicLine (dyn, ox, oy, oz);
5737 		else if (in->DimensionModel == GAIA_XY_M)
5738 		    gaiaAppendPointMToDynamicLine (dyn, ox, oy, om);
5739 		else if (in->DimensionModel == GAIA_XY_Z_M)
5740 		    gaiaAppendPointZMToDynamicLine (dyn, ox, oy, oz, om);
5741 		else
5742 		    gaiaAppendPointToDynamicLine (dyn, ox, oy);
5743 		count = 1;
5744 		tot_length = 0.0;
5745 	    }
5746 
5747 	  /* inserting a point */
5748 	  if (in->DimensionModel == GAIA_XY_Z)
5749 	      gaiaAppendPointZToDynamicLine (dyn, x, y, z);
5750 	  else if (in->DimensionModel == GAIA_XY_M)
5751 	      gaiaAppendPointMToDynamicLine (dyn, x, y, m);
5752 	  else if (in->DimensionModel == GAIA_XY_Z_M)
5753 	      gaiaAppendPointZMToDynamicLine (dyn, x, y, z, m);
5754 	  else
5755 	      gaiaAppendPointToDynamicLine (dyn, x, y);
5756 	  if (count > 0)
5757 	    {
5758 		if (max_length > 0.0)
5759 		  {
5760 		      double dist =
5761 			  sqrt (((ox - x) * (ox - x)) + ((oy - y) * (oy - y)));
5762 		      tot_length += dist;
5763 		  }
5764 	    }
5765 	  ox = x;
5766 	  oy = y;
5767 	  count++;
5768       }
5769 
5770     if (dyn->First != NULL)
5771       {
5772 	  /* flushing the last Line */
5773 	  do_split_line (geom, dyn);
5774       }
5775     gaiaFreeDynamicLine (dyn);
5776 }
5777 
5778 static gaiaGeomCollPtr
do_linearize(gaiaGeomCollPtr geom)5779 do_linearize (gaiaGeomCollPtr geom)
5780 {
5781 /* attempts to transform Polygon Rings into a (multi)linestring */
5782     gaiaGeomCollPtr result;
5783     gaiaLinestringPtr new_ln;
5784     gaiaPolygonPtr pg;
5785     gaiaRingPtr rng;
5786     int iv;
5787     int ib;
5788     double x;
5789     double y;
5790     double m;
5791     double z;
5792     if (!geom)
5793 	return NULL;
5794 
5795     if (geom->DimensionModel == GAIA_XY_Z_M)
5796 	result = gaiaAllocGeomCollXYZM ();
5797     else if (geom->DimensionModel == GAIA_XY_Z)
5798 	result = gaiaAllocGeomCollXYZ ();
5799     else if (geom->DimensionModel == GAIA_XY_M)
5800 	result = gaiaAllocGeomCollXYM ();
5801     else
5802 	result = gaiaAllocGeomColl ();
5803     result->Srid = geom->Srid;
5804 
5805     pg = geom->FirstPolygon;
5806     while (pg)
5807       {
5808 	  /* dissolving any POLYGON as simple LINESTRINGs (rings) */
5809 	  rng = pg->Exterior;
5810 	  new_ln = gaiaAddLinestringToGeomColl (result, rng->Points);
5811 	  for (iv = 0; iv < rng->Points; iv++)
5812 	    {
5813 		/* copying the EXTERIOR RING as LINESTRING */
5814 		if (geom->DimensionModel == GAIA_XY_Z_M)
5815 		  {
5816 		      gaiaGetPointXYZM (rng->Coords, iv, &x, &y, &z, &m);
5817 		      gaiaSetPointXYZM (new_ln->Coords, iv, x, y, z, m);
5818 		  }
5819 		else if (geom->DimensionModel == GAIA_XY_Z)
5820 		  {
5821 		      gaiaGetPointXYZ (rng->Coords, iv, &x, &y, &z);
5822 		      gaiaSetPointXYZ (new_ln->Coords, iv, x, y, z);
5823 		  }
5824 		else if (geom->DimensionModel == GAIA_XY_M)
5825 		  {
5826 		      gaiaGetPointXYM (rng->Coords, iv, &x, &y, &m);
5827 		      gaiaSetPointXYM (new_ln->Coords, iv, x, y, m);
5828 		  }
5829 		else
5830 		  {
5831 		      gaiaGetPoint (rng->Coords, iv, &x, &y);
5832 		      gaiaSetPoint (new_ln->Coords, iv, x, y);
5833 		  }
5834 	    }
5835 	  for (ib = 0; ib < pg->NumInteriors; ib++)
5836 	    {
5837 		rng = pg->Interiors + ib;
5838 		new_ln = gaiaAddLinestringToGeomColl (result, rng->Points);
5839 		for (iv = 0; iv < rng->Points; iv++)
5840 		  {
5841 		      /* copying an INTERIOR RING as LINESTRING */
5842 		      if (geom->DimensionModel == GAIA_XY_Z_M)
5843 			{
5844 			    gaiaGetPointXYZM (rng->Coords, iv, &x, &y, &z, &m);
5845 			    gaiaSetPointXYZM (new_ln->Coords, iv, x, y, z, m);
5846 			}
5847 		      else if (geom->DimensionModel == GAIA_XY_Z)
5848 			{
5849 			    gaiaGetPointXYZ (rng->Coords, iv, &x, &y, &z);
5850 			    gaiaSetPointXYZ (new_ln->Coords, iv, x, y, z);
5851 			}
5852 		      else if (geom->DimensionModel == GAIA_XY_M)
5853 			{
5854 			    gaiaGetPointXYM (rng->Coords, iv, &x, &y, &m);
5855 			    gaiaSetPointXYM (new_ln->Coords, iv, x, y, m);
5856 			}
5857 		      else
5858 			{
5859 			    gaiaGetPoint (rng->Coords, iv, &x, &y);
5860 			    gaiaSetPoint (new_ln->Coords, iv, x, y);
5861 			}
5862 		  }
5863 	    }
5864 	  pg = pg->Next;
5865       }
5866     if (result->FirstLinestring == NULL)
5867       {
5868 	  gaiaFreeGeomColl (result);
5869 	  return NULL;
5870       }
5871     return result;
5872 }
5873 
5874 GAIATOPO_DECLARE gaiaGeomCollPtr
gaiaTopoGeo_SubdivideLines(gaiaGeomCollPtr geom,int line_max_points,double max_length)5875 gaiaTopoGeo_SubdivideLines (gaiaGeomCollPtr geom, int line_max_points,
5876 			    double max_length)
5877 {
5878 /* subdividing a (multi)Linestring into a collection of simpler Linestrings */
5879     gaiaLinestringPtr ln;
5880     gaiaGeomCollPtr result;
5881 
5882     if (geom == NULL)
5883 	return NULL;
5884 
5885     if (geom->FirstPoint != NULL)
5886 	return NULL;
5887     if (geom->FirstLinestring == NULL && geom->FirstPolygon != NULL)
5888 	return NULL;
5889 
5890     if (geom->DimensionModel == GAIA_XY_Z)
5891 	result = gaiaAllocGeomCollXYZ ();
5892     else if (geom->DimensionModel == GAIA_XY_M)
5893 	result = gaiaAllocGeomCollXYM ();
5894     else if (geom->DimensionModel == GAIA_XY_Z_M)
5895 	result = gaiaAllocGeomCollXYZM ();
5896     else
5897 	result = gaiaAllocGeomColl ();
5898     result->Srid = geom->Srid;
5899     result->DeclaredType = GAIA_MULTILINESTRING;
5900     ln = geom->FirstLinestring;
5901     while (ln != NULL)
5902       {
5903 	  do_geom_split_line (result, ln, line_max_points, max_length);
5904 	  ln = ln->Next;
5905       }
5906 
5907     if (geom->FirstPolygon != NULL)
5908       {
5909 	  /* transforming all Polygon Rings into Linestrings */
5910 	  gaiaGeomCollPtr pg_rings = do_linearize (geom);
5911 	  if (pg_rings != NULL)
5912 	    {
5913 		ln = pg_rings->FirstLinestring;
5914 		while (ln != NULL)
5915 		  {
5916 		      do_geom_split_line (result, ln, line_max_points,
5917 					  max_length);
5918 		      ln = ln->Next;
5919 		  }
5920 		gaiaFreeGeomColl (pg_rings);
5921 	    }
5922       }
5923     return result;
5924 }
5925 
5926 static gaiaGeomCollPtr
do_build_failing_point(int srid,int dims,gaiaPointPtr pt)5927 do_build_failing_point (int srid, int dims, gaiaPointPtr pt)
5928 {
5929 /* building a Point geometry */
5930     gaiaGeomCollPtr geom;
5931     if (dims == GAIA_XY_Z)
5932 	geom = gaiaAllocGeomCollXYZ ();
5933     else if (dims == GAIA_XY_M)
5934 	geom = gaiaAllocGeomCollXYM ();
5935     else if (dims == GAIA_XY_Z_M)
5936 	geom = gaiaAllocGeomCollXYZM ();
5937     else
5938 	geom = gaiaAllocGeomColl ();
5939     geom->Srid = srid;
5940     if (dims == GAIA_XY_Z)
5941 	gaiaAddPointToGeomCollXYZ (geom, pt->X, pt->Y, pt->Z);
5942     else if (geom->DimensionModel == GAIA_XY_M)
5943 	gaiaAddPointToGeomCollXYM (geom, pt->X, pt->Y, pt->M);
5944     else if (geom->DimensionModel == GAIA_XY_Z_M)
5945 	gaiaAddPointToGeomCollXYZM (geom, pt->X, pt->Y, pt->Z, pt->M);
5946     else
5947 	gaiaAddPointToGeomColl (geom, pt->X, pt->Y);
5948     return geom;
5949 }
5950 
5951 static gaiaGeomCollPtr
do_build_failing_line(int srid,int dims,gaiaLinestringPtr line)5952 do_build_failing_line (int srid, int dims, gaiaLinestringPtr line)
5953 {
5954 /* building a Linestring geometry */
5955     gaiaGeomCollPtr geom;
5956     gaiaLinestringPtr ln;
5957     if (dims == GAIA_XY_Z)
5958 	geom = gaiaAllocGeomCollXYZ ();
5959     else if (dims == GAIA_XY_M)
5960 	geom = gaiaAllocGeomCollXYM ();
5961     else if (dims == GAIA_XY_Z_M)
5962 	geom = gaiaAllocGeomCollXYZM ();
5963     else
5964 	geom = gaiaAllocGeomColl ();
5965     geom->Srid = srid;
5966     ln = gaiaAddLinestringToGeomColl (geom, line->Points);
5967     gaiaCopyLinestringCoords (ln, line);
5968     return geom;
5969 }
5970 
5971 TOPOLOGY_PRIVATE int
auxtopo_insert_into_topology(GaiaTopologyAccessorPtr accessor,gaiaGeomCollPtr geom,double tolerance,int line_max_points,double max_length,int mode,gaiaGeomCollPtr * failing_geometry)5972 auxtopo_insert_into_topology (GaiaTopologyAccessorPtr accessor,
5973 			      gaiaGeomCollPtr geom, double tolerance,
5974 			      int line_max_points, double max_length, int mode,
5975 			      gaiaGeomCollPtr * failing_geometry)
5976 {
5977 /* processing all individual geometry items */
5978     gaiaPointPtr pt;
5979     gaiaLinestringPtr ln;
5980     gaiaGeomCollPtr g;
5981     gaiaGeomCollPtr split = NULL;
5982     gaiaGeomCollPtr pg_rings;
5983     sqlite3_int64 *ids = NULL;
5984     int ids_count;
5985     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5986 
5987     if (failing_geometry != NULL)
5988 	*failing_geometry = NULL;
5989     if (topo == NULL)
5990 	return 0;
5991 
5992     pt = geom->FirstPoint;
5993     while (pt != NULL)
5994       {
5995 	  /* looping on Point items */
5996 	  if (gaiaTopoGeo_AddPoint (accessor, pt, tolerance) < 0)
5997 	    {
5998 		if (failing_geometry != NULL)
5999 		    *failing_geometry =
6000 			do_build_failing_point (geom->Srid,
6001 						geom->DimensionModel, pt);
6002 		return 0;
6003 	    }
6004 	  pt = pt->Next;
6005       }
6006 
6007     if (line_max_points <= 0 && max_length <= 0.0)
6008 	g = geom;
6009     else
6010       {
6011 	  /* subdividing Linestrings */
6012 	  split =
6013 	      gaiaTopoGeo_SubdivideLines (geom, line_max_points, max_length);
6014 	  if (split != NULL)
6015 	      g = split;
6016 	  else
6017 	      g = geom;
6018       }
6019     ln = g->FirstLinestring;
6020     while (ln != NULL)
6021       {
6022 	  /* looping on Linestrings items */
6023 	  int ret;
6024 	  if (mode == GAIA_MODE_TOPO_NO_FACE)
6025 	      ret = gaiaTopoGeo_AddLineStringNoFace
6026 		  (accessor, ln, tolerance, &ids, &ids_count);
6027 	  else
6028 	      ret = gaiaTopoGeo_AddLineString
6029 		  (accessor, ln, tolerance, &ids, &ids_count);
6030 	  if (ret == 0)
6031 	    {
6032 		if (failing_geometry != NULL)
6033 		    *failing_geometry =
6034 			do_build_failing_line (geom->Srid, geom->DimensionModel,
6035 					       ln);
6036 		if (ids != NULL)
6037 		    free (ids);
6038 		if (split != NULL)
6039 		    gaiaFreeGeomColl (split);
6040 		return 0;
6041 	    }
6042 	  if (ids != NULL)
6043 	      free (ids);
6044 	  ln = ln->Next;
6045       }
6046     if (split != NULL)
6047 	gaiaFreeGeomColl (split);
6048     split = NULL;
6049 
6050 /* transforming all Polygon Rings into Linestrings */
6051     pg_rings = do_linearize (geom);
6052     if (pg_rings != NULL)
6053       {
6054 	  if (line_max_points <= 0 && max_length <= 0.0)
6055 	      g = pg_rings;
6056 	  else
6057 	    {
6058 		/* subdividing Linestrings */
6059 		split =
6060 		    gaiaTopoGeo_SubdivideLines (pg_rings, line_max_points,
6061 						max_length);
6062 		if (split != NULL)
6063 		    g = split;
6064 		else
6065 		    g = pg_rings;
6066 	    }
6067 	  ln = g->FirstLinestring;
6068 	  while (ln != NULL)
6069 	    {
6070 		/* looping on Linestrings items */
6071 		int ret;
6072 		if (mode == GAIA_MODE_TOPO_NO_FACE)
6073 		    ret = gaiaTopoGeo_AddLineStringNoFace
6074 			(accessor, ln, tolerance, &ids, &ids_count);
6075 		else
6076 		    ret = gaiaTopoGeo_AddLineString
6077 			(accessor, ln, tolerance, &ids, &ids_count);
6078 		if (ret == 0)
6079 		  {
6080 		      if (failing_geometry != NULL)
6081 			  *failing_geometry =
6082 			      do_build_failing_line (geom->Srid,
6083 						     geom->DimensionModel, ln);
6084 		      if (ids != NULL)
6085 			  free (ids);
6086 		      gaiaFreeGeomColl (pg_rings);
6087 		      if (split != NULL)
6088 			  gaiaFreeGeomColl (split);
6089 		      return 0;
6090 		  }
6091 		if (ids != NULL)
6092 		    free (ids);
6093 		ln = ln->Next;
6094 	    }
6095 	  gaiaFreeGeomColl (pg_rings);
6096 	  if (split != NULL)
6097 	      gaiaFreeGeomColl (split);
6098 	  split = NULL;
6099       }
6100 
6101     return 1;
6102 }
6103 
6104 #endif /* end ENABLE_RTTOPO conditionals */
6105 
6106 SPATIALITE_PRIVATE void
finalize_topologies(const void * p_cache)6107 finalize_topologies (const void *p_cache)
6108 {
6109 /* finalizing all topology related prepared statements */
6110     struct splite_internal_cache *cache =
6111 	(struct splite_internal_cache *) p_cache;
6112     if (cache == NULL)
6113 	return;
6114 
6115 #ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
6116     finalize_all_topo_prepared_stmts (p_cache);
6117 #endif /* end ENABLE_RTTOPO conditionals */
6118 }
6119