Coverage Report

Created: 2025-12-03 08:24

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