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 * Permission is hereby granted, free of charge, to any person obtaining a
15 * copy of this software and associated documentation files (the "Software"),
16 * to deal in the Software without restriction, including without limitation
17 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 * and/or sell copies of the Software, and to permit persons to whom the
19 * Software is furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included
22 * in all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 * DEALINGS IN THE SOFTWARE.
31 ****************************************************************************
32 *
33 * dataset.cpp:
34 * Initialization of the GDALWMSdriver, parsing the XML configuration file,
35 * instantiation of the minidrivers and accessors used by minidrivers.
36 *
37 ***************************************************************************/
38
39 #include "wmsdriver.h"
40 #include "minidriver_wms.h"
41 #include "minidriver_tileservice.h"
42 #include "minidriver_worldwind.h"
43 #include "minidriver_tms.h"
44 #include "minidriver_tiled_wms.h"
45 #include "minidriver_virtualearth.h"
46
47 #include <algorithm>
48
49 CPL_CVSID("$Id: gdalwmsdataset.cpp 5e703afd56b19ad73c76ab865cb4f1988211b9ad 2021-01-20 14:45:04 -0800 Lucian Plesea $")
50
51 /************************************************************************/
52 /* GDALWMSDataset() */
53 /************************************************************************/
GDALWMSDataset()54 GDALWMSDataset::GDALWMSDataset() :
55 m_mini_driver(nullptr),
56 m_cache(nullptr),
57 m_poColorTable(nullptr),
58 m_data_type(GDT_Byte),
59 m_block_size_x(0),
60 m_block_size_y(0),
61 m_use_advise_read(0),
62 m_verify_advise_read(0),
63 m_offline_mode(0),
64 m_http_max_conn(0),
65 m_http_timeout(0),
66 m_http_options(nullptr),
67 m_tileOO(nullptr),
68 m_clamp_requests(true),
69 m_unsafeSsl(false),
70 m_zeroblock_on_serverexceptions(0),
71 m_default_block_size_x(1024),
72 m_default_block_size_y(1024),
73 m_default_tile_count_x(1),
74 m_default_tile_count_y(1),
75 m_default_overview_count(-1),
76 m_bNeedsDataWindow(true)
77 {
78 m_hint.m_valid = false;
79 m_data_window.m_sx = -1;
80 nBands = 0;
81 }
82
83 /************************************************************************/
84 /* ~GDALWMSDataset() */
85 /************************************************************************/
~GDALWMSDataset()86 GDALWMSDataset::~GDALWMSDataset() {
87 if (m_mini_driver) delete m_mini_driver;
88 if (m_cache) delete m_cache;
89 if (m_poColorTable) delete m_poColorTable;
90 CSLDestroy(m_http_options);
91 CSLDestroy(m_tileOO);
92 }
93
94 /************************************************************************/
95 /* Initialize() */
96 /************************************************************************/
Initialize(CPLXMLNode * config,char ** l_papszOpenOptions)97 CPLErr GDALWMSDataset::Initialize(CPLXMLNode *config, char **l_papszOpenOptions) {
98 CPLErr ret = CE_None;
99
100 char* pszXML = CPLSerializeXMLTree( config );
101 if (pszXML)
102 {
103 m_osXML = pszXML;
104 CPLFree(pszXML);
105 }
106
107 // Generic options that apply to all minidrivers
108
109 // UserPwd
110 const char *pszUserPwd = CPLGetXMLValue(config, "UserPwd", "");
111 if (pszUserPwd[0] != '\0')
112 m_osUserPwd = pszUserPwd;
113
114 const char *pszUserAgent = CPLGetXMLValue(config, "UserAgent", "");
115 if (pszUserAgent[0] != '\0')
116 m_osUserAgent = pszUserAgent;
117 else
118 m_osUserAgent = CPLGetConfigOption("GDAL_HTTP_USERAGENT", "");
119
120 const char *pszReferer = CPLGetXMLValue(config, "Referer", "");
121 if (pszReferer[0] != '\0')
122 m_osReferer = pszReferer;
123
124 {
125 const char *pszHttpZeroBlockCodes = CPLGetXMLValue(config, "ZeroBlockHttpCodes", "");
126 if (pszHttpZeroBlockCodes[0] == '\0') {
127 m_http_zeroblock_codes.insert(204);
128 }
129 else {
130 char **kv = CSLTokenizeString2(pszHttpZeroBlockCodes, ",", CSLT_HONOURSTRINGS);
131 for (int i = 0; i < CSLCount(kv); i++) {
132 int code = atoi(kv[i]);
133 if (code <= 0) {
134 CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Invalid value of ZeroBlockHttpCodes "
135 "\"%s\", comma separated HTTP response codes expected.", kv[i]);
136 ret = CE_Failure;
137 break;
138 }
139 m_http_zeroblock_codes.insert(code);
140 }
141 CSLDestroy(kv);
142 }
143 }
144
145 if (ret == CE_None) {
146 const char *pszZeroExceptions = CPLGetXMLValue(config, "ZeroBlockOnServerException", "");
147 if (pszZeroExceptions[0] != '\0') {
148 m_zeroblock_on_serverexceptions = StrToBool(pszZeroExceptions);
149 if (m_zeroblock_on_serverexceptions == -1) {
150 CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Invalid value of ZeroBlockOnServerException "
151 "\"%s\", true/false expected.", pszZeroExceptions);
152 ret = CE_Failure;
153 }
154 }
155 }
156
157 if (ret == CE_None) {
158 const char *max_conn = CPLGetXMLValue(config, "MaxConnections", "");
159 if (max_conn[0] == '\0') {
160 max_conn = CPLGetConfigOption("GDAL_MAX_CONNECTIONS", "");
161 }
162 if (max_conn[0] != '\0') {
163 m_http_max_conn = atoi(max_conn);
164 }
165 else {
166 m_http_max_conn = 2;
167 }
168 }
169
170 if (ret == CE_None) {
171 const char *timeout = CPLGetXMLValue(config, "Timeout", "");
172 if (timeout[0] != '\0') {
173 m_http_timeout = atoi(timeout);
174 }
175 else {
176 m_http_timeout = 300;
177 }
178 }
179
180 if (ret == CE_None) {
181 m_osAccept = CPLGetXMLValue(config, "Accept", "");
182 }
183
184 if (ret == CE_None) {
185 const char *offline_mode = CPLGetXMLValue(config, "OfflineMode", "");
186 if (offline_mode[0] != '\0') {
187 const int offline_mode_bool = StrToBool(offline_mode);
188 if (offline_mode_bool == -1) {
189 CPLError(CE_Failure, CPLE_AppDefined,
190 "GDALWMS: Invalid value of OfflineMode, true / false expected.");
191 ret = CE_Failure;
192 }
193 else {
194 m_offline_mode = offline_mode_bool;
195 }
196 }
197 else {
198 m_offline_mode = 0;
199 }
200 }
201
202 if (ret == CE_None) {
203 const char *advise_read = CPLGetXMLValue(config, "AdviseRead", "");
204 if (advise_read[0] != '\0') {
205 const int advise_read_bool = StrToBool(advise_read);
206 if (advise_read_bool == -1) {
207 CPLError(CE_Failure, CPLE_AppDefined,
208 "GDALWMS: Invalid value of AdviseRead, true / false expected.");
209 ret = CE_Failure;
210 }
211 else {
212 m_use_advise_read = advise_read_bool;
213 }
214 }
215 else {
216 m_use_advise_read = 0;
217 }
218 }
219
220 if (ret == CE_None) {
221 const char *verify_advise_read = CPLGetXMLValue(config, "VerifyAdviseRead", "");
222 if (m_use_advise_read) {
223 if (verify_advise_read[0] != '\0') {
224 const int verify_advise_read_bool = StrToBool(verify_advise_read);
225 if (verify_advise_read_bool == -1) {
226 CPLError(CE_Failure, CPLE_AppDefined,
227 "GDALWMS: Invalid value of VerifyAdviseRead, true / false expected.");
228 ret = CE_Failure;
229 }
230 else {
231 m_verify_advise_read = verify_advise_read_bool;
232 }
233 }
234 else {
235 m_verify_advise_read = 1;
236 }
237 }
238 }
239
240 CPLXMLNode *service_node = CPLGetXMLNode(config, "Service");
241 if (service_node == nullptr) {
242 CPLError(CE_Failure, CPLE_AppDefined,
243 "GDALWMS: No Service specified.");
244 return CE_Failure;
245 }
246
247 if (ret == CE_None) {
248 CPLXMLNode *cache_node = CPLGetXMLNode(config, "Cache");
249 if (cache_node != nullptr) {
250 m_cache = new GDALWMSCache();
251 if (m_cache->Initialize(CPLGetXMLValue(service_node, "ServerUrl", nullptr),
252 cache_node) != CE_None) {
253 delete m_cache;
254 m_cache = nullptr;
255 CPLError(CE_Failure, CPLE_AppDefined,
256 "GDALWMS: Failed to initialize cache.");
257 ret = CE_Failure;
258 }
259 else {
260 // NOTE: Save cache path to metadata. For example, this is
261 // useful for deleting a cache folder when removing dataset or
262 // to fill the cache for specified area and zoom levels
263 SetMetadataItem("CACHE_PATH", m_cache->CachePath(), nullptr);
264 }
265 }
266 }
267
268 if (ret == CE_None) {
269 const int v = StrToBool(CPLGetXMLValue(config, "UnsafeSSL", "false"));
270 if (v == -1) {
271 CPLError(CE_Failure, CPLE_AppDefined,
272 "GDALWMS: Invalid value of UnsafeSSL: true or false expected.");
273 ret = CE_Failure;
274 }
275 else {
276 m_unsafeSsl = v;
277 }
278 }
279
280 // Initialize the minidriver, which can set parameters for the dataset using member functions
281
282 const CPLString service_name = CPLGetXMLValue(service_node, "name", "");
283 if (service_name.empty()) {
284 CPLError(CE_Failure, CPLE_AppDefined,
285 "GDALWMS: No Service name specified.");
286 return CE_Failure;
287 }
288
289 m_mini_driver = NewWMSMiniDriver(service_name);
290 if (m_mini_driver == nullptr) {
291 CPLError(CE_Failure, CPLE_AppDefined,
292 "GDALWMS: No mini-driver registered for '%s'.", service_name.c_str());
293 return CE_Failure;
294 }
295
296 // Set up minidriver
297 m_mini_driver->m_parent_dataset = this;
298 if (m_mini_driver->Initialize(service_node, l_papszOpenOptions) != CE_None)
299 {
300 CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Failed to initialize minidriver.");
301 delete m_mini_driver;
302 m_mini_driver = nullptr;
303 ret = CE_Failure;
304 }
305 else
306 {
307 m_mini_driver->GetCapabilities(&m_mini_driver_caps);
308 }
309
310 /*
311 Parameters that could be set by minidriver already
312 If the size is set, minidriver has done this already
313 A "server" side minidriver needs to set at least:
314 - Blocksize (x and y)
315 - Clamp flag (defaults to true)
316 - DataWindow
317 - Band Count
318 - Data Type
319 It should also initialize and register the bands and overviews.
320 */
321
322 if (m_data_window.m_sx<1)
323 {
324 int nOverviews = 0;
325
326 if (ret == CE_None)
327 {
328 m_block_size_x = atoi(CPLGetXMLValue(config, "BlockSizeX",
329 CPLString().Printf("%d", m_default_block_size_x)));
330 m_block_size_y = atoi(CPLGetXMLValue(config, "BlockSizeY",
331 CPLString().Printf("%d", m_default_block_size_y)));
332 if (m_block_size_x <= 0 || m_block_size_y <= 0)
333 {
334 CPLError( CE_Failure, CPLE_AppDefined,
335 "GDALWMS: Invalid value in BlockSizeX or BlockSizeY" );
336 ret = CE_Failure;
337 }
338 }
339
340 if (ret == CE_None)
341 {
342 m_clamp_requests = StrToBool(CPLGetXMLValue(config, "ClampRequests", "true"));
343 if (m_clamp_requests<0)
344 {
345 CPLError(CE_Failure, CPLE_AppDefined,
346 "GDALWMS: Invalid value of ClampRequests, true/false expected.");
347 ret = CE_Failure;
348 }
349 }
350
351 if (ret == CE_None)
352 {
353 CPLXMLNode *data_window_node = CPLGetXMLNode(config, "DataWindow");
354 if (data_window_node == nullptr && m_bNeedsDataWindow)
355 {
356 CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: DataWindow missing.");
357 ret = CE_Failure;
358 }
359 else
360 {
361 CPLString osDefaultX0, osDefaultX1, osDefaultY0, osDefaultY1;
362 CPLString osDefaultTileCountX, osDefaultTileCountY, osDefaultTileLevel;
363 CPLString osDefaultOverviewCount;
364 osDefaultX0.Printf("%.8f", m_default_data_window.m_x0);
365 osDefaultX1.Printf("%.8f", m_default_data_window.m_x1);
366 osDefaultY0.Printf("%.8f", m_default_data_window.m_y0);
367 osDefaultY1.Printf("%.8f", m_default_data_window.m_y1);
368 osDefaultTileCountX.Printf("%d", m_default_tile_count_x);
369 osDefaultTileCountY.Printf("%d", m_default_tile_count_y);
370 if (m_default_data_window.m_tlevel >= 0)
371 osDefaultTileLevel.Printf("%d", m_default_data_window.m_tlevel);
372 if (m_default_overview_count >= 0)
373 osDefaultOverviewCount.Printf("%d", m_default_overview_count);
374 const char *overview_count = CPLGetXMLValue(config, "OverviewCount", osDefaultOverviewCount);
375 const char *ulx = CPLGetXMLValue(data_window_node, "UpperLeftX", osDefaultX0);
376 const char *uly = CPLGetXMLValue(data_window_node, "UpperLeftY", osDefaultY0);
377 const char *lrx = CPLGetXMLValue(data_window_node, "LowerRightX", osDefaultX1);
378 const char *lry = CPLGetXMLValue(data_window_node, "LowerRightY", osDefaultY1);
379 const char *sx = CPLGetXMLValue(data_window_node, "SizeX", "");
380 const char *sy = CPLGetXMLValue(data_window_node, "SizeY", "");
381 const char *tx = CPLGetXMLValue(data_window_node, "TileX", "0");
382 const char *ty = CPLGetXMLValue(data_window_node, "TileY", "0");
383 const char *tlevel =
384 CPLGetXMLValue(data_window_node, "TileLevel", osDefaultTileLevel);
385 const char *str_tile_count_x =
386 CPLGetXMLValue(data_window_node, "TileCountX", osDefaultTileCountX);
387 const char *str_tile_count_y =
388 CPLGetXMLValue(data_window_node, "TileCountY", osDefaultTileCountY);
389 const char *y_origin = CPLGetXMLValue(data_window_node, "YOrigin", "default");
390
391 if ((ulx[0] != '\0') && (uly[0] != '\0') && (lrx[0] != '\0') && (lry[0] != '\0'))
392 {
393 m_data_window.m_x0 = CPLAtof(ulx);
394 m_data_window.m_y0 = CPLAtof(uly);
395 m_data_window.m_x1 = CPLAtof(lrx);
396 m_data_window.m_y1 = CPLAtof(lry);
397 }
398 else
399 {
400 CPLError(CE_Failure, CPLE_AppDefined,
401 "GDALWMS: Mandatory elements of DataWindow missing: "
402 "UpperLeftX, UpperLeftY, LowerRightX, LowerRightY.");
403 ret = CE_Failure;
404 }
405
406 m_data_window.m_tlevel = atoi(tlevel);
407
408 if (ret == CE_None)
409 {
410 if ((sx[0] != '\0') && (sy[0] != '\0'))
411 {
412 m_data_window.m_sx = atoi(sx);
413 m_data_window.m_sy = atoi(sy);
414 }
415 else if ((tlevel[0] != '\0') && (str_tile_count_x[0] != '\0') && (str_tile_count_y[0] != '\0'))
416 {
417 int tile_count_x = atoi(str_tile_count_x);
418 int tile_count_y = atoi(str_tile_count_y);
419 m_data_window.m_sx = tile_count_x * m_block_size_x * (1 << m_data_window.m_tlevel);
420 m_data_window.m_sy = tile_count_y * m_block_size_y * (1 << m_data_window.m_tlevel);
421 }
422 else
423 {
424 CPLError(CE_Failure, CPLE_AppDefined,
425 "GDALWMS: Mandatory elements of DataWindow missing: SizeX, SizeY.");
426 ret = CE_Failure;
427 }
428 }
429 if (ret == CE_None)
430 {
431 if ((tx[0] != '\0') && (ty[0] != '\0'))
432 {
433 m_data_window.m_tx = atoi(tx);
434 m_data_window.m_ty = atoi(ty);
435 }
436 else
437 {
438 CPLError(CE_Failure, CPLE_AppDefined,
439 "GDALWMS: Mandatory elements of DataWindow missing: TileX, TileY.");
440 ret = CE_Failure;
441 }
442 }
443
444 if (ret == CE_None)
445 {
446 if (overview_count[0] != '\0')
447 {
448 nOverviews = atoi(overview_count);
449 }
450 else if (tlevel[0] != '\0')
451 {
452 nOverviews = m_data_window.m_tlevel;
453 }
454 else
455 {
456 const int min_overview_size =
457 std::max(32, std::min(m_block_size_x,
458 m_block_size_y));
459 double a =
460 log(static_cast<double>(
461 std::min(m_data_window.m_sx,
462 m_data_window.m_sy))) / log(2.0)
463 - log(static_cast<double>(min_overview_size)) /
464 log(2.0);
465 nOverviews =
466 std::max(0,
467 std::min(static_cast<int>(ceil(a)), 32));
468 }
469 }
470 if (ret == CE_None)
471 {
472 CPLString y_origin_str = y_origin;
473 if (y_origin_str == "top") {
474 m_data_window.m_y_origin = GDALWMSDataWindow::TOP;
475 } else if (y_origin_str == "bottom") {
476 m_data_window.m_y_origin = GDALWMSDataWindow::BOTTOM;
477 } else if (y_origin_str == "default") {
478 m_data_window.m_y_origin = GDALWMSDataWindow::DEFAULT;
479 } else {
480 CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: DataWindow YOrigin must be set to "
481 "one of 'default', 'top', or 'bottom', not '%s'.", y_origin_str.c_str());
482 ret = CE_Failure;
483 }
484 }
485 }
486 }
487
488 if (ret == CE_None)
489 {
490 if (nBands<1)
491 nBands=atoi(CPLGetXMLValue(config,"BandsCount","3"));
492 if (nBands<1)
493 {
494 CPLError(CE_Failure, CPLE_AppDefined,
495 "GDALWMS: Bad number of bands.");
496 ret = CE_Failure;
497 }
498 }
499
500 if (ret == CE_None)
501 {
502 const char *data_type = CPLGetXMLValue(config, "DataType", "Byte");
503 if (!STARTS_WITH(data_type, "Byte"))
504 SetTileOO("@DATATYPE", data_type);
505 m_data_type = GDALGetDataTypeByName(data_type);
506 if (m_data_type == GDT_Unknown || m_data_type >= GDT_TypeCount)
507 {
508 CPLError(CE_Failure, CPLE_AppDefined,
509 "GDALWMS: Invalid value in DataType. Data type \"%s\" is not supported.", data_type);
510 ret = CE_Failure;
511 }
512 }
513
514 // Initialize the bands and the overviews. Assumes overviews are powers of two
515 if (ret == CE_None)
516 {
517 nRasterXSize = m_data_window.m_sx;
518 nRasterYSize = m_data_window.m_sy;
519
520 if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize) ||
521 !GDALCheckBandCount(nBands, TRUE))
522 {
523 return CE_Failure;
524 }
525
526 GDALColorInterp default_color_interp[4][4] = {
527 { GCI_GrayIndex, GCI_Undefined, GCI_Undefined, GCI_Undefined },
528 { GCI_GrayIndex, GCI_AlphaBand, GCI_Undefined, GCI_Undefined },
529 { GCI_RedBand, GCI_GreenBand, GCI_BlueBand, GCI_Undefined },
530 { GCI_RedBand, GCI_GreenBand, GCI_BlueBand, GCI_AlphaBand }
531 };
532 for (int i = 0; i < nBands; ++i)
533 {
534 GDALColorInterp color_interp = (nBands <= 4 && i <= 3 ? default_color_interp[nBands - 1][i] : GCI_Undefined);
535 GDALWMSRasterBand *band = new GDALWMSRasterBand(this, i, 1.0);
536 band->m_color_interp = color_interp;
537 SetBand(i + 1, band);
538 double scale = 0.5;
539 for (int j = 0; j < nOverviews; ++j)
540 {
541 if( !band->AddOverview(scale) )
542 break;
543 band->m_color_interp = color_interp;
544 scale *= 0.5;
545 }
546 }
547 }
548 }
549
550 // Let the local configuration override the minidriver supplied projection
551 if (ret == CE_None) {
552 const char *proj = CPLGetXMLValue(config, "Projection", "");
553 if (proj[0] != '\0') {
554 m_projection = ProjToWKT(proj);
555 if (m_projection.empty()) {
556 CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Bad projection specified.");
557 ret = CE_Failure;
558 }
559 }
560 }
561
562 // Same for Min, Max and NoData, defined per band or per dataset
563 // If they are set as null strings, they clear the server declared values
564 if (ret == CE_None) {
565 // Data values are attributes, they include NoData Min and Max
566 if (nullptr!=CPLGetXMLNode(config,"DataValues")) {
567 const char *nodata = CPLGetXMLValue(config, "DataValues.NoData", "");
568 if (strlen(nodata) > 0) {
569 SetTileOO("@NDV", nodata);
570 WMSSetNoDataValue(nodata);
571 }
572 const char *min = CPLGetXMLValue(config, "DataValues.min", nullptr);
573 if (min != nullptr) WMSSetMinValue(min);
574 const char *max = CPLGetXMLValue(config, "DataValues.max", nullptr);
575 if (max != nullptr) WMSSetMaxValue(max);
576 }
577 }
578
579 if (ret == CE_None) {
580 if (!m_projection.size()) {
581 const char *proj = m_mini_driver->GetProjectionInWKT();
582 if (proj != nullptr) {
583 m_projection = proj;
584 }
585 }
586 }
587
588 // Finish the minidriver initialization
589 if (ret == CE_None)
590 m_mini_driver->EndInit();
591
592 return ret;
593 }
594
595 /************************************************************************/
596 /* IRasterIO() */
597 /************************************************************************/
IRasterIO(GDALRWFlag rw,int x0,int y0,int sx,int sy,void * buffer,int bsx,int bsy,GDALDataType bdt,int band_count,int * band_map,GSpacing nPixelSpace,GSpacing nLineSpace,GSpacing nBandSpace,GDALRasterIOExtraArg * psExtraArg)598 CPLErr GDALWMSDataset::IRasterIO(GDALRWFlag rw, int x0, int y0, int sx, int sy,
599 void *buffer, int bsx, int bsy, GDALDataType bdt,
600 int band_count, int *band_map,
601 GSpacing nPixelSpace, GSpacing nLineSpace,
602 GSpacing nBandSpace,
603 GDALRasterIOExtraArg* psExtraArg) {
604 CPLErr ret;
605
606 if (rw != GF_Read) return CE_Failure;
607 if (buffer == nullptr) return CE_Failure;
608 if ((sx == 0) || (sy == 0) || (bsx == 0) || (bsy == 0) || (band_count == 0)) return CE_None;
609
610 m_hint.m_x0 = x0;
611 m_hint.m_y0 = y0;
612 m_hint.m_sx = sx;
613 m_hint.m_sy = sy;
614 m_hint.m_overview = -1;
615 m_hint.m_valid = true;
616 // printf("[%p] GDALWMSDataset::IRasterIO(x0: %d, y0: %d, sx: %d, sy: %d, bsx: %d, bsy: %d, band_count: %d, band_map: %p)\n", this, x0, y0, sx, sy, bsx, bsy, band_count, band_map);
617 ret = GDALDataset::IRasterIO(rw, x0, y0, sx, sy, buffer, bsx, bsy, bdt, band_count, band_map,
618 nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
619 m_hint.m_valid = false;
620
621 return ret;
622 }
623
624 /************************************************************************/
625 /* GetProjectionRef() */
626 /************************************************************************/
_GetProjectionRef()627 const char *GDALWMSDataset::_GetProjectionRef() {
628 return m_projection.c_str();
629 }
630
631 /************************************************************************/
632 /* SetProjection() */
633 /************************************************************************/
_SetProjection(const char *)634 CPLErr GDALWMSDataset::_SetProjection(const char*) {
635 return CE_Failure;
636 }
637
638 /************************************************************************/
639 /* GetGeoTransform() */
640 /************************************************************************/
GetGeoTransform(double * gt)641 CPLErr GDALWMSDataset::GetGeoTransform(double *gt) {
642 if( !(m_mini_driver_caps.m_has_geotransform) )
643 {
644 gt[0] = 0;
645 gt[1] = 1;
646 gt[2] = 0;
647 gt[3] = 0;
648 gt[4] = 0;
649 gt[5] = 1;
650 return CE_Failure;
651 }
652 gt[0] = m_data_window.m_x0;
653 gt[1] = (m_data_window.m_x1 - m_data_window.m_x0) / static_cast<double>(m_data_window.m_sx);
654 gt[2] = 0.0;
655 gt[3] = m_data_window.m_y0;
656 gt[4] = 0.0;
657 gt[5] = (m_data_window.m_y1 - m_data_window.m_y0) / static_cast<double>(m_data_window.m_sy);
658 return CE_None;
659 }
660
661 /************************************************************************/
662 /* SetGeoTransform() */
663 /************************************************************************/
SetGeoTransform(CPL_UNUSED double * gt)664 CPLErr GDALWMSDataset::SetGeoTransform(CPL_UNUSED double *gt) {
665 return CE_Failure;
666 }
667
668 /************************************************************************/
669 /* AdviseRead() */
670 /************************************************************************/
AdviseRead(int x0,int y0,int sx,int sy,int bsx,int bsy,GDALDataType bdt,CPL_UNUSED int band_count,CPL_UNUSED int * band_map,char ** options)671 CPLErr GDALWMSDataset::AdviseRead(int x0, int y0,
672 int sx, int sy,
673 int bsx, int bsy,
674 GDALDataType bdt,
675 CPL_UNUSED int band_count,
676 CPL_UNUSED int *band_map,
677 char **options) {
678 // printf("AdviseRead(%d, %d, %d, %d)\n", x0, y0, sx, sy);
679 if (m_offline_mode || !m_use_advise_read) return CE_None;
680 if (m_cache == nullptr) return CE_Failure;
681
682 GDALRasterBand *band = GetRasterBand(1);
683 if (band == nullptr) return CE_Failure;
684 return band->AdviseRead(x0, y0, sx, sy, bsx, bsy, bdt, options);
685 }
686
687 /************************************************************************/
688 /* GetMetadataDomainList() */
689 /************************************************************************/
690
GetMetadataDomainList()691 char **GDALWMSDataset::GetMetadataDomainList()
692 {
693 return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
694 TRUE,
695 "WMS", nullptr);
696 }
697
698 /************************************************************************/
699 /* GetMetadataItem() */
700 /************************************************************************/
GetMetadataItem(const char * pszName,const char * pszDomain)701 const char *GDALWMSDataset::GetMetadataItem( const char * pszName,
702 const char * pszDomain )
703 {
704 if( pszName != nullptr && EQUAL(pszName, "XML") &&
705 pszDomain != nullptr && EQUAL(pszDomain, "WMS") )
706 {
707 return (m_osXML.size()) ? m_osXML.c_str() : nullptr;
708 }
709
710 return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
711 }
712
713 // Builds a CSL of options or returns the previous one
GetHTTPRequestOpts()714 const char * const * GDALWMSDataset::GetHTTPRequestOpts()
715 {
716 if (m_http_options != nullptr)
717 return m_http_options;
718
719 char **opts = nullptr;
720 if (m_http_timeout != -1)
721 opts = CSLAddString(opts, CPLOPrintf("TIMEOUT=%d", m_http_timeout));
722
723 if (!m_osUserAgent.empty())
724 opts = CSLAddNameValue(opts, "USERAGENT", m_osUserAgent);
725 else
726 opts = CSLAddString(opts, "USERAGENT=GDAL WMS driver (http://www.gdal.org/frmt_wms.html)");
727
728 if (!m_osReferer.empty())
729 opts = CSLAddNameValue(opts, "REFERER", m_osReferer);
730
731 if (m_unsafeSsl >= 1)
732 opts = CSLAddString(opts, "UNSAFESSL=1");
733
734 if (!m_osUserPwd.empty())
735 opts = CSLAddNameValue(opts, "USERPWD", m_osUserPwd);
736
737 if (m_http_max_conn > 0)
738 opts = CSLAddString(opts, CPLOPrintf("MAXCONN=%d", m_http_max_conn));
739
740 if (!m_osAccept.empty() )
741 opts = CSLAddNameValue(opts, "ACCEPT", m_osAccept.c_str());
742
743 m_http_options = opts;
744 return m_http_options;
745 }
746
SetTileOO(const char * pszName,const char * pszValue)747 void GDALWMSDataset::SetTileOO(const char* pszName, const char* pszValue) {
748 if (pszName == nullptr || strlen(pszName) == 0)
749 return;
750 int oldidx = CSLFindName(m_tileOO, pszName);
751 if (oldidx >= 0)
752 m_tileOO = CSLRemoveStrings(m_tileOO, oldidx, 1, nullptr);
753 if (pszValue != nullptr && strlen(pszValue))
754 m_tileOO = CSLAddNameValue(m_tileOO, pszName, pszValue);
755 }
756