LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - ogrgeopackageutility.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 275 312 88.1 %
Date: 2025-12-29 15:50:47 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        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             : }

Generated by: LCOV version 1.14