1 /*
2
3 test_map_rgb.c -- RasterLite-2 Test Case
4
5 Author: Sandro Furieri <a.furieri@lqt.it>
6
7 ------------------------------------------------------------------------------
8
9 Version: MPL 1.1/GPL 2.0/LGPL 2.1
10
11 The contents of this file are subject to the Mozilla Public License Version
12 1.1 (the "License"); you may not use this file except in compliance with
13 the License. You may obtain a copy of the License at
14 http://www.mozilla.org/MPL/
15
16 Software distributed under the License is distributed on an "AS IS" basis,
17 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
18 for the specific language governing rights and limitations under the
19 License.
20
21 The Original Code is the SpatiaLite library
22
23 The Initial Developer of the Original Code is Alessandro Furieri
24
25 Portions created by the Initial Developer are Copyright (C) 2013
26 the Initial Developer. All Rights Reserved.
27
28 Contributor(s):
29
30 Alternatively, the contents of this file may be used under the terms of
31 either the GNU General Public License Version 2 or later (the "GPL"), or
32 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 in which case the provisions of the GPL or the LGPL are applicable instead
34 of those above. If you wish to allow use of your version of this file only
35 under the terms of either the GPL or the LGPL, and not to allow others to
36 use your version of this file under the terms of the MPL, indicate your
37 decision by deleting the provisions above and replace them with the notice
38 and other provisions required by the GPL or the LGPL. If you do not delete
39 the provisions above, a recipient may use your version of this file under
40 the terms of any one of the MPL, the GPL or the LGPL.
41
42 */
43 #define _GNU_SOURCE
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <stdio.h>
47 #include <string.h>
48
49 #include "sqlite3.h"
50 #include "spatialite.h"
51 #include "spatialite/gaiaaux.h"
52
53 #include "rasterlite2/rasterlite2.h"
54
55 #define TILE_256 256
56 #define TILE_512 512
57 #define TILE_1024 1024
58
59 static int
execute_check(sqlite3 * sqlite,const char * sql)60 execute_check (sqlite3 * sqlite, const char *sql)
61 {
62 /* executing an SQL statement returning True/False */
63 sqlite3_stmt *stmt;
64 int ret;
65 int retcode = 0;
66
67 ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
68 if (ret != SQLITE_OK)
69 return SQLITE_ERROR;
70 ret = sqlite3_step (stmt);
71 if (ret == SQLITE_DONE || ret == SQLITE_ROW)
72 {
73 if (sqlite3_column_int (stmt, 0) == 1)
74 retcode = 1;
75 }
76 sqlite3_finalize (stmt);
77 if (retcode == 1)
78 return SQLITE_OK;
79 return SQLITE_ERROR;
80 }
81
82 static int
get_max_tile_id(sqlite3 * sqlite,const char * coverage)83 get_max_tile_id (sqlite3 * sqlite, const char *coverage)
84 {
85 /* retriving the Max tile_id for a given Coverage */
86 char *sql;
87 char *table;
88 char *xtable;
89 sqlite3_stmt *stmt;
90 int ret;
91 int max = 0;
92
93 table = sqlite3_mprintf ("%s_tile_data", coverage);
94 xtable = gaiaDoubleQuotedSql (table);
95 sqlite3_free (table);
96 sql = sqlite3_mprintf ("SELECT Max(tile_id) FROM \"%s\"", xtable);
97 free (xtable);
98 ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
99 sqlite3_free (sql);
100 if (ret != SQLITE_OK)
101 return 0;
102 while (1)
103 {
104 /* scrolling the result set rows */
105 ret = sqlite3_step (stmt);
106 if (ret == SQLITE_DONE)
107 break; /* end of result set */
108 if (ret == SQLITE_ROW)
109 max = sqlite3_column_int (stmt, 0);
110 }
111 sqlite3_finalize (stmt);
112 return max;
113 }
114
115 static int
do_export_tile_image(sqlite3 * sqlite,const char * coverage,int tile_id)116 do_export_tile_image (sqlite3 * sqlite, const char *coverage, int tile_id)
117 {
118 /* attempting to export a visible Tile */
119 char *sql;
120 char *path;
121 int ret;
122 int transparent = 1;
123
124 if (tile_id <= 1)
125 transparent = 0;
126 if (tile_id < 0)
127 tile_id = get_max_tile_id (sqlite, coverage);
128 path = sqlite3_mprintf ("./%s_tile_%d.png", coverage, tile_id);
129 sql =
130 sqlite3_mprintf
131 ("SELECT BlobToFile(RL2_GetTileImage(%Q, %d, '#e0ffe0', %d), %Q)",
132 coverage, tile_id, transparent, path);
133 ret = execute_check (sqlite, sql);
134 sqlite3_free (sql);
135 unlink (path);
136 sqlite3_free (path);
137 if (ret != SQLITE_OK)
138 {
139 fprintf (stderr,
140 "ERROR: Unable to export an Image from \"%s\" tile_id=%d\n",
141 coverage, tile_id);
142 return 0;
143 }
144 return 1;
145 }
146
147 static int
get_base_resolution(sqlite3 * sqlite,const char * coverage,double * x_res,double * y_res)148 get_base_resolution (sqlite3 * sqlite, const char *coverage, double *x_res,
149 double *y_res)
150 {
151 /* attempting to retrieve the Coverage's base resolution */
152 char *sql;
153 sqlite3_stmt *stmt;
154 int ret;
155 int ok = 0;
156
157 sql = sqlite3_mprintf ("SELECT horz_resolution, vert_resolution "
158 "FROM raster_coverages WHERE coverage_name = %Q",
159 coverage);
160 ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
161 sqlite3_free (sql);
162 if (ret != SQLITE_OK)
163 return 0;
164
165 while (1)
166 {
167 /* scrolling the result set rows */
168 ret = sqlite3_step (stmt);
169 if (ret == SQLITE_DONE)
170 break; /* end of result set */
171 if (ret == SQLITE_ROW)
172 {
173 *x_res = sqlite3_column_double (stmt, 0);
174 *y_res = sqlite3_column_double (stmt, 1);
175 ok = 1;
176 }
177 }
178 sqlite3_finalize (stmt);
179 return ok;
180 }
181
182 static int
do_export_geotiff(sqlite3 * sqlite,const char * coverage,const char * type,gaiaGeomCollPtr geom,int scale)183 do_export_geotiff (sqlite3 * sqlite, const char *coverage, const char *type,
184 gaiaGeomCollPtr geom, int scale)
185 {
186 /* exporting a GeoTiff */
187 char *sql;
188 char *path;
189 sqlite3_stmt *stmt;
190 int ret;
191 double x_res;
192 double y_res;
193 double xx_res;
194 double yy_res;
195 unsigned char *blob;
196 int blob_size;
197 int retcode = 0;
198
199 path = sqlite3_mprintf ("./%s_%s_gt_%d.tif", coverage, type, scale);
200
201 if (!get_base_resolution (sqlite, coverage, &x_res, &y_res))
202 return 0;
203 xx_res = x_res * (double) scale;
204 yy_res = y_res * (double) scale;
205
206 sql = "SELECT RL2_WriteGeoTiff(?, ?, ?, ?, ?, ?, ?, ?, ?)";
207 ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
208 if (ret != SQLITE_OK)
209 return 0;
210 sqlite3_reset (stmt);
211 sqlite3_clear_bindings (stmt);
212 sqlite3_bind_text (stmt, 1, coverage, strlen (coverage), SQLITE_STATIC);
213 sqlite3_bind_text (stmt, 2, path, strlen (path), SQLITE_STATIC);
214 sqlite3_bind_int (stmt, 3, 1024);
215 sqlite3_bind_int (stmt, 4, 1024);
216 gaiaToSpatiaLiteBlobWkb (geom, &blob, &blob_size);
217 sqlite3_bind_blob (stmt, 5, blob, blob_size, free);
218 sqlite3_bind_double (stmt, 6, xx_res);
219 sqlite3_bind_double (stmt, 7, yy_res);
220 sqlite3_bind_int (stmt, 8, 0);
221 sqlite3_bind_text (stmt, 9, "JPEG", 4, SQLITE_TRANSIENT);
222 ret = sqlite3_step (stmt);
223 if (ret == SQLITE_DONE || ret == SQLITE_ROW)
224 {
225 if (sqlite3_column_int (stmt, 0) == 1)
226 retcode = 1;
227 }
228 sqlite3_finalize (stmt);
229 unlink (path);
230 if (!retcode)
231 fprintf (stderr, "ERROR: unable to export \"%s\"\n", path);
232 sqlite3_free (path);
233 return retcode;
234 }
235
236 static int
do_export_tiff(sqlite3 * sqlite,const char * coverage,gaiaGeomCollPtr geom,int scale)237 do_export_tiff (sqlite3 * sqlite, const char *coverage, gaiaGeomCollPtr geom,
238 int scale)
239 {
240 /* exporting a Tiff + Worldfile */
241 char *sql;
242 char *path;
243 sqlite3_stmt *stmt;
244 int ret;
245 double x_res;
246 double y_res;
247 double xx_res;
248 double yy_res;
249 unsigned char *blob;
250 int blob_size;
251 int retcode = 0;
252
253 path = sqlite3_mprintf ("./%s_tfw_%d.tif", coverage, scale);
254
255 if (!get_base_resolution (sqlite, coverage, &x_res, &y_res))
256 return 0;
257 xx_res = x_res * (double) scale;
258 yy_res = y_res * (double) scale;
259
260 sql = "SELECT RL2_WriteTiffTfw(?, ?, ?, ?, ?, ?, ?, ?)";
261 ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
262 if (ret != SQLITE_OK)
263 return 0;
264 sqlite3_reset (stmt);
265 sqlite3_clear_bindings (stmt);
266 sqlite3_bind_text (stmt, 1, coverage, strlen (coverage), SQLITE_STATIC);
267 sqlite3_bind_text (stmt, 2, path, strlen (path), SQLITE_STATIC);
268 sqlite3_bind_int (stmt, 3, 1024);
269 sqlite3_bind_int (stmt, 4, 1024);
270 gaiaToSpatiaLiteBlobWkb (geom, &blob, &blob_size);
271 sqlite3_bind_blob (stmt, 5, blob, blob_size, free);
272 sqlite3_bind_double (stmt, 6, xx_res);
273 sqlite3_bind_double (stmt, 7, yy_res);
274 sqlite3_bind_text (stmt, 8, "DEFLATE", 7, SQLITE_TRANSIENT);
275 ret = sqlite3_step (stmt);
276 if (ret == SQLITE_DONE || ret == SQLITE_ROW)
277 {
278 if (sqlite3_column_int (stmt, 0) == 1)
279 retcode = 1;
280 }
281 sqlite3_finalize (stmt);
282 unlink (path);
283 if (!retcode)
284 fprintf (stderr, "ERROR: unable to export \"%s\"\n", path);
285 sqlite3_free (path);
286 path = sqlite3_mprintf ("./%s_tfw_%d.tfw", coverage, scale);
287 unlink (path);
288 sqlite3_free (path);
289 return retcode;
290 }
291
292 static gaiaGeomCollPtr
get_center_point(sqlite3 * sqlite,const char * coverage)293 get_center_point (sqlite3 * sqlite, const char *coverage)
294 {
295 /* attempting to retrieve the Coverage's Center Point */
296 char *sql;
297 sqlite3_stmt *stmt;
298 gaiaGeomCollPtr geom = NULL;
299 int ret;
300
301 sql = sqlite3_mprintf ("SELECT MakePoint("
302 "extent_minx + ((extent_maxx - extent_minx) / 2.0), "
303 "extent_miny + ((extent_maxy - extent_miny) / 2.0)) "
304 "FROM raster_coverages WHERE coverage_name = %Q",
305 coverage);
306 ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
307 sqlite3_free (sql);
308 if (ret != SQLITE_OK)
309 return NULL;
310
311 while (1)
312 {
313 /* scrolling the result set rows */
314 ret = sqlite3_step (stmt);
315 if (ret == SQLITE_DONE)
316 break; /* end of result set */
317 if (ret == SQLITE_ROW)
318 {
319 const unsigned char *blob = sqlite3_column_blob (stmt, 0);
320 int blob_sz = sqlite3_column_bytes (stmt, 0);
321 geom = gaiaFromSpatiaLiteBlobWkb (blob, blob_sz);
322 }
323 }
324 sqlite3_finalize (stmt);
325 return geom;
326 }
327
328 static int
do_export_image(sqlite3 * sqlite,const char * coverage,gaiaGeomCollPtr geom,double radius,const char * suffix)329 do_export_image (sqlite3 * sqlite, const char *coverage, gaiaGeomCollPtr geom,
330 double radius, const char *suffix)
331 {
332 /* exporting a PNG/JPEG/TIFF/PDF image */
333 char *sql;
334 char *path;
335 sqlite3_stmt *stmt;
336 int ret;
337 unsigned char *blob;
338 int blob_size;
339 int retcode = 0;
340 const char *mime_type = "text/plain";
341
342 path = sqlite3_mprintf ("./%s_%1.0f%s", coverage, radius, suffix);
343
344 sql =
345 "SELECT RL2_GetMapImage(?, ST_Buffer(?, ?), 512, 512, 'default', ?, '#ffffff', 1, 80)";
346 ret = sqlite3_prepare_v2 (sqlite, sql, strlen (sql), &stmt, NULL);
347 if (ret != SQLITE_OK)
348 return 0;
349 sqlite3_reset (stmt);
350 sqlite3_clear_bindings (stmt);
351 sqlite3_bind_text (stmt, 1, coverage, strlen (coverage), SQLITE_STATIC);
352 gaiaToSpatiaLiteBlobWkb (geom, &blob, &blob_size);
353 sqlite3_bind_blob (stmt, 2, blob, blob_size, free);
354 sqlite3_bind_double (stmt, 3, radius);
355 if (strcmp (suffix, ".png") == 0)
356 mime_type = "image/png";
357 if (strcmp (suffix, ".jpg") == 0)
358 mime_type = "image/jpeg";
359 if (strcmp (suffix, ".tif") == 0)
360 mime_type = "image/tiff";
361 if (strcmp (suffix, ".pdf") == 0)
362 mime_type = "application/x-pdf";
363 sqlite3_bind_text (stmt, 4, mime_type, strlen (mime_type),
364 SQLITE_TRANSIENT);
365 ret = sqlite3_step (stmt);
366 if (ret == SQLITE_DONE || ret == SQLITE_ROW)
367 {
368 if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB)
369 {
370 FILE *out;
371 blob = (unsigned char *) sqlite3_column_blob (stmt, 0);
372 blob_size = sqlite3_column_bytes (stmt, 0);
373 out = fopen (path, "wb");
374 if (out != NULL)
375 {
376 /* writing the output image */
377 if ((int) fwrite (blob, 1, blob_size, out) == blob_size)
378 retcode = 1;
379 fclose (out);
380 }
381 }
382 }
383 sqlite3_finalize (stmt);
384 if (!retcode)
385 fprintf (stderr, "ERROR: unable to GetMap \"%s\"\n", path);
386 unlink (path);
387 sqlite3_free (path);
388 return retcode;
389 }
390
391 static int
test_coverage(sqlite3 * sqlite,unsigned char pixel,unsigned char compression,int tile_sz,int * retcode)392 test_coverage (sqlite3 * sqlite, unsigned char pixel,
393 unsigned char compression, int tile_sz, int *retcode)
394 {
395 /* testing some DBMS Coverage */
396 int ret;
397 char *err_msg = NULL;
398 const char *coverage;
399 const char *sample_name;
400 const char *pixel_name;
401 unsigned char num_bands;
402 const char *compression_name;
403 int qlty;
404 char *sql;
405 int tile_size;
406 gaiaGeomCollPtr geom;
407
408 /* setting the coverage name */
409 switch (pixel)
410 {
411 case RL2_PIXEL_RGB:
412 switch (compression)
413 {
414 case RL2_COMPRESSION_NONE:
415 switch (tile_sz)
416 {
417 case TILE_256:
418 coverage = "rgb_none_256";
419 break;
420 case TILE_512:
421 coverage = "rgb_none_512";
422 break;
423 case TILE_1024:
424 coverage = "rgb_none_1024";
425 break;
426 };
427 break;
428 case RL2_COMPRESSION_PNG:
429 switch (tile_sz)
430 {
431 case TILE_256:
432 coverage = "rgb_png_256";
433 break;
434 case TILE_512:
435 coverage = "rgb_png_512";
436 break;
437 case TILE_1024:
438 coverage = "rgb_png_1024";
439 break;
440 };
441 break;
442 case RL2_COMPRESSION_JPEG:
443 switch (tile_sz)
444 {
445 case TILE_256:
446 coverage = "rgb_jpeg_256";
447 break;
448 case TILE_512:
449 coverage = "rgb_jpeg_512";
450 break;
451 case TILE_1024:
452 coverage = "rgb_jpeg_1024";
453 break;
454 };
455 break;
456 case RL2_COMPRESSION_LOSSY_WEBP:
457 switch (tile_sz)
458 {
459 case TILE_256:
460 coverage = "rgb_webp_256";
461 break;
462 case TILE_512:
463 coverage = "rgb_webp_512";
464 break;
465 case TILE_1024:
466 coverage = "rgb_webp_1024";
467 break;
468 };
469 break;
470 case RL2_COMPRESSION_LOSSLESS_WEBP:
471 switch (tile_sz)
472 {
473 case TILE_256:
474 coverage = "rgb_llwebp_256";
475 break;
476 case TILE_512:
477 coverage = "rgb_llwebp_512";
478 break;
479 case TILE_1024:
480 coverage = "rgb_llwebp_1024";
481 break;
482 };
483 break;
484 };
485 break;
486 case RL2_PIXEL_GRAYSCALE:
487 switch (compression)
488 {
489 case RL2_COMPRESSION_NONE:
490 switch (tile_sz)
491 {
492 case TILE_256:
493 coverage = "gray_none_256";
494 break;
495 case TILE_512:
496 coverage = "gray_none_512";
497 break;
498 case TILE_1024:
499 coverage = "gray_none_1024";
500 break;
501 };
502 break;
503 case RL2_COMPRESSION_PNG:
504 switch (tile_sz)
505 {
506 case TILE_256:
507 coverage = "gray_png_256";
508 break;
509 case TILE_512:
510 coverage = "gray_png_512";
511 break;
512 case TILE_1024:
513 coverage = "gray_png_1024";
514 break;
515 };
516 break;
517 case RL2_COMPRESSION_JPEG:
518 switch (tile_sz)
519 {
520 case TILE_256:
521 coverage = "gray_jpeg_256";
522 break;
523 case TILE_512:
524 coverage = "gray_jpeg_512";
525 break;
526 case TILE_1024:
527 coverage = "gray_jpeg_1024";
528 break;
529 };
530 break;
531 case RL2_COMPRESSION_LOSSY_WEBP:
532 switch (tile_sz)
533 {
534 case TILE_256:
535 coverage = "gray_webp_256";
536 break;
537 case TILE_512:
538 coverage = "gray_webp_512";
539 break;
540 case TILE_1024:
541 coverage = "gray_webp_1024";
542 break;
543 };
544 break;
545 case RL2_COMPRESSION_LOSSLESS_WEBP:
546 switch (tile_sz)
547 {
548 case TILE_256:
549 coverage = "gray_llwebp_256";
550 break;
551 case TILE_512:
552 coverage = "gray_llwebp_512";
553 break;
554 case TILE_1024:
555 coverage = "gray_llwebp_1024";
556 break;
557 };
558 break;
559 };
560 break;
561 };
562
563 /* preparing misc Coverage's parameters */
564 sample_name = "UINT8";
565 switch (pixel)
566 {
567 case RL2_PIXEL_RGB:
568 num_bands = 3;
569 pixel_name = "RGB";
570 break;
571 case RL2_PIXEL_GRAYSCALE:
572 num_bands = 1;
573 pixel_name = "GRAYSCALE";
574 break;
575 };
576 switch (compression)
577 {
578 case RL2_COMPRESSION_NONE:
579 compression_name = "NONE";
580 qlty = 100;
581 break;
582 case RL2_COMPRESSION_PNG:
583 compression_name = "PNG";
584 qlty = 100;
585 break;
586 case RL2_COMPRESSION_JPEG:
587 compression_name = "JPEG";
588 qlty = 80;
589 break;
590 case RL2_COMPRESSION_LOSSY_WEBP:
591 compression_name = "WEBP";
592 qlty = 80;
593 break;
594 case RL2_COMPRESSION_LOSSLESS_WEBP:
595 compression_name = "LL_WEBP";
596 qlty = 100;
597 break;
598 };
599 switch (tile_sz)
600 {
601 case TILE_256:
602 tile_size = 256;
603 break;
604 case TILE_512:
605 tile_size = 512;
606 break;
607 case TILE_1024:
608 tile_size = 1024;
609 break;
610 };
611
612 /* creating the DBMS Coverage */
613 sql = sqlite3_mprintf ("SELECT RL2_CreateCoverage("
614 "%Q, %Q, %Q, %d, %Q, %d, %d, %d, %d, %1.16f, %1.16f)",
615 coverage, sample_name, pixel_name, num_bands,
616 compression_name, qlty, tile_size, tile_size, 26914,
617 0.152400030480006134, 0.152400030480006134);
618 ret = execute_check (sqlite, sql);
619 sqlite3_free (sql);
620 if (ret != SQLITE_OK)
621 {
622 fprintf (stderr, "CreateCoverage \"%s\" error: %s\n", coverage,
623 err_msg);
624 sqlite3_free (err_msg);
625 *retcode += -1;
626 return 0;
627 }
628
629 /* loading from directory */
630 sql =
631 sqlite3_mprintf
632 ("SELECT RL2_LoadRastersFromDir(%Q, %Q, %Q, 0, 26914, 0, 1)", coverage,
633 "map_samples/usgs-rgb", ".tif");
634 ret = execute_check (sqlite, sql);
635 sqlite3_free (sql);
636 if (ret != SQLITE_OK)
637 {
638 fprintf (stderr, "LoadRastersFromDir \"%s\" error: %s\n", coverage,
639 err_msg);
640 sqlite3_free (err_msg);
641 *retcode += -2;
642 return 0;
643 }
644
645 /* deleting the first section */
646 sql = sqlite3_mprintf ("SELECT RL2_DeleteSection(%Q, %Q, 1)",
647 coverage, "rgb1");
648 ret = execute_check (sqlite, sql);
649 sqlite3_free (sql);
650 if (ret != SQLITE_OK)
651 {
652 fprintf (stderr, "DeleteSection \"%s\" error: %s\n", coverage,
653 err_msg);
654 sqlite3_free (err_msg);
655 *retcode += -3;
656 return 0;
657 }
658
659 /* re-loading yet again the first section */
660 sql = sqlite3_mprintf ("SELECT RL2_LoadRaster(%Q, %Q, 0, 26914, 1, 1)",
661 coverage, "map_samples/usgs-rgb/rgb1.tif");
662 ret = execute_check (sqlite, sql);
663 sqlite3_free (sql);
664 if (ret != SQLITE_OK)
665 {
666 fprintf (stderr, "LoadRaster \"%s\" error: %s\n", coverage, err_msg);
667 sqlite3_free (err_msg);
668 *retcode += -4;
669 return 0;
670 }
671
672 /* building the Pyramid Levels */
673 sql = sqlite3_mprintf ("SELECT RL2_Pyramidize(%Q, NULL, 0, 1)", coverage);
674 ret = execute_check (sqlite, sql);
675 sqlite3_free (sql);
676 if (ret != SQLITE_OK)
677 {
678 fprintf (stderr, "Pyramidize \"%s\" error: %s\n", coverage, err_msg);
679 sqlite3_free (err_msg);
680 *retcode += -5;
681 return 0;
682 }
683
684 /* export tests */
685 geom = get_center_point (sqlite, coverage);
686 if (geom == NULL)
687 {
688 *retcode += -6;
689 return 0;
690 }
691 if (!do_export_geotiff (sqlite, coverage, "norm", geom, 1))
692 {
693 *retcode += -7;
694 return 0;
695 }
696 if (!do_export_geotiff (sqlite, coverage, "norm", geom, 2))
697 {
698 *retcode += -8;
699 return 0;
700 }
701 if (!do_export_geotiff (sqlite, coverage, "norm", geom, 4))
702 {
703 *retcode += -9;
704 return 0;
705 }
706 if (!do_export_geotiff (sqlite, coverage, "norm", geom, 8))
707 {
708 *retcode += -10;
709 return 0;
710 }
711 if (!do_export_tiff (sqlite, coverage, geom, 1))
712 {
713 *retcode += -11;
714 return 0;
715 }
716 if (!do_export_tiff (sqlite, coverage, geom, 2))
717 {
718 *retcode += -12;
719 return 0;
720 }
721 if (!do_export_tiff (sqlite, coverage, geom, 4))
722 {
723 *retcode += -13;
724 return 0;
725 }
726 if (!do_export_tiff (sqlite, coverage, geom, 8))
727 {
728 *retcode += -14;
729 return 0;
730 }
731 if (!do_export_image (sqlite, coverage, geom, 39.015, ".jpg"))
732 {
733 *retcode += -15;
734 return 0;
735 }
736 if (!do_export_image (sqlite, coverage, geom, 69.0, ".jpg"))
737 {
738 *retcode += -16;
739 return 0;
740 }
741 if (!do_export_image (sqlite, coverage, geom, 39.015, ".png"))
742 {
743 *retcode += -17;
744 return 0;
745 }
746 if (!do_export_image (sqlite, coverage, geom, 16.0, ".png"))
747 {
748 *retcode += -18;
749 return 0;
750 }
751 if (!do_export_image (sqlite, coverage, geom, 39.015, ".tif"))
752 {
753 *retcode += -19;
754 return 0;
755 }
756 if (!do_export_image (sqlite, coverage, geom, 16.0, ".tif"))
757 {
758 *retcode += -20;
759 return 0;
760 }
761 if (!do_export_image (sqlite, coverage, geom, 39.015, ".pdf"))
762 {
763 *retcode += -21;
764 return 0;
765 }
766 if (!do_export_image (sqlite, coverage, geom, 16.0, ".pdf"))
767 {
768 *retcode += -22;
769 return 0;
770 }
771 gaiaFreeGeomColl (geom);
772
773 /* testing GetTileImage() */
774 if (!do_export_tile_image (sqlite, coverage, 1))
775 {
776 *retcode += -23;
777 return 0;
778 }
779 if (!do_export_tile_image (sqlite, coverage, 2))
780 {
781 *retcode += -24;
782 return 0;
783 }
784 if (!do_export_tile_image (sqlite, coverage, -1))
785 {
786 *retcode += -25;
787 return 0;
788 }
789
790 if (strcmp (coverage, "rgb_jpeg_512") == 0)
791 {
792 /* testing a Monolithic Pyramid */
793 geom = get_center_point (sqlite, coverage);
794 sql =
795 sqlite3_mprintf ("SELECT RL2_PyramidizeMonolithic(%Q, 1, 1)",
796 coverage);
797 ret = execute_check (sqlite, sql);
798 sqlite3_free (sql);
799 if (ret != SQLITE_OK)
800 {
801 fprintf (stderr, "PyramidizeMonolithic \"%s\" error: %s\n",
802 coverage, err_msg);
803 sqlite3_free (err_msg);
804 *retcode += -26;
805 return 0;
806 }
807
808 /* export tests */
809 if (geom == NULL)
810 {
811 *retcode += -27;
812 return 0;
813 }
814 if (!do_export_geotiff (sqlite, coverage, "mono", geom, 1))
815 {
816 *retcode += -28;
817 return 0;
818 }
819 if (!do_export_geotiff (sqlite, coverage, "mono", geom, 2))
820 {
821 *retcode += -29;
822 return 0;
823 }
824 if (!do_export_geotiff (sqlite, coverage, "mono", geom, 4))
825 {
826 *retcode += -30;
827 return 0;
828 }
829 if (!do_export_geotiff (sqlite, coverage, "mono", geom, 8))
830 {
831 *retcode += -31;
832 return 0;
833 }
834 gaiaFreeGeomColl (geom);
835 }
836
837 return 1;
838 }
839
840 static int
drop_coverage(sqlite3 * sqlite,unsigned char pixel,unsigned char compression,int tile_sz,int * retcode)841 drop_coverage (sqlite3 * sqlite, unsigned char pixel,
842 unsigned char compression, int tile_sz, int *retcode)
843 {
844 /* dropping some DBMS Coverage */
845 int ret;
846 char *err_msg = NULL;
847 const char *coverage;
848 char *sql;
849
850 /* setting the coverage name */
851 switch (pixel)
852 {
853 case RL2_PIXEL_RGB:
854 switch (compression)
855 {
856 case RL2_COMPRESSION_NONE:
857 switch (tile_sz)
858 {
859 case TILE_256:
860 coverage = "rgb_none_256";
861 break;
862 case TILE_512:
863 coverage = "rgb_none_512";
864 break;
865 case TILE_1024:
866 coverage = "rgb_none_1024";
867 break;
868 };
869 break;
870 case RL2_COMPRESSION_PNG:
871 switch (tile_sz)
872 {
873 case TILE_256:
874 coverage = "rgb_png_256";
875 break;
876 case TILE_512:
877 coverage = "rgb_png_512";
878 break;
879 case TILE_1024:
880 coverage = "rgb_png_1024";
881 break;
882 };
883 break;
884 case RL2_COMPRESSION_JPEG:
885 switch (tile_sz)
886 {
887 case TILE_256:
888 coverage = "rgb_jpeg_256";
889 break;
890 case TILE_512:
891 coverage = "rgb_jpeg_512";
892 break;
893 case TILE_1024:
894 coverage = "rgb_jpeg_1024";
895 break;
896 };
897 break;
898 case RL2_COMPRESSION_LOSSY_WEBP:
899 switch (tile_sz)
900 {
901 case TILE_256:
902 coverage = "rgb_webp_256";
903 break;
904 case TILE_512:
905 coverage = "rgb_webp_512";
906 break;
907 case TILE_1024:
908 coverage = "rgb_webp_1024";
909 break;
910 };
911 break;
912 case RL2_COMPRESSION_LOSSLESS_WEBP:
913 switch (tile_sz)
914 {
915 case TILE_256:
916 coverage = "rgb_llwebp_256";
917 break;
918 case TILE_512:
919 coverage = "rgb_llwebp_512";
920 break;
921 case TILE_1024:
922 coverage = "rgb_llwebp_1024";
923 break;
924 };
925 break;
926 };
927 break;
928 case RL2_PIXEL_GRAYSCALE:
929 switch (compression)
930 {
931 case RL2_COMPRESSION_NONE:
932 switch (tile_sz)
933 {
934 case TILE_256:
935 coverage = "gray_none_256";
936 break;
937 case TILE_512:
938 coverage = "gray_none_512";
939 break;
940 case TILE_1024:
941 coverage = "gray_none_1024";
942 break;
943 };
944 break;
945 case RL2_COMPRESSION_PNG:
946 switch (tile_sz)
947 {
948 case TILE_256:
949 coverage = "gray_png_256";
950 break;
951 case TILE_512:
952 coverage = "gray_png_512";
953 break;
954 case TILE_1024:
955 coverage = "gray_png_1024";
956 break;
957 };
958 break;
959 case RL2_COMPRESSION_JPEG:
960 switch (tile_sz)
961 {
962 case TILE_256:
963 coverage = "gray_jpeg_256";
964 break;
965 case TILE_512:
966 coverage = "gray_jpeg_512";
967 break;
968 case TILE_1024:
969 coverage = "gray_jpeg_1024";
970 break;
971 };
972 break;
973 case RL2_COMPRESSION_LOSSY_WEBP:
974 switch (tile_sz)
975 {
976 case TILE_256:
977 coverage = "gray_webp_256";
978 break;
979 case TILE_512:
980 coverage = "gray_webp_512";
981 break;
982 case TILE_1024:
983 coverage = "gray_webp_1024";
984 break;
985 };
986 break;
987 case RL2_COMPRESSION_LOSSLESS_WEBP:
988 switch (tile_sz)
989 {
990 case TILE_256:
991 coverage = "gray_llwebp_256";
992 break;
993 case TILE_512:
994 coverage = "gray_llwebp_512";
995 break;
996 case TILE_1024:
997 coverage = "gray_llwebp_1024";
998 break;
999 };
1000 break;
1001 };
1002 break;
1003 };
1004
1005 /* dropping the DBMS Coverage */
1006 sql = sqlite3_mprintf ("SELECT RL2_DropCoverage(%Q, 1)", coverage);
1007 ret = execute_check (sqlite, sql);
1008 sqlite3_free (sql);
1009 if (ret != SQLITE_OK)
1010 {
1011 fprintf (stderr, "DropCoverage \"%s\" error: %s\n", coverage,
1012 err_msg);
1013 sqlite3_free (err_msg);
1014 *retcode += -1;
1015 return 0;
1016 }
1017
1018 return 1;
1019 }
1020
1021 int
main(int argc,char * argv[])1022 main (int argc, char *argv[])
1023 {
1024 int result = 0;
1025 int ret;
1026 char *err_msg = NULL;
1027 sqlite3 *db_handle;
1028 void *cache = spatialite_alloc_connection ();
1029 char *old_SPATIALITE_SECURITY_ENV = NULL;
1030
1031 if (argc > 1 || argv[0] == NULL)
1032 argc = 1; /* silencing stupid compiler warnings */
1033
1034 old_SPATIALITE_SECURITY_ENV = getenv ("SPATIALITE_SECURITY");
1035 #ifdef _WIN32
1036 putenv ("SPATIALITE_SECURITY=relaxed");
1037 #else /* not WIN32 */
1038 setenv ("SPATIALITE_SECURITY", "relaxed", 1);
1039 #endif
1040
1041 /* opening and initializing the "memory" test DB */
1042 ret = sqlite3_open_v2 (":memory:", &db_handle,
1043 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
1044 if (ret != SQLITE_OK)
1045 {
1046 fprintf (stderr, "sqlite3_open_v2() error: %s\n",
1047 sqlite3_errmsg (db_handle));
1048 return -1;
1049 }
1050 spatialite_init_ex (db_handle, cache, 0);
1051 rl2_init (db_handle, 0);
1052 ret =
1053 sqlite3_exec (db_handle, "SELECT InitSpatialMetadata(1)", NULL, NULL,
1054 &err_msg);
1055 if (ret != SQLITE_OK)
1056 {
1057 fprintf (stderr, "InitSpatialMetadata() error: %s\n", err_msg);
1058 sqlite3_free (err_msg);
1059 return -2;
1060 }
1061 ret =
1062 sqlite3_exec (db_handle, "SELECT CreateRasterCoveragesTable()", NULL,
1063 NULL, &err_msg);
1064 if (ret != SQLITE_OK)
1065 {
1066 fprintf (stderr, "CreateRasterCoveragesTable() error: %s\n", err_msg);
1067 sqlite3_free (err_msg);
1068 return -3;
1069 }
1070
1071 /* RGB tests */
1072 ret = -100;
1073 if (!test_coverage
1074 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_NONE, TILE_256, &ret))
1075 return ret;
1076 ret = -120;
1077 if (!test_coverage
1078 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_NONE, TILE_512, &ret))
1079 return ret;
1080 ret = -140;
1081 if (!test_coverage
1082 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_NONE, TILE_1024, &ret))
1083 return ret;
1084 ret = -200;
1085 if (!test_coverage
1086 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_PNG, TILE_256, &ret))
1087 return ret;
1088 ret = -220;
1089 if (!test_coverage
1090 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_PNG, TILE_512, &ret))
1091 return ret;
1092 ret = -240;
1093 if (!test_coverage
1094 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_PNG, TILE_1024, &ret))
1095 return ret;
1096 ret = -300;
1097 if (!test_coverage
1098 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_JPEG, TILE_256, &ret))
1099 return ret;
1100 ret = -320;
1101 if (!test_coverage
1102 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_JPEG, TILE_512, &ret))
1103 return ret;
1104 ret = -340;
1105 if (!test_coverage
1106 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_JPEG, TILE_1024, &ret))
1107 return ret;
1108 ret = -400;
1109 if (!test_coverage
1110 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_LOSSY_WEBP, TILE_256, &ret))
1111 return ret;
1112 ret = -420;
1113 if (!test_coverage
1114 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_LOSSY_WEBP, TILE_512, &ret))
1115 return ret;
1116 ret = -440;
1117 if (!test_coverage
1118 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_LOSSY_WEBP, TILE_1024, &ret))
1119 return ret;
1120 ret = -500;
1121 if (!test_coverage
1122 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_LOSSLESS_WEBP, TILE_256,
1123 &ret))
1124 return ret;
1125 ret = -520;
1126 if (!test_coverage
1127 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_LOSSLESS_WEBP, TILE_512,
1128 &ret))
1129 return ret;
1130 ret = -540;
1131 if (!test_coverage
1132 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_LOSSLESS_WEBP, TILE_1024,
1133 &ret))
1134 return ret;
1135
1136 /* GRAYSCALE tests */
1137 ret = -600;
1138 if (!test_coverage
1139 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_NONE, TILE_256, &ret))
1140 return ret;
1141 ret = -620;
1142 if (!test_coverage
1143 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_NONE, TILE_512, &ret))
1144 return ret;
1145 ret = -640;
1146 if (!test_coverage
1147 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_NONE, TILE_1024, &ret))
1148 return ret;
1149 ret = -700;
1150 if (!test_coverage
1151 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_PNG, TILE_256, &ret))
1152 return ret;
1153 ret = -720;
1154 if (!test_coverage
1155 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_PNG, TILE_512, &ret))
1156 return ret;
1157 ret = -740;
1158 if (!test_coverage
1159 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_PNG, TILE_1024, &ret))
1160 return ret;
1161 ret = -800;
1162 if (!test_coverage
1163 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_JPEG, TILE_256, &ret))
1164 return ret;
1165 ret = -820;
1166 if (!test_coverage
1167 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_JPEG, TILE_512, &ret))
1168 return ret;
1169 ret = -840;
1170 if (!test_coverage
1171 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_JPEG, TILE_1024, &ret))
1172 return ret;
1173 ret = -900;
1174 if (!test_coverage
1175 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_LOSSY_WEBP, TILE_256,
1176 &ret))
1177 return ret;
1178 ret = -920;
1179 if (!test_coverage
1180 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_LOSSY_WEBP, TILE_512,
1181 &ret))
1182 return ret;
1183 ret = -940;
1184 if (!test_coverage
1185 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_LOSSY_WEBP, TILE_1024,
1186 &ret))
1187 return ret;
1188 ret = -1000;
1189 if (!test_coverage
1190 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_LOSSLESS_WEBP,
1191 TILE_256, &ret))
1192 return ret;
1193 ret = -1020;
1194 if (!test_coverage
1195 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_LOSSLESS_WEBP,
1196 TILE_512, &ret))
1197 return ret;
1198 ret = -1040;
1199 if (!test_coverage
1200 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_LOSSLESS_WEBP,
1201 TILE_1024, &ret))
1202 return ret;
1203
1204 /* dropping all RGB Coverages */
1205 ret = -170;
1206 if (!drop_coverage
1207 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_NONE, TILE_256, &ret))
1208 return ret;
1209 ret = -180;
1210 if (!drop_coverage
1211 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_NONE, TILE_512, &ret))
1212 return ret;
1213 ret = -190;
1214 if (!drop_coverage
1215 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_NONE, TILE_1024, &ret))
1216 return ret;
1217 ret = -270;
1218 if (!drop_coverage
1219 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_PNG, TILE_256, &ret))
1220 return ret;
1221 ret = -280;
1222 if (!drop_coverage
1223 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_PNG, TILE_512, &ret))
1224 return ret;
1225 ret = -290;
1226 if (!drop_coverage
1227 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_PNG, TILE_1024, &ret))
1228 return ret;
1229 ret = -370;
1230 if (!drop_coverage
1231 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_JPEG, TILE_256, &ret))
1232 return ret;
1233 ret = -380;
1234 if (!drop_coverage
1235 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_JPEG, TILE_512, &ret))
1236 return ret;
1237 ret = -390;
1238 if (!drop_coverage
1239 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_JPEG, TILE_1024, &ret))
1240 return ret;
1241 ret = -470;
1242 if (!drop_coverage
1243 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_LOSSY_WEBP, TILE_256, &ret))
1244 return ret;
1245 ret = -480;
1246 if (!drop_coverage
1247 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_LOSSY_WEBP, TILE_512, &ret))
1248 return ret;
1249 ret = -490;
1250 if (!drop_coverage
1251 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_LOSSY_WEBP, TILE_1024, &ret))
1252 return ret;
1253 ret = -570;
1254 if (!drop_coverage
1255 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_LOSSLESS_WEBP, TILE_256,
1256 &ret))
1257 return ret;
1258 ret = -580;
1259 if (!drop_coverage
1260 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_LOSSLESS_WEBP, TILE_512,
1261 &ret))
1262 return ret;
1263 ret = -590;
1264 if (!drop_coverage
1265 (db_handle, RL2_PIXEL_RGB, RL2_COMPRESSION_LOSSLESS_WEBP, TILE_1024,
1266 &ret))
1267 return ret;
1268
1269 /* dropping all GRAYSCALE Coverages */
1270 ret = -670;
1271 if (!drop_coverage
1272 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_NONE, TILE_256, &ret))
1273 return ret;
1274 ret = -680;
1275 if (!drop_coverage
1276 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_NONE, TILE_512, &ret))
1277 return ret;
1278 ret = -690;
1279 if (!drop_coverage
1280 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_NONE, TILE_1024, &ret))
1281 return ret;
1282 ret = -770;
1283 if (!drop_coverage
1284 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_PNG, TILE_256, &ret))
1285 return ret;
1286 ret = -780;
1287 if (!drop_coverage
1288 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_PNG, TILE_512, &ret))
1289 return ret;
1290 ret = -790;
1291 if (!drop_coverage
1292 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_PNG, TILE_1024, &ret))
1293 return ret;
1294 ret = -870;
1295 if (!drop_coverage
1296 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_JPEG, TILE_256, &ret))
1297 return ret;
1298 ret = -880;
1299 if (!drop_coverage
1300 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_JPEG, TILE_512, &ret))
1301 return ret;
1302 ret = -890;
1303 if (!drop_coverage
1304 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_JPEG, TILE_1024, &ret))
1305 return ret;
1306 ret = -970;
1307 if (!drop_coverage
1308 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_LOSSY_WEBP, TILE_256,
1309 &ret))
1310 return ret;
1311 ret = -980;
1312 if (!drop_coverage
1313 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_LOSSY_WEBP, TILE_512,
1314 &ret))
1315 return ret;
1316 ret = -990;
1317 if (!drop_coverage
1318 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_LOSSY_WEBP, TILE_1024,
1319 &ret))
1320 return ret;
1321 ret = -1070;
1322 if (!drop_coverage
1323 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_LOSSLESS_WEBP,
1324 TILE_256, &ret))
1325 return ret;
1326 ret = -1080;
1327 if (!drop_coverage
1328 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_LOSSLESS_WEBP,
1329 TILE_512, &ret))
1330 return ret;
1331 ret = -1090;
1332 if (!drop_coverage
1333 (db_handle, RL2_PIXEL_GRAYSCALE, RL2_COMPRESSION_LOSSLESS_WEBP,
1334 TILE_1024, &ret))
1335 return ret;
1336
1337 /* closing the DB */
1338 sqlite3_close (db_handle);
1339 spatialite_shutdown ();
1340 if (old_SPATIALITE_SECURITY_ENV)
1341 {
1342 #ifdef _WIN32
1343 char *env = sqlite3_mprintf ("SPATIALITE_SECURITY=%s",
1344 old_SPATIALITE_SECURITY_ENV);
1345 putenv (env);
1346 sqlite3_free (env);
1347 #else /* not WIN32 */
1348 setenv ("SPATIALITE_SECURITY", old_SPATIALITE_SECURITY_ENV, 1);
1349 #endif
1350 }
1351 else
1352 {
1353 #ifdef _WIN32
1354 putenv ("SPATIALITE_SECURITY=");
1355 #else /* not WIN32 */
1356 unsetenv ("SPATIALITE_SECURITY");
1357 #endif
1358 }
1359 return result;
1360 }
1361