Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements Open FileGDB raster driver.
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "cpl_conv.h"
15 : #include "cpl_minixml.h"
16 :
17 : #include "ogr_openfilegdb.h"
18 :
19 : #include "gdal_rat.h"
20 : #include "filegdbtable_priv.h"
21 :
22 : #include <algorithm>
23 : #include <cassert>
24 : #include <cmath>
25 : #include <limits>
26 : #include <new>
27 : #include <utility>
28 :
29 : using namespace OpenFileGDB;
30 :
31 : /************************************************************************/
32 : /* OpenRaster() */
33 : /************************************************************************/
34 :
35 29 : bool OGROpenFileGDBDataSource::OpenRaster(const GDALOpenInfo *poOpenInfo,
36 : const std::string &osLayerName,
37 : const std::string &osDefinition,
38 : const std::string &osDocumentation)
39 : {
40 29 : m_osRasterLayerName = osLayerName;
41 :
42 : const std::string osBndTableName(
43 87 : std::string("fras_bnd_").append(osLayerName).c_str());
44 29 : const auto oIter = m_osMapNameToIdx.find(osBndTableName);
45 29 : if (oIter == m_osMapNameToIdx.end())
46 : {
47 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
48 : osBndTableName.c_str());
49 0 : return false;
50 : }
51 29 : const int nBndIdx = oIter->second;
52 :
53 58 : FileGDBTable oTable;
54 :
55 : const std::string osBndFilename(CPLFormFilenameSafe(
56 58 : m_osDirName, CPLSPrintf("a%08x.gdbtable", nBndIdx), nullptr));
57 29 : if (!oTable.Open(osBndFilename.c_str(), false))
58 : {
59 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open table %s",
60 : osBndTableName.c_str());
61 0 : return false;
62 : }
63 :
64 29 : const int i_rasterband_id = oTable.GetFieldIdx("rasterband_id");
65 29 : const int i_sequence_nbr = oTable.GetFieldIdx("sequence_nbr");
66 29 : const int i_raster_id = oTable.GetFieldIdx("raster_id");
67 29 : const int i_band_width = oTable.GetFieldIdx("band_width");
68 29 : const int i_band_height = oTable.GetFieldIdx("band_height");
69 29 : const int i_band_types = oTable.GetFieldIdx("band_types");
70 29 : const int i_block_width = oTable.GetFieldIdx("block_width");
71 29 : const int i_block_height = oTable.GetFieldIdx("block_height");
72 29 : const int i_block_origin_x = oTable.GetFieldIdx("block_origin_x");
73 29 : const int i_block_origin_y = oTable.GetFieldIdx("block_origin_y");
74 29 : const int i_eminx = oTable.GetFieldIdx("eminx");
75 29 : const int i_eminy = oTable.GetFieldIdx("eminy");
76 29 : const int i_emaxx = oTable.GetFieldIdx("emaxx");
77 29 : const int i_emaxy = oTable.GetFieldIdx("emaxy");
78 29 : const int i_srid = oTable.GetFieldIdx("srid");
79 29 : if (i_rasterband_id < 0 || i_sequence_nbr < 0 || i_raster_id < 0 ||
80 29 : i_band_width < 0 || i_band_height < 0 || i_band_types < 0 ||
81 29 : i_block_width < 0 || i_block_height < 0 || i_block_origin_x < 0 ||
82 29 : i_block_origin_y < 0 || i_eminx < 0 || i_eminy < 0 || i_emaxx < 0 ||
83 58 : i_emaxy < 0 || i_srid < 0 ||
84 58 : oTable.GetField(i_rasterband_id)->GetType() != FGFT_OBJECTID ||
85 58 : oTable.GetField(i_sequence_nbr)->GetType() != FGFT_INT32 ||
86 58 : oTable.GetField(i_raster_id)->GetType() != FGFT_INT32 ||
87 58 : oTable.GetField(i_band_width)->GetType() != FGFT_INT32 ||
88 58 : oTable.GetField(i_band_height)->GetType() != FGFT_INT32 ||
89 58 : oTable.GetField(i_band_types)->GetType() != FGFT_INT32 ||
90 58 : oTable.GetField(i_block_width)->GetType() != FGFT_INT32 ||
91 58 : oTable.GetField(i_block_height)->GetType() != FGFT_INT32 ||
92 58 : oTable.GetField(i_block_origin_x)->GetType() != FGFT_FLOAT64 ||
93 58 : oTable.GetField(i_block_origin_y)->GetType() != FGFT_FLOAT64 ||
94 58 : oTable.GetField(i_eminx)->GetType() != FGFT_FLOAT64 ||
95 58 : oTable.GetField(i_eminy)->GetType() != FGFT_FLOAT64 ||
96 58 : oTable.GetField(i_emaxx)->GetType() != FGFT_FLOAT64 ||
97 87 : oTable.GetField(i_emaxy)->GetType() != FGFT_FLOAT64 ||
98 29 : oTable.GetField(i_srid)->GetType() != FGFT_INT32)
99 : {
100 0 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong structure for %s table",
101 : osBndTableName.c_str());
102 0 : return false;
103 : }
104 :
105 29 : int64_t iRow = 0;
106 111 : while (iRow < oTable.GetTotalRecordCount() &&
107 41 : (iRow = oTable.GetAndSelectNextNonEmptyRow(iRow)) >= 0)
108 : {
109 41 : if (iRow >= INT32_MAX)
110 : {
111 0 : return false;
112 : }
113 41 : auto psField = oTable.GetFieldValue(i_raster_id);
114 41 : if (!psField)
115 : {
116 0 : CPLError(CE_Failure, CPLE_AppDefined,
117 : "Cannot read field %s in %s table", "raster_id",
118 : osBndTableName.c_str());
119 0 : return false;
120 : }
121 41 : if (psField->Integer != 1)
122 : {
123 0 : CPLError(CE_Warning, CPLE_AppDefined,
124 : "Raster with raster_id = %d (!= 1) ignored",
125 0 : psField->Integer);
126 0 : continue;
127 : }
128 :
129 41 : const int nGDBRasterBandId = static_cast<int>(iRow) + 1;
130 :
131 41 : psField = oTable.GetFieldValue(i_sequence_nbr);
132 41 : if (!psField)
133 : {
134 0 : CPLError(CE_Failure, CPLE_AppDefined,
135 : "Cannot read field %s in %s table", "sequence_nbr",
136 : osBndTableName.c_str());
137 0 : return false;
138 : }
139 41 : const int nSequenceNr = psField->Integer;
140 :
141 41 : m_oMapGDALBandToGDBBandId[nSequenceNr] = nGDBRasterBandId;
142 :
143 41 : ++iRow;
144 : }
145 :
146 29 : if (m_oMapGDALBandToGDBBandId.empty())
147 : {
148 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot read record in %s table",
149 : osBndTableName.c_str());
150 0 : return false;
151 : }
152 :
153 29 : auto psField = oTable.GetFieldValue(i_band_width);
154 29 : if (!psField)
155 : {
156 0 : CPLError(CE_Failure, CPLE_AppDefined,
157 : "Cannot read field %s in %s table", "band_width",
158 : osBndTableName.c_str());
159 0 : return false;
160 : }
161 29 : int nWidth = psField->Integer;
162 :
163 29 : psField = oTable.GetFieldValue(i_band_height);
164 29 : if (!psField)
165 : {
166 0 : CPLError(CE_Failure, CPLE_AppDefined,
167 : "Cannot read field %s in %s table", "band_height",
168 : osBndTableName.c_str());
169 0 : return false;
170 : }
171 29 : int nHeight = psField->Integer;
172 :
173 29 : const int l_nBands = static_cast<int>(m_oMapGDALBandToGDBBandId.size());
174 58 : if (!GDALCheckDatasetDimensions(nWidth, nHeight) ||
175 29 : !GDALCheckBandCount(l_nBands, /*bIsZeroAllowed=*/false))
176 : {
177 0 : return false;
178 : }
179 :
180 29 : psField = oTable.GetFieldValue(i_block_width);
181 29 : if (!psField)
182 : {
183 0 : CPLError(CE_Failure, CPLE_AppDefined,
184 : "Cannot read field %s in %s table", "block_width",
185 : osBndTableName.c_str());
186 0 : return false;
187 : }
188 29 : const int nBlockWidth = psField->Integer;
189 :
190 : // 32768 somewhat arbitrary
191 29 : if (nBlockWidth <= 0 || nBlockWidth > 32768)
192 : {
193 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s in %s table",
194 : "block_width", osBndTableName.c_str());
195 0 : return false;
196 : }
197 :
198 29 : psField = oTable.GetFieldValue(i_block_height);
199 29 : if (!psField)
200 : {
201 0 : CPLError(CE_Failure, CPLE_AppDefined,
202 : "Cannot read field %s in %s table", "block_height",
203 : osBndTableName.c_str());
204 0 : return false;
205 : }
206 29 : const int nBlockHeight = psField->Integer;
207 :
208 : // 32768 somewhat arbitrary
209 29 : if (nBlockHeight <= 0 || nBlockHeight > 32768)
210 : {
211 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s in %s table",
212 : "block_height", osBndTableName.c_str());
213 0 : return false;
214 : }
215 :
216 29 : psField = oTable.GetFieldValue(i_band_types);
217 29 : if (!psField)
218 : {
219 0 : CPLError(CE_Failure, CPLE_AppDefined,
220 : "Cannot read field %s in %s table", "band_types",
221 : osBndTableName.c_str());
222 0 : return false;
223 : }
224 29 : const int nBandTypes = psField->Integer;
225 :
226 29 : psField = oTable.GetFieldValue(i_eminx);
227 29 : if (!psField)
228 : {
229 0 : CPLError(CE_Failure, CPLE_AppDefined,
230 : "Cannot read field %s in %s table", "eminx",
231 : osBndTableName.c_str());
232 0 : return false;
233 : }
234 29 : const double dfMinX = psField->Real;
235 :
236 29 : psField = oTable.GetFieldValue(i_eminy);
237 29 : if (!psField)
238 : {
239 0 : CPLError(CE_Failure, CPLE_AppDefined,
240 : "Cannot read field %s in %s table", "eminy",
241 : osBndTableName.c_str());
242 0 : return false;
243 : }
244 29 : const double dfMinY = psField->Real;
245 :
246 29 : psField = oTable.GetFieldValue(i_emaxx);
247 29 : if (!psField)
248 : {
249 0 : CPLError(CE_Failure, CPLE_AppDefined,
250 : "Cannot read field %s in %s table", "emaxx",
251 : osBndTableName.c_str());
252 0 : return false;
253 : }
254 29 : const double dfMaxX = psField->Real;
255 :
256 29 : psField = oTable.GetFieldValue(i_emaxy);
257 29 : if (!psField)
258 : {
259 0 : CPLError(CE_Failure, CPLE_AppDefined,
260 : "Cannot read field %s in %s table", "emaxy",
261 : osBndTableName.c_str());
262 0 : return false;
263 : }
264 29 : const double dfMaxY = psField->Real;
265 :
266 29 : psField = oTable.GetFieldValue(i_block_origin_x);
267 29 : if (!psField)
268 : {
269 0 : CPLError(CE_Failure, CPLE_AppDefined,
270 : "Cannot read field %s in %s table", "block_origin_x",
271 : osBndTableName.c_str());
272 0 : return false;
273 : }
274 29 : const double dfBlockOriginX = psField->Real;
275 :
276 29 : psField = oTable.GetFieldValue(i_block_origin_y);
277 29 : if (!psField)
278 : {
279 0 : CPLError(CE_Failure, CPLE_AppDefined,
280 : "Cannot read field %s in %s table", "block_origin_y",
281 : osBndTableName.c_str());
282 0 : return false;
283 : }
284 29 : const double dfBlockOriginY = psField->Real;
285 :
286 : // Figure out data type
287 29 : GDALDataType eDT = GDT_UInt8;
288 29 : const int nBitWidth = (nBandTypes >> 19) & ((1 << 7) - 1);
289 29 : const int nBitType = (nBandTypes >> 16) & ((1 << 2) - 1);
290 29 : constexpr int IS_UNSIGNED = 0;
291 29 : constexpr int IS_SIGNED = 1;
292 29 : constexpr int IS_FLOATING_POINT = 2;
293 29 : if ((nBitWidth >= 1 && nBitWidth < 8) && nBitType == IS_UNSIGNED)
294 : {
295 2 : eDT = GDT_UInt8;
296 : }
297 27 : else if (nBitWidth == 8 && nBitType <= IS_SIGNED)
298 : {
299 13 : eDT = nBitType == IS_SIGNED ? GDT_Int8 : GDT_UInt8;
300 : }
301 14 : else if (nBitWidth == 16 && nBitType <= IS_SIGNED)
302 : {
303 4 : eDT = nBitType == IS_SIGNED ? GDT_Int16 : GDT_UInt16;
304 : }
305 10 : else if (nBitWidth == 32 && nBitType <= IS_FLOATING_POINT)
306 : {
307 13 : eDT = nBitType == IS_FLOATING_POINT ? GDT_Float32
308 4 : : nBitType == IS_SIGNED ? GDT_Int32
309 : : GDT_UInt32;
310 : }
311 1 : else if (nBitWidth == 64 && nBitType == 0)
312 : {
313 1 : eDT = GDT_Float64;
314 : }
315 : else
316 : {
317 0 : CPLError(CE_Failure, CPLE_AppDefined,
318 : "Unhandled nBitWidth=%d, nBitType=%d in %s table", nBitWidth,
319 : nBitType, osBndTableName.c_str());
320 0 : return false;
321 : }
322 :
323 : // To avoid potential integer overflows in IReadBlock()
324 58 : if (nBlockWidth * nBlockHeight >
325 29 : std::numeric_limits<int>::max() / nBitWidth)
326 : {
327 0 : CPLError(CE_Failure, CPLE_AppDefined,
328 : "Too large block %dx%d in %s table", nBlockWidth, nBlockHeight,
329 : osBndTableName.c_str());
330 0 : return false;
331 : }
332 :
333 : // Figure out compression
334 29 : const int nCompression = (nBandTypes >> 8) & 0xff;
335 29 : switch (nCompression)
336 : {
337 6 : case 0:
338 6 : m_eRasterCompression = Compression::NONE;
339 6 : break;
340 19 : case 4:
341 19 : m_eRasterCompression = Compression::LZ77;
342 19 : SetMetadataItem("COMPRESSION", "DEFLATE", "IMAGE_STRUCTURE");
343 19 : break;
344 2 : case 8:
345 2 : m_eRasterCompression = Compression::JPEG;
346 2 : SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
347 2 : break;
348 2 : case 12:
349 2 : m_eRasterCompression = Compression::JPEG2000;
350 2 : SetMetadataItem("COMPRESSION", "JPEG2000", "IMAGE_STRUCTURE");
351 2 : break;
352 0 : default:
353 : {
354 0 : CPLError(CE_Failure, CPLE_AppDefined,
355 : "Unhandled compression %d in %s table", nCompression,
356 : osBndTableName.c_str());
357 0 : return false;
358 : }
359 : }
360 :
361 : // Figure out geotransform
362 :
363 29 : if (!(dfMaxX > dfMinX && dfMaxY > dfMinY))
364 : {
365 0 : CPLError(CE_Failure, CPLE_AppDefined,
366 : "!(dfMaxX > dfMinX && dfMaxY > dfMinY)");
367 0 : return false;
368 : }
369 29 : else if (nWidth == 1 || nHeight == 1)
370 : {
371 0 : CPLError(CE_Warning, CPLE_AppDefined,
372 : "nWidth == 1 || nHeight == 1: cannot determine geotransform");
373 : }
374 : else
375 : {
376 : // FileGDB uses a center-of-pixel convention for georeferencing
377 : // Transform to GDAL's corner-of-pixel convention.
378 29 : const double dfResX = (dfMaxX - dfMinX) / (nWidth - 1);
379 29 : const double dfResY = (dfMaxY - dfMinY) / (nHeight - 1);
380 29 : m_bHasGeoTransform = true;
381 29 : const double dfBlockGeorefWidth = dfResX * nBlockWidth;
382 29 : if (dfMinX != dfBlockOriginX)
383 : {
384 : // Take into account MinX by making sure the raster origin is
385 : // close to it, while being shifted from an integer number of blocks
386 : // from BlockOriginX
387 : const double dfTmp =
388 1 : std::floor((dfMinX - dfBlockOriginX) / dfBlockGeorefWidth);
389 1 : if (std::fabs(dfTmp) > std::numeric_limits<int>::max())
390 : {
391 0 : CPLError(CE_Warning, CPLE_AppDefined,
392 : "Inconsistent eminx=%g and block_origin_x=%g", dfMinX,
393 : dfBlockOriginX);
394 0 : return false;
395 : }
396 1 : m_nShiftBlockX = static_cast<int>(dfTmp);
397 1 : CPLDebug("OpenFileGDB", "m_nShiftBlockX = %d", m_nShiftBlockX);
398 1 : const double dfMinXAdjusted =
399 1 : dfBlockOriginX + m_nShiftBlockX * dfBlockGeorefWidth;
400 1 : nWidth = 1 + static_cast<int>(
401 1 : std::round((dfMaxX - dfMinXAdjusted) / dfResX));
402 : }
403 58 : m_gt[0] =
404 29 : (dfBlockOriginX + m_nShiftBlockX * dfBlockGeorefWidth) - dfResX / 2;
405 29 : m_gt[1] = dfResX;
406 29 : m_gt[2] = 0.0;
407 29 : const double dfBlockGeorefHeight = dfResY * nBlockHeight;
408 29 : if (dfMaxY != dfBlockOriginY)
409 : {
410 : // Take into account MaxY by making sure the raster origin is
411 : // close to it, while being shifted from an integer number of blocks
412 : // from BlockOriginY
413 : const double dfTmp =
414 3 : std::floor((dfBlockOriginY - dfMaxY) / dfBlockGeorefHeight);
415 3 : if (std::fabs(dfTmp) > std::numeric_limits<int>::max())
416 : {
417 0 : CPLError(CE_Warning, CPLE_AppDefined,
418 : "Inconsistent emaxy=%g and block_origin_y=%g", dfMaxY,
419 : dfBlockOriginY);
420 0 : return false;
421 : }
422 3 : m_nShiftBlockY = static_cast<int>(dfTmp);
423 3 : CPLDebug("OpenFileGDB", "m_nShiftBlockY = %d", m_nShiftBlockY);
424 3 : const double dfMaxYAdjusted =
425 3 : dfBlockOriginY - m_nShiftBlockY * dfBlockGeorefHeight;
426 3 : nHeight = 1 + static_cast<int>(
427 3 : std::round((dfMaxYAdjusted - dfMinY) / dfResY));
428 : }
429 58 : m_gt[3] = (dfBlockOriginY - m_nShiftBlockY * dfBlockGeorefHeight) +
430 29 : dfResY / 2;
431 29 : m_gt[4] = 0.0;
432 29 : m_gt[5] = -dfResY;
433 : }
434 :
435 : // Two cases:
436 : // - osDefinition is empty (that is FileGDB v9): find the SRS by looking
437 : // at the SRS attached to the RASTER field definition of the .gdbtable
438 : // file of the main table of the raster (that is the one without fras_XXX
439 : // prefixes)
440 : // - or osDefinition is not empty (that is FileGDB v10): get SRID from the
441 : // "srid" field of the _fras_bnd table, and use that has the key to
442 : // lookup the corresponding WKT from the GDBSpatialRefs table.
443 : // In some cases srid might be 0 (invalid), then we try to get it from
444 : // Definition column of the GDB_Items table, stored in osDefinition
445 29 : psField = oTable.GetFieldValue(i_srid);
446 29 : if (osDefinition.empty())
447 : {
448 : // osDefinition empty for FileGDB v9
449 2 : const auto oIter2 = m_osMapNameToIdx.find(osLayerName);
450 2 : if (oIter2 != m_osMapNameToIdx.end())
451 : {
452 2 : const int nTableIdx = oIter2->second;
453 :
454 4 : FileGDBTable oTableMain;
455 :
456 : const std::string osTableMain(CPLFormFilenameSafe(
457 4 : m_osDirName, CPLSPrintf("a%08x.gdbtable", nTableIdx), nullptr));
458 2 : if (oTableMain.Open(osTableMain.c_str(), false))
459 : {
460 2 : const int iRasterFieldIdx = oTableMain.GetFieldIdx("RASTER");
461 2 : if (iRasterFieldIdx >= 0)
462 : {
463 2 : const auto poField = oTableMain.GetField(iRasterFieldIdx);
464 2 : if (poField->GetType() == FGFT_RASTER)
465 : {
466 2 : const auto poFieldRaster =
467 : static_cast<FileGDBRasterField *>(poField);
468 2 : const auto &osWKT = poFieldRaster->GetWKT();
469 2 : if (!osWKT.empty() && osWKT[0] != '{')
470 : {
471 4 : auto poSRS = BuildSRS(osWKT.c_str());
472 2 : if (poSRS)
473 : {
474 2 : m_oRasterSRS = *poSRS;
475 : }
476 : }
477 : }
478 : }
479 : }
480 : }
481 : }
482 27 : else if (!psField)
483 : {
484 0 : CPLError(CE_Warning, CPLE_AppDefined,
485 : "Cannot read field %s in %s table", "srid",
486 : osBndTableName.c_str());
487 : }
488 27 : else if (m_osGDBSpatialRefsFilename.empty())
489 : {
490 0 : CPLError(CE_Warning, CPLE_AppDefined, "No GDBSpatialRefs table");
491 : }
492 : else
493 : {
494 : // FileGDB v10 case
495 27 : const int nSRID = psField->Integer;
496 54 : FileGDBTable oTableSRS;
497 27 : if (oTableSRS.Open(m_osGDBSpatialRefsFilename.c_str(), false))
498 : {
499 27 : const int iSRTEXT = oTableSRS.GetFieldIdx("SRTEXT");
500 54 : if (iSRTEXT < 0 ||
501 27 : oTableSRS.GetField(iSRTEXT)->GetType() != FGFT_STRING)
502 : {
503 0 : CPLError(CE_Warning, CPLE_AppDefined,
504 : "Could not find field %s in table %s", "SRTEXT",
505 0 : oTableSRS.GetFilename().c_str());
506 : }
507 27 : else if (nSRID == 0)
508 : {
509 : // BldgHeights.gdb is such. We must fetch the SRS from the
510 : // Definition column of the GDB_Items table
511 : CPLXMLTreeCloser psTree(
512 0 : CPLParseXMLString(osDefinition.c_str()));
513 0 : if (psTree == nullptr)
514 : {
515 0 : CPLError(
516 : CE_Warning, CPLE_AppDefined,
517 : "Cannot parse XML definition. SRS will be missing");
518 : }
519 : else
520 : {
521 0 : CPLStripXMLNamespace(psTree.get(), nullptr, TRUE);
522 : const CPLXMLNode *psInfo =
523 0 : CPLSearchXMLNode(psTree.get(), "=DERasterDataset");
524 0 : if (psInfo)
525 : {
526 0 : auto poSRS = BuildSRS(psInfo);
527 0 : if (poSRS)
528 0 : m_oRasterSRS = *poSRS;
529 : }
530 0 : if (m_oRasterSRS.IsEmpty())
531 : {
532 0 : CPLError(CE_Warning, CPLE_AppDefined,
533 : "Cannot get SRS from XML definition");
534 : }
535 : }
536 : }
537 54 : else if (nSRID < 0 || !oTableSRS.SelectRow(nSRID - 1) ||
538 27 : oTableSRS.HasGotError())
539 : {
540 0 : CPLError(CE_Warning, CPLE_AppDefined,
541 : "Cannot find record corresponding to SRID = %d",
542 : nSRID);
543 : }
544 : else
545 : {
546 27 : const auto psSRTEXT = oTableSRS.GetFieldValue(iSRTEXT);
547 27 : if (psSRTEXT && psSRTEXT->String)
548 : {
549 27 : if (psSRTEXT->String[0] != '{')
550 : {
551 52 : auto poSRS = BuildSRS(psSRTEXT->String);
552 26 : if (poSRS)
553 : {
554 26 : m_oRasterSRS = *poSRS;
555 : }
556 27 : }
557 : }
558 : else
559 : {
560 0 : CPLError(CE_Warning, CPLE_AppDefined,
561 : "Cannot get SRTEXT corresponding to SRID = %d",
562 : nSRID);
563 : }
564 : }
565 : }
566 : }
567 :
568 : // Open the fras_blk_XXX table, which contains pixel data, as a OGR layer
569 : const std::string osBlkTableName(
570 87 : std::string("fras_blk_").append(osLayerName).c_str());
571 29 : m_poBlkLayer = BuildLayerFromName(osBlkTableName.c_str());
572 29 : if (!m_poBlkLayer)
573 : {
574 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
575 : osBlkTableName.c_str());
576 0 : return false;
577 : }
578 29 : auto poFDefn = m_poBlkLayer->GetLayerDefn();
579 29 : if (poFDefn->GetFieldIndex("rasterband_id") < 0 ||
580 29 : poFDefn->GetFieldIndex("rrd_factor") < 0 ||
581 29 : poFDefn->GetFieldIndex("row_nbr") < 0 ||
582 29 : poFDefn->GetFieldIndex("col_nbr") < 0 ||
583 87 : poFDefn->GetFieldIndex("block_data") < 0 ||
584 29 : poFDefn->GetFieldIndex("block_key") < 0)
585 : {
586 0 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong structure for %s table",
587 : osBlkTableName.c_str());
588 0 : return false;
589 : }
590 :
591 29 : nRasterXSize = nWidth;
592 29 : nRasterYSize = nHeight;
593 :
594 29 : if (m_oMapGDALBandToGDBBandId.size() > 1)
595 : {
596 6 : SetMetadataItem("INTERLEAVE", "BAND", "IMAGE_STRUCTURE");
597 : }
598 :
599 : // Figure out number of overviews by looking at the biggest block_key
600 : // (should only involve looking in the corresponding index).
601 29 : int nOverviewCount = 0;
602 58 : CPLString osSQL;
603 29 : osSQL.Printf("SELECT MAX(block_key) FROM \"%s\"", osBlkTableName.c_str());
604 29 : auto poSQLLyr = ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
605 29 : if (poSQLLyr)
606 : {
607 58 : auto poFeat = std::unique_ptr<OGRFeature>(poSQLLyr->GetNextFeature());
608 29 : if (poFeat)
609 : {
610 29 : const char *pszMaxKey = poFeat->GetFieldAsString(0);
611 29 : if (strlen(pszMaxKey) == strlen("0000BANDOVYYYYXXXX ") ||
612 1 : strlen(pszMaxKey) == strlen("0000BANDOV-YYYYXXXX ") ||
613 1 : strlen(pszMaxKey) == strlen("0000BANDOVYYYY-XXXX ") ||
614 1 : strlen(pszMaxKey) == strlen("0000BANDOV-YYYY-XXXX "))
615 : {
616 28 : char szHex[3] = {0};
617 28 : memcpy(szHex, pszMaxKey + 8, 2);
618 28 : unsigned nMaxRRD = 0;
619 28 : sscanf(szHex, "%02X", &nMaxRRD);
620 28 : nOverviewCount =
621 28 : static_cast<int>(std::min<unsigned>(31, nMaxRRD));
622 : }
623 : }
624 29 : ReleaseResultSet(poSQLLyr);
625 : }
626 :
627 29 : if (m_eRasterCompression == Compression::JPEG)
628 : {
629 2 : GuessJPEGQuality(nOverviewCount);
630 : }
631 :
632 : // It seems that the top left corner of overviews is registered against
633 : // (eminx, emaxy), contrary to the full resolution layer which is registered
634 : // against (block_origin_x, block_origin_y).
635 : // At least, that's what was observed on the dataset
636 : // ftp://ftp.gisdata.mn.gov/pub/gdrs/data/pub/us_mn_state_dnr/water_lake_bathymetry/fgdb_water_lake_bathymetry.zip
637 29 : if ((dfBlockOriginX != dfMinX || dfBlockOriginY != dfMaxY) &&
638 : nOverviewCount > 0)
639 : {
640 0 : CPLDebug("OpenFileGDB",
641 : "Ignoring overviews as block origin != (minx, maxy)");
642 0 : nOverviewCount = 0;
643 : }
644 :
645 : // Create raster bands
646 :
647 : // Create mask band of full resolution, if we don't assign a nodata value
648 29 : std::unique_ptr<GDALOpenFileGDBRasterBand> poMaskBand;
649 :
650 : // Default "nodata" padding in areas whose validity mask is 0 ?
651 : // Not reliable on integer data types.
652 : // Byte -> 0
653 : // Int8 -> -128 ?
654 : // Int16 -> 32767
655 : // UInt16 -> 0
656 : // (u)int10 -> 65535
657 : // (u)int12 -> 65535
658 : // Int32 -> 2147483647
659 : // UInt32 -> 2147483647
660 : // Float32 -> 3.4e+38
661 : // Float64 -> 1.79e+308
662 :
663 29 : bool bHasNoData = false;
664 29 : double dfNoData = 0.0;
665 58 : const char *pszNoDataOrMask = CSLFetchNameValueDef(
666 29 : poOpenInfo->papszOpenOptions, "NODATA_OR_MASK", "AUTO");
667 29 : if (EQUAL(pszNoDataOrMask, "AUTO"))
668 : {
669 : // In AUTO mode, we only set nodata for Float32/Float64
670 : // For other data types, report a mask band.
671 29 : if (eDT == GDT_Float32)
672 : {
673 5 : bHasNoData = true;
674 5 : dfNoData = static_cast<double>(static_cast<float>(3.4e+38));
675 : }
676 24 : else if (eDT == GDT_Float64)
677 : {
678 1 : bHasNoData = true;
679 1 : dfNoData = 1.79e+308;
680 : }
681 : else
682 : {
683 23 : poMaskBand = std::make_unique<GDALOpenFileGDBRasterBand>(
684 46 : this, 1, GDT_UInt8, 8, nBlockWidth, nBlockHeight, 0, true);
685 : }
686 : }
687 0 : else if (EQUAL(pszNoDataOrMask, "MASK"))
688 : {
689 0 : poMaskBand = std::make_unique<GDALOpenFileGDBRasterBand>(
690 0 : this, 1, GDT_UInt8, 8, nBlockWidth, nBlockHeight, 0, true);
691 : }
692 0 : else if (!EQUAL(pszNoDataOrMask, "NONE"))
693 : {
694 0 : dfNoData = CPLAtof(pszNoDataOrMask);
695 0 : if (eDT == GDT_Float64)
696 : {
697 0 : bHasNoData = true;
698 : }
699 0 : else if (eDT == GDT_Float32)
700 : {
701 0 : if (std::fabs(dfNoData) > std::numeric_limits<float>::max())
702 : {
703 0 : CPLError(CE_Failure, CPLE_AppDefined,
704 : "Invalid nodata value %.17g for Float32", dfNoData);
705 0 : return false;
706 : }
707 0 : bHasNoData = true;
708 : }
709 0 : else if (GDALDataTypeIsInteger(eDT))
710 : {
711 0 : double dfMin = 0, dfMax = 0;
712 0 : switch (eDT)
713 : {
714 0 : case GDT_Int8:
715 0 : dfMin = std::numeric_limits<int8_t>::min();
716 0 : dfMax = std::numeric_limits<int8_t>::max();
717 0 : break;
718 0 : case GDT_UInt8:
719 0 : dfMin = std::numeric_limits<uint8_t>::min();
720 0 : dfMax = std::numeric_limits<uint8_t>::max();
721 0 : break;
722 0 : case GDT_Int16:
723 0 : dfMin = std::numeric_limits<int16_t>::min();
724 0 : dfMax = std::numeric_limits<int16_t>::max();
725 0 : break;
726 0 : case GDT_UInt16:
727 0 : dfMin = std::numeric_limits<uint16_t>::min();
728 0 : dfMax = std::numeric_limits<uint16_t>::max();
729 0 : break;
730 0 : case GDT_Int32:
731 0 : dfMin = std::numeric_limits<int32_t>::min();
732 0 : dfMax = std::numeric_limits<int32_t>::max();
733 0 : break;
734 0 : case GDT_UInt32:
735 0 : dfMin = std::numeric_limits<uint32_t>::min();
736 0 : dfMax = std::numeric_limits<uint32_t>::max();
737 0 : break;
738 0 : default:
739 0 : CPLAssert(false);
740 : return false;
741 : }
742 0 : if (!std::isfinite(dfNoData) || dfNoData < dfMin ||
743 0 : dfNoData > dfMax ||
744 0 : dfNoData != static_cast<double>(static_cast<int64_t>(dfNoData)))
745 : {
746 0 : CPLError(CE_Failure, CPLE_AppDefined,
747 : "Invalid nodata value %.17g for %s", dfNoData,
748 : GDALGetDataTypeName(eDT));
749 0 : return false;
750 : }
751 0 : bHasNoData = true;
752 : }
753 : }
754 :
755 29 : GDALOpenFileGDBRasterBand *poMaskBandRef = poMaskBand.get();
756 :
757 70 : for (int iBand = 1; iBand <= l_nBands; ++iBand)
758 : {
759 : auto poBand = new GDALOpenFileGDBRasterBand(
760 41 : this, iBand, eDT, nBitWidth, nBlockWidth, nBlockHeight, 0, false);
761 41 : if (poMaskBandRef)
762 : {
763 35 : if (iBand == 1)
764 : {
765 : // Make the mask band owned by the first raster band
766 23 : poBand->m_poMaskBandOwned = std::move(poMaskBand);
767 23 : poMaskBandRef = poBand->m_poMaskBandOwned.get();
768 23 : poMaskBandRef->m_poMainBand = poBand;
769 : }
770 35 : poBand->m_poMaskBand = poMaskBandRef;
771 : }
772 6 : else if (bHasNoData)
773 : {
774 6 : poBand->m_dfNoData = dfNoData;
775 6 : poBand->m_bHasNoData = true;
776 : }
777 :
778 : // Create overview bands
779 116 : for (int iOvr = 0; iOvr < nOverviewCount; ++iOvr)
780 : {
781 : auto poOvrBand = std::make_unique<GDALOpenFileGDBRasterBand>(
782 : this, iBand, eDT, nBitWidth, nBlockWidth, nBlockHeight,
783 150 : iOvr + 1, false);
784 75 : if (poBand->m_bHasNoData)
785 : {
786 9 : poOvrBand->m_dfNoData = dfNoData;
787 9 : poOvrBand->m_bHasNoData = true;
788 : }
789 75 : poBand->m_apoOverviewBands.emplace_back(std::move(poOvrBand));
790 : }
791 :
792 41 : SetBand(iBand, poBand);
793 : }
794 :
795 : // Create mask band of overview bands
796 29 : if (poMaskBandRef)
797 : {
798 59 : for (int iOvr = 0; iOvr < nOverviewCount; ++iOvr)
799 : {
800 102 : for (int iBand = 1; iBand <= l_nBands; ++iBand)
801 : {
802 66 : auto poOvrBand = cpl::down_cast<GDALOpenFileGDBRasterBand *>(
803 : GetRasterBand(iBand))
804 66 : ->m_apoOverviewBands[iOvr]
805 66 : .get();
806 66 : if (iBand == 1)
807 : {
808 : // Make the mask band owned by the first raster band
809 : poOvrBand->m_poMaskBandOwned =
810 36 : std::make_unique<GDALOpenFileGDBRasterBand>(
811 0 : this, 1, GDT_UInt8, 8, nBlockWidth, nBlockHeight,
812 36 : iOvr + 1, true);
813 36 : poMaskBandRef = poOvrBand->m_poMaskBandOwned.get();
814 36 : poMaskBandRef->m_poMainBand = poOvrBand;
815 : }
816 66 : poOvrBand->m_poMaskBand = poMaskBandRef;
817 : }
818 : }
819 : }
820 :
821 29 : ReadAuxTable(osLayerName);
822 :
823 29 : SetMetadataItem("RASTER_DATASET", m_osRasterLayerName.c_str());
824 :
825 29 : if (!osDefinition.empty())
826 : {
827 27 : const char *const apszMD[] = {osDefinition.c_str(), nullptr};
828 27 : SetMetadata(const_cast<char **>(apszMD), "xml:definition");
829 : }
830 :
831 29 : if (!osDocumentation.empty())
832 : {
833 26 : const char *const apszMD[] = {osDocumentation.c_str(), nullptr};
834 26 : SetMetadata(const_cast<char **>(apszMD), "xml:documentation");
835 : }
836 :
837 : // We are all fine after all those preliminary checks and setups !
838 29 : return true;
839 : }
840 :
841 : /************************************************************************/
842 : /* GuessJPEGQuality() */
843 : /************************************************************************/
844 :
845 2 : void OGROpenFileGDBDataSource::GuessJPEGQuality(int nOverviewCount)
846 : {
847 : // For JPEG, fetch JPEG_QUALITY from the data of the smallest overview level
848 4 : CPLString osFilter;
849 : osFilter.Printf("block_key = '0000%04X%02X%04X%04X'",
850 : 1, // band
851 : nOverviewCount,
852 : 0, // nBlockYOff
853 : 0 // nBlockXOff
854 2 : );
855 :
856 2 : CPLAssert(m_poBlkLayer);
857 2 : m_poBlkLayer->SetAttributeFilter(osFilter.c_str());
858 : auto poFeature =
859 4 : std::unique_ptr<OGRFeature>(m_poBlkLayer->GetNextFeature());
860 2 : if (poFeature)
861 : {
862 2 : const int nFieldIdx = poFeature->GetFieldIndex("block_data");
863 2 : CPLAssert(nFieldIdx >= 0);
864 2 : if (poFeature->IsFieldSetAndNotNull(nFieldIdx))
865 : {
866 2 : int nInBytes = 0;
867 : const GByte *pabyData =
868 2 : poFeature->GetFieldAsBinary(nFieldIdx, &nInBytes);
869 2 : if (nInBytes >= 5)
870 : {
871 2 : uint32_t nJPEGSize = nInBytes - 1;
872 2 : uint32_t nJPEGOffset = 1;
873 2 : if (pabyData[0] == 0xFE)
874 : {
875 : // JPEG followed by binary mask
876 2 : memcpy(&nJPEGSize, pabyData + 1, sizeof(uint32_t));
877 2 : CPL_LSBPTR32(&nJPEGSize);
878 2 : if (nJPEGSize > static_cast<unsigned>(nInBytes - 5))
879 : {
880 0 : nJPEGSize = 0;
881 : }
882 2 : nJPEGOffset = 5;
883 : }
884 0 : else if (pabyData[0] != 1)
885 : {
886 0 : nJPEGSize = 0;
887 : }
888 2 : if (nJPEGSize)
889 : {
890 : const CPLString osTmpFilename(
891 4 : VSIMemGenerateHiddenFilename("openfilegdb.jpg"));
892 2 : VSIFCloseL(VSIFileFromMemBuffer(
893 : osTmpFilename.c_str(),
894 2 : const_cast<GByte *>(pabyData + nJPEGOffset), nJPEGSize,
895 : false));
896 2 : const char *const apszDrivers[] = {"JPEG", nullptr};
897 : auto poJPEGDS = std::unique_ptr<GDALDataset>(
898 : GDALDataset::Open(osTmpFilename.c_str(), GDAL_OF_RASTER,
899 4 : apszDrivers));
900 2 : if (poJPEGDS)
901 : {
902 2 : const char *pszQuality = poJPEGDS->GetMetadataItem(
903 2 : "JPEG_QUALITY", "IMAGE_STRUCTURE");
904 2 : if (pszQuality)
905 : {
906 2 : SetMetadataItem("JPEG_QUALITY", pszQuality,
907 2 : "IMAGE_STRUCTURE");
908 : }
909 : }
910 2 : VSIUnlink(osTmpFilename);
911 : }
912 : }
913 : }
914 : }
915 2 : }
916 :
917 : /************************************************************************/
918 : /* ReadAuxTable() */
919 : /************************************************************************/
920 :
921 : // Record type=9 of table fras_ras_XXXX contains a PropertySet object,
922 : // which may contain statistics
923 : // For example on
924 : // https://listdata.thelist.tas.gov.au/opendata/data/NCH_ES_WATER_LOGGING_HAZARD_STATEWIDE.zip
925 29 : void OGROpenFileGDBDataSource::ReadAuxTable(const std::string &osLayerName)
926 : {
927 : const std::string osAuxTableName(
928 58 : std::string("fras_aux_").append(osLayerName).c_str());
929 29 : auto poLayer = BuildLayerFromName(osAuxTableName.c_str());
930 29 : if (!poLayer)
931 : {
932 0 : CPLDebug("OpenFileGDB", "Cannot find table %s", osAuxTableName.c_str());
933 0 : return;
934 : }
935 29 : auto poFDefn = poLayer->GetLayerDefn();
936 29 : const int iFieldObjectIdx = poFDefn->GetFieldIndex("object");
937 29 : if (poFDefn->GetFieldIndex("type") < 0 || iFieldObjectIdx < 0)
938 : {
939 0 : CPLDebug("OpenFileGDB", "Wrong structure for %s table",
940 : osAuxTableName.c_str());
941 0 : return;
942 : }
943 29 : poLayer->SetAttributeFilter("type = 9");
944 29 : auto poFeature = std::unique_ptr<OGRFeature>(poLayer->GetNextFeature());
945 29 : if (!poFeature)
946 3 : return;
947 26 : if (!poFeature->IsFieldSetAndNotNull(iFieldObjectIdx))
948 0 : return;
949 26 : int nBytes = 0;
950 : const GByte *pabyData =
951 26 : poFeature->GetFieldAsBinary(iFieldObjectIdx, &nBytes);
952 26 : if (!pabyData || nBytes == 0)
953 0 : return;
954 26 : int iOffset = 0;
955 :
956 545 : const auto ReadString = [pabyData, &iOffset, nBytes](std::string &osStr)
957 : {
958 110 : if (iOffset > nBytes - 4)
959 21 : return false;
960 : int nStrLength;
961 89 : memcpy(&nStrLength, pabyData + iOffset, 4);
962 89 : CPL_LSBPTR32(&nStrLength);
963 89 : iOffset += 4;
964 89 : if (nStrLength <= 2 || iOffset > nBytes - nStrLength)
965 5 : return false;
966 84 : if ((nStrLength % 2) != 0)
967 0 : return false;
968 : // nStrLength / 2 to get the number of characters
969 : // and - 1 to remove the null terminating one
970 84 : osStr = ReadUTF16String(pabyData + iOffset, nStrLength / 2 - 1);
971 84 : iOffset += nStrLength;
972 84 : return true;
973 26 : };
974 :
975 : // pabyData is an ArcObject "PropertySet" object, which is key/value
976 : // dictionary. This is hard to parse given there are variable-length value
977 : // whose size is not explicit. So let's use a heuristics by looking for
978 : // the beginning of a inner PropertySet with band properties that starts
979 : // with a KIND=BAND key value pair.
980 26 : constexpr GByte abyNeedle[] = {
981 : 'K', 0, 'I', 0, 'N', 0, 'D', 0, 0, 0, 8, 0, // 8 = string
982 : 10, 0, 0, 0, // number of bytes of following value
983 : 'B', 0, 'A', 0, 'N', 0, 'D', 0, 0, 0};
984 26 : constexpr int nNeedleSize = static_cast<int>(sizeof(abyNeedle));
985 :
986 26 : for (int iBand = 1; iBand <= nBands; ++iBand)
987 : {
988 26 : int iNewOffset = -1;
989 10218 : for (int i = iOffset; i < nBytes - nNeedleSize; ++i)
990 : {
991 10218 : if (pabyData[i] == 'K' &&
992 78 : memcmp(pabyData + i, abyNeedle, nNeedleSize) == 0)
993 : {
994 26 : iNewOffset = i + nNeedleSize;
995 26 : break;
996 : }
997 : }
998 26 : if (iNewOffset < 0)
999 0 : return;
1000 26 : iOffset = iNewOffset;
1001 :
1002 : // Try to read as many key/value pairs as possible
1003 : while (true)
1004 : {
1005 : // Read key
1006 94 : std::string osKey;
1007 94 : if (!ReadString(osKey))
1008 26 : return;
1009 :
1010 : // Read value type as a short
1011 : uint16_t nValueType;
1012 68 : if (iOffset > nBytes - 2)
1013 0 : return;
1014 68 : memcpy(&nValueType, pabyData + iOffset, 2);
1015 68 : CPL_LSBPTR16(&nValueType);
1016 68 : iOffset += 2;
1017 :
1018 : // Skip over non-string values
1019 68 : if (nValueType == 0 || nValueType == 1) // null / empty value
1020 : {
1021 0 : continue;
1022 : }
1023 68 : if (nValueType == 2) // short value
1024 : {
1025 26 : if (iOffset > nBytes - 2)
1026 0 : return;
1027 26 : iOffset += 2;
1028 26 : continue;
1029 : }
1030 :
1031 42 : if (nValueType == 3 || nValueType == 4) // int or long value
1032 : {
1033 26 : if (iOffset > nBytes - 4)
1034 0 : return;
1035 26 : iOffset += 4;
1036 26 : continue;
1037 : }
1038 :
1039 16 : if (nValueType == 5 || nValueType == 7) // double or date value
1040 : {
1041 0 : if (iOffset > nBytes - 8)
1042 0 : return;
1043 0 : iOffset += 8;
1044 0 : continue;
1045 : }
1046 :
1047 16 : if (nValueType != 8) // 8 = string
1048 : {
1049 : // Give up with this band as the value type is not handled,
1050 : // and we can't skip over it.
1051 0 : break;
1052 : }
1053 :
1054 : // Read string value
1055 16 : std::string osValue;
1056 16 : if (!ReadString(osValue))
1057 0 : return;
1058 :
1059 32 : GetRasterBand(iBand)->SetMetadataItem(osKey.c_str(),
1060 16 : osValue.c_str());
1061 68 : }
1062 : }
1063 : }
1064 :
1065 : /************************************************************************/
1066 : /* GetGeoTransform() */
1067 : /************************************************************************/
1068 :
1069 4 : CPLErr OGROpenFileGDBDataSource::GetGeoTransform(GDALGeoTransform >) const
1070 : {
1071 4 : gt = m_gt;
1072 4 : return m_bHasGeoTransform ? CE_None : CE_Failure;
1073 : }
1074 :
1075 : /************************************************************************/
1076 : /* GetSpatialRef() */
1077 : /************************************************************************/
1078 :
1079 4 : const OGRSpatialReference *OGROpenFileGDBDataSource::GetSpatialRef() const
1080 : {
1081 4 : return m_oRasterSRS.IsEmpty() ? nullptr : &m_oRasterSRS;
1082 : }
1083 :
1084 : /************************************************************************/
1085 : /* GDALOpenFileGDBRasterBand() */
1086 : /************************************************************************/
1087 :
1088 175 : GDALOpenFileGDBRasterBand::GDALOpenFileGDBRasterBand(
1089 : OGROpenFileGDBDataSource *poDSIn, int nBandIn, GDALDataType eDT,
1090 : int nBitWidth, int nBlockWidth, int nBlockHeight, int nOverviewLevel,
1091 175 : bool bIsMask)
1092 : : m_nBitWidth(nBitWidth), m_nOverviewLevel(nOverviewLevel),
1093 175 : m_bIsMask(bIsMask)
1094 : {
1095 175 : poDS = poDSIn;
1096 175 : nBand = nBandIn;
1097 175 : eDataType = eDT;
1098 175 : nRasterXSize = std::max(1, poDSIn->GetRasterXSize() >> nOverviewLevel);
1099 175 : nRasterYSize = std::max(1, poDSIn->GetRasterYSize() >> nOverviewLevel);
1100 175 : nBlockXSize = nBlockWidth;
1101 175 : nBlockYSize = nBlockHeight;
1102 175 : if (nBitWidth < 8)
1103 : {
1104 8 : SetMetadataItem("NBITS", CPLSPrintf("%d", nBitWidth),
1105 : "IMAGE_STRUCTURE");
1106 : }
1107 175 : }
1108 :
1109 : /************************************************************************/
1110 : /* SetNoDataFromMask() */
1111 : /************************************************************************/
1112 :
1113 : template <class T>
1114 0 : static void SetNoDataFromMask(void *pImage, const GByte *pabyMask,
1115 : size_t nPixels, double dfNoData)
1116 : {
1117 0 : const T noData = static_cast<T>(dfNoData);
1118 0 : const T noDataReplacement =
1119 0 : noData == std::numeric_limits<T>::max() ? noData - 1 : noData + 1;
1120 0 : bool bHasWarned = false;
1121 0 : for (size_t i = 0; i < nPixels; ++i)
1122 : {
1123 0 : if (pabyMask && !(pabyMask[i / 8] & (0x80 >> (i & 7))))
1124 : {
1125 0 : static_cast<T *>(pImage)[i] = noData;
1126 : }
1127 0 : else if (static_cast<T *>(pImage)[i] == noData)
1128 : {
1129 0 : static_cast<T *>(pImage)[i] = noDataReplacement;
1130 0 : if (!bHasWarned)
1131 : {
1132 0 : bHasWarned = true;
1133 0 : CPLError(CE_Warning, CPLE_AppDefined,
1134 : "Valid data found with value equal to nodata (%.0f). "
1135 : "Got substituted with %.0f",
1136 : static_cast<double>(noData),
1137 : static_cast<double>(noDataReplacement));
1138 : }
1139 : }
1140 : }
1141 0 : }
1142 :
1143 : /************************************************************************/
1144 : /* IReadBlock() */
1145 : /************************************************************************/
1146 :
1147 118 : CPLErr GDALOpenFileGDBRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1148 : void *pImage)
1149 : {
1150 118 : auto poGDS = cpl::down_cast<OGROpenFileGDBDataSource *>(poDS);
1151 118 : auto &poLyr = poGDS->m_poBlkLayer;
1152 :
1153 : // Return (pointer to image data, owner block). Works when called from main band
1154 : // or mask band. owner block must be DropLock() once done (if not null)
1155 238 : const auto GetImageData = [this, nBlockXOff, nBlockYOff, pImage]()
1156 : {
1157 118 : void *pImageData = nullptr;
1158 118 : GDALRasterBlock *poBlock = nullptr;
1159 118 : if (m_bIsMask)
1160 : {
1161 1 : CPLAssert(m_poMainBand);
1162 1 : poBlock =
1163 1 : m_poMainBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
1164 1 : if (poBlock)
1165 : {
1166 : // The block is already in cache. Return (null, null)
1167 0 : poBlock->DropLock();
1168 0 : poBlock = nullptr;
1169 : }
1170 : else
1171 : {
1172 2 : poBlock = m_poMainBand->GetLockedBlockRef(nBlockXOff,
1173 1 : nBlockYOff, true);
1174 1 : if (poBlock)
1175 1 : pImageData = poBlock->GetDataRef();
1176 : }
1177 : }
1178 : else
1179 : {
1180 117 : pImageData = pImage;
1181 : }
1182 236 : return std::make_pair(pImageData, poBlock);
1183 118 : };
1184 :
1185 : // Return (pointer to mask data, owner block). Works when called from main band
1186 : // or mask band. owner block must be DropLock() once done (if not null)
1187 404 : const auto GetMaskData = [this, nBlockXOff, nBlockYOff, pImage]()
1188 : {
1189 114 : void *pMaskData = nullptr;
1190 114 : GDALRasterBlock *poBlock = nullptr;
1191 114 : if (m_bIsMask)
1192 : {
1193 1 : pMaskData = pImage;
1194 : }
1195 : else
1196 : {
1197 113 : CPLAssert(m_poMaskBand);
1198 113 : poBlock =
1199 113 : m_poMaskBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
1200 113 : if (poBlock)
1201 : {
1202 : // The block is already in cache. Return (null, null)
1203 50 : poBlock->DropLock();
1204 50 : poBlock = nullptr;
1205 : }
1206 : else
1207 : {
1208 126 : poBlock = m_poMaskBand->GetLockedBlockRef(nBlockXOff,
1209 63 : nBlockYOff, true);
1210 63 : if (poBlock)
1211 63 : pMaskData = poBlock->GetDataRef();
1212 : }
1213 : }
1214 228 : return std::make_pair(pMaskData, poBlock);
1215 118 : };
1216 :
1217 : const GDALDataType eImageDT =
1218 118 : m_poMainBand ? m_poMainBand->GetRasterDataType() : eDataType;
1219 118 : const size_t nPixels = static_cast<size_t>(nBlockXSize) * nBlockYSize;
1220 :
1221 : const auto FillMissingBlock =
1222 6 : [this, eImageDT, nPixels, &GetImageData, &GetMaskData]()
1223 : {
1224 : // Set image data to nodata / 0
1225 : {
1226 1 : auto imageDataAndBlock = GetImageData();
1227 1 : auto pImageData = imageDataAndBlock.first;
1228 1 : auto poBlock = imageDataAndBlock.second;
1229 1 : if (pImageData)
1230 : {
1231 1 : const int nDTSize = GDALGetDataTypeSizeBytes(eImageDT);
1232 1 : if (m_bHasNoData)
1233 : {
1234 1 : GDALCopyWords64(&m_dfNoData, GDT_Float64, 0, pImageData,
1235 : eImageDT, nDTSize, nPixels);
1236 : }
1237 : else
1238 : {
1239 0 : memset(pImageData, 0, nPixels * nDTSize);
1240 : }
1241 : }
1242 1 : if (poBlock)
1243 0 : poBlock->DropLock();
1244 : }
1245 :
1246 : // Set mask band to 0 (when it exists)
1247 1 : if (m_poMaskBand || m_bIsMask)
1248 : {
1249 0 : auto maskDataAndBlock = GetMaskData();
1250 0 : auto pMaskData = maskDataAndBlock.first;
1251 0 : auto poBlock = maskDataAndBlock.second;
1252 0 : if (pMaskData)
1253 : {
1254 0 : const size_t nSize =
1255 0 : static_cast<size_t>(nBlockXSize) * nBlockYSize;
1256 0 : memset(pMaskData, 0, nSize);
1257 : }
1258 0 : if (poBlock)
1259 0 : poBlock->DropLock();
1260 : }
1261 1 : };
1262 :
1263 : // Fetch block data from fras_blk_XXX layer
1264 118 : const int nGDALBandId = m_bIsMask ? 1 : nBand;
1265 118 : auto oIter = poGDS->m_oMapGDALBandToGDBBandId.find(nGDALBandId);
1266 118 : if (oIter == poGDS->m_oMapGDALBandToGDBBandId.end())
1267 : {
1268 0 : CPLError(CE_Failure, CPLE_AppDefined,
1269 : "poGDS->m_oMapGDALBandToGDBBandId.find(%d) failed",
1270 : nGDALBandId);
1271 0 : return CE_Failure;
1272 : }
1273 118 : const int nGDBRasterBandId = oIter->second;
1274 :
1275 236 : CPLString osFilter;
1276 : /* osFilter.Printf("rasterband_id = %d AND rrd_factor = %d AND row_nbr = %d "
1277 : "AND col_nbr = %d",
1278 : nGDBRasterBandId,
1279 : m_nOverviewLevel, nBlockYOff, nBlockXOff);
1280 : */
1281 118 : const int nColNbr = nBlockXOff + poGDS->m_nShiftBlockX;
1282 118 : const int nRowNbr = nBlockYOff + poGDS->m_nShiftBlockY;
1283 118 : if (nRowNbr >= 0 && nColNbr >= 0)
1284 : {
1285 118 : osFilter.Printf("block_key = '0000%04X%02X%04X%04X'", nGDBRasterBandId,
1286 118 : m_nOverviewLevel, nRowNbr, nColNbr);
1287 : }
1288 0 : else if (nRowNbr < 0 && nColNbr >= 0)
1289 : {
1290 0 : osFilter.Printf("block_key = '0000%04X%02X-%04X%04X'", nGDBRasterBandId,
1291 0 : m_nOverviewLevel, -nRowNbr, nColNbr);
1292 : }
1293 0 : else if (nRowNbr >= 0 && nColNbr < 0)
1294 : {
1295 0 : osFilter.Printf("block_key = '0000%04X%02X%04X-%04X'", nGDBRasterBandId,
1296 0 : m_nOverviewLevel, nRowNbr, -nColNbr);
1297 : }
1298 : else /* if( nRowNbr < 0 && nColNbr < 0 ) */
1299 : {
1300 : osFilter.Printf("block_key = '0000%04X%02X-%04X-%04X'",
1301 0 : nGDBRasterBandId, m_nOverviewLevel, -nRowNbr, -nColNbr);
1302 : }
1303 : // CPLDebug("OpenFileGDB", "Request %s", osFilter.c_str());
1304 118 : poLyr->SetAttributeFilter(osFilter.c_str());
1305 236 : auto poFeature = std::unique_ptr<OGRFeature>(poLyr->GetNextFeature());
1306 118 : const int nImageDTSize = GDALGetDataTypeSizeBytes(eImageDT);
1307 118 : if (!poFeature)
1308 : {
1309 : // Missing blocks are legit
1310 1 : FillMissingBlock();
1311 1 : return CE_None;
1312 : }
1313 117 : const int nFieldIdx = poFeature->GetFieldIndex("block_data");
1314 117 : CPLAssert(nFieldIdx >= 0);
1315 117 : int nInBytes = 0;
1316 117 : if (!poFeature->IsFieldSetAndNotNull(nFieldIdx))
1317 : {
1318 : // block_data unset found on ForestFalls.gdb
1319 0 : FillMissingBlock();
1320 0 : return CE_None;
1321 : }
1322 117 : const GByte *pabyData = poFeature->GetFieldAsBinary(nFieldIdx, &nInBytes);
1323 117 : if (nInBytes == 0)
1324 : {
1325 0 : CPLError(CE_Failure, CPLE_AppDefined, "Image block is empty");
1326 0 : return CE_Failure;
1327 : }
1328 :
1329 : // The input buffer may be concatenated with a 1-bit binary mask
1330 117 : const size_t nImageSize = nPixels * nImageDTSize;
1331 117 : const int nImageBitWidth =
1332 117 : m_poMainBand ? m_poMainBand->m_nBitWidth : m_nBitWidth;
1333 117 : const size_t nImageSizePacked = (nPixels * nImageBitWidth + 7) / 8;
1334 117 : const size_t nBinaryMaskSize = (nPixels + 7) / 8;
1335 117 : const size_t nImageSizeWithBinaryMask = nImageSizePacked + nBinaryMaskSize;
1336 :
1337 : // Unpack 1-bit, 2-bit, 4-bit data to full byte
1338 : const auto ExpandSubByteData =
1339 2175500 : [nPixels, nImageBitWidth](const GByte *pabyInput, void *pDstBuffer)
1340 : {
1341 24 : CPLAssert(nImageBitWidth < 8);
1342 :
1343 24 : size_t iBitOffset = 0;
1344 393240 : for (size_t i = 0; i < nPixels; ++i)
1345 : {
1346 393216 : unsigned nOutWord = 0;
1347 :
1348 1523710 : for (int iBit = 0; iBit < nImageBitWidth; ++iBit)
1349 : {
1350 1130500 : if (pabyInput[iBitOffset >> 3] & (0x80 >> (iBitOffset & 7)))
1351 : {
1352 258524 : nOutWord |= (1 << (nImageBitWidth - 1 - iBit));
1353 : }
1354 1130500 : ++iBitOffset;
1355 : }
1356 :
1357 393216 : static_cast<GByte *>(pDstBuffer)[i] = static_cast<GByte>(nOutWord);
1358 : }
1359 141 : };
1360 :
1361 117 : const GByte *pabyMask = nullptr;
1362 116 : auto &abyTmpBuffer =
1363 117 : m_poMainBand ? m_poMainBand->m_abyTmpBuffer : m_abyTmpBuffer;
1364 :
1365 117 : switch (poGDS->m_eRasterCompression)
1366 : {
1367 6 : case OGROpenFileGDBDataSource::Compression::NONE:
1368 : {
1369 6 : if (static_cast<unsigned>(nInBytes) != nImageSizePacked &&
1370 6 : static_cast<unsigned>(nInBytes) != nImageSizeWithBinaryMask)
1371 : {
1372 0 : CPLError(CE_Failure, CPLE_AppDefined,
1373 : "Not expected number of input bytes: %d", nInBytes);
1374 0 : return CE_Failure;
1375 : }
1376 :
1377 6 : auto imageDataAndBlock = GetImageData();
1378 6 : auto pImageData = imageDataAndBlock.first;
1379 6 : auto poBlock = imageDataAndBlock.second;
1380 :
1381 6 : if (pImageData)
1382 : {
1383 6 : if (nImageSizePacked == nImageSize)
1384 : {
1385 6 : memcpy(pImageData, pabyData, nImageSize);
1386 : #ifdef CPL_LSB
1387 6 : if (nImageDTSize > 1)
1388 : {
1389 5 : GDALSwapWordsEx(pImageData, nImageDTSize, nPixels,
1390 : nImageDTSize);
1391 : }
1392 : #endif
1393 : }
1394 : else
1395 : {
1396 0 : ExpandSubByteData(pabyData, pImageData);
1397 : }
1398 : }
1399 6 : if (poBlock)
1400 0 : poBlock->DropLock();
1401 :
1402 6 : if (static_cast<unsigned>(nInBytes) == nImageSizeWithBinaryMask)
1403 6 : pabyMask = pabyData + nImageSizePacked;
1404 6 : break;
1405 : }
1406 :
1407 63 : case OGROpenFileGDBDataSource::Compression::LZ77:
1408 : {
1409 63 : if (abyTmpBuffer.empty())
1410 : {
1411 : try
1412 : {
1413 18 : abyTmpBuffer.resize(nImageSizeWithBinaryMask);
1414 : }
1415 0 : catch (const std::bad_alloc &e)
1416 : {
1417 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1418 0 : return CE_Failure;
1419 : }
1420 : }
1421 :
1422 63 : size_t nOutBytes = 0;
1423 63 : GByte *outPtr = abyTmpBuffer.data();
1424 63 : assert(outPtr != nullptr); // For Coverity Scan
1425 63 : if (!CPLZLibInflate(pabyData, nInBytes, outPtr, abyTmpBuffer.size(),
1426 126 : &nOutBytes) ||
1427 63 : !(nOutBytes == nImageSizePacked ||
1428 48 : nOutBytes == nImageSizeWithBinaryMask))
1429 : {
1430 0 : CPLError(
1431 : CE_Failure, CPLE_AppDefined,
1432 : "CPLZLibInflate() failed: nInBytes = %u, nOutBytes = %u, "
1433 : "nImageSizePacked = %u, "
1434 : "nImageSizeWithBinaryMask = %u",
1435 : unsigned(nInBytes), unsigned(nOutBytes),
1436 : unsigned(nImageSizePacked),
1437 : unsigned(nImageSizeWithBinaryMask));
1438 0 : return CE_Failure;
1439 : }
1440 :
1441 63 : auto imageDataAndBlock = GetImageData();
1442 63 : auto pImageData = imageDataAndBlock.first;
1443 63 : auto poBlock = imageDataAndBlock.second;
1444 :
1445 63 : if (pImageData)
1446 : {
1447 63 : if (nImageSizePacked == nImageSize)
1448 : {
1449 39 : memcpy(pImageData, abyTmpBuffer.data(), nImageSize);
1450 : #ifdef CPL_LSB
1451 39 : if (nImageDTSize > 1)
1452 : {
1453 6 : GDALSwapWordsEx(pImageData, nImageDTSize, nPixels,
1454 : nImageDTSize);
1455 : }
1456 : #endif
1457 : }
1458 : else
1459 : {
1460 24 : ExpandSubByteData(abyTmpBuffer.data(), pImageData);
1461 : }
1462 : }
1463 63 : if (poBlock)
1464 1 : poBlock->DropLock();
1465 :
1466 63 : if (nOutBytes == nImageSizeWithBinaryMask)
1467 48 : pabyMask = abyTmpBuffer.data() + nImageSizePacked;
1468 63 : break;
1469 : }
1470 :
1471 24 : case OGROpenFileGDBDataSource::Compression::JPEG:
1472 : {
1473 24 : if (GDALGetDriverByName("JPEG") == nullptr)
1474 : {
1475 0 : CPLError(CE_Failure, CPLE_AppDefined, "JPEG driver missing");
1476 0 : return CE_Failure;
1477 : }
1478 :
1479 24 : if (static_cast<unsigned>(nInBytes) < 5)
1480 : {
1481 0 : CPLError(CE_Failure, CPLE_AppDefined,
1482 : "Not expected number of input bytes: %d", nInBytes);
1483 0 : return CE_Failure;
1484 : }
1485 24 : uint32_t nJPEGSize = nInBytes - 1;
1486 24 : uint32_t nJPEGOffset = 1;
1487 24 : if (pabyData[0] == 0xFE)
1488 : {
1489 : // JPEG followed by binary mask
1490 15 : memcpy(&nJPEGSize, pabyData + 1, sizeof(uint32_t));
1491 15 : CPL_LSBPTR32(&nJPEGSize);
1492 15 : if (nJPEGSize > static_cast<unsigned>(nInBytes - 5))
1493 : {
1494 0 : CPLError(CE_Failure, CPLE_AppDefined,
1495 : "Invalid nJPEGSize = %u", nJPEGSize);
1496 0 : return CE_Failure;
1497 : }
1498 15 : nJPEGOffset = 5;
1499 :
1500 15 : if (abyTmpBuffer.empty())
1501 : {
1502 : try
1503 : {
1504 3 : abyTmpBuffer.resize(nBinaryMaskSize);
1505 : }
1506 0 : catch (const std::bad_alloc &e)
1507 : {
1508 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1509 0 : return CE_Failure;
1510 : }
1511 : }
1512 15 : size_t nOutBytes = 0;
1513 15 : GByte *outPtr = abyTmpBuffer.data();
1514 15 : assert(outPtr != nullptr); // For Coverity Scan
1515 45 : if (CPLZLibInflate(pabyData + 5 + nJPEGSize,
1516 15 : nInBytes - 5 - nJPEGSize, outPtr,
1517 30 : nBinaryMaskSize, &nOutBytes) &&
1518 15 : nOutBytes == nBinaryMaskSize)
1519 : {
1520 15 : pabyMask = abyTmpBuffer.data();
1521 : }
1522 : else
1523 : {
1524 0 : CPLError(CE_Warning, CPLE_AppDefined,
1525 : "Cannot decompress binary mask");
1526 : }
1527 : }
1528 9 : else if (pabyData[0] != 1)
1529 : {
1530 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid JPEG blob");
1531 0 : return CE_Failure;
1532 : }
1533 :
1534 : const CPLString osTmpFilename(
1535 24 : VSIMemGenerateHiddenFilename("openfilegdb.jpg"));
1536 24 : VSIFCloseL(VSIFileFromMemBuffer(
1537 : osTmpFilename.c_str(),
1538 24 : const_cast<GByte *>(pabyData + nJPEGOffset), nJPEGSize, false));
1539 24 : const char *const apszDrivers[] = {"JPEG", nullptr};
1540 : auto poJPEGDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1541 24 : osTmpFilename.c_str(), GDAL_OF_RASTER, apszDrivers));
1542 24 : if (!poJPEGDS)
1543 : {
1544 0 : VSIUnlink(osTmpFilename.c_str());
1545 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open JPEG blob");
1546 0 : return CE_Failure;
1547 : }
1548 24 : if (poJPEGDS->GetRasterCount() != 1 ||
1549 48 : poJPEGDS->GetRasterXSize() != nBlockXSize ||
1550 24 : poJPEGDS->GetRasterYSize() != nBlockYSize)
1551 : {
1552 0 : VSIUnlink(osTmpFilename.c_str());
1553 0 : CPLError(CE_Failure, CPLE_AppDefined,
1554 : "Inconsistent characteristics of JPEG blob");
1555 0 : return CE_Failure;
1556 : }
1557 :
1558 24 : auto imageDataAndBlock = GetImageData();
1559 24 : auto pImageData = imageDataAndBlock.first;
1560 24 : auto poBlock = imageDataAndBlock.second;
1561 :
1562 : const CPLErr eErr =
1563 : pImageData
1564 24 : ? poJPEGDS->GetRasterBand(1)->RasterIO(
1565 : GF_Read, 0, 0, nBlockXSize, nBlockYSize, pImageData,
1566 : nBlockXSize, nBlockYSize, eImageDT, 0, 0, nullptr)
1567 24 : : CE_None;
1568 24 : VSIUnlink(osTmpFilename.c_str());
1569 24 : if (poBlock)
1570 0 : poBlock->DropLock();
1571 :
1572 24 : if (eErr != CE_None)
1573 : {
1574 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot read JPEG blob");
1575 0 : return CE_Failure;
1576 : }
1577 :
1578 24 : break;
1579 : }
1580 :
1581 24 : case OGROpenFileGDBDataSource::Compression::JPEG2000:
1582 : {
1583 24 : const char *const apszDrivers[] = {"JP2KAK", "JP2ECW",
1584 : "JP2OpenJPEG", "JP2MrSID",
1585 : "JP2Lura", nullptr};
1586 24 : bool bFoundJP2Driver = false;
1587 48 : for (const char *pszDriver : apszDrivers)
1588 : {
1589 48 : if (pszDriver && GDALGetDriverByName(pszDriver))
1590 : {
1591 24 : bFoundJP2Driver = true;
1592 24 : break;
1593 : }
1594 : }
1595 24 : if (!bFoundJP2Driver)
1596 : {
1597 0 : CPLError(CE_Failure, CPLE_AppDefined,
1598 : "Did not find any JPEG2000 capable driver");
1599 0 : return CE_Failure;
1600 : }
1601 :
1602 24 : if (static_cast<unsigned>(nInBytes) < 5)
1603 : {
1604 0 : CPLError(CE_Failure, CPLE_AppDefined,
1605 : "Not expected number of input bytes: %d", nInBytes);
1606 0 : return CE_Failure;
1607 : }
1608 24 : uint32_t nJPEGSize = nInBytes - 1;
1609 24 : uint32_t nJPEGOffset = 1;
1610 24 : if (pabyData[0] == 0xFF)
1611 : {
1612 : // JPEG2000 followed by binary mask
1613 15 : memcpy(&nJPEGSize, pabyData + 1, sizeof(uint32_t));
1614 15 : CPL_LSBPTR32(&nJPEGSize);
1615 15 : if (nJPEGSize > static_cast<unsigned>(nInBytes - 5))
1616 : {
1617 0 : CPLError(CE_Failure, CPLE_AppDefined,
1618 : "Invalid nJPEGSize = %u", nJPEGSize);
1619 0 : return CE_Failure;
1620 : }
1621 15 : nJPEGOffset = 5;
1622 :
1623 15 : if (abyTmpBuffer.empty())
1624 : {
1625 : try
1626 : {
1627 3 : abyTmpBuffer.resize(nBinaryMaskSize);
1628 : }
1629 0 : catch (const std::bad_alloc &e)
1630 : {
1631 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1632 0 : return CE_Failure;
1633 : }
1634 : }
1635 15 : size_t nOutBytes = 0;
1636 15 : GByte *outPtr = abyTmpBuffer.data();
1637 15 : assert(outPtr != nullptr); // For Coverity Scan
1638 45 : if (CPLZLibInflate(pabyData + 5 + nJPEGSize,
1639 15 : nInBytes - 5 - nJPEGSize, outPtr,
1640 30 : nBinaryMaskSize, &nOutBytes) &&
1641 15 : nOutBytes == nBinaryMaskSize)
1642 : {
1643 15 : pabyMask = outPtr;
1644 : }
1645 : else
1646 : {
1647 0 : CPLError(CE_Warning, CPLE_AppDefined,
1648 : "Cannot decompress binary mask");
1649 : }
1650 : }
1651 9 : else if (pabyData[0] != 0)
1652 : {
1653 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid JPEG2000 blob");
1654 0 : return CE_Failure;
1655 : }
1656 :
1657 : const CPLString osTmpFilename(
1658 24 : VSIMemGenerateHiddenFilename("openfilegdb.j2k"));
1659 24 : VSIFCloseL(VSIFileFromMemBuffer(
1660 : osTmpFilename.c_str(),
1661 24 : const_cast<GByte *>(pabyData + nJPEGOffset), nJPEGSize, false));
1662 : auto poJP2KDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1663 24 : osTmpFilename.c_str(), GDAL_OF_RASTER, apszDrivers));
1664 24 : if (!poJP2KDS)
1665 : {
1666 0 : VSIUnlink(osTmpFilename.c_str());
1667 0 : CPLError(CE_Failure, CPLE_AppDefined,
1668 : "Cannot open JPEG2000 blob");
1669 0 : return CE_Failure;
1670 : }
1671 24 : if (poJP2KDS->GetRasterCount() != 1 ||
1672 48 : poJP2KDS->GetRasterXSize() != nBlockXSize ||
1673 24 : poJP2KDS->GetRasterYSize() != nBlockYSize)
1674 : {
1675 0 : VSIUnlink(osTmpFilename.c_str());
1676 0 : CPLError(CE_Failure, CPLE_AppDefined,
1677 : "Inconsistent characteristics of JPEG2000 blob");
1678 0 : return CE_Failure;
1679 : }
1680 :
1681 24 : auto imageDataAndBlock = GetImageData();
1682 24 : auto pImageData = imageDataAndBlock.first;
1683 24 : auto poBlock = imageDataAndBlock.second;
1684 :
1685 : const CPLErr eErr =
1686 : pImageData
1687 24 : ? poJP2KDS->GetRasterBand(1)->RasterIO(
1688 : GF_Read, 0, 0, nBlockXSize, nBlockYSize, pImageData,
1689 : nBlockXSize, nBlockYSize, eImageDT, 0, 0, nullptr)
1690 24 : : CE_None;
1691 24 : VSIUnlink(osTmpFilename.c_str());
1692 24 : if (poBlock)
1693 0 : poBlock->DropLock();
1694 :
1695 24 : if (eErr != CE_None)
1696 : {
1697 0 : CPLError(CE_Failure, CPLE_AppDefined,
1698 : "Cannot read JPEG2000 blob");
1699 0 : return CE_Failure;
1700 : }
1701 :
1702 24 : break;
1703 : }
1704 : }
1705 :
1706 117 : if (m_bIsMask || m_poMaskBand)
1707 : {
1708 114 : auto maskDataAndBlock = GetMaskData();
1709 114 : auto pMaskData = maskDataAndBlock.first;
1710 114 : auto poBlock = maskDataAndBlock.second;
1711 :
1712 114 : if (pMaskData)
1713 : {
1714 64 : if (pabyMask)
1715 : {
1716 : // Unpack 1-bit array
1717 802865 : for (size_t i = 0; i < nPixels; ++i)
1718 : {
1719 802816 : static_cast<GByte *>(pMaskData)[i] =
1720 802816 : (pabyMask[i / 8] & (0x80 >> (i & 7))) ? 255 : 0;
1721 : }
1722 : }
1723 : else
1724 : {
1725 : // No explicit mask in source block --> all valid
1726 15 : memset(pMaskData, 255, nPixels);
1727 : }
1728 : }
1729 :
1730 114 : if (poBlock)
1731 114 : poBlock->DropLock();
1732 : }
1733 3 : else if (m_bHasNoData)
1734 : {
1735 3 : if (eImageDT == GDT_UInt8)
1736 : {
1737 0 : SetNoDataFromMask<uint8_t>(pImage, pabyMask, nPixels, m_dfNoData);
1738 : }
1739 3 : else if (eImageDT == GDT_Int8)
1740 : {
1741 0 : SetNoDataFromMask<int8_t>(pImage, pabyMask, nPixels, m_dfNoData);
1742 : }
1743 3 : else if (eImageDT == GDT_UInt16)
1744 : {
1745 0 : SetNoDataFromMask<uint16_t>(pImage, pabyMask, nPixels, m_dfNoData);
1746 : }
1747 3 : else if (eImageDT == GDT_Int16)
1748 : {
1749 0 : SetNoDataFromMask<int16_t>(pImage, pabyMask, nPixels, m_dfNoData);
1750 : }
1751 3 : else if (eImageDT == GDT_UInt32)
1752 : {
1753 0 : SetNoDataFromMask<uint32_t>(pImage, pabyMask, nPixels, m_dfNoData);
1754 : }
1755 3 : else if (eImageDT == GDT_Int32)
1756 : {
1757 0 : SetNoDataFromMask<int32_t>(pImage, pabyMask, nPixels, m_dfNoData);
1758 : }
1759 3 : else if (eImageDT == GDT_Float32)
1760 : {
1761 2 : if (pabyMask)
1762 : {
1763 32770 : for (size_t i = 0; i < nPixels; ++i)
1764 : {
1765 32768 : if (!(pabyMask[i / 8] & (0x80 >> (i & 7))))
1766 : {
1767 31968 : static_cast<float *>(pImage)[i] =
1768 31968 : static_cast<float>(m_dfNoData);
1769 : }
1770 : }
1771 : }
1772 : }
1773 1 : else if (eImageDT == GDT_Float64)
1774 : {
1775 1 : if (pabyMask)
1776 : {
1777 16385 : for (size_t i = 0; i < nPixels; ++i)
1778 : {
1779 16384 : if (!(pabyMask[i / 8] & (0x80 >> (i & 7))))
1780 : {
1781 15984 : static_cast<double *>(pImage)[i] = m_dfNoData;
1782 : }
1783 : }
1784 : }
1785 : }
1786 : else
1787 : {
1788 0 : CPLAssert(false);
1789 : }
1790 : }
1791 :
1792 : #if 0
1793 : printf("Data:\n"); // ok
1794 : if (eDataType == GDT_UInt8)
1795 : {
1796 : for (int y = 0; y < nBlockYSize; ++y)
1797 : {
1798 : for (int x = 0; x < nBlockXSize; ++x)
1799 : {
1800 : printf("%d ", // ok
1801 : static_cast<GByte *>(pImage)[y * nBlockXSize + x]);
1802 : }
1803 : printf("\n"); // ok
1804 : }
1805 : }
1806 : else if (eDataType == GDT_Int8)
1807 : {
1808 : for (int y = 0; y < nBlockYSize; ++y)
1809 : {
1810 : for (int x = 0; x < nBlockXSize; ++x)
1811 : {
1812 : printf("%d ", // ok
1813 : static_cast<int8_t *>(pImage)[y * nBlockXSize + x]);
1814 : }
1815 : printf("\n"); // ok
1816 : }
1817 : }
1818 : else if (eDataType == GDT_UInt16)
1819 : {
1820 : for (int y = 0; y < nBlockYSize; ++y)
1821 : {
1822 : for (int x = 0; x < nBlockXSize; ++x)
1823 : {
1824 : printf("%d ", // ok
1825 : static_cast<uint16_t *>(pImage)[y * nBlockXSize + x]);
1826 : }
1827 : printf("\n"); // ok
1828 : }
1829 : }
1830 : else if (eDataType == GDT_Int16)
1831 : {
1832 : for (int y = 0; y < nBlockYSize; ++y)
1833 : {
1834 : for (int x = 0; x < nBlockXSize; ++x)
1835 : {
1836 : printf("%d ", // ok
1837 : static_cast<int16_t *>(pImage)[y * nBlockXSize + x]);
1838 : }
1839 : printf("\n"); // ok
1840 : }
1841 : }
1842 : else if (eDataType == GDT_UInt32)
1843 : {
1844 : for (int y = 0; y < nBlockYSize; ++y)
1845 : {
1846 : for (int x = 0; x < nBlockXSize; ++x)
1847 : {
1848 : printf("%d ", // ok
1849 : static_cast<uint32_t *>(pImage)[y * nBlockXSize + x]);
1850 : }
1851 : printf("\n"); // ok
1852 : }
1853 : }
1854 : else if (eDataType == GDT_Int32)
1855 : {
1856 : for (int y = 0; y < nBlockYSize; ++y)
1857 : {
1858 : for (int x = 0; x < nBlockXSize; ++x)
1859 : {
1860 : printf("%d ", // ok
1861 : static_cast<int32_t *>(pImage)[y * nBlockXSize + x]);
1862 : }
1863 : printf("\n"); // ok
1864 : }
1865 : }
1866 : else if (eDataType == GDT_Float32)
1867 : {
1868 : for (int y = 0; y < nBlockYSize; ++y)
1869 : {
1870 : for (int x = 0; x < nBlockXSize; ++x)
1871 : {
1872 : printf("%.8g ", // ok
1873 : static_cast<float *>(pImage)[y * nBlockXSize + x]);
1874 : }
1875 : printf("\n"); // ok
1876 : }
1877 : }
1878 : else if (eDataType == GDT_Float64)
1879 : {
1880 : for (int y = 0; y < nBlockYSize; ++y)
1881 : {
1882 : for (int x = 0; x < nBlockXSize; ++x)
1883 : {
1884 : printf("%.17g ", // ok
1885 : static_cast<double *>(pImage)[y * nBlockXSize + x]);
1886 : }
1887 : printf("\n"); // ok
1888 : }
1889 : }
1890 : #endif
1891 :
1892 : #if 0
1893 : if (pabyMask)
1894 : {
1895 : printf("Mask:\n"); // ok
1896 : for (int y = 0; y < nBlockYSize; ++y)
1897 : {
1898 : for (int x = 0; x < nBlockXSize; ++x)
1899 : {
1900 : printf("%d ", // ok
1901 : (pabyMask[(y * nBlockXSize + x) / 8] &
1902 : (0x80 >> ((y * nBlockXSize + x) & 7)))
1903 : ? 1
1904 : : 0);
1905 : }
1906 : printf("\n"); // ok
1907 : }
1908 : }
1909 : #endif
1910 :
1911 117 : return CE_None;
1912 : }
1913 :
1914 : /************************************************************************/
1915 : /* GetDefaultRAT() */
1916 : /************************************************************************/
1917 :
1918 3 : GDALRasterAttributeTable *GDALOpenFileGDBRasterBand::GetDefaultRAT()
1919 : {
1920 3 : if (m_poRAT)
1921 1 : return m_poRAT.get();
1922 2 : if (poDS->GetRasterCount() > 1 || m_bIsMask)
1923 0 : return nullptr;
1924 2 : auto poGDS = cpl::down_cast<OGROpenFileGDBDataSource *>(poDS);
1925 : const std::string osVATTableName(
1926 6 : std::string("VAT_").append(poGDS->m_osRasterLayerName));
1927 : // Instantiate a new dataset, os that the RAT is standalone
1928 4 : auto poDSNew = std::make_unique<OGROpenFileGDBDataSource>();
1929 4 : GDALOpenInfo oOpenInfo(poGDS->m_osDirName.c_str(), GA_ReadOnly);
1930 2 : bool bRetryFileGDBUnused = false;
1931 2 : if (!poDSNew->Open(&oOpenInfo, bRetryFileGDBUnused))
1932 0 : return nullptr;
1933 4 : auto poVatLayer = poDSNew->BuildLayerFromName(osVATTableName.c_str());
1934 2 : if (!poVatLayer)
1935 1 : return nullptr;
1936 2 : m_poRAT = std::make_unique<GDALOpenFileGDBRasterAttributeTable>(
1937 2 : std::move(poDSNew), osVATTableName, std::move(poVatLayer));
1938 1 : return m_poRAT.get();
1939 : }
1940 :
1941 : /************************************************************************/
1942 : /* GDALOpenFileGDBRasterAttributeTable::Clone() */
1943 : /************************************************************************/
1944 :
1945 1 : GDALRasterAttributeTable *GDALOpenFileGDBRasterAttributeTable::Clone() const
1946 : {
1947 2 : auto poDS = std::make_unique<OGROpenFileGDBDataSource>();
1948 2 : GDALOpenInfo oOpenInfo(m_poDS->m_osDirName.c_str(), GA_ReadOnly);
1949 1 : bool bRetryFileGDBUnused = false;
1950 1 : if (!poDS->Open(&oOpenInfo, bRetryFileGDBUnused))
1951 0 : return nullptr;
1952 2 : auto poVatLayer = poDS->BuildLayerFromName(m_osVATTableName.c_str());
1953 1 : if (!poVatLayer)
1954 0 : return nullptr;
1955 : return new GDALOpenFileGDBRasterAttributeTable(
1956 1 : std::move(poDS), m_osVATTableName, std::move(poVatLayer));
1957 : }
|