LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - ogrgeopackageutility.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 239 259 92.3 %
Date: 2024-05-02 00:41:30 Functions: 7 7 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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "ogrgeopackageutility.h"
      30             : #include "ogr_p.h"
      31             : #include "ogr_wkb.h"
      32             : #include "sqlite/ogrsqlitebase.h"
      33             : #include <limits>
      34             : 
      35             : /* Requirement 20: A GeoPackage SHALL store feature table geometries */
      36             : /* with the basic simple feature geometry types (Geometry, Point, */
      37             : /* LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, */
      38             : /* GeomCollection) */
      39             : /* http://opengis.github.io/geopackage/#geometry_types */
      40        2481 : OGRwkbGeometryType GPkgGeometryTypeToWKB(const char *pszGpkgType, bool bHasZ,
      41             :                                          bool bHasM)
      42             : {
      43             :     OGRwkbGeometryType oType;
      44             : 
      45        2481 :     if (EQUAL("Geometry", pszGpkgType))
      46        1525 :         oType = wkbUnknown;
      47             :     /* The 1.0 spec is not completely clear on what should be used... */
      48         956 :     else if (EQUAL("GeomCollection", pszGpkgType) ||
      49         956 :              EQUAL("GeometryCollection", pszGpkgType))
      50           2 :         oType = wkbGeometryCollection;
      51             :     else
      52             :     {
      53         954 :         oType = OGRFromOGCGeomType(pszGpkgType);
      54         954 :         if (oType == wkbUnknown)
      55           3 :             oType = wkbNone;
      56             :     }
      57             : 
      58        2481 :     if ((oType != wkbNone) && bHasZ)
      59             :     {
      60          54 :         oType = wkbSetZ(oType);
      61             :     }
      62        2481 :     if ((oType != wkbNone) && bHasM)
      63             :     {
      64          13 :         oType = wkbSetM(oType);
      65             :     }
      66             : 
      67        2481 :     return oType;
      68             : }
      69             : 
      70             : /* Requirement 5: The columns of tables in a GeoPackage SHALL only be */
      71             : /* declared using one of the data types specified in table GeoPackage */
      72             : /* Data Types. */
      73             : /* http://opengis.github.io/geopackage/#table_column_data_types */
      74             : // return a OGRFieldType value or OFTMaxType + 1
      75        3485 : int GPkgFieldToOGR(const char *pszGpkgType, OGRFieldSubType &eSubType,
      76             :                    int &nMaxWidth)
      77             : {
      78        3485 :     eSubType = OFSTNone;
      79        3485 :     nMaxWidth = 0;
      80             : 
      81             :     /* Integer types */
      82        3485 :     if (STRNCASECMP("INT", pszGpkgType, 3) == 0)
      83             :     {
      84        1024 :         if (!EQUAL("INT", pszGpkgType) && !EQUAL("INTEGER", pszGpkgType))
      85             :         {
      86           0 :             CPLError(CE_Warning, CPLE_AppDefined,
      87             :                      "Field format '%s' not supported. "
      88             :                      "Interpreted as INT",
      89             :                      pszGpkgType);
      90             :         }
      91        1024 :         return OFTInteger64;
      92             :     }
      93        2461 :     else if (EQUAL("MEDIUMINT", pszGpkgType))
      94          91 :         return OFTInteger;
      95        2370 :     else if (EQUAL("SMALLINT", pszGpkgType))
      96             :     {
      97          16 :         eSubType = OFSTInt16;
      98          16 :         return OFTInteger;
      99             :     }
     100        2354 :     else if (EQUAL("TINYINT", pszGpkgType))
     101           9 :         return OFTInteger;  // [-128, 127]
     102        2345 :     else if (EQUAL("BOOLEAN", pszGpkgType))
     103             :     {
     104          19 :         eSubType = OFSTBoolean;
     105          19 :         return OFTInteger;
     106             :     }
     107             : 
     108             :     /* Real types */
     109        2326 :     else if (EQUAL("FLOAT", pszGpkgType))
     110             :     {
     111          16 :         eSubType = OFSTFloat32;
     112          16 :         return OFTReal;
     113             :     }
     114        2310 :     else if (EQUAL("DOUBLE", pszGpkgType))
     115          53 :         return OFTReal;
     116        2257 :     else if (EQUAL("REAL", pszGpkgType))
     117         490 :         return OFTReal;
     118             : 
     119             :     // Only used normally in gpkg_data_column_constraints table, and we
     120             :     // need this only is reading it through ExecuteSQL()
     121        1767 :     else if (EQUAL("NUMERIC", pszGpkgType))
     122           2 :         return OFTReal;
     123             : 
     124             :     /* String/binary types */
     125        1765 :     else if (STRNCASECMP("TEXT", pszGpkgType, 4) == 0)
     126             :     {
     127        1060 :         if (pszGpkgType[4] == '(')
     128          72 :             nMaxWidth = atoi(pszGpkgType + 5);
     129         988 :         else if (pszGpkgType[4] != '\0')
     130             :         {
     131           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     132             :                      "Field format '%s' not supported. "
     133             :                      "Interpreted as TEXT",
     134             :                      pszGpkgType);
     135             :         }
     136        1060 :         return OFTString;
     137             :     }
     138             : 
     139         705 :     else if (STRNCASECMP("BLOB", pszGpkgType, 4) == 0)
     140             :     {
     141          40 :         if (pszGpkgType[4] != '(' && pszGpkgType[4] != '\0')
     142             :         {
     143           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     144             :                      "Field format '%s' not supported. "
     145             :                      "Interpreted as BLOB",
     146             :                      pszGpkgType);
     147             :         }
     148          40 :         return OFTBinary;
     149             :     }
     150             : 
     151             :     /* Date types */
     152         665 :     else if (EQUAL("DATE", pszGpkgType))
     153          29 :         return OFTDate;
     154         636 :     else if (EQUAL("DATETIME", pszGpkgType))
     155          51 :         return OFTDateTime;
     156             : 
     157             :     /* Illegal! */
     158             :     else
     159             :     {
     160         585 :         if (GPkgGeometryTypeToWKB(pszGpkgType, false, false) == wkbNone)
     161             :         {
     162           3 :             CPLError(CE_Warning, CPLE_AppDefined,
     163             :                      "Field format '%s' not supported", pszGpkgType);
     164             :         }
     165         585 :         return OFTMaxType + 1;
     166             :     }
     167             : }
     168             : 
     169             : /* Requirement 5: The columns of tables in a GeoPackage SHALL only be */
     170             : /* declared using one of the data types specified in table GeoPackage */
     171             : /* Data Types. */
     172             : /* http://opengis.github.io/geopackage/#table_column_data_types */
     173        1679 : const char *GPkgFieldFromOGR(OGRFieldType eType, OGRFieldSubType eSubType,
     174             :                              int nMaxWidth)
     175             : {
     176        1679 :     switch (eType)
     177             :     {
     178         870 :         case OFTInteger:
     179             :         {
     180         870 :             if (eSubType == OFSTBoolean)
     181           8 :                 return "BOOLEAN";
     182         862 :             else if (eSubType == OFSTInt16)
     183           7 :                 return "SMALLINT";
     184             :             else
     185         855 :                 return "MEDIUMINT";
     186             :         }
     187          64 :         case OFTInteger64:
     188          64 :             return "INTEGER";
     189         108 :         case OFTReal:
     190             :         {
     191         108 :             if (eSubType == OFSTFloat32)
     192          12 :                 return "FLOAT";
     193             :             else
     194          96 :                 return "REAL";
     195             :         }
     196         530 :         case OFTString:
     197             :         {
     198         530 :             if (nMaxWidth > 0)
     199          44 :                 return CPLSPrintf("TEXT(%d)", nMaxWidth);
     200             :             else
     201         486 :                 return "TEXT";
     202             :         }
     203          14 :         case OFTBinary:
     204          14 :             return "BLOB";
     205          38 :         case OFTDate:
     206          38 :             return "DATE";
     207          53 :         case OFTDateTime:
     208          53 :             return "DATETIME";
     209           2 :         default:
     210           2 :             return "TEXT";
     211             :     }
     212             : }
     213             : 
     214             : /* Requirement 19: A GeoPackage SHALL store feature table geometries
     215             :  *  with or without optional elevation (Z) and/or measure (M) values in SQL
     216             :  *  BLOBs using the Standard GeoPackageBinary format specified in table
     217             :  * GeoPackage SQL Geometry Binary Format and clause Geometry Encoding.
     218             :  *
     219             :  *  http://opengis.github.io/geopackage/#gpb_format
     220             :  *
     221             :  *   GeoPackageBinaryHeader {
     222             :  *     byte[2] magic = 0x4750;
     223             :  *     byte version;
     224             :  *     byte flags;
     225             :  *     int32 srs_id;
     226             :  *     double[] envelope;
     227             :  *    }
     228             :  *
     229             :  *   StandardGeoPackageBinary {
     230             :  *     GeoPackageBinaryHeader header;
     231             :  *     WKBGeometry geometry;
     232             :  *   }
     233             :  *
     234             :  *  Flags byte contents:
     235             :  *  Bit 7: Reserved for future
     236             :  *  Bit 6: Reserved for future
     237             :  *  Bit 5: Using Extended GPKG Binary?
     238             :  *  Bit 4: Geometry is Empty?
     239             :  *  Bit 3,2,1: Envelope contents (0 none, 1=X/Y, 2=X/Y/Z, 3=X/Y/M, 4=X/Y/Z/M)
     240             :  *  Bit 0: Byte order of header (0=big/XDR, 1=little/NDR)
     241             :  *
     242             :  */
     243             : 
     244      251353 : GByte *GPkgGeometryFromOGR(const OGRGeometry *poGeometry, int iSrsId,
     245             :                            const OGRGeomCoordinateBinaryPrecision *psPrecision,
     246             :                            size_t *pnWkbLen)
     247             : {
     248      251353 :     CPLAssert(poGeometry != nullptr);
     249             : 
     250      251353 :     GByte byFlags = 0;
     251      251353 :     GByte byEnv = 1;
     252      251353 :     OGRwkbExportOptions wkbExportOptions;
     253      251353 :     if (psPrecision)
     254      251325 :         wkbExportOptions.sPrecision = *psPrecision;
     255      251353 :     wkbExportOptions.eByteOrder = static_cast<OGRwkbByteOrder>(CPL_IS_LSB);
     256             :     OGRErr err;
     257      251353 :     OGRBoolean bPoint = (wkbFlatten(poGeometry->getGeometryType()) == wkbPoint);
     258      251353 :     OGRBoolean bEmpty = poGeometry->IsEmpty();
     259             :     /* We voluntarily use getCoordinateDimension() so as to get only 2 for
     260             :      * XY/XYM */
     261             :     /* and 3 for XYZ/XYZM as we currently don't write envelopes with M extent.
     262             :      */
     263      251353 :     int iDims = poGeometry->getCoordinateDimension();
     264             : 
     265             :     /* Header has 8 bytes for sure, and optional extra space for bounds */
     266      251353 :     size_t nHeaderLen = 2 + 1 + 1 + 4;
     267      251353 :     if (!bPoint && !bEmpty)
     268             :     {
     269        2884 :         nHeaderLen += 8 * 2 * iDims;
     270             :     }
     271             : 
     272             :     /* Total BLOB size is header + WKB size */
     273      251353 :     size_t nWkbLen = nHeaderLen + poGeometry->WkbSize();
     274      251353 :     if (nWkbLen > static_cast<size_t>(std::numeric_limits<int>::max()))
     275             :     {
     276           0 :         CPLError(CE_Failure, CPLE_NotSupported, "too big geometry blob");
     277           0 :         return nullptr;
     278             :     }
     279      251353 :     GByte *pabyWkb = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbLen));
     280      251353 :     if (!pabyWkb)
     281           0 :         return nullptr;
     282      251353 :     if (pnWkbLen)
     283      251353 :         *pnWkbLen = nWkbLen;
     284             : 
     285             :     /* Header Magic */
     286      251353 :     pabyWkb[0] = 0x47;
     287      251353 :     pabyWkb[1] = 0x50;
     288             : 
     289             :     /* GPKG BLOB Version */
     290      251353 :     pabyWkb[2] = 0;
     291             : 
     292             :     /* Extended? No. */
     293             : 
     294             :     /* Envelope dimensionality? */
     295             : 
     296             :     /* Don't write envelope for point type */
     297      251353 :     if (bPoint)
     298      248452 :         byEnv = 0;
     299             :     else
     300             :         /* 3D envelope for 3D data */
     301        2901 :         if (iDims == 3)
     302          27 :             byEnv = 2;
     303             :         /* 2D envelope otherwise */
     304             :         else
     305        2874 :             byEnv = 1;
     306             : 
     307             :     /* Empty? No envelope then. */
     308      251353 :     if (bEmpty)
     309             :     {
     310        1027 :         byEnv = 0;
     311             :         /* Set empty flag */
     312        1027 :         byFlags |= (1 << 4);
     313             :     }
     314             : 
     315             :     /* Set envelope flags */
     316      251353 :     byFlags |= (byEnv << 1);
     317             : 
     318             :     /* Byte order of header? */
     319             :     /* Use native endianness */
     320      251353 :     byFlags |= wkbExportOptions.eByteOrder;
     321             : 
     322             :     /* Write flags byte */
     323      251353 :     pabyWkb[3] = byFlags;
     324             : 
     325             :     /* Write srs_id */
     326      251353 :     memcpy(pabyWkb + 4, &iSrsId, 4);
     327             : 
     328             :     /* Write envelope */
     329      251353 :     if (!bEmpty && !bPoint)
     330             :     {
     331        2884 :         double *padPtr = reinterpret_cast<double *>(pabyWkb + 8);
     332        2884 :         if (iDims == 3)
     333             :         {
     334          27 :             OGREnvelope3D oEnv3d;
     335          27 :             poGeometry->getEnvelope(&oEnv3d);
     336          27 :             padPtr[0] = oEnv3d.MinX;
     337          27 :             padPtr[1] = oEnv3d.MaxX;
     338          27 :             padPtr[2] = oEnv3d.MinY;
     339          27 :             padPtr[3] = oEnv3d.MaxY;
     340          27 :             padPtr[4] = oEnv3d.MinZ;
     341          27 :             padPtr[5] = oEnv3d.MaxZ;
     342             :         }
     343             :         else
     344             :         {
     345        2857 :             OGREnvelope oEnv;
     346        2857 :             poGeometry->getEnvelope(&oEnv);
     347        2857 :             padPtr[0] = oEnv.MinX;
     348        2857 :             padPtr[1] = oEnv.MaxX;
     349        2857 :             padPtr[2] = oEnv.MinY;
     350        2857 :             padPtr[3] = oEnv.MaxY;
     351             :         }
     352             :     }
     353             : 
     354      251353 :     GByte *pabyPtr = pabyWkb + nHeaderLen;
     355             : 
     356             :     /* Use the wkbVariantIso for ISO SQL/MM output (differs for 3d geometry) */
     357      251353 :     wkbExportOptions.eWkbVariant = wkbVariantIso;
     358      251353 :     err = poGeometry->exportToWkb(pabyPtr, &wkbExportOptions);
     359      251353 :     if (err != OGRERR_NONE)
     360             :     {
     361           0 :         CPLFree(pabyWkb);
     362           0 :         return nullptr;
     363             :     }
     364             : 
     365      251353 :     return pabyWkb;
     366             : }
     367             : 
     368     1312680 : OGRErr GPkgHeaderFromWKB(const GByte *pabyGpkg, size_t nGpkgLen,
     369             :                          GPkgHeader *poHeader)
     370             : {
     371     1312680 :     CPLAssert(pabyGpkg != nullptr);
     372     1312680 :     CPLAssert(poHeader != nullptr);
     373             : 
     374             :     /* Magic (match required) */
     375     1312680 :     if (nGpkgLen < 8 || pabyGpkg[0] != 0x47 || pabyGpkg[1] != 0x50 ||
     376     1312620 :         pabyGpkg[2] != 0) /* Version (only 0 supported at this time)*/
     377             :     {
     378          67 :         memset(poHeader, 0, sizeof(*poHeader));
     379          67 :         return OGRERR_FAILURE;
     380             :     }
     381             : 
     382             :     /* Flags */
     383     1312620 :     GByte byFlags = pabyGpkg[3];
     384     1312620 :     poHeader->bEmpty = (byFlags & (0x01 << 4)) >> 4;
     385     1312620 :     poHeader->bExtended = (byFlags & (0x01 << 5)) >> 5;
     386     1312620 :     poHeader->eByteOrder = static_cast<OGRwkbByteOrder>(byFlags & 0x01);
     387     1312620 :     poHeader->bExtentHasXY = false;
     388     1312620 :     poHeader->bExtentHasZ = false;
     389             : #ifdef notdef
     390             :     poHeader->bExtentHasM = false;
     391             : #endif
     392     1312620 :     OGRBoolean bSwap = OGR_SWAP(poHeader->eByteOrder);
     393             : 
     394             :     /* Envelope */
     395     1312620 :     int iEnvelope = (byFlags & (0x07 << 1)) >> 1;
     396     1312620 :     int nEnvelopeDim = 0;
     397     1312620 :     if (iEnvelope)
     398             :     {
     399       16968 :         poHeader->bExtentHasXY = true;
     400       16968 :         if (iEnvelope == 1)
     401             :         {
     402       16799 :             nEnvelopeDim = 2; /* 2D envelope */
     403             :         }
     404         169 :         else if (iEnvelope == 2)
     405             :         {
     406         155 :             poHeader->bExtentHasZ = true;
     407         155 :             nEnvelopeDim = 3; /* 2D+Z envelope */
     408             :         }
     409          14 :         else if (iEnvelope == 3)
     410             :         {
     411             : #ifdef notdef
     412             :             poHeader->bExtentHasM = true;
     413             : #endif
     414           7 :             nEnvelopeDim = 3; /* 2D+M envelope */
     415             :         }
     416           7 :         else if (iEnvelope == 4)
     417             :         {
     418           7 :             poHeader->bExtentHasZ = true;
     419             : #ifdef notdef
     420             :             poHeader->bExtentHasM = true;
     421             : #endif
     422           7 :             nEnvelopeDim = 4; /* 2D+ZM envelope */
     423             :         }
     424             :         else
     425             :         {
     426           0 :             return OGRERR_FAILURE;
     427             :         }
     428             :     }
     429             : 
     430             :     /* SrsId */
     431     1312620 :     int iSrsId = 0;
     432     1312620 :     memcpy(&iSrsId, pabyGpkg + 4, 4);
     433     1312620 :     if (bSwap)
     434             :     {
     435           0 :         iSrsId = CPL_SWAP32(iSrsId);
     436             :     }
     437     1312620 :     poHeader->iSrsId = iSrsId;
     438             : 
     439     1312620 :     if (nGpkgLen < static_cast<size_t>(8 + 8 * 2 * nEnvelopeDim))
     440             :     {
     441             :         // Not enough bytes
     442           0 :         return OGRERR_FAILURE;
     443             :     }
     444             : 
     445             :     /* Envelope */
     446     1312620 :     const double *padPtr = reinterpret_cast<const double *>(pabyGpkg + 8);
     447     1312620 :     if (poHeader->bExtentHasXY)
     448             :     {
     449       16968 :         poHeader->MinX = padPtr[0];
     450       16968 :         poHeader->MaxX = padPtr[1];
     451       16968 :         poHeader->MinY = padPtr[2];
     452       16968 :         poHeader->MaxY = padPtr[3];
     453       16968 :         if (bSwap)
     454             :         {
     455           0 :             CPL_SWAPDOUBLE(&(poHeader->MinX));
     456           0 :             CPL_SWAPDOUBLE(&(poHeader->MaxX));
     457           0 :             CPL_SWAPDOUBLE(&(poHeader->MinY));
     458           0 :             CPL_SWAPDOUBLE(&(poHeader->MaxY));
     459             :         }
     460             :     }
     461     1312620 :     if (poHeader->bExtentHasZ)
     462             :     {
     463         162 :         poHeader->MinZ = padPtr[4];
     464         162 :         poHeader->MaxZ = padPtr[5];
     465         162 :         if (bSwap)
     466             :         {
     467           0 :             CPL_SWAPDOUBLE(&(poHeader->MinZ));
     468           0 :             CPL_SWAPDOUBLE(&(poHeader->MaxZ));
     469             :         }
     470             :     }
     471             : #ifdef notdef
     472             :     if (poHeader->bExtentHasM)
     473             :     {
     474             :         poHeader->MinM = padPtr[(poHeader->bExtentHasZ) ? 6 : 4];
     475             :         poHeader->MaxM = padPtr[(poHeader->bExtentHasZ) ? 7 : 5];
     476             :         if (bSwap)
     477             :         {
     478             :             CPL_SWAPDOUBLE(&(poHeader->MinM));
     479             :             CPL_SWAPDOUBLE(&(poHeader->MaxM));
     480             :         }
     481             :     }
     482             : #endif
     483             : 
     484             :     /* Header size in byte stream */
     485     1312620 :     poHeader->nHeaderLen = 8 + 8 * 2 * nEnvelopeDim;
     486             : 
     487     1312620 :     return OGRERR_NONE;
     488             : }
     489             : 
     490       21738 : OGRGeometry *GPkgGeometryToOGR(const GByte *pabyGpkg, size_t nGpkgLen,
     491             :                                OGRSpatialReference *poSrs)
     492             : {
     493       21738 :     CPLAssert(pabyGpkg != nullptr);
     494             : 
     495             :     GPkgHeader oHeader;
     496             : 
     497             :     /* Read header */
     498       21738 :     OGRErr err = GPkgHeaderFromWKB(pabyGpkg, nGpkgLen, &oHeader);
     499       21738 :     if (err != OGRERR_NONE)
     500           3 :         return nullptr;
     501             : 
     502             :     /* WKB pointer */
     503       21735 :     const GByte *pabyWkb = pabyGpkg + oHeader.nHeaderLen;
     504       21735 :     size_t nWkbLen = nGpkgLen - oHeader.nHeaderLen;
     505             : 
     506             :     /* Parse WKB */
     507       21735 :     OGRGeometry *poGeom = nullptr;
     508       21735 :     err = OGRGeometryFactory::createFromWkb(pabyWkb, poSrs, &poGeom,
     509             :                                             static_cast<int>(nWkbLen));
     510       21735 :     if (err != OGRERR_NONE)
     511           0 :         return nullptr;
     512             : 
     513       21735 :     return poGeom;
     514             : }
     515             : 
     516             : /************************************************************************/
     517             : /*                     OGRGeoPackageGetHeader()                         */
     518             : /************************************************************************/
     519             : 
     520     1235110 : bool OGRGeoPackageGetHeader(sqlite3_context * /*pContext*/, int /*argc*/,
     521             :                             sqlite3_value **argv, GPkgHeader *psHeader,
     522             :                             bool bNeedExtent, bool bNeedExtent3D, int iGeomIdx)
     523             : {
     524             : 
     525             :     // Extent3D implies extent
     526     1235110 :     const bool bNeedAnyExtent{bNeedExtent || bNeedExtent3D};
     527             : 
     528     1235110 :     if (sqlite3_value_type(argv[iGeomIdx]) != SQLITE_BLOB)
     529             :     {
     530           8 :         memset(psHeader, 0, sizeof(*psHeader));
     531           8 :         return false;
     532             :     }
     533     1235100 :     const int nBLOBLen = sqlite3_value_bytes(argv[iGeomIdx]);
     534             :     const GByte *pabyBLOB =
     535     1235100 :         reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[iGeomIdx]));
     536             : 
     537     1235100 :     if (nBLOBLen < 8)
     538             :     {
     539           4 :         memset(psHeader, 0, sizeof(*psHeader));
     540           4 :         return false;
     541             :     }
     542     1235100 :     else if (GPkgHeaderFromWKB(pabyBLOB, nBLOBLen, psHeader) != OGRERR_NONE)
     543             :     {
     544          10 :         bool bEmpty = false;
     545          10 :         memset(psHeader, 0, sizeof(*psHeader));
     546          10 :         if (OGRSQLiteGetSpatialiteGeometryHeader(
     547             :                 pabyBLOB, nBLOBLen, &(psHeader->iSrsId), nullptr, &bEmpty,
     548             :                 &(psHeader->MinX), &(psHeader->MinY), &(psHeader->MaxX),
     549          10 :                 &(psHeader->MaxY)) == OGRERR_NONE)
     550             :         {
     551           9 :             psHeader->bEmpty = bEmpty;
     552           9 :             psHeader->bExtentHasXY = !bEmpty;
     553           9 :             if (!bNeedExtent3D && !(bEmpty && bNeedAnyExtent))
     554           9 :                 return true;
     555             :         }
     556             : 
     557           1 :         return false;
     558             :     }
     559             : 
     560     1235090 :     if (psHeader->bEmpty && bNeedAnyExtent)
     561             :     {
     562           1 :         return false;
     563             :     }
     564     1235090 :     else if (!psHeader->bExtentHasXY && bNeedExtent && !bNeedExtent3D)
     565             :     {
     566      957673 :         OGREnvelope sEnvelope;
     567      957673 :         if (OGRWKBGetBoundingBox(pabyBLOB + psHeader->nHeaderLen,
     568      957673 :                                  static_cast<size_t>(nBLOBLen) -
     569      957673 :                                      psHeader->nHeaderLen,
     570             :                                  sEnvelope))
     571             :         {
     572      957673 :             psHeader->MinX = sEnvelope.MinX;
     573      957673 :             psHeader->MaxX = sEnvelope.MaxX;
     574      957673 :             psHeader->MinY = sEnvelope.MinY;
     575      957673 :             psHeader->MaxY = sEnvelope.MaxY;
     576      957673 :             return true;
     577             :         }
     578           0 :         return false;
     579             :     }
     580      277414 :     else if (!psHeader->bExtentHasZ && bNeedExtent3D)
     581             :     {
     582          17 :         OGREnvelope3D sEnvelope3D;
     583          17 :         if (OGRWKBGetBoundingBox(pabyBLOB + psHeader->nHeaderLen,
     584          17 :                                  static_cast<size_t>(nBLOBLen) -
     585          17 :                                      psHeader->nHeaderLen,
     586             :                                  sEnvelope3D))
     587             :         {
     588          17 :             psHeader->MinX = sEnvelope3D.MinX;
     589          17 :             psHeader->MaxX = sEnvelope3D.MaxX;
     590          17 :             psHeader->MinY = sEnvelope3D.MinY;
     591          17 :             psHeader->MaxY = sEnvelope3D.MaxY;
     592          17 :             psHeader->MinZ = sEnvelope3D.MinZ;
     593          17 :             psHeader->MaxZ = sEnvelope3D.MaxZ;
     594          17 :             return true;
     595             :         }
     596           0 :         return false;
     597             :     }
     598      277397 :     return true;
     599             : }

Generated by: LCOV version 1.14