/src/gdal/frmts/wms/gdalwmsrasterband.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: WMS Client Driver |
4 | | * Purpose: GDALWMSRasterBand implementation. |
5 | | * Author: Adam Nowacki, nowak@xpam.de |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2007, Adam Nowacki |
9 | | * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * Copyright (c) 2017, Dmitry Baryshnikov, <polimax@mail.ru> |
11 | | * Copyright (c) 2017, NextGIS, <info@nextgis.com> |
12 | | * |
13 | | * SPDX-License-Identifier: MIT |
14 | | ****************************************************************************/ |
15 | | |
16 | | #include "wmsdriver.h" |
17 | | |
18 | | #include "gdal_colortable.h" |
19 | | #include "gdal_rasterblock.h" |
20 | | #include "gdal_cpp_functions.h" |
21 | | |
22 | | #include <algorithm> |
23 | | |
24 | | GDALWMSRasterBand::GDALWMSRasterBand(GDALWMSDataset *parent_dataset, int band, |
25 | | double scale) |
26 | 811k | : m_parent_dataset(parent_dataset), m_scale(scale), m_overview(-1), |
27 | 811k | m_color_interp(GCI_Undefined), m_nAdviseReadBX0(-1), m_nAdviseReadBY0(-1), |
28 | 811k | m_nAdviseReadBX1(-1), m_nAdviseReadBY1(-1) |
29 | 811k | { |
30 | | #ifdef DEBUG_VERBOSE |
31 | | printf("[%p] GDALWMSRasterBand::GDALWMSRasterBand(%p, %d, %f)\n", /*ok*/ |
32 | | this, parent_dataset, band, scale); |
33 | | #endif |
34 | | |
35 | 811k | if (scale == 1.0) |
36 | 39.5k | poDS = parent_dataset; |
37 | 771k | else |
38 | 771k | poDS = nullptr; |
39 | 811k | if (parent_dataset->m_mini_driver_caps.m_overview_dim_computation_method == |
40 | 811k | OVERVIEW_ROUNDED) |
41 | 811k | { |
42 | 811k | nRasterXSize = static_cast<int>( |
43 | 811k | m_parent_dataset->m_data_window.m_sx * scale + 0.5); |
44 | 811k | nRasterYSize = static_cast<int>( |
45 | 811k | m_parent_dataset->m_data_window.m_sy * scale + 0.5); |
46 | 811k | } |
47 | 0 | else |
48 | 0 | { |
49 | 0 | nRasterXSize = |
50 | 0 | static_cast<int>(m_parent_dataset->m_data_window.m_sx * scale); |
51 | 0 | nRasterYSize = |
52 | 0 | static_cast<int>(m_parent_dataset->m_data_window.m_sy * scale); |
53 | 0 | } |
54 | 811k | nBand = band; |
55 | 811k | eDataType = m_parent_dataset->m_data_type; |
56 | 811k | nBlockXSize = m_parent_dataset->m_block_size_x; |
57 | 811k | nBlockYSize = m_parent_dataset->m_block_size_y; |
58 | 811k | } |
59 | | |
60 | | GDALWMSRasterBand::~GDALWMSRasterBand() |
61 | 811k | { |
62 | 1.58M | while (!m_overviews.empty()) |
63 | 771k | { |
64 | 771k | delete m_overviews.back(); |
65 | 771k | m_overviews.pop_back(); |
66 | 771k | } |
67 | 811k | } |
68 | | |
69 | | // Request for x, y but all blocks between bx0-bx1 and by0-by1 should be read |
70 | | CPLErr GDALWMSRasterBand::ReadBlocks(int x, int y, void *buffer, int bx0, |
71 | | int by0, int bx1, int by1, int advise_read) |
72 | 1.17k | { |
73 | 1.17k | CPLErr ret = CE_None; |
74 | | |
75 | | // Get a vector of requests large enough for this call |
76 | 1.17k | std::vector<WMSHTTPRequest> requests(static_cast<size_t>(bx1 - bx0 + 1) * |
77 | 1.17k | (by1 - by0 + 1)); |
78 | | |
79 | 1.17k | size_t count = 0; // How many requests are valid |
80 | 1.17k | GDALWMSCache *cache = m_parent_dataset->m_cache; |
81 | 1.17k | int offline = m_parent_dataset->m_offline_mode; |
82 | 1.17k | const char *const *options = m_parent_dataset->GetHTTPRequestOpts(); |
83 | | |
84 | 2.80k | for (int iy = by0; iy <= by1; ++iy) |
85 | 1.62k | { |
86 | 4.17k | for (int ix = bx0; ix <= bx1; ++ix) |
87 | 2.54k | { |
88 | 2.54k | WMSHTTPRequest &request = requests[count]; |
89 | 2.54k | request.x = ix; |
90 | 2.54k | request.y = iy; |
91 | 2.54k | bool need_this_block = false; |
92 | 2.54k | if (!advise_read) |
93 | 2.54k | { |
94 | 10.7k | for (int ib = 1; ib <= m_parent_dataset->nBands; ++ib) |
95 | 8.21k | { |
96 | 8.21k | if ((ix == x) && (iy == y) && (ib == nBand)) |
97 | 1.17k | { |
98 | 1.17k | need_this_block = true; |
99 | 1.17k | } |
100 | 7.03k | else |
101 | 7.03k | { |
102 | 7.03k | GDALWMSRasterBand *band = |
103 | 7.03k | static_cast<GDALWMSRasterBand *>( |
104 | 7.03k | m_parent_dataset->GetRasterBand(ib)); |
105 | 7.03k | if (m_overview >= 0) |
106 | 1.08k | band = static_cast<GDALWMSRasterBand *>( |
107 | 1.08k | band->GetOverview(m_overview)); |
108 | 7.03k | if (!band->IsBlockInCache(ix, iy)) |
109 | 6.90k | need_this_block = true; |
110 | 7.03k | } |
111 | 8.21k | } |
112 | 2.54k | } |
113 | 0 | else |
114 | 0 | { |
115 | 0 | need_this_block = true; |
116 | 0 | } |
117 | | |
118 | 2.54k | void *p = ((ix == x) && (iy == y)) ? buffer : nullptr; |
119 | 2.54k | if (need_this_block) |
120 | 2.51k | { |
121 | 2.51k | ret = AskMiniDriverForBlock(request, ix, iy); |
122 | 2.51k | if (ret != CE_None) |
123 | 0 | { |
124 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "%s", |
125 | 0 | request.Error.c_str()); |
126 | 0 | ret = CE_Failure; |
127 | 0 | } |
128 | | // A missing tile is signaled by setting a range of "none" |
129 | 2.51k | if (EQUAL(request.Range, "none")) |
130 | 0 | { |
131 | 0 | if (!advise_read) |
132 | 0 | { |
133 | 0 | if (EmptyBlock(ix, iy, nBand, p) != CE_None) |
134 | 0 | { |
135 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
136 | 0 | "GDALWMS: EmptyBlock failed."); |
137 | 0 | ret = CE_Failure; |
138 | 0 | } |
139 | 0 | } |
140 | 0 | need_this_block = false; |
141 | 0 | } |
142 | 2.51k | if (ret == CE_None && cache != nullptr) |
143 | 544 | { |
144 | 544 | if (cache->GetItemStatus(request.URL) == CACHE_ITEM_OK) |
145 | 180 | { |
146 | 180 | if (advise_read) |
147 | 0 | { |
148 | 0 | need_this_block = false; |
149 | 0 | } |
150 | 180 | else |
151 | 180 | { |
152 | 180 | if (ReadBlockFromCache(request.URL, ix, iy, nBand, |
153 | 180 | p, 0) == CE_None) |
154 | 116 | { |
155 | 116 | need_this_block = false; |
156 | 116 | } |
157 | 180 | } |
158 | 180 | } |
159 | 544 | } |
160 | 2.51k | } |
161 | | |
162 | 2.54k | if (need_this_block) |
163 | 2.39k | { |
164 | 2.39k | if (offline) |
165 | 0 | { |
166 | 0 | if (!advise_read) |
167 | 0 | { |
168 | 0 | if (EmptyBlock(ix, iy, nBand, p) != CE_None) |
169 | 0 | { |
170 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
171 | 0 | "GDALWMS: EmptyBlock failed."); |
172 | 0 | ret = CE_Failure; |
173 | 0 | } |
174 | 0 | } |
175 | 0 | } |
176 | 2.39k | else |
177 | 2.39k | { |
178 | 2.39k | request.options = options; |
179 | 2.39k | WMSHTTPInitializeRequest(&request); |
180 | 2.39k | count++; |
181 | 2.39k | } |
182 | 2.39k | } |
183 | 2.54k | } |
184 | 1.62k | } |
185 | | |
186 | | // Fetch all the requests, OK to call with count of 0 |
187 | 1.17k | if (WMSHTTPFetchMulti(count ? &requests[0] : nullptr, |
188 | 1.17k | static_cast<int>(count)) != CE_None) |
189 | 0 | { |
190 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
191 | 0 | "GDALWMS: CPLHTTPFetchMulti failed."); |
192 | 0 | ret = CE_Failure; |
193 | 0 | } |
194 | | |
195 | 3.57k | for (size_t i = 0; i < count; ++i) |
196 | 2.39k | { |
197 | 2.39k | WMSHTTPRequest &request = requests[i]; |
198 | 2.39k | void *p = ((request.x == x) && (request.y == y)) ? buffer : nullptr; |
199 | 2.39k | if (ret == CE_None) |
200 | 1.18k | { |
201 | 1.18k | int success = (request.nStatus == 200) || |
202 | 1.12k | (!request.Range.empty() && request.nStatus == 206); |
203 | 1.18k | if (success && (request.pabyData != nullptr) && |
204 | 56 | (request.nDataLen > 0)) |
205 | 56 | { |
206 | 56 | CPLString file_name( |
207 | 56 | BufferToVSIFile(request.pabyData, request.nDataLen)); |
208 | 56 | if (!file_name.empty()) |
209 | 56 | { |
210 | | /* check for error xml */ |
211 | 56 | if (request.nDataLen >= 20) |
212 | 56 | { |
213 | 56 | const char *download_data = |
214 | 56 | reinterpret_cast<char *>(request.pabyData); |
215 | 56 | if (STARTS_WITH_CI(download_data, "<?xml ") || |
216 | 56 | STARTS_WITH_CI(download_data, "<!DOCTYPE ") || |
217 | 56 | STARTS_WITH_CI(download_data, "<ServiceException")) |
218 | 0 | { |
219 | 0 | if (ReportWMSException(file_name) != CE_None) |
220 | 0 | { |
221 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
222 | 0 | "GDALWMS: The server returned unknown " |
223 | 0 | "exception."); |
224 | 0 | } |
225 | 0 | ret = CE_Failure; |
226 | 0 | } |
227 | 56 | } |
228 | 56 | if (ret == CE_None) |
229 | 56 | { |
230 | 56 | if (advise_read && |
231 | 0 | !m_parent_dataset->m_verify_advise_read) |
232 | 0 | { |
233 | 0 | if (cache != nullptr) |
234 | 0 | cache->Insert(request.URL, file_name); |
235 | 0 | } |
236 | 56 | else |
237 | 56 | { |
238 | 56 | ret = ReadBlockFromFile(file_name, request.x, |
239 | 56 | request.y, nBand, p, |
240 | 56 | advise_read); |
241 | 56 | if (ret == CE_None) |
242 | 52 | { |
243 | 52 | if (cache != nullptr) |
244 | 52 | cache->Insert(request.URL, file_name); |
245 | 52 | } |
246 | 4 | else |
247 | 4 | { |
248 | 4 | CPLError( |
249 | 4 | ret, CPLE_AppDefined, |
250 | 4 | "GDALWMS: ReadBlockFromFile (%s) failed.", |
251 | 4 | request.URL.c_str()); |
252 | 4 | } |
253 | 56 | } |
254 | 56 | } |
255 | 0 | else if (m_parent_dataset->m_zeroblock_on_serverexceptions) |
256 | 0 | { |
257 | 0 | ret = EmptyBlock(request.x, request.y, nBand, p); |
258 | 0 | if (ret != CE_None) |
259 | 0 | CPLError(ret, CPLE_AppDefined, |
260 | 0 | "GDALWMS: EmptyBlock failed."); |
261 | 0 | } |
262 | 56 | VSIUnlink(file_name); |
263 | 56 | } |
264 | 56 | } |
265 | 1.12k | else |
266 | 1.12k | { // HTTP error |
267 | | // One more try to get cached block. For example if no web |
268 | | // access available |
269 | 1.12k | CPLDebug("WMS", "ReadBlockFromCache"); |
270 | | |
271 | 1.12k | if (m_parent_dataset->m_cache != nullptr) |
272 | 88 | ret = ReadBlockFromCache(request.URL, request.x, request.y, |
273 | 88 | nBand, p, advise_read); |
274 | 1.04k | else |
275 | 1.04k | ret = CE_Failure; |
276 | | |
277 | 1.12k | if (ret != CE_None) |
278 | 1.12k | { |
279 | 1.12k | CPLDebug("WMS", "After ReadBlockFromCache"); |
280 | 1.12k | if (m_parent_dataset->m_http_zeroblock_codes.find( |
281 | 1.12k | request.nStatus) != |
282 | 1.12k | m_parent_dataset->m_http_zeroblock_codes.end()) |
283 | 0 | { |
284 | 0 | if (!advise_read) |
285 | 0 | { |
286 | 0 | ret = EmptyBlock(request.x, request.y, nBand, p); |
287 | 0 | if (ret != CE_None) |
288 | 0 | CPLError(ret, CPLE_AppDefined, |
289 | 0 | "GDALWMS: EmptyBlock failed."); |
290 | 0 | } |
291 | 0 | } |
292 | 1.12k | else |
293 | 1.12k | { |
294 | 1.12k | ret = CE_Failure; |
295 | 1.12k | CPLError(ret, CPLE_AppDefined, |
296 | 1.12k | "GDALWMS: Unable to download block %d, %d.\n" |
297 | 1.12k | "URL: %s\n HTTP status code: %d, error: %s.\n" |
298 | 1.12k | "Add the HTTP status code to " |
299 | 1.12k | "<ZeroBlockHttpCodes> to ignore this error " |
300 | 1.12k | "(see https://gdal.org/frmt_wms.html).", |
301 | 1.12k | request.x, request.y, |
302 | 1.12k | !request.URL.empty() ? request.Error.c_str() |
303 | 1.12k | : "(null)", |
304 | 1.12k | request.nStatus, |
305 | 1.12k | !request.Error.empty() ? request.Error.c_str() |
306 | 1.12k | : "(null)"); |
307 | 1.12k | } |
308 | 1.12k | } |
309 | 1.12k | } |
310 | 1.18k | } |
311 | 2.39k | } |
312 | | |
313 | 1.17k | return ret; |
314 | 1.17k | } |
315 | | |
316 | | CPLErr GDALWMSRasterBand::IReadBlock(int x, int y, void *buffer) |
317 | 1.17k | { |
318 | 1.17k | int bx0 = x; |
319 | 1.17k | int by0 = y; |
320 | 1.17k | int bx1 = x; |
321 | 1.17k | int by1 = y; |
322 | | |
323 | 1.17k | bool bCancelHint = false; |
324 | 1.17k | if ((m_parent_dataset->m_hint.m_valid) && |
325 | 1.17k | (m_parent_dataset->m_hint.m_overview == m_overview)) |
326 | 1.17k | { |
327 | 1.17k | int tbx0 = m_parent_dataset->m_hint.m_x0 / nBlockXSize; |
328 | 1.17k | int tby0 = m_parent_dataset->m_hint.m_y0 / nBlockYSize; |
329 | 1.17k | int tbx1 = (m_parent_dataset->m_hint.m_x0 + |
330 | 1.17k | m_parent_dataset->m_hint.m_sx - 1) / |
331 | 1.17k | nBlockXSize; |
332 | 1.17k | int tby1 = (m_parent_dataset->m_hint.m_y0 + |
333 | 1.17k | m_parent_dataset->m_hint.m_sy - 1) / |
334 | 1.17k | nBlockYSize; |
335 | 1.17k | if ((tbx0 <= x) && (tby0 <= y) && (tbx1 >= x) && (tby1 >= y)) |
336 | 1.17k | { |
337 | | // Avoid downloading a insane number of tiles at once. |
338 | | // Limit to 30x30 tiles centered around block of interest. |
339 | 1.17k | bx0 = std::max(x - 15, tbx0); |
340 | 1.17k | by0 = std::max(y - 15, tby0); |
341 | 1.17k | bx1 = std::min(x + 15, tbx1); |
342 | 1.17k | by1 = std::min(y + 15, tby1); |
343 | 1.17k | bCancelHint = |
344 | 1.17k | (bx0 == tbx0 && by0 == tby0 && bx1 == tbx1 && by1 == tby1); |
345 | 1.17k | } |
346 | 1.17k | } |
347 | | |
348 | 1.17k | CPLErr eErr = ReadBlocks(x, y, buffer, bx0, by0, bx1, by1, 0); |
349 | | |
350 | 1.17k | if (bCancelHint) |
351 | 1.14k | { |
352 | 1.14k | m_parent_dataset->m_hint.m_valid = false; |
353 | 1.14k | } |
354 | | |
355 | 1.17k | return eErr; |
356 | 1.17k | } |
357 | | |
358 | | CPLErr GDALWMSRasterBand::IRasterIO(GDALRWFlag rw, int x0, int y0, int sx, |
359 | | int sy, void *buffer, int bsx, int bsy, |
360 | | GDALDataType bdt, GSpacing nPixelSpace, |
361 | | GSpacing nLineSpace, |
362 | | GDALRasterIOExtraArg *psExtraArg) |
363 | 1.56k | { |
364 | 1.56k | CPLErr ret; |
365 | | |
366 | 1.56k | if (rw != GF_Read) |
367 | 0 | return CE_Failure; |
368 | 1.56k | if (buffer == nullptr) |
369 | 0 | return CE_Failure; |
370 | 1.56k | if ((sx == 0) || (sy == 0) || (bsx == 0) || (bsy == 0)) |
371 | 0 | return CE_None; |
372 | | |
373 | 1.56k | m_parent_dataset->m_hint.m_x0 = x0; |
374 | 1.56k | m_parent_dataset->m_hint.m_y0 = y0; |
375 | 1.56k | m_parent_dataset->m_hint.m_sx = sx; |
376 | 1.56k | m_parent_dataset->m_hint.m_sy = sy; |
377 | 1.56k | m_parent_dataset->m_hint.m_overview = m_overview; |
378 | 1.56k | m_parent_dataset->m_hint.m_valid = true; |
379 | 1.56k | ret = GDALRasterBand::IRasterIO(rw, x0, y0, sx, sy, buffer, bsx, bsy, bdt, |
380 | 1.56k | nPixelSpace, nLineSpace, psExtraArg); |
381 | 1.56k | m_parent_dataset->m_hint.m_valid = false; |
382 | | |
383 | 1.56k | return ret; |
384 | 1.56k | } |
385 | | |
386 | | int GDALWMSRasterBand::HasArbitraryOverviews() |
387 | 0 | { |
388 | | // return m_parent_dataset->m_mini_driver_caps.m_has_arb_overviews; |
389 | 0 | return 0; // not implemented yet |
390 | 0 | } |
391 | | |
392 | | int GDALWMSRasterBand::GetOverviewCount() |
393 | 3.47k | { |
394 | 3.47k | return static_cast<int>(m_overviews.size()); |
395 | 3.47k | } |
396 | | |
397 | | GDALRasterBand *GDALWMSRasterBand::GetOverview(int n) |
398 | 48.7k | { |
399 | 48.7k | if ((!m_overviews.empty()) && (static_cast<size_t>(n) < m_overviews.size())) |
400 | 48.7k | return m_overviews[n]; |
401 | 0 | else |
402 | 0 | return nullptr; |
403 | 48.7k | } |
404 | | |
405 | | bool GDALWMSRasterBand::AddOverview(double scale) |
406 | 771k | { |
407 | 771k | GDALWMSRasterBand *overview = |
408 | 771k | new GDALWMSRasterBand(m_parent_dataset, nBand, scale); |
409 | 771k | if (overview->GetXSize() == 0 || overview->GetYSize() == 0) |
410 | 9 | { |
411 | 9 | delete overview; |
412 | 9 | return false; |
413 | 9 | } |
414 | 771k | std::vector<GDALWMSRasterBand *>::iterator it = m_overviews.begin(); |
415 | 8.10M | for (; it != m_overviews.end(); ++it) |
416 | 7.33M | { |
417 | 7.33M | GDALWMSRasterBand *p = *it; |
418 | 7.33M | if (p->m_scale < scale) |
419 | 0 | break; |
420 | 7.33M | } |
421 | 771k | m_overviews.insert(it, overview); |
422 | 771k | it = m_overviews.begin(); |
423 | 8.87M | for (int i = 0; it != m_overviews.end(); ++it, ++i) |
424 | 8.10M | { |
425 | 8.10M | GDALWMSRasterBand *p = *it; |
426 | 8.10M | p->m_overview = i; |
427 | 8.10M | } |
428 | 771k | return true; |
429 | 771k | } |
430 | | |
431 | | bool GDALWMSRasterBand::IsBlockInCache(int x, int y) |
432 | 7.66k | { |
433 | 7.66k | bool ret = false; |
434 | 7.66k | GDALRasterBlock *b = TryGetLockedBlockRef(x, y); |
435 | 7.66k | if (b != nullptr) |
436 | 128 | { |
437 | 128 | ret = true; |
438 | 128 | b->DropLock(); |
439 | 128 | } |
440 | 7.66k | return ret; |
441 | 7.66k | } |
442 | | |
443 | | // This is the function that calculates the block coordinates for the fetch |
444 | | CPLErr GDALWMSRasterBand::AskMiniDriverForBlock(WMSHTTPRequest &r, int x, int y) |
445 | 2.51k | { |
446 | 2.51k | GDALWMSImageRequestInfo iri; |
447 | 2.51k | GDALWMSTiledImageRequestInfo tiri; |
448 | | |
449 | 2.51k | ComputeRequestInfo(iri, tiri, x, y); |
450 | 2.51k | return m_parent_dataset->m_mini_driver->TiledImageRequest(r, iri, tiri); |
451 | 2.51k | } |
452 | | |
453 | | void GDALWMSRasterBand::ComputeRequestInfo(GDALWMSImageRequestInfo &iri, |
454 | | GDALWMSTiledImageRequestInfo &tiri, |
455 | | int x, int y) |
456 | 2.51k | { |
457 | 2.51k | int x0 = std::max(0, x * nBlockXSize); |
458 | 2.51k | int y0 = std::max(0, y * nBlockYSize); |
459 | 2.51k | int x1 = std::max(0, (x + 1) * nBlockXSize); |
460 | 2.51k | int y1 = std::max(0, (y + 1) * nBlockYSize); |
461 | 2.51k | if (m_parent_dataset->m_clamp_requests) |
462 | 2.51k | { |
463 | 2.51k | x0 = std::min(x0, nRasterXSize); |
464 | 2.51k | y0 = std::min(y0, nRasterYSize); |
465 | 2.51k | x1 = std::min(x1, nRasterXSize); |
466 | 2.51k | y1 = std::min(y1, nRasterYSize); |
467 | 2.51k | } |
468 | | |
469 | 2.51k | const double rx = (m_parent_dataset->m_data_window.m_x1 - |
470 | 2.51k | m_parent_dataset->m_data_window.m_x0) / |
471 | 2.51k | static_cast<double>(nRasterXSize); |
472 | 2.51k | const double ry = (m_parent_dataset->m_data_window.m_y1 - |
473 | 2.51k | m_parent_dataset->m_data_window.m_y0) / |
474 | 2.51k | static_cast<double>(nRasterYSize); |
475 | | /* Use different method for x0,y0 and x1,y1 to make sure calculated values |
476 | | * are exact for corner requests */ |
477 | 2.51k | iri.m_x0 = x0 * rx + m_parent_dataset->m_data_window.m_x0; |
478 | 2.51k | iri.m_y0 = y0 * ry + m_parent_dataset->m_data_window.m_y0; |
479 | 2.51k | iri.m_x1 = m_parent_dataset->m_data_window.m_x1 - (nRasterXSize - x1) * rx; |
480 | 2.51k | iri.m_y1 = m_parent_dataset->m_data_window.m_y1 - (nRasterYSize - y1) * ry; |
481 | 2.51k | iri.m_sx = x1 - x0; |
482 | 2.51k | iri.m_sy = y1 - y0; |
483 | | |
484 | 2.51k | int level = m_overview + 1; |
485 | 2.51k | tiri.m_x = (m_parent_dataset->m_data_window.m_tx >> level) + x; |
486 | 2.51k | tiri.m_y = (m_parent_dataset->m_data_window.m_ty >> level) + y; |
487 | 2.51k | tiri.m_level = m_parent_dataset->m_data_window.m_tlevel - level; |
488 | 2.51k | } |
489 | | |
490 | | /************************************************************************/ |
491 | | /* GetMetadataDomainList() */ |
492 | | /************************************************************************/ |
493 | | |
494 | | char **GDALWMSRasterBand::GetMetadataDomainList() |
495 | 0 | { |
496 | 0 | char **m_list = GDALPamRasterBand::GetMetadataDomainList(); |
497 | 0 | char **mini_list = m_parent_dataset->m_mini_driver->GetMetadataDomainList(); |
498 | 0 | if (mini_list != nullptr) |
499 | 0 | { |
500 | 0 | m_list = CSLMerge(m_list, mini_list); |
501 | 0 | CSLDestroy(mini_list); |
502 | 0 | } |
503 | 0 | return m_list; |
504 | 0 | } |
505 | | |
506 | | const char *GDALWMSRasterBand::GetMetadataItem(const char *pszName, |
507 | | const char *pszDomain) |
508 | 2.04k | { |
509 | 2.04k | if (!m_parent_dataset->m_mini_driver_caps.m_has_getinfo || |
510 | 2.04k | !(pszDomain != nullptr && EQUAL(pszDomain, "LocationInfo") && |
511 | 0 | (STARTS_WITH_CI(pszName, "Pixel_") || |
512 | 0 | STARTS_WITH_CI(pszName, "GeoPixel_")))) |
513 | 2.04k | return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain); |
514 | | |
515 | | /* ==================================================================== */ |
516 | | /* LocationInfo handling. */ |
517 | | /* ==================================================================== */ |
518 | | |
519 | | /* -------------------------------------------------------------------- */ |
520 | | /* What pixel are we aiming at? */ |
521 | | /* -------------------------------------------------------------------- */ |
522 | 0 | int iPixel, iLine; |
523 | 0 | if (STARTS_WITH_CI(pszName, "Pixel_")) |
524 | 0 | { |
525 | 0 | if (sscanf(pszName + 6, "%d_%d", &iPixel, &iLine) != 2) |
526 | 0 | return nullptr; |
527 | 0 | } |
528 | 0 | else if (STARTS_WITH_CI(pszName, "GeoPixel_")) |
529 | 0 | { |
530 | 0 | GDALGeoTransform gt, invGT; |
531 | 0 | double dfGeoX, dfGeoY; |
532 | |
|
533 | 0 | { |
534 | 0 | dfGeoX = CPLAtof(pszName + 9); |
535 | 0 | const char *pszUnderscore = strchr(pszName + 9, '_'); |
536 | 0 | if (!pszUnderscore) |
537 | 0 | return nullptr; |
538 | 0 | dfGeoY = CPLAtof(pszUnderscore + 1); |
539 | 0 | } |
540 | | |
541 | 0 | if (m_parent_dataset->GetGeoTransform(gt) != CE_None) |
542 | 0 | return nullptr; |
543 | | |
544 | 0 | if (!GDALInvGeoTransform(gt.data(), invGT.data())) |
545 | 0 | return nullptr; |
546 | | |
547 | 0 | iPixel = static_cast<int>( |
548 | 0 | floor(invGT[0] + invGT[1] * dfGeoX + invGT[2] * dfGeoY)); |
549 | 0 | iLine = static_cast<int>( |
550 | 0 | floor(invGT[3] + invGT[4] * dfGeoX + invGT[5] * dfGeoY)); |
551 | | |
552 | | /* The GetDataset() for the WMS driver is always the main overview |
553 | | * level, so rescale */ |
554 | | /* the values if we are an overview */ |
555 | 0 | if (m_overview >= 0) |
556 | 0 | { |
557 | 0 | iPixel = static_cast<int>( |
558 | 0 | 1.0 * iPixel * GetXSize() / |
559 | 0 | m_parent_dataset->GetRasterBand(1)->GetXSize()); |
560 | 0 | iLine = static_cast<int>( |
561 | 0 | 1.0 * iLine * GetYSize() / |
562 | 0 | m_parent_dataset->GetRasterBand(1)->GetYSize()); |
563 | 0 | } |
564 | 0 | } |
565 | 0 | else |
566 | 0 | return nullptr; |
567 | | |
568 | 0 | if (iPixel < 0 || iLine < 0 || iPixel >= GetXSize() || iLine >= GetYSize()) |
569 | 0 | return nullptr; |
570 | | |
571 | 0 | if (nBand != 1) |
572 | 0 | { |
573 | 0 | GDALRasterBand *poFirstBand = m_parent_dataset->GetRasterBand(1); |
574 | 0 | if (m_overview >= 0) |
575 | 0 | poFirstBand = poFirstBand->GetOverview(m_overview); |
576 | 0 | if (poFirstBand) |
577 | 0 | return poFirstBand->GetMetadataItem(pszName, pszDomain); |
578 | 0 | } |
579 | | |
580 | 0 | GDALWMSImageRequestInfo iri; |
581 | 0 | GDALWMSTiledImageRequestInfo tiri; |
582 | 0 | int nBlockXOff = iPixel / nBlockXSize; |
583 | 0 | int nBlockYOff = iLine / nBlockYSize; |
584 | |
|
585 | 0 | ComputeRequestInfo(iri, tiri, nBlockXOff, nBlockYOff); |
586 | |
|
587 | 0 | CPLString url; |
588 | 0 | m_parent_dataset->m_mini_driver->GetTiledImageInfo( |
589 | 0 | url, iri, tiri, iPixel % nBlockXSize, iLine % nBlockXSize); |
590 | |
|
591 | 0 | if (url.empty()) |
592 | 0 | return nullptr; |
593 | | |
594 | 0 | CPLDebug("WMS", "URL = %s", url.c_str()); |
595 | |
|
596 | 0 | if (url == osMetadataItemURL) |
597 | 0 | { |
598 | | // osMetadataItem.c_str() MUST be used, and not osMetadataItem, |
599 | | // otherwise a temporary copy is returned |
600 | 0 | return !osMetadataItem.empty() ? osMetadataItem.c_str() : nullptr; |
601 | 0 | } |
602 | | |
603 | 0 | osMetadataItemURL = url; |
604 | | |
605 | | // This is OK, CPLHTTPFetch does not touch the options |
606 | 0 | CSLConstList papszOptions = |
607 | 0 | const_cast<char **>(m_parent_dataset->GetHTTPRequestOpts()); |
608 | 0 | CPLHTTPResult *psResult = CPLHTTPFetch(url, papszOptions); |
609 | |
|
610 | 0 | CPLString pszRes; |
611 | |
|
612 | 0 | if (psResult && psResult->pabyData) |
613 | 0 | pszRes = reinterpret_cast<const char *>(psResult->pabyData); |
614 | 0 | CPLHTTPDestroyResult(psResult); |
615 | |
|
616 | 0 | if (pszRes.empty()) |
617 | 0 | { |
618 | 0 | osMetadataItem = ""; |
619 | 0 | return nullptr; |
620 | 0 | } |
621 | | |
622 | 0 | osMetadataItem = "<LocationInfo>"; |
623 | 0 | CPLPushErrorHandler(CPLQuietErrorHandler); |
624 | 0 | CPLXMLNode *psXML = CPLParseXMLString(pszRes); |
625 | 0 | CPLPopErrorHandler(); |
626 | 0 | if (psXML != nullptr && psXML->eType == CXT_Element) |
627 | 0 | { |
628 | 0 | if (strcmp(psXML->pszValue, "?xml") == 0) |
629 | 0 | { |
630 | 0 | if (psXML->psNext) |
631 | 0 | { |
632 | 0 | char *pszXML = CPLSerializeXMLTree(psXML->psNext); |
633 | 0 | osMetadataItem += pszXML; |
634 | 0 | CPLFree(pszXML); |
635 | 0 | } |
636 | 0 | } |
637 | 0 | else |
638 | 0 | { |
639 | 0 | osMetadataItem += pszRes; |
640 | 0 | } |
641 | 0 | } |
642 | 0 | else |
643 | 0 | { |
644 | 0 | char *pszEscapedXML = CPLEscapeString(pszRes, -1, CPLES_XML_BUT_QUOTES); |
645 | 0 | osMetadataItem += pszEscapedXML; |
646 | 0 | CPLFree(pszEscapedXML); |
647 | 0 | } |
648 | 0 | if (psXML != nullptr) |
649 | 0 | CPLDestroyXMLNode(psXML); |
650 | |
|
651 | 0 | osMetadataItem += "</LocationInfo>"; |
652 | | |
653 | | // osMetadataItem.c_str() MUST be used, and not osMetadataItem, |
654 | | // otherwise a temporary copy is returned |
655 | 0 | return osMetadataItem.c_str(); |
656 | 0 | } |
657 | | |
658 | | static const int *GetBandMapForExpand(int nSourceBands, int nWmsBands) |
659 | 236 | { |
660 | 236 | static const int bandmap1to1[] = {1}; |
661 | 236 | static const int bandmap2to1[] = {1}; |
662 | 236 | static const int bandmap3to1[] = {1}; |
663 | 236 | static const int bandmap4to1[] = {1}; |
664 | | |
665 | 236 | static const int bandmap1to2[] = {1, 0}; // 0 == full opaque alpha band |
666 | 236 | static const int bandmap2to2[] = {1, 2}; |
667 | 236 | static const int bandmap3to2[] = {1, 0}; |
668 | 236 | static const int bandmap4to2[] = {1, 4}; |
669 | | |
670 | 236 | static const int bandmap1to3[] = {1, 1, 1}; |
671 | 236 | static const int bandmap2to3[] = {1, 1, 1}; |
672 | 236 | static const int bandmap3to3[] = {1, 2, 3}; |
673 | 236 | static const int bandmap4to3[] = {1, 2, 3}; |
674 | | |
675 | 236 | static const int bandmap1to4[] = {1, 1, 1, 0}; |
676 | 236 | static const int bandmap2to4[] = {1, 1, 1, 2}; |
677 | 236 | static const int bandmap3to4[] = {1, 2, 3, 0}; |
678 | 236 | static const int bandmap4to4[] = {1, 2, 3, 4}; |
679 | | |
680 | 236 | static const int *const bandmap_selector[4][4] = { |
681 | 236 | {bandmap1to1, bandmap2to1, bandmap3to1, bandmap4to1}, |
682 | 236 | {bandmap1to2, bandmap2to2, bandmap3to2, bandmap4to2}, |
683 | 236 | {bandmap1to3, bandmap2to3, bandmap3to3, bandmap4to3}, |
684 | 236 | {bandmap1to4, bandmap2to4, bandmap3to4, bandmap4to4}, |
685 | 236 | }; |
686 | | |
687 | 236 | if (nSourceBands > 4 || nSourceBands < 1) |
688 | 0 | { |
689 | 0 | return nullptr; |
690 | 0 | } |
691 | 236 | if (nWmsBands > 4 || nWmsBands < 1) |
692 | 0 | { |
693 | 0 | return nullptr; |
694 | 0 | } |
695 | 236 | return bandmap_selector[nWmsBands - 1][nSourceBands - 1]; |
696 | 236 | } |
697 | | |
698 | | CPLErr GDALWMSRasterBand::ReadBlockFromDataset(GDALDataset *ds, int x, int y, |
699 | | int to_buffer_band, void *buffer, |
700 | | int advise_read) |
701 | 236 | { |
702 | 236 | CPLErr ret = CE_None; |
703 | 236 | GByte *color_table = nullptr; |
704 | 236 | int i; |
705 | | |
706 | | // CPLDebug("WMS", "ReadBlockFromDataset: to_buffer_band=%d, (x,y)=(%d, |
707 | | // %d)", to_buffer_band, x, y); |
708 | | |
709 | | /* expected size */ |
710 | 236 | const int esx = MIN(MAX(0, (x + 1) * nBlockXSize), nRasterXSize) - |
711 | 236 | MIN(MAX(0, x * nBlockXSize), nRasterXSize); |
712 | 236 | const int esy = MIN(MAX(0, (y + 1) * nBlockYSize), nRasterYSize) - |
713 | 236 | MIN(MAX(0, y * nBlockYSize), nRasterYSize); |
714 | | |
715 | 236 | int sx = ds->GetRasterXSize(); |
716 | 236 | int sy = ds->GetRasterYSize(); |
717 | | /* Allow bigger than expected so pre-tiled constant size images work on |
718 | | * corners */ |
719 | 236 | if ((sx > nBlockXSize) || (sy > nBlockYSize) || (sx < esx) || (sy < esy)) |
720 | 68 | { |
721 | 68 | CPLError(CE_Failure, CPLE_AppDefined, |
722 | 68 | "GDALWMS: Incorrect size %d x %d of downloaded block, " |
723 | 68 | "expected %d x %d, max %d x %d.", |
724 | 68 | sx, sy, esx, esy, nBlockXSize, nBlockYSize); |
725 | 68 | ret = CE_Failure; |
726 | 68 | } |
727 | | |
728 | 236 | int nDSRasterCount = ds->GetRasterCount(); |
729 | 236 | if (ret == CE_None) |
730 | 168 | { |
731 | 168 | if (nDSRasterCount != m_parent_dataset->nBands) |
732 | 40 | { |
733 | | /* Maybe its an image with color table */ |
734 | 40 | if ((eDataType == GDT_UInt8) && (ds->GetRasterCount() == 1)) |
735 | 0 | { |
736 | 0 | GDALRasterBand *rb = ds->GetRasterBand(1); |
737 | 0 | if (rb->GetRasterDataType() == GDT_UInt8) |
738 | 0 | { |
739 | 0 | GDALColorTable *ct = rb->GetColorTable(); |
740 | 0 | if (ct != nullptr) |
741 | 0 | { |
742 | 0 | if (!advise_read) |
743 | 0 | { |
744 | 0 | color_table = new GByte[256 * 4]; |
745 | 0 | const int count = |
746 | 0 | MIN(256, ct->GetColorEntryCount()); |
747 | 0 | for (i = 0; i < count; ++i) |
748 | 0 | { |
749 | 0 | GDALColorEntry ce; |
750 | 0 | ct->GetColorEntryAsRGB(i, &ce); |
751 | 0 | color_table[i] = static_cast<GByte>(ce.c1); |
752 | 0 | color_table[i + 256] = |
753 | 0 | static_cast<GByte>(ce.c2); |
754 | 0 | color_table[i + 512] = |
755 | 0 | static_cast<GByte>(ce.c3); |
756 | 0 | color_table[i + 768] = |
757 | 0 | static_cast<GByte>(ce.c4); |
758 | 0 | } |
759 | |
|
760 | 0 | for (i = count; i < 256; ++i) |
761 | 0 | { |
762 | 0 | color_table[i] = 0; |
763 | 0 | color_table[i + 256] = 0; |
764 | 0 | color_table[i + 512] = 0; |
765 | 0 | color_table[i + 768] = 0; |
766 | 0 | } |
767 | 0 | } |
768 | 0 | } |
769 | 0 | else if (m_parent_dataset->nBands <= 4) |
770 | 0 | { // Promote single band to fake color table |
771 | 0 | color_table = new GByte[256 * 4]; |
772 | 0 | for (i = 0; i < 256; i++) |
773 | 0 | { |
774 | 0 | color_table[i] = static_cast<GByte>(i); |
775 | 0 | color_table[i + 256] = static_cast<GByte>(i); |
776 | 0 | color_table[i + 512] = static_cast<GByte>(i); |
777 | 0 | color_table[i + 768] = 255; // Transparency |
778 | 0 | } |
779 | 0 | if (m_parent_dataset->nBands == 2) |
780 | 0 | { // Luma-Alpha fixup |
781 | 0 | for (i = 0; i < 256; i++) |
782 | 0 | { |
783 | 0 | color_table[i + 256] = 255; |
784 | 0 | } |
785 | 0 | } |
786 | 0 | } |
787 | 0 | } |
788 | 0 | } |
789 | 40 | } |
790 | 168 | } |
791 | | |
792 | 236 | if (!advise_read) |
793 | 236 | { |
794 | 236 | const int *const bandmap = |
795 | 236 | GetBandMapForExpand(nDSRasterCount, m_parent_dataset->nBands); |
796 | 1.18k | for (int ib = 1; ib <= m_parent_dataset->nBands; ++ib) |
797 | 944 | { |
798 | 944 | if (ret == CE_None) |
799 | 672 | { |
800 | 672 | void *p = nullptr; |
801 | 672 | GDALRasterBlock *b = nullptr; |
802 | 672 | if ((buffer != nullptr) && (ib == to_buffer_band)) |
803 | 42 | { |
804 | 42 | p = buffer; |
805 | 42 | } |
806 | 630 | else |
807 | 630 | { |
808 | 630 | GDALWMSRasterBand *band = static_cast<GDALWMSRasterBand *>( |
809 | 630 | m_parent_dataset->GetRasterBand(ib)); |
810 | 630 | if (m_overview >= 0) |
811 | 0 | { |
812 | 0 | band = static_cast<GDALWMSRasterBand *>( |
813 | 0 | band->GetOverview(m_overview)); |
814 | 0 | } |
815 | 630 | if (!band->IsBlockInCache(x, y)) |
816 | 630 | { |
817 | 630 | b = band->GetLockedBlockRef(x, y, true); |
818 | 630 | if (b != nullptr) |
819 | 630 | { |
820 | 630 | p = b->GetDataRef(); |
821 | 630 | if (p == nullptr) |
822 | 0 | { |
823 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
824 | 0 | "GDALWMS: GetDataRef returned NULL."); |
825 | 0 | ret = CE_Failure; |
826 | 0 | } |
827 | 630 | } |
828 | 630 | } |
829 | 0 | else |
830 | 0 | { |
831 | | // CPLDebug("WMS", "Band %d, block (x,y)=(%d, %d) |
832 | | // already in cache", band->GetBand(), x, y); |
833 | 0 | } |
834 | 630 | } |
835 | | |
836 | 672 | if (p != nullptr) |
837 | 672 | { |
838 | 672 | int pixel_space = GDALGetDataTypeSizeBytes(eDataType); |
839 | 672 | int line_space = pixel_space * nBlockXSize; |
840 | 672 | if (color_table == nullptr) |
841 | 672 | { |
842 | 672 | if (bandmap == nullptr || bandmap[ib - 1] != 0) |
843 | 672 | { |
844 | 672 | GDALDataType dt = eDataType; |
845 | 672 | int nSourceBand = ib; |
846 | 672 | if (bandmap != nullptr) |
847 | 672 | { |
848 | 672 | nSourceBand = bandmap[ib - 1]; |
849 | 672 | } |
850 | | // Get the data from the PNG as stored instead of |
851 | | // converting, if the server asks for that |
852 | | // TODO: This hack is from #3493 - not sure it |
853 | | // really belongs here. |
854 | 672 | if ((GDT_Int16 == dt) && |
855 | 0 | (GDT_UInt16 == |
856 | 0 | ds->GetRasterBand(ib)->GetRasterDataType())) |
857 | 0 | { |
858 | 0 | dt = GDT_UInt16; |
859 | 0 | } |
860 | | |
861 | 672 | if (ds->RasterIO(GF_Read, 0, 0, sx, sy, p, sx, sy, |
862 | 672 | dt, 1, &nSourceBand, pixel_space, |
863 | 672 | line_space, 0, nullptr) != CE_None) |
864 | 0 | { |
865 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
866 | 0 | "GDALWMS: RasterIO failed on " |
867 | 0 | "downloaded block."); |
868 | 0 | ret = CE_Failure; |
869 | 0 | } |
870 | 672 | } |
871 | 0 | else // if( bandmap != nullptr && bandmap[ib - 1] == 0 |
872 | | // ) |
873 | 0 | { // parent expects 4 bands but file has fewer count so |
874 | | // generate a all "opaque" 4th band |
875 | 0 | GByte *byte_buffer = reinterpret_cast<GByte *>(p); |
876 | 0 | for (int l_y = 0; l_y < sy; ++l_y) |
877 | 0 | { |
878 | 0 | for (int l_x = 0; l_x < sx; ++l_x) |
879 | 0 | { |
880 | 0 | const int offset = l_x + l_y * line_space; |
881 | 0 | byte_buffer[offset] = |
882 | 0 | 255; // fill with opaque |
883 | 0 | } |
884 | 0 | } |
885 | 0 | } |
886 | 672 | } |
887 | 0 | else if (ib <= 4) |
888 | 0 | { |
889 | 0 | if (ds->RasterIO(GF_Read, 0, 0, sx, sy, p, sx, sy, |
890 | 0 | eDataType, 1, nullptr, pixel_space, |
891 | 0 | line_space, 0, nullptr) != CE_None) |
892 | 0 | { |
893 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
894 | 0 | "GDALWMS: RasterIO failed on downloaded " |
895 | 0 | "block."); |
896 | 0 | ret = CE_Failure; |
897 | 0 | } |
898 | |
|
899 | 0 | if (ret == CE_None) |
900 | 0 | { |
901 | 0 | GByte *band_color_table = |
902 | 0 | color_table + 256 * (ib - 1); |
903 | 0 | GByte *byte_buffer = reinterpret_cast<GByte *>(p); |
904 | 0 | for (int l_y = 0; l_y < sy; ++l_y) |
905 | 0 | { |
906 | 0 | for (int l_x = 0; l_x < sx; ++l_x) |
907 | 0 | { |
908 | 0 | const int offset = l_x + l_y * line_space; |
909 | 0 | byte_buffer[offset] = |
910 | 0 | band_color_table[byte_buffer[offset]]; |
911 | 0 | } |
912 | 0 | } |
913 | 0 | } |
914 | 0 | } |
915 | 0 | else |
916 | 0 | { |
917 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
918 | 0 | "GDALWMS: Color table supports at most 4 " |
919 | 0 | "components."); |
920 | 0 | ret = CE_Failure; |
921 | 0 | } |
922 | 672 | } |
923 | 672 | if (b != nullptr) |
924 | 630 | { |
925 | 630 | b->DropLock(); |
926 | 630 | } |
927 | 672 | } |
928 | 944 | } |
929 | 236 | } |
930 | 236 | GDALClose(ds); |
931 | | |
932 | 236 | if (color_table != nullptr) |
933 | 0 | { |
934 | 0 | delete[] color_table; |
935 | 0 | } |
936 | | |
937 | 236 | return ret; |
938 | 236 | } |
939 | | |
940 | | CPLErr GDALWMSRasterBand::ReadBlockFromFile(const CPLString &soFileName, int x, |
941 | | int y, int to_buffer_band, |
942 | | void *buffer, int advise_read) |
943 | 56 | { |
944 | 56 | GDALDataset *ds = GDALDataset::FromHandle(GDALOpenEx( |
945 | 56 | soFileName, GDAL_OF_RASTER | GDAL_OF_READONLY | GDAL_OF_VERBOSE_ERROR, |
946 | 56 | nullptr, m_parent_dataset->m_tileOO, nullptr)); |
947 | 56 | if (ds == nullptr) |
948 | 0 | { |
949 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
950 | 0 | "GDALWMS: Unable to open downloaded block."); |
951 | 0 | return CE_Failure; |
952 | 0 | } |
953 | | |
954 | 56 | return ReadBlockFromDataset(ds, x, y, to_buffer_band, buffer, advise_read); |
955 | 56 | } |
956 | | |
957 | | CPLErr GDALWMSRasterBand::ReadBlockFromCache(const char *pszKey, int x, int y, |
958 | | int to_buffer_band, void *buffer, |
959 | | int advise_read) |
960 | 268 | { |
961 | 268 | GDALWMSCache *cache = m_parent_dataset->m_cache; |
962 | 268 | if (nullptr == cache) |
963 | 0 | { |
964 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
965 | 0 | "GDALWMS: Unable to open downloaded block."); |
966 | 0 | return CE_Failure; |
967 | 0 | } |
968 | 268 | GDALDataset *ds = cache->GetDataset(pszKey, m_parent_dataset->m_tileOO); |
969 | 268 | if (ds == nullptr) |
970 | 88 | { |
971 | 88 | CPLError(CE_Failure, CPLE_AppDefined, |
972 | 88 | "GDALWMS: Unable to open downloaded block."); |
973 | 88 | return CE_Failure; |
974 | 88 | } |
975 | | |
976 | 180 | return ReadBlockFromDataset(ds, x, y, to_buffer_band, buffer, advise_read); |
977 | 268 | } |
978 | | |
979 | | CPLErr GDALWMSRasterBand::EmptyBlock(int x, int y, int to_buffer_band, |
980 | | void *buffer) |
981 | 0 | { |
982 | 0 | CPLErr ret = CE_None; |
983 | |
|
984 | 0 | for (int ib = 1; ib <= m_parent_dataset->nBands; ++ib) |
985 | 0 | { |
986 | 0 | if (ret == CE_None) |
987 | 0 | { |
988 | 0 | void *p = nullptr; |
989 | 0 | GDALRasterBlock *b = nullptr; |
990 | 0 | GDALWMSRasterBand *band = static_cast<GDALWMSRasterBand *>( |
991 | 0 | m_parent_dataset->GetRasterBand(ib)); |
992 | 0 | if (m_overview >= 0) |
993 | 0 | band = static_cast<GDALWMSRasterBand *>( |
994 | 0 | band->GetOverview(m_overview)); |
995 | 0 | if ((buffer != nullptr) && (ib == to_buffer_band)) |
996 | 0 | { |
997 | 0 | p = buffer; |
998 | 0 | } |
999 | 0 | else |
1000 | 0 | { |
1001 | 0 | if (!band->IsBlockInCache(x, y)) |
1002 | 0 | { |
1003 | 0 | b = band->GetLockedBlockRef(x, y, true); |
1004 | 0 | if (b != nullptr) |
1005 | 0 | { |
1006 | 0 | p = b->GetDataRef(); |
1007 | 0 | if (p == nullptr) |
1008 | 0 | { |
1009 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1010 | 0 | "GDALWMS: GetDataRef returned NULL."); |
1011 | 0 | ret = CE_Failure; |
1012 | 0 | } |
1013 | 0 | } |
1014 | 0 | } |
1015 | 0 | } |
1016 | 0 | if (p != nullptr) |
1017 | 0 | { |
1018 | 0 | int hasNDV; |
1019 | 0 | double valNDV = band->GetNoDataValue(&hasNDV); |
1020 | 0 | if (!hasNDV) |
1021 | 0 | valNDV = 0; |
1022 | 0 | GDALCopyWords(&valNDV, GDT_Float64, 0, p, eDataType, |
1023 | 0 | GDALGetDataTypeSizeBytes(eDataType), |
1024 | 0 | nBlockXSize * nBlockYSize); |
1025 | 0 | } |
1026 | 0 | if (b != nullptr) |
1027 | 0 | { |
1028 | 0 | b->DropLock(); |
1029 | 0 | } |
1030 | 0 | } |
1031 | 0 | } |
1032 | |
|
1033 | 0 | return ret; |
1034 | 0 | } |
1035 | | |
1036 | | CPLErr GDALWMSRasterBand::ReportWMSException(const char *file_name) |
1037 | 0 | { |
1038 | 0 | CPLErr ret = CE_None; |
1039 | 0 | int reported_errors_count = 0; |
1040 | |
|
1041 | 0 | CPLXMLNode *orig_root = CPLParseXMLFile(file_name); |
1042 | 0 | CPLXMLNode *root = orig_root; |
1043 | 0 | if (root != nullptr) |
1044 | 0 | { |
1045 | 0 | root = CPLGetXMLNode(root, "=ServiceExceptionReport"); |
1046 | 0 | } |
1047 | 0 | if (root != nullptr) |
1048 | 0 | { |
1049 | 0 | CPLXMLNode *n = CPLGetXMLNode(root, "ServiceException"); |
1050 | 0 | while (n != nullptr) |
1051 | 0 | { |
1052 | 0 | const char *exception = CPLGetXMLValue(n, "=ServiceException", ""); |
1053 | 0 | const char *exception_code = |
1054 | 0 | CPLGetXMLValue(n, "=ServiceException.code", ""); |
1055 | 0 | if (exception[0] != '\0') |
1056 | 0 | { |
1057 | 0 | if (exception_code[0] != '\0') |
1058 | 0 | { |
1059 | 0 | CPLError( |
1060 | 0 | CE_Failure, CPLE_AppDefined, |
1061 | 0 | "GDALWMS: The server returned exception code '%s': %s", |
1062 | 0 | exception_code, exception); |
1063 | 0 | ++reported_errors_count; |
1064 | 0 | } |
1065 | 0 | else |
1066 | 0 | { |
1067 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1068 | 0 | "GDALWMS: The server returned exception: %s", |
1069 | 0 | exception); |
1070 | 0 | ++reported_errors_count; |
1071 | 0 | } |
1072 | 0 | } |
1073 | 0 | else if (exception_code[0] != '\0') |
1074 | 0 | { |
1075 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1076 | 0 | "GDALWMS: The server returned exception code '%s'.", |
1077 | 0 | exception_code); |
1078 | 0 | ++reported_errors_count; |
1079 | 0 | } |
1080 | |
|
1081 | 0 | n = n->psNext; |
1082 | 0 | if (n != nullptr) |
1083 | 0 | { |
1084 | 0 | n = CPLGetXMLNode(n, "=ServiceException"); |
1085 | 0 | } |
1086 | 0 | } |
1087 | 0 | } |
1088 | 0 | else |
1089 | 0 | { |
1090 | 0 | ret = CE_Failure; |
1091 | 0 | } |
1092 | 0 | if (orig_root != nullptr) |
1093 | 0 | { |
1094 | 0 | CPLDestroyXMLNode(orig_root); |
1095 | 0 | } |
1096 | |
|
1097 | 0 | if (reported_errors_count == 0) |
1098 | 0 | { |
1099 | 0 | ret = CE_Failure; |
1100 | 0 | } |
1101 | |
|
1102 | 0 | return ret; |
1103 | 0 | } |
1104 | | |
1105 | | CPLErr GDALWMSRasterBand::AdviseRead(int nXOff, int nYOff, int nXSize, |
1106 | | int nYSize, int nBufXSize, int nBufYSize, |
1107 | | GDALDataType eDT, |
1108 | | CSLConstList papszOptions) |
1109 | 0 | { |
1110 | | // printf("AdviseRead(%d, %d, %d, %d)\n", nXOff, nYOff, nXSize, nYSize); |
1111 | 0 | if (m_parent_dataset->m_offline_mode || |
1112 | 0 | !m_parent_dataset->m_use_advise_read) |
1113 | 0 | return CE_None; |
1114 | 0 | if (m_parent_dataset->m_cache == nullptr) |
1115 | 0 | return CE_Failure; |
1116 | | |
1117 | | /* ==================================================================== */ |
1118 | | /* Do we have overviews that would be appropriate to satisfy */ |
1119 | | /* this request? */ |
1120 | | /* ==================================================================== */ |
1121 | 0 | if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0) |
1122 | 0 | { |
1123 | 0 | const int nOverview = GDALBandGetBestOverviewLevel2( |
1124 | 0 | this, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, nullptr); |
1125 | 0 | if (nOverview >= 0) |
1126 | 0 | { |
1127 | 0 | GDALRasterBand *poOverviewBand = GetOverview(nOverview); |
1128 | 0 | if (poOverviewBand == nullptr) |
1129 | 0 | return CE_Failure; |
1130 | | |
1131 | 0 | return poOverviewBand->AdviseRead(nXOff, nYOff, nXSize, nYSize, |
1132 | 0 | nBufXSize, nBufYSize, eDT, |
1133 | 0 | papszOptions); |
1134 | 0 | } |
1135 | 0 | } |
1136 | | |
1137 | 0 | int bx0 = nXOff / nBlockXSize; |
1138 | 0 | int by0 = nYOff / nBlockYSize; |
1139 | 0 | int bx1 = (nXOff + nXSize - 1) / nBlockXSize; |
1140 | 0 | int by1 = (nYOff + nYSize - 1) / nBlockYSize; |
1141 | | |
1142 | | // Avoid downloading a insane number of tiles |
1143 | 0 | const int MAX_TILES = 1000; // arbitrary number |
1144 | 0 | if ((bx1 - bx0 + 1) > MAX_TILES / (by1 - by0 + 1)) |
1145 | 0 | { |
1146 | 0 | CPLDebug("WMS", "Too many tiles for AdviseRead()"); |
1147 | 0 | return CE_Failure; |
1148 | 0 | } |
1149 | | |
1150 | 0 | if (m_nAdviseReadBX0 == bx0 && m_nAdviseReadBY0 == by0 && |
1151 | 0 | m_nAdviseReadBX1 == bx1 && m_nAdviseReadBY1 == by1) |
1152 | 0 | { |
1153 | 0 | return CE_None; |
1154 | 0 | } |
1155 | 0 | m_nAdviseReadBX0 = bx0; |
1156 | 0 | m_nAdviseReadBY0 = by0; |
1157 | 0 | m_nAdviseReadBX1 = bx1; |
1158 | 0 | m_nAdviseReadBY1 = by1; |
1159 | |
|
1160 | 0 | return ReadBlocks(0, 0, nullptr, bx0, by0, bx1, by1, 1); |
1161 | 0 | } |
1162 | | |
1163 | | GDALColorInterp GDALWMSRasterBand::GetColorInterpretation() |
1164 | 0 | { |
1165 | 0 | return m_color_interp; |
1166 | 0 | } |
1167 | | |
1168 | | CPLErr GDALWMSRasterBand::SetColorInterpretation(GDALColorInterp eNewInterp) |
1169 | 0 | { |
1170 | 0 | m_color_interp = eNewInterp; |
1171 | 0 | return CE_None; |
1172 | 0 | } |
1173 | | |
1174 | | // Utility function, returns a value from a vector corresponding to the band |
1175 | | // index or the first entry |
1176 | | static double getBandValue(const std::vector<double> &v, size_t idx) |
1177 | 0 | { |
1178 | 0 | idx--; |
1179 | 0 | if (v.size() > idx) |
1180 | 0 | return v[idx]; |
1181 | 0 | return v[0]; |
1182 | 0 | } |
1183 | | |
1184 | | double GDALWMSRasterBand::GetNoDataValue(int *pbSuccess) |
1185 | 0 | { |
1186 | 0 | std::vector<double> &v = m_parent_dataset->vNoData; |
1187 | 0 | if (v.empty()) |
1188 | 0 | return GDALPamRasterBand::GetNoDataValue(pbSuccess); |
1189 | 0 | if (pbSuccess) |
1190 | 0 | *pbSuccess = TRUE; |
1191 | 0 | return getBandValue(v, nBand); |
1192 | 0 | } |
1193 | | |
1194 | | double GDALWMSRasterBand::GetMinimum(int *pbSuccess) |
1195 | 0 | { |
1196 | 0 | std::vector<double> &v = m_parent_dataset->vMin; |
1197 | 0 | if (v.empty()) |
1198 | 0 | return GDALPamRasterBand::GetMinimum(pbSuccess); |
1199 | 0 | if (pbSuccess) |
1200 | 0 | *pbSuccess = TRUE; |
1201 | 0 | return getBandValue(v, nBand); |
1202 | 0 | } |
1203 | | |
1204 | | double GDALWMSRasterBand::GetMaximum(int *pbSuccess) |
1205 | 0 | { |
1206 | 0 | std::vector<double> &v = m_parent_dataset->vMax; |
1207 | 0 | if (v.empty()) |
1208 | 0 | return GDALPamRasterBand::GetMaximum(pbSuccess); |
1209 | 0 | if (pbSuccess) |
1210 | 0 | *pbSuccess = TRUE; |
1211 | 0 | return getBandValue(v, nBand); |
1212 | 0 | } |
1213 | | |
1214 | | GDALColorTable *GDALWMSRasterBand::GetColorTable() |
1215 | 0 | { |
1216 | 0 | return m_parent_dataset->m_poColorTable; |
1217 | 0 | } |