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