Coverage Report

Created: 2026-02-14 09:00

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