LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pmtiles - ogrpmtilesvectorlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 268 283 94.7 %
Date: 2025-09-10 17:48:50 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implementation of PMTiles
       5             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, Planet Labs
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_pmtiles.h"
      14             : 
      15             : #include "mvtutils.h"
      16             : 
      17             : #include <algorithm>
      18             : #include <time.h>
      19             : 
      20             : /************************************************************************/
      21             : /*                        OGRPMTilesVectorLayer()                       */
      22             : /************************************************************************/
      23             : 
      24          40 : OGRPMTilesVectorLayer::OGRPMTilesVectorLayer(
      25             :     OGRPMTilesDataset *poDS, const char *pszLayerName,
      26             :     const CPLJSONObject &oFields, const CPLJSONArray &oAttributesFromTileStats,
      27             :     bool bJsonField, double dfMinX, double dfMinY, double dfMaxX, double dfMaxY,
      28             :     OGRwkbGeometryType eGeomType, int nZoomLevel,
      29          40 :     bool bZoomLevelFromSpatialFilter)
      30          40 :     : m_poDS(poDS), m_poFeatureDefn(new OGRFeatureDefn(pszLayerName)),
      31          80 :       m_bJsonField(bJsonField)
      32             : {
      33          40 :     SetDescription(pszLayerName);
      34          40 :     m_poFeatureDefn->SetGeomType(eGeomType);
      35          40 :     OGRSpatialReference *poSRS = new OGRSpatialReference();
      36          40 :     poSRS->importFromEPSG(3857);
      37          40 :     m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
      38          40 :     poSRS->Release();
      39          40 :     m_poFeatureDefn->Reference();
      40             : 
      41          40 :     if (m_bJsonField)
      42             :     {
      43           2 :         OGRFieldDefn oFieldDefnId("mvt_id", OFTInteger64);
      44           1 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefnId);
      45             :     }
      46             :     else
      47             :     {
      48          39 :         OGRMVTInitFields(m_poFeatureDefn, oFields, oAttributesFromTileStats);
      49             :     }
      50             : 
      51          40 :     m_sExtent.MinX = dfMinX;
      52          40 :     m_sExtent.MinY = dfMinY;
      53          40 :     m_sExtent.MaxX = dfMaxX;
      54          40 :     m_sExtent.MaxY = dfMaxY;
      55             : 
      56          40 :     m_nZoomLevel = nZoomLevel;
      57          40 :     m_bZoomLevelAuto = bZoomLevelFromSpatialFilter;
      58          40 :     OGRPMTilesVectorLayer::SetSpatialFilter(nullptr);
      59             : 
      60             :     // If the metadata contains an empty fields object, this may be a sign
      61             :     // that it doesn't know the schema. In that case check if a tile has
      62             :     // attributes, and in that case create a json field.
      63          40 :     if (!m_bJsonField && oFields.IsValid() && oFields.GetChildren().empty())
      64             :     {
      65           1 :         m_bJsonField = true;
      66           2 :         auto poSrcFeature = GetNextSrcFeature();
      67           1 :         m_bJsonField = false;
      68             : 
      69           1 :         if (poSrcFeature)
      70             :         {
      71             :             // There is at least the mvt_id field
      72           1 :             if (poSrcFeature->GetFieldCount() > 1)
      73             :             {
      74           0 :                 m_bJsonField = true;
      75             :             }
      76             :         }
      77           1 :         OGRPMTilesVectorLayer::ResetReading();
      78             :     }
      79             : 
      80          40 :     if (m_bJsonField)
      81             :     {
      82           2 :         OGRFieldDefn oFieldDefn("json", OFTString);
      83           1 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
      84             :     }
      85          40 : }
      86             : 
      87             : /************************************************************************/
      88             : /*                       ~OGRPMTilesVectorLayer()                       */
      89             : /************************************************************************/
      90             : 
      91          80 : OGRPMTilesVectorLayer::~OGRPMTilesVectorLayer()
      92             : {
      93          40 :     m_poFeatureDefn->Release();
      94          80 : }
      95             : 
      96             : /************************************************************************/
      97             : /*                          ResetReading()                              */
      98             : /************************************************************************/
      99             : 
     100         401 : void OGRPMTilesVectorLayer::ResetReading()
     101             : {
     102         401 :     m_poTileDS.reset();
     103         401 :     m_poTileLayer = nullptr;
     104         401 :     m_poTileIterator.reset();
     105         401 : }
     106             : 
     107             : /************************************************************************/
     108             : /*                      GuessGeometryType()                             */
     109             : /************************************************************************/
     110             : 
     111             : /* static */
     112           2 : OGRwkbGeometryType OGRPMTilesVectorLayer::GuessGeometryType(
     113             :     OGRPMTilesDataset *poDS, const char *pszLayerName, int nZoomLevel)
     114             : {
     115           4 :     OGRPMTilesTileIterator oIterator(poDS, nZoomLevel);
     116             : 
     117           2 :     const char *const apszAllowedDrivers[] = {"MVT", nullptr};
     118           4 :     CPLStringList aosOpenOptions;
     119             :     aosOpenOptions.SetNameValue("METADATA_FILE",
     120           2 :                                 poDS->GetMetadataFilename().c_str());
     121           4 :     std::string osTileData;
     122           2 :     bool bFirst = true;
     123           2 :     OGRwkbGeometryType eGeomType = wkbUnknown;
     124             :     time_t nStart;
     125           2 :     time(&nStart);
     126             :     while (true)
     127             :     {
     128           5 :         uint32_t nRunLength = 0;
     129           5 :         const auto sTile = oIterator.GetNextTile(&nRunLength);
     130           5 :         if (sTile.offset == 0)
     131             :         {
     132           2 :             break;
     133             :         }
     134             : 
     135           3 :         const auto *posStr = poDS->ReadTileData(sTile.offset, sTile.length);
     136           3 :         if (!posStr)
     137             :         {
     138           0 :             continue;
     139             :         }
     140           3 :         osTileData = *posStr;
     141             : 
     142             :         const std::string osTmpFilename = VSIMemGenerateHiddenFilename(
     143           3 :             CPLSPrintf("pmtiles_%u_%u.pbf", sTile.x, sTile.y));
     144           3 :         VSIFCloseL(VSIFileFromMemBuffer(
     145           3 :             osTmpFilename.c_str(), reinterpret_cast<GByte *>(&osTileData[0]),
     146           3 :             osTileData.size(), false));
     147             : 
     148             :         auto poTileDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     149           3 :             ("MVT:" + osTmpFilename).c_str(), GDAL_OF_VECTOR | GDAL_OF_INTERNAL,
     150           6 :             apszAllowedDrivers, aosOpenOptions.List(), nullptr));
     151           3 :         if (poTileDS)
     152             :         {
     153           3 :             auto poTileLayer = poTileDS->GetLayerByName(pszLayerName);
     154           3 :             if (poTileLayer)
     155             :             {
     156           3 :                 if (bFirst)
     157             :                 {
     158           2 :                     eGeomType = poTileLayer->GetGeomType();
     159           2 :                     if (eGeomType != wkbUnknown)
     160           2 :                         bFirst = false;
     161             :                 }
     162           1 :                 else if (eGeomType != poTileLayer->GetGeomType())
     163             :                 {
     164           0 :                     VSIUnlink(osTmpFilename.c_str());
     165           0 :                     return wkbUnknown;
     166             :                 }
     167           3 :                 if (nRunLength > 1)
     168           1 :                     oIterator.SkipRunLength();
     169             :             }
     170             :         }
     171           3 :         VSIUnlink(osTmpFilename.c_str());
     172             : 
     173             :         // Browse through tiles no longer than 1 sec
     174             :         time_t nNow;
     175           3 :         time(&nNow);
     176           3 :         if (nNow - nStart > 1)
     177           0 :             break;
     178           3 :     }
     179             : 
     180           2 :     return eGeomType;
     181             : }
     182             : 
     183             : /************************************************************************/
     184             : /*                    GetTotalFeatureCount()                            */
     185             : /************************************************************************/
     186             : 
     187           8 : GIntBig OGRPMTilesVectorLayer::GetTotalFeatureCount() const
     188             : {
     189          16 :     OGRPMTilesTileIterator oIterator(m_poDS, m_nZoomLevel);
     190             : 
     191           8 :     GIntBig nFeatureCount = 0;
     192           8 :     const char *const apszAllowedDrivers[] = {"MVT", nullptr};
     193          16 :     CPLStringList aosOpenOptions;
     194             :     aosOpenOptions.SetNameValue("METADATA_FILE",
     195           8 :                                 m_poDS->GetMetadataFilename().c_str());
     196           8 :     std::string osTileData;
     197             :     while (true)
     198             :     {
     199          23 :         uint32_t nRunLength = 0;
     200          23 :         const auto sTile = oIterator.GetNextTile(&nRunLength);
     201          23 :         if (sTile.offset == 0)
     202             :         {
     203           8 :             break;
     204             :         }
     205             : 
     206          15 :         const auto *posStr = m_poDS->ReadTileData(sTile.offset, sTile.length);
     207          15 :         if (!posStr)
     208             :         {
     209           0 :             continue;
     210             :         }
     211          15 :         osTileData = *posStr;
     212             : 
     213             :         const std::string osTmpFilename = VSIMemGenerateHiddenFilename(
     214          30 :             CPLSPrintf("pmtiles_%u_%u_getfeaturecount.pbf", sTile.x, sTile.y));
     215          15 :         VSIFCloseL(VSIFileFromMemBuffer(
     216          15 :             osTmpFilename.c_str(), reinterpret_cast<GByte *>(&osTileData[0]),
     217          15 :             osTileData.size(), false));
     218             : 
     219             :         auto poTileDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     220          15 :             ("MVT:" + osTmpFilename).c_str(), GDAL_OF_VECTOR | GDAL_OF_INTERNAL,
     221          45 :             apszAllowedDrivers, aosOpenOptions.List(), nullptr));
     222          15 :         if (poTileDS)
     223             :         {
     224          15 :             auto poTileLayer = poTileDS->GetLayerByName(GetDescription());
     225          15 :             if (poTileLayer)
     226             :             {
     227             :                 const GIntBig nTileFeatureCount =
     228          15 :                     poTileLayer->GetFeatureCount();
     229          15 :                 nFeatureCount += nRunLength * nTileFeatureCount;
     230          15 :                 if (nRunLength > 1)
     231           1 :                     oIterator.SkipRunLength();
     232             :             }
     233             :         }
     234          15 :         VSIUnlink(osTmpFilename.c_str());
     235          15 :     }
     236             : 
     237          16 :     return nFeatureCount;
     238             : }
     239             : 
     240             : /************************************************************************/
     241             : /*                         GetFeatureCount()                            */
     242             : /************************************************************************/
     243             : 
     244          70 : GIntBig OGRPMTilesVectorLayer::GetFeatureCount(int bForce)
     245             : {
     246          70 :     if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
     247             :     {
     248          32 :         if (m_nFeatureCount < 0)
     249             :         {
     250           8 :             m_nFeatureCount = GetTotalFeatureCount();
     251             :         }
     252          32 :         return m_nFeatureCount;
     253             :     }
     254          38 :     return OGRLayer::GetFeatureCount(bForce);
     255             : }
     256             : 
     257             : /************************************************************************/
     258             : /*                           GetFeature()                               */
     259             : /************************************************************************/
     260             : 
     261          35 : OGRFeature *OGRPMTilesVectorLayer::GetFeature(GIntBig nFID)
     262             : {
     263          35 :     if (nFID < 0)
     264           4 :         return nullptr;
     265          31 :     const int nZ = m_nZoomLevel;
     266          31 :     const int nX = static_cast<int>(nFID & ((1 << nZ) - 1));
     267          31 :     const int nY = static_cast<int>((nFID >> nZ) & ((1 << nZ) - 1));
     268          31 :     const GIntBig nTileFID = nFID >> (2 * nZ);
     269             : 
     270          62 :     OGRPMTilesTileIterator oIterator(m_poDS, m_nZoomLevel, nX, nY, nX, nY);
     271          31 :     const auto sTile = oIterator.GetNextTile();
     272          31 :     if (sTile.offset == 0)
     273             :     {
     274           8 :         return nullptr;
     275             :     }
     276          23 :     CPLAssert(sTile.z == m_nZoomLevel);
     277          23 :     CPLAssert(sTile.x == static_cast<uint32_t>(nX));
     278          23 :     CPLAssert(sTile.y == static_cast<uint32_t>(nY));
     279             : 
     280          23 :     const auto *posStr = m_poDS->ReadTileData(sTile.offset, sTile.length);
     281          23 :     if (!posStr)
     282             :     {
     283           0 :         return nullptr;
     284             :     }
     285          46 :     std::string osTileData = *posStr;
     286             : 
     287             :     const std::string osTmpFilename = VSIMemGenerateHiddenFilename(
     288          46 :         CPLSPrintf("pmtiles_getfeature_%u_%u.pbf", sTile.x, sTile.y));
     289          23 :     VSIFCloseL(VSIFileFromMemBuffer(osTmpFilename.c_str(),
     290          23 :                                     reinterpret_cast<GByte *>(&osTileData[0]),
     291          23 :                                     osTileData.size(), false));
     292             : 
     293          23 :     const char *const apszAllowedDrivers[] = {"MVT", nullptr};
     294          46 :     CPLStringList aosOpenOptions;
     295          23 :     aosOpenOptions.SetNameValue("X", CPLSPrintf("%u", sTile.x));
     296          23 :     aosOpenOptions.SetNameValue("Y", CPLSPrintf("%u", sTile.y));
     297          23 :     aosOpenOptions.SetNameValue("Z", CPLSPrintf("%d", m_nZoomLevel));
     298             :     aosOpenOptions.SetNameValue(
     299             :         "METADATA_FILE",
     300          23 :         m_bJsonField ? "" : m_poDS->GetMetadataFilename().c_str());
     301          23 :     if (!m_poDS->GetClipOpenOption().empty())
     302             :     {
     303             :         aosOpenOptions.SetNameValue("CLIP",
     304           0 :                                     m_poDS->GetClipOpenOption().c_str());
     305             :     }
     306             :     auto poTileDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     307          23 :         ("MVT:" + osTmpFilename).c_str(), GDAL_OF_VECTOR | GDAL_OF_INTERNAL,
     308          69 :         apszAllowedDrivers, aosOpenOptions.List(), nullptr));
     309          23 :     std::unique_ptr<OGRFeature> poFeature;
     310          23 :     if (poTileDS)
     311             :     {
     312          23 :         auto poTileLayer = poTileDS->GetLayerByName(GetDescription());
     313          23 :         if (poTileLayer)
     314             :         {
     315             :             auto poUnderlyingFeature =
     316          46 :                 std::unique_ptr<OGRFeature>(poTileLayer->GetFeature(nTileFID));
     317          23 :             if (poUnderlyingFeature)
     318             :             {
     319          22 :                 poFeature = CreateFeatureFrom(poUnderlyingFeature.get());
     320          22 :                 poFeature->SetFID(nFID);
     321             :             }
     322             :         }
     323             :     }
     324          23 :     VSIUnlink(osTmpFilename.c_str());
     325             : 
     326          23 :     return poFeature.release();
     327             : }
     328             : 
     329             : /************************************************************************/
     330             : /*                        GetNextSrcFeature()                           */
     331             : /************************************************************************/
     332             : 
     333        1919 : std::unique_ptr<OGRFeature> OGRPMTilesVectorLayer::GetNextSrcFeature()
     334             : {
     335        1919 :     if (!m_poTileIterator)
     336             :     {
     337             :         int nMinTileX;
     338             :         int nMinTileY;
     339             :         int nMaxTileX;
     340             :         int nMaxTileY;
     341         185 :         ExtentToTileExtent(m_sExtent, nMinTileX, nMinTileY, nMaxTileX,
     342             :                            nMaxTileY);
     343             : 
     344             :         // Optimization: if the spatial filter is totally out of the extent,
     345             :         // exit early
     346         185 :         if (m_nFilterMaxX < nMinTileX || m_nFilterMaxY < nMinTileY ||
     347         185 :             m_nFilterMinX > nMaxTileX || m_nFilterMinY > nMaxTileY)
     348             :         {
     349           4 :             return nullptr;
     350             :         }
     351             : 
     352         181 :         m_poTileIterator = std::make_unique<OGRPMTilesTileIterator>(
     353         181 :             m_poDS, m_nZoomLevel, m_nFilterMinX, m_nFilterMinY, m_nFilterMaxX,
     354         181 :             m_nFilterMaxY);
     355             :     }
     356             : 
     357        1915 :     OGRFeature *poTileFeat = nullptr;
     358        3649 :     if (!m_poTileLayer ||
     359        1734 :         (poTileFeat = m_poTileLayer->GetNextFeature()) == nullptr)
     360             :     {
     361         784 :         const char *const apszAllowedDrivers[] = {"MVT", nullptr};
     362             : 
     363             :         while (true)
     364             :         {
     365         784 :             const auto sTile = m_poTileIterator->GetNextTile();
     366         784 :             if (sTile.offset == 0)
     367             :             {
     368         128 :                 return nullptr;
     369             :             }
     370             : 
     371         656 :             m_nX = sTile.x;
     372         656 :             m_nY = sTile.y;
     373             : 
     374             :             // Do the reset before the later 'm_osTileData = *posStr', otherwise
     375             :             // the destructor might read file content that is no longer valid
     376         656 :             m_poTileDS.reset();
     377             : 
     378         656 :             if (sTile.offset == m_nLastTileOffset)
     379             :             {
     380             :                 // In case of run-length encoded tiles, we do not need to
     381             :                 // re-read it from disk
     382             :             }
     383             :             else
     384             :             {
     385         210 :                 m_nLastTileOffset = sTile.offset;
     386         210 :                 CPLDebugOnly("PMTiles", "Opening tile X=%u, Y=%u, Z=%d",
     387             :                              sTile.x, sTile.y, m_nZoomLevel);
     388             : 
     389             :                 const auto *posStr =
     390         210 :                     m_poDS->ReadTileData(sTile.offset, sTile.length);
     391         210 :                 if (!posStr)
     392             :                 {
     393           0 :                     return nullptr;
     394             :                 }
     395         210 :                 m_osTileData = *posStr;
     396             :             }
     397             : 
     398             :             const std::string osTmpFilename = VSIMemGenerateHiddenFilename(
     399         656 :                 CPLSPrintf("pmtiles_%u_%u.pbf", sTile.x, sTile.y));
     400         656 :             VSIFCloseL(VSIFileFromMemBuffer(
     401             :                 osTmpFilename.c_str(),
     402         656 :                 reinterpret_cast<GByte *>(&m_osTileData[0]),
     403         656 :                 m_osTileData.size(), false));
     404             : 
     405         656 :             CPLStringList aosOpenOptions;
     406         656 :             aosOpenOptions.SetNameValue("X", CPLSPrintf("%u", sTile.x));
     407         656 :             aosOpenOptions.SetNameValue("Y", CPLSPrintf("%u", sTile.y));
     408         656 :             aosOpenOptions.SetNameValue("Z", CPLSPrintf("%d", m_nZoomLevel));
     409             :             aosOpenOptions.SetNameValue(
     410             :                 "METADATA_FILE",
     411         656 :                 m_bJsonField ? "" : m_poDS->GetMetadataFilename().c_str());
     412         656 :             if (!m_poDS->GetClipOpenOption().empty())
     413             :             {
     414             :                 aosOpenOptions.SetNameValue(
     415           2 :                     "CLIP", m_poDS->GetClipOpenOption().c_str());
     416             :             }
     417         656 :             m_poTileDS.reset(GDALDataset::Open(
     418        1312 :                 ("MVT:" + osTmpFilename).c_str(),
     419             :                 GDAL_OF_VECTOR | GDAL_OF_INTERNAL, apszAllowedDrivers,
     420         656 :                 aosOpenOptions.List(), nullptr));
     421         656 :             if (m_poTileDS)
     422             :             {
     423         656 :                 m_poTileDS->SetDescription(osTmpFilename.c_str());
     424         656 :                 m_poTileDS->MarkSuppressOnClose();
     425         656 :                 m_poTileLayer = m_poTileDS->GetLayerByName(GetDescription());
     426         656 :                 if (m_poTileLayer)
     427             :                 {
     428         656 :                     poTileFeat = m_poTileLayer->GetNextFeature();
     429         656 :                     if (poTileFeat)
     430             :                     {
     431         656 :                         break;
     432             :                     }
     433             :                 }
     434           0 :                 m_poTileDS.reset();
     435           0 :                 m_poTileLayer = nullptr;
     436             :             }
     437             :             else
     438             :             {
     439           0 :                 VSIUnlink(osTmpFilename.c_str());
     440             :             }
     441           0 :         }
     442             :     }
     443             : 
     444        1787 :     return std::unique_ptr<OGRFeature>(poTileFeat);
     445             : }
     446             : 
     447             : /************************************************************************/
     448             : /*                         CreateFeatureFrom()                          */
     449             : /************************************************************************/
     450             : 
     451             : std::unique_ptr<OGRFeature>
     452        1808 : OGRPMTilesVectorLayer::CreateFeatureFrom(OGRFeature *poSrcFeature)
     453             : {
     454             :     return std::unique_ptr<OGRFeature>(OGRMVTCreateFeatureFrom(
     455        1808 :         poSrcFeature, m_poFeatureDefn, m_bJsonField, GetSpatialRef()));
     456             : }
     457             : 
     458             : /************************************************************************/
     459             : /*                        GetNextRawFeature()                           */
     460             : /************************************************************************/
     461             : 
     462        1918 : OGRFeature *OGRPMTilesVectorLayer::GetNextRawFeature()
     463             : {
     464        3836 :     auto poSrcFeat = GetNextSrcFeature();
     465        1918 :     if (poSrcFeat == nullptr)
     466         132 :         return nullptr;
     467             : 
     468        1786 :     const GIntBig nFIDBase =
     469        1786 :         (static_cast<GIntBig>(m_nY) << m_nZoomLevel) | m_nX;
     470        3572 :     auto poFeature = CreateFeatureFrom(poSrcFeat.get());
     471        1786 :     poFeature->SetFID((poSrcFeat->GetFID() << (2 * m_nZoomLevel)) | nFIDBase);
     472             : 
     473        1786 :     return poFeature.release();
     474             : }
     475             : 
     476             : /************************************************************************/
     477             : /*                           TestCapability()                           */
     478             : /************************************************************************/
     479             : 
     480         144 : int OGRPMTilesVectorLayer::TestCapability(const char *pszCap) const
     481             : {
     482         144 :     if (EQUAL(pszCap, OLCStringsAsUTF8) ||
     483          96 :         EQUAL(pszCap, OLCFastSpatialFilter) || EQUAL(pszCap, OLCFastGetExtent))
     484             :     {
     485          56 :         return TRUE;
     486             :     }
     487             : 
     488          88 :     if (EQUAL(pszCap, OLCFastFeatureCount))
     489           0 :         return m_nFeatureCount >= 0 && !m_poFilterGeom && !m_poAttrQuery;
     490             : 
     491          88 :     return FALSE;
     492             : }
     493             : 
     494             : /************************************************************************/
     495             : /*                            IGetExtent()                              */
     496             : /************************************************************************/
     497             : 
     498          23 : OGRErr OGRPMTilesVectorLayer::IGetExtent(int /* iGeomField */,
     499             :                                          OGREnvelope *psExtent, bool)
     500             : {
     501          23 :     *psExtent = m_sExtent;
     502          23 :     return OGRERR_NONE;
     503             : }
     504             : 
     505             : /************************************************************************/
     506             : /*                         ExtentToTileExtent()                         */
     507             : /************************************************************************/
     508             : 
     509         219 : void OGRPMTilesVectorLayer::ExtentToTileExtent(const OGREnvelope &sEnvelope,
     510             :                                                int &nTileMinX, int &nTileMinY,
     511             :                                                int &nTileMaxX,
     512             :                                                int &nTileMaxY) const
     513             : {
     514         219 :     const double dfTileDim = 2 * MAX_GM / (1 << m_nZoomLevel);
     515         219 :     constexpr double EPS = 1e-5;
     516         438 :     nTileMinX = std::max(0, static_cast<int>(floor(
     517         219 :                                 (sEnvelope.MinX + MAX_GM) / dfTileDim + EPS)));
     518             :     // PMTiles and MVT uses a Y=MAX_GM as the y=0 tile
     519         438 :     nTileMinY = std::max(0, static_cast<int>(floor(
     520         219 :                                 (MAX_GM - sEnvelope.MaxY) / dfTileDim + EPS)));
     521         219 :     nTileMaxX = std::min(
     522         438 :         static_cast<int>(floor((sEnvelope.MaxX + MAX_GM) / dfTileDim + EPS)),
     523         219 :         (1 << m_nZoomLevel) - 1);
     524         219 :     nTileMaxY = std::min(
     525         438 :         static_cast<int>(floor((MAX_GM - sEnvelope.MinY) / dfTileDim + EPS)),
     526         219 :         (1 << m_nZoomLevel) - 1);
     527         219 : }
     528             : 
     529             : /************************************************************************/
     530             : /*                         ISetSpatialFilter()                           */
     531             : /************************************************************************/
     532             : 
     533         150 : OGRErr OGRPMTilesVectorLayer::ISetSpatialFilter(int iGeomField,
     534             :                                                 const OGRGeometry *poGeomIn)
     535             : {
     536         150 :     OGRLayer::ISetSpatialFilter(iGeomField, poGeomIn);
     537             : 
     538         150 :     if (m_poFilterGeom != nullptr && m_sFilterEnvelope.MinX <= -MAX_GM &&
     539          12 :         m_sFilterEnvelope.MinY <= -MAX_GM && m_sFilterEnvelope.MaxX >= MAX_GM &&
     540           8 :         m_sFilterEnvelope.MaxY >= MAX_GM)
     541             :     {
     542           8 :         if (m_bZoomLevelAuto)
     543             :         {
     544           0 :             m_nZoomLevel = m_poDS->GetMinZoomLevel();
     545             :         }
     546           8 :         m_nFilterMinX = 0;
     547           8 :         m_nFilterMinY = 0;
     548           8 :         m_nFilterMaxX = (1 << m_nZoomLevel) - 1;
     549           8 :         m_nFilterMaxY = (1 << m_nZoomLevel) - 1;
     550             :     }
     551         142 :     else if (m_poFilterGeom != nullptr &&
     552          34 :              m_sFilterEnvelope.MinX >= -10 * MAX_GM &&
     553          34 :              m_sFilterEnvelope.MinY >= -10 * MAX_GM &&
     554          34 :              m_sFilterEnvelope.MaxX <= 10 * MAX_GM &&
     555          34 :              m_sFilterEnvelope.MaxY <= 10 * MAX_GM)
     556             :     {
     557          34 :         if (m_bZoomLevelAuto)
     558             :         {
     559             :             double dfExtent =
     560           4 :                 std::min(m_sFilterEnvelope.MaxX - m_sFilterEnvelope.MinX,
     561           2 :                          m_sFilterEnvelope.MaxY - m_sFilterEnvelope.MinY);
     562           2 :             m_nZoomLevel = std::max(
     563           2 :                 m_poDS->GetMinZoomLevel(),
     564           4 :                 std::min(static_cast<int>(0.5 + log(2 * MAX_GM / dfExtent) /
     565             :                                                     log(2.0)),
     566           4 :                          m_poDS->GetMaxZoomLevel()));
     567           2 :             CPLDebug("PMTiles", "Zoom level = %d", m_nZoomLevel);
     568             :         }
     569          34 :         ExtentToTileExtent(m_sFilterEnvelope, m_nFilterMinX, m_nFilterMinY,
     570          34 :                            m_nFilterMaxX, m_nFilterMaxY);
     571             :     }
     572             :     else
     573             :     {
     574         108 :         if (m_bZoomLevelAuto)
     575             :         {
     576           2 :             m_nZoomLevel = m_poDS->GetMaxZoomLevel();
     577             :         }
     578         108 :         m_nFilterMinX = 0;
     579         108 :         m_nFilterMinY = 0;
     580         108 :         m_nFilterMaxX = (1 << m_nZoomLevel) - 1;
     581         108 :         m_nFilterMaxY = (1 << m_nZoomLevel) - 1;
     582             :     }
     583         150 :     return OGRERR_NONE;
     584             : }

Generated by: LCOV version 1.14