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