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