Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements Open FileGDB raster driver.
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "cpl_conv.h"
15 : #include "cpl_minixml.h"
16 :
17 : #include "ogr_openfilegdb.h"
18 :
19 : #include "gdal_rat.h"
20 : #include "filegdbtable_priv.h"
21 :
22 : #include <algorithm>
23 : #include <cassert>
24 : #include <cmath>
25 : #include <limits>
26 : #include <new>
27 : #include <utility>
28 :
29 : using namespace OpenFileGDB;
30 :
31 : /************************************************************************/
32 : /* OpenRaster() */
33 : /************************************************************************/
34 :
35 29 : bool OGROpenFileGDBDataSource::OpenRaster(const GDALOpenInfo *poOpenInfo,
36 : const std::string &osLayerName,
37 : const std::string &osDefinition,
38 : const std::string &osDocumentation)
39 : {
40 29 : m_osRasterLayerName = osLayerName;
41 :
42 : const std::string osBndTableName(
43 87 : std::string("fras_bnd_").append(osLayerName).c_str());
44 29 : const auto oIter = m_osMapNameToIdx.find(osBndTableName);
45 29 : if (oIter == m_osMapNameToIdx.end())
46 : {
47 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
48 : osBndTableName.c_str());
49 0 : return false;
50 : }
51 29 : const int nBndIdx = oIter->second;
52 :
53 58 : FileGDBTable oTable;
54 :
55 : const std::string osBndFilename(CPLFormFilenameSafe(
56 58 : m_osDirName, CPLSPrintf("a%08x.gdbtable", nBndIdx), nullptr));
57 29 : if (!oTable.Open(osBndFilename.c_str(), false))
58 : {
59 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open table %s",
60 : osBndTableName.c_str());
61 0 : return false;
62 : }
63 :
64 29 : const int i_rasterband_id = oTable.GetFieldIdx("rasterband_id");
65 29 : const int i_sequence_nbr = oTable.GetFieldIdx("sequence_nbr");
66 29 : const int i_raster_id = oTable.GetFieldIdx("raster_id");
67 29 : const int i_band_width = oTable.GetFieldIdx("band_width");
68 29 : const int i_band_height = oTable.GetFieldIdx("band_height");
69 29 : const int i_band_types = oTable.GetFieldIdx("band_types");
70 29 : const int i_block_width = oTable.GetFieldIdx("block_width");
71 29 : const int i_block_height = oTable.GetFieldIdx("block_height");
72 29 : const int i_block_origin_x = oTable.GetFieldIdx("block_origin_x");
73 29 : const int i_block_origin_y = oTable.GetFieldIdx("block_origin_y");
74 29 : const int i_eminx = oTable.GetFieldIdx("eminx");
75 29 : const int i_eminy = oTable.GetFieldIdx("eminy");
76 29 : const int i_emaxx = oTable.GetFieldIdx("emaxx");
77 29 : const int i_emaxy = oTable.GetFieldIdx("emaxy");
78 29 : const int i_srid = oTable.GetFieldIdx("srid");
79 29 : if (i_rasterband_id < 0 || i_sequence_nbr < 0 || i_raster_id < 0 ||
80 29 : i_band_width < 0 || i_band_height < 0 || i_band_types < 0 ||
81 29 : i_block_width < 0 || i_block_height < 0 || i_block_origin_x < 0 ||
82 29 : i_block_origin_y < 0 || i_eminx < 0 || i_eminy < 0 || i_emaxx < 0 ||
83 58 : i_emaxy < 0 || i_srid < 0 ||
84 58 : oTable.GetField(i_rasterband_id)->GetType() != FGFT_OBJECTID ||
85 58 : oTable.GetField(i_sequence_nbr)->GetType() != FGFT_INT32 ||
86 58 : oTable.GetField(i_raster_id)->GetType() != FGFT_INT32 ||
87 58 : oTable.GetField(i_band_width)->GetType() != FGFT_INT32 ||
88 58 : oTable.GetField(i_band_height)->GetType() != FGFT_INT32 ||
89 58 : oTable.GetField(i_band_types)->GetType() != FGFT_INT32 ||
90 58 : oTable.GetField(i_block_width)->GetType() != FGFT_INT32 ||
91 58 : oTable.GetField(i_block_height)->GetType() != FGFT_INT32 ||
92 58 : oTable.GetField(i_block_origin_x)->GetType() != FGFT_FLOAT64 ||
93 58 : oTable.GetField(i_block_origin_y)->GetType() != FGFT_FLOAT64 ||
94 58 : oTable.GetField(i_eminx)->GetType() != FGFT_FLOAT64 ||
95 58 : oTable.GetField(i_eminy)->GetType() != FGFT_FLOAT64 ||
96 58 : oTable.GetField(i_emaxx)->GetType() != FGFT_FLOAT64 ||
97 87 : oTable.GetField(i_emaxy)->GetType() != FGFT_FLOAT64 ||
98 29 : oTable.GetField(i_srid)->GetType() != FGFT_INT32)
99 : {
100 0 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong structure for %s table",
101 : osBndTableName.c_str());
102 0 : return false;
103 : }
104 :
105 29 : int64_t iRow = 0;
106 111 : while (iRow < oTable.GetTotalRecordCount() &&
107 41 : (iRow = oTable.GetAndSelectNextNonEmptyRow(iRow)) >= 0)
108 : {
109 41 : if (iRow >= INT32_MAX)
110 : {
111 0 : return false;
112 : }
113 41 : auto psField = oTable.GetFieldValue(i_raster_id);
114 41 : if (!psField)
115 : {
116 0 : CPLError(CE_Failure, CPLE_AppDefined,
117 : "Cannot read field %s in %s table", "raster_id",
118 : osBndTableName.c_str());
119 0 : return false;
120 : }
121 41 : if (psField->Integer != 1)
122 : {
123 0 : CPLError(CE_Warning, CPLE_AppDefined,
124 : "Raster with raster_id = %d (!= 1) ignored",
125 0 : psField->Integer);
126 0 : continue;
127 : }
128 :
129 41 : const int nGDBRasterBandId = static_cast<int>(iRow) + 1;
130 :
131 41 : psField = oTable.GetFieldValue(i_sequence_nbr);
132 41 : if (!psField)
133 : {
134 0 : CPLError(CE_Failure, CPLE_AppDefined,
135 : "Cannot read field %s in %s table", "sequence_nbr",
136 : osBndTableName.c_str());
137 0 : return false;
138 : }
139 41 : const int nSequenceNr = psField->Integer;
140 :
141 41 : m_oMapGDALBandToGDBBandId[nSequenceNr] = nGDBRasterBandId;
142 :
143 41 : ++iRow;
144 : }
145 :
146 29 : if (m_oMapGDALBandToGDBBandId.empty())
147 : {
148 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot read record in %s table",
149 : osBndTableName.c_str());
150 0 : return false;
151 : }
152 :
153 29 : auto psField = oTable.GetFieldValue(i_band_width);
154 29 : if (!psField)
155 : {
156 0 : CPLError(CE_Failure, CPLE_AppDefined,
157 : "Cannot read field %s in %s table", "band_width",
158 : osBndTableName.c_str());
159 0 : return false;
160 : }
161 29 : int nWidth = psField->Integer;
162 :
163 29 : psField = oTable.GetFieldValue(i_band_height);
164 29 : if (!psField)
165 : {
166 0 : CPLError(CE_Failure, CPLE_AppDefined,
167 : "Cannot read field %s in %s table", "band_height",
168 : osBndTableName.c_str());
169 0 : return false;
170 : }
171 29 : int nHeight = psField->Integer;
172 :
173 29 : const int l_nBands = static_cast<int>(m_oMapGDALBandToGDBBandId.size());
174 58 : if (!GDALCheckDatasetDimensions(nWidth, nHeight) ||
175 29 : !GDALCheckBandCount(l_nBands, /*bIsZeroAllowed=*/false))
176 : {
177 0 : return false;
178 : }
179 :
180 29 : psField = oTable.GetFieldValue(i_block_width);
181 29 : if (!psField)
182 : {
183 0 : CPLError(CE_Failure, CPLE_AppDefined,
184 : "Cannot read field %s in %s table", "block_width",
185 : osBndTableName.c_str());
186 0 : return false;
187 : }
188 29 : const int nBlockWidth = psField->Integer;
189 :
190 : // 32768 somewhat arbitrary
191 29 : if (nBlockWidth <= 0 || nBlockWidth > 32768)
192 : {
193 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s in %s table",
194 : "block_width", osBndTableName.c_str());
195 0 : return false;
196 : }
197 :
198 29 : psField = oTable.GetFieldValue(i_block_height);
199 29 : if (!psField)
200 : {
201 0 : CPLError(CE_Failure, CPLE_AppDefined,
202 : "Cannot read field %s in %s table", "block_height",
203 : osBndTableName.c_str());
204 0 : return false;
205 : }
206 29 : const int nBlockHeight = psField->Integer;
207 :
208 : // 32768 somewhat arbitrary
209 29 : if (nBlockHeight <= 0 || nBlockHeight > 32768)
210 : {
211 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s in %s table",
212 : "block_height", osBndTableName.c_str());
213 0 : return false;
214 : }
215 :
216 29 : psField = oTable.GetFieldValue(i_band_types);
217 29 : if (!psField)
218 : {
219 0 : CPLError(CE_Failure, CPLE_AppDefined,
220 : "Cannot read field %s in %s table", "band_types",
221 : osBndTableName.c_str());
222 0 : return false;
223 : }
224 29 : const int nBandTypes = psField->Integer;
225 :
226 29 : psField = oTable.GetFieldValue(i_eminx);
227 29 : if (!psField)
228 : {
229 0 : CPLError(CE_Failure, CPLE_AppDefined,
230 : "Cannot read field %s in %s table", "eminx",
231 : osBndTableName.c_str());
232 0 : return false;
233 : }
234 29 : const double dfMinX = psField->Real;
235 :
236 29 : psField = oTable.GetFieldValue(i_eminy);
237 29 : if (!psField)
238 : {
239 0 : CPLError(CE_Failure, CPLE_AppDefined,
240 : "Cannot read field %s in %s table", "eminy",
241 : osBndTableName.c_str());
242 0 : return false;
243 : }
244 29 : const double dfMinY = psField->Real;
245 :
246 29 : psField = oTable.GetFieldValue(i_emaxx);
247 29 : if (!psField)
248 : {
249 0 : CPLError(CE_Failure, CPLE_AppDefined,
250 : "Cannot read field %s in %s table", "emaxx",
251 : osBndTableName.c_str());
252 0 : return false;
253 : }
254 29 : const double dfMaxX = psField->Real;
255 :
256 29 : psField = oTable.GetFieldValue(i_emaxy);
257 29 : if (!psField)
258 : {
259 0 : CPLError(CE_Failure, CPLE_AppDefined,
260 : "Cannot read field %s in %s table", "emaxy",
261 : osBndTableName.c_str());
262 0 : return false;
263 : }
264 29 : const double dfMaxY = psField->Real;
265 :
266 29 : psField = oTable.GetFieldValue(i_block_origin_x);
267 29 : if (!psField)
268 : {
269 0 : CPLError(CE_Failure, CPLE_AppDefined,
270 : "Cannot read field %s in %s table", "block_origin_x",
271 : osBndTableName.c_str());
272 0 : return false;
273 : }
274 29 : const double dfBlockOriginX = psField->Real;
275 :
276 29 : psField = oTable.GetFieldValue(i_block_origin_y);
277 29 : if (!psField)
278 : {
279 0 : CPLError(CE_Failure, CPLE_AppDefined,
280 : "Cannot read field %s in %s table", "block_origin_y",
281 : osBndTableName.c_str());
282 0 : return false;
283 : }
284 29 : const double dfBlockOriginY = psField->Real;
285 :
286 : // Figure out data type
287 29 : GDALDataType eDT = GDT_UInt8;
288 29 : const int nBitWidth = (nBandTypes >> 19) & ((1 << 7) - 1);
289 29 : const int nBitType = (nBandTypes >> 16) & ((1 << 2) - 1);
290 29 : constexpr int IS_UNSIGNED = 0;
291 29 : constexpr int IS_SIGNED = 1;
292 29 : constexpr int IS_FLOATING_POINT = 2;
293 29 : if ((nBitWidth >= 1 && nBitWidth < 8) && nBitType == IS_UNSIGNED)
294 : {
295 2 : eDT = GDT_UInt8;
296 : }
297 27 : else if (nBitWidth == 8 && nBitType <= IS_SIGNED)
298 : {
299 13 : eDT = nBitType == IS_SIGNED ? GDT_Int8 : GDT_UInt8;
300 : }
301 14 : else if (nBitWidth == 16 && nBitType <= IS_SIGNED)
302 : {
303 4 : eDT = nBitType == IS_SIGNED ? GDT_Int16 : GDT_UInt16;
304 : }
305 10 : else if (nBitWidth == 32 && nBitType <= IS_FLOATING_POINT)
306 : {
307 13 : eDT = nBitType == IS_FLOATING_POINT ? GDT_Float32
308 4 : : nBitType == IS_SIGNED ? GDT_Int32
309 : : GDT_UInt32;
310 : }
311 1 : else if (nBitWidth == 64 && nBitType == 0)
312 : {
313 1 : eDT = GDT_Float64;
314 : }
315 : else
316 : {
317 0 : CPLError(CE_Failure, CPLE_AppDefined,
318 : "Unhandled nBitWidth=%d, nBitType=%d in %s table", nBitWidth,
319 : nBitType, osBndTableName.c_str());
320 0 : return false;
321 : }
322 :
323 : // To avoid potential integer overflows in IReadBlock()
324 58 : if (nBlockWidth * nBlockHeight >
325 29 : std::numeric_limits<int>::max() / nBitWidth)
326 : {
327 0 : CPLError(CE_Failure, CPLE_AppDefined,
328 : "Too large block %dx%d in %s table", nBlockWidth, nBlockHeight,
329 : osBndTableName.c_str());
330 0 : return false;
331 : }
332 :
333 : // Figure out compression
334 29 : const int nCompression = (nBandTypes >> 8) & 0xff;
335 29 : switch (nCompression)
336 : {
337 6 : case 0:
338 6 : m_eRasterCompression = Compression::NONE;
339 6 : break;
340 19 : case 4:
341 19 : m_eRasterCompression = Compression::LZ77;
342 19 : SetMetadataItem(GDALMD_COMPRESSION, "DEFLATE",
343 19 : GDAL_MDD_IMAGE_STRUCTURE);
344 19 : break;
345 2 : case 8:
346 2 : m_eRasterCompression = Compression::JPEG;
347 2 : SetMetadataItem(GDALMD_COMPRESSION, "JPEG",
348 2 : GDAL_MDD_IMAGE_STRUCTURE);
349 2 : break;
350 2 : case 12:
351 2 : m_eRasterCompression = Compression::JPEG2000;
352 2 : SetMetadataItem(GDALMD_COMPRESSION, "JPEG2000",
353 2 : GDAL_MDD_IMAGE_STRUCTURE);
354 2 : break;
355 0 : default:
356 : {
357 0 : CPLError(CE_Failure, CPLE_AppDefined,
358 : "Unhandled compression %d in %s table", nCompression,
359 : osBndTableName.c_str());
360 0 : return false;
361 : }
362 : }
363 :
364 : // Figure out geotransform
365 :
366 29 : if (!(dfMaxX > dfMinX && dfMaxY > dfMinY))
367 : {
368 0 : CPLError(CE_Failure, CPLE_AppDefined,
369 : "!(dfMaxX > dfMinX && dfMaxY > dfMinY)");
370 0 : return false;
371 : }
372 29 : else if (nWidth == 1 || nHeight == 1)
373 : {
374 0 : CPLError(CE_Warning, CPLE_AppDefined,
375 : "nWidth == 1 || nHeight == 1: cannot determine geotransform");
376 : }
377 : else
378 : {
379 : // FileGDB uses a center-of-pixel convention for georeferencing
380 : // Transform to GDAL's corner-of-pixel convention.
381 29 : const double dfResX = (dfMaxX - dfMinX) / (nWidth - 1);
382 29 : const double dfResY = (dfMaxY - dfMinY) / (nHeight - 1);
383 29 : m_bHasGeoTransform = true;
384 29 : const double dfBlockGeorefWidth = dfResX * nBlockWidth;
385 29 : if (dfMinX != dfBlockOriginX)
386 : {
387 : // Take into account MinX by making sure the raster origin is
388 : // close to it, while being shifted from an integer number of blocks
389 : // from BlockOriginX
390 : const double dfTmp =
391 1 : std::floor((dfMinX - dfBlockOriginX) / dfBlockGeorefWidth);
392 1 : if (std::fabs(dfTmp) > std::numeric_limits<int>::max())
393 : {
394 0 : CPLError(CE_Warning, CPLE_AppDefined,
395 : "Inconsistent eminx=%g and block_origin_x=%g", dfMinX,
396 : dfBlockOriginX);
397 0 : return false;
398 : }
399 1 : m_nShiftBlockX = static_cast<int>(dfTmp);
400 1 : CPLDebug("OpenFileGDB", "m_nShiftBlockX = %d", m_nShiftBlockX);
401 1 : const double dfMinXAdjusted =
402 1 : dfBlockOriginX + m_nShiftBlockX * dfBlockGeorefWidth;
403 1 : nWidth = 1 + static_cast<int>(
404 1 : std::round((dfMaxX - dfMinXAdjusted) / dfResX));
405 : }
406 58 : m_gt[0] =
407 29 : (dfBlockOriginX + m_nShiftBlockX * dfBlockGeorefWidth) - dfResX / 2;
408 29 : m_gt[1] = dfResX;
409 29 : m_gt[2] = 0.0;
410 29 : const double dfBlockGeorefHeight = dfResY * nBlockHeight;
411 29 : if (dfMaxY != dfBlockOriginY)
412 : {
413 : // Take into account MaxY by making sure the raster origin is
414 : // close to it, while being shifted from an integer number of blocks
415 : // from BlockOriginY
416 : const double dfTmp =
417 3 : std::floor((dfBlockOriginY - dfMaxY) / dfBlockGeorefHeight);
418 3 : if (std::fabs(dfTmp) > std::numeric_limits<int>::max())
419 : {
420 0 : CPLError(CE_Warning, CPLE_AppDefined,
421 : "Inconsistent emaxy=%g and block_origin_y=%g", dfMaxY,
422 : dfBlockOriginY);
423 0 : return false;
424 : }
425 3 : m_nShiftBlockY = static_cast<int>(dfTmp);
426 3 : CPLDebug("OpenFileGDB", "m_nShiftBlockY = %d", m_nShiftBlockY);
427 3 : const double dfMaxYAdjusted =
428 3 : dfBlockOriginY - m_nShiftBlockY * dfBlockGeorefHeight;
429 3 : nHeight = 1 + static_cast<int>(
430 3 : std::round((dfMaxYAdjusted - dfMinY) / dfResY));
431 : }
432 58 : m_gt[3] = (dfBlockOriginY - m_nShiftBlockY * dfBlockGeorefHeight) +
433 29 : dfResY / 2;
434 29 : m_gt[4] = 0.0;
435 29 : m_gt[5] = -dfResY;
436 : }
437 :
438 : // Two cases:
439 : // - osDefinition is empty (that is FileGDB v9): find the SRS by looking
440 : // at the SRS attached to the RASTER field definition of the .gdbtable
441 : // file of the main table of the raster (that is the one without fras_XXX
442 : // prefixes)
443 : // - or osDefinition is not empty (that is FileGDB v10): get SRID from the
444 : // "srid" field of the _fras_bnd table, and use that has the key to
445 : // lookup the corresponding WKT from the GDBSpatialRefs table.
446 : // In some cases srid might be 0 (invalid), then we try to get it from
447 : // Definition column of the GDB_Items table, stored in osDefinition
448 29 : psField = oTable.GetFieldValue(i_srid);
449 29 : if (osDefinition.empty())
450 : {
451 : // osDefinition empty for FileGDB v9
452 2 : const auto oIter2 = m_osMapNameToIdx.find(osLayerName);
453 2 : if (oIter2 != m_osMapNameToIdx.end())
454 : {
455 2 : const int nTableIdx = oIter2->second;
456 :
457 4 : FileGDBTable oTableMain;
458 :
459 : const std::string osTableMain(CPLFormFilenameSafe(
460 4 : m_osDirName, CPLSPrintf("a%08x.gdbtable", nTableIdx), nullptr));
461 2 : if (oTableMain.Open(osTableMain.c_str(), false))
462 : {
463 2 : const int iRasterFieldIdx = oTableMain.GetFieldIdx("RASTER");
464 2 : if (iRasterFieldIdx >= 0)
465 : {
466 2 : const auto poField = oTableMain.GetField(iRasterFieldIdx);
467 2 : if (poField->GetType() == FGFT_RASTER)
468 : {
469 2 : const auto poFieldRaster =
470 : static_cast<FileGDBRasterField *>(poField);
471 2 : const auto &osWKT = poFieldRaster->GetWKT();
472 2 : if (!osWKT.empty() && osWKT[0] != '{')
473 : {
474 4 : auto poSRS = BuildSRS(osWKT.c_str());
475 2 : if (poSRS)
476 : {
477 2 : m_oRasterSRS = *poSRS;
478 : }
479 : }
480 : }
481 : }
482 : }
483 : }
484 : }
485 27 : else if (!psField)
486 : {
487 0 : CPLError(CE_Warning, CPLE_AppDefined,
488 : "Cannot read field %s in %s table", "srid",
489 : osBndTableName.c_str());
490 : }
491 27 : else if (m_osGDBSpatialRefsFilename.empty())
492 : {
493 0 : CPLError(CE_Warning, CPLE_AppDefined, "No GDBSpatialRefs table");
494 : }
495 : else
496 : {
497 : // FileGDB v10 case
498 27 : const int nSRID = psField->Integer;
499 54 : FileGDBTable oTableSRS;
500 27 : if (oTableSRS.Open(m_osGDBSpatialRefsFilename.c_str(), false))
501 : {
502 27 : const int iSRTEXT = oTableSRS.GetFieldIdx("SRTEXT");
503 54 : if (iSRTEXT < 0 ||
504 27 : oTableSRS.GetField(iSRTEXT)->GetType() != FGFT_STRING)
505 : {
506 0 : CPLError(CE_Warning, CPLE_AppDefined,
507 : "Could not find field %s in table %s", "SRTEXT",
508 0 : oTableSRS.GetFilename().c_str());
509 : }
510 27 : else if (nSRID == 0)
511 : {
512 : // BldgHeights.gdb is such. We must fetch the SRS from the
513 : // Definition column of the GDB_Items table
514 : CPLXMLTreeCloser psTree(
515 0 : CPLParseXMLString(osDefinition.c_str()));
516 0 : if (psTree == nullptr)
517 : {
518 0 : CPLError(
519 : CE_Warning, CPLE_AppDefined,
520 : "Cannot parse XML definition. SRS will be missing");
521 : }
522 : else
523 : {
524 0 : CPLStripXMLNamespace(psTree.get(), nullptr, TRUE);
525 : const CPLXMLNode *psInfo =
526 0 : CPLSearchXMLNode(psTree.get(), "=DERasterDataset");
527 0 : if (psInfo)
528 : {
529 0 : auto poSRS = BuildSRS(psInfo);
530 0 : if (poSRS)
531 0 : m_oRasterSRS = *poSRS;
532 : }
533 0 : if (m_oRasterSRS.IsEmpty())
534 : {
535 0 : CPLError(CE_Warning, CPLE_AppDefined,
536 : "Cannot get SRS from XML definition");
537 : }
538 : }
539 : }
540 54 : else if (nSRID < 0 || !oTableSRS.SelectRow(nSRID - 1) ||
541 27 : oTableSRS.HasGotError())
542 : {
543 0 : CPLError(CE_Warning, CPLE_AppDefined,
544 : "Cannot find record corresponding to SRID = %d",
545 : nSRID);
546 : }
547 : else
548 : {
549 27 : const auto psSRTEXT = oTableSRS.GetFieldValue(iSRTEXT);
550 27 : if (psSRTEXT && psSRTEXT->String)
551 : {
552 27 : if (psSRTEXT->String[0] != '{')
553 : {
554 52 : auto poSRS = BuildSRS(psSRTEXT->String);
555 26 : if (poSRS)
556 : {
557 26 : m_oRasterSRS = *poSRS;
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(GDALMD_INTERLEAVE, "BAND", GDAL_MDD_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_UInt8, 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_UInt8, 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_UInt8:
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_UInt8, 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", GDAL_MDD_IMAGE_STRUCTURE);
907 2 : if (pszQuality)
908 : {
909 2 : SetMetadataItem("JPEG_QUALITY", pszQuality,
910 2 : GDAL_MDD_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(GDALGeoTransform >) const
1073 : {
1074 4 : gt = m_gt;
1075 4 : return m_bHasGeoTransform ? CE_None : CE_Failure;
1076 : }
1077 :
1078 : /************************************************************************/
1079 : /* GetSpatialRef() */
1080 : /************************************************************************/
1081 :
1082 4 : const OGRSpatialReference *OGROpenFileGDBDataSource::GetSpatialRef() const
1083 : {
1084 4 : return m_oRasterSRS.IsEmpty() ? nullptr : &m_oRasterSRS;
1085 : }
1086 :
1087 : /************************************************************************/
1088 : /* GDALOpenFileGDBRasterBand() */
1089 : /************************************************************************/
1090 :
1091 175 : GDALOpenFileGDBRasterBand::GDALOpenFileGDBRasterBand(
1092 : OGROpenFileGDBDataSource *poDSIn, int nBandIn, GDALDataType eDT,
1093 : int nBitWidth, int nBlockWidth, int nBlockHeight, int nOverviewLevel,
1094 175 : bool bIsMask)
1095 : : m_nBitWidth(nBitWidth), m_nOverviewLevel(nOverviewLevel),
1096 175 : m_bIsMask(bIsMask)
1097 : {
1098 175 : poDS = poDSIn;
1099 175 : nBand = nBandIn;
1100 175 : eDataType = eDT;
1101 175 : nRasterXSize = std::max(1, poDSIn->GetRasterXSize() >> nOverviewLevel);
1102 175 : nRasterYSize = std::max(1, poDSIn->GetRasterYSize() >> nOverviewLevel);
1103 175 : nBlockXSize = nBlockWidth;
1104 175 : nBlockYSize = nBlockHeight;
1105 175 : if (nBitWidth < 8)
1106 : {
1107 8 : SetMetadataItem(GDALMD_NBITS, CPLSPrintf("%d", nBitWidth),
1108 : GDAL_MDD_IMAGE_STRUCTURE);
1109 : }
1110 175 : }
1111 :
1112 : /************************************************************************/
1113 : /* SetNoDataFromMask() */
1114 : /************************************************************************/
1115 :
1116 : template <class T>
1117 0 : static void SetNoDataFromMask(void *pImage, const GByte *pabyMask,
1118 : size_t nPixels, double dfNoData)
1119 : {
1120 0 : const T noData = static_cast<T>(dfNoData);
1121 0 : const T noDataReplacement =
1122 0 : noData == std::numeric_limits<T>::max() ? noData - 1 : noData + 1;
1123 0 : bool bHasWarned = false;
1124 0 : for (size_t i = 0; i < nPixels; ++i)
1125 : {
1126 0 : if (pabyMask && !(pabyMask[i / 8] & (0x80 >> (i & 7))))
1127 : {
1128 0 : static_cast<T *>(pImage)[i] = noData;
1129 : }
1130 0 : else if (static_cast<T *>(pImage)[i] == noData)
1131 : {
1132 0 : static_cast<T *>(pImage)[i] = noDataReplacement;
1133 0 : if (!bHasWarned)
1134 : {
1135 0 : bHasWarned = true;
1136 0 : CPLError(CE_Warning, CPLE_AppDefined,
1137 : "Valid data found with value equal to nodata (%.0f). "
1138 : "Got substituted with %.0f",
1139 : static_cast<double>(noData),
1140 : static_cast<double>(noDataReplacement));
1141 : }
1142 : }
1143 : }
1144 0 : }
1145 :
1146 : /************************************************************************/
1147 : /* IReadBlock() */
1148 : /************************************************************************/
1149 :
1150 118 : CPLErr GDALOpenFileGDBRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1151 : void *pImage)
1152 : {
1153 118 : auto poGDS = cpl::down_cast<OGROpenFileGDBDataSource *>(poDS);
1154 118 : auto &poLyr = poGDS->m_poBlkLayer;
1155 :
1156 : // Return (pointer to image data, owner block). Works when called from main band
1157 : // or mask band. owner block must be DropLock() once done (if not null)
1158 238 : const auto GetImageData = [this, nBlockXOff, nBlockYOff, pImage]()
1159 : {
1160 118 : void *pImageData = nullptr;
1161 118 : GDALRasterBlock *poBlock = nullptr;
1162 118 : if (m_bIsMask)
1163 : {
1164 1 : CPLAssert(m_poMainBand);
1165 1 : poBlock =
1166 1 : m_poMainBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
1167 1 : if (poBlock)
1168 : {
1169 : // The block is already in cache. Return (null, null)
1170 0 : poBlock->DropLock();
1171 0 : poBlock = nullptr;
1172 : }
1173 : else
1174 : {
1175 2 : poBlock = m_poMainBand->GetLockedBlockRef(nBlockXOff,
1176 1 : nBlockYOff, true);
1177 1 : if (poBlock)
1178 1 : pImageData = poBlock->GetDataRef();
1179 : }
1180 : }
1181 : else
1182 : {
1183 117 : pImageData = pImage;
1184 : }
1185 236 : return std::make_pair(pImageData, poBlock);
1186 118 : };
1187 :
1188 : // Return (pointer to mask data, owner block). Works when called from main band
1189 : // or mask band. owner block must be DropLock() once done (if not null)
1190 404 : const auto GetMaskData = [this, nBlockXOff, nBlockYOff, pImage]()
1191 : {
1192 114 : void *pMaskData = nullptr;
1193 114 : GDALRasterBlock *poBlock = nullptr;
1194 114 : if (m_bIsMask)
1195 : {
1196 1 : pMaskData = pImage;
1197 : }
1198 : else
1199 : {
1200 113 : CPLAssert(m_poMaskBand);
1201 113 : poBlock =
1202 113 : m_poMaskBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
1203 113 : if (poBlock)
1204 : {
1205 : // The block is already in cache. Return (null, null)
1206 50 : poBlock->DropLock();
1207 50 : poBlock = nullptr;
1208 : }
1209 : else
1210 : {
1211 126 : poBlock = m_poMaskBand->GetLockedBlockRef(nBlockXOff,
1212 63 : nBlockYOff, true);
1213 63 : if (poBlock)
1214 63 : pMaskData = poBlock->GetDataRef();
1215 : }
1216 : }
1217 228 : return std::make_pair(pMaskData, poBlock);
1218 118 : };
1219 :
1220 : const GDALDataType eImageDT =
1221 118 : m_poMainBand ? m_poMainBand->GetRasterDataType() : eDataType;
1222 118 : const size_t nPixels = static_cast<size_t>(nBlockXSize) * nBlockYSize;
1223 :
1224 : const auto FillMissingBlock =
1225 6 : [this, eImageDT, nPixels, &GetImageData, &GetMaskData]()
1226 : {
1227 : // Set image data to nodata / 0
1228 : {
1229 1 : auto imageDataAndBlock = GetImageData();
1230 1 : auto pImageData = imageDataAndBlock.first;
1231 1 : auto poBlock = imageDataAndBlock.second;
1232 1 : if (pImageData)
1233 : {
1234 1 : const int nDTSize = GDALGetDataTypeSizeBytes(eImageDT);
1235 1 : if (m_bHasNoData)
1236 : {
1237 1 : GDALCopyWords64(&m_dfNoData, GDT_Float64, 0, pImageData,
1238 : eImageDT, nDTSize, nPixels);
1239 : }
1240 : else
1241 : {
1242 0 : memset(pImageData, 0, nPixels * nDTSize);
1243 : }
1244 : }
1245 1 : if (poBlock)
1246 0 : poBlock->DropLock();
1247 : }
1248 :
1249 : // Set mask band to 0 (when it exists)
1250 1 : if (m_poMaskBand || m_bIsMask)
1251 : {
1252 0 : auto maskDataAndBlock = GetMaskData();
1253 0 : auto pMaskData = maskDataAndBlock.first;
1254 0 : auto poBlock = maskDataAndBlock.second;
1255 0 : if (pMaskData)
1256 : {
1257 0 : const size_t nSize =
1258 0 : static_cast<size_t>(nBlockXSize) * nBlockYSize;
1259 0 : memset(pMaskData, 0, nSize);
1260 : }
1261 0 : if (poBlock)
1262 0 : poBlock->DropLock();
1263 : }
1264 1 : };
1265 :
1266 : // Fetch block data from fras_blk_XXX layer
1267 118 : const int nGDALBandId = m_bIsMask ? 1 : nBand;
1268 118 : auto oIter = poGDS->m_oMapGDALBandToGDBBandId.find(nGDALBandId);
1269 118 : if (oIter == poGDS->m_oMapGDALBandToGDBBandId.end())
1270 : {
1271 0 : CPLError(CE_Failure, CPLE_AppDefined,
1272 : "poGDS->m_oMapGDALBandToGDBBandId.find(%d) failed",
1273 : nGDALBandId);
1274 0 : return CE_Failure;
1275 : }
1276 118 : const int nGDBRasterBandId = oIter->second;
1277 :
1278 236 : CPLString osFilter;
1279 : /* osFilter.Printf("rasterband_id = %d AND rrd_factor = %d AND row_nbr = %d "
1280 : "AND col_nbr = %d",
1281 : nGDBRasterBandId,
1282 : m_nOverviewLevel, nBlockYOff, nBlockXOff);
1283 : */
1284 118 : const int nColNbr = nBlockXOff + poGDS->m_nShiftBlockX;
1285 118 : const int nRowNbr = nBlockYOff + poGDS->m_nShiftBlockY;
1286 118 : if (nRowNbr >= 0 && nColNbr >= 0)
1287 : {
1288 118 : osFilter.Printf("block_key = '0000%04X%02X%04X%04X'", nGDBRasterBandId,
1289 118 : m_nOverviewLevel, nRowNbr, nColNbr);
1290 : }
1291 0 : else if (nRowNbr < 0 && nColNbr >= 0)
1292 : {
1293 0 : osFilter.Printf("block_key = '0000%04X%02X-%04X%04X'", nGDBRasterBandId,
1294 0 : m_nOverviewLevel, -nRowNbr, nColNbr);
1295 : }
1296 0 : else if (nRowNbr >= 0 && nColNbr < 0)
1297 : {
1298 0 : osFilter.Printf("block_key = '0000%04X%02X%04X-%04X'", nGDBRasterBandId,
1299 0 : m_nOverviewLevel, nRowNbr, -nColNbr);
1300 : }
1301 : else /* if( nRowNbr < 0 && nColNbr < 0 ) */
1302 : {
1303 : osFilter.Printf("block_key = '0000%04X%02X-%04X-%04X'",
1304 0 : nGDBRasterBandId, m_nOverviewLevel, -nRowNbr, -nColNbr);
1305 : }
1306 : // CPLDebug("OpenFileGDB", "Request %s", osFilter.c_str());
1307 118 : poLyr->SetAttributeFilter(osFilter.c_str());
1308 236 : auto poFeature = std::unique_ptr<OGRFeature>(poLyr->GetNextFeature());
1309 118 : const int nImageDTSize = GDALGetDataTypeSizeBytes(eImageDT);
1310 118 : if (!poFeature)
1311 : {
1312 : // Missing blocks are legit
1313 1 : FillMissingBlock();
1314 1 : return CE_None;
1315 : }
1316 117 : const int nFieldIdx = poFeature->GetFieldIndex("block_data");
1317 117 : CPLAssert(nFieldIdx >= 0);
1318 117 : int nInBytes = 0;
1319 117 : if (!poFeature->IsFieldSetAndNotNull(nFieldIdx))
1320 : {
1321 : // block_data unset found on ForestFalls.gdb
1322 0 : FillMissingBlock();
1323 0 : return CE_None;
1324 : }
1325 117 : const GByte *pabyData = poFeature->GetFieldAsBinary(nFieldIdx, &nInBytes);
1326 117 : if (nInBytes == 0)
1327 : {
1328 0 : CPLError(CE_Failure, CPLE_AppDefined, "Image block is empty");
1329 0 : return CE_Failure;
1330 : }
1331 :
1332 : // The input buffer may be concatenated with a 1-bit binary mask
1333 117 : const size_t nImageSize = nPixels * nImageDTSize;
1334 117 : const int nImageBitWidth =
1335 117 : m_poMainBand ? m_poMainBand->m_nBitWidth : m_nBitWidth;
1336 117 : const size_t nImageSizePacked = (nPixels * nImageBitWidth + 7) / 8;
1337 117 : const size_t nBinaryMaskSize = (nPixels + 7) / 8;
1338 117 : const size_t nImageSizeWithBinaryMask = nImageSizePacked + nBinaryMaskSize;
1339 :
1340 : // Unpack 1-bit, 2-bit, 4-bit data to full byte
1341 : const auto ExpandSubByteData =
1342 2175500 : [nPixels, nImageBitWidth](const GByte *pabyInput, void *pDstBuffer)
1343 : {
1344 24 : CPLAssert(nImageBitWidth < 8);
1345 :
1346 24 : size_t iBitOffset = 0;
1347 393240 : for (size_t i = 0; i < nPixels; ++i)
1348 : {
1349 393216 : unsigned nOutWord = 0;
1350 :
1351 1523710 : for (int iBit = 0; iBit < nImageBitWidth; ++iBit)
1352 : {
1353 1130500 : if (pabyInput[iBitOffset >> 3] & (0x80 >> (iBitOffset & 7)))
1354 : {
1355 258524 : nOutWord |= (1 << (nImageBitWidth - 1 - iBit));
1356 : }
1357 1130500 : ++iBitOffset;
1358 : }
1359 :
1360 393216 : static_cast<GByte *>(pDstBuffer)[i] = static_cast<GByte>(nOutWord);
1361 : }
1362 141 : };
1363 :
1364 117 : const GByte *pabyMask = nullptr;
1365 116 : auto &abyTmpBuffer =
1366 117 : m_poMainBand ? m_poMainBand->m_abyTmpBuffer : m_abyTmpBuffer;
1367 :
1368 117 : switch (poGDS->m_eRasterCompression)
1369 : {
1370 6 : case OGROpenFileGDBDataSource::Compression::NONE:
1371 : {
1372 6 : if (static_cast<unsigned>(nInBytes) != nImageSizePacked &&
1373 6 : static_cast<unsigned>(nInBytes) != nImageSizeWithBinaryMask)
1374 : {
1375 0 : CPLError(CE_Failure, CPLE_AppDefined,
1376 : "Not expected number of input bytes: %d", nInBytes);
1377 0 : return CE_Failure;
1378 : }
1379 :
1380 6 : auto imageDataAndBlock = GetImageData();
1381 6 : auto pImageData = imageDataAndBlock.first;
1382 6 : auto poBlock = imageDataAndBlock.second;
1383 :
1384 6 : if (pImageData)
1385 : {
1386 6 : if (nImageSizePacked == nImageSize)
1387 : {
1388 6 : memcpy(pImageData, pabyData, nImageSize);
1389 : #ifdef CPL_LSB
1390 6 : if (nImageDTSize > 1)
1391 : {
1392 5 : GDALSwapWordsEx(pImageData, nImageDTSize, nPixels,
1393 : nImageDTSize);
1394 : }
1395 : #endif
1396 : }
1397 : else
1398 : {
1399 0 : ExpandSubByteData(pabyData, pImageData);
1400 : }
1401 : }
1402 6 : if (poBlock)
1403 0 : poBlock->DropLock();
1404 :
1405 6 : if (static_cast<unsigned>(nInBytes) == nImageSizeWithBinaryMask)
1406 6 : pabyMask = pabyData + nImageSizePacked;
1407 6 : break;
1408 : }
1409 :
1410 63 : case OGROpenFileGDBDataSource::Compression::LZ77:
1411 : {
1412 63 : if (abyTmpBuffer.empty())
1413 : {
1414 : try
1415 : {
1416 18 : abyTmpBuffer.resize(nImageSizeWithBinaryMask);
1417 : }
1418 0 : catch (const std::bad_alloc &e)
1419 : {
1420 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1421 0 : return CE_Failure;
1422 : }
1423 : }
1424 :
1425 63 : size_t nOutBytes = 0;
1426 63 : GByte *outPtr = abyTmpBuffer.data();
1427 63 : assert(outPtr != nullptr); // For Coverity Scan
1428 63 : if (!CPLZLibInflate(pabyData, nInBytes, outPtr, abyTmpBuffer.size(),
1429 126 : &nOutBytes) ||
1430 63 : !(nOutBytes == nImageSizePacked ||
1431 48 : nOutBytes == nImageSizeWithBinaryMask))
1432 : {
1433 0 : CPLError(
1434 : CE_Failure, CPLE_AppDefined,
1435 : "CPLZLibInflate() failed: nInBytes = %u, nOutBytes = %u, "
1436 : "nImageSizePacked = %u, "
1437 : "nImageSizeWithBinaryMask = %u",
1438 : unsigned(nInBytes), unsigned(nOutBytes),
1439 : unsigned(nImageSizePacked),
1440 : unsigned(nImageSizeWithBinaryMask));
1441 0 : return CE_Failure;
1442 : }
1443 :
1444 63 : auto imageDataAndBlock = GetImageData();
1445 63 : auto pImageData = imageDataAndBlock.first;
1446 63 : auto poBlock = imageDataAndBlock.second;
1447 :
1448 63 : if (pImageData)
1449 : {
1450 63 : if (nImageSizePacked == nImageSize)
1451 : {
1452 39 : memcpy(pImageData, abyTmpBuffer.data(), nImageSize);
1453 : #ifdef CPL_LSB
1454 39 : if (nImageDTSize > 1)
1455 : {
1456 6 : GDALSwapWordsEx(pImageData, nImageDTSize, nPixels,
1457 : nImageDTSize);
1458 : }
1459 : #endif
1460 : }
1461 : else
1462 : {
1463 24 : ExpandSubByteData(abyTmpBuffer.data(), pImageData);
1464 : }
1465 : }
1466 63 : if (poBlock)
1467 1 : poBlock->DropLock();
1468 :
1469 63 : if (nOutBytes == nImageSizeWithBinaryMask)
1470 48 : pabyMask = abyTmpBuffer.data() + nImageSizePacked;
1471 63 : break;
1472 : }
1473 :
1474 24 : case OGROpenFileGDBDataSource::Compression::JPEG:
1475 : {
1476 24 : if (GDALGetDriverByName("JPEG") == nullptr)
1477 : {
1478 0 : CPLError(CE_Failure, CPLE_AppDefined, "JPEG driver missing");
1479 0 : return CE_Failure;
1480 : }
1481 :
1482 24 : if (static_cast<unsigned>(nInBytes) < 5)
1483 : {
1484 0 : CPLError(CE_Failure, CPLE_AppDefined,
1485 : "Not expected number of input bytes: %d", nInBytes);
1486 0 : return CE_Failure;
1487 : }
1488 24 : uint32_t nJPEGSize = nInBytes - 1;
1489 24 : uint32_t nJPEGOffset = 1;
1490 24 : if (pabyData[0] == 0xFE)
1491 : {
1492 : // JPEG followed by binary mask
1493 15 : memcpy(&nJPEGSize, pabyData + 1, sizeof(uint32_t));
1494 15 : CPL_LSBPTR32(&nJPEGSize);
1495 15 : if (nJPEGSize > static_cast<unsigned>(nInBytes - 5))
1496 : {
1497 0 : CPLError(CE_Failure, CPLE_AppDefined,
1498 : "Invalid nJPEGSize = %u", nJPEGSize);
1499 0 : return CE_Failure;
1500 : }
1501 15 : nJPEGOffset = 5;
1502 :
1503 15 : if (abyTmpBuffer.empty())
1504 : {
1505 : try
1506 : {
1507 3 : abyTmpBuffer.resize(nBinaryMaskSize);
1508 : }
1509 0 : catch (const std::bad_alloc &e)
1510 : {
1511 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1512 0 : return CE_Failure;
1513 : }
1514 : }
1515 15 : size_t nOutBytes = 0;
1516 15 : GByte *outPtr = abyTmpBuffer.data();
1517 15 : assert(outPtr != nullptr); // For Coverity Scan
1518 45 : if (CPLZLibInflate(pabyData + 5 + nJPEGSize,
1519 15 : nInBytes - 5 - nJPEGSize, outPtr,
1520 30 : nBinaryMaskSize, &nOutBytes) &&
1521 15 : nOutBytes == nBinaryMaskSize)
1522 : {
1523 15 : pabyMask = abyTmpBuffer.data();
1524 : }
1525 : else
1526 : {
1527 0 : CPLError(CE_Warning, CPLE_AppDefined,
1528 : "Cannot decompress binary mask");
1529 : }
1530 : }
1531 9 : else if (pabyData[0] != 1)
1532 : {
1533 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid JPEG blob");
1534 0 : return CE_Failure;
1535 : }
1536 :
1537 : const CPLString osTmpFilename(
1538 24 : VSIMemGenerateHiddenFilename("openfilegdb.jpg"));
1539 24 : VSIFCloseL(VSIFileFromMemBuffer(
1540 : osTmpFilename.c_str(),
1541 24 : const_cast<GByte *>(pabyData + nJPEGOffset), nJPEGSize, false));
1542 24 : const char *const apszDrivers[] = {"JPEG", nullptr};
1543 : auto poJPEGDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1544 24 : osTmpFilename.c_str(), GDAL_OF_RASTER, apszDrivers));
1545 24 : if (!poJPEGDS)
1546 : {
1547 0 : VSIUnlink(osTmpFilename.c_str());
1548 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open JPEG blob");
1549 0 : return CE_Failure;
1550 : }
1551 24 : if (poJPEGDS->GetRasterCount() != 1 ||
1552 48 : poJPEGDS->GetRasterXSize() != nBlockXSize ||
1553 24 : poJPEGDS->GetRasterYSize() != nBlockYSize)
1554 : {
1555 0 : VSIUnlink(osTmpFilename.c_str());
1556 0 : CPLError(CE_Failure, CPLE_AppDefined,
1557 : "Inconsistent characteristics of JPEG blob");
1558 0 : return CE_Failure;
1559 : }
1560 :
1561 24 : auto imageDataAndBlock = GetImageData();
1562 24 : auto pImageData = imageDataAndBlock.first;
1563 24 : auto poBlock = imageDataAndBlock.second;
1564 :
1565 : const CPLErr eErr =
1566 : pImageData
1567 24 : ? poJPEGDS->GetRasterBand(1)->RasterIO(
1568 : GF_Read, 0, 0, nBlockXSize, nBlockYSize, pImageData,
1569 : nBlockXSize, nBlockYSize, eImageDT, 0, 0, nullptr)
1570 24 : : CE_None;
1571 24 : VSIUnlink(osTmpFilename.c_str());
1572 24 : if (poBlock)
1573 0 : poBlock->DropLock();
1574 :
1575 24 : if (eErr != CE_None)
1576 : {
1577 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot read JPEG blob");
1578 0 : return CE_Failure;
1579 : }
1580 :
1581 24 : break;
1582 : }
1583 :
1584 24 : case OGROpenFileGDBDataSource::Compression::JPEG2000:
1585 : {
1586 24 : const char *const apszDrivers[] = {"JP2KAK", "JP2ECW",
1587 : "JP2OpenJPEG", "JP2MrSID",
1588 : "JP2Lura", nullptr};
1589 24 : bool bFoundJP2Driver = false;
1590 48 : for (const char *pszDriver : apszDrivers)
1591 : {
1592 48 : if (pszDriver && GDALGetDriverByName(pszDriver))
1593 : {
1594 24 : bFoundJP2Driver = true;
1595 24 : break;
1596 : }
1597 : }
1598 24 : if (!bFoundJP2Driver)
1599 : {
1600 0 : CPLError(CE_Failure, CPLE_AppDefined,
1601 : "Did not find any JPEG2000 capable driver");
1602 0 : return CE_Failure;
1603 : }
1604 :
1605 24 : if (static_cast<unsigned>(nInBytes) < 5)
1606 : {
1607 0 : CPLError(CE_Failure, CPLE_AppDefined,
1608 : "Not expected number of input bytes: %d", nInBytes);
1609 0 : return CE_Failure;
1610 : }
1611 24 : uint32_t nJPEGSize = nInBytes - 1;
1612 24 : uint32_t nJPEGOffset = 1;
1613 24 : if (pabyData[0] == 0xFF)
1614 : {
1615 : // JPEG2000 followed by binary mask
1616 15 : memcpy(&nJPEGSize, pabyData + 1, sizeof(uint32_t));
1617 15 : CPL_LSBPTR32(&nJPEGSize);
1618 15 : if (nJPEGSize > static_cast<unsigned>(nInBytes - 5))
1619 : {
1620 0 : CPLError(CE_Failure, CPLE_AppDefined,
1621 : "Invalid nJPEGSize = %u", nJPEGSize);
1622 0 : return CE_Failure;
1623 : }
1624 15 : nJPEGOffset = 5;
1625 :
1626 15 : if (abyTmpBuffer.empty())
1627 : {
1628 : try
1629 : {
1630 3 : abyTmpBuffer.resize(nBinaryMaskSize);
1631 : }
1632 0 : catch (const std::bad_alloc &e)
1633 : {
1634 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1635 0 : return CE_Failure;
1636 : }
1637 : }
1638 15 : size_t nOutBytes = 0;
1639 15 : GByte *outPtr = abyTmpBuffer.data();
1640 15 : assert(outPtr != nullptr); // For Coverity Scan
1641 45 : if (CPLZLibInflate(pabyData + 5 + nJPEGSize,
1642 15 : nInBytes - 5 - nJPEGSize, outPtr,
1643 30 : nBinaryMaskSize, &nOutBytes) &&
1644 15 : nOutBytes == nBinaryMaskSize)
1645 : {
1646 15 : pabyMask = outPtr;
1647 : }
1648 : else
1649 : {
1650 0 : CPLError(CE_Warning, CPLE_AppDefined,
1651 : "Cannot decompress binary mask");
1652 : }
1653 : }
1654 9 : else if (pabyData[0] != 0)
1655 : {
1656 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid JPEG2000 blob");
1657 0 : return CE_Failure;
1658 : }
1659 :
1660 : const CPLString osTmpFilename(
1661 24 : VSIMemGenerateHiddenFilename("openfilegdb.j2k"));
1662 24 : VSIFCloseL(VSIFileFromMemBuffer(
1663 : osTmpFilename.c_str(),
1664 24 : const_cast<GByte *>(pabyData + nJPEGOffset), nJPEGSize, false));
1665 : auto poJP2KDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1666 24 : osTmpFilename.c_str(), GDAL_OF_RASTER, apszDrivers));
1667 24 : if (!poJP2KDS)
1668 : {
1669 0 : VSIUnlink(osTmpFilename.c_str());
1670 0 : CPLError(CE_Failure, CPLE_AppDefined,
1671 : "Cannot open JPEG2000 blob");
1672 0 : return CE_Failure;
1673 : }
1674 24 : if (poJP2KDS->GetRasterCount() != 1 ||
1675 48 : poJP2KDS->GetRasterXSize() != nBlockXSize ||
1676 24 : poJP2KDS->GetRasterYSize() != nBlockYSize)
1677 : {
1678 0 : VSIUnlink(osTmpFilename.c_str());
1679 0 : CPLError(CE_Failure, CPLE_AppDefined,
1680 : "Inconsistent characteristics of JPEG2000 blob");
1681 0 : return CE_Failure;
1682 : }
1683 :
1684 24 : auto imageDataAndBlock = GetImageData();
1685 24 : auto pImageData = imageDataAndBlock.first;
1686 24 : auto poBlock = imageDataAndBlock.second;
1687 :
1688 : const CPLErr eErr =
1689 : pImageData
1690 24 : ? poJP2KDS->GetRasterBand(1)->RasterIO(
1691 : GF_Read, 0, 0, nBlockXSize, nBlockYSize, pImageData,
1692 : nBlockXSize, nBlockYSize, eImageDT, 0, 0, nullptr)
1693 24 : : CE_None;
1694 24 : VSIUnlink(osTmpFilename.c_str());
1695 24 : if (poBlock)
1696 0 : poBlock->DropLock();
1697 :
1698 24 : if (eErr != CE_None)
1699 : {
1700 0 : CPLError(CE_Failure, CPLE_AppDefined,
1701 : "Cannot read JPEG2000 blob");
1702 0 : return CE_Failure;
1703 : }
1704 :
1705 24 : break;
1706 : }
1707 : }
1708 :
1709 117 : if (m_bIsMask || m_poMaskBand)
1710 : {
1711 114 : auto maskDataAndBlock = GetMaskData();
1712 114 : auto pMaskData = maskDataAndBlock.first;
1713 114 : auto poBlock = maskDataAndBlock.second;
1714 :
1715 114 : if (pMaskData)
1716 : {
1717 64 : if (pabyMask)
1718 : {
1719 : // Unpack 1-bit array
1720 802865 : for (size_t i = 0; i < nPixels; ++i)
1721 : {
1722 802816 : static_cast<GByte *>(pMaskData)[i] =
1723 802816 : (pabyMask[i / 8] & (0x80 >> (i & 7))) ? 255 : 0;
1724 : }
1725 : }
1726 : else
1727 : {
1728 : // No explicit mask in source block --> all valid
1729 15 : memset(pMaskData, 255, nPixels);
1730 : }
1731 : }
1732 :
1733 114 : if (poBlock)
1734 114 : poBlock->DropLock();
1735 : }
1736 3 : else if (m_bHasNoData)
1737 : {
1738 3 : if (eImageDT == GDT_UInt8)
1739 : {
1740 0 : SetNoDataFromMask<uint8_t>(pImage, pabyMask, nPixels, m_dfNoData);
1741 : }
1742 3 : else if (eImageDT == GDT_Int8)
1743 : {
1744 0 : SetNoDataFromMask<int8_t>(pImage, pabyMask, nPixels, m_dfNoData);
1745 : }
1746 3 : else if (eImageDT == GDT_UInt16)
1747 : {
1748 0 : SetNoDataFromMask<uint16_t>(pImage, pabyMask, nPixels, m_dfNoData);
1749 : }
1750 3 : else if (eImageDT == GDT_Int16)
1751 : {
1752 0 : SetNoDataFromMask<int16_t>(pImage, pabyMask, nPixels, m_dfNoData);
1753 : }
1754 3 : else if (eImageDT == GDT_UInt32)
1755 : {
1756 0 : SetNoDataFromMask<uint32_t>(pImage, pabyMask, nPixels, m_dfNoData);
1757 : }
1758 3 : else if (eImageDT == GDT_Int32)
1759 : {
1760 0 : SetNoDataFromMask<int32_t>(pImage, pabyMask, nPixels, m_dfNoData);
1761 : }
1762 3 : else if (eImageDT == GDT_Float32)
1763 : {
1764 2 : if (pabyMask)
1765 : {
1766 32770 : for (size_t i = 0; i < nPixels; ++i)
1767 : {
1768 32768 : if (!(pabyMask[i / 8] & (0x80 >> (i & 7))))
1769 : {
1770 31968 : static_cast<float *>(pImage)[i] =
1771 31968 : static_cast<float>(m_dfNoData);
1772 : }
1773 : }
1774 : }
1775 : }
1776 1 : else if (eImageDT == GDT_Float64)
1777 : {
1778 1 : if (pabyMask)
1779 : {
1780 16385 : for (size_t i = 0; i < nPixels; ++i)
1781 : {
1782 16384 : if (!(pabyMask[i / 8] & (0x80 >> (i & 7))))
1783 : {
1784 15984 : static_cast<double *>(pImage)[i] = m_dfNoData;
1785 : }
1786 : }
1787 : }
1788 : }
1789 : else
1790 : {
1791 0 : CPLAssert(false);
1792 : }
1793 : }
1794 :
1795 : #if 0
1796 : printf("Data:\n"); // ok
1797 : if (eDataType == GDT_UInt8)
1798 : {
1799 : for (int y = 0; y < nBlockYSize; ++y)
1800 : {
1801 : for (int x = 0; x < nBlockXSize; ++x)
1802 : {
1803 : printf("%d ", // ok
1804 : static_cast<GByte *>(pImage)[y * nBlockXSize + x]);
1805 : }
1806 : printf("\n"); // ok
1807 : }
1808 : }
1809 : else if (eDataType == GDT_Int8)
1810 : {
1811 : for (int y = 0; y < nBlockYSize; ++y)
1812 : {
1813 : for (int x = 0; x < nBlockXSize; ++x)
1814 : {
1815 : printf("%d ", // ok
1816 : static_cast<int8_t *>(pImage)[y * nBlockXSize + x]);
1817 : }
1818 : printf("\n"); // ok
1819 : }
1820 : }
1821 : else if (eDataType == GDT_UInt16)
1822 : {
1823 : for (int y = 0; y < nBlockYSize; ++y)
1824 : {
1825 : for (int x = 0; x < nBlockXSize; ++x)
1826 : {
1827 : printf("%d ", // ok
1828 : static_cast<uint16_t *>(pImage)[y * nBlockXSize + x]);
1829 : }
1830 : printf("\n"); // ok
1831 : }
1832 : }
1833 : else if (eDataType == GDT_Int16)
1834 : {
1835 : for (int y = 0; y < nBlockYSize; ++y)
1836 : {
1837 : for (int x = 0; x < nBlockXSize; ++x)
1838 : {
1839 : printf("%d ", // ok
1840 : static_cast<int16_t *>(pImage)[y * nBlockXSize + x]);
1841 : }
1842 : printf("\n"); // ok
1843 : }
1844 : }
1845 : else if (eDataType == GDT_UInt32)
1846 : {
1847 : for (int y = 0; y < nBlockYSize; ++y)
1848 : {
1849 : for (int x = 0; x < nBlockXSize; ++x)
1850 : {
1851 : printf("%d ", // ok
1852 : static_cast<uint32_t *>(pImage)[y * nBlockXSize + x]);
1853 : }
1854 : printf("\n"); // ok
1855 : }
1856 : }
1857 : else if (eDataType == GDT_Int32)
1858 : {
1859 : for (int y = 0; y < nBlockYSize; ++y)
1860 : {
1861 : for (int x = 0; x < nBlockXSize; ++x)
1862 : {
1863 : printf("%d ", // ok
1864 : static_cast<int32_t *>(pImage)[y * nBlockXSize + x]);
1865 : }
1866 : printf("\n"); // ok
1867 : }
1868 : }
1869 : else if (eDataType == GDT_Float32)
1870 : {
1871 : for (int y = 0; y < nBlockYSize; ++y)
1872 : {
1873 : for (int x = 0; x < nBlockXSize; ++x)
1874 : {
1875 : printf("%.8g ", // ok
1876 : static_cast<float *>(pImage)[y * nBlockXSize + x]);
1877 : }
1878 : printf("\n"); // ok
1879 : }
1880 : }
1881 : else if (eDataType == GDT_Float64)
1882 : {
1883 : for (int y = 0; y < nBlockYSize; ++y)
1884 : {
1885 : for (int x = 0; x < nBlockXSize; ++x)
1886 : {
1887 : printf("%.17g ", // ok
1888 : static_cast<double *>(pImage)[y * nBlockXSize + x]);
1889 : }
1890 : printf("\n"); // ok
1891 : }
1892 : }
1893 : #endif
1894 :
1895 : #if 0
1896 : if (pabyMask)
1897 : {
1898 : printf("Mask:\n"); // ok
1899 : for (int y = 0; y < nBlockYSize; ++y)
1900 : {
1901 : for (int x = 0; x < nBlockXSize; ++x)
1902 : {
1903 : printf("%d ", // ok
1904 : (pabyMask[(y * nBlockXSize + x) / 8] &
1905 : (0x80 >> ((y * nBlockXSize + x) & 7)))
1906 : ? 1
1907 : : 0);
1908 : }
1909 : printf("\n"); // ok
1910 : }
1911 : }
1912 : #endif
1913 :
1914 117 : return CE_None;
1915 : }
1916 :
1917 : /************************************************************************/
1918 : /* GetDefaultRAT() */
1919 : /************************************************************************/
1920 :
1921 3 : GDALRasterAttributeTable *GDALOpenFileGDBRasterBand::GetDefaultRAT()
1922 : {
1923 3 : if (m_poRAT)
1924 1 : return m_poRAT.get();
1925 2 : if (poDS->GetRasterCount() > 1 || m_bIsMask)
1926 0 : return nullptr;
1927 2 : auto poGDS = cpl::down_cast<OGROpenFileGDBDataSource *>(poDS);
1928 : const std::string osVATTableName(
1929 6 : std::string("VAT_").append(poGDS->m_osRasterLayerName));
1930 : // Instantiate a new dataset, os that the RAT is standalone
1931 4 : auto poDSNew = std::make_unique<OGROpenFileGDBDataSource>();
1932 4 : GDALOpenInfo oOpenInfo(poGDS->m_osDirName.c_str(), GA_ReadOnly);
1933 2 : bool bRetryFileGDBUnused = false;
1934 2 : if (!poDSNew->Open(&oOpenInfo, bRetryFileGDBUnused))
1935 0 : return nullptr;
1936 4 : auto poVatLayer = poDSNew->BuildLayerFromName(osVATTableName.c_str());
1937 2 : if (!poVatLayer)
1938 1 : return nullptr;
1939 2 : m_poRAT = std::make_unique<GDALOpenFileGDBRasterAttributeTable>(
1940 2 : std::move(poDSNew), osVATTableName, std::move(poVatLayer));
1941 1 : return m_poRAT.get();
1942 : }
1943 :
1944 : /************************************************************************/
1945 : /* GDALOpenFileGDBRasterAttributeTable::Clone() */
1946 : /************************************************************************/
1947 :
1948 1 : GDALRasterAttributeTable *GDALOpenFileGDBRasterAttributeTable::Clone() const
1949 : {
1950 2 : auto poDS = std::make_unique<OGROpenFileGDBDataSource>();
1951 2 : GDALOpenInfo oOpenInfo(m_poDS->m_osDirName.c_str(), GA_ReadOnly);
1952 1 : bool bRetryFileGDBUnused = false;
1953 1 : if (!poDS->Open(&oOpenInfo, bRetryFileGDBUnused))
1954 0 : return nullptr;
1955 2 : auto poVatLayer = poDS->BuildLayerFromName(m_osVATTableName.c_str());
1956 1 : if (!poVatLayer)
1957 0 : return nullptr;
1958 : return new GDALOpenFileGDBRasterAttributeTable(
1959 1 : std::move(poDS), m_osVATTableName, std::move(poVatLayer));
1960 : }
|