LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - ogrgeopackageutility.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 261 298 87.6 %
Date: 2024-11-21 22:18:42 Functions: 8 8 100.0 %

          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        2857 : OGRwkbGeometryType GPkgGeometryTypeToWKB(const char *pszGpkgType, bool bHasZ,
      25             :                                          bool bHasM)
      26             : {
      27             :     OGRwkbGeometryType oType;
      28             : 
      29        2857 :     if (EQUAL("Geometry", pszGpkgType))
      30        1858 :         oType = wkbUnknown;
      31             :     /* The 1.0 spec is not completely clear on what should be used... */
      32         999 :     else if (EQUAL("GeomCollection", pszGpkgType) ||
      33         999 :              EQUAL("GeometryCollection", pszGpkgType))
      34           2 :         oType = wkbGeometryCollection;
      35             :     else
      36             :     {
      37         997 :         oType = OGRFromOGCGeomType(pszGpkgType);
      38         997 :         if (oType == wkbUnknown)
      39           3 :             oType = wkbNone;
      40             :     }
      41             : 
      42        2857 :     if ((oType != wkbNone) && bHasZ)
      43             :     {
      44          60 :         oType = wkbSetZ(oType);
      45             :     }
      46        2857 :     if ((oType != wkbNone) && bHasM)
      47             :     {
      48          13 :         oType = wkbSetM(oType);
      49             :     }
      50             : 
      51        2857 :     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        3842 : int GPkgFieldToOGR(const char *pszGpkgType, OGRFieldSubType &eSubType,
      60             :                    int &nMaxWidth)
      61             : {
      62        3842 :     eSubType = OFSTNone;
      63        3842 :     nMaxWidth = 0;
      64             : 
      65             :     /* Integer types */
      66        3842 :     if (STRNCASECMP("INT", pszGpkgType, 3) == 0)
      67             :     {
      68        1131 :         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        1131 :         return OFTInteger64;
      76             :     }
      77        2711 :     else if (EQUAL("MEDIUMINT", pszGpkgType))
      78         100 :         return OFTInteger;
      79        2611 :     else if (EQUAL("SMALLINT", pszGpkgType))
      80             :     {
      81          16 :         eSubType = OFSTInt16;
      82          16 :         return OFTInteger;
      83             :     }
      84        2595 :     else if (EQUAL("TINYINT", pszGpkgType))
      85           9 :         return OFTInteger;  // [-128, 127]
      86        2586 :     else if (EQUAL("BOOLEAN", pszGpkgType))
      87             :     {
      88          20 :         eSubType = OFSTBoolean;
      89          20 :         return OFTInteger;
      90             :     }
      91             : 
      92             :     /* Real types */
      93        2566 :     else if (EQUAL("FLOAT", pszGpkgType))
      94             :     {
      95          16 :         eSubType = OFSTFloat32;
      96          16 :         return OFTReal;
      97             :     }
      98        2550 :     else if (EQUAL("DOUBLE", pszGpkgType))
      99          53 :         return OFTReal;
     100        2497 :     else if (EQUAL("REAL", pszGpkgType))
     101         501 :         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        1996 :     else if (EQUAL("NUMERIC", pszGpkgType))
     106           2 :         return OFTReal;
     107             : 
     108             :     /* String/binary types */
     109        1994 :     else if (STRNCASECMP("TEXT", pszGpkgType, 4) == 0)
     110             :     {
     111        1162 :         if (pszGpkgType[4] == '(')
     112          80 :             nMaxWidth = atoi(pszGpkgType + 5);
     113        1082 :         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        1162 :         return OFTString;
     121             :     }
     122             : 
     123         832 :     else if (STRNCASECMP("BLOB", pszGpkgType, 4) == 0)
     124             :     {
     125          40 :         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          40 :         return OFTBinary;
     133             :     }
     134             : 
     135             :     /* Date types */
     136         792 :     else if (EQUAL("DATE", pszGpkgType))
     137          53 :         return OFTDate;
     138         739 :     else if (EQUAL("DATETIME", pszGpkgType))
     139          64 :         return OFTDateTime;
     140             : 
     141             :     /* Illegal! */
     142             :     else
     143             :     {
     144         675 :         if (GPkgGeometryTypeToWKB(pszGpkgType, false, false) == wkbNone)
     145             :         {
     146           3 :             CPLError(CE_Warning, CPLE_AppDefined,
     147             :                      "Field format '%s' not supported", pszGpkgType);
     148             :         }
     149         675 :         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        3726 : const char *GPkgFieldFromOGR(OGRFieldType eType, OGRFieldSubType eSubType,
     158             :                              int nMaxWidth)
     159             : {
     160        3726 :     switch (eType)
     161             :     {
     162         876 :         case OFTInteger:
     163             :         {
     164         876 :             if (eSubType == OFSTBoolean)
     165           8 :                 return "BOOLEAN";
     166         868 :             else if (eSubType == OFSTInt16)
     167           7 :                 return "SMALLINT";
     168             :             else
     169         861 :                 return "MEDIUMINT";
     170             :         }
     171          66 :         case OFTInteger64:
     172          66 :             return "INTEGER";
     173         119 :         case OFTReal:
     174             :         {
     175         119 :             if (eSubType == OFSTFloat32)
     176          12 :                 return "FLOAT";
     177             :             else
     178         107 :                 return "REAL";
     179             :         }
     180        2555 :         case OFTString:
     181             :         {
     182        2555 :             if (nMaxWidth > 0)
     183          49 :                 return CPLSPrintf("TEXT(%d)", nMaxWidth);
     184             :             else
     185        2506 :                 return "TEXT";
     186             :         }
     187          14 :         case OFTBinary:
     188          14 :             return "BLOB";
     189          38 :         case OFTDate:
     190          38 :             return "DATE";
     191          53 :         case OFTDateTime:
     192          53 :             return "DATETIME";
     193           5 :         default:
     194           5 :             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      251690 : GByte *GPkgGeometryFromOGR(const OGRGeometry *poGeometry, int iSrsId,
     229             :                            const OGRGeomCoordinateBinaryPrecision *psPrecision,
     230             :                            size_t *pnWkbLen)
     231             : {
     232      251690 :     CPLAssert(poGeometry != nullptr);
     233             : 
     234      251690 :     GByte byFlags = 0;
     235      251690 :     GByte byEnv = 1;
     236      251690 :     OGRwkbExportOptions wkbExportOptions;
     237      251690 :     if (psPrecision)
     238      251688 :         wkbExportOptions.sPrecision = *psPrecision;
     239      251690 :     wkbExportOptions.eByteOrder = static_cast<OGRwkbByteOrder>(CPL_IS_LSB);
     240             :     OGRErr err;
     241      251690 :     OGRBoolean bPoint = (wkbFlatten(poGeometry->getGeometryType()) == wkbPoint);
     242      251690 :     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      251690 :     int iDims = poGeometry->getCoordinateDimension();
     248             : 
     249             :     /* Header has 8 bytes for sure, and optional extra space for bounds */
     250      251690 :     size_t nHeaderLen = 2 + 1 + 1 + 4;
     251      251690 :     if (!bPoint && !bEmpty)
     252             :     {
     253        3220 :         nHeaderLen += 8 * 2 * iDims;
     254             :     }
     255             : 
     256             :     /* Total BLOB size is header + WKB size */
     257      251690 :     size_t nWkbLen = nHeaderLen + poGeometry->WkbSize();
     258      251690 :     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      251690 :     GByte *pabyWkb = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbLen));
     264      251690 :     if (!pabyWkb)
     265           0 :         return nullptr;
     266      251690 :     if (pnWkbLen)
     267      251690 :         *pnWkbLen = nWkbLen;
     268             : 
     269             :     /* Header Magic */
     270      251690 :     pabyWkb[0] = 0x47;
     271      251690 :     pabyWkb[1] = 0x50;
     272             : 
     273             :     /* GPKG BLOB Version */
     274      251690 :     pabyWkb[2] = 0;
     275             : 
     276             :     /* Extended? No. */
     277             : 
     278             :     /* Envelope dimensionality? */
     279             : 
     280             :     /* Don't write envelope for point type */
     281      251690 :     if (bPoint)
     282      248453 :         byEnv = 0;
     283             :     else
     284             :         /* 3D envelope for 3D data */
     285        3237 :         if (iDims == 3)
     286          29 :             byEnv = 2;
     287             :         /* 2D envelope otherwise */
     288             :         else
     289        3208 :             byEnv = 1;
     290             : 
     291             :     /* Empty? No envelope then. */
     292      251690 :     if (bEmpty)
     293             :     {
     294        1027 :         byEnv = 0;
     295             :         /* Set empty flag */
     296        1027 :         byFlags |= (1 << 4);
     297             :     }
     298             : 
     299             :     /* Set envelope flags */
     300      251690 :     byFlags |= (byEnv << 1);
     301             : 
     302             :     /* Byte order of header? */
     303             :     /* Use native endianness */
     304      251690 :     byFlags |= wkbExportOptions.eByteOrder;
     305             : 
     306             :     /* Write flags byte */
     307      251690 :     pabyWkb[3] = byFlags;
     308             : 
     309             :     /* Write srs_id */
     310      251690 :     memcpy(pabyWkb + 4, &iSrsId, 4);
     311             : 
     312             :     /* Write envelope */
     313      251690 :     if (!bEmpty && !bPoint)
     314             :     {
     315        3220 :         double *padPtr = reinterpret_cast<double *>(pabyWkb + 8);
     316        3220 :         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        3191 :             OGREnvelope oEnv;
     330        3191 :             poGeometry->getEnvelope(&oEnv);
     331        3191 :             padPtr[0] = oEnv.MinX;
     332        3191 :             padPtr[1] = oEnv.MaxX;
     333        3191 :             padPtr[2] = oEnv.MinY;
     334        3191 :             padPtr[3] = oEnv.MaxY;
     335             :         }
     336             :     }
     337             : 
     338      251690 :     GByte *pabyPtr = pabyWkb + nHeaderLen;
     339             : 
     340             :     /* Use the wkbVariantIso for ISO SQL/MM output (differs for 3d geometry) */
     341      251690 :     wkbExportOptions.eWkbVariant = wkbVariantIso;
     342      251690 :     err = poGeometry->exportToWkb(pabyPtr, &wkbExportOptions);
     343      251690 :     if (err != OGRERR_NONE)
     344             :     {
     345           0 :         CPLFree(pabyWkb);
     346           0 :         return nullptr;
     347             :     }
     348             : 
     349      251690 :     return pabyWkb;
     350             : }
     351             : 
     352     1314850 : OGRErr GPkgHeaderFromWKB(const GByte *pabyGpkg, size_t nGpkgLen,
     353             :                          GPkgHeader *poHeader)
     354             : {
     355     1314850 :     CPLAssert(pabyGpkg != nullptr);
     356     1314850 :     CPLAssert(poHeader != nullptr);
     357             : 
     358             :     /* Magic (match required) */
     359     1314850 :     if (nGpkgLen < 8 || pabyGpkg[0] != 0x47 || pabyGpkg[1] != 0x50 ||
     360     1314780 :         pabyGpkg[2] != 0) /* Version (only 0 supported at this time)*/
     361             :     {
     362          67 :         memset(poHeader, 0, sizeof(*poHeader));
     363          67 :         return OGRERR_FAILURE;
     364             :     }
     365             : 
     366             :     /* Flags */
     367     1314780 :     GByte byFlags = pabyGpkg[3];
     368     1314780 :     poHeader->bEmpty = (byFlags & (0x01 << 4)) >> 4;
     369     1314780 :     poHeader->bExtended = (byFlags & (0x01 << 5)) >> 5;
     370     1314780 :     poHeader->eByteOrder = static_cast<OGRwkbByteOrder>(byFlags & 0x01);
     371     1314780 :     poHeader->bExtentHasXY = false;
     372     1314780 :     poHeader->bExtentHasZ = false;
     373             : #ifdef notdef
     374             :     poHeader->bExtentHasM = false;
     375             : #endif
     376     1314780 :     OGRBoolean bSwap = OGR_SWAP(poHeader->eByteOrder);
     377             : 
     378             :     /* Envelope */
     379     1314780 :     int iEnvelope = (byFlags & (0x07 << 1)) >> 1;
     380     1314780 :     int nEnvelopeDim = 0;
     381     1314780 :     if (iEnvelope)
     382             :     {
     383       19104 :         poHeader->bExtentHasXY = true;
     384       19104 :         if (iEnvelope == 1)
     385             :         {
     386       18925 :             nEnvelopeDim = 2; /* 2D envelope */
     387             :         }
     388         179 :         else if (iEnvelope == 2)
     389             :         {
     390         165 :             poHeader->bExtentHasZ = true;
     391         165 :             nEnvelopeDim = 3; /* 2D+Z envelope */
     392             :         }
     393          14 :         else if (iEnvelope == 3)
     394             :         {
     395             : #ifdef notdef
     396             :             poHeader->bExtentHasM = true;
     397             : #endif
     398           7 :             nEnvelopeDim = 3; /* 2D+M envelope */
     399             :         }
     400           7 :         else if (iEnvelope == 4)
     401             :         {
     402           7 :             poHeader->bExtentHasZ = true;
     403             : #ifdef notdef
     404             :             poHeader->bExtentHasM = true;
     405             : #endif
     406           7 :             nEnvelopeDim = 4; /* 2D+ZM envelope */
     407             :         }
     408             :         else
     409             :         {
     410           0 :             return OGRERR_FAILURE;
     411             :         }
     412             :     }
     413             : 
     414             :     /* SrsId */
     415     1314780 :     int iSrsId = 0;
     416     1314780 :     memcpy(&iSrsId, pabyGpkg + 4, 4);
     417     1314780 :     if (bSwap)
     418             :     {
     419           0 :         iSrsId = CPL_SWAP32(iSrsId);
     420             :     }
     421     1314780 :     poHeader->iSrsId = iSrsId;
     422             : 
     423     1314780 :     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     1314780 :     const double *padPtr = reinterpret_cast<const double *>(pabyGpkg + 8);
     431     1314780 :     if (poHeader->bExtentHasXY)
     432             :     {
     433       19104 :         poHeader->MinX = padPtr[0];
     434       19104 :         poHeader->MaxX = padPtr[1];
     435       19104 :         poHeader->MinY = padPtr[2];
     436       19104 :         poHeader->MaxY = padPtr[3];
     437       19104 :         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     1314780 :     if (poHeader->bExtentHasZ)
     446             :     {
     447         172 :         poHeader->MinZ = padPtr[4];
     448         172 :         poHeader->MaxZ = padPtr[5];
     449         172 :         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     1314780 :     poHeader->nHeaderLen = 8 + 8 * 2 * nEnvelopeDim;
     470             : 
     471     1314780 :     return OGRERR_NONE;
     472             : }
     473             : 
     474          26 : bool GPkgUpdateHeader(GByte *pabyGpkg, size_t nGpkgLen, int nSrsId, double MinX,
     475             :                       double MaxX, double MinY, double MaxY, double MinZ,
     476             :                       double MaxZ)
     477             : {
     478          26 :     CPLAssert(nGpkgLen >= 8);
     479             : 
     480             :     /* Flags */
     481          26 :     const GByte byFlags = pabyGpkg[3];
     482          26 :     const auto eByteOrder = static_cast<OGRwkbByteOrder>(byFlags & 0x01);
     483          26 :     const OGRBoolean bSwap = OGR_SWAP(eByteOrder);
     484             : 
     485             :     /* SrsId */
     486          26 :     if (bSwap)
     487             :     {
     488           0 :         nSrsId = CPL_SWAP32(nSrsId);
     489             :     }
     490          26 :     memcpy(pabyGpkg + 4, &nSrsId, 4);
     491             : 
     492             :     /* Envelope */
     493          26 :     const int iEnvelope = (byFlags & (0x07 << 1)) >> 1;
     494          26 :     int nEnvelopeDim = 0;
     495          26 :     if (iEnvelope)
     496             :     {
     497          23 :         if (iEnvelope == 1)
     498             :         {
     499          23 :             nEnvelopeDim = 2; /* 2D envelope */
     500             :         }
     501           0 :         else if (iEnvelope == 2)
     502             :         {
     503           0 :             nEnvelopeDim = 3; /* 2D+Z envelope */
     504             :         }
     505           0 :         else if (iEnvelope == 3)
     506             :         {
     507           0 :             nEnvelopeDim = 3; /* 2D+M envelope */
     508             :         }
     509           0 :         else if (iEnvelope == 4)
     510             :         {
     511           0 :             nEnvelopeDim = 4; /* 2D+ZM envelope */
     512             :         }
     513             :         else
     514             :         {
     515           0 :             return false;
     516             :         }
     517             :     }
     518             :     else
     519             :     {
     520           3 :         return true;
     521             :     }
     522             : 
     523          23 :     if (nGpkgLen < static_cast<size_t>(8 + 8 * 2 * nEnvelopeDim))
     524             :     {
     525             :         // Not enough bytes
     526           0 :         return false;
     527             :     }
     528             : 
     529             :     /* Envelope */
     530          23 :     if (bSwap)
     531             :     {
     532           0 :         CPL_SWAPDOUBLE(&(MinX));
     533           0 :         CPL_SWAPDOUBLE(&(MaxX));
     534           0 :         CPL_SWAPDOUBLE(&(MinY));
     535           0 :         CPL_SWAPDOUBLE(&(MaxY));
     536           0 :         CPL_SWAPDOUBLE(&(MinZ));
     537           0 :         CPL_SWAPDOUBLE(&(MaxZ));
     538             :     }
     539             : 
     540          23 :     double *padPtr = reinterpret_cast<double *>(pabyGpkg + 8);
     541          23 :     memcpy(&padPtr[0], &MinX, sizeof(double));
     542          23 :     memcpy(&padPtr[1], &MaxX, sizeof(double));
     543          23 :     memcpy(&padPtr[2], &MinY, sizeof(double));
     544          23 :     memcpy(&padPtr[3], &MaxY, sizeof(double));
     545             : 
     546          23 :     if (iEnvelope == 2 || iEnvelope == 4)
     547             :     {
     548           0 :         memcpy(&padPtr[4], &MinZ, sizeof(double));
     549           0 :         memcpy(&padPtr[5], &MaxZ, sizeof(double));
     550             :     }
     551             : 
     552          23 :     return true;
     553             : }
     554             : 
     555       11984 : OGRGeometry *GPkgGeometryToOGR(const GByte *pabyGpkg, size_t nGpkgLen,
     556             :                                OGRSpatialReference *poSrs)
     557             : {
     558       11984 :     CPLAssert(pabyGpkg != nullptr);
     559             : 
     560             :     GPkgHeader oHeader;
     561             : 
     562             :     /* Read header */
     563       11984 :     OGRErr err = GPkgHeaderFromWKB(pabyGpkg, nGpkgLen, &oHeader);
     564       11984 :     if (err != OGRERR_NONE)
     565           3 :         return nullptr;
     566             : 
     567             :     /* WKB pointer */
     568       11981 :     const GByte *pabyWkb = pabyGpkg + oHeader.nHeaderLen;
     569       11981 :     size_t nWkbLen = nGpkgLen - oHeader.nHeaderLen;
     570             : 
     571             :     /* Parse WKB */
     572       11981 :     OGRGeometry *poGeom = nullptr;
     573       11981 :     err = OGRGeometryFactory::createFromWkb(pabyWkb, poSrs, &poGeom,
     574             :                                             static_cast<int>(nWkbLen));
     575       11981 :     if (err != OGRERR_NONE)
     576           0 :         return nullptr;
     577             : 
     578       11981 :     return poGeom;
     579             : }
     580             : 
     581             : /************************************************************************/
     582             : /*                     OGRGeoPackageGetHeader()                         */
     583             : /************************************************************************/
     584             : 
     585     1237010 : bool OGRGeoPackageGetHeader(sqlite3_context * /*pContext*/, int /*argc*/,
     586             :                             sqlite3_value **argv, GPkgHeader *psHeader,
     587             :                             bool bNeedExtent, bool bNeedExtent3D, int iGeomIdx)
     588             : {
     589             : 
     590             :     // Extent3D implies extent
     591     1237010 :     const bool bNeedAnyExtent{bNeedExtent || bNeedExtent3D};
     592             : 
     593     1237010 :     if (sqlite3_value_type(argv[iGeomIdx]) != SQLITE_BLOB)
     594             :     {
     595           8 :         memset(psHeader, 0, sizeof(*psHeader));
     596           8 :         return false;
     597             :     }
     598     1237000 :     const int nBLOBLen = sqlite3_value_bytes(argv[iGeomIdx]);
     599             :     const GByte *pabyBLOB =
     600     1237000 :         reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[iGeomIdx]));
     601             : 
     602     1237000 :     if (nBLOBLen < 8)
     603             :     {
     604           6 :         memset(psHeader, 0, sizeof(*psHeader));
     605           6 :         return false;
     606             :     }
     607     1236990 :     else if (GPkgHeaderFromWKB(pabyBLOB, nBLOBLen, psHeader) != OGRERR_NONE)
     608             :     {
     609          10 :         bool bEmpty = false;
     610          10 :         memset(psHeader, 0, sizeof(*psHeader));
     611          10 :         if (OGRSQLiteGetSpatialiteGeometryHeader(
     612             :                 pabyBLOB, nBLOBLen, &(psHeader->iSrsId), nullptr, &bEmpty,
     613             :                 &(psHeader->MinX), &(psHeader->MinY), &(psHeader->MaxX),
     614          10 :                 &(psHeader->MaxY)) == OGRERR_NONE)
     615             :         {
     616           9 :             psHeader->bEmpty = bEmpty;
     617           9 :             psHeader->bExtentHasXY = !bEmpty;
     618           9 :             if (!bNeedExtent3D && !(bEmpty && bNeedAnyExtent))
     619           9 :                 return true;
     620             :         }
     621             : 
     622           1 :         return false;
     623             :     }
     624             : 
     625     1236980 :     if (psHeader->bEmpty && bNeedAnyExtent)
     626             :     {
     627           1 :         return false;
     628             :     }
     629     1236980 :     else if (!psHeader->bExtentHasXY && bNeedExtent && !bNeedExtent3D)
     630             :     {
     631      957689 :         OGREnvelope sEnvelope;
     632      957689 :         if (OGRWKBGetBoundingBox(pabyBLOB + psHeader->nHeaderLen,
     633      957689 :                                  static_cast<size_t>(nBLOBLen) -
     634      957689 :                                      psHeader->nHeaderLen,
     635             :                                  sEnvelope))
     636             :         {
     637      957689 :             psHeader->MinX = sEnvelope.MinX;
     638      957689 :             psHeader->MaxX = sEnvelope.MaxX;
     639      957689 :             psHeader->MinY = sEnvelope.MinY;
     640      957689 :             psHeader->MaxY = sEnvelope.MaxY;
     641      957689 :             return true;
     642             :         }
     643           0 :         return false;
     644             :     }
     645      279294 :     else if (!psHeader->bExtentHasZ && bNeedExtent3D)
     646             :     {
     647          17 :         OGREnvelope3D sEnvelope3D;
     648          17 :         if (OGRWKBGetBoundingBox(pabyBLOB + psHeader->nHeaderLen,
     649          17 :                                  static_cast<size_t>(nBLOBLen) -
     650          17 :                                      psHeader->nHeaderLen,
     651             :                                  sEnvelope3D))
     652             :         {
     653          17 :             psHeader->MinX = sEnvelope3D.MinX;
     654          17 :             psHeader->MaxX = sEnvelope3D.MaxX;
     655          17 :             psHeader->MinY = sEnvelope3D.MinY;
     656          17 :             psHeader->MaxY = sEnvelope3D.MaxY;
     657          17 :             psHeader->MinZ = sEnvelope3D.MinZ;
     658          17 :             psHeader->MaxZ = sEnvelope3D.MaxZ;
     659          17 :             return true;
     660             :         }
     661           0 :         return false;
     662             :     }
     663      279277 :     return true;
     664             : }

Generated by: LCOV version 1.14