• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.83
/frmts/wms/gdalwmsdataset.cpp
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 <algorithm>
32

33
/************************************************************************/
34
/*                           GDALWMSDataset()                           */
35
/************************************************************************/
36
GDALWMSDataset::GDALWMSDataset()
351✔
37
    : m_mini_driver(nullptr), m_cache(nullptr), m_poColorTable(nullptr),
38
      m_data_type(GDT_Byte), m_block_size_x(0), m_block_size_y(0),
39
      m_use_advise_read(0), m_verify_advise_read(0), m_offline_mode(0),
40
      m_http_max_conn(0), m_http_timeout(0), m_http_options(nullptr),
41
      m_tileOO(nullptr), m_clamp_requests(true), m_unsafeSsl(false),
42
      m_zeroblock_on_serverexceptions(0), m_default_block_size_x(1024),
43
      m_default_block_size_y(1024), m_default_tile_count_x(1),
44
      m_default_tile_count_y(1), m_default_overview_count(-1),
45
      m_bNeedsDataWindow(true)
351✔
46
{
47
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
351✔
48
    m_hint.m_valid = false;
351✔
49
    m_data_window.m_sx = -1;
351✔
50
    nBands = 0;
351✔
51
}
351✔
52

53
/************************************************************************/
54
/*                          ~GDALWMSDataset()                           */
55
/************************************************************************/
56
GDALWMSDataset::~GDALWMSDataset()
702✔
57
{
58
    if (m_mini_driver)
351✔
59
        delete m_mini_driver;
349✔
60
    if (m_cache)
351✔
61
        delete m_cache;
217✔
62
    if (m_poColorTable)
351✔
63
        delete m_poColorTable;
×
64
    CSLDestroy(m_http_options);
351✔
65
    CSLDestroy(m_tileOO);
351✔
66
}
702✔
67

68
/************************************************************************/
69
/*                             Initialize()                             */
70
/************************************************************************/
71
CPLErr GDALWMSDataset::Initialize(CPLXMLNode *config, char **l_papszOpenOptions)
351✔
72
{
73
    CPLErr ret = CE_None;
351✔
74

75
    char *pszXML = CPLSerializeXMLTree(config);
351✔
76
    if (pszXML)
351✔
77
    {
78
        m_osXML = pszXML;
351✔
79
        CPLFree(pszXML);
351✔
80
    }
81

82
    // Generic options that apply to all minidrivers
83

84
    // UserPwd
85
    const char *pszUserPwd = CPLGetXMLValue(config, "UserPwd", "");
351✔
86
    if (pszUserPwd[0] != '\0')
351✔
87
        m_osUserPwd = pszUserPwd;
×
88

89
    const char *pszUserAgent = CPLGetXMLValue(config, "UserAgent", "");
351✔
90
    if (pszUserAgent[0] != '\0')
351✔
91
        m_osUserAgent = pszUserAgent;
2✔
92
    else
93
        m_osUserAgent = CPLGetConfigOption("GDAL_HTTP_USERAGENT", "");
349✔
94

95
    const char *pszReferer = CPLGetXMLValue(config, "Referer", "");
351✔
96
    if (pszReferer[0] != '\0')
351✔
97
        m_osReferer = pszReferer;
×
98

99
    {
100
        const char *pszHttpZeroBlockCodes =
101
            CPLGetXMLValue(config, "ZeroBlockHttpCodes", "");
351✔
102
        if (pszHttpZeroBlockCodes[0] == '\0')
351✔
103
        {
104
            m_http_zeroblock_codes.insert(204);
261✔
105
        }
106
        else
107
        {
108
            char **kv = CSLTokenizeString2(pszHttpZeroBlockCodes, ",",
90✔
109
                                           CSLT_HONOURSTRINGS);
110
            for (int i = 0; i < CSLCount(kv); i++)
270✔
111
            {
112
                int code = atoi(kv[i]);
180✔
113
                if (code <= 0)
180✔
114
                {
115
                    CPLError(
×
116
                        CE_Failure, CPLE_AppDefined,
117
                        "GDALWMS: Invalid value of ZeroBlockHttpCodes "
118
                        "\"%s\", comma separated HTTP response codes expected.",
119
                        kv[i]);
×
120
                    ret = CE_Failure;
×
121
                    break;
×
122
                }
123
                m_http_zeroblock_codes.insert(code);
180✔
124
            }
125
            CSLDestroy(kv);
90✔
126
        }
127
    }
128

129
    if (ret == CE_None)
351✔
130
    {
131
        const char *pszZeroExceptions =
132
            CPLGetXMLValue(config, "ZeroBlockOnServerException", "");
351✔
133
        if (pszZeroExceptions[0] != '\0')
351✔
134
        {
135
            m_zeroblock_on_serverexceptions = StrToBool(pszZeroExceptions);
90✔
136
            if (m_zeroblock_on_serverexceptions == -1)
90✔
137
            {
138
                CPLError(CE_Failure, CPLE_AppDefined,
×
139
                         "GDALWMS: Invalid value of ZeroBlockOnServerException "
140
                         "\"%s\", true/false expected.",
141
                         pszZeroExceptions);
142
                ret = CE_Failure;
×
143
            }
144
        }
145
    }
146

147
    if (ret == CE_None)
351✔
148
    {
149
        const char *max_conn = CPLGetXMLValue(config, "MaxConnections", "");
351✔
150
        if (max_conn[0] == '\0')
351✔
151
        {
152
            max_conn = CPLGetConfigOption("GDAL_MAX_CONNECTIONS", "");
265✔
153
        }
154
        if (max_conn[0] != '\0')
351✔
155
        {
156
            m_http_max_conn = atoi(max_conn);
86✔
157
        }
158
        else
159
        {
160
            m_http_max_conn = 2;
265✔
161
        }
162
    }
163

164
    if (ret == CE_None)
351✔
165
    {
166
        const char *timeout = CPLGetXMLValue(config, "Timeout", "");
351✔
167
        if (timeout[0] != '\0')
351✔
168
        {
169
            m_http_timeout = atoi(timeout);
×
170
        }
171
        else
172
        {
173
            m_http_timeout =
351✔
174
                atoi(CPLGetConfigOption("GDAL_HTTP_TIMEOUT", "300"));
351✔
175
        }
176
    }
177

178
    if (ret == CE_None)
351✔
179
    {
180
        m_osAccept = CPLGetXMLValue(config, "Accept", "");
351✔
181
    }
182

183
    if (ret == CE_None)
351✔
184
    {
185
        const char *offline_mode = CPLGetXMLValue(config, "OfflineMode", "");
351✔
186
        if (offline_mode[0] != '\0')
351✔
187
        {
188
            const int offline_mode_bool = StrToBool(offline_mode);
×
189
            if (offline_mode_bool == -1)
×
190
            {
191
                CPLError(CE_Failure, CPLE_AppDefined,
×
192
                         "GDALWMS: Invalid value of OfflineMode, true / false "
193
                         "expected.");
194
                ret = CE_Failure;
×
195
            }
196
            else
197
            {
198
                m_offline_mode = offline_mode_bool;
×
199
            }
200
        }
201
        else
202
        {
203
            m_offline_mode = 0;
351✔
204
        }
205
    }
206

207
    if (ret == CE_None)
351✔
208
    {
209
        const char *advise_read = CPLGetXMLValue(config, "AdviseRead", "");
351✔
210
        if (advise_read[0] != '\0')
351✔
211
        {
212
            const int advise_read_bool = StrToBool(advise_read);
×
213
            if (advise_read_bool == -1)
×
214
            {
215
                CPLError(CE_Failure, CPLE_AppDefined,
×
216
                         "GDALWMS: Invalid value of AdviseRead, true / false "
217
                         "expected.");
218
                ret = CE_Failure;
×
219
            }
220
            else
221
            {
222
                m_use_advise_read = advise_read_bool;
×
223
            }
224
        }
225
        else
226
        {
227
            m_use_advise_read = 0;
351✔
228
        }
229
    }
230

231
    if (ret == CE_None)
351✔
232
    {
233
        const char *verify_advise_read =
234
            CPLGetXMLValue(config, "VerifyAdviseRead", "");
351✔
235
        if (m_use_advise_read)
351✔
236
        {
237
            if (verify_advise_read[0] != '\0')
×
238
            {
239
                const int verify_advise_read_bool =
240
                    StrToBool(verify_advise_read);
×
241
                if (verify_advise_read_bool == -1)
×
242
                {
243
                    CPLError(CE_Failure, CPLE_AppDefined,
×
244
                             "GDALWMS: Invalid value of VerifyAdviseRead, true "
245
                             "/ false expected.");
246
                    ret = CE_Failure;
×
247
                }
248
                else
249
                {
250
                    m_verify_advise_read = verify_advise_read_bool;
×
251
                }
252
            }
253
            else
254
            {
255
                m_verify_advise_read = 1;
×
256
            }
257
        }
258
    }
259

260
    CPLXMLNode *service_node = CPLGetXMLNode(config, "Service");
351✔
261
    if (service_node == nullptr)
351✔
262
    {
263
        CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: No Service specified.");
×
264
        return CE_Failure;
×
265
    }
266

267
    if (ret == CE_None)
351✔
268
    {
269
        const char *pszEnableCache =
270
            CPLGetConfigOption("GDAL_ENABLE_WMS_CACHE", "YES");
351✔
271
        CPLXMLNode *cache_node = CPLGetXMLNode(config, "Cache");
351✔
272
        if (cache_node != nullptr && CPLTestBool(pszEnableCache))
351✔
273
        {
274
            m_cache = new GDALWMSCache();
217✔
275
            if (m_cache->Initialize(
217✔
276
                    CPLGetXMLValue(service_node, "ServerUrl", nullptr),
277
                    cache_node) != CE_None)
217✔
278
            {
279
                delete m_cache;
×
280
                m_cache = nullptr;
×
281
                CPLError(CE_Failure, CPLE_AppDefined,
×
282
                         "GDALWMS: Failed to initialize cache.");
283
                ret = CE_Failure;
×
284
            }
285
            else
286
            {
287
                // NOTE: Save cache path to metadata. For example, this is
288
                // useful for deleting a cache folder when removing dataset or
289
                // to fill the cache for specified area and zoom levels
290
                SetMetadataItem("CACHE_PATH", m_cache->CachePath(), nullptr);
217✔
291
            }
292
        }
293
    }
294

295
    if (ret == CE_None)
351✔
296
    {
297
        const int v = StrToBool(CPLGetXMLValue(config, "UnsafeSSL", "false"));
351✔
298
        if (v == -1)
351✔
299
        {
300
            CPLError(
×
301
                CE_Failure, CPLE_AppDefined,
302
                "GDALWMS: Invalid value of UnsafeSSL: true or false expected.");
303
            ret = CE_Failure;
×
304
        }
305
        else
306
        {
307
            m_unsafeSsl = v;
351✔
308
        }
309
    }
310

311
    // Initialize the minidriver, which can set parameters for the dataset using
312
    // member functions
313

314
    const CPLString service_name = CPLGetXMLValue(service_node, "name", "");
702✔
315
    if (service_name.empty())
351✔
316
    {
317
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
318
                 "GDALWMS: No Service name specified.");
319
        return CE_Failure;
1✔
320
    }
321

322
    m_mini_driver = NewWMSMiniDriver(service_name);
350✔
323
    if (m_mini_driver == nullptr)
350✔
324
    {
325
        CPLError(CE_Failure, CPLE_AppDefined,
×
326
                 "GDALWMS: No mini-driver registered for '%s'.",
327
                 service_name.c_str());
328
        return CE_Failure;
×
329
    }
330

331
    // Set up minidriver
332
    m_mini_driver->m_parent_dataset = this;
350✔
333
    if (m_mini_driver->Initialize(service_node, l_papszOpenOptions) != CE_None)
350✔
334
    {
335
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
336
                 "GDALWMS: Failed to initialize minidriver.");
337
        delete m_mini_driver;
1✔
338
        m_mini_driver = nullptr;
1✔
339
        ret = CE_Failure;
1✔
340
    }
341
    else
342
    {
343
        m_mini_driver->GetCapabilities(&m_mini_driver_caps);
349✔
344
    }
345

346
    /*
347
      Parameters that could be set by minidriver already
348
      If the size is set, minidriver has done this already
349
      A "server" side minidriver needs to set at least:
350
      - Blocksize (x and y)
351
      - Clamp flag (defaults to true)
352
      - DataWindow
353
      - Band Count
354
      - Data Type
355
      It should also initialize and register the bands and overviews.
356
    */
357

358
    if (m_data_window.m_sx < 1)
350✔
359
    {
360
        int nOverviews = 0;
347✔
361

362
        if (ret == CE_None)
347✔
363
        {
364
            m_block_size_x = atoi(CPLGetXMLValue(
346✔
365
                config, "BlockSizeX",
366
                CPLString().Printf("%d", m_default_block_size_x)));
692✔
367
            m_block_size_y = atoi(CPLGetXMLValue(
346✔
368
                config, "BlockSizeY",
369
                CPLString().Printf("%d", m_default_block_size_y)));
692✔
370
            if (m_block_size_x <= 0 || m_block_size_y <= 0)
346✔
371
            {
372
                CPLError(CE_Failure, CPLE_AppDefined,
×
373
                         "GDALWMS: Invalid value in BlockSizeX or BlockSizeY");
374
                ret = CE_Failure;
×
375
            }
376
        }
377

378
        if (ret == CE_None)
347✔
379
        {
380
            m_clamp_requests =
346✔
381
                StrToBool(CPLGetXMLValue(config, "ClampRequests", "true"));
346✔
382
            if (m_clamp_requests < 0)
346✔
383
            {
384
                CPLError(CE_Failure, CPLE_AppDefined,
×
385
                         "GDALWMS: Invalid value of ClampRequests, true/false "
386
                         "expected.");
387
                ret = CE_Failure;
×
388
            }
389
        }
390

391
        if (ret == CE_None)
347✔
392
        {
393
            CPLXMLNode *data_window_node = CPLGetXMLNode(config, "DataWindow");
346✔
394
            if (data_window_node == nullptr && m_bNeedsDataWindow)
346✔
395
            {
396
                CPLError(CE_Failure, CPLE_AppDefined,
×
397
                         "GDALWMS: DataWindow missing.");
398
                ret = CE_Failure;
×
399
            }
400
            else
401
            {
402
                CPLString osDefaultX0, osDefaultX1, osDefaultY0, osDefaultY1;
346✔
403
                CPLString osDefaultTileCountX, osDefaultTileCountY,
346✔
404
                    osDefaultTileLevel;
346✔
405
                CPLString osDefaultOverviewCount;
346✔
406
                osDefaultX0.Printf("%.8f", m_default_data_window.m_x0);
346✔
407
                osDefaultX1.Printf("%.8f", m_default_data_window.m_x1);
346✔
408
                osDefaultY0.Printf("%.8f", m_default_data_window.m_y0);
346✔
409
                osDefaultY1.Printf("%.8f", m_default_data_window.m_y1);
346✔
410
                osDefaultTileCountX.Printf("%d", m_default_tile_count_x);
346✔
411
                osDefaultTileCountY.Printf("%d", m_default_tile_count_y);
346✔
412
                if (m_default_data_window.m_tlevel >= 0)
346✔
413
                    osDefaultTileLevel.Printf("%d",
414
                                              m_default_data_window.m_tlevel);
1✔
415
                if (m_default_overview_count >= 0)
346✔
416
                    osDefaultOverviewCount.Printf("%d",
417
                                                  m_default_overview_count);
1✔
418
                const char *overview_count = CPLGetXMLValue(
346✔
419
                    config, "OverviewCount", osDefaultOverviewCount);
420
                const char *ulx =
421
                    CPLGetXMLValue(data_window_node, "UpperLeftX", osDefaultX0);
346✔
422
                const char *uly =
423
                    CPLGetXMLValue(data_window_node, "UpperLeftY", osDefaultY0);
346✔
424
                const char *lrx = CPLGetXMLValue(data_window_node,
346✔
425
                                                 "LowerRightX", osDefaultX1);
426
                const char *lry = CPLGetXMLValue(data_window_node,
346✔
427
                                                 "LowerRightY", osDefaultY1);
428
                const char *sx = CPLGetXMLValue(data_window_node, "SizeX", "");
346✔
429
                const char *sy = CPLGetXMLValue(data_window_node, "SizeY", "");
346✔
430
                const char *tx = CPLGetXMLValue(data_window_node, "TileX", "0");
346✔
431
                const char *ty = CPLGetXMLValue(data_window_node, "TileY", "0");
346✔
432
                const char *tlevel = CPLGetXMLValue(
346✔
433
                    data_window_node, "TileLevel", osDefaultTileLevel);
434
                const char *str_tile_count_x = CPLGetXMLValue(
346✔
435
                    data_window_node, "TileCountX", osDefaultTileCountX);
436
                const char *str_tile_count_y = CPLGetXMLValue(
346✔
437
                    data_window_node, "TileCountY", osDefaultTileCountY);
438
                const char *y_origin =
439
                    CPLGetXMLValue(data_window_node, "YOrigin", "default");
346✔
440

441
                if ((ulx[0] != '\0') && (uly[0] != '\0') && (lrx[0] != '\0') &&
346✔
442
                    (lry[0] != '\0'))
346✔
443
                {
444
                    m_data_window.m_x0 = CPLAtof(ulx);
346✔
445
                    m_data_window.m_y0 = CPLAtof(uly);
346✔
446
                    m_data_window.m_x1 = CPLAtof(lrx);
346✔
447
                    m_data_window.m_y1 = CPLAtof(lry);
346✔
448
                }
449
                else
450
                {
451
                    CPLError(
×
452
                        CE_Failure, CPLE_AppDefined,
453
                        "GDALWMS: Mandatory elements of DataWindow missing: "
454
                        "UpperLeftX, UpperLeftY, LowerRightX, LowerRightY.");
455
                    ret = CE_Failure;
×
456
                }
457

458
                m_data_window.m_tlevel = atoi(tlevel);
346✔
459
                // Limit to 30 to avoid 1 << m_tlevel overflow
460
                if (m_data_window.m_tlevel < 0 || m_data_window.m_tlevel > 30)
346✔
461
                {
462
                    CPLError(CE_Failure, CPLE_AppDefined,
×
463
                             "Invalid value for TileLevel");
464
                    return CE_Failure;
×
465
                }
466

467
                if (ret == CE_None)
346✔
468
                {
469
                    if ((sx[0] != '\0') && (sy[0] != '\0'))
346✔
470
                    {
471
                        m_data_window.m_sx = atoi(sx);
329✔
472
                        m_data_window.m_sy = atoi(sy);
329✔
473
                    }
474
                    else if ((tlevel[0] != '\0') &&
17✔
475
                             (str_tile_count_x[0] != '\0') &&
17✔
476
                             (str_tile_count_y[0] != '\0'))
17✔
477
                    {
478
                        const int tile_count_x = atoi(str_tile_count_x);
17✔
479
                        if (tile_count_x <= 0)
17✔
480
                        {
481
                            CPLError(CE_Failure, CPLE_AppDefined,
×
482
                                     "Invalid value for TileCountX");
483
                            return CE_Failure;
×
484
                        }
485
                        if (tile_count_x > INT_MAX / m_block_size_x ||
17✔
486
                            tile_count_x * m_block_size_x >
17✔
487
                                INT_MAX / (1 << m_data_window.m_tlevel))
17✔
488
                        {
489
                            CPLError(CE_Failure, CPLE_AppDefined,
×
490
                                     "Integer overflow in tile_count_x * "
491
                                     "m_block_size_x * (1 << "
492
                                     "m_data_window.m_tlevel)");
493
                            return CE_Failure;
×
494
                        }
495
                        m_data_window.m_sx = tile_count_x * m_block_size_x *
17✔
496
                                             (1 << m_data_window.m_tlevel);
17✔
497

498
                        const int tile_count_y = atoi(str_tile_count_y);
17✔
499
                        if (tile_count_y <= 0)
17✔
500
                        {
501
                            CPLError(CE_Failure, CPLE_AppDefined,
×
502
                                     "Invalid value for TileCountY");
503
                            return CE_Failure;
×
504
                        }
505
                        if (tile_count_y > INT_MAX / m_block_size_y ||
17✔
506
                            tile_count_y * m_block_size_y >
17✔
507
                                INT_MAX / (1 << m_data_window.m_tlevel))
17✔
508
                        {
509
                            CPLError(CE_Failure, CPLE_AppDefined,
×
510
                                     "Integer overflow in tile_count_y * "
511
                                     "m_block_size_y * (1 << "
512
                                     "m_data_window.m_tlevel)");
513
                            return CE_Failure;
×
514
                        }
515
                        m_data_window.m_sy = tile_count_y * m_block_size_y *
17✔
516
                                             (1 << m_data_window.m_tlevel);
17✔
517
                    }
518
                    else
519
                    {
520
                        CPLError(CE_Failure, CPLE_AppDefined,
×
521
                                 "GDALWMS: Mandatory elements of DataWindow "
522
                                 "missing: SizeX, SizeY.");
523
                        ret = CE_Failure;
×
524
                    }
525
                }
526
                if (ret == CE_None)
346✔
527
                {
528
                    if ((tx[0] != '\0') && (ty[0] != '\0'))
346✔
529
                    {
530
                        m_data_window.m_tx = atoi(tx);
346✔
531
                        m_data_window.m_ty = atoi(ty);
346✔
532
                    }
533
                    else
534
                    {
535
                        CPLError(CE_Failure, CPLE_AppDefined,
×
536
                                 "GDALWMS: Mandatory elements of DataWindow "
537
                                 "missing: TileX, TileY.");
538
                        ret = CE_Failure;
×
539
                    }
540
                }
541

542
                if (ret == CE_None)
346✔
543
                {
544
                    if (overview_count[0] != '\0')
346✔
545
                    {
546
                        nOverviews = atoi(overview_count);
5✔
547
                    }
548
                    else if (tlevel[0] != '\0')
341✔
549
                    {
550
                        nOverviews = m_data_window.m_tlevel;
335✔
551
                    }
552
                    else
553
                    {
554
                        const int min_overview_size = std::max(
555
                            32, std::min(m_block_size_x, m_block_size_y));
6✔
556
                        double a =
557
                            log(static_cast<double>(std::min(
6✔
558
                                m_data_window.m_sx, m_data_window.m_sy))) /
6✔
559
                                log(2.0) -
560
                            log(static_cast<double>(min_overview_size)) /
6✔
561
                                log(2.0);
6✔
562
                        nOverviews = std::max(
6✔
563
                            0, std::min(static_cast<int>(ceil(a)), 32));
6✔
564
                    }
565
                }
566
                if (ret == CE_None)
346✔
567
                {
568
                    CPLString y_origin_str = y_origin;
692✔
569
                    if (y_origin_str == "top")
346✔
570
                    {
571
                        m_data_window.m_y_origin = GDALWMSDataWindow::TOP;
332✔
572
                    }
573
                    else if (y_origin_str == "bottom")
14✔
574
                    {
575
                        m_data_window.m_y_origin = GDALWMSDataWindow::BOTTOM;
×
576
                    }
577
                    else if (y_origin_str == "default")
14✔
578
                    {
579
                        m_data_window.m_y_origin = GDALWMSDataWindow::DEFAULT;
14✔
580
                    }
581
                    else
582
                    {
583
                        CPLError(
×
584
                            CE_Failure, CPLE_AppDefined,
585
                            "GDALWMS: DataWindow YOrigin must be set to "
586
                            "one of 'default', 'top', or 'bottom', not '%s'.",
587
                            y_origin_str.c_str());
588
                        ret = CE_Failure;
×
589
                    }
590
                }
591
            }
592
        }
593

594
        if (ret == CE_None)
347✔
595
        {
596
            if (nBands < 1)
346✔
597
                nBands = atoi(CPLGetXMLValue(config, "BandsCount", "3"));
346✔
598
            if (nBands < 1)
346✔
599
            {
600
                CPLError(CE_Failure, CPLE_AppDefined,
×
601
                         "GDALWMS: Bad number of bands.");
602
                ret = CE_Failure;
×
603
            }
604
        }
605

606
        if (ret == CE_None)
347✔
607
        {
608
            const char *data_type = CPLGetXMLValue(config, "DataType", "Byte");
346✔
609
            if (!STARTS_WITH(data_type, "Byte"))
346✔
610
                SetTileOO("@DATATYPE", data_type);
3✔
611
            m_data_type = GDALGetDataTypeByName(data_type);
346✔
612
            if (m_data_type == GDT_Unknown || m_data_type >= GDT_TypeCount)
346✔
613
            {
614
                CPLError(CE_Failure, CPLE_AppDefined,
×
615
                         "GDALWMS: Invalid value in DataType. Data type \"%s\" "
616
                         "is not supported.",
617
                         data_type);
618
                ret = CE_Failure;
×
619
            }
620
        }
621

622
        // Initialize the bands and the overviews.  Assumes overviews are powers
623
        // of two
624
        if (ret == CE_None)
347✔
625
        {
626
            nRasterXSize = m_data_window.m_sx;
346✔
627
            nRasterYSize = m_data_window.m_sy;
346✔
628

629
            if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize) ||
692✔
630
                !GDALCheckBandCount(nBands, TRUE))
346✔
631
            {
632
                return CE_Failure;
×
633
            }
634

635
            GDALColorInterp default_color_interp[4][4] = {
346✔
636
                {GCI_GrayIndex, GCI_Undefined, GCI_Undefined, GCI_Undefined},
637
                {GCI_GrayIndex, GCI_AlphaBand, GCI_Undefined, GCI_Undefined},
638
                {GCI_RedBand, GCI_GreenBand, GCI_BlueBand, GCI_Undefined},
639
                {GCI_RedBand, GCI_GreenBand, GCI_BlueBand, GCI_AlphaBand}};
640
            for (int i = 0; i < nBands; ++i)
1,673✔
641
            {
642
                GDALColorInterp color_interp =
1,327✔
643
                    (nBands <= 4 && i <= 3 ? default_color_interp[nBands - 1][i]
1,327✔
644
                                           : GCI_Undefined);
645
                GDALWMSRasterBand *band = new GDALWMSRasterBand(this, i, 1.0);
1,327✔
646
                band->m_color_interp = color_interp;
1,327✔
647
                SetBand(i + 1, band);
1,327✔
648
                double scale = 0.5;
1,327✔
649
                for (int j = 0; j < nOverviews; ++j)
6,878✔
650
                {
651
                    if (!band->AddOverview(scale))
5,551✔
652
                        break;
×
653
                    band->m_color_interp = color_interp;
5,551✔
654
                    scale *= 0.5;
5,551✔
655
                }
656
            }
657
        }
658
    }
659

660
    // Let the local configuration override the minidriver supplied projection
661
    if (ret == CE_None)
350✔
662
    {
663
        const char *proj = CPLGetXMLValue(config, "Projection", "");
349✔
664
        if (proj[0] != '\0')
349✔
665
        {
666
            m_oSRS = ProjToSRS(proj);
158✔
667
            if (m_oSRS.IsEmpty())
158✔
668
            {
669
                CPLError(CE_Failure, CPLE_AppDefined,
×
670
                         "GDALWMS: Bad projection specified.");
671
                ret = CE_Failure;
×
672
            }
673
        }
674
    }
675

676
    // Same for Min, Max and NoData, defined per band or per dataset
677
    // If they are set as null strings, they clear the server declared values
678
    if (ret == CE_None)
350✔
679
    {
680
        // Data values are attributes, they include NoData Min and Max
681
        if (nullptr != CPLGetXMLNode(config, "DataValues"))
349✔
682
        {
683
            const char *nodata =
684
                CPLGetXMLValue(config, "DataValues.NoData", "");
3✔
685
            if (strlen(nodata) > 0)
3✔
686
            {
687
                SetTileOO("@NDV", nodata);
3✔
688
                WMSSetNoDataValue(nodata);
3✔
689
            }
690
            const char *min = CPLGetXMLValue(config, "DataValues.min", nullptr);
3✔
691
            if (min != nullptr)
3✔
692
                WMSSetMinValue(min);
×
693
            const char *max = CPLGetXMLValue(config, "DataValues.max", nullptr);
3✔
694
            if (max != nullptr)
3✔
695
                WMSSetMaxValue(max);
×
696
        }
697
    }
698

699
    if (ret == CE_None)
350✔
700
    {
701
        if (m_oSRS.IsEmpty())
349✔
702
        {
703
            const auto &oSRS = m_mini_driver->GetSpatialRef();
191✔
704
            if (!oSRS.IsEmpty())
191✔
705
            {
706
                m_oSRS = oSRS;
6✔
707
            }
708
        }
709
    }
710

711
    // Finish the minidriver initialization
712
    if (ret == CE_None)
350✔
713
        m_mini_driver->EndInit();
349✔
714

715
    return ret;
350✔
716
}
717

718
/************************************************************************/
719
/*                             IRasterIO()                              */
720
/************************************************************************/
721
CPLErr GDALWMSDataset::IRasterIO(GDALRWFlag rw, int x0, int y0, int sx, int sy,
9✔
722
                                 void *buffer, int bsx, int bsy,
723
                                 GDALDataType bdt, int band_count,
724
                                 BANDMAP_TYPE band_map, GSpacing nPixelSpace,
725
                                 GSpacing nLineSpace, GSpacing nBandSpace,
726
                                 GDALRasterIOExtraArg *psExtraArg)
727
{
728
    CPLErr ret;
729

730
    if (rw != GF_Read)
9✔
731
        return CE_Failure;
×
732
    if (buffer == nullptr)
9✔
733
        return CE_Failure;
×
734
    if ((sx == 0) || (sy == 0) || (bsx == 0) || (bsy == 0) || (band_count == 0))
9✔
735
        return CE_None;
×
736

737
    m_hint.m_x0 = x0;
9✔
738
    m_hint.m_y0 = y0;
9✔
739
    m_hint.m_sx = sx;
9✔
740
    m_hint.m_sy = sy;
9✔
741
    m_hint.m_overview = -1;
9✔
742
    m_hint.m_valid = true;
9✔
743
    // printf("[%p] GDALWMSDataset::IRasterIO(x0: %d, y0: %d, sx: %d, sy: %d,
744
    // bsx: %d, bsy: %d, band_count: %d, band_map: %p)\n", this, x0, y0, sx, sy,
745
    // bsx, bsy, band_count, band_map);
746
    ret = GDALDataset::IRasterIO(rw, x0, y0, sx, sy, buffer, bsx, bsy, bdt,
9✔
747
                                 band_count, band_map, nPixelSpace, nLineSpace,
748
                                 nBandSpace, psExtraArg);
749
    m_hint.m_valid = false;
9✔
750

751
    return ret;
9✔
752
}
753

754
/************************************************************************/
755
/*                          GetSpatialRef()                             */
756
/************************************************************************/
757
const OGRSpatialReference *GDALWMSDataset::GetSpatialRef() const
87✔
758
{
759
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
87✔
760
}
761

762
/************************************************************************/
763
/*                           SetSpatialRef()                            */
764
/************************************************************************/
765
CPLErr GDALWMSDataset::SetSpatialRef(const OGRSpatialReference *)
×
766
{
767
    return CE_Failure;
×
768
}
769

770
/************************************************************************/
771
/*                          GetGeoTransform()                           */
772
/************************************************************************/
773
CPLErr GDALWMSDataset::GetGeoTransform(GDALGeoTransform &gt) const
171✔
774
{
775
    if (!(m_mini_driver_caps.m_has_geotransform))
171✔
776
    {
777
        gt = GDALGeoTransform();
×
778
        return CE_Failure;
×
779
    }
780
    gt[0] = m_data_window.m_x0;
171✔
781
    gt[1] = (m_data_window.m_x1 - m_data_window.m_x0) /
342✔
782
            static_cast<double>(m_data_window.m_sx);
171✔
783
    gt[2] = 0.0;
171✔
784
    gt[3] = m_data_window.m_y0;
171✔
785
    gt[4] = 0.0;
171✔
786
    gt[5] = (m_data_window.m_y1 - m_data_window.m_y0) /
342✔
787
            static_cast<double>(m_data_window.m_sy);
171✔
788
    return CE_None;
171✔
789
}
790

791
/************************************************************************/
792
/*                          SetGeoTransform()                           */
793
/************************************************************************/
794
CPLErr GDALWMSDataset::SetGeoTransform(const GDALGeoTransform &)
×
795
{
796
    return CE_Failure;
×
797
}
798

799
/************************************************************************/
800
/*                             AdviseRead()                             */
801
/************************************************************************/
802
CPLErr GDALWMSDataset::AdviseRead(int x0, int y0, int sx, int sy, int bsx,
20✔
803
                                  int bsy, GDALDataType bdt,
804
                                  CPL_UNUSED int band_count,
805
                                  CPL_UNUSED int *band_map, char **options)
806
{
807
    //    printf("AdviseRead(%d, %d, %d, %d)\n", x0, y0, sx, sy);
808
    if (m_offline_mode || !m_use_advise_read)
20✔
809
        return CE_None;
20✔
810
    if (m_cache == nullptr)
×
811
        return CE_Failure;
×
812

813
    GDALRasterBand *band = GetRasterBand(1);
×
814
    if (band == nullptr)
×
815
        return CE_Failure;
×
816
    return band->AdviseRead(x0, y0, sx, sy, bsx, bsy, bdt, options);
×
817
}
818

819
/************************************************************************/
820
/*                      GetMetadataDomainList()                         */
821
/************************************************************************/
822

823
char **GDALWMSDataset::GetMetadataDomainList()
×
824
{
825
    return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
×
826
                                   TRUE, "WMS", nullptr);
×
827
}
828

829
/************************************************************************/
830
/*                          GetMetadataItem()                           */
831
/************************************************************************/
832
const char *GDALWMSDataset::GetMetadataItem(const char *pszName,
374✔
833
                                            const char *pszDomain)
834
{
835
    if (pszName != nullptr && EQUAL(pszName, "XML") && pszDomain != nullptr &&
374✔
836
        EQUAL(pszDomain, "WMS"))
3✔
837
    {
838
        return (m_osXML.size()) ? m_osXML.c_str() : nullptr;
3✔
839
    }
840

841
    return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
371✔
842
}
843

844
// Builds a CSL of options or returns the previous one
845
const char *const *GDALWMSDataset::GetHTTPRequestOpts()
41✔
846
{
847
    if (m_http_options != nullptr)
41✔
848
        return m_http_options;
7✔
849

850
    char **opts = nullptr;
34✔
851
    if (m_http_timeout != -1)
34✔
852
        opts = CSLAddString(opts, CPLOPrintf("TIMEOUT=%d", m_http_timeout));
34✔
853

854
    if (!m_osUserAgent.empty())
34✔
855
        opts = CSLAddNameValue(opts, "USERAGENT", m_osUserAgent);
×
856
    else
857
        opts = CSLAddString(
34✔
858
            opts, "USERAGENT=GDAL WMS driver (https://gdal.org/frmt_wms.html)");
859

860
    if (!m_osReferer.empty())
34✔
861
        opts = CSLAddNameValue(opts, "REFERER", m_osReferer);
×
862

863
    if (m_unsafeSsl >= 1)
34✔
864
        opts = CSLAddString(opts, "UNSAFESSL=1");
11✔
865

866
    if (!m_osUserPwd.empty())
34✔
867
        opts = CSLAddNameValue(opts, "USERPWD", m_osUserPwd);
×
868

869
    if (m_http_max_conn > 0)
34✔
870
        opts = CSLAddString(opts, CPLOPrintf("MAXCONN=%d", m_http_max_conn));
34✔
871

872
    if (!m_osAccept.empty())
34✔
873
        opts = CSLAddNameValue(opts, "ACCEPT", m_osAccept.c_str());
1✔
874

875
    m_http_options = opts;
34✔
876
    return m_http_options;
34✔
877
}
878

879
void GDALWMSDataset::SetTileOO(const char *pszName, const char *pszValue)
6✔
880
{
881
    if (pszName == nullptr || strlen(pszName) == 0)
6✔
882
        return;
×
883
    int oldidx = CSLFindName(m_tileOO, pszName);
6✔
884
    if (oldidx >= 0)
6✔
885
        m_tileOO = CSLRemoveStrings(m_tileOO, oldidx, 1, nullptr);
×
886
    if (pszValue != nullptr && strlen(pszValue))
6✔
887
        m_tileOO = CSLAddNameValue(m_tileOO, pszName, pszValue);
6✔
888
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc