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