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