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