LCOV - code coverage report
Current view: top level - frmts/wms - wmsmetadataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 193 432 44.7 %
Date: 2024-11-21 22:18:42 Functions: 13 18 72.2 %

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

Generated by: LCOV version 1.14