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