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