LCOV - code coverage report
Current view: top level - frmts/wms - wmsdriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 282 542 52.0 %
Date: 2024-11-21 22:18:42 Functions: 50 54 92.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  WMS Client Driver
       4             :  * Purpose:  Implementation of Dataset and RasterBand classes for WMS
       5             :  *           and other similar services.
       6             :  * Author:   Adam Nowacki, nowak@xpam.de
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2007, Adam Nowacki
      10             :  * Copyright (c) 2009-2014, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "gdal_frmts.h"
      16             : #include "wmsdriver.h"
      17             : #include "wmsmetadataset.h"
      18             : 
      19             : #include "minidriver_wms.h"
      20             : #include "minidriver_tileservice.h"
      21             : #include "minidriver_worldwind.h"
      22             : #include "minidriver_tms.h"
      23             : #include "minidriver_tiled_wms.h"
      24             : #include "minidriver_virtualearth.h"
      25             : #include "minidriver_arcgis_server.h"
      26             : #include "minidriver_iip.h"
      27             : #include "minidriver_mrf.h"
      28             : #include "minidriver_ogcapimaps.h"
      29             : #include "minidriver_ogcapicoverage.h"
      30             : #include "wmsdrivercore.h"
      31             : 
      32             : #include "cpl_json.h"
      33             : 
      34             : #include <limits>
      35             : #include <utility>
      36             : #include <algorithm>
      37             : 
      38             : //
      39             : // A static map holding seen server GetTileService responses, per process
      40             : // It makes opening and reopening rasters from the same server faster
      41             : //
      42             : GDALWMSDataset::StringMap_t GDALWMSDataset::cfg;
      43             : CPLMutex *GDALWMSDataset::cfgmtx = nullptr;
      44             : 
      45             : /************************************************************************/
      46             : /*              GDALWMSDatasetGetConfigFromURL()                        */
      47             : /************************************************************************/
      48             : 
      49           1 : static CPLXMLNode *GDALWMSDatasetGetConfigFromURL(GDALOpenInfo *poOpenInfo)
      50             : {
      51           1 :     const char *pszBaseURL = poOpenInfo->pszFilename;
      52           1 :     if (STARTS_WITH_CI(pszBaseURL, "WMS:"))
      53           1 :         pszBaseURL += strlen("WMS:");
      54             : 
      55           2 :     const CPLString osLayer = CPLURLGetValue(pszBaseURL, "LAYERS");
      56           2 :     CPLString osVersion = CPLURLGetValue(pszBaseURL, "VERSION");
      57           2 :     const CPLString osSRS = CPLURLGetValue(pszBaseURL, "SRS");
      58           2 :     const CPLString osCRS = CPLURLGetValue(pszBaseURL, "CRS");
      59           2 :     CPLString osBBOX = CPLURLGetValue(pszBaseURL, "BBOX");
      60           2 :     CPLString osFormat = CPLURLGetValue(pszBaseURL, "FORMAT");
      61           2 :     const CPLString osTransparent = CPLURLGetValue(pszBaseURL, "TRANSPARENT");
      62             : 
      63             :     /* GDAL specific extensions to alter the default settings */
      64             :     const CPLString osOverviewCount =
      65           2 :         CPLURLGetValue(pszBaseURL, "OVERVIEWCOUNT");
      66           2 :     const CPLString osTileSize = CPLURLGetValue(pszBaseURL, "TILESIZE");
      67             :     const CPLString osMinResolution =
      68           2 :         CPLURLGetValue(pszBaseURL, "MINRESOLUTION");
      69           2 :     CPLString osBBOXOrder = CPLURLGetValue(pszBaseURL, "BBOXORDER");
      70             : 
      71           2 :     CPLString osBaseURL = pszBaseURL;
      72             :     /* Remove all keywords to get base URL */
      73             : 
      74           1 :     if (osBBOXOrder.empty() && !osCRS.empty() &&
      75           0 :         VersionStringToInt(osVersion.c_str()) >= VersionStringToInt("1.3.0"))
      76             :     {
      77           0 :         OGRSpatialReference oSRS;
      78           0 :         oSRS.SetFromUserInput(
      79             :             osCRS, OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
      80           0 :         oSRS.AutoIdentifyEPSG();
      81           0 :         if (oSRS.EPSGTreatsAsLatLong() || oSRS.EPSGTreatsAsNorthingEasting())
      82             :         {
      83           0 :             osBBOXOrder = "yxYX";
      84             :         }
      85             :     }
      86             : 
      87           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "VERSION", nullptr);
      88           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "REQUEST", nullptr);
      89           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "LAYERS", nullptr);
      90           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "SRS", nullptr);
      91           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "CRS", nullptr);
      92           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "BBOX", nullptr);
      93           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "FORMAT", nullptr);
      94           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "TRANSPARENT", nullptr);
      95           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "STYLES", nullptr);
      96           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "WIDTH", nullptr);
      97           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "HEIGHT", nullptr);
      98             : 
      99           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "OVERVIEWCOUNT", nullptr);
     100           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "TILESIZE", nullptr);
     101           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "MINRESOLUTION", nullptr);
     102           1 :     osBaseURL = CPLURLAddKVP(osBaseURL, "BBOXORDER", nullptr);
     103             : 
     104           1 :     if (!osBaseURL.empty() && osBaseURL.back() == '&')
     105           1 :         osBaseURL.pop_back();
     106             : 
     107           1 :     if (osVersion.empty())
     108           0 :         osVersion = "1.1.1";
     109             : 
     110           2 :     CPLString osSRSTag;
     111           2 :     CPLString osSRSValue;
     112           1 :     if (VersionStringToInt(osVersion.c_str()) >= VersionStringToInt("1.3.0"))
     113             :     {
     114           0 :         if (!osSRS.empty())
     115             :         {
     116           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     117             :                      "WMS version 1.3 and above expects CRS however SRS was "
     118             :                      "set instead.");
     119             :         }
     120           0 :         osSRSValue = osCRS;
     121           0 :         osSRSTag = "CRS";
     122             :     }
     123             :     else
     124             :     {
     125           1 :         if (!osCRS.empty())
     126             :         {
     127           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     128             :                      "WMS version 1.1.1 and below expects SRS however CRS was "
     129             :                      "set instead.");
     130             :         }
     131           1 :         osSRSValue = osSRS;
     132           1 :         osSRSTag = "SRS";
     133             :     }
     134             : 
     135           1 :     if (osSRSValue.empty())
     136             :     {
     137           0 :         osSRSValue = "EPSG:4326";
     138             : 
     139           0 :         if (osBBOX.empty())
     140             :         {
     141           0 :             if (osBBOXOrder.compare("yxYX") == 0)
     142           0 :                 osBBOX = "-90,-180,90,180";
     143             :             else
     144           0 :                 osBBOX = "-180,-90,180,90";
     145             :         }
     146             :     }
     147             :     else
     148             :     {
     149           1 :         if (osBBOX.empty())
     150             :         {
     151           0 :             OGRSpatialReference oSRS;
     152           0 :             oSRS.SetFromUserInput(osSRSValue);
     153           0 :             oSRS.AutoIdentifyEPSG();
     154             : 
     155             :             double dfWestLongitudeDeg, dfSouthLatitudeDeg, dfEastLongitudeDeg,
     156             :                 dfNorthLatitudeDeg;
     157           0 :             if (!oSRS.GetAreaOfUse(&dfWestLongitudeDeg, &dfSouthLatitudeDeg,
     158             :                                    &dfEastLongitudeDeg, &dfNorthLatitudeDeg,
     159             :                                    nullptr))
     160             :             {
     161           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     162             :                          "Failed retrieving a default bounding box for the "
     163             :                          "requested SRS");
     164           0 :                 return nullptr;
     165             :             }
     166             :             auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
     167             :                 OGRCreateCoordinateTransformation(
     168           0 :                     OGRSpatialReference::GetWGS84SRS(), &oSRS));
     169           0 :             if (!poCT)
     170             :             {
     171           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     172             :                          "Failed creating a coordinate transformation for the "
     173             :                          "requested SRS");
     174           0 :                 return nullptr;
     175             :             }
     176           0 :             if (!poCT->Transform(1, &dfWestLongitudeDeg, &dfNorthLatitudeDeg) ||
     177           0 :                 !poCT->Transform(1, &dfEastLongitudeDeg, &dfSouthLatitudeDeg))
     178             :             {
     179           0 :                 CPLError(
     180             :                     CE_Failure, CPLE_AppDefined,
     181             :                     "Failed transforming coordinates to the requested SRS");
     182           0 :                 return nullptr;
     183             :             }
     184             :             const double dfMaxX =
     185           0 :                 std::max(dfWestLongitudeDeg, dfEastLongitudeDeg);
     186             :             const double dfMinX =
     187           0 :                 std::min(dfWestLongitudeDeg, dfEastLongitudeDeg);
     188             :             const double dfMaxY =
     189           0 :                 std::max(dfNorthLatitudeDeg, dfSouthLatitudeDeg);
     190             :             const double dfMinY =
     191           0 :                 std::min(dfNorthLatitudeDeg, dfSouthLatitudeDeg);
     192           0 :             if (osBBOXOrder.compare("yxYX") == 0)
     193             :             {
     194             :                 osBBOX = CPLSPrintf("%lf,%lf,%lf,%lf", dfMinY, dfMinX, dfMaxY,
     195           0 :                                     dfMaxX);
     196             :             }
     197             :             else
     198             :             {
     199             :                 osBBOX = CPLSPrintf("%lf,%lf,%lf,%lf", dfMinX, dfMinY, dfMaxX,
     200           0 :                                     dfMaxY);
     201             :             }
     202             :         }
     203             :     }
     204             : 
     205           1 :     char **papszTokens = CSLTokenizeStringComplex(osBBOX, ",", 0, 0);
     206           1 :     if (CSLCount(papszTokens) != 4)
     207             :     {
     208           0 :         CSLDestroy(papszTokens);
     209           0 :         return nullptr;
     210             :     }
     211           1 :     const char *pszMinX = papszTokens[0];
     212           1 :     const char *pszMinY = papszTokens[1];
     213           1 :     const char *pszMaxX = papszTokens[2];
     214           1 :     const char *pszMaxY = papszTokens[3];
     215             : 
     216           1 :     if (osBBOXOrder.compare("yxYX") == 0)
     217             :     {
     218           0 :         std::swap(pszMinX, pszMinY);
     219           0 :         std::swap(pszMaxX, pszMaxY);
     220             :     }
     221             : 
     222           1 :     double dfMinX = CPLAtofM(pszMinX);
     223           1 :     double dfMinY = CPLAtofM(pszMinY);
     224           1 :     double dfMaxX = CPLAtofM(pszMaxX);
     225           1 :     double dfMaxY = CPLAtofM(pszMaxY);
     226             : 
     227           1 :     if (dfMaxY <= dfMinY || dfMaxX <= dfMinX)
     228             :     {
     229           0 :         CSLDestroy(papszTokens);
     230           0 :         return nullptr;
     231             :     }
     232             : 
     233           1 :     int nTileSize = atoi(osTileSize);
     234           1 :     if (nTileSize <= 128 || nTileSize > 2048)
     235           1 :         nTileSize = 1024;
     236             : 
     237             :     int nXSize, nYSize;
     238             :     double dXSize, dYSize;
     239             : 
     240           1 :     int nOverviewCount = (osOverviewCount.size()) ? atoi(osOverviewCount) : 20;
     241             : 
     242           1 :     if (!osMinResolution.empty())
     243             :     {
     244           0 :         double dfMinResolution = CPLAtofM(osMinResolution);
     245             : 
     246           0 :         while (nOverviewCount > 20)
     247             :         {
     248           0 :             nOverviewCount--;
     249           0 :             dfMinResolution *= 2;
     250             :         }
     251             : 
     252             :         // Determine a suitable size that doesn't overflow max int.
     253           0 :         dXSize = ((dfMaxX - dfMinX) / dfMinResolution + 0.5);
     254           0 :         dYSize = ((dfMaxY - dfMinY) / dfMinResolution + 0.5);
     255             : 
     256           0 :         while (dXSize > (std::numeric_limits<int>::max)() ||
     257           0 :                dYSize > (std::numeric_limits<int>::max)())
     258             :         {
     259           0 :             dfMinResolution *= 2;
     260             : 
     261           0 :             dXSize = ((dfMaxX - dfMinX) / dfMinResolution + 0.5);
     262           0 :             dYSize = ((dfMaxY - dfMinY) / dfMinResolution + 0.5);
     263             :         }
     264             :     }
     265             :     else
     266             :     {
     267           1 :         double dfRatio = (dfMaxX - dfMinX) / (dfMaxY - dfMinY);
     268           1 :         if (dfRatio > 1)
     269             :         {
     270           1 :             dXSize = nTileSize;
     271           1 :             dYSize = dXSize / dfRatio;
     272             :         }
     273             :         else
     274             :         {
     275           0 :             dYSize = nTileSize;
     276           0 :             dXSize = dYSize * dfRatio;
     277             :         }
     278             : 
     279           1 :         if (nOverviewCount < 0 || nOverviewCount > 20)
     280           0 :             nOverviewCount = 20;
     281             : 
     282           1 :         dXSize = dXSize * (1 << nOverviewCount);
     283           1 :         dYSize = dYSize * (1 << nOverviewCount);
     284             : 
     285             :         // Determine a suitable size that doesn't overflow max int.
     286           2 :         while (dXSize > (std::numeric_limits<int>::max)() ||
     287           1 :                dYSize > (std::numeric_limits<int>::max)())
     288             :         {
     289           0 :             dXSize /= 2;
     290           0 :             dYSize /= 2;
     291             :         }
     292             :     }
     293             : 
     294           1 :     nXSize = (int)dXSize;
     295           1 :     nYSize = (int)dYSize;
     296             : 
     297           1 :     bool bTransparent = !osTransparent.empty() && CPLTestBool(osTransparent);
     298             : 
     299           1 :     if (osFormat.empty())
     300             :     {
     301           1 :         if (!bTransparent)
     302             :         {
     303           1 :             osFormat = "image/jpeg";
     304             :         }
     305             :         else
     306             :         {
     307           0 :             osFormat = "image/png";
     308             :         }
     309             :     }
     310             : 
     311           1 :     char *pszEscapedURL = CPLEscapeString(osBaseURL.c_str(), -1, CPLES_XML);
     312           1 :     char *pszEscapedLayerXML = CPLEscapeString(osLayer.c_str(), -1, CPLES_XML);
     313             : 
     314             :     CPLString osXML = CPLSPrintf(
     315             :         "<GDAL_WMS>\n"
     316             :         "  <Service name=\"WMS\">\n"
     317             :         "    <Version>%s</Version>\n"
     318             :         "    <ServerUrl>%s</ServerUrl>\n"
     319             :         "    <Layers>%s</Layers>\n"
     320             :         "    <%s>%s</%s>\n"
     321             :         "    <ImageFormat>%s</ImageFormat>\n"
     322             :         "    <Transparent>%s</Transparent>\n"
     323             :         "    <BBoxOrder>%s</BBoxOrder>\n"
     324             :         "  </Service>\n"
     325             :         "  <DataWindow>\n"
     326             :         "    <UpperLeftX>%s</UpperLeftX>\n"
     327             :         "    <UpperLeftY>%s</UpperLeftY>\n"
     328             :         "    <LowerRightX>%s</LowerRightX>\n"
     329             :         "    <LowerRightY>%s</LowerRightY>\n"
     330             :         "    <SizeX>%d</SizeX>\n"
     331             :         "    <SizeY>%d</SizeY>\n"
     332             :         "  </DataWindow>\n"
     333             :         "  <BandsCount>%d</BandsCount>\n"
     334             :         "  <BlockSizeX>%d</BlockSizeX>\n"
     335             :         "  <BlockSizeY>%d</BlockSizeY>\n"
     336             :         "  <OverviewCount>%d</OverviewCount>\n"
     337             :         "</GDAL_WMS>\n",
     338             :         osVersion.c_str(), pszEscapedURL, pszEscapedLayerXML, osSRSTag.c_str(),
     339             :         osSRSValue.c_str(), osSRSTag.c_str(), osFormat.c_str(),
     340             :         (bTransparent) ? "TRUE" : "FALSE",
     341           0 :         (osBBOXOrder.size()) ? osBBOXOrder.c_str() : "xyXY", pszMinX, pszMaxY,
     342             :         pszMaxX, pszMinY, nXSize, nYSize, (bTransparent) ? 4 : 3, nTileSize,
     343           2 :         nTileSize, nOverviewCount);
     344             : 
     345           1 :     CPLFree(pszEscapedURL);
     346           1 :     CPLFree(pszEscapedLayerXML);
     347             : 
     348           1 :     CSLDestroy(papszTokens);
     349             : 
     350           1 :     CPLDebug("WMS", "Opening WMS :\n%s", osXML.c_str());
     351             : 
     352           1 :     return CPLParseXMLString(osXML);
     353             : }
     354             : 
     355             : /************************************************************************/
     356             : /*              GDALWMSDatasetGetConfigFromTileMap()                    */
     357             : /************************************************************************/
     358             : 
     359           0 : static CPLXMLNode *GDALWMSDatasetGetConfigFromTileMap(CPLXMLNode *psXML)
     360             : {
     361           0 :     CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=TileMap");
     362           0 :     if (psRoot == nullptr)
     363           0 :         return nullptr;
     364             : 
     365           0 :     CPLXMLNode *psTileSets = CPLGetXMLNode(psRoot, "TileSets");
     366           0 :     if (psTileSets == nullptr)
     367           0 :         return nullptr;
     368             : 
     369           0 :     const char *pszURL = CPLGetXMLValue(psRoot, "tilemapservice", nullptr);
     370             : 
     371           0 :     int bCanChangeURL = TRUE;
     372             : 
     373           0 :     CPLString osURL;
     374           0 :     if (pszURL)
     375             :     {
     376           0 :         osURL = pszURL;
     377             :         /* Special hack for
     378             :          * http://tilecache.osgeo.org/wms-c/Basic.py/1.0.0/basic/ */
     379           0 :         if (strlen(pszURL) > 10 &&
     380           0 :             STARTS_WITH(pszURL,
     381           0 :                         "http://tilecache.osgeo.org/wms-c/Basic.py/1.0.0/") &&
     382           0 :             strcmp(pszURL + strlen(pszURL) - strlen("1.0.0/"), "1.0.0/") == 0)
     383             :         {
     384           0 :             osURL.resize(strlen(pszURL) - strlen("1.0.0/"));
     385           0 :             bCanChangeURL = FALSE;
     386             :         }
     387           0 :         osURL += "${z}/${x}/${y}.${format}";
     388             :     }
     389             : 
     390           0 :     const char *pszSRS = CPLGetXMLValue(psRoot, "SRS", nullptr);
     391           0 :     if (pszSRS == nullptr)
     392           0 :         return nullptr;
     393             : 
     394           0 :     CPLXMLNode *psBoundingBox = CPLGetXMLNode(psRoot, "BoundingBox");
     395           0 :     if (psBoundingBox == nullptr)
     396           0 :         return nullptr;
     397             : 
     398           0 :     const char *pszMinX = CPLGetXMLValue(psBoundingBox, "minx", nullptr);
     399           0 :     const char *pszMinY = CPLGetXMLValue(psBoundingBox, "miny", nullptr);
     400           0 :     const char *pszMaxX = CPLGetXMLValue(psBoundingBox, "maxx", nullptr);
     401           0 :     const char *pszMaxY = CPLGetXMLValue(psBoundingBox, "maxy", nullptr);
     402           0 :     if (pszMinX == nullptr || pszMinY == nullptr || pszMaxX == nullptr ||
     403             :         pszMaxY == nullptr)
     404           0 :         return nullptr;
     405             : 
     406           0 :     double dfMinX = CPLAtofM(pszMinX);
     407           0 :     double dfMinY = CPLAtofM(pszMinY);
     408           0 :     double dfMaxX = CPLAtofM(pszMaxX);
     409           0 :     double dfMaxY = CPLAtofM(pszMaxY);
     410           0 :     if (dfMaxY <= dfMinY || dfMaxX <= dfMinX)
     411           0 :         return nullptr;
     412             : 
     413           0 :     CPLXMLNode *psTileFormat = CPLGetXMLNode(psRoot, "TileFormat");
     414           0 :     if (psTileFormat == nullptr)
     415           0 :         return nullptr;
     416             : 
     417           0 :     const char *pszTileWidth = CPLGetXMLValue(psTileFormat, "width", nullptr);
     418           0 :     const char *pszTileHeight = CPLGetXMLValue(psTileFormat, "height", nullptr);
     419             :     const char *pszTileFormat =
     420           0 :         CPLGetXMLValue(psTileFormat, "extension", nullptr);
     421           0 :     if (pszTileWidth == nullptr || pszTileHeight == nullptr ||
     422             :         pszTileFormat == nullptr)
     423           0 :         return nullptr;
     424             : 
     425           0 :     int nTileWidth = atoi(pszTileWidth);
     426           0 :     int nTileHeight = atoi(pszTileHeight);
     427           0 :     if (nTileWidth < 128 || nTileHeight < 128)
     428           0 :         return nullptr;
     429             : 
     430           0 :     CPLXMLNode *psIter = psTileSets->psChild;
     431           0 :     int nLevelCount = 0;
     432           0 :     double dfPixelSize = 0;
     433           0 :     for (; psIter != nullptr; psIter = psIter->psNext)
     434             :     {
     435           0 :         if (psIter->eType == CXT_Element && EQUAL(psIter->pszValue, "TileSet"))
     436             :         {
     437           0 :             const char *pszOrder = CPLGetXMLValue(psIter, "order", nullptr);
     438           0 :             if (pszOrder == nullptr)
     439             :             {
     440           0 :                 CPLDebug("WMS", "Cannot find order attribute");
     441           0 :                 return nullptr;
     442             :             }
     443           0 :             if (atoi(pszOrder) != nLevelCount)
     444             :             {
     445           0 :                 CPLDebug("WMS", "Expected order=%d, got %s", nLevelCount,
     446             :                          pszOrder);
     447           0 :                 return nullptr;
     448             :             }
     449             : 
     450           0 :             const char *pszHref = CPLGetXMLValue(psIter, "href", nullptr);
     451           0 :             if (nLevelCount == 0 && pszHref != nullptr)
     452             :             {
     453           0 :                 if (bCanChangeURL && strlen(pszHref) > 10 &&
     454           0 :                     strcmp(pszHref + strlen(pszHref) - strlen("/0"), "/0") == 0)
     455             :                 {
     456           0 :                     osURL = pszHref;
     457           0 :                     osURL.resize(strlen(pszHref) - strlen("/0"));
     458           0 :                     osURL += "/${z}/${x}/${y}.${format}";
     459             :                 }
     460             :             }
     461             :             const char *pszUnitsPerPixel =
     462           0 :                 CPLGetXMLValue(psIter, "units-per-pixel", nullptr);
     463           0 :             if (pszUnitsPerPixel == nullptr)
     464           0 :                 return nullptr;
     465           0 :             dfPixelSize = CPLAtofM(pszUnitsPerPixel);
     466             : 
     467           0 :             nLevelCount++;
     468             :         }
     469             :     }
     470             : 
     471           0 :     if (nLevelCount == 0 || osURL.empty())
     472           0 :         return nullptr;
     473             : 
     474           0 :     int nXSize = 0;
     475           0 :     int nYSize = 0;
     476             : 
     477           0 :     while (nLevelCount > 0)
     478             :     {
     479           0 :         GIntBig nXSizeBig = (GIntBig)((dfMaxX - dfMinX) / dfPixelSize + 0.5);
     480           0 :         GIntBig nYSizeBig = (GIntBig)((dfMaxY - dfMinY) / dfPixelSize + 0.5);
     481           0 :         if (nXSizeBig < INT_MAX && nYSizeBig < INT_MAX)
     482             :         {
     483           0 :             nXSize = (int)nXSizeBig;
     484           0 :             nYSize = (int)nYSizeBig;
     485           0 :             break;
     486             :         }
     487           0 :         CPLDebug(
     488             :             "WMS",
     489             :             "Dropping one overview level so raster size fits into 32bit...");
     490           0 :         dfPixelSize *= 2;
     491           0 :         nLevelCount--;
     492             :     }
     493             : 
     494           0 :     char *pszEscapedURL = CPLEscapeString(osURL.c_str(), -1, CPLES_XML);
     495             : 
     496             :     CPLString osXML = CPLSPrintf("<GDAL_WMS>\n"
     497             :                                  "  <Service name=\"TMS\">\n"
     498             :                                  "    <ServerUrl>%s</ServerUrl>\n"
     499             :                                  "    <Format>%s</Format>\n"
     500             :                                  "  </Service>\n"
     501             :                                  "  <DataWindow>\n"
     502             :                                  "    <UpperLeftX>%s</UpperLeftX>\n"
     503             :                                  "    <UpperLeftY>%s</UpperLeftY>\n"
     504             :                                  "    <LowerRightX>%s</LowerRightX>\n"
     505             :                                  "    <LowerRightY>%s</LowerRightY>\n"
     506             :                                  "    <TileLevel>%d</TileLevel>\n"
     507             :                                  "    <SizeX>%d</SizeX>\n"
     508             :                                  "    <SizeY>%d</SizeY>\n"
     509             :                                  "  </DataWindow>\n"
     510             :                                  "  <Projection>%s</Projection>\n"
     511             :                                  "  <BlockSizeX>%d</BlockSizeX>\n"
     512             :                                  "  <BlockSizeY>%d</BlockSizeY>\n"
     513             :                                  "  <BandsCount>%d</BandsCount>\n"
     514             :                                  "</GDAL_WMS>\n",
     515             :                                  pszEscapedURL, pszTileFormat, pszMinX, pszMaxY,
     516             :                                  pszMaxX, pszMinY, nLevelCount - 1, nXSize,
     517           0 :                                  nYSize, pszSRS, nTileWidth, nTileHeight, 3);
     518           0 :     CPLDebug("WMS", "Opening TMS :\n%s", osXML.c_str());
     519             : 
     520           0 :     CPLFree(pszEscapedURL);
     521             : 
     522           0 :     return CPLParseXMLString(osXML);
     523             : }
     524             : 
     525             : /************************************************************************/
     526             : /*             GDALWMSDatasetGetConfigFromArcGISJSON()                  */
     527             : /************************************************************************/
     528             : 
     529           1 : static CPLXMLNode *GDALWMSDatasetGetConfigFromArcGISJSON(const char *pszURL,
     530             :                                                          const char *pszContent)
     531             : {
     532           2 :     CPLJSONDocument oDoc;
     533           1 :     if (!oDoc.LoadMemory(std::string(pszContent)))
     534           0 :         return nullptr;
     535           2 :     auto oRoot(oDoc.GetRoot());
     536           3 :     auto oTileInfo(oRoot["tileInfo"]);
     537           1 :     if (!oTileInfo.IsValid())
     538             :     {
     539           0 :         CPLDebug("WMS", "Did not get tileInfo");
     540           0 :         return nullptr;
     541             :     }
     542           1 :     int nTileWidth = oTileInfo.GetInteger("cols", -1);
     543           1 :     int nTileHeight = oTileInfo.GetInteger("rows", -1);
     544             : 
     545           3 :     auto oSpatialReference(oTileInfo["spatialReference"]);
     546           1 :     if (!oSpatialReference.IsValid())
     547             :     {
     548           0 :         CPLDebug("WMS", "Did not get spatialReference");
     549           0 :         return nullptr;
     550             :     }
     551           1 :     int nWKID = oSpatialReference.GetInteger("wkid", -1);
     552           1 :     int nLatestWKID = oSpatialReference.GetInteger("latestWkid", -1);
     553           3 :     CPLString osWKT(oSpatialReference.GetString("wkt"));
     554             : 
     555           3 :     auto oOrigin(oTileInfo["origin"]);
     556           1 :     if (!oOrigin.IsValid())
     557             :     {
     558           0 :         CPLDebug("WMS", "Did not get origin");
     559           0 :         return nullptr;
     560             :     }
     561             :     double dfMinX =
     562           1 :         oOrigin.GetDouble("x", std::numeric_limits<double>::infinity());
     563             :     double dfMaxY =
     564           1 :         oOrigin.GetDouble("y", std::numeric_limits<double>::infinity());
     565             : 
     566           3 :     auto oLods(oTileInfo["lods"].ToArray());
     567           1 :     if (!oLods.IsValid())
     568             :     {
     569           0 :         CPLDebug("WMS", "Did not get lods");
     570           0 :         return nullptr;
     571             :     }
     572           1 :     double dfBaseResolution = 0.0;
     573           1 :     for (int i = 0; i < oLods.Size(); i++)
     574             :     {
     575           1 :         if (oLods[i].GetInteger("level", -1) == 0)
     576             :         {
     577           1 :             dfBaseResolution = oLods[i].GetDouble("resolution");
     578           1 :             break;
     579             :         }
     580             :     }
     581             : 
     582           1 :     int nLevelCount = oLods.Size() - 1;
     583           1 :     if (nLevelCount < 1)
     584             :     {
     585           0 :         CPLDebug("WMS", "Did not get levels");
     586           0 :         return nullptr;
     587             :     }
     588             : 
     589           1 :     if (nTileWidth <= 0)
     590             :     {
     591           0 :         CPLDebug("WMS", "Did not get tile width");
     592           0 :         return nullptr;
     593             :     }
     594           1 :     if (nTileHeight <= 0)
     595             :     {
     596           0 :         CPLDebug("WMS", "Did not get tile height");
     597           0 :         return nullptr;
     598             :     }
     599           1 :     if (nWKID <= 0 && osWKT.empty())
     600             :     {
     601           0 :         CPLDebug("WMS", "Did not get WKID");
     602           0 :         return nullptr;
     603             :     }
     604           1 :     if (dfMinX == std::numeric_limits<double>::infinity())
     605             :     {
     606           0 :         CPLDebug("WMS", "Did not get min x");
     607           0 :         return nullptr;
     608             :     }
     609           1 :     if (dfMaxY == std::numeric_limits<double>::infinity())
     610             :     {
     611           0 :         CPLDebug("WMS", "Did not get max y");
     612           0 :         return nullptr;
     613             :     }
     614             : 
     615           1 :     if (nLatestWKID > 0)
     616           1 :         nWKID = nLatestWKID;
     617             : 
     618           1 :     if (nWKID == 102100)
     619           0 :         nWKID = 3857;
     620             : 
     621           1 :     const char *pszEndURL = strstr(pszURL, "/?f=json");
     622           1 :     if (pszEndURL == nullptr)
     623           1 :         pszEndURL = strstr(pszURL, "?f=json");
     624           1 :     CPLAssert(pszEndURL);
     625           2 :     CPLString osURL(pszURL);
     626           1 :     osURL.resize(pszEndURL - pszURL);
     627             : 
     628           1 :     double dfMaxX = dfMinX + dfBaseResolution * nTileWidth;
     629           1 :     double dfMinY = dfMaxY - dfBaseResolution * nTileHeight;
     630             : 
     631           1 :     int nTileCountX = 1;
     632           1 :     if (fabs(dfMinX - -180) < 1e-4 && fabs(dfMaxY - 90) < 1e-4 &&
     633           0 :         fabs(dfMinY - -90) < 1e-4)
     634             :     {
     635           0 :         nTileCountX = 2;
     636           0 :         dfMaxX = 180;
     637             :     }
     638             : 
     639           1 :     const int nLevelCountOri = nLevelCount;
     640           2 :     while ((double)nTileCountX * nTileWidth * (1 << nLevelCount) > INT_MAX)
     641           1 :         nLevelCount--;
     642           1 :     while (nLevelCount >= 0 &&
     643           1 :            (double)nTileHeight * (1 << nLevelCount) > INT_MAX)
     644           0 :         nLevelCount--;
     645           1 :     if (nLevelCount != nLevelCountOri)
     646           1 :         CPLDebug("WMS",
     647             :                  "Had to limit level count to %d instead of %d to stay within "
     648             :                  "GDAL raster size limits",
     649             :                  nLevelCount, nLevelCountOri);
     650             : 
     651           2 :     CPLString osEscapedWKT;
     652           1 :     if (nWKID < 0 && !osWKT.empty())
     653             :     {
     654           0 :         OGRSpatialReference oSRS;
     655           0 :         oSRS.importFromWkt(osWKT);
     656             : 
     657           0 :         const auto poSRSMatch = oSRS.FindBestMatch(100);
     658           0 :         if (poSRSMatch)
     659             :         {
     660           0 :             oSRS = *poSRSMatch;
     661           0 :             poSRSMatch->Release();
     662           0 :             const char *pszAuthName = oSRS.GetAuthorityName(nullptr);
     663           0 :             const char *pszCode = oSRS.GetAuthorityCode(nullptr);
     664           0 :             if (pszAuthName && EQUAL(pszAuthName, "EPSG") && pszCode)
     665           0 :                 nWKID = atoi(pszCode);
     666             :         }
     667             : 
     668           0 :         char *pszWKT = nullptr;
     669           0 :         oSRS.exportToWkt(&pszWKT);
     670           0 :         osWKT = pszWKT;
     671           0 :         CPLFree(pszWKT);
     672             : 
     673           0 :         char *pszEscaped = CPLEscapeString(osWKT, -1, CPLES_XML);
     674           0 :         osEscapedWKT = pszEscaped;
     675           0 :         CPLFree(pszEscaped);
     676             :     }
     677             : 
     678             :     CPLString osXML = CPLSPrintf(
     679             :         "<GDAL_WMS>\n"
     680             :         "  <Service name=\"TMS\">\n"
     681             :         "    <ServerUrl>%s/tile/${z}/${y}/${x}</ServerUrl>\n"
     682             :         "  </Service>\n"
     683             :         "  <DataWindow>\n"
     684             :         "    <UpperLeftX>%.8f</UpperLeftX>\n"
     685             :         "    <UpperLeftY>%.8f</UpperLeftY>\n"
     686             :         "    <LowerRightX>%.8f</LowerRightX>\n"
     687             :         "    <LowerRightY>%.8f</LowerRightY>\n"
     688             :         "    <TileLevel>%d</TileLevel>\n"
     689             :         "    <TileCountX>%d</TileCountX>\n"
     690             :         "    <YOrigin>top</YOrigin>\n"
     691             :         "  </DataWindow>\n"
     692             :         "  <Projection>%s</Projection>\n"
     693             :         "  <BlockSizeX>%d</BlockSizeX>\n"
     694             :         "  <BlockSizeY>%d</BlockSizeY>\n"
     695             :         "  <Cache/>\n"
     696             :         "</GDAL_WMS>\n",
     697             :         osURL.c_str(), dfMinX, dfMaxY, dfMaxX, dfMinY, nLevelCount, nTileCountX,
     698           1 :         nWKID > 0 ? CPLSPrintf("EPSG:%d", nWKID) : osEscapedWKT.c_str(),
     699           3 :         nTileWidth, nTileHeight);
     700           1 :     CPLDebug("WMS", "Opening TMS :\n%s", osXML.c_str());
     701             : 
     702           1 :     return CPLParseXMLString(osXML);
     703             : }
     704             : 
     705             : /************************************************************************/
     706             : /*                                 Open()                               */
     707             : /************************************************************************/
     708             : 
     709         352 : GDALDataset *GDALWMSDataset::Open(GDALOpenInfo *poOpenInfo)
     710             : {
     711         352 :     CPLXMLNode *config = nullptr;
     712         352 :     CPLErr ret = CE_None;
     713             : 
     714         352 :     const char *pszFilename = poOpenInfo->pszFilename;
     715         352 :     const char *pabyHeader = (const char *)poOpenInfo->pabyHeader;
     716             : 
     717         352 :     if (!WMSDriverIdentify(poOpenInfo))
     718           0 :         return nullptr;
     719             : 
     720         352 :     if (poOpenInfo->nHeaderBytes == 0 &&
     721         337 :         STARTS_WITH_CI(pszFilename, "<GDAL_WMS>"))
     722             :     {
     723         333 :         config = CPLParseXMLString(pszFilename);
     724             :     }
     725          19 :     else if (poOpenInfo->nHeaderBytes >= 10 &&
     726          15 :              STARTS_WITH_CI(pabyHeader, "<GDAL_WMS>"))
     727             :     {
     728          13 :         config = CPLParseXMLFile(pszFilename);
     729             :     }
     730           6 :     else if (poOpenInfo->nHeaderBytes == 0 &&
     731           4 :              (STARTS_WITH_CI(pszFilename, "WMS:http") ||
     732           3 :               STARTS_WITH_CI(pszFilename, "http")) &&
     733           3 :              (strstr(pszFilename, "/MapServer?f=json") != nullptr ||
     734           2 :               strstr(pszFilename, "/MapServer/?f=json") != nullptr ||
     735           2 :               strstr(pszFilename, "/ImageServer?f=json") != nullptr ||
     736           2 :               strstr(pszFilename, "/ImageServer/?f=json") != nullptr))
     737             :     {
     738           1 :         if (STARTS_WITH_CI(pszFilename, "WMS:http"))
     739           0 :             pszFilename += 4;
     740           1 :         CPLString osURL(pszFilename);
     741           1 :         if (strstr(pszFilename, "&pretty=true") == nullptr)
     742           0 :             osURL += "&pretty=true";
     743           1 :         CPLHTTPResult *psResult = CPLHTTPFetch(osURL.c_str(), nullptr);
     744           1 :         if (psResult == nullptr)
     745           0 :             return nullptr;
     746           1 :         if (psResult->pabyData == nullptr)
     747             :         {
     748           0 :             CPLHTTPDestroyResult(psResult);
     749           0 :             return nullptr;
     750             :         }
     751           1 :         config = GDALWMSDatasetGetConfigFromArcGISJSON(
     752           1 :             osURL, (const char *)psResult->pabyData);
     753           2 :         CPLHTTPDestroyResult(psResult);
     754             :     }
     755             : 
     756           8 :     else if (poOpenInfo->nHeaderBytes == 0 &&
     757           3 :              (STARTS_WITH_CI(pszFilename, "WMS:") ||
     758           7 :               CPLString(pszFilename).ifind("SERVICE=WMS") !=
     759           2 :                   std::string::npos ||
     760           2 :               (poOpenInfo->IsSingleAllowedDriver("WMS") &&
     761           1 :                (STARTS_WITH(poOpenInfo->pszFilename, "http://") ||
     762           0 :                 STARTS_WITH(poOpenInfo->pszFilename, "https://")))))
     763             :     {
     764           2 :         CPLString osLayers = CPLURLGetValue(pszFilename, "LAYERS");
     765           2 :         CPLString osRequest = CPLURLGetValue(pszFilename, "REQUEST");
     766           2 :         if (!osLayers.empty())
     767           1 :             config = GDALWMSDatasetGetConfigFromURL(poOpenInfo);
     768           1 :         else if (EQUAL(osRequest, "GetTileService"))
     769           0 :             return GDALWMSMetaDataset::DownloadGetTileService(poOpenInfo);
     770             :         else
     771           1 :             return GDALWMSMetaDataset::DownloadGetCapabilities(poOpenInfo);
     772             :     }
     773           3 :     else if (poOpenInfo->nHeaderBytes != 0 &&
     774           2 :              (strstr(pabyHeader, "<WMT_MS_Capabilities") != nullptr ||
     775           2 :               strstr(pabyHeader, "<WMS_Capabilities") != nullptr ||
     776           2 :               strstr(pabyHeader, "<!DOCTYPE WMT_MS_Capabilities") != nullptr))
     777             :     {
     778           0 :         CPLXMLNode *psXML = CPLParseXMLFile(pszFilename);
     779           0 :         if (psXML == nullptr)
     780           0 :             return nullptr;
     781           0 :         GDALDataset *poRet = GDALWMSMetaDataset::AnalyzeGetCapabilities(psXML);
     782           0 :         CPLDestroyXMLNode(psXML);
     783           0 :         return poRet;
     784             :     }
     785           3 :     else if (poOpenInfo->nHeaderBytes != 0 &&
     786           2 :              strstr(pabyHeader, "<WMS_Tile_Service") != nullptr)
     787             :     {
     788           2 :         CPLXMLNode *psXML = CPLParseXMLFile(pszFilename);
     789           2 :         if (psXML == nullptr)
     790           0 :             return nullptr;
     791             :         GDALDataset *poRet =
     792           2 :             GDALWMSMetaDataset::AnalyzeGetTileService(psXML, poOpenInfo);
     793           2 :         CPLDestroyXMLNode(psXML);
     794           2 :         return poRet;
     795             :     }
     796           1 :     else if (poOpenInfo->nHeaderBytes != 0 &&
     797           0 :              strstr(pabyHeader, "<TileMap version=\"1.0.0\"") != nullptr)
     798             :     {
     799           0 :         CPLXMLNode *psXML = CPLParseXMLFile(pszFilename);
     800           0 :         if (psXML == nullptr)
     801           0 :             return nullptr;
     802           0 :         config = GDALWMSDatasetGetConfigFromTileMap(psXML);
     803           0 :         CPLDestroyXMLNode(psXML);
     804             :     }
     805           1 :     else if (poOpenInfo->nHeaderBytes != 0 &&
     806           0 :              strstr(pabyHeader, "<Services") != nullptr &&
     807           0 :              strstr(pabyHeader, "<TileMapService version=\"1.0") != nullptr)
     808             :     {
     809           0 :         CPLXMLNode *psXML = CPLParseXMLFile(pszFilename);
     810           0 :         if (psXML == nullptr)
     811           0 :             return nullptr;
     812           0 :         CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=Services");
     813           0 :         GDALDataset *poRet = nullptr;
     814           0 :         if (psRoot)
     815             :         {
     816             :             CPLXMLNode *psTileMapService =
     817           0 :                 CPLGetXMLNode(psRoot, "TileMapService");
     818           0 :             if (psTileMapService)
     819             :             {
     820             :                 const char *pszHref =
     821           0 :                     CPLGetXMLValue(psTileMapService, "href", nullptr);
     822           0 :                 if (pszHref)
     823             :                 {
     824           0 :                     poRet = (GDALDataset *)GDALOpen(pszHref, GA_ReadOnly);
     825             :                 }
     826             :             }
     827             :         }
     828           0 :         CPLDestroyXMLNode(psXML);
     829           0 :         return poRet;
     830             :     }
     831           1 :     else if (poOpenInfo->nHeaderBytes != 0 &&
     832           0 :              strstr(pabyHeader, "<TileMapService version=\"1.0.0\"") != nullptr)
     833             :     {
     834           0 :         CPLXMLNode *psXML = CPLParseXMLFile(pszFilename);
     835           0 :         if (psXML == nullptr)
     836           0 :             return nullptr;
     837           0 :         GDALDataset *poRet = GDALWMSMetaDataset::AnalyzeTileMapService(psXML);
     838           0 :         CPLDestroyXMLNode(psXML);
     839           0 :         return poRet;
     840             :     }
     841           1 :     else if (poOpenInfo->nHeaderBytes == 0 &&
     842           1 :              STARTS_WITH_CI(pszFilename, "AGS:"))
     843             :     {
     844           0 :         return nullptr;
     845             :     }
     846           1 :     else if (poOpenInfo->nHeaderBytes == 0 &&
     847           1 :              STARTS_WITH_CI(pszFilename, "IIP:"))
     848             :     {
     849           1 :         CPLString osURL(pszFilename + 4);
     850           1 :         osURL += "&obj=Basic-Info";
     851           1 :         CPLHTTPResult *psResult = CPLHTTPFetch(osURL.c_str(), nullptr);
     852           1 :         if (psResult == nullptr)
     853           0 :             return nullptr;
     854           1 :         if (psResult->pabyData == nullptr)
     855             :         {
     856           0 :             CPLHTTPDestroyResult(psResult);
     857           0 :             return nullptr;
     858             :         }
     859             :         int nXSize, nYSize;
     860             :         const char *pszMaxSize =
     861           1 :             strstr((const char *)psResult->pabyData, "Max-size:");
     862             :         const char *pszResolutionNumber =
     863           1 :             strstr((const char *)psResult->pabyData, "Resolution-number:");
     864           2 :         if (pszMaxSize &&
     865           1 :             sscanf(pszMaxSize + strlen("Max-size:"), "%d %d", &nXSize,
     866           2 :                    &nYSize) == 2 &&
     867             :             pszResolutionNumber)
     868             :         {
     869             :             int nResolutions =
     870           1 :                 atoi(pszResolutionNumber + strlen("Resolution-number:"));
     871             :             char *pszEscapedURL =
     872           1 :                 CPLEscapeString(pszFilename + 4, -1, CPLES_XML);
     873             :             CPLString osXML =
     874             :                 CPLSPrintf("<GDAL_WMS>"
     875             :                            "    <Service name=\"IIP\">"
     876             :                            "        <ServerUrl>%s</ServerUrl>"
     877             :                            "    </Service>"
     878             :                            "    <DataWindow>"
     879             :                            "        <SizeX>%d</SizeX>"
     880             :                            "        <SizeY>%d</SizeY>"
     881             :                            "        <TileLevel>%d</TileLevel>"
     882             :                            "    </DataWindow>"
     883             :                            "    <BlockSizeX>256</BlockSizeX>"
     884             :                            "    <BlockSizeY>256</BlockSizeY>"
     885             :                            "    <BandsCount>3</BandsCount>"
     886             :                            "    <Cache />"
     887             :                            "</GDAL_WMS>",
     888           2 :                            pszEscapedURL, nXSize, nYSize, nResolutions - 1);
     889           1 :             config = CPLParseXMLString(osXML);
     890           1 :             CPLFree(pszEscapedURL);
     891             :         }
     892           2 :         CPLHTTPDestroyResult(psResult);
     893             :     }
     894             :     else
     895           0 :         return nullptr;
     896         349 :     if (config == nullptr)
     897           0 :         return nullptr;
     898             : 
     899             :     /* -------------------------------------------------------------------- */
     900             :     /*      Confirm the requested access is supported.                      */
     901             :     /* -------------------------------------------------------------------- */
     902         349 :     if (poOpenInfo->eAccess == GA_Update)
     903             :     {
     904           0 :         CPLDestroyXMLNode(config);
     905           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     906             :                  "The WMS poDriver does not support update access to existing"
     907             :                  " datasets.\n");
     908           0 :         return nullptr;
     909             :     }
     910             : 
     911         349 :     GDALWMSDataset *ds = new GDALWMSDataset();
     912         349 :     ret = ds->Initialize(config, poOpenInfo->papszOpenOptions);
     913         349 :     if (ret != CE_None)
     914             :     {
     915           2 :         delete ds;
     916           2 :         ds = nullptr;
     917             :     }
     918         349 :     CPLDestroyXMLNode(config);
     919             : 
     920             :     /* -------------------------------------------------------------------- */
     921             :     /*      Initialize any PAM information.                                 */
     922             :     /* -------------------------------------------------------------------- */
     923         349 :     if (ds != nullptr)
     924             :     {
     925         347 :         if (poOpenInfo->pszFilename && poOpenInfo->pszFilename[0] == '<')
     926             :         {
     927         332 :             ds->nPamFlags = GPF_DISABLED;
     928             :         }
     929             :         else
     930             :         {
     931          15 :             ds->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
     932          15 :             ds->SetDescription(poOpenInfo->pszFilename);
     933          15 :             ds->TryLoadXML();
     934             :         }
     935             :     }
     936             : 
     937         349 :     return ds;
     938             : }
     939             : 
     940             : /************************************************************************/
     941             : /*                             GetServerConfig()                        */
     942             : /************************************************************************/
     943             : 
     944           2 : const char *GDALWMSDataset::GetServerConfig(const char *URI,
     945             :                                             char **papszHTTPOptions)
     946             : {
     947           4 :     CPLMutexHolder oHolder(&cfgmtx);
     948             : 
     949             :     // Might have it cached already
     950           2 :     if (cfg.end() != cfg.find(URI))
     951           1 :         return cfg.find(URI)->second;
     952             : 
     953           1 :     CPLHTTPResult *psResult = CPLHTTPFetch(URI, papszHTTPOptions);
     954             : 
     955           1 :     if (nullptr == psResult)
     956           0 :         return nullptr;
     957             : 
     958             :     // Capture the result in buffer, get rid of http result
     959           1 :     if ((psResult->nStatus == 0) && (nullptr != psResult->pabyData) &&
     960           1 :         ('\0' != psResult->pabyData[0]))
     961           2 :         cfg.insert(make_pair(
     962           1 :             URI, static_cast<CPLString>(
     963           2 :                      reinterpret_cast<const char *>(psResult->pabyData))));
     964             : 
     965           1 :     CPLHTTPDestroyResult(psResult);
     966             : 
     967           1 :     if (cfg.end() != cfg.find(URI))
     968           1 :         return cfg.find(URI)->second;
     969             :     else
     970           0 :         return nullptr;
     971             : }
     972             : 
     973             : // Empties the server configuration cache and removes the mutex
     974           0 : void GDALWMSDataset::ClearConfigCache()
     975             : {
     976             :     // Obviously not thread safe, should only be called when no WMS files are
     977             :     // being opened
     978           0 :     cfg.clear();
     979           0 :     DestroyCfgMutex();
     980           0 : }
     981             : 
     982           5 : void GDALWMSDataset::DestroyCfgMutex()
     983             : {
     984           5 :     if (cfgmtx)
     985           0 :         CPLDestroyMutex(cfgmtx);
     986           5 :     cfgmtx = nullptr;
     987           5 : }
     988             : 
     989             : /************************************************************************/
     990             : /*                             CreateCopy()                             */
     991             : /************************************************************************/
     992             : 
     993          19 : GDALDataset *GDALWMSDataset::CreateCopy(const char *pszFilename,
     994             :                                         GDALDataset *poSrcDS,
     995             :                                         CPL_UNUSED int bStrict,
     996             :                                         CPL_UNUSED char **papszOptions,
     997             :                                         CPL_UNUSED GDALProgressFunc pfnProgress,
     998             :                                         CPL_UNUSED void *pProgressData)
     999             : {
    1000          38 :     if (poSrcDS->GetDriver() == nullptr ||
    1001          19 :         !EQUAL(poSrcDS->GetDriver()->GetDescription(), "WMS"))
    1002             :     {
    1003          18 :         CPLError(CE_Failure, CPLE_NotSupported,
    1004             :                  "Source dataset must be a WMS dataset");
    1005          18 :         return nullptr;
    1006             :     }
    1007             : 
    1008           1 :     const char *pszXML = poSrcDS->GetMetadataItem("XML", "WMS");
    1009           1 :     if (pszXML == nullptr)
    1010             :     {
    1011           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1012             :                  "Cannot get XML definition of source WMS dataset");
    1013           0 :         return nullptr;
    1014             :     }
    1015             : 
    1016           1 :     VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
    1017           1 :     if (fp == nullptr)
    1018           0 :         return nullptr;
    1019             : 
    1020           1 :     VSIFWriteL(pszXML, 1, strlen(pszXML), fp);
    1021           1 :     VSIFCloseL(fp);
    1022             : 
    1023           2 :     GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
    1024           1 :     return Open(&oOpenInfo);
    1025             : }
    1026             : 
    1027           5 : void WMSDeregister(CPL_UNUSED GDALDriver *d)
    1028             : {
    1029           5 :     GDALWMSDataset::DestroyCfgMutex();
    1030           5 : }
    1031             : 
    1032             : // Define a minidriver factory type, create one and register it
    1033             : #define RegisterMinidriver(name)                                               \
    1034             :     class WMSMiniDriverFactory_##name : public WMSMiniDriverFactory            \
    1035             :     {                                                                          \
    1036             :       public:                                                                  \
    1037             :         WMSMiniDriverFactory_##name()                                          \
    1038             :         {                                                                      \
    1039             :             m_name = CPLString(#name);                                         \
    1040             :         }                                                                      \
    1041             :         virtual ~WMSMiniDriverFactory_##name()                                 \
    1042             :         {                                                                      \
    1043             :         }                                                                      \
    1044             :         virtual WMSMiniDriver *New() const override                            \
    1045             :         {                                                                      \
    1046             :             return new WMSMiniDriver_##name;                                   \
    1047             :         }                                                                      \
    1048             :     };                                                                         \
    1049             :     WMSRegisterMiniDriverFactory(new WMSMiniDriverFactory_##name());
    1050             : 
    1051             : /************************************************************************/
    1052             : /*                          GDALRegister_WMS()                          */
    1053             : /************************************************************************/
    1054             : 
    1055             : //
    1056             : // Do not define any open options here!
    1057             : // Doing so will enable checking the open options, which will generate warnings
    1058             : // for undeclared options which may be handled by individual minidrivers
    1059             : //
    1060             : 
    1061           9 : void GDALRegister_WMS()
    1062             : 
    1063             : {
    1064           9 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
    1065           0 :         return;
    1066             : 
    1067             :     // Register all minidrivers here
    1068          40 :     RegisterMinidriver(WMS);
    1069          37 :     RegisterMinidriver(TileService);
    1070          36 :     RegisterMinidriver(WorldWind);
    1071         369 :     RegisterMinidriver(TMS);
    1072          40 :     RegisterMinidriver(TiledWMS);
    1073          37 :     RegisterMinidriver(VirtualEarth);
    1074          38 :     RegisterMinidriver(AGS);
    1075          37 :     RegisterMinidriver(IIP);
    1076          36 :     RegisterMinidriver(MRF);
    1077          37 :     RegisterMinidriver(OGCAPIMaps);
    1078          37 :     RegisterMinidriver(OGCAPICoverage);
    1079             : 
    1080           9 :     GDALDriver *poDriver = new GDALDriver();
    1081           9 :     WMSDriverSetCommonMetadata(poDriver);
    1082             : 
    1083           9 :     poDriver->pfnOpen = GDALWMSDataset::Open;
    1084           9 :     poDriver->pfnUnloadDriver = WMSDeregister;
    1085           9 :     poDriver->pfnCreateCopy = GDALWMSDataset::CreateCopy;
    1086             : 
    1087           9 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1088             : }

Generated by: LCOV version 1.14