LCOV - code coverage report
Current view: top level - frmts/wms - gdalwmsdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 298 393 75.8 %
Date: 2025-10-21 01:37:22 Functions: 11 14 78.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  WMS Client Driver
       4             :  * Purpose:  Implementation of Dataset and RasterBand classes for WMS
       5             :  *           and other similar services.
       6             :  * Author:   Adam Nowacki, nowak@xpam.de
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2007, Adam Nowacki
      10             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      11             :  * Copyright (c) 2017, Dmitry Baryshnikov, <polimax@mail.ru>
      12             :  * Copyright (c) 2017, NextGIS, <info@nextgis.com>
      13             :  *
      14             :  * SPDX-License-Identifier: MIT
      15             :  ****************************************************************************
      16             :  *
      17             :  * dataset.cpp:
      18             :  * Initialization of the GDALWMSdriver, parsing the XML configuration file,
      19             :  * instantiation of the minidrivers and accessors used by minidrivers.
      20             :  *
      21             :  ***************************************************************************/
      22             : 
      23             : #include "wmsdriver.h"
      24             : #include "minidriver_wms.h"
      25             : #include "minidriver_tileservice.h"
      26             : #include "minidriver_worldwind.h"
      27             : #include "minidriver_tms.h"
      28             : #include "minidriver_tiled_wms.h"
      29             : #include "minidriver_virtualearth.h"
      30             : 
      31             : #include "gdal_colortable.h"
      32             : #include "gdal_cpp_functions.h"
      33             : 
      34             : #include <algorithm>
      35             : 
      36             : /************************************************************************/
      37             : /*                           GDALWMSDataset()                           */
      38             : /************************************************************************/
      39         351 : GDALWMSDataset::GDALWMSDataset()
      40             :     : m_mini_driver(nullptr), m_cache(nullptr), m_poColorTable(nullptr),
      41             :       m_data_type(GDT_Byte), m_block_size_x(0), m_block_size_y(0),
      42             :       m_use_advise_read(0), m_verify_advise_read(0), m_offline_mode(0),
      43             :       m_http_max_conn(0), m_http_timeout(0), m_http_options(nullptr),
      44             :       m_tileOO(nullptr), m_clamp_requests(true), m_unsafeSsl(false),
      45             :       m_zeroblock_on_serverexceptions(0), m_default_block_size_x(1024),
      46             :       m_default_block_size_y(1024), m_default_tile_count_x(1),
      47             :       m_default_tile_count_y(1), m_default_overview_count(-1),
      48         351 :       m_bNeedsDataWindow(true)
      49             : {
      50         351 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      51         351 :     m_hint.m_valid = false;
      52         351 :     m_data_window.m_sx = -1;
      53         351 :     nBands = 0;
      54         351 : }
      55             : 
      56             : /************************************************************************/
      57             : /*                          ~GDALWMSDataset()                           */
      58             : /************************************************************************/
      59         702 : GDALWMSDataset::~GDALWMSDataset()
      60             : {
      61         351 :     if (m_mini_driver)
      62         349 :         delete m_mini_driver;
      63         351 :     if (m_cache)
      64         217 :         delete m_cache;
      65         351 :     if (m_poColorTable)
      66           0 :         delete m_poColorTable;
      67         351 :     CSLDestroy(m_http_options);
      68         351 :     CSLDestroy(m_tileOO);
      69         702 : }
      70             : 
      71             : /************************************************************************/
      72             : /*                             Initialize()                             */
      73             : /************************************************************************/
      74         351 : CPLErr GDALWMSDataset::Initialize(CPLXMLNode *config, char **l_papszOpenOptions)
      75             : {
      76         351 :     CPLErr ret = CE_None;
      77             : 
      78         351 :     char *pszXML = CPLSerializeXMLTree(config);
      79         351 :     if (pszXML)
      80             :     {
      81         351 :         m_osXML = pszXML;
      82         351 :         CPLFree(pszXML);
      83             :     }
      84             : 
      85             :     // Generic options that apply to all minidrivers
      86             : 
      87             :     // UserPwd
      88         351 :     const char *pszUserPwd = CPLGetXMLValue(config, "UserPwd", "");
      89         351 :     if (pszUserPwd[0] != '\0')
      90           0 :         m_osUserPwd = pszUserPwd;
      91             : 
      92         351 :     const char *pszUserAgent = CPLGetXMLValue(config, "UserAgent", "");
      93         351 :     if (pszUserAgent[0] != '\0')
      94           2 :         m_osUserAgent = pszUserAgent;
      95             :     else
      96         349 :         m_osUserAgent = CPLGetConfigOption("GDAL_HTTP_USERAGENT", "");
      97             : 
      98         351 :     const char *pszReferer = CPLGetXMLValue(config, "Referer", "");
      99         351 :     if (pszReferer[0] != '\0')
     100           0 :         m_osReferer = pszReferer;
     101             : 
     102             :     {
     103             :         const char *pszHttpZeroBlockCodes =
     104         351 :             CPLGetXMLValue(config, "ZeroBlockHttpCodes", "");
     105         351 :         if (pszHttpZeroBlockCodes[0] == '\0')
     106             :         {
     107         261 :             m_http_zeroblock_codes.insert(204);
     108             :         }
     109             :         else
     110             :         {
     111          90 :             char **kv = CSLTokenizeString2(pszHttpZeroBlockCodes, ",",
     112             :                                            CSLT_HONOURSTRINGS);
     113         270 :             for (int i = 0; i < CSLCount(kv); i++)
     114             :             {
     115         180 :                 int code = atoi(kv[i]);
     116         180 :                 if (code <= 0)
     117             :                 {
     118           0 :                     CPLError(
     119             :                         CE_Failure, CPLE_AppDefined,
     120             :                         "GDALWMS: Invalid value of ZeroBlockHttpCodes "
     121             :                         "\"%s\", comma separated HTTP response codes expected.",
     122           0 :                         kv[i]);
     123           0 :                     ret = CE_Failure;
     124           0 :                     break;
     125             :                 }
     126         180 :                 m_http_zeroblock_codes.insert(code);
     127             :             }
     128          90 :             CSLDestroy(kv);
     129             :         }
     130             :     }
     131             : 
     132         351 :     if (ret == CE_None)
     133             :     {
     134             :         const char *pszZeroExceptions =
     135         351 :             CPLGetXMLValue(config, "ZeroBlockOnServerException", "");
     136         351 :         if (pszZeroExceptions[0] != '\0')
     137             :         {
     138          90 :             m_zeroblock_on_serverexceptions = StrToBool(pszZeroExceptions);
     139          90 :             if (m_zeroblock_on_serverexceptions == -1)
     140             :             {
     141           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     142             :                          "GDALWMS: Invalid value of ZeroBlockOnServerException "
     143             :                          "\"%s\", true/false expected.",
     144             :                          pszZeroExceptions);
     145           0 :                 ret = CE_Failure;
     146             :             }
     147             :         }
     148             :     }
     149             : 
     150         351 :     if (ret == CE_None)
     151             :     {
     152         351 :         const char *max_conn = CPLGetXMLValue(config, "MaxConnections", "");
     153         351 :         if (max_conn[0] == '\0')
     154             :         {
     155         265 :             max_conn = CPLGetConfigOption("GDAL_MAX_CONNECTIONS", "");
     156             :         }
     157         351 :         if (max_conn[0] != '\0')
     158             :         {
     159          86 :             m_http_max_conn = atoi(max_conn);
     160             :         }
     161             :         else
     162             :         {
     163         265 :             m_http_max_conn = 2;
     164             :         }
     165             :     }
     166             : 
     167         351 :     if (ret == CE_None)
     168             :     {
     169         351 :         const char *timeout = CPLGetXMLValue(config, "Timeout", "");
     170         351 :         if (timeout[0] != '\0')
     171             :         {
     172           0 :             m_http_timeout = atoi(timeout);
     173             :         }
     174             :         else
     175             :         {
     176         351 :             m_http_timeout =
     177         351 :                 atoi(CPLGetConfigOption("GDAL_HTTP_TIMEOUT", "300"));
     178             :         }
     179             :     }
     180             : 
     181         351 :     if (ret == CE_None)
     182             :     {
     183         351 :         m_osAccept = CPLGetXMLValue(config, "Accept", "");
     184             :     }
     185             : 
     186         351 :     if (ret == CE_None)
     187             :     {
     188         351 :         const char *offline_mode = CPLGetXMLValue(config, "OfflineMode", "");
     189         351 :         if (offline_mode[0] != '\0')
     190             :         {
     191           0 :             const int offline_mode_bool = StrToBool(offline_mode);
     192           0 :             if (offline_mode_bool == -1)
     193             :             {
     194           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     195             :                          "GDALWMS: Invalid value of OfflineMode, true / false "
     196             :                          "expected.");
     197           0 :                 ret = CE_Failure;
     198             :             }
     199             :             else
     200             :             {
     201           0 :                 m_offline_mode = offline_mode_bool;
     202             :             }
     203             :         }
     204             :         else
     205             :         {
     206         351 :             m_offline_mode = 0;
     207             :         }
     208             :     }
     209             : 
     210         351 :     if (ret == CE_None)
     211             :     {
     212         351 :         const char *advise_read = CPLGetXMLValue(config, "AdviseRead", "");
     213         351 :         if (advise_read[0] != '\0')
     214             :         {
     215           0 :             const int advise_read_bool = StrToBool(advise_read);
     216           0 :             if (advise_read_bool == -1)
     217             :             {
     218           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     219             :                          "GDALWMS: Invalid value of AdviseRead, true / false "
     220             :                          "expected.");
     221           0 :                 ret = CE_Failure;
     222             :             }
     223             :             else
     224             :             {
     225           0 :                 m_use_advise_read = advise_read_bool;
     226             :             }
     227             :         }
     228             :         else
     229             :         {
     230         351 :             m_use_advise_read = 0;
     231             :         }
     232             :     }
     233             : 
     234         351 :     if (ret == CE_None)
     235             :     {
     236             :         const char *verify_advise_read =
     237         351 :             CPLGetXMLValue(config, "VerifyAdviseRead", "");
     238         351 :         if (m_use_advise_read)
     239             :         {
     240           0 :             if (verify_advise_read[0] != '\0')
     241             :             {
     242             :                 const int verify_advise_read_bool =
     243           0 :                     StrToBool(verify_advise_read);
     244           0 :                 if (verify_advise_read_bool == -1)
     245             :                 {
     246           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     247             :                              "GDALWMS: Invalid value of VerifyAdviseRead, true "
     248             :                              "/ false expected.");
     249           0 :                     ret = CE_Failure;
     250             :                 }
     251             :                 else
     252             :                 {
     253           0 :                     m_verify_advise_read = verify_advise_read_bool;
     254             :                 }
     255             :             }
     256             :             else
     257             :             {
     258           0 :                 m_verify_advise_read = 1;
     259             :             }
     260             :         }
     261             :     }
     262             : 
     263         351 :     CPLXMLNode *service_node = CPLGetXMLNode(config, "Service");
     264         351 :     if (service_node == nullptr)
     265             :     {
     266           0 :         CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: No Service specified.");
     267           0 :         return CE_Failure;
     268             :     }
     269             : 
     270         351 :     if (ret == CE_None)
     271             :     {
     272             :         const char *pszEnableCache =
     273         351 :             CPLGetConfigOption("GDAL_ENABLE_WMS_CACHE", "YES");
     274         351 :         CPLXMLNode *cache_node = CPLGetXMLNode(config, "Cache");
     275         351 :         if (cache_node != nullptr && CPLTestBool(pszEnableCache))
     276             :         {
     277         217 :             m_cache = new GDALWMSCache();
     278         217 :             if (m_cache->Initialize(
     279             :                     CPLGetXMLValue(service_node, "ServerUrl", nullptr),
     280         217 :                     cache_node) != CE_None)
     281             :             {
     282           0 :                 delete m_cache;
     283           0 :                 m_cache = nullptr;
     284           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     285             :                          "GDALWMS: Failed to initialize cache.");
     286           0 :                 ret = CE_Failure;
     287             :             }
     288             :             else
     289             :             {
     290             :                 // NOTE: Save cache path to metadata. For example, this is
     291             :                 // useful for deleting a cache folder when removing dataset or
     292             :                 // to fill the cache for specified area and zoom levels
     293         217 :                 SetMetadataItem("CACHE_PATH", m_cache->CachePath(), nullptr);
     294             :             }
     295             :         }
     296             :     }
     297             : 
     298         351 :     if (ret == CE_None)
     299             :     {
     300         351 :         const int v = StrToBool(CPLGetXMLValue(config, "UnsafeSSL", "false"));
     301         351 :         if (v == -1)
     302             :         {
     303           0 :             CPLError(
     304             :                 CE_Failure, CPLE_AppDefined,
     305             :                 "GDALWMS: Invalid value of UnsafeSSL: true or false expected.");
     306           0 :             ret = CE_Failure;
     307             :         }
     308             :         else
     309             :         {
     310         351 :             m_unsafeSsl = v;
     311             :         }
     312             :     }
     313             : 
     314             :     // Initialize the minidriver, which can set parameters for the dataset using
     315             :     // member functions
     316             : 
     317         702 :     const CPLString service_name = CPLGetXMLValue(service_node, "name", "");
     318         351 :     if (service_name.empty())
     319             :     {
     320           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     321             :                  "GDALWMS: No Service name specified.");
     322           1 :         return CE_Failure;
     323             :     }
     324             : 
     325         350 :     m_mini_driver = NewWMSMiniDriver(service_name);
     326         350 :     if (m_mini_driver == nullptr)
     327             :     {
     328           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     329             :                  "GDALWMS: No mini-driver registered for '%s'.",
     330             :                  service_name.c_str());
     331           0 :         return CE_Failure;
     332             :     }
     333             : 
     334             :     // Set up minidriver
     335         350 :     m_mini_driver->m_parent_dataset = this;
     336         350 :     if (m_mini_driver->Initialize(service_node, l_papszOpenOptions) != CE_None)
     337             :     {
     338           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     339             :                  "GDALWMS: Failed to initialize minidriver.");
     340           1 :         delete m_mini_driver;
     341           1 :         m_mini_driver = nullptr;
     342           1 :         ret = CE_Failure;
     343             :     }
     344             :     else
     345             :     {
     346         349 :         m_mini_driver->GetCapabilities(&m_mini_driver_caps);
     347             :     }
     348             : 
     349             :     /*
     350             :       Parameters that could be set by minidriver already
     351             :       If the size is set, minidriver has done this already
     352             :       A "server" side minidriver needs to set at least:
     353             :       - Blocksize (x and y)
     354             :       - Clamp flag (defaults to true)
     355             :       - DataWindow
     356             :       - Band Count
     357             :       - Data Type
     358             :       It should also initialize and register the bands and overviews.
     359             :     */
     360             : 
     361         350 :     if (m_data_window.m_sx < 1)
     362             :     {
     363         347 :         int nOverviews = 0;
     364             : 
     365         347 :         if (ret == CE_None)
     366             :         {
     367         346 :             m_block_size_x = atoi(CPLGetXMLValue(
     368             :                 config, "BlockSizeX",
     369         692 :                 CPLString().Printf("%d", m_default_block_size_x)));
     370         346 :             m_block_size_y = atoi(CPLGetXMLValue(
     371             :                 config, "BlockSizeY",
     372         692 :                 CPLString().Printf("%d", m_default_block_size_y)));
     373         346 :             if (m_block_size_x <= 0 || m_block_size_y <= 0)
     374             :             {
     375           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     376             :                          "GDALWMS: Invalid value in BlockSizeX or BlockSizeY");
     377           0 :                 ret = CE_Failure;
     378             :             }
     379             :         }
     380             : 
     381         347 :         if (ret == CE_None)
     382             :         {
     383         346 :             m_clamp_requests =
     384         346 :                 StrToBool(CPLGetXMLValue(config, "ClampRequests", "true"));
     385         346 :             if (m_clamp_requests < 0)
     386             :             {
     387           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     388             :                          "GDALWMS: Invalid value of ClampRequests, true/false "
     389             :                          "expected.");
     390           0 :                 ret = CE_Failure;
     391             :             }
     392             :         }
     393             : 
     394         347 :         if (ret == CE_None)
     395             :         {
     396         346 :             CPLXMLNode *data_window_node = CPLGetXMLNode(config, "DataWindow");
     397         346 :             if (data_window_node == nullptr && m_bNeedsDataWindow)
     398             :             {
     399           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     400             :                          "GDALWMS: DataWindow missing.");
     401           0 :                 ret = CE_Failure;
     402             :             }
     403             :             else
     404             :             {
     405         346 :                 CPLString osDefaultX0, osDefaultX1, osDefaultY0, osDefaultY1;
     406         346 :                 CPLString osDefaultTileCountX, osDefaultTileCountY,
     407         346 :                     osDefaultTileLevel;
     408         346 :                 CPLString osDefaultOverviewCount;
     409         346 :                 osDefaultX0.Printf("%.8f", m_default_data_window.m_x0);
     410         346 :                 osDefaultX1.Printf("%.8f", m_default_data_window.m_x1);
     411         346 :                 osDefaultY0.Printf("%.8f", m_default_data_window.m_y0);
     412         346 :                 osDefaultY1.Printf("%.8f", m_default_data_window.m_y1);
     413         346 :                 osDefaultTileCountX.Printf("%d", m_default_tile_count_x);
     414         346 :                 osDefaultTileCountY.Printf("%d", m_default_tile_count_y);
     415         346 :                 if (m_default_data_window.m_tlevel >= 0)
     416             :                     osDefaultTileLevel.Printf("%d",
     417           1 :                                               m_default_data_window.m_tlevel);
     418         346 :                 if (m_default_overview_count >= 0)
     419             :                     osDefaultOverviewCount.Printf("%d",
     420           1 :                                                   m_default_overview_count);
     421         346 :                 const char *overview_count = CPLGetXMLValue(
     422             :                     config, "OverviewCount", osDefaultOverviewCount);
     423             :                 const char *ulx =
     424         346 :                     CPLGetXMLValue(data_window_node, "UpperLeftX", osDefaultX0);
     425             :                 const char *uly =
     426         346 :                     CPLGetXMLValue(data_window_node, "UpperLeftY", osDefaultY0);
     427         346 :                 const char *lrx = CPLGetXMLValue(data_window_node,
     428             :                                                  "LowerRightX", osDefaultX1);
     429         346 :                 const char *lry = CPLGetXMLValue(data_window_node,
     430             :                                                  "LowerRightY", osDefaultY1);
     431         346 :                 const char *sx = CPLGetXMLValue(data_window_node, "SizeX", "");
     432         346 :                 const char *sy = CPLGetXMLValue(data_window_node, "SizeY", "");
     433         346 :                 const char *tx = CPLGetXMLValue(data_window_node, "TileX", "0");
     434         346 :                 const char *ty = CPLGetXMLValue(data_window_node, "TileY", "0");
     435         346 :                 const char *tlevel = CPLGetXMLValue(
     436             :                     data_window_node, "TileLevel", osDefaultTileLevel);
     437         346 :                 const char *str_tile_count_x = CPLGetXMLValue(
     438             :                     data_window_node, "TileCountX", osDefaultTileCountX);
     439         346 :                 const char *str_tile_count_y = CPLGetXMLValue(
     440             :                     data_window_node, "TileCountY", osDefaultTileCountY);
     441             :                 const char *y_origin =
     442         346 :                     CPLGetXMLValue(data_window_node, "YOrigin", "default");
     443             : 
     444         346 :                 if ((ulx[0] != '\0') && (uly[0] != '\0') && (lrx[0] != '\0') &&
     445         346 :                     (lry[0] != '\0'))
     446             :                 {
     447         346 :                     m_data_window.m_x0 = CPLAtof(ulx);
     448         346 :                     m_data_window.m_y0 = CPLAtof(uly);
     449         346 :                     m_data_window.m_x1 = CPLAtof(lrx);
     450         346 :                     m_data_window.m_y1 = CPLAtof(lry);
     451             :                 }
     452             :                 else
     453             :                 {
     454           0 :                     CPLError(
     455             :                         CE_Failure, CPLE_AppDefined,
     456             :                         "GDALWMS: Mandatory elements of DataWindow missing: "
     457             :                         "UpperLeftX, UpperLeftY, LowerRightX, LowerRightY.");
     458           0 :                     ret = CE_Failure;
     459             :                 }
     460             : 
     461         346 :                 m_data_window.m_tlevel = atoi(tlevel);
     462             :                 // Limit to 30 to avoid 1 << m_tlevel overflow
     463         346 :                 if (m_data_window.m_tlevel < 0 || m_data_window.m_tlevel > 30)
     464             :                 {
     465           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     466             :                              "Invalid value for TileLevel");
     467           0 :                     return CE_Failure;
     468             :                 }
     469             : 
     470         346 :                 if (ret == CE_None)
     471             :                 {
     472         346 :                     if ((sx[0] != '\0') && (sy[0] != '\0'))
     473             :                     {
     474         329 :                         m_data_window.m_sx = atoi(sx);
     475         329 :                         m_data_window.m_sy = atoi(sy);
     476             :                     }
     477          17 :                     else if ((tlevel[0] != '\0') &&
     478          17 :                              (str_tile_count_x[0] != '\0') &&
     479          17 :                              (str_tile_count_y[0] != '\0'))
     480             :                     {
     481          17 :                         const int tile_count_x = atoi(str_tile_count_x);
     482          17 :                         if (tile_count_x <= 0)
     483             :                         {
     484           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     485             :                                      "Invalid value for TileCountX");
     486           0 :                             return CE_Failure;
     487             :                         }
     488          17 :                         if (tile_count_x > INT_MAX / m_block_size_x ||
     489          17 :                             tile_count_x * m_block_size_x >
     490          17 :                                 INT_MAX / (1 << m_data_window.m_tlevel))
     491             :                         {
     492           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     493             :                                      "Integer overflow in tile_count_x * "
     494             :                                      "m_block_size_x * (1 << "
     495             :                                      "m_data_window.m_tlevel)");
     496           0 :                             return CE_Failure;
     497             :                         }
     498          17 :                         m_data_window.m_sx = tile_count_x * m_block_size_x *
     499          17 :                                              (1 << m_data_window.m_tlevel);
     500             : 
     501          17 :                         const int tile_count_y = atoi(str_tile_count_y);
     502          17 :                         if (tile_count_y <= 0)
     503             :                         {
     504           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     505             :                                      "Invalid value for TileCountY");
     506           0 :                             return CE_Failure;
     507             :                         }
     508          17 :                         if (tile_count_y > INT_MAX / m_block_size_y ||
     509          17 :                             tile_count_y * m_block_size_y >
     510          17 :                                 INT_MAX / (1 << m_data_window.m_tlevel))
     511             :                         {
     512           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     513             :                                      "Integer overflow in tile_count_y * "
     514             :                                      "m_block_size_y * (1 << "
     515             :                                      "m_data_window.m_tlevel)");
     516           0 :                             return CE_Failure;
     517             :                         }
     518          17 :                         m_data_window.m_sy = tile_count_y * m_block_size_y *
     519          17 :                                              (1 << m_data_window.m_tlevel);
     520             :                     }
     521             :                     else
     522             :                     {
     523           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     524             :                                  "GDALWMS: Mandatory elements of DataWindow "
     525             :                                  "missing: SizeX, SizeY.");
     526           0 :                         ret = CE_Failure;
     527             :                     }
     528             :                 }
     529         346 :                 if (ret == CE_None)
     530             :                 {
     531         346 :                     if ((tx[0] != '\0') && (ty[0] != '\0'))
     532             :                     {
     533         346 :                         m_data_window.m_tx = atoi(tx);
     534         346 :                         m_data_window.m_ty = atoi(ty);
     535             :                     }
     536             :                     else
     537             :                     {
     538           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     539             :                                  "GDALWMS: Mandatory elements of DataWindow "
     540             :                                  "missing: TileX, TileY.");
     541           0 :                         ret = CE_Failure;
     542             :                     }
     543             :                 }
     544             : 
     545         346 :                 if (ret == CE_None)
     546             :                 {
     547         346 :                     if (overview_count[0] != '\0')
     548             :                     {
     549           5 :                         nOverviews = atoi(overview_count);
     550             :                     }
     551         341 :                     else if (tlevel[0] != '\0')
     552             :                     {
     553         335 :                         nOverviews = m_data_window.m_tlevel;
     554             :                     }
     555             :                     else
     556             :                     {
     557             :                         const int min_overview_size = std::max(
     558           6 :                             32, std::min(m_block_size_x, m_block_size_y));
     559             :                         double a =
     560           6 :                             log(static_cast<double>(std::min(
     561           6 :                                 m_data_window.m_sx, m_data_window.m_sy))) /
     562             :                                 log(2.0) -
     563           6 :                             log(static_cast<double>(min_overview_size)) /
     564           6 :                                 log(2.0);
     565           6 :                         nOverviews = std::max(
     566           6 :                             0, std::min(static_cast<int>(ceil(a)), 32));
     567             :                     }
     568             :                 }
     569         346 :                 if (ret == CE_None)
     570             :                 {
     571         692 :                     CPLString y_origin_str = y_origin;
     572         346 :                     if (y_origin_str == "top")
     573             :                     {
     574         332 :                         m_data_window.m_y_origin = GDALWMSDataWindow::TOP;
     575             :                     }
     576          14 :                     else if (y_origin_str == "bottom")
     577             :                     {
     578           0 :                         m_data_window.m_y_origin = GDALWMSDataWindow::BOTTOM;
     579             :                     }
     580          14 :                     else if (y_origin_str == "default")
     581             :                     {
     582          14 :                         m_data_window.m_y_origin = GDALWMSDataWindow::DEFAULT;
     583             :                     }
     584             :                     else
     585             :                     {
     586           0 :                         CPLError(
     587             :                             CE_Failure, CPLE_AppDefined,
     588             :                             "GDALWMS: DataWindow YOrigin must be set to "
     589             :                             "one of 'default', 'top', or 'bottom', not '%s'.",
     590             :                             y_origin_str.c_str());
     591           0 :                         ret = CE_Failure;
     592             :                     }
     593             :                 }
     594             :             }
     595             :         }
     596             : 
     597         347 :         if (ret == CE_None)
     598             :         {
     599         346 :             if (nBands < 1)
     600         346 :                 nBands = atoi(CPLGetXMLValue(config, "BandsCount", "3"));
     601         346 :             if (nBands < 1)
     602             :             {
     603           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     604             :                          "GDALWMS: Bad number of bands.");
     605           0 :                 ret = CE_Failure;
     606             :             }
     607             :         }
     608             : 
     609         347 :         if (ret == CE_None)
     610             :         {
     611         346 :             const char *data_type = CPLGetXMLValue(config, "DataType", "Byte");
     612         346 :             if (!STARTS_WITH(data_type, "Byte"))
     613           3 :                 SetTileOO("@DATATYPE", data_type);
     614         346 :             m_data_type = GDALGetDataTypeByName(data_type);
     615         346 :             if (m_data_type == GDT_Unknown || m_data_type >= GDT_TypeCount)
     616             :             {
     617           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     618             :                          "GDALWMS: Invalid value in DataType. Data type \"%s\" "
     619             :                          "is not supported.",
     620             :                          data_type);
     621           0 :                 ret = CE_Failure;
     622             :             }
     623             :         }
     624             : 
     625             :         // Initialize the bands and the overviews.  Assumes overviews are powers
     626             :         // of two
     627         347 :         if (ret == CE_None)
     628             :         {
     629         346 :             nRasterXSize = m_data_window.m_sx;
     630         346 :             nRasterYSize = m_data_window.m_sy;
     631             : 
     632         692 :             if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize) ||
     633         346 :                 !GDALCheckBandCount(nBands, TRUE))
     634             :             {
     635           0 :                 return CE_Failure;
     636             :             }
     637             : 
     638         346 :             GDALColorInterp default_color_interp[4][4] = {
     639             :                 {GCI_GrayIndex, GCI_Undefined, GCI_Undefined, GCI_Undefined},
     640             :                 {GCI_GrayIndex, GCI_AlphaBand, GCI_Undefined, GCI_Undefined},
     641             :                 {GCI_RedBand, GCI_GreenBand, GCI_BlueBand, GCI_Undefined},
     642             :                 {GCI_RedBand, GCI_GreenBand, GCI_BlueBand, GCI_AlphaBand}};
     643        1673 :             for (int i = 0; i < nBands; ++i)
     644             :             {
     645        1327 :                 GDALColorInterp color_interp =
     646        1327 :                     (nBands <= 4 && i <= 3 ? default_color_interp[nBands - 1][i]
     647             :                                            : GCI_Undefined);
     648        1327 :                 GDALWMSRasterBand *band = new GDALWMSRasterBand(this, i, 1.0);
     649        1327 :                 band->m_color_interp = color_interp;
     650        1327 :                 SetBand(i + 1, band);
     651        1327 :                 double scale = 0.5;
     652        6878 :                 for (int j = 0; j < nOverviews; ++j)
     653             :                 {
     654        5551 :                     if (!band->AddOverview(scale))
     655           0 :                         break;
     656        5551 :                     band->m_color_interp = color_interp;
     657        5551 :                     scale *= 0.5;
     658             :                 }
     659             :             }
     660             :         }
     661             :     }
     662             : 
     663             :     // Let the local configuration override the minidriver supplied projection
     664         350 :     if (ret == CE_None)
     665             :     {
     666         349 :         const char *proj = CPLGetXMLValue(config, "Projection", "");
     667         349 :         if (proj[0] != '\0')
     668             :         {
     669         158 :             m_oSRS = ProjToSRS(proj);
     670         158 :             if (m_oSRS.IsEmpty())
     671             :             {
     672           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     673             :                          "GDALWMS: Bad projection specified.");
     674           0 :                 ret = CE_Failure;
     675             :             }
     676             :         }
     677             :     }
     678             : 
     679             :     // Same for Min, Max and NoData, defined per band or per dataset
     680             :     // If they are set as null strings, they clear the server declared values
     681         350 :     if (ret == CE_None)
     682             :     {
     683             :         // Data values are attributes, they include NoData Min and Max
     684         349 :         if (nullptr != CPLGetXMLNode(config, "DataValues"))
     685             :         {
     686             :             const char *nodata =
     687           3 :                 CPLGetXMLValue(config, "DataValues.NoData", "");
     688           3 :             if (strlen(nodata) > 0)
     689             :             {
     690           3 :                 SetTileOO("@NDV", nodata);
     691           3 :                 WMSSetNoDataValue(nodata);
     692             :             }
     693           3 :             const char *min = CPLGetXMLValue(config, "DataValues.min", nullptr);
     694           3 :             if (min != nullptr)
     695           0 :                 WMSSetMinValue(min);
     696           3 :             const char *max = CPLGetXMLValue(config, "DataValues.max", nullptr);
     697           3 :             if (max != nullptr)
     698           0 :                 WMSSetMaxValue(max);
     699             :         }
     700             :     }
     701             : 
     702         350 :     if (ret == CE_None)
     703             :     {
     704         349 :         if (m_oSRS.IsEmpty())
     705             :         {
     706         191 :             const auto &oSRS = m_mini_driver->GetSpatialRef();
     707         191 :             if (!oSRS.IsEmpty())
     708             :             {
     709           6 :                 m_oSRS = oSRS;
     710             :             }
     711             :         }
     712             :     }
     713             : 
     714             :     // Finish the minidriver initialization
     715         350 :     if (ret == CE_None)
     716         349 :         m_mini_driver->EndInit();
     717             : 
     718         350 :     return ret;
     719             : }
     720             : 
     721             : /************************************************************************/
     722             : /*                             IRasterIO()                              */
     723             : /************************************************************************/
     724           9 : CPLErr GDALWMSDataset::IRasterIO(GDALRWFlag rw, int x0, int y0, int sx, int sy,
     725             :                                  void *buffer, int bsx, int bsy,
     726             :                                  GDALDataType bdt, int band_count,
     727             :                                  BANDMAP_TYPE band_map, GSpacing nPixelSpace,
     728             :                                  GSpacing nLineSpace, GSpacing nBandSpace,
     729             :                                  GDALRasterIOExtraArg *psExtraArg)
     730             : {
     731             :     CPLErr ret;
     732             : 
     733           9 :     if (rw != GF_Read)
     734           0 :         return CE_Failure;
     735           9 :     if (buffer == nullptr)
     736           0 :         return CE_Failure;
     737           9 :     if ((sx == 0) || (sy == 0) || (bsx == 0) || (bsy == 0) || (band_count == 0))
     738           0 :         return CE_None;
     739             : 
     740           9 :     m_hint.m_x0 = x0;
     741           9 :     m_hint.m_y0 = y0;
     742           9 :     m_hint.m_sx = sx;
     743           9 :     m_hint.m_sy = sy;
     744           9 :     m_hint.m_overview = -1;
     745           9 :     m_hint.m_valid = true;
     746             :     // printf("[%p] GDALWMSDataset::IRasterIO(x0: %d, y0: %d, sx: %d, sy: %d,
     747             :     // bsx: %d, bsy: %d, band_count: %d, band_map: %p)\n", this, x0, y0, sx, sy,
     748             :     // bsx, bsy, band_count, band_map);
     749           9 :     ret = GDALDataset::IRasterIO(rw, x0, y0, sx, sy, buffer, bsx, bsy, bdt,
     750             :                                  band_count, band_map, nPixelSpace, nLineSpace,
     751             :                                  nBandSpace, psExtraArg);
     752           9 :     m_hint.m_valid = false;
     753             : 
     754           9 :     return ret;
     755             : }
     756             : 
     757             : /************************************************************************/
     758             : /*                          GetSpatialRef()                             */
     759             : /************************************************************************/
     760          87 : const OGRSpatialReference *GDALWMSDataset::GetSpatialRef() const
     761             : {
     762          87 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     763             : }
     764             : 
     765             : /************************************************************************/
     766             : /*                           SetSpatialRef()                            */
     767             : /************************************************************************/
     768           0 : CPLErr GDALWMSDataset::SetSpatialRef(const OGRSpatialReference *)
     769             : {
     770           0 :     return CE_Failure;
     771             : }
     772             : 
     773             : /************************************************************************/
     774             : /*                          GetGeoTransform()                           */
     775             : /************************************************************************/
     776         171 : CPLErr GDALWMSDataset::GetGeoTransform(GDALGeoTransform &gt) const
     777             : {
     778         171 :     if (!(m_mini_driver_caps.m_has_geotransform))
     779             :     {
     780           0 :         gt = GDALGeoTransform();
     781           0 :         return CE_Failure;
     782             :     }
     783         171 :     gt[0] = m_data_window.m_x0;
     784         342 :     gt[1] = (m_data_window.m_x1 - m_data_window.m_x0) /
     785         171 :             static_cast<double>(m_data_window.m_sx);
     786         171 :     gt[2] = 0.0;
     787         171 :     gt[3] = m_data_window.m_y0;
     788         171 :     gt[4] = 0.0;
     789         342 :     gt[5] = (m_data_window.m_y1 - m_data_window.m_y0) /
     790         171 :             static_cast<double>(m_data_window.m_sy);
     791         171 :     return CE_None;
     792             : }
     793             : 
     794             : /************************************************************************/
     795             : /*                          SetGeoTransform()                           */
     796             : /************************************************************************/
     797           0 : CPLErr GDALWMSDataset::SetGeoTransform(const GDALGeoTransform &)
     798             : {
     799           0 :     return CE_Failure;
     800             : }
     801             : 
     802             : /************************************************************************/
     803             : /*                             AdviseRead()                             */
     804             : /************************************************************************/
     805          20 : CPLErr GDALWMSDataset::AdviseRead(int x0, int y0, int sx, int sy, int bsx,
     806             :                                   int bsy, GDALDataType bdt,
     807             :                                   CPL_UNUSED int band_count,
     808             :                                   CPL_UNUSED int *band_map, char **options)
     809             : {
     810             :     //    printf("AdviseRead(%d, %d, %d, %d)\n", x0, y0, sx, sy);
     811          20 :     if (m_offline_mode || !m_use_advise_read)
     812          20 :         return CE_None;
     813           0 :     if (m_cache == nullptr)
     814           0 :         return CE_Failure;
     815             : 
     816           0 :     GDALRasterBand *band = GetRasterBand(1);
     817           0 :     if (band == nullptr)
     818           0 :         return CE_Failure;
     819           0 :     return band->AdviseRead(x0, y0, sx, sy, bsx, bsy, bdt, options);
     820             : }
     821             : 
     822             : /************************************************************************/
     823             : /*                      GetMetadataDomainList()                         */
     824             : /************************************************************************/
     825             : 
     826           0 : char **GDALWMSDataset::GetMetadataDomainList()
     827             : {
     828           0 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
     829           0 :                                    TRUE, "WMS", nullptr);
     830             : }
     831             : 
     832             : /************************************************************************/
     833             : /*                          GetMetadataItem()                           */
     834             : /************************************************************************/
     835         374 : const char *GDALWMSDataset::GetMetadataItem(const char *pszName,
     836             :                                             const char *pszDomain)
     837             : {
     838         374 :     if (pszName != nullptr && EQUAL(pszName, "XML") && pszDomain != nullptr &&
     839           3 :         EQUAL(pszDomain, "WMS"))
     840             :     {
     841           3 :         return (m_osXML.size()) ? m_osXML.c_str() : nullptr;
     842             :     }
     843             : 
     844         371 :     return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
     845             : }
     846             : 
     847             : // Builds a CSL of options or returns the previous one
     848          41 : const char *const *GDALWMSDataset::GetHTTPRequestOpts()
     849             : {
     850          41 :     if (m_http_options != nullptr)
     851           7 :         return m_http_options;
     852             : 
     853          34 :     char **opts = nullptr;
     854          34 :     if (m_http_timeout != -1)
     855          34 :         opts = CSLAddString(opts, CPLOPrintf("TIMEOUT=%d", m_http_timeout));
     856             : 
     857          34 :     if (!m_osUserAgent.empty())
     858           0 :         opts = CSLAddNameValue(opts, "USERAGENT", m_osUserAgent);
     859             :     else
     860          34 :         opts = CSLAddString(
     861             :             opts, "USERAGENT=GDAL WMS driver (https://gdal.org/frmt_wms.html)");
     862             : 
     863          34 :     if (!m_osReferer.empty())
     864           0 :         opts = CSLAddNameValue(opts, "REFERER", m_osReferer);
     865             : 
     866          34 :     if (m_unsafeSsl >= 1)
     867          11 :         opts = CSLAddString(opts, "UNSAFESSL=1");
     868             : 
     869          34 :     if (!m_osUserPwd.empty())
     870           0 :         opts = CSLAddNameValue(opts, "USERPWD", m_osUserPwd);
     871             : 
     872          34 :     if (m_http_max_conn > 0)
     873          34 :         opts = CSLAddString(opts, CPLOPrintf("MAXCONN=%d", m_http_max_conn));
     874             : 
     875          34 :     if (!m_osAccept.empty())
     876           1 :         opts = CSLAddNameValue(opts, "ACCEPT", m_osAccept.c_str());
     877             : 
     878          34 :     m_http_options = opts;
     879          34 :     return m_http_options;
     880             : }
     881             : 
     882           6 : void GDALWMSDataset::SetTileOO(const char *pszName, const char *pszValue)
     883             : {
     884           6 :     if (pszName == nullptr || strlen(pszName) == 0)
     885           0 :         return;
     886           6 :     int oldidx = CSLFindName(m_tileOO, pszName);
     887           6 :     if (oldidx >= 0)
     888           0 :         m_tileOO = CSLRemoveStrings(m_tileOO, oldidx, 1, nullptr);
     889           6 :     if (pszValue != nullptr && strlen(pszValue))
     890           6 :         m_tileOO = CSLAddNameValue(m_tileOO, pszName, pszValue);
     891             : }

Generated by: LCOV version 1.14