Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GeoPackage Translator
4 : * Purpose: Utility functions for OGR GeoPackage driver.
5 : * Author: Paul Ramsey, pramsey@boundlessgeo.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2013, Paul Ramsey <pramsey@boundlessgeo.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogrgeopackageutility.h"
14 : #include "ogr_p.h"
15 : #include "ogr_wkb.h"
16 : #include "sqlite/ogrsqlitebase.h"
17 : #include <limits>
18 :
19 : /* Requirement 20: A GeoPackage SHALL store feature table geometries */
20 : /* with the basic simple feature geometry types (Geometry, Point, */
21 : /* LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, */
22 : /* GeomCollection) */
23 : /* http://opengis.github.io/geopackage/#geometry_types */
24 3619 : OGRwkbGeometryType GPkgGeometryTypeToWKB(const char *pszGpkgType, bool bHasZ,
25 : bool bHasM)
26 : {
27 : OGRwkbGeometryType oType;
28 :
29 3619 : if (EQUAL("Geometry", pszGpkgType))
30 2129 : oType = wkbUnknown;
31 : /* The 1.0 spec is not completely clear on what should be used... */
32 1490 : else if (EQUAL("GeomCollection", pszGpkgType) ||
33 1490 : EQUAL("GeometryCollection", pszGpkgType))
34 2 : oType = wkbGeometryCollection;
35 : else
36 : {
37 1488 : oType = OGRFromOGCGeomType(pszGpkgType);
38 1488 : if (oType == wkbUnknown)
39 3 : oType = wkbNone;
40 : }
41 :
42 3619 : if ((oType != wkbNone) && bHasZ)
43 : {
44 66 : oType = wkbSetZ(oType);
45 : }
46 3619 : if ((oType != wkbNone) && bHasM)
47 : {
48 19 : oType = wkbSetM(oType);
49 : }
50 :
51 3619 : return oType;
52 : }
53 :
54 : /* Requirement 5: The columns of tables in a GeoPackage SHALL only be */
55 : /* declared using one of the data types specified in table GeoPackage */
56 : /* Data Types. */
57 : /* http://opengis.github.io/geopackage/#table_column_data_types */
58 : // return a OGRFieldType value or OFTMaxType + 1
59 4780 : int GPkgFieldToOGR(const char *pszGpkgType, OGRFieldSubType &eSubType,
60 : int &nMaxWidth)
61 : {
62 4780 : eSubType = OFSTNone;
63 4780 : nMaxWidth = 0;
64 :
65 : /* Integer types */
66 4780 : if (STRNCASECMP("INT", pszGpkgType, 3) == 0)
67 : {
68 1450 : if (!EQUAL("INT", pszGpkgType) && !EQUAL("INTEGER", pszGpkgType))
69 : {
70 0 : CPLError(CE_Warning, CPLE_AppDefined,
71 : "Field format '%s' not supported. "
72 : "Interpreted as INT",
73 : pszGpkgType);
74 : }
75 1450 : return OFTInteger64;
76 : }
77 3330 : else if (EQUAL("MEDIUMINT", pszGpkgType))
78 161 : return OFTInteger;
79 3169 : else if (EQUAL("SMALLINT", pszGpkgType))
80 : {
81 16 : eSubType = OFSTInt16;
82 16 : return OFTInteger;
83 : }
84 3153 : else if (EQUAL("TINYINT", pszGpkgType))
85 9 : return OFTInteger; // [-128, 127]
86 3144 : else if (EQUAL("BOOLEAN", pszGpkgType))
87 : {
88 21 : eSubType = OFSTBoolean;
89 21 : return OFTInteger;
90 : }
91 :
92 : /* Real types */
93 3123 : else if (EQUAL("FLOAT", pszGpkgType))
94 : {
95 16 : eSubType = OFSTFloat32;
96 16 : return OFTReal;
97 : }
98 3107 : else if (EQUAL("DOUBLE", pszGpkgType))
99 58 : return OFTReal;
100 3049 : else if (EQUAL("REAL", pszGpkgType))
101 616 : return OFTReal;
102 :
103 : // Only used normally in gpkg_data_column_constraints table, and we
104 : // need this only is reading it through ExecuteSQL()
105 2433 : else if (EQUAL("NUMERIC", pszGpkgType))
106 2 : return OFTReal;
107 :
108 : /* String/binary types */
109 2431 : else if (STRNCASECMP("TEXT", pszGpkgType, 4) == 0)
110 : {
111 1412 : if (pszGpkgType[4] == '(')
112 150 : nMaxWidth = atoi(pszGpkgType + 5);
113 1262 : else if (pszGpkgType[4] != '\0')
114 : {
115 0 : CPLError(CE_Warning, CPLE_AppDefined,
116 : "Field format '%s' not supported. "
117 : "Interpreted as TEXT",
118 : pszGpkgType);
119 : }
120 1412 : return OFTString;
121 : }
122 :
123 1019 : else if (STRNCASECMP("BLOB", pszGpkgType, 4) == 0)
124 : {
125 42 : if (pszGpkgType[4] != '(' && pszGpkgType[4] != '\0')
126 : {
127 0 : CPLError(CE_Warning, CPLE_AppDefined,
128 : "Field format '%s' not supported. "
129 : "Interpreted as BLOB",
130 : pszGpkgType);
131 : }
132 42 : return OFTBinary;
133 : }
134 :
135 : /* Date types */
136 977 : else if (EQUAL("DATE", pszGpkgType))
137 55 : return OFTDate;
138 922 : else if (EQUAL("DATETIME", pszGpkgType))
139 71 : return OFTDateTime;
140 :
141 : /* Illegal! */
142 : else
143 : {
144 851 : if (GPkgGeometryTypeToWKB(pszGpkgType, false, false) == wkbNone)
145 : {
146 3 : CPLError(CE_Warning, CPLE_AppDefined,
147 : "Field format '%s' not supported", pszGpkgType);
148 : }
149 851 : return OFTMaxType + 1;
150 : }
151 : }
152 :
153 : /* Requirement 5: The columns of tables in a GeoPackage SHALL only be */
154 : /* declared using one of the data types specified in table GeoPackage */
155 : /* Data Types. */
156 : /* http://opengis.github.io/geopackage/#table_column_data_types */
157 4317 : const char *GPkgFieldFromOGR(OGRFieldType eType, OGRFieldSubType eSubType,
158 : int nMaxWidth)
159 : {
160 4317 : switch (eType)
161 : {
162 959 : case OFTInteger:
163 : {
164 959 : if (eSubType == OFSTBoolean)
165 8 : return "BOOLEAN";
166 951 : else if (eSubType == OFSTInt16)
167 7 : return "SMALLINT";
168 : else
169 944 : return "MEDIUMINT";
170 : }
171 167 : case OFTInteger64:
172 167 : return "INTEGER";
173 224 : case OFTReal:
174 : {
175 224 : if (eSubType == OFSTFloat32)
176 12 : return "FLOAT";
177 : else
178 212 : return "REAL";
179 : }
180 2843 : case OFTString:
181 : {
182 2843 : if (nMaxWidth > 0)
183 88 : return CPLSPrintf("TEXT(%d)", nMaxWidth);
184 : else
185 2755 : return "TEXT";
186 : }
187 17 : case OFTBinary:
188 17 : return "BLOB";
189 40 : case OFTDate:
190 40 : return "DATE";
191 60 : case OFTDateTime:
192 60 : return "DATETIME";
193 7 : default:
194 7 : return "TEXT";
195 : }
196 : }
197 :
198 : /* Requirement 19: A GeoPackage SHALL store feature table geometries
199 : * with or without optional elevation (Z) and/or measure (M) values in SQL
200 : * BLOBs using the Standard GeoPackageBinary format specified in table
201 : * GeoPackage SQL Geometry Binary Format and clause Geometry Encoding.
202 : *
203 : * http://opengis.github.io/geopackage/#gpb_format
204 : *
205 : * GeoPackageBinaryHeader {
206 : * byte[2] magic = 0x4750;
207 : * byte version;
208 : * byte flags;
209 : * int32 srs_id;
210 : * double[] envelope;
211 : * }
212 : *
213 : * StandardGeoPackageBinary {
214 : * GeoPackageBinaryHeader header;
215 : * WKBGeometry geometry;
216 : * }
217 : *
218 : * Flags byte contents:
219 : * Bit 7: Reserved for future
220 : * Bit 6: Reserved for future
221 : * Bit 5: Using Extended GPKG Binary?
222 : * Bit 4: Geometry is Empty?
223 : * Bit 3,2,1: Envelope contents (0 none, 1=X/Y, 2=X/Y/Z, 3=X/Y/M, 4=X/Y/Z/M)
224 : * Bit 0: Byte order of header (0=big/XDR, 1=little/NDR)
225 : *
226 : */
227 :
228 262574 : GByte *GPkgGeometryFromOGR(const OGRGeometry *poGeometry, int iSrsId,
229 : const OGRGeomCoordinateBinaryPrecision *psPrecision,
230 : size_t *pnWkbLen)
231 : {
232 262574 : CPLAssert(poGeometry != nullptr);
233 :
234 262574 : GByte byFlags = 0;
235 262574 : GByte byEnv = 1;
236 262574 : OGRwkbExportOptions wkbExportOptions;
237 262574 : if (psPrecision)
238 262572 : wkbExportOptions.sPrecision = *psPrecision;
239 262574 : wkbExportOptions.eByteOrder = static_cast<OGRwkbByteOrder>(CPL_IS_LSB);
240 : OGRErr err;
241 262574 : OGRBoolean bPoint = (wkbFlatten(poGeometry->getGeometryType()) == wkbPoint);
242 262574 : OGRBoolean bEmpty = poGeometry->IsEmpty();
243 : /* We voluntarily use getCoordinateDimension() so as to get only 2 for
244 : * XY/XYM */
245 : /* and 3 for XYZ/XYZM as we currently don't write envelopes with M extent.
246 : */
247 262574 : int iDims = poGeometry->getCoordinateDimension();
248 :
249 : /* Header has 8 bytes for sure, and optional extra space for bounds */
250 262574 : size_t nHeaderLen = 2 + 1 + 1 + 4;
251 262574 : if (!bPoint && !bEmpty)
252 : {
253 6155 : nHeaderLen += 8 * 2 * iDims;
254 : }
255 :
256 : /* Total BLOB size is header + WKB size */
257 262574 : size_t nWkbLen = nHeaderLen + poGeometry->WkbSize();
258 262574 : if (nWkbLen > static_cast<size_t>(std::numeric_limits<int>::max()))
259 : {
260 0 : CPLError(CE_Failure, CPLE_NotSupported, "too big geometry blob");
261 0 : return nullptr;
262 : }
263 262574 : GByte *pabyWkb = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbLen));
264 262574 : if (!pabyWkb)
265 0 : return nullptr;
266 262574 : if (pnWkbLen)
267 262574 : *pnWkbLen = nWkbLen;
268 :
269 : /* Header Magic */
270 262574 : pabyWkb[0] = 0x47;
271 262574 : pabyWkb[1] = 0x50;
272 :
273 : /* GPKG BLOB Version */
274 262574 : pabyWkb[2] = 0;
275 :
276 : /* Extended? No. */
277 :
278 : /* Envelope dimensionality? */
279 :
280 : /* Don't write envelope for point type */
281 262574 : if (bPoint)
282 256390 : byEnv = 0;
283 : else
284 : /* 3D envelope for 3D data */
285 6184 : if (iDims == 3)
286 35 : byEnv = 2;
287 : /* 2D envelope otherwise */
288 : else
289 6149 : byEnv = 1;
290 :
291 : /* Empty? No envelope then. */
292 262574 : if (bEmpty)
293 : {
294 1041 : byEnv = 0;
295 : /* Set empty flag */
296 1041 : byFlags |= (1 << 4);
297 : }
298 :
299 : /* Set envelope flags */
300 262574 : byFlags |= (byEnv << 1);
301 :
302 : /* Byte order of header? */
303 : /* Use native endianness */
304 262574 : byFlags |= wkbExportOptions.eByteOrder;
305 :
306 : /* Write flags byte */
307 262574 : pabyWkb[3] = byFlags;
308 :
309 : /* Write srs_id */
310 262574 : memcpy(pabyWkb + 4, &iSrsId, 4);
311 :
312 : /* Write envelope */
313 262574 : if (!bEmpty && !bPoint)
314 : {
315 6155 : double *padPtr = reinterpret_cast<double *>(pabyWkb + 8);
316 6155 : if (iDims == 3)
317 : {
318 29 : OGREnvelope3D oEnv3d;
319 29 : poGeometry->getEnvelope(&oEnv3d);
320 29 : padPtr[0] = oEnv3d.MinX;
321 29 : padPtr[1] = oEnv3d.MaxX;
322 29 : padPtr[2] = oEnv3d.MinY;
323 29 : padPtr[3] = oEnv3d.MaxY;
324 29 : padPtr[4] = oEnv3d.MinZ;
325 29 : padPtr[5] = oEnv3d.MaxZ;
326 : }
327 : else
328 : {
329 6126 : OGREnvelope oEnv;
330 6126 : poGeometry->getEnvelope(&oEnv);
331 6126 : padPtr[0] = oEnv.MinX;
332 6126 : padPtr[1] = oEnv.MaxX;
333 6126 : padPtr[2] = oEnv.MinY;
334 6126 : padPtr[3] = oEnv.MaxY;
335 : }
336 : }
337 :
338 262574 : GByte *pabyPtr = pabyWkb + nHeaderLen;
339 :
340 : /* Use the wkbVariantIso for ISO SQL/MM output (differs for 3d geometry) */
341 262574 : wkbExportOptions.eWkbVariant = wkbVariantIso;
342 262574 : err = poGeometry->exportToWkb(pabyPtr, &wkbExportOptions);
343 262574 : if (err != OGRERR_NONE)
344 : {
345 0 : CPLFree(pabyWkb);
346 0 : return nullptr;
347 : }
348 :
349 262574 : return pabyWkb;
350 : }
351 :
352 1364750 : OGRErr GPkgHeaderFromWKB(const GByte *pabyGpkg, size_t nGpkgLen,
353 : GPkgHeader *poHeader)
354 : {
355 1364750 : CPLAssert(pabyGpkg != nullptr);
356 1364750 : CPLAssert(poHeader != nullptr);
357 :
358 : /* Magic (match required) */
359 1364750 : if (nGpkgLen < 8 || pabyGpkg[0] != 0x47 || pabyGpkg[1] != 0x50 ||
360 1364690 : pabyGpkg[2] != 0) /* Version (only 0 supported at this time)*/
361 : {
362 63 : memset(poHeader, 0, sizeof(*poHeader));
363 63 : return OGRERR_FAILURE;
364 : }
365 :
366 : /* Flags */
367 1364690 : GByte byFlags = pabyGpkg[3];
368 1364690 : poHeader->bEmpty = (byFlags & (0x01 << 4)) >> 4;
369 1364690 : poHeader->bExtended = (byFlags & (0x01 << 5)) >> 5;
370 1364690 : poHeader->eByteOrder = static_cast<OGRwkbByteOrder>(byFlags & 0x01);
371 1364690 : poHeader->bExtentHasXY = false;
372 1364690 : poHeader->bExtentHasZ = false;
373 : #ifdef notdef
374 : poHeader->bExtentHasM = false;
375 : #endif
376 1364690 : OGRBoolean bSwap = OGR_SWAP(poHeader->eByteOrder);
377 :
378 : /* Envelope */
379 1364690 : int iEnvelope = (byFlags & (0x07 << 1)) >> 1;
380 1364690 : int nEnvelopeDim = 0;
381 1364690 : if (iEnvelope)
382 : {
383 29255 : poHeader->bExtentHasXY = true;
384 29255 : if (iEnvelope == 1)
385 : {
386 29074 : nEnvelopeDim = 2; /* 2D envelope */
387 : }
388 181 : else if (iEnvelope == 2)
389 : {
390 165 : poHeader->bExtentHasZ = true;
391 165 : nEnvelopeDim = 3; /* 2D+Z envelope */
392 : }
393 16 : else if (iEnvelope == 3)
394 : {
395 : #ifdef notdef
396 : poHeader->bExtentHasM = true;
397 : #endif
398 8 : nEnvelopeDim = 3; /* 2D+M envelope */
399 : }
400 8 : else if (iEnvelope == 4)
401 : {
402 8 : poHeader->bExtentHasZ = true;
403 : #ifdef notdef
404 : poHeader->bExtentHasM = true;
405 : #endif
406 8 : nEnvelopeDim = 4; /* 2D+ZM envelope */
407 : }
408 : else
409 : {
410 0 : return OGRERR_FAILURE;
411 : }
412 : }
413 :
414 : /* SrsId */
415 1364690 : int iSrsId = 0;
416 1364690 : memcpy(&iSrsId, pabyGpkg + 4, 4);
417 1364690 : if (bSwap)
418 : {
419 0 : iSrsId = CPL_SWAP32(iSrsId);
420 : }
421 1364690 : poHeader->iSrsId = iSrsId;
422 :
423 1364690 : if (nGpkgLen < static_cast<size_t>(8 + 8 * 2 * nEnvelopeDim))
424 : {
425 : // Not enough bytes
426 0 : return OGRERR_FAILURE;
427 : }
428 :
429 : /* Envelope */
430 1364690 : const double *padPtr = reinterpret_cast<const double *>(pabyGpkg + 8);
431 1364690 : if (poHeader->bExtentHasXY)
432 : {
433 29255 : poHeader->MinX = padPtr[0];
434 29255 : poHeader->MaxX = padPtr[1];
435 29255 : poHeader->MinY = padPtr[2];
436 29255 : poHeader->MaxY = padPtr[3];
437 29255 : if (bSwap)
438 : {
439 0 : CPL_SWAPDOUBLE(&(poHeader->MinX));
440 0 : CPL_SWAPDOUBLE(&(poHeader->MaxX));
441 0 : CPL_SWAPDOUBLE(&(poHeader->MinY));
442 0 : CPL_SWAPDOUBLE(&(poHeader->MaxY));
443 : }
444 : }
445 1364690 : if (poHeader->bExtentHasZ)
446 : {
447 173 : poHeader->MinZ = padPtr[4];
448 173 : poHeader->MaxZ = padPtr[5];
449 173 : if (bSwap)
450 : {
451 0 : CPL_SWAPDOUBLE(&(poHeader->MinZ));
452 0 : CPL_SWAPDOUBLE(&(poHeader->MaxZ));
453 : }
454 : }
455 : #ifdef notdef
456 : if (poHeader->bExtentHasM)
457 : {
458 : poHeader->MinM = padPtr[(poHeader->bExtentHasZ) ? 6 : 4];
459 : poHeader->MaxM = padPtr[(poHeader->bExtentHasZ) ? 7 : 5];
460 : if (bSwap)
461 : {
462 : CPL_SWAPDOUBLE(&(poHeader->MinM));
463 : CPL_SWAPDOUBLE(&(poHeader->MaxM));
464 : }
465 : }
466 : #endif
467 :
468 : /* Header size in byte stream */
469 1364690 : poHeader->nHeaderLen = 8 + 8 * 2 * nEnvelopeDim;
470 :
471 : #ifdef DEBUG_VERBOSE
472 : std::string s;
473 : for (size_t i = poHeader->nHeaderLen; i < nGpkgLen; ++i)
474 : {
475 : s += CPLSPrintf("%02X ", pabyGpkg[i]);
476 : }
477 : CPLDebug("GPKG", "Bytes after GPKG header: %s", s.c_str());
478 : #endif
479 :
480 : // Workaround for a Spatialite bug. The CastToXYZ() function, when
481 : // called on an empty geometry, and EnableGpkgMode() is enabled,
482 : // returns an empty geometry, not tagged as such, but with an invalid
483 : // bounding box.
484 : // Cf https://github.com/OSGeo/gdal/issues/13557
485 1362290 : if (!poHeader->bEmpty && poHeader->bExtentHasXY &&
486 58329 : poHeader->nHeaderLen == 40 &&
487 29074 : poHeader->MinX == std::numeric_limits<double>::max() &&
488 28 : poHeader->MaxX == -std::numeric_limits<double>::max() &&
489 14 : poHeader->MinY == std::numeric_limits<double>::max() &&
490 2726980 : poHeader->MaxY == -std::numeric_limits<double>::max() &&
491 : // CastToXYZ(POLYGON EMPTY) returns just the GPKG header for some reason
492 4 : (nGpkgLen == 40 ||
493 : // CastToXYZ(LINESTRING EMPTY) returns a LINESTRING Z EMPTY
494 2 : (nGpkgLen == 49 &&
495 2 : memcmp(pabyGpkg + 40, "\x01\xEA\x03\x00\x00\x00\x00\x00\x00", 9) ==
496 2 : 0) ||
497 : // CastToXYZ(POINT EMPTY) returns a Point Z (NaN NaN 0)
498 : // CastToXYZ(POINT Z EMPTY) returns a Point Z (NaN NaN NaN)
499 2 : (nGpkgLen == 69 && memcmp(pabyGpkg + 40,
500 : "\x01"
501 : "\xE9\x03\x00\x00"
502 : "\x00\x00\x00\x00\x00\x00\xF8\x7F"
503 : "\x00\x00\x00\x00\x00\x00\xF8\x7F",
504 : 21) == 0)))
505 : {
506 14 : CPLDebugOnce("GPKG",
507 : "Work arounding a Spatialite bug with empty geometries");
508 14 : poHeader->bEmpty = true;
509 14 : poHeader->bExtentHasXY = false;
510 : }
511 :
512 1364690 : return OGRERR_NONE;
513 : }
514 :
515 26 : bool GPkgUpdateHeader(GByte *pabyGpkg, size_t nGpkgLen, int nSrsId, double MinX,
516 : double MaxX, double MinY, double MaxY, double MinZ,
517 : double MaxZ)
518 : {
519 26 : CPLAssert(nGpkgLen >= 8);
520 :
521 : /* Flags */
522 26 : const GByte byFlags = pabyGpkg[3];
523 26 : const auto eByteOrder = static_cast<OGRwkbByteOrder>(byFlags & 0x01);
524 26 : const OGRBoolean bSwap = OGR_SWAP(eByteOrder);
525 :
526 : /* SrsId */
527 26 : if (bSwap)
528 : {
529 0 : nSrsId = CPL_SWAP32(nSrsId);
530 : }
531 26 : memcpy(pabyGpkg + 4, &nSrsId, 4);
532 :
533 : /* Envelope */
534 26 : const int iEnvelope = (byFlags & (0x07 << 1)) >> 1;
535 26 : int nEnvelopeDim = 0;
536 26 : if (iEnvelope)
537 : {
538 23 : if (iEnvelope == 1)
539 : {
540 23 : nEnvelopeDim = 2; /* 2D envelope */
541 : }
542 0 : else if (iEnvelope == 2)
543 : {
544 0 : nEnvelopeDim = 3; /* 2D+Z envelope */
545 : }
546 0 : else if (iEnvelope == 3)
547 : {
548 0 : nEnvelopeDim = 3; /* 2D+M envelope */
549 : }
550 0 : else if (iEnvelope == 4)
551 : {
552 0 : nEnvelopeDim = 4; /* 2D+ZM envelope */
553 : }
554 : else
555 : {
556 0 : return false;
557 : }
558 : }
559 : else
560 : {
561 3 : return true;
562 : }
563 :
564 23 : if (nGpkgLen < static_cast<size_t>(8 + 8 * 2 * nEnvelopeDim))
565 : {
566 : // Not enough bytes
567 0 : return false;
568 : }
569 :
570 : /* Envelope */
571 23 : if (bSwap)
572 : {
573 0 : CPL_SWAPDOUBLE(&(MinX));
574 0 : CPL_SWAPDOUBLE(&(MaxX));
575 0 : CPL_SWAPDOUBLE(&(MinY));
576 0 : CPL_SWAPDOUBLE(&(MaxY));
577 0 : CPL_SWAPDOUBLE(&(MinZ));
578 0 : CPL_SWAPDOUBLE(&(MaxZ));
579 : }
580 :
581 23 : double *padPtr = reinterpret_cast<double *>(pabyGpkg + 8);
582 23 : memcpy(&padPtr[0], &MinX, sizeof(double));
583 23 : memcpy(&padPtr[1], &MaxX, sizeof(double));
584 23 : memcpy(&padPtr[2], &MinY, sizeof(double));
585 23 : memcpy(&padPtr[3], &MaxY, sizeof(double));
586 :
587 23 : if (iEnvelope == 2 || iEnvelope == 4)
588 : {
589 0 : memcpy(&padPtr[4], &MinZ, sizeof(double));
590 0 : memcpy(&padPtr[5], &MaxZ, sizeof(double));
591 : }
592 :
593 23 : return true;
594 : }
595 :
596 12347 : OGRGeometry *GPkgGeometryToOGR(const GByte *pabyGpkg, size_t nGpkgLen,
597 : OGRSpatialReference *poSrs)
598 : {
599 12347 : CPLAssert(pabyGpkg != nullptr);
600 :
601 : GPkgHeader oHeader;
602 :
603 : /* Read header */
604 12347 : OGRErr err = GPkgHeaderFromWKB(pabyGpkg, nGpkgLen, &oHeader);
605 12347 : if (err != OGRERR_NONE)
606 1 : return nullptr;
607 :
608 : /* WKB pointer */
609 12346 : const GByte *pabyWkb = pabyGpkg + oHeader.nHeaderLen;
610 12346 : size_t nWkbLen = nGpkgLen - oHeader.nHeaderLen;
611 :
612 : /* Parse WKB */
613 12346 : OGRGeometry *poGeom = nullptr;
614 12346 : err = OGRGeometryFactory::createFromWkb(pabyWkb, poSrs, &poGeom,
615 : static_cast<int>(nWkbLen));
616 12346 : if (err != OGRERR_NONE)
617 0 : return nullptr;
618 :
619 12346 : return poGeom;
620 : }
621 :
622 : /************************************************************************/
623 : /* OGRGeoPackageGetHeader() */
624 : /************************************************************************/
625 :
626 1286300 : bool OGRGeoPackageGetHeader(sqlite3_context * /*pContext*/, int /*argc*/,
627 : sqlite3_value **argv, GPkgHeader *psHeader,
628 : bool bNeedExtent, bool bNeedExtent3D, int iGeomIdx)
629 : {
630 :
631 : // Extent3D implies extent
632 1286300 : const bool bNeedAnyExtent{bNeedExtent || bNeedExtent3D};
633 :
634 1286300 : if (sqlite3_value_type(argv[iGeomIdx]) != SQLITE_BLOB)
635 : {
636 10 : memset(psHeader, 0, sizeof(*psHeader));
637 10 : return false;
638 : }
639 1286280 : const int nBLOBLen = sqlite3_value_bytes(argv[iGeomIdx]);
640 : const GByte *pabyBLOB =
641 1286280 : reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[iGeomIdx]));
642 :
643 1286280 : if (nBLOBLen < 8)
644 : {
645 6 : memset(psHeader, 0, sizeof(*psHeader));
646 6 : return false;
647 : }
648 1286280 : else if (GPkgHeaderFromWKB(pabyBLOB, nBLOBLen, psHeader) != OGRERR_NONE)
649 : {
650 10 : bool bEmpty = false;
651 10 : memset(psHeader, 0, sizeof(*psHeader));
652 10 : if (OGRSQLiteGetSpatialiteGeometryHeader(
653 : pabyBLOB, nBLOBLen, &(psHeader->iSrsId), nullptr, &bEmpty,
654 : &(psHeader->MinX), &(psHeader->MinY), &(psHeader->MaxX),
655 10 : &(psHeader->MaxY)) == OGRERR_NONE)
656 : {
657 9 : psHeader->bEmpty = bEmpty;
658 9 : psHeader->bExtentHasXY = !bEmpty;
659 9 : if (!bNeedExtent3D && !(bEmpty && bNeedAnyExtent))
660 9 : return true;
661 : }
662 :
663 1 : return false;
664 : }
665 :
666 1286270 : if (psHeader->bEmpty && bNeedAnyExtent)
667 : {
668 15 : return false;
669 : }
670 1286250 : else if (!psHeader->bExtentHasXY && bNeedExtent && !bNeedExtent3D)
671 : {
672 989429 : OGREnvelope sEnvelope;
673 989429 : if (OGRWKBGetBoundingBox(pabyBLOB + psHeader->nHeaderLen,
674 989429 : static_cast<size_t>(nBLOBLen) -
675 989429 : psHeader->nHeaderLen,
676 : sEnvelope))
677 : {
678 989429 : psHeader->MinX = sEnvelope.MinX;
679 989429 : psHeader->MaxX = sEnvelope.MaxX;
680 989429 : psHeader->MinY = sEnvelope.MinY;
681 989429 : psHeader->MaxY = sEnvelope.MaxY;
682 989429 : return true;
683 : }
684 0 : return false;
685 : }
686 296825 : else if (!psHeader->bExtentHasZ && bNeedExtent3D)
687 : {
688 17 : OGREnvelope3D sEnvelope3D;
689 17 : if (OGRWKBGetBoundingBox(pabyBLOB + psHeader->nHeaderLen,
690 17 : static_cast<size_t>(nBLOBLen) -
691 17 : psHeader->nHeaderLen,
692 : sEnvelope3D))
693 : {
694 17 : psHeader->MinX = sEnvelope3D.MinX;
695 17 : psHeader->MaxX = sEnvelope3D.MaxX;
696 17 : psHeader->MinY = sEnvelope3D.MinY;
697 17 : psHeader->MaxY = sEnvelope3D.MaxY;
698 17 : psHeader->MinZ = sEnvelope3D.MinZ;
699 17 : psHeader->MaxZ = sEnvelope3D.MaxZ;
700 17 : return true;
701 : }
702 0 : return false;
703 : }
704 296808 : return true;
705 : }
|