Coverage Report

Created: 2026-02-14 09:00

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
13.1k
    : m_mini_driver(nullptr), m_cache(nullptr), m_poColorTable(nullptr),
41
13.1k
      m_data_type(GDT_UInt8), m_block_size_x(0), m_block_size_y(0),
42
13.1k
      m_use_advise_read(0), m_verify_advise_read(0), m_offline_mode(0),
43
13.1k
      m_http_max_conn(0), m_http_timeout(0), m_http_options(nullptr),
44
13.1k
      m_tileOO(nullptr), m_clamp_requests(true), m_unsafeSsl(false),
45
13.1k
      m_zeroblock_on_serverexceptions(0), m_default_block_size_x(1024),
46
13.1k
      m_default_block_size_y(1024), m_default_tile_count_x(1),
47
13.1k
      m_default_tile_count_y(1), m_default_overview_count(-1),
48
13.1k
      m_bNeedsDataWindow(true)
49
13.1k
{
50
13.1k
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
51
13.1k
    m_hint.m_valid = false;
52
13.1k
    m_data_window.m_sx = -1;
53
13.1k
    nBands = 0;
54
13.1k
}
55
56
/************************************************************************/
57
/*                          ~GDALWMSDataset()                           */
58
/************************************************************************/
59
GDALWMSDataset::~GDALWMSDataset()
60
13.1k
{
61
13.1k
    if (m_mini_driver)
62
13.1k
        delete m_mini_driver;
63
13.1k
    if (m_cache)
64
259
        delete m_cache;
65
13.1k
    if (m_poColorTable)
66
0
        delete m_poColorTable;
67
13.1k
    CSLDestroy(m_http_options);
68
13.1k
    CSLDestroy(m_tileOO);
69
13.1k
}
70
71
/************************************************************************/
72
/*                             Initialize()                             */
73
/************************************************************************/
74
CPLErr GDALWMSDataset::Initialize(CPLXMLNode *config, char **l_papszOpenOptions)
75
13.1k
{
76
13.1k
    CPLErr ret = CE_None;
77
78
13.1k
    char *pszXML = CPLSerializeXMLTree(config);
79
13.1k
    if (pszXML)
80
13.1k
    {
81
13.1k
        m_osXML = pszXML;
82
13.1k
        CPLFree(pszXML);
83
13.1k
    }
84
85
    // Generic options that apply to all minidrivers
86
87
    // UserPwd
88
13.1k
    const char *pszUserPwd = CPLGetXMLValue(config, "UserPwd", "");
89
13.1k
    if (pszUserPwd[0] != '\0')
90
0
        m_osUserPwd = pszUserPwd;
91
92
13.1k
    const char *pszUserAgent = CPLGetXMLValue(config, "UserAgent", "");
93
13.1k
    if (pszUserAgent[0] != '\0')
94
0
        m_osUserAgent = pszUserAgent;
95
13.1k
    else
96
13.1k
        m_osUserAgent = CPLGetConfigOption("GDAL_HTTP_USERAGENT", "");
97
98
13.1k
    const char *pszReferer = CPLGetXMLValue(config, "Referer", "");
99
13.1k
    if (pszReferer[0] != '\0')
100
0
        m_osReferer = pszReferer;
101
102
13.1k
    {
103
13.1k
        const char *pszHttpZeroBlockCodes =
104
13.1k
            CPLGetXMLValue(config, "ZeroBlockHttpCodes", "");
105
13.1k
        if (pszHttpZeroBlockCodes[0] == '\0')
106
12.8k
        {
107
12.8k
            m_http_zeroblock_codes.insert(204);
108
12.8k
        }
109
259
        else
110
259
        {
111
259
            char **kv = CSLTokenizeString2(pszHttpZeroBlockCodes, ",",
112
259
                                           CSLT_HONOURSTRINGS);
113
777
            for (int i = 0; i < CSLCount(kv); i++)
114
518
            {
115
518
                int code = atoi(kv[i]);
116
518
                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
518
                m_http_zeroblock_codes.insert(code);
127
518
            }
128
259
            CSLDestroy(kv);
129
259
        }
130
13.1k
    }
131
132
13.1k
    if (ret == CE_None)
133
13.1k
    {
134
13.1k
        const char *pszZeroExceptions =
135
13.1k
            CPLGetXMLValue(config, "ZeroBlockOnServerException", "");
136
13.1k
        if (pszZeroExceptions[0] != '\0')
137
259
        {
138
259
            m_zeroblock_on_serverexceptions = StrToBool(pszZeroExceptions);
139
259
            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
259
        }
148
13.1k
    }
149
150
13.1k
    if (ret == CE_None)
151
13.1k
    {
152
13.1k
        const char *max_conn = CPLGetXMLValue(config, "MaxConnections", "");
153
13.1k
        if (max_conn[0] == '\0')
154
13.1k
        {
155
13.1k
            max_conn = CPLGetConfigOption("GDAL_MAX_CONNECTIONS", "");
156
13.1k
        }
157
13.1k
        if (max_conn[0] != '\0')
158
0
        {
159
0
            m_http_max_conn = atoi(max_conn);
160
0
        }
161
13.1k
        else
162
13.1k
        {
163
13.1k
            m_http_max_conn = 2;
164
13.1k
        }
165
13.1k
    }
166
167
13.1k
    if (ret == CE_None)
168
13.1k
    {
169
13.1k
        const char *timeout = CPLGetXMLValue(config, "Timeout", "");
170
13.1k
        if (timeout[0] != '\0')
171
0
        {
172
0
            m_http_timeout = atoi(timeout);
173
0
        }
174
13.1k
        else
175
13.1k
        {
176
13.1k
            m_http_timeout =
177
13.1k
                atoi(CPLGetConfigOption("GDAL_HTTP_TIMEOUT", "300"));
178
13.1k
        }
179
13.1k
    }
180
181
13.1k
    if (ret == CE_None)
182
13.1k
    {
183
13.1k
        m_osAccept = CPLGetXMLValue(config, "Accept", "");
184
13.1k
    }
185
186
13.1k
    if (ret == CE_None)
187
13.1k
    {
188
13.1k
        const char *offline_mode = CPLGetXMLValue(config, "OfflineMode", "");
189
13.1k
        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
13.1k
        else
205
13.1k
        {
206
13.1k
            m_offline_mode = 0;
207
13.1k
        }
208
13.1k
    }
209
210
13.1k
    if (ret == CE_None)
211
13.1k
    {
212
13.1k
        const char *advise_read = CPLGetXMLValue(config, "AdviseRead", "");
213
13.1k
        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
13.1k
        else
229
13.1k
        {
230
13.1k
            m_use_advise_read = 0;
231
13.1k
        }
232
13.1k
    }
233
234
13.1k
    if (ret == CE_None)
235
13.1k
    {
236
13.1k
        const char *verify_advise_read =
237
13.1k
            CPLGetXMLValue(config, "VerifyAdviseRead", "");
238
13.1k
        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
13.1k
    }
262
263
13.1k
    CPLXMLNode *service_node = CPLGetXMLNode(config, "Service");
264
13.1k
    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
13.1k
    if (ret == CE_None)
271
13.1k
    {
272
13.1k
        const char *pszEnableCache =
273
13.1k
            CPLGetConfigOption("GDAL_ENABLE_WMS_CACHE", "YES");
274
13.1k
        CPLXMLNode *cache_node = CPLGetXMLNode(config, "Cache");
275
13.1k
        if (cache_node != nullptr && CPLTestBool(pszEnableCache))
276
259
        {
277
259
            m_cache = new GDALWMSCache();
278
259
            if (m_cache->Initialize(
279
259
                    CPLGetXMLValue(service_node, "ServerUrl", nullptr),
280
259
                    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
259
            else
289
259
            {
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
259
                SetMetadataItem("CACHE_PATH", m_cache->CachePath(), nullptr);
294
259
            }
295
259
        }
296
13.1k
    }
297
298
13.1k
    if (ret == CE_None)
299
13.1k
    {
300
13.1k
        const int v = StrToBool(CPLGetXMLValue(config, "UnsafeSSL", "false"));
301
13.1k
        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
13.1k
        else
309
13.1k
        {
310
13.1k
            m_unsafeSsl = v;
311
13.1k
        }
312
13.1k
    }
313
314
    // Initialize the minidriver, which can set parameters for the dataset using
315
    // member functions
316
317
13.1k
    const CPLString service_name = CPLGetXMLValue(service_node, "name", "");
318
13.1k
    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
13.1k
    m_mini_driver = NewWMSMiniDriver(service_name);
326
13.1k
    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
13.1k
    m_mini_driver->m_parent_dataset = this;
336
13.1k
    if (m_mini_driver->Initialize(service_node, l_papszOpenOptions) != CE_None)
337
5
    {
338
5
        CPLError(CE_Failure, CPLE_AppDefined,
339
5
                 "GDALWMS: Failed to initialize minidriver.");
340
5
        delete m_mini_driver;
341
5
        m_mini_driver = nullptr;
342
5
        ret = CE_Failure;
343
5
    }
344
13.1k
    else
345
13.1k
    {
346
13.1k
        m_mini_driver->GetCapabilities(&m_mini_driver_caps);
347
13.1k
    }
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
13.1k
    if (m_data_window.m_sx < 1)
362
13.1k
    {
363
13.1k
        int nOverviews = 0;
364
365
13.1k
        if (ret == CE_None)
366
13.1k
        {
367
13.1k
            m_block_size_x = atoi(CPLGetXMLValue(
368
13.1k
                config, "BlockSizeX",
369
13.1k
                CPLString().Printf("%d", m_default_block_size_x)));
370
13.1k
            m_block_size_y = atoi(CPLGetXMLValue(
371
13.1k
                config, "BlockSizeY",
372
13.1k
                CPLString().Printf("%d", m_default_block_size_y)));
373
13.1k
            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
13.1k
        }
380
381
13.1k
        if (ret == CE_None)
382
13.1k
        {
383
13.1k
            m_clamp_requests =
384
13.1k
                StrToBool(CPLGetXMLValue(config, "ClampRequests", "true"));
385
13.1k
            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
13.1k
        }
393
394
13.1k
        if (ret == CE_None)
395
13.1k
        {
396
13.1k
            CPLXMLNode *data_window_node = CPLGetXMLNode(config, "DataWindow");
397
13.1k
            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
13.1k
            else
404
13.1k
            {
405
13.1k
                CPLString osDefaultX0, osDefaultX1, osDefaultY0, osDefaultY1;
406
13.1k
                CPLString osDefaultTileCountX, osDefaultTileCountY,
407
13.1k
                    osDefaultTileLevel;
408
13.1k
                CPLString osDefaultOverviewCount;
409
13.1k
                osDefaultX0.Printf("%.8f", m_default_data_window.m_x0);
410
13.1k
                osDefaultX1.Printf("%.8f", m_default_data_window.m_x1);
411
13.1k
                osDefaultY0.Printf("%.8f", m_default_data_window.m_y0);
412
13.1k
                osDefaultY1.Printf("%.8f", m_default_data_window.m_y1);
413
13.1k
                osDefaultTileCountX.Printf("%d", m_default_tile_count_x);
414
13.1k
                osDefaultTileCountY.Printf("%d", m_default_tile_count_y);
415
13.1k
                if (m_default_data_window.m_tlevel >= 0)
416
0
                    osDefaultTileLevel.Printf("%d",
417
0
                                              m_default_data_window.m_tlevel);
418
13.1k
                if (m_default_overview_count >= 0)
419
0
                    osDefaultOverviewCount.Printf("%d",
420
0
                                                  m_default_overview_count);
421
13.1k
                const char *overview_count = CPLGetXMLValue(
422
13.1k
                    config, "OverviewCount", osDefaultOverviewCount);
423
13.1k
                const char *ulx =
424
13.1k
                    CPLGetXMLValue(data_window_node, "UpperLeftX", osDefaultX0);
425
13.1k
                const char *uly =
426
13.1k
                    CPLGetXMLValue(data_window_node, "UpperLeftY", osDefaultY0);
427
13.1k
                const char *lrx = CPLGetXMLValue(data_window_node,
428
13.1k
                                                 "LowerRightX", osDefaultX1);
429
13.1k
                const char *lry = CPLGetXMLValue(data_window_node,
430
13.1k
                                                 "LowerRightY", osDefaultY1);
431
13.1k
                const char *sx = CPLGetXMLValue(data_window_node, "SizeX", "");
432
13.1k
                const char *sy = CPLGetXMLValue(data_window_node, "SizeY", "");
433
13.1k
                const char *tx = CPLGetXMLValue(data_window_node, "TileX", "0");
434
13.1k
                const char *ty = CPLGetXMLValue(data_window_node, "TileY", "0");
435
13.1k
                const char *tlevel = CPLGetXMLValue(
436
13.1k
                    data_window_node, "TileLevel", osDefaultTileLevel);
437
13.1k
                const char *str_tile_count_x = CPLGetXMLValue(
438
13.1k
                    data_window_node, "TileCountX", osDefaultTileCountX);
439
13.1k
                const char *str_tile_count_y = CPLGetXMLValue(
440
13.1k
                    data_window_node, "TileCountY", osDefaultTileCountY);
441
13.1k
                const char *y_origin =
442
13.1k
                    CPLGetXMLValue(data_window_node, "YOrigin", "default");
443
444
13.1k
                if ((ulx[0] != '\0') && (uly[0] != '\0') && (lrx[0] != '\0') &&
445
13.1k
                    (lry[0] != '\0'))
446
13.1k
                {
447
13.1k
                    m_data_window.m_x0 = CPLAtof(ulx);
448
13.1k
                    m_data_window.m_y0 = CPLAtof(uly);
449
13.1k
                    m_data_window.m_x1 = CPLAtof(lrx);
450
13.1k
                    m_data_window.m_y1 = CPLAtof(lry);
451
13.1k
                }
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
13.1k
                m_data_window.m_tlevel = atoi(tlevel);
462
                // Limit to 30 to avoid 1 << m_tlevel overflow
463
13.1k
                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
13.1k
                if (ret == CE_None)
471
13.1k
                {
472
13.1k
                    if ((sx[0] != '\0') && (sy[0] != '\0'))
473
13.1k
                    {
474
13.1k
                        m_data_window.m_sx = atoi(sx);
475
13.1k
                        m_data_window.m_sy = atoi(sy);
476
13.1k
                    }
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
13.1k
                }
529
13.1k
                if (ret == CE_None)
530
13.1k
                {
531
13.1k
                    if ((tx[0] != '\0') && (ty[0] != '\0'))
532
13.1k
                    {
533
13.1k
                        m_data_window.m_tx = atoi(tx);
534
13.1k
                        m_data_window.m_ty = atoi(ty);
535
13.1k
                    }
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
13.1k
                }
544
545
13.1k
                if (ret == CE_None)
546
13.1k
                {
547
13.1k
                    if (overview_count[0] != '\0')
548
12.8k
                    {
549
12.8k
                        nOverviews = atoi(overview_count);
550
12.8k
                    }
551
259
                    else if (tlevel[0] != '\0')
552
259
                    {
553
259
                        nOverviews = m_data_window.m_tlevel;
554
259
                    }
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
13.1k
                }
569
13.1k
                if (ret == CE_None)
570
13.1k
                {
571
13.1k
                    CPLString y_origin_str = y_origin;
572
13.1k
                    if (y_origin_str == "top")
573
259
                    {
574
259
                        m_data_window.m_y_origin = GDALWMSDataWindow::TOP;
575
259
                    }
576
12.8k
                    else if (y_origin_str == "bottom")
577
0
                    {
578
0
                        m_data_window.m_y_origin = GDALWMSDataWindow::BOTTOM;
579
0
                    }
580
12.8k
                    else if (y_origin_str == "default")
581
12.8k
                    {
582
12.8k
                        m_data_window.m_y_origin = GDALWMSDataWindow::DEFAULT;
583
12.8k
                    }
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
13.1k
                }
594
13.1k
            }
595
13.1k
        }
596
597
13.1k
        if (ret == CE_None)
598
13.1k
        {
599
13.1k
            if (nBands < 1)
600
13.1k
                nBands = atoi(CPLGetXMLValue(config, "BandsCount", "3"));
601
13.1k
            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
13.1k
        }
608
609
13.1k
        if (ret == CE_None)
610
13.1k
        {
611
13.1k
            const char *data_type = CPLGetXMLValue(config, "DataType", "Byte");
612
13.1k
            if (!STARTS_WITH(data_type, "Byte"))
613
0
                SetTileOO("@DATATYPE", data_type);
614
13.1k
            m_data_type = GDALGetDataTypeByName(data_type);
615
13.1k
            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
13.1k
        }
624
625
        // Initialize the bands and the overviews.  Assumes overviews are powers
626
        // of two
627
13.1k
        if (ret == CE_None)
628
13.1k
        {
629
13.1k
            nRasterXSize = m_data_window.m_sx;
630
13.1k
            nRasterYSize = m_data_window.m_sy;
631
632
13.1k
            if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize) ||
633
13.1k
                !GDALCheckBandCount(nBands, TRUE))
634
8
            {
635
8
                return CE_Failure;
636
8
            }
637
638
13.1k
            GDALColorInterp default_color_interp[4][4] = {
639
13.1k
                {GCI_GrayIndex, GCI_Undefined, GCI_Undefined, GCI_Undefined},
640
13.1k
                {GCI_GrayIndex, GCI_AlphaBand, GCI_Undefined, GCI_Undefined},
641
13.1k
                {GCI_RedBand, GCI_GreenBand, GCI_BlueBand, GCI_Undefined},
642
13.1k
                {GCI_RedBand, GCI_GreenBand, GCI_BlueBand, GCI_AlphaBand}};
643
52.7k
            for (int i = 0; i < nBands; ++i)
644
39.5k
            {
645
39.5k
                GDALColorInterp color_interp =
646
39.5k
                    (nBands <= 4 && i <= 3 ? default_color_interp[nBands - 1][i]
647
39.5k
                                           : GCI_Undefined);
648
39.5k
                GDALWMSRasterBand *band = new GDALWMSRasterBand(this, i, 1.0);
649
39.5k
                band->m_color_interp = color_interp;
650
39.5k
                SetBand(i + 1, band);
651
39.5k
                double scale = 0.5;
652
811k
                for (int j = 0; j < nOverviews; ++j)
653
771k
                {
654
771k
                    if (!band->AddOverview(scale))
655
9
                        break;
656
771k
                    band->m_color_interp = color_interp;
657
771k
                    scale *= 0.5;
658
771k
                }
659
39.5k
            }
660
13.1k
        }
661
13.1k
    }
662
663
    // Let the local configuration override the minidriver supplied projection
664
13.1k
    if (ret == CE_None)
665
13.1k
    {
666
13.1k
        const char *proj = CPLGetXMLValue(config, "Projection", "");
667
13.1k
        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
13.1k
    }
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
13.1k
    if (ret == CE_None)
682
13.1k
    {
683
        // Data values are attributes, they include NoData Min and Max
684
13.1k
        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
13.1k
    }
701
702
13.1k
    if (ret == CE_None)
703
13.1k
    {
704
13.1k
        if (m_oSRS.IsEmpty())
705
13.1k
        {
706
13.1k
            const auto &oSRS = m_mini_driver->GetSpatialRef();
707
13.1k
            if (!oSRS.IsEmpty())
708
12.7k
            {
709
12.7k
                m_oSRS = oSRS;
710
12.7k
            }
711
13.1k
        }
712
13.1k
    }
713
714
    // Finish the minidriver initialization
715
13.1k
    if (ret == CE_None)
716
13.1k
        m_mini_driver->EndInit();
717
718
13.1k
    return ret;
719
13.1k
}
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.34k
{
762
2.34k
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
763
2.34k
}
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.34k
{
778
2.34k
    if (!(m_mini_driver_caps.m_has_geotransform))
779
0
    {
780
0
        gt = GDALGeoTransform();
781
0
        return CE_Failure;
782
0
    }
783
2.34k
    gt.xorig = m_data_window.m_x0;
784
2.34k
    gt.xscale = (m_data_window.m_x1 - m_data_window.m_x0) /
785
2.34k
                static_cast<double>(m_data_window.m_sx);
786
2.34k
    gt.xrot = 0.0;
787
2.34k
    gt.yorig = m_data_window.m_y0;
788
2.34k
    gt.yrot = 0.0;
789
2.34k
    gt.yscale = (m_data_window.m_y1 - m_data_window.m_y0) /
790
2.34k
                static_cast<double>(m_data_window.m_sy);
791
2.34k
    return CE_None;
792
2.34k
}
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,
809
                                  CSLConstList options)
810
0
{
811
    //    printf("AdviseRead(%d, %d, %d, %d)\n", x0, y0, sx, sy);
812
0
    if (m_offline_mode || !m_use_advise_read)
813
0
        return CE_None;
814
0
    if (m_cache == nullptr)
815
0
        return CE_Failure;
816
817
0
    GDALRasterBand *band = GetRasterBand(1);
818
0
    if (band == nullptr)
819
0
        return CE_Failure;
820
0
    return band->AdviseRead(x0, y0, sx, sy, bsx, bsy, bdt, options);
821
0
}
822
823
/************************************************************************/
824
/*                       GetMetadataDomainList()                        */
825
/************************************************************************/
826
827
char **GDALWMSDataset::GetMetadataDomainList()
828
0
{
829
0
    return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
830
0
                                   TRUE, "WMS", nullptr);
831
0
}
832
833
/************************************************************************/
834
/*                          GetMetadataItem()                           */
835
/************************************************************************/
836
const char *GDALWMSDataset::GetMetadataItem(const char *pszName,
837
                                            const char *pszDomain)
838
0
{
839
0
    if (pszName != nullptr && EQUAL(pszName, "XML") && pszDomain != nullptr &&
840
0
        EQUAL(pszDomain, "WMS"))
841
0
    {
842
0
        return (m_osXML.size()) ? m_osXML.c_str() : nullptr;
843
0
    }
844
845
0
    return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
846
0
}
847
848
// Builds a CSL of options or returns the previous one
849
const char *const *GDALWMSDataset::GetHTTPRequestOpts()
850
1.17k
{
851
1.17k
    if (m_http_options != nullptr)
852
186
        return m_http_options;
853
854
989
    char **opts = nullptr;
855
989
    if (m_http_timeout != -1)
856
989
        opts = CSLAddString(opts, CPLOPrintf("TIMEOUT=%d", m_http_timeout));
857
858
989
    if (!m_osUserAgent.empty())
859
0
        opts = CSLAddNameValue(opts, "USERAGENT", m_osUserAgent);
860
989
    else
861
989
        opts = CSLAddString(
862
989
            opts, "USERAGENT=GDAL WMS driver (https://gdal.org/frmt_wms.html)");
863
864
989
    if (!m_osReferer.empty())
865
0
        opts = CSLAddNameValue(opts, "REFERER", m_osReferer);
866
867
989
    if (m_unsafeSsl >= 1)
868
33
        opts = CSLAddString(opts, "UNSAFESSL=1");
869
870
989
    if (!m_osUserPwd.empty())
871
0
        opts = CSLAddNameValue(opts, "USERPWD", m_osUserPwd);
872
873
989
    if (m_http_max_conn > 0)
874
989
        opts = CSLAddString(opts, CPLOPrintf("MAXCONN=%d", m_http_max_conn));
875
876
989
    if (!m_osAccept.empty())
877
0
        opts = CSLAddNameValue(opts, "ACCEPT", m_osAccept.c_str());
878
879
989
    m_http_options = opts;
880
989
    return m_http_options;
881
1.17k
}
882
883
void GDALWMSDataset::SetTileOO(const char *pszName, const char *pszValue)
884
0
{
885
0
    if (pszName == nullptr || strlen(pszName) == 0)
886
0
        return;
887
0
    int oldidx = CSLFindName(m_tileOO, pszName);
888
0
    if (oldidx >= 0)
889
0
        m_tileOO = CSLRemoveStrings(m_tileOO, oldidx, 1, nullptr);
890
0
    if (pszValue != nullptr && strlen(pszValue))
891
0
        m_tileOO = CSLAddNameValue(m_tileOO, pszName, pszValue);
892
0
}