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