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: 2024-11-21 22:18:42 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          41 : 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          41 :     bool bZoomLevelFromSpatialFilter)
      30          41 :     : m_poDS(poDS), m_poFeatureDefn(new OGRFeatureDefn(pszLayerName)),
      31          82 :       m_bJsonField(bJsonField)
      32             : {
      33          41 :     SetDescription(pszLayerName);
      34          41 :     m_poFeatureDefn->SetGeomType(eGeomType);
      35          41 :     OGRSpatialReference *poSRS = new OGRSpatialReference();
      36          41 :     poSRS->importFromEPSG(3857);
      37          41 :     m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
      38          41 :     poSRS->Release();
      39          41 :     m_poFeatureDefn->Reference();
      40             : 
      41          41 :     if (m_bJsonField)
      42             :     {
      43           2 :         OGRFieldDefn oFieldDefnId("mvt_id", OFTInteger64);
      44           1 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefnId);
      45             :     }
      46             :     else
      47             :     {
      48          40 :         OGRMVTInitFields(m_poFeatureDefn, oFields, oAttributesFromTileStats);
      49             :     }
      50             : 
      51          41 :     m_sExtent.MinX = dfMinX;
      52          41 :     m_sExtent.MinY = dfMinY;
      53          41 :     m_sExtent.MaxX = dfMaxX;
      54          41 :     m_sExtent.MaxY = dfMaxY;
      55             : 
      56          41 :     m_nZoomLevel = nZoomLevel;
      57          41 :     m_bZoomLevelAuto = bZoomLevelFromSpatialFilter;
      58          41 :     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          41 :     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          41 :     if (m_bJsonField)
      81             :     {
      82           2 :         OGRFieldDefn oFieldDefn("json", OFTString);
      83           1 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
      84             :     }
      85          41 : }
      86             : 
      87             : /************************************************************************/
      88             : /*                       ~OGRPMTilesVectorLayer()                       */
      89             : /************************************************************************/
      90             : 
      91          82 : OGRPMTilesVectorLayer::~OGRPMTilesVectorLayer()
      92             : {
      93          41 :     m_poFeatureDefn->Release();
      94          82 : }
      95             : 
      96             : /************************************************************************/
      97             : /*                          ResetReading()                              */
      98             : /************************************************************************/
      99             : 
     100         393 : void OGRPMTilesVectorLayer::ResetReading()
     101             : {
     102         393 :     m_poTileDS.reset();
     103         393 :     m_poTileLayer = nullptr;
     104         393 :     m_poTileIterator.reset();
     105         393 : }
     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        1849 : std::unique_ptr<OGRFeature> OGRPMTilesVectorLayer::GetNextSrcFeature()
     334             : {
     335        1849 :     if (!m_poTileIterator)
     336             :     {
     337             :         int nMinTileX;
     338             :         int nMinTileY;
     339             :         int nMaxTileX;
     340             :         int nMaxTileY;
     341         177 :         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         177 :         if (m_nFilterMaxX < nMinTileX || m_nFilterMaxY < nMinTileY ||
     347         177 :             m_nFilterMinX > nMaxTileX || m_nFilterMinY > nMaxTileY)
     348             :         {
     349           4 :             return nullptr;
     350             :         }
     351             : 
     352         173 :         m_poTileIterator = std::make_unique<OGRPMTilesTileIterator>(
     353         173 :             m_poDS, m_nZoomLevel, m_nFilterMinX, m_nFilterMinY, m_nFilterMaxX,
     354         173 :             m_nFilterMaxY);
     355             :     }
     356             : 
     357        1845 :     OGRFeature *poTileFeat = nullptr;
     358        3517 :     if (!m_poTileLayer ||
     359        1672 :         (poTileFeat = m_poTileLayer->GetNextFeature()) == nullptr)
     360             :     {
     361         758 :         const char *const apszAllowedDrivers[] = {"MVT", nullptr};
     362             : 
     363             :         while (true)
     364             :         {
     365         758 :             const auto sTile = m_poTileIterator->GetNextTile();
     366         758 :             if (sTile.offset == 0)
     367             :             {
     368         116 :                 return nullptr;
     369             :             }
     370             : 
     371         642 :             m_nX = sTile.x;
     372         642 :             m_nY = sTile.y;
     373             : 
     374         642 :             if (sTile.offset == m_nLastTileOffset)
     375             :             {
     376             :                 // In case of run-length encoded tiles, we do not need to
     377             :                 // re-read it from disk
     378             :             }
     379             :             else
     380             :             {
     381         202 :                 m_nLastTileOffset = sTile.offset;
     382         202 :                 CPLDebugOnly("PMTiles", "Opening tile X=%u, Y=%u, Z=%d",
     383             :                              sTile.x, sTile.y, m_nZoomLevel);
     384             : 
     385             :                 const auto *posStr =
     386         202 :                     m_poDS->ReadTileData(sTile.offset, sTile.length);
     387         202 :                 if (!posStr)
     388             :                 {
     389           0 :                     return nullptr;
     390             :                 }
     391         202 :                 m_osTileData = *posStr;
     392             :             }
     393             : 
     394         642 :             m_poTileDS.reset();
     395             :             const std::string osTmpFilename = VSIMemGenerateHiddenFilename(
     396         642 :                 CPLSPrintf("pmtiles_%u_%u.pbf", sTile.x, sTile.y));
     397         642 :             VSIFCloseL(VSIFileFromMemBuffer(
     398             :                 osTmpFilename.c_str(),
     399         642 :                 reinterpret_cast<GByte *>(&m_osTileData[0]),
     400         642 :                 m_osTileData.size(), false));
     401             : 
     402         642 :             CPLStringList aosOpenOptions;
     403         642 :             aosOpenOptions.SetNameValue("X", CPLSPrintf("%u", sTile.x));
     404         642 :             aosOpenOptions.SetNameValue("Y", CPLSPrintf("%u", sTile.y));
     405         642 :             aosOpenOptions.SetNameValue("Z", CPLSPrintf("%d", m_nZoomLevel));
     406             :             aosOpenOptions.SetNameValue(
     407             :                 "METADATA_FILE",
     408         642 :                 m_bJsonField ? "" : m_poDS->GetMetadataFilename().c_str());
     409         642 :             if (!m_poDS->GetClipOpenOption().empty())
     410             :             {
     411             :                 aosOpenOptions.SetNameValue(
     412           2 :                     "CLIP", m_poDS->GetClipOpenOption().c_str());
     413             :             }
     414         642 :             m_poTileDS.reset(GDALDataset::Open(
     415        1284 :                 ("MVT:" + osTmpFilename).c_str(),
     416             :                 GDAL_OF_VECTOR | GDAL_OF_INTERNAL, apszAllowedDrivers,
     417         642 :                 aosOpenOptions.List(), nullptr));
     418         642 :             if (m_poTileDS)
     419             :             {
     420         642 :                 m_poTileDS->SetDescription(osTmpFilename.c_str());
     421         642 :                 m_poTileDS->MarkSuppressOnClose();
     422         642 :                 m_poTileLayer = m_poTileDS->GetLayerByName(GetDescription());
     423         642 :                 if (m_poTileLayer)
     424             :                 {
     425         642 :                     poTileFeat = m_poTileLayer->GetNextFeature();
     426         642 :                     if (poTileFeat)
     427             :                     {
     428         642 :                         break;
     429             :                     }
     430             :                 }
     431           0 :                 m_poTileDS.reset();
     432           0 :                 m_poTileLayer = nullptr;
     433             :             }
     434             :             else
     435             :             {
     436           0 :                 VSIUnlink(osTmpFilename.c_str());
     437             :             }
     438           0 :         }
     439             :     }
     440             : 
     441        1729 :     return std::unique_ptr<OGRFeature>(poTileFeat);
     442             : }
     443             : 
     444             : /************************************************************************/
     445             : /*                         CreateFeatureFrom()                          */
     446             : /************************************************************************/
     447             : 
     448             : std::unique_ptr<OGRFeature>
     449        1750 : OGRPMTilesVectorLayer::CreateFeatureFrom(OGRFeature *poSrcFeature)
     450             : {
     451             :     return std::unique_ptr<OGRFeature>(OGRMVTCreateFeatureFrom(
     452        1750 :         poSrcFeature, m_poFeatureDefn, m_bJsonField, GetSpatialRef()));
     453             : }
     454             : 
     455             : /************************************************************************/
     456             : /*                        GetNextRawFeature()                           */
     457             : /************************************************************************/
     458             : 
     459        1848 : OGRFeature *OGRPMTilesVectorLayer::GetNextRawFeature()
     460             : {
     461        3696 :     auto poSrcFeat = GetNextSrcFeature();
     462        1848 :     if (poSrcFeat == nullptr)
     463         120 :         return nullptr;
     464             : 
     465        1728 :     const GIntBig nFIDBase =
     466        1728 :         (static_cast<GIntBig>(m_nY) << m_nZoomLevel) | m_nX;
     467        3456 :     auto poFeature = CreateFeatureFrom(poSrcFeat.get());
     468        1728 :     poFeature->SetFID((poSrcFeat->GetFID() << (2 * m_nZoomLevel)) | nFIDBase);
     469             : 
     470        1728 :     return poFeature.release();
     471             : }
     472             : 
     473             : /************************************************************************/
     474             : /*                           TestCapability()                           */
     475             : /************************************************************************/
     476             : 
     477         140 : int OGRPMTilesVectorLayer::TestCapability(const char *pszCap)
     478             : {
     479         140 :     if (EQUAL(pszCap, OLCStringsAsUTF8) ||
     480          92 :         EQUAL(pszCap, OLCFastSpatialFilter) || EQUAL(pszCap, OLCFastGetExtent))
     481             :     {
     482          56 :         return TRUE;
     483             :     }
     484             : 
     485          84 :     if (EQUAL(pszCap, OLCFastFeatureCount))
     486           0 :         return m_nFeatureCount >= 0 && !m_poFilterGeom && !m_poAttrQuery;
     487             : 
     488          84 :     return FALSE;
     489             : }
     490             : 
     491             : /************************************************************************/
     492             : /*                             GetExtent()                              */
     493             : /************************************************************************/
     494             : 
     495          23 : OGRErr OGRPMTilesVectorLayer::GetExtent(OGREnvelope *psExtent, int)
     496             : {
     497          23 :     *psExtent = m_sExtent;
     498          23 :     return OGRERR_NONE;
     499             : }
     500             : 
     501             : /************************************************************************/
     502             : /*                         ExtentToTileExtent()                         */
     503             : /************************************************************************/
     504             : 
     505         211 : void OGRPMTilesVectorLayer::ExtentToTileExtent(const OGREnvelope &sEnvelope,
     506             :                                                int &nTileMinX, int &nTileMinY,
     507             :                                                int &nTileMaxX,
     508             :                                                int &nTileMaxY) const
     509             : {
     510         211 :     const double dfTileDim = 2 * MAX_GM / (1 << m_nZoomLevel);
     511         211 :     constexpr double EPS = 1e-5;
     512         422 :     nTileMinX = std::max(0, static_cast<int>(floor(
     513         211 :                                 (sEnvelope.MinX + MAX_GM) / dfTileDim + EPS)));
     514             :     // PMTiles and MVT uses a Y=MAX_GM as the y=0 tile
     515         422 :     nTileMinY = std::max(0, static_cast<int>(floor(
     516         211 :                                 (MAX_GM - sEnvelope.MaxY) / dfTileDim + EPS)));
     517         211 :     nTileMaxX = std::min(
     518         422 :         static_cast<int>(floor((sEnvelope.MaxX + MAX_GM) / dfTileDim + EPS)),
     519         211 :         (1 << m_nZoomLevel) - 1);
     520         211 :     nTileMaxY = std::min(
     521         422 :         static_cast<int>(floor((MAX_GM - sEnvelope.MinY) / dfTileDim + EPS)),
     522         211 :         (1 << m_nZoomLevel) - 1);
     523         211 : }
     524             : 
     525             : /************************************************************************/
     526             : /*                         SetSpatialFilter()                           */
     527             : /************************************************************************/
     528             : 
     529         151 : void OGRPMTilesVectorLayer::SetSpatialFilter(OGRGeometry *poGeomIn)
     530             : {
     531         151 :     OGRLayer::SetSpatialFilter(poGeomIn);
     532             : 
     533         151 :     if (m_poFilterGeom != nullptr && m_sFilterEnvelope.MinX <= -MAX_GM &&
     534          12 :         m_sFilterEnvelope.MinY <= -MAX_GM && m_sFilterEnvelope.MaxX >= MAX_GM &&
     535           8 :         m_sFilterEnvelope.MaxY >= MAX_GM)
     536             :     {
     537           8 :         if (m_bZoomLevelAuto)
     538             :         {
     539           0 :             m_nZoomLevel = m_poDS->GetMinZoomLevel();
     540             :         }
     541           8 :         m_nFilterMinX = 0;
     542           8 :         m_nFilterMinY = 0;
     543           8 :         m_nFilterMaxX = (1 << m_nZoomLevel) - 1;
     544           8 :         m_nFilterMaxY = (1 << m_nZoomLevel) - 1;
     545             :     }
     546         143 :     else if (m_poFilterGeom != nullptr &&
     547          34 :              m_sFilterEnvelope.MinX >= -10 * MAX_GM &&
     548          34 :              m_sFilterEnvelope.MinY >= -10 * MAX_GM &&
     549          34 :              m_sFilterEnvelope.MaxX <= 10 * MAX_GM &&
     550          34 :              m_sFilterEnvelope.MaxY <= 10 * MAX_GM)
     551             :     {
     552          34 :         if (m_bZoomLevelAuto)
     553             :         {
     554             :             double dfExtent =
     555           4 :                 std::min(m_sFilterEnvelope.MaxX - m_sFilterEnvelope.MinX,
     556           2 :                          m_sFilterEnvelope.MaxY - m_sFilterEnvelope.MinY);
     557           2 :             m_nZoomLevel = std::max(
     558           2 :                 m_poDS->GetMinZoomLevel(),
     559           4 :                 std::min(static_cast<int>(0.5 + log(2 * MAX_GM / dfExtent) /
     560             :                                                     log(2.0)),
     561           4 :                          m_poDS->GetMaxZoomLevel()));
     562           2 :             CPLDebug("PMTiles", "Zoom level = %d", m_nZoomLevel);
     563             :         }
     564          34 :         ExtentToTileExtent(m_sFilterEnvelope, m_nFilterMinX, m_nFilterMinY,
     565          34 :                            m_nFilterMaxX, m_nFilterMaxY);
     566             :     }
     567             :     else
     568             :     {
     569         109 :         if (m_bZoomLevelAuto)
     570             :         {
     571           2 :             m_nZoomLevel = m_poDS->GetMaxZoomLevel();
     572             :         }
     573         109 :         m_nFilterMinX = 0;
     574         109 :         m_nFilterMinY = 0;
     575         109 :         m_nFilterMaxX = (1 << m_nZoomLevel) - 1;
     576         109 :         m_nFilterMaxY = (1 << m_nZoomLevel) - 1;
     577             :     }
     578         151 : }

Generated by: LCOV version 1.14