LCOV - code coverage report
Current view: top level - frmts/wms - wmsmetadataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 191 430 44.4 %
Date: 2025-11-04 19:10:06 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           6 : GDALWMSMetaDataset::~GDALWMSMetaDataset()
      30             : {
      31           3 :     CSLDestroy(papszSubDatasets);
      32           6 : }
      33             : 
      34             : /************************************************************************/
      35             : /*                            AddSubDataset()                           */
      36             : /************************************************************************/
      37             : 
      38           9 : void GDALWMSMetaDataset::AddSubDataset(const char *pszName, const char *pszDesc)
      39             : {
      40             :     char szName[80];
      41           9 :     int nCount = CSLCount(papszSubDatasets) / 2;
      42             : 
      43           9 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
      44           9 :     papszSubDatasets = CSLSetNameValue(papszSubDatasets, szName, pszName);
      45             : 
      46           9 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
      47           9 :     papszSubDatasets = CSLSetNameValue(papszSubDatasets, szName, pszDesc);
      48           9 : }
      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, "SUBDATASETS", nullptr);
     201             : }
     202             : 
     203             : /************************************************************************/
     204             : /*                            GetMetadata()                             */
     205             : /************************************************************************/
     206             : 
     207           2 : char **GDALWMSMetaDataset::GetMetadata(const char *pszDomain)
     208             : 
     209             : {
     210           2 :     if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
     211           2 :         return papszSubDatasets;
     212             : 
     213           0 :     return GDALPamDataset::GetMetadata(pszDomain);
     214             : }
     215             : 
     216             : /************************************************************************/
     217             : /*                           AddSubDataset()                            */
     218             : /************************************************************************/
     219             : 
     220           5 : 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          10 :     CPLString osSubdatasetName = "WMS:";
     227           5 :     osSubdatasetName += osGetURL;
     228           5 :     osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "SERVICE", "WMS");
     229           5 :     osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "VERSION", osVersion);
     230           5 :     osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "REQUEST", "GetMap");
     231           5 :     char *pszEscapedLayerName = CPLEscapeString(pszLayerName, -1, CPLES_URL);
     232             :     osSubdatasetName =
     233           5 :         CPLURLAddKVP(osSubdatasetName, "LAYERS", pszEscapedLayerName);
     234           5 :     CPLFree(pszEscapedLayerName);
     235           5 :     if (VersionStringToInt(osVersion.c_str()) >= VersionStringToInt("1.3.0"))
     236             :     {
     237           5 :         osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "CRS", pszSRS);
     238             :     }
     239             :     else
     240           0 :         osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "SRS", pszSRS);
     241          10 :     osSubdatasetName = CPLURLAddKVP(
     242             :         osSubdatasetName, "BBOX",
     243           5 :         CPLSPrintf("%s,%s,%s,%s", pszMinX, pszMinY, pszMaxX, pszMaxY));
     244           5 :     if (!osFormat.empty())
     245             :         osSubdatasetName =
     246           0 :             CPLURLAddKVP(osSubdatasetName, "FORMAT", osFormat.c_str());
     247           5 :     if (!osTransparent.empty())
     248           0 :         osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "TRANSPARENT",
     249           0 :                                         osTransparent.c_str());
     250             : 
     251           5 :     if (pszTitle)
     252             :     {
     253          10 :         if (!osXMLEncoding.empty() && osXMLEncoding != "utf-8" &&
     254           5 :             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           5 :             AddSubDataset(osSubdatasetName, pszTitle);
     267             :         }
     268             :     }
     269             :     else
     270             :     {
     271           0 :         AddSubDataset(osSubdatasetName, pszLayerName);
     272             :     }
     273           5 : }
     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           5 : 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           5 :     const char *pszName = CPLGetXMLValue(psXML, "Name", nullptr);
     359           5 :     const char *pszTitle = CPLGetXMLValue(psXML, "Title", nullptr);
     360           5 :     const char *pszAbstract = CPLGetXMLValue(psXML, "Abstract", nullptr);
     361             : 
     362           5 :     CPLXMLNode *psSRS = nullptr;
     363           5 :     const char *pszSRSLocal = nullptr;
     364           5 :     const char *pszMinXLocal = nullptr;
     365           5 :     const char *pszMinYLocal = nullptr;
     366           5 :     const char *pszMaxXLocal = nullptr;
     367           5 :     const char *pszMaxYLocal = nullptr;
     368             : 
     369             :     const bool bIsWMS130 =
     370           5 :         VersionStringToInt(osVersion.c_str()) >= VersionStringToInt("1.3.0");
     371             : 
     372           5 :     const auto GetCRS = [bIsWMS130](const CPLXMLNode *psNode)
     373             :     {
     374             :         const char *pszVal =
     375           5 :             CPLGetXMLValue(psNode, bIsWMS130 ? "CRS" : "SRS", nullptr);
     376           5 :         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           5 :         return pszVal;
     396           5 :     };
     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           5 :     CPLXMLNode *psIter = psXML->psChild;
     402          42 :     while (psIter != nullptr)
     403             :     {
     404          42 :         if (psIter->eType == CXT_Element &&
     405          29 :             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          37 :         psIter = psIter->psNext;
     417             :     }
     418             : 
     419           5 :     if (psSRS == nullptr)
     420             :     {
     421           0 :         psSRS = CPLGetXMLNode(psXML, "LatLonBoundingBox");
     422           0 :         pszSRSLocal = GetCRS(psSRS);
     423           0 :         if (pszSRSLocal == nullptr)
     424           0 :             pszSRSLocal = "EPSG:4326";
     425             :     }
     426             : 
     427           5 :     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           5 :     if (pszName != nullptr && pszSRS && pszMinX && pszMinY && pszMaxX &&
     445             :         pszMaxY)
     446             :     {
     447          10 :         CPLString osLocalTransparent(osTransparent);
     448           5 :         if (osLocalTransparent.empty())
     449             :         {
     450           5 :             const char *pszOpaque = CPLGetXMLValue(psXML, "opaque", "0");
     451           5 :             if (EQUAL(pszOpaque, "1"))
     452           0 :                 osLocalTransparent = "FALSE";
     453             :         }
     454             : 
     455          10 :         WMSCKeyType oWMSCKey(pszName, pszSRS);
     456             :         std::map<WMSCKeyType, WMSCTileSetDesc>::iterator oIter =
     457           5 :             osMapWMSCTileSet.find(oWMSCKey);
     458           5 :         if (oIter != osMapWMSCTileSet.end())
     459             :         {
     460           0 :             AddWMSCSubDataset(oIter->second, pszTitle, osLocalTransparent);
     461             :         }
     462             :         else
     463             :         {
     464           5 :             AddSubDataset(pszName, pszTitle, pszAbstract, pszSRS, pszMinX,
     465             :                           pszMinY, pszMaxX, pszMaxY, osFormat,
     466             :                           osLocalTransparent);
     467             :         }
     468             :     }
     469             : 
     470           5 :     psIter = psXML->psChild;
     471          59 :     for (; psIter != nullptr; psIter = psIter->psNext)
     472             :     {
     473          54 :         if (psIter->eType == CXT_Element)
     474             :         {
     475          41 :             if (EQUAL(psIter->pszValue, "Layer"))
     476           4 :                 ExploreLayer(psIter, osFormat, osTransparent, osPreferredSRS,
     477             :                              pszSRS, pszMinX, pszMinY, pszMaxX, pszMaxY);
     478             :         }
     479             :     }
     480           5 : }
     481             : 
     482             : /************************************************************************/
     483             : /*                         ParseWMSCTileSets()                          */
     484             : /************************************************************************/
     485             : 
     486           0 : void GDALWMSMetaDataset::ParseWMSCTileSets(CPLXMLNode *psXML)
     487             : {
     488           0 :     CPLXMLNode *psIter = psXML->psChild;
     489           0 :     for (; psIter; psIter = psIter->psNext)
     490             :     {
     491           0 :         if (psIter->eType == CXT_Element && EQUAL(psIter->pszValue, "TileSet"))
     492             :         {
     493           0 :             const char *pszSRS = CPLGetXMLValue(psIter, "SRS", nullptr);
     494           0 :             if (pszSRS == nullptr)
     495           0 :                 continue;
     496             : 
     497           0 :             CPLXMLNode *psBoundingBox = CPLGetXMLNode(psIter, "BoundingBox");
     498           0 :             if (psBoundingBox == nullptr)
     499           0 :                 continue;
     500             : 
     501             :             const char *pszMinX =
     502           0 :                 CPLGetXMLValue(psBoundingBox, "minx", nullptr);
     503             :             const char *pszMinY =
     504           0 :                 CPLGetXMLValue(psBoundingBox, "miny", nullptr);
     505             :             const char *pszMaxX =
     506           0 :                 CPLGetXMLValue(psBoundingBox, "maxx", nullptr);
     507             :             const char *pszMaxY =
     508           0 :                 CPLGetXMLValue(psBoundingBox, "maxy", nullptr);
     509           0 :             if (pszMinX == nullptr || pszMinY == nullptr ||
     510           0 :                 pszMaxX == nullptr || pszMaxY == nullptr)
     511           0 :                 continue;
     512             : 
     513           0 :             double dfMinX = CPLAtofM(pszMinX);
     514           0 :             double dfMinY = CPLAtofM(pszMinY);
     515           0 :             double dfMaxX = CPLAtofM(pszMaxX);
     516           0 :             double dfMaxY = CPLAtofM(pszMaxY);
     517           0 :             if (dfMaxY <= dfMinY || dfMaxX <= dfMinX)
     518           0 :                 continue;
     519             : 
     520           0 :             const char *pszFormat = CPLGetXMLValue(psIter, "Format", nullptr);
     521           0 :             if (pszFormat == nullptr)
     522           0 :                 continue;
     523           0 :             if (strstr(pszFormat, "kml"))
     524           0 :                 continue;
     525             : 
     526           0 :             const char *pszTileWidth = CPLGetXMLValue(psIter, "Width", nullptr);
     527             :             const char *pszTileHeight =
     528           0 :                 CPLGetXMLValue(psIter, "Height", nullptr);
     529           0 :             if (pszTileWidth == nullptr || pszTileHeight == nullptr)
     530           0 :                 continue;
     531             : 
     532           0 :             int nTileWidth = atoi(pszTileWidth);
     533           0 :             int nTileHeight = atoi(pszTileHeight);
     534           0 :             if (nTileWidth < 128 || nTileHeight < 128)
     535           0 :                 continue;
     536             : 
     537           0 :             const char *pszLayers = CPLGetXMLValue(psIter, "Layers", nullptr);
     538           0 :             if (pszLayers == nullptr)
     539           0 :                 continue;
     540             : 
     541             :             const char *pszResolutions =
     542           0 :                 CPLGetXMLValue(psIter, "Resolutions", nullptr);
     543           0 :             if (pszResolutions == nullptr)
     544           0 :                 continue;
     545             :             char **papszTokens =
     546           0 :                 CSLTokenizeStringComplex(pszResolutions, " ", 0, 0);
     547           0 :             double dfMinResolution = 0;
     548             :             int i;
     549           0 :             for (i = 0; papszTokens && papszTokens[i]; i++)
     550             :             {
     551           0 :                 double dfResolution = CPLAtofM(papszTokens[i]);
     552           0 :                 if (i == 0 || dfResolution < dfMinResolution)
     553           0 :                     dfMinResolution = dfResolution;
     554             :             }
     555           0 :             CSLDestroy(papszTokens);
     556           0 :             int nResolutions = i;
     557           0 :             if (nResolutions == 0)
     558           0 :                 continue;
     559             : 
     560           0 :             const char *pszStyles = CPLGetXMLValue(psIter, "Styles", "");
     561             : 
     562             :             /* http://demo.opengeo.org/geoserver/gwc/service/wms?tiled=TRUE&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities
     563             :              */
     564             :             /* has different variations of formats for the same (formats, SRS)
     565             :              * tuple, so just */
     566             :             /* keep the first one which is a png format */
     567           0 :             WMSCKeyType oWMSCKey(pszLayers, pszSRS);
     568             :             std::map<WMSCKeyType, WMSCTileSetDesc>::iterator oIter =
     569           0 :                 osMapWMSCTileSet.find(oWMSCKey);
     570           0 :             if (oIter != osMapWMSCTileSet.end())
     571           0 :                 continue;
     572             : 
     573           0 :             WMSCTileSetDesc oWMSCTileSet;
     574           0 :             oWMSCTileSet.osLayers = pszLayers;
     575           0 :             oWMSCTileSet.osSRS = pszSRS;
     576           0 :             oWMSCTileSet.osMinX = pszMinX;
     577           0 :             oWMSCTileSet.osMinY = pszMinY;
     578           0 :             oWMSCTileSet.osMaxX = pszMaxX;
     579           0 :             oWMSCTileSet.osMaxY = pszMaxY;
     580           0 :             oWMSCTileSet.dfMinX = dfMinX;
     581           0 :             oWMSCTileSet.dfMinY = dfMinY;
     582           0 :             oWMSCTileSet.dfMaxX = dfMaxX;
     583           0 :             oWMSCTileSet.dfMaxY = dfMaxY;
     584           0 :             oWMSCTileSet.nResolutions = nResolutions;
     585           0 :             oWMSCTileSet.dfMinResolution = dfMinResolution;
     586           0 :             oWMSCTileSet.osFormat = pszFormat;
     587           0 :             oWMSCTileSet.osStyle = pszStyles;
     588           0 :             oWMSCTileSet.nTileWidth = nTileWidth;
     589           0 :             oWMSCTileSet.nTileHeight = nTileHeight;
     590             : 
     591           0 :             osMapWMSCTileSet[oWMSCKey] = std::move(oWMSCTileSet);
     592             :         }
     593             :     }
     594           0 : }
     595             : 
     596             : /************************************************************************/
     597             : /*                        AnalyzeGetCapabilities()                      */
     598             : /************************************************************************/
     599             : 
     600           1 : GDALDataset *GDALWMSMetaDataset::AnalyzeGetCapabilities(
     601             :     CPLXMLNode *psXML, const std::string &osFormat,
     602             :     const std::string &osTransparent, const std::string &osPreferredSRS)
     603             : {
     604           1 :     const char *pszEncoding = nullptr;
     605           1 :     if (psXML->eType == CXT_Element && strcmp(psXML->pszValue, "?xml") == 0)
     606           1 :         pszEncoding = CPLGetXMLValue(psXML, "encoding", nullptr);
     607             : 
     608           1 :     CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=WMT_MS_Capabilities");
     609           1 :     if (psRoot == nullptr)
     610           1 :         psRoot = CPLGetXMLNode(psXML, "=WMS_Capabilities");
     611           1 :     if (psRoot == nullptr)
     612           0 :         return nullptr;
     613           1 :     CPLXMLNode *psCapability = CPLGetXMLNode(psRoot, "Capability");
     614           1 :     if (psCapability == nullptr)
     615           0 :         return nullptr;
     616             : 
     617           1 :     CPLXMLNode *psOnlineResource = CPLGetXMLNode(
     618             :         psCapability, "Request.GetMap.DCPType.HTTP.Get.OnlineResource");
     619           1 :     if (psOnlineResource == nullptr)
     620           0 :         return nullptr;
     621             :     const char *pszGetURL =
     622           1 :         CPLGetXMLValue(psOnlineResource, "xlink:href", nullptr);
     623           1 :     if (pszGetURL == nullptr)
     624           0 :         return nullptr;
     625             : 
     626           1 :     CPLXMLNode *psLayer = CPLGetXMLNode(psCapability, "Layer");
     627           1 :     if (psLayer == nullptr)
     628           0 :         return nullptr;
     629             : 
     630             :     CPLXMLNode *psVendorSpecificCapabilities =
     631           1 :         CPLGetXMLNode(psCapability, "VendorSpecificCapabilities");
     632             : 
     633           1 :     GDALWMSMetaDataset *poDS = new GDALWMSMetaDataset();
     634           1 :     const char *pszVersion = CPLGetXMLValue(psRoot, "version", nullptr);
     635           1 :     if (pszVersion)
     636           1 :         poDS->osVersion = pszVersion;
     637             :     else
     638           0 :         poDS->osVersion = "1.1.1";
     639           1 :     poDS->osGetURL = pszGetURL;
     640           1 :     poDS->osXMLEncoding = pszEncoding ? pszEncoding : "";
     641           1 :     if (psVendorSpecificCapabilities)
     642           0 :         poDS->ParseWMSCTileSets(psVendorSpecificCapabilities);
     643           1 :     poDS->ExploreLayer(psLayer, osFormat, osTransparent, osPreferredSRS);
     644             : 
     645           1 :     return poDS;
     646             : }
     647             : 
     648             : /************************************************************************/
     649             : /*                          AddTiledSubDataset()                        */
     650             : /************************************************************************/
     651             : 
     652             : // tiledWMS only
     653           4 : void GDALWMSMetaDataset::AddTiledSubDataset(const char *pszTiledGroupName,
     654             :                                             const char *pszTitle,
     655             :                                             const char *const *papszChanges)
     656             : {
     657             :     CPLString osSubdatasetName =
     658           8 :         "<GDAL_WMS><Service name=\"TiledWMS\"><ServerUrl>";
     659           4 :     osSubdatasetName += osGetURL;
     660           4 :     osSubdatasetName += "</ServerUrl><TiledGroupName>";
     661           4 :     osSubdatasetName += pszTiledGroupName;
     662           4 :     osSubdatasetName += "</TiledGroupName>";
     663             : 
     664           4 :     for (int i = 0; papszChanges != nullptr && papszChanges[i] != nullptr; i++)
     665             :     {
     666           0 :         char *key = nullptr;
     667           0 :         const char *value = CPLParseNameValue(papszChanges[i], &key);
     668           0 :         if (value != nullptr && key != nullptr)
     669             :             osSubdatasetName +=
     670           0 :                 CPLSPrintf("<Change key=\"${%s}\">%s</Change>", key, value);
     671           0 :         CPLFree(key);
     672             :     }
     673             : 
     674           4 :     osSubdatasetName += "</Service></GDAL_WMS>";
     675             : 
     676           4 :     if (pszTitle)
     677             :     {
     678           4 :         if (!osXMLEncoding.empty() && osXMLEncoding != "utf-8" &&
     679           0 :             osXMLEncoding != "UTF-8")
     680             :         {
     681             :             char *pszRecodedTitle =
     682           0 :                 CPLRecode(pszTitle, osXMLEncoding.c_str(), CPL_ENC_UTF8);
     683           0 :             if (pszRecodedTitle)
     684           0 :                 AddSubDataset(osSubdatasetName, pszRecodedTitle);
     685             :             else
     686           0 :                 AddSubDataset(osSubdatasetName, pszTitle);
     687           0 :             CPLFree(pszRecodedTitle);
     688             :         }
     689             :         else
     690             :         {
     691           4 :             AddSubDataset(osSubdatasetName, pszTitle);
     692             :         }
     693             :     }
     694             :     else
     695             :     {
     696           0 :         AddSubDataset(osSubdatasetName, pszTiledGroupName);
     697             :     }
     698           4 : }
     699             : 
     700             : /************************************************************************/
     701             : /*                     AnalyzeGetTileServiceRecurse()                   */
     702             : /************************************************************************/
     703             : // tiledWMS only
     704           2 : void GDALWMSMetaDataset::AnalyzeGetTileServiceRecurse(CPLXMLNode *psXML,
     705             :                                                       GDALOpenInfo *poOpenInfo)
     706             : {
     707             :     // Only list tiled groups that contain the string in the open option
     708             :     // TiledGroupName, if given
     709           2 :     char **papszLocalOpenOptions =
     710           2 :         poOpenInfo ? poOpenInfo->papszOpenOptions : nullptr;
     711             :     CPLString osMatch(
     712           4 :         CSLFetchNameValueDef(papszLocalOpenOptions, "TiledGroupName", ""));
     713           2 :     osMatch.toupper();
     714             :     // Also pass the change patterns, if provided
     715             :     char **papszChanges =
     716           2 :         CSLFetchNameValueMultiple(papszLocalOpenOptions, "Change");
     717             : 
     718           2 :     CPLXMLNode *psIter = psXML->psChild;
     719          10 :     for (; psIter != nullptr; psIter = psIter->psNext)
     720             :     {
     721           8 :         if (psIter->eType == CXT_Element &&
     722           8 :             EQUAL(psIter->pszValue, "TiledGroup"))
     723             :         {
     724           4 :             const char *pszName = CPLGetXMLValue(psIter, "Name", nullptr);
     725           4 :             if (pszName)
     726             :             {
     727           4 :                 const char *pszTitle = CPLGetXMLValue(psIter, "Title", nullptr);
     728           4 :                 if (osMatch.empty())
     729             :                 {
     730           4 :                     AddTiledSubDataset(pszName, pszTitle, papszChanges);
     731             :                 }
     732             :                 else
     733             :                 {
     734           0 :                     CPLString osNameUpper(pszName);
     735           0 :                     osNameUpper.toupper();
     736           0 :                     if (std::string::npos != osNameUpper.find(osMatch))
     737           0 :                         AddTiledSubDataset(pszName, pszTitle, papszChanges);
     738             :                 }
     739           4 :             }
     740             :         }
     741           4 :         else if (psIter->eType == CXT_Element &&
     742           4 :                  EQUAL(psIter->pszValue, "TiledGroups"))
     743             :         {
     744           0 :             AnalyzeGetTileServiceRecurse(psIter, poOpenInfo);
     745             :         }
     746             :     }
     747           2 :     CPLFree(papszChanges);
     748           2 : }
     749             : 
     750             : /************************************************************************/
     751             : /*                        AnalyzeGetTileService()                       */
     752             : /************************************************************************/
     753             : // tiledWMS only
     754           2 : GDALDataset *GDALWMSMetaDataset::AnalyzeGetTileService(CPLXMLNode *psXML,
     755             :                                                        GDALOpenInfo *poOpenInfo)
     756             : {
     757           2 :     const char *pszEncoding = nullptr;
     758           2 :     if (psXML->eType == CXT_Element && strcmp(psXML->pszValue, "?xml") == 0)
     759           0 :         pszEncoding = CPLGetXMLValue(psXML, "encoding", nullptr);
     760             : 
     761           2 :     CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=WMS_Tile_Service");
     762           2 :     if (psRoot == nullptr)
     763           0 :         return nullptr;
     764           2 :     CPLXMLNode *psTiledPatterns = CPLGetXMLNode(psRoot, "TiledPatterns");
     765           2 :     if (psTiledPatterns == nullptr)
     766           0 :         return nullptr;
     767             : 
     768             :     const char *pszURL =
     769           2 :         CPLGetXMLValue(psTiledPatterns, "OnlineResource.xlink:href", nullptr);
     770           2 :     if (pszURL == nullptr)
     771           0 :         return nullptr;
     772             : 
     773           2 :     GDALWMSMetaDataset *poDS = new GDALWMSMetaDataset();
     774           2 :     poDS->osGetURL = pszURL;
     775           2 :     poDS->osXMLEncoding = pszEncoding ? pszEncoding : "";
     776             : 
     777           2 :     poDS->AnalyzeGetTileServiceRecurse(psTiledPatterns, poOpenInfo);
     778             : 
     779           2 :     return poDS;
     780             : }
     781             : 
     782             : /************************************************************************/
     783             : /*                        AnalyzeTileMapService()                       */
     784             : /************************************************************************/
     785             : 
     786           0 : GDALDataset *GDALWMSMetaDataset::AnalyzeTileMapService(CPLXMLNode *psXML)
     787             : {
     788           0 :     CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=TileMapService");
     789           0 :     if (psRoot == nullptr)
     790           0 :         return nullptr;
     791           0 :     CPLXMLNode *psTileMaps = CPLGetXMLNode(psRoot, "TileMaps");
     792           0 :     if (psTileMaps == nullptr)
     793           0 :         return nullptr;
     794             : 
     795           0 :     GDALWMSMetaDataset *poDS = new GDALWMSMetaDataset();
     796             : 
     797           0 :     CPLXMLNode *psIter = psTileMaps->psChild;
     798           0 :     for (; psIter != nullptr; psIter = psIter->psNext)
     799             :     {
     800           0 :         if (psIter->eType == CXT_Element && EQUAL(psIter->pszValue, "TileMap"))
     801             :         {
     802           0 :             const char *pszHref = CPLGetXMLValue(psIter, "href", nullptr);
     803           0 :             const char *pszTitle = CPLGetXMLValue(psIter, "title", nullptr);
     804           0 :             if (pszHref && pszTitle)
     805             :             {
     806           0 :                 CPLString osHref(pszHref);
     807           0 :                 const char *pszDup100 = strstr(pszHref, "1.0.0/1.0.0/");
     808           0 :                 if (pszDup100)
     809             :                 {
     810           0 :                     osHref.resize(pszDup100 - pszHref);
     811           0 :                     osHref += pszDup100 + strlen("1.0.0/");
     812             :                 }
     813           0 :                 poDS->AddSubDataset(osHref, pszTitle);
     814             :             }
     815             :         }
     816             :     }
     817             : 
     818           0 :     return poDS;
     819             : }

Generated by: LCOV version 1.14