LCOV - code coverage report
Current view: top level - frmts/wms - wmsmetadataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 234 469 49.9 %
Date: 2026-07-06 17:13:05 Functions: 12 17 70.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  WMS Client Driver
       4             :  * Purpose:  Definition of GDALWMSMetaDataset class
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "wmsmetadataset.h"
      14             : 
      15             : #include "gdal_openinfo.h"
      16             : 
      17             : int VersionStringToInt(const char *version);
      18             : 
      19             : /************************************************************************/
      20             : /*                         GDALWMSMetaDataset()                         */
      21             : /************************************************************************/
      22             : 
      23             : GDALWMSMetaDataset::GDALWMSMetaDataset() = default;
      24             : 
      25             : /************************************************************************/
      26             : /*                        ~GDALWMSMetaDataset()                         */
      27             : /************************************************************************/
      28             : 
      29           8 : GDALWMSMetaDataset::~GDALWMSMetaDataset()
      30             : {
      31           4 :     CSLDestroy(papszSubDatasets);
      32           8 : }
      33             : 
      34             : /************************************************************************/
      35             : /*                           AddSubDataset()                            */
      36             : /************************************************************************/
      37             : 
      38          14 : void GDALWMSMetaDataset::AddSubDataset(const char *pszName, const char *pszDesc)
      39             : {
      40             :     char szName[80];
      41          14 :     int nCount = CSLCount(papszSubDatasets) / 2;
      42             : 
      43          14 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
      44          14 :     papszSubDatasets = CSLSetNameValue(papszSubDatasets, szName, pszName);
      45             : 
      46          14 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
      47          14 :     papszSubDatasets = CSLSetNameValue(papszSubDatasets, szName, pszDesc);
      48          14 : }
      49             : 
      50             : /************************************************************************/
      51             : /*                      DownloadGetCapabilities()                       */
      52             : /************************************************************************/
      53             : 
      54             : GDALDataset *
      55           1 : GDALWMSMetaDataset::DownloadGetCapabilities(GDALOpenInfo *poOpenInfo)
      56             : {
      57           1 :     const char *pszURL = poOpenInfo->pszFilename;
      58           1 :     if (STARTS_WITH_CI(pszURL, "WMS:"))
      59           0 :         pszURL += 4;
      60             : 
      61           2 :     CPLString osFormat = CPLURLGetValue(pszURL, "FORMAT");
      62           2 :     CPLString osTransparent = CPLURLGetValue(pszURL, "TRANSPARENT");
      63           2 :     CPLString osVersion = CPLURLGetValue(pszURL, "VERSION");
      64           2 :     CPLString osPreferredSRS = CPLURLGetValue(pszURL, "SRS");
      65           1 :     if (osPreferredSRS.empty())
      66           1 :         osPreferredSRS = CPLURLGetValue(pszURL, "CRS");
      67             : 
      68           1 :     if (osVersion.empty())
      69           1 :         osVersion = "1.1.1";
      70             : 
      71           2 :     CPLString osURL(pszURL);
      72           1 :     osURL = CPLURLAddKVP(osURL, "SERVICE", "WMS");
      73           1 :     osURL = CPLURLAddKVP(osURL, "VERSION", osVersion);
      74           1 :     osURL = CPLURLAddKVP(osURL, "REQUEST", "GetCapabilities");
      75             :     /* Remove all other keywords */
      76           1 :     osURL = CPLURLAddKVP(osURL, "LAYERS", nullptr);
      77           1 :     osURL = CPLURLAddKVP(osURL, "SRS", nullptr);
      78           1 :     osURL = CPLURLAddKVP(osURL, "CRS", nullptr);
      79           1 :     osURL = CPLURLAddKVP(osURL, "BBOX", nullptr);
      80           1 :     osURL = CPLURLAddKVP(osURL, "FORMAT", nullptr);
      81           1 :     osURL = CPLURLAddKVP(osURL, "TRANSPARENT", nullptr);
      82           1 :     osURL = CPLURLAddKVP(osURL, "STYLES", nullptr);
      83           1 :     osURL = CPLURLAddKVP(osURL, "WIDTH", nullptr);
      84           1 :     osURL = CPLURLAddKVP(osURL, "HEIGHT", nullptr);
      85             : 
      86           1 :     CPLHTTPResult *psResult = CPLHTTPFetch(osURL, nullptr);
      87           1 :     if (psResult == nullptr)
      88             :     {
      89           0 :         return nullptr;
      90             :     }
      91           1 :     if (psResult->nStatus != 0 || psResult->pszErrBuf != nullptr)
      92             :     {
      93           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      94             :                  "Error returned by server : %s (%d)",
      95           0 :                  (psResult->pszErrBuf) ? psResult->pszErrBuf : "unknown",
      96             :                  psResult->nStatus);
      97           0 :         CPLHTTPDestroyResult(psResult);
      98           0 :         return nullptr;
      99             :     }
     100           1 :     if (psResult->pabyData == nullptr)
     101             :     {
     102           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     103             :                  "Empty content returned by server");
     104           0 :         CPLHTTPDestroyResult(psResult);
     105           0 :         return nullptr;
     106             :     }
     107             : 
     108             :     CPLXMLNode *psXML =
     109           1 :         CPLParseXMLString(reinterpret_cast<const char *>(psResult->pabyData));
     110           1 :     if (psXML == nullptr)
     111             :     {
     112           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
     113             :                  psResult->pabyData);
     114           0 :         CPLHTTPDestroyResult(psResult);
     115           0 :         return nullptr;
     116             :     }
     117             : 
     118             :     GDALDataset *poRet =
     119           1 :         AnalyzeGetCapabilities(psXML, osFormat, osTransparent, osPreferredSRS);
     120             : 
     121           1 :     CPLHTTPDestroyResult(psResult);
     122           1 :     CPLDestroyXMLNode(psXML);
     123             : 
     124           1 :     return poRet;
     125             : }
     126             : 
     127             : /************************************************************************/
     128             : /*                       DownloadGetTileService()                       */
     129             : /************************************************************************/
     130             : 
     131             : GDALDataset *
     132           0 : GDALWMSMetaDataset::DownloadGetTileService(GDALOpenInfo *poOpenInfo)
     133             : {
     134           0 :     const char *pszURL = poOpenInfo->pszFilename;
     135           0 :     if (STARTS_WITH_CI(pszURL, "WMS:"))
     136           0 :         pszURL += 4;
     137             : 
     138           0 :     CPLString osURL(pszURL);
     139           0 :     osURL = CPLURLAddKVP(osURL, "SERVICE", "WMS");
     140           0 :     osURL = CPLURLAddKVP(osURL, "REQUEST", "GetTileService");
     141             :     /* Remove all other keywords */
     142           0 :     osURL = CPLURLAddKVP(osURL, "VERSION", nullptr);
     143           0 :     osURL = CPLURLAddKVP(osURL, "LAYERS", nullptr);
     144           0 :     osURL = CPLURLAddKVP(osURL, "SRS", nullptr);
     145           0 :     osURL = CPLURLAddKVP(osURL, "CRS", nullptr);
     146           0 :     osURL = CPLURLAddKVP(osURL, "BBOX", nullptr);
     147           0 :     osURL = CPLURLAddKVP(osURL, "FORMAT", nullptr);
     148           0 :     osURL = CPLURLAddKVP(osURL, "TRANSPARENT", nullptr);
     149           0 :     osURL = CPLURLAddKVP(osURL, "STYLES", nullptr);
     150           0 :     osURL = CPLURLAddKVP(osURL, "WIDTH", nullptr);
     151           0 :     osURL = CPLURLAddKVP(osURL, "HEIGHT", nullptr);
     152             : 
     153           0 :     CPLHTTPResult *psResult = CPLHTTPFetch(osURL, nullptr);
     154           0 :     if (psResult == nullptr)
     155             :     {
     156           0 :         return nullptr;
     157             :     }
     158           0 :     if (psResult->nStatus != 0 || psResult->pszErrBuf != nullptr)
     159             :     {
     160           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     161             :                  "Error returned by server : %s (%d)",
     162           0 :                  (psResult->pszErrBuf) ? psResult->pszErrBuf : "unknown",
     163             :                  psResult->nStatus);
     164           0 :         CPLHTTPDestroyResult(psResult);
     165           0 :         return nullptr;
     166             :     }
     167           0 :     if (psResult->pabyData == nullptr)
     168             :     {
     169           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     170             :                  "Empty content returned by server");
     171           0 :         CPLHTTPDestroyResult(psResult);
     172           0 :         return nullptr;
     173             :     }
     174             : 
     175             :     CPLXMLNode *psXML =
     176           0 :         CPLParseXMLString(reinterpret_cast<const char *>(psResult->pabyData));
     177           0 :     if (psXML == nullptr)
     178             :     {
     179           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
     180             :                  psResult->pabyData);
     181           0 :         CPLHTTPDestroyResult(psResult);
     182           0 :         return nullptr;
     183             :     }
     184             : 
     185           0 :     GDALDataset *poRet = AnalyzeGetTileService(psXML, poOpenInfo);
     186             : 
     187           0 :     CPLHTTPDestroyResult(psResult);
     188           0 :     CPLDestroyXMLNode(psXML);
     189             : 
     190           0 :     return poRet;
     191             : }
     192             : 
     193             : /************************************************************************/
     194             : /*                       GetMetadataDomainList()                        */
     195             : /************************************************************************/
     196             : 
     197           0 : char **GDALWMSMetaDataset::GetMetadataDomainList()
     198             : {
     199           0 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
     200           0 :                                    TRUE, GDAL_MDD_SUBDATASETS, nullptr);
     201             : }
     202             : 
     203             : /************************************************************************/
     204             : /*                            GetMetadata()                             */
     205             : /************************************************************************/
     206             : 
     207           3 : CSLConstList GDALWMSMetaDataset::GetMetadata(const char *pszDomain)
     208             : 
     209             : {
     210           3 :     if (pszDomain != nullptr && EQUAL(pszDomain, GDAL_MDD_SUBDATASETS))
     211           3 :         return papszSubDatasets;
     212             : 
     213           0 :     return GDALPamDataset::GetMetadata(pszDomain);
     214             : }
     215             : 
     216             : /************************************************************************/
     217             : /*                           AddSubDataset()                            */
     218             : /************************************************************************/
     219             : 
     220          10 : void GDALWMSMetaDataset::AddSubDataset(
     221             :     const char *pszLayerName, const char *pszTitle,
     222             :     CPL_UNUSED const char *pszAbstract, const char *pszSRS, const char *pszMinX,
     223             :     const char *pszMinY, const char *pszMaxX, const char *pszMaxY,
     224             :     const std::string &osFormat, const std::string &osTransparent)
     225             : {
     226          20 :     CPLString osSubdatasetName = "WMS:";
     227          10 :     osSubdatasetName += osGetURL;
     228          10 :     osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "SERVICE", "WMS");
     229          10 :     osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "VERSION", osVersion);
     230          10 :     osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "REQUEST", "GetMap");
     231          10 :     char *pszEscapedLayerName = CPLEscapeString(pszLayerName, -1, CPLES_URL);
     232             :     osSubdatasetName =
     233          10 :         CPLURLAddKVP(osSubdatasetName, "LAYERS", pszEscapedLayerName);
     234          10 :     CPLFree(pszEscapedLayerName);
     235          10 :     if (VersionStringToInt(osVersion.c_str()) >= VersionStringToInt("1.3.0"))
     236             :     {
     237          10 :         osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "CRS", pszSRS);
     238             :     }
     239             :     else
     240           0 :         osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "SRS", pszSRS);
     241          20 :     osSubdatasetName = CPLURLAddKVP(
     242             :         osSubdatasetName, "BBOX",
     243          10 :         CPLSPrintf("%s,%s,%s,%s", pszMinX, pszMinY, pszMaxX, pszMaxY));
     244          10 :     if (!osFormat.empty())
     245             :         osSubdatasetName =
     246           0 :             CPLURLAddKVP(osSubdatasetName, "FORMAT", osFormat.c_str());
     247          10 :     if (!osTransparent.empty())
     248           0 :         osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "TRANSPARENT",
     249           0 :                                         osTransparent.c_str());
     250             : 
     251          10 :     if (pszTitle)
     252             :     {
     253          20 :         if (!osXMLEncoding.empty() && osXMLEncoding != "utf-8" &&
     254          10 :             osXMLEncoding != "UTF-8")
     255             :         {
     256             :             char *pszRecodedTitle =
     257           0 :                 CPLRecode(pszTitle, osXMLEncoding.c_str(), CPL_ENC_UTF8);
     258           0 :             if (pszRecodedTitle)
     259           0 :                 AddSubDataset(osSubdatasetName, pszRecodedTitle);
     260             :             else
     261           0 :                 AddSubDataset(osSubdatasetName, pszTitle);
     262           0 :             CPLFree(pszRecodedTitle);
     263             :         }
     264             :         else
     265             :         {
     266          10 :             AddSubDataset(osSubdatasetName, pszTitle);
     267             :         }
     268             :     }
     269             :     else
     270             :     {
     271           0 :         AddSubDataset(osSubdatasetName, pszLayerName);
     272             :     }
     273          10 : }
     274             : 
     275             : /************************************************************************/
     276             : /*                         AddWMSCSubDataset()                          */
     277             : /************************************************************************/
     278             : 
     279           0 : void GDALWMSMetaDataset::AddWMSCSubDataset(WMSCTileSetDesc &oWMSCTileSetDesc,
     280             :                                            const char *pszTitle,
     281             :                                            const CPLString &osTransparent)
     282             : {
     283           0 :     CPLString osSubdatasetName = "WMS:";
     284           0 :     osSubdatasetName += osGetURL;
     285           0 :     osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "SERVICE", "WMS");
     286           0 :     osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "VERSION", osVersion);
     287           0 :     osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "REQUEST", "GetMap");
     288             :     osSubdatasetName =
     289           0 :         CPLURLAddKVP(osSubdatasetName, "LAYERS", oWMSCTileSetDesc.osLayers);
     290           0 :     if (VersionStringToInt(osVersion.c_str()) >= VersionStringToInt("1.3.0"))
     291             :         osSubdatasetName =
     292           0 :             CPLURLAddKVP(osSubdatasetName, "CRS", oWMSCTileSetDesc.osSRS);
     293             :     else
     294             :         osSubdatasetName =
     295           0 :             CPLURLAddKVP(osSubdatasetName, "SRS", oWMSCTileSetDesc.osSRS);
     296             :     osSubdatasetName =
     297           0 :         CPLURLAddKVP(osSubdatasetName, "BBOX",
     298             :                      CPLSPrintf("%s,%s,%s,%s", oWMSCTileSetDesc.osMinX.c_str(),
     299             :                                 oWMSCTileSetDesc.osMinY.c_str(),
     300             :                                 oWMSCTileSetDesc.osMaxX.c_str(),
     301           0 :                                 oWMSCTileSetDesc.osMaxY.c_str()));
     302             : 
     303             :     osSubdatasetName =
     304           0 :         CPLURLAddKVP(osSubdatasetName, "FORMAT", oWMSCTileSetDesc.osFormat);
     305           0 :     if (!osTransparent.empty())
     306             :         osSubdatasetName =
     307           0 :             CPLURLAddKVP(osSubdatasetName, "TRANSPARENT", osTransparent);
     308           0 :     if (oWMSCTileSetDesc.nTileWidth != oWMSCTileSetDesc.nTileHeight)
     309           0 :         CPLDebug("WMS", "Weird: nTileWidth != nTileHeight for %s",
     310             :                  oWMSCTileSetDesc.osLayers.c_str());
     311             :     osSubdatasetName =
     312           0 :         CPLURLAddKVP(osSubdatasetName, "TILESIZE",
     313           0 :                      CPLSPrintf("%d", oWMSCTileSetDesc.nTileWidth));
     314             :     osSubdatasetName =
     315           0 :         CPLURLAddKVP(osSubdatasetName, "OVERVIEWCOUNT",
     316           0 :                      CPLSPrintf("%d", oWMSCTileSetDesc.nResolutions - 1));
     317             :     osSubdatasetName =
     318           0 :         CPLURLAddKVP(osSubdatasetName, "MINRESOLUTION",
     319           0 :                      CPLSPrintf("%.16f", oWMSCTileSetDesc.dfMinResolution));
     320           0 :     osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "TILED", "true");
     321             : 
     322           0 :     if (pszTitle)
     323             :     {
     324           0 :         if (!osXMLEncoding.empty() && osXMLEncoding != "utf-8" &&
     325           0 :             osXMLEncoding != "UTF-8")
     326             :         {
     327             :             char *pszRecodedTitle =
     328           0 :                 CPLRecode(pszTitle, osXMLEncoding.c_str(), CPL_ENC_UTF8);
     329           0 :             if (pszRecodedTitle)
     330           0 :                 AddSubDataset(osSubdatasetName, pszRecodedTitle);
     331             :             else
     332           0 :                 AddSubDataset(osSubdatasetName, pszTitle);
     333           0 :             CPLFree(pszRecodedTitle);
     334             :         }
     335             :         else
     336             :         {
     337           0 :             AddSubDataset(osSubdatasetName, pszTitle);
     338             :         }
     339             :     }
     340             :     else
     341             :     {
     342           0 :         AddSubDataset(osSubdatasetName, oWMSCTileSetDesc.osLayers);
     343             :     }
     344           0 : }
     345             : 
     346             : /************************************************************************/
     347             : /*                            ExploreLayer()                            */
     348             : /************************************************************************/
     349             : 
     350          10 : void GDALWMSMetaDataset::ExploreLayer(CPLXMLNode *psXML,
     351             :                                       const CPLString &osFormat,
     352             :                                       const CPLString &osTransparent,
     353             :                                       const CPLString &osPreferredSRS,
     354             :                                       const char *pszSRS, const char *pszMinX,
     355             :                                       const char *pszMinY, const char *pszMaxX,
     356             :                                       const char *pszMaxY)
     357             : {
     358          10 :     const char *pszName = CPLGetXMLValue(psXML, "Name", nullptr);
     359          10 :     const char *pszTitle = CPLGetXMLValue(psXML, "Title", nullptr);
     360          10 :     const char *pszAbstract = CPLGetXMLValue(psXML, "Abstract", nullptr);
     361             : 
     362          10 :     CPLXMLNode *psSRS = nullptr;
     363          10 :     const char *pszSRSLocal = nullptr;
     364          10 :     const char *pszMinXLocal = nullptr;
     365          10 :     const char *pszMinYLocal = nullptr;
     366          10 :     const char *pszMaxXLocal = nullptr;
     367          10 :     const char *pszMaxYLocal = nullptr;
     368             : 
     369             :     const bool bIsWMS130 =
     370          10 :         VersionStringToInt(osVersion.c_str()) >= VersionStringToInt("1.3.0");
     371             : 
     372          20 :     const auto GetCRS = [bIsWMS130](const CPLXMLNode *psNode)
     373             :     {
     374             :         const char *pszVal =
     375          15 :             CPLGetXMLValue(psNode, bIsWMS130 ? "CRS" : "SRS", nullptr);
     376          15 :         if (!pszVal && !bIsWMS130)
     377             :         {
     378             :             // Some WMS servers have a non-conformant WMS 1.1.1 implementation
     379             :             // that use "CRS" instead of "SRS"
     380             :             // like https://mapy.geoportal.gov.pl/wss/service/PZGIK/BDOO/WMS/aktualne?service=wms&request=getcapabilities
     381           0 :             pszVal = CPLGetXMLValue(psNode, "CRS", nullptr);
     382           0 :             if (pszVal)
     383             :             {
     384             :                 static bool bWarned = false;
     385           0 :                 if (!bWarned)
     386             :                 {
     387           0 :                     bWarned = true;
     388           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     389             :                              "WMS server uses non-standard CRS attribute for "
     390             :                              "WMS 1.1.0. Things might mis-behave. Perhaps try "
     391             :                              "using VERSION=1.3.0");
     392             :                 }
     393             :             }
     394             :         }
     395          15 :         return pszVal;
     396          10 :     };
     397             : 
     398             :     /* Use local bounding box if available, otherwise use the one */
     399             :     /* that comes from an upper layer */
     400             :     /* such as in http://neowms.sci.gsfc.nasa.gov/wms/wms */
     401          10 :     CPLXMLNode *psIter = psXML->psChild;
     402         303 :     while (psIter != nullptr)
     403             :     {
     404         298 :         if (psIter->eType == CXT_Element &&
     405         280 :             strcmp(psIter->pszValue, "BoundingBox") == 0)
     406             :         {
     407           5 :             psSRS = psIter;
     408           5 :             pszSRSLocal = GetCRS(psSRS);
     409           5 :             if (osPreferredSRS.empty() || pszSRSLocal == nullptr)
     410           5 :                 break;
     411           0 :             if (EQUAL(osPreferredSRS, pszSRSLocal))
     412           0 :                 break;
     413           0 :             psSRS = nullptr;
     414           0 :             pszSRSLocal = nullptr;
     415             :         }
     416         293 :         psIter = psIter->psNext;
     417             :     }
     418             : 
     419          10 :     if (psSRS == nullptr)
     420             :     {
     421           5 :         psSRS = CPLGetXMLNode(psXML, "LatLonBoundingBox");
     422           5 :         pszSRSLocal = GetCRS(psSRS);
     423           5 :         if (pszSRSLocal == nullptr)
     424           5 :             pszSRSLocal = "EPSG:4326";
     425             :     }
     426             : 
     427          10 :     if (pszSRSLocal != nullptr && psSRS != nullptr)
     428             :     {
     429           5 :         pszMinXLocal = CPLGetXMLValue(psSRS, "minx", nullptr);
     430           5 :         pszMinYLocal = CPLGetXMLValue(psSRS, "miny", nullptr);
     431           5 :         pszMaxXLocal = CPLGetXMLValue(psSRS, "maxx", nullptr);
     432           5 :         pszMaxYLocal = CPLGetXMLValue(psSRS, "maxy", nullptr);
     433             : 
     434           5 :         if (pszMinXLocal && pszMinYLocal && pszMaxXLocal && pszMaxYLocal)
     435             :         {
     436           5 :             pszSRS = pszSRSLocal;
     437           5 :             pszMinX = pszMinXLocal;
     438           5 :             pszMinY = pszMinYLocal;
     439           5 :             pszMaxX = pszMaxXLocal;
     440           5 :             pszMaxY = pszMaxYLocal;
     441             :         }
     442             :     }
     443             : 
     444          20 :     std::string osMinX, osMinY, osMaxX, osMaxY;
     445          10 :     if (psSRS == nullptr)
     446             :     {
     447             :         const auto psExGeographicBoundingBox =
     448           5 :             CPLGetXMLNode(psXML, "EX_GeographicBoundingBox");
     449           5 :         if (psExGeographicBoundingBox)
     450             :         {
     451           5 :             pszMinXLocal = CPLGetXMLValue(psExGeographicBoundingBox,
     452             :                                           "westBoundLongitude", nullptr);
     453           5 :             pszMinYLocal = CPLGetXMLValue(psExGeographicBoundingBox,
     454             :                                           "southBoundLatitude", nullptr);
     455           5 :             pszMaxXLocal = CPLGetXMLValue(psExGeographicBoundingBox,
     456             :                                           "eastBoundLongitude", nullptr);
     457           5 :             pszMaxYLocal = CPLGetXMLValue(psExGeographicBoundingBox,
     458             :                                           "northBoundLatitude", nullptr);
     459             : 
     460           5 :             if (pszMinXLocal && pszMinYLocal && pszMaxXLocal && pszMaxYLocal)
     461             :             {
     462           5 :                 pszSRS = "EPSG:4326";
     463           5 :                 pszMinX = pszMinXLocal;
     464           5 :                 pszMinY = pszMinYLocal;
     465           5 :                 pszMaxX = pszMaxXLocal;
     466           5 :                 pszMaxY = pszMaxYLocal;
     467             : 
     468             :                 // Try to reproject the bbox to the default CRS
     469           5 :                 pszSRSLocal = GetCRS(psXML);
     470           5 :                 if (pszSRSLocal)
     471             :                 {
     472          10 :                     OGRSpatialReference oSrcSRS;
     473           5 :                     oSrcSRS.importFromEPSG(4326);
     474           5 :                     oSrcSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     475             : 
     476          10 :                     OGRSpatialReference oDstSRS;
     477           5 :                     oDstSRS.SetFromUserInput(
     478             :                         pszSRSLocal, OGRSpatialReference::
     479             :                                          SET_FROM_USER_INPUT_LIMITATIONS_get());
     480           5 :                     oDstSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     481             : 
     482             :                     auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
     483          10 :                         OGRCreateCoordinateTransformation(&oSrcSRS, &oDstSRS));
     484           5 :                     double dfMinX = 0;
     485           5 :                     double dfMinY = 0;
     486           5 :                     double dfMaxX = 0;
     487           5 :                     double dfMaxY = 0;
     488          10 :                     if (poCT &&
     489          10 :                         poCT->TransformBounds(
     490             :                             CPLAtof(pszMinXLocal), CPLAtof(pszMinYLocal),
     491             :                             CPLAtof(pszMaxXLocal), CPLAtof(pszMaxYLocal),
     492             :                             &dfMinX, &dfMinY, &dfMaxX, &dfMaxY,
     493          10 :                             /* densify_pts = */ 21))
     494             :                     {
     495           5 :                         osMinX = CPLSPrintf("%.17g", dfMinX);
     496           5 :                         osMinY = CPLSPrintf("%.17g", dfMinY);
     497           5 :                         osMaxX = CPLSPrintf("%.17g", dfMaxX);
     498           5 :                         osMaxY = CPLSPrintf("%.17g", dfMaxY);
     499           5 :                         pszSRS = pszSRSLocal;
     500           5 :                         pszMinX = osMinX.c_str();
     501           5 :                         pszMinY = osMinY.c_str();
     502           5 :                         pszMaxX = osMaxX.c_str();
     503           5 :                         pszMaxY = osMaxY.c_str();
     504             :                     }
     505             :                 }
     506             :             }
     507             :         }
     508             :     }
     509             : 
     510          10 :     if (pszName != nullptr && pszSRS && pszMinX && pszMinY && pszMaxX &&
     511             :         pszMaxY)
     512             :     {
     513          20 :         CPLString osLocalTransparent(osTransparent);
     514          10 :         if (osLocalTransparent.empty())
     515             :         {
     516          10 :             const char *pszOpaque = CPLGetXMLValue(psXML, "opaque", "0");
     517          10 :             if (EQUAL(pszOpaque, "1"))
     518           0 :                 osLocalTransparent = "FALSE";
     519             :         }
     520             : 
     521          20 :         WMSCKeyType oWMSCKey(pszName, pszSRS);
     522             :         std::map<WMSCKeyType, WMSCTileSetDesc>::iterator oIter =
     523          10 :             osMapWMSCTileSet.find(oWMSCKey);
     524          10 :         if (oIter != osMapWMSCTileSet.end())
     525             :         {
     526           0 :             AddWMSCSubDataset(oIter->second, pszTitle, osLocalTransparent);
     527             :         }
     528             :         else
     529             :         {
     530          10 :             AddSubDataset(pszName, pszTitle, pszAbstract, pszSRS, pszMinX,
     531             :                           pszMinY, pszMaxX, pszMaxY, osFormat,
     532             :                           osLocalTransparent);
     533             :         }
     534             :     }
     535             : 
     536          10 :     psIter = psXML->psChild;
     537         320 :     for (; psIter != nullptr; psIter = psIter->psNext)
     538             :     {
     539         310 :         if (psIter->eType == CXT_Element)
     540             :         {
     541         292 :             if (EQUAL(psIter->pszValue, "Layer"))
     542           8 :                 ExploreLayer(psIter, osFormat, osTransparent, osPreferredSRS,
     543             :                              pszSRS, pszMinX, pszMinY, pszMaxX, pszMaxY);
     544             :         }
     545             :     }
     546          10 : }
     547             : 
     548             : /************************************************************************/
     549             : /*                         ParseWMSCTileSets()                          */
     550             : /************************************************************************/
     551             : 
     552           0 : void GDALWMSMetaDataset::ParseWMSCTileSets(CPLXMLNode *psXML)
     553             : {
     554           0 :     CPLXMLNode *psIter = psXML->psChild;
     555           0 :     for (; psIter; psIter = psIter->psNext)
     556             :     {
     557           0 :         if (psIter->eType == CXT_Element && EQUAL(psIter->pszValue, "TileSet"))
     558             :         {
     559           0 :             const char *pszSRS = CPLGetXMLValue(psIter, "SRS", nullptr);
     560           0 :             if (pszSRS == nullptr)
     561           0 :                 continue;
     562             : 
     563           0 :             CPLXMLNode *psBoundingBox = CPLGetXMLNode(psIter, "BoundingBox");
     564           0 :             if (psBoundingBox == nullptr)
     565           0 :                 continue;
     566             : 
     567             :             const char *pszMinX =
     568           0 :                 CPLGetXMLValue(psBoundingBox, "minx", nullptr);
     569             :             const char *pszMinY =
     570           0 :                 CPLGetXMLValue(psBoundingBox, "miny", nullptr);
     571             :             const char *pszMaxX =
     572           0 :                 CPLGetXMLValue(psBoundingBox, "maxx", nullptr);
     573             :             const char *pszMaxY =
     574           0 :                 CPLGetXMLValue(psBoundingBox, "maxy", nullptr);
     575           0 :             if (pszMinX == nullptr || pszMinY == nullptr ||
     576           0 :                 pszMaxX == nullptr || pszMaxY == nullptr)
     577           0 :                 continue;
     578             : 
     579           0 :             double dfMinX = CPLAtofM(pszMinX);
     580           0 :             double dfMinY = CPLAtofM(pszMinY);
     581           0 :             double dfMaxX = CPLAtofM(pszMaxX);
     582           0 :             double dfMaxY = CPLAtofM(pszMaxY);
     583           0 :             if (dfMaxY <= dfMinY || dfMaxX <= dfMinX)
     584           0 :                 continue;
     585             : 
     586           0 :             const char *pszFormat = CPLGetXMLValue(psIter, "Format", nullptr);
     587           0 :             if (pszFormat == nullptr)
     588           0 :                 continue;
     589           0 :             if (strstr(pszFormat, "kml"))
     590           0 :                 continue;
     591             : 
     592           0 :             const char *pszTileWidth = CPLGetXMLValue(psIter, "Width", nullptr);
     593             :             const char *pszTileHeight =
     594           0 :                 CPLGetXMLValue(psIter, "Height", nullptr);
     595           0 :             if (pszTileWidth == nullptr || pszTileHeight == nullptr)
     596           0 :                 continue;
     597             : 
     598           0 :             int nTileWidth = atoi(pszTileWidth);
     599           0 :             int nTileHeight = atoi(pszTileHeight);
     600           0 :             if (nTileWidth < 128 || nTileHeight < 128)
     601           0 :                 continue;
     602             : 
     603           0 :             const char *pszLayers = CPLGetXMLValue(psIter, "Layers", nullptr);
     604           0 :             if (pszLayers == nullptr)
     605           0 :                 continue;
     606             : 
     607             :             const char *pszResolutions =
     608           0 :                 CPLGetXMLValue(psIter, "Resolutions", nullptr);
     609           0 :             if (pszResolutions == nullptr)
     610           0 :                 continue;
     611             :             char **papszTokens =
     612           0 :                 CSLTokenizeStringComplex(pszResolutions, " ", 0, 0);
     613           0 :             double dfMinResolution = 0;
     614             :             int i;
     615           0 :             for (i = 0; papszTokens && papszTokens[i]; i++)
     616             :             {
     617           0 :                 double dfResolution = CPLAtofM(papszTokens[i]);
     618           0 :                 if (i == 0 || dfResolution < dfMinResolution)
     619           0 :                     dfMinResolution = dfResolution;
     620             :             }
     621           0 :             CSLDestroy(papszTokens);
     622           0 :             int nResolutions = i;
     623           0 :             if (nResolutions == 0)
     624           0 :                 continue;
     625             : 
     626           0 :             const char *pszStyles = CPLGetXMLValue(psIter, "Styles", "");
     627             : 
     628             :             /* http://demo.opengeo.org/geoserver/gwc/service/wms?tiled=TRUE&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities
     629             :              */
     630             :             /* has different variations of formats for the same (formats, SRS)
     631             :              * tuple, so just */
     632             :             /* keep the first one which is a png format */
     633           0 :             WMSCKeyType oWMSCKey(pszLayers, pszSRS);
     634             :             std::map<WMSCKeyType, WMSCTileSetDesc>::iterator oIter =
     635           0 :                 osMapWMSCTileSet.find(oWMSCKey);
     636           0 :             if (oIter != osMapWMSCTileSet.end())
     637           0 :                 continue;
     638             : 
     639           0 :             WMSCTileSetDesc oWMSCTileSet;
     640           0 :             oWMSCTileSet.osLayers = pszLayers;
     641           0 :             oWMSCTileSet.osSRS = pszSRS;
     642           0 :             oWMSCTileSet.osMinX = pszMinX;
     643           0 :             oWMSCTileSet.osMinY = pszMinY;
     644           0 :             oWMSCTileSet.osMaxX = pszMaxX;
     645           0 :             oWMSCTileSet.osMaxY = pszMaxY;
     646           0 :             oWMSCTileSet.dfMinX = dfMinX;
     647           0 :             oWMSCTileSet.dfMinY = dfMinY;
     648           0 :             oWMSCTileSet.dfMaxX = dfMaxX;
     649           0 :             oWMSCTileSet.dfMaxY = dfMaxY;
     650           0 :             oWMSCTileSet.nResolutions = nResolutions;
     651           0 :             oWMSCTileSet.dfMinResolution = dfMinResolution;
     652           0 :             oWMSCTileSet.osFormat = pszFormat;
     653           0 :             oWMSCTileSet.osStyle = pszStyles;
     654           0 :             oWMSCTileSet.nTileWidth = nTileWidth;
     655           0 :             oWMSCTileSet.nTileHeight = nTileHeight;
     656             : 
     657           0 :             osMapWMSCTileSet[oWMSCKey] = std::move(oWMSCTileSet);
     658             :         }
     659             :     }
     660           0 : }
     661             : 
     662             : /************************************************************************/
     663             : /*                       AnalyzeGetCapabilities()                       */
     664             : /************************************************************************/
     665             : 
     666           2 : GDALDataset *GDALWMSMetaDataset::AnalyzeGetCapabilities(
     667             :     CPLXMLNode *psXML, const std::string &osFormat,
     668             :     const std::string &osTransparent, const std::string &osPreferredSRS)
     669             : {
     670           2 :     const char *pszEncoding = nullptr;
     671           2 :     if (psXML->eType == CXT_Element && strcmp(psXML->pszValue, "?xml") == 0)
     672           2 :         pszEncoding = CPLGetXMLValue(psXML, "encoding", nullptr);
     673             : 
     674           2 :     CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=WMT_MS_Capabilities");
     675           2 :     if (psRoot == nullptr)
     676           2 :         psRoot = CPLGetXMLNode(psXML, "=WMS_Capabilities");
     677           2 :     if (psRoot == nullptr)
     678           0 :         return nullptr;
     679           2 :     CPLXMLNode *psCapability = CPLGetXMLNode(psRoot, "Capability");
     680           2 :     if (psCapability == nullptr)
     681           0 :         return nullptr;
     682             : 
     683           2 :     CPLXMLNode *psOnlineResource = CPLGetXMLNode(
     684             :         psCapability, "Request.GetMap.DCPType.HTTP.Get.OnlineResource");
     685           2 :     if (psOnlineResource == nullptr)
     686           0 :         return nullptr;
     687             :     const char *pszGetURL =
     688           2 :         CPLGetXMLValue(psOnlineResource, "xlink:href", nullptr);
     689           2 :     if (pszGetURL == nullptr)
     690           0 :         return nullptr;
     691             : 
     692           2 :     CPLXMLNode *psLayer = CPLGetXMLNode(psCapability, "Layer");
     693           2 :     if (psLayer == nullptr)
     694           0 :         return nullptr;
     695             : 
     696             :     CPLXMLNode *psVendorSpecificCapabilities =
     697           2 :         CPLGetXMLNode(psCapability, "VendorSpecificCapabilities");
     698             : 
     699           2 :     GDALWMSMetaDataset *poDS = new GDALWMSMetaDataset();
     700           2 :     const char *pszVersion = CPLGetXMLValue(psRoot, "version", nullptr);
     701           2 :     if (pszVersion)
     702           2 :         poDS->osVersion = pszVersion;
     703             :     else
     704           0 :         poDS->osVersion = "1.1.1";
     705           2 :     poDS->osGetURL = pszGetURL;
     706           2 :     poDS->osXMLEncoding = pszEncoding ? pszEncoding : "";
     707           2 :     if (psVendorSpecificCapabilities)
     708           0 :         poDS->ParseWMSCTileSets(psVendorSpecificCapabilities);
     709           2 :     poDS->ExploreLayer(psLayer, osFormat, osTransparent, osPreferredSRS);
     710             : 
     711           2 :     return poDS;
     712             : }
     713             : 
     714             : /************************************************************************/
     715             : /*                         AddTiledSubDataset()                         */
     716             : /************************************************************************/
     717             : 
     718             : // tiledWMS only
     719           4 : void GDALWMSMetaDataset::AddTiledSubDataset(const char *pszTiledGroupName,
     720             :                                             const char *pszTitle,
     721             :                                             const char *const *papszChanges)
     722             : {
     723             :     CPLString osSubdatasetName =
     724           8 :         "<GDAL_WMS><Service name=\"TiledWMS\"><ServerUrl>";
     725           4 :     osSubdatasetName += osGetURL;
     726           4 :     osSubdatasetName += "</ServerUrl><TiledGroupName>";
     727           4 :     osSubdatasetName += pszTiledGroupName;
     728           4 :     osSubdatasetName += "</TiledGroupName>";
     729             : 
     730           4 :     for (int i = 0; papszChanges != nullptr && papszChanges[i] != nullptr; i++)
     731             :     {
     732           0 :         char *key = nullptr;
     733           0 :         const char *value = CPLParseNameValue(papszChanges[i], &key);
     734           0 :         if (value != nullptr && key != nullptr)
     735             :             osSubdatasetName +=
     736           0 :                 CPLSPrintf("<Change key=\"${%s}\">%s</Change>", key, value);
     737           0 :         CPLFree(key);
     738             :     }
     739             : 
     740           4 :     osSubdatasetName += "</Service></GDAL_WMS>";
     741             : 
     742           4 :     if (pszTitle)
     743             :     {
     744           4 :         if (!osXMLEncoding.empty() && osXMLEncoding != "utf-8" &&
     745           0 :             osXMLEncoding != "UTF-8")
     746             :         {
     747             :             char *pszRecodedTitle =
     748           0 :                 CPLRecode(pszTitle, osXMLEncoding.c_str(), CPL_ENC_UTF8);
     749           0 :             if (pszRecodedTitle)
     750           0 :                 AddSubDataset(osSubdatasetName, pszRecodedTitle);
     751             :             else
     752           0 :                 AddSubDataset(osSubdatasetName, pszTitle);
     753           0 :             CPLFree(pszRecodedTitle);
     754             :         }
     755             :         else
     756             :         {
     757           4 :             AddSubDataset(osSubdatasetName, pszTitle);
     758             :         }
     759             :     }
     760             :     else
     761             :     {
     762           0 :         AddSubDataset(osSubdatasetName, pszTiledGroupName);
     763             :     }
     764           4 : }
     765             : 
     766             : /************************************************************************/
     767             : /*                    AnalyzeGetTileServiceRecurse()                    */
     768             : /************************************************************************/
     769             : // tiledWMS only
     770           2 : void GDALWMSMetaDataset::AnalyzeGetTileServiceRecurse(CPLXMLNode *psXML,
     771             :                                                       GDALOpenInfo *poOpenInfo)
     772             : {
     773             :     // Only list tiled groups that contain the string in the open option
     774             :     // TiledGroupName, if given
     775           2 :     char **papszLocalOpenOptions =
     776           2 :         poOpenInfo ? poOpenInfo->papszOpenOptions : nullptr;
     777             :     CPLString osMatch(
     778           4 :         CSLFetchNameValueDef(papszLocalOpenOptions, "TiledGroupName", ""));
     779           2 :     osMatch.toupper();
     780             :     // Also pass the change patterns, if provided
     781             :     char **papszChanges =
     782           2 :         CSLFetchNameValueMultiple(papszLocalOpenOptions, "Change");
     783             : 
     784           2 :     CPLXMLNode *psIter = psXML->psChild;
     785          10 :     for (; psIter != nullptr; psIter = psIter->psNext)
     786             :     {
     787           8 :         if (psIter->eType == CXT_Element &&
     788           8 :             EQUAL(psIter->pszValue, "TiledGroup"))
     789             :         {
     790           4 :             const char *pszName = CPLGetXMLValue(psIter, "Name", nullptr);
     791           4 :             if (pszName)
     792             :             {
     793           4 :                 const char *pszTitle = CPLGetXMLValue(psIter, "Title", nullptr);
     794           4 :                 if (osMatch.empty())
     795             :                 {
     796           4 :                     AddTiledSubDataset(pszName, pszTitle, papszChanges);
     797             :                 }
     798             :                 else
     799             :                 {
     800           0 :                     CPLString osNameUpper(pszName);
     801           0 :                     osNameUpper.toupper();
     802           0 :                     if (std::string::npos != osNameUpper.find(osMatch))
     803           0 :                         AddTiledSubDataset(pszName, pszTitle, papszChanges);
     804             :                 }
     805           4 :             }
     806             :         }
     807           4 :         else if (psIter->eType == CXT_Element &&
     808           4 :                  EQUAL(psIter->pszValue, "TiledGroups"))
     809             :         {
     810           0 :             AnalyzeGetTileServiceRecurse(psIter, poOpenInfo);
     811             :         }
     812             :     }
     813           2 :     CPLFree(papszChanges);
     814           2 : }
     815             : 
     816             : /************************************************************************/
     817             : /*                       AnalyzeGetTileService()                        */
     818             : /************************************************************************/
     819             : // tiledWMS only
     820           2 : GDALDataset *GDALWMSMetaDataset::AnalyzeGetTileService(CPLXMLNode *psXML,
     821             :                                                        GDALOpenInfo *poOpenInfo)
     822             : {
     823           2 :     const char *pszEncoding = nullptr;
     824           2 :     if (psXML->eType == CXT_Element && strcmp(psXML->pszValue, "?xml") == 0)
     825           0 :         pszEncoding = CPLGetXMLValue(psXML, "encoding", nullptr);
     826             : 
     827           2 :     CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=WMS_Tile_Service");
     828           2 :     if (psRoot == nullptr)
     829           0 :         return nullptr;
     830           2 :     CPLXMLNode *psTiledPatterns = CPLGetXMLNode(psRoot, "TiledPatterns");
     831           2 :     if (psTiledPatterns == nullptr)
     832           0 :         return nullptr;
     833             : 
     834             :     const char *pszURL =
     835           2 :         CPLGetXMLValue(psTiledPatterns, "OnlineResource.xlink:href", nullptr);
     836           2 :     if (pszURL == nullptr)
     837           0 :         return nullptr;
     838             : 
     839           2 :     GDALWMSMetaDataset *poDS = new GDALWMSMetaDataset();
     840           2 :     poDS->osGetURL = pszURL;
     841           2 :     poDS->osXMLEncoding = pszEncoding ? pszEncoding : "";
     842             : 
     843           2 :     poDS->AnalyzeGetTileServiceRecurse(psTiledPatterns, poOpenInfo);
     844             : 
     845           2 :     return poDS;
     846             : }
     847             : 
     848             : /************************************************************************/
     849             : /*                       AnalyzeTileMapService()                        */
     850             : /************************************************************************/
     851             : 
     852           0 : GDALDataset *GDALWMSMetaDataset::AnalyzeTileMapService(CPLXMLNode *psXML)
     853             : {
     854           0 :     CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=TileMapService");
     855           0 :     if (psRoot == nullptr)
     856           0 :         return nullptr;
     857           0 :     CPLXMLNode *psTileMaps = CPLGetXMLNode(psRoot, "TileMaps");
     858           0 :     if (psTileMaps == nullptr)
     859           0 :         return nullptr;
     860             : 
     861           0 :     GDALWMSMetaDataset *poDS = new GDALWMSMetaDataset();
     862             : 
     863           0 :     CPLXMLNode *psIter = psTileMaps->psChild;
     864           0 :     for (; psIter != nullptr; psIter = psIter->psNext)
     865             :     {
     866           0 :         if (psIter->eType == CXT_Element && EQUAL(psIter->pszValue, "TileMap"))
     867             :         {
     868           0 :             const char *pszHref = CPLGetXMLValue(psIter, "href", nullptr);
     869           0 :             const char *pszTitle = CPLGetXMLValue(psIter, "title", nullptr);
     870           0 :             if (pszHref && pszTitle)
     871             :             {
     872           0 :                 CPLString osHref(pszHref);
     873           0 :                 const char *pszDup100 = strstr(pszHref, "1.0.0/1.0.0/");
     874           0 :                 if (pszDup100)
     875             :                 {
     876           0 :                     osHref.resize(pszDup100 - pszHref);
     877           0 :                     osHref += pszDup100 + strlen("1.0.0/");
     878             :                 }
     879           0 :                 poDS->AddSubDataset(osHref, pszTitle);
     880             :             }
     881             :         }
     882             :     }
     883             : 
     884           0 :     return poDS;
     885             : }

Generated by: LCOV version 1.14