Coverage Report

Created: 2025-06-09 07:07

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