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

Generated by: LCOV version 1.14