1 /*
2 
3  gaia_auxtopo_table.c -- implementation of the Topology module
4                          methods processing a whole GeoTable
5 
6  version 5.0, 2020 August 1
7 
8  Author: Sandro Furieri a.furieri@lqt.it
9 
10  -----------------------------------------------------------------------------
11 
12  Version: MPL 1.1/GPL 2.0/LGPL 2.1
13 
14  The contents of this file are subject to the Mozilla Public License Version
15  1.1 (the "License"); you may not use this file except in compliance with
16  the License. You may obtain a copy of the License at
17  http://www.mozilla.org/MPL/
18 
19 Software distributed under the License is distributed on an "AS IS" basis,
20 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
21 for the specific language governing rights and limitations under the
22 License.
23 
24 The Original Code is the SpatiaLite library
25 
26 The Initial Developer of the Original Code is Alessandro Furieri
27 
28 Portions created by the Initial Developer are Copyright (C) 2015-2021
29 the Initial Developer. All Rights Reserved.
30 
31 Contributor(s):
32 
33 Alternatively, the contents of this file may be used under the terms of
34 either the GNU General Public License Version 2 or later (the "GPL"), or
35 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
36 in which case the provisions of the GPL or the LGPL are applicable instead
37 of those above. If you wish to allow use of your version of this file only
38 under the terms of either the GPL or the LGPL, and not to allow others to
39 use your version of this file under the terms of the MPL, indicate your
40 decision by deleting the provisions above and replace them with the notice
41 and other provisions required by the GPL or the LGPL. If you do not delete
42 the provisions above, a recipient may use your version of this file under
43 the terms of any one of the MPL, the GPL or the LGPL.
44 
45 */
46 
47 /*
48 
49 CREDITS:
50 
51 this module has been completely funded by:
52 Regione Toscana - Settore Sistema Informativo Territoriale ed Ambientale
53 (Topology support)
54 
55 CIG: 6038019AE5
56 
57 */
58 
59 #include <stdlib.h>
60 #include <stdio.h>
61 #include <string.h>
62 #include <float.h>
63 #include <math.h>
64 
65 #if defined(_WIN32) && !defined(__MINGW32__)
66 #include "config-msvc.h"
67 #else
68 #include "config.h"
69 #endif
70 
71 #ifdef ENABLE_RTTOPO		/* only if RTTOPO is enabled */
72 
73 #include <spatialite/sqlite.h>
74 #include <spatialite/debug.h>
75 #include <spatialite/gaiageo.h>
76 #include <spatialite/gaia_topology.h>
77 #include <spatialite/gaia_network.h>
78 #include <spatialite/gaiaaux.h>
79 
80 #include <spatialite.h>
81 #include <spatialite_private.h>
82 
83 #include <librttopo.h>
84 
85 #include <lwn_network.h>
86 
87 #include "topology_private.h"
88 #include "network_private.h"
89 
90 #ifdef _WIN32
91 #define strcasecmp	_stricmp
92 #endif /* not WIN32 */
93 
94 #define GAIA_UNUSED() if (argc || argv) argc = argc;
95 
96 GAIATOPO_DECLARE int
gaiaTopoGeo_FromGeoTable(GaiaTopologyAccessorPtr accessor,const char * db_prefix,const char * table,const char * column,double tolerance,int line_max_points,double max_length)97 gaiaTopoGeo_FromGeoTable (GaiaTopologyAccessorPtr accessor,
98 			  const char *db_prefix, const char *table,
99 			  const char *column, double tolerance,
100 			  int line_max_points, double max_length)
101 {
102 /* attempting to import a whole GeoTable into a Topology-Geometry */
103     struct gaia_topology *topo = (struct gaia_topology *) accessor;
104     sqlite3_stmt *stmt = NULL;
105     int ret;
106     char *sql;
107     char *xprefix;
108     char *xtable;
109     char *xcolumn;
110     int gpkg_amphibious = 0;
111     int gpkg_mode = 0;
112 
113     if (topo == NULL)
114 	return 0;
115     if (topo->cache != NULL)
116       {
117 	  struct splite_internal_cache *cache =
118 	      (struct splite_internal_cache *) (topo->cache);
119 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
120 	  gpkg_mode = cache->gpkg_mode;
121       }
122 
123 /* building the SQL statement */
124     xprefix = gaiaDoubleQuotedSql (db_prefix);
125     xtable = gaiaDoubleQuotedSql (table);
126     xcolumn = gaiaDoubleQuotedSql (column);
127     sql =
128 	sqlite3_mprintf ("SELECT \"%s\" FROM \"%s\".\"%s\"", xcolumn,
129 			 xprefix, xtable);
130     free (xprefix);
131     free (xtable);
132     free (xcolumn);
133     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
134     sqlite3_free (sql);
135     if (ret != SQLITE_OK)
136       {
137 	  char *msg = sqlite3_mprintf ("TopoGeo_FromGeoTable error: \"%s\"",
138 				       sqlite3_errmsg (topo->db_handle));
139 	  gaiatopo_set_last_error_msg (accessor, msg);
140 	  sqlite3_free (msg);
141 	  goto error;
142       }
143 
144 /* setting up the prepared statement */
145     sqlite3_reset (stmt);
146     sqlite3_clear_bindings (stmt);
147 
148     while (1)
149       {
150 	  /* scrolling the result set rows */
151 	  ret = sqlite3_step (stmt);
152 	  if (ret == SQLITE_DONE)
153 	      break;		/* end of result set */
154 	  if (ret == SQLITE_ROW)
155 	    {
156 		if (sqlite3_column_type (stmt, 0) == SQLITE_NULL)
157 		    continue;
158 		if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB)
159 		  {
160 		      const unsigned char *blob = sqlite3_column_blob (stmt, 0);
161 		      int blob_sz = sqlite3_column_bytes (stmt, 0);
162 		      gaiaGeomCollPtr geom =
163 			  gaiaFromSpatiaLiteBlobWkbEx (blob, blob_sz, gpkg_mode,
164 						       gpkg_amphibious);
165 		      if (geom != NULL)
166 			{
167 			    if (!auxtopo_insert_into_topology
168 				(accessor, geom, tolerance, line_max_points,
169 				 max_length, GAIA_MODE_TOPO_FACE, NULL))
170 			      {
171 				  gaiaFreeGeomColl (geom);
172 				  goto error;
173 			      }
174 			    gaiaFreeGeomColl (geom);
175 			}
176 		      else
177 			{
178 			    char *msg =
179 				sqlite3_mprintf
180 				("TopoGeo_FromGeoTable error: Invalid Geometry");
181 			    gaiatopo_set_last_error_msg (accessor, msg);
182 			    sqlite3_free (msg);
183 			    goto error;
184 			}
185 		  }
186 		else
187 		  {
188 		      char *msg =
189 			  sqlite3_mprintf
190 			  ("TopoGeo_FromGeoTable error: not a BLOB value");
191 		      gaiatopo_set_last_error_msg (accessor, msg);
192 		      sqlite3_free (msg);
193 		      goto error;
194 		  }
195 	    }
196 	  else
197 	    {
198 		char *msg =
199 		    sqlite3_mprintf ("TopoGeo_FromGeoTable error: \"%s\"",
200 				     sqlite3_errmsg (topo->db_handle));
201 		gaiatopo_set_last_error_msg (accessor, msg);
202 		sqlite3_free (msg);
203 		goto error;
204 	    }
205       }
206 
207     sqlite3_finalize (stmt);
208     return 1;
209 
210   error:
211     if (stmt != NULL)
212 	sqlite3_finalize (stmt);
213     return 0;
214 }
215 
216 GAIATOPO_DECLARE int
gaiaTopoGeo_FromGeoTableNoFace(GaiaTopologyAccessorPtr accessor,const char * db_prefix,const char * table,const char * column,double tolerance,int line_max_points,double max_length)217 gaiaTopoGeo_FromGeoTableNoFace (GaiaTopologyAccessorPtr accessor,
218 				const char *db_prefix, const char *table,
219 				const char *column, double tolerance,
220 				int line_max_points, double max_length)
221 {
222 /* attempting to import a whole GeoTable into a Topology-Geometry
223 /  without determining generated faces */
224     struct gaia_topology *topo = (struct gaia_topology *) accessor;
225     sqlite3_stmt *stmt = NULL;
226     int ret;
227     char *sql;
228     char *xprefix;
229     char *xtable;
230     char *xcolumn;
231     int gpkg_amphibious = 0;
232     int gpkg_mode = 0;
233 
234     if (topo == NULL)
235 	return 0;
236     if (topo->cache != NULL)
237       {
238 	  struct splite_internal_cache *cache =
239 	      (struct splite_internal_cache *) (topo->cache);
240 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
241 	  gpkg_mode = cache->gpkg_mode;
242       }
243 
244 /* building the SQL statement */
245     xprefix = gaiaDoubleQuotedSql (db_prefix);
246     xtable = gaiaDoubleQuotedSql (table);
247     xcolumn = gaiaDoubleQuotedSql (column);
248     sql =
249 	sqlite3_mprintf ("SELECT \"%s\" FROM \"%s\".\"%s\"", xcolumn,
250 			 xprefix, xtable);
251     free (xprefix);
252     free (xtable);
253     free (xcolumn);
254     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
255     sqlite3_free (sql);
256     if (ret != SQLITE_OK)
257       {
258 	  char *msg =
259 	      sqlite3_mprintf ("TopoGeo_FromGeoTableNoFace error: \"%s\"",
260 			       sqlite3_errmsg (topo->db_handle));
261 	  gaiatopo_set_last_error_msg (accessor, msg);
262 	  sqlite3_free (msg);
263 	  goto error;
264       }
265 
266 /* setting up the prepared statement */
267     sqlite3_reset (stmt);
268     sqlite3_clear_bindings (stmt);
269 
270     while (1)
271       {
272 	  /* scrolling the result set rows */
273 	  ret = sqlite3_step (stmt);
274 	  if (ret == SQLITE_DONE)
275 	      break;		/* end of result set */
276 	  if (ret == SQLITE_ROW)
277 	    {
278 		if (sqlite3_column_type (stmt, 0) == SQLITE_NULL)
279 		    continue;
280 		if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB)
281 		  {
282 		      const unsigned char *blob = sqlite3_column_blob (stmt, 0);
283 		      int blob_sz = sqlite3_column_bytes (stmt, 0);
284 		      gaiaGeomCollPtr geom =
285 			  gaiaFromSpatiaLiteBlobWkbEx (blob, blob_sz, gpkg_mode,
286 						       gpkg_amphibious);
287 		      if (geom != NULL)
288 			{
289 			    if (!auxtopo_insert_into_topology
290 				(accessor, geom, tolerance, line_max_points,
291 				 max_length, GAIA_MODE_TOPO_NO_FACE, NULL))
292 			      {
293 				  gaiaFreeGeomColl (geom);
294 				  goto error;
295 			      }
296 			    gaiaFreeGeomColl (geom);
297 			}
298 		      else
299 			{
300 			    char *msg =
301 				sqlite3_mprintf
302 				("TopoGeo_FromGeoTableNoFace error: Invalid Geometry");
303 			    gaiatopo_set_last_error_msg (accessor, msg);
304 			    sqlite3_free (msg);
305 			    goto error;
306 			}
307 		  }
308 		else
309 		  {
310 		      char *msg =
311 			  sqlite3_mprintf
312 			  ("TopoGeo_FromGeoTableNoFace error: not a BLOB value");
313 		      gaiatopo_set_last_error_msg (accessor, msg);
314 		      sqlite3_free (msg);
315 		      goto error;
316 		  }
317 	    }
318 	  else
319 	    {
320 		char *msg =
321 		    sqlite3_mprintf ("TopoGeo_FromGeoTableNoFace error: \"%s\"",
322 				     sqlite3_errmsg (topo->db_handle));
323 		gaiatopo_set_last_error_msg (accessor, msg);
324 		sqlite3_free (msg);
325 		goto error;
326 	    }
327       }
328 
329     sqlite3_finalize (stmt);
330     return 1;
331 
332   error:
333     if (stmt != NULL)
334 	sqlite3_finalize (stmt);
335     return 0;
336 }
337 
338 static int
insert_into_dustbin(sqlite3 * sqlite,const void * cache,sqlite3_stmt * stmt_dustbin,sqlite3_int64 pk_value,const char * message,double tolerance,int * count,gaiaGeomCollPtr geom)339 insert_into_dustbin (sqlite3 * sqlite, const void *cache,
340 		     sqlite3_stmt * stmt_dustbin, sqlite3_int64 pk_value,
341 		     const char *message, double tolerance, int *count,
342 		     gaiaGeomCollPtr geom)
343 {
344 /* failing feature: inserting a reference into the dustbin table */
345     int ret;
346 
347     start_topo_savepoint (sqlite, cache);
348     sqlite3_reset (stmt_dustbin);
349     sqlite3_clear_bindings (stmt_dustbin);
350 /* binding the Primary Key */
351     sqlite3_bind_int64 (stmt_dustbin, 1, pk_value);
352 /* binding the error message */
353     sqlite3_bind_text (stmt_dustbin, 2, message, strlen (message),
354 		       SQLITE_STATIC);
355 /* binding the tolerance value */
356     sqlite3_bind_double (stmt_dustbin, 3, tolerance);
357 /* binding the failing geometry */
358     if (geom == NULL)
359 	sqlite3_bind_null (stmt_dustbin, 4);
360     else
361       {
362 	  unsigned char *blob = NULL;
363 	  int blob_size = 0;
364 	  gaiaToSpatiaLiteBlobWkb (geom, &blob, &blob_size);
365 	  if (blob == NULL)
366 	      sqlite3_bind_null (stmt_dustbin, 4);
367 	  else
368 	      sqlite3_bind_blob (stmt_dustbin, 4, blob, blob_size, free);
369       }
370     ret = sqlite3_step (stmt_dustbin);
371 
372 /* inserting the row into the dustbin table */
373     if (ret == SQLITE_DONE || ret == SQLITE_ROW)
374       {
375 	  release_topo_savepoint (sqlite, cache);
376 	  *count += 1;
377 	  return 1;
378       }
379 
380     /* some unexpected error occurred */
381     spatialite_e ("TopoGeo_FromGeoTableExt error: \"%s\"",
382 		  sqlite3_errmsg (sqlite));
383     rollback_topo_savepoint (sqlite, cache);
384     return 0;
385 }
386 
387 static int
do_FromGeoTableExtended_block(GaiaTopologyAccessorPtr accessor,sqlite3_stmt * stmt,sqlite3_stmt * stmt_dustbin,double tolerance,int line_max_points,double max_length,sqlite3_int64 start,sqlite3_int64 * last,sqlite3_int64 * invalid,int * dustbin_count,sqlite3_int64 * dustbin_row,int mode)388 do_FromGeoTableExtended_block (GaiaTopologyAccessorPtr accessor,
389 			       sqlite3_stmt * stmt, sqlite3_stmt * stmt_dustbin,
390 			       double tolerance, int line_max_points,
391 			       double max_length, sqlite3_int64 start,
392 			       sqlite3_int64 * last, sqlite3_int64 * invalid,
393 			       int *dustbin_count, sqlite3_int64 * dustbin_row,
394 			       int mode)
395 {
396 /* attempting to import a whole block of input features */
397     struct gaia_topology *topo = (struct gaia_topology *) accessor;
398     int ret;
399     int gpkg_amphibious = 0;
400     int gpkg_mode = 0;
401     int totcnt = 0;
402     sqlite3_int64 last_rowid;
403 
404     if (topo->cache != NULL)
405       {
406 	  struct splite_internal_cache *cache =
407 	      (struct splite_internal_cache *) (topo->cache);
408 	  gpkg_amphibious = cache->gpkg_amphibious_mode;
409 	  gpkg_mode = cache->gpkg_mode;
410       }
411 
412     start_topo_savepoint (topo->db_handle, topo->cache);
413 
414 /* setting up the prepared statement */
415     sqlite3_reset (stmt);
416     sqlite3_clear_bindings (stmt);
417     sqlite3_bind_int64 (stmt, 1, start);
418 
419     while (1)
420       {
421 	  /* scrolling the result set rows */
422 	  ret = sqlite3_step (stmt);
423 	  if (ret == SQLITE_DONE)
424 	      break;		/* end of result set */
425 	  if (ret == SQLITE_ROW)
426 	    {
427 		sqlite3_int64 rowid = sqlite3_column_int64 (stmt, 0);
428 		int igeo = sqlite3_column_count (stmt) - 1;	/* geometry always corresponds to the last resultset column */
429 		if (rowid == *invalid)
430 		  {
431 		      /* succesfully recovered a previously failing block */
432 		      release_topo_savepoint (topo->db_handle, topo->cache);
433 		      *last = last_rowid;
434 		      return 1;
435 		  }
436 		totcnt++;
437 		if (totcnt > 256)
438 		  {
439 		      /* succesfully imported a full block */
440 		      release_topo_savepoint (topo->db_handle, topo->cache);
441 		      *last = last_rowid;
442 		      return 1;
443 		  }
444 		if (sqlite3_column_type (stmt, igeo) == SQLITE_NULL)
445 		  {
446 		      last_rowid = rowid;
447 		      continue;
448 		  }
449 		if (sqlite3_column_type (stmt, igeo) == SQLITE_BLOB)
450 		  {
451 		      const unsigned char *blob =
452 			  sqlite3_column_blob (stmt, igeo);
453 		      int blob_sz = sqlite3_column_bytes (stmt, igeo);
454 		      gaiaGeomCollPtr geom =
455 			  gaiaFromSpatiaLiteBlobWkbEx (blob, blob_sz, gpkg_mode,
456 						       gpkg_amphibious);
457 		      if (geom != NULL)
458 			{
459 			    gaiaGeomCollPtr failing_geometry = NULL;
460 			    gaiatopo_reset_last_error_msg (accessor);
461 			    if (tolerance < 0.0)
462 				tolerance = topo->tolerance;
463 			    if (!auxtopo_insert_into_topology
464 				(accessor, geom, tolerance, line_max_points,
465 				 max_length, mode, &failing_geometry))
466 			      {
467 				  char *msg;
468 				  const char *rt_msg =
469 				      gaiaGetRtTopoErrorMsg (topo->cache);
470 				  if (rt_msg == NULL)
471 				      msg =
472 					  sqlite3_mprintf
473 					  ("TopoGeo_FromGeoTableExt exception: UNKNOWN reason");
474 				  else
475 				      msg = sqlite3_mprintf ("%s", rt_msg);
476 				  rollback_topo_savepoint (topo->db_handle,
477 							   topo->cache);
478 				  gaiaFreeGeomColl (geom);
479 				  if (tolerance < 0.0)
480 				      tolerance = topo->tolerance;
481 				  if (!insert_into_dustbin
482 				      (topo->db_handle, topo->cache,
483 				       stmt_dustbin, rowid, msg, tolerance,
484 				       dustbin_count, failing_geometry))
485 				    {
486 					sqlite3_free (msg);
487 					goto error;
488 				    }
489 				  sqlite3_free (msg);
490 				  if (failing_geometry != NULL)
491 				      gaiaFreeGeomColl (failing_geometry);
492 				  last_rowid = rowid;
493 				  *invalid = rowid;
494 				  *dustbin_row =
495 				      sqlite3_last_insert_rowid
496 				      (topo->db_handle);
497 				  return 0;
498 			      }
499 			    gaiaFreeGeomColl (geom);
500 			    if (failing_geometry != NULL)
501 				gaiaFreeGeomColl (failing_geometry);
502 			    last_rowid = rowid;
503 			}
504 		      else
505 			{
506 			    rollback_topo_savepoint (topo->db_handle,
507 						     topo->cache);
508 			    if (tolerance < 0.0)
509 				tolerance = topo->tolerance;
510 			    if (!insert_into_dustbin
511 				(topo->db_handle, topo->cache, stmt_dustbin,
512 				 rowid,
513 				 "TopoGeo_FromGeoTableExt error: Invalid Geometry",
514 				 tolerance, dustbin_count, NULL))
515 				goto error;
516 			}
517 		      last_rowid = rowid;
518 		  }
519 		else
520 		  {
521 		      rollback_topo_savepoint (topo->db_handle, topo->cache);
522 		      if (!insert_into_dustbin
523 			  (topo->db_handle, topo->cache, stmt_dustbin, rowid,
524 			   "TopoGeo_FromGeoTableExt error: not a BLOB value",
525 			   tolerance, dustbin_count, NULL))
526 			  goto error;
527 		  }
528 	    }
529 	  else
530 	    {
531 		char *msg =
532 		    sqlite3_mprintf ("TopoGeo_FromGeoTableExt error: \"%s\"",
533 				     sqlite3_errmsg (topo->db_handle));
534 		gaiatopo_set_last_error_msg (accessor, msg);
535 		sqlite3_free (msg);
536 		rollback_topo_savepoint (topo->db_handle, topo->cache);
537 		goto error;
538 	    }
539       }
540 /* eof */
541     release_topo_savepoint (topo->db_handle, topo->cache);
542     return 2;
543 
544   error:
545     return -1;
546 }
547 
548 GAIATOPO_DECLARE int
gaiaTopoGeo_FromGeoTableExtended(GaiaTopologyAccessorPtr accessor,const char * sql_in,const char * sql_out,const char * sql_in2,double tolerance,int line_max_points,double max_length)549 gaiaTopoGeo_FromGeoTableExtended (GaiaTopologyAccessorPtr accessor,
550 				  const char *sql_in, const char *sql_out,
551 				  const char *sql_in2, double tolerance,
552 				  int line_max_points, double max_length)
553 {
554 /* attempting to import a whole GeoTable into a Topology-Geometry - Extended mode */
555     struct gaia_topology *topo = (struct gaia_topology *) accessor;
556     sqlite3_stmt *stmt = NULL;
557     sqlite3_stmt *stmt_dustbin = NULL;
558     sqlite3_stmt *stmt_retry = NULL;
559     int ret;
560     int dustbin_count = 0;
561     sqlite3_int64 start = -1;
562     sqlite3_int64 last;
563     sqlite3_int64 invalid = -1;
564     sqlite3_int64 dustbin_row = -1;
565 
566     if (topo == NULL)
567 	return 0;
568     if (sql_in == NULL)
569 	return 0;
570     if (sql_out == NULL)
571 	return 0;
572 
573 /* building the SQL statement */
574     ret =
575 	sqlite3_prepare_v2 (topo->db_handle, sql_in, strlen (sql_in), &stmt,
576 			    NULL);
577     if (ret != SQLITE_OK)
578       {
579 	  char *msg = sqlite3_mprintf ("TopoGeo_FromGeoTableExt error: \"%s\"",
580 				       sqlite3_errmsg (topo->db_handle));
581 	  gaiatopo_set_last_error_msg (accessor, msg);
582 	  sqlite3_free (msg);
583 	  goto error;
584       }
585 
586 /* building the SQL dustbin statement */
587     ret =
588 	sqlite3_prepare_v2 (topo->db_handle, sql_out, strlen (sql_out),
589 			    &stmt_dustbin, NULL);
590     if (ret != SQLITE_OK)
591       {
592 	  char *msg = sqlite3_mprintf ("TopoGeo_FromGeoTableExt error: \"%s\"",
593 				       sqlite3_errmsg (topo->db_handle));
594 	  gaiatopo_set_last_error_msg (accessor, msg);
595 	  sqlite3_free (msg);
596 	  goto error;
597       }
598 
599 /* building the SQL retry statement */
600     ret =
601 	sqlite3_prepare_v2 (topo->db_handle, sql_in2, strlen (sql_in2),
602 			    &stmt_retry, NULL);
603     if (ret != SQLITE_OK)
604       {
605 	  char *msg = sqlite3_mprintf ("TopoGeo_FromGeoTableExt error: \"%s\"",
606 				       sqlite3_errmsg (topo->db_handle));
607 	  gaiatopo_set_last_error_msg (accessor, msg);
608 	  sqlite3_free (msg);
609 	  goto error;
610       }
611 
612     while (1)
613       {
614 	  /* main loop: attempting to import a block of features */
615 	  ret =
616 	      do_FromGeoTableExtended_block (accessor, stmt, stmt_dustbin,
617 					     tolerance, line_max_points,
618 					     max_length, start, &last, &invalid,
619 					     &dustbin_count, &dustbin_row,
620 					     GAIA_MODE_TOPO_FACE);
621 	  if (ret < 0)		/* some unexpected error occurred */
622 	      goto error;
623 	  if (ret > 1)
624 	    {
625 		/* eof */
626 		break;
627 	    }
628 	  if (ret == 0)
629 	    {
630 		/* found a failing feature; recovering */
631 		ret =
632 		    do_FromGeoTableExtended_block (accessor, stmt, stmt_dustbin,
633 						   tolerance, line_max_points,
634 						   max_length, start, &last,
635 						   &invalid, &dustbin_count,
636 						   &dustbin_row,
637 						   GAIA_MODE_TOPO_FACE);
638 		if (ret != 1)
639 		    goto error;
640 		start = invalid;
641 		invalid = -1;
642 		dustbin_row = -1;
643 		continue;
644 	    }
645 	  start = last;
646 	  invalid = -1;
647 	  dustbin_row = -1;
648       }
649 
650     sqlite3_finalize (stmt);
651     sqlite3_finalize (stmt_dustbin);
652     sqlite3_finalize (stmt_retry);
653     return dustbin_count;
654 
655   error:
656     if (stmt != NULL)
657 	sqlite3_finalize (stmt);
658     if (stmt_dustbin != NULL)
659 	sqlite3_finalize (stmt_dustbin);
660     return -1;
661 }
662 
663 GAIATOPO_DECLARE int
gaiaTopoGeo_FromGeoTableNoFaceExtended(GaiaTopologyAccessorPtr accessor,const char * sql_in,const char * sql_out,const char * sql_in2,double tolerance,int line_max_points,double max_length)664 gaiaTopoGeo_FromGeoTableNoFaceExtended (GaiaTopologyAccessorPtr accessor,
665 					const char *sql_in, const char *sql_out,
666 					const char *sql_in2, double tolerance,
667 					int line_max_points, double max_length)
668 {
669 /* attempting to import a whole GeoTable into a Topology-Geometry
670 /  without determining generated faces - Extended mode */
671     struct gaia_topology *topo = (struct gaia_topology *) accessor;
672     sqlite3_stmt *stmt = NULL;
673     sqlite3_stmt *stmt_dustbin = NULL;
674     sqlite3_stmt *stmt_retry = NULL;
675     int ret;
676     int dustbin_count = 0;
677     sqlite3_int64 start = -1;
678     sqlite3_int64 last;
679     sqlite3_int64 invalid = -1;
680     sqlite3_int64 dustbin_row = -1;
681 
682     if (topo == NULL)
683 	return 0;
684     if (sql_in == NULL)
685 	return 0;
686     if (sql_out == NULL)
687 	return 0;
688 
689 /* building the SQL statement */
690     ret =
691 	sqlite3_prepare_v2 (topo->db_handle, sql_in, strlen (sql_in), &stmt,
692 			    NULL);
693     if (ret != SQLITE_OK)
694       {
695 	  char *msg =
696 	      sqlite3_mprintf ("TopoGeo_FromGeoTableNoFaceExt error: \"%s\"",
697 			       sqlite3_errmsg (topo->db_handle));
698 	  gaiatopo_set_last_error_msg (accessor, msg);
699 	  sqlite3_free (msg);
700 	  goto error;
701       }
702 
703 /* building the SQL dustbin statement */
704     ret =
705 	sqlite3_prepare_v2 (topo->db_handle, sql_out, strlen (sql_out),
706 			    &stmt_dustbin, NULL);
707     if (ret != SQLITE_OK)
708       {
709 	  char *msg =
710 	      sqlite3_mprintf ("TopoGeo_FromGeoTableNoFaceExt error: \"%s\"",
711 			       sqlite3_errmsg (topo->db_handle));
712 	  gaiatopo_set_last_error_msg (accessor, msg);
713 	  sqlite3_free (msg);
714 	  goto error;
715       }
716 
717 /* building the SQL retry statement */
718     ret =
719 	sqlite3_prepare_v2 (topo->db_handle, sql_in2, strlen (sql_in2),
720 			    &stmt_retry, NULL);
721     if (ret != SQLITE_OK)
722       {
723 	  char *msg =
724 	      sqlite3_mprintf ("TopoGeo_FromGeoTableNoFaceExt error: \"%s\"",
725 			       sqlite3_errmsg (topo->db_handle));
726 	  gaiatopo_set_last_error_msg (accessor, msg);
727 	  sqlite3_free (msg);
728 	  goto error;
729       }
730 
731     while (1)
732       {
733 	  /* main loop: attempting to import a block of features */
734 	  ret =
735 	      do_FromGeoTableExtended_block (accessor, stmt, stmt_dustbin,
736 					     tolerance, line_max_points,
737 					     max_length, start, &last, &invalid,
738 					     &dustbin_count, &dustbin_row,
739 					     GAIA_MODE_TOPO_NO_FACE);
740 	  if (ret < 0)		/* some unexpected error occurred */
741 	      goto error;
742 	  if (ret > 1)
743 	    {
744 		/* eof */
745 		break;
746 	    }
747 	  if (ret == 0)
748 	    {
749 		/* found a failing feature; recovering */
750 		ret =
751 		    do_FromGeoTableExtended_block (accessor, stmt, stmt_dustbin,
752 						   tolerance, line_max_points,
753 						   max_length, start, &last,
754 						   &invalid, &dustbin_count,
755 						   &dustbin_row,
756 						   GAIA_MODE_TOPO_NO_FACE);
757 		if (ret != 1)
758 		    goto error;
759 		start = invalid;
760 		invalid = -1;
761 		dustbin_row = -1;
762 		continue;
763 	    }
764 	  start = last;
765 	  invalid = -1;
766 	  dustbin_row = -1;
767       }
768 
769     sqlite3_finalize (stmt);
770     sqlite3_finalize (stmt_dustbin);
771     sqlite3_finalize (stmt_retry);
772     return dustbin_count;
773 
774   error:
775     if (stmt != NULL)
776 	sqlite3_finalize (stmt);
777     if (stmt_dustbin != NULL)
778 	sqlite3_finalize (stmt_dustbin);
779     return -1;
780 }
781 
782 GAIATOPO_DECLARE gaiaGeomCollPtr
gaiaGetEdgeSeed(GaiaTopologyAccessorPtr accessor,sqlite3_int64 edge)783 gaiaGetEdgeSeed (GaiaTopologyAccessorPtr accessor, sqlite3_int64 edge)
784 {
785 /* attempting to get a Point (seed) identifying a Topology Edge */
786     struct gaia_topology *topo = (struct gaia_topology *) accessor;
787     sqlite3_stmt *stmt = NULL;
788     int ret;
789     char *sql;
790     char *table;
791     char *xtable;
792     gaiaGeomCollPtr point = NULL;
793     if (topo == NULL)
794 	return NULL;
795 
796 /* building the SQL statement */
797     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
798     xtable = gaiaDoubleQuotedSql (table);
799     sqlite3_free (table);
800     sql =
801 	sqlite3_mprintf ("SELECT geom FROM MAIN.\"%s\" WHERE edge_id = ?",
802 			 xtable);
803     free (xtable);
804     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
805     sqlite3_free (sql);
806     if (ret != SQLITE_OK)
807       {
808 	  char *msg = sqlite3_mprintf ("GetEdgeSeed error: \"%s\"",
809 				       sqlite3_errmsg (topo->db_handle));
810 	  gaiatopo_set_last_error_msg (accessor, msg);
811 	  sqlite3_free (msg);
812 	  goto error;
813       }
814 
815 /* setting up the prepared statement */
816     sqlite3_reset (stmt);
817     sqlite3_clear_bindings (stmt);
818     sqlite3_bind_int64 (stmt, 1, edge);
819 
820     while (1)
821       {
822 	  /* scrolling the result set rows */
823 	  ret = sqlite3_step (stmt);
824 	  if (ret == SQLITE_DONE)
825 	      break;		/* end of result set */
826 	  if (ret == SQLITE_ROW)
827 	    {
828 		if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB)
829 		  {
830 		      const unsigned char *blob = sqlite3_column_blob (stmt, 0);
831 		      int blob_sz = sqlite3_column_bytes (stmt, 0);
832 		      gaiaGeomCollPtr geom =
833 			  gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
834 		      if (geom != NULL)
835 			{
836 			    int iv;
837 			    double x;
838 			    double y;
839 			    double z = 0.0;
840 			    double m = 0.0;
841 			    gaiaLinestringPtr ln = geom->FirstLinestring;
842 			    if (ln == NULL)
843 			      {
844 				  char *msg =
845 				      sqlite3_mprintf
846 				      ("TopoGeo_GetEdgeSeed error: Invalid Geometry");
847 				  gaiatopo_set_last_error_msg (accessor, msg);
848 				  sqlite3_free (msg);
849 				  gaiaFreeGeomColl (geom);
850 				  goto error;
851 			      }
852 			    if (ln->Points == 2)
853 			      {
854 				  /*
855 				     // special case: if the Edge has only 2 points then
856 				     // the Seed will always be placed on the first point
857 				   */
858 				  iv = 0;
859 			      }
860 			    else
861 			      {
862 				  /* ordinary case: placing the Seed into the middle */
863 				  iv = ln->Points / 2;
864 			      }
865 			    if (ln->DimensionModel == GAIA_XY_Z)
866 			      {
867 				  gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z);
868 			      }
869 			    else if (ln->DimensionModel == GAIA_XY_M)
870 			      {
871 				  gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m);
872 			      }
873 			    else if (ln->DimensionModel == GAIA_XY_Z_M)
874 			      {
875 				  gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z,
876 						    &m);
877 			      }
878 			    else
879 			      {
880 				  gaiaGetPoint (ln->Coords, iv, &x, &y);
881 			      }
882 			    gaiaFreeGeomColl (geom);
883 			    if (topo->has_z)
884 			      {
885 				  point = gaiaAllocGeomCollXYZ ();
886 				  gaiaAddPointToGeomCollXYZ (point, x, y, z);
887 			      }
888 			    else
889 			      {
890 				  point = gaiaAllocGeomColl ();
891 				  gaiaAddPointToGeomColl (point, x, y);
892 			      }
893 			    point->Srid = topo->srid;
894 			}
895 		      else
896 			{
897 			    char *msg =
898 				sqlite3_mprintf
899 				("TopoGeo_GetEdgeSeed error: Invalid Geometry");
900 			    gaiatopo_set_last_error_msg (accessor, msg);
901 			    sqlite3_free (msg);
902 			    goto error;
903 			}
904 		  }
905 		else
906 		  {
907 		      char *msg =
908 			  sqlite3_mprintf
909 			  ("TopoGeo_GetEdgeSeed error: not a BLOB value");
910 		      gaiatopo_set_last_error_msg (accessor, msg);
911 		      sqlite3_free (msg);
912 		      goto error;
913 		  }
914 	    }
915 	  else
916 	    {
917 		char *msg =
918 		    sqlite3_mprintf ("TopoGeo_GetEdgeSeed error: \"%s\"",
919 				     sqlite3_errmsg (topo->db_handle));
920 		gaiatopo_set_last_error_msg (accessor, msg);
921 		sqlite3_free (msg);
922 		goto error;
923 	    }
924       }
925 
926     sqlite3_finalize (stmt);
927     return point;
928 
929   error:
930     if (stmt != NULL)
931 	sqlite3_finalize (stmt);
932     return NULL;
933 }
934 
935 GAIATOPO_DECLARE gaiaGeomCollPtr
gaiaGetFaceSeed(GaiaTopologyAccessorPtr accessor,sqlite3_int64 face)936 gaiaGetFaceSeed (GaiaTopologyAccessorPtr accessor, sqlite3_int64 face)
937 {
938 /* attempting to get a Point (seed) identifying a Topology Face */
939     struct gaia_topology *topo = (struct gaia_topology *) accessor;
940     sqlite3_stmt *stmt = NULL;
941     int ret;
942     char *sql;
943     gaiaGeomCollPtr point = NULL;
944     if (topo == NULL)
945 	return NULL;
946 
947 /* building the SQL statement */
948     sql =
949 	sqlite3_mprintf ("SELECT ST_PointOnSurface(ST_GetFaceGeometry(%Q, ?))",
950 			 topo->topology_name);
951     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
952     sqlite3_free (sql);
953     if (ret != SQLITE_OK)
954       {
955 	  char *msg = sqlite3_mprintf ("GetFaceSeed error: \"%s\"",
956 				       sqlite3_errmsg (topo->db_handle));
957 	  gaiatopo_set_last_error_msg (accessor, msg);
958 	  sqlite3_free (msg);
959 	  goto error;
960       }
961 
962 /* setting up the prepared statement */
963     sqlite3_reset (stmt);
964     sqlite3_clear_bindings (stmt);
965     sqlite3_bind_int64 (stmt, 1, face);
966 
967     while (1)
968       {
969 	  /* scrolling the result set rows */
970 	  ret = sqlite3_step (stmt);
971 	  if (ret == SQLITE_DONE)
972 	      break;		/* end of result set */
973 	  if (ret == SQLITE_ROW)
974 	    {
975 		if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB)
976 		  {
977 		      const unsigned char *blob = sqlite3_column_blob (stmt, 0);
978 		      int blob_sz = sqlite3_column_bytes (stmt, 0);
979 		      point = gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
980 		      if (point == NULL)
981 			{
982 			    char *msg =
983 				sqlite3_mprintf
984 				("TopoGeo_GetFaceSeed error: Invalid Geometry");
985 			    gaiatopo_set_last_error_msg (accessor, msg);
986 			    sqlite3_free (msg);
987 			    goto error;
988 			}
989 		  }
990 		else
991 		  {
992 		      char *msg =
993 			  sqlite3_mprintf
994 			  ("TopoGeo_GetFaceSeed error: not a BLOB value");
995 		      gaiatopo_set_last_error_msg (accessor, msg);
996 		      sqlite3_free (msg);
997 		      goto error;
998 		  }
999 	    }
1000 	  else
1001 	    {
1002 		char *msg =
1003 		    sqlite3_mprintf ("TopoGeo_GetFaceSeed error: \"%s\"",
1004 				     sqlite3_errmsg (topo->db_handle));
1005 		gaiatopo_set_last_error_msg (accessor, msg);
1006 		sqlite3_free (msg);
1007 		goto error;
1008 	    }
1009       }
1010 
1011     sqlite3_finalize (stmt);
1012     return point;
1013 
1014   error:
1015     if (stmt != NULL)
1016 	sqlite3_finalize (stmt);
1017     return NULL;
1018 }
1019 
1020 static int
delete_all_seeds(struct gaia_topology * topo)1021 delete_all_seeds (struct gaia_topology *topo)
1022 {
1023 /* deleting all existing Seeds */
1024     char *table;
1025     char *xtable;
1026     char *sql;
1027     char *errMsg;
1028     int ret;
1029 
1030     table = sqlite3_mprintf ("%s_seeds", topo->topology_name);
1031     xtable = gaiaDoubleQuotedSql (table);
1032     sqlite3_free (table);
1033     sql = sqlite3_mprintf ("DELETE FROM MAIN.\"%s\"", xtable);
1034     free (xtable);
1035     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
1036     sqlite3_free (sql);
1037     if (ret != SQLITE_OK)
1038       {
1039 	  char *msg =
1040 	      sqlite3_mprintf ("TopoGeo_UpdateSeeds() error: \"%s\"", errMsg);
1041 	  sqlite3_free (errMsg);
1042 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1043 	  sqlite3_free (msg);
1044 	  return 0;
1045       }
1046     return 1;
1047 }
1048 
1049 static int
update_outdated_edge_seeds(struct gaia_topology * topo)1050 update_outdated_edge_seeds (struct gaia_topology *topo)
1051 {
1052 /* updating all outdated Edge Seeds */
1053     char *table;
1054     char *xseeds;
1055     char *xedges;
1056     char *sql;
1057     int ret;
1058     sqlite3_stmt *stmt_out;
1059     sqlite3_stmt *stmt_in;
1060 
1061 /* preparing the UPDATE statement */
1062     table = sqlite3_mprintf ("%s_seeds", topo->topology_name);
1063     xseeds = gaiaDoubleQuotedSql (table);
1064     sqlite3_free (table);
1065     sql = sqlite3_mprintf ("UPDATE MAIN.\"%s\" SET geom = "
1066 			   "TopoGeo_GetEdgeSeed(%Q, edge_id) WHERE edge_id = ?",
1067 			   xseeds, topo->topology_name);
1068     free (xseeds);
1069     ret =
1070 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_out,
1071 			    NULL);
1072     sqlite3_free (sql);
1073     if (ret != SQLITE_OK)
1074       {
1075 	  char *msg = sqlite3_mprintf ("TopoGeo_UpdateSeeds() error: \"%s\"",
1076 				       sqlite3_errmsg (topo->db_handle));
1077 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1078 	  sqlite3_free (msg);
1079 	  goto error;
1080       }
1081 
1082 /* preparing the SELECT statement */
1083     table = sqlite3_mprintf ("%s_seeds", topo->topology_name);
1084     xseeds = gaiaDoubleQuotedSql (table);
1085     sqlite3_free (table);
1086     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
1087     xedges = gaiaDoubleQuotedSql (table);
1088     sqlite3_free (table);
1089     sql = sqlite3_mprintf ("SELECT s.edge_id FROM MAIN.\"%s\" AS s "
1090 			   "JOIN MAIN.\"%s\" AS e ON (e.edge_id = s.edge_id) "
1091 			   "WHERE s.edge_id IS NOT NULL AND e.timestamp > s.timestamp",
1092 			   xseeds, xedges);
1093     free (xseeds);
1094     free (xedges);
1095     ret =
1096 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
1097     sqlite3_free (sql);
1098     if (ret != SQLITE_OK)
1099       {
1100 	  char *msg = sqlite3_mprintf ("TopoGeo_UpdateSeeds() error: \"%s\"",
1101 				       sqlite3_errmsg (topo->db_handle));
1102 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1103 	  sqlite3_free (msg);
1104 	  goto error;
1105       }
1106 
1107     sqlite3_reset (stmt_in);
1108     sqlite3_clear_bindings (stmt_in);
1109     while (1)
1110       {
1111 	  /* scrolling the result set rows */
1112 	  ret = sqlite3_step (stmt_in);
1113 	  if (ret == SQLITE_DONE)
1114 	      break;		/* end of result set */
1115 	  if (ret == SQLITE_ROW)
1116 	    {
1117 		sqlite3_reset (stmt_out);
1118 		sqlite3_clear_bindings (stmt_out);
1119 		sqlite3_bind_int64 (stmt_out, 1,
1120 				    sqlite3_column_int64 (stmt_in, 0));
1121 		/* updating the Seeds table */
1122 		ret = sqlite3_step (stmt_out);
1123 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
1124 		    ;
1125 		else
1126 		  {
1127 		      char *msg =
1128 			  sqlite3_mprintf
1129 			  ("TopoGeo_UpdateSeeds() error: \"%s\"",
1130 			   sqlite3_errmsg (topo->db_handle));
1131 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
1132 						   topo, msg);
1133 		      sqlite3_free (msg);
1134 		      goto error;
1135 		  }
1136 	    }
1137 	  else
1138 	    {
1139 		char *msg =
1140 		    sqlite3_mprintf ("TopoGeo_UpdateSeeds() error: \"%s\"",
1141 				     sqlite3_errmsg (topo->db_handle));
1142 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
1143 					     msg);
1144 		sqlite3_free (msg);
1145 		goto error;
1146 	    }
1147       }
1148 
1149     sqlite3_finalize (stmt_in);
1150     sqlite3_finalize (stmt_out);
1151     return 1;
1152 
1153   error:
1154     if (stmt_in != NULL)
1155 	sqlite3_finalize (stmt_in);
1156     if (stmt_out != NULL)
1157 	sqlite3_finalize (stmt_out);
1158     return 0;
1159 }
1160 
1161 static int
update_outdated_face_seeds(struct gaia_topology * topo)1162 update_outdated_face_seeds (struct gaia_topology *topo)
1163 {
1164 /* updating all outdated Face Seeds */
1165     char *table;
1166     char *xseeds;
1167     char *xedges;
1168     char *xfaces;
1169     char *sql;
1170     int ret;
1171     sqlite3_stmt *stmt_out;
1172     sqlite3_stmt *stmt_in;
1173 
1174 /* preparing the UPDATE statement */
1175     table = sqlite3_mprintf ("%s_seeds", topo->topology_name);
1176     xseeds = gaiaDoubleQuotedSql (table);
1177     sqlite3_free (table);
1178     sql = sqlite3_mprintf ("UPDATE MAIN.\"%s\" SET geom = "
1179 			   "TopoGeo_GetFaceSeed(%Q, face_id) WHERE face_id = ?",
1180 			   xseeds, topo->topology_name);
1181     free (xseeds);
1182     ret =
1183 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_out,
1184 			    NULL);
1185     sqlite3_free (sql);
1186     if (ret != SQLITE_OK)
1187       {
1188 	  char *msg = sqlite3_mprintf ("TopoGeo_UpdateSeeds() error: \"%s\"",
1189 				       sqlite3_errmsg (topo->db_handle));
1190 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1191 	  sqlite3_free (msg);
1192 	  goto error;
1193       }
1194 
1195 /* preparing the SELECT statement */
1196     table = sqlite3_mprintf ("%s_seeds", topo->topology_name);
1197     xseeds = gaiaDoubleQuotedSql (table);
1198     sqlite3_free (table);
1199     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
1200     xedges = gaiaDoubleQuotedSql (table);
1201     sqlite3_free (table);
1202     table = sqlite3_mprintf ("%s_face", topo->topology_name);
1203     xfaces = gaiaDoubleQuotedSql (table);
1204     sqlite3_free (table);
1205     sql = sqlite3_mprintf ("SELECT x.face_id FROM MAIN.\"%s\" AS s, "
1206 			   "(SELECT f.face_id AS face_id, Max(e.timestamp) AS max_tm "
1207 			   "FROM MAIN.\"%s\" AS f "
1208 			   "JOIN MAIN.\"%s\" AS e ON (e.left_face = f.face_id OR e.right_face = f.face_id) "
1209 			   "GROUP BY f.face_id) AS x "
1210 			   "WHERE s.face_id IS NOT NULL AND s.face_id = x.face_id AND x.max_tm > s.timestamp",
1211 			   xseeds, xfaces, xedges);
1212     free (xseeds);
1213     free (xedges);
1214     free (xfaces);
1215     ret =
1216 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
1217     sqlite3_free (sql);
1218     if (ret != SQLITE_OK)
1219       {
1220 	  char *msg = sqlite3_mprintf ("TopoGeo_UpdateSeeds() error: \"%s\"",
1221 				       sqlite3_errmsg (topo->db_handle));
1222 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1223 	  sqlite3_free (msg);
1224 	  goto error;
1225       }
1226 
1227     sqlite3_reset (stmt_in);
1228     sqlite3_clear_bindings (stmt_in);
1229     while (1)
1230       {
1231 	  /* scrolling the result set rows */
1232 	  ret = sqlite3_step (stmt_in);
1233 	  if (ret == SQLITE_DONE)
1234 	      break;		/* end of result set */
1235 	  if (ret == SQLITE_ROW)
1236 	    {
1237 		sqlite3_reset (stmt_out);
1238 		sqlite3_clear_bindings (stmt_out);
1239 		sqlite3_bind_int64 (stmt_out, 1,
1240 				    sqlite3_column_int64 (stmt_in, 0));
1241 		/* updating the Seeds table */
1242 		ret = sqlite3_step (stmt_out);
1243 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
1244 		    ;
1245 		else
1246 		  {
1247 		      char *msg =
1248 			  sqlite3_mprintf
1249 			  ("TopoGeo_UpdateSeeds() error: \"%s\"",
1250 			   sqlite3_errmsg (topo->db_handle));
1251 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
1252 						   topo, msg);
1253 		      sqlite3_free (msg);
1254 		      goto error;
1255 		  }
1256 	    }
1257 	  else
1258 	    {
1259 		char *msg =
1260 		    sqlite3_mprintf ("TopoGeo_UpdateSeeds() error: \"%s\"",
1261 				     sqlite3_errmsg (topo->db_handle));
1262 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
1263 					     msg);
1264 		sqlite3_free (msg);
1265 		goto error;
1266 	    }
1267       }
1268 
1269     sqlite3_finalize (stmt_in);
1270     sqlite3_finalize (stmt_out);
1271     return 1;
1272 
1273   error:
1274     if (stmt_in != NULL)
1275 	sqlite3_finalize (stmt_in);
1276     if (stmt_out != NULL)
1277 	sqlite3_finalize (stmt_out);
1278     return 0;
1279 }
1280 
1281 GAIATOPO_DECLARE int
gaiaTopoGeoUpdateSeeds(GaiaTopologyAccessorPtr accessor,int incremental_mode)1282 gaiaTopoGeoUpdateSeeds (GaiaTopologyAccessorPtr accessor, int incremental_mode)
1283 {
1284 /* updating all TopoGeo Seeds */
1285     char *table;
1286     char *xseeds;
1287     char *xedges;
1288     char *xfaces;
1289     char *sql;
1290     char *errMsg;
1291     int ret;
1292     struct gaia_topology *topo = (struct gaia_topology *) accessor;
1293     if (topo == NULL)
1294 	return 0;
1295 
1296     if (!incremental_mode)
1297       {
1298 	  /* deleting all existing Seeds */
1299 	  if (!delete_all_seeds (topo))
1300 	      return 0;
1301       }
1302 
1303 /* paranoid precaution: deleting all orphan Edge Seeds */
1304     table = sqlite3_mprintf ("%s_seeds", topo->topology_name);
1305     xseeds = gaiaDoubleQuotedSql (table);
1306     sqlite3_free (table);
1307     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
1308     xedges = gaiaDoubleQuotedSql (table);
1309     sqlite3_free (table);
1310     sql = sqlite3_mprintf ("DELETE FROM MAIN.\"%s\" WHERE edge_id IN ("
1311 			   "SELECT s.edge_id FROM MAIN.\"%s\" AS s "
1312 			   "LEFT JOIN MAIN.\"%s\" AS e ON (s.edge_id = e.edge_id) "
1313 			   "WHERE s.edge_id IS NOT NULL AND e.edge_id IS NULL)",
1314 			   xseeds, xseeds, xedges);
1315     free (xseeds);
1316     free (xedges);
1317     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
1318     sqlite3_free (sql);
1319     if (ret != SQLITE_OK)
1320       {
1321 	  char *msg =
1322 	      sqlite3_mprintf ("TopoGeo_UpdateSeeds() error: \"%s\"", errMsg);
1323 	  sqlite3_free (errMsg);
1324 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1325 	  sqlite3_free (msg);
1326 	  return 0;
1327       }
1328 
1329 /* paranoid precaution: deleting all orphan Face Seeds */
1330     table = sqlite3_mprintf ("%s_seeds", topo->topology_name);
1331     xseeds = gaiaDoubleQuotedSql (table);
1332     sqlite3_free (table);
1333     table = sqlite3_mprintf ("%s_face", topo->topology_name);
1334     xfaces = gaiaDoubleQuotedSql (table);
1335     sqlite3_free (table);
1336     sql = sqlite3_mprintf ("DELETE FROM MAIN.\"%s\" WHERE face_id IN ("
1337 			   "SELECT s.face_id FROM MAIN.\"%s\" AS s "
1338 			   "LEFT JOIN MAIN.\"%s\" AS f ON (s.face_id = f.face_id) "
1339 			   "WHERE s.face_id IS NOT NULL AND f.face_id IS NULL)",
1340 			   xseeds, xseeds, xfaces);
1341     free (xseeds);
1342     free (xfaces);
1343     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
1344     sqlite3_free (sql);
1345     if (ret != SQLITE_OK)
1346       {
1347 	  char *msg =
1348 	      sqlite3_mprintf ("TopoGeo_UpdateSeeds() error: \"%s\"", errMsg);
1349 	  sqlite3_free (errMsg);
1350 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1351 	  sqlite3_free (msg);
1352 	  return 0;
1353       }
1354 
1355 /* updating all outdated Edge Seeds */
1356     if (!update_outdated_edge_seeds (topo))
1357 	return 0;
1358 
1359 /* updating all outdated Facee Seeds */
1360     if (!update_outdated_face_seeds (topo))
1361 	return 0;
1362 
1363 /* inserting all missing Edge Seeds */
1364     table = sqlite3_mprintf ("%s_seeds", topo->topology_name);
1365     xseeds = gaiaDoubleQuotedSql (table);
1366     sqlite3_free (table);
1367     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
1368     xedges = gaiaDoubleQuotedSql (table);
1369     sqlite3_free (table);
1370     sql =
1371 	sqlite3_mprintf
1372 	("INSERT INTO MAIN.\"%s\" (seed_id, edge_id, face_id, geom) "
1373 	 "SELECT NULL, e.edge_id, NULL, TopoGeo_GetEdgeSeed(%Q, e.edge_id) "
1374 	 "FROM MAIN.\"%s\" AS e "
1375 	 "LEFT JOIN MAIN.\"%s\" AS s ON (e.edge_id = s.edge_id) WHERE s.edge_id IS NULL",
1376 	 xseeds, topo->topology_name, xedges, xseeds);
1377     free (xseeds);
1378     free (xedges);
1379     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
1380     sqlite3_free (sql);
1381     if (ret != SQLITE_OK)
1382       {
1383 	  char *msg =
1384 	      sqlite3_mprintf ("TopoGeo_UpdateSeeds() error: \"%s\"", errMsg);
1385 	  sqlite3_free (errMsg);
1386 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1387 	  sqlite3_free (msg);
1388 	  return 0;
1389       }
1390 
1391     /* inserting all missing Face Seeds */
1392     table = sqlite3_mprintf ("%s_seeds", topo->topology_name);
1393     xseeds = gaiaDoubleQuotedSql (table);
1394     sqlite3_free (table);
1395     table = sqlite3_mprintf ("%s_face", topo->topology_name);
1396     xfaces = gaiaDoubleQuotedSql (table);
1397     sqlite3_free (table);
1398     sql =
1399 	sqlite3_mprintf
1400 	("INSERT INTO MAIN.\"%s\" (seed_id, edge_id, face_id, geom) "
1401 	 "SELECT NULL, NULL, f.face_id, TopoGeo_GetFaceSeed(%Q, f.face_id) "
1402 	 "FROM MAIN.\"%s\" AS f "
1403 	 "LEFT JOIN MAIN.\"%s\" AS s ON (f.face_id = s.face_id) "
1404 	 "WHERE s.face_id IS NULL AND f.face_id <> 0", xseeds,
1405 	 topo->topology_name, xfaces, xseeds);
1406     free (xseeds);
1407     free (xfaces);
1408     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
1409     sqlite3_free (sql);
1410     if (ret != SQLITE_OK)
1411       {
1412 	  char *msg =
1413 	      sqlite3_mprintf ("TopoGeo_UpdateSeeds() error: \"%s\"", errMsg);
1414 	  sqlite3_free (errMsg);
1415 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1416 	  sqlite3_free (msg);
1417 	  return 0;
1418       }
1419 
1420     return 1;
1421 }
1422 
1423 GAIATOPO_DECLARE gaiaGeomCollPtr
gaiaTopoGeoSnapPointToSeed(GaiaTopologyAccessorPtr accessor,gaiaGeomCollPtr pt,double distance)1424 gaiaTopoGeoSnapPointToSeed (GaiaTopologyAccessorPtr accessor,
1425 			    gaiaGeomCollPtr pt, double distance)
1426 {
1427 /* snapping a Point to TopoSeeds */
1428     char *sql;
1429     char *table;
1430     char *xtable;
1431     sqlite3_stmt *stmt = NULL;
1432     sqlite3_stmt *stmt_snap = NULL;
1433     int ret;
1434     unsigned char *blob;
1435     int blob_size;
1436     unsigned char *blob2;
1437     int blob_size2;
1438     gaiaGeomCollPtr result = NULL;
1439     struct gaia_topology *topo = (struct gaia_topology *) accessor;
1440     if (topo == NULL)
1441 	return NULL;
1442 
1443 /* preparing the Snap statement */
1444     sql = "SELECT ST_Snap(?, ?, ?)";
1445     ret =
1446 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_snap,
1447 			    NULL);
1448     if (ret != SQLITE_OK)
1449       {
1450 	  char *msg =
1451 	      sqlite3_mprintf ("TopoGeo_SnapPointToSeed() error: \"%s\"",
1452 			       sqlite3_errmsg (topo->db_handle));
1453 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1454 	  sqlite3_free (msg);
1455 	  goto error;
1456       }
1457 
1458 /* preparing the SELECT statement */
1459     table = sqlite3_mprintf ("%s_node", topo->topology_name);
1460     xtable = gaiaDoubleQuotedSql (table);
1461     sql = sqlite3_mprintf ("SELECT geom "
1462 			   "FROM \"%s\" WHERE ST_Distance(?, geom) <= ? AND rowid IN "
1463 			   "(SELECT rowid FROM SpatialIndex WHERE f_table_name = %Q AND search_frame = ST_Buffer(?, ?))",
1464 			   xtable, table);
1465     free (xtable);
1466     sqlite3_free (table);
1467     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
1468     sqlite3_free (sql);
1469     if (ret != SQLITE_OK)
1470       {
1471 	  char *msg =
1472 	      sqlite3_mprintf ("TopoGeo_SnapPointToSeed() error: \"%s\"",
1473 			       sqlite3_errmsg (topo->db_handle));
1474 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1475 	  sqlite3_free (msg);
1476 	  goto error;
1477       }
1478 
1479 /* querying Seeds */
1480     if (topo->has_z)
1481 	result = gaiaAllocGeomCollXYZ ();
1482     else
1483 	result = gaiaAllocGeomColl ();
1484     result->Srid = pt->Srid;
1485     gaiaToSpatiaLiteBlobWkb (pt, &blob, &blob_size);
1486     gaiaToSpatiaLiteBlobWkb (pt, &blob2, &blob_size2);
1487     sqlite3_reset (stmt);
1488     sqlite3_clear_bindings (stmt);
1489     sqlite3_bind_blob (stmt, 1, blob, blob_size, free);
1490     sqlite3_bind_double (stmt, 2, distance);
1491     sqlite3_bind_blob (stmt, 3, blob2, blob_size2, free);
1492     sqlite3_bind_double (stmt, 4, distance * 1.2);
1493     while (1)
1494       {
1495 	  /* scrolling the result set rows */
1496 	  ret = sqlite3_step (stmt);
1497 	  if (ret == SQLITE_DONE)
1498 	      break;		/* end of result set */
1499 	  if (ret == SQLITE_ROW)
1500 	    {
1501 		const unsigned char *p_blob = sqlite3_column_blob (stmt, 0);
1502 		int blobsz = sqlite3_column_bytes (stmt, 0);
1503 		gaiaGeomCollPtr geom =
1504 		    gaiaFromSpatiaLiteBlobWkb (p_blob, blobsz);
1505 		if (geom != NULL)
1506 		  {
1507 		      gaiaPointPtr pt = geom->FirstPoint;
1508 		      while (pt != NULL)
1509 			{
1510 			    /* copying all Points into the result Geometry */
1511 			    if (topo->has_z)
1512 				gaiaAddPointToGeomCollXYZ (result, pt->X, pt->Y,
1513 							   pt->Z);
1514 			    else
1515 				gaiaAddPointToGeomColl (result, pt->X, pt->Y);
1516 			    pt = pt->Next;
1517 			}
1518 		      gaiaFreeGeomColl (geom);
1519 		  }
1520 	    }
1521 	  else
1522 	    {
1523 		char *msg =
1524 		    sqlite3_mprintf ("TopoGeo_SnapPointToSeed error: \"%s\"",
1525 				     sqlite3_errmsg (topo->db_handle));
1526 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
1527 					     msg);
1528 		sqlite3_free (msg);
1529 		goto error;
1530 	    }
1531       }
1532 
1533     sqlite3_finalize (stmt);
1534     stmt = NULL;
1535     if (result->FirstPoint == NULL)
1536 	goto error;
1537 
1538 /* Snap */
1539     gaiaToSpatiaLiteBlobWkb (pt, &blob, &blob_size);
1540     gaiaToSpatiaLiteBlobWkb (result, &blob2, &blob_size2);
1541     gaiaFreeGeomColl (result);
1542     result = NULL;
1543     sqlite3_reset (stmt_snap);
1544     sqlite3_clear_bindings (stmt_snap);
1545     sqlite3_bind_blob (stmt_snap, 1, blob, blob_size, free);
1546     sqlite3_bind_blob (stmt_snap, 2, blob2, blob_size2, free);
1547     sqlite3_bind_double (stmt_snap, 3, distance);
1548     while (1)
1549       {
1550 	  /* scrolling the result set rows */
1551 	  ret = sqlite3_step (stmt_snap);
1552 
1553 	  if (ret == SQLITE_DONE)
1554 	      break;		/* end of result set */
1555 	  if (ret == SQLITE_ROW)
1556 	    {
1557 		if (sqlite3_column_type (stmt_snap, 0) != SQLITE_NULL)
1558 		  {
1559 		      const unsigned char *p_blob =
1560 			  sqlite3_column_blob (stmt_snap, 0);
1561 		      int blobsz = sqlite3_column_bytes (stmt_snap, 0);
1562 		      if (result != NULL)
1563 			  gaiaFreeGeomColl (result);
1564 		      result = gaiaFromSpatiaLiteBlobWkb (p_blob, blobsz);
1565 		  }
1566 	    }
1567 	  else
1568 	    {
1569 		char *msg =
1570 		    sqlite3_mprintf ("TopoGeo_SnapPointToSeed error: \"%s\"",
1571 				     sqlite3_errmsg (topo->db_handle));
1572 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
1573 					     msg);
1574 		sqlite3_free (msg);
1575 		goto error;
1576 	    }
1577       }
1578     sqlite3_finalize (stmt_snap);
1579     stmt_snap = NULL;
1580     if (result == NULL)
1581 	goto error;
1582     if (result->FirstLinestring != NULL || result->FirstPolygon != NULL)
1583 	goto error;
1584     if (result->FirstPoint == NULL)
1585 	goto error;
1586     if (result->FirstPoint != result->LastPoint)
1587 	goto error;
1588     return result;
1589 
1590   error:
1591     if (stmt != NULL)
1592 	sqlite3_finalize (stmt);
1593     if (stmt_snap != NULL)
1594 	sqlite3_finalize (stmt_snap);
1595     if (result != NULL)
1596 	gaiaFreeGeomColl (result);
1597     return NULL;
1598 }
1599 
1600 GAIATOPO_DECLARE gaiaGeomCollPtr
gaiaTopoGeoSnapLinestringToSeed(GaiaTopologyAccessorPtr accessor,gaiaGeomCollPtr ln,double distance)1601 gaiaTopoGeoSnapLinestringToSeed (GaiaTopologyAccessorPtr accessor,
1602 				 gaiaGeomCollPtr ln, double distance)
1603 {
1604 /* snapping a Linestring to TopoSeeds */
1605     char *sql;
1606     char *table;
1607     char *xtable;
1608     int ret;
1609     unsigned char *blob;
1610     int blob_size;
1611     unsigned char *blob2;
1612     int blob_size2;
1613     gaiaGeomCollPtr result = NULL;
1614     sqlite3_stmt *stmt = NULL;
1615     sqlite3_stmt *stmt_snap = NULL;
1616     struct gaia_topology *topo = (struct gaia_topology *) accessor;
1617     if (topo == NULL)
1618 	return NULL;
1619 
1620 /* preparing the Snap statement */
1621     sql = "SELECT ST_Snap(?, ?, ?)";
1622     ret =
1623 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_snap,
1624 			    NULL);
1625     if (ret != SQLITE_OK)
1626       {
1627 	  char *msg =
1628 	      sqlite3_mprintf ("TopoGeo_SnapLinestringToSeed() error: \"%s\"",
1629 			       sqlite3_errmsg (topo->db_handle));
1630 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1631 	  sqlite3_free (msg);
1632 	  goto error;
1633       }
1634 
1635 /* preparing the SELECT statement */
1636     table = sqlite3_mprintf ("%s_seeds", topo->topology_name);
1637     xtable = gaiaDoubleQuotedSql (table);
1638     sql = sqlite3_mprintf ("SELECT edge_id, geom "
1639 			   "FROM \"%s\" WHERE ST_Distance(?, geom) <= ? AND rowid IN "
1640 			   "(SELECT rowid FROM SpatialIndex WHERE f_table_name = %Q AND search_frame = ST_Buffer(?, ?))",
1641 			   xtable, table);
1642     free (xtable);
1643     sqlite3_free (table);
1644     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
1645     sqlite3_free (sql);
1646     if (ret != SQLITE_OK)
1647       {
1648 	  char *msg =
1649 	      sqlite3_mprintf ("TopoGeo_SnapLinestringToSeed() error: \"%s\"",
1650 			       sqlite3_errmsg (topo->db_handle));
1651 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
1652 	  sqlite3_free (msg);
1653 	  goto error;
1654       }
1655 
1656 /* querying Seeds */
1657     if (topo->has_z)
1658 	result = gaiaAllocGeomCollXYZ ();
1659     else
1660 	result = gaiaAllocGeomColl ();
1661     result->Srid = ln->Srid;
1662     gaiaToSpatiaLiteBlobWkb (ln, &blob, &blob_size);
1663     gaiaToSpatiaLiteBlobWkb (ln, &blob2, &blob_size2);
1664     sqlite3_reset (stmt);
1665     sqlite3_clear_bindings (stmt);
1666     sqlite3_bind_blob (stmt, 1, blob, blob_size, free);
1667     sqlite3_bind_double (stmt, 2, distance);
1668     sqlite3_bind_blob (stmt, 3, blob2, blob_size2, free);
1669     sqlite3_bind_double (stmt, 4, distance * 1.2);
1670     while (1)
1671       {
1672 	  /* scrolling the result set rows */
1673 	  ret = sqlite3_step (stmt);
1674 	  if (ret == SQLITE_DONE)
1675 	      break;		/* end of result set */
1676 	  if (ret == SQLITE_ROW)
1677 	    {
1678 		if (sqlite3_column_type (stmt, 0) != SQLITE_NULL)
1679 		  {
1680 		      const unsigned char *p_blob =
1681 			  sqlite3_column_blob (stmt, 1);
1682 		      int blobsz = sqlite3_column_bytes (stmt, 1);
1683 		      gaiaGeomCollPtr geom =
1684 			  gaiaFromSpatiaLiteBlobWkb (p_blob, blobsz);
1685 		      if (geom != NULL)
1686 			{
1687 			    gaiaPointPtr pt = geom->FirstPoint;
1688 			    while (pt != NULL)
1689 			      {
1690 				  /* copying all Points into the result Geometry */
1691 				  if (topo->has_z)
1692 				      gaiaAddPointToGeomCollXYZ (result, pt->X,
1693 								 pt->Y, pt->Z);
1694 				  else
1695 				      gaiaAddPointToGeomColl (result, pt->X,
1696 							      pt->Y);
1697 				  pt = pt->Next;
1698 			      }
1699 			    gaiaFreeGeomColl (geom);
1700 			}
1701 		  }
1702 	    }
1703 	  else
1704 	    {
1705 		char *msg =
1706 		    sqlite3_mprintf
1707 		    ("TopoGeo_SnapLinestringToSeed error: \"%s\"",
1708 		     sqlite3_errmsg (topo->db_handle));
1709 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
1710 					     msg);
1711 		sqlite3_free (msg);
1712 		goto error;
1713 	    }
1714       }
1715 
1716     sqlite3_finalize (stmt);
1717     stmt = NULL;
1718     if (result->FirstPoint == NULL)
1719 	goto error;
1720 
1721 /* Snap */
1722     gaiaToSpatiaLiteBlobWkb (ln, &blob, &blob_size);
1723     gaiaToSpatiaLiteBlobWkb (result, &blob2, &blob_size2);
1724     gaiaFreeGeomColl (result);
1725     result = NULL;
1726     sqlite3_reset (stmt_snap);
1727     sqlite3_clear_bindings (stmt_snap);
1728     sqlite3_bind_blob (stmt_snap, 1, blob, blob_size, free);
1729     sqlite3_bind_blob (stmt_snap, 2, blob2, blob_size2, free);
1730     sqlite3_bind_double (stmt_snap, 3, distance);
1731     while (1)
1732       {
1733 	  /* scrolling the result set rows */
1734 	  ret = sqlite3_step (stmt_snap);
1735 	  if (ret == SQLITE_DONE)
1736 	      break;		/* end of result set */
1737 	  if (ret == SQLITE_ROW)
1738 	    {
1739 		if (sqlite3_column_type (stmt_snap, 0) == SQLITE_BLOB)
1740 		  {
1741 		      const unsigned char *p_blob =
1742 			  sqlite3_column_blob (stmt_snap, 0);
1743 		      int blobsz = sqlite3_column_bytes (stmt_snap, 0);
1744 		      if (result != NULL)
1745 			  gaiaFreeGeomColl (result);
1746 		      result = gaiaFromSpatiaLiteBlobWkb (p_blob, blobsz);
1747 		  }
1748 	    }
1749 	  else
1750 	    {
1751 		char *msg =
1752 		    sqlite3_mprintf
1753 		    ("TopoGeo_SnapLinestringToSeed error: \"%s\"",
1754 		     sqlite3_errmsg (topo->db_handle));
1755 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
1756 					     msg);
1757 		sqlite3_free (msg);
1758 		goto error;
1759 	    }
1760       }
1761     sqlite3_finalize (stmt_snap);
1762     stmt_snap = NULL;
1763     if (result == NULL)
1764 	goto error;
1765     if (result->FirstPoint != NULL || result->FirstPolygon != NULL)
1766 	goto error;
1767     if (result->FirstLinestring == NULL)
1768 	goto error;
1769     if (result->FirstLinestring != result->LastLinestring)
1770 	goto error;
1771     return result;
1772 
1773   error:
1774     if (stmt != NULL)
1775 	sqlite3_finalize (stmt);
1776     if (stmt_snap != NULL)
1777 	sqlite3_finalize (stmt_snap);
1778     if (result != NULL)
1779 	gaiaFreeGeomColl (result);
1780     return NULL;
1781 }
1782 
1783 static gaiaGeomCollPtr
make_geom_from_polyg(int srid,gaiaPolygonPtr pg)1784 make_geom_from_polyg (int srid, gaiaPolygonPtr pg)
1785 {
1786 /* quick constructor: Geometry based on external polyg */
1787     gaiaGeomCollPtr reference;
1788     if (pg->DimensionModel == GAIA_XY_Z_M)
1789 	reference = gaiaAllocGeomCollXYZM ();
1790     else if (pg->DimensionModel == GAIA_XY_Z)
1791 	reference = gaiaAllocGeomCollXYZ ();
1792     else if (pg->DimensionModel == GAIA_XY_M)
1793 	reference = gaiaAllocGeomCollXYM ();
1794     else
1795 	reference = gaiaAllocGeomColl ();
1796     reference->Srid = srid;
1797     pg->Next = NULL;
1798     reference->FirstPolygon = pg;
1799     reference->LastPolygon = pg;
1800     return reference;
1801 }
1802 
1803 static void
do_eval_topogeo_point(struct gaia_topology * topo,gaiaGeomCollPtr result,gaiaGeomCollPtr reference,sqlite3_stmt * stmt_node)1804 do_eval_topogeo_point (struct gaia_topology *topo, gaiaGeomCollPtr result,
1805 		       gaiaGeomCollPtr reference, sqlite3_stmt * stmt_node)
1806 {
1807 /* retrieving Points from Topology */
1808     int ret;
1809     unsigned char *p_blob;
1810     int n_bytes;
1811 
1812 /* initializing the Topo-Node query */
1813     gaiaToSpatiaLiteBlobWkb (reference, &p_blob, &n_bytes);
1814     sqlite3_reset (stmt_node);
1815     sqlite3_clear_bindings (stmt_node);
1816     sqlite3_bind_blob (stmt_node, 1, p_blob, n_bytes, SQLITE_TRANSIENT);
1817     sqlite3_bind_blob (stmt_node, 2, p_blob, n_bytes, SQLITE_TRANSIENT);
1818     free (p_blob);
1819 
1820     while (1)
1821       {
1822 	  /* scrolling the result set rows */
1823 	  ret = sqlite3_step (stmt_node);
1824 	  if (ret == SQLITE_DONE)
1825 	      break;		/* end of result set */
1826 	  if (ret == SQLITE_ROW)
1827 	    {
1828 		const unsigned char *blob = sqlite3_column_blob (stmt_node, 0);
1829 		int blob_sz = sqlite3_column_bytes (stmt_node, 0);
1830 		gaiaGeomCollPtr geom =
1831 		    gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
1832 		if (geom != NULL)
1833 		  {
1834 		      gaiaPointPtr pt = geom->FirstPoint;
1835 		      while (pt != NULL)
1836 			{
1837 			    /* copying all Points into the result Geometry */
1838 			    if (topo->has_z)
1839 				gaiaAddPointToGeomCollXYZ (result, pt->X, pt->Y,
1840 							   pt->Z);
1841 			    else
1842 				gaiaAddPointToGeomColl (result, pt->X, pt->Y);
1843 			    pt = pt->Next;
1844 			}
1845 		      gaiaFreeGeomColl (geom);
1846 		  }
1847 	    }
1848 	  else
1849 	    {
1850 		char *msg = sqlite3_mprintf ("TopoGeo_ToGeoTable error: \"%s\"",
1851 					     sqlite3_errmsg (topo->db_handle));
1852 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
1853 					     msg);
1854 		sqlite3_free (msg);
1855 		return;
1856 	    }
1857       }
1858 }
1859 
1860 static void
do_collect_topo_edges(struct gaia_topology * topo,gaiaGeomCollPtr sparse,sqlite3_stmt * stmt_edge,sqlite3_int64 edge_id)1861 do_collect_topo_edges (struct gaia_topology *topo, gaiaGeomCollPtr sparse,
1862 		       sqlite3_stmt * stmt_edge, sqlite3_int64 edge_id)
1863 {
1864 /* collecting Edge Geometries one by one */
1865     int ret;
1866 
1867 /* initializing the Topo-Edge query */
1868     sqlite3_reset (stmt_edge);
1869     sqlite3_clear_bindings (stmt_edge);
1870     sqlite3_bind_int64 (stmt_edge, 1, edge_id);
1871 
1872     while (1)
1873       {
1874 	  /* scrolling the result set rows */
1875 	  ret = sqlite3_step (stmt_edge);
1876 	  if (ret == SQLITE_DONE)
1877 	      break;		/* end of result set */
1878 	  if (ret == SQLITE_ROW)
1879 	    {
1880 		const unsigned char *blob = sqlite3_column_blob (stmt_edge, 0);
1881 		int blob_sz = sqlite3_column_bytes (stmt_edge, 0);
1882 		gaiaGeomCollPtr geom =
1883 		    gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
1884 		if (geom != NULL)
1885 		  {
1886 		      gaiaLinestringPtr ln = geom->FirstLinestring;
1887 		      while (ln != NULL)
1888 			{
1889 			    if (topo->has_z)
1890 				auxtopo_copy_linestring3d (ln, sparse);
1891 			    else
1892 				auxtopo_copy_linestring (ln, sparse);
1893 			    ln = ln->Next;
1894 			}
1895 		      gaiaFreeGeomColl (geom);
1896 		  }
1897 	    }
1898 	  else
1899 	    {
1900 		char *msg = sqlite3_mprintf ("TopoGeo_ToGeoTable error: \"%s\"",
1901 					     sqlite3_errmsg (topo->db_handle));
1902 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
1903 					     msg);
1904 		sqlite3_free (msg);
1905 		return;
1906 	    }
1907       }
1908 }
1909 
1910 static void
do_eval_topogeo_line(struct gaia_topology * topo,gaiaGeomCollPtr result,gaiaGeomCollPtr reference,sqlite3_stmt * stmt_seed_edge,sqlite3_stmt * stmt_edge)1911 do_eval_topogeo_line (struct gaia_topology *topo, gaiaGeomCollPtr result,
1912 		      gaiaGeomCollPtr reference, sqlite3_stmt * stmt_seed_edge,
1913 		      sqlite3_stmt * stmt_edge)
1914 {
1915 /* retrieving Linestrings from Topology */
1916     int ret;
1917     unsigned char *p_blob;
1918     int n_bytes;
1919     gaiaGeomCollPtr sparse;
1920     gaiaGeomCollPtr rearranged;
1921     gaiaLinestringPtr ln;
1922 
1923     if (topo->has_z)
1924 	sparse = gaiaAllocGeomCollXYZ ();
1925     else
1926 	sparse = gaiaAllocGeomColl ();
1927     sparse->Srid = topo->srid;
1928 
1929 /* initializing the Topo-Seed-Edge query */
1930     gaiaToSpatiaLiteBlobWkb (reference, &p_blob, &n_bytes);
1931     sqlite3_reset (stmt_seed_edge);
1932     sqlite3_clear_bindings (stmt_seed_edge);
1933     sqlite3_bind_blob (stmt_seed_edge, 1, p_blob, n_bytes, SQLITE_TRANSIENT);
1934     sqlite3_bind_blob (stmt_seed_edge, 2, p_blob, n_bytes, SQLITE_TRANSIENT);
1935     free (p_blob);
1936 
1937     while (1)
1938       {
1939 	  /* scrolling the result set rows */
1940 	  ret = sqlite3_step (stmt_seed_edge);
1941 	  if (ret == SQLITE_DONE)
1942 	      break;		/* end of result set */
1943 	  if (ret == SQLITE_ROW)
1944 	    {
1945 		sqlite3_int64 edge_id =
1946 		    sqlite3_column_int64 (stmt_seed_edge, 0);
1947 		do_collect_topo_edges (topo, sparse, stmt_edge, edge_id);
1948 	    }
1949 	  else
1950 	    {
1951 		char *msg = sqlite3_mprintf ("TopoGeo_ToGeoTable error: \"%s\"",
1952 					     sqlite3_errmsg (topo->db_handle));
1953 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
1954 					     msg);
1955 		sqlite3_free (msg);
1956 		gaiaFreeGeomColl (sparse);
1957 		return;
1958 	    }
1959       }
1960 
1961 /* attempting to rearrange sparse lines */
1962     rearranged = gaiaLineMerge_r (topo->cache, sparse);
1963     gaiaFreeGeomColl (sparse);
1964     if (rearranged == NULL)
1965 	return;
1966     ln = rearranged->FirstLinestring;
1967     while (ln != NULL)
1968       {
1969 	  if (topo->has_z)
1970 	      auxtopo_copy_linestring3d (ln, result);
1971 	  else
1972 	      auxtopo_copy_linestring (ln, result);
1973 	  ln = ln->Next;
1974       }
1975     gaiaFreeGeomColl (rearranged);
1976 }
1977 
1978 static void
do_explode_topo_face(struct gaia_topology * topo,struct face_edges * list,sqlite3_stmt * stmt_face,sqlite3_int64 face_id)1979 do_explode_topo_face (struct gaia_topology *topo, struct face_edges *list,
1980 		      sqlite3_stmt * stmt_face, sqlite3_int64 face_id)
1981 {
1982 /* exploding all Edges required by the same face */
1983     int ret;
1984 
1985 /* initializing the Topo-Face query */
1986     sqlite3_reset (stmt_face);
1987     sqlite3_clear_bindings (stmt_face);
1988     sqlite3_bind_int64 (stmt_face, 1, face_id);
1989     sqlite3_bind_int64 (stmt_face, 2, face_id);
1990 
1991     while (1)
1992       {
1993 	  /* scrolling the result set rows */
1994 	  ret = sqlite3_step (stmt_face);
1995 	  if (ret == SQLITE_DONE)
1996 	      break;		/* end of result set */
1997 	  if (ret == SQLITE_ROW)
1998 	    {
1999 		sqlite3_int64 edge_id = sqlite3_column_int64 (stmt_face, 0);
2000 		sqlite3_int64 left_face = sqlite3_column_int64 (stmt_face, 1);
2001 		sqlite3_int64 right_face = sqlite3_column_int64 (stmt_face, 2);
2002 		const unsigned char *blob = sqlite3_column_blob (stmt_face, 3);
2003 		int blob_sz = sqlite3_column_bytes (stmt_face, 3);
2004 		gaiaGeomCollPtr geom =
2005 		    gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
2006 		if (geom != NULL)
2007 		    auxtopo_add_face_edge (list, face_id, edge_id, left_face,
2008 					   right_face, geom);
2009 	    }
2010 	  else
2011 	    {
2012 		char *msg = sqlite3_mprintf ("TopoGeo_ToGeoTable error: \"%s\"",
2013 					     sqlite3_errmsg (topo->db_handle));
2014 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
2015 					     msg);
2016 		sqlite3_free (msg);
2017 		return;
2018 	    }
2019       }
2020 }
2021 
2022 static void
do_copy_ring3d(gaiaRingPtr in,gaiaRingPtr out)2023 do_copy_ring3d (gaiaRingPtr in, gaiaRingPtr out)
2024 {
2025 /* inserting/copying a Ring 3D into another Ring */
2026     int iv;
2027     double x;
2028     double y;
2029     double z;
2030     for (iv = 0; iv < in->Points; iv++)
2031       {
2032 	  gaiaGetPointXYZ (in->Coords, iv, &x, &y, &z);
2033 	  gaiaSetPointXYZ (out->Coords, iv, x, y, z);
2034       }
2035 }
2036 
2037 static void
do_copy_ring(gaiaRingPtr in,gaiaRingPtr out)2038 do_copy_ring (gaiaRingPtr in, gaiaRingPtr out)
2039 {
2040 /* inserting/copying a Ring into another Ring */
2041     int iv;
2042     double x;
2043     double y;
2044     for (iv = 0; iv < in->Points; iv++)
2045       {
2046 	  gaiaGetPoint (in->Coords, iv, &x, &y);
2047 	  gaiaSetPoint (out->Coords, iv, x, y);
2048       }
2049 }
2050 
2051 static void
do_copy_polygon3d(gaiaPolygonPtr in,gaiaGeomCollPtr geom)2052 do_copy_polygon3d (gaiaPolygonPtr in, gaiaGeomCollPtr geom)
2053 {
2054 /* inserting/copying a Polygon 3D into another Geometry */
2055     int ib;
2056     gaiaRingPtr rng_out;
2057     gaiaRingPtr rng_in = in->Exterior;
2058     gaiaPolygonPtr out =
2059 	gaiaAddPolygonToGeomColl (geom, rng_in->Points, in->NumInteriors);
2060     rng_out = out->Exterior;
2061     do_copy_ring3d (rng_in, rng_out);
2062     for (ib = 0; ib < in->NumInteriors; ib++)
2063       {
2064 	  rng_in = in->Interiors + ib;
2065 	  rng_out = gaiaAddInteriorRing (out, ib, rng_in->Points);
2066 	  do_copy_ring3d (rng_in, rng_out);
2067       }
2068 }
2069 
2070 static void
do_copy_polygon(gaiaPolygonPtr in,gaiaGeomCollPtr geom)2071 do_copy_polygon (gaiaPolygonPtr in, gaiaGeomCollPtr geom)
2072 {
2073 /* inserting/copying a Polygon into another Geometry */
2074     int ib;
2075     gaiaRingPtr rng_out;
2076     gaiaRingPtr rng_in = in->Exterior;
2077     gaiaPolygonPtr out =
2078 	gaiaAddPolygonToGeomColl (geom, rng_in->Points, in->NumInteriors);
2079     rng_out = out->Exterior;
2080     do_copy_ring (rng_in, rng_out);
2081     for (ib = 0; ib < in->NumInteriors; ib++)
2082       {
2083 	  rng_in = in->Interiors + ib;
2084 	  rng_out = gaiaAddInteriorRing (out, ib, rng_in->Points);
2085 	  do_copy_ring (rng_in, rng_out);
2086       }
2087 }
2088 
2089 static void
do_copy_filter_polygon3d(gaiaPolygonPtr in,gaiaGeomCollPtr geom,const void * cache,double tolerance)2090 do_copy_filter_polygon3d (gaiaPolygonPtr in, gaiaGeomCollPtr geom,
2091 			  const void *cache, double tolerance)
2092 {
2093 /* inserting/copying a Polygon 3D into another Geometry (with tolerance) */
2094     int ib;
2095     gaiaGeomCollPtr polyg;
2096     gaiaPolygonPtr pg;
2097     gaiaRingPtr rng_out;
2098     gaiaRingPtr rng_in = in->Exterior;
2099     gaiaPolygonPtr out;
2100     double area;
2101     int ret;
2102     int numints = 0;
2103 
2104     polyg = gaiaAllocGeomCollXYZ ();
2105     pg = gaiaAddPolygonToGeomColl (polyg, rng_in->Points, 0);
2106     rng_out = pg->Exterior;
2107     do_copy_ring3d (rng_in, rng_out);
2108     ret = gaiaGeomCollArea_r (cache, polyg, &area);
2109     gaiaFreeGeomColl (polyg);
2110     if (!ret)
2111 	return;
2112     if ((tolerance * tolerance) > area)
2113 	return;			/* skipping smaller polygons */
2114 
2115     for (ib = 0; ib < in->NumInteriors; ib++)
2116       {
2117 	  /* counting how many interior rings do we really have */
2118 	  rng_in = in->Interiors + ib;
2119 	  polyg = gaiaAllocGeomCollXYZ ();
2120 	  pg = gaiaAddPolygonToGeomColl (polyg, rng_in->Points, 0);
2121 	  rng_out = pg->Exterior;
2122 	  do_copy_ring3d (rng_in, rng_out);
2123 	  ret = gaiaGeomCollArea_r (cache, polyg, &area);
2124 	  gaiaFreeGeomColl (polyg);
2125 	  if (!ret)
2126 	      continue;
2127 	  if ((tolerance * tolerance) > area)
2128 	      continue;		/* skipping smaller holes */
2129 	  numints++;
2130       }
2131 
2132     rng_in = in->Exterior;
2133     out = gaiaAddPolygonToGeomColl (geom, rng_in->Points, numints);
2134     rng_out = out->Exterior;
2135     do_copy_ring3d (rng_in, rng_out);
2136     numints = 0;
2137     for (ib = 0; ib < in->NumInteriors; ib++)
2138       {
2139 	  /* copying interior rings */
2140 	  rng_in = in->Interiors + ib;
2141 	  polyg = gaiaAllocGeomCollXYZ ();
2142 	  pg = gaiaAddPolygonToGeomColl (polyg, rng_in->Points, 0);
2143 	  rng_out = pg->Exterior;
2144 	  do_copy_ring3d (rng_in, rng_out);
2145 	  ret = gaiaGeomCollArea_r (cache, polyg, &area);
2146 	  gaiaFreeGeomColl (polyg);
2147 	  if (!ret)
2148 	      continue;
2149 	  if ((tolerance * tolerance) > area)
2150 	      continue;		/* skipping smaller holes */
2151 	  rng_out = gaiaAddInteriorRing (out, numints++, rng_in->Points);
2152 	  do_copy_ring3d (rng_in, rng_out);
2153       }
2154 }
2155 
2156 static void
do_copy_filter_polygon(gaiaPolygonPtr in,gaiaGeomCollPtr geom,const void * cache,double tolerance)2157 do_copy_filter_polygon (gaiaPolygonPtr in, gaiaGeomCollPtr geom,
2158 			const void *cache, double tolerance)
2159 {
2160 /* inserting/copying a Polygon into another Geometry (with tolerance) */
2161     int ib;
2162     gaiaGeomCollPtr polyg;
2163     gaiaPolygonPtr pg;
2164     gaiaRingPtr rng_out;
2165     gaiaRingPtr rng_in = in->Exterior;
2166     gaiaPolygonPtr out;
2167     double area;
2168     int ret;
2169     int numints = 0;
2170 
2171     polyg = gaiaAllocGeomColl ();
2172     pg = gaiaAddPolygonToGeomColl (polyg, rng_in->Points, 0);
2173     rng_out = pg->Exterior;
2174     do_copy_ring (rng_in, rng_out);
2175     ret = gaiaGeomCollArea_r (cache, polyg, &area);
2176     gaiaFreeGeomColl (polyg);
2177     if (!ret)
2178 	return;
2179     if ((tolerance * tolerance) > area)
2180 	return;			/* skipping smaller polygons */
2181 
2182     for (ib = 0; ib < in->NumInteriors; ib++)
2183       {
2184 	  /* counting how many interior rings do we really have */
2185 	  rng_in = in->Interiors + ib;
2186 	  polyg = gaiaAllocGeomColl ();
2187 	  pg = gaiaAddPolygonToGeomColl (polyg, rng_in->Points, 0);
2188 	  rng_out = pg->Exterior;
2189 	  do_copy_ring (rng_in, rng_out);
2190 	  ret = gaiaGeomCollArea_r (cache, polyg, &area);
2191 	  gaiaFreeGeomColl (polyg);
2192 	  if (!ret)
2193 	      continue;
2194 	  if ((tolerance * tolerance) > area)
2195 	      continue;		/* skipping smaller holes */
2196 	  numints++;
2197       }
2198 
2199     rng_in = in->Exterior;
2200     out = gaiaAddPolygonToGeomColl (geom, rng_in->Points, numints);
2201     rng_out = out->Exterior;
2202     do_copy_ring (rng_in, rng_out);
2203     numints = 0;
2204     for (ib = 0; ib < in->NumInteriors; ib++)
2205       {
2206 	  /* copying interior rings */
2207 	  rng_in = in->Interiors + ib;
2208 	  polyg = gaiaAllocGeomColl ();
2209 	  pg = gaiaAddPolygonToGeomColl (polyg, rng_in->Points, 0);
2210 	  rng_out = pg->Exterior;
2211 	  do_copy_ring (rng_in, rng_out);
2212 	  ret = gaiaGeomCollArea_r (cache, polyg, &area);
2213 	  gaiaFreeGeomColl (polyg);
2214 	  if (!ret)
2215 	      continue;
2216 	  if ((tolerance * tolerance) > area)
2217 	      continue;		/* skipping smaller holes */
2218 	  rng_out = gaiaAddInteriorRing (out, numints++, rng_in->Points);
2219 	  do_copy_ring (rng_in, rng_out);
2220       }
2221 }
2222 
2223 static void
do_eval_topo_polyg(struct gaia_topology * topo,gaiaGeomCollPtr result,gaiaGeomCollPtr reference,sqlite3_stmt * stmt_seed_face,sqlite3_stmt * stmt_face)2224 do_eval_topo_polyg (struct gaia_topology *topo, gaiaGeomCollPtr result,
2225 		    gaiaGeomCollPtr reference, sqlite3_stmt * stmt_seed_face,
2226 		    sqlite3_stmt * stmt_face)
2227 {
2228 /* retrieving Polygons from Topology */
2229     int ret;
2230     unsigned char *p_blob;
2231     int n_bytes;
2232     gaiaGeomCollPtr rearranged;
2233     gaiaPolygonPtr pg;
2234     struct face_edges *list =
2235 	auxtopo_create_face_edges (topo->has_z, topo->srid);
2236 
2237 /* initializing the Topo-Seed-Face query */
2238     gaiaToSpatiaLiteBlobWkb (reference, &p_blob, &n_bytes);
2239     sqlite3_reset (stmt_seed_face);
2240     sqlite3_clear_bindings (stmt_seed_face);
2241     sqlite3_bind_blob (stmt_seed_face, 1, p_blob, n_bytes, SQLITE_TRANSIENT);
2242     sqlite3_bind_blob (stmt_seed_face, 2, p_blob, n_bytes, SQLITE_TRANSIENT);
2243     free (p_blob);
2244 
2245     while (1)
2246       {
2247 	  /* scrolling the result set rows */
2248 	  ret = sqlite3_step (stmt_seed_face);
2249 	  if (ret == SQLITE_DONE)
2250 	      break;		/* end of result set */
2251 	  if (ret == SQLITE_ROW)
2252 	    {
2253 		sqlite3_int64 face_id =
2254 		    sqlite3_column_int64 (stmt_seed_face, 0);
2255 		do_explode_topo_face (topo, list, stmt_face, face_id);
2256 	    }
2257 	  else
2258 	    {
2259 		char *msg = sqlite3_mprintf ("TopoGeo_ToGeoTable error: \"%s\"",
2260 					     sqlite3_errmsg (topo->db_handle));
2261 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
2262 					     msg);
2263 		sqlite3_free (msg);
2264 		auxtopo_free_face_edges (list);
2265 		return;
2266 	    }
2267       }
2268 
2269 /* attempting to rearrange sparse lines into Polygons */
2270     auxtopo_select_valid_face_edges (list);
2271     rearranged = auxtopo_polygonize_face_edges (list, topo->cache);
2272     auxtopo_free_face_edges (list);
2273     if (rearranged == NULL)
2274 	return;
2275     pg = rearranged->FirstPolygon;
2276     while (pg != NULL)
2277       {
2278 	  if (topo->has_z)
2279 	      do_copy_polygon3d (pg, result);
2280 	  else
2281 	      do_copy_polygon (pg, result);
2282 	  pg = pg->Next;
2283       }
2284     gaiaFreeGeomColl (rearranged);
2285 }
2286 
2287 static void
do_eval_topo_polyg_generalize(struct gaia_topology * topo,gaiaGeomCollPtr result,gaiaGeomCollPtr reference,sqlite3_stmt * stmt_seed_face,sqlite3_stmt * stmt_face,double tolerance)2288 do_eval_topo_polyg_generalize (struct gaia_topology *topo,
2289 			       gaiaGeomCollPtr result,
2290 			       gaiaGeomCollPtr reference,
2291 			       sqlite3_stmt * stmt_seed_face,
2292 			       sqlite3_stmt * stmt_face, double tolerance)
2293 {
2294 /* retrieving Polygons from Topology */
2295     int ret;
2296     unsigned char *p_blob;
2297     int n_bytes;
2298     gaiaGeomCollPtr rearranged;
2299     gaiaPolygonPtr pg;
2300     struct face_edges *list =
2301 	auxtopo_create_face_edges (topo->has_z, topo->srid);
2302 
2303 /* initializing the Topo-Seed-Face query */
2304     gaiaToSpatiaLiteBlobWkb (reference, &p_blob, &n_bytes);
2305     sqlite3_reset (stmt_seed_face);
2306     sqlite3_clear_bindings (stmt_seed_face);
2307     sqlite3_bind_blob (stmt_seed_face, 1, p_blob, n_bytes, SQLITE_TRANSIENT);
2308     sqlite3_bind_blob (stmt_seed_face, 2, p_blob, n_bytes, SQLITE_TRANSIENT);
2309     free (p_blob);
2310 
2311     while (1)
2312       {
2313 	  /* scrolling the result set rows */
2314 	  ret = sqlite3_step (stmt_seed_face);
2315 	  if (ret == SQLITE_DONE)
2316 	      break;		/* end of result set */
2317 	  if (ret == SQLITE_ROW)
2318 	    {
2319 		sqlite3_int64 face_id =
2320 		    sqlite3_column_int64 (stmt_seed_face, 0);
2321 		do_explode_topo_face (topo, list, stmt_face, face_id);
2322 	    }
2323 	  else
2324 	    {
2325 		char *msg = sqlite3_mprintf ("TopoGeo_ToGeoTable error: \"%s\"",
2326 					     sqlite3_errmsg (topo->db_handle));
2327 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
2328 					     msg);
2329 		sqlite3_free (msg);
2330 		auxtopo_free_face_edges (list);
2331 		return;
2332 	    }
2333       }
2334 
2335 /* attempting to rearrange sparse lines into Polygons */
2336     auxtopo_select_valid_face_edges (list);
2337     rearranged = auxtopo_polygonize_face_edges_generalize (list, topo->cache);
2338     auxtopo_free_face_edges (list);
2339     if (rearranged == NULL)
2340 	return;
2341     pg = rearranged->FirstPolygon;
2342     while (pg != NULL)
2343       {
2344 	  if (topo->has_z)
2345 	      do_copy_filter_polygon3d (pg, result, topo->cache, tolerance);
2346 	  else
2347 	      do_copy_filter_polygon (pg, result, topo->cache, tolerance);
2348 	  pg = pg->Next;
2349       }
2350     gaiaFreeGeomColl (rearranged);
2351 }
2352 
2353 static gaiaGeomCollPtr
do_eval_topogeo_geom(struct gaia_topology * topo,gaiaGeomCollPtr geom,sqlite3_stmt * stmt_seed_edge,sqlite3_stmt * stmt_seed_face,sqlite3_stmt * stmt_node,sqlite3_stmt * stmt_edge,sqlite3_stmt * stmt_face,int out_type,double tolerance)2354 do_eval_topogeo_geom (struct gaia_topology *topo, gaiaGeomCollPtr geom,
2355 		      sqlite3_stmt * stmt_seed_edge,
2356 		      sqlite3_stmt * stmt_seed_face, sqlite3_stmt * stmt_node,
2357 		      sqlite3_stmt * stmt_edge, sqlite3_stmt * stmt_face,
2358 		      int out_type, double tolerance)
2359 {
2360 /* retrieving Topology-Geometry geometries via matching Seeds */
2361     gaiaGeomCollPtr result;
2362 
2363     if (topo->has_z)
2364 	result = gaiaAllocGeomCollXYZ ();
2365     else
2366 	result = gaiaAllocGeomColl ();
2367     result->Srid = topo->srid;
2368     result->DeclaredType = out_type;
2369 
2370     if (out_type == GAIA_POINT || out_type == GAIA_MULTIPOINT
2371 	|| out_type == GAIA_GEOMETRYCOLLECTION || out_type == GAIA_UNKNOWN)
2372       {
2373 	  /* processing all Points */
2374 	  gaiaPointPtr pt = geom->FirstPoint;
2375 	  while (pt != NULL)
2376 	    {
2377 		gaiaPointPtr next = pt->Next;
2378 		gaiaGeomCollPtr reference = (gaiaGeomCollPtr)
2379 		    auxtopo_make_geom_from_point (topo->srid, topo->has_z, pt);
2380 		do_eval_topogeo_point (topo, result, reference, stmt_node);
2381 		auxtopo_destroy_geom_from (reference);
2382 		pt->Next = next;
2383 		pt = pt->Next;
2384 	    }
2385       }
2386 
2387     if (out_type == GAIA_MULTILINESTRING || out_type == GAIA_GEOMETRYCOLLECTION
2388 	|| out_type == GAIA_UNKNOWN)
2389       {
2390 	  /* processing all Linestrings */
2391 	  gaiaLinestringPtr ln = geom->FirstLinestring;
2392 	  while (ln != NULL)
2393 	    {
2394 		gaiaLinestringPtr next = ln->Next;
2395 		gaiaGeomCollPtr reference = (gaiaGeomCollPtr)
2396 		    auxtopo_make_geom_from_line (topo->srid, ln);
2397 		do_eval_topogeo_line (topo, result, reference, stmt_seed_edge,
2398 				      stmt_edge);
2399 		auxtopo_destroy_geom_from (reference);
2400 		ln->Next = next;
2401 		ln = ln->Next;
2402 	    }
2403       }
2404 
2405     if (out_type == GAIA_MULTIPOLYGON || out_type == GAIA_GEOMETRYCOLLECTION
2406 	|| out_type == GAIA_UNKNOWN)
2407       {
2408 	  /* processing all Polygons */
2409 	  gaiaPolygonPtr pg = geom->FirstPolygon;
2410 	  while (pg != NULL)
2411 	    {
2412 		gaiaPolygonPtr next = pg->Next;
2413 		gaiaGeomCollPtr reference =
2414 		    make_geom_from_polyg (topo->srid, pg);
2415 		if (tolerance > 0.0)
2416 		    do_eval_topo_polyg_generalize (topo, result, reference,
2417 						   stmt_seed_face, stmt_face,
2418 						   tolerance);
2419 		else
2420 		    do_eval_topo_polyg (topo, result, reference, stmt_seed_face,
2421 					stmt_face);
2422 		auxtopo_destroy_geom_from (reference);
2423 		pg->Next = next;
2424 		pg = pg->Next;
2425 	    }
2426       }
2427 
2428     if (result->FirstPoint == NULL && result->FirstLinestring == NULL
2429 	&& result->FirstPolygon == NULL)
2430 	goto error;
2431     return result;
2432 
2433   error:
2434     gaiaFreeGeomColl (result);
2435     return NULL;
2436 }
2437 
2438 static int
do_eval_topogeo_seeds(struct gaia_topology * topo,sqlite3_stmt * stmt_ref,int ref_geom_col,sqlite3_stmt * stmt_ins,sqlite3_stmt * stmt_seed_edge,sqlite3_stmt * stmt_seed_face,sqlite3_stmt * stmt_node,sqlite3_stmt * stmt_edge,sqlite3_stmt * stmt_face,int out_type,double tolerance)2439 do_eval_topogeo_seeds (struct gaia_topology *topo, sqlite3_stmt * stmt_ref,
2440 		       int ref_geom_col, sqlite3_stmt * stmt_ins,
2441 		       sqlite3_stmt * stmt_seed_edge,
2442 		       sqlite3_stmt * stmt_seed_face, sqlite3_stmt * stmt_node,
2443 		       sqlite3_stmt * stmt_edge, sqlite3_stmt * stmt_face,
2444 		       int out_type, double tolerance)
2445 {
2446 /* querying the ref-table */
2447     int ret;
2448 
2449     sqlite3_reset (stmt_ref);
2450     sqlite3_clear_bindings (stmt_ref);
2451     while (1)
2452       {
2453 	  /* scrolling the result set rows */
2454 	  ret = sqlite3_step (stmt_ref);
2455 	  if (ret == SQLITE_DONE)
2456 	      break;		/* end of result set */
2457 	  if (ret == SQLITE_ROW)
2458 	    {
2459 		int icol;
2460 		int ncol = sqlite3_column_count (stmt_ref);
2461 		sqlite3_reset (stmt_ins);
2462 		sqlite3_clear_bindings (stmt_ins);
2463 		for (icol = 0; icol < ncol; icol++)
2464 		  {
2465 		      int col_type = sqlite3_column_type (stmt_ref, icol);
2466 		      if (icol == ref_geom_col)
2467 			{
2468 			    /* the geometry column */
2469 			    const unsigned char *blob =
2470 				sqlite3_column_blob (stmt_ref, icol);
2471 			    int blob_sz = sqlite3_column_bytes (stmt_ref, icol);
2472 			    gaiaGeomCollPtr geom =
2473 				gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
2474 			    if (geom != NULL)
2475 			      {
2476 				  gaiaGeomCollPtr result;
2477 				  unsigned char *p_blob;
2478 				  int n_bytes;
2479 				  int gpkg_mode = 0;
2480 				  int tiny_point = 0;
2481 				  if (topo->cache != NULL)
2482 				    {
2483 					struct splite_internal_cache *cache =
2484 					    (struct splite_internal_cache
2485 					     *) (topo->cache);
2486 					gpkg_mode = cache->gpkg_mode;
2487 					tiny_point = cache->tinyPointEnabled;
2488 				    }
2489 				  result = do_eval_topogeo_geom (topo, geom,
2490 								 stmt_seed_edge,
2491 								 stmt_seed_face,
2492 								 stmt_node,
2493 								 stmt_edge,
2494 								 stmt_face,
2495 								 out_type,
2496 								 tolerance);
2497 				  gaiaFreeGeomColl (geom);
2498 				  if (result != NULL)
2499 				    {
2500 					gaiaToSpatiaLiteBlobWkbEx2 (result,
2501 								    &p_blob,
2502 								    &n_bytes,
2503 								    gpkg_mode,
2504 								    tiny_point);
2505 					gaiaFreeGeomColl (result);
2506 					sqlite3_bind_blob (stmt_ins, icol + 1,
2507 							   p_blob, n_bytes,
2508 							   free);
2509 				    }
2510 				  else
2511 				      sqlite3_bind_null (stmt_ins, icol + 1);
2512 			      }
2513 			    else
2514 				sqlite3_bind_null (stmt_ins, icol + 1);
2515 			    continue;
2516 			}
2517 		      switch (col_type)
2518 			{
2519 			case SQLITE_INTEGER:
2520 			    sqlite3_bind_int64 (stmt_ins, icol + 1,
2521 						sqlite3_column_int64 (stmt_ref,
2522 								      icol));
2523 			    break;
2524 			case SQLITE_FLOAT:
2525 			    sqlite3_bind_double (stmt_ins, icol + 1,
2526 						 sqlite3_column_double
2527 						 (stmt_ref, icol));
2528 			    break;
2529 			case SQLITE_TEXT:
2530 			    sqlite3_bind_text (stmt_ins, icol + 1,
2531 					       (const char *)
2532 					       sqlite3_column_text (stmt_ref,
2533 								    icol),
2534 					       sqlite3_column_bytes (stmt_ref,
2535 								     icol),
2536 					       SQLITE_STATIC);
2537 			    break;
2538 			case SQLITE_BLOB:
2539 			    sqlite3_bind_blob (stmt_ins, icol + 1,
2540 					       sqlite3_column_blob (stmt_ref,
2541 								    icol),
2542 					       sqlite3_column_bytes (stmt_ref,
2543 								     icol),
2544 					       SQLITE_STATIC);
2545 			    break;
2546 			default:
2547 			    sqlite3_bind_null (stmt_ins, icol + 1);
2548 			    break;
2549 			};
2550 		  }
2551 		ret = sqlite3_step (stmt_ins);
2552 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
2553 		    ;
2554 		else
2555 		  {
2556 		      char *msg =
2557 			  sqlite3_mprintf ("TopoGeo_ToGeoTable() error: \"%s\"",
2558 					   sqlite3_errmsg (topo->db_handle));
2559 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
2560 						   topo, msg);
2561 		      sqlite3_free (msg);
2562 		      return 0;
2563 		  }
2564 	    }
2565 	  else
2566 	    {
2567 		char *msg =
2568 		    sqlite3_mprintf ("TopoGeo_ToGeoTable() error: \"%s\"",
2569 				     sqlite3_errmsg (topo->db_handle));
2570 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
2571 					     msg);
2572 		sqlite3_free (msg);
2573 		return 0;
2574 	    }
2575       }
2576     return 1;
2577 }
2578 
2579 GAIATOPO_DECLARE int
gaiaTopoGeo_ToGeoTableGeneralize(GaiaTopologyAccessorPtr accessor,const char * db_prefix,const char * ref_table,const char * ref_column,const char * out_table,double tolerance,int with_spatial_index)2580 gaiaTopoGeo_ToGeoTableGeneralize (GaiaTopologyAccessorPtr accessor,
2581 				  const char *db_prefix, const char *ref_table,
2582 				  const char *ref_column, const char *out_table,
2583 				  double tolerance, int with_spatial_index)
2584 {
2585 /*
2586 / attempting to create and populate a new GeoTable out from a Topology-Geometry
2587 / (simplified/generalized form)
2588 */
2589     struct gaia_topology *topo = (struct gaia_topology *) accessor;
2590     sqlite3_stmt *stmt_ref = NULL;
2591     sqlite3_stmt *stmt_ins = NULL;
2592     sqlite3_stmt *stmt_seed_edge = NULL;
2593     sqlite3_stmt *stmt_seed_face = NULL;
2594     sqlite3_stmt *stmt_node = NULL;
2595     sqlite3_stmt *stmt_edge = NULL;
2596     sqlite3_stmt *stmt_face = NULL;
2597     int ret;
2598     char *create;
2599     char *select;
2600     char *insert;
2601     char *sql;
2602     char *errMsg;
2603     char *xprefix;
2604     char *xtable;
2605     int ref_type;
2606     const char *type;
2607     int out_type;
2608     int ref_geom_col;
2609     if (topo == NULL)
2610 	return 0;
2611 
2612 /* incrementally updating all Topology Seeds */
2613     if (!gaiaTopoGeoUpdateSeeds (accessor, 1))
2614 	return 0;
2615 
2616 /* composing the CREATE TABLE output-table statement */
2617     if (!auxtopo_create_togeotable_sql
2618 	(topo->db_handle, db_prefix, ref_table, ref_column, out_table, &create,
2619 	 &select, &insert, &ref_geom_col))
2620 	goto error;
2621 
2622 /* creating the output-table */
2623     ret = sqlite3_exec (topo->db_handle, create, NULL, NULL, &errMsg);
2624     sqlite3_free (create);
2625     create = NULL;
2626     if (ret != SQLITE_OK)
2627       {
2628 	  char *msg =
2629 	      sqlite3_mprintf ("TopoGeo_ToGeoTableGeneralize() error: \"%s\"",
2630 			       errMsg);
2631 	  sqlite3_free (errMsg);
2632 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
2633 	  sqlite3_free (msg);
2634 	  goto error;
2635       }
2636 
2637 /* checking the Geometry Type */
2638     if (!auxtopo_retrieve_geometry_type
2639 	(topo->db_handle, db_prefix, ref_table, ref_column, &ref_type))
2640 	goto error;
2641     switch (ref_type)
2642       {
2643       case GAIA_POINT:
2644       case GAIA_POINTZ:
2645       case GAIA_POINTM:
2646       case GAIA_POINTZM:
2647 	  type = "POINT";
2648 	  out_type = GAIA_POINT;
2649 	  break;
2650       case GAIA_MULTIPOINT:
2651       case GAIA_MULTIPOINTZ:
2652       case GAIA_MULTIPOINTM:
2653       case GAIA_MULTIPOINTZM:
2654 	  type = "MULTIPOINT";
2655 	  out_type = GAIA_MULTIPOINT;
2656 	  break;
2657       case GAIA_LINESTRING:
2658       case GAIA_LINESTRINGZ:
2659       case GAIA_LINESTRINGM:
2660       case GAIA_LINESTRINGZM:
2661       case GAIA_MULTILINESTRING:
2662       case GAIA_MULTILINESTRINGZ:
2663       case GAIA_MULTILINESTRINGM:
2664       case GAIA_MULTILINESTRINGZM:
2665 	  type = "MULTILINESTRING";
2666 	  out_type = GAIA_MULTILINESTRING;
2667 	  break;
2668       case GAIA_POLYGON:
2669       case GAIA_POLYGONZ:
2670       case GAIA_POLYGONM:
2671       case GAIA_POLYGONZM:
2672       case GAIA_MULTIPOLYGON:
2673       case GAIA_MULTIPOLYGONZ:
2674       case GAIA_MULTIPOLYGONM:
2675       case GAIA_MULTIPOLYGONZM:
2676 	  type = "MULTIPOLYGON";
2677 	  out_type = GAIA_MULTIPOLYGON;
2678 	  break;
2679       case GAIA_GEOMETRYCOLLECTION:
2680       case GAIA_GEOMETRYCOLLECTIONZ:
2681       case GAIA_GEOMETRYCOLLECTIONM:
2682       case GAIA_GEOMETRYCOLLECTIONZM:
2683 	  type = "GEOMETRYCOLLECTION";
2684 	  out_type = GAIA_GEOMETRYCOLLECTION;
2685 	  break;
2686       default:
2687 	  type = "GEOMETRY";
2688 	  out_type = GAIA_UNKNOWN;
2689 	  break;
2690       };
2691 
2692 /* creating the output Geometry Column */
2693     sql =
2694 	sqlite3_mprintf
2695 	("SELECT AddGeometryColumn(Lower(%Q), Lower(%Q), %d, '%s', '%s')",
2696 	 out_table, ref_column, topo->srid, type,
2697 	 (topo->has_z == 0) ? "XY" : "XYZ");
2698     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
2699     sqlite3_free (sql);
2700     if (ret != SQLITE_OK)
2701       {
2702 	  char *msg =
2703 	      sqlite3_mprintf ("TopoGeo_ToGeoTableGeneralize() error: \"%s\"",
2704 			       errMsg);
2705 	  sqlite3_free (errMsg);
2706 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
2707 	  sqlite3_free (msg);
2708 	  goto error;
2709       }
2710 
2711     if (with_spatial_index)
2712       {
2713 	  /* adding a Spatial Index supporting the Geometry Column */
2714 	  sql =
2715 	      sqlite3_mprintf
2716 	      ("SELECT CreateSpatialIndex(Lower(%Q), Lower(%Q))",
2717 	       out_table, ref_column);
2718 	  ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
2719 	  sqlite3_free (sql);
2720 	  if (ret != SQLITE_OK)
2721 	    {
2722 		char *msg =
2723 		    sqlite3_mprintf
2724 		    ("TopoGeo_ToGeoTableGeneralize() error: \"%s\"",
2725 		     errMsg);
2726 		sqlite3_free (errMsg);
2727 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
2728 					     msg);
2729 		sqlite3_free (msg);
2730 		goto error;
2731 	    }
2732       }
2733 
2734 /* preparing the "SELECT * FROM ref-table" query */
2735     ret =
2736 	sqlite3_prepare_v2 (topo->db_handle, select, strlen (select), &stmt_ref,
2737 			    NULL);
2738     sqlite3_free (select);
2739     select = NULL;
2740     if (ret != SQLITE_OK)
2741       {
2742 	  char *msg =
2743 	      sqlite3_mprintf ("TopoGeo_ToGeoTableGeneralize() error: \"%s\"",
2744 			       sqlite3_errmsg (topo->db_handle));
2745 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
2746 	  sqlite3_free (msg);
2747 	  goto error;
2748       }
2749 
2750 /* preparing the "INSERT INTO out-table" query */
2751     ret =
2752 	sqlite3_prepare_v2 (topo->db_handle, insert, strlen (insert), &stmt_ins,
2753 			    NULL);
2754     sqlite3_free (insert);
2755     insert = NULL;
2756     if (ret != SQLITE_OK)
2757       {
2758 	  char *msg =
2759 	      sqlite3_mprintf ("TopoGeo_ToGeoTableGeneralize() error: \"%s\"",
2760 			       sqlite3_errmsg (topo->db_handle));
2761 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
2762 	  sqlite3_free (msg);
2763 	  goto error;
2764       }
2765 
2766 /* preparing the Topo-Seed-Edges query */
2767     xprefix = sqlite3_mprintf ("%s_seeds", topo->topology_name);
2768     xtable = gaiaDoubleQuotedSql (xprefix);
2769     sql = sqlite3_mprintf ("SELECT edge_id FROM MAIN.\"%s\" "
2770 			   "WHERE edge_id IS NOT NULL AND ST_Intersects(geom, ?) = 1 AND ROWID IN ("
2771 			   "SELECT ROWID FROM SpatialIndex WHERE f_table_name = %Q AND search_frame = ?)",
2772 			   xtable, xprefix);
2773     free (xtable);
2774     sqlite3_free (xprefix);
2775     ret =
2776 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_seed_edge,
2777 			    NULL);
2778     sqlite3_free (sql);
2779     if (ret != SQLITE_OK)
2780       {
2781 	  char *msg =
2782 	      sqlite3_mprintf ("TopoGeo_ToGeoTableGeneralize() error: \"%s\"",
2783 			       sqlite3_errmsg (topo->db_handle));
2784 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
2785 	  sqlite3_free (msg);
2786 	  goto error;
2787       }
2788 
2789 /* preparing the Topo-Seed-Faces query */
2790     xprefix = sqlite3_mprintf ("%s_seeds", topo->topology_name);
2791     xtable = gaiaDoubleQuotedSql (xprefix);
2792     sql = sqlite3_mprintf ("SELECT face_id FROM MAIN.\"%s\" "
2793 			   "WHERE face_id IS NOT NULL AND ST_Intersects(geom, ?) = 1 AND ROWID IN ("
2794 			   "SELECT ROWID FROM SpatialIndex WHERE f_table_name = %Q AND search_frame = ?)",
2795 			   xtable, xprefix);
2796     free (xtable);
2797     sqlite3_free (xprefix);
2798     ret =
2799 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_seed_face,
2800 			    NULL);
2801     sqlite3_free (sql);
2802     if (ret != SQLITE_OK)
2803       {
2804 	  char *msg =
2805 	      sqlite3_mprintf ("TopoGeo_ToGeoTableGeneralize() error: \"%s\"",
2806 			       sqlite3_errmsg (topo->db_handle));
2807 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
2808 	  sqlite3_free (msg);
2809 	  goto error;
2810       }
2811 
2812 /* preparing the Topo-Nodes query */
2813     xprefix = sqlite3_mprintf ("%s_node", topo->topology_name);
2814     xtable = gaiaDoubleQuotedSql (xprefix);
2815     sql = sqlite3_mprintf ("SELECT geom FROM MAIN.\"%s\" "
2816 			   "WHERE ST_Intersects(geom, ?) = 1 AND ROWID IN ("
2817 			   "SELECT ROWID FROM SpatialIndex WHERE f_table_name = %Q AND search_frame = ?)",
2818 			   xtable, xprefix);
2819     free (xtable);
2820     sqlite3_free (xprefix);
2821     ret =
2822 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_node,
2823 			    NULL);
2824     sqlite3_free (sql);
2825     if (ret != SQLITE_OK)
2826       {
2827 	  char *msg =
2828 	      sqlite3_mprintf ("TopoGeo_ToGeoTableGeneralize() error: \"%s\"",
2829 			       sqlite3_errmsg (topo->db_handle));
2830 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
2831 	  sqlite3_free (msg);
2832 	  goto error;
2833       }
2834 
2835 /* preparing the Topo-Edges query */
2836     xprefix = sqlite3_mprintf ("%s_edge", topo->topology_name);
2837     xtable = gaiaDoubleQuotedSql (xprefix);
2838     if (tolerance > 0.0)
2839 	sql =
2840 	    sqlite3_mprintf
2841 	    ("SELECT ST_SimplifyPreserveTopology(geom, %1.6f) FROM MAIN.\"%s\" WHERE edge_id = ?",
2842 	     tolerance, xtable, xprefix);
2843     else
2844 	sql = sqlite3_mprintf ("SELECT geom FROM MAIN.\"%s\" WHERE edge_id = ?",
2845 			       xtable, xprefix);
2846     free (xtable);
2847     sqlite3_free (xprefix);
2848     ret =
2849 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_edge,
2850 			    NULL);
2851     sqlite3_free (sql);
2852     if (ret != SQLITE_OK)
2853       {
2854 	  char *msg =
2855 	      sqlite3_mprintf ("TopoGeo_ToGeoTableGeneralize() error: \"%s\"",
2856 			       sqlite3_errmsg (topo->db_handle));
2857 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
2858 	  sqlite3_free (msg);
2859 	  goto error;
2860       }
2861 
2862 /* preparing the Topo-Faces query */
2863     xprefix = sqlite3_mprintf ("%s_edge", topo->topology_name);
2864     xtable = gaiaDoubleQuotedSql (xprefix);
2865     if (tolerance > 0.0)
2866 	sql =
2867 	    sqlite3_mprintf
2868 	    ("SELECT edge_id, left_face, right_face, ST_SimplifyPreserveTopology(geom, %1.6f) FROM MAIN.\"%s\" "
2869 	     "WHERE left_face = ? OR right_face = ?", tolerance, xtable);
2870     else
2871 	sql =
2872 	    sqlite3_mprintf
2873 	    ("SELECT edge_id, left_face, right_face, geom FROM MAIN.\"%s\" "
2874 	     "WHERE left_face = ? OR right_face = ?", xtable);
2875     free (xtable);
2876     sqlite3_free (xprefix);
2877     ret =
2878 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_face,
2879 			    NULL);
2880     sqlite3_free (sql);
2881     if (ret != SQLITE_OK)
2882       {
2883 	  char *msg =
2884 	      sqlite3_mprintf ("TopoGeo_ToGeoTableGeneralize() error: \"%s\"",
2885 			       sqlite3_errmsg (topo->db_handle));
2886 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
2887 	  sqlite3_free (msg);
2888 	  goto error;
2889       }
2890 
2891 /* evaluating feature/topology matching via coincident topo-seeds */
2892     if (!do_eval_topogeo_seeds
2893 	(topo, stmt_ref, ref_geom_col, stmt_ins, stmt_seed_edge, stmt_seed_face,
2894 	 stmt_node, stmt_edge, stmt_face, out_type, tolerance))
2895 	goto error;
2896 
2897     sqlite3_finalize (stmt_ref);
2898     sqlite3_finalize (stmt_ins);
2899     sqlite3_finalize (stmt_seed_edge);
2900     sqlite3_finalize (stmt_seed_face);
2901     sqlite3_finalize (stmt_node);
2902     sqlite3_finalize (stmt_edge);
2903     sqlite3_finalize (stmt_face);
2904     return 1;
2905 
2906   error:
2907     if (create != NULL)
2908 	sqlite3_free (create);
2909     if (select != NULL)
2910 	sqlite3_free (select);
2911     if (insert != NULL)
2912 	sqlite3_free (insert);
2913     if (stmt_ref != NULL)
2914 	sqlite3_finalize (stmt_ref);
2915     if (stmt_ins != NULL)
2916 	sqlite3_finalize (stmt_ins);
2917     if (stmt_seed_edge != NULL)
2918 	sqlite3_finalize (stmt_seed_edge);
2919     if (stmt_seed_face != NULL)
2920 	sqlite3_finalize (stmt_seed_face);
2921     if (stmt_node != NULL)
2922 	sqlite3_finalize (stmt_node);
2923     if (stmt_edge != NULL)
2924 	sqlite3_finalize (stmt_edge);
2925     if (stmt_face != NULL)
2926 	sqlite3_finalize (stmt_face);
2927     return 0;
2928 }
2929 
2930 static int
do_remove_small_faces2(struct gaia_topology * topo,sqlite3_int64 edge_id,sqlite3_stmt * stmt_rem)2931 do_remove_small_faces2 (struct gaia_topology *topo, sqlite3_int64 edge_id,
2932 			sqlite3_stmt * stmt_rem)
2933 {
2934 /* removing an Edge from a Face (step 2) */
2935     int ret;
2936     sqlite3_reset (stmt_rem);
2937     sqlite3_clear_bindings (stmt_rem);
2938     sqlite3_bind_int64 (stmt_rem, 1, edge_id);
2939 
2940     ret = sqlite3_step (stmt_rem);
2941     if (ret == SQLITE_DONE || ret == SQLITE_ROW)
2942 	return 1;
2943     else
2944       {
2945 	  char *msg = sqlite3_mprintf ("TopoGeo_RemoveSmallFaces error: \"%s\"",
2946 				       sqlite3_errmsg (topo->db_handle));
2947 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
2948 	  sqlite3_free (msg);
2949       }
2950     return 0;
2951 }
2952 
2953 static int
do_remove_small_faces1(struct gaia_topology * topo,sqlite3_int64 face_id,sqlite3_stmt * stmt_edge,sqlite3_stmt * stmt_rem)2954 do_remove_small_faces1 (struct gaia_topology *topo, sqlite3_int64 face_id,
2955 			sqlite3_stmt * stmt_edge, sqlite3_stmt * stmt_rem)
2956 {
2957 /* removing the longer Edge from a Face (step 1) */
2958     int ret;
2959     int first = 1;
2960     sqlite3_reset (stmt_edge);
2961     sqlite3_clear_bindings (stmt_edge);
2962     sqlite3_bind_int64 (stmt_edge, 1, face_id);
2963     sqlite3_bind_int64 (stmt_edge, 2, face_id);
2964 
2965     while (1)
2966       {
2967 	  /* scrolling the result set rows */
2968 	  ret = sqlite3_step (stmt_edge);
2969 	  if (ret == SQLITE_DONE)
2970 	      break;		/* end of result set */
2971 	  if (ret == SQLITE_ROW)
2972 	    {
2973 		sqlite3_int64 edge_id = sqlite3_column_int64 (stmt_edge, 0);
2974 		if (first)
2975 		  {
2976 		      first = 0;
2977 		      if (do_remove_small_faces2 (topo, edge_id, stmt_rem))
2978 			  goto error;
2979 		  }
2980 	    }
2981 	  else
2982 	    {
2983 		char *msg =
2984 		    sqlite3_mprintf ("TopoGeo_RemoveSmallFaces error: \"%s\"",
2985 				     sqlite3_errmsg (topo->db_handle));
2986 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
2987 					     msg);
2988 		sqlite3_free (msg);
2989 		goto error;
2990 	    }
2991       }
2992     return 1;
2993 
2994   error:
2995     return 0;
2996 
2997 }
2998 
2999 GAIATOPO_DECLARE int
gaiaTopoGeo_RemoveSmallFaces(GaiaTopologyAccessorPtr accessor,double min_circularity,double min_area)3000 gaiaTopoGeo_RemoveSmallFaces (GaiaTopologyAccessorPtr accessor,
3001 			      double min_circularity, double min_area)
3002 {
3003 /*
3004 / attempting to remove all small faces from a Topology-Geometry
3005 */
3006     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3007     sqlite3_stmt *stmt_rem = NULL;
3008     sqlite3_stmt *stmt_face = NULL;
3009     sqlite3_stmt *stmt_edge = NULL;
3010     int ret;
3011     char *sql;
3012     char *table;
3013     char *xtable;
3014     int count;
3015     if (topo == NULL)
3016 	return 0;
3017 
3018 /* preparing the SELECT Face query */
3019     table = sqlite3_mprintf ("%s_face", topo->topology_name);
3020     xtable = gaiaDoubleQuotedSql (table);
3021     sqlite3_free (table);
3022     if (min_circularity < 1.0 && min_area > 0.0)
3023       {
3024 	  sql = sqlite3_mprintf ("SELECT face_id FROM (SELECT face_id, "
3025 				 "ST_GetFaceGeometry(%Q, face_id) AS geom FROM MAIN.\"%s\" "
3026 				 "WHERE face_id > 0) WHERE Circularity(geom) < %1.12f "
3027 				 "AND ST_Area(geom) < %1.12f",
3028 				 topo->topology_name, xtable, min_circularity,
3029 				 min_area);
3030       }
3031     else if (min_circularity >= 1.0 && min_area > 0.0)
3032       {
3033 	  sql =
3034 	      sqlite3_mprintf
3035 	      ("SELECT face_id FROM MAIN.\"%s\" WHERE face_id > 0 "
3036 	       "AND ST_Area(ST_GetFaceGeometry(%Q, face_id)) < %1.12f", xtable,
3037 	       topo->topology_name, min_area);
3038       }
3039     else if (min_circularity < 1.0 && min_area <= 0.0)
3040       {
3041 	  sql =
3042 	      sqlite3_mprintf
3043 	      ("SELECT face_id FROM MAIN.\"%s\" WHERE face_id > 0 "
3044 	       "AND Circularity(ST_GetFaceGeometry(%Q, face_id)) < %1.12f",
3045 	       xtable, topo->topology_name, min_circularity);
3046       }
3047     else
3048       {
3049 	  free (xtable);
3050 	  return 0;
3051       }
3052     free (xtable);
3053     ret =
3054 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_face,
3055 			    NULL);
3056     sqlite3_free (sql);
3057     sql = NULL;
3058     if (ret != SQLITE_OK)
3059       {
3060 	  char *msg =
3061 	      sqlite3_mprintf ("TopoGeo_RemoveSmallFaces() error: \"%s\"",
3062 			       sqlite3_errmsg (topo->db_handle));
3063 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
3064 	  sqlite3_free (msg);
3065 	  goto error;
3066       }
3067 
3068 /* preparing the SELECT Edge query */
3069     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
3070     xtable = gaiaDoubleQuotedSql (table);
3071     sqlite3_free (table);
3072     sql =
3073 	sqlite3_mprintf ("SELECT edge_id FROM MAIN.\"%s\" WHERE right_face = ? "
3074 			 "OR left_face = ? ORDER BY ST_Length(geom) DESC",
3075 			 xtable);
3076     free (xtable);
3077     ret =
3078 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_edge,
3079 			    NULL);
3080     sqlite3_free (sql);
3081     sql = NULL;
3082     if (ret != SQLITE_OK)
3083       {
3084 	  char *msg =
3085 	      sqlite3_mprintf ("TopoGeo_RemoveSmallFaces() error: \"%s\"",
3086 			       sqlite3_errmsg (topo->db_handle));
3087 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
3088 	  sqlite3_free (msg);
3089 	  goto error;
3090       }
3091 
3092 /* preparing the ST_RemEdgeNewFace() query */
3093     sql =
3094 	sqlite3_mprintf ("SELECT ST_RemEdgeNewFace(%Q, ?)",
3095 			 topo->topology_name);
3096     ret =
3097 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_rem,
3098 			    NULL);
3099     sqlite3_free (sql);
3100     sql = NULL;
3101     if (ret != SQLITE_OK)
3102       {
3103 	  char *msg =
3104 	      sqlite3_mprintf ("TopoGeo_RemoveSmallFaces() error: \"%s\"",
3105 			       sqlite3_errmsg (topo->db_handle));
3106 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
3107 	  sqlite3_free (msg);
3108 	  goto error;
3109       }
3110 
3111     count = 1;
3112     while (count)
3113       {
3114 	  sqlite3_reset (stmt_face);
3115 	  sqlite3_clear_bindings (stmt_face);
3116 	  count = 0;
3117 	  while (1)
3118 	    {
3119 		/* scrolling the result set rows */
3120 		ret = sqlite3_step (stmt_face);
3121 		if (ret == SQLITE_DONE)
3122 		    break;	/* end of result set */
3123 		if (ret == SQLITE_ROW)
3124 		  {
3125 		      sqlite3_int64 face_id =
3126 			  sqlite3_column_int64 (stmt_face, 0);
3127 		      if (do_remove_small_faces1
3128 			  (topo, face_id, stmt_edge, stmt_rem))
3129 			  goto error;
3130 		      count++;
3131 		  }
3132 		else
3133 		  {
3134 		      char *msg =
3135 			  sqlite3_mprintf
3136 			  ("TopoGeo_RemoveSmallFaces error: \"%s\"",
3137 			   sqlite3_errmsg (topo->db_handle));
3138 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
3139 						   topo, msg);
3140 		      sqlite3_free (msg);
3141 		      goto error;
3142 		  }
3143 	    }
3144       }
3145 
3146     sqlite3_finalize (stmt_face);
3147     sqlite3_finalize (stmt_edge);
3148     sqlite3_finalize (stmt_rem);
3149     return 1;
3150 
3151   error:
3152     if (sql != NULL)
3153 	sqlite3_free (sql);
3154     if (stmt_face != NULL)
3155 	sqlite3_finalize (stmt_face);
3156     if (stmt_edge != NULL)
3157 	sqlite3_finalize (stmt_edge);
3158     if (stmt_rem != NULL)
3159 	sqlite3_finalize (stmt_rem);
3160     return 0;
3161 }
3162 
3163 GAIATOPO_DECLARE int
gaiaTopoGeo_RemoveDanglingEdges(GaiaTopologyAccessorPtr accessor)3164 gaiaTopoGeo_RemoveDanglingEdges (GaiaTopologyAccessorPtr accessor)
3165 {
3166 /*
3167 / attempting to remove all dangling edges from a Topology-Geometry
3168 */
3169     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3170     int ret;
3171     char *sql;
3172     char *table;
3173     char *xtable;
3174     char *err_msg = NULL;
3175     if (topo == NULL)
3176 	return 0;
3177 
3178 /* preparing the ST_RemEdgeNewFace() query */
3179     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
3180     xtable = gaiaDoubleQuotedSql (table);
3181     sqlite3_free (table);
3182     sql =
3183 	sqlite3_mprintf
3184 	("SELECT ST_RemEdgeNewFace(%Q, edge_id) FROM MAIN.\"%s\" "
3185 	 "WHERE left_face = right_face", topo->topology_name, xtable);
3186     free (xtable);
3187     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &err_msg);
3188     sqlite3_free (sql);
3189     if (ret != SQLITE_OK)
3190       {
3191 	  char *msg =
3192 	      sqlite3_mprintf ("TopoGeo_RemoveDanglingEdges error: \"%s\"",
3193 			       err_msg);
3194 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
3195 	  sqlite3_free (err_msg);
3196 	  sqlite3_free (msg);
3197 	  return 0;
3198       }
3199     return 1;
3200 }
3201 
3202 GAIATOPO_DECLARE int
gaiaTopoGeo_RemoveDanglingNodes(GaiaTopologyAccessorPtr accessor)3203 gaiaTopoGeo_RemoveDanglingNodes (GaiaTopologyAccessorPtr accessor)
3204 {
3205 /*
3206 / attempting to remove all dangling nodes from a Topology-Geometry
3207 */
3208     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3209     int ret;
3210     char *sql;
3211     char *table;
3212     char *xtable;
3213     char *err_msg = NULL;
3214     if (topo == NULL)
3215 	return 0;
3216 
3217 /* preparing the ST_RemIsoNode() query */
3218     table = sqlite3_mprintf ("%s_node", topo->topology_name);
3219     xtable = gaiaDoubleQuotedSql (table);
3220     sqlite3_free (table);
3221     sql =
3222 	sqlite3_mprintf ("SELECT ST_RemIsoNode(%Q, node_id) FROM MAIN.\"%s\" "
3223 			 "WHERE containing_face IS NOT NULL",
3224 			 topo->topology_name, xtable);
3225     free (xtable);
3226     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &err_msg);
3227     sqlite3_free (sql);
3228     if (ret != SQLITE_OK)
3229       {
3230 	  char *msg =
3231 	      sqlite3_mprintf ("TopoGeo_RemoveDanglingNodes error: \"%s\"",
3232 			       err_msg);
3233 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
3234 	  sqlite3_free (err_msg);
3235 	  sqlite3_free (msg);
3236 	  return 0;
3237       }
3238     return 1;
3239 }
3240 
3241 GAIATOPO_DECLARE int
gaiaTopoGeo_ToGeoTable(GaiaTopologyAccessorPtr accessor,const char * db_prefix,const char * ref_table,const char * ref_column,const char * out_table,int with_spatial_index)3242 gaiaTopoGeo_ToGeoTable (GaiaTopologyAccessorPtr accessor,
3243 			const char *db_prefix, const char *ref_table,
3244 			const char *ref_column, const char *out_table,
3245 			int with_spatial_index)
3246 {
3247 /* attempting to create and populate a new GeoTable out from a Topology-Geometry */
3248     return gaiaTopoGeo_ToGeoTableGeneralize (accessor, db_prefix, ref_table,
3249 					     ref_column, out_table, -1.0,
3250 					     with_spatial_index);
3251 }
3252 
3253 SPATIALITE_PRIVATE int
gaia_do_eval_disjoint(const void * handle,const char * matrix)3254 gaia_do_eval_disjoint (const void *handle, const char *matrix)
3255 {
3256 /* same as ST_Disjoint */
3257     sqlite3 *sqlite = (sqlite3 *) handle;
3258     char **results;
3259     int ret;
3260     int rows;
3261     int columns;
3262     int i;
3263     int value = 0;
3264     char *sql =
3265 	sqlite3_mprintf ("SELECT ST_RelateMatch(%Q, 'FF*FF****')", matrix);
3266     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
3267     sqlite3_free (sql);
3268     if (ret != SQLITE_OK)
3269 	return 0;
3270     for (i = 1; i <= rows; i++)
3271 	value = atoi (results[(i * columns) + 0]);
3272     sqlite3_free_table (results);
3273     return value;
3274 }
3275 
3276 SPATIALITE_PRIVATE int
gaia_do_eval_overlaps(const void * handle,const char * matrix)3277 gaia_do_eval_overlaps (const void *handle, const char *matrix)
3278 {
3279 /* same as ST_Overlaps */
3280     sqlite3 *sqlite = (sqlite3 *) handle;
3281     char **results;
3282     int ret;
3283     int rows;
3284     int columns;
3285     int i;
3286     int value = 0;
3287     char *sql = sqlite3_mprintf ("SELECT ST_RelateMatch(%Q, 'T*T***T**') "
3288 				 "OR ST_RelateMatch(%Q, '1*T***T**')", matrix,
3289 				 matrix);
3290     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
3291     sqlite3_free (sql);
3292     if (ret != SQLITE_OK)
3293 	return 0;
3294     for (i = 1; i <= rows; i++)
3295 	value = atoi (results[(i * columns) + 0]);
3296     sqlite3_free_table (results);
3297     return value;
3298 }
3299 
3300 SPATIALITE_PRIVATE int
gaia_do_eval_covers(const void * handle,const char * matrix)3301 gaia_do_eval_covers (const void *handle, const char *matrix)
3302 {
3303 /* same as ST_Covers */
3304     sqlite3 *sqlite = (sqlite3 *) handle;
3305     char **results;
3306     int ret;
3307     int rows;
3308     int columns;
3309     int i;
3310     int value = 0;
3311     char *sql = sqlite3_mprintf ("SELECT ST_RelateMatch(%Q, 'T*****FF*') "
3312 				 "OR ST_RelateMatch(%Q, '*T****FF*') OR ST_RelateMatch(%Q, '***T**FF*') "
3313 				 "OR ST_RelateMatch(%Q, '****T*FF*')", matrix,
3314 				 matrix, matrix, matrix);
3315     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
3316     sqlite3_free (sql);
3317     if (ret != SQLITE_OK)
3318 	return 0;
3319     for (i = 1; i <= rows; i++)
3320 	value = atoi (results[(i * columns) + 0]);
3321     sqlite3_free_table (results);
3322     return value;
3323 }
3324 
3325 SPATIALITE_PRIVATE int
gaia_do_eval_covered_by(const void * handle,const char * matrix)3326 gaia_do_eval_covered_by (const void *handle, const char *matrix)
3327 {
3328 /* same as ST_CoveredBy */
3329     sqlite3 *sqlite = (sqlite3 *) handle;
3330     char **results;
3331     int ret;
3332     int rows;
3333     int columns;
3334     int i;
3335     int value = 0;
3336     char *sql = sqlite3_mprintf ("SELECT ST_RelateMatch(%Q, 'T*F**F***') "
3337 				 "OR ST_RelateMatch(%Q, '*TF**F***') OR ST_RelateMatch(%Q, '**FT*F***') "
3338 				 "OR ST_RelateMatch(%Q, '**F*TF***')", matrix,
3339 				 matrix, matrix, matrix);
3340     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
3341     sqlite3_free (sql);
3342     if (ret != SQLITE_OK)
3343 	return 0;
3344     for (i = 1; i <= rows; i++)
3345 	value = atoi (results[(i * columns) + 0]);
3346     sqlite3_free_table (results);
3347     return value;
3348 }
3349 
3350 static int
find_polyface_matches(struct gaia_topology * topo,sqlite3_stmt * stmt_ref,sqlite3_stmt * stmt_ins,sqlite3_int64 face_id,sqlite3_int64 containing_face)3351 find_polyface_matches (struct gaia_topology *topo, sqlite3_stmt * stmt_ref,
3352 		       sqlite3_stmt * stmt_ins, sqlite3_int64 face_id,
3353 		       sqlite3_int64 containing_face)
3354 {
3355 /* retrieving PolyFace matches */
3356     int ret;
3357     int count = 0;
3358 
3359     sqlite3_reset (stmt_ref);
3360     sqlite3_clear_bindings (stmt_ref);
3361     sqlite3_bind_int64 (stmt_ref, 1, face_id);
3362 
3363     while (1)
3364       {
3365 	  /* scrolling the result set rows - Spatial Relationships */
3366 	  ret = sqlite3_step (stmt_ref);
3367 	  if (ret == SQLITE_DONE)
3368 	      break;		/* end of result set */
3369 	  if (ret == SQLITE_ROW)
3370 	    {
3371 		sqlite3_int64 rowid = sqlite3_column_int64 (stmt_ref, 0);
3372 
3373 		sqlite3_reset (stmt_ins);
3374 		sqlite3_clear_bindings (stmt_ins);
3375 		sqlite3_bind_int64 (stmt_ins, 1, face_id);
3376 		if (containing_face <= 0)
3377 		  {
3378 		      sqlite3_bind_int (stmt_ins, 2, 0);
3379 		      sqlite3_bind_null (stmt_ins, 3);
3380 		  }
3381 		else
3382 		  {
3383 		      sqlite3_bind_int (stmt_ins, 2, 1);
3384 		      sqlite3_bind_int64 (stmt_ins, 3, containing_face);
3385 		  }
3386 		sqlite3_bind_int64 (stmt_ins, 4, rowid);
3387 		/* inserting a row into the output table */
3388 		ret = sqlite3_step (stmt_ins);
3389 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3390 		    count++;
3391 		else
3392 		  {
3393 		      char *msg =
3394 			  sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3395 					   sqlite3_errmsg (topo->db_handle));
3396 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
3397 						   topo, msg);
3398 		      sqlite3_free (msg);
3399 		      return 0;
3400 		  }
3401 	    }
3402 	  else
3403 	    {
3404 		char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3405 					     sqlite3_errmsg (topo->db_handle));
3406 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
3407 					     msg);
3408 		sqlite3_free (msg);
3409 		return 0;
3410 	    }
3411       }
3412 
3413     if (count == 0)
3414       {
3415 	  /* unrelated Face */
3416 	  sqlite3_reset (stmt_ins);
3417 	  sqlite3_clear_bindings (stmt_ins);
3418 	  sqlite3_bind_int64 (stmt_ins, 1, face_id);
3419 	  if (containing_face <= 0)
3420 	    {
3421 		sqlite3_bind_int (stmt_ins, 2, 0);
3422 		sqlite3_bind_null (stmt_ins, 3);
3423 	    }
3424 	  else
3425 	    {
3426 		sqlite3_bind_int (stmt_ins, 2, 1);
3427 		sqlite3_bind_int64 (stmt_ins, 3, containing_face);
3428 	    }
3429 	  sqlite3_bind_null (stmt_ins, 4);
3430 	  /* inserting a row into the output table */
3431 	  ret = sqlite3_step (stmt_ins);
3432 	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3433 	      ;
3434 	  else
3435 	    {
3436 		char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3437 					     sqlite3_errmsg (topo->db_handle));
3438 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
3439 					     msg);
3440 		sqlite3_free (msg);
3441 		return 0;
3442 	    }
3443       }
3444     return 1;
3445 }
3446 
3447 static int
insert_polyface_reverse(struct gaia_topology * topo,sqlite3_stmt * stmt_ins,sqlite3_int64 polygon_id)3448 insert_polyface_reverse (struct gaia_topology *topo, sqlite3_stmt * stmt_ins,
3449 			 sqlite3_int64 polygon_id)
3450 {
3451 /* found a mismatching RefPolygon - inserting into the output table */
3452     int ret;
3453 
3454     sqlite3_reset (stmt_ins);
3455     sqlite3_clear_bindings (stmt_ins);
3456     sqlite3_bind_null (stmt_ins, 1);
3457     sqlite3_bind_int (stmt_ins, 2, 0);
3458     sqlite3_bind_null (stmt_ins, 3);
3459     sqlite3_bind_int64 (stmt_ins, 4, polygon_id);
3460     /* inserting a row into the output table */
3461     ret = sqlite3_step (stmt_ins);
3462     if (ret == SQLITE_DONE || ret == SQLITE_ROW)
3463 	;
3464     else
3465       {
3466 	  char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3467 				       sqlite3_errmsg (topo->db_handle));
3468 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
3469 	  sqlite3_free (msg);
3470 	  return 0;
3471       }
3472     return 1;
3473 }
3474 
3475 SPATIALITE_PRIVATE int
gaia_check_spatial_index(const void * handle,const char * db_prefix,const char * ref_table,const char * ref_column)3476 gaia_check_spatial_index (const void *handle, const char *db_prefix,
3477 			  const char *ref_table, const char *ref_column)
3478 {
3479 /* testing if the RefTable has an R*Tree Spatial Index */
3480     sqlite3 *sqlite = (sqlite3 *) handle;
3481     char *sql;
3482     char *xprefix;
3483     int has_rtree = 0;
3484     char **results;
3485     int ret;
3486     int rows;
3487     int columns;
3488     int i;
3489     char *errMsg = NULL;
3490 
3491     xprefix = gaiaDoubleQuotedSql (db_prefix);
3492     sql =
3493 	sqlite3_mprintf
3494 	("SELECT spatial_index_enabled FROM \"%s\".geometry_columns "
3495 	 "WHERE f_table_name = %Q AND f_geometry_column = %Q", xprefix,
3496 	 ref_table, ref_column);
3497     free (xprefix);
3498     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
3499     sqlite3_free (sql);
3500     if (ret != SQLITE_OK)
3501 	return 0;
3502     for (i = 1; i <= rows; i++)
3503       {
3504 	  has_rtree = atoi (results[(i * columns) + 0]);
3505       }
3506     sqlite3_free_table (results);
3507     return has_rtree;
3508 }
3509 
3510 GAIATOPO_DECLARE int
gaiaTopoGeo_PolyFacesList(GaiaTopologyAccessorPtr accessor,const char * db_prefix,const char * ref_table,const char * ref_column,const char * out_table)3511 gaiaTopoGeo_PolyFacesList (GaiaTopologyAccessorPtr accessor,
3512 			   const char *db_prefix, const char *ref_table,
3513 			   const char *ref_column, const char *out_table)
3514 {
3515 /* creating and populating a new Table reporting about Faces/Polygon correspondencies */
3516     struct gaia_topology *topo = (struct gaia_topology *) accessor;
3517     sqlite3_stmt *stmt_holes = NULL;
3518     sqlite3_stmt *stmt_ref = NULL;
3519     sqlite3_stmt *stmt_rev = NULL;
3520     sqlite3_stmt *stmt_ins = NULL;
3521     int ret;
3522     char *sql;
3523     char *face;
3524     char *table;
3525     char *idx_name;
3526     char *xface;
3527     char *xtable;
3528     char *xprefix;
3529     char *xcolumn;
3530     char *xidx_name;
3531     char *rtree_name;
3532     char *seeds;
3533     char *xseeds;
3534     int ref_has_spatial_index = 0;
3535     if (topo == NULL)
3536 	return 0;
3537 
3538 /* attempting to build the output table */
3539     xtable = gaiaDoubleQuotedSql (out_table);
3540     sql = sqlite3_mprintf ("CREATE TABLE main.\"%s\" (\n"
3541 			   "\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n"
3542 			   "\tface_id INTEGER,\n"
3543 			   "\tis_hole INTEGER NOT NULL,\n"
3544 			   "\tcontaining_face INTEGER,\n"
3545 			   "\tref_rowid INTEGER)", xtable);
3546     free (xtable);
3547     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, NULL);
3548     sqlite3_free (sql);
3549     if (ret != SQLITE_OK)
3550       {
3551 	  char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3552 				       sqlite3_errmsg (topo->db_handle));
3553 	  gaiatopo_set_last_error_msg (accessor, msg);
3554 	  sqlite3_free (msg);
3555 	  goto error;
3556       }
3557     idx_name = sqlite3_mprintf ("idx_%s_face_id", out_table);
3558     xidx_name = gaiaDoubleQuotedSql (idx_name);
3559     sqlite3_free (idx_name);
3560     xtable = gaiaDoubleQuotedSql (out_table);
3561     sql =
3562 	sqlite3_mprintf
3563 	("CREATE INDEX main.\"%s\" ON \"%s\" (face_id, ref_rowid)", xidx_name,
3564 	 xtable);
3565     free (xidx_name);
3566     free (xtable);
3567     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, NULL);
3568     sqlite3_free (sql);
3569     if (ret != SQLITE_OK)
3570       {
3571 	  char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3572 				       sqlite3_errmsg (topo->db_handle));
3573 	  gaiatopo_set_last_error_msg (accessor, msg);
3574 	  sqlite3_free (msg);
3575 	  goto error;
3576       }
3577     idx_name = sqlite3_mprintf ("idx_%s_holes", out_table);
3578     xidx_name = gaiaDoubleQuotedSql (idx_name);
3579     sqlite3_free (idx_name);
3580     xtable = gaiaDoubleQuotedSql (out_table);
3581     sql =
3582 	sqlite3_mprintf
3583 	("CREATE INDEX main.\"%s\" ON \"%s\" (containing_face, face_id)",
3584 	 xidx_name, xtable);
3585     free (xidx_name);
3586     free (xtable);
3587     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, NULL);
3588     sqlite3_free (sql);
3589     if (ret != SQLITE_OK)
3590       {
3591 	  char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3592 				       sqlite3_errmsg (topo->db_handle));
3593 	  gaiatopo_set_last_error_msg (accessor, msg);
3594 	  sqlite3_free (msg);
3595 	  goto error;
3596       }
3597 
3598 /* building the IsHole SQL statement */
3599     face = sqlite3_mprintf ("%s_face", topo->topology_name);
3600     xface = gaiaDoubleQuotedSql (face);
3601     sqlite3_free (face);
3602     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
3603     xtable = gaiaDoubleQuotedSql (table);
3604     sqlite3_free (table);
3605     sql =
3606 	sqlite3_mprintf
3607 	("SELECT f.face_id, Count(DISTINCT r.left_face) AS cnt1, "
3608 	 "Count(DISTINCT l.right_face) AS cnt2, r.left_face, l.right_face "
3609 	 "FROM main.\"%s\" AS f "
3610 	 "LEFT JOIN main.\"%s\" AS r ON (f.face_id = r.right_face) "
3611 	 "LEFT JOIN main.\"%s\" AS l ON (f.face_id = l.left_face) "
3612 	 "GROUP BY f.face_id", xface, xtable, xtable);
3613     free (xface);
3614     free (xtable);
3615     ret =
3616 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_holes,
3617 			    NULL);
3618     sqlite3_free (sql);
3619     if (ret != SQLITE_OK)
3620       {
3621 	  char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3622 				       sqlite3_errmsg (topo->db_handle));
3623 	  gaiatopo_set_last_error_msg (accessor, msg);
3624 	  sqlite3_free (msg);
3625 	  goto error;
3626       }
3627 
3628 /* building the RefTable SQL statement */
3629     seeds = sqlite3_mprintf ("%s_seeds", topo->topology_name);
3630     rtree_name = sqlite3_mprintf ("DB=%s.%s", db_prefix, ref_table);
3631     ref_has_spatial_index =
3632 	gaia_check_spatial_index (topo->db_handle, db_prefix, ref_table,
3633 				  ref_column);
3634     xprefix = gaiaDoubleQuotedSql (db_prefix);
3635     xtable = gaiaDoubleQuotedSql (ref_table);
3636     xcolumn = gaiaDoubleQuotedSql (ref_column);
3637     xseeds = gaiaDoubleQuotedSql (seeds);
3638     if (ref_has_spatial_index)
3639 	sql =
3640 	    sqlite3_mprintf
3641 	    ("SELECT r.rowid FROM MAIN.\"%s\" AS s, \"%s\".\"%s\" AS r "
3642 	     "WHERE ST_Intersects(r.\"%s\", s.geom) == 1 AND s.face_id = ? "
3643 	     "AND r.rowid IN (SELECT rowid FROM SpatialIndex WHERE f_table_name = %Q "
3644 	     "AND f_geometry_column = %Q AND search_frame = s.geom)", xseeds,
3645 	     xprefix, xtable, xcolumn, rtree_name, xcolumn);
3646     else
3647 	sql =
3648 	    sqlite3_mprintf
3649 	    ("SELECT r.rowid FROM MAIN.\"%s\" AS s, \"%s\".\"%s\" AS r "
3650 	     "WHERE  ST_Intersects(r.\"%s\", s.geom) == 1 AND s.face_id = ?",
3651 	     xseeds, xprefix, xtable, xcolumn);
3652     free (xprefix);
3653     free (xtable);
3654     free (xcolumn);
3655     free (xseeds);
3656     sqlite3_free (rtree_name);
3657     sqlite3_free (seeds);
3658     ret =
3659 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_ref,
3660 			    NULL);
3661     sqlite3_free (sql);
3662     if (ret != SQLITE_OK)
3663       {
3664 	  char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3665 				       sqlite3_errmsg (topo->db_handle));
3666 	  gaiatopo_set_last_error_msg (accessor, msg);
3667 	  sqlite3_free (msg);
3668 	  goto error;
3669       }
3670 
3671 /* building the Reverse RefTable SQL statement */
3672     seeds = sqlite3_mprintf ("%s_seeds", topo->topology_name);
3673     rtree_name =
3674 	sqlite3_mprintf ("DB=%s.%s_seeds", db_prefix, topo->topology_name);
3675     xprefix = gaiaDoubleQuotedSql (db_prefix);
3676     xtable = gaiaDoubleQuotedSql (ref_table);
3677     xcolumn = gaiaDoubleQuotedSql (ref_column);
3678     xseeds = gaiaDoubleQuotedSql (seeds);
3679     sql = sqlite3_mprintf ("SELECT r.rowid FROM \"%s\".\"%s\" AS r "
3680 			   "LEFT JOIN MAIN.\"%s\" AS s ON (ST_Intersects(r.\"%s\", s.geom) = 1 "
3681 			   "AND s.face_id IS NOT NULL AND s.rowid IN (SELECT rowid FROM SpatialIndex "
3682 			   "WHERE f_table_name = %Q AND search_frame = r.\"%s\")) "
3683 			   "WHERE s.face_id IS NULL", xprefix, xtable, xseeds,
3684 			   xcolumn, rtree_name, xcolumn);
3685     free (xprefix);
3686     free (xtable);
3687     free (xcolumn);
3688     free (xseeds);
3689     sqlite3_free (rtree_name);
3690     sqlite3_free (seeds);
3691     ret =
3692 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_rev,
3693 			    NULL);
3694     sqlite3_free (sql);
3695     if (ret != SQLITE_OK)
3696       {
3697 	  char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3698 				       sqlite3_errmsg (topo->db_handle));
3699 	  gaiatopo_set_last_error_msg (accessor, msg);
3700 	  sqlite3_free (msg);
3701 	  goto error;
3702       }
3703 
3704 /* building the Insert SQL statement */
3705     xtable = gaiaDoubleQuotedSql (out_table);
3706     sql = sqlite3_mprintf ("INSERT INTO main.\"%s\" (id, face_id, is_hole, "
3707 			   "containing_face, ref_rowid) "
3708 			   "VALUES (NULL, ?, ?, ?, ?)", xtable);
3709     free (xtable);
3710     ret =
3711 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_ins,
3712 			    NULL);
3713     sqlite3_free (sql);
3714     if (ret != SQLITE_OK)
3715       {
3716 	  char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3717 				       sqlite3_errmsg (topo->db_handle));
3718 	  gaiatopo_set_last_error_msg (accessor, msg);
3719 	  sqlite3_free (msg);
3720 	  goto error;
3721       }
3722 
3723     while (1)
3724       {
3725 	  /* scrolling the result set rows - Faces/Holes */
3726 	  ret = sqlite3_step (stmt_holes);
3727 	  if (ret == SQLITE_DONE)
3728 	      break;		/* end of result set */
3729 	  if (ret == SQLITE_ROW)
3730 	    {
3731 		sqlite3_int64 containing_face = -1;
3732 		sqlite3_int64 face_id = sqlite3_column_int64 (stmt_holes, 0);
3733 		int count1 = sqlite3_column_int (stmt_holes, 1);
3734 		int count2 = sqlite3_column_int (stmt_holes, 2);
3735 		if (count1 == 1 && count2 == 1)
3736 		  {
3737 		      sqlite3_int64 id1 = sqlite3_column_int64 (stmt_holes, 3);
3738 		      sqlite3_int64 id2 = sqlite3_column_int64 (stmt_holes, 4);
3739 		      if (id1 == id2)
3740 			  containing_face = id1;
3741 		  }
3742 		else if (count1 == 1 && count2 == 0)
3743 		    containing_face = sqlite3_column_int64 (stmt_holes, 3);
3744 		else if (count1 == 0 && count2 == 1)
3745 		    containing_face = sqlite3_column_int64 (stmt_holes, 4);
3746 		if (!find_polyface_matches
3747 		    (topo, stmt_ref, stmt_ins, face_id, containing_face))
3748 		    goto error;
3749 	    }
3750 	  else
3751 	    {
3752 		char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3753 					     sqlite3_errmsg (topo->db_handle));
3754 		gaiatopo_set_last_error_msg (accessor, msg);
3755 		sqlite3_free (msg);
3756 		goto error;
3757 	    }
3758       }
3759 
3760     while (1)
3761       {
3762 	  /* scrolling the Reverse result set rows - Polygons */
3763 	  ret = sqlite3_step (stmt_rev);
3764 	  if (ret == SQLITE_DONE)
3765 	      break;		/* end of result set */
3766 	  if (ret == SQLITE_ROW)
3767 	    {
3768 		sqlite3_int64 polyg_id = sqlite3_column_int64 (stmt_rev, 0);
3769 		if (!insert_polyface_reverse (topo, stmt_ins, polyg_id))
3770 		    goto error;
3771 	    }
3772 	  else
3773 	    {
3774 		char *msg = sqlite3_mprintf ("PolyFacesList error: \"%s\"",
3775 					     sqlite3_errmsg (topo->db_handle));
3776 		gaiatopo_set_last_error_msg (accessor, msg);
3777 		sqlite3_free (msg);
3778 		goto error;
3779 	    }
3780       }
3781 
3782     sqlite3_finalize (stmt_holes);
3783     sqlite3_finalize (stmt_ref);
3784     sqlite3_finalize (stmt_rev);
3785     sqlite3_finalize (stmt_ins);
3786     return 1;
3787 
3788   error:
3789     if (stmt_holes != NULL)
3790 	sqlite3_finalize (stmt_holes);
3791     if (stmt_ref != NULL)
3792 	sqlite3_finalize (stmt_ref);
3793     if (stmt_rev != NULL)
3794 	sqlite3_finalize (stmt_rev);
3795     if (stmt_ins != NULL)
3796 	sqlite3_finalize (stmt_ins);
3797     return 0;
3798 }
3799 
3800 static int
do_find_matching_point(gaiaLinestringPtr ln1,int * idx1,gaiaLinestringPtr ln2,int * idx2)3801 do_find_matching_point (gaiaLinestringPtr ln1, int *idx1, gaiaLinestringPtr ln2,
3802 			int *idx2)
3803 {
3804 /* searching for a common point in both Linestrings */
3805     int i1;
3806     int i2;
3807     double x1;
3808     double y1;
3809     double z1;
3810     double m1;
3811     double x2;
3812     double y2;
3813     double z2;
3814     double m2;
3815     for (i1 = 0; i1 < ln1->Points; i1++)
3816       {
3817 	  /* extracting a Vertex from the first Linestring */
3818 	  z1 = 0.0;
3819 	  m1 = 0.0;
3820 	  if (ln1->DimensionModel == GAIA_XY_Z)
3821 	    {
3822 		gaiaGetPointXYZ (ln1->Coords, i1, &x1, &y1, &z1);
3823 	    }
3824 	  else if (ln1->DimensionModel == GAIA_XY_M)
3825 	    {
3826 		gaiaGetPointXYM (ln1->Coords, i1, &x1, &y1, &m1);
3827 	    }
3828 	  else if (ln1->DimensionModel == GAIA_XY_Z_M)
3829 	    {
3830 		gaiaGetPointXYZM (ln1->Coords, i1, &x1, &y1, &z1, &m1);
3831 	    }
3832 	  else
3833 	    {
3834 		gaiaGetPoint (ln1->Coords, i1, &x1, &y1);
3835 	    }
3836 	  for (i2 = 0; i2 < ln2->Points; i2++)
3837 	    {
3838 		/* extracting a Vertex from the second Linestring */
3839 		z2 = 0.0;
3840 		m2 = 0.0;
3841 		if (ln2->DimensionModel == GAIA_XY_Z)
3842 		  {
3843 		      gaiaGetPointXYZ (ln2->Coords, i2, &x2, &y2, &z2);
3844 		  }
3845 		else if (ln2->DimensionModel == GAIA_XY_M)
3846 		  {
3847 		      gaiaGetPointXYM (ln2->Coords, i2, &x2, &y2, &m2);
3848 		  }
3849 		else if (ln2->DimensionModel == GAIA_XY_Z_M)
3850 		  {
3851 		      gaiaGetPointXYZM (ln2->Coords, i2, &x2, &y2, &z2, &m2);
3852 		  }
3853 		else
3854 		  {
3855 		      gaiaGetPoint (ln2->Coords, i2, &x2, &y2);
3856 		  }
3857 		if (x1 == x2 && y1 == y2 && z1 == z2 && m1 == m2)
3858 		  {
3859 		      *idx1 = i1;
3860 		      *idx2 = i2;
3861 		      return 1;
3862 		  }
3863 	    }
3864       }
3865     *idx1 = -1;
3866     *idx2 = -1;
3867     return 0;
3868 }
3869 
3870 static int
do_check_forward(gaiaLinestringPtr ln1,int idx1,gaiaLinestringPtr ln2,int idx2)3871 do_check_forward (gaiaLinestringPtr ln1, int idx1, gaiaLinestringPtr ln2,
3872 		  int idx2)
3873 {
3874 /* testing for further matching Vertices (same directions) */
3875     int i1;
3876     int i2;
3877     double x1;
3878     double y1;
3879     double z1;
3880     double m1;
3881     double x2;
3882     double y2;
3883     double z2;
3884     double m2;
3885     int count = 0;
3886     for (i1 = idx1; i1 < ln1->Points; i1++)
3887       {
3888 	  /* extracting a Vertex from the first Linestring */
3889 	  z1 = 0.0;
3890 	  m1 = 0.0;
3891 	  if (ln1->DimensionModel == GAIA_XY_Z)
3892 	    {
3893 		gaiaGetPointXYZ (ln1->Coords, i1, &x1, &y1, &z1);
3894 	    }
3895 	  else if (ln1->DimensionModel == GAIA_XY_M)
3896 	    {
3897 		gaiaGetPointXYM (ln1->Coords, i1, &x1, &y1, &m1);
3898 	    }
3899 	  else if (ln1->DimensionModel == GAIA_XY_Z_M)
3900 	    {
3901 		gaiaGetPointXYZM (ln1->Coords, i1, &x1, &y1, &z1, &m1);
3902 	    }
3903 	  else
3904 	    {
3905 		gaiaGetPoint (ln1->Coords, i1, &x1, &y1);
3906 	    }
3907 	  for (i2 = idx2; i2 < ln2->Points; i2++)
3908 	    {
3909 		/* extracting a Vertex from the second Linestring */
3910 		z2 = 0.0;
3911 		m2 = 0.0;
3912 		if (ln2->DimensionModel == GAIA_XY_Z)
3913 		  {
3914 		      gaiaGetPointXYZ (ln2->Coords, i2, &x2, &y2, &z2);
3915 		  }
3916 		else if (ln2->DimensionModel == GAIA_XY_M)
3917 		  {
3918 		      gaiaGetPointXYM (ln2->Coords, i2, &x2, &y2, &m2);
3919 		  }
3920 		else if (ln2->DimensionModel == GAIA_XY_Z_M)
3921 		  {
3922 		      gaiaGetPointXYZM (ln2->Coords, i2, &x2, &y2, &z2, &m2);
3923 		  }
3924 		else
3925 		  {
3926 		      gaiaGetPoint (ln2->Coords, i2, &x2, &y2);
3927 		  }
3928 		if (x1 == x2 && y1 == y2 && z1 == z2 && m1 == m2)
3929 		  {
3930 		      idx2++;
3931 		      count++;
3932 		      break;
3933 		  }
3934 	    }
3935       }
3936     if (count >= 2)
3937 	return 1;
3938     return 0;
3939 }
3940 
3941 static int
do_check_backward(gaiaLinestringPtr ln1,int idx1,gaiaLinestringPtr ln2,int idx2)3942 do_check_backward (gaiaLinestringPtr ln1, int idx1, gaiaLinestringPtr ln2,
3943 		   int idx2)
3944 {
3945 /* testing for further matching Vertices (opposite directions) */
3946     int i1;
3947     int i2;
3948     double x1;
3949     double y1;
3950     double z1;
3951     double m1;
3952     double x2;
3953     double y2;
3954     double z2;
3955     double m2;
3956     int count = 0;
3957     for (i1 = idx1; i1 < ln1->Points; i1++)
3958       {
3959 	  /* extracting a Vertex from the first Linestring */
3960 	  z1 = 0.0;
3961 	  m1 = 0.0;
3962 	  if (ln1->DimensionModel == GAIA_XY_Z)
3963 	    {
3964 		gaiaGetPointXYZ (ln1->Coords, i1, &x1, &y1, &z1);
3965 	    }
3966 	  else if (ln1->DimensionModel == GAIA_XY_M)
3967 	    {
3968 		gaiaGetPointXYM (ln1->Coords, i1, &x1, &y1, &m1);
3969 	    }
3970 	  else if (ln1->DimensionModel == GAIA_XY_Z_M)
3971 	    {
3972 		gaiaGetPointXYZM (ln1->Coords, i1, &x1, &y1, &z1, &m1);
3973 	    }
3974 	  else
3975 	    {
3976 		gaiaGetPoint (ln1->Coords, i1, &x1, &y1);
3977 	    }
3978 	  for (i2 = idx2; i2 >= 0; i2--)
3979 	    {
3980 		/* extracting a Vertex from the second Linestring */
3981 		z2 = 0.0;
3982 		m2 = 0.0;
3983 		if (ln2->DimensionModel == GAIA_XY_Z)
3984 		  {
3985 		      gaiaGetPointXYZ (ln2->Coords, i2, &x2, &y2, &z2);
3986 		  }
3987 		else if (ln2->DimensionModel == GAIA_XY_M)
3988 		  {
3989 		      gaiaGetPointXYM (ln2->Coords, i2, &x2, &y2, &m2);
3990 		  }
3991 		else if (ln2->DimensionModel == GAIA_XY_Z_M)
3992 		  {
3993 		      gaiaGetPointXYZM (ln2->Coords, i2, &x2, &y2, &z2, &m2);
3994 		  }
3995 		else
3996 		  {
3997 		      gaiaGetPoint (ln2->Coords, i2, &x2, &y2);
3998 		  }
3999 		if (x1 == x2 && y1 == y2 && z1 == z2 && m1 == m2)
4000 		  {
4001 		      idx2--;
4002 		      count++;
4003 		      break;
4004 		  }
4005 	    }
4006       }
4007     if (count >= 2)
4008 	return 1;
4009     return 0;
4010 }
4011 
4012 SPATIALITE_PRIVATE void
gaia_do_check_direction(const void * x1,const void * x2,char * direction)4013 gaia_do_check_direction (const void *x1, const void *x2, char *direction)
4014 {
4015 /* checking if two Linestrings have the same direction */
4016     gaiaGeomCollPtr g1 = (gaiaGeomCollPtr) x1;
4017     gaiaGeomCollPtr g2 = (gaiaGeomCollPtr) x2;
4018     int idx1;
4019     int idx2;
4020     gaiaLinestringPtr ln1 = g1->FirstLinestring;
4021     gaiaLinestringPtr ln2 = g2->FirstLinestring;
4022     while (ln2 != NULL)
4023       {
4024 	  /* the second Geometry could be a MultiLinestring */
4025 	  if (do_find_matching_point (ln1, &idx1, ln2, &idx2))
4026 	    {
4027 		if (do_check_forward (ln1, idx1, ln2, idx2))
4028 		  {
4029 		      /* ok, same directions */
4030 		      *direction = '+';
4031 		      return;
4032 		  }
4033 		if (do_check_backward (ln1, idx1, ln2, idx2))
4034 		  {
4035 		      /* ok, opposite directions */
4036 		      *direction = '-';
4037 		      return;
4038 		  }
4039 	    }
4040 	  ln2 = ln2->Next;
4041       }
4042     *direction = '?';
4043 }
4044 
4045 static int
find_lineedge_relationships(struct gaia_topology * topo,sqlite3_stmt * stmt_ref,sqlite3_stmt * stmt_ins,sqlite3_int64 edge_id,const unsigned char * blob,int blob_sz)4046 find_lineedge_relationships (struct gaia_topology *topo,
4047 			     sqlite3_stmt * stmt_ref, sqlite3_stmt * stmt_ins,
4048 			     sqlite3_int64 edge_id, const unsigned char *blob,
4049 			     int blob_sz)
4050 {
4051 /* retrieving LineEdge relationships */
4052     int ret;
4053     int count = 0;
4054     char direction[2];
4055     strcpy (direction, "?");
4056 
4057     sqlite3_reset (stmt_ref);
4058     sqlite3_clear_bindings (stmt_ref);
4059     sqlite3_bind_blob (stmt_ref, 1, blob, blob_sz, SQLITE_STATIC);
4060     sqlite3_bind_blob (stmt_ref, 2, blob, blob_sz, SQLITE_STATIC);
4061 
4062     while (1)
4063       {
4064 	  /* scrolling the result set rows - Spatial Relationships */
4065 	  ret = sqlite3_step (stmt_ref);
4066 	  if (ret == SQLITE_DONE)
4067 	      break;		/* end of result set */
4068 	  if (ret == SQLITE_ROW)
4069 	    {
4070 		int overlaps = 0;
4071 		int covers = 0;
4072 		int covered_by = 0;
4073 		sqlite3_int64 rowid = sqlite3_column_int64 (stmt_ref, 0);
4074 		const char *matrix =
4075 		    (const char *) sqlite3_column_text (stmt_ref, 1);
4076 		if (gaia_do_eval_disjoint (topo->db_handle, matrix))
4077 		    continue;
4078 		overlaps = gaia_do_eval_overlaps (topo->db_handle, matrix);
4079 		covers = gaia_do_eval_covers (topo->db_handle, matrix);
4080 		covered_by = gaia_do_eval_covered_by (topo->db_handle, matrix);
4081 		if (!overlaps && !covers && !covered_by)
4082 		    continue;
4083 
4084 		if (sqlite3_column_type (stmt_ref, 2) == SQLITE_BLOB)
4085 		  {
4086 		      /* testing directions */
4087 		      gaiaGeomCollPtr geom_edge = NULL;
4088 		      gaiaGeomCollPtr geom_line = NULL;
4089 		      const unsigned char *blob2 =
4090 			  sqlite3_column_blob (stmt_ref, 2);
4091 		      int blob2_sz = sqlite3_column_bytes (stmt_ref, 2);
4092 		      geom_edge = gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
4093 		      geom_line = gaiaFromSpatiaLiteBlobWkb (blob2, blob2_sz);
4094 		      if (geom_edge != NULL && geom_line != NULL)
4095 			  gaia_do_check_direction (geom_edge, geom_line,
4096 						   direction);
4097 		      if (geom_edge != NULL)
4098 			  gaiaFreeGeomColl (geom_edge);
4099 		      if (geom_line != NULL)
4100 			  gaiaFreeGeomColl (geom_line);
4101 		  }
4102 
4103 		sqlite3_reset (stmt_ins);
4104 		sqlite3_clear_bindings (stmt_ins);
4105 		sqlite3_bind_int64 (stmt_ins, 1, edge_id);
4106 		sqlite3_bind_int64 (stmt_ins, 2, rowid);
4107 		sqlite3_bind_text (stmt_ins, 3, direction, 1, SQLITE_STATIC);
4108 		sqlite3_bind_text (stmt_ins, 4, matrix, strlen (matrix),
4109 				   SQLITE_STATIC);
4110 		sqlite3_bind_int (stmt_ins, 5, overlaps);
4111 		sqlite3_bind_int (stmt_ins, 6, covers);
4112 		sqlite3_bind_int (stmt_ins, 7, covered_by);
4113 		/* inserting a row into the output table */
4114 		ret = sqlite3_step (stmt_ins);
4115 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4116 		    count++;
4117 		else
4118 		  {
4119 		      char *msg =
4120 			  sqlite3_mprintf ("LineEdgesList error: \"%s\"",
4121 					   sqlite3_errmsg (topo->db_handle));
4122 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
4123 						   topo, msg);
4124 		      sqlite3_free (msg);
4125 		      return 0;
4126 		  }
4127 	    }
4128 	  else
4129 	    {
4130 		char *msg = sqlite3_mprintf ("LineEdgesList error: \"%s\"",
4131 					     sqlite3_errmsg (topo->db_handle));
4132 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
4133 					     msg);
4134 		sqlite3_free (msg);
4135 		return 0;
4136 	    }
4137       }
4138 
4139     if (count == 0)
4140       {
4141 	  /* unrelated Edge */
4142 	  sqlite3_reset (stmt_ins);
4143 	  sqlite3_clear_bindings (stmt_ins);
4144 	  sqlite3_bind_int64 (stmt_ins, 1, edge_id);
4145 	  sqlite3_bind_null (stmt_ins, 2);
4146 	  sqlite3_bind_null (stmt_ins, 3);
4147 	  sqlite3_bind_null (stmt_ins, 4);
4148 	  sqlite3_bind_null (stmt_ins, 5);
4149 	  sqlite3_bind_null (stmt_ins, 6);
4150 	  sqlite3_bind_null (stmt_ins, 7);
4151 	  /* inserting a row into the output table */
4152 	  ret = sqlite3_step (stmt_ins);
4153 	  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
4154 	      ;
4155 	  else
4156 	    {
4157 		char *msg = sqlite3_mprintf ("LineEdgesList error: \"%s\"",
4158 					     sqlite3_errmsg (topo->db_handle));
4159 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
4160 					     msg);
4161 		sqlite3_free (msg);
4162 		return 0;
4163 	    }
4164       }
4165     return 1;
4166 }
4167 
4168 GAIATOPO_DECLARE int
gaiaTopoGeo_LineEdgesList(GaiaTopologyAccessorPtr accessor,const char * db_prefix,const char * ref_table,const char * ref_column,const char * out_table)4169 gaiaTopoGeo_LineEdgesList (GaiaTopologyAccessorPtr accessor,
4170 			   const char *db_prefix, const char *ref_table,
4171 			   const char *ref_column, const char *out_table)
4172 {
4173 /* creating and populating a new Table reporting about Edges/Linestring correspondencies */
4174     struct gaia_topology *topo = (struct gaia_topology *) accessor;
4175     sqlite3_stmt *stmt_edges = NULL;
4176     sqlite3_stmt *stmt_ref = NULL;
4177     sqlite3_stmt *stmt_ins = NULL;
4178     int ret;
4179     char *sql;
4180     char *table;
4181     char *idx_name;
4182     char *xtable;
4183     char *xprefix;
4184     char *xcolumn;
4185     char *xidx_name;
4186     char *rtree_name;
4187     int ref_has_spatial_index = 0;
4188     if (topo == NULL)
4189 	return 0;
4190 
4191 /* attempting to build the output table */
4192     xtable = gaiaDoubleQuotedSql (out_table);
4193     sql = sqlite3_mprintf ("CREATE TABLE main.\"%s\" (\n"
4194 			   "\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n"
4195 			   "\tedge_id INTEGER NOT NULL,\n"
4196 			   "\tref_rowid INTEGER,\n"
4197 			   "\tdirection TEXT,\n"
4198 			   "\tmatrix TEXT,\n"
4199 			   "\toverlaps INTEGER,\n"
4200 			   "\tcovers INTEGER,\n"
4201 			   "\tcovered_by INTEGER)", xtable);
4202     free (xtable);
4203     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, NULL);
4204     sqlite3_free (sql);
4205     if (ret != SQLITE_OK)
4206       {
4207 	  char *msg = sqlite3_mprintf ("LineEdgesList error: \"%s\"",
4208 				       sqlite3_errmsg (topo->db_handle));
4209 	  gaiatopo_set_last_error_msg (accessor, msg);
4210 	  sqlite3_free (msg);
4211 	  goto error;
4212       }
4213     idx_name = sqlite3_mprintf ("idx_%s_edge_id", out_table);
4214     xidx_name = gaiaDoubleQuotedSql (idx_name);
4215     sqlite3_free (idx_name);
4216     xtable = gaiaDoubleQuotedSql (out_table);
4217     sql =
4218 	sqlite3_mprintf
4219 	("CREATE INDEX main.\"%s\" ON \"%s\" (edge_id, ref_rowid)", xidx_name,
4220 	 xtable);
4221     free (xidx_name);
4222     free (xtable);
4223     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, NULL);
4224     sqlite3_free (sql);
4225     if (ret != SQLITE_OK)
4226       {
4227 	  char *msg = sqlite3_mprintf ("LineEdgesList error: \"%s\"",
4228 				       sqlite3_errmsg (topo->db_handle));
4229 	  gaiatopo_set_last_error_msg (accessor, msg);
4230 	  sqlite3_free (msg);
4231 	  goto error;
4232       }
4233 
4234 /* building the Edges SQL statement */
4235     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
4236     xtable = gaiaDoubleQuotedSql (table);
4237     sqlite3_free (table);
4238     sql = sqlite3_mprintf ("SELECT edge_id, geom FROM main.\"%s\"", xtable);
4239     free (xtable);
4240     ret =
4241 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_edges,
4242 			    NULL);
4243     sqlite3_free (sql);
4244     if (ret != SQLITE_OK)
4245       {
4246 	  char *msg = sqlite3_mprintf ("LineEdgesList error: \"%s\"",
4247 				       sqlite3_errmsg (topo->db_handle));
4248 	  gaiatopo_set_last_error_msg (accessor, msg);
4249 	  sqlite3_free (msg);
4250 	  goto error;
4251       }
4252 
4253 /* building the RefTable SQL statement */
4254     rtree_name = sqlite3_mprintf ("DB=%s.%s", db_prefix, ref_table);
4255     ref_has_spatial_index =
4256 	gaia_check_spatial_index (topo->db_handle, db_prefix, ref_table,
4257 				  ref_column);
4258     xprefix = gaiaDoubleQuotedSql (db_prefix);
4259     xtable = gaiaDoubleQuotedSql (ref_table);
4260     xcolumn = gaiaDoubleQuotedSql (ref_column);
4261     if (ref_has_spatial_index)
4262 	sql =
4263 	    sqlite3_mprintf
4264 	    ("SELECT rowid, ST_Relate(?, \"%s\"), \"%s\" FROM \"%s\".\"%s\" "
4265 	     "WHERE  rowid IN ("
4266 	     "SELECT rowid FROM SpatialIndex WHERE f_table_name = %Q AND "
4267 	     "f_geometry_column = %Q AND search_frame = ?)", xcolumn, xcolumn,
4268 	     xprefix, xtable, rtree_name, ref_column);
4269     else
4270 	sql =
4271 	    sqlite3_mprintf
4272 	    ("SELECT rowid, ST_Relate(?, \"%s\"), \"%s\"  FROM \"%s\".\"%s\" "
4273 	     "WHERE MbrIntersects(?, \"%s\")", xcolumn, xcolumn, xprefix,
4274 	     xtable, xcolumn);
4275     free (xprefix);
4276     free (xtable);
4277     free (xcolumn);
4278     sqlite3_free (rtree_name);
4279     ret =
4280 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_ref,
4281 			    NULL);
4282     sqlite3_free (sql);
4283     if (ret != SQLITE_OK)
4284       {
4285 	  char *msg = sqlite3_mprintf ("LineEdgesList error: \"%s\"",
4286 				       sqlite3_errmsg (topo->db_handle));
4287 	  gaiatopo_set_last_error_msg (accessor, msg);
4288 	  sqlite3_free (msg);
4289 	  goto error;
4290       }
4291 
4292 /* building the Insert SQL statement */
4293     xtable = gaiaDoubleQuotedSql (out_table);
4294     sql = sqlite3_mprintf ("INSERT INTO main.\"%s\" (id, edge_id, ref_rowid, "
4295 			   "direction, matrix, overlaps, covers, covered_by) "
4296 			   "VALUES (NULL, ?, ?, ?, ?, ?, ?, ?)", xtable);
4297     free (xtable);
4298     ret =
4299 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_ins,
4300 			    NULL);
4301     sqlite3_free (sql);
4302     if (ret != SQLITE_OK)
4303       {
4304 	  char *msg = sqlite3_mprintf ("LineEdgesList error: \"%s\"",
4305 				       sqlite3_errmsg (topo->db_handle));
4306 	  gaiatopo_set_last_error_msg (accessor, msg);
4307 	  sqlite3_free (msg);
4308 	  goto error;
4309       }
4310 
4311     while (1)
4312       {
4313 	  /* scrolling the result set rows - Edges */
4314 	  ret = sqlite3_step (stmt_edges);
4315 	  if (ret == SQLITE_DONE)
4316 	      break;		/* end of result set */
4317 	  if (ret == SQLITE_ROW)
4318 	    {
4319 		sqlite3_int64 edge_id = sqlite3_column_int64 (stmt_edges, 0);
4320 		if (sqlite3_column_type (stmt_edges, 1) == SQLITE_BLOB)
4321 		  {
4322 		      if (!find_lineedge_relationships
4323 			  (topo, stmt_ref, stmt_ins, edge_id,
4324 			   sqlite3_column_blob (stmt_edges, 1),
4325 			   sqlite3_column_bytes (stmt_edges, 1)))
4326 			  goto error;
4327 		  }
4328 		else
4329 		  {
4330 		      char *msg =
4331 			  sqlite3_mprintf
4332 			  ("LineEdgesList error: Edge not a BLOB value");
4333 		      gaiatopo_set_last_error_msg (accessor, msg);
4334 		      sqlite3_free (msg);
4335 		      goto error;
4336 		  }
4337 	    }
4338 	  else
4339 	    {
4340 		char *msg = sqlite3_mprintf ("LineEdgesList error: \"%s\"",
4341 					     sqlite3_errmsg (topo->db_handle));
4342 		gaiatopo_set_last_error_msg (accessor, msg);
4343 		sqlite3_free (msg);
4344 		goto error;
4345 	    }
4346       }
4347 
4348     sqlite3_finalize (stmt_edges);
4349     sqlite3_finalize (stmt_ref);
4350     sqlite3_finalize (stmt_ins);
4351     return 1;
4352 
4353   error:
4354     if (stmt_edges != NULL)
4355 	sqlite3_finalize (stmt_edges);
4356     if (stmt_ref != NULL)
4357 	sqlite3_finalize (stmt_ref);
4358     if (stmt_ins != NULL)
4359 	sqlite3_finalize (stmt_ins);
4360     return 0;
4361 }
4362 
4363 static struct face_item *
create_face_item(sqlite3_int64 face_id)4364 create_face_item (sqlite3_int64 face_id)
4365 {
4366 /* creating a Face Item */
4367     struct face_item *item = malloc (sizeof (struct face_item));
4368     item->face_id = face_id;
4369     item->next = NULL;
4370     return item;
4371 }
4372 
4373 static void
destroy_face_item(struct face_item * item)4374 destroy_face_item (struct face_item *item)
4375 {
4376 /* destroying a Face Item */
4377     if (item == NULL)
4378 	return;
4379     free (item);
4380 }
4381 
4382 static struct face_edge_item *
create_face_edge_item(sqlite3_int64 edge_id,sqlite3_int64 left_face,sqlite3_int64 right_face,gaiaGeomCollPtr geom)4383 create_face_edge_item (sqlite3_int64 edge_id, sqlite3_int64 left_face,
4384 		       sqlite3_int64 right_face, gaiaGeomCollPtr geom)
4385 {
4386 /* creating a Face-Edge Item */
4387     struct face_edge_item *item = malloc (sizeof (struct face_edge_item));
4388     item->edge_id = edge_id;
4389     item->left_face = left_face;
4390     item->right_face = right_face;
4391     item->geom = geom;
4392     item->count = 0;
4393     item->next = NULL;
4394     return item;
4395 }
4396 
4397 static void
destroy_face_edge_item(struct face_edge_item * item)4398 destroy_face_edge_item (struct face_edge_item *item)
4399 {
4400 /* destroying a Face-Edge Item */
4401     if (item == NULL)
4402 	return;
4403     if (item->geom != NULL)
4404 	gaiaFreeGeomColl (item->geom);
4405     free (item);
4406 }
4407 
4408 TOPOLOGY_PRIVATE struct face_edges *
auxtopo_create_face_edges(int has_z,int srid)4409 auxtopo_create_face_edges (int has_z, int srid)
4410 {
4411 /* creating an empty Face-Edges list */
4412     struct face_edges *list = malloc (sizeof (struct face_edges));
4413     list->has_z = has_z;
4414     list->srid = srid;
4415     list->first_edge = NULL;
4416     list->last_edge = NULL;
4417     list->first_face = NULL;
4418     list->last_face = NULL;
4419     return list;
4420 }
4421 
4422 TOPOLOGY_PRIVATE void
auxtopo_free_face_edges(struct face_edges * list)4423 auxtopo_free_face_edges (struct face_edges *list)
4424 {
4425 /* destroying a Face-Edges list */
4426     struct face_edge_item *fe;
4427     struct face_edge_item *fen;
4428     struct face_item *f;
4429     struct face_item *fn;
4430     if (list == NULL)
4431 	return;
4432 
4433     fe = list->first_edge;
4434     while (fe != NULL)
4435       {
4436 	  /* destroying all Face-Edge items */
4437 	  fen = fe->next;
4438 	  destroy_face_edge_item (fe);
4439 	  fe = fen;
4440       }
4441     f = list->first_face;
4442     while (f != NULL)
4443       {
4444 	  /* destroying all Face items */
4445 	  fn = f->next;
4446 	  destroy_face_item (f);
4447 	  f = fn;
4448       }
4449     free (list);
4450 }
4451 
4452 TOPOLOGY_PRIVATE void
auxtopo_add_face_edge(struct face_edges * list,sqlite3_int64 face_id,sqlite3_int64 edge_id,sqlite3_int64 left_face,sqlite3_int64 right_face,gaiaGeomCollPtr geom)4453 auxtopo_add_face_edge (struct face_edges *list, sqlite3_int64 face_id,
4454 		       sqlite3_int64 edge_id, sqlite3_int64 left_face,
4455 		       sqlite3_int64 right_face, gaiaGeomCollPtr geom)
4456 {
4457 /* adding a Face-Edge Item into the list */
4458     struct face_item *f;
4459     struct face_edge_item *fe =
4460 	create_face_edge_item (edge_id, left_face, right_face, geom);
4461     if (list->first_edge == NULL)
4462 	list->first_edge = fe;
4463     if (list->last_edge != NULL)
4464 	list->last_edge->next = fe;
4465     list->last_edge = fe;
4466 
4467     f = list->first_face;
4468     while (f != NULL)
4469       {
4470 	  if (f->face_id == face_id)
4471 	      return;
4472 	  f = f->next;
4473       }
4474 
4475     /* inserting the Face-ID into the list */
4476     f = create_face_item (face_id);
4477     if (list->first_face == NULL)
4478 	list->first_face = f;
4479     if (list->last_face != NULL)
4480 	list->last_face->next = f;
4481     list->last_face = f;
4482 }
4483 
4484 TOPOLOGY_PRIVATE void
auxtopo_select_valid_face_edges(struct face_edges * list)4485 auxtopo_select_valid_face_edges (struct face_edges *list)
4486 {
4487 /* identifying all useless Edges */
4488     struct face_edge_item *fe = list->first_edge;
4489     while (fe != NULL)
4490       {
4491 	  struct face_item *f = list->first_face;
4492 	  while (f != NULL)
4493 	    {
4494 		if (f->face_id == fe->left_face)
4495 		    fe->count += 1;
4496 		if (f->face_id == fe->right_face)
4497 		    fe->count += 1;
4498 		f = f->next;
4499 	    }
4500 	  fe = fe->next;
4501       }
4502 }
4503 
4504 TOPOLOGY_PRIVATE gaiaGeomCollPtr
auxtopo_polygonize_face_edges(struct face_edges * list,const void * cache)4505 auxtopo_polygonize_face_edges (struct face_edges *list, const void *cache)
4506 {
4507 /* attempting to reaggregrate Polygons from valid Edges */
4508     gaiaGeomCollPtr sparse;
4509     gaiaGeomCollPtr rearranged;
4510     struct face_edge_item *fe;
4511 
4512     if (list->has_z)
4513 	sparse = gaiaAllocGeomCollXYZ ();
4514     else
4515 	sparse = gaiaAllocGeomColl ();
4516     sparse->Srid = list->srid;
4517 
4518     fe = list->first_edge;
4519     while (fe != NULL)
4520       {
4521 	  if (fe->count < 2)
4522 	    {
4523 		/* found a valid Edge: adding to the MultiListring */
4524 		gaiaLinestringPtr ln = fe->geom->FirstLinestring;
4525 		while (ln != NULL)
4526 		  {
4527 		      if (list->has_z)
4528 			  auxtopo_copy_linestring3d (ln, sparse);
4529 		      else
4530 			  auxtopo_copy_linestring (ln, sparse);
4531 		      ln = ln->Next;
4532 		  }
4533 	    }
4534 	  fe = fe->next;
4535       }
4536     rearranged = gaiaPolygonize_r (cache, sparse, 0);
4537     gaiaFreeGeomColl (sparse);
4538     return rearranged;
4539 }
4540 
4541 TOPOLOGY_PRIVATE gaiaGeomCollPtr
auxtopo_polygonize_face_edges_generalize(struct face_edges * list,const void * cache)4542 auxtopo_polygonize_face_edges_generalize (struct face_edges *list,
4543 					  const void *cache)
4544 {
4545 /* attempting to reaggregrate Polygons from valid Edges */
4546     gaiaGeomCollPtr sparse;
4547     gaiaGeomCollPtr renoded;
4548     gaiaGeomCollPtr rearranged;
4549     struct face_edge_item *fe;
4550 
4551     if (list->has_z)
4552 	sparse = gaiaAllocGeomCollXYZ ();
4553     else
4554 	sparse = gaiaAllocGeomColl ();
4555     sparse->Srid = list->srid;
4556 
4557     fe = list->first_edge;
4558     while (fe != NULL)
4559       {
4560 	  if (fe->count < 2)
4561 	    {
4562 		/* found a valid Edge: adding to the MultiListring */
4563 		gaiaLinestringPtr ln = fe->geom->FirstLinestring;
4564 		while (ln != NULL)
4565 		  {
4566 		      if (list->has_z)
4567 			  auxtopo_copy_linestring3d (ln, sparse);
4568 		      else
4569 			  auxtopo_copy_linestring (ln, sparse);
4570 		      ln = ln->Next;
4571 		  }
4572 	    }
4573 	  fe = fe->next;
4574       }
4575     renoded = gaiaNodeLines (cache, sparse);
4576     gaiaFreeGeomColl (sparse);
4577     if (renoded == NULL)
4578 	return NULL;
4579     rearranged = gaiaPolygonize_r (cache, renoded, 0);
4580     gaiaFreeGeomColl (renoded);
4581     return rearranged;
4582 }
4583 
4584 TOPOLOGY_PRIVATE gaiaGeomCollPtr
auxtopo_make_geom_from_point(int srid,int has_z,gaiaPointPtr pt)4585 auxtopo_make_geom_from_point (int srid, int has_z, gaiaPointPtr pt)
4586 {
4587 /* quick constructor: Geometry based on external point */
4588     gaiaGeomCollPtr reference;
4589     if (has_z)
4590 	reference = gaiaAllocGeomCollXYZ ();
4591     else
4592 	reference = gaiaAllocGeomColl ();
4593     reference->Srid = srid;
4594     pt->Next = NULL;
4595     reference->FirstPoint = pt;
4596     reference->LastPoint = pt;
4597     return reference;
4598 }
4599 
4600 TOPOLOGY_PRIVATE gaiaGeomCollPtr
auxtopo_make_geom_from_line(int srid,gaiaLinestringPtr ln)4601 auxtopo_make_geom_from_line (int srid, gaiaLinestringPtr ln)
4602 {
4603 /* quick constructor: Geometry based on external line */
4604     gaiaGeomCollPtr reference;
4605     if (ln->DimensionModel == GAIA_XY_Z_M)
4606 	reference = gaiaAllocGeomCollXYZM ();
4607     else if (ln->DimensionModel == GAIA_XY_Z)
4608 	reference = gaiaAllocGeomCollXYZ ();
4609     else if (ln->DimensionModel == GAIA_XY_M)
4610 	reference = gaiaAllocGeomCollXYM ();
4611     else
4612 	reference = gaiaAllocGeomColl ();
4613     reference->Srid = srid;
4614     ln->Next = NULL;
4615     reference->FirstLinestring = ln;
4616     reference->LastLinestring = ln;
4617     return reference;
4618 }
4619 
4620 TOPOLOGY_PRIVATE void
auxtopo_copy_linestring3d(gaiaLinestringPtr in,gaiaGeomCollPtr geom)4621 auxtopo_copy_linestring3d (gaiaLinestringPtr in, gaiaGeomCollPtr geom)
4622 {
4623 /* inserting/copying a Linestring 3D into another Geometry */
4624     int iv;
4625     double x;
4626     double y;
4627     double z;
4628     gaiaLinestringPtr out = gaiaAddLinestringToGeomColl (geom, in->Points);
4629     for (iv = 0; iv < in->Points; iv++)
4630       {
4631 	  gaiaGetPointXYZ (in->Coords, iv, &x, &y, &z);
4632 	  gaiaSetPointXYZ (out->Coords, iv, x, y, z);
4633       }
4634 }
4635 
4636 TOPOLOGY_PRIVATE void
auxtopo_copy_linestring(gaiaLinestringPtr in,gaiaGeomCollPtr geom)4637 auxtopo_copy_linestring (gaiaLinestringPtr in, gaiaGeomCollPtr geom)
4638 {
4639 /* inserting/copying a Linestring into another Geometry */
4640     int iv;
4641     double x;
4642     double y;
4643     gaiaLinestringPtr out = gaiaAddLinestringToGeomColl (geom, in->Points);
4644     for (iv = 0; iv < in->Points; iv++)
4645       {
4646 	  gaiaGetPoint (in->Coords, iv, &x, &y);
4647 	  gaiaSetPoint (out->Coords, iv, x, y);
4648       }
4649 }
4650 
4651 TOPOLOGY_PRIVATE void
auxtopo_destroy_geom_from(gaiaGeomCollPtr reference)4652 auxtopo_destroy_geom_from (gaiaGeomCollPtr reference)
4653 {
4654 /* safely destroying a reference geometry */
4655     if (reference == NULL)
4656 	return;
4657 
4658 /* releasing ownership on external points, lines and polygs */
4659     reference->FirstPoint = NULL;
4660     reference->LastPoint = NULL;
4661     reference->FirstLinestring = NULL;
4662     reference->LastLinestring = NULL;
4663     reference->FirstPolygon = NULL;
4664     reference->LastPolygon = NULL;
4665 
4666     gaiaFreeGeomColl (reference);
4667 }
4668 
4669 TOPOLOGY_PRIVATE int
auxtopo_retrieve_geometry_type(sqlite3 * db_handle,const char * db_prefix,const char * table,const char * column,int * ref_type)4670 auxtopo_retrieve_geometry_type (sqlite3 * db_handle, const char *db_prefix,
4671 				const char *table, const char *column,
4672 				int *ref_type)
4673 {
4674 /* attempting to retrieve the reference Geometry Type */
4675     int ret;
4676     int i;
4677     char **results;
4678     int rows;
4679     int columns;
4680     char *errMsg = NULL;
4681     char *sql;
4682     char *xprefix;
4683     int type = -1;
4684 
4685 /* querying GEOMETRY_COLUMNS */
4686     xprefix = gaiaDoubleQuotedSql (db_prefix);
4687     sql =
4688 	sqlite3_mprintf
4689 	("SELECT geometry_type "
4690 	 "FROM \"%s\".geometry_columns WHERE Lower(f_table_name) = Lower(%Q) AND "
4691 	 "Lower(f_geometry_column) = Lower(%Q)", xprefix, table, column);
4692     free (xprefix);
4693     ret =
4694 	sqlite3_get_table (db_handle, sql, &results, &rows, &columns, &errMsg);
4695     sqlite3_free (sql);
4696     if (ret != SQLITE_OK)
4697       {
4698 	  sqlite3_free (errMsg);
4699 	  return 0;
4700       }
4701     for (i = 1; i <= rows; i++)
4702       {
4703 	  type = atoi (results[(i * columns) + 0]);
4704       }
4705     sqlite3_free_table (results);
4706 
4707     if (type < 0)
4708 	return 0;
4709 
4710     *ref_type = type;
4711     return 1;
4712 }
4713 
4714 TOPOLOGY_PRIVATE int
auxtopo_create_togeotable_sql(sqlite3 * db_handle,const char * db_prefix,const char * ref_table,const char * ref_column,const char * out_table,char ** xcreate,char ** xselect,char ** xinsert,int * ref_geom_col)4715 auxtopo_create_togeotable_sql (sqlite3 * db_handle, const char *db_prefix,
4716 			       const char *ref_table, const char *ref_column,
4717 			       const char *out_table, char **xcreate,
4718 			       char **xselect, char **xinsert,
4719 			       int *ref_geom_col)
4720 {
4721 /* composing the CREATE TABLE output-table statement */
4722     char *create = NULL;
4723     char *select = NULL;
4724     char *insert = NULL;
4725     char *prev;
4726     char *sql;
4727     char *xprefix;
4728     char *xtable;
4729     int i;
4730     char **results;
4731     int rows;
4732     int columns;
4733     const char *name;
4734     const char *type;
4735     int notnull;
4736     int pk_no;
4737     int ret;
4738     int first_create = 1;
4739     int first_select = 1;
4740     int first_insert = 1;
4741     int npk = 0;
4742     int ipk;
4743     int ncols = 0;
4744     int icol;
4745     int ref_col = 0;
4746     int xref_geom_col;
4747 
4748     *xcreate = NULL;
4749     *xselect = NULL;
4750     *xinsert = NULL;
4751     *ref_geom_col = -1;
4752 
4753     xtable = gaiaDoubleQuotedSql (out_table);
4754     create = sqlite3_mprintf ("CREATE TABLE MAIN.\"%s\" (", xtable);
4755     select = sqlite3_mprintf ("SELECT ");
4756     insert = sqlite3_mprintf ("INSERT INTO MAIN.\"%s\" (", xtable);
4757     free (xtable);
4758 
4759     xprefix = gaiaDoubleQuotedSql (db_prefix);
4760     xtable = gaiaDoubleQuotedSql (ref_table);
4761     sql = sqlite3_mprintf ("PRAGMA \"%s\".table_info(\"%s\")", xprefix, xtable);
4762     free (xprefix);
4763     free (xtable);
4764     ret = sqlite3_get_table (db_handle, sql, &results, &rows, &columns, NULL);
4765     sqlite3_free (sql);
4766     if (ret != SQLITE_OK)
4767 	goto error;
4768     if (rows < 1)
4769 	;
4770     else
4771       {
4772 	  for (i = 1; i <= rows; i++)
4773 	    {
4774 		/* counting how many PK columns are there */
4775 		if (atoi (results[(i * columns) + 5]) != 0)
4776 		    npk++;
4777 	    }
4778 	  for (i = 1; i <= rows; i++)
4779 	    {
4780 		name = results[(i * columns) + 1];
4781 		type = results[(i * columns) + 2];
4782 		notnull = atoi (results[(i * columns) + 3]);
4783 		pk_no = atoi (results[(i * columns) + 5]);
4784 		/* SELECT: adding a column */
4785 		xprefix = gaiaDoubleQuotedSql (name);
4786 		prev = select;
4787 		if (first_select)
4788 		    select = sqlite3_mprintf ("%s\"%s\"", prev, xprefix);
4789 		else
4790 		    select = sqlite3_mprintf ("%s, \"%s\"", prev, xprefix);
4791 		first_select = 0;
4792 		free (xprefix);
4793 		sqlite3_free (prev);
4794 		if (strcasecmp (name, ref_column) == 0)
4795 		  {
4796 		      /* saving the index of ref-geometry */
4797 		      xref_geom_col = ref_col;
4798 		  }
4799 		ref_col++;
4800 		/* INSERT: adding a column */
4801 		xprefix = gaiaDoubleQuotedSql (name);
4802 		prev = insert;
4803 		if (first_insert)
4804 		    insert = sqlite3_mprintf ("%s\"%s\"", prev, xprefix);
4805 		else
4806 		    insert = sqlite3_mprintf ("%s, \"%s\"", prev, xprefix);
4807 		first_insert = 0;
4808 		free (xprefix);
4809 		sqlite3_free (prev);
4810 		ncols++;
4811 		/* CREATE: adding a column definition */
4812 		if (strcasecmp (name, ref_column) == 0)
4813 		  {
4814 		      /* skipping the geometry column */
4815 		      continue;
4816 		  }
4817 		prev = create;
4818 		xprefix = gaiaDoubleQuotedSql (name);
4819 		if (first_create)
4820 		  {
4821 		      first_create = 0;
4822 		      if (notnull)
4823 			  create =
4824 			      sqlite3_mprintf ("%s\n\t\"%s\" %s NOT NULL", prev,
4825 					       xprefix, type);
4826 		      else
4827 			  create =
4828 			      sqlite3_mprintf ("%s\n\t\"%s\" %s", prev, xprefix,
4829 					       type);
4830 		  }
4831 		else
4832 		  {
4833 		      if (notnull)
4834 			  create =
4835 			      sqlite3_mprintf ("%s,\n\t\"%s\" %s NOT NULL",
4836 					       prev, xprefix, type);
4837 		      else
4838 			  create =
4839 			      sqlite3_mprintf ("%s,\n\t\"%s\" %s", prev,
4840 					       xprefix, type);
4841 		  }
4842 		free (xprefix);
4843 		sqlite3_free (prev);
4844 		if (npk == 1 && pk_no != 0)
4845 		  {
4846 		      /* declaring a single-column Primary Key */
4847 		      prev = create;
4848 		      create = sqlite3_mprintf ("%s PRIMARY KEY", prev);
4849 		      sqlite3_free (prev);
4850 		  }
4851 	    }
4852 	  if (npk > 1)
4853 	    {
4854 		/* declaring a multi-column Primary Key */
4855 		prev = create;
4856 		sql = sqlite3_mprintf ("pk_%s", out_table);
4857 		xprefix = gaiaDoubleQuotedSql (sql);
4858 		sqlite3_free (sql);
4859 		create =
4860 		    sqlite3_mprintf ("%s,\n\tCONSTRAINT \"%s\" PRIMARY KEY (",
4861 				     prev, xprefix);
4862 		free (xprefix);
4863 		sqlite3_free (prev);
4864 		for (ipk = 1; ipk <= npk; ipk++)
4865 		  {
4866 		      /* searching a Primary Key column */
4867 		      for (i = 1; i <= rows; i++)
4868 			{
4869 			    if (atoi (results[(i * columns) + 5]) == ipk)
4870 			      {
4871 				  /* declaring a Primary Key column */
4872 				  name = results[(i * columns) + 1];
4873 				  xprefix = gaiaDoubleQuotedSql (name);
4874 				  prev = create;
4875 				  if (ipk == 1)
4876 				      create =
4877 					  sqlite3_mprintf ("%s\"%s\"", prev,
4878 							   xprefix);
4879 				  else
4880 				      create =
4881 					  sqlite3_mprintf ("%s, \"%s\"", prev,
4882 							   xprefix);
4883 				  free (xprefix);
4884 				  sqlite3_free (prev);
4885 			      }
4886 			}
4887 		  }
4888 		prev = create;
4889 		create = sqlite3_mprintf ("%s)", prev);
4890 		sqlite3_free (prev);
4891 	    }
4892       }
4893     sqlite3_free_table (results);
4894 
4895 /* completing the SQL statements */
4896     prev = create;
4897     create = sqlite3_mprintf ("%s)", prev);
4898     sqlite3_free (prev);
4899     prev = select;
4900     xprefix = gaiaDoubleQuotedSql (db_prefix);
4901     xtable = gaiaDoubleQuotedSql (ref_table);
4902     select = sqlite3_mprintf ("%s FROM \"%s\".\"%s\"", prev, xprefix, xtable);
4903     free (xprefix);
4904     free (xtable);
4905     sqlite3_free (prev);
4906     prev = insert;
4907     insert = sqlite3_mprintf ("%s) VALUES (", prev);
4908     sqlite3_free (prev);
4909     for (icol = 0; icol < ncols; icol++)
4910       {
4911 	  prev = insert;
4912 	  if (icol == 0)
4913 	      insert = sqlite3_mprintf ("%s?", prev);
4914 	  else
4915 	      insert = sqlite3_mprintf ("%s, ?", prev);
4916 	  sqlite3_free (prev);
4917       }
4918     prev = insert;
4919     insert = sqlite3_mprintf ("%s)", prev);
4920     sqlite3_free (prev);
4921 
4922     *xcreate = create;
4923     *xselect = select;
4924     *xinsert = insert;
4925     *ref_geom_col = xref_geom_col;
4926     return 1;
4927 
4928   error:
4929     if (create != NULL)
4930 	sqlite3_free (create);
4931     if (select != NULL)
4932 	sqlite3_free (select);
4933     if (insert != NULL)
4934 	sqlite3_free (insert);
4935     return 0;
4936 }
4937 
4938 static int
is_geometry_column(sqlite3 * db_handle,const char * db_prefix,const char * table,const char * column)4939 is_geometry_column (sqlite3 * db_handle, const char *db_prefix,
4940 		    const char *table, const char *column)
4941 {
4942 /* testing for Geometry columns */
4943     int ret;
4944     int i;
4945     char **results;
4946     int rows;
4947     int columns;
4948     char *errMsg = NULL;
4949     char *sql;
4950     char *xprefix;
4951     int count = 0;
4952 
4953 /* querying GEOMETRY_COLUMNS */
4954     xprefix = gaiaDoubleQuotedSql (db_prefix);
4955     sql =
4956 	sqlite3_mprintf
4957 	("SELECT Count(*) "
4958 	 "FROM \"%s\".geometry_columns WHERE Lower(f_table_name) = Lower(%Q) AND "
4959 	 "Lower(f_geometry_column) = Lower(%Q)", xprefix, table, column);
4960     free (xprefix);
4961     ret =
4962 	sqlite3_get_table (db_handle, sql, &results, &rows, &columns, &errMsg);
4963     sqlite3_free (sql);
4964     if (ret != SQLITE_OK)
4965       {
4966 	  sqlite3_free (errMsg);
4967 	  return 0;
4968       }
4969     for (i = 1; i <= rows; i++)
4970       {
4971 	  count = atoi (results[(i * columns) + 0]);
4972       }
4973     sqlite3_free_table (results);
4974 
4975     if (count > 0)
4976 	return 1;
4977     return 0;
4978 }
4979 
4980 static int
auxtopo_create_features_sql(sqlite3 * db_handle,const char * db_prefix,const char * ref_table,const char * ref_column,const char * topology_name,sqlite3_int64 topolayer_id,char ** xcreate,char ** xselect,char ** xinsert)4981 auxtopo_create_features_sql (sqlite3 * db_handle, const char *db_prefix,
4982 			     const char *ref_table, const char *ref_column,
4983 			     const char *topology_name,
4984 			     sqlite3_int64 topolayer_id, char **xcreate,
4985 			     char **xselect, char **xinsert)
4986 {
4987 /* composing the CREATE TABLE fatures-table statement */
4988     char *create = NULL;
4989     char *select = NULL;
4990     char *insert = NULL;
4991     char *prev;
4992     char *sql;
4993     char *xprefix;
4994     char *xgeom;
4995     char *table;
4996     char *xtable;
4997     char dummy[64];
4998     int i;
4999     char **results;
5000     int rows;
5001     int columns;
5002     const char *name;
5003     const char *type;
5004     int notnull;
5005     int ret;
5006     int first_select = 1;
5007     int first_insert = 1;
5008     int ncols = 0;
5009     int icol;
5010     int ref_col = 0;
5011 
5012     *xcreate = NULL;
5013     *xselect = NULL;
5014     *xinsert = NULL;
5015 
5016     sprintf (dummy, "%lld", topolayer_id);
5017     table = sqlite3_mprintf ("%s_topofeatures_%s", topology_name, dummy);
5018     xtable = gaiaDoubleQuotedSql (table);
5019     sqlite3_free (table);
5020     create =
5021 	sqlite3_mprintf
5022 	("CREATE TABLE MAIN.\"%s\" (\n\tfid INTEGER PRIMARY KEY AUTOINCREMENT",
5023 	 xtable);
5024     select = sqlite3_mprintf ("SELECT ");
5025     insert = sqlite3_mprintf ("INSERT INTO MAIN.\"%s\" (", xtable);
5026     free (xtable);
5027 
5028     xprefix = gaiaDoubleQuotedSql (db_prefix);
5029     xtable = gaiaDoubleQuotedSql (ref_table);
5030     sql = sqlite3_mprintf ("PRAGMA \"%s\".table_info(\"%s\")", xprefix, xtable);
5031     free (xprefix);
5032     free (xtable);
5033     ret = sqlite3_get_table (db_handle, sql, &results, &rows, &columns, NULL);
5034     sqlite3_free (sql);
5035     if (ret != SQLITE_OK)
5036 	goto error;
5037     if (rows < 1)
5038 	;
5039     else
5040       {
5041 	  for (i = 1; i <= rows; i++)
5042 	    {
5043 		name = results[(i * columns) + 1];
5044 		type = results[(i * columns) + 2];
5045 		notnull = atoi (results[(i * columns) + 3]);
5046 		if (strcasecmp (name, "fid") == 0)
5047 		    continue;
5048 		if (is_geometry_column (db_handle, db_prefix, ref_table, name))
5049 		    continue;
5050 		if (ref_column != NULL)
5051 		  {
5052 		      if (strcasecmp (ref_column, name) == 0)
5053 			  continue;
5054 		  }
5055 		/* SELECT: adding a column */
5056 		xprefix = gaiaDoubleQuotedSql (name);
5057 		prev = select;
5058 		if (first_select)
5059 		    select = sqlite3_mprintf ("%s\"%s\"", prev, xprefix);
5060 		else
5061 		    select = sqlite3_mprintf ("%s, \"%s\"", prev, xprefix);
5062 		first_select = 0;
5063 		free (xprefix);
5064 		sqlite3_free (prev);
5065 		ref_col++;
5066 		/* INSERT: adding a column */
5067 		xprefix = gaiaDoubleQuotedSql (name);
5068 		prev = insert;
5069 		if (first_insert)
5070 		    insert = sqlite3_mprintf ("%s\"%s\"", prev, xprefix);
5071 		else
5072 		    insert = sqlite3_mprintf ("%s, \"%s\"", prev, xprefix);
5073 		first_insert = 0;
5074 		free (xprefix);
5075 		sqlite3_free (prev);
5076 		ncols++;
5077 		/* CREATE: adding a column definition */
5078 		prev = create;
5079 		xprefix = gaiaDoubleQuotedSql (name);
5080 		if (notnull)
5081 		    create =
5082 			sqlite3_mprintf ("%s,\n\t\"%s\" %s NOT NULL",
5083 					 prev, xprefix, type);
5084 		else
5085 		    create =
5086 			sqlite3_mprintf ("%s,\n\t\"%s\" %s", prev,
5087 					 xprefix, type);
5088 		free (xprefix);
5089 		sqlite3_free (prev);
5090 	    }
5091       }
5092     sqlite3_free_table (results);
5093 
5094 /* completing the SQL statements */
5095     prev = create;
5096     create = sqlite3_mprintf ("%s)", prev);
5097     sqlite3_free (prev);
5098     prev = select;
5099     xprefix = gaiaDoubleQuotedSql (db_prefix);
5100     xtable = gaiaDoubleQuotedSql (ref_table);
5101     if (ref_column != NULL)
5102       {
5103 	  xgeom = gaiaDoubleQuotedSql (ref_column);
5104 	  select =
5105 	      sqlite3_mprintf ("%s, \"%s\" FROM \"%s\".\"%s\"", prev, xgeom,
5106 			       xprefix, xtable);
5107 	  free (xgeom);
5108       }
5109     else
5110 	select =
5111 	    sqlite3_mprintf ("%s FROM \"%s\".\"%s\"", prev, xprefix, xtable);
5112     free (xprefix);
5113     free (xtable);
5114     sqlite3_free (prev);
5115     prev = insert;
5116     insert = sqlite3_mprintf ("%s) VALUES (", prev);
5117     sqlite3_free (prev);
5118     for (icol = 0; icol < ncols; icol++)
5119       {
5120 	  prev = insert;
5121 	  if (icol == 0)
5122 	      insert = sqlite3_mprintf ("%s?", prev);
5123 	  else
5124 	      insert = sqlite3_mprintf ("%s, ?", prev);
5125 	  sqlite3_free (prev);
5126       }
5127     prev = insert;
5128     insert = sqlite3_mprintf ("%s)", prev);
5129     sqlite3_free (prev);
5130 
5131     *xcreate = create;
5132     *xselect = select;
5133     *xinsert = insert;
5134     return 1;
5135 
5136   error:
5137     if (create != NULL)
5138 	sqlite3_free (create);
5139     if (select != NULL)
5140 	sqlite3_free (select);
5141     if (insert != NULL)
5142 	sqlite3_free (insert);
5143     return 0;
5144 }
5145 
5146 static int
do_register_topolayer(struct gaia_topology * topo,const char * topolayer_name,sqlite3_int64 * topolayer_id)5147 do_register_topolayer (struct gaia_topology *topo, const char *topolayer_name,
5148 		       sqlite3_int64 * topolayer_id)
5149 {
5150 /* attempting to register a new TopoLayer */
5151     char *table;
5152     char *xtable;
5153     char *sql;
5154     int ret;
5155     char *err_msg = NULL;
5156 
5157     table = sqlite3_mprintf ("%s_topolayers", topo->topology_name);
5158     xtable = gaiaDoubleQuotedSql (table);
5159     sqlite3_free (table);
5160     sql =
5161 	sqlite3_mprintf
5162 	("INSERT INTO \"%s\" (topolayer_name) VALUES (Lower(%Q))", xtable,
5163 	 topolayer_name);
5164     free (xtable);
5165     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &err_msg);
5166     sqlite3_free (sql);
5167     if (ret != SQLITE_OK)
5168       {
5169 	  char *msg =
5170 	      sqlite3_mprintf ("RegisterTopoLayer error: \"%s\"", err_msg);
5171 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
5172 	  sqlite3_free (err_msg);
5173 	  sqlite3_free (msg);
5174 	  return 0;
5175       }
5176 
5177     *topolayer_id = sqlite3_last_insert_rowid (topo->db_handle);
5178     return 1;
5179 }
5180 
5181 static int
do_eval_topolayer_point(struct gaia_topology * topo,gaiaGeomCollPtr reference,sqlite3_stmt * stmt_node,sqlite3_stmt * stmt_rels,sqlite3_int64 topolayer_id,sqlite3_int64 fid)5182 do_eval_topolayer_point (struct gaia_topology *topo, gaiaGeomCollPtr reference,
5183 			 sqlite3_stmt * stmt_node, sqlite3_stmt * stmt_rels,
5184 			 sqlite3_int64 topolayer_id, sqlite3_int64 fid)
5185 {
5186 /* retrieving Points from Topology */
5187     int ret;
5188     unsigned char *p_blob;
5189     int n_bytes;
5190 
5191 /* initializing the Topo-Node query */
5192     gaiaToSpatiaLiteBlobWkb (reference, &p_blob, &n_bytes);
5193     sqlite3_reset (stmt_node);
5194     sqlite3_clear_bindings (stmt_node);
5195     sqlite3_bind_blob (stmt_node, 1, p_blob, n_bytes, SQLITE_TRANSIENT);
5196     sqlite3_bind_blob (stmt_node, 2, p_blob, n_bytes, SQLITE_TRANSIENT);
5197     free (p_blob);
5198 
5199     while (1)
5200       {
5201 	  /* scrolling the result set rows */
5202 	  ret = sqlite3_step (stmt_node);
5203 	  if (ret == SQLITE_DONE)
5204 	      break;		/* end of result set */
5205 	  if (ret == SQLITE_ROW)
5206 	    {
5207 		sqlite3_int64 node_id = sqlite3_column_int64 (stmt_node, 0);
5208 		sqlite3_reset (stmt_rels);
5209 		sqlite3_clear_bindings (stmt_rels);
5210 		sqlite3_bind_int64 (stmt_rels, 1, node_id);
5211 		sqlite3_bind_null (stmt_rels, 2);
5212 		sqlite3_bind_null (stmt_rels, 3);
5213 		sqlite3_bind_int64 (stmt_rels, 4, topolayer_id);
5214 		sqlite3_bind_int64 (stmt_rels, 5, fid);
5215 		ret = sqlite3_step (stmt_rels);
5216 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
5217 		    ;
5218 		else
5219 		  {
5220 		      char *msg =
5221 			  sqlite3_mprintf
5222 			  ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5223 			   sqlite3_errmsg (topo->db_handle));
5224 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
5225 						   topo, msg);
5226 		      sqlite3_free (msg);
5227 		      return 0;
5228 		  }
5229 	    }
5230 	  else
5231 	    {
5232 		char *msg =
5233 		    sqlite3_mprintf ("TopoGeo_CreateTopoLayer error: \"%s\"",
5234 				     sqlite3_errmsg (topo->db_handle));
5235 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
5236 					     msg);
5237 		sqlite3_free (msg);
5238 		return 0;
5239 	    }
5240       }
5241     return 1;
5242 }
5243 
5244 static int
do_eval_topolayer_line(struct gaia_topology * topo,gaiaGeomCollPtr reference,sqlite3_stmt * stmt_edge,sqlite3_stmt * stmt_rels,sqlite3_int64 topolayer_id,sqlite3_int64 fid)5245 do_eval_topolayer_line (struct gaia_topology *topo, gaiaGeomCollPtr reference,
5246 			sqlite3_stmt * stmt_edge, sqlite3_stmt * stmt_rels,
5247 			sqlite3_int64 topolayer_id, sqlite3_int64 fid)
5248 {
5249 /* retrieving Linestrings from Topology */
5250     int ret;
5251     unsigned char *p_blob;
5252     int n_bytes;
5253 
5254 /* initializing the Topo-Edge query */
5255     gaiaToSpatiaLiteBlobWkb (reference, &p_blob, &n_bytes);
5256     sqlite3_reset (stmt_edge);
5257     sqlite3_clear_bindings (stmt_edge);
5258     sqlite3_bind_blob (stmt_edge, 1, p_blob, n_bytes, SQLITE_TRANSIENT);
5259     sqlite3_bind_blob (stmt_edge, 2, p_blob, n_bytes, SQLITE_TRANSIENT);
5260     free (p_blob);
5261 
5262     while (1)
5263       {
5264 	  /* scrolling the result set rows */
5265 	  ret = sqlite3_step (stmt_edge);
5266 	  if (ret == SQLITE_DONE)
5267 	      break;		/* end of result set */
5268 	  if (ret == SQLITE_ROW)
5269 	    {
5270 		sqlite3_int64 edge_id = sqlite3_column_int64 (stmt_edge, 0);
5271 		sqlite3_reset (stmt_rels);
5272 		sqlite3_clear_bindings (stmt_rels);
5273 		sqlite3_bind_null (stmt_rels, 1);
5274 		sqlite3_bind_int64 (stmt_rels, 2, edge_id);
5275 		sqlite3_bind_null (stmt_rels, 3);
5276 		sqlite3_bind_int64 (stmt_rels, 4, topolayer_id);
5277 		sqlite3_bind_int64 (stmt_rels, 5, fid);
5278 		ret = sqlite3_step (stmt_rels);
5279 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
5280 		    ;
5281 		else
5282 		  {
5283 		      char *msg =
5284 			  sqlite3_mprintf
5285 			  ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5286 			   sqlite3_errmsg (topo->db_handle));
5287 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
5288 						   topo, msg);
5289 		      sqlite3_free (msg);
5290 		      return 0;
5291 		  }
5292 	    }
5293 	  else
5294 	    {
5295 		char *msg =
5296 		    sqlite3_mprintf ("TopoGeo_CreateTopoLayer error: \"%s\"",
5297 				     sqlite3_errmsg (topo->db_handle));
5298 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
5299 					     msg);
5300 		sqlite3_free (msg);
5301 		return 0;
5302 	    }
5303       }
5304     return 1;
5305 }
5306 
5307 static int
do_eval_topolayer_polyg(struct gaia_topology * topo,gaiaGeomCollPtr reference,sqlite3_stmt * stmt_face,sqlite3_stmt * stmt_rels,sqlite3_int64 topolayer_id,sqlite3_int64 fid)5308 do_eval_topolayer_polyg (struct gaia_topology *topo, gaiaGeomCollPtr reference,
5309 			 sqlite3_stmt * stmt_face, sqlite3_stmt * stmt_rels,
5310 			 sqlite3_int64 topolayer_id, sqlite3_int64 fid)
5311 {
5312 /* retrieving Polygon from Topology */
5313     int ret;
5314     unsigned char *p_blob;
5315     int n_bytes;
5316 
5317 /* initializing the Topo-Face query */
5318     gaiaToSpatiaLiteBlobWkb (reference, &p_blob, &n_bytes);
5319     sqlite3_reset (stmt_face);
5320     sqlite3_clear_bindings (stmt_face);
5321     sqlite3_bind_blob (stmt_face, 1, p_blob, n_bytes, SQLITE_TRANSIENT);
5322     sqlite3_bind_blob (stmt_face, 2, p_blob, n_bytes, SQLITE_TRANSIENT);
5323     free (p_blob);
5324 
5325     while (1)
5326       {
5327 	  /* scrolling the result set rows */
5328 	  ret = sqlite3_step (stmt_face);
5329 	  if (ret == SQLITE_DONE)
5330 	      break;		/* end of result set */
5331 	  if (ret == SQLITE_ROW)
5332 	    {
5333 		sqlite3_int64 face_id = sqlite3_column_int64 (stmt_face, 0);
5334 		sqlite3_reset (stmt_rels);
5335 		sqlite3_clear_bindings (stmt_rels);
5336 		sqlite3_bind_null (stmt_rels, 1);
5337 		sqlite3_bind_null (stmt_rels, 2);
5338 		sqlite3_bind_int64 (stmt_rels, 3, face_id);
5339 		sqlite3_bind_int64 (stmt_rels, 4, topolayer_id);
5340 		sqlite3_bind_int64 (stmt_rels, 5, fid);
5341 		ret = sqlite3_step (stmt_rels);
5342 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
5343 		    ;
5344 		else
5345 		  {
5346 		      char *msg =
5347 			  sqlite3_mprintf
5348 			  ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5349 			   sqlite3_errmsg (topo->db_handle));
5350 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
5351 						   topo, msg);
5352 		      sqlite3_free (msg);
5353 		      return 0;
5354 		  }
5355 	    }
5356 	  else
5357 	    {
5358 		char *msg =
5359 		    sqlite3_mprintf ("TopoGeo_CreateTopoLayer error: \"%s\"",
5360 				     sqlite3_errmsg (topo->db_handle));
5361 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
5362 					     msg);
5363 		sqlite3_free (msg);
5364 		return 0;
5365 	    }
5366       }
5367     return 1;
5368 }
5369 
5370 static int
do_eval_topolayer_geom(struct gaia_topology * topo,gaiaGeomCollPtr geom,sqlite3_stmt * stmt_node,sqlite3_stmt * stmt_edge,sqlite3_stmt * stmt_face,sqlite3_stmt * stmt_rels,sqlite3_int64 topolayer_id,sqlite3_int64 fid)5371 do_eval_topolayer_geom (struct gaia_topology *topo, gaiaGeomCollPtr geom,
5372 			sqlite3_stmt * stmt_node, sqlite3_stmt * stmt_edge,
5373 			sqlite3_stmt * stmt_face, sqlite3_stmt * stmt_rels,
5374 			sqlite3_int64 topolayer_id, sqlite3_int64 fid)
5375 {
5376 /* retrieving Features via matching Seeds/Geometries */
5377     gaiaPointPtr pt;
5378     gaiaLinestringPtr ln;
5379     gaiaPolygonPtr pg;
5380     int ret;
5381 
5382     /* processing all Points */
5383     pt = geom->FirstPoint;
5384     while (pt != NULL)
5385       {
5386 	  gaiaPointPtr next = pt->Next;
5387 	  gaiaGeomCollPtr reference = (gaiaGeomCollPtr)
5388 	      auxtopo_make_geom_from_point (topo->srid, topo->has_z, pt);
5389 	  ret =
5390 	      do_eval_topolayer_point (topo, reference, stmt_node, stmt_rels,
5391 				       topolayer_id, fid);
5392 	  auxtopo_destroy_geom_from (reference);
5393 	  pt->Next = next;
5394 	  if (ret == 0)
5395 	      return 0;
5396 	  pt = pt->Next;
5397       }
5398 
5399     /* processing all Linestrings */
5400     ln = geom->FirstLinestring;
5401     while (ln != NULL)
5402       {
5403 	  gaiaLinestringPtr next = ln->Next;
5404 	  gaiaGeomCollPtr reference = (gaiaGeomCollPtr)
5405 	      auxtopo_make_geom_from_line (topo->srid, ln);
5406 	  ret =
5407 	      do_eval_topolayer_line (topo, reference, stmt_edge, stmt_rels,
5408 				      topolayer_id, fid);
5409 	  auxtopo_destroy_geom_from (reference);
5410 	  ln->Next = next;
5411 	  if (ret == 0)
5412 	      return 0;
5413 	  ln = ln->Next;
5414       }
5415 
5416     /* processing all Polygons */
5417     pg = geom->FirstPolygon;
5418     while (pg != NULL)
5419       {
5420 	  gaiaPolygonPtr next = pg->Next;
5421 	  gaiaGeomCollPtr reference = make_geom_from_polyg (topo->srid, pg);
5422 	  ret = do_eval_topolayer_polyg (topo, reference, stmt_face, stmt_rels,
5423 					 topolayer_id, fid);
5424 	  auxtopo_destroy_geom_from (reference);
5425 	  pg->Next = next;
5426 	  if (ret == 0)
5427 	      return 0;
5428 	  pg = pg->Next;
5429       }
5430 
5431     return 1;
5432 }
5433 
5434 static int
do_eval_topolayer_seeds(struct gaia_topology * topo,sqlite3_stmt * stmt_ref,sqlite3_stmt * stmt_ins,sqlite3_stmt * stmt_rels,sqlite3_stmt * stmt_node,sqlite3_stmt * stmt_edge,sqlite3_stmt * stmt_face,sqlite3_int64 topolayer_id)5435 do_eval_topolayer_seeds (struct gaia_topology *topo, sqlite3_stmt * stmt_ref,
5436 			 sqlite3_stmt * stmt_ins, sqlite3_stmt * stmt_rels,
5437 			 sqlite3_stmt * stmt_node, sqlite3_stmt * stmt_edge,
5438 			 sqlite3_stmt * stmt_face, sqlite3_int64 topolayer_id)
5439 {
5440 /* querying the ref-table */
5441     int ret;
5442 
5443     sqlite3_reset (stmt_ref);
5444     sqlite3_clear_bindings (stmt_ref);
5445     while (1)
5446       {
5447 	  /* scrolling the result set rows */
5448 	  gaiaGeomCollPtr geom = NULL;
5449 	  sqlite3_int64 fid;
5450 	  ret = sqlite3_step (stmt_ref);
5451 	  if (ret == SQLITE_DONE)
5452 	      break;		/* end of result set */
5453 	  if (ret == SQLITE_ROW)
5454 	    {
5455 		int icol;
5456 		int ncol = sqlite3_column_count (stmt_ref);
5457 		sqlite3_reset (stmt_ins);
5458 		sqlite3_clear_bindings (stmt_ins);
5459 		for (icol = 0; icol < ncol; icol++)
5460 		  {
5461 		      int col_type = sqlite3_column_type (stmt_ref, icol);
5462 		      if (icol == ncol - 1)
5463 			{
5464 			    /* the last column is always expected to be the geometry column */
5465 			    const unsigned char *blob =
5466 				sqlite3_column_blob (stmt_ref, icol);
5467 			    int blob_sz = sqlite3_column_bytes (stmt_ref, icol);
5468 			    geom = gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
5469 			    continue;
5470 			}
5471 		      switch (col_type)
5472 			{
5473 			case SQLITE_INTEGER:
5474 			    sqlite3_bind_int64 (stmt_ins, icol + 1,
5475 						sqlite3_column_int64 (stmt_ref,
5476 								      icol));
5477 			    break;
5478 			case SQLITE_FLOAT:
5479 			    sqlite3_bind_double (stmt_ins, icol + 1,
5480 						 sqlite3_column_double
5481 						 (stmt_ref, icol));
5482 			    break;
5483 			case SQLITE_TEXT:
5484 			    sqlite3_bind_text (stmt_ins, icol + 1,
5485 					       (const char *)
5486 					       sqlite3_column_text (stmt_ref,
5487 								    icol),
5488 					       sqlite3_column_bytes (stmt_ref,
5489 								     icol),
5490 					       SQLITE_STATIC);
5491 			    break;
5492 			case SQLITE_BLOB:
5493 			    sqlite3_bind_blob (stmt_ins, icol + 1,
5494 					       sqlite3_column_blob (stmt_ref,
5495 								    icol),
5496 					       sqlite3_column_bytes (stmt_ref,
5497 								     icol),
5498 					       SQLITE_STATIC);
5499 			    break;
5500 			default:
5501 			    sqlite3_bind_null (stmt_ins, icol + 1);
5502 			    break;
5503 			};
5504 		  }
5505 		ret = sqlite3_step (stmt_ins);
5506 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
5507 		    ;
5508 		else
5509 		  {
5510 		      char *msg =
5511 			  sqlite3_mprintf
5512 			  ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5513 			   sqlite3_errmsg (topo->db_handle));
5514 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
5515 						   topo, msg);
5516 		      sqlite3_free (msg);
5517 		      return 0;
5518 		  }
5519 		fid = sqlite3_last_insert_rowid (topo->db_handle);
5520 		/* evaluating the feature's geometry */
5521 		if (geom != NULL)
5522 		  {
5523 		      ret =
5524 			  do_eval_topolayer_geom (topo, geom, stmt_node,
5525 						  stmt_edge, stmt_face,
5526 						  stmt_rels, topolayer_id, fid);
5527 		      gaiaFreeGeomColl (geom);
5528 		      if (ret == 0)
5529 			  return 0;
5530 		  }
5531 	    }
5532 	  else
5533 	    {
5534 		char *msg =
5535 		    sqlite3_mprintf ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5536 				     sqlite3_errmsg (topo->db_handle));
5537 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
5538 					     msg);
5539 		sqlite3_free (msg);
5540 		return 0;
5541 	    }
5542       }
5543 
5544     return 1;
5545 }
5546 
5547 GAIATOPO_DECLARE int
gaiaTopoGeo_CreateTopoLayer(GaiaTopologyAccessorPtr accessor,const char * db_prefix,const char * ref_table,const char * ref_column,const char * topolayer_name)5548 gaiaTopoGeo_CreateTopoLayer (GaiaTopologyAccessorPtr accessor,
5549 			     const char *db_prefix, const char *ref_table,
5550 			     const char *ref_column, const char *topolayer_name)
5551 {
5552 /* attempting to create a TopoLayer */
5553     sqlite3_int64 topolayer_id;
5554     sqlite3_stmt *stmt_ref = NULL;
5555     sqlite3_stmt *stmt_ins = NULL;
5556     sqlite3_stmt *stmt_rels = NULL;
5557     sqlite3_stmt *stmt_node = NULL;
5558     sqlite3_stmt *stmt_edge = NULL;
5559     sqlite3_stmt *stmt_face = NULL;
5560     int ret;
5561     char *sql;
5562     char *create;
5563     char *select;
5564     char *insert;
5565     char *xprefix;
5566     char *table;
5567     char *xtable;
5568     char *errMsg;
5569     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5570 
5571     if (topo == NULL)
5572 	return 0;
5573 
5574 /* attempting to register a new TopoLayer */
5575     if (!do_register_topolayer (topo, topolayer_name, &topolayer_id))
5576 	return 0;
5577 
5578 /* incrementally updating all Topology Seeds */
5579     if (!gaiaTopoGeoUpdateSeeds (accessor, 1))
5580 	return 0;
5581 
5582 /* composing the CREATE TABLE feature-table statement */
5583     if (!auxtopo_create_features_sql
5584 	(topo->db_handle, db_prefix, ref_table, ref_column, topo->topology_name,
5585 	 topolayer_id, &create, &select, &insert))
5586 	goto error;
5587 
5588 /* creating the feature-table */
5589     ret = sqlite3_exec (topo->db_handle, create, NULL, NULL, &errMsg);
5590     sqlite3_free (create);
5591     create = NULL;
5592     if (ret != SQLITE_OK)
5593       {
5594 	  char *msg =
5595 	      sqlite3_mprintf ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5596 			       errMsg);
5597 	  sqlite3_free (errMsg);
5598 	  gaiatopo_set_last_error_msg (accessor, msg);
5599 	  sqlite3_free (msg);
5600 	  goto error;
5601       }
5602 
5603 /* preparing the "SELECT * FROM ref-table" query */
5604     ret =
5605 	sqlite3_prepare_v2 (topo->db_handle, select, strlen (select), &stmt_ref,
5606 			    NULL);
5607     sqlite3_free (select);
5608     select = NULL;
5609     if (ret != SQLITE_OK)
5610       {
5611 	  char *msg =
5612 	      sqlite3_mprintf ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5613 			       sqlite3_errmsg (topo->db_handle));
5614 	  gaiatopo_set_last_error_msg (accessor, msg);
5615 	  sqlite3_free (msg);
5616 	  goto error;
5617       }
5618 
5619 /* preparing the "INSERT INTO features-table" query */
5620     ret =
5621 	sqlite3_prepare_v2 (topo->db_handle, insert, strlen (insert), &stmt_ins,
5622 			    NULL);
5623     sqlite3_free (insert);
5624     insert = NULL;
5625     if (ret != SQLITE_OK)
5626       {
5627 	  char *msg =
5628 	      sqlite3_mprintf ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5629 			       sqlite3_errmsg (topo->db_handle));
5630 	  gaiatopo_set_last_error_msg (accessor, msg);
5631 	  sqlite3_free (msg);
5632 	  goto error;
5633       }
5634 
5635 /* preparing the "INSERT INTO features-ref" query */
5636     table = sqlite3_mprintf ("%s_topofeatures", topo->topology_name);
5637     xtable = gaiaDoubleQuotedSql (table);
5638     sqlite3_free (table);
5639     sql =
5640 	sqlite3_mprintf
5641 	("INSERT INTO \"%s\" (node_id, edge_id, face_id, topolayer_id, fid) "
5642 	 "VALUES (?, ?, ?, ?, ?)", xtable);
5643     free (xtable);
5644     ret =
5645 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_rels,
5646 			    NULL);
5647     sqlite3_free (sql);
5648     sql = NULL;
5649     if (ret != SQLITE_OK)
5650       {
5651 	  char *msg =
5652 	      sqlite3_mprintf ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5653 			       sqlite3_errmsg (topo->db_handle));
5654 	  gaiatopo_set_last_error_msg (accessor, msg);
5655 	  sqlite3_free (msg);
5656 	  goto error;
5657       }
5658 
5659 /* preparing the Topo-Seed-Edges query */
5660     xprefix = sqlite3_mprintf ("%s_seeds", topo->topology_name);
5661     xtable = gaiaDoubleQuotedSql (xprefix);
5662     sql = sqlite3_mprintf ("SELECT edge_id FROM MAIN.\"%s\" "
5663 			   "WHERE edge_id IS NOT NULL AND ST_Intersects(geom, ?) = 1 AND ROWID IN ("
5664 			   "SELECT ROWID FROM SpatialIndex WHERE f_table_name = %Q AND search_frame = ?)",
5665 			   xtable, xprefix);
5666     free (xtable);
5667     sqlite3_free (xprefix);
5668     ret =
5669 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_edge,
5670 			    NULL);
5671     sqlite3_free (sql);
5672     if (ret != SQLITE_OK)
5673       {
5674 	  char *msg =
5675 	      sqlite3_mprintf ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5676 			       sqlite3_errmsg (topo->db_handle));
5677 	  gaiatopo_set_last_error_msg (accessor, msg);
5678 	  sqlite3_free (msg);
5679 	  goto error;
5680       }
5681 
5682 /* preparing the Topo-Seed-Faces query */
5683     xprefix = sqlite3_mprintf ("%s_seeds", topo->topology_name);
5684     xtable = gaiaDoubleQuotedSql (xprefix);
5685     sql = sqlite3_mprintf ("SELECT face_id FROM MAIN.\"%s\" "
5686 			   "WHERE face_id IS NOT NULL AND ST_Intersects(geom, ?) = 1 AND ROWID IN ("
5687 			   "SELECT ROWID FROM SpatialIndex WHERE f_table_name = %Q AND search_frame = ?)",
5688 			   xtable, xprefix);
5689     free (xtable);
5690     sqlite3_free (xprefix);
5691     ret =
5692 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_face,
5693 			    NULL);
5694     sqlite3_free (sql);
5695     if (ret != SQLITE_OK)
5696       {
5697 	  char *msg =
5698 	      sqlite3_mprintf ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5699 			       sqlite3_errmsg (topo->db_handle));
5700 	  gaiatopo_set_last_error_msg (accessor, msg);
5701 	  sqlite3_free (msg);
5702 	  goto error;
5703       }
5704 
5705 /* preparing the Topo-Nodes query */
5706     xprefix = sqlite3_mprintf ("%s_node", topo->topology_name);
5707     xtable = gaiaDoubleQuotedSql (xprefix);
5708     sql = sqlite3_mprintf ("SELECT node_id FROM MAIN.\"%s\" "
5709 			   "WHERE ST_Intersects(geom, ?) = 1 AND ROWID IN ("
5710 			   "SELECT ROWID FROM SpatialIndex WHERE f_table_name = %Q AND search_frame = ?)",
5711 			   xtable, xprefix);
5712     free (xtable);
5713     sqlite3_free (xprefix);
5714     ret =
5715 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_node,
5716 			    NULL);
5717     sqlite3_free (sql);
5718     if (ret != SQLITE_OK)
5719       {
5720 	  char *msg =
5721 	      sqlite3_mprintf ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5722 			       sqlite3_errmsg (topo->db_handle));
5723 	  gaiatopo_set_last_error_msg (accessor, msg);
5724 	  sqlite3_free (msg);
5725 	  goto error;
5726       }
5727 
5728 /* evaluating feature/topology matching via coincident topo-seeds */
5729     if (!do_eval_topolayer_seeds
5730 	(topo, stmt_ref, stmt_ins, stmt_rels, stmt_node, stmt_edge, stmt_face,
5731 	 topolayer_id))
5732 	goto error;
5733 
5734     sqlite3_finalize (stmt_ref);
5735     sqlite3_finalize (stmt_ins);
5736     sqlite3_finalize (stmt_rels);
5737     sqlite3_finalize (stmt_node);
5738     sqlite3_finalize (stmt_edge);
5739     sqlite3_finalize (stmt_face);
5740     return 1;
5741 
5742   error:
5743     if (create != NULL)
5744 	sqlite3_free (create);
5745     if (select != NULL)
5746 	sqlite3_free (select);
5747     if (insert != NULL)
5748 	sqlite3_free (insert);
5749     if (stmt_ref != NULL)
5750 	sqlite3_finalize (stmt_ref);
5751     if (stmt_ins != NULL)
5752 	sqlite3_finalize (stmt_ins);
5753     if (stmt_rels != NULL)
5754 	sqlite3_finalize (stmt_rels);
5755     if (stmt_node != NULL)
5756 	sqlite3_finalize (stmt_node);
5757     if (stmt_edge != NULL)
5758 	sqlite3_finalize (stmt_edge);
5759     if (stmt_face != NULL)
5760 	sqlite3_finalize (stmt_face);
5761     return 0;
5762 }
5763 
5764 static int
do_populate_topolayer(struct gaia_topology * topo,sqlite3_stmt * stmt_ref,sqlite3_stmt * stmt_ins)5765 do_populate_topolayer (struct gaia_topology *topo, sqlite3_stmt * stmt_ref,
5766 		       sqlite3_stmt * stmt_ins)
5767 {
5768 /* querying the ref-table */
5769     int ret;
5770 
5771     sqlite3_reset (stmt_ref);
5772     sqlite3_clear_bindings (stmt_ref);
5773     while (1)
5774       {
5775 	  /* scrolling the result set rows */
5776 	  ret = sqlite3_step (stmt_ref);
5777 	  if (ret == SQLITE_DONE)
5778 	      break;		/* end of result set */
5779 	  if (ret == SQLITE_ROW)
5780 	    {
5781 		int icol;
5782 		int ncol = sqlite3_column_count (stmt_ref);
5783 		sqlite3_reset (stmt_ins);
5784 		sqlite3_clear_bindings (stmt_ins);
5785 		for (icol = 0; icol < ncol; icol++)
5786 		  {
5787 		      int col_type = sqlite3_column_type (stmt_ref, icol);
5788 		      switch (col_type)
5789 			{
5790 			case SQLITE_INTEGER:
5791 			    sqlite3_bind_int64 (stmt_ins, icol + 1,
5792 						sqlite3_column_int64 (stmt_ref,
5793 								      icol));
5794 			    break;
5795 			case SQLITE_FLOAT:
5796 			    sqlite3_bind_double (stmt_ins, icol + 1,
5797 						 sqlite3_column_double
5798 						 (stmt_ref, icol));
5799 			    break;
5800 			case SQLITE_TEXT:
5801 			    sqlite3_bind_text (stmt_ins, icol + 1,
5802 					       (const char *)
5803 					       sqlite3_column_text (stmt_ref,
5804 								    icol),
5805 					       sqlite3_column_bytes (stmt_ref,
5806 								     icol),
5807 					       SQLITE_STATIC);
5808 			    break;
5809 			case SQLITE_BLOB:
5810 			    sqlite3_bind_blob (stmt_ins, icol + 1,
5811 					       sqlite3_column_blob (stmt_ref,
5812 								    icol),
5813 					       sqlite3_column_bytes (stmt_ref,
5814 								     icol),
5815 					       SQLITE_STATIC);
5816 			    break;
5817 			default:
5818 			    sqlite3_bind_null (stmt_ins, icol + 1);
5819 			    break;
5820 			};
5821 		  }
5822 		ret = sqlite3_step (stmt_ins);
5823 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
5824 		    ;
5825 		else
5826 		  {
5827 		      char *msg =
5828 			  sqlite3_mprintf
5829 			  ("TopoGeo_InitTopoLayer() error: \"%s\"",
5830 			   sqlite3_errmsg (topo->db_handle));
5831 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
5832 						   topo, msg);
5833 		      sqlite3_free (msg);
5834 		      return 0;
5835 		  }
5836 	    }
5837 	  else
5838 	    {
5839 		char *msg =
5840 		    sqlite3_mprintf ("TopoGeo_InitTopoLayer() error: \"%s\"",
5841 				     sqlite3_errmsg (topo->db_handle));
5842 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
5843 					     msg);
5844 		sqlite3_free (msg);
5845 		return 0;
5846 	    }
5847       }
5848 
5849     return 1;
5850 }
5851 
5852 GAIATOPO_DECLARE int
gaiaTopoGeo_InitTopoLayer(GaiaTopologyAccessorPtr accessor,const char * db_prefix,const char * ref_table,const char * topolayer_name)5853 gaiaTopoGeo_InitTopoLayer (GaiaTopologyAccessorPtr accessor,
5854 			   const char *db_prefix, const char *ref_table,
5855 			   const char *topolayer_name)
5856 {
5857 /* attempting to create a TopoLayer */
5858     sqlite3_int64 topolayer_id;
5859     sqlite3_stmt *stmt_ref = NULL;
5860     sqlite3_stmt *stmt_ins = NULL;
5861     int ret;
5862     char *create;
5863     char *select;
5864     char *insert;
5865     char *errMsg;
5866     struct gaia_topology *topo = (struct gaia_topology *) accessor;
5867 
5868     if (topo == NULL)
5869 	return 0;
5870 
5871 /* attempting to register a new TopoLayer */
5872     if (!do_register_topolayer (topo, topolayer_name, &topolayer_id))
5873 	return 0;
5874 
5875 /* composing the CREATE TABLE feature-table statement */
5876     if (!auxtopo_create_features_sql
5877 	(topo->db_handle, db_prefix, ref_table, NULL, topo->topology_name,
5878 	 topolayer_id, &create, &select, &insert))
5879 	goto error;
5880 
5881 /* creating the feature-table */
5882     ret = sqlite3_exec (topo->db_handle, create, NULL, NULL, &errMsg);
5883     sqlite3_free (create);
5884     create = NULL;
5885     if (ret != SQLITE_OK)
5886       {
5887 	  char *msg = sqlite3_mprintf ("TopoGeo_InitTopoLayer() error: \"%s\"",
5888 				       errMsg);
5889 	  sqlite3_free (errMsg);
5890 	  gaiatopo_set_last_error_msg (accessor, msg);
5891 	  sqlite3_free (msg);
5892 	  goto error;
5893       }
5894 
5895 /* preparing the "SELECT * FROM ref-table" query */
5896     ret =
5897 	sqlite3_prepare_v2 (topo->db_handle, select, strlen (select), &stmt_ref,
5898 			    NULL);
5899     sqlite3_free (select);
5900     select = NULL;
5901     if (ret != SQLITE_OK)
5902       {
5903 	  char *msg =
5904 	      sqlite3_mprintf ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5905 			       sqlite3_errmsg (topo->db_handle));
5906 	  gaiatopo_set_last_error_msg (accessor, msg);
5907 	  sqlite3_free (msg);
5908 	  goto error;
5909       }
5910 
5911 /* preparing the "INSERT INTO features-table" query */
5912     ret =
5913 	sqlite3_prepare_v2 (topo->db_handle, insert, strlen (insert), &stmt_ins,
5914 			    NULL);
5915     sqlite3_free (insert);
5916     insert = NULL;
5917     if (ret != SQLITE_OK)
5918       {
5919 	  char *msg =
5920 	      sqlite3_mprintf ("TopoGeo_CreateTopoLayer() error: \"%s\"",
5921 			       sqlite3_errmsg (topo->db_handle));
5922 	  gaiatopo_set_last_error_msg (accessor, msg);
5923 	  sqlite3_free (msg);
5924 	  goto error;
5925       }
5926 
5927 /* populating the TopoFeatures table */
5928     if (!do_populate_topolayer (topo, stmt_ref, stmt_ins))
5929 	goto error;
5930 
5931     sqlite3_finalize (stmt_ref);
5932     sqlite3_finalize (stmt_ins);
5933     return 1;
5934 
5935   error:
5936     if (create != NULL)
5937 	sqlite3_free (create);
5938     if (select != NULL)
5939 	sqlite3_free (select);
5940     if (insert != NULL)
5941 	sqlite3_free (insert);
5942     if (stmt_ref != NULL)
5943 	sqlite3_finalize (stmt_ref);
5944     if (stmt_ins != NULL)
5945 	sqlite3_finalize (stmt_ins);
5946     return 0;
5947 }
5948 
5949 static int
check_topolayer(struct gaia_topology * topo,const char * topolayer_name,sqlite3_int64 * topolayer_id)5950 check_topolayer (struct gaia_topology *topo, const char *topolayer_name,
5951 		 sqlite3_int64 * topolayer_id)
5952 {
5953 /* checking if a TopoLayer do really exist */
5954     char *table;
5955     char *xtable;
5956     char *sql;
5957     int ret;
5958     int found = 0;
5959     sqlite3_stmt *stmt = NULL;
5960 
5961 /* creating the SQL statement - SELECT */
5962     table = sqlite3_mprintf ("%s_topolayers", topo->topology_name);
5963     xtable = gaiaDoubleQuotedSql (table);
5964     sqlite3_free (table);
5965     sql =
5966 	sqlite3_mprintf
5967 	("SELECT topolayer_id FROM \"%s\" WHERE topolayer_name = Lower(%Q)",
5968 	 xtable, topolayer_name);
5969     free (xtable);
5970     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
5971     sqlite3_free (sql);
5972     if (ret != SQLITE_OK)
5973       {
5974 	  char *msg = sqlite3_mprintf ("Check_TopoLayer() error: \"%s\"",
5975 				       sqlite3_errmsg (topo->db_handle));
5976 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
5977 	  sqlite3_free (msg);
5978 	  goto error;
5979       }
5980 
5981 /* retrieving the TopoLayer ID */
5982     sqlite3_reset (stmt);
5983     sqlite3_clear_bindings (stmt);
5984     while (1)
5985       {
5986 	  /* scrolling the result set rows */
5987 	  ret = sqlite3_step (stmt);
5988 	  if (ret == SQLITE_DONE)
5989 	      break;		/* end of result set */
5990 	  if (ret == SQLITE_ROW)
5991 	    {
5992 		*topolayer_id = sqlite3_column_int64 (stmt, 0);
5993 		found = 1;
5994 	    }
5995 	  else
5996 	    {
5997 		char *msg = sqlite3_mprintf ("Check_TopoLayer() error: \"%s\"",
5998 					     sqlite3_errmsg (topo->db_handle));
5999 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
6000 					     msg);
6001 		sqlite3_free (msg);
6002 		goto error;
6003 	    }
6004       }
6005     if (!found)
6006 	goto error;
6007 
6008     sqlite3_finalize (stmt);
6009     return 1;
6010 
6011   error:
6012     if (stmt != NULL)
6013 	sqlite3_finalize (stmt);
6014     return 0;
6015 }
6016 
6017 static int
do_unregister_topolayer(struct gaia_topology * topo,const char * topolayer_name,sqlite3_int64 * topolayer_id)6018 do_unregister_topolayer (struct gaia_topology *topo, const char *topolayer_name,
6019 			 sqlite3_int64 * topolayer_id)
6020 {
6021 /* attempting to unregister a existing TopoLayer */
6022     char *table;
6023     char *xtable;
6024     char *sql;
6025     int ret;
6026     sqlite3_int64 id;
6027     sqlite3_stmt *stmt = NULL;
6028 
6029     if (!check_topolayer (topo, topolayer_name, &id))
6030 	return 0;
6031 
6032 /* creating the first SQL statement - DELETE */
6033     table = sqlite3_mprintf ("%s_topolayers", topo->topology_name);
6034     xtable = gaiaDoubleQuotedSql (table);
6035     sqlite3_free (table);
6036     sql = sqlite3_mprintf ("DELETE FROM \"%s\" WHERE topolayer_id = ?", xtable);
6037     free (xtable);
6038     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt, NULL);
6039     create_all_topo_prepared_stmts (topo->cache);	/* recreating prepared stsms */
6040     sqlite3_free (sql);
6041     if (ret != SQLITE_OK)
6042       {
6043 	  char *msg =
6044 	      sqlite3_mprintf ("TopoGeo_RemoveTopoLayer() error: \"%s\"",
6045 			       sqlite3_errmsg (topo->db_handle));
6046 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
6047 	  sqlite3_free (msg);
6048 	  goto error;
6049       }
6050 
6051 /* deleting the TopoLayer */
6052     sqlite3_reset (stmt);
6053     sqlite3_clear_bindings (stmt);
6054     sqlite3_bind_int64 (stmt, 1, id);
6055     ret = sqlite3_step (stmt);
6056     if (ret == SQLITE_DONE || ret == SQLITE_ROW)
6057 	;
6058     else
6059       {
6060 	  char *msg =
6061 	      sqlite3_mprintf ("TopoGeo_RemoveTopoLayer() error: \"%s\"",
6062 			       sqlite3_errmsg (topo->db_handle));
6063 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
6064 	  sqlite3_free (msg);
6065 	  goto error;
6066       }
6067 
6068     *topolayer_id = id;
6069     sqlite3_finalize (stmt);
6070     return 1;
6071 
6072   error:
6073     if (stmt != NULL)
6074 	sqlite3_finalize (stmt);
6075     return 0;
6076 }
6077 
6078 GAIATOPO_DECLARE int
gaiaTopoGeo_RemoveTopoLayer(GaiaTopologyAccessorPtr accessor,const char * topolayer_name)6079 gaiaTopoGeo_RemoveTopoLayer (GaiaTopologyAccessorPtr accessor,
6080 			     const char *topolayer_name)
6081 {
6082 /* attempting to remove a TopoLayer */
6083     sqlite3_int64 topolayer_id;
6084     int ret;
6085     char *sql;
6086     char *errMsg;
6087     char *table;
6088     char *xtable;
6089     char *xtable2;
6090     char dummy[64];
6091     struct gaia_topology *topo = (struct gaia_topology *) accessor;
6092 
6093     if (topo == NULL)
6094 	return 0;
6095 
6096 /* deleting all Feature relations */
6097     table = sqlite3_mprintf ("%s_topofeatures", topo->topology_name);
6098     xtable = gaiaDoubleQuotedSql (table);
6099     sqlite3_free (table);
6100     table = sqlite3_mprintf ("%s_topolayers", topo->topology_name);
6101     xtable2 = gaiaDoubleQuotedSql (table);
6102     sqlite3_free (table);
6103     sql = sqlite3_mprintf ("DELETE FROM \"%s\" WHERE topolayer_id = "
6104 			   "(SELECT topolayer_id FROM \"%s\" WHERE topolayer_name = Lower(%Q))",
6105 			   xtable, xtable2, topolayer_name);
6106     free (xtable);
6107     free (xtable2);
6108     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
6109     sqlite3_free (sql);
6110     if (ret != SQLITE_OK)
6111       {
6112 	  char *msg = sqlite3_mprintf ("TopoGeo_RemoveTopoLayer() error: %s\n",
6113 				       errMsg);
6114 	  sqlite3_free (errMsg);
6115 	  gaiatopo_set_last_error_msg (accessor, msg);
6116 	  sqlite3_free (msg);
6117 	  return 0;
6118       }
6119 
6120 /* unregistering the TopoLayer */
6121     if (!do_unregister_topolayer (topo, topolayer_name, &topolayer_id))
6122 	return 0;
6123 
6124 /* finalizing all prepared Statements */
6125     finalize_all_topo_prepared_stmts (topo->cache);
6126 
6127 /* dropping the TopoFeatures Table */
6128     sprintf (dummy, "%lld", topolayer_id);
6129     table = sqlite3_mprintf ("%s_topofeatures_%s", topo->topology_name, dummy);
6130     xtable = gaiaDoubleQuotedSql (table);
6131     sqlite3_free (table);
6132     sql = sqlite3_mprintf ("DROP TABLE \"%s\"", xtable);
6133     free (xtable);
6134     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
6135     create_all_topo_prepared_stmts (topo->cache);	/* recreating prepared stsms */
6136     sqlite3_free (sql);
6137     if (ret != SQLITE_OK)
6138       {
6139 	  char *msg = sqlite3_mprintf ("TopoGeo_RemoveTopoLayer() error: %s\n",
6140 				       errMsg);
6141 	  sqlite3_free (errMsg);
6142 	  gaiatopo_set_last_error_msg (accessor, msg);
6143 	  sqlite3_free (msg);
6144 	  return 0;
6145       }
6146 
6147     return 1;
6148 }
6149 
6150 static int
is_unique_geom_name(sqlite3 * sqlite,const char * table,const char * geom)6151 is_unique_geom_name (sqlite3 * sqlite, const char *table, const char *geom)
6152 {
6153 /* checking for duplicate names */
6154     char *xtable;
6155     char *sql;
6156     int ret;
6157     int i;
6158     char **results;
6159     int rows;
6160     int columns;
6161     const char *name;
6162 
6163     xtable = gaiaDoubleQuotedSql (table);
6164     sql = sqlite3_mprintf ("PRAGMA MAIN.table_info(\"%s\")", xtable);
6165     free (xtable);
6166     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, NULL);
6167     sqlite3_free (sql);
6168     if (ret != SQLITE_OK)
6169 	return 0;
6170     if (rows < 1)
6171 	;
6172     else
6173       {
6174 	  for (i = 1; i <= rows; i++)
6175 	    {
6176 		name = results[(i * columns) + 1];
6177 		if (strcasecmp (name, geom) == 0)
6178 		    continue;
6179 	    }
6180       }
6181     sqlite3_free_table (results);
6182 
6183     return 1;
6184 }
6185 
6186 static int
auxtopo_create_export_sql(struct gaia_topology * topo,const char * topolayer_name,const char * out_table,char ** xcreate,char ** xselect,char ** xinsert,char ** geometry,sqlite3_int64 * topolayer_id)6187 auxtopo_create_export_sql (struct gaia_topology *topo,
6188 			   const char *topolayer_name, const char *out_table,
6189 			   char **xcreate, char **xselect, char **xinsert,
6190 			   char **geometry, sqlite3_int64 * topolayer_id)
6191 {
6192 /* composing the CREATE TABLE export-table statement */
6193     char *create = NULL;
6194     char *select = NULL;
6195     char *insert = NULL;
6196     char *prev;
6197     char *sql;
6198     char *table;
6199     char *xtable;
6200     char *xprefix;
6201     char dummy[64];
6202     int i;
6203     char **results;
6204     int rows;
6205     int columns;
6206     const char *name;
6207     const char *type;
6208     int notnull;
6209     int ret;
6210     int first_select = 1;
6211     int first_insert = 1;
6212     int ncols = 0;
6213     int icol;
6214     int ref_col = 0;
6215     char *geometry_name;
6216     int geom_alias = 0;
6217 
6218     *xcreate = NULL;
6219     *xselect = NULL;
6220     *xinsert = NULL;
6221 
6222 /* checking the TopoLayer */
6223     if (!check_topolayer (topo, topolayer_name, topolayer_id))
6224 	return 0;
6225 
6226     xtable = gaiaDoubleQuotedSql (out_table);
6227     create =
6228 	sqlite3_mprintf
6229 	("CREATE TABLE MAIN.\"%s\" (\n\tfid INTEGER PRIMARY KEY", xtable);
6230     select = sqlite3_mprintf ("SELECT fid, ");
6231     insert = sqlite3_mprintf ("INSERT INTO MAIN.\"%s\" (fid, ", xtable);
6232     free (xtable);
6233     sprintf (dummy, "%lld", *topolayer_id);
6234     table = sqlite3_mprintf ("%s_topofeatures_%s", topo->topology_name, dummy);
6235     xtable = gaiaDoubleQuotedSql (table);
6236     sqlite3_free (table);
6237     sql = sqlite3_mprintf ("PRAGMA MAIN.table_info(\"%s\")", xtable);
6238     free (xtable);
6239     ret =
6240 	sqlite3_get_table (topo->db_handle, sql, &results, &rows, &columns,
6241 			   NULL);
6242     sqlite3_free (sql);
6243     if (ret != SQLITE_OK)
6244 	goto error;
6245     if (rows < 1)
6246 	;
6247     else
6248       {
6249 	  for (i = 1; i <= rows; i++)
6250 	    {
6251 		name = results[(i * columns) + 1];
6252 		if (strcmp (name, "fid") == 0)
6253 		    continue;
6254 		type = results[(i * columns) + 2];
6255 		notnull = atoi (results[(i * columns) + 3]);
6256 		/* SELECT: adding a column */
6257 		xprefix = gaiaDoubleQuotedSql (name);
6258 		prev = select;
6259 		if (first_select)
6260 		    select = sqlite3_mprintf ("%s\"%s\"", prev, xprefix);
6261 		else
6262 		    select = sqlite3_mprintf ("%s, \"%s\"", prev, xprefix);
6263 		first_select = 0;
6264 		free (xprefix);
6265 		sqlite3_free (prev);
6266 		ref_col++;
6267 		/* INSERT: adding a column */
6268 		xprefix = gaiaDoubleQuotedSql (name);
6269 		prev = insert;
6270 		if (first_insert)
6271 		    insert = sqlite3_mprintf ("%s\"%s\"", prev, xprefix);
6272 		else
6273 		    insert = sqlite3_mprintf ("%s, \"%s\"", prev, xprefix);
6274 		first_insert = 0;
6275 		free (xprefix);
6276 		sqlite3_free (prev);
6277 		ncols++;
6278 		/* CREATE: adding a column definition */
6279 		prev = create;
6280 		xprefix = gaiaDoubleQuotedSql (name);
6281 		if (notnull)
6282 		    create =
6283 			sqlite3_mprintf ("%s,\n\t\"%s\" %s NOT NULL",
6284 					 prev, xprefix, type);
6285 		else
6286 		    create =
6287 			sqlite3_mprintf ("%s,\n\t\"%s\" %s", prev,
6288 					 xprefix, type);
6289 		free (xprefix);
6290 		sqlite3_free (prev);
6291 	    }
6292       }
6293     sqlite3_free_table (results);
6294 
6295     geometry_name = malloc (strlen ("geometry") + 1);
6296     strcpy (geometry_name, "geometry");
6297     sprintf (dummy, "%lld", *topolayer_id);
6298     table = sqlite3_mprintf ("%s_topofeatures_%s", topo->topology_name, dummy);
6299     while (1)
6300       {
6301 	  /* searching an unique Geometry name */
6302 	  if (is_unique_geom_name (topo->db_handle, table, geometry_name))
6303 	      break;
6304 	  sprintf (dummy, "geom_%d", ++geom_alias);
6305 	  free (geometry_name);
6306 	  geometry_name = malloc (strlen (dummy) + 1);
6307 	  strcpy (geometry_name, dummy);
6308       }
6309     sqlite3_free (table);
6310 
6311 /* completing the SQL statements */
6312     prev = create;
6313     create = sqlite3_mprintf ("%s)", prev);
6314     sqlite3_free (prev);
6315     prev = select;
6316     sprintf (dummy, "%lld", *topolayer_id);
6317     table = sqlite3_mprintf ("%s_topofeatures_%s", topo->topology_name, dummy);
6318     xtable = gaiaDoubleQuotedSql (table);
6319     sqlite3_free (table);
6320     select = sqlite3_mprintf ("%s FROM MAIN.\"%s\"", prev, xtable);
6321     free (xtable);
6322     sqlite3_free (prev);
6323     prev = insert;
6324     insert = sqlite3_mprintf ("%s, \"%s\") VALUES (?, ", prev, geometry_name);
6325     sqlite3_free (prev);
6326     for (icol = 0; icol < ncols; icol++)
6327       {
6328 	  prev = insert;
6329 	  if (icol == 0)
6330 	      insert = sqlite3_mprintf ("%s?", prev);
6331 	  else
6332 	      insert = sqlite3_mprintf ("%s, ?", prev);
6333 	  sqlite3_free (prev);
6334       }
6335     prev = insert;
6336     insert = sqlite3_mprintf ("%s, ?)", prev);
6337     sqlite3_free (prev);
6338 
6339     *xcreate = create;
6340     *xselect = select;
6341     *xinsert = insert;
6342     *geometry = geometry_name;
6343     return 1;
6344 
6345   error:
6346     if (create != NULL)
6347 	sqlite3_free (create);
6348     if (select != NULL)
6349 	sqlite3_free (select);
6350     if (insert != NULL)
6351 	sqlite3_free (insert);
6352     return 0;
6353 }
6354 
6355 static int
auxtopo_retrieve_export_geometry_type(struct gaia_topology * topo,const char * topolayer_name,int * ref_type)6356 auxtopo_retrieve_export_geometry_type (struct gaia_topology *topo,
6357 				       const char *topolayer_name,
6358 				       int *ref_type)
6359 {
6360 /* determining the Geometry Type for Export TopoLayer */
6361     char *table;
6362     char *xtable;
6363     char *xtable2;
6364     char *sql;
6365     int ret;
6366     int i;
6367     char **results;
6368     int rows;
6369     int columns;
6370     int nodes;
6371     int edges;
6372     int faces;
6373 
6374     table = sqlite3_mprintf ("%s_topolayers", topo->topology_name);
6375     xtable = gaiaDoubleQuotedSql (table);
6376     sqlite3_free (table);
6377     table = sqlite3_mprintf ("%s_topofeatures", topo->topology_name);
6378     xtable2 = gaiaDoubleQuotedSql (table);
6379     sqlite3_free (table);
6380     sql =
6381 	sqlite3_mprintf
6382 	("SELECT Count(f.node_id), Count(f.edge_id), Count(f.face_id) "
6383 	 "FROM \"%s\" AS l JOIN \"%s\" AS f ON (l.topolayer_id = f.topolayer_id) "
6384 	 "WHERE l.topolayer_name = Lower(%Q)", xtable, xtable2, topolayer_name);
6385     free (xtable);
6386     free (xtable2);
6387     ret =
6388 	sqlite3_get_table (topo->db_handle, sql, &results, &rows, &columns,
6389 			   NULL);
6390     sqlite3_free (sql);
6391     if (ret != SQLITE_OK)
6392 	return 0;
6393     if (rows < 1)
6394 	;
6395     else
6396       {
6397 	  for (i = 1; i <= rows; i++)
6398 	    {
6399 		nodes = atoi (results[(i * columns) + 0]);
6400 		edges = atoi (results[(i * columns) + 1]);
6401 		faces = atoi (results[(i * columns) + 2]);
6402 	    }
6403       }
6404     sqlite3_free_table (results);
6405 
6406     *ref_type = GAIA_UNKNOWN;
6407     if (nodes && !edges && !faces)
6408 	*ref_type = GAIA_POINT;
6409     if (!nodes && edges && !faces)
6410 	*ref_type = GAIA_LINESTRING;
6411     if (!nodes && !edges && faces)
6412 	*ref_type = GAIA_POLYGON;
6413 
6414     return 1;
6415 }
6416 
6417 static void
do_eval_topo_node(struct gaia_topology * topo,sqlite3_stmt * stmt_node,sqlite3_int64 node_id,gaiaGeomCollPtr result)6418 do_eval_topo_node (struct gaia_topology *topo, sqlite3_stmt * stmt_node,
6419 		   sqlite3_int64 node_id, gaiaGeomCollPtr result)
6420 {
6421 /* retrieving Points from Topology Nodes */
6422     int ret;
6423 
6424 /* initializing the Topo-Node query */
6425     sqlite3_reset (stmt_node);
6426     sqlite3_clear_bindings (stmt_node);
6427     sqlite3_bind_int64 (stmt_node, 1, node_id);
6428 
6429     while (1)
6430       {
6431 	  /* scrolling the result set rows */
6432 	  ret = sqlite3_step (stmt_node);
6433 	  if (ret == SQLITE_DONE)
6434 	      break;		/* end of result set */
6435 	  if (ret == SQLITE_ROW)
6436 	    {
6437 		const unsigned char *blob = sqlite3_column_blob (stmt_node, 0);
6438 		int blob_sz = sqlite3_column_bytes (stmt_node, 0);
6439 		gaiaGeomCollPtr geom =
6440 		    gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
6441 		if (geom != NULL)
6442 		  {
6443 		      gaiaPointPtr pt = geom->FirstPoint;
6444 		      while (pt != NULL)
6445 			{
6446 			    /* copying all Points into the result Geometry */
6447 			    if (topo->has_z)
6448 				gaiaAddPointToGeomCollXYZ (result, pt->X, pt->Y,
6449 							   pt->Z);
6450 			    else
6451 				gaiaAddPointToGeomColl (result, pt->X, pt->Y);
6452 			    pt = pt->Next;
6453 			}
6454 		      gaiaFreeGeomColl (geom);
6455 		  }
6456 	    }
6457 	  else
6458 	    {
6459 		char *msg =
6460 		    sqlite3_mprintf
6461 		    ("TopoGeo_FeatureFromTopoLayer error: \"%s\"",
6462 		     sqlite3_errmsg (topo->db_handle));
6463 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
6464 					     msg);
6465 		sqlite3_free (msg);
6466 		return;
6467 	    }
6468       }
6469 }
6470 
6471 static void
do_eval_topo_edge(struct gaia_topology * topo,sqlite3_stmt * stmt_edge,sqlite3_int64 edge_id,gaiaGeomCollPtr result)6472 do_eval_topo_edge (struct gaia_topology *topo, sqlite3_stmt * stmt_edge,
6473 		   sqlite3_int64 edge_id, gaiaGeomCollPtr result)
6474 {
6475 /* retrieving Linestrings from Topology Edges */
6476     int ret;
6477 
6478 /* initializing the Topo-Edge query */
6479     sqlite3_reset (stmt_edge);
6480     sqlite3_clear_bindings (stmt_edge);
6481     sqlite3_bind_int64 (stmt_edge, 1, edge_id);
6482 
6483     while (1)
6484       {
6485 	  /* scrolling the result set rows */
6486 	  ret = sqlite3_step (stmt_edge);
6487 	  if (ret == SQLITE_DONE)
6488 	      break;		/* end of result set */
6489 	  if (ret == SQLITE_ROW)
6490 	    {
6491 		const unsigned char *blob = sqlite3_column_blob (stmt_edge, 0);
6492 		int blob_sz = sqlite3_column_bytes (stmt_edge, 0);
6493 		gaiaGeomCollPtr geom =
6494 		    gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
6495 		if (geom != NULL)
6496 		  {
6497 		      gaiaLinestringPtr ln = geom->FirstLinestring;
6498 		      while (ln != NULL)
6499 			{
6500 			    /* copying all Linestrings into the result Geometry */
6501 			    if (topo->has_z)
6502 				auxtopo_copy_linestring3d (ln, result);
6503 			    else
6504 				auxtopo_copy_linestring (ln, result);
6505 			    ln = ln->Next;
6506 			}
6507 		      gaiaFreeGeomColl (geom);
6508 		  }
6509 	    }
6510 	  else
6511 	    {
6512 		char *msg =
6513 		    sqlite3_mprintf
6514 		    ("TopoGeo_FeatureFromTopoLayer error: \"%s\"",
6515 		     sqlite3_errmsg (topo->db_handle));
6516 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
6517 					     msg);
6518 		sqlite3_free (msg);
6519 		return;
6520 	    }
6521       }
6522 }
6523 
6524 static gaiaGeomCollPtr
do_eval_topo_geometry(struct gaia_topology * topo,sqlite3_stmt * stmt_rels,sqlite3_stmt * stmt_node,sqlite3_stmt * stmt_edge,sqlite3_stmt * stmt_face,sqlite3_int64 fid,sqlite3_int64 topolayer_id,int out_type)6525 do_eval_topo_geometry (struct gaia_topology *topo, sqlite3_stmt * stmt_rels,
6526 		       sqlite3_stmt * stmt_node, sqlite3_stmt * stmt_edge,
6527 		       sqlite3_stmt * stmt_face, sqlite3_int64 fid,
6528 		       sqlite3_int64 topolayer_id, int out_type)
6529 {
6530 /* materializing a Geometry out of Topology */
6531     int ret;
6532     gaiaGeomCollPtr geom;
6533     gaiaGeomCollPtr sparse_lines;
6534     struct face_edges *list =
6535 	auxtopo_create_face_edges (topo->has_z, topo->srid);
6536 
6537     if (topo->has_z)
6538       {
6539 	  geom = gaiaAllocGeomCollXYZ ();
6540 	  sparse_lines = gaiaAllocGeomCollXYZ ();
6541       }
6542     else
6543       {
6544 	  geom = gaiaAllocGeomColl ();
6545 	  sparse_lines = gaiaAllocGeomColl ();
6546       }
6547     geom->Srid = topo->srid;
6548     geom->DeclaredType = out_type;
6549 
6550     sqlite3_reset (stmt_rels);
6551     sqlite3_clear_bindings (stmt_rels);
6552     sqlite3_bind_int64 (stmt_rels, 1, topolayer_id);
6553     sqlite3_bind_int64 (stmt_rels, 2, fid);
6554     while (1)
6555       {
6556 	  /* scrolling the result set rows */
6557 	  sqlite3_int64 id;
6558 	  ret = sqlite3_step (stmt_rels);
6559 	  if (ret == SQLITE_DONE)
6560 	      break;		/* end of result set */
6561 	  if (ret == SQLITE_ROW)
6562 	    {
6563 		if (sqlite3_column_type (stmt_rels, 0) != SQLITE_NULL)
6564 		  {
6565 		      id = sqlite3_column_int64 (stmt_rels, 0);
6566 		      do_eval_topo_node (topo, stmt_node, id, geom);
6567 		  }
6568 		if (sqlite3_column_type (stmt_rels, 1) != SQLITE_NULL)
6569 		  {
6570 		      id = sqlite3_column_int64 (stmt_rels, 1);
6571 		      do_eval_topo_edge (topo, stmt_edge, id, sparse_lines);
6572 		  }
6573 		if (sqlite3_column_type (stmt_rels, 2) != SQLITE_NULL)
6574 		  {
6575 		      id = sqlite3_column_int64 (stmt_rels, 2);
6576 		      do_explode_topo_face (topo, list, stmt_face, id);
6577 		  }
6578 	    }
6579 	  else
6580 	    {
6581 		char *msg =
6582 		    sqlite3_mprintf
6583 		    ("TopoGeo_FeatureFromTopoLayer() error: \"%s\"",
6584 		     sqlite3_errmsg (topo->db_handle));
6585 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
6586 					     msg);
6587 		sqlite3_free (msg);
6588 		goto error;
6589 	    }
6590       }
6591 
6592 
6593     if (sparse_lines->FirstLinestring != NULL)
6594       {
6595 	  /* attempting to better rearrange sparse lines */
6596 	  gaiaGeomCollPtr rearranged =
6597 	      gaiaLineMerge_r (topo->cache, sparse_lines);
6598 	  gaiaFreeGeomColl (sparse_lines);
6599 	  if (rearranged != NULL)
6600 	    {
6601 		gaiaLinestringPtr ln = rearranged->FirstLinestring;
6602 		while (ln != NULL)
6603 		  {
6604 		      if (topo->has_z)
6605 			  auxtopo_copy_linestring3d (ln, geom);
6606 		      else
6607 			  auxtopo_copy_linestring (ln, geom);
6608 		      ln = ln->Next;
6609 		  }
6610 		gaiaFreeGeomColl (rearranged);
6611 	    }
6612       }
6613     else
6614 	gaiaFreeGeomColl (sparse_lines);
6615     sparse_lines = NULL;
6616 
6617     if (list->first_edge != NULL)
6618       {
6619 	  /* attempting to rearrange sparse lines into Polygons */
6620 	  gaiaGeomCollPtr rearranged;
6621 	  auxtopo_select_valid_face_edges (list);
6622 	  rearranged = auxtopo_polygonize_face_edges (list, topo->cache);
6623 	  auxtopo_free_face_edges (list);
6624 	  list = NULL;
6625 	  if (rearranged != NULL)
6626 	    {
6627 		gaiaPolygonPtr pg = rearranged->FirstPolygon;
6628 		while (pg != NULL)
6629 		  {
6630 		      if (topo->has_z)
6631 			  do_copy_polygon3d (pg, geom);
6632 		      else
6633 			  do_copy_polygon (pg, geom);
6634 		      pg = pg->Next;
6635 		  }
6636 		gaiaFreeGeomColl (rearranged);
6637 	    }
6638       }
6639 
6640     if (geom->FirstPoint == NULL && geom->FirstLinestring == NULL
6641 	&& geom->FirstPolygon == NULL)
6642 	goto error;
6643     auxtopo_free_face_edges (list);
6644     return geom;
6645 
6646   error:
6647     gaiaFreeGeomColl (geom);
6648     if (sparse_lines != NULL)
6649 	gaiaFreeGeomColl (sparse_lines);
6650     if (list != NULL)
6651 	auxtopo_free_face_edges (list);
6652     return NULL;
6653 }
6654 
6655 static int
do_eval_topogeo_features(struct gaia_topology * topo,sqlite3_stmt * stmt_ref,sqlite3_stmt * stmt_ins,sqlite3_stmt * stmt_rels,sqlite3_stmt * stmt_node,sqlite3_stmt * stmt_edge,sqlite3_stmt * stmt_face,sqlite3_int64 topolayer_id,int out_type)6656 do_eval_topogeo_features (struct gaia_topology *topo, sqlite3_stmt * stmt_ref,
6657 			  sqlite3_stmt * stmt_ins, sqlite3_stmt * stmt_rels,
6658 			  sqlite3_stmt * stmt_node, sqlite3_stmt * stmt_edge,
6659 			  sqlite3_stmt * stmt_face, sqlite3_int64 topolayer_id,
6660 			  int out_type)
6661 {
6662 /* querying the ref-table */
6663     int ret;
6664 
6665     sqlite3_reset (stmt_ref);
6666     sqlite3_clear_bindings (stmt_ref);
6667     while (1)
6668       {
6669 	  /* scrolling the result set rows */
6670 	  gaiaGeomCollPtr geom = NULL;
6671 	  sqlite3_int64 fid;
6672 	  ret = sqlite3_step (stmt_ref);
6673 	  if (ret == SQLITE_DONE)
6674 	      break;		/* end of result set */
6675 	  if (ret == SQLITE_ROW)
6676 	    {
6677 		int icol;
6678 		int ncol = sqlite3_column_count (stmt_ref);
6679 		fid = sqlite3_column_int64 (stmt_ref, 0);
6680 		sqlite3_reset (stmt_ins);
6681 		sqlite3_clear_bindings (stmt_ins);
6682 		for (icol = 0; icol < ncol; icol++)
6683 		  {
6684 		      int col_type = sqlite3_column_type (stmt_ref, icol);
6685 		      switch (col_type)
6686 			{
6687 			case SQLITE_INTEGER:
6688 			    sqlite3_bind_int64 (stmt_ins, icol + 1,
6689 						sqlite3_column_int64 (stmt_ref,
6690 								      icol));
6691 			    break;
6692 			case SQLITE_FLOAT:
6693 			    sqlite3_bind_double (stmt_ins, icol + 1,
6694 						 sqlite3_column_double
6695 						 (stmt_ref, icol));
6696 			    break;
6697 			case SQLITE_TEXT:
6698 			    sqlite3_bind_text (stmt_ins, icol + 1,
6699 					       (const char *)
6700 					       sqlite3_column_text (stmt_ref,
6701 								    icol),
6702 					       sqlite3_column_bytes (stmt_ref,
6703 								     icol),
6704 					       SQLITE_STATIC);
6705 			    break;
6706 			case SQLITE_BLOB:
6707 			    sqlite3_bind_blob (stmt_ins, icol + 1,
6708 					       sqlite3_column_blob (stmt_ref,
6709 								    icol),
6710 					       sqlite3_column_bytes (stmt_ref,
6711 								     icol),
6712 					       SQLITE_STATIC);
6713 			    break;
6714 			default:
6715 			    sqlite3_bind_null (stmt_ins, icol + 1);
6716 			    break;
6717 			};
6718 		  }
6719 		/* the Geometry column */
6720 		ncol = sqlite3_bind_parameter_count (stmt_ins);
6721 		geom =
6722 		    do_eval_topo_geometry (topo, stmt_rels, stmt_node,
6723 					   stmt_edge, stmt_face, fid,
6724 					   topolayer_id, out_type);
6725 		if (geom != NULL)
6726 		  {
6727 		      unsigned char *p_blob;
6728 		      int n_bytes;
6729 		      gaiaToSpatiaLiteBlobWkb (geom, &p_blob, &n_bytes);
6730 		      sqlite3_bind_blob (stmt_ins, ncol, p_blob, n_bytes,
6731 					 SQLITE_TRANSIENT);
6732 		      free (p_blob);
6733 		      gaiaFreeGeomColl (geom);
6734 		  }
6735 		else
6736 		    sqlite3_bind_null (stmt_ins, ncol);
6737 		ret = sqlite3_step (stmt_ins);
6738 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
6739 		    ;
6740 		else
6741 		  {
6742 		      char *msg =
6743 			  sqlite3_mprintf
6744 			  ("TopoGeo_ExportTopoLayer() error: \"%s\"",
6745 			   sqlite3_errmsg (topo->db_handle));
6746 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
6747 						   topo, msg);
6748 		      sqlite3_free (msg);
6749 		      return 0;
6750 		  }
6751 	    }
6752 	  else
6753 	    {
6754 		char *msg =
6755 		    sqlite3_mprintf ("TopoGeo_ExportTopoLayer() error: \"%s\"",
6756 				     sqlite3_errmsg (topo->db_handle));
6757 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
6758 					     msg);
6759 		sqlite3_free (msg);
6760 		return 0;
6761 	    }
6762       }
6763 
6764     return 1;
6765 }
6766 
6767 GAIATOPO_DECLARE int
gaiaTopoGeo_ExportTopoLayer(GaiaTopologyAccessorPtr accessor,const char * topolayer_name,const char * out_table,int with_spatial_index,int create_only)6768 gaiaTopoGeo_ExportTopoLayer (GaiaTopologyAccessorPtr accessor,
6769 			     const char *topolayer_name, const char *out_table,
6770 			     int with_spatial_index, int create_only)
6771 {
6772 /* attempting to export a full TopoLayer */
6773     struct gaia_topology *topo = (struct gaia_topology *) accessor;
6774     sqlite3_stmt *stmt_ref = NULL;
6775     sqlite3_stmt *stmt_ins = NULL;
6776     sqlite3_stmt *stmt_rels = NULL;
6777     sqlite3_stmt *stmt_node = NULL;
6778     sqlite3_stmt *stmt_edge = NULL;
6779     sqlite3_stmt *stmt_face = NULL;
6780     int ret;
6781     char *create = NULL;
6782     char *select = NULL;
6783     char *insert;
6784     char *geometry_name;
6785     char *sql;
6786     char *errMsg;
6787     char *xprefix;
6788     char *table;
6789     char *xtable;
6790     int ref_type;
6791     const char *type;
6792     int out_type;
6793     sqlite3_int64 topolayer_id;
6794     if (topo == NULL)
6795 	return 0;
6796 
6797 /* composing the CREATE TABLE output-table statement */
6798     if (!auxtopo_create_export_sql
6799 	(topo, topolayer_name, out_table, &create,
6800 	 &select, &insert, &geometry_name, &topolayer_id))
6801 	goto error;
6802 
6803 /* creating the output-table */
6804     ret = sqlite3_exec (topo->db_handle, create, NULL, NULL, &errMsg);
6805     sqlite3_free (create);
6806     create = NULL;
6807     if (ret != SQLITE_OK)
6808       {
6809 	  char *msg =
6810 	      sqlite3_mprintf ("TopoGeo_ExportTopoLayer() error: \"%s\"",
6811 			       errMsg);
6812 	  sqlite3_free (errMsg);
6813 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
6814 	  sqlite3_free (msg);
6815 	  goto error;
6816       }
6817 
6818 /* checking the Geometry Type */
6819     if (!auxtopo_retrieve_export_geometry_type
6820 	(topo, topolayer_name, &ref_type))
6821 	goto error;
6822     switch (ref_type)
6823       {
6824       case GAIA_POINT:
6825 	  type = "MULTIPOINT";
6826 	  out_type = GAIA_MULTIPOINT;
6827 	  break;
6828       case GAIA_LINESTRING:
6829 	  type = "MULTILINESTRING";
6830 	  out_type = GAIA_MULTILINESTRING;
6831 	  break;
6832       case GAIA_POLYGON:
6833 	  type = "MULTIPOLYGON";
6834 	  out_type = GAIA_MULTIPOLYGON;
6835 	  break;
6836       case GAIA_GEOMETRYCOLLECTION:
6837 	  type = "GEOMETRYCOLLECTION";
6838 	  out_type = GAIA_GEOMETRYCOLLECTION;
6839 	  break;
6840       default:
6841 	  type = "GEOMETRY";
6842 	  out_type = GAIA_UNKNOWN;
6843 	  break;
6844       };
6845 
6846 /* creating the output Geometry Column */
6847     sql =
6848 	sqlite3_mprintf
6849 	("SELECT AddGeometryColumn(Lower(%Q), %Q, %d, '%s', '%s')",
6850 	 out_table, geometry_name, topo->srid, type,
6851 	 (topo->has_z == 0) ? "XY" : "XYZ");
6852     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
6853     sqlite3_free (sql);
6854     if (ret != SQLITE_OK)
6855       {
6856 	  char *msg =
6857 	      sqlite3_mprintf ("TopoGeo_ExportTopoLayer() error: \"%s\"",
6858 			       errMsg);
6859 	  sqlite3_free (errMsg);
6860 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
6861 	  sqlite3_free (msg);
6862 	  goto error;
6863       }
6864 
6865     if (with_spatial_index)
6866       {
6867 	  /* creating a Spatial Index supporting the Geometry Column */
6868 	  sql =
6869 	      sqlite3_mprintf
6870 	      ("SELECT CreateSpatialIndex(Lower(%Q), %Q)",
6871 	       out_table, geometry_name);
6872 	  ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
6873 	  sqlite3_free (sql);
6874 	  if (ret != SQLITE_OK)
6875 	    {
6876 		char *msg =
6877 		    sqlite3_mprintf ("TopoGeo_ExportTopoLayer() error: \"%s\"",
6878 				     errMsg);
6879 		sqlite3_free (errMsg);
6880 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
6881 					     msg);
6882 		sqlite3_free (msg);
6883 		goto error;
6884 	    }
6885       }
6886     free (geometry_name);
6887     if (create_only)
6888       {
6889 	  sqlite3_free (select);
6890 	  sqlite3_free (insert);
6891 	  return 1;
6892       }
6893 
6894 /* preparing the "SELECT * FROM topo-features-table" query */
6895     ret =
6896 	sqlite3_prepare_v2 (topo->db_handle, select, strlen (select), &stmt_ref,
6897 			    NULL);
6898     sqlite3_free (select);
6899     select = NULL;
6900     if (ret != SQLITE_OK)
6901       {
6902 	  char *msg =
6903 	      sqlite3_mprintf ("TopoGeo_ExportTopoLayer() error: \"%s\"",
6904 			       sqlite3_errmsg (topo->db_handle));
6905 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
6906 	  sqlite3_free (msg);
6907 	  goto error;
6908       }
6909 
6910 /* preparing the "INSERT INTO out-table" query */
6911     ret =
6912 	sqlite3_prepare_v2 (topo->db_handle, insert, strlen (insert), &stmt_ins,
6913 			    NULL);
6914     sqlite3_free (insert);
6915     insert = NULL;
6916     if (ret != SQLITE_OK)
6917       {
6918 	  char *msg =
6919 	      sqlite3_mprintf ("TopoGeo_ExportTopoLayer() error: \"%s\"",
6920 			       sqlite3_errmsg (topo->db_handle));
6921 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
6922 	  sqlite3_free (msg);
6923 	  goto error;
6924       }
6925 
6926 /* preparing the "SELECT * FROM topo-features" query */
6927     table = sqlite3_mprintf ("%s_topofeatures", topo->topology_name);
6928     xtable = gaiaDoubleQuotedSql (table);
6929     sqlite3_free (table);
6930     sql = sqlite3_mprintf ("SELECT node_id, edge_id, face_id FROM \"%s\" "
6931 			   "WHERE topolayer_id = ? AND fid = ?", xtable);
6932     free (xtable);
6933     ret =
6934 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_rels,
6935 			    NULL);
6936     sqlite3_free (sql);
6937     select = NULL;
6938     if (ret != SQLITE_OK)
6939       {
6940 	  char *msg =
6941 	      sqlite3_mprintf ("TopoGeo_ExportTopoLayer() error: \"%s\"",
6942 			       sqlite3_errmsg (topo->db_handle));
6943 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
6944 	  sqlite3_free (msg);
6945 	  goto error;
6946       }
6947 
6948 /* preparing the Topo-Nodes query */
6949     xprefix = sqlite3_mprintf ("%s_node", topo->topology_name);
6950     xtable = gaiaDoubleQuotedSql (xprefix);
6951     sql = sqlite3_mprintf ("SELECT geom FROM MAIN.\"%s\" WHERE node_id = ?",
6952 			   xtable, xprefix);
6953     free (xtable);
6954     sqlite3_free (xprefix);
6955     ret =
6956 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_node,
6957 			    NULL);
6958     sqlite3_free (sql);
6959     if (ret != SQLITE_OK)
6960       {
6961 	  char *msg =
6962 	      sqlite3_mprintf ("TopoGeo_ExportTopoLayer() error: \"%s\"",
6963 			       sqlite3_errmsg (topo->db_handle));
6964 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
6965 	  sqlite3_free (msg);
6966 	  goto error;
6967       }
6968 
6969 /* preparing the Topo-Edges query */
6970     xprefix = sqlite3_mprintf ("%s_edge", topo->topology_name);
6971     xtable = gaiaDoubleQuotedSql (xprefix);
6972     sql = sqlite3_mprintf ("SELECT geom FROM MAIN.\"%s\" WHERE edge_id = ?",
6973 			   xtable, xprefix);
6974     free (xtable);
6975     sqlite3_free (xprefix);
6976     ret =
6977 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_edge,
6978 			    NULL);
6979     sqlite3_free (sql);
6980     if (ret != SQLITE_OK)
6981       {
6982 	  char *msg =
6983 	      sqlite3_mprintf ("TopoGeo_ExportTopoLayer() error: \"%s\"",
6984 			       sqlite3_errmsg (topo->db_handle));
6985 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
6986 	  sqlite3_free (msg);
6987 	  goto error;
6988       }
6989 
6990 /* preparing the Topo-Faces query */
6991     xprefix = sqlite3_mprintf ("%s_edge", topo->topology_name);
6992     xtable = gaiaDoubleQuotedSql (xprefix);
6993     sql =
6994 	sqlite3_mprintf
6995 	("SELECT edge_id, left_face, right_face, geom FROM MAIN.\"%s\" "
6996 	 "WHERE left_face = ? OR right_face = ?", xtable);
6997     free (xtable);
6998     sqlite3_free (xprefix);
6999     ret =
7000 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_face,
7001 			    NULL);
7002     sqlite3_free (sql);
7003     if (ret != SQLITE_OK)
7004       {
7005 	  char *msg =
7006 	      sqlite3_mprintf ("TopoGeo_ExportTopoLayer() error: \"%s\"",
7007 			       sqlite3_errmsg (topo->db_handle));
7008 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
7009 	  sqlite3_free (msg);
7010 	  goto error;
7011       }
7012 
7013 /* evaluating TopoLayer's Features */
7014     if (!do_eval_topogeo_features
7015 	(topo, stmt_ref, stmt_ins, stmt_rels, stmt_node, stmt_edge, stmt_face,
7016 	 topolayer_id, out_type))
7017 	goto error;
7018 
7019     sqlite3_finalize (stmt_ref);
7020     sqlite3_finalize (stmt_ins);
7021     sqlite3_finalize (stmt_rels);
7022     sqlite3_finalize (stmt_node);
7023     sqlite3_finalize (stmt_edge);
7024     sqlite3_finalize (stmt_face);
7025     return 1;
7026 
7027   error:
7028     if (create != NULL)
7029 	sqlite3_free (create);
7030     if (select != NULL)
7031 	sqlite3_free (select);
7032     if (insert != NULL)
7033 	sqlite3_free (insert);
7034     if (stmt_ref != NULL)
7035 	sqlite3_finalize (stmt_ref);
7036     if (stmt_ins != NULL)
7037 	sqlite3_finalize (stmt_ins);
7038     if (stmt_rels != NULL)
7039 	sqlite3_finalize (stmt_rels);
7040     if (stmt_node != NULL)
7041 	sqlite3_finalize (stmt_node);
7042     if (stmt_edge != NULL)
7043 	sqlite3_finalize (stmt_edge);
7044     if (stmt_face != NULL)
7045 	sqlite3_finalize (stmt_face);
7046     return 0;
7047 }
7048 
7049 static int
check_output_table(struct gaia_topology * topo,const char * out_table,int * out_type)7050 check_output_table (struct gaia_topology *topo, const char *out_table,
7051 		    int *out_type)
7052 {
7053 /* checking the output table */
7054     char *sql;
7055     int ret;
7056     int i;
7057     char **results;
7058     int rows;
7059     int columns;
7060     int count = 0;
7061     int type;
7062 
7063     sql =
7064 	sqlite3_mprintf
7065 	("SELECT geometry_type FROM MAIN.geometry_columns WHERE f_table_name = Lower(%Q)",
7066 	 out_table);
7067     ret =
7068 	sqlite3_get_table (topo->db_handle, sql, &results, &rows, &columns,
7069 			   NULL);
7070     sqlite3_free (sql);
7071     if (ret != SQLITE_OK)
7072 	return 0;
7073     if (rows < 1)
7074 	;
7075     else
7076       {
7077 	  for (i = 1; i <= rows; i++)
7078 	    {
7079 		type = atoi (results[(i * columns) + 0]);
7080 		count++;
7081 	    }
7082       }
7083     sqlite3_free_table (results);
7084 
7085     if (count != 1)
7086 	return 0;
7087 
7088     switch (type)
7089       {
7090       case GAIA_POINT:
7091       case GAIA_POINTZ:
7092       case GAIA_POINTM:
7093       case GAIA_POINTZM:
7094 	  *out_type = GAIA_POINT;
7095 	  break;
7096       case GAIA_MULTIPOINT:
7097       case GAIA_MULTIPOINTZ:
7098       case GAIA_MULTIPOINTM:
7099       case GAIA_MULTIPOINTZM:
7100 	  *out_type = GAIA_MULTIPOINT;
7101 	  break;
7102       case GAIA_LINESTRING:
7103       case GAIA_LINESTRINGZ:
7104       case GAIA_LINESTRINGM:
7105       case GAIA_LINESTRINGZM:
7106       case GAIA_MULTILINESTRING:
7107       case GAIA_MULTILINESTRINGZ:
7108       case GAIA_MULTILINESTRINGM:
7109       case GAIA_MULTILINESTRINGZM:
7110 	  *out_type = GAIA_MULTILINESTRING;
7111 	  break;
7112       case GAIA_POLYGON:
7113       case GAIA_POLYGONZ:
7114       case GAIA_POLYGONM:
7115       case GAIA_POLYGONZM:
7116       case GAIA_MULTIPOLYGON:
7117       case GAIA_MULTIPOLYGONZ:
7118       case GAIA_MULTIPOLYGONM:
7119       case GAIA_MULTIPOLYGONZM:
7120 	  *out_type = GAIA_MULTIPOLYGON;
7121 	  break;
7122       case GAIA_GEOMETRYCOLLECTION:
7123       case GAIA_GEOMETRYCOLLECTIONZ:
7124       case GAIA_GEOMETRYCOLLECTIONM:
7125       case GAIA_GEOMETRYCOLLECTIONZM:
7126 	  *out_type = GAIA_GEOMETRYCOLLECTION;
7127 	  break;
7128       default:
7129 	  *out_type = GAIA_UNKNOWN;
7130 	  break;
7131       };
7132     return 1;
7133 }
7134 
7135 static int
auxtopo_export_feature_sql(struct gaia_topology * topo,const char * topolayer_name,const char * out_table,char ** xselect,char ** xinsert,sqlite3_int64 * topolayer_id,int * out_type)7136 auxtopo_export_feature_sql (struct gaia_topology *topo,
7137 			    const char *topolayer_name, const char *out_table,
7138 			    char **xselect, char **xinsert,
7139 			    sqlite3_int64 * topolayer_id, int *out_type)
7140 {
7141 /* composing the CREATE TABLE insert-feature statement */
7142     char *select = NULL;
7143     char *insert = NULL;
7144     char *prev;
7145     char *sql;
7146     char *table;
7147     char *xtable;
7148     char *xprefix;
7149     char dummy[64];
7150     int i;
7151     char **results;
7152     int rows;
7153     int columns;
7154     const char *name;
7155     int ret;
7156     int first_select = 1;
7157     int first_insert = 1;
7158     int ncols = 0;
7159     int icol;
7160     int ref_col = 0;
7161     char *geometry_name = NULL;
7162     int geom_alias = 0;
7163 
7164     *xselect = NULL;
7165     *xinsert = NULL;
7166 
7167 /* checking the TopoLayer */
7168     if (!check_topolayer (topo, topolayer_name, topolayer_id))
7169 	return 0;
7170 
7171 /* checking the output table */
7172     if (!check_output_table (topo, out_table, out_type))
7173 	return 0;
7174 
7175     xtable = gaiaDoubleQuotedSql (out_table);
7176     select = sqlite3_mprintf ("SELECT fid, ");
7177     insert = sqlite3_mprintf ("INSERT INTO MAIN.\"%s\" (fid, ", xtable);
7178     free (xtable);
7179     sprintf (dummy, "%lld", *topolayer_id);
7180     table = sqlite3_mprintf ("%s_topofeatures_%s", topo->topology_name, dummy);
7181     xtable = gaiaDoubleQuotedSql (table);
7182     sqlite3_free (table);
7183     sql = sqlite3_mprintf ("PRAGMA MAIN.table_info(\"%s\")", xtable);
7184     free (xtable);
7185     ret =
7186 	sqlite3_get_table (topo->db_handle, sql, &results, &rows, &columns,
7187 			   NULL);
7188     sqlite3_free (sql);
7189     if (ret != SQLITE_OK)
7190 	goto error;
7191     if (rows < 1)
7192 	;
7193     else
7194       {
7195 	  for (i = 1; i <= rows; i++)
7196 	    {
7197 		name = results[(i * columns) + 1];
7198 		if (strcmp (name, "fid") == 0)
7199 		    continue;
7200 		/* SELECT: adding a column */
7201 		xprefix = gaiaDoubleQuotedSql (name);
7202 		prev = select;
7203 		if (first_select)
7204 		    select = sqlite3_mprintf ("%s\"%s\"", prev, xprefix);
7205 		else
7206 		    select = sqlite3_mprintf ("%s, \"%s\"", prev, xprefix);
7207 		first_select = 0;
7208 		free (xprefix);
7209 		sqlite3_free (prev);
7210 		ref_col++;
7211 		/* INSERT: adding a column */
7212 		xprefix = gaiaDoubleQuotedSql (name);
7213 		prev = insert;
7214 		if (first_insert)
7215 		    insert = sqlite3_mprintf ("%s\"%s\"", prev, xprefix);
7216 		else
7217 		    insert = sqlite3_mprintf ("%s, \"%s\"", prev, xprefix);
7218 		first_insert = 0;
7219 		free (xprefix);
7220 		sqlite3_free (prev);
7221 		ncols++;
7222 	    }
7223       }
7224     sqlite3_free_table (results);
7225 
7226     geometry_name = malloc (strlen ("geometry") + 1);
7227     strcpy (geometry_name, "geometry");
7228     sprintf (dummy, "%lld", *topolayer_id);
7229     table = sqlite3_mprintf ("%s_topofeatures_%s", topo->topology_name, dummy);
7230     while (1)
7231       {
7232 	  /* searching an unique Geometry name */
7233 	  if (is_unique_geom_name (topo->db_handle, table, geometry_name))
7234 	      break;
7235 	  sprintf (dummy, "geom_%d", ++geom_alias);
7236 	  free (geometry_name);
7237 	  geometry_name = malloc (strlen (dummy) + 1);
7238 	  strcpy (geometry_name, dummy);
7239       }
7240     sqlite3_free (table);
7241 
7242 /* completing the SQL statements */
7243     prev = select;
7244     sprintf (dummy, "%lld", *topolayer_id);
7245     table = sqlite3_mprintf ("%s_topofeatures_%s", topo->topology_name, dummy);
7246     xtable = gaiaDoubleQuotedSql (table);
7247     sqlite3_free (table);
7248     select =
7249 	sqlite3_mprintf ("%s FROM MAIN.\"%s\" WHERE fid = ?", prev, xtable);
7250     free (xtable);
7251     sqlite3_free (prev);
7252     prev = insert;
7253     insert = sqlite3_mprintf ("%s, \"%s\") VALUES (?, ", prev, geometry_name);
7254     sqlite3_free (prev);
7255     for (icol = 0; icol < ncols; icol++)
7256       {
7257 	  prev = insert;
7258 	  if (icol == 0)
7259 	      insert = sqlite3_mprintf ("%s?", prev);
7260 	  else
7261 	      insert = sqlite3_mprintf ("%s, ?", prev);
7262 	  sqlite3_free (prev);
7263       }
7264     prev = insert;
7265     insert = sqlite3_mprintf ("%s, ?)", prev);
7266     sqlite3_free (prev);
7267 
7268     free (geometry_name);
7269     *xselect = select;
7270     *xinsert = insert;
7271     return 1;
7272 
7273   error:
7274     if (geometry_name != NULL)
7275 	free (geometry_name);
7276     if (select != NULL)
7277 	sqlite3_free (select);
7278     if (insert != NULL)
7279 	sqlite3_free (insert);
7280     return 0;
7281 }
7282 
7283 static int
do_eval_topogeo_single_feature(struct gaia_topology * topo,sqlite3_stmt * stmt_ref,sqlite3_stmt * stmt_ins,sqlite3_stmt * stmt_rels,sqlite3_stmt * stmt_node,sqlite3_stmt * stmt_edge,sqlite3_stmt * stmt_face,sqlite3_int64 topolayer_id,int out_type,sqlite3_int64 fid)7284 do_eval_topogeo_single_feature (struct gaia_topology *topo,
7285 				sqlite3_stmt * stmt_ref,
7286 				sqlite3_stmt * stmt_ins,
7287 				sqlite3_stmt * stmt_rels,
7288 				sqlite3_stmt * stmt_node,
7289 				sqlite3_stmt * stmt_edge,
7290 				sqlite3_stmt * stmt_face,
7291 				sqlite3_int64 topolayer_id, int out_type,
7292 				sqlite3_int64 fid)
7293 {
7294 /* querying the ref-table */
7295     int ret;
7296     int count = 0;
7297 
7298     sqlite3_reset (stmt_ref);
7299     sqlite3_clear_bindings (stmt_ref);
7300     sqlite3_bind_int64 (stmt_ref, 1, fid);
7301     while (1)
7302       {
7303 	  /* scrolling the result set rows */
7304 	  gaiaGeomCollPtr geom = NULL;
7305 	  ret = sqlite3_step (stmt_ref);
7306 	  if (ret == SQLITE_DONE)
7307 	      break;		/* end of result set */
7308 	  if (ret == SQLITE_ROW)
7309 	    {
7310 		int icol;
7311 		int ncol = sqlite3_column_count (stmt_ref);
7312 		sqlite3_reset (stmt_ins);
7313 		sqlite3_clear_bindings (stmt_ins);
7314 		for (icol = 0; icol < ncol; icol++)
7315 		  {
7316 		      int col_type = sqlite3_column_type (stmt_ref, icol);
7317 		      switch (col_type)
7318 			{
7319 			case SQLITE_INTEGER:
7320 			    sqlite3_bind_int64 (stmt_ins, icol + 1,
7321 						sqlite3_column_int64 (stmt_ref,
7322 								      icol));
7323 			    break;
7324 			case SQLITE_FLOAT:
7325 			    sqlite3_bind_double (stmt_ins, icol + 1,
7326 						 sqlite3_column_double
7327 						 (stmt_ref, icol));
7328 			    break;
7329 			case SQLITE_TEXT:
7330 			    sqlite3_bind_text (stmt_ins, icol + 1,
7331 					       (const char *)
7332 					       sqlite3_column_text (stmt_ref,
7333 								    icol),
7334 					       sqlite3_column_bytes (stmt_ref,
7335 								     icol),
7336 					       SQLITE_STATIC);
7337 			    break;
7338 			case SQLITE_BLOB:
7339 			    sqlite3_bind_blob (stmt_ins, icol + 1,
7340 					       sqlite3_column_blob (stmt_ref,
7341 								    icol),
7342 					       sqlite3_column_bytes (stmt_ref,
7343 								     icol),
7344 					       SQLITE_STATIC);
7345 			    break;
7346 			default:
7347 			    sqlite3_bind_null (stmt_ins, icol + 1);
7348 			    break;
7349 			};
7350 		  }
7351 		/* the Geometry column */
7352 		ncol = sqlite3_bind_parameter_count (stmt_ins);
7353 		geom =
7354 		    do_eval_topo_geometry (topo, stmt_rels, stmt_node,
7355 					   stmt_edge, stmt_face, fid,
7356 					   topolayer_id, out_type);
7357 		if (geom != NULL)
7358 		  {
7359 		      unsigned char *p_blob;
7360 		      int n_bytes;
7361 		      gaiaToSpatiaLiteBlobWkb (geom, &p_blob, &n_bytes);
7362 		      sqlite3_bind_blob (stmt_ins, ncol, p_blob, n_bytes,
7363 					 SQLITE_TRANSIENT);
7364 		      free (p_blob);
7365 		      gaiaFreeGeomColl (geom);
7366 		  }
7367 		else
7368 		    sqlite3_bind_null (stmt_ins, ncol);
7369 		ret = sqlite3_step (stmt_ins);
7370 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
7371 		    ;
7372 		else
7373 		  {
7374 		      char *msg =
7375 			  sqlite3_mprintf
7376 			  ("InsertFeatureFromTopoLayer() error: \"%s\"",
7377 			   sqlite3_errmsg (topo->db_handle));
7378 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
7379 						   topo, msg);
7380 		      sqlite3_free (msg);
7381 		      return 0;
7382 		  }
7383 		count++;
7384 	    }
7385 	  else
7386 	    {
7387 		char *msg =
7388 		    sqlite3_mprintf
7389 		    ("InsertFeatureFromTopoLayer() error: \"%s\"",
7390 		     sqlite3_errmsg (topo->db_handle));
7391 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
7392 					     msg);
7393 		sqlite3_free (msg);
7394 		return 0;
7395 	    }
7396       }
7397 
7398     if (count <= 0)
7399       {
7400 	  char *msg =
7401 	      sqlite3_mprintf
7402 	      ("InsertFeatureFromTopoLayer(): not existing TopoFeature");
7403 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
7404 	  sqlite3_free (msg);
7405 	  return 0;
7406       }
7407     return 1;
7408 }
7409 
7410 GAIATOPO_DECLARE int
gaiaTopoGeo_InsertFeatureFromTopoLayer(GaiaTopologyAccessorPtr accessor,const char * topolayer_name,const char * out_table,sqlite3_int64 fid)7411 gaiaTopoGeo_InsertFeatureFromTopoLayer (GaiaTopologyAccessorPtr accessor,
7412 					const char *topolayer_name,
7413 					const char *out_table,
7414 					sqlite3_int64 fid)
7415 {
7416 /* attempting to insert a single TopoLayer's Feature into the output GeoTable */
7417     struct gaia_topology *topo = (struct gaia_topology *) accessor;
7418     sqlite3_stmt *stmt_ref = NULL;
7419     sqlite3_stmt *stmt_ins = NULL;
7420     sqlite3_stmt *stmt_rels = NULL;
7421     sqlite3_stmt *stmt_node = NULL;
7422     sqlite3_stmt *stmt_edge = NULL;
7423     sqlite3_stmt *stmt_face = NULL;
7424     int ret;
7425     char *select;
7426     char *insert;
7427     char *sql;
7428     char *xprefix;
7429     char *table;
7430     char *xtable;
7431     int out_type;
7432     sqlite3_int64 topolayer_id;
7433     if (topo == NULL)
7434 	return 0;
7435 
7436 /* composing the SQL statements */
7437     if (!auxtopo_export_feature_sql
7438 	(topo, topolayer_name, out_table, &select, &insert, &topolayer_id,
7439 	 &out_type))
7440 	goto error;
7441 
7442 /* preparing the "SELECT * FROM topo-features-table" query */
7443     ret =
7444 	sqlite3_prepare_v2 (topo->db_handle, select, strlen (select), &stmt_ref,
7445 			    NULL);
7446     sqlite3_free (select);
7447     select = NULL;
7448     if (ret != SQLITE_OK)
7449       {
7450 	  char *msg =
7451 	      sqlite3_mprintf ("InsertFeatureFromTopoLayer() error: \"%s\"",
7452 			       sqlite3_errmsg (topo->db_handle));
7453 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
7454 	  sqlite3_free (msg);
7455 	  goto error;
7456       }
7457 
7458 /* preparing the "INSERT INTO out-table" query */
7459     ret =
7460 	sqlite3_prepare_v2 (topo->db_handle, insert, strlen (insert), &stmt_ins,
7461 			    NULL);
7462     sqlite3_free (insert);
7463     insert = NULL;
7464     if (ret != SQLITE_OK)
7465       {
7466 	  char *msg =
7467 	      sqlite3_mprintf ("InsertFeatureFromTopoLayer() error: \"%s\"",
7468 			       sqlite3_errmsg (topo->db_handle));
7469 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
7470 	  goto error;
7471       }
7472 
7473 /* preparing the "SELECT * FROM topo-features" query */
7474     table = sqlite3_mprintf ("%s_topofeatures", topo->topology_name);
7475     xtable = gaiaDoubleQuotedSql (table);
7476     sqlite3_free (table);
7477     sql = sqlite3_mprintf ("SELECT node_id, edge_id, face_id FROM \"%s\" "
7478 			   "WHERE topolayer_id = ? AND fid = ?", xtable);
7479     free (xtable);
7480     ret =
7481 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_rels,
7482 			    NULL);
7483     sqlite3_free (sql);
7484     select = NULL;
7485     if (ret != SQLITE_OK)
7486       {
7487 	  char *msg =
7488 	      sqlite3_mprintf ("InsertFeatureFromTopoLayer() error: \"%s\"",
7489 			       sqlite3_errmsg (topo->db_handle));
7490 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
7491 	  sqlite3_free (msg);
7492 	  goto error;
7493       }
7494 
7495 /* preparing the Topo-Nodes query */
7496     xprefix = sqlite3_mprintf ("%s_node", topo->topology_name);
7497     xtable = gaiaDoubleQuotedSql (xprefix);
7498     sql = sqlite3_mprintf ("SELECT geom FROM MAIN.\"%s\" WHERE node_id = ?",
7499 			   xtable, xprefix);
7500     free (xtable);
7501     sqlite3_free (xprefix);
7502     ret =
7503 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_node,
7504 			    NULL);
7505     sqlite3_free (sql);
7506     if (ret != SQLITE_OK)
7507       {
7508 	  char *msg =
7509 	      sqlite3_mprintf ("InsertFeatureFromTopoLayer() error: \"%s\"",
7510 			       sqlite3_errmsg (topo->db_handle));
7511 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
7512 	  sqlite3_free (msg);
7513 	  goto error;
7514       }
7515 
7516 /* preparing the Topo-Edges query */
7517     xprefix = sqlite3_mprintf ("%s_edge", topo->topology_name);
7518     xtable = gaiaDoubleQuotedSql (xprefix);
7519     sql = sqlite3_mprintf ("SELECT geom FROM MAIN.\"%s\" WHERE edge_id = ?",
7520 			   xtable, xprefix);
7521     free (xtable);
7522     sqlite3_free (xprefix);
7523     ret =
7524 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_edge,
7525 			    NULL);
7526     sqlite3_free (sql);
7527     if (ret != SQLITE_OK)
7528       {
7529 	  char *msg =
7530 	      sqlite3_mprintf ("InsertFeatureFromTopoLayer() error: \"%s\"",
7531 			       sqlite3_errmsg (topo->db_handle));
7532 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
7533 	  sqlite3_free (msg);
7534 	  goto error;
7535       }
7536 
7537 /* preparing the Topo-Faces query */
7538     xprefix = sqlite3_mprintf ("%s_edge", topo->topology_name);
7539     xtable = gaiaDoubleQuotedSql (xprefix);
7540     sql =
7541 	sqlite3_mprintf
7542 	("SELECT edge_id, left_face, right_face, geom FROM MAIN.\"%s\" "
7543 	 "WHERE left_face = ? OR right_face = ?", xtable);
7544     free (xtable);
7545     sqlite3_free (xprefix);
7546     ret =
7547 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_face,
7548 			    NULL);
7549     sqlite3_free (sql);
7550     if (ret != SQLITE_OK)
7551       {
7552 	  char *msg =
7553 	      sqlite3_mprintf ("InsertFeatureFromTopoLayer() error: \"%s\"",
7554 			       sqlite3_errmsg (topo->db_handle));
7555 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
7556 	  sqlite3_free (msg);
7557 	  goto error;
7558       }
7559 
7560 /* evaluating TopoLayer's Features */
7561     if (!do_eval_topogeo_single_feature
7562 	(topo, stmt_ref, stmt_ins, stmt_rels, stmt_node, stmt_edge, stmt_face,
7563 	 topolayer_id, out_type, fid))
7564 	goto error;
7565 
7566     sqlite3_finalize (stmt_ref);
7567     sqlite3_finalize (stmt_ins);
7568     sqlite3_finalize (stmt_rels);
7569     sqlite3_finalize (stmt_node);
7570     sqlite3_finalize (stmt_edge);
7571     sqlite3_finalize (stmt_face);
7572     return 1;
7573 
7574   error:
7575     if (select != NULL)
7576 	sqlite3_free (select);
7577     if (insert != NULL)
7578 	sqlite3_free (insert);
7579     if (stmt_ref != NULL)
7580 	sqlite3_finalize (stmt_ref);
7581     if (stmt_ins != NULL)
7582 	sqlite3_finalize (stmt_ins);
7583     if (stmt_rels != NULL)
7584 	sqlite3_finalize (stmt_rels);
7585     if (stmt_node != NULL)
7586 	sqlite3_finalize (stmt_node);
7587     if (stmt_edge != NULL)
7588 	sqlite3_finalize (stmt_edge);
7589     if (stmt_face != NULL)
7590 	sqlite3_finalize (stmt_face);
7591     return 0;
7592 }
7593 
7594 static int
do_topo_snap(struct gaia_topology * topo,int geom_col,int geo_type,double tolerance_snap,double tolerance_removal,int iterate,sqlite3_stmt * stmt_in,sqlite3_stmt * stmt_out)7595 do_topo_snap (struct gaia_topology *topo, int geom_col, int geo_type,
7596 	      double tolerance_snap, double tolerance_removal, int iterate,
7597 	      sqlite3_stmt * stmt_in, sqlite3_stmt * stmt_out)
7598 {
7599 /* snapping geometries againt Topology */
7600     int ret;
7601 
7602     sqlite3_reset (stmt_in);
7603     sqlite3_clear_bindings (stmt_in);
7604     while (1)
7605       {
7606 	  /* scrolling the result set rows */
7607 	  ret = sqlite3_step (stmt_in);
7608 	  if (ret == SQLITE_DONE)
7609 	      break;		/* end of result set */
7610 	  if (ret == SQLITE_ROW)
7611 	    {
7612 		int icol;
7613 		int ncol = sqlite3_column_count (stmt_in);
7614 		sqlite3_reset (stmt_out);
7615 		sqlite3_clear_bindings (stmt_out);
7616 		for (icol = 0; icol < ncol; icol++)
7617 		  {
7618 		      int col_type = sqlite3_column_type (stmt_in, icol);
7619 		      if (icol == geom_col)
7620 			{
7621 			    /* the geometry column */
7622 			    const unsigned char *blob =
7623 				sqlite3_column_blob (stmt_in, icol);
7624 			    int blob_sz = sqlite3_column_bytes (stmt_in, icol);
7625 			    gaiaGeomCollPtr geom =
7626 				gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
7627 			    if (geom != NULL)
7628 			      {
7629 				  gaiaGeomCollPtr result;
7630 				  unsigned char *p_blob;
7631 				  int n_bytes;
7632 				  int gpkg_mode = 0;
7633 				  int tiny_point = 0;
7634 				  if (topo->cache != NULL)
7635 				    {
7636 					struct splite_internal_cache *cache =
7637 					    (struct splite_internal_cache
7638 					     *) (topo->cache);
7639 					gpkg_mode = cache->gpkg_mode;
7640 					tiny_point = cache->tinyPointEnabled;
7641 				    }
7642 				  result =
7643 				      gaiaTopoSnap ((GaiaTopologyAccessorPtr)
7644 						    topo, geom, tolerance_snap,
7645 						    tolerance_removal, iterate);
7646 				  gaiaFreeGeomColl (geom);
7647 				  if (result != NULL)
7648 				    {
7649 					result->DeclaredType = geo_type;
7650 					gaiaToSpatiaLiteBlobWkbEx2 (result,
7651 								    &p_blob,
7652 								    &n_bytes,
7653 								    gpkg_mode,
7654 								    tiny_point);
7655 					gaiaFreeGeomColl (result);
7656 					sqlite3_bind_blob (stmt_out, icol + 1,
7657 							   p_blob, n_bytes,
7658 							   free);
7659 				    }
7660 				  else
7661 				      sqlite3_bind_null (stmt_out, icol + 1);
7662 			      }
7663 			    else
7664 				sqlite3_bind_null (stmt_out, icol + 1);
7665 			    continue;
7666 			}
7667 		      switch (col_type)
7668 			{
7669 			case SQLITE_INTEGER:
7670 			    sqlite3_bind_int64 (stmt_out, icol + 1,
7671 						sqlite3_column_int64 (stmt_in,
7672 								      icol));
7673 			    break;
7674 			case SQLITE_FLOAT:
7675 			    sqlite3_bind_double (stmt_out, icol + 1,
7676 						 sqlite3_column_double
7677 						 (stmt_in, icol));
7678 			    break;
7679 			case SQLITE_TEXT:
7680 			    sqlite3_bind_text (stmt_out, icol + 1,
7681 					       (const char *)
7682 					       sqlite3_column_text (stmt_in,
7683 								    icol),
7684 					       sqlite3_column_bytes (stmt_in,
7685 								     icol),
7686 					       SQLITE_STATIC);
7687 			    break;
7688 			case SQLITE_BLOB:
7689 			    sqlite3_bind_blob (stmt_out, icol + 1,
7690 					       sqlite3_column_blob (stmt_in,
7691 								    icol),
7692 					       sqlite3_column_bytes (stmt_in,
7693 								     icol),
7694 					       SQLITE_STATIC);
7695 			    break;
7696 			default:
7697 			    sqlite3_bind_null (stmt_out, icol + 1);
7698 			    break;
7699 			};
7700 		  }
7701 		ret = sqlite3_step (stmt_out);
7702 		if (ret == SQLITE_DONE || ret == SQLITE_ROW)
7703 		    ;
7704 		else
7705 		  {
7706 		      char *msg =
7707 			  sqlite3_mprintf
7708 			  ("TopoGeo_SnappedGeoTable() error: \"%s\"",
7709 			   sqlite3_errmsg (topo->db_handle));
7710 		      gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr)
7711 						   topo, msg);
7712 		      sqlite3_free (msg);
7713 		      return 0;
7714 		  }
7715 	    }
7716 	  else
7717 	    {
7718 		char *msg =
7719 		    sqlite3_mprintf ("TopoGeo_SnappedGeoTable() error: \"%s\"",
7720 				     sqlite3_errmsg (topo->db_handle));
7721 		gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo,
7722 					     msg);
7723 		sqlite3_free (msg);
7724 		return 0;
7725 	    }
7726       }
7727     return 1;
7728 }
7729 
7730 GAIATOPO_DECLARE int
gaiaTopoGeo_SnappedGeoTable(GaiaTopologyAccessorPtr accessor,const char * db_prefix,const char * table,const char * column,const char * out_table,double tolerance_snap,double tolerance_removal,int iterate)7731 gaiaTopoGeo_SnappedGeoTable (GaiaTopologyAccessorPtr accessor,
7732 			     const char *db_prefix, const char *table,
7733 			     const char *column, const char *out_table,
7734 			     double tolerance_snap, double tolerance_removal,
7735 			     int iterate)
7736 {
7737 /*
7738 / attempting to create and populate a new GeoTable by snapping all Geometries
7739 / contained into another GeoTable against a given Topology
7740 */
7741     struct gaia_topology *topo = (struct gaia_topology *) accessor;
7742     sqlite3_stmt *stmt_in = NULL;
7743     sqlite3_stmt *stmt_out = NULL;
7744     int ret;
7745     char *create;
7746     char *select;
7747     char *insert;
7748     char *sql;
7749     char *errMsg;
7750     int geo_type;
7751     const char *type;
7752     int geom_col;
7753     if (topo == NULL)
7754 	return 0;
7755 
7756 /* composing the CREATE TABLE output-table statement */
7757     if (!auxtopo_create_togeotable_sql
7758 	(topo->db_handle, db_prefix, table, column, out_table, &create,
7759 	 &select, &insert, &geom_col))
7760 	goto error;
7761 
7762 /* creating the output-table */
7763     ret = sqlite3_exec (topo->db_handle, create, NULL, NULL, &errMsg);
7764     sqlite3_free (create);
7765     create = NULL;
7766     if (ret != SQLITE_OK)
7767       {
7768 	  char *msg =
7769 	      sqlite3_mprintf ("TopoGeo_SnappedGeoTable() error: \"%s\"",
7770 			       errMsg);
7771 	  sqlite3_free (errMsg);
7772 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
7773 	  sqlite3_free (msg);
7774 	  goto error;
7775       }
7776 
7777 /* checking the Geometry Type */
7778     if (!auxtopo_retrieve_geometry_type
7779 	(topo->db_handle, db_prefix, table, column, &geo_type))
7780 	goto error;
7781     switch (geo_type)
7782       {
7783       case GAIA_POINT:
7784       case GAIA_POINTZ:
7785       case GAIA_POINTM:
7786       case GAIA_POINTZM:
7787 	  type = "POINT";
7788 	  break;
7789       case GAIA_MULTIPOINT:
7790       case GAIA_MULTIPOINTZ:
7791       case GAIA_MULTIPOINTM:
7792       case GAIA_MULTIPOINTZM:
7793 	  type = "MULTIPOINT";
7794 	  break;
7795       case GAIA_LINESTRING:
7796       case GAIA_LINESTRINGZ:
7797       case GAIA_LINESTRINGM:
7798       case GAIA_LINESTRINGZM:
7799 	  type = "LINESTRING";
7800 	  break;
7801       case GAIA_MULTILINESTRING:
7802       case GAIA_MULTILINESTRINGZ:
7803       case GAIA_MULTILINESTRINGM:
7804       case GAIA_MULTILINESTRINGZM:
7805 	  type = "MULTILINESTRING";
7806 	  break;
7807       case GAIA_POLYGON:
7808       case GAIA_POLYGONZ:
7809       case GAIA_POLYGONM:
7810       case GAIA_POLYGONZM:
7811 	  type = "POLYGON";
7812 	  break;
7813       case GAIA_MULTIPOLYGON:
7814       case GAIA_MULTIPOLYGONZ:
7815       case GAIA_MULTIPOLYGONM:
7816       case GAIA_MULTIPOLYGONZM:
7817 	  type = "MULTIPOLYGON";
7818 	  break;
7819       case GAIA_GEOMETRYCOLLECTION:
7820       case GAIA_GEOMETRYCOLLECTIONZ:
7821       case GAIA_GEOMETRYCOLLECTIONM:
7822       case GAIA_GEOMETRYCOLLECTIONZM:
7823 	  type = "GEOMETRYCOLLECTION";
7824 	  break;
7825       default:
7826 	  type = "GEOMETRY";
7827 	  break;
7828       };
7829 
7830 /* creating the output Geometry Column */
7831     sql =
7832 	sqlite3_mprintf
7833 	("SELECT AddGeometryColumn(Lower(%Q), Lower(%Q), %d, '%s', '%s')",
7834 	 out_table, column, topo->srid, type,
7835 	 (topo->has_z == 0) ? "XY" : "XYZ");
7836     ret = sqlite3_exec (topo->db_handle, sql, NULL, NULL, &errMsg);
7837     sqlite3_free (sql);
7838     if (ret != SQLITE_OK)
7839       {
7840 	  char *msg =
7841 	      sqlite3_mprintf ("TopoGeo_SnappedGeoTable() error: \"%s\"",
7842 			       errMsg);
7843 	  sqlite3_free (errMsg);
7844 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
7845 	  sqlite3_free (msg);
7846 	  goto error;
7847       }
7848 
7849 /* preparing the "SELECT * FROM ref-table" query */
7850     ret =
7851 	sqlite3_prepare_v2 (topo->db_handle, select, strlen (select), &stmt_in,
7852 			    NULL);
7853     sqlite3_free (select);
7854     select = NULL;
7855     if (ret != SQLITE_OK)
7856       {
7857 	  char *msg =
7858 	      sqlite3_mprintf ("TopoGeo_SnappedGeoTable() error: \"%s\"",
7859 			       sqlite3_errmsg (topo->db_handle));
7860 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
7861 	  sqlite3_free (msg);
7862 	  goto error;
7863       }
7864 
7865 /* preparing the "INSERT INTO out-table" query */
7866     ret =
7867 	sqlite3_prepare_v2 (topo->db_handle, insert, strlen (insert), &stmt_out,
7868 			    NULL);
7869     sqlite3_free (insert);
7870     insert = NULL;
7871     if (ret != SQLITE_OK)
7872       {
7873 	  char *msg =
7874 	      sqlite3_mprintf ("TopoGeo_SnappedGeoTable() error: \"%s\"",
7875 			       sqlite3_errmsg (topo->db_handle));
7876 	  gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
7877 	  sqlite3_free (msg);
7878 	  goto error;
7879       }
7880 
7881 /* snapping all Geometries against the Topology */
7882     if (!do_topo_snap
7883 	(topo, geom_col, geo_type, tolerance_snap, tolerance_removal, iterate,
7884 	 stmt_in, stmt_out))
7885 	goto error;
7886 
7887     sqlite3_finalize (stmt_in);
7888     sqlite3_finalize (stmt_out);
7889     return 1;
7890 
7891   error:
7892     if (create != NULL)
7893 	sqlite3_free (create);
7894     if (select != NULL)
7895 	sqlite3_free (select);
7896     if (insert != NULL)
7897 	sqlite3_free (insert);
7898     if (stmt_in != NULL)
7899 	sqlite3_finalize (stmt_in);
7900     if (stmt_out != NULL)
7901 	sqlite3_finalize (stmt_out);
7902     return 0;
7903 }
7904 
7905 SPATIALITE_PRIVATE int
test_inconsistent_topology(const void * handle)7906 test_inconsistent_topology (const void *handle)
7907 {
7908 /* testing for a Topology presenting an inconsistent state */
7909     struct gaia_topology *topo = (struct gaia_topology *) handle;
7910     int ret;
7911     char *errMsg = NULL;
7912     int count = 0;
7913     int i;
7914     char **results;
7915     int rows;
7916     int columns;
7917     char *sql;
7918     char *table;
7919     char *xtable;
7920 
7921     table = sqlite3_mprintf ("%s_edge", topo->topology_name);
7922     xtable = gaiaDoubleQuotedSql (table);
7923     sqlite3_free (table);
7924     sql =
7925 	sqlite3_mprintf ("SELECT Count(*) FROM \"%s\" WHERE left_face IS NULL "
7926 			 "OR right_face IS NULL", xtable);
7927     free (xtable);
7928     ret =
7929 	sqlite3_get_table (topo->db_handle, sql, &results, &rows, &columns,
7930 			   &errMsg);
7931     sqlite3_free (sql);
7932     if (ret != SQLITE_OK)
7933       {
7934 	  spatialite_e ("test_inconsistent_topology error: %s\n", errMsg);
7935 	  sqlite3_free (errMsg);
7936 	  return -1;
7937       }
7938     for (i = 1; i <= rows; i++)
7939 	count = atoi (results[(i * columns) + 0]);
7940     sqlite3_free_table (results);
7941     return count;
7942 }
7943 
7944 static int
topoGeo_EdgeHeal_common(GaiaTopologyAccessorPtr accessor,int mode_new)7945 topoGeo_EdgeHeal_common (GaiaTopologyAccessorPtr accessor, int mode_new)
7946 {
7947 /* common implementation of GeoTable EdgeHeal */
7948     struct gaia_topology *topo = (struct gaia_topology *) accessor;
7949     int ret;
7950     char *sql;
7951     char *node;
7952     char *xnode;
7953     char *edge;
7954     char *xedge;
7955     int loop = 1;
7956     int loop_count;
7957     sqlite3_stmt *stmt1 = NULL;
7958     sqlite3_stmt *stmt2 = NULL;
7959     sqlite3_stmt *stmt3 = NULL;
7960     if (topo == NULL)
7961 	return 0;
7962 
7963     ret = test_inconsistent_topology (accessor);
7964     if (ret != 0)
7965 	return 0;
7966 
7967 /* preparing the SQL query identifying all Nodes of cardinality = 2 */
7968     node = sqlite3_mprintf ("%s_node", topo->topology_name);
7969     xnode = gaiaDoubleQuotedSql (node);
7970     sqlite3_free (node);
7971     edge = sqlite3_mprintf ("%s_edge", topo->topology_name);
7972     xedge = gaiaDoubleQuotedSql (edge);
7973     sqlite3_free (edge);
7974     sql =
7975 	sqlite3_mprintf ("SELECT n.node_id, Count(*) AS cnt FROM \"%s\" AS n "
7976 			 "JOIN \"%s\" AS e ON (n.node_id = e.start_node OR n.node_id = e.end_node) "
7977 			 "GROUP BY n.node_id HAVING cnt = 2", xnode, xedge);
7978     free (xnode);
7979     free (xedge);
7980     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt1, NULL);
7981     sqlite3_free (sql);
7982     if (ret != SQLITE_OK)
7983       {
7984 	  char *msg = sqlite3_mprintf ("TopoGeo_%sEdgeHeal error: \"%s\"",
7985 				       mode_new ? "New" : "Mod",
7986 				       sqlite3_errmsg (topo->db_handle));
7987 	  gaiatopo_set_last_error_msg (accessor, msg);
7988 	  sqlite3_free (msg);
7989 	  goto error;
7990       }
7991 
7992 /* preparing the SQL query identifying a couple of Edges to be Healed */
7993     node = sqlite3_mprintf ("%s_node", topo->topology_name);
7994     xnode = gaiaDoubleQuotedSql (node);
7995     sqlite3_free (node);
7996     edge = sqlite3_mprintf ("%s_edge", topo->topology_name);
7997     xedge = gaiaDoubleQuotedSql (edge);
7998     sqlite3_free (edge);
7999     sql =
8000 	sqlite3_mprintf ("SELECT e.edge_id FROM \"%s\" AS n "
8001 			 "JOIN \"%s\" AS e ON (n.node_id = e.start_node OR n.node_id = e.end_node) "
8002 			 "WHERE n.node_id = ? AND e.start_node <> e.end_node",
8003 			 xnode, xedge);
8004     free (xnode);
8005     free (xedge);
8006     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt2, NULL);
8007     sqlite3_free (sql);
8008     if (ret != SQLITE_OK)
8009       {
8010 	  char *msg = sqlite3_mprintf ("TopoGeo_%sEdgeHeal error: \"%s\"",
8011 				       mode_new ? "New" : "Mod",
8012 				       sqlite3_errmsg (topo->db_handle));
8013 	  gaiatopo_set_last_error_msg (accessor, msg);
8014 	  sqlite3_free (msg);
8015 	  goto error;
8016       }
8017 
8018 /* preparing the SQL query Healing a couple of Edges */
8019     sql =
8020 	sqlite3_mprintf ("SELECT ST_%sEdgeHeal(%Q, ?, ?)",
8021 			 mode_new ? "New" : "Mod", topo->topology_name);
8022     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt3, NULL);
8023     sqlite3_free (sql);
8024     if (ret != SQLITE_OK)
8025       {
8026 	  char *msg = sqlite3_mprintf ("TopoGeo_%sEdgeHeal error: \"%s\"",
8027 				       mode_new ? "New" : "Mod",
8028 				       sqlite3_errmsg (topo->db_handle));
8029 	  gaiatopo_set_last_error_msg (accessor, msg);
8030 	  sqlite3_free (msg);
8031 	  goto error;
8032       }
8033 
8034     while (loop)
8035       {
8036 	  /* looping until all possible Edges have been healed */
8037 	  sqlite3_reset (stmt1);
8038 	  sqlite3_clear_bindings (stmt1);
8039 	  loop = 0;
8040 
8041 	  while (1)
8042 	    {
8043 		/* scrolling the result set rows */
8044 		sqlite3_int64 edge_1_id = -1;
8045 		sqlite3_int64 edge_2_id = -1;
8046 		ret = sqlite3_step (stmt1);
8047 		if (ret == SQLITE_DONE)
8048 		    break;	/* end of result set */
8049 		if (ret == SQLITE_ROW)
8050 		  {
8051 		      sqlite3_reset (stmt2);
8052 		      sqlite3_clear_bindings (stmt2);
8053 		      sqlite3_bind_int64 (stmt2, 1,
8054 					  sqlite3_column_int64 (stmt1, 0));
8055 
8056 		      loop_count = 0;
8057 		      while (1)
8058 			{
8059 			    /* scrolling the result set rows */
8060 			    ret = sqlite3_step (stmt2);
8061 			    if (ret == SQLITE_DONE)
8062 				break;	/* end of result set */
8063 			    if (ret == SQLITE_ROW)
8064 			      {
8065 				  loop_count++;
8066 				  if (loop_count == 1)
8067 				      edge_1_id =
8068 					  sqlite3_column_int64 (stmt2, 0);
8069 				  else if (loop_count == 2)
8070 				      edge_2_id =
8071 					  sqlite3_column_int64 (stmt2, 0);
8072 				  else
8073 				    {
8074 					char *msg =
8075 					    sqlite3_mprintf
8076 					    ("TopoGeo_%sEdgeHeal error: \"Unexpected loop_count > 2\"",
8077 					     mode_new ? "New" : "Mod");
8078 					gaiatopo_set_last_error_msg (accessor,
8079 								     msg);
8080 					sqlite3_free (msg);
8081 					goto error;
8082 				    }
8083 			      }
8084 			    else
8085 			      {
8086 				  char *msg =
8087 				      sqlite3_mprintf
8088 				      ("TopoGeo_%sEdgeHeal error: \"%s\"",
8089 				       mode_new ? "New" : "Mod",
8090 				       sqlite3_errmsg (topo->db_handle));
8091 				  gaiatopo_set_last_error_msg (accessor, msg);
8092 				  sqlite3_free (msg);
8093 				  goto error;
8094 			      }
8095 			}
8096 		      if (loop_count != 2 || edge_1_id < 0 || edge_2_id < 0)
8097 			{
8098 			    /* discarding invalid Edges */
8099 			    edge_1_id = -1;
8100 			    edge_2_id = -1;
8101 			    loop_count = 0;
8102 			    continue;
8103 			}
8104 		      if (edge_1_id == edge_2_id)
8105 			{
8106 			    /* can't Heal - dog chasing its tail */
8107 			    edge_1_id = -1;
8108 			    edge_2_id = -1;
8109 			    loop_count = 0;
8110 			    continue;
8111 			}
8112 
8113 		      /* healing a couple of Edges */
8114 		      sqlite3_reset (stmt3);
8115 		      sqlite3_clear_bindings (stmt3);
8116 		      sqlite3_bind_int64 (stmt3, 1, edge_1_id);
8117 		      sqlite3_bind_int64 (stmt3, 2, edge_2_id);
8118 		      ret = sqlite3_step (stmt3);
8119 		      if (ret == SQLITE_DONE || ret == SQLITE_ROW)
8120 			{
8121 			    loop = 1;
8122 			    break;
8123 			}
8124 		      else
8125 			{
8126 			    char *msg =
8127 				sqlite3_mprintf
8128 				("TopoGeo_%sEdgeHeal error: \"%s\"",
8129 				 mode_new ? "New" : "Mod",
8130 				 sqlite3_errmsg (topo->db_handle));
8131 			    gaiatopo_set_last_error_msg (accessor, msg);
8132 			    sqlite3_free (msg);
8133 			    goto error;
8134 			}
8135 		  }
8136 	    }
8137       }
8138 
8139     sqlite3_finalize (stmt1);
8140     sqlite3_finalize (stmt2);
8141     sqlite3_finalize (stmt3);
8142     return 1;
8143 
8144   error:
8145     if (stmt1 != NULL)
8146 	sqlite3_finalize (stmt1);
8147     if (stmt2 != NULL)
8148 	sqlite3_finalize (stmt2);
8149     if (stmt3 != NULL)
8150 	sqlite3_finalize (stmt3);
8151     return 0;
8152 }
8153 
8154 GAIATOPO_DECLARE int
gaiaTopoGeo_NewEdgeHeal(GaiaTopologyAccessorPtr ptr)8155 gaiaTopoGeo_NewEdgeHeal (GaiaTopologyAccessorPtr ptr)
8156 {
8157     return topoGeo_EdgeHeal_common (ptr, 1);
8158 }
8159 
8160 GAIATOPO_DECLARE int
gaiaTopoGeo_ModEdgeHeal(GaiaTopologyAccessorPtr ptr)8161 gaiaTopoGeo_ModEdgeHeal (GaiaTopologyAccessorPtr ptr)
8162 {
8163     return topoGeo_EdgeHeal_common (ptr, 0);
8164 }
8165 
8166 static int
do_split_edge(GaiaTopologyAccessorPtr accessor,sqlite3 * sqlite,sqlite3_stmt * stmt,sqlite3_int64 edge_id,gaiaGeomCollPtr geom,int line_max_points,double max_length,int * count)8167 do_split_edge (GaiaTopologyAccessorPtr accessor, sqlite3 * sqlite,
8168 	       sqlite3_stmt * stmt, sqlite3_int64 edge_id, gaiaGeomCollPtr geom,
8169 	       int line_max_points, double max_length, int *count)
8170 {
8171 /* attempting to split an Edge in two halves */
8172     int nlns = 0;
8173     char *msg;
8174     double x;
8175     double y;
8176     double z;
8177     int last;
8178     unsigned char *blob = NULL;
8179     int blob_size = 0;
8180     int ret;
8181     gaiaLinestringPtr ln;
8182     gaiaGeomCollPtr point = NULL;
8183     gaiaGeomCollPtr split =
8184 	gaiaTopoGeo_SubdivideLines (geom, line_max_points, max_length);
8185 
8186     ln = split->FirstLinestring;
8187     while (ln != NULL)
8188       {
8189 	  nlns++;
8190 	  ln = ln->Next;
8191       }
8192     if (nlns < 2)
8193 	return 1;		/* Edge not requiring to be split */
8194 
8195     ln = split->FirstLinestring;
8196     last = ln->Points - 1;
8197     if (split->DimensionModel == GAIA_XY_Z)
8198       {
8199 	  /* 3D topology */
8200 	  point = gaiaAllocGeomCollXYZ ();
8201 	  point->Srid = geom->Srid;
8202 	  gaiaGetPointXYZ (ln->Coords, last, &x, &y, &z);
8203 	  gaiaAddPointToGeomCollXYZ (point, x, y, z);
8204       }
8205     else
8206       {
8207 	  /* 2D topology */
8208 	  point = gaiaAllocGeomColl ();
8209 	  point->Srid = geom->Srid;
8210 	  gaiaGetPoint (ln->Coords, last, &x, &y);
8211 	  gaiaAddPointToGeomColl (point, x, y);
8212       }
8213 
8214 /* splitting the Edge */
8215     sqlite3_reset (stmt);
8216     sqlite3_clear_bindings (stmt);
8217     sqlite3_bind_int64 (stmt, 1, edge_id);
8218     gaiaToSpatiaLiteBlobWkb (point, &blob, &blob_size);
8219     sqlite3_bind_blob (stmt, 2, blob, blob_size, free);
8220     ret = sqlite3_step (stmt);
8221     if (ret == SQLITE_DONE || ret == SQLITE_ROW)
8222       {
8223 	  *count += 1;
8224 	  return 1;
8225       }
8226 
8227 /* some unexpected error occurred */
8228     msg = sqlite3_mprintf ("Edge Split error: \"%s\"", sqlite3_errmsg (sqlite));
8229     gaiatopo_set_last_error_msg (accessor, msg);
8230     sqlite3_free (msg);
8231     return 0;
8232 }
8233 
8234 static int
topoGeo_EdgeSplit_common(GaiaTopologyAccessorPtr accessor,int mode_new,int line_max_points,double max_length)8235 topoGeo_EdgeSplit_common (GaiaTopologyAccessorPtr accessor, int mode_new,
8236 			  int line_max_points, double max_length)
8237 {
8238 /* common implementation of GeoTable EdgeSplit */
8239     struct gaia_topology *topo = (struct gaia_topology *) accessor;
8240     int ret;
8241     char *sql;
8242     char *edge;
8243     char *xedge;
8244     sqlite3_stmt *stmt1 = NULL;
8245     sqlite3_stmt *stmt2 = NULL;
8246     if (topo == NULL)
8247 	return 0;
8248 
8249     ret = test_inconsistent_topology (accessor);
8250     if (ret != 0)
8251 	return 0;
8252 
8253 /* preparing the SQL query identifying all Edges */
8254     edge = sqlite3_mprintf ("%s_edge", topo->topology_name);
8255     xedge = gaiaDoubleQuotedSql (edge);
8256     sqlite3_free (edge);
8257     sql =
8258 	sqlite3_mprintf ("SELECT edge_id, geom FROM \"%s\" ORDER BY edge_id",
8259 			 xedge);
8260     free (xedge);
8261     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt1, NULL);
8262     sqlite3_free (sql);
8263     if (ret != SQLITE_OK)
8264       {
8265 	  char *msg = sqlite3_mprintf ("TopoGeo_%sSplit error: \"%s\"",
8266 				       mode_new ? "NewEdges" : "ModEdge",
8267 				       sqlite3_errmsg (topo->db_handle));
8268 	  gaiatopo_set_last_error_msg (accessor, msg);
8269 	  sqlite3_free (msg);
8270 	  goto error;
8271       }
8272 
8273 /* preparing the SQL query splitting an Edge in two halves */
8274     sql =
8275 	sqlite3_mprintf ("SELECT ST_%sSplit(%Q, ?, ?)",
8276 			 mode_new ? "NewEdges" : "ModEdge",
8277 			 topo->topology_name);
8278     ret = sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt2, NULL);
8279     sqlite3_free (sql);
8280     if (ret != SQLITE_OK)
8281       {
8282 	  char *msg = sqlite3_mprintf ("TopoGeo_%sSplit error: \"%s\"",
8283 				       mode_new ? "NewEdges" : "ModEdge",
8284 				       sqlite3_errmsg (topo->db_handle));
8285 	  gaiatopo_set_last_error_msg (accessor, msg);
8286 	  sqlite3_free (msg);
8287 	  goto error;
8288       }
8289 
8290     while (1)
8291       {
8292 	  /* repeatedly looping on all Edges */
8293 	  int count = 0;
8294 	  sqlite3_reset (stmt1);
8295 	  sqlite3_clear_bindings (stmt1);
8296 
8297 	  while (1)
8298 	    {
8299 		/* scrolling the result set rows */
8300 		ret = sqlite3_step (stmt1);
8301 		if (ret == SQLITE_DONE)
8302 		    break;	/* end of result set */
8303 		if (ret == SQLITE_ROW)
8304 		  {
8305 		      sqlite3_int64 edge_id = sqlite3_column_int64 (stmt1, 0);
8306 		      if (sqlite3_column_type (stmt1, 1) == SQLITE_BLOB)
8307 			{
8308 			    const unsigned char *blob =
8309 				sqlite3_column_blob (stmt1, 1);
8310 			    int blob_sz = sqlite3_column_bytes (stmt1, 1);
8311 			    gaiaGeomCollPtr geom =
8312 				gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
8313 			    if (geom != NULL)
8314 			      {
8315 				  if (!do_split_edge
8316 				      (accessor, topo->db_handle, stmt2,
8317 				       edge_id, geom, line_max_points,
8318 				       max_length, &count))
8319 				    {
8320 					gaiaFreeGeomColl (geom);
8321 					goto error;
8322 				    }
8323 			      }
8324 			    gaiaFreeGeomColl (geom);
8325 			}
8326 		  }
8327 		else
8328 		  {
8329 		      char *msg =
8330 			  sqlite3_mprintf ("TopoGeo_%sSplit error: \"%s\"",
8331 					   mode_new ? "NewEdges" : "ModEdge",
8332 					   sqlite3_errmsg (topo->db_handle));
8333 		      gaiatopo_set_last_error_msg (accessor, msg);
8334 		      sqlite3_free (msg);
8335 		      goto error;
8336 		  }
8337 	    }
8338 	  if (count == 0)
8339 	      break;
8340       }
8341 
8342     sqlite3_finalize (stmt1);
8343     sqlite3_finalize (stmt2);
8344     return 1;
8345 
8346   error:
8347     if (stmt1 != NULL)
8348 	sqlite3_finalize (stmt1);
8349     if (stmt2 != NULL)
8350 	sqlite3_finalize (stmt2);
8351     return 0;
8352 }
8353 
8354 GAIATOPO_DECLARE int
gaiaTopoGeo_NewEdgesSplit(GaiaTopologyAccessorPtr ptr,int line_max_points,double max_length)8355 gaiaTopoGeo_NewEdgesSplit (GaiaTopologyAccessorPtr ptr, int line_max_points,
8356 			   double max_length)
8357 {
8358     return topoGeo_EdgeSplit_common (ptr, 1, line_max_points, max_length);
8359 }
8360 
8361 GAIATOPO_DECLARE int
gaiaTopoGeo_ModEdgeSplit(GaiaTopologyAccessorPtr ptr,int line_max_points,double max_length)8362 gaiaTopoGeo_ModEdgeSplit (GaiaTopologyAccessorPtr ptr, int line_max_points,
8363 			  double max_length)
8364 {
8365     return topoGeo_EdgeSplit_common (ptr, 0, line_max_points, max_length);
8366 }
8367 
8368 static gaiaGeomCollPtr
do_interpolate_middlepoint(gaiaGeomCollPtr geom)8369 do_interpolate_middlepoint (gaiaGeomCollPtr geom)
8370 {
8371 /* building a three-point segment */
8372     gaiaGeomCollPtr newg;
8373     gaiaLinestringPtr old_ln;
8374     gaiaLinestringPtr new_ln;
8375     double x0;
8376     double y0;
8377     double z0;
8378     double x1;
8379     double y1;
8380     double z1;
8381     double mx;
8382     double my;
8383     double mz;
8384 
8385     if (geom == NULL)
8386 	return NULL;
8387     if (geom->FirstPoint != NULL || geom->FirstPolygon != NULL)
8388 	return NULL;
8389     if (geom->FirstLinestring != geom->LastLinestring)
8390 	return NULL;
8391     old_ln = geom->FirstLinestring;
8392     if (old_ln == NULL)
8393 	return NULL;
8394     if (old_ln->Points != 2)
8395 	return NULL;
8396 
8397     if (geom->DimensionModel == GAIA_XY_Z)
8398       {
8399 	  gaiaGetPointXYZ (old_ln->Coords, 0, &x0, &y0, &z0);
8400 	  gaiaGetPointXYZ (old_ln->Coords, 1, &x1, &y1, &z1);
8401 	  newg = gaiaAllocGeomCollXYZ ();
8402       }
8403     else
8404       {
8405 	  gaiaGetPoint (old_ln->Coords, 0, &x0, &y0);
8406 	  gaiaGetPoint (old_ln->Coords, 1, &x1, &y1);
8407 	  newg = gaiaAllocGeomColl ();
8408       }
8409     newg->Srid = geom->Srid;
8410 
8411     if (x0 > x1)
8412 	mx = x1 + ((x0 - x1) / 2.0);
8413     else
8414 	mx = x0 + ((x1 - x0) / 2.0);
8415     if (y0 > y1)
8416 	my = y1 + ((y0 - y1) / 2.0);
8417     else
8418 	my = y0 + ((y1 - y0) / 2.0);
8419     if (geom->DimensionModel == GAIA_XY_Z)
8420       {
8421 	  if (z0 > z1)
8422 	      mz = z1 + ((z0 - z1) / 2.0);
8423 	  else
8424 	      mz = z0 + ((z1 - z0) / 2.0);
8425       }
8426 
8427     new_ln = gaiaAddLinestringToGeomColl (newg, 3);
8428     if (newg->DimensionModel == GAIA_XY_Z)
8429       {
8430 	  gaiaSetPointXYZ (new_ln->Coords, 0, x0, y0, z0);
8431 	  gaiaSetPointXYZ (new_ln->Coords, 1, mx, my, mz);
8432 	  gaiaSetPointXYZ (new_ln->Coords, 2, x1, y1, z1);
8433       }
8434     else
8435       {
8436 	  gaiaSetPoint (new_ln->Coords, 0, x0, y0);
8437 	  gaiaSetPoint (new_ln->Coords, 1, mx, my);
8438 	  gaiaSetPoint (new_ln->Coords, 2, x1, y1);
8439       }
8440 
8441     return newg;
8442 }
8443 
8444 GAIATOPO_DECLARE int
gaiaTopoGeo_DisambiguateSegmentEdges(GaiaTopologyAccessorPtr accessor)8445 gaiaTopoGeo_DisambiguateSegmentEdges (GaiaTopologyAccessorPtr accessor)
8446 {
8447 /*
8448 / Ensures that all Edges on a Topology-Geometry will have not less
8449 / than three vertices; for all Edges found beign simple two-points
8450 / segments a third intermediate point will be interpolated.
8451 */
8452     struct gaia_topology *topo = (struct gaia_topology *) accessor;
8453     int ret;
8454     char *sql;
8455     char *edge;
8456     char *xedge;
8457     sqlite3_stmt *stmt_in = NULL;
8458     sqlite3_stmt *stmt_out = NULL;
8459     int count = 0;
8460     if (topo == NULL)
8461 	return -1;
8462 
8463 /* preparing the SQL query identifying all two-points Edges */
8464     edge = sqlite3_mprintf ("%s_edge", topo->topology_name);
8465     xedge = gaiaDoubleQuotedSql (edge);
8466     sqlite3_free (edge);
8467     sql =
8468 	sqlite3_mprintf
8469 	("SELECT edge_id, geom FROM \"%s\" WHERE ST_NumPoints(geom) = 2 "
8470 	 "ORDER BY edge_id", xedge);
8471     free (xedge);
8472     ret =
8473 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_in, NULL);
8474     sqlite3_free (sql);
8475     if (ret != SQLITE_OK)
8476       {
8477 	  char *msg =
8478 	      sqlite3_mprintf ("TopoGeo_DisambiguateSegmentEdges error: \"%s\"",
8479 			       sqlite3_errmsg (topo->db_handle));
8480 	  gaiatopo_set_last_error_msg (accessor, msg);
8481 	  sqlite3_free (msg);
8482 	  goto error;
8483       }
8484 
8485 /* preparing the UPDATE SQL query */
8486     sql =
8487 	sqlite3_mprintf ("SELECT ST_ChangeEdgeGeom(%Q, ?, ?)",
8488 			 topo->topology_name);
8489     ret =
8490 	sqlite3_prepare_v2 (topo->db_handle, sql, strlen (sql), &stmt_out,
8491 			    NULL);
8492     sqlite3_free (sql);
8493     if (ret != SQLITE_OK)
8494       {
8495 	  char *msg =
8496 	      sqlite3_mprintf ("TopoGeo_DisambiguateSegmentEdges error: \"%s\"",
8497 			       sqlite3_errmsg (topo->db_handle));
8498 	  gaiatopo_set_last_error_msg (accessor, msg);
8499 	  sqlite3_free (msg);
8500 	  goto error;
8501       }
8502 
8503     while (1)
8504       {
8505 	  /* scrolling the result set rows */
8506 	  ret = sqlite3_step (stmt_in);
8507 	  if (ret == SQLITE_DONE)
8508 	      break;		/* end of result set */
8509 	  if (ret == SQLITE_ROW)
8510 	    {
8511 		sqlite3_int64 edge_id = sqlite3_column_int64 (stmt_in, 0);
8512 		if (sqlite3_column_type (stmt_in, 1) == SQLITE_BLOB)
8513 		  {
8514 		      const unsigned char *blob =
8515 			  sqlite3_column_blob (stmt_in, 1);
8516 		      int blob_sz = sqlite3_column_bytes (stmt_in, 1);
8517 		      gaiaGeomCollPtr geom =
8518 			  gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
8519 		      if (geom != NULL)
8520 			{
8521 			    gaiaGeomCollPtr newg =
8522 				do_interpolate_middlepoint (geom);
8523 			    gaiaFreeGeomColl (geom);
8524 			    if (newg != NULL)
8525 			      {
8526 				  unsigned char *outblob = NULL;
8527 				  int outblob_size = 0;
8528 				  sqlite3_reset (stmt_out);
8529 				  sqlite3_clear_bindings (stmt_out);
8530 				  sqlite3_bind_int64 (stmt_out, 1, edge_id);
8531 				  gaiaToSpatiaLiteBlobWkb (newg, &outblob,
8532 							   &outblob_size);
8533 				  gaiaFreeGeomColl (newg);
8534 				  if (blob == NULL)
8535 				      continue;
8536 				  else
8537 				      sqlite3_bind_blob (stmt_out, 2, outblob,
8538 							 outblob_size, free);
8539 				  /* updating the Edges table */
8540 				  ret = sqlite3_step (stmt_out);
8541 				  if (ret == SQLITE_DONE || ret == SQLITE_ROW)
8542 				      count++;
8543 				  else
8544 				    {
8545 					char *msg =
8546 					    sqlite3_mprintf
8547 					    ("TopoGeo_DisambiguateSegmentEdges() error: \"%s\"",
8548 					     sqlite3_errmsg (topo->db_handle));
8549 					gaiatopo_set_last_error_msg ((GaiaTopologyAccessorPtr) topo, msg);
8550 					sqlite3_free (msg);
8551 					goto error;
8552 				    }
8553 			      }
8554 			}
8555 		  }
8556 	    }
8557 	  else
8558 	    {
8559 		char *msg =
8560 		    sqlite3_mprintf
8561 		    ("TopoGeo_DisambiguateSegmentEdges error: \"%s\"",
8562 		     sqlite3_errmsg (topo->db_handle));
8563 		gaiatopo_set_last_error_msg (accessor, msg);
8564 		sqlite3_free (msg);
8565 		goto error;
8566 	    }
8567       }
8568 
8569     sqlite3_finalize (stmt_in);
8570     sqlite3_finalize (stmt_out);
8571     return count;
8572 
8573   error:
8574     if (stmt_out != NULL)
8575 	sqlite3_finalize (stmt_in);
8576     if (stmt_out != NULL)
8577 	sqlite3_finalize (stmt_out);
8578     return -1;
8579 }
8580 
8581 #endif /* end ENABLE_RTTOPO conditionals */
8582