1 /*
2
3 GeoPackage extensions for SpatiaLite / SQLite
4
5 Version: MPL 1.1/GPL 2.0/LGPL 2.1
6
7 The contents of this file are subject to the Mozilla Public License Version
8 1.1 (the "License"); you may not use this file except in compliance with
9 the License. You may obtain a copy of the License at
10 http://www.mozilla.org/MPL/
11
12 Software distributed under the License is distributed on an "AS IS" basis,
13 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 for the specific language governing rights and limitations under the
15 License.
16
17 The Original Code is GeoPackage Extensions
18
19 The Initial Developer of the Original Code is Brad Hards (bradh@frogmouth.net)
20
21 Portions created by the Initial Developer are Copyright (C) 2014-2015
22 the Initial Developer. All Rights Reserved.
23
24 Contributor(s):
25
26 Alternatively, the contents of this file may be used under the terms of
27 either the GNU General Public License Version 2 or later (the "GPL"), or
28 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 in which case the provisions of the GPL or the LGPL are applicable instead
30 of those above. If you wish to allow use of your version of this file only
31 under the terms of either the GPL or the LGPL, and not to allow others to
32 use your version of this file under the terms of the MPL, indicate your
33 decision by deleting the provisions above and replace them with the notice
34 and other provisions required by the GPL or the LGPL. If you do not delete
35 the provisions above, a recipient may use your version of this file under
36 the terms of any one of the MPL, the GPL or the LGPL.
37
38 */
39
40 #include "spatialite/geopackage.h"
41 #include "geopackage_internal.h"
42
43 #if defined(_WIN32) && !defined(__MINGW32__)
44 #include "config-msvc.h"
45 #else
46 #include "config.h"
47 #endif
48
49 #ifdef _WIN32
50 #define strcasecmp _stricmp
51 #endif /* not WIN32 */
52
53 #ifdef ENABLE_GEOPACKAGE
54
55 #define GEOPACKAGE_UNUSED() if (argc || argv) argc = argc;
56
57 GEOPACKAGE_DECLARE void
gpkgSetHeader2DLittleEndian(unsigned char * ptr,int srid,int endian_arch)58 gpkgSetHeader2DLittleEndian (unsigned char *ptr, int srid, int endian_arch)
59 {
60 *ptr = GEOPACKAGE_MAGIC1;
61 *(ptr + 1) = GEOPACKAGE_MAGIC2;
62 *(ptr + 2) = GEOPACKAGE_VERSION;
63 *(ptr + 3) = GEOPACKAGE_FLAGS_2D_LITTLEENDIAN;
64 gaiaExport32 (ptr + 4, srid, 1, endian_arch); /* the SRID */
65 }
66
67 GEOPACKAGE_DECLARE void
gpkgSetHeader2DMbr(unsigned char * ptr,double min_x,double min_y,double max_x,double max_y,int endian_arch)68 gpkgSetHeader2DMbr (unsigned char *ptr, double min_x, double min_y,
69 double max_x, double max_y, int endian_arch)
70 {
71 gaiaExport64 (ptr, min_x, 1, endian_arch);
72 gaiaExport64 (ptr + sizeof (double), max_x, 1, endian_arch);
73 gaiaExport64 (ptr + 2 * sizeof (double), min_y, 1, endian_arch);
74 gaiaExport64 (ptr + 3 * sizeof (double), max_y, 1, endian_arch);
75 }
76
77 GEOPACKAGE_DECLARE void
gaiaToGPB(gaiaGeomCollPtr geom,unsigned char ** result,int * size)78 gaiaToGPB (gaiaGeomCollPtr geom, unsigned char **result, int *size)
79 {
80 int wkbOnlyLength;
81 unsigned char *wkbOnlyGeometry = NULL;
82 unsigned char *ptr;
83 int endian_arch = gaiaEndianArch ();
84
85 gaiaToWkb (geom, &wkbOnlyGeometry, &wkbOnlyLength);
86 /* Calculate output size */
87 /* We only do 2D envelopes (MBR) irrespective of the input geometry dimensions */
88 *size = GEOPACKAGE_HEADER_LEN + GEOPACKAGE_2D_ENVELOPE_LEN;
89 *size += wkbOnlyLength;
90
91 /* allocate result and fill with "canary" value */
92 *result = malloc (*size);
93 if (*result == NULL)
94 {
95 return;
96 }
97 memset (*result, 0xD9, *size);
98 ptr = *result;
99
100 /* build header */
101 gpkgSetHeader2DLittleEndian (ptr, geom->Srid, endian_arch);
102
103 /* build MBR */
104 gpkgSetHeader2DMbr (ptr +
105 GEOPACKAGE_HEADER_LEN,
106 geom->MinX,
107 geom->MinY, geom->MaxX, geom->MaxY, endian_arch);
108
109 /* copy wkbonly results to result */
110 memcpy (ptr + GEOPACKAGE_HEADER_LEN +
111 GEOPACKAGE_2D_ENVELOPE_LEN, wkbOnlyGeometry, wkbOnlyLength);
112
113 free (wkbOnlyGeometry);
114 }
115
116
117 GEOPACKAGE_PRIVATE void
fnct_ToGPB(sqlite3_context * context,int argc,sqlite3_value ** argv)118 fnct_ToGPB (sqlite3_context * context, int argc, sqlite3_value ** argv)
119 {
120 /* SQL function:
121 / AsGPB(BLOB encoded geometry)
122 /
123 / converts the (spatialite) geometry blob into a GeoPackage format geometry blob
124 / or NULL if any error is encountered
125 */
126 unsigned char *p_blob;
127 int n_bytes;
128 gaiaGeomCollPtr geo = NULL;
129 int len;
130 unsigned char *p_result = NULL;
131 GEOPACKAGE_UNUSED (); /* LCOV_EXCL_LINE */
132 if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
133 {
134 sqlite3_result_null (context);
135 return;
136 }
137 p_blob = (unsigned char *) sqlite3_value_blob (argv[0]);
138 n_bytes = sqlite3_value_bytes (argv[0]);
139 geo = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes);
140 if (!geo)
141 {
142 sqlite3_result_null (context);
143 return;
144 }
145 else
146 {
147 gaiaToGPB (geo, &p_result, &len);
148 if (!p_result)
149 {
150 sqlite3_result_null (context);
151 }
152 else
153 {
154 sqlite3_result_blob (context, p_result, len, free);
155 }
156 }
157 gaiaFreeGeomColl (geo);
158 }
159
160 static int
sanity_check_gpb(const unsigned char * wkb,int size,int * srid,unsigned int * envelope_length)161 sanity_check_gpb (const unsigned char *wkb, int size, int *srid,
162 unsigned int *envelope_length)
163 {
164 /* checking type coherency for GeoPackageBinary encoded GEOMETRY */
165 int little_endian;
166 int endian_arch = gaiaEndianArch ();
167 char flags;
168 int envelope_code;
169
170 if (size < GEOPACKAGE_HEADER_LEN)
171 {
172 return 0; /* too short to be GPB */
173 }
174 if ((*(wkb + 0) != GEOPACKAGE_MAGIC1) || (*(wkb + 1) != GEOPACKAGE_MAGIC2))
175 {
176 return 0; /* doesn't match required signature */
177 }
178 if (*(wkb + 2) != GEOPACKAGE_VERSION)
179 {
180 return 0; /* we only know how to parse version 1 */
181 }
182
183 flags = *(wkb + 3);
184
185 little_endian = flags & GEOPACKAGE_WKB_LITTLEENDIAN;
186
187 envelope_code = ((flags >> 1) & 0x07);
188 switch (envelope_code)
189 {
190 case 0:
191 *envelope_length = 0;
192 break;
193 case 1:
194 *envelope_length = 32;
195 break;
196 case 2:
197 case 3:
198 *envelope_length = 48;
199 break;
200 case 4:
201 *envelope_length = 64;
202 break;
203 default:
204 /* illegal value - not valid GeoPackageBinary */
205 fprintf (stderr, "Unsupported geopackage envelope value: 0x%x\n",
206 envelope_code);
207 return 0;
208 }
209
210 if (flags & GEOPACKAGE_WKB_EXTENDEDGEOMETRY_FLAG)
211 {
212 fprintf (stderr,
213 "unsupported geopackage binary type (extended geopackage binary)\n");
214 return 0;
215 }
216
217 *srid = gaiaImport32 (wkb + 4, little_endian, endian_arch);
218
219 return 1;
220 }
221
222 GEOPACKAGE_DECLARE gaiaGeomCollPtr
gaiaFromGeoPackageGeometryBlob(const unsigned char * gpb,unsigned int gpb_len)223 gaiaFromGeoPackageGeometryBlob (const unsigned char *gpb, unsigned int gpb_len)
224 {
225 gaiaGeomCollPtr geo = NULL;
226 int srid = GEOPACKAGE_DEFAULT_UNDEFINED_SRID;
227 unsigned int envelope_length = 0;
228 const unsigned char *wkb;
229 unsigned int wkb_len;
230
231 if (!sanity_check_gpb (gpb, gpb_len, &srid, &envelope_length))
232 {
233 return NULL;
234 }
235
236 wkb = gpb + GEOPACKAGE_HEADER_LEN + envelope_length;
237 wkb_len = gpb_len - (GEOPACKAGE_HEADER_LEN + envelope_length);
238
239 geo = gaiaFromWkb (wkb, wkb_len);
240 if (geo == NULL)
241 {
242 return NULL;
243 }
244 geo->Srid = srid;
245
246 return geo;
247 }
248
249 GEOPACKAGE_PRIVATE void
fnct_GeomFromGPB(sqlite3_context * context,int argc,sqlite3_value ** argv)250 fnct_GeomFromGPB (sqlite3_context * context, int argc, sqlite3_value ** argv)
251 {
252 /* SQL function:
253 / GeomFromGPB(GPB encoded geometry)
254 /
255 / returns a geometry created by parsing a GeoPackageBinary encoded blob
256 / or NULL if any error is encountered
257 */
258 int len;
259 unsigned char *p_result = NULL;
260 const unsigned char *gpb;
261 unsigned int gpb_len;
262 gaiaGeomCollPtr geo = NULL;
263
264
265 GEOPACKAGE_UNUSED (); /* LCOV_EXCL_LINE */
266 if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
267 {
268 sqlite3_result_null (context);
269 return;
270 }
271 gpb = sqlite3_value_blob (argv[0]);
272 gpb_len = sqlite3_value_bytes (argv[0]);
273
274 geo = gaiaFromGeoPackageGeometryBlob (gpb, gpb_len);
275 if (geo == NULL)
276 {
277 sqlite3_result_null (context);
278 return;
279 }
280
281 gaiaToSpatiaLiteBlobWkb (geo, &p_result, &len);
282 gaiaFreeGeomColl (geo);
283 sqlite3_result_blob (context, p_result, len, free);
284 }
285
286 /* Sandro Furieri - 2014-05-19 */
287
288 GEOPACKAGE_DECLARE int
gaiaIsValidGPB(const unsigned char * gpb,int gpb_len)289 gaiaIsValidGPB (const unsigned char *gpb, int gpb_len)
290 {
291 /* checks for a valid GPB */
292 int srid;
293 unsigned int envelope_length;
294
295 if (gpb == NULL)
296 return 0;
297 return sanity_check_gpb (gpb, gpb_len, &srid, &envelope_length);
298 }
299
300 GEOPACKAGE_DECLARE int
gaiaGetSridFromGPB(const unsigned char * gpb,int gpb_len)301 gaiaGetSridFromGPB (const unsigned char *gpb, int gpb_len)
302 {
303 /* extracts the SRID from a valid GPB */
304 int srid;
305 unsigned int envelope_length;
306
307 if (gpb == NULL)
308 return -1;
309 if (!sanity_check_gpb (gpb, gpb_len, &srid, &envelope_length))
310 return -1;
311 return srid;
312 }
313
314 GEOPACKAGE_DECLARE int
gaiaIsEmptyGPB(const unsigned char * gpb,int gpb_len)315 gaiaIsEmptyGPB (const unsigned char *gpb, int gpb_len)
316 {
317 /* checks for an empty GPB */
318 int srid;
319 unsigned int envelope_length;
320 int is_empty;
321
322 if (gpb == NULL)
323 return -1;
324 if (!sanity_check_gpb (gpb, gpb_len, &srid, &envelope_length))
325 return -1;
326 is_empty = *(gpb + 3) & GEOPACKAGE_WKB_EMPTY_FLAG;
327 return is_empty;
328 }
329
330 GEOPACKAGE_DECLARE int
gaiaGetEnvelopeFromGPB(const unsigned char * gpb,int gpb_len,double * min_x,double * max_x,double * min_y,double * max_y,int * has_z,double * min_z,double * max_z,int * has_m,double * min_m,double * max_m)331 gaiaGetEnvelopeFromGPB (const unsigned char *gpb, int gpb_len, double *min_x,
332 double *max_x, double *min_y, double *max_y, int *has_z,
333 double *min_z, double *max_z, int *has_m, double *min_m,
334 double *max_m)
335 {
336 /* attempts to retrieve a full Envelope from a GPB */
337 gaiaGeomCollPtr geo;
338 double min;
339 double max;
340 if (gpb == NULL)
341 return 0;
342 geo = gaiaFromGeoPackageGeometryBlob (gpb, gpb_len);
343 if (geo == NULL)
344 return 0;
345 /*
346 / defensive programming
347 /
348 / the GPKG seems to be a rather sparse and inconsistent standard
349 / so we'll always ignore the Envelope declared by GPB
350 / and we'll instead recompute 'our' Envelope from scratch
351 */
352 gaiaMbrGeometry (geo);
353 *min_x = geo->MinX;
354 *max_x = geo->MaxX;
355 *min_y = geo->MinY;
356 *max_y = geo->MaxY;
357 if (geo->DimensionModel == GAIA_XY_Z || geo->DimensionModel == GAIA_XY_Z_M)
358 {
359 *has_z = 1;
360 gaiaZRangeGeometry (geo, &min, &max);
361 *min_z = min;
362 *max_z = max;
363 }
364 else
365 *has_z = 0;
366 if (geo->DimensionModel == GAIA_XY_M || geo->DimensionModel == GAIA_XY_Z_M)
367 {
368 *has_m = 1;
369 gaiaMRangeGeometry (geo, &min, &max);
370 *min_m = min;
371 *max_m = max;
372 }
373 else
374 *has_m = 0;
375 gaiaFreeGeomColl (geo);
376 return 1;
377 }
378
379 GEOPACKAGE_DECLARE char *
gaiaGetGeometryTypeFromGPB(const unsigned char * gpb,int gpb_len)380 gaiaGetGeometryTypeFromGPB (const unsigned char *gpb, int gpb_len)
381 {
382 /* attempts to retrieve the Geometry Type from a GPB */
383 gaiaGeomCollPtr geo;
384 const char *type = NULL;
385 int len;
386 char *gtype;
387
388 if (gpb == NULL)
389 return NULL;
390 geo = gaiaFromGeoPackageGeometryBlob (gpb, gpb_len);
391 if (geo == NULL)
392 return NULL;
393 /*
394 / defensive programming
395 /
396 / the GPKG seems to be a rather sparse and inconsistent standard
397 / so we'll always fetch the Geometry Type from 'our' Geometry Type
398 */
399 switch (gaiaGeometryType (geo))
400 {
401 case GAIA_POINT:
402 case GAIA_POINTZ:
403 case GAIA_POINTM:
404 case GAIA_POINTZM:
405 type = "POINT";
406 break;
407 case GAIA_LINESTRING:
408 case GAIA_LINESTRINGZ:
409 case GAIA_LINESTRINGM:
410 case GAIA_LINESTRINGZM:
411 type = "LINESTRING";
412 break;
413 case GAIA_POLYGON:
414 case GAIA_POLYGONZ:
415 case GAIA_POLYGONM:
416 case GAIA_POLYGONZM:
417 type = "POLYGON";
418 break;
419 case GAIA_MULTIPOINT:
420 case GAIA_MULTIPOINTZ:
421 case GAIA_MULTIPOINTM:
422 case GAIA_MULTIPOINTZM:
423 type = "MULTIPOINT";
424 break;
425 case GAIA_MULTILINESTRING:
426 case GAIA_MULTILINESTRINGZ:
427 case GAIA_MULTILINESTRINGM:
428 case GAIA_MULTILINESTRINGZM:
429 type = "MULTILINESTRING";
430 break;
431 case GAIA_MULTIPOLYGON:
432 case GAIA_MULTIPOLYGONZ:
433 case GAIA_MULTIPOLYGONM:
434 case GAIA_MULTIPOLYGONZM:
435 type = "MULTIPOLYGON";
436 break;
437 case GAIA_GEOMETRYCOLLECTION:
438 case GAIA_GEOMETRYCOLLECTIONZ:
439 case GAIA_GEOMETRYCOLLECTIONM:
440 case GAIA_GEOMETRYCOLLECTIONZM:
441 type = "GEOMCOLLECTION";
442 break;
443 };
444 gaiaFreeGeomColl (geo);
445
446 if (type == NULL)
447 return NULL;
448 len = strlen (type);
449 gtype = malloc (len + 1);
450 strcpy (gtype, type);
451 return gtype;
452 }
453
454 GEOPACKAGE_PRIVATE void
fnct_GPKG_IsAssignable(sqlite3_context * context,int argc,sqlite3_value ** argv)455 fnct_GPKG_IsAssignable (sqlite3_context * context, int argc,
456 sqlite3_value ** argv)
457 {
458 /* SQL function:
459 / GPKG_IsAssignale(expected_type_name TEXT, actual_type_name TEXT)
460 /
461 / returns:
462 / 1 if the expected type is the same or a super-type of actual type
463 / 0 otherwise
464 */
465 const char *expected;
466 const char *actual;
467 int ret = 0;
468
469 GEOPACKAGE_UNUSED (); /* LCOV_EXCL_LINE */
470 if (sqlite3_value_type (argv[0]) != SQLITE_TEXT)
471 {
472 sqlite3_result_int (context, ret);
473 return;
474 }
475 if (sqlite3_value_type (argv[1]) != SQLITE_TEXT)
476 {
477 sqlite3_result_int (context, ret);
478 return;
479 }
480 expected = (const char *) sqlite3_value_text (argv[0]);
481 actual = (const char *) sqlite3_value_text (argv[1]);
482 if (strcasecmp (expected, actual) == 0)
483 ret = 1;
484 if (strcasecmp (expected, "GEOMETRY") == 0)
485 ret = 1;
486 if (strcasecmp (expected, "MULTIPOINT")
487 == 0 && strcasecmp (actual, "POINT") == 0)
488 ret = 1;
489 if (strcasecmp
490 (expected, "MULTILINESTRING") == 0
491 && strcasecmp (actual, "LINESTRING") == 0)
492 ret = 1;
493 if (strcasecmp
494 (expected, "MULTIPOLYGON") == 0 && strcasecmp (actual, "POLYGON") == 0)
495 ret = 1;
496 sqlite3_result_int (context, ret);
497 }
498
499 GEOPACKAGE_PRIVATE void
fnct_IsValidGPB(sqlite3_context * context,int argc,sqlite3_value ** argv)500 fnct_IsValidGPB (sqlite3_context * context, int argc, sqlite3_value ** argv)
501 {
502 /* SQL function:
503 / IsValidGPB(GPB encoded geometry)
504 /
505 / check for a valid GPB encoded geometry
506 */
507 const unsigned char *gpb;
508 unsigned int gpb_len;
509
510
511 GEOPACKAGE_UNUSED (); /* LCOV_EXCL_LINE */
512 if (sqlite3_value_type (argv[0]) != SQLITE_BLOB)
513 {
514 sqlite3_result_int (context, 0);
515 return;
516 }
517 gpb = sqlite3_value_blob (argv[0]);
518 gpb_len = sqlite3_value_bytes (argv[0]);
519 sqlite3_result_int (context, gaiaIsValidGPB (gpb, gpb_len));
520 }
521
522 /* end Sandro Furieri - 2014-05-19 */
523
524 #endif
525