LCOV - code coverage report
Current view: top level - gcore - tilematrixset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 332 337 98.5 %
Date: 2025-05-31 00:00:17 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Class to handle TileMatrixSet
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2020, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_json.h"
      14             : #include "ogr_spatialref.h"
      15             : 
      16             : #include <algorithm>
      17             : #include <cmath>
      18             : #include <cfloat>
      19             : #include <limits>
      20             : 
      21             : #include "tilematrixset.hpp"
      22             : 
      23             : //! @cond Doxygen_Suppress
      24             : 
      25             : namespace gdal
      26             : {
      27             : 
      28             : /************************************************************************/
      29             : /*                   listPredefinedTileMatrixSets()                     */
      30             : /************************************************************************/
      31             : 
      32         560 : std::vector<std::string> TileMatrixSet::listPredefinedTileMatrixSets()
      33             : {
      34             :     std::vector<std::string> l{"GoogleMapsCompatible", "WorldCRS84Quad",
      35             :                                "WorldMercatorWGS84Quad", "GoogleCRS84Quad",
      36        4480 :                                "PseudoTMS_GlobalMercator"};
      37         560 :     const char *pszSomeFile = CPLFindFile("gdal", "tms_NZTM2000.json");
      38         560 :     if (pszSomeFile)
      39             :     {
      40        1120 :         std::set<std::string> set;
      41             :         CPLStringList aosList(
      42        1120 :             VSIReadDir(CPLGetDirnameSafe(pszSomeFile).c_str()));
      43       91840 :         for (int i = 0; i < aosList.size(); i++)
      44             :         {
      45       91280 :             const size_t nLen = strlen(aosList[i]);
      46      180320 :             if (nLen > strlen("tms_") + strlen(".json") &&
      47       93520 :                 STARTS_WITH(aosList[i], "tms_") &&
      48        2240 :                 EQUAL(aosList[i] + nLen - strlen(".json"), ".json"))
      49             :             {
      50        4480 :                 std::string id(aosList[i] + strlen("tms_"),
      51        4480 :                                nLen - (strlen("tms_") + strlen(".json")));
      52        2240 :                 set.insert(std::move(id));
      53             :             }
      54             :         }
      55        2800 :         for (const std::string &id : set)
      56        2240 :             l.push_back(id);
      57             :     }
      58         560 :     return l;
      59             : }
      60             : 
      61             : /************************************************************************/
      62             : /*                              parse()                                 */
      63             : /************************************************************************/
      64             : 
      65        5251 : std::unique_ptr<TileMatrixSet> TileMatrixSet::parse(const char *fileOrDef)
      66             : {
      67       10502 :     CPLJSONDocument oDoc;
      68       10502 :     std::unique_ptr<TileMatrixSet> poTMS(new TileMatrixSet());
      69             : 
      70        5251 :     constexpr double HALF_CIRCUMFERENCE = 6378137 * M_PI;
      71             : 
      72        5251 :     if (EQUAL(fileOrDef, "GoogleMapsCompatible") ||
      73        4569 :         EQUAL(fileOrDef, "WebMercatorQuad") ||
      74        4569 :         EQUAL(
      75             :             fileOrDef,
      76             :             "http://www.opengis.net/def/tilematrixset/OGC/1.0/WebMercatorQuad"))
      77             :     {
      78             :         /* See http://portal.opengeospatial.org/files/?artifact_id=35326
      79             :          * (WMTS 1.0), Annex E.4 */
      80             :         // or https://docs.ogc.org/is/17-083r4/17-083r4.html#toc49
      81         682 :         poTMS->mTitle = "GoogleMapsCompatible";
      82         682 :         poTMS->mIdentifier = "GoogleMapsCompatible";
      83         682 :         poTMS->mCrs = "http://www.opengis.net/def/crs/EPSG/0/3857";
      84         682 :         poTMS->mBbox.mCrs = poTMS->mCrs;
      85         682 :         poTMS->mBbox.mLowerCornerX = -HALF_CIRCUMFERENCE;
      86         682 :         poTMS->mBbox.mLowerCornerY = -HALF_CIRCUMFERENCE;
      87         682 :         poTMS->mBbox.mUpperCornerX = HALF_CIRCUMFERENCE;
      88         682 :         poTMS->mBbox.mUpperCornerY = HALF_CIRCUMFERENCE;
      89         682 :         poTMS->mWellKnownScaleSet =
      90         682 :             "http://www.opengis.net/def/wkss/OGC/1.0/GoogleMapsCompatible";
      91       21824 :         for (int i = 0; i <= 30; i++)
      92             :         {
      93       42284 :             TileMatrix tm;
      94       21142 :             tm.mId = CPLSPrintf("%d", i);
      95       21142 :             tm.mResX = 2 * HALF_CIRCUMFERENCE / 256 / (1 << i);
      96       21142 :             tm.mResY = tm.mResX;
      97       21142 :             tm.mScaleDenominator = tm.mResX / 0.28e-3;
      98       21142 :             tm.mTopLeftX = -HALF_CIRCUMFERENCE;
      99       21142 :             tm.mTopLeftY = HALF_CIRCUMFERENCE;
     100       21142 :             tm.mTileWidth = 256;
     101       21142 :             tm.mTileHeight = 256;
     102       21142 :             tm.mMatrixWidth = 1 << i;
     103       21142 :             tm.mMatrixHeight = 1 << i;
     104       21142 :             poTMS->mTileMatrixList.emplace_back(std::move(tm));
     105             :         }
     106         682 :         return poTMS;
     107             :     }
     108             : 
     109        4569 :     if (EQUAL(fileOrDef, "WorldMercatorWGS84Quad") ||
     110        4010 :         EQUAL(fileOrDef, "http://www.opengis.net/def/tilematrixset/OGC/1.0/"
     111             :                          "WorldMercatorWGS84Quad"))
     112             :     {
     113             :         // See https://docs.ogc.org/is/17-083r4/17-083r4.html#toc51
     114         559 :         poTMS->mTitle = "WorldMercatorWGS84Quad";
     115         559 :         poTMS->mIdentifier = "WorldMercatorWGS84Quad";
     116         559 :         poTMS->mCrs = "http://www.opengis.net/def/crs/EPSG/0/3395";
     117         559 :         poTMS->mBbox.mCrs = poTMS->mCrs;
     118         559 :         poTMS->mBbox.mLowerCornerX = -HALF_CIRCUMFERENCE;
     119         559 :         poTMS->mBbox.mLowerCornerY = -HALF_CIRCUMFERENCE;
     120         559 :         poTMS->mBbox.mUpperCornerX = HALF_CIRCUMFERENCE;
     121         559 :         poTMS->mBbox.mUpperCornerY = HALF_CIRCUMFERENCE;
     122         559 :         poTMS->mWellKnownScaleSet =
     123         559 :             "http://www.opengis.net/def/wkss/OGC/1.0/WorldMercatorWGS84Quad";
     124       17888 :         for (int i = 0; i <= 30; i++)
     125             :         {
     126       34658 :             TileMatrix tm;
     127       17329 :             tm.mId = CPLSPrintf("%d", i);
     128       17329 :             tm.mResX = 2 * HALF_CIRCUMFERENCE / 256 / (1 << i);
     129       17329 :             tm.mResY = tm.mResX;
     130       17329 :             tm.mScaleDenominator = tm.mResX / 0.28e-3;
     131       17329 :             tm.mTopLeftX = -HALF_CIRCUMFERENCE;
     132       17329 :             tm.mTopLeftY = HALF_CIRCUMFERENCE;
     133       17329 :             tm.mTileWidth = 256;
     134       17329 :             tm.mTileHeight = 256;
     135       17329 :             tm.mMatrixWidth = 1 << i;
     136       17329 :             tm.mMatrixHeight = 1 << i;
     137       17329 :             poTMS->mTileMatrixList.emplace_back(std::move(tm));
     138             :         }
     139         559 :         return poTMS;
     140             :     }
     141             : 
     142        4010 :     if (EQUAL(fileOrDef, "PseudoTMS_GlobalMercator"))
     143             :     {
     144             :         /* See global-mercator at
     145             :            http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification */
     146         559 :         poTMS->mTitle = "PseudoTMS_GlobalMercator";
     147         559 :         poTMS->mIdentifier = "PseudoTMS_GlobalMercator";
     148         559 :         poTMS->mCrs = "http://www.opengis.net/def/crs/EPSG/0/3857";
     149         559 :         poTMS->mBbox.mCrs = poTMS->mCrs;
     150         559 :         poTMS->mBbox.mLowerCornerX = -HALF_CIRCUMFERENCE;
     151         559 :         poTMS->mBbox.mLowerCornerY = -HALF_CIRCUMFERENCE;
     152         559 :         poTMS->mBbox.mUpperCornerX = HALF_CIRCUMFERENCE;
     153         559 :         poTMS->mBbox.mUpperCornerY = HALF_CIRCUMFERENCE;
     154       17329 :         for (int i = 0; i <= 29; i++)
     155             :         {
     156       33540 :             TileMatrix tm;
     157       16770 :             tm.mId = CPLSPrintf("%d", i);
     158       16770 :             tm.mResX = HALF_CIRCUMFERENCE / 256 / (1 << i);
     159       16770 :             tm.mResY = tm.mResX;
     160       16770 :             tm.mScaleDenominator = tm.mResX / 0.28e-3;
     161       16770 :             tm.mTopLeftX = -HALF_CIRCUMFERENCE;
     162       16770 :             tm.mTopLeftY = HALF_CIRCUMFERENCE;
     163       16770 :             tm.mTileWidth = 256;
     164       16770 :             tm.mTileHeight = 256;
     165       16770 :             tm.mMatrixWidth = 2 * (1 << i);
     166       16770 :             tm.mMatrixHeight = 2 * (1 << i);
     167       16770 :             poTMS->mTileMatrixList.emplace_back(std::move(tm));
     168             :         }
     169         559 :         return poTMS;
     170             :     }
     171             : 
     172        3451 :     if (EQUAL(fileOrDef, "InspireCRS84Quad") ||
     173        3445 :         EQUAL(fileOrDef, "PseudoTMS_GlobalGeodetic") ||
     174        3445 :         EQUAL(fileOrDef, "WorldCRS84Quad") ||
     175        2884 :         EQUAL(
     176             :             fileOrDef,
     177             :             "http://www.opengis.net/def/tilematrixset/OGC/1.0/WorldCRS84Quad"))
     178             :     {
     179             :         /* See InspireCRS84Quad at
     180             :          * http://inspire.ec.europa.eu/documents/Network_Services/TechnicalGuidance_ViewServices_v3.0.pdf
     181             :          */
     182             :         /* This is exactly the same as PseudoTMS_GlobalGeodetic */
     183             :         /* See global-geodetic at
     184             :          * http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification */
     185             :         // See also http://docs.opengeospatial.org/is/17-083r2/17-083r2.html#76
     186         567 :         poTMS->mTitle = "WorldCRS84Quad";
     187         567 :         poTMS->mIdentifier = "WorldCRS84Quad";
     188         567 :         poTMS->mCrs = "http://www.opengis.net/def/crs/OGC/1.3/CRS84";
     189         567 :         poTMS->mBbox.mCrs = poTMS->mCrs;
     190         567 :         poTMS->mBbox.mLowerCornerX = -180;
     191         567 :         poTMS->mBbox.mLowerCornerY = -90;
     192         567 :         poTMS->mBbox.mUpperCornerX = 180;
     193         567 :         poTMS->mBbox.mUpperCornerY = 90;
     194         567 :         poTMS->mWellKnownScaleSet =
     195         567 :             "http://www.opengis.net/def/wkss/OGC/1.0/GoogleCRS84Quad";
     196             :         // Limit to zoom level 29, because at that zoom level nMatrixWidth = 2 * (1 << 29) = 1073741824
     197             :         // and at 30 it would overflow int32.
     198       17577 :         for (int i = 0; i <= 29; i++)
     199             :         {
     200       34020 :             TileMatrix tm;
     201       17010 :             tm.mId = CPLSPrintf("%d", i);
     202       17010 :             tm.mResX = 180. / 256 / (1 << i);
     203       17010 :             tm.mResY = tm.mResX;
     204       17010 :             tm.mScaleDenominator =
     205       17010 :                 tm.mResX * (HALF_CIRCUMFERENCE / 180) / 0.28e-3;
     206       17010 :             tm.mTopLeftX = -180;
     207       17010 :             tm.mTopLeftY = 90;
     208       17010 :             tm.mTileWidth = 256;
     209       17010 :             tm.mTileHeight = 256;
     210       17010 :             tm.mMatrixWidth = 2 * (1 << i);
     211       17010 :             tm.mMatrixHeight = 1 << i;
     212       17010 :             poTMS->mTileMatrixList.emplace_back(std::move(tm));
     213             :         }
     214         567 :         return poTMS;
     215             :     }
     216             : 
     217        2884 :     if (EQUAL(fileOrDef, "GoogleCRS84Quad") ||
     218        2307 :         EQUAL(fileOrDef,
     219             :               "http://www.opengis.net/def/wkss/OGC/1.0/GoogleCRS84Quad"))
     220             :     {
     221             :         /* See http://portal.opengeospatial.org/files/?artifact_id=35326 (WMTS 1.0),
     222             :                Annex E.3 */
     223         577 :         poTMS->mTitle = "GoogleCRS84Quad";
     224         577 :         poTMS->mIdentifier = "GoogleCRS84Quad";
     225         577 :         poTMS->mCrs = "http://www.opengis.net/def/crs/OGC/1.3/CRS84";
     226         577 :         poTMS->mBbox.mCrs = poTMS->mCrs;
     227         577 :         poTMS->mBbox.mLowerCornerX = -180;
     228         577 :         poTMS->mBbox.mLowerCornerY = -90;
     229         577 :         poTMS->mBbox.mUpperCornerX = 180;
     230         577 :         poTMS->mBbox.mUpperCornerY = 90;
     231         577 :         poTMS->mWellKnownScaleSet =
     232         577 :             "http://www.opengis.net/def/wkss/OGC/1.0/GoogleCRS84Quad";
     233       18464 :         for (int i = 0; i <= 30; i++)
     234             :         {
     235       35774 :             TileMatrix tm;
     236       17887 :             tm.mId = CPLSPrintf("%d", i);
     237       17887 :             tm.mResX = 360. / 256 / (1 << i);
     238       17887 :             tm.mResY = tm.mResX;
     239       17887 :             tm.mScaleDenominator =
     240       17887 :                 tm.mResX * (HALF_CIRCUMFERENCE / 180) / 0.28e-3;
     241       17887 :             tm.mTopLeftX = -180;
     242       17887 :             tm.mTopLeftY = 180;
     243       17887 :             tm.mTileWidth = 256;
     244       17887 :             tm.mTileHeight = 256;
     245       17887 :             tm.mMatrixWidth = 1 << i;
     246       17887 :             tm.mMatrixHeight = 1 << i;
     247       17887 :             poTMS->mTileMatrixList.emplace_back(std::move(tm));
     248             :         }
     249         577 :         return poTMS;
     250             :     }
     251             : 
     252        2307 :     bool loadOk = false;
     253        2307 :     if (  // TMS 2.0 spec
     254        2307 :         (strstr(fileOrDef, "\"crs\"") &&
     255          45 :          strstr(fileOrDef, "\"tileMatrices\"")) ||
     256             :         // TMS 1.0 spec
     257        2285 :         (strstr(fileOrDef, "\"type\"") &&
     258          31 :          strstr(fileOrDef, "\"TileMatrixSetType\"")) ||
     259        2254 :         (strstr(fileOrDef, "\"identifier\"") &&
     260           1 :          strstr(fileOrDef, "\"boundingBox\"") &&
     261           1 :          strstr(fileOrDef, "\"tileMatrix\"")))
     262             :     {
     263          54 :         loadOk = oDoc.LoadMemory(fileOrDef);
     264             :     }
     265        2253 :     else if (STARTS_WITH_CI(fileOrDef, "http://") ||
     266        2252 :              STARTS_WITH_CI(fileOrDef, "https://"))
     267             :     {
     268           1 :         const char *const apszOptions[] = {"MAX_FILE_SIZE=1000000", nullptr};
     269           1 :         loadOk = oDoc.LoadUrl(fileOrDef, apszOptions);
     270             :     }
     271             :     else
     272             :     {
     273             :         VSIStatBufL sStat;
     274        2252 :         if (VSIStatL(fileOrDef, &sStat) == 0)
     275             :         {
     276           1 :             loadOk = oDoc.Load(fileOrDef);
     277             :         }
     278             :         else
     279             :         {
     280        2251 :             const char *pszFilename = CPLFindFile(
     281        4502 :                 "gdal", (std::string("tms_") + fileOrDef + ".json").c_str());
     282        2251 :             if (pszFilename)
     283             :             {
     284        2249 :                 loadOk = oDoc.Load(pszFilename);
     285             :             }
     286             :             else
     287             :             {
     288           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
     289             :                          "Invalid tiling matrix set name");
     290             :             }
     291             :         }
     292             :     }
     293        2307 :     if (!loadOk)
     294             :     {
     295           4 :         return nullptr;
     296             :     }
     297             : 
     298        4606 :     auto oRoot = oDoc.GetRoot();
     299             :     const bool bIsTMSv2 =
     300        2303 :         oRoot.GetObj("crs").IsValid() && oRoot.GetObj("tileMatrices").IsValid();
     301             : 
     302        4607 :     if (!bIsTMSv2 && oRoot.GetString("type") != "TileMatrixSetType" &&
     303        2304 :         !oRoot.GetObj("tileMatrix").IsValid())
     304             :     {
     305           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     306             :                  "Expected type = TileMatrixSetType");
     307           0 :         return nullptr;
     308             :     }
     309             : 
     310        2892 :     const auto GetCRS = [](const CPLJSONObject &j)
     311             :     {
     312        2892 :         if (j.IsValid())
     313             :         {
     314        2891 :             if (j.GetType() == CPLJSONObject::Type::String)
     315        2888 :                 return j.ToString();
     316             : 
     317           3 :             else if (j.GetType() == CPLJSONObject::Type::Object)
     318             :             {
     319           6 :                 std::string osURI = j.GetString("uri");
     320           3 :                 if (!osURI.empty())
     321           1 :                     return osURI;
     322             : 
     323             :                 // Quite a bit of confusion around wkt.
     324             :                 // See https://github.com/opengeospatial/ogcapi-tiles/issues/170
     325           4 :                 const auto jWKT = j.GetObj("wkt");
     326           2 :                 if (jWKT.GetType() == CPLJSONObject::Type::String)
     327             :                 {
     328           2 :                     std::string osWKT = jWKT.ToString();
     329           1 :                     if (!osWKT.empty())
     330           1 :                         return osWKT;
     331             :                 }
     332           1 :                 else if (jWKT.GetType() == CPLJSONObject::Type::Object)
     333             :                 {
     334           2 :                     std::string osWKT = jWKT.ToString();
     335           1 :                     if (!osWKT.empty())
     336           1 :                         return osWKT;
     337             :                 }
     338             :             }
     339             :         }
     340           1 :         return std::string();
     341             :     };
     342             : 
     343        2303 :     poTMS->mIdentifier = oRoot.GetString(bIsTMSv2 ? "id" : "identifier");
     344        2303 :     poTMS->mTitle = oRoot.GetString("title");
     345        2303 :     poTMS->mAbstract = oRoot.GetString(bIsTMSv2 ? "description" : "abstract");
     346        6909 :     const auto oBbox = oRoot.GetObj("boundingBox");
     347        2303 :     if (oBbox.IsValid())
     348             :     {
     349         589 :         poTMS->mBbox.mCrs = GetCRS(oBbox.GetObj("crs"));
     350        1767 :         const auto oLowerCorner = oBbox.GetArray("lowerCorner");
     351         589 :         if (oLowerCorner.IsValid() && oLowerCorner.Size() == 2)
     352             :         {
     353         589 :             poTMS->mBbox.mLowerCornerX = oLowerCorner[0].ToDouble(NaN);
     354         589 :             poTMS->mBbox.mLowerCornerY = oLowerCorner[1].ToDouble(NaN);
     355             :         }
     356        1767 :         const auto oUpperCorner = oBbox.GetArray("upperCorner");
     357         589 :         if (oUpperCorner.IsValid() && oUpperCorner.Size() == 2)
     358             :         {
     359         589 :             poTMS->mBbox.mUpperCornerX = oUpperCorner[0].ToDouble(NaN);
     360         589 :             poTMS->mBbox.mUpperCornerY = oUpperCorner[1].ToDouble(NaN);
     361             :         }
     362             :     }
     363        2303 :     poTMS->mCrs = GetCRS(oRoot.GetObj(bIsTMSv2 ? "crs" : "supportedCRS"));
     364        2303 :     poTMS->mWellKnownScaleSet = oRoot.GetString("wellKnownScaleSet");
     365             : 
     366        4606 :     OGRSpatialReference oCrs;
     367        2303 :     if (oCrs.SetFromUserInput(
     368        2303 :             poTMS->mCrs.c_str(),
     369        2303 :             OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS) !=
     370             :         OGRERR_NONE)
     371             :     {
     372           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse CRS %s",
     373           1 :                  poTMS->mCrs.c_str());
     374           1 :         return nullptr;
     375             :     }
     376        2302 :     double dfMetersPerUnit = 1.0;
     377        2302 :     if (oCrs.IsProjected())
     378             :     {
     379        2256 :         dfMetersPerUnit = oCrs.GetLinearUnits();
     380             :     }
     381          46 :     else if (oCrs.IsGeographic())
     382             :     {
     383          46 :         dfMetersPerUnit = oCrs.GetSemiMajor() * M_PI / 180;
     384             :     }
     385             : 
     386             :     const auto oTileMatrices =
     387        6906 :         oRoot.GetArray(bIsTMSv2 ? "tileMatrices" : "tileMatrix");
     388        2302 :     if (oTileMatrices.IsValid())
     389             :     {
     390        2302 :         double dfLastScaleDenominator = std::numeric_limits<double>::max();
     391       46159 :         for (const auto &oTM : oTileMatrices)
     392             :         {
     393       43863 :             TileMatrix tm;
     394       43863 :             tm.mId = oTM.GetString(bIsTMSv2 ? "id" : "identifier");
     395       43863 :             tm.mScaleDenominator = oTM.GetDouble("scaleDenominator");
     396       43863 :             if (tm.mScaleDenominator >= dfLastScaleDenominator ||
     397       43863 :                 tm.mScaleDenominator <= 0)
     398             :             {
     399           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     400             :                          "Invalid scale denominator or non-decreasing series "
     401             :                          "of scale denominators");
     402           1 :                 return nullptr;
     403             :             }
     404       43862 :             dfLastScaleDenominator = tm.mScaleDenominator;
     405             :             // See note g of Table 2 of
     406             :             // http://docs.opengeospatial.org/is/17-083r2/17-083r2.html
     407       43862 :             tm.mResX = tm.mScaleDenominator * 0.28e-3 / dfMetersPerUnit;
     408       43862 :             tm.mResY = tm.mResX;
     409       43862 :             if (bIsTMSv2)
     410             :             {
     411        1635 :                 const auto osCornerOfOrigin = oTM.GetString("cornerOfOrigin");
     412         545 :                 if (!osCornerOfOrigin.empty() && osCornerOfOrigin != "topLeft")
     413             :                 {
     414           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     415             :                              "cornerOfOrigin = %s not supported",
     416             :                              osCornerOfOrigin.c_str());
     417             :                 }
     418             :             }
     419             :             const auto oTopLeftCorner =
     420       87724 :                 oTM.GetArray(bIsTMSv2 ? "pointOfOrigin" : "topLeftCorner");
     421       43862 :             if (oTopLeftCorner.IsValid() && oTopLeftCorner.Size() == 2)
     422             :             {
     423       43862 :                 tm.mTopLeftX = oTopLeftCorner[0].ToDouble(NaN);
     424       43862 :                 tm.mTopLeftY = oTopLeftCorner[1].ToDouble(NaN);
     425             :             }
     426       43862 :             tm.mTileWidth = oTM.GetInteger("tileWidth");
     427       43862 :             if (tm.mTileWidth <= 0)
     428             :             {
     429           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid tileWidth: %d",
     430             :                          tm.mTileWidth);
     431           1 :                 return nullptr;
     432             :             }
     433       43861 :             tm.mTileHeight = oTM.GetInteger("tileHeight");
     434       43861 :             if (tm.mTileHeight <= 0)
     435             :             {
     436           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid tileHeight: %d",
     437             :                          tm.mTileHeight);
     438           1 :                 return nullptr;
     439             :             }
     440       43860 :             if (tm.mTileWidth > INT_MAX / tm.mTileHeight)
     441             :             {
     442           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     443             :                          "tileWidth(%d) x tileHeight(%d) larger than "
     444             :                          "INT_MAX",
     445             :                          tm.mTileWidth, tm.mTileHeight);
     446           1 :                 return nullptr;
     447             :             }
     448       43859 :             tm.mMatrixWidth = oTM.GetInteger("matrixWidth");
     449       43859 :             if (tm.mMatrixWidth <= 0)
     450             :             {
     451           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid matrixWidth: %d",
     452             :                          tm.mMatrixWidth);
     453           1 :                 return nullptr;
     454             :             }
     455       43858 :             tm.mMatrixHeight = oTM.GetInteger("matrixHeight");
     456       43858 :             if (tm.mMatrixHeight <= 0)
     457             :             {
     458           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     459             :                          "Invalid matrixHeight: %d", tm.mMatrixHeight);
     460           1 :                 return nullptr;
     461             :             }
     462             : 
     463             :             const auto oVariableMatrixWidths = oTM.GetArray(
     464      131571 :                 bIsTMSv2 ? "variableMatrixWidths" : "variableMatrixWidth");
     465       43857 :             if (oVariableMatrixWidths.IsValid())
     466             :             {
     467           8 :                 for (const auto &oVMW : oVariableMatrixWidths)
     468             :                 {
     469           5 :                     TileMatrix::VariableMatrixWidth vmw;
     470           5 :                     vmw.mCoalesce = oVMW.GetInteger("coalesce");
     471           5 :                     vmw.mMinTileRow = oVMW.GetInteger("minTileRow");
     472           5 :                     vmw.mMaxTileRow = oVMW.GetInteger("maxTileRow");
     473           5 :                     tm.mVariableMatrixWidthList.emplace_back(std::move(vmw));
     474             :                 }
     475             :             }
     476             : 
     477       43857 :             poTMS->mTileMatrixList.emplace_back(std::move(tm));
     478             :         }
     479             :     }
     480        2296 :     if (poTMS->mTileMatrixList.empty())
     481             :     {
     482           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No tileMatrix defined");
     483           0 :         return nullptr;
     484             :     }
     485             : 
     486        2296 :     return poTMS;
     487             : }
     488             : 
     489             : /************************************************************************/
     490             : /*                       haveAllLevelsSameTopLeft()                     */
     491             : /************************************************************************/
     492             : 
     493        4246 : bool TileMatrixSet::haveAllLevelsSameTopLeft() const
     494             : {
     495      112984 :     for (const auto &oTM : mTileMatrixList)
     496             :     {
     497      217477 :         if (oTM.mTopLeftX != mTileMatrixList[0].mTopLeftX ||
     498      108738 :             oTM.mTopLeftY != mTileMatrixList[0].mTopLeftY)
     499             :         {
     500           1 :             return false;
     501             :         }
     502             :     }
     503        4245 :     return true;
     504             : }
     505             : 
     506             : /************************************************************************/
     507             : /*                      haveAllLevelsSameTileSize()                     */
     508             : /************************************************************************/
     509             : 
     510        4246 : bool TileMatrixSet::haveAllLevelsSameTileSize() const
     511             : {
     512      112984 :     for (const auto &oTM : mTileMatrixList)
     513             :     {
     514      217477 :         if (oTM.mTileWidth != mTileMatrixList[0].mTileWidth ||
     515      108738 :             oTM.mTileHeight != mTileMatrixList[0].mTileHeight)
     516             :         {
     517           1 :             return false;
     518             :         }
     519             :     }
     520        4245 :     return true;
     521             : }
     522             : 
     523             : /************************************************************************/
     524             : /*                    hasOnlyPowerOfTwoVaryingScales()                  */
     525             : /************************************************************************/
     526             : 
     527        2308 : bool TileMatrixSet::hasOnlyPowerOfTwoVaryingScales() const
     528             : {
     529       50592 :     for (size_t i = 1; i < mTileMatrixList.size(); i++)
     530             :     {
     531       97570 :         if (mTileMatrixList[i].mScaleDenominator == 0 ||
     532       48785 :             std::fabs(mTileMatrixList[i - 1].mScaleDenominator /
     533       48785 :                           mTileMatrixList[i].mScaleDenominator -
     534             :                       2) > 1e-10)
     535             :         {
     536         501 :             return false;
     537             :         }
     538             :     }
     539        1807 :     return true;
     540             : }
     541             : 
     542             : /************************************************************************/
     543             : /*                        hasVariableMatrixWidth()                      */
     544             : /************************************************************************/
     545             : 
     546        4702 : bool TileMatrixSet::hasVariableMatrixWidth() const
     547             : {
     548      127370 :     for (const auto &oTM : mTileMatrixList)
     549             :     {
     550      122670 :         if (!oTM.mVariableMatrixWidthList.empty())
     551             :         {
     552           2 :             return true;
     553             :         }
     554             :     }
     555        4700 :     return false;
     556             : }
     557             : 
     558             : /************************************************************************/
     559             : /*                            createRaster()                            */
     560             : /************************************************************************/
     561             : 
     562             : /* static */
     563             : std::unique_ptr<TileMatrixSet>
     564           4 : TileMatrixSet::createRaster(int width, int height, int tileSize,
     565             :                             int zoomLevelCount, double dfTopLeftX,
     566             :                             double dfTopLeftY, double dfResXFull,
     567             :                             double dfResYFull, const std::string &crs)
     568             : {
     569           4 :     CPLAssert(width > 0);
     570           4 :     CPLAssert(height > 0);
     571           4 :     CPLAssert(tileSize > 0);
     572           4 :     CPLAssert(zoomLevelCount > 0);
     573           4 :     std::unique_ptr<TileMatrixSet> poTMS(new TileMatrixSet());
     574           4 :     poTMS->mTitle = "raster";
     575           4 :     poTMS->mIdentifier = "raster";
     576           4 :     poTMS->mCrs = crs;
     577           4 :     poTMS->mBbox.mCrs = poTMS->mCrs;
     578           4 :     poTMS->mBbox.mLowerCornerX = dfTopLeftX;
     579           4 :     poTMS->mBbox.mLowerCornerY = dfTopLeftY - height * dfResYFull;
     580           4 :     poTMS->mBbox.mUpperCornerX = dfTopLeftX + width * dfResYFull;
     581           4 :     poTMS->mBbox.mUpperCornerY = dfTopLeftY;
     582          10 :     for (int i = 0; i < zoomLevelCount; i++)
     583             :     {
     584          12 :         TileMatrix tm;
     585           6 :         tm.mId = CPLSPrintf("%d", i);
     586           6 :         tm.mResX = dfResXFull * (1 << (zoomLevelCount - 1 - i));
     587           6 :         tm.mResY = dfResYFull * (1 << (zoomLevelCount - 1 - i));
     588           6 :         tm.mScaleDenominator = tm.mResX / 0.28e-3;
     589           6 :         tm.mTopLeftX = poTMS->mBbox.mLowerCornerX;
     590           6 :         tm.mTopLeftY = poTMS->mBbox.mUpperCornerY;
     591           6 :         tm.mTileWidth = tileSize;
     592           6 :         tm.mTileHeight = tileSize;
     593           6 :         tm.mMatrixWidth = std::max(
     594           6 :             1, ((width >> (zoomLevelCount - 1 - i)) + tileSize - 1) / tileSize);
     595           6 :         tm.mMatrixHeight =
     596          12 :             std::max(1, ((height >> (zoomLevelCount - 1 - i)) + tileSize - 1) /
     597           6 :                             tileSize);
     598           6 :         poTMS->mTileMatrixList.emplace_back(std::move(tm));
     599             :     }
     600           4 :     return poTMS;
     601             : }
     602             : 
     603             : }  // namespace gdal
     604             : 
     605             : //! @endcond

Generated by: LCOV version 1.14