Coverage Report

Created: 2026-09-26 08:22

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
1.17M
    : m_parent_dataset(parent_dataset), m_scale(scale), m_overview(-1),
27
1.17M
      m_color_interp(GCI_Undefined), m_nAdviseReadBX0(-1), m_nAdviseReadBY0(-1),
28
1.17M
      m_nAdviseReadBX1(-1), m_nAdviseReadBY1(-1)
29
1.17M
{
30
#ifdef DEBUG_VERBOSE
31
    printf("[%p] GDALWMSRasterBand::GDALWMSRasterBand(%p, %d, %f)\n", /*ok*/
32
           this, parent_dataset, band, scale);
33
#endif
34
35
1.17M
    if (scale == 1.0)
36
56.7k
        poDS = parent_dataset;
37
1.11M
    else
38
1.11M
        poDS = nullptr;
39
1.17M
    if (parent_dataset->m_mini_driver_caps.m_overview_dim_computation_method ==
40
1.17M
        OVERVIEW_ROUNDED)
41
1.17M
    {
42
1.17M
        nRasterXSize = static_cast<int>(
43
1.17M
            m_parent_dataset->m_data_window.m_sx * scale + 0.5);
44
1.17M
        nRasterYSize = static_cast<int>(
45
1.17M
            m_parent_dataset->m_data_window.m_sy * scale + 0.5);
46
1.17M
    }
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
1.17M
    nBand = band;
55
1.17M
    eDataType = m_parent_dataset->m_data_type;
56
1.17M
    nBlockXSize = m_parent_dataset->m_block_size_x;
57
1.17M
    nBlockYSize = m_parent_dataset->m_block_size_y;
58
1.17M
}
59
60
GDALWMSRasterBand::~GDALWMSRasterBand()
61
1.17M
{
62
2.28M
    while (!m_overviews.empty())
63
1.11M
    {
64
1.11M
        delete m_overviews.back();
65
1.11M
        m_overviews.pop_back();
66
1.11M
    }
67
1.17M
}
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.79k
    for (int iy = by0; iy <= by1; ++iy)
85
1.62k
    {
86
4.16k
        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.20k
                {
96
8.20k
                    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.20k
                }
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.50k
            {
121
2.50k
                ret = AskMiniDriverForBlock(request, ix, iy);
122
2.50k
                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.50k
                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.50k
                if (ret == CE_None && cache != nullptr)
143
548
                {
144
548
                    if (cache->GetItemStatus(request.URL) == CACHE_ITEM_OK)
145
116
                    {
146
116
                        if (advise_read)
147
0
                        {
148
0
                            need_this_block = false;
149
0
                        }
150
116
                        else
151
116
                        {
152
116
                            if (ReadBlockFromCache(request.URL, ix, iy, nBand,
153
116
                                                   p, 0) == CE_None)
154
116
                            {
155
116
                                need_this_block = false;
156
116
                            }
157
116
                        }
158
116
                    }
159
548
                }
160
2.50k
            }
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.56k
    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
55
                (request.nDataLen > 0))
205
55
            {
206
55
                CPLString file_name(
207
55
                    BufferToVSIFile(request.pabyData, request.nDataLen));
208
55
                if (!file_name.empty())
209
55
                {
210
                    /* check for error xml */
211
55
                    if (request.nDataLen >= 20)
212
55
                    {
213
55
                        const char *download_data =
214
55
                            reinterpret_cast<char *>(request.pabyData);
215
55
                        if (STARTS_WITH_CI(download_data, "<?xml ") ||
216
55
                            STARTS_WITH_CI(download_data, "<!DOCTYPE ") ||
217
55
                            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
55
                    }
228
55
                    if (ret == CE_None)
229
55
                    {
230
55
                        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
55
                        else
237
55
                        {
238
55
                            ret = ReadBlockFromFile(file_name, request.x,
239
55
                                                    request.y, nBand, p,
240
55
                                                    advise_read);
241
55
                            if (ret == CE_None)
242
52
                            {
243
52
                                if (cache != nullptr)
244
52
                                    cache->Insert(request.URL, file_name);
245
52
                            }
246
3
                            else
247
3
                            {
248
3
                                CPLError(
249
3
                                    ret, CPLE_AppDefined,
250
3
                                    "GDALWMS: ReadBlockFromFile (%s) failed.",
251
3
                                    request.URL.c_str());
252
3
                            }
253
55
                        }
254
55
                    }
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
55
                    VSIUnlink(file_name);
263
55
                }
264
55
            }
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
93
                    ret = ReadBlockFromCache(request.URL, request.x, request.y,
273
93
                                             nBand, p, advise_read);
274
1.03k
                else
275
1.03k
                    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.55k
{
364
1.55k
    CPLErr ret;
365
366
1.55k
    if (rw != GF_Read)
367
0
        return CE_Failure;
368
1.55k
    if (buffer == nullptr)
369
0
        return CE_Failure;
370
1.55k
    if ((sx == 0) || (sy == 0) || (bsx == 0) || (bsy == 0))
371
0
        return CE_None;
372
373
1.55k
    m_parent_dataset->m_hint.m_x0 = x0;
374
1.55k
    m_parent_dataset->m_hint.m_y0 = y0;
375
1.55k
    m_parent_dataset->m_hint.m_sx = sx;
376
1.55k
    m_parent_dataset->m_hint.m_sy = sy;
377
1.55k
    m_parent_dataset->m_hint.m_overview = m_overview;
378
1.55k
    m_parent_dataset->m_hint.m_valid = true;
379
1.55k
    ret = GDALRasterBand::IRasterIO(rw, x0, y0, sx, sy, buffer, bsx, bsy, bdt,
380
1.55k
                                    nPixelSpace, nLineSpace, psExtraArg);
381
1.55k
    m_parent_dataset->m_hint.m_valid = false;
382
383
1.55k
    return ret;
384
1.55k
}
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.44k
{
394
3.44k
    return static_cast<int>(m_overviews.size());
395
3.44k
}
396
397
GDALRasterBand *GDALWMSRasterBand::GetOverview(int n)
398
48.2k
{
399
48.2k
    if ((!m_overviews.empty()) && (static_cast<size_t>(n) < m_overviews.size()))
400
48.2k
        return m_overviews[n];
401
0
    else
402
0
        return nullptr;
403
48.2k
}
404
405
bool GDALWMSRasterBand::AddOverview(double scale)
406
1.11M
{
407
1.11M
    GDALWMSRasterBand *overview =
408
1.11M
        new GDALWMSRasterBand(m_parent_dataset, nBand, scale);
409
1.11M
    if (overview->GetXSize() == 0 || overview->GetYSize() == 0)
410
9
    {
411
9
        delete overview;
412
9
        return false;
413
9
    }
414
1.11M
    std::vector<GDALWMSRasterBand *>::iterator it = m_overviews.begin();
415
11.7M
    for (; it != m_overviews.end(); ++it)
416
10.5M
    {
417
10.5M
        GDALWMSRasterBand *p = *it;
418
10.5M
        if (p->m_scale < scale)
419
0
            break;
420
10.5M
    }
421
1.11M
    m_overviews.insert(it, overview);
422
1.11M
    it = m_overviews.begin();
423
12.8M
    for (int i = 0; it != m_overviews.end(); ++it, ++i)
424
11.7M
    {
425
11.7M
        GDALWMSRasterBand *p = *it;
426
11.7M
        p->m_overview = i;
427
11.7M
    }
428
1.11M
    return true;
429
1.11M
}
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.50k
{
446
2.50k
    GDALWMSImageRequestInfo iri;
447
2.50k
    GDALWMSTiledImageRequestInfo tiri;
448
449
2.50k
    ComputeRequestInfo(iri, tiri, x, y);
450
2.50k
    return m_parent_dataset->m_mini_driver->TiledImageRequest(r, iri, tiri);
451
2.50k
}
452
453
void GDALWMSRasterBand::ComputeRequestInfo(GDALWMSImageRequestInfo &iri,
454
                                           GDALWMSTiledImageRequestInfo &tiri,
455
                                           int x, int y)
456
2.50k
{
457
2.50k
    int x0 = std::max(0, x * nBlockXSize);
458
2.50k
    int y0 = std::max(0, y * nBlockYSize);
459
2.50k
    int x1 = std::max(0, (x + 1) * nBlockXSize);
460
2.50k
    int y1 = std::max(0, (y + 1) * nBlockYSize);
461
2.50k
    if (m_parent_dataset->m_clamp_requests)
462
2.50k
    {
463
2.50k
        x0 = std::min(x0, nRasterXSize);
464
2.50k
        y0 = std::min(y0, nRasterYSize);
465
2.50k
        x1 = std::min(x1, nRasterXSize);
466
2.50k
        y1 = std::min(y1, nRasterYSize);
467
2.50k
    }
468
469
2.50k
    const double rx = (m_parent_dataset->m_data_window.m_x1 -
470
2.50k
                       m_parent_dataset->m_data_window.m_x0) /
471
2.50k
                      static_cast<double>(nRasterXSize);
472
2.50k
    const double ry = (m_parent_dataset->m_data_window.m_y1 -
473
2.50k
                       m_parent_dataset->m_data_window.m_y0) /
474
2.50k
                      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.50k
    iri.m_x0 = x0 * rx + m_parent_dataset->m_data_window.m_x0;
478
2.50k
    iri.m_y0 = y0 * ry + m_parent_dataset->m_data_window.m_y0;
479
2.50k
    iri.m_x1 = m_parent_dataset->m_data_window.m_x1 - (nRasterXSize - x1) * rx;
480
2.50k
    iri.m_y1 = m_parent_dataset->m_data_window.m_y1 - (nRasterYSize - y1) * ry;
481
2.50k
    iri.m_sx = x1 - x0;
482
2.50k
    iri.m_sy = y1 - y0;
483
484
2.50k
    int level = m_overview + 1;
485
2.50k
    tiri.m_x = (m_parent_dataset->m_data_window.m_tx >> level) + x;
486
2.50k
    tiri.m_y = (m_parent_dataset->m_data_window.m_ty >> level) + y;
487
2.50k
    tiri.m_level = m_parent_dataset->m_data_window.m_tlevel - level;
488
2.50k
}
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
171
{
660
171
    static const int bandmap1to1[] = {1};
661
171
    static const int bandmap2to1[] = {1};
662
171
    static const int bandmap3to1[] = {1};
663
171
    static const int bandmap4to1[] = {1};
664
665
171
    static const int bandmap1to2[] = {1, 0};  // 0 == full opaque alpha band
666
171
    static const int bandmap2to2[] = {1, 2};
667
171
    static const int bandmap3to2[] = {1, 0};
668
171
    static const int bandmap4to2[] = {1, 4};
669
670
171
    static const int bandmap1to3[] = {1, 1, 1};
671
171
    static const int bandmap2to3[] = {1, 1, 1};
672
171
    static const int bandmap3to3[] = {1, 2, 3};
673
171
    static const int bandmap4to3[] = {1, 2, 3};
674
675
171
    static const int bandmap1to4[] = {1, 1, 1, 0};
676
171
    static const int bandmap2to4[] = {1, 1, 1, 2};
677
171
    static const int bandmap3to4[] = {1, 2, 3, 0};
678
171
    static const int bandmap4to4[] = {1, 2, 3, 4};
679
680
171
    static const int *const bandmap_selector[4][4] = {
681
171
        {bandmap1to1, bandmap2to1, bandmap3to1, bandmap4to1},
682
171
        {bandmap1to2, bandmap2to2, bandmap3to2, bandmap4to2},
683
171
        {bandmap1to3, bandmap2to3, bandmap3to3, bandmap4to3},
684
171
        {bandmap1to4, bandmap2to4, bandmap3to4, bandmap4to4},
685
171
    };
686
687
171
    if (nSourceBands > 4 || nSourceBands < 1)
688
0
    {
689
0
        return nullptr;
690
0
    }
691
171
    if (nWmsBands > 4 || nWmsBands < 1)
692
0
    {
693
0
        return nullptr;
694
0
    }
695
171
    return bandmap_selector[nWmsBands - 1][nSourceBands - 1];
696
171
}
697
698
CPLErr GDALWMSRasterBand::ReadBlockFromDataset(GDALDataset *ds, int x, int y,
699
                                               int to_buffer_band, void *buffer,
700
                                               int advise_read)
701
171
{
702
171
    CPLErr ret = CE_None;
703
171
    GByte *color_table = nullptr;
704
171
    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
171
    const int expected_sx = std::clamp((x + 1) * nBlockXSize, 0, nRasterXSize) -
711
171
                            std::clamp(x * nBlockXSize, 0, nRasterXSize);
712
171
    const int expected_sy = std::clamp((y + 1) * nBlockYSize, 0, nRasterYSize) -
713
171
                            std::clamp(y * nBlockYSize, 0, nRasterYSize);
714
715
171
    int sx = ds->GetRasterXSize();
716
171
    int sy = ds->GetRasterYSize();
717
    /* Allow bigger than expected so pre-tiled constant size images work on
718
     * corners */
719
171
    if ((sx > nBlockXSize) || (sy > nBlockYSize) || (sx < expected_sx) ||
720
168
        (sy < expected_sy))
721
3
    {
722
3
        CPLError(CE_Failure, CPLE_AppDefined,
723
3
                 "GDALWMS: Incorrect size %d x %d of downloaded block, "
724
3
                 "expected %d x %d, max %d x %d.",
725
3
                 sx, sy, expected_sx, expected_sy, nBlockXSize, nBlockYSize);
726
3
        ret = CE_Failure;
727
3
    }
728
729
171
    int nDSRasterCount = ds->GetRasterCount();
730
171
    if (ret == CE_None)
731
168
    {
732
168
        if (nDSRasterCount != m_parent_dataset->nBands)
733
40
        {
734
            /* Maybe its an image with color table */
735
40
            if ((eDataType == GDT_UInt8) && (ds->GetRasterCount() == 1))
736
0
            {
737
0
                GDALRasterBand *rb = ds->GetRasterBand(1);
738
0
                if (rb->GetRasterDataType() == GDT_UInt8)
739
0
                {
740
0
                    GDALColorTable *ct = rb->GetColorTable();
741
0
                    if (ct != nullptr)
742
0
                    {
743
0
                        if (!advise_read)
744
0
                        {
745
0
                            color_table = new GByte[256 * 4];
746
0
                            const int count =
747
0
                                std::min(256, ct->GetColorEntryCount());
748
0
                            for (i = 0; i < count; ++i)
749
0
                            {
750
0
                                GDALColorEntry ce;
751
0
                                ct->GetColorEntryAsRGB(i, &ce);
752
0
                                color_table[i] = static_cast<GByte>(ce.c1);
753
0
                                color_table[i + 256] =
754
0
                                    static_cast<GByte>(ce.c2);
755
0
                                color_table[i + 512] =
756
0
                                    static_cast<GByte>(ce.c3);
757
0
                                color_table[i + 768] =
758
0
                                    static_cast<GByte>(ce.c4);
759
0
                            }
760
761
0
                            for (i = count; i < 256; ++i)
762
0
                            {
763
0
                                color_table[i] = 0;
764
0
                                color_table[i + 256] = 0;
765
0
                                color_table[i + 512] = 0;
766
0
                                color_table[i + 768] = 0;
767
0
                            }
768
0
                        }
769
0
                    }
770
0
                    else if (m_parent_dataset->nBands <= 4)
771
0
                    {  // Promote single band to fake color table
772
0
                        color_table = new GByte[256 * 4];
773
0
                        for (i = 0; i < 256; i++)
774
0
                        {
775
0
                            color_table[i] = static_cast<GByte>(i);
776
0
                            color_table[i + 256] = static_cast<GByte>(i);
777
0
                            color_table[i + 512] = static_cast<GByte>(i);
778
0
                            color_table[i + 768] = 255;  // Transparency
779
0
                        }
780
0
                        if (m_parent_dataset->nBands == 2)
781
0
                        {  // Luma-Alpha fixup
782
0
                            for (i = 0; i < 256; i++)
783
0
                            {
784
0
                                color_table[i + 256] = 255;
785
0
                            }
786
0
                        }
787
0
                    }
788
0
                }
789
0
            }
790
40
        }
791
168
    }
792
793
171
    if (!advise_read)
794
171
    {
795
171
        const int *const bandmap =
796
171
            GetBandMapForExpand(nDSRasterCount, m_parent_dataset->nBands);
797
855
        for (int ib = 1; ib <= m_parent_dataset->nBands; ++ib)
798
684
        {
799
684
            if (ret == CE_None)
800
672
            {
801
672
                void *p = nullptr;
802
672
                GDALRasterBlock *b = nullptr;
803
672
                if ((buffer != nullptr) && (ib == to_buffer_band))
804
42
                {
805
42
                    p = buffer;
806
42
                }
807
630
                else
808
630
                {
809
630
                    GDALWMSRasterBand *band = static_cast<GDALWMSRasterBand *>(
810
630
                        m_parent_dataset->GetRasterBand(ib));
811
630
                    if (m_overview >= 0)
812
0
                    {
813
0
                        band = static_cast<GDALWMSRasterBand *>(
814
0
                            band->GetOverview(m_overview));
815
0
                    }
816
630
                    if (!band->IsBlockInCache(x, y))
817
630
                    {
818
630
                        b = band->GetLockedBlockRef(x, y, true);
819
630
                        if (b != nullptr)
820
630
                        {
821
630
                            p = b->GetDataRef();
822
630
                            if (p == nullptr)
823
0
                            {
824
0
                                CPLError(CE_Failure, CPLE_AppDefined,
825
0
                                         "GDALWMS: GetDataRef returned NULL.");
826
0
                                ret = CE_Failure;
827
0
                            }
828
630
                        }
829
630
                    }
830
0
                    else
831
0
                    {
832
                        // CPLDebug("WMS", "Band %d, block (x,y)=(%d, %d)
833
                        // already in cache", band->GetBand(), x, y);
834
0
                    }
835
630
                }
836
837
672
                if (p != nullptr)
838
672
                {
839
672
                    int pixel_space = GDALGetDataTypeSizeBytes(eDataType);
840
672
                    int line_space = pixel_space * nBlockXSize;
841
672
                    if (color_table == nullptr)
842
672
                    {
843
672
                        if (bandmap == nullptr || bandmap[ib - 1] != 0)
844
672
                        {
845
672
                            GDALDataType dt = eDataType;
846
672
                            int nSourceBand = ib;
847
672
                            if (bandmap != nullptr)
848
672
                            {
849
672
                                nSourceBand = bandmap[ib - 1];
850
672
                            }
851
                            // Get the data from the PNG as stored instead of
852
                            // converting, if the server asks for that
853
                            // TODO: This hack is from #3493 - not sure it
854
                            // really belongs here.
855
672
                            if ((GDT_Int16 == dt) &&
856
0
                                (GDT_UInt16 ==
857
0
                                 ds->GetRasterBand(ib)->GetRasterDataType()))
858
0
                            {
859
0
                                dt = GDT_UInt16;
860
0
                            }
861
862
672
                            if (ds->RasterIO(GF_Read, 0, 0, sx, sy, p, sx, sy,
863
672
                                             dt, 1, &nSourceBand, pixel_space,
864
672
                                             line_space, 0, nullptr) != CE_None)
865
0
                            {
866
0
                                CPLError(CE_Failure, CPLE_AppDefined,
867
0
                                         "GDALWMS: RasterIO failed on "
868
0
                                         "downloaded block.");
869
0
                                ret = CE_Failure;
870
0
                            }
871
672
                        }
872
0
                        else  // if( bandmap != nullptr && bandmap[ib - 1] == 0
873
                              // )
874
0
                        {  // parent expects 4 bands but file has fewer count so
875
                            // generate a all "opaque" 4th band
876
0
                            GByte *byte_buffer = reinterpret_cast<GByte *>(p);
877
0
                            for (int l_y = 0; l_y < sy; ++l_y)
878
0
                            {
879
0
                                for (int l_x = 0; l_x < sx; ++l_x)
880
0
                                {
881
0
                                    const int offset = l_x + l_y * line_space;
882
0
                                    byte_buffer[offset] =
883
0
                                        255;  // fill with opaque
884
0
                                }
885
0
                            }
886
0
                        }
887
672
                    }
888
0
                    else if (ib <= 4)
889
0
                    {
890
0
                        if (ds->RasterIO(GF_Read, 0, 0, sx, sy, p, sx, sy,
891
0
                                         eDataType, 1, nullptr, pixel_space,
892
0
                                         line_space, 0, nullptr) != CE_None)
893
0
                        {
894
0
                            CPLError(CE_Failure, CPLE_AppDefined,
895
0
                                     "GDALWMS: RasterIO failed on downloaded "
896
0
                                     "block.");
897
0
                            ret = CE_Failure;
898
0
                        }
899
900
0
                        if (ret == CE_None)
901
0
                        {
902
0
                            GByte *band_color_table =
903
0
                                color_table + 256 * (ib - 1);
904
0
                            GByte *byte_buffer = reinterpret_cast<GByte *>(p);
905
0
                            for (int l_y = 0; l_y < sy; ++l_y)
906
0
                            {
907
0
                                for (int l_x = 0; l_x < sx; ++l_x)
908
0
                                {
909
0
                                    const int offset = l_x + l_y * line_space;
910
0
                                    byte_buffer[offset] =
911
0
                                        band_color_table[byte_buffer[offset]];
912
0
                                }
913
0
                            }
914
0
                        }
915
0
                    }
916
0
                    else
917
0
                    {
918
0
                        CPLError(CE_Failure, CPLE_AppDefined,
919
0
                                 "GDALWMS: Color table supports at most 4 "
920
0
                                 "components.");
921
0
                        ret = CE_Failure;
922
0
                    }
923
672
                }
924
672
                if (b != nullptr)
925
630
                {
926
630
                    b->DropLock();
927
630
                }
928
672
            }
929
684
        }
930
171
    }
931
171
    GDALClose(ds);
932
933
171
    if (color_table != nullptr)
934
0
    {
935
0
        delete[] color_table;
936
0
    }
937
938
171
    return ret;
939
171
}
940
941
CPLErr GDALWMSRasterBand::ReadBlockFromFile(const CPLString &soFileName, int x,
942
                                            int y, int to_buffer_band,
943
                                            void *buffer, int advise_read)
944
55
{
945
55
    GDALDataset *ds = GDALDataset::FromHandle(GDALOpenEx(
946
55
        soFileName, GDAL_OF_RASTER | GDAL_OF_READONLY | GDAL_OF_VERBOSE_ERROR,
947
55
        nullptr, m_parent_dataset->m_tileOO, nullptr));
948
55
    if (ds == nullptr)
949
0
    {
950
0
        CPLError(CE_Failure, CPLE_AppDefined,
951
0
                 "GDALWMS: Unable to open downloaded block.");
952
0
        return CE_Failure;
953
0
    }
954
955
55
    return ReadBlockFromDataset(ds, x, y, to_buffer_band, buffer, advise_read);
956
55
}
957
958
CPLErr GDALWMSRasterBand::ReadBlockFromCache(const char *pszKey, int x, int y,
959
                                             int to_buffer_band, void *buffer,
960
                                             int advise_read)
961
209
{
962
209
    GDALWMSCache *cache = m_parent_dataset->m_cache;
963
209
    if (nullptr == cache)
964
0
    {
965
0
        CPLError(CE_Failure, CPLE_AppDefined,
966
0
                 "GDALWMS: Unable to open downloaded block.");
967
0
        return CE_Failure;
968
0
    }
969
209
    GDALDataset *ds = cache->GetDataset(pszKey, m_parent_dataset->m_tileOO);
970
209
    if (ds == nullptr)
971
93
    {
972
93
        CPLError(CE_Failure, CPLE_AppDefined,
973
93
                 "GDALWMS: Unable to open downloaded block.");
974
93
        return CE_Failure;
975
93
    }
976
977
116
    return ReadBlockFromDataset(ds, x, y, to_buffer_band, buffer, advise_read);
978
209
}
979
980
CPLErr GDALWMSRasterBand::EmptyBlock(int x, int y, int to_buffer_band,
981
                                     void *buffer)
982
0
{
983
0
    CPLErr ret = CE_None;
984
985
0
    for (int ib = 1; ib <= m_parent_dataset->nBands; ++ib)
986
0
    {
987
0
        if (ret == CE_None)
988
0
        {
989
0
            void *p = nullptr;
990
0
            GDALRasterBlock *b = nullptr;
991
0
            GDALWMSRasterBand *band = static_cast<GDALWMSRasterBand *>(
992
0
                m_parent_dataset->GetRasterBand(ib));
993
0
            if (m_overview >= 0)
994
0
                band = static_cast<GDALWMSRasterBand *>(
995
0
                    band->GetOverview(m_overview));
996
0
            if ((buffer != nullptr) && (ib == to_buffer_band))
997
0
            {
998
0
                p = buffer;
999
0
            }
1000
0
            else
1001
0
            {
1002
0
                if (!band->IsBlockInCache(x, y))
1003
0
                {
1004
0
                    b = band->GetLockedBlockRef(x, y, true);
1005
0
                    if (b != nullptr)
1006
0
                    {
1007
0
                        p = b->GetDataRef();
1008
0
                        if (p == nullptr)
1009
0
                        {
1010
0
                            CPLError(CE_Failure, CPLE_AppDefined,
1011
0
                                     "GDALWMS: GetDataRef returned NULL.");
1012
0
                            ret = CE_Failure;
1013
0
                        }
1014
0
                    }
1015
0
                }
1016
0
            }
1017
0
            if (p != nullptr)
1018
0
            {
1019
0
                int hasNDV;
1020
0
                double valNDV = band->GetNoDataValue(&hasNDV);
1021
0
                if (!hasNDV)
1022
0
                    valNDV = 0;
1023
0
                GDALCopyWords(&valNDV, GDT_Float64, 0, p, eDataType,
1024
0
                              GDALGetDataTypeSizeBytes(eDataType),
1025
0
                              nBlockXSize * nBlockYSize);
1026
0
            }
1027
0
            if (b != nullptr)
1028
0
            {
1029
0
                b->DropLock();
1030
0
            }
1031
0
        }
1032
0
    }
1033
1034
0
    return ret;
1035
0
}
1036
1037
CPLErr GDALWMSRasterBand::ReportWMSException(const char *file_name)
1038
0
{
1039
0
    CPLErr ret = CE_None;
1040
0
    int reported_errors_count = 0;
1041
1042
0
    CPLXMLNode *orig_root = CPLParseXMLFile(file_name);
1043
0
    CPLXMLNode *root = orig_root;
1044
0
    if (root != nullptr)
1045
0
    {
1046
0
        root = CPLGetXMLNode(root, "=ServiceExceptionReport");
1047
0
    }
1048
0
    if (root != nullptr)
1049
0
    {
1050
0
        CPLXMLNode *n = CPLGetXMLNode(root, "ServiceException");
1051
0
        while (n != nullptr)
1052
0
        {
1053
0
            const char *exception = CPLGetXMLValue(n, "=ServiceException", "");
1054
0
            const char *exception_code =
1055
0
                CPLGetXMLValue(n, "=ServiceException.code", "");
1056
0
            if (exception[0] != '\0')
1057
0
            {
1058
0
                if (exception_code[0] != '\0')
1059
0
                {
1060
0
                    CPLError(
1061
0
                        CE_Failure, CPLE_AppDefined,
1062
0
                        "GDALWMS: The server returned exception code '%s': %s",
1063
0
                        exception_code, exception);
1064
0
                    ++reported_errors_count;
1065
0
                }
1066
0
                else
1067
0
                {
1068
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1069
0
                             "GDALWMS: The server returned exception: %s",
1070
0
                             exception);
1071
0
                    ++reported_errors_count;
1072
0
                }
1073
0
            }
1074
0
            else if (exception_code[0] != '\0')
1075
0
            {
1076
0
                CPLError(CE_Failure, CPLE_AppDefined,
1077
0
                         "GDALWMS: The server returned exception code '%s'.",
1078
0
                         exception_code);
1079
0
                ++reported_errors_count;
1080
0
            }
1081
1082
0
            n = n->psNext;
1083
0
            if (n != nullptr)
1084
0
            {
1085
0
                n = CPLGetXMLNode(n, "=ServiceException");
1086
0
            }
1087
0
        }
1088
0
    }
1089
0
    else
1090
0
    {
1091
0
        ret = CE_Failure;
1092
0
    }
1093
0
    if (orig_root != nullptr)
1094
0
    {
1095
0
        CPLDestroyXMLNode(orig_root);
1096
0
    }
1097
1098
0
    if (reported_errors_count == 0)
1099
0
    {
1100
0
        ret = CE_Failure;
1101
0
    }
1102
1103
0
    return ret;
1104
0
}
1105
1106
CPLErr GDALWMSRasterBand::AdviseRead(int nXOff, int nYOff, int nXSize,
1107
                                     int nYSize, int nBufXSize, int nBufYSize,
1108
                                     GDALDataType eDT,
1109
                                     CSLConstList papszOptions)
1110
0
{
1111
    //    printf("AdviseRead(%d, %d, %d, %d)\n", nXOff, nYOff, nXSize, nYSize);
1112
0
    if (m_parent_dataset->m_offline_mode ||
1113
0
        !m_parent_dataset->m_use_advise_read)
1114
0
        return CE_None;
1115
0
    if (m_parent_dataset->m_cache == nullptr)
1116
0
        return CE_Failure;
1117
1118
    /* ==================================================================== */
1119
    /*      Do we have overviews that would be appropriate to satisfy       */
1120
    /*      this request?                                                   */
1121
    /* ==================================================================== */
1122
0
    if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
1123
0
    {
1124
0
        const int nOverview = GDALBandGetBestOverviewLevel2(
1125
0
            this, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, nullptr);
1126
0
        if (nOverview >= 0)
1127
0
        {
1128
0
            GDALRasterBand *poOverviewBand = GetOverview(nOverview);
1129
0
            if (poOverviewBand == nullptr)
1130
0
                return CE_Failure;
1131
1132
0
            return poOverviewBand->AdviseRead(nXOff, nYOff, nXSize, nYSize,
1133
0
                                              nBufXSize, nBufYSize, eDT,
1134
0
                                              papszOptions);
1135
0
        }
1136
0
    }
1137
1138
0
    int bx0 = nXOff / nBlockXSize;
1139
0
    int by0 = nYOff / nBlockYSize;
1140
0
    int bx1 = (nXOff + nXSize - 1) / nBlockXSize;
1141
0
    int by1 = (nYOff + nYSize - 1) / nBlockYSize;
1142
1143
    // Avoid downloading a insane number of tiles
1144
0
    const int MAX_TILES = 1000;  // arbitrary number
1145
0
    if ((bx1 - bx0 + 1) > MAX_TILES / (by1 - by0 + 1))
1146
0
    {
1147
0
        CPLDebug("WMS", "Too many tiles for AdviseRead()");
1148
0
        return CE_Failure;
1149
0
    }
1150
1151
0
    if (m_nAdviseReadBX0 == bx0 && m_nAdviseReadBY0 == by0 &&
1152
0
        m_nAdviseReadBX1 == bx1 && m_nAdviseReadBY1 == by1)
1153
0
    {
1154
0
        return CE_None;
1155
0
    }
1156
0
    m_nAdviseReadBX0 = bx0;
1157
0
    m_nAdviseReadBY0 = by0;
1158
0
    m_nAdviseReadBX1 = bx1;
1159
0
    m_nAdviseReadBY1 = by1;
1160
1161
0
    return ReadBlocks(0, 0, nullptr, bx0, by0, bx1, by1, 1);
1162
0
}
1163
1164
GDALColorInterp GDALWMSRasterBand::GetColorInterpretation()
1165
0
{
1166
0
    return m_color_interp;
1167
0
}
1168
1169
CPLErr GDALWMSRasterBand::SetColorInterpretation(GDALColorInterp eNewInterp)
1170
0
{
1171
0
    m_color_interp = eNewInterp;
1172
0
    return CE_None;
1173
0
}
1174
1175
// Utility function, returns a value from a vector corresponding to the band
1176
// index or the first entry
1177
static double getBandValue(const std::vector<double> &v, size_t idx)
1178
0
{
1179
0
    idx--;
1180
0
    if (v.size() > idx)
1181
0
        return v[idx];
1182
0
    return v[0];
1183
0
}
1184
1185
double GDALWMSRasterBand::GetNoDataValue(int *pbSuccess)
1186
0
{
1187
0
    std::vector<double> &v = m_parent_dataset->vNoData;
1188
0
    if (v.empty())
1189
0
        return GDALPamRasterBand::GetNoDataValue(pbSuccess);
1190
0
    if (pbSuccess)
1191
0
        *pbSuccess = TRUE;
1192
0
    return getBandValue(v, nBand);
1193
0
}
1194
1195
double GDALWMSRasterBand::GetMinimum(int *pbSuccess)
1196
0
{
1197
0
    std::vector<double> &v = m_parent_dataset->vMin;
1198
0
    if (v.empty())
1199
0
        return GDALPamRasterBand::GetMinimum(pbSuccess);
1200
0
    if (pbSuccess)
1201
0
        *pbSuccess = TRUE;
1202
0
    return getBandValue(v, nBand);
1203
0
}
1204
1205
double GDALWMSRasterBand::GetMaximum(int *pbSuccess)
1206
0
{
1207
0
    std::vector<double> &v = m_parent_dataset->vMax;
1208
0
    if (v.empty())
1209
0
        return GDALPamRasterBand::GetMaximum(pbSuccess);
1210
0
    if (pbSuccess)
1211
0
        *pbSuccess = TRUE;
1212
0
    return getBandValue(v, nBand);
1213
0
}
1214
1215
GDALColorTable *GDALWMSRasterBand::GetColorTable()
1216
0
{
1217
0
    return m_parent_dataset->m_poColorTable;
1218
0
}