LCOV - code coverage report
Current view: top level - frmts/wms - gdalwmsrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 352 580 60.7 %
Date: 2025-10-23 10:45:21 Functions: 22 29 75.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  WMS Client Driver
       4             :  * Purpose:  GDALWMSRasterBand implementation.
       5             :  * Author:   Adam Nowacki, nowak@xpam.de
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2007, Adam Nowacki
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  * Copyright (c) 2017, Dmitry Baryshnikov, <polimax@mail.ru>
      11             :  * Copyright (c) 2017, NextGIS, <info@nextgis.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "wmsdriver.h"
      17             : 
      18             : #include "gdal_colortable.h"
      19             : #include "gdal_rasterblock.h"
      20             : #include "gdal_cpp_functions.h"
      21             : 
      22             : #include <algorithm>
      23             : 
      24        6959 : GDALWMSRasterBand::GDALWMSRasterBand(GDALWMSDataset *parent_dataset, int band,
      25        6959 :                                      double scale)
      26             :     : m_parent_dataset(parent_dataset), m_scale(scale), m_overview(-1),
      27             :       m_color_interp(GCI_Undefined), m_nAdviseReadBX0(-1), m_nAdviseReadBY0(-1),
      28        6959 :       m_nAdviseReadBX1(-1), m_nAdviseReadBY1(-1)
      29             : {
      30             : #ifdef DEBUG_VERBOSE
      31             :     printf("[%p] GDALWMSRasterBand::GDALWMSRasterBand(%p, %d, %f)\n", /*ok*/
      32             :            this, parent_dataset, band, scale);
      33             : #endif
      34             : 
      35        6959 :     if (scale == 1.0)
      36        1336 :         poDS = parent_dataset;
      37             :     else
      38        5623 :         poDS = nullptr;
      39        6959 :     if (parent_dataset->m_mini_driver_caps.m_overview_dim_computation_method ==
      40             :         OVERVIEW_ROUNDED)
      41             :     {
      42        6887 :         nRasterXSize = static_cast<int>(
      43        6887 :             m_parent_dataset->m_data_window.m_sx * scale + 0.5);
      44        6887 :         nRasterYSize = static_cast<int>(
      45        6887 :             m_parent_dataset->m_data_window.m_sy * scale + 0.5);
      46             :     }
      47             :     else
      48             :     {
      49          72 :         nRasterXSize =
      50          72 :             static_cast<int>(m_parent_dataset->m_data_window.m_sx * scale);
      51          72 :         nRasterYSize =
      52          72 :             static_cast<int>(m_parent_dataset->m_data_window.m_sy * scale);
      53             :     }
      54        6959 :     nBand = band;
      55        6959 :     eDataType = m_parent_dataset->m_data_type;
      56        6959 :     nBlockXSize = m_parent_dataset->m_block_size_x;
      57        6959 :     nBlockYSize = m_parent_dataset->m_block_size_y;
      58        6959 : }
      59             : 
      60       20877 : GDALWMSRasterBand::~GDALWMSRasterBand()
      61             : {
      62       12582 :     while (!m_overviews.empty())
      63             :     {
      64        5623 :         delete m_overviews.back();
      65        5623 :         m_overviews.pop_back();
      66             :     }
      67       13918 : }
      68             : 
      69             : // Request for x, y but all blocks between bx0-bx1 and by0-by1 should be read
      70          39 : CPLErr GDALWMSRasterBand::ReadBlocks(int x, int y, void *buffer, int bx0,
      71             :                                      int by0, int bx1, int by1, int advise_read)
      72             : {
      73          39 :     CPLErr ret = CE_None;
      74             : 
      75             :     // Get a vector of requests large enough for this call
      76          39 :     std::vector<WMSHTTPRequest> requests(static_cast<size_t>(bx1 - bx0 + 1) *
      77          39 :                                          (by1 - by0 + 1));
      78             : 
      79          39 :     size_t count = 0;  // How many requests are valid
      80          39 :     GDALWMSCache *cache = m_parent_dataset->m_cache;
      81          39 :     int offline = m_parent_dataset->m_offline_mode;
      82          39 :     const char *const *options = m_parent_dataset->GetHTTPRequestOpts();
      83             : 
      84          83 :     for (int iy = by0; iy <= by1; ++iy)
      85             :     {
      86         103 :         for (int ix = bx0; ix <= bx1; ++ix)
      87             :         {
      88          59 :             WMSHTTPRequest &request = requests[count];
      89          59 :             request.x = ix;
      90          59 :             request.y = iy;
      91          59 :             bool need_this_block = false;
      92          59 :             if (!advise_read)
      93             :             {
      94         248 :                 for (int ib = 1; ib <= m_parent_dataset->nBands; ++ib)
      95             :                 {
      96         189 :                     if ((ix == x) && (iy == y) && (ib == nBand))
      97             :                     {
      98          39 :                         need_this_block = true;
      99             :                     }
     100             :                     else
     101             :                     {
     102             :                         GDALWMSRasterBand *band =
     103             :                             static_cast<GDALWMSRasterBand *>(
     104         150 :                                 m_parent_dataset->GetRasterBand(ib));
     105         150 :                         if (m_overview >= 0)
     106             :                             band = static_cast<GDALWMSRasterBand *>(
     107          75 :                                 band->GetOverview(m_overview));
     108         150 :                         if (!band->IsBlockInCache(ix, iy))
     109         150 :                             need_this_block = true;
     110             :                     }
     111             :                 }
     112             :             }
     113             :             else
     114             :             {
     115           0 :                 need_this_block = true;
     116             :             }
     117             : 
     118          59 :             void *p = ((ix == x) && (iy == y)) ? buffer : nullptr;
     119          59 :             if (need_this_block)
     120             :             {
     121          59 :                 ret = AskMiniDriverForBlock(request, ix, iy);
     122          59 :                 if (ret != CE_None)
     123             :                 {
     124           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "%s",
     125             :                              request.Error.c_str());
     126           0 :                     ret = CE_Failure;
     127             :                 }
     128             :                 // A missing tile is signaled by setting a range of "none"
     129          59 :                 if (EQUAL(request.Range, "none"))
     130             :                 {
     131           0 :                     if (!advise_read)
     132             :                     {
     133           0 :                         if (EmptyBlock(ix, iy, nBand, p) != CE_None)
     134             :                         {
     135           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     136             :                                      "GDALWMS: EmptyBlock failed.");
     137           0 :                             ret = CE_Failure;
     138             :                         }
     139             :                     }
     140           0 :                     need_this_block = false;
     141             :                 }
     142          59 :                 if (ret == CE_None && cache != nullptr)
     143             :                 {
     144          36 :                     if (cache->GetItemStatus(request.URL) == CACHE_ITEM_OK)
     145             :                     {
     146          12 :                         if (advise_read)
     147             :                         {
     148           0 :                             need_this_block = false;
     149             :                         }
     150             :                         else
     151             :                         {
     152          12 :                             if (ReadBlockFromCache(request.URL, ix, iy, nBand,
     153          12 :                                                    p, 0) == CE_None)
     154             :                             {
     155          12 :                                 need_this_block = false;
     156             :                             }
     157             :                         }
     158             :                     }
     159             :                 }
     160             :             }
     161             : 
     162          59 :             if (need_this_block)
     163             :             {
     164          47 :                 if (offline)
     165             :                 {
     166           0 :                     if (!advise_read)
     167             :                     {
     168           0 :                         if (EmptyBlock(ix, iy, nBand, p) != CE_None)
     169             :                         {
     170           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     171             :                                      "GDALWMS: EmptyBlock failed.");
     172           0 :                             ret = CE_Failure;
     173             :                         }
     174             :                     }
     175             :                 }
     176             :                 else
     177             :                 {
     178          47 :                     request.options = options;
     179          47 :                     WMSHTTPInitializeRequest(&request);
     180          47 :                     count++;
     181             :                 }
     182             :             }
     183             :         }
     184             :     }
     185             : 
     186             :     // Fetch all the requests, OK to call with count of 0
     187          39 :     if (WMSHTTPFetchMulti(count ? &requests[0] : nullptr,
     188          39 :                           static_cast<int>(count)) != CE_None)
     189             :     {
     190           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     191             :                  "GDALWMS: CPLHTTPFetchMulti failed.");
     192           0 :         ret = CE_Failure;
     193             :     }
     194             : 
     195          86 :     for (size_t i = 0; i < count; ++i)
     196             :     {
     197          47 :         WMSHTTPRequest &request = requests[i];
     198          47 :         void *p = ((request.x == x) && (request.y == y)) ? buffer : nullptr;
     199          47 :         if (ret == CE_None)
     200             :         {
     201          53 :             int success = (request.nStatus == 200) ||
     202           6 :                           (!request.Range.empty() && request.nStatus == 206);
     203          47 :             if (success && (request.pabyData != nullptr) &&
     204          41 :                 (request.nDataLen > 0))
     205             :             {
     206             :                 CPLString file_name(
     207          82 :                     BufferToVSIFile(request.pabyData, request.nDataLen));
     208          41 :                 if (!file_name.empty())
     209             :                 {
     210             :                     /* check for error xml */
     211          41 :                     if (request.nDataLen >= 20)
     212             :                     {
     213          41 :                         const char *download_data =
     214             :                             reinterpret_cast<char *>(request.pabyData);
     215          41 :                         if (STARTS_WITH_CI(download_data, "<?xml ") ||
     216          41 :                             STARTS_WITH_CI(download_data, "<!DOCTYPE ") ||
     217          41 :                             STARTS_WITH_CI(download_data, "<ServiceException"))
     218             :                         {
     219           0 :                             if (ReportWMSException(file_name) != CE_None)
     220             :                             {
     221           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
     222             :                                          "GDALWMS: The server returned unknown "
     223             :                                          "exception.");
     224             :                             }
     225           0 :                             ret = CE_Failure;
     226             :                         }
     227             :                     }
     228          41 :                     if (ret == CE_None)
     229             :                     {
     230          41 :                         if (advise_read &&
     231           0 :                             !m_parent_dataset->m_verify_advise_read)
     232             :                         {
     233           0 :                             if (cache != nullptr)
     234           0 :                                 cache->Insert(request.URL, file_name);
     235             :                         }
     236             :                         else
     237             :                         {
     238          41 :                             ret = ReadBlockFromFile(file_name, request.x,
     239             :                                                     request.y, nBand, p,
     240             :                                                     advise_read);
     241          41 :                             if (ret == CE_None)
     242             :                             {
     243          41 :                                 if (cache != nullptr)
     244          22 :                                     cache->Insert(request.URL, file_name);
     245             :                             }
     246             :                             else
     247             :                             {
     248           0 :                                 CPLError(
     249             :                                     ret, CPLE_AppDefined,
     250             :                                     "GDALWMS: ReadBlockFromFile (%s) failed.",
     251             :                                     request.URL.c_str());
     252             :                             }
     253             :                         }
     254             :                     }
     255           0 :                     else if (m_parent_dataset->m_zeroblock_on_serverexceptions)
     256             :                     {
     257           0 :                         ret = EmptyBlock(request.x, request.y, nBand, p);
     258           0 :                         if (ret != CE_None)
     259           0 :                             CPLError(ret, CPLE_AppDefined,
     260             :                                      "GDALWMS: EmptyBlock failed.");
     261             :                     }
     262          41 :                     VSIUnlink(file_name);
     263          41 :                 }
     264             :             }
     265             :             else
     266             :             {  // HTTP error
     267             :                 // One more try to get cached block. For example if no web
     268             :                 // access available
     269           6 :                 CPLDebug("WMS", "ReadBlockFromCache");
     270             : 
     271           6 :                 if (m_parent_dataset->m_cache != nullptr)
     272           2 :                     ret = ReadBlockFromCache(request.URL, request.x, request.y,
     273             :                                              nBand, p, advise_read);
     274             :                 else
     275           4 :                     ret = CE_Failure;
     276             : 
     277           6 :                 if (ret != CE_None)
     278             :                 {
     279           6 :                     CPLDebug("WMS", "After ReadBlockFromCache");
     280          12 :                     if (m_parent_dataset->m_http_zeroblock_codes.find(
     281           6 :                             request.nStatus) !=
     282          12 :                         m_parent_dataset->m_http_zeroblock_codes.end())
     283             :                     {
     284           2 :                         if (!advise_read)
     285             :                         {
     286           2 :                             ret = EmptyBlock(request.x, request.y, nBand, p);
     287           2 :                             if (ret != CE_None)
     288           0 :                                 CPLError(ret, CPLE_AppDefined,
     289             :                                          "GDALWMS: EmptyBlock failed.");
     290             :                         }
     291             :                     }
     292             :                     else
     293             :                     {
     294           4 :                         ret = CE_Failure;
     295           7 :                         CPLError(ret, CPLE_AppDefined,
     296             :                                  "GDALWMS: Unable to download block %d, %d.\n"
     297             :                                  "URL: %s\n  HTTP status code: %d, error: %s.\n"
     298             :                                  "Add the HTTP status code to "
     299             :                                  "<ZeroBlockHttpCodes> to ignore this error "
     300             :                                  "(see https://gdal.org/frmt_wms.html).",
     301             :                                  request.x, request.y,
     302           4 :                                  !request.URL.empty() ? request.Error.c_str()
     303             :                                                       : "(null)",
     304             :                                  request.nStatus,
     305           4 :                                  !request.Error.empty() ? request.Error.c_str()
     306             :                                                         : "(null)");
     307             :                     }
     308             :                 }
     309             :             }
     310             :         }
     311             :     }
     312             : 
     313          78 :     return ret;
     314             : }
     315             : 
     316          39 : CPLErr GDALWMSRasterBand::IReadBlock(int x, int y, void *buffer)
     317             : {
     318          39 :     int bx0 = x;
     319          39 :     int by0 = y;
     320          39 :     int bx1 = x;
     321          39 :     int by1 = y;
     322             : 
     323          39 :     bool bCancelHint = false;
     324          39 :     if ((m_parent_dataset->m_hint.m_valid) &&
     325          39 :         (m_parent_dataset->m_hint.m_overview == m_overview))
     326             :     {
     327          39 :         int tbx0 = m_parent_dataset->m_hint.m_x0 / nBlockXSize;
     328          39 :         int tby0 = m_parent_dataset->m_hint.m_y0 / nBlockYSize;
     329          39 :         int tbx1 = (m_parent_dataset->m_hint.m_x0 +
     330          39 :                     m_parent_dataset->m_hint.m_sx - 1) /
     331          39 :                    nBlockXSize;
     332          39 :         int tby1 = (m_parent_dataset->m_hint.m_y0 +
     333          39 :                     m_parent_dataset->m_hint.m_sy - 1) /
     334          39 :                    nBlockYSize;
     335          39 :         if ((tbx0 <= x) && (tby0 <= y) && (tbx1 >= x) && (tby1 >= y))
     336             :         {
     337             :             // Avoid downloading a insane number of tiles at once.
     338             :             // Limit to 30x30 tiles centered around block of interest.
     339          39 :             bx0 = std::max(x - 15, tbx0);
     340          39 :             by0 = std::max(y - 15, tby0);
     341          39 :             bx1 = std::min(x + 15, tbx1);
     342          39 :             by1 = std::min(y + 15, tby1);
     343          39 :             bCancelHint =
     344          39 :                 (bx0 == tbx0 && by0 == tby0 && bx1 == tbx1 && by1 == tby1);
     345             :         }
     346             :     }
     347             : 
     348          39 :     CPLErr eErr = ReadBlocks(x, y, buffer, bx0, by0, bx1, by1, 0);
     349             : 
     350          39 :     if (bCancelHint)
     351             :     {
     352          39 :         m_parent_dataset->m_hint.m_valid = false;
     353             :     }
     354             : 
     355          39 :     return eErr;
     356             : }
     357             : 
     358         100 : CPLErr GDALWMSRasterBand::IRasterIO(GDALRWFlag rw, int x0, int y0, int sx,
     359             :                                     int sy, void *buffer, int bsx, int bsy,
     360             :                                     GDALDataType bdt, GSpacing nPixelSpace,
     361             :                                     GSpacing nLineSpace,
     362             :                                     GDALRasterIOExtraArg *psExtraArg)
     363             : {
     364             :     CPLErr ret;
     365             : 
     366         100 :     if (rw != GF_Read)
     367           0 :         return CE_Failure;
     368         100 :     if (buffer == nullptr)
     369           0 :         return CE_Failure;
     370         100 :     if ((sx == 0) || (sy == 0) || (bsx == 0) || (bsy == 0))
     371           0 :         return CE_None;
     372             : 
     373         100 :     m_parent_dataset->m_hint.m_x0 = x0;
     374         100 :     m_parent_dataset->m_hint.m_y0 = y0;
     375         100 :     m_parent_dataset->m_hint.m_sx = sx;
     376         100 :     m_parent_dataset->m_hint.m_sy = sy;
     377         100 :     m_parent_dataset->m_hint.m_overview = m_overview;
     378         100 :     m_parent_dataset->m_hint.m_valid = true;
     379         100 :     ret = GDALRasterBand::IRasterIO(rw, x0, y0, sx, sy, buffer, bsx, bsy, bdt,
     380             :                                     nPixelSpace, nLineSpace, psExtraArg);
     381         100 :     m_parent_dataset->m_hint.m_valid = false;
     382             : 
     383         100 :     return ret;
     384             : }
     385             : 
     386           0 : int GDALWMSRasterBand::HasArbitraryOverviews()
     387             : {
     388             :     //    return m_parent_dataset->m_mini_driver_caps.m_has_arb_overviews;
     389           0 :     return 0;  // not implemented yet
     390             : }
     391             : 
     392         188 : int GDALWMSRasterBand::GetOverviewCount()
     393             : {
     394         188 :     return static_cast<int>(m_overviews.size());
     395             : }
     396             : 
     397         293 : GDALRasterBand *GDALWMSRasterBand::GetOverview(int n)
     398             : {
     399         293 :     if ((!m_overviews.empty()) && (static_cast<size_t>(n) < m_overviews.size()))
     400         293 :         return m_overviews[n];
     401             :     else
     402           0 :         return nullptr;
     403             : }
     404             : 
     405        5623 : bool GDALWMSRasterBand::AddOverview(double scale)
     406             : {
     407             :     GDALWMSRasterBand *overview =
     408        5623 :         new GDALWMSRasterBand(m_parent_dataset, nBand, scale);
     409        5623 :     if (overview->GetXSize() == 0 || overview->GetYSize() == 0)
     410             :     {
     411           0 :         delete overview;
     412           0 :         return false;
     413             :     }
     414        5623 :     std::vector<GDALWMSRasterBand *>::iterator it = m_overviews.begin();
     415       36761 :     for (; it != m_overviews.end(); ++it)
     416             :     {
     417       31138 :         GDALWMSRasterBand *p = *it;
     418       31138 :         if (p->m_scale < scale)
     419           0 :             break;
     420             :     }
     421        5623 :     m_overviews.insert(it, overview);
     422        5623 :     it = m_overviews.begin();
     423       42384 :     for (int i = 0; it != m_overviews.end(); ++it, ++i)
     424             :     {
     425       36761 :         GDALWMSRasterBand *p = *it;
     426       36761 :         p->m_overview = i;
     427             :     }
     428        5623 :     return true;
     429             : }
     430             : 
     431         289 : bool GDALWMSRasterBand::IsBlockInCache(int x, int y)
     432             : {
     433         289 :     bool ret = false;
     434         289 :     GDALRasterBlock *b = TryGetLockedBlockRef(x, y);
     435         289 :     if (b != nullptr)
     436             :     {
     437           0 :         ret = true;
     438           0 :         b->DropLock();
     439             :     }
     440         289 :     return ret;
     441             : }
     442             : 
     443             : // This is the function that calculates the block coordinates for the fetch
     444          59 : CPLErr GDALWMSRasterBand::AskMiniDriverForBlock(WMSHTTPRequest &r, int x, int y)
     445             : {
     446          59 :     GDALWMSImageRequestInfo iri;
     447          59 :     GDALWMSTiledImageRequestInfo tiri;
     448             : 
     449          59 :     ComputeRequestInfo(iri, tiri, x, y);
     450         118 :     return m_parent_dataset->m_mini_driver->TiledImageRequest(r, iri, tiri);
     451             : }
     452             : 
     453          59 : void GDALWMSRasterBand::ComputeRequestInfo(GDALWMSImageRequestInfo &iri,
     454             :                                            GDALWMSTiledImageRequestInfo &tiri,
     455             :                                            int x, int y)
     456             : {
     457          59 :     int x0 = std::max(0, x * nBlockXSize);
     458          59 :     int y0 = std::max(0, y * nBlockYSize);
     459          59 :     int x1 = std::max(0, (x + 1) * nBlockXSize);
     460          59 :     int y1 = std::max(0, (y + 1) * nBlockYSize);
     461          59 :     if (m_parent_dataset->m_clamp_requests)
     462             :     {
     463          59 :         x0 = std::min(x0, nRasterXSize);
     464          59 :         y0 = std::min(y0, nRasterYSize);
     465          59 :         x1 = std::min(x1, nRasterXSize);
     466          59 :         y1 = std::min(y1, nRasterYSize);
     467             :     }
     468             : 
     469          59 :     const double rx = (m_parent_dataset->m_data_window.m_x1 -
     470          59 :                        m_parent_dataset->m_data_window.m_x0) /
     471          59 :                       static_cast<double>(nRasterXSize);
     472          59 :     const double ry = (m_parent_dataset->m_data_window.m_y1 -
     473          59 :                        m_parent_dataset->m_data_window.m_y0) /
     474          59 :                       static_cast<double>(nRasterYSize);
     475             :     /* Use different method for x0,y0 and x1,y1 to make sure calculated values
     476             :      * are exact for corner requests */
     477          59 :     iri.m_x0 = x0 * rx + m_parent_dataset->m_data_window.m_x0;
     478          59 :     iri.m_y0 = y0 * ry + m_parent_dataset->m_data_window.m_y0;
     479          59 :     iri.m_x1 = m_parent_dataset->m_data_window.m_x1 - (nRasterXSize - x1) * rx;
     480          59 :     iri.m_y1 = m_parent_dataset->m_data_window.m_y1 - (nRasterYSize - y1) * ry;
     481          59 :     iri.m_sx = x1 - x0;
     482          59 :     iri.m_sy = y1 - y0;
     483             : 
     484          59 :     int level = m_overview + 1;
     485          59 :     tiri.m_x = (m_parent_dataset->m_data_window.m_tx >> level) + x;
     486          59 :     tiri.m_y = (m_parent_dataset->m_data_window.m_ty >> level) + y;
     487          59 :     tiri.m_level = m_parent_dataset->m_data_window.m_tlevel - level;
     488          59 : }
     489             : 
     490             : /************************************************************************/
     491             : /*                      GetMetadataDomainList()                         */
     492             : /************************************************************************/
     493             : 
     494           0 : char **GDALWMSRasterBand::GetMetadataDomainList()
     495             : {
     496           0 :     char **m_list = GDALPamRasterBand::GetMetadataDomainList();
     497           0 :     char **mini_list = m_parent_dataset->m_mini_driver->GetMetadataDomainList();
     498           0 :     if (mini_list != nullptr)
     499             :     {
     500           0 :         m_list = CSLMerge(m_list, mini_list);
     501           0 :         CSLDestroy(mini_list);
     502             :     }
     503           0 :     return m_list;
     504             : }
     505             : 
     506        1025 : const char *GDALWMSRasterBand::GetMetadataItem(const char *pszName,
     507             :                                                const char *pszDomain)
     508             : {
     509        1025 :     if (!m_parent_dataset->m_mini_driver_caps.m_has_getinfo ||
     510          20 :         !(pszDomain != nullptr && EQUAL(pszDomain, "LocationInfo") &&
     511           0 :           (STARTS_WITH_CI(pszName, "Pixel_") ||
     512           0 :            STARTS_WITH_CI(pszName, "GeoPixel_"))))
     513        1025 :         return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain);
     514             : 
     515             :     /* ==================================================================== */
     516             :     /*      LocationInfo handling.                                          */
     517             :     /* ==================================================================== */
     518             : 
     519             :     /* -------------------------------------------------------------------- */
     520             :     /*      What pixel are we aiming at?                                    */
     521             :     /* -------------------------------------------------------------------- */
     522             :     int iPixel, iLine;
     523           0 :     if (STARTS_WITH_CI(pszName, "Pixel_"))
     524             :     {
     525           0 :         if (sscanf(pszName + 6, "%d_%d", &iPixel, &iLine) != 2)
     526           0 :             return nullptr;
     527             :     }
     528           0 :     else if (STARTS_WITH_CI(pszName, "GeoPixel_"))
     529             :     {
     530           0 :         GDALGeoTransform gt, invGT;
     531             :         double dfGeoX, dfGeoY;
     532             : 
     533             :         {
     534           0 :             dfGeoX = CPLAtof(pszName + 9);
     535           0 :             const char *pszUnderscore = strchr(pszName + 9, '_');
     536           0 :             if (!pszUnderscore)
     537           0 :                 return nullptr;
     538           0 :             dfGeoY = CPLAtof(pszUnderscore + 1);
     539             :         }
     540             : 
     541           0 :         if (m_parent_dataset->GetGeoTransform(gt) != CE_None)
     542           0 :             return nullptr;
     543             : 
     544           0 :         if (!GDALInvGeoTransform(gt.data(), invGT.data()))
     545           0 :             return nullptr;
     546             : 
     547           0 :         iPixel = static_cast<int>(
     548           0 :             floor(invGT[0] + invGT[1] * dfGeoX + invGT[2] * dfGeoY));
     549           0 :         iLine = static_cast<int>(
     550           0 :             floor(invGT[3] + invGT[4] * dfGeoX + invGT[5] * dfGeoY));
     551             : 
     552             :         /* The GetDataset() for the WMS driver is always the main overview
     553             :          * level, so rescale */
     554             :         /* the values if we are an overview */
     555           0 :         if (m_overview >= 0)
     556             :         {
     557           0 :             iPixel = static_cast<int>(
     558           0 :                 1.0 * iPixel * GetXSize() /
     559           0 :                 m_parent_dataset->GetRasterBand(1)->GetXSize());
     560           0 :             iLine = static_cast<int>(
     561           0 :                 1.0 * iLine * GetYSize() /
     562           0 :                 m_parent_dataset->GetRasterBand(1)->GetYSize());
     563             :         }
     564             :     }
     565             :     else
     566           0 :         return nullptr;
     567             : 
     568           0 :     if (iPixel < 0 || iLine < 0 || iPixel >= GetXSize() || iLine >= GetYSize())
     569           0 :         return nullptr;
     570             : 
     571           0 :     if (nBand != 1)
     572             :     {
     573           0 :         GDALRasterBand *poFirstBand = m_parent_dataset->GetRasterBand(1);
     574           0 :         if (m_overview >= 0)
     575           0 :             poFirstBand = poFirstBand->GetOverview(m_overview);
     576           0 :         if (poFirstBand)
     577           0 :             return poFirstBand->GetMetadataItem(pszName, pszDomain);
     578             :     }
     579             : 
     580           0 :     GDALWMSImageRequestInfo iri;
     581           0 :     GDALWMSTiledImageRequestInfo tiri;
     582           0 :     int nBlockXOff = iPixel / nBlockXSize;
     583           0 :     int nBlockYOff = iLine / nBlockYSize;
     584             : 
     585           0 :     ComputeRequestInfo(iri, tiri, nBlockXOff, nBlockYOff);
     586             : 
     587           0 :     CPLString url;
     588           0 :     m_parent_dataset->m_mini_driver->GetTiledImageInfo(
     589           0 :         url, iri, tiri, iPixel % nBlockXSize, iLine % nBlockXSize);
     590             : 
     591           0 :     if (url.empty())
     592           0 :         return nullptr;
     593             : 
     594           0 :     CPLDebug("WMS", "URL = %s", url.c_str());
     595             : 
     596           0 :     if (url == osMetadataItemURL)
     597             :     {
     598             :         // osMetadataItem.c_str() MUST be used, and not osMetadataItem,
     599             :         // otherwise a temporary copy is returned
     600           0 :         return !osMetadataItem.empty() ? osMetadataItem.c_str() : nullptr;
     601             :     }
     602             : 
     603           0 :     osMetadataItemURL = url;
     604             : 
     605             :     // This is OK, CPLHTTPFetch does not touch the options
     606             :     char **papszOptions =
     607           0 :         const_cast<char **>(m_parent_dataset->GetHTTPRequestOpts());
     608           0 :     CPLHTTPResult *psResult = CPLHTTPFetch(url, papszOptions);
     609             : 
     610           0 :     CPLString pszRes;
     611             : 
     612           0 :     if (psResult && psResult->pabyData)
     613           0 :         pszRes = reinterpret_cast<const char *>(psResult->pabyData);
     614           0 :     CPLHTTPDestroyResult(psResult);
     615             : 
     616           0 :     if (pszRes.empty())
     617             :     {
     618           0 :         osMetadataItem = "";
     619           0 :         return nullptr;
     620             :     }
     621             : 
     622           0 :     osMetadataItem = "<LocationInfo>";
     623           0 :     CPLPushErrorHandler(CPLQuietErrorHandler);
     624           0 :     CPLXMLNode *psXML = CPLParseXMLString(pszRes);
     625           0 :     CPLPopErrorHandler();
     626           0 :     if (psXML != nullptr && psXML->eType == CXT_Element)
     627             :     {
     628           0 :         if (strcmp(psXML->pszValue, "?xml") == 0)
     629             :         {
     630           0 :             if (psXML->psNext)
     631             :             {
     632           0 :                 char *pszXML = CPLSerializeXMLTree(psXML->psNext);
     633           0 :                 osMetadataItem += pszXML;
     634           0 :                 CPLFree(pszXML);
     635             :             }
     636             :         }
     637             :         else
     638             :         {
     639           0 :             osMetadataItem += pszRes;
     640           0 :         }
     641             :     }
     642             :     else
     643             :     {
     644           0 :         char *pszEscapedXML = CPLEscapeString(pszRes, -1, CPLES_XML_BUT_QUOTES);
     645           0 :         osMetadataItem += pszEscapedXML;
     646           0 :         CPLFree(pszEscapedXML);
     647             :     }
     648           0 :     if (psXML != nullptr)
     649           0 :         CPLDestroyXMLNode(psXML);
     650             : 
     651           0 :     osMetadataItem += "</LocationInfo>";
     652             : 
     653             :     // osMetadataItem.c_str() MUST be used, and not osMetadataItem,
     654             :     // otherwise a temporary copy is returned
     655           0 :     return osMetadataItem.c_str();
     656             : }
     657             : 
     658          53 : static const int *GetBandMapForExpand(int nSourceBands, int nWmsBands)
     659             : {
     660             :     static const int bandmap1to1[] = {1};
     661             :     static const int bandmap2to1[] = {1};
     662             :     static const int bandmap3to1[] = {1};
     663             :     static const int bandmap4to1[] = {1};
     664             : 
     665             :     static const int bandmap1to2[] = {1, 0};  // 0 == full opaque alpha band
     666             :     static const int bandmap2to2[] = {1, 2};
     667             :     static const int bandmap3to2[] = {1, 0};
     668             :     static const int bandmap4to2[] = {1, 4};
     669             : 
     670             :     static const int bandmap1to3[] = {1, 1, 1};
     671             :     static const int bandmap2to3[] = {1, 1, 1};
     672             :     static const int bandmap3to3[] = {1, 2, 3};
     673             :     static const int bandmap4to3[] = {1, 2, 3};
     674             : 
     675             :     static const int bandmap1to4[] = {1, 1, 1, 0};
     676             :     static const int bandmap2to4[] = {1, 1, 1, 2};
     677             :     static const int bandmap3to4[] = {1, 2, 3, 0};
     678             :     static const int bandmap4to4[] = {1, 2, 3, 4};
     679             : 
     680             :     static const int *const bandmap_selector[4][4] = {
     681             :         {bandmap1to1, bandmap2to1, bandmap3to1, bandmap4to1},
     682             :         {bandmap1to2, bandmap2to2, bandmap3to2, bandmap4to2},
     683             :         {bandmap1to3, bandmap2to3, bandmap3to3, bandmap4to3},
     684             :         {bandmap1to4, bandmap2to4, bandmap3to4, bandmap4to4},
     685             :     };
     686             : 
     687          53 :     if (nSourceBands > 4 || nSourceBands < 1)
     688             :     {
     689           0 :         return nullptr;
     690             :     }
     691          53 :     if (nWmsBands > 4 || nWmsBands < 1)
     692             :     {
     693           0 :         return nullptr;
     694             :     }
     695          53 :     return bandmap_selector[nWmsBands - 1][nSourceBands - 1];
     696             : }
     697             : 
     698          53 : CPLErr GDALWMSRasterBand::ReadBlockFromDataset(GDALDataset *ds, int x, int y,
     699             :                                                int to_buffer_band, void *buffer,
     700             :                                                int advise_read)
     701             : {
     702          53 :     CPLErr ret = CE_None;
     703          53 :     GByte *color_table = nullptr;
     704             :     int i;
     705             : 
     706             :     // CPLDebug("WMS", "ReadBlockFromDataset: to_buffer_band=%d, (x,y)=(%d,
     707             :     // %d)", to_buffer_band, x, y);
     708             : 
     709             :     /* expected size */
     710          53 :     const int esx = MIN(MAX(0, (x + 1) * nBlockXSize), nRasterXSize) -
     711          53 :                     MIN(MAX(0, x * nBlockXSize), nRasterXSize);
     712          53 :     const int esy = MIN(MAX(0, (y + 1) * nBlockYSize), nRasterYSize) -
     713          53 :                     MIN(MAX(0, y * nBlockYSize), nRasterYSize);
     714             : 
     715          53 :     int sx = ds->GetRasterXSize();
     716          53 :     int sy = ds->GetRasterYSize();
     717             :     /* Allow bigger than expected so pre-tiled constant size images work on
     718             :      * corners */
     719          53 :     if ((sx > nBlockXSize) || (sy > nBlockYSize) || (sx < esx) || (sy < esy))
     720             :     {
     721           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     722             :                  "GDALWMS: Incorrect size %d x %d of downloaded block, "
     723             :                  "expected %d x %d, max %d x %d.",
     724             :                  sx, sy, esx, esy, nBlockXSize, nBlockYSize);
     725           0 :         ret = CE_Failure;
     726             :     }
     727             : 
     728          53 :     int nDSRasterCount = ds->GetRasterCount();
     729          53 :     if (ret == CE_None)
     730             :     {
     731          53 :         if (nDSRasterCount != m_parent_dataset->nBands)
     732             :         {
     733             :             /* Maybe its an image with color table */
     734          34 :             if ((eDataType == GDT_Byte) && (ds->GetRasterCount() == 1))
     735             :             {
     736          24 :                 GDALRasterBand *rb = ds->GetRasterBand(1);
     737          24 :                 if (rb->GetRasterDataType() == GDT_Byte)
     738             :                 {
     739          24 :                     GDALColorTable *ct = rb->GetColorTable();
     740          24 :                     if (ct != nullptr)
     741             :                     {
     742          19 :                         if (!advise_read)
     743             :                         {
     744          19 :                             color_table = new GByte[256 * 4];
     745             :                             const int count =
     746          19 :                                 MIN(256, ct->GetColorEntryCount());
     747         796 :                             for (i = 0; i < count; ++i)
     748             :                             {
     749             :                                 GDALColorEntry ce;
     750         777 :                                 ct->GetColorEntryAsRGB(i, &ce);
     751         777 :                                 color_table[i] = static_cast<GByte>(ce.c1);
     752         777 :                                 color_table[i + 256] =
     753         777 :                                     static_cast<GByte>(ce.c2);
     754         777 :                                 color_table[i + 512] =
     755         777 :                                     static_cast<GByte>(ce.c3);
     756         777 :                                 color_table[i + 768] =
     757         777 :                                     static_cast<GByte>(ce.c4);
     758             :                             }
     759             : 
     760        4106 :                             for (i = count; i < 256; ++i)
     761             :                             {
     762        4087 :                                 color_table[i] = 0;
     763        4087 :                                 color_table[i + 256] = 0;
     764        4087 :                                 color_table[i + 512] = 0;
     765        4087 :                                 color_table[i + 768] = 0;
     766             :                             }
     767             :                         }
     768             :                     }
     769           5 :                     else if (m_parent_dataset->nBands <= 4)
     770             :                     {  // Promote single band to fake color table
     771           5 :                         color_table = new GByte[256 * 4];
     772        1285 :                         for (i = 0; i < 256; i++)
     773             :                         {
     774        1280 :                             color_table[i] = static_cast<GByte>(i);
     775        1280 :                             color_table[i + 256] = static_cast<GByte>(i);
     776        1280 :                             color_table[i + 512] = static_cast<GByte>(i);
     777        1280 :                             color_table[i + 768] = 255;  // Transparency
     778             :                         }
     779           5 :                         if (m_parent_dataset->nBands == 2)
     780             :                         {  // Luma-Alpha fixup
     781           0 :                             for (i = 0; i < 256; i++)
     782             :                             {
     783           0 :                                 color_table[i + 256] = 255;
     784             :                             }
     785             :                         }
     786             :                     }
     787             :                 }
     788             :             }
     789             :         }
     790             :     }
     791             : 
     792          53 :     if (!advise_read)
     793             :     {
     794             :         const int *const bandmap =
     795          53 :             GetBandMapForExpand(nDSRasterCount, m_parent_dataset->nBands);
     796         219 :         for (int ib = 1; ib <= m_parent_dataset->nBands; ++ib)
     797             :         {
     798         166 :             if (ret == CE_None)
     799             :             {
     800         166 :                 void *p = nullptr;
     801         166 :                 GDALRasterBlock *b = nullptr;
     802         166 :                 if ((buffer != nullptr) && (ib == to_buffer_band))
     803             :                 {
     804          33 :                     p = buffer;
     805             :                 }
     806             :                 else
     807             :                 {
     808             :                     GDALWMSRasterBand *band = static_cast<GDALWMSRasterBand *>(
     809         133 :                         m_parent_dataset->GetRasterBand(ib));
     810         133 :                     if (m_overview >= 0)
     811             :                     {
     812             :                         band = static_cast<GDALWMSRasterBand *>(
     813          73 :                             band->GetOverview(m_overview));
     814             :                     }
     815         133 :                     if (!band->IsBlockInCache(x, y))
     816             :                     {
     817         133 :                         b = band->GetLockedBlockRef(x, y, true);
     818         133 :                         if (b != nullptr)
     819             :                         {
     820         133 :                             p = b->GetDataRef();
     821         133 :                             if (p == nullptr)
     822             :                             {
     823           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
     824             :                                          "GDALWMS: GetDataRef returned NULL.");
     825           0 :                                 ret = CE_Failure;
     826             :                             }
     827             :                         }
     828             :                     }
     829             :                     else
     830             :                     {
     831             :                         // CPLDebug("WMS", "Band %d, block (x,y)=(%d, %d)
     832             :                         // already in cache", band->GetBand(), x, y);
     833             :                     }
     834             :                 }
     835             : 
     836         166 :                 if (p != nullptr)
     837             :                 {
     838         166 :                     int pixel_space = GDALGetDataTypeSizeBytes(eDataType);
     839         166 :                     int line_space = pixel_space * nBlockXSize;
     840         166 :                     if (color_table == nullptr)
     841             :                     {
     842          88 :                         if (bandmap == nullptr || bandmap[ib - 1] != 0)
     843             :                         {
     844          87 :                             GDALDataType dt = eDataType;
     845          87 :                             int nSourceBand = ib;
     846          87 :                             if (bandmap != nullptr)
     847             :                             {
     848          87 :                                 nSourceBand = bandmap[ib - 1];
     849             :                             }
     850             :                             // Get the data from the PNG as stored instead of
     851             :                             // converting, if the server asks for that
     852             :                             // TODO: This hack is from #3493 - not sure it
     853             :                             // really belongs here.
     854          87 :                             if ((GDT_Int16 == dt) &&
     855             :                                 (GDT_UInt16 ==
     856           0 :                                  ds->GetRasterBand(ib)->GetRasterDataType()))
     857             :                             {
     858           0 :                                 dt = GDT_UInt16;
     859             :                             }
     860             : 
     861          87 :                             if (ds->RasterIO(GF_Read, 0, 0, sx, sy, p, sx, sy,
     862             :                                              dt, 1, &nSourceBand, pixel_space,
     863          87 :                                              line_space, 0, nullptr) != CE_None)
     864             :                             {
     865           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
     866             :                                          "GDALWMS: RasterIO failed on "
     867             :                                          "downloaded block.");
     868           0 :                                 ret = CE_Failure;
     869          87 :                             }
     870             :                         }
     871             :                         else  // if( bandmap != nullptr && bandmap[ib - 1] == 0
     872             :                               // )
     873             :                         {  // parent expects 4 bands but file has fewer count so
     874             :                             // generate a all "opaque" 4th band
     875           1 :                             GByte *byte_buffer = reinterpret_cast<GByte *>(p);
     876         129 :                             for (int l_y = 0; l_y < sy; ++l_y)
     877             :                             {
     878       16512 :                                 for (int l_x = 0; l_x < sx; ++l_x)
     879             :                                 {
     880       16384 :                                     const int offset = l_x + l_y * line_space;
     881       16384 :                                     byte_buffer[offset] =
     882             :                                         255;  // fill with opaque
     883             :                                 }
     884             :                             }
     885             :                         }
     886             :                     }
     887          78 :                     else if (ib <= 4)
     888             :                     {
     889          78 :                         if (ds->RasterIO(GF_Read, 0, 0, sx, sy, p, sx, sy,
     890             :                                          eDataType, 1, nullptr, pixel_space,
     891          78 :                                          line_space, 0, nullptr) != CE_None)
     892             :                         {
     893           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     894             :                                      "GDALWMS: RasterIO failed on downloaded "
     895             :                                      "block.");
     896           0 :                             ret = CE_Failure;
     897             :                         }
     898             : 
     899          78 :                         if (ret == CE_None)
     900             :                         {
     901          78 :                             GByte *band_color_table =
     902          78 :                                 color_table + 256 * (ib - 1);
     903          78 :                             GByte *byte_buffer = reinterpret_cast<GByte *>(p);
     904       20558 :                             for (int l_y = 0; l_y < sy; ++l_y)
     905             :                             {
     906     5918720 :                                 for (int l_x = 0; l_x < sx; ++l_x)
     907             :                                 {
     908     5898240 :                                     const int offset = l_x + l_y * line_space;
     909     5898240 :                                     byte_buffer[offset] =
     910     5898240 :                                         band_color_table[byte_buffer[offset]];
     911             :                                 }
     912             :                             }
     913             :                         }
     914             :                     }
     915             :                     else
     916             :                     {
     917           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     918             :                                  "GDALWMS: Color table supports at most 4 "
     919             :                                  "components.");
     920           0 :                         ret = CE_Failure;
     921             :                     }
     922             :                 }
     923         166 :                 if (b != nullptr)
     924             :                 {
     925         133 :                     b->DropLock();
     926             :                 }
     927             :             }
     928             :         }
     929             :     }
     930          53 :     GDALClose(ds);
     931             : 
     932          53 :     if (color_table != nullptr)
     933             :     {
     934          24 :         delete[] color_table;
     935             :     }
     936             : 
     937          53 :     return ret;
     938             : }
     939             : 
     940          41 : CPLErr GDALWMSRasterBand::ReadBlockFromFile(const CPLString &soFileName, int x,
     941             :                                             int y, int to_buffer_band,
     942             :                                             void *buffer, int advise_read)
     943             : {
     944          41 :     GDALDataset *ds = GDALDataset::FromHandle(GDALOpenEx(
     945             :         soFileName, GDAL_OF_RASTER | GDAL_OF_READONLY | GDAL_OF_VERBOSE_ERROR,
     946          41 :         nullptr, m_parent_dataset->m_tileOO, nullptr));
     947          41 :     if (ds == nullptr)
     948             :     {
     949           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     950             :                  "GDALWMS: Unable to open downloaded block.");
     951           0 :         return CE_Failure;
     952             :     }
     953             : 
     954          41 :     return ReadBlockFromDataset(ds, x, y, to_buffer_band, buffer, advise_read);
     955             : }
     956             : 
     957          14 : CPLErr GDALWMSRasterBand::ReadBlockFromCache(const char *pszKey, int x, int y,
     958             :                                              int to_buffer_band, void *buffer,
     959             :                                              int advise_read)
     960             : {
     961          14 :     GDALWMSCache *cache = m_parent_dataset->m_cache;
     962          14 :     if (nullptr == cache)
     963             :     {
     964           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     965             :                  "GDALWMS: Unable to open downloaded block.");
     966           0 :         return CE_Failure;
     967             :     }
     968          14 :     GDALDataset *ds = cache->GetDataset(pszKey, m_parent_dataset->m_tileOO);
     969          14 :     if (ds == nullptr)
     970             :     {
     971           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     972             :                  "GDALWMS: Unable to open downloaded block.");
     973           2 :         return CE_Failure;
     974             :     }
     975             : 
     976          12 :     return ReadBlockFromDataset(ds, x, y, to_buffer_band, buffer, advise_read);
     977             : }
     978             : 
     979           2 : CPLErr GDALWMSRasterBand::EmptyBlock(int x, int y, int to_buffer_band,
     980             :                                      void *buffer)
     981             : {
     982           2 :     CPLErr ret = CE_None;
     983             : 
     984          10 :     for (int ib = 1; ib <= m_parent_dataset->nBands; ++ib)
     985             :     {
     986           8 :         if (ret == CE_None)
     987             :         {
     988           8 :             void *p = nullptr;
     989           8 :             GDALRasterBlock *b = nullptr;
     990             :             GDALWMSRasterBand *band = static_cast<GDALWMSRasterBand *>(
     991           8 :                 m_parent_dataset->GetRasterBand(ib));
     992           8 :             if (m_overview >= 0)
     993             :                 band = static_cast<GDALWMSRasterBand *>(
     994           0 :                     band->GetOverview(m_overview));
     995           8 :             if ((buffer != nullptr) && (ib == to_buffer_band))
     996             :             {
     997           2 :                 p = buffer;
     998             :             }
     999             :             else
    1000             :             {
    1001           6 :                 if (!band->IsBlockInCache(x, y))
    1002             :                 {
    1003           6 :                     b = band->GetLockedBlockRef(x, y, true);
    1004           6 :                     if (b != nullptr)
    1005             :                     {
    1006           6 :                         p = b->GetDataRef();
    1007           6 :                         if (p == nullptr)
    1008             :                         {
    1009           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    1010             :                                      "GDALWMS: GetDataRef returned NULL.");
    1011           0 :                             ret = CE_Failure;
    1012             :                         }
    1013             :                     }
    1014             :                 }
    1015             :             }
    1016           8 :             if (p != nullptr)
    1017             :             {
    1018             :                 int hasNDV;
    1019           8 :                 double valNDV = band->GetNoDataValue(&hasNDV);
    1020           8 :                 if (!hasNDV)
    1021           8 :                     valNDV = 0;
    1022           8 :                 GDALCopyWords(&valNDV, GDT_Float64, 0, p, eDataType,
    1023             :                               GDALGetDataTypeSizeBytes(eDataType),
    1024           8 :                               nBlockXSize * nBlockYSize);
    1025             :             }
    1026           8 :             if (b != nullptr)
    1027             :             {
    1028           6 :                 b->DropLock();
    1029             :             }
    1030             :         }
    1031             :     }
    1032             : 
    1033           2 :     return ret;
    1034             : }
    1035             : 
    1036           0 : CPLErr GDALWMSRasterBand::ReportWMSException(const char *file_name)
    1037             : {
    1038           0 :     CPLErr ret = CE_None;
    1039           0 :     int reported_errors_count = 0;
    1040             : 
    1041           0 :     CPLXMLNode *orig_root = CPLParseXMLFile(file_name);
    1042           0 :     CPLXMLNode *root = orig_root;
    1043           0 :     if (root != nullptr)
    1044             :     {
    1045           0 :         root = CPLGetXMLNode(root, "=ServiceExceptionReport");
    1046             :     }
    1047           0 :     if (root != nullptr)
    1048             :     {
    1049           0 :         CPLXMLNode *n = CPLGetXMLNode(root, "ServiceException");
    1050           0 :         while (n != nullptr)
    1051             :         {
    1052           0 :             const char *exception = CPLGetXMLValue(n, "=ServiceException", "");
    1053             :             const char *exception_code =
    1054           0 :                 CPLGetXMLValue(n, "=ServiceException.code", "");
    1055           0 :             if (exception[0] != '\0')
    1056             :             {
    1057           0 :                 if (exception_code[0] != '\0')
    1058             :                 {
    1059           0 :                     CPLError(
    1060             :                         CE_Failure, CPLE_AppDefined,
    1061             :                         "GDALWMS: The server returned exception code '%s': %s",
    1062             :                         exception_code, exception);
    1063           0 :                     ++reported_errors_count;
    1064             :                 }
    1065             :                 else
    1066             :                 {
    1067           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1068             :                              "GDALWMS: The server returned exception: %s",
    1069             :                              exception);
    1070           0 :                     ++reported_errors_count;
    1071             :                 }
    1072             :             }
    1073           0 :             else if (exception_code[0] != '\0')
    1074             :             {
    1075           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1076             :                          "GDALWMS: The server returned exception code '%s'.",
    1077             :                          exception_code);
    1078           0 :                 ++reported_errors_count;
    1079             :             }
    1080             : 
    1081           0 :             n = n->psNext;
    1082           0 :             if (n != nullptr)
    1083             :             {
    1084           0 :                 n = CPLGetXMLNode(n, "=ServiceException");
    1085             :             }
    1086             :         }
    1087             :     }
    1088             :     else
    1089             :     {
    1090           0 :         ret = CE_Failure;
    1091             :     }
    1092           0 :     if (orig_root != nullptr)
    1093             :     {
    1094           0 :         CPLDestroyXMLNode(orig_root);
    1095             :     }
    1096             : 
    1097           0 :     if (reported_errors_count == 0)
    1098             :     {
    1099           0 :         ret = CE_Failure;
    1100             :     }
    1101             : 
    1102           0 :     return ret;
    1103             : }
    1104             : 
    1105           0 : CPLErr GDALWMSRasterBand::AdviseRead(int nXOff, int nYOff, int nXSize,
    1106             :                                      int nYSize, int nBufXSize, int nBufYSize,
    1107             :                                      GDALDataType eDT, char **papszOptions)
    1108             : {
    1109             :     //    printf("AdviseRead(%d, %d, %d, %d)\n", nXOff, nYOff, nXSize, nYSize);
    1110           0 :     if (m_parent_dataset->m_offline_mode ||
    1111           0 :         !m_parent_dataset->m_use_advise_read)
    1112           0 :         return CE_None;
    1113           0 :     if (m_parent_dataset->m_cache == nullptr)
    1114           0 :         return CE_Failure;
    1115             : 
    1116             :     /* ==================================================================== */
    1117             :     /*      Do we have overviews that would be appropriate to satisfy       */
    1118             :     /*      this request?                                                   */
    1119             :     /* ==================================================================== */
    1120           0 :     if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
    1121             :     {
    1122           0 :         const int nOverview = GDALBandGetBestOverviewLevel2(
    1123             :             this, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, nullptr);
    1124           0 :         if (nOverview >= 0)
    1125             :         {
    1126           0 :             GDALRasterBand *poOverviewBand = GetOverview(nOverview);
    1127           0 :             if (poOverviewBand == nullptr)
    1128           0 :                 return CE_Failure;
    1129             : 
    1130           0 :             return poOverviewBand->AdviseRead(nXOff, nYOff, nXSize, nYSize,
    1131             :                                               nBufXSize, nBufYSize, eDT,
    1132           0 :                                               papszOptions);
    1133             :         }
    1134             :     }
    1135             : 
    1136           0 :     int bx0 = nXOff / nBlockXSize;
    1137           0 :     int by0 = nYOff / nBlockYSize;
    1138           0 :     int bx1 = (nXOff + nXSize - 1) / nBlockXSize;
    1139           0 :     int by1 = (nYOff + nYSize - 1) / nBlockYSize;
    1140             : 
    1141             :     // Avoid downloading a insane number of tiles
    1142           0 :     const int MAX_TILES = 1000;  // arbitrary number
    1143           0 :     if ((bx1 - bx0 + 1) > MAX_TILES / (by1 - by0 + 1))
    1144             :     {
    1145           0 :         CPLDebug("WMS", "Too many tiles for AdviseRead()");
    1146           0 :         return CE_Failure;
    1147             :     }
    1148             : 
    1149           0 :     if (m_nAdviseReadBX0 == bx0 && m_nAdviseReadBY0 == by0 &&
    1150           0 :         m_nAdviseReadBX1 == bx1 && m_nAdviseReadBY1 == by1)
    1151             :     {
    1152           0 :         return CE_None;
    1153             :     }
    1154           0 :     m_nAdviseReadBX0 = bx0;
    1155           0 :     m_nAdviseReadBY0 = by0;
    1156           0 :     m_nAdviseReadBX1 = bx1;
    1157           0 :     m_nAdviseReadBY1 = by1;
    1158             : 
    1159           0 :     return ReadBlocks(0, 0, nullptr, bx0, by0, bx1, by1, 1);
    1160             : }
    1161             : 
    1162         415 : GDALColorInterp GDALWMSRasterBand::GetColorInterpretation()
    1163             : {
    1164         415 :     return m_color_interp;
    1165             : }
    1166             : 
    1167           9 : CPLErr GDALWMSRasterBand::SetColorInterpretation(GDALColorInterp eNewInterp)
    1168             : {
    1169           9 :     m_color_interp = eNewInterp;
    1170           9 :     return CE_None;
    1171             : }
    1172             : 
    1173             : // Utility function, returns a value from a vector corresponding to the band
    1174             : // index or the first entry
    1175           0 : static double getBandValue(const std::vector<double> &v, size_t idx)
    1176             : {
    1177           0 :     idx--;
    1178           0 :     if (v.size() > idx)
    1179           0 :         return v[idx];
    1180           0 :     return v[0];
    1181             : }
    1182             : 
    1183         493 : double GDALWMSRasterBand::GetNoDataValue(int *pbSuccess)
    1184             : {
    1185         493 :     std::vector<double> &v = m_parent_dataset->vNoData;
    1186         493 :     if (v.empty())
    1187         493 :         return GDALPamRasterBand::GetNoDataValue(pbSuccess);
    1188           0 :     if (pbSuccess)
    1189           0 :         *pbSuccess = TRUE;
    1190           0 :     return getBandValue(v, nBand);
    1191             : }
    1192             : 
    1193           0 : double GDALWMSRasterBand::GetMinimum(int *pbSuccess)
    1194             : {
    1195           0 :     std::vector<double> &v = m_parent_dataset->vMin;
    1196           0 :     if (v.empty())
    1197           0 :         return GDALPamRasterBand::GetMinimum(pbSuccess);
    1198           0 :     if (pbSuccess)
    1199           0 :         *pbSuccess = TRUE;
    1200           0 :     return getBandValue(v, nBand);
    1201             : }
    1202             : 
    1203           0 : double GDALWMSRasterBand::GetMaximum(int *pbSuccess)
    1204             : {
    1205           0 :     std::vector<double> &v = m_parent_dataset->vMax;
    1206           0 :     if (v.empty())
    1207           0 :         return GDALPamRasterBand::GetMaximum(pbSuccess);
    1208           0 :     if (pbSuccess)
    1209           0 :         *pbSuccess = TRUE;
    1210           0 :     return getBandValue(v, nBand);
    1211             : }
    1212             : 
    1213         316 : GDALColorTable *GDALWMSRasterBand::GetColorTable()
    1214             : {
    1215         316 :     return m_parent_dataset->m_poColorTable;
    1216             : }

Generated by: LCOV version 1.14