Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Hierarchical Data Format Release 5 (HDF5)
4 : * Purpose: Read BAG datasets.
5 : * Author: Frank Warmerdam <warmerdam@pobox.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2009-2018, Even Rouault <even dot rouault at spatialys dot com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "hdf5dataset.h"
16 : #include "hdf5drivercore.h"
17 : #include "gh5_convenience.h"
18 :
19 : #include "cpl_mem_cache.h"
20 : #include "cpl_string.h"
21 : #include "cpl_time.h"
22 : #include "gdal_alg.h"
23 : #include "gdal_frmts.h"
24 : #include "gdal_pam.h"
25 : #include "gdal_priv.h"
26 : #include "gdal_rat.h"
27 : #include "iso19115_srs.h"
28 : #include "ogr_core.h"
29 : #include "ogr_spatialref.h"
30 : #include "ogrsf_frmts.h"
31 : #include "rat.h"
32 :
33 : #ifdef EMBED_RESOURCE_FILES
34 : #include "embedded_resources.h"
35 : #endif
36 :
37 : #include <cassert>
38 : #include <cmath>
39 : #include <algorithm>
40 : #include <limits>
41 : #include <map>
42 : #include <utility>
43 : #include <set>
44 :
45 : struct BAGRefinementGrid
46 : {
47 : unsigned nIndex = 0;
48 : unsigned nWidth = 0;
49 : unsigned nHeight = 0;
50 : float fResX = 0.0f;
51 : float fResY = 0.0f;
52 : float fSWX = 0.0f; // offset from (bottom left corner of) the south west
53 : // low resolution grid, in center-pixel convention
54 : float fSWY = 0.0f; // offset from (bottom left corner of) the south west
55 : // low resolution grid, in center-pixel convention
56 : };
57 :
58 : constexpr float fDEFAULT_NODATA = 1000000.0f;
59 :
60 : /************************************************************************/
61 : /* ==================================================================== */
62 : /* BAGDataset */
63 : /* ==================================================================== */
64 : /************************************************************************/
65 : class BAGDataset final : public GDALPamDataset
66 : {
67 : friend class BAGRasterBand;
68 : friend class BAGSuperGridBand;
69 : friend class BAGBaseBand;
70 : friend class BAGResampledBand;
71 : friend class BAGGeorefMDSuperGridBand;
72 : friend class BAGInterpolatedBand;
73 :
74 : bool m_bReportVertCRS = true;
75 :
76 : enum class Population
77 : {
78 : MAX,
79 : MIN,
80 : MEAN,
81 : COUNT
82 : };
83 : Population m_ePopulation = Population::MAX;
84 : bool m_bMask = false;
85 :
86 : bool m_bIsChild = false;
87 : std::vector<std::unique_ptr<BAGDataset>> m_apoOverviewDS{};
88 :
89 : std::shared_ptr<GDAL::HDF5SharedResources> m_poSharedResources{};
90 : std::shared_ptr<GDALGroup> m_poRootGroup{};
91 :
92 : std::unique_ptr<OGRLayer> m_poTrackingListLayer{};
93 :
94 : OGRSpatialReference m_oSRS{};
95 : GDALGeoTransform m_gt{};
96 :
97 : int m_nLowResWidth = 0;
98 : int m_nLowResHeight = 0;
99 :
100 : double m_dfLowResMinX = 0.0;
101 : double m_dfLowResMinY = 0.0;
102 : double m_dfLowResMaxX = 0.0;
103 : double m_dfLowResMaxY = 0.0;
104 :
105 : void LoadMetadata();
106 :
107 : char *pszXMLMetadata = nullptr;
108 : char *apszMDList[2]{};
109 :
110 : int m_nChunkXSizeVarresMD = 0;
111 : int m_nChunkYSizeVarresMD = 0;
112 : void GetVarresMetadataChunkSizes(int &nChunkXSize, int &nChunkYSize);
113 :
114 : unsigned m_nChunkSizeVarresRefinement = 0;
115 : void GetVarresRefinementChunkSize(unsigned &nChunkSize);
116 :
117 : bool ReadVarresMetadataValue(int y, int x, hid_t memspace,
118 : BAGRefinementGrid *rgrid, int height,
119 : int width);
120 :
121 : bool LookForRefinementGrids(CSLConstList l_papszOpenOptions, int nY,
122 : int nX);
123 :
124 : hid_t m_hVarresMetadata = -1;
125 : hid_t m_hVarresMetadataDataType = -1;
126 : hid_t m_hVarresMetadataDataspace = -1;
127 : hid_t m_hVarresMetadataNative = -1;
128 : std::map<int, BAGRefinementGrid> m_oMapRefinemendGrids{};
129 :
130 : CPLStringList m_aosSubdatasets{};
131 :
132 : hid_t m_hVarresRefinements = -1;
133 : hid_t m_hVarresRefinementsDataType = -1;
134 : hid_t m_hVarresRefinementsDataspace = -1;
135 : hid_t m_hVarresRefinementsNative = -1;
136 : unsigned m_nRefinementsSize = 0;
137 :
138 : unsigned m_nSuperGridRefinementStartIndex = 0;
139 :
140 : lru11::Cache<unsigned, std::vector<float>> m_oCacheRefinementValues{};
141 : const float *GetRefinementValues(unsigned nRefinementIndex);
142 :
143 : bool GetMeanSupergridsResolution(double &dfResX, double &dfResY);
144 :
145 : double m_dfResFilterMin = 0;
146 : double m_dfResFilterMax = std::numeric_limits<double>::infinity();
147 :
148 : void InitOverviewDS(BAGDataset *poParentDS, int nXSize, int nYSize);
149 :
150 : bool m_bMetadataWritten = false;
151 : CPLStringList m_aosCreationOptions{};
152 : bool WriteMetadataIfNeeded();
153 :
154 : bool OpenRaster(GDALOpenInfo *poOpenInfo, const CPLString &osFilename,
155 : bool bOpenSuperGrid, int nX, int nY, bool bIsSubdataset,
156 : const CPLString &osGeorefMetadataLayer,
157 : CPLString &outOsSubDsName);
158 : bool OpenVector();
159 :
160 154 : inline hid_t GetHDF5Handle()
161 : {
162 154 : return m_poSharedResources->m_hHDF5;
163 : }
164 :
165 : CPL_DISALLOW_COPY_ASSIGN(BAGDataset)
166 :
167 : public:
168 : BAGDataset();
169 : BAGDataset(BAGDataset *poParentDS, int nOvrFactor);
170 : BAGDataset(BAGDataset *poParentDS, int nXSize, int nYSize);
171 : ~BAGDataset() override;
172 :
173 : CPLErr GetGeoTransform(GDALGeoTransform >) const override;
174 : const OGRSpatialReference *GetSpatialRef() const override;
175 :
176 : CPLErr SetGeoTransform(const GDALGeoTransform >) override;
177 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
178 :
179 : char **GetMetadataDomainList() override;
180 : char **GetMetadata(const char *pszDomain = "") override;
181 :
182 139 : int GetLayerCount() const override
183 : {
184 139 : return m_poTrackingListLayer ? 1 : 0;
185 : }
186 :
187 : const OGRLayer *GetLayer(int idx) const override;
188 :
189 : static GDALDataset *Open(GDALOpenInfo *);
190 : static GDALDataset *OpenForCreate(GDALOpenInfo *, int nXSizeIn,
191 : int nYSizeIn, int nBandsIn,
192 : CSLConstList papszCreationOptions);
193 : static GDALDataset *CreateCopy(const char *pszFilename,
194 : GDALDataset *poSrcDS, int bStrict,
195 : char **papszOptions,
196 : GDALProgressFunc pfnProgress,
197 : void *pProgressData);
198 : static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
199 : int nBandsIn, GDALDataType eType,
200 : char **papszOptions);
201 :
202 : OGRErr ParseWKTFromXML(const char *pszISOXML);
203 : };
204 :
205 : /************************************************************************/
206 : /* BAGCreator */
207 : /************************************************************************/
208 :
209 : class BAGCreator
210 : {
211 : hid_t m_hdf5 = -1;
212 : hid_t m_bagRoot = -1;
213 :
214 : bool CreateBase(const char *pszFilename, char **papszOptions);
215 : bool CreateTrackingListDataset();
216 : bool CreateElevationOrUncertainty(
217 : GDALDataset *poSrcDS, int nBand, const char *pszDSName,
218 : const char *pszMaxAttrName, const char *pszMinAttrName,
219 : char **papszOptions, GDALProgressFunc pfnProgress, void *pProgressData);
220 : bool Close();
221 :
222 : public:
223 74 : BAGCreator() = default;
224 : ~BAGCreator();
225 :
226 : static bool SubstituteVariables(CPLXMLNode *psNode, char **papszDict);
227 : static CPLString GenerateMetadata(int nXSize, int nYSize,
228 : const GDALGeoTransform >,
229 : const OGRSpatialReference *poSRS,
230 : char **papszOptions);
231 : static bool CreateAndWriteMetadata(hid_t hdf5,
232 : const CPLString &osXMLMetadata);
233 :
234 : bool Create(const char *pszFilename, GDALDataset *poSrcDS,
235 : char **papszOptions, GDALProgressFunc pfnProgress,
236 : void *pProgressData);
237 :
238 : bool Create(const char *pszFilename, int nBands, GDALDataType eType,
239 : char **papszOptions);
240 : };
241 :
242 : /************************************************************************/
243 : /* ==================================================================== */
244 : /* BAGRasterBand */
245 : /* ==================================================================== */
246 : /************************************************************************/
247 : class BAGRasterBand final : public GDALPamRasterBand
248 : {
249 : friend class BAGDataset;
250 :
251 : hid_t m_hDatasetID = -1;
252 : hid_t m_hNative = -1;
253 : hid_t m_hDataspace = -1;
254 :
255 : bool m_bMinMaxSet = false;
256 : double m_dfMinimum = std::numeric_limits<double>::max();
257 : double m_dfMaximum = -std::numeric_limits<double>::max();
258 :
259 : bool m_bHasNoData = false;
260 : float m_fNoDataValue = std::numeric_limits<float>::quiet_NaN();
261 :
262 : bool CreateDatasetIfNeeded();
263 : void FinalizeDataset();
264 :
265 : public:
266 : BAGRasterBand(BAGDataset *, int);
267 : ~BAGRasterBand() override;
268 :
269 : bool Initialize(hid_t hDataset, const char *pszName);
270 :
271 : CPLErr IReadBlock(int, int, void *) override;
272 : CPLErr IWriteBlock(int, int, void *) override;
273 : double GetNoDataValue(int *) override;
274 : CPLErr SetNoDataValue(double dfNoData) override;
275 :
276 : double GetMinimum(int *pbSuccess = nullptr) override;
277 : double GetMaximum(int *pbSuccess = nullptr) override;
278 : };
279 :
280 : /************************************************************************/
281 : /* ==================================================================== */
282 : /* BAGBaseBand */
283 : /* ==================================================================== */
284 : /************************************************************************/
285 :
286 : class BAGBaseBand CPL_NON_FINAL : public GDALRasterBand
287 : {
288 : protected:
289 114 : BAGBaseBand() = default;
290 :
291 : bool m_bHasNoData = false;
292 : float m_fNoDataValue = std::numeric_limits<float>::quiet_NaN();
293 :
294 : public:
295 : double GetNoDataValue(int *) override;
296 :
297 : int GetOverviewCount() override;
298 : GDALRasterBand *GetOverview(int) override;
299 : };
300 :
301 : /************************************************************************/
302 : /* GetNoDataValue() */
303 : /************************************************************************/
304 :
305 59 : double BAGBaseBand::GetNoDataValue(int *pbSuccess)
306 :
307 : {
308 59 : if (pbSuccess)
309 43 : *pbSuccess = m_bHasNoData;
310 59 : if (m_bHasNoData)
311 58 : return m_fNoDataValue;
312 :
313 1 : return GDALRasterBand::GetNoDataValue(pbSuccess);
314 : }
315 :
316 : /************************************************************************/
317 : /* GetOverviewCount() */
318 : /************************************************************************/
319 :
320 53 : int BAGBaseBand::GetOverviewCount()
321 : {
322 53 : BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
323 53 : return static_cast<int>(poGDS->m_apoOverviewDS.size());
324 : }
325 :
326 : /************************************************************************/
327 : /* GetOverview() */
328 : /************************************************************************/
329 :
330 23 : GDALRasterBand *BAGBaseBand::GetOverview(int i)
331 :
332 : {
333 23 : if (i < 0 || i >= GetOverviewCount())
334 2 : return nullptr;
335 21 : BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
336 21 : return poGDS->m_apoOverviewDS[i]->GetRasterBand(nBand);
337 : }
338 :
339 : /************************************************************************/
340 : /* ==================================================================== */
341 : /* BAGSuperGridBand */
342 : /* ==================================================================== */
343 : /************************************************************************/
344 :
345 28 : class BAGSuperGridBand final : public BAGBaseBand
346 : {
347 : friend class BAGDataset;
348 :
349 : public:
350 : BAGSuperGridBand(BAGDataset *, int, bool bHasNoData, float fNoDataValue);
351 : ~BAGSuperGridBand() override;
352 :
353 : CPLErr IReadBlock(int, int, void *) override;
354 : };
355 :
356 : /************************************************************************/
357 : /* ==================================================================== */
358 : /* BAGResampledBand */
359 : /* ==================================================================== */
360 : /************************************************************************/
361 :
362 180 : class BAGResampledBand final : public BAGBaseBand
363 : {
364 : friend class BAGDataset;
365 :
366 : bool m_bMinMaxSet = false;
367 : double m_dfMinimum = 0.0;
368 : double m_dfMaximum = 0.0;
369 :
370 : public:
371 : BAGResampledBand(BAGDataset *, int nBandIn, bool bHasNoData,
372 : float fNoDataValue, bool bInitializeMinMax);
373 : ~BAGResampledBand() override;
374 :
375 : void InitializeMinMax();
376 :
377 : CPLErr IReadBlock(int, int, void *) override;
378 :
379 : double GetMinimum(int *pbSuccess = nullptr) override;
380 : double GetMaximum(int *pbSuccess = nullptr) override;
381 : };
382 :
383 : /************************************************************************/
384 : /* ==================================================================== */
385 : /* BAGInterpolatedBand */
386 : /* ==================================================================== */
387 : /************************************************************************/
388 :
389 20 : class BAGInterpolatedBand final : public BAGBaseBand
390 : {
391 : friend class BAGDataset;
392 :
393 : bool m_bMinMaxSet = false;
394 : double m_dfMinimum = 0.0;
395 : double m_dfMaximum = 0.0;
396 :
397 : void LoadClosestRefinedNodes(
398 : double dfX, double dfY, int iXRefinedGrid, int iYRefinedGrid,
399 : const std::vector<BAGRefinementGrid> &rgrids, int nLowResMinIdxX,
400 : int nLowResMinIdxY, int nCountLowResX, int nCountLowResY,
401 : double dfLowResMinX, double dfLowResMinY, double dfLowResResX,
402 : double dfLowResResY, std::vector<double> &adfX,
403 : std::vector<double> &adfY, std::vector<float> &afDepth,
404 : std::vector<float> &afUncrt);
405 :
406 : public:
407 : BAGInterpolatedBand(BAGDataset *, int nBandIn, bool bHasNoData,
408 : float fNoDataValue, bool bInitializeMinMax);
409 : ~BAGInterpolatedBand() override;
410 :
411 : void InitializeMinMax();
412 :
413 : CPLErr IReadBlock(int, int, void *) override;
414 :
415 : double GetMinimum(int *pbSuccess = nullptr) override;
416 : double GetMaximum(int *pbSuccess = nullptr) override;
417 : };
418 :
419 : /************************************************************************/
420 : /* BAGRasterBand() */
421 : /************************************************************************/
422 154 : BAGRasterBand::BAGRasterBand(BAGDataset *poDSIn, int nBandIn)
423 : {
424 154 : poDS = poDSIn;
425 154 : nBand = nBandIn;
426 154 : }
427 :
428 : /************************************************************************/
429 : /* ~BAGRasterBand() */
430 : /************************************************************************/
431 :
432 308 : BAGRasterBand::~BAGRasterBand()
433 : {
434 : HDF5_GLOBAL_LOCK();
435 :
436 154 : if (eAccess == GA_Update)
437 : {
438 8 : CreateDatasetIfNeeded();
439 8 : FinalizeDataset();
440 : }
441 :
442 154 : if (m_hDataspace > 0)
443 153 : H5Sclose(m_hDataspace);
444 :
445 154 : if (m_hNative > 0)
446 153 : H5Tclose(m_hNative);
447 :
448 154 : if (m_hDatasetID > 0)
449 153 : H5Dclose(m_hDatasetID);
450 308 : }
451 :
452 : /************************************************************************/
453 : /* Initialize() */
454 : /************************************************************************/
455 :
456 145 : bool BAGRasterBand::Initialize(hid_t hDatasetIDIn, const char *pszName)
457 :
458 : {
459 145 : GDALRasterBand::SetDescription(pszName);
460 :
461 145 : m_hDatasetID = hDatasetIDIn;
462 :
463 145 : const hid_t datatype = H5Dget_type(m_hDatasetID);
464 145 : m_hDataspace = H5Dget_space(m_hDatasetID);
465 145 : const int n_dims = H5Sget_simple_extent_ndims(m_hDataspace);
466 145 : m_hNative = H5Tget_native_type(datatype, H5T_DIR_ASCEND);
467 :
468 145 : eDataType = GH5_GetDataType(m_hNative);
469 :
470 145 : if (n_dims == 2)
471 : {
472 145 : hsize_t dims[2] = {static_cast<hsize_t>(0), static_cast<hsize_t>(0)};
473 145 : hsize_t maxdims[2] = {static_cast<hsize_t>(0), static_cast<hsize_t>(0)};
474 :
475 145 : H5Sget_simple_extent_dims(m_hDataspace, dims, maxdims);
476 :
477 145 : if (dims[0] > INT_MAX || dims[1] > INT_MAX)
478 : {
479 1 : CPLError(CE_Failure, CPLE_NotSupported,
480 : "At least one dimension size exceeds INT_MAX !");
481 1 : return false;
482 : }
483 144 : nRasterXSize = static_cast<int>(dims[1]);
484 144 : nRasterYSize = static_cast<int>(dims[0]);
485 : }
486 : else
487 : {
488 0 : CPLError(CE_Failure, CPLE_AppDefined, "Dataset not of rank 2.");
489 0 : return false;
490 : }
491 :
492 144 : nBlockXSize = nRasterXSize;
493 144 : nBlockYSize = 1;
494 :
495 : // Check for chunksize, and use it as blocksize for optimized reading.
496 144 : const hid_t listid = H5Dget_create_plist(hDatasetIDIn);
497 144 : if (listid > 0)
498 : {
499 144 : if (H5Pget_layout(listid) == H5D_CHUNKED)
500 : {
501 28 : hsize_t panChunkDims[3] = {static_cast<hsize_t>(0),
502 : static_cast<hsize_t>(0),
503 : static_cast<hsize_t>(0)};
504 28 : const int nDimSize = H5Pget_chunk(listid, 3, panChunkDims);
505 28 : nBlockXSize = static_cast<int>(panChunkDims[nDimSize - 1]);
506 28 : nBlockYSize = static_cast<int>(panChunkDims[nDimSize - 2]);
507 : }
508 :
509 144 : H5D_fill_value_t fillType = H5D_FILL_VALUE_UNDEFINED;
510 288 : if (H5Pfill_value_defined(listid, &fillType) >= 0 &&
511 144 : fillType == H5D_FILL_VALUE_USER_DEFINED)
512 : {
513 141 : float fNoDataValue = 0.0f;
514 141 : if (H5Pget_fill_value(listid, H5T_NATIVE_FLOAT, &fNoDataValue) >= 0)
515 : {
516 141 : m_bHasNoData = true;
517 141 : m_fNoDataValue = fNoDataValue;
518 : }
519 : }
520 :
521 144 : int nfilters = H5Pget_nfilters(listid);
522 :
523 144 : char name[120] = {};
524 144 : size_t cd_nelmts = 20;
525 144 : unsigned int cd_values[20] = {};
526 144 : unsigned int flags = 0;
527 172 : for (int i = 0; i < nfilters; i++)
528 : {
529 28 : const H5Z_filter_t filter = H5Pget_filter(
530 : listid, i, &flags, &cd_nelmts, cd_values, sizeof(name), name);
531 28 : if (filter == H5Z_FILTER_DEFLATE)
532 28 : poDS->GDALDataset::SetMetadataItem("COMPRESSION", "DEFLATE",
533 : "IMAGE_STRUCTURE");
534 0 : else if (filter == H5Z_FILTER_NBIT)
535 0 : poDS->GDALDataset::SetMetadataItem("COMPRESSION", "NBIT",
536 : "IMAGE_STRUCTURE");
537 0 : else if (filter == H5Z_FILTER_SCALEOFFSET)
538 0 : poDS->GDALDataset::SetMetadataItem("COMPRESSION", "SCALEOFFSET",
539 : "IMAGE_STRUCTURE");
540 0 : else if (filter == H5Z_FILTER_SZIP)
541 0 : poDS->GDALDataset::SetMetadataItem("COMPRESSION", "SZIP",
542 : "IMAGE_STRUCTURE");
543 : }
544 :
545 144 : H5Pclose(listid);
546 : }
547 :
548 : // Load min/max information.
549 387 : if (EQUAL(pszName, "elevation") &&
550 99 : GH5_FetchAttribute(hDatasetIDIn, "Maximum Elevation Value",
551 243 : m_dfMaximum) &&
552 87 : GH5_FetchAttribute(hDatasetIDIn, "Minimum Elevation Value",
553 87 : m_dfMinimum))
554 : {
555 87 : m_bMinMaxSet = true;
556 : }
557 157 : else if (EQUAL(pszName, "uncertainty") &&
558 43 : GH5_FetchAttribute(hDatasetIDIn, "Maximum Uncertainty Value",
559 100 : m_dfMaximum) &&
560 38 : GH5_FetchAttribute(hDatasetIDIn, "Minimum Uncertainty Value",
561 38 : m_dfMinimum))
562 : {
563 : // Some products where uncertainty band is completely set to nodata
564 : // wrongly declare minimum and maximum to 0.0.
565 38 : if (m_dfMinimum != 0.0 || m_dfMaximum != 0.0)
566 36 : m_bMinMaxSet = true;
567 : }
568 40 : else if (EQUAL(pszName, "nominal_elevation") &&
569 21 : GH5_FetchAttribute(hDatasetIDIn, "max_value", m_dfMaximum) &&
570 2 : GH5_FetchAttribute(hDatasetIDIn, "min_value", m_dfMinimum))
571 : {
572 2 : m_bMinMaxSet = true;
573 : }
574 :
575 144 : return true;
576 : }
577 :
578 : /************************************************************************/
579 : /* CreateDatasetIfNeeded() */
580 : /************************************************************************/
581 :
582 283 : bool BAGRasterBand::CreateDatasetIfNeeded()
583 : {
584 283 : if (m_hDatasetID > 0 || eAccess == GA_ReadOnly)
585 275 : return true;
586 :
587 8 : hsize_t dims[2] = {static_cast<hsize_t>(nRasterYSize),
588 8 : static_cast<hsize_t>(nRasterXSize)};
589 :
590 8 : m_hDataspace = H5_CHECK(H5Screate_simple(2, dims, nullptr));
591 8 : if (m_hDataspace < 0)
592 0 : return false;
593 :
594 8 : BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
595 8 : bool bDeflate = EQUAL(
596 : poGDS->m_aosCreationOptions.FetchNameValueDef("COMPRESS", "DEFLATE"),
597 : "DEFLATE");
598 : int nCompressionLevel =
599 8 : atoi(poGDS->m_aosCreationOptions.FetchNameValueDef("ZLEVEL", "6"));
600 :
601 8 : bool ret = false;
602 8 : hid_t hDataType = -1;
603 8 : hid_t hParams = -1;
604 : do
605 : {
606 8 : hDataType = H5_CHECK(H5Tcopy(H5T_NATIVE_FLOAT));
607 8 : if (hDataType < 0)
608 0 : break;
609 :
610 8 : if (H5_CHECK(H5Tset_order(hDataType, H5T_ORDER_LE)) < 0)
611 0 : break;
612 :
613 8 : hParams = H5_CHECK(H5Pcreate(H5P_DATASET_CREATE));
614 8 : if (hParams < 0)
615 0 : break;
616 :
617 8 : if (H5_CHECK(H5Pset_fill_time(hParams, H5D_FILL_TIME_ALLOC)) < 0)
618 0 : break;
619 :
620 8 : if (H5_CHECK(H5Pset_fill_value(hParams, hDataType, &m_fNoDataValue)) <
621 : 0)
622 0 : break;
623 :
624 8 : if (H5_CHECK(H5Pset_layout(hParams, H5D_CHUNKED)) < 0)
625 0 : break;
626 8 : hsize_t chunk_size[2] = {static_cast<hsize_t>(nBlockYSize),
627 8 : static_cast<hsize_t>(nBlockXSize)};
628 8 : if (H5_CHECK(H5Pset_chunk(hParams, 2, chunk_size)) < 0)
629 0 : break;
630 :
631 8 : if (bDeflate)
632 : {
633 8 : if (H5_CHECK(H5Pset_deflate(hParams, nCompressionLevel)) < 0)
634 0 : break;
635 : }
636 :
637 8 : m_hDatasetID = H5_CHECK(H5Dcreate(poGDS->GetHDF5Handle(),
638 : nBand == 1 ? "/BAG_root/elevation"
639 : : "/BAG_root/uncertainty",
640 : hDataType, m_hDataspace, hParams));
641 8 : if (m_hDatasetID < 0)
642 0 : break;
643 :
644 8 : ret = true;
645 : } while (false);
646 :
647 8 : if (hParams >= 0)
648 8 : H5_CHECK(H5Pclose(hParams));
649 8 : if (hDataType > 0)
650 8 : H5_CHECK(H5Tclose(hDataType));
651 :
652 8 : m_hNative = H5_CHECK(H5Tcopy(H5T_NATIVE_FLOAT));
653 :
654 8 : return ret;
655 : }
656 :
657 : /************************************************************************/
658 : /* FinalizeDataset() */
659 : /************************************************************************/
660 :
661 8 : void BAGRasterBand::FinalizeDataset()
662 : {
663 8 : if (m_dfMinimum > m_dfMaximum)
664 3 : return;
665 :
666 5 : const char *pszMaxAttrName =
667 5 : nBand == 1 ? "Maximum Elevation Value" : "Maximum Uncertainty Value";
668 5 : const char *pszMinAttrName =
669 5 : nBand == 1 ? "Minimum Elevation Value" : "Minimum Uncertainty Value";
670 :
671 5 : if (!GH5_CreateAttribute(m_hDatasetID, pszMaxAttrName, m_hNative))
672 0 : return;
673 :
674 5 : if (!GH5_CreateAttribute(m_hDatasetID, pszMinAttrName, m_hNative))
675 0 : return;
676 :
677 5 : if (!GH5_WriteAttribute(m_hDatasetID, pszMaxAttrName, m_dfMaximum))
678 0 : return;
679 :
680 5 : if (!GH5_WriteAttribute(m_hDatasetID, pszMinAttrName, m_dfMinimum))
681 0 : return;
682 : }
683 :
684 : /************************************************************************/
685 : /* GetMinimum() */
686 : /************************************************************************/
687 :
688 8 : double BAGRasterBand::GetMinimum(int *pbSuccess)
689 :
690 : {
691 8 : if (m_bMinMaxSet)
692 : {
693 8 : if (pbSuccess)
694 8 : *pbSuccess = TRUE;
695 8 : return m_dfMinimum;
696 : }
697 :
698 0 : return GDALRasterBand::GetMinimum(pbSuccess);
699 : }
700 :
701 : /************************************************************************/
702 : /* GetMaximum() */
703 : /************************************************************************/
704 :
705 8 : double BAGRasterBand::GetMaximum(int *pbSuccess)
706 :
707 : {
708 8 : if (m_bMinMaxSet)
709 : {
710 8 : if (pbSuccess)
711 8 : *pbSuccess = TRUE;
712 8 : return m_dfMaximum;
713 : }
714 :
715 0 : return GDALRasterBand::GetMaximum(pbSuccess);
716 : }
717 :
718 : /************************************************************************/
719 : /* GetNoDataValue() */
720 : /************************************************************************/
721 107 : double BAGRasterBand::GetNoDataValue(int *pbSuccess)
722 :
723 : {
724 107 : if (pbSuccess)
725 97 : *pbSuccess = m_bHasNoData;
726 107 : if (m_bHasNoData)
727 107 : return m_fNoDataValue;
728 :
729 0 : return GDALPamRasterBand::GetNoDataValue(pbSuccess);
730 : }
731 :
732 : /************************************************************************/
733 : /* SetNoDataValue() */
734 : /************************************************************************/
735 :
736 4 : CPLErr BAGRasterBand::SetNoDataValue(double dfNoData)
737 : {
738 4 : if (eAccess == GA_ReadOnly)
739 0 : return GDALPamRasterBand::SetNoDataValue(dfNoData);
740 :
741 4 : if (m_hDatasetID > 0)
742 : {
743 0 : CPLError(CE_Failure, CPLE_NotSupported,
744 : "Setting the nodata value after grid values have been written "
745 : "is not supported");
746 0 : return CE_Failure;
747 : }
748 :
749 4 : m_bHasNoData = true;
750 4 : m_fNoDataValue = static_cast<float>(dfNoData);
751 4 : return CE_None;
752 : }
753 :
754 : /************************************************************************/
755 : /* IReadBlock() */
756 : /************************************************************************/
757 252 : CPLErr BAGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
758 : {
759 : HDF5_GLOBAL_LOCK();
760 :
761 252 : if (!CreateDatasetIfNeeded())
762 0 : return CE_Failure;
763 :
764 252 : const int nXOff = nBlockXOff * nBlockXSize;
765 : H5OFFSET_TYPE offset[2] = {
766 252 : static_cast<H5OFFSET_TYPE>(
767 252 : std::max(0, nRasterYSize - (nBlockYOff + 1) * nBlockYSize)),
768 252 : static_cast<H5OFFSET_TYPE>(nXOff)};
769 :
770 252 : const int nSizeOfData = static_cast<int>(H5Tget_size(m_hNative));
771 252 : memset(pImage, 0,
772 252 : static_cast<size_t>(nBlockXSize) * nBlockYSize * nSizeOfData);
773 :
774 : // Blocksize may not be a multiple of imagesize.
775 252 : hsize_t count[3] = {
776 252 : std::min(static_cast<hsize_t>(nBlockYSize), GetYSize() - offset[0]),
777 252 : std::min(static_cast<hsize_t>(nBlockXSize), GetXSize() - offset[1]),
778 252 : static_cast<hsize_t>(0)};
779 :
780 252 : if (nRasterYSize - (nBlockYOff + 1) * nBlockYSize < 0)
781 : {
782 0 : count[0] +=
783 0 : (nRasterYSize - static_cast<hsize_t>(nBlockYOff + 1) * nBlockYSize);
784 : }
785 :
786 : // Select block from file space.
787 : {
788 252 : const herr_t status = H5Sselect_hyperslab(
789 : m_hDataspace, H5S_SELECT_SET, offset, nullptr, count, nullptr);
790 252 : if (status < 0)
791 0 : return CE_Failure;
792 : }
793 :
794 : // Create memory space to receive the data.
795 252 : hsize_t col_dims[2] = {static_cast<hsize_t>(nBlockYSize),
796 252 : static_cast<hsize_t>(nBlockXSize)};
797 252 : const int rank = 2;
798 252 : const hid_t memspace = H5Screate_simple(rank, col_dims, nullptr);
799 252 : H5OFFSET_TYPE mem_offset[2] = {0, 0};
800 252 : const herr_t status = H5Sselect_hyperslab(
801 : memspace, H5S_SELECT_SET, mem_offset, nullptr, count, nullptr);
802 252 : if (status < 0)
803 : {
804 0 : H5Sclose(memspace);
805 0 : return CE_Failure;
806 : }
807 :
808 252 : const herr_t status_read = H5Dread(m_hDatasetID, m_hNative, memspace,
809 : m_hDataspace, H5P_DEFAULT, pImage);
810 :
811 252 : H5Sclose(memspace);
812 :
813 : // Y flip the data.
814 252 : const int nLinesToFlip = static_cast<int>(count[0]);
815 252 : const int nLineSize = nSizeOfData * nBlockXSize;
816 252 : GByte *const pabyTemp = static_cast<GByte *>(CPLMalloc(nLineSize));
817 252 : GByte *const pbyImage = static_cast<GByte *>(pImage);
818 :
819 316 : for (int iY = 0; iY < nLinesToFlip / 2; iY++)
820 : {
821 64 : memcpy(pabyTemp, pbyImage + iY * nLineSize, nLineSize);
822 64 : memcpy(pbyImage + iY * nLineSize,
823 64 : pbyImage + (nLinesToFlip - iY - 1) * nLineSize, nLineSize);
824 64 : memcpy(pbyImage + (nLinesToFlip - iY - 1) * nLineSize, pabyTemp,
825 : nLineSize);
826 : }
827 :
828 252 : CPLFree(pabyTemp);
829 :
830 : // Return success or failure.
831 252 : if (status_read < 0)
832 : {
833 0 : CPLError(CE_Failure, CPLE_AppDefined, "H5Dread() failed for block.");
834 0 : return CE_Failure;
835 : }
836 :
837 252 : return CE_None;
838 : }
839 :
840 : /************************************************************************/
841 : /* IWriteBlock() */
842 : /************************************************************************/
843 :
844 15 : CPLErr BAGRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
845 : {
846 : HDF5_GLOBAL_LOCK();
847 :
848 15 : if (!CreateDatasetIfNeeded())
849 0 : return CE_Failure;
850 :
851 15 : const int nXOff = nBlockXOff * nBlockXSize;
852 15 : H5OFFSET_TYPE offset[3] = {
853 15 : static_cast<H5OFFSET_TYPE>(
854 15 : std::max(0, nRasterYSize - (nBlockYOff + 1) * nBlockYSize)),
855 15 : static_cast<H5OFFSET_TYPE>(nXOff)};
856 :
857 : // Blocksize may not be a multiple of imagesize.
858 15 : hsize_t count[3] = {
859 15 : std::min(static_cast<hsize_t>(nBlockYSize), GetYSize() - offset[0]),
860 15 : std::min(static_cast<hsize_t>(nBlockXSize), GetXSize() - offset[1])};
861 :
862 15 : if (nRasterYSize - (nBlockYOff + 1) * nBlockYSize < 0)
863 : {
864 0 : count[0] +=
865 0 : (nRasterYSize - static_cast<hsize_t>(nBlockYOff + 1) * nBlockYSize);
866 : }
867 :
868 : // Select block from file space.
869 : {
870 15 : const herr_t status = H5Sselect_hyperslab(
871 : m_hDataspace, H5S_SELECT_SET, offset, nullptr, count, nullptr);
872 15 : if (status < 0)
873 0 : return CE_Failure;
874 : }
875 :
876 : // Create memory space to receive the data.
877 15 : hsize_t col_dims[2] = {static_cast<hsize_t>(nBlockYSize),
878 15 : static_cast<hsize_t>(nBlockXSize)};
879 15 : const int rank = 2;
880 15 : const hid_t memspace = H5Screate_simple(rank, col_dims, nullptr);
881 15 : H5OFFSET_TYPE mem_offset[2] = {0, 0};
882 15 : const herr_t status = H5Sselect_hyperslab(
883 : memspace, H5S_SELECT_SET, mem_offset, nullptr, count, nullptr);
884 15 : if (status < 0)
885 : {
886 0 : H5Sclose(memspace);
887 0 : return CE_Failure;
888 : }
889 :
890 : // Y flip the data.
891 15 : const int nLinesToFlip = static_cast<int>(count[0]);
892 15 : const int nSizeOfData = static_cast<int>(H5Tget_size(m_hNative));
893 15 : const int nLineSize = nSizeOfData * nBlockXSize;
894 : GByte *const pabyTemp = static_cast<GByte *>(
895 15 : CPLMalloc(static_cast<size_t>(nLineSize) * nLinesToFlip));
896 15 : GByte *const pbyImage = static_cast<GByte *>(pImage);
897 :
898 65 : for (int iY = 0; iY < nLinesToFlip; iY++)
899 : {
900 50 : memcpy(pabyTemp + iY * nLineSize,
901 50 : pbyImage + (nLinesToFlip - iY - 1) * nLineSize, nLineSize);
902 504 : for (int iX = 0; iX < static_cast<int>(count[1]); iX++)
903 : {
904 : float f;
905 454 : GDALCopyWords(pabyTemp + iY * nLineSize + iX * nSizeOfData,
906 : eDataType, 0, &f, GDT_Float32, 0, 1);
907 454 : if (!m_bHasNoData || m_fNoDataValue != f)
908 : {
909 452 : m_dfMinimum = std::min(m_dfMinimum, static_cast<double>(f));
910 452 : m_dfMaximum = std::max(m_dfMaximum, static_cast<double>(f));
911 : }
912 : }
913 : }
914 :
915 15 : const herr_t status_write = H5Dwrite(m_hDatasetID, m_hNative, memspace,
916 : m_hDataspace, H5P_DEFAULT, pabyTemp);
917 :
918 15 : H5Sclose(memspace);
919 :
920 15 : CPLFree(pabyTemp);
921 :
922 : // Return success or failure.
923 15 : if (status_write < 0)
924 : {
925 0 : CPLError(CE_Failure, CPLE_AppDefined, "H5Dwrite() failed for block.");
926 0 : return CE_Failure;
927 : }
928 :
929 15 : return CE_None;
930 : }
931 :
932 : /************************************************************************/
933 : /* BAGSuperGridBand() */
934 : /************************************************************************/
935 :
936 14 : BAGSuperGridBand::BAGSuperGridBand(BAGDataset *poDSIn, int nBandIn,
937 14 : bool bHasNoData, float fNoDataValue)
938 : {
939 14 : poDS = poDSIn;
940 14 : nBand = nBandIn;
941 14 : nRasterXSize = poDS->GetRasterXSize();
942 14 : nRasterYSize = poDS->GetRasterYSize();
943 14 : nBlockXSize = nRasterXSize;
944 14 : nBlockYSize = 1;
945 14 : eDataType = GDT_Float32;
946 14 : GDALRasterBand::SetDescription(nBand == 1 ? "elevation" : "uncertainty");
947 14 : m_bHasNoData = bHasNoData;
948 14 : m_fNoDataValue = fNoDataValue;
949 14 : }
950 :
951 : /************************************************************************/
952 : /* ~BAGSuperGridBand() */
953 : /************************************************************************/
954 :
955 : BAGSuperGridBand::~BAGSuperGridBand() = default;
956 :
957 : /************************************************************************/
958 : /* IReadBlock() */
959 : /************************************************************************/
960 :
961 8 : CPLErr BAGSuperGridBand::IReadBlock(int, int nBlockYOff, void *pImage)
962 : {
963 : HDF5_GLOBAL_LOCK();
964 :
965 8 : BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
966 8 : H5OFFSET_TYPE offset[2] = {
967 : static_cast<H5OFFSET_TYPE>(0),
968 : static_cast<H5OFFSET_TYPE>(
969 8 : poGDS->m_nSuperGridRefinementStartIndex +
970 8 : static_cast<H5OFFSET_TYPE>(nRasterYSize - 1 - nBlockYOff) *
971 8 : nBlockXSize)};
972 8 : hsize_t count[2] = {1, static_cast<hsize_t>(nBlockXSize)};
973 : {
974 8 : herr_t status = H5Sselect_hyperslab(
975 : poGDS->m_hVarresRefinementsDataspace, H5S_SELECT_SET, offset,
976 : nullptr, count, nullptr);
977 8 : if (status < 0)
978 0 : return CE_Failure;
979 : }
980 :
981 : // Create memory space to receive the data.
982 8 : const hid_t memspace = H5Screate_simple(2, count, nullptr);
983 8 : H5OFFSET_TYPE mem_offset[2] = {0, 0};
984 : {
985 8 : const herr_t status = H5Sselect_hyperslab(
986 : memspace, H5S_SELECT_SET, mem_offset, nullptr, count, nullptr);
987 8 : if (status < 0)
988 : {
989 0 : H5Sclose(memspace);
990 0 : return CE_Failure;
991 : }
992 : }
993 :
994 8 : float *afBuffer = new float[2 * nBlockXSize];
995 : {
996 8 : const herr_t status = H5Dread(
997 : poGDS->m_hVarresRefinements, poGDS->m_hVarresRefinementsNative,
998 : memspace, poGDS->m_hVarresRefinementsDataspace, H5P_DEFAULT,
999 : afBuffer);
1000 8 : if (status < 0)
1001 : {
1002 0 : H5Sclose(memspace);
1003 0 : delete[] afBuffer;
1004 0 : return CE_Failure;
1005 : }
1006 : }
1007 :
1008 8 : GDALCopyWords(afBuffer + nBand - 1, GDT_Float32, 2 * sizeof(float), pImage,
1009 : GDT_Float32, sizeof(float), nBlockXSize);
1010 :
1011 8 : H5Sclose(memspace);
1012 8 : delete[] afBuffer;
1013 8 : return CE_None;
1014 : }
1015 :
1016 : /************************************************************************/
1017 : /* BAGResampledBand() */
1018 : /************************************************************************/
1019 :
1020 90 : BAGResampledBand::BAGResampledBand(BAGDataset *poDSIn, int nBandIn,
1021 : bool bHasNoData, float fNoDataValue,
1022 90 : bool bInitializeMinMax)
1023 : {
1024 90 : poDS = poDSIn;
1025 90 : nBand = nBandIn;
1026 90 : nRasterXSize = poDS->GetRasterXSize();
1027 90 : nRasterYSize = poDS->GetRasterYSize();
1028 : // Mostly for autotest purposes
1029 : const int nBlockSize =
1030 90 : std::max(1, atoi(CPLGetConfigOption("GDAL_BAG_BLOCK_SIZE", "256")));
1031 90 : nBlockXSize = std::min(nBlockSize, poDS->GetRasterXSize());
1032 90 : nBlockYSize = std::min(nBlockSize, poDS->GetRasterYSize());
1033 90 : if (poDSIn->m_bMask)
1034 : {
1035 1 : eDataType = GDT_UInt8;
1036 : }
1037 89 : else if (poDSIn->m_ePopulation == BAGDataset::Population::COUNT)
1038 : {
1039 1 : eDataType = GDT_UInt32;
1040 1 : GDALRasterBand::SetDescription("count");
1041 : }
1042 : else
1043 : {
1044 88 : m_bHasNoData = true;
1045 88 : m_fNoDataValue = bHasNoData ? fNoDataValue : fDEFAULT_NODATA;
1046 88 : eDataType = GDT_Float32;
1047 88 : GDALRasterBand::SetDescription(nBand == 1 ? "elevation"
1048 : : "uncertainty");
1049 : }
1050 90 : if (bInitializeMinMax)
1051 : {
1052 36 : InitializeMinMax();
1053 : }
1054 90 : }
1055 :
1056 : /************************************************************************/
1057 : /* ~BAGResampledBand() */
1058 : /************************************************************************/
1059 :
1060 : BAGResampledBand::~BAGResampledBand() = default;
1061 :
1062 : /************************************************************************/
1063 : /* InitializeMinMax() */
1064 : /************************************************************************/
1065 :
1066 36 : void BAGResampledBand::InitializeMinMax()
1067 : {
1068 36 : BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
1069 90 : if (nBand == 1 &&
1070 18 : GH5_FetchAttribute(poGDS->m_hVarresRefinements, "max_depth",
1071 54 : m_dfMaximum) &&
1072 18 : GH5_FetchAttribute(poGDS->m_hVarresRefinements, "min_depth",
1073 18 : m_dfMinimum))
1074 : {
1075 18 : m_bMinMaxSet = true;
1076 : }
1077 54 : else if (nBand == 2 &&
1078 18 : GH5_FetchAttribute(poGDS->m_hVarresRefinements, "max_uncrt",
1079 36 : m_dfMaximum) &&
1080 18 : GH5_FetchAttribute(poGDS->m_hVarresRefinements, "min_uncrt",
1081 18 : m_dfMinimum))
1082 : {
1083 18 : m_bMinMaxSet = true;
1084 : }
1085 36 : }
1086 :
1087 : /************************************************************************/
1088 : /* GetMinimum() */
1089 : /************************************************************************/
1090 :
1091 2 : double BAGResampledBand::GetMinimum(int *pbSuccess)
1092 :
1093 : {
1094 2 : if (m_bMinMaxSet)
1095 : {
1096 2 : if (pbSuccess)
1097 2 : *pbSuccess = TRUE;
1098 2 : return m_dfMinimum;
1099 : }
1100 :
1101 0 : return GDALRasterBand::GetMinimum(pbSuccess);
1102 : }
1103 :
1104 : /************************************************************************/
1105 : /* GetMaximum() */
1106 : /************************************************************************/
1107 :
1108 2 : double BAGResampledBand::GetMaximum(int *pbSuccess)
1109 :
1110 : {
1111 2 : if (m_bMinMaxSet)
1112 : {
1113 2 : if (pbSuccess)
1114 2 : *pbSuccess = TRUE;
1115 2 : return m_dfMaximum;
1116 : }
1117 :
1118 0 : return GDALRasterBand::GetMaximum(pbSuccess);
1119 : }
1120 :
1121 : /************************************************************************/
1122 : /* IReadBlock() */
1123 : /************************************************************************/
1124 :
1125 289 : CPLErr BAGResampledBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1126 : void *pImage)
1127 : {
1128 : HDF5_GLOBAL_LOCK();
1129 :
1130 289 : BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
1131 : #ifdef DEBUG_VERBOSE
1132 : CPLDebug(
1133 : "BAG",
1134 : "IReadBlock: nRasterXSize=%d, nBlockXOff=%d, nBlockYOff=%d, nBand=%d",
1135 : nRasterXSize, nBlockXOff, nBlockYOff, nBand);
1136 : #endif
1137 :
1138 289 : const float fNoDataValue = m_fNoDataValue;
1139 289 : const float fNoSuperGridValue = m_fNoDataValue;
1140 289 : float *depthsPtr = nullptr;
1141 289 : float *uncrtPtr = nullptr;
1142 :
1143 289 : GDALRasterBlock *poBlock = nullptr;
1144 289 : if (poGDS->nBands == 2)
1145 : {
1146 287 : if (nBand == 1)
1147 : {
1148 287 : depthsPtr = static_cast<float *>(pImage);
1149 287 : poBlock = poGDS->GetRasterBand(2)->GetLockedBlockRef(
1150 287 : nBlockXOff, nBlockYOff, TRUE);
1151 287 : if (poBlock)
1152 : {
1153 287 : uncrtPtr = static_cast<float *>(poBlock->GetDataRef());
1154 : }
1155 : else
1156 : {
1157 0 : return CE_Failure;
1158 : }
1159 : }
1160 : else
1161 : {
1162 0 : uncrtPtr = static_cast<float *>(pImage);
1163 0 : poBlock = poGDS->GetRasterBand(1)->GetLockedBlockRef(
1164 0 : nBlockXOff, nBlockYOff, TRUE);
1165 0 : if (poBlock)
1166 : {
1167 0 : depthsPtr = static_cast<float *>(poBlock->GetDataRef());
1168 : }
1169 : else
1170 : {
1171 0 : return CE_Failure;
1172 : }
1173 : }
1174 : }
1175 :
1176 289 : if (depthsPtr)
1177 : {
1178 287 : GDALCopyWords(&fNoSuperGridValue, GDT_Float32, 0, depthsPtr,
1179 : GDT_Float32, static_cast<int>(sizeof(float)),
1180 287 : nBlockXSize * nBlockYSize);
1181 : }
1182 :
1183 289 : if (uncrtPtr)
1184 : {
1185 287 : GDALCopyWords(&fNoSuperGridValue, GDT_Float32, 0, uncrtPtr, GDT_Float32,
1186 : static_cast<int>(sizeof(float)),
1187 287 : nBlockXSize * nBlockYSize);
1188 : }
1189 :
1190 578 : std::vector<int> counts;
1191 289 : if (poGDS->m_bMask)
1192 : {
1193 1 : CPLAssert(pImage); // to make CLang Static Analyzer happy
1194 1 : memset(pImage, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize);
1195 : }
1196 288 : else if (poGDS->m_ePopulation == BAGDataset::Population::MEAN)
1197 : {
1198 1 : counts.resize(static_cast<size_t>(nBlockXSize) * nBlockYSize);
1199 : }
1200 287 : else if (poGDS->m_ePopulation == BAGDataset::Population::COUNT)
1201 : {
1202 1 : CPLAssert(pImage); // to make CLang Static Analyzer happy
1203 1 : memset(pImage, 0,
1204 1 : static_cast<size_t>(nBlockXSize) * nBlockYSize *
1205 1 : GDALGetDataTypeSizeBytes(eDataType));
1206 : }
1207 :
1208 : const int nReqCountX =
1209 289 : std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
1210 : const int nReqCountY =
1211 289 : std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
1212 : // Compute extent of block in georeferenced coordinates
1213 : double dfBlockMinX =
1214 289 : poGDS->m_gt[0] + nBlockXOff * nBlockXSize * poGDS->m_gt[1];
1215 289 : double dfBlockMaxX = dfBlockMinX + nReqCountX * poGDS->m_gt[1];
1216 : double dfBlockMaxY =
1217 289 : poGDS->m_gt[3] + nBlockYOff * nBlockYSize * poGDS->m_gt[5];
1218 289 : double dfBlockMinY = dfBlockMaxY + nReqCountY * poGDS->m_gt[5];
1219 :
1220 : // Compute min/max indices of intersecting supergrids (origin bottom-left)
1221 289 : const double dfLowResResX =
1222 289 : (poGDS->m_dfLowResMaxX - poGDS->m_dfLowResMinX) / poGDS->m_nLowResWidth;
1223 289 : const double dfLowResResY =
1224 289 : (poGDS->m_dfLowResMaxY - poGDS->m_dfLowResMinY) /
1225 289 : poGDS->m_nLowResHeight;
1226 : int nLowResMinIdxX =
1227 578 : std::max(0, static_cast<int>((dfBlockMinX - poGDS->m_dfLowResMinX) /
1228 289 : dfLowResResX));
1229 : int nLowResMinIdxY =
1230 578 : std::max(0, static_cast<int>((dfBlockMinY - poGDS->m_dfLowResMinY) /
1231 289 : dfLowResResY));
1232 : int nLowResMaxIdxX = std::min(
1233 578 : poGDS->m_nLowResWidth - 1,
1234 289 : static_cast<int>((dfBlockMaxX - poGDS->m_dfLowResMinX) / dfLowResResX));
1235 : int nLowResMaxIdxY = std::min(
1236 578 : poGDS->m_nLowResHeight - 1,
1237 289 : static_cast<int>((dfBlockMaxY - poGDS->m_dfLowResMinY) / dfLowResResY));
1238 :
1239 : // Create memory space to receive the data.
1240 289 : const int nCountLowResX = nLowResMaxIdxX - nLowResMinIdxX + 1;
1241 289 : const int nCountLowResY = nLowResMaxIdxY - nLowResMinIdxY + 1;
1242 289 : hsize_t countVarresMD[2] = {static_cast<hsize_t>(nCountLowResY),
1243 289 : static_cast<hsize_t>(nCountLowResX)};
1244 289 : const hid_t memspaceVarresMD = H5Screate_simple(2, countVarresMD, nullptr);
1245 289 : H5OFFSET_TYPE mem_offset[2] = {static_cast<H5OFFSET_TYPE>(0),
1246 : static_cast<H5OFFSET_TYPE>(0)};
1247 289 : if (H5Sselect_hyperslab(memspaceVarresMD, H5S_SELECT_SET, mem_offset,
1248 289 : nullptr, countVarresMD, nullptr) < 0)
1249 : {
1250 0 : H5Sclose(memspaceVarresMD);
1251 0 : if (poBlock != nullptr)
1252 : {
1253 0 : poBlock->DropLock();
1254 0 : poBlock = nullptr;
1255 : }
1256 0 : return CE_Failure;
1257 : }
1258 :
1259 289 : std::vector<BAGRefinementGrid> rgrids(static_cast<size_t>(nCountLowResY) *
1260 578 : nCountLowResX);
1261 289 : if (!(poGDS->ReadVarresMetadataValue(nLowResMinIdxY, nLowResMinIdxX,
1262 : memspaceVarresMD, rgrids.data(),
1263 : nCountLowResY, nCountLowResX)))
1264 : {
1265 0 : H5Sclose(memspaceVarresMD);
1266 0 : if (poBlock != nullptr)
1267 : {
1268 0 : poBlock->DropLock();
1269 0 : poBlock = nullptr;
1270 : }
1271 0 : return CE_Failure;
1272 : }
1273 :
1274 289 : H5Sclose(memspaceVarresMD);
1275 :
1276 289 : CPLErr eErr = CE_None;
1277 722 : for (int y = nLowResMinIdxY; y <= nLowResMaxIdxY; y++)
1278 : {
1279 1336 : for (int x = nLowResMinIdxX; x <= nLowResMaxIdxX; x++)
1280 : {
1281 903 : const auto &rgrid = rgrids[(y - nLowResMinIdxY) * nCountLowResX +
1282 903 : (x - nLowResMinIdxX)];
1283 903 : if (rgrid.nWidth == 0)
1284 : {
1285 80 : continue;
1286 : }
1287 903 : const float gridRes = std::max(rgrid.fResX, rgrid.fResY);
1288 903 : if (!(gridRes > poGDS->m_dfResFilterMin &&
1289 851 : gridRes <= poGDS->m_dfResFilterMax))
1290 : {
1291 80 : continue;
1292 : }
1293 :
1294 : // Super grid bounding box with pixel-center convention
1295 823 : const double dfMinX =
1296 823 : poGDS->m_dfLowResMinX + x * dfLowResResX + rgrid.fSWX;
1297 823 : const double dfMaxX =
1298 823 : dfMinX + (rgrid.nWidth - 1) * static_cast<double>(rgrid.fResX);
1299 823 : const double dfMinY =
1300 823 : poGDS->m_dfLowResMinY + y * dfLowResResY + rgrid.fSWY;
1301 823 : const double dfMaxY =
1302 823 : dfMinY + (rgrid.nHeight - 1) * static_cast<double>(rgrid.fResY);
1303 :
1304 : // Intersection of super grid with block
1305 823 : const double dfInterMinX = std::max(dfBlockMinX, dfMinX);
1306 823 : const double dfInterMinY = std::max(dfBlockMinY, dfMinY);
1307 823 : const double dfInterMaxX = std::min(dfBlockMaxX, dfMaxX);
1308 823 : const double dfInterMaxY = std::min(dfBlockMaxY, dfMaxY);
1309 :
1310 : // Min/max indices in the super grid
1311 : const int nMinSrcX = std::max(
1312 823 : 0, static_cast<int>((dfInterMinX - dfMinX) / rgrid.fResX));
1313 : const int nMinSrcY = std::max(
1314 823 : 0, static_cast<int>((dfInterMinY - dfMinY) / rgrid.fResY));
1315 : // Need to use ceil due to numerical imprecision
1316 : const int nMaxSrcX =
1317 1646 : std::min(static_cast<int>(rgrid.nWidth) - 1,
1318 1646 : static_cast<int>(
1319 823 : std::ceil((dfInterMaxX - dfMinX) / rgrid.fResX)));
1320 : const int nMaxSrcY =
1321 1646 : std::min(static_cast<int>(rgrid.nHeight) - 1,
1322 1646 : static_cast<int>(
1323 823 : std::ceil((dfInterMaxY - dfMinY) / rgrid.fResY)));
1324 : #ifdef DEBUG_VERBOSE
1325 : CPLDebug(
1326 : "BAG",
1327 : "y = %d, x = %d, minx = %d, miny = %d, maxx = %d, maxy = %d", y,
1328 : x, nMinSrcX, nMinSrcY, nMaxSrcX, nMaxSrcY);
1329 : #endif
1330 823 : const double dfCstX = (dfMinX - dfBlockMinX) / poGDS->m_gt[1];
1331 823 : const double dfMulX = rgrid.fResX / poGDS->m_gt[1];
1332 :
1333 3709 : for (int super_y = nMinSrcY; super_y <= nMaxSrcY; super_y++)
1334 : {
1335 2886 : const double dfSrcY =
1336 2886 : dfMinY + super_y * static_cast<double>(rgrid.fResY);
1337 : const int nTargetY = static_cast<int>(
1338 2886 : std::floor((dfBlockMaxY - dfSrcY) / -poGDS->m_gt[5]));
1339 2886 : if (!(nTargetY >= 0 && nTargetY < nReqCountY))
1340 : {
1341 752 : continue;
1342 : }
1343 :
1344 2134 : const unsigned nTargetIdxBase = nTargetY * nBlockXSize;
1345 2134 : const unsigned nRefinementIndexase =
1346 2134 : rgrid.nIndex + super_y * rgrid.nWidth;
1347 :
1348 11648 : for (int super_x = nMinSrcX; super_x <= nMaxSrcX; super_x++)
1349 : {
1350 : /*
1351 : const double dfSrcX = dfMinX + super_x * rgrid.fResX;
1352 : const int nTargetX = static_cast<int>(std::floor(
1353 : (dfSrcX - dfBlockMinX) / poGDS->m_gt[1]));
1354 : */
1355 9514 : const int nTargetX =
1356 9514 : static_cast<int>(std::floor(dfCstX + super_x * dfMulX));
1357 9514 : if (!(nTargetX >= 0 && nTargetX < nReqCountX))
1358 : {
1359 1260 : continue;
1360 : }
1361 :
1362 8254 : const unsigned nTargetIdx = nTargetIdxBase + nTargetX;
1363 8254 : if (poGDS->m_bMask)
1364 : {
1365 502 : static_cast<GByte *>(pImage)[nTargetIdx] = 255;
1366 502 : continue;
1367 : }
1368 :
1369 7752 : if (poGDS->m_ePopulation == BAGDataset::Population::COUNT)
1370 : {
1371 556 : static_cast<GUInt32 *>(pImage)[nTargetIdx]++;
1372 556 : continue;
1373 : }
1374 :
1375 7196 : CPLAssert(depthsPtr);
1376 7196 : CPLAssert(uncrtPtr);
1377 :
1378 7196 : const unsigned nRefinementIndex =
1379 7196 : nRefinementIndexase + super_x;
1380 : const auto *pafRefValues =
1381 7196 : poGDS->GetRefinementValues(nRefinementIndex);
1382 7196 : if (!pafRefValues)
1383 : {
1384 0 : eErr = CE_Failure;
1385 0 : goto end;
1386 : }
1387 :
1388 7196 : float depth = pafRefValues[0];
1389 7196 : if (depth == fNoDataValue)
1390 : {
1391 0 : if (depthsPtr[nTargetIdx] == fNoSuperGridValue)
1392 : {
1393 0 : depthsPtr[nTargetIdx] = fNoDataValue;
1394 : }
1395 0 : continue;
1396 : }
1397 :
1398 7196 : if (poGDS->m_ePopulation == BAGDataset::Population::MEAN)
1399 : {
1400 :
1401 556 : if (counts[nTargetIdx] == 0)
1402 : {
1403 133 : depthsPtr[nTargetIdx] = depth;
1404 : }
1405 : else
1406 : {
1407 423 : depthsPtr[nTargetIdx] += depth;
1408 : }
1409 556 : counts[nTargetIdx]++;
1410 :
1411 556 : auto uncrt = pafRefValues[1];
1412 556 : auto &target_uncrt_ptr = uncrtPtr[nTargetIdx];
1413 556 : if (uncrt > target_uncrt_ptr ||
1414 408 : target_uncrt_ptr == fNoDataValue)
1415 : {
1416 281 : target_uncrt_ptr = uncrt;
1417 : }
1418 : }
1419 6640 : else if ((poGDS->m_ePopulation ==
1420 6084 : BAGDataset::Population::MAX &&
1421 6084 : depth > depthsPtr[nTargetIdx]) ||
1422 5371 : (poGDS->m_ePopulation ==
1423 556 : BAGDataset::Population::MIN &&
1424 556 : depth < depthsPtr[nTargetIdx]) ||
1425 5145 : depthsPtr[nTargetIdx] == fNoDataValue ||
1426 2172 : depthsPtr[nTargetIdx] == fNoSuperGridValue)
1427 : {
1428 4468 : depthsPtr[nTargetIdx] = depth;
1429 4468 : uncrtPtr[nTargetIdx] = pafRefValues[1];
1430 : }
1431 : }
1432 : }
1433 : }
1434 : }
1435 :
1436 289 : if (poGDS->m_ePopulation == BAGDataset::Population::MEAN && depthsPtr)
1437 : {
1438 151 : for (int i = 0; i < nBlockXSize * nBlockYSize; i++)
1439 : {
1440 150 : if (counts[i])
1441 : {
1442 133 : depthsPtr[i] /= counts[i];
1443 : }
1444 : }
1445 : }
1446 :
1447 288 : end:
1448 289 : if (poBlock != nullptr)
1449 : {
1450 287 : poBlock->DropLock();
1451 287 : poBlock = nullptr;
1452 : }
1453 :
1454 289 : return eErr;
1455 : }
1456 :
1457 : /************************************************************************/
1458 : /* BAGInterpolatedBand() */
1459 : /************************************************************************/
1460 :
1461 10 : BAGInterpolatedBand::BAGInterpolatedBand(BAGDataset *poDSIn, int nBandIn,
1462 : bool bHasNoData, float fNoDataValue,
1463 10 : bool bInitializeMinMax)
1464 : {
1465 10 : poDS = poDSIn;
1466 10 : nBand = nBandIn;
1467 10 : nRasterXSize = poDS->GetRasterXSize();
1468 10 : nRasterYSize = poDS->GetRasterYSize();
1469 : // Mostly for autotest purposes
1470 : const int nBlockSize =
1471 10 : std::max(1, atoi(CPLGetConfigOption("GDAL_BAG_BLOCK_SIZE", "256")));
1472 10 : nBlockXSize = std::min(nBlockSize, poDS->GetRasterXSize());
1473 10 : nBlockYSize = std::min(nBlockSize, poDS->GetRasterYSize());
1474 :
1475 10 : m_bHasNoData = true;
1476 10 : m_fNoDataValue = bHasNoData ? fNoDataValue : fDEFAULT_NODATA;
1477 10 : eDataType = GDT_Float32;
1478 10 : GDALRasterBand::SetDescription(nBand == 1 ? "elevation" : "uncertainty");
1479 10 : if (bInitializeMinMax)
1480 : {
1481 2 : InitializeMinMax();
1482 : }
1483 10 : }
1484 :
1485 : /************************************************************************/
1486 : /* ~BAGInterpolatedBand() */
1487 : /************************************************************************/
1488 :
1489 : BAGInterpolatedBand::~BAGInterpolatedBand() = default;
1490 :
1491 : /************************************************************************/
1492 : /* InitializeMinMax() */
1493 : /************************************************************************/
1494 :
1495 2 : void BAGInterpolatedBand::InitializeMinMax()
1496 : {
1497 2 : BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
1498 5 : if (nBand == 1 &&
1499 1 : GH5_FetchAttribute(poGDS->m_hVarresRefinements, "max_depth",
1500 3 : m_dfMaximum) &&
1501 0 : GH5_FetchAttribute(poGDS->m_hVarresRefinements, "min_depth",
1502 0 : m_dfMinimum))
1503 : {
1504 0 : m_bMinMaxSet = true;
1505 : }
1506 5 : else if (nBand == 2 &&
1507 1 : GH5_FetchAttribute(poGDS->m_hVarresRefinements, "max_uncrt",
1508 3 : m_dfMaximum) &&
1509 0 : GH5_FetchAttribute(poGDS->m_hVarresRefinements, "min_uncrt",
1510 0 : m_dfMinimum))
1511 : {
1512 0 : m_bMinMaxSet = true;
1513 : }
1514 2 : }
1515 :
1516 : /************************************************************************/
1517 : /* GetMinimum() */
1518 : /************************************************************************/
1519 :
1520 2 : double BAGInterpolatedBand::GetMinimum(int *pbSuccess)
1521 :
1522 : {
1523 2 : if (m_bMinMaxSet)
1524 : {
1525 0 : if (pbSuccess)
1526 0 : *pbSuccess = TRUE;
1527 0 : return m_dfMinimum;
1528 : }
1529 :
1530 2 : return GDALRasterBand::GetMinimum(pbSuccess);
1531 : }
1532 :
1533 : /************************************************************************/
1534 : /* GetMaximum() */
1535 : /************************************************************************/
1536 :
1537 2 : double BAGInterpolatedBand::GetMaximum(int *pbSuccess)
1538 :
1539 : {
1540 2 : if (m_bMinMaxSet)
1541 : {
1542 0 : if (pbSuccess)
1543 0 : *pbSuccess = TRUE;
1544 0 : return m_dfMaximum;
1545 : }
1546 :
1547 2 : return GDALRasterBand::GetMaximum(pbSuccess);
1548 : }
1549 :
1550 : /************************************************************************/
1551 : /* BarycentricInterpolation() */
1552 : /************************************************************************/
1553 :
1554 203 : static bool BarycentricInterpolation(double dfX, double dfY,
1555 : const double adfX[3], const double adfY[3],
1556 : double &dfCoord0, double &dfCoord1,
1557 : double &dfCoord2)
1558 : {
1559 : /* See https://en.wikipedia.org/wiki/Barycentric_coordinate_system */
1560 203 : const double dfDenom = (adfY[1] - adfY[2]) * (adfX[0] - adfX[2]) +
1561 203 : (adfX[2] - adfX[1]) * (adfY[0] - adfY[2]);
1562 203 : if (fabs(dfDenom) < 1e-5)
1563 : {
1564 : // Degenerate triangle
1565 0 : return false;
1566 : }
1567 203 : const double dfTerm0X = adfY[1] - adfY[2];
1568 203 : const double dfTerm0Y = adfX[2] - adfX[1];
1569 203 : const double dfTerm1X = adfY[2] - adfY[0];
1570 203 : const double dfTerm1Y = adfX[0] - adfX[2];
1571 203 : const double dfDiffX = dfX - adfX[2];
1572 203 : const double dfDiffY = dfY - adfY[2];
1573 203 : dfCoord0 = (dfTerm0X * dfDiffX + dfTerm0Y * dfDiffY) / dfDenom;
1574 203 : dfCoord1 = (dfTerm1X * dfDiffX + dfTerm1Y * dfDiffY) / dfDenom;
1575 203 : dfCoord2 = 1 - dfCoord0 - dfCoord1;
1576 203 : return (dfCoord0 >= 0 && dfCoord0 <= 1 && dfCoord1 >= 0 && dfCoord1 <= 1 &&
1577 406 : dfCoord2 >= 0 && dfCoord2 <= 1);
1578 : }
1579 :
1580 : /************************************************************************/
1581 : /* SQ() */
1582 : /************************************************************************/
1583 :
1584 205823 : static inline double SQ(double v)
1585 : {
1586 205823 : return v * v;
1587 : }
1588 :
1589 : /************************************************************************/
1590 : /* IReadBlock() */
1591 : /************************************************************************/
1592 :
1593 5 : CPLErr BAGInterpolatedBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1594 : void *pImage)
1595 : {
1596 : HDF5_GLOBAL_LOCK();
1597 :
1598 5 : BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
1599 : #ifdef DEBUG_VERBOSE
1600 : CPLDebug(
1601 : "BAG",
1602 : "IReadBlock: nRasterXSize=%d, nBlockXOff=%d, nBlockYOff=%d, nBand=%d",
1603 : nRasterXSize, nBlockXOff, nBlockYOff, nBand);
1604 : #endif
1605 :
1606 5 : float *depthsPtr = nullptr;
1607 5 : float *uncrtPtr = nullptr;
1608 :
1609 5 : GDALRasterBlock *poBlock = nullptr;
1610 5 : if (nBand == 1)
1611 : {
1612 5 : depthsPtr = static_cast<float *>(pImage);
1613 5 : poBlock = poGDS->GetRasterBand(2)->GetLockedBlockRef(nBlockXOff,
1614 5 : nBlockYOff, TRUE);
1615 5 : if (poBlock)
1616 : {
1617 5 : uncrtPtr = static_cast<float *>(poBlock->GetDataRef());
1618 : }
1619 : }
1620 : else
1621 : {
1622 0 : uncrtPtr = static_cast<float *>(pImage);
1623 0 : poBlock = poGDS->GetRasterBand(1)->GetLockedBlockRef(nBlockXOff,
1624 0 : nBlockYOff, TRUE);
1625 0 : if (poBlock)
1626 : {
1627 0 : depthsPtr = static_cast<float *>(poBlock->GetDataRef());
1628 : }
1629 : }
1630 :
1631 5 : if (!depthsPtr || !uncrtPtr)
1632 0 : return CE_Failure;
1633 :
1634 5 : GDALCopyWords(&m_fNoDataValue, GDT_Float32, 0, depthsPtr, GDT_Float32,
1635 5 : static_cast<int>(sizeof(float)), nBlockXSize * nBlockYSize);
1636 :
1637 5 : GDALCopyWords(&m_fNoDataValue, GDT_Float32, 0, uncrtPtr, GDT_Float32,
1638 5 : static_cast<int>(sizeof(float)), nBlockXSize * nBlockYSize);
1639 :
1640 : const int nReqCountX =
1641 5 : std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
1642 : const int nReqCountY =
1643 5 : std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
1644 : // Compute extent of block in georeferenced coordinates
1645 : const double dfBlockMinX =
1646 5 : poGDS->m_gt[0] + nBlockXOff * nBlockXSize * poGDS->m_gt[1];
1647 5 : const double dfBlockMaxX = dfBlockMinX + nReqCountX * poGDS->m_gt[1];
1648 : const double dfBlockMaxY =
1649 5 : poGDS->m_gt[3] + nBlockYOff * nBlockYSize * poGDS->m_gt[5];
1650 5 : const double dfBlockMinY = dfBlockMaxY + nReqCountY * poGDS->m_gt[5];
1651 :
1652 : // Compute min/max indices of intersecting supergrids (origin bottom-left)
1653 : // We add a margin of (dfLowResResX, dfLowResResY) to be able to
1654 : // properly interpolate if the edges of a GDAL block align with the edge
1655 : // of a low-res cell
1656 5 : const double dfLowResResX =
1657 5 : (poGDS->m_dfLowResMaxX - poGDS->m_dfLowResMinX) / poGDS->m_nLowResWidth;
1658 5 : const double dfLowResResY =
1659 5 : (poGDS->m_dfLowResMaxY - poGDS->m_dfLowResMinY) /
1660 5 : poGDS->m_nLowResHeight;
1661 : const int nLowResMinIdxX = std::max(
1662 10 : 0,
1663 10 : static_cast<int>((dfBlockMinX - poGDS->m_dfLowResMinX - dfLowResResX) /
1664 5 : dfLowResResX));
1665 : const int nLowResMinIdxY = std::max(
1666 10 : 0,
1667 10 : static_cast<int>((dfBlockMinY - poGDS->m_dfLowResMinY - dfLowResResY) /
1668 5 : dfLowResResY));
1669 : const int nLowResMaxIdxX = std::min(
1670 10 : poGDS->m_nLowResWidth - 1,
1671 10 : static_cast<int>((dfBlockMaxX - poGDS->m_dfLowResMinX + dfLowResResX) /
1672 5 : dfLowResResX));
1673 : const int nLowResMaxIdxY = std::min(
1674 10 : poGDS->m_nLowResHeight - 1,
1675 10 : static_cast<int>((dfBlockMaxY - poGDS->m_dfLowResMinY + dfLowResResY) /
1676 5 : dfLowResResY));
1677 5 : if (nLowResMinIdxX >= poGDS->m_nLowResWidth ||
1678 5 : nLowResMinIdxY >= poGDS->m_nLowResHeight || nLowResMaxIdxX < 0 ||
1679 : nLowResMaxIdxY < 0)
1680 : {
1681 0 : return CE_None;
1682 : }
1683 :
1684 : // Create memory space to receive the data.
1685 5 : const int nCountLowResX = nLowResMaxIdxX - nLowResMinIdxX + 1;
1686 5 : const int nCountLowResY = nLowResMaxIdxY - nLowResMinIdxY + 1;
1687 5 : hsize_t countVarresMD[2] = {static_cast<hsize_t>(nCountLowResY),
1688 5 : static_cast<hsize_t>(nCountLowResX)};
1689 5 : const hid_t memspaceVarresMD = H5Screate_simple(2, countVarresMD, nullptr);
1690 5 : H5OFFSET_TYPE mem_offset[2] = {static_cast<H5OFFSET_TYPE>(0),
1691 : static_cast<H5OFFSET_TYPE>(0)};
1692 5 : if (H5Sselect_hyperslab(memspaceVarresMD, H5S_SELECT_SET, mem_offset,
1693 5 : nullptr, countVarresMD, nullptr) < 0)
1694 : {
1695 0 : H5Sclose(memspaceVarresMD);
1696 0 : if (poBlock != nullptr)
1697 : {
1698 0 : poBlock->DropLock();
1699 0 : poBlock = nullptr;
1700 : }
1701 0 : return CE_Failure;
1702 : }
1703 :
1704 5 : std::vector<BAGRefinementGrid> rgrids(static_cast<size_t>(nCountLowResY) *
1705 10 : nCountLowResX);
1706 5 : if (!(poGDS->ReadVarresMetadataValue(nLowResMinIdxY, nLowResMinIdxX,
1707 : memspaceVarresMD, rgrids.data(),
1708 : nCountLowResY, nCountLowResX)))
1709 : {
1710 0 : H5Sclose(memspaceVarresMD);
1711 0 : if (poBlock != nullptr)
1712 : {
1713 0 : poBlock->DropLock();
1714 0 : poBlock = nullptr;
1715 : }
1716 0 : return CE_Failure;
1717 : }
1718 :
1719 5 : H5Sclose(memspaceVarresMD);
1720 :
1721 5 : CPLErr eErr = CE_None;
1722 5 : const double dfLowResMinX = poGDS->m_dfLowResMinX;
1723 5 : const double dfLowResMinY = poGDS->m_dfLowResMinY;
1724 :
1725 : // georeferenced (X,Y) coordinates of the source nodes from the refinement
1726 : //grids
1727 10 : std::vector<double> adfX, adfY;
1728 : // Depth and uncertainty values from the source nodes from the refinement
1729 : //grids
1730 10 : std::vector<float> afDepth, afUncrt;
1731 : // Work variable to sort adfX, adfY, afDepth, afUncrt w.r.t to their
1732 : // distance with the (dfX, dfY) point to be interpolated
1733 5 : std::vector<int> anIndices;
1734 :
1735 : // Maximum distance of candidate source nodes to the point to be
1736 : // interpolated
1737 5 : const double dfMaxDistance = 0.5 * std::max(dfLowResResX, dfLowResResY);
1738 :
1739 253 : for (int y = 0; y < nReqCountY; ++y)
1740 : {
1741 : // Y georeference ordinate of the center of the cell to interpolate
1742 248 : const double dfY = dfBlockMaxY + (y + 0.5) * poGDS->m_gt[5];
1743 : // Y index of the corresponding refinement grid
1744 248 : const int iYRefinedGrid =
1745 248 : static_cast<int>(floor((dfY - dfLowResMinY) / dfLowResResY));
1746 248 : if (iYRefinedGrid < nLowResMinIdxY || iYRefinedGrid > nLowResMaxIdxY)
1747 0 : continue;
1748 30928 : for (int x = 0; x < nReqCountX; ++x)
1749 : {
1750 : // X georeference ordinate of the center of the cell to interpolate
1751 30680 : const double dfX = dfBlockMinX + (x + 0.5) * poGDS->m_gt[1];
1752 : // X index of the corresponding refinement grid
1753 30680 : const int iXRefinedGrid =
1754 30680 : static_cast<int>((dfX - dfLowResMinX) / dfLowResResX);
1755 30680 : if (iXRefinedGrid < nLowResMinIdxX ||
1756 : iXRefinedGrid > nLowResMaxIdxX)
1757 0 : continue;
1758 :
1759 : // Correspond refinement grid
1760 : const auto &rgrid =
1761 30680 : rgrids[(iYRefinedGrid - nLowResMinIdxY) * nCountLowResX +
1762 30680 : (iXRefinedGrid - nLowResMinIdxX)];
1763 30680 : if (rgrid.nWidth == 0)
1764 : {
1765 0 : continue;
1766 : }
1767 30680 : const float gridRes = std::max(rgrid.fResX, rgrid.fResY);
1768 30680 : if (!(gridRes > poGDS->m_dfResFilterMin &&
1769 30680 : gridRes <= poGDS->m_dfResFilterMax))
1770 : {
1771 0 : continue;
1772 : }
1773 :
1774 : // (dfMinRefinedX, dfMinRefinedY) is the georeferenced coordinate
1775 : // of the bottom-left corner of the refinement grid
1776 30680 : const double dfMinRefinedX =
1777 30680 : dfLowResMinX + iXRefinedGrid * dfLowResResX + rgrid.fSWX;
1778 30680 : const double dfMinRefinedY =
1779 30680 : dfLowResMinY + iYRefinedGrid * dfLowResResY + rgrid.fSWY;
1780 :
1781 : // (iXInRefinedGrid, iYInRefinedGrid) is the index of the cell
1782 : // within the refinement grid into which (dfX, dfY) falls into.
1783 30680 : const int iXInRefinedGrid =
1784 30680 : static_cast<int>(floor((dfX - dfMinRefinedX) / rgrid.fResX));
1785 30680 : const int iYInRefinedGrid =
1786 30680 : static_cast<int>(floor((dfY - dfMinRefinedY) / rgrid.fResY));
1787 :
1788 30680 : if (iXInRefinedGrid >= 0 &&
1789 30680 : iXInRefinedGrid < static_cast<int>(rgrid.nWidth) - 1 &&
1790 2088 : iYInRefinedGrid >= 0 &&
1791 2088 : iYInRefinedGrid < static_cast<int>(rgrid.nHeight) - 1)
1792 : {
1793 : // The point to interpolate is fully within a single refinement
1794 : // grid
1795 126 : const unsigned nRefinementIndexBase =
1796 126 : rgrid.nIndex + iYInRefinedGrid * rgrid.nWidth +
1797 126 : iXInRefinedGrid;
1798 : float d[2][2];
1799 : float u[2][2];
1800 126 : int nCountNoData = 0;
1801 :
1802 : // Load the depth and uncertainty values of the 4 nodes of
1803 : // the refinement grid surrounding the center of the target
1804 : // cell.
1805 378 : for (int j = 0; j < 2; ++j)
1806 : {
1807 756 : for (int i = 0; i < 2; ++i)
1808 : {
1809 504 : const unsigned nRefinementIndex =
1810 504 : nRefinementIndexBase + j * rgrid.nWidth + i;
1811 : const auto pafRefValues =
1812 504 : poGDS->GetRefinementValues(nRefinementIndex);
1813 504 : if (!pafRefValues)
1814 : {
1815 0 : eErr = CE_Failure;
1816 0 : goto end;
1817 : }
1818 504 : d[j][i] = pafRefValues[0];
1819 504 : u[j][i] = pafRefValues[1];
1820 504 : if (d[j][i] == m_fNoDataValue)
1821 121 : ++nCountNoData;
1822 : }
1823 : }
1824 :
1825 : // Compute the relative distance of the point to be
1826 : // interpolated compared to the closest bottom-left most node
1827 : // of the refinement grid.
1828 : // (alphaX,alphaY)=(0,0): point to be interpolated matches the
1829 : // closest bottom-left most node
1830 : // (alphaX,alphaY)=(1,1): point to be interpolated matches the
1831 : // closest top-right most node
1832 : const double alphaX =
1833 126 : fmod(dfX - dfMinRefinedX, rgrid.fResX) / rgrid.fResX;
1834 : const double alphaY =
1835 126 : fmod(dfY - dfMinRefinedY, rgrid.fResY) / rgrid.fResY;
1836 126 : if (nCountNoData == 0)
1837 : {
1838 : // If the 4 nodes of the supergrid around the point
1839 : // to be interpolated are valid, do bilinear interpolation
1840 : #define BILINEAR_INTERP(var) \
1841 : ((1 - alphaY) * ((1 - alphaX) * var[0][0] + alphaX * var[0][1]) + \
1842 : alphaY * ((1 - alphaX) * var[1][0] + alphaX * var[1][1]))
1843 :
1844 5 : depthsPtr[y * nBlockXSize + x] =
1845 5 : static_cast<float>(BILINEAR_INTERP(d));
1846 5 : uncrtPtr[y * nBlockXSize + x] =
1847 5 : static_cast<float>(BILINEAR_INTERP(u));
1848 : }
1849 121 : else if (nCountNoData == 1)
1850 : {
1851 : // If only one of the 4 nodes is at nodata, determine if the
1852 : // point to be interpolated is within the triangle formed
1853 : // by the remaining 3 valid nodes.
1854 : // If so, do barycentric interpolation
1855 121 : adfX.resize(3);
1856 121 : adfY.resize(3);
1857 121 : afDepth.resize(3);
1858 121 : afUncrt.resize(3);
1859 :
1860 121 : int idx = 0;
1861 363 : for (int j = 0; j < 2; ++j)
1862 : {
1863 726 : for (int i = 0; i < 2; ++i)
1864 : {
1865 484 : if (d[j][i] != m_fNoDataValue)
1866 : {
1867 363 : CPLAssert(idx < 3);
1868 363 : adfX[idx] = i;
1869 363 : adfY[idx] = j;
1870 363 : afDepth[idx] = d[j][i];
1871 363 : afUncrt[idx] = u[j][i];
1872 363 : ++idx;
1873 : }
1874 : }
1875 : }
1876 121 : CPLAssert(idx == 3);
1877 : double dfCoord0;
1878 : double dfCoord1;
1879 : double dfCoord2;
1880 121 : if (BarycentricInterpolation(alphaX, alphaY, adfX.data(),
1881 121 : adfY.data(), dfCoord0,
1882 : dfCoord1, dfCoord2))
1883 : {
1884 : // Inside triangle
1885 98 : depthsPtr[y * nBlockXSize + x] = static_cast<float>(
1886 98 : dfCoord0 * afDepth[0] + dfCoord1 * afDepth[1] +
1887 98 : dfCoord2 * afDepth[2]);
1888 98 : uncrtPtr[y * nBlockXSize + x] = static_cast<float>(
1889 98 : dfCoord0 * afUncrt[0] + dfCoord1 * afUncrt[1] +
1890 98 : dfCoord2 * afUncrt[2]);
1891 : }
1892 126 : }
1893 : // else: 2 or more nodes invalid. Target point is set at nodata
1894 : }
1895 : else
1896 : {
1897 : // Point to interpolate is on an edge or corner of the
1898 : // refinement grid
1899 30554 : adfX.clear();
1900 30554 : adfY.clear();
1901 30554 : afDepth.clear();
1902 30554 : afUncrt.clear();
1903 :
1904 : const auto LoadValues =
1905 92778 : [this, dfX, dfY, &rgrids, nLowResMinIdxX, nLowResMinIdxY,
1906 : nCountLowResX, nCountLowResY, dfLowResMinX, dfLowResMinY,
1907 : dfLowResResX, dfLowResResY, &adfX, &adfY, &afDepth,
1908 92778 : &afUncrt](int iX, int iY)
1909 : {
1910 92778 : LoadClosestRefinedNodes(
1911 : dfX, dfY, iX, iY, rgrids, nLowResMinIdxX,
1912 : nLowResMinIdxY, nCountLowResX, nCountLowResY,
1913 : dfLowResMinX, dfLowResMinY, dfLowResResX, dfLowResResY,
1914 : adfX, adfY, afDepth, afUncrt);
1915 123332 : };
1916 :
1917 : // Load values of the closest point to the point to be
1918 : // interpolated in the current refinement grid
1919 30554 : LoadValues(iXRefinedGrid, iYRefinedGrid);
1920 :
1921 30554 : const bool bAtLeft = iXInRefinedGrid < 0 && iXRefinedGrid > 0;
1922 30554 : const bool bAtRight =
1923 59146 : iXInRefinedGrid >= static_cast<int>(rgrid.nWidth) - 1 &&
1924 28592 : iXRefinedGrid + 1 < poGDS->m_nLowResWidth;
1925 30554 : const bool bAtBottom = iYInRefinedGrid < 0 && iYRefinedGrid > 0;
1926 30554 : const bool bAtTop =
1927 59396 : iYInRefinedGrid >= static_cast<int>(rgrid.nHeight) - 1 &&
1928 28842 : iYRefinedGrid + 1 < poGDS->m_nLowResHeight;
1929 30554 : const int nXShift = bAtLeft ? -1 : bAtRight ? 1 : 0;
1930 30554 : const int nYShift = bAtBottom ? -1 : bAtTop ? 1 : 0;
1931 :
1932 : // Load values of the closest point to the point to be
1933 : // interpolated in the surrounding refinement grids.
1934 30554 : if (nXShift)
1935 : {
1936 23824 : LoadValues(iXRefinedGrid + nXShift, iYRefinedGrid);
1937 23824 : if (nYShift)
1938 : {
1939 16778 : LoadValues(iXRefinedGrid + nXShift,
1940 : iYRefinedGrid + nYShift);
1941 : }
1942 : }
1943 30554 : if (nYShift)
1944 : {
1945 21622 : LoadValues(iXRefinedGrid, iYRefinedGrid + nYShift);
1946 : }
1947 :
1948 : // Filter out candidate source points that are away from
1949 : // target point of more than dfMaxDistance
1950 30554 : size_t j = 0;
1951 129888 : for (size_t i = 0; i < adfX.size(); ++i)
1952 : {
1953 99334 : if (SQ(adfX[i] - dfX) + SQ(adfY[i] - dfY) <=
1954 99334 : dfMaxDistance * dfMaxDistance)
1955 : {
1956 28244 : adfX[j] = adfX[i];
1957 28244 : adfY[j] = adfY[i];
1958 28244 : afDepth[j] = afDepth[i];
1959 28244 : afUncrt[j] = afUncrt[i];
1960 28244 : ++j;
1961 : }
1962 : }
1963 30554 : adfX.resize(j);
1964 30554 : adfY.resize(j);
1965 30554 : afDepth.resize(j);
1966 30554 : afUncrt.resize(j);
1967 :
1968 : // Now interpolate the target point from the source values
1969 30554 : bool bTryIDW = false;
1970 30554 : if (adfX.size() >= 3)
1971 : {
1972 : // If there are at least 3 source nodes, sort them
1973 : // by increasing distance w.r.t the point to be interpolated
1974 319 : anIndices.clear();
1975 1571 : for (size_t i = 0; i < adfX.size(); ++i)
1976 1252 : anIndices.push_back(static_cast<int>(i));
1977 : // Sort nodes by increasing distance w.r.t (dfX, dfY)
1978 319 : std::sort(
1979 : anIndices.begin(), anIndices.end(),
1980 14240 : [&adfX, &adfY, dfX, dfY](int i1, int i2)
1981 : {
1982 1780 : return SQ(adfX[i1] - dfX) + SQ(adfY[i1] - dfY) <
1983 1780 : SQ(adfX[i2] - dfX) + SQ(adfY[i2] - dfY);
1984 : });
1985 : double adfXSorted[3];
1986 : double adfYSorted[3];
1987 : float afDepthSorted[3];
1988 : float afUncrtSorted[3];
1989 1276 : for (int i = 0; i < 3; ++i)
1990 : {
1991 957 : adfXSorted[i] = adfX[anIndices[i]];
1992 957 : adfYSorted[i] = adfY[anIndices[i]];
1993 957 : afDepthSorted[i] = afDepth[anIndices[i]];
1994 957 : afUncrtSorted[i] = afUncrt[anIndices[i]];
1995 : }
1996 : double dfCoord0;
1997 : double dfCoord1;
1998 : double dfCoord2;
1999 : // Perform barycentric interpolation with those 3
2000 : // points if they are all valid, and if the point to
2001 : // be interpolated falls into it.
2002 319 : if (afDepthSorted[0] != m_fNoDataValue &&
2003 230 : afDepthSorted[1] != m_fNoDataValue &&
2004 174 : afDepthSorted[2] != m_fNoDataValue)
2005 : {
2006 82 : if (BarycentricInterpolation(dfX, dfY, adfXSorted,
2007 : adfYSorted, dfCoord0,
2008 : dfCoord1, dfCoord2))
2009 : {
2010 : // Inside triangle
2011 77 : depthsPtr[y * nBlockXSize + x] =
2012 77 : static_cast<float>(dfCoord0 * afDepthSorted[0] +
2013 77 : dfCoord1 * afDepthSorted[1] +
2014 77 : dfCoord2 * afDepthSorted[2]);
2015 77 : uncrtPtr[y * nBlockXSize + x] =
2016 77 : static_cast<float>(dfCoord0 * afUncrtSorted[0] +
2017 77 : dfCoord1 * afUncrtSorted[1] +
2018 77 : dfCoord2 * afUncrtSorted[2]);
2019 : }
2020 : else
2021 : {
2022 : // Attempt inverse distance weighting in the
2023 : // cases where the point to be interpolated doesn't
2024 : // fall within the triangle formed by the 3
2025 : // closes points.
2026 5 : bTryIDW = true;
2027 : }
2028 : }
2029 : }
2030 30554 : if (bTryIDW)
2031 : {
2032 : // Do inverse distance weighting a a fallback.
2033 :
2034 5 : int nCountValid = 0;
2035 5 : double dfTotalDepth = 0;
2036 5 : double dfTotalUncrt = 0;
2037 5 : double dfTotalWeight = 0;
2038 : // Epsilon value to add to weights to avoid potential
2039 : // divergence to infinity if a source node is too close
2040 : // to the target point
2041 : const double EPS =
2042 5 : SQ(std::min(poGDS->m_gt[1], -poGDS->m_gt[5]) / 10);
2043 20 : for (size_t i = 0; i < adfX.size(); ++i)
2044 : {
2045 15 : if (afDepth[i] != m_fNoDataValue)
2046 : {
2047 15 : nCountValid++;
2048 : double dfSqrDistance =
2049 15 : SQ(adfX[i] - dfX) + SQ(adfY[i] - dfY) + EPS;
2050 15 : double dfWeight = 1. / dfSqrDistance;
2051 15 : dfTotalDepth += dfWeight * afDepth[i];
2052 15 : dfTotalUncrt += dfWeight * afUncrt[i];
2053 15 : dfTotalWeight += dfWeight;
2054 : }
2055 : }
2056 5 : if (nCountValid >= 3)
2057 : {
2058 5 : depthsPtr[y * nBlockXSize + x] =
2059 5 : static_cast<float>(dfTotalDepth / dfTotalWeight);
2060 5 : uncrtPtr[y * nBlockXSize + x] =
2061 5 : static_cast<float>(dfTotalUncrt / dfTotalWeight);
2062 : }
2063 : }
2064 : }
2065 : }
2066 : }
2067 5 : end:
2068 5 : if (poBlock != nullptr)
2069 : {
2070 5 : poBlock->DropLock();
2071 5 : poBlock = nullptr;
2072 : }
2073 :
2074 5 : return eErr;
2075 : }
2076 :
2077 : /************************************************************************/
2078 : /* LoadClosestRefinedNodes() */
2079 : /************************************************************************/
2080 :
2081 92778 : void BAGInterpolatedBand::LoadClosestRefinedNodes(
2082 : double dfX, double dfY, int iXRefinedGrid, int iYRefinedGrid,
2083 : const std::vector<BAGRefinementGrid> &rgrids, int nLowResMinIdxX,
2084 : int nLowResMinIdxY, int nCountLowResX, int nCountLowResY,
2085 : double dfLowResMinX, double dfLowResMinY, double dfLowResResX,
2086 : double dfLowResResY, std::vector<double> &adfX, std::vector<double> &adfY,
2087 : std::vector<float> &afDepth, std::vector<float> &afUncrt)
2088 : {
2089 92778 : BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
2090 92778 : CPLAssert(iXRefinedGrid >= nLowResMinIdxX);
2091 92778 : CPLAssert(iXRefinedGrid < nLowResMinIdxX + nCountLowResX);
2092 92778 : CPLAssert(iYRefinedGrid >= nLowResMinIdxY);
2093 92778 : CPLAssert(iYRefinedGrid < nLowResMinIdxY + nCountLowResY);
2094 92778 : CPL_IGNORE_RET_VAL(nCountLowResY);
2095 : const auto &rgrid =
2096 92778 : rgrids[(iYRefinedGrid - nLowResMinIdxY) * nCountLowResX +
2097 92778 : (iXRefinedGrid - nLowResMinIdxX)];
2098 92778 : if (rgrid.nWidth == 0)
2099 : {
2100 0 : return;
2101 : }
2102 92778 : const float gridRes = std::max(rgrid.fResX, rgrid.fResY);
2103 92778 : if (!(gridRes > poGDS->m_dfResFilterMin &&
2104 92778 : gridRes <= poGDS->m_dfResFilterMax))
2105 : {
2106 0 : return;
2107 : }
2108 :
2109 92778 : const double dfMinRefinedX =
2110 92778 : dfLowResMinX + iXRefinedGrid * dfLowResResX + rgrid.fSWX;
2111 92778 : const double dfMinRefinedY =
2112 92778 : dfLowResMinY + iYRefinedGrid * dfLowResResY + rgrid.fSWY;
2113 92778 : const int iXInRefinedGrid =
2114 92778 : static_cast<int>(floor((dfX - dfMinRefinedX) / rgrid.fResX));
2115 92778 : const int iYInRefinedGrid =
2116 92778 : static_cast<int>(floor((dfY - dfMinRefinedY) / rgrid.fResY));
2117 :
2118 99334 : const auto LoadValues = [poGDS, dfMinRefinedX, dfMinRefinedY, &rgrid, &adfX,
2119 : &adfY, &afDepth,
2120 496670 : &afUncrt](int iXAdjusted, int iYAdjusted)
2121 : {
2122 99334 : const unsigned nRefinementIndex =
2123 99334 : rgrid.nIndex + iYAdjusted * rgrid.nWidth + iXAdjusted;
2124 99334 : const auto pafRefValues = poGDS->GetRefinementValues(nRefinementIndex);
2125 99334 : if (pafRefValues)
2126 : {
2127 0 : adfX.push_back(dfMinRefinedX +
2128 99334 : iXAdjusted * static_cast<double>(rgrid.fResX));
2129 198668 : adfY.push_back(dfMinRefinedY +
2130 99334 : iYAdjusted * static_cast<double>(rgrid.fResY));
2131 99334 : afDepth.push_back(pafRefValues[0]);
2132 99334 : afUncrt.push_back(pafRefValues[1]);
2133 : }
2134 99334 : };
2135 :
2136 : const int iXAdjusted = std::max(
2137 92778 : 0, std::min(iXInRefinedGrid, static_cast<int>(rgrid.nWidth) - 1));
2138 : const int iYAdjusted = std::max(
2139 92778 : 0, std::min(iYInRefinedGrid, static_cast<int>(rgrid.nHeight) - 1));
2140 92778 : LoadValues(iXAdjusted, iYAdjusted);
2141 92778 : if (iYInRefinedGrid >= 0 &&
2142 54378 : iYInRefinedGrid < static_cast<int>(rgrid.nHeight) - 1)
2143 3145 : LoadValues(iXAdjusted, iYInRefinedGrid + 1);
2144 92778 : if (iXInRefinedGrid >= 0 &&
2145 52176 : iXInRefinedGrid < static_cast<int>(rgrid.nWidth) - 1)
2146 3411 : LoadValues(iXInRefinedGrid + 1, iYAdjusted);
2147 : }
2148 :
2149 : /************************************************************************/
2150 : /* ==================================================================== */
2151 : /* BAGGeorefMDBandBase */
2152 : /* ==================================================================== */
2153 : /************************************************************************/
2154 :
2155 : class BAGGeorefMDBandBase CPL_NON_FINAL : public GDALPamRasterBand
2156 : {
2157 : protected:
2158 : std::shared_ptr<GDALMDArray> m_poKeys;
2159 : std::unique_ptr<GDALRasterBand> m_poElevBand;
2160 : std::unique_ptr<GDALRasterAttributeTable> m_poRAT{};
2161 :
2162 6 : BAGGeorefMDBandBase(const std::shared_ptr<GDALMDArray> &poValues,
2163 : const std::shared_ptr<GDALMDArray> &poKeys,
2164 : GDALRasterBand *poElevBand)
2165 6 : : m_poKeys(poKeys), m_poElevBand(poElevBand),
2166 6 : m_poRAT(HDF5CreateRAT(poValues, false))
2167 : {
2168 6 : }
2169 :
2170 : CPLErr IReadBlockFromElevBand(int nBlockXOff, int nBlockYOff, void *pImage);
2171 :
2172 : public:
2173 1 : GDALRasterAttributeTable *GetDefaultRAT() override
2174 : {
2175 1 : return m_poRAT.get();
2176 : }
2177 :
2178 : double GetNoDataValue(int *pbSuccess) override;
2179 : };
2180 :
2181 : /************************************************************************/
2182 : /* GetNoDataValue() */
2183 : /************************************************************************/
2184 :
2185 1 : double BAGGeorefMDBandBase::GetNoDataValue(int *pbSuccess)
2186 : {
2187 1 : if (pbSuccess)
2188 1 : *pbSuccess = TRUE;
2189 1 : return 0;
2190 : }
2191 :
2192 : /************************************************************************/
2193 : /* IReadBlockFromElevBand() */
2194 : /************************************************************************/
2195 :
2196 8 : CPLErr BAGGeorefMDBandBase::IReadBlockFromElevBand(int nBlockXOff,
2197 : int nBlockYOff, void *pImage)
2198 : {
2199 16 : std::vector<float> afData(static_cast<size_t>(nBlockXSize) * nBlockYSize);
2200 8 : const int nXOff = nBlockXOff * nBlockXSize;
2201 8 : const int nReqXSize = std::min(nBlockXSize, nRasterXSize - nXOff);
2202 8 : const int nYOff = nBlockYOff * nBlockYSize;
2203 8 : const int nReqYSize = std::min(nBlockYSize, nRasterYSize - nYOff);
2204 16 : if (m_poElevBand->RasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
2205 8 : &afData[0], nReqXSize, nReqYSize, GDT_Float32, 4,
2206 16 : nBlockXSize * 4, nullptr) != CE_None)
2207 : {
2208 0 : return CE_Failure;
2209 : }
2210 8 : int bHasNoData = FALSE;
2211 : const float fNoDataValue =
2212 8 : static_cast<float>(m_poElevBand->GetNoDataValue(&bHasNoData));
2213 8 : GByte *const pbyImage = static_cast<GByte *>(pImage);
2214 16 : for (int y = 0; y < nReqYSize; y++)
2215 : {
2216 40 : for (int x = 0; x < nReqXSize; x++)
2217 : {
2218 32 : pbyImage[y * nBlockXSize + x] =
2219 32 : (afData[y * nBlockXSize + x] == fNoDataValue ||
2220 29 : std::isnan(afData[y * nBlockXSize + x]))
2221 61 : ? 0
2222 : : 1;
2223 : }
2224 : }
2225 :
2226 8 : return CE_None;
2227 : }
2228 :
2229 : /************************************************************************/
2230 : /* ==================================================================== */
2231 : /* BAGGeorefMDBand */
2232 : /* ==================================================================== */
2233 : /************************************************************************/
2234 :
2235 : class BAGGeorefMDBand final : public BAGGeorefMDBandBase
2236 : {
2237 : public:
2238 : BAGGeorefMDBand(const std::shared_ptr<GDALMDArray> &poValues,
2239 : const std::shared_ptr<GDALMDArray> &poKeys,
2240 : GDALRasterBand *poElevBand);
2241 :
2242 : CPLErr IReadBlock(int, int, void *) override;
2243 : };
2244 :
2245 : /************************************************************************/
2246 : /* BAGGeorefMDBand() */
2247 : /************************************************************************/
2248 :
2249 2 : BAGGeorefMDBand::BAGGeorefMDBand(const std::shared_ptr<GDALMDArray> &poValues,
2250 : const std::shared_ptr<GDALMDArray> &poKeys,
2251 2 : GDALRasterBand *poElevBand)
2252 2 : : BAGGeorefMDBandBase(poValues, poKeys, poElevBand)
2253 : {
2254 2 : nRasterXSize = poElevBand->GetXSize();
2255 2 : nRasterYSize = poElevBand->GetYSize();
2256 2 : if (poKeys)
2257 : {
2258 2 : auto blockSize = poKeys->GetBlockSize();
2259 1 : CPLAssert(blockSize.size() == 2);
2260 1 : nBlockYSize = static_cast<int>(blockSize[0]);
2261 1 : nBlockXSize = static_cast<int>(blockSize[1]);
2262 1 : eDataType = poKeys->GetDataType().GetNumericDataType();
2263 1 : if (nBlockXSize == 0 || nBlockYSize == 0)
2264 : {
2265 0 : nBlockXSize = nRasterXSize;
2266 0 : nBlockYSize = 1;
2267 : }
2268 : }
2269 : else
2270 : {
2271 1 : eDataType = GDT_UInt8;
2272 1 : m_poElevBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
2273 : }
2274 :
2275 : // For testing purposes
2276 : const char *pszBlockXSize =
2277 2 : CPLGetConfigOption("BAG_GEOREF_MD_BLOCKXSIZE", nullptr);
2278 2 : if (pszBlockXSize)
2279 0 : nBlockXSize = atoi(pszBlockXSize);
2280 : const char *pszBlockYSize =
2281 2 : CPLGetConfigOption("BAG_GEOREF_MD_BLOCKYSIZE", nullptr);
2282 2 : if (pszBlockYSize)
2283 0 : nBlockYSize = atoi(pszBlockYSize);
2284 2 : }
2285 :
2286 : /************************************************************************/
2287 : /* IReadBlock() */
2288 : /************************************************************************/
2289 :
2290 8 : CPLErr BAGGeorefMDBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
2291 : {
2292 : HDF5_GLOBAL_LOCK();
2293 :
2294 8 : if (m_poKeys)
2295 : {
2296 : const GUInt64 arrayStartIdx[2] = {
2297 4 : static_cast<GUInt64>(
2298 4 : std::max(0, nRasterYSize - (nBlockYOff + 1) * nBlockYSize)),
2299 4 : static_cast<GUInt64>(nBlockXOff) * nBlockXSize};
2300 : size_t count[2] = {
2301 8 : std::min(static_cast<size_t>(nBlockYSize),
2302 4 : static_cast<size_t>(GetYSize() - arrayStartIdx[0])),
2303 8 : std::min(static_cast<size_t>(nBlockXSize),
2304 4 : static_cast<size_t>(GetXSize() - arrayStartIdx[1]))};
2305 4 : if (nRasterYSize - (nBlockYOff + 1) * nBlockYSize < 0)
2306 : {
2307 0 : count[0] += (nRasterYSize - (nBlockYOff + 1) * nBlockYSize);
2308 : }
2309 4 : const GInt64 arrayStep[2] = {1, 1};
2310 4 : const GPtrDiff_t bufferStride[2] = {nBlockXSize, 1};
2311 :
2312 8 : if (!m_poKeys->Read(arrayStartIdx, count, arrayStep, bufferStride,
2313 4 : m_poKeys->GetDataType(), pImage))
2314 : {
2315 0 : return CE_Failure;
2316 : }
2317 :
2318 : // Y flip the data.
2319 4 : const int nLinesToFlip = static_cast<int>(count[0]);
2320 4 : if (nLinesToFlip > 1)
2321 : {
2322 : const int nLineSize =
2323 4 : GDALGetDataTypeSizeBytes(eDataType) * nBlockXSize;
2324 4 : GByte *const pabyTemp = static_cast<GByte *>(CPLMalloc(nLineSize));
2325 4 : GByte *const pbyImage = static_cast<GByte *>(pImage);
2326 :
2327 8 : for (int iY = 0; iY < nLinesToFlip / 2; iY++)
2328 : {
2329 4 : memcpy(pabyTemp, pbyImage + iY * nLineSize, nLineSize);
2330 4 : memcpy(pbyImage + iY * nLineSize,
2331 4 : pbyImage + (nLinesToFlip - iY - 1) * nLineSize,
2332 : nLineSize);
2333 4 : memcpy(pbyImage + (nLinesToFlip - iY - 1) * nLineSize, pabyTemp,
2334 : nLineSize);
2335 : }
2336 :
2337 4 : CPLFree(pabyTemp);
2338 : }
2339 4 : return CE_None;
2340 : }
2341 : else
2342 : {
2343 4 : return IReadBlockFromElevBand(nBlockXOff, nBlockYOff, pImage);
2344 : }
2345 : }
2346 :
2347 : /************************************************************************/
2348 : /* ==================================================================== */
2349 : /* BAGGeorefMDSuperGridBand */
2350 : /* ==================================================================== */
2351 : /************************************************************************/
2352 :
2353 : class BAGGeorefMDSuperGridBand final : public BAGGeorefMDBandBase
2354 : {
2355 : public:
2356 : BAGGeorefMDSuperGridBand(const std::shared_ptr<GDALMDArray> &poValues,
2357 : const std::shared_ptr<GDALMDArray> &poKeys,
2358 : GDALRasterBand *poElevBand);
2359 :
2360 : CPLErr IReadBlock(int, int, void *) override;
2361 : };
2362 :
2363 : /************************************************************************/
2364 : /* BAGGeorefMDSuperGridBand() */
2365 : /************************************************************************/
2366 :
2367 4 : BAGGeorefMDSuperGridBand::BAGGeorefMDSuperGridBand(
2368 : const std::shared_ptr<GDALMDArray> &poValues,
2369 4 : const std::shared_ptr<GDALMDArray> &poKeys, GDALRasterBand *poElevBand)
2370 4 : : BAGGeorefMDBandBase(poValues, poKeys, poElevBand)
2371 : {
2372 4 : nRasterXSize = poElevBand->GetXSize();
2373 4 : nRasterYSize = poElevBand->GetYSize();
2374 4 : if (poKeys)
2375 : {
2376 2 : nBlockYSize = 1;
2377 2 : nBlockXSize = nRasterXSize;
2378 2 : eDataType = poKeys->GetDataType().GetNumericDataType();
2379 : }
2380 : else
2381 : {
2382 2 : eDataType = GDT_UInt8;
2383 2 : m_poElevBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
2384 : }
2385 4 : }
2386 :
2387 : /************************************************************************/
2388 : /* IReadBlock() */
2389 : /************************************************************************/
2390 :
2391 8 : CPLErr BAGGeorefMDSuperGridBand::IReadBlock(int nBlockXOff, int nBlockYOff,
2392 : void *pImage)
2393 : {
2394 8 : BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
2395 8 : if (m_poKeys)
2396 : {
2397 4 : const GUInt64 arrayStartIdx[2] = {
2398 4 : 0, poGDS->m_nSuperGridRefinementStartIndex +
2399 4 : static_cast<GUInt64>(nRasterYSize - 1 - nBlockYOff) *
2400 4 : nBlockXSize};
2401 4 : size_t count[2] = {1, static_cast<size_t>(nBlockXSize)};
2402 4 : const GInt64 arrayStep[2] = {1, 1};
2403 4 : const GPtrDiff_t bufferStride[2] = {nBlockXSize, 1};
2404 :
2405 8 : if (!m_poKeys->Read(arrayStartIdx, count, arrayStep, bufferStride,
2406 4 : m_poKeys->GetDataType(), pImage))
2407 : {
2408 0 : return CE_Failure;
2409 : }
2410 4 : return CE_None;
2411 : }
2412 : else
2413 : {
2414 4 : return IReadBlockFromElevBand(nBlockXOff, nBlockYOff, pImage);
2415 : }
2416 : }
2417 :
2418 : /************************************************************************/
2419 : /* ==================================================================== */
2420 : /* BAGDataset */
2421 : /* ==================================================================== */
2422 : /************************************************************************/
2423 :
2424 : /************************************************************************/
2425 : /* BAGDataset() */
2426 : /************************************************************************/
2427 :
2428 89 : BAGDataset::BAGDataset()
2429 : {
2430 89 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2431 89 : }
2432 :
2433 28 : BAGDataset::BAGDataset(BAGDataset *poParentDS, int nOvrFactor)
2434 : {
2435 28 : const int nXSize = poParentDS->nRasterXSize / nOvrFactor;
2436 28 : const int nYSize = poParentDS->nRasterYSize / nOvrFactor;
2437 28 : InitOverviewDS(poParentDS, nXSize, nYSize);
2438 28 : }
2439 :
2440 17 : BAGDataset::BAGDataset(BAGDataset *poParentDS, int nXSize, int nYSize)
2441 : {
2442 17 : InitOverviewDS(poParentDS, nXSize, nYSize);
2443 17 : }
2444 :
2445 45 : void BAGDataset::InitOverviewDS(BAGDataset *poParentDS, int nXSize, int nYSize)
2446 : {
2447 45 : m_ePopulation = poParentDS->m_ePopulation;
2448 45 : m_bMask = poParentDS->m_bMask;
2449 45 : m_bIsChild = true;
2450 : // m_apoOverviewDS
2451 45 : m_poSharedResources = poParentDS->m_poSharedResources;
2452 45 : m_poRootGroup = poParentDS->m_poRootGroup;
2453 45 : m_oSRS = poParentDS->m_oSRS;
2454 45 : nRasterXSize = nXSize;
2455 45 : nRasterYSize = nYSize;
2456 45 : m_gt[0] = poParentDS->m_gt[0];
2457 45 : m_gt[1] = poParentDS->m_gt[1] * poParentDS->nRasterXSize / nRasterXSize;
2458 45 : m_gt[2] = poParentDS->m_gt[2];
2459 45 : m_gt[3] = poParentDS->m_gt[3];
2460 45 : m_gt[4] = poParentDS->m_gt[4];
2461 45 : m_gt[5] = poParentDS->m_gt[5] * poParentDS->nRasterYSize / nRasterYSize;
2462 45 : m_nLowResWidth = poParentDS->m_nLowResWidth;
2463 45 : m_nLowResHeight = poParentDS->m_nLowResHeight;
2464 45 : m_dfLowResMinX = poParentDS->m_dfLowResMinX;
2465 45 : m_dfLowResMinY = poParentDS->m_dfLowResMinY;
2466 45 : m_dfLowResMaxX = poParentDS->m_dfLowResMaxX;
2467 45 : m_dfLowResMaxY = poParentDS->m_dfLowResMaxY;
2468 : // char *pszXMLMetadata = nullptr;
2469 : // char *apszMDList[2]{};
2470 45 : m_nChunkXSizeVarresMD = poParentDS->m_nChunkXSizeVarresMD;
2471 45 : m_nChunkYSizeVarresMD = poParentDS->m_nChunkYSizeVarresMD;
2472 45 : m_nChunkSizeVarresRefinement = poParentDS->m_nChunkSizeVarresRefinement;
2473 :
2474 45 : m_hVarresMetadata = poParentDS->m_hVarresMetadata;
2475 45 : m_hVarresMetadataDataType = poParentDS->m_hVarresMetadataDataType;
2476 45 : m_hVarresMetadataDataspace = poParentDS->m_hVarresMetadataDataspace;
2477 45 : m_hVarresMetadataNative = poParentDS->m_hVarresMetadataNative;
2478 : // m_oMapRefinemendGrids;
2479 :
2480 : // m_aosSubdatasets;
2481 :
2482 45 : m_hVarresRefinements = poParentDS->m_hVarresRefinements;
2483 45 : m_hVarresRefinementsDataType = poParentDS->m_hVarresRefinementsDataType;
2484 45 : m_hVarresRefinementsDataspace = poParentDS->m_hVarresRefinementsDataspace;
2485 45 : m_hVarresRefinementsNative = poParentDS->m_hVarresRefinementsNative;
2486 45 : m_nRefinementsSize = poParentDS->m_nRefinementsSize;
2487 :
2488 45 : m_nSuperGridRefinementStartIndex =
2489 45 : poParentDS->m_nSuperGridRefinementStartIndex;
2490 45 : m_dfResFilterMin = poParentDS->m_dfResFilterMin;
2491 45 : m_dfResFilterMax = poParentDS->m_dfResFilterMax;
2492 :
2493 45 : if (poParentDS->GetRasterCount() > 1)
2494 : {
2495 45 : GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
2496 : }
2497 45 : }
2498 :
2499 : /************************************************************************/
2500 : /* ~BAGDataset() */
2501 : /************************************************************************/
2502 268 : BAGDataset::~BAGDataset()
2503 : {
2504 134 : if (eAccess == GA_Update && nBands == 1)
2505 : {
2506 2 : auto poFirstBand = cpl::down_cast<BAGRasterBand *>(GetRasterBand(1));
2507 2 : auto poBand = new BAGRasterBand(this, 2);
2508 2 : poBand->nBlockXSize = poFirstBand->nBlockXSize;
2509 2 : poBand->nBlockYSize = poFirstBand->nBlockYSize;
2510 2 : poBand->eDataType = GDT_Float32;
2511 2 : poBand->m_bHasNoData = true;
2512 2 : poBand->m_fNoDataValue = poFirstBand->m_fNoDataValue;
2513 2 : SetBand(2, poBand);
2514 : }
2515 :
2516 134 : if (eAccess == GA_Update)
2517 : {
2518 12 : for (int i = 0; i < nBands; i++)
2519 : {
2520 : cpl::down_cast<BAGRasterBand *>(GetRasterBand(i + 1))
2521 8 : ->CreateDatasetIfNeeded();
2522 : }
2523 : }
2524 :
2525 134 : FlushCache(true);
2526 :
2527 134 : m_apoOverviewDS.clear();
2528 134 : if (!m_bIsChild)
2529 : {
2530 89 : if (m_hVarresMetadataDataType >= 0)
2531 63 : H5Tclose(m_hVarresMetadataDataType);
2532 :
2533 89 : if (m_hVarresMetadataDataspace >= 0)
2534 63 : H5Sclose(m_hVarresMetadataDataspace);
2535 :
2536 89 : if (m_hVarresMetadataNative >= 0)
2537 63 : H5Tclose(m_hVarresMetadataNative);
2538 :
2539 89 : if (m_hVarresMetadata >= 0)
2540 63 : H5Dclose(m_hVarresMetadata);
2541 :
2542 89 : if (m_hVarresRefinementsDataType >= 0)
2543 63 : H5Tclose(m_hVarresRefinementsDataType);
2544 :
2545 89 : if (m_hVarresRefinementsDataspace >= 0)
2546 63 : H5Sclose(m_hVarresRefinementsDataspace);
2547 :
2548 89 : if (m_hVarresRefinementsNative >= 0)
2549 63 : H5Tclose(m_hVarresRefinementsNative);
2550 :
2551 89 : if (m_hVarresRefinements >= 0)
2552 63 : H5Dclose(m_hVarresRefinements);
2553 :
2554 89 : CPLFree(pszXMLMetadata);
2555 : }
2556 268 : }
2557 :
2558 : /************************************************************************/
2559 : /* GH5DopenNoWarning() */
2560 : /************************************************************************/
2561 :
2562 83 : static hid_t GH5DopenNoWarning(hid_t hHDF5, const char *pszDatasetName)
2563 : {
2564 : hid_t hDataset;
2565 : #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT
2566 : #pragma GCC diagnostic push
2567 : #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
2568 : #endif
2569 83 : H5E_BEGIN_TRY
2570 : {
2571 83 : hDataset = H5Dopen(hHDF5, pszDatasetName);
2572 : }
2573 83 : H5E_END_TRY;
2574 :
2575 : #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT
2576 : #pragma GCC diagnostic pop
2577 : #endif
2578 83 : return hDataset;
2579 : }
2580 :
2581 : /************************************************************************/
2582 : /* Open() */
2583 : /************************************************************************/
2584 :
2585 98 : GDALDataset *BAGDataset::Open(GDALOpenInfo *poOpenInfo)
2586 :
2587 : {
2588 : // Confirm that this appears to be a BAG file.
2589 98 : if (!BAGDatasetIdentify(poOpenInfo))
2590 12 : return nullptr;
2591 :
2592 : HDF5_GLOBAL_LOCK();
2593 :
2594 86 : if (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)
2595 : {
2596 0 : return HDF5Dataset::OpenMultiDim(poOpenInfo);
2597 : }
2598 :
2599 : // Confirm the requested access is supported.
2600 86 : if (poOpenInfo->eAccess == GA_Update)
2601 : {
2602 0 : ReportUpdateNotSupportedByDriver("BAG");
2603 0 : return nullptr;
2604 : }
2605 :
2606 86 : bool bOpenSuperGrid = false;
2607 86 : int nX = -1;
2608 86 : int nY = -1;
2609 172 : CPLString osFilename(poOpenInfo->pszFilename);
2610 172 : CPLString osGeorefMetadataLayer;
2611 86 : bool bIsSubdataset = false;
2612 86 : if (STARTS_WITH(poOpenInfo->pszFilename, "BAG:"))
2613 : {
2614 16 : bIsSubdataset = true;
2615 : char **papszTokens =
2616 16 : CSLTokenizeString2(poOpenInfo->pszFilename, ":",
2617 : CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES);
2618 :
2619 17 : if (CSLCount(papszTokens) == 3 &&
2620 1 : EQUAL(papszTokens[2], "bathymetry_coverage"))
2621 : {
2622 1 : osFilename = papszTokens[1];
2623 : }
2624 18 : else if (CSLCount(papszTokens) == 4 &&
2625 3 : EQUAL(papszTokens[2], "georef_metadata"))
2626 : {
2627 3 : osFilename = papszTokens[1];
2628 3 : osGeorefMetadataLayer = papszTokens[3];
2629 : }
2630 16 : else if (CSLCount(papszTokens) == 6 &&
2631 4 : EQUAL(papszTokens[2], "georef_metadata"))
2632 : {
2633 4 : osFilename = papszTokens[1];
2634 4 : osGeorefMetadataLayer = papszTokens[3];
2635 4 : bOpenSuperGrid = true;
2636 4 : nY = atoi(papszTokens[4]);
2637 4 : nX = atoi(papszTokens[5]);
2638 : }
2639 : else
2640 : {
2641 8 : if (CSLCount(papszTokens) != 5)
2642 : {
2643 0 : CSLDestroy(papszTokens);
2644 0 : return nullptr;
2645 : }
2646 8 : bOpenSuperGrid = true;
2647 8 : osFilename = papszTokens[1];
2648 8 : nY = atoi(papszTokens[3]);
2649 8 : nX = atoi(papszTokens[4]);
2650 : }
2651 16 : if (bOpenSuperGrid)
2652 : {
2653 :
2654 12 : if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINX") !=
2655 11 : nullptr ||
2656 11 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINY") !=
2657 11 : nullptr ||
2658 11 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXX") !=
2659 11 : nullptr ||
2660 11 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXY") !=
2661 23 : nullptr ||
2662 11 : CSLFetchNameValue(poOpenInfo->papszOpenOptions,
2663 : "SUPERGRIDS_INDICES") != nullptr)
2664 : {
2665 1 : CPLError(
2666 : CE_Warning, CPLE_AppDefined,
2667 : "Open options MINX/MINY/MAXX/MAXY/SUPERGRIDS_INDICES are "
2668 : "ignored when opening a supergrid");
2669 : }
2670 : }
2671 16 : CSLDestroy(papszTokens);
2672 : }
2673 :
2674 : // Open the file as an HDF5 file.
2675 86 : hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
2676 86 : H5Pset_driver(fapl, HDF5GetFileDriver(), nullptr);
2677 86 : hid_t hHDF5 = H5Fopen(
2678 : osFilename,
2679 86 : /*poOpenInfo->eAccess == GA_Update ? H5F_ACC_RDWR : */ H5F_ACC_RDONLY,
2680 : fapl);
2681 86 : H5Pclose(fapl);
2682 86 : if (hHDF5 < 0)
2683 1 : return nullptr;
2684 :
2685 : // Confirm it is a BAG dataset by checking for the
2686 : // BAG_root/Bag Version attribute.
2687 85 : const hid_t hBagRoot = H5Gopen(hHDF5, "/BAG_root");
2688 : const hid_t hVersion =
2689 85 : hBagRoot >= 0 ? H5Aopen_name(hBagRoot, "Bag Version") : -1;
2690 :
2691 85 : if (hVersion < 0)
2692 : {
2693 0 : if (hBagRoot >= 0)
2694 0 : H5Gclose(hBagRoot);
2695 0 : H5Fclose(hHDF5);
2696 0 : return nullptr;
2697 : }
2698 85 : H5Aclose(hVersion);
2699 :
2700 170 : auto poSharedResources = GDAL::HDF5SharedResources::Create(osFilename);
2701 85 : poSharedResources->m_hHDF5 = hHDF5;
2702 :
2703 170 : auto poRootGroup = HDF5Dataset::OpenGroup(poSharedResources);
2704 85 : if (poRootGroup == nullptr)
2705 0 : return nullptr;
2706 :
2707 : // Create a corresponding dataset.
2708 85 : BAGDataset *const poDS = new BAGDataset();
2709 :
2710 85 : poDS->eAccess = poOpenInfo->eAccess;
2711 85 : poDS->m_poRootGroup = std::move(poRootGroup);
2712 85 : poDS->m_poSharedResources = std::move(poSharedResources);
2713 :
2714 : // Extract version as metadata.
2715 170 : CPLString osVersion;
2716 :
2717 85 : if (GH5_FetchAttribute(hBagRoot, "Bag Version", osVersion))
2718 85 : poDS->GDALDataset::SetMetadataItem("BagVersion", osVersion);
2719 :
2720 85 : H5Gclose(hBagRoot);
2721 :
2722 170 : CPLString osSubDsName;
2723 85 : if (poOpenInfo->nOpenFlags & GDAL_OF_RASTER)
2724 : {
2725 84 : if (poDS->OpenRaster(poOpenInfo, osFilename, bOpenSuperGrid, nX, nY,
2726 : bIsSubdataset, osGeorefMetadataLayer, osSubDsName))
2727 : {
2728 76 : if (!osSubDsName.empty())
2729 : {
2730 2 : delete poDS;
2731 4 : GDALOpenInfo oOpenInfo(osSubDsName, GA_ReadOnly);
2732 2 : oOpenInfo.nOpenFlags = poOpenInfo->nOpenFlags;
2733 2 : return Open(&oOpenInfo);
2734 : }
2735 : }
2736 : else
2737 : {
2738 8 : delete poDS;
2739 8 : return nullptr;
2740 : }
2741 : }
2742 :
2743 75 : if (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)
2744 : {
2745 47 : if (!poDS->OpenVector() &&
2746 1 : (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0)
2747 : {
2748 0 : delete poDS;
2749 0 : return nullptr;
2750 : }
2751 : }
2752 :
2753 75 : return poDS;
2754 : }
2755 :
2756 : /************************************************************************/
2757 : /* OpenRaster() */
2758 : /************************************************************************/
2759 :
2760 84 : bool BAGDataset::OpenRaster(GDALOpenInfo *poOpenInfo,
2761 : const CPLString &osFilename, bool bOpenSuperGrid,
2762 : int nX, int nY, bool bIsSubdataset,
2763 : const CPLString &osGeorefMetadataLayer,
2764 : CPLString &outOsSubDsName)
2765 : {
2766 : const char *pszMode =
2767 84 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "MODE", "AUTO");
2768 84 : const bool bLowResGrid = EQUAL(pszMode, "LOW_RES_GRID");
2769 85 : if (bLowResGrid &&
2770 1 : (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINX") != nullptr ||
2771 0 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINY") != nullptr ||
2772 0 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXX") != nullptr ||
2773 0 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXY") != nullptr ||
2774 0 : CSLFetchNameValue(poOpenInfo->papszOpenOptions,
2775 : "SUPERGRIDS_INDICES") != nullptr))
2776 : {
2777 1 : CPLError(CE_Warning, CPLE_AppDefined,
2778 : "Open options MINX/MINY/MAXX/MAXY/SUPERGRIDS_INDICES are "
2779 : "ignored when opening the low resolution grid");
2780 : }
2781 :
2782 : const bool bListSubDS =
2783 167 : !bLowResGrid &&
2784 83 : (EQUAL(pszMode, "LIST_SUPERGRIDS") ||
2785 78 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINX") != nullptr ||
2786 76 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINY") != nullptr ||
2787 75 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXX") != nullptr ||
2788 75 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXY") != nullptr ||
2789 75 : CSLFetchNameValue(poOpenInfo->papszOpenOptions,
2790 84 : "SUPERGRIDS_INDICES") != nullptr);
2791 84 : const bool bResampledGrid = EQUAL(pszMode, "RESAMPLED_GRID");
2792 84 : const bool bInterpolate = EQUAL(pszMode, "INTERPOLATED");
2793 :
2794 : const char *pszNoDataValue =
2795 84 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "NODATA_VALUE");
2796 84 : bool bHasNoData = pszNoDataValue != nullptr;
2797 : float fNoDataValue =
2798 84 : bHasNoData ? static_cast<float>(CPLAtof(pszNoDataValue)) : 0.0f;
2799 :
2800 : // Fetch the elevation dataset and attach as a band.
2801 84 : int nNextBand = 1;
2802 :
2803 84 : BAGRasterBand *poElevBand = new BAGRasterBand(this, nNextBand);
2804 :
2805 : {
2806 : const hid_t hElevation =
2807 84 : H5Dopen(GetHDF5Handle(), "/BAG_root/elevation");
2808 84 : if (hElevation < 0 || !poElevBand->Initialize(hElevation, "elevation"))
2809 : {
2810 2 : delete poElevBand;
2811 2 : return false;
2812 : }
2813 : }
2814 :
2815 82 : m_nLowResWidth = poElevBand->nRasterXSize;
2816 82 : m_nLowResHeight = poElevBand->nRasterYSize;
2817 :
2818 82 : if (bOpenSuperGrid || bListSubDS || bResampledGrid || bInterpolate)
2819 : {
2820 53 : if (!bHasNoData)
2821 : {
2822 53 : int nHasNoData = FALSE;
2823 53 : double dfNoData = poElevBand->GetNoDataValue(&nHasNoData);
2824 53 : if (nHasNoData)
2825 : {
2826 53 : bHasNoData = true;
2827 53 : fNoDataValue = static_cast<float>(dfNoData);
2828 : }
2829 : }
2830 53 : delete poElevBand;
2831 53 : nRasterXSize = 0;
2832 53 : nRasterYSize = 0;
2833 : }
2834 29 : else if (!osGeorefMetadataLayer.empty())
2835 : {
2836 3 : auto poGeoref_metadataLayer = m_poRootGroup->OpenGroupFromFullname(
2837 3 : "/BAG_root/georef_metadata/" + osGeorefMetadataLayer, nullptr);
2838 3 : if (poGeoref_metadataLayer == nullptr)
2839 : {
2840 1 : CPLError(CE_Failure, CPLE_AppDefined,
2841 : "Cannot find georef_metadata layer %s",
2842 : osGeorefMetadataLayer.c_str());
2843 1 : delete poElevBand;
2844 1 : return false;
2845 : }
2846 :
2847 4 : auto poKeys = poGeoref_metadataLayer->OpenMDArray("keys");
2848 2 : if (poKeys != nullptr)
2849 : {
2850 1 : auto poDims = poKeys->GetDimensions();
2851 1 : if (poDims.size() != 2 ||
2852 1 : poDims[0]->GetSize() !=
2853 3 : static_cast<size_t>(poElevBand->nRasterYSize) ||
2854 1 : poDims[1]->GetSize() !=
2855 1 : static_cast<size_t>(poElevBand->nRasterXSize))
2856 : {
2857 0 : CPLError(CE_Failure, CPLE_AppDefined,
2858 : "Wrong dimensions for %s/keys",
2859 : osGeorefMetadataLayer.c_str());
2860 0 : delete poElevBand;
2861 0 : return false;
2862 : }
2863 2 : if (poKeys->GetDataType().GetClass() != GEDTC_NUMERIC ||
2864 1 : !GDALDataTypeIsInteger(
2865 1 : poKeys->GetDataType().GetNumericDataType()))
2866 : {
2867 0 : CPLError(CE_Failure, CPLE_AppDefined,
2868 : "Only integer data type supported for %s/keys",
2869 : osGeorefMetadataLayer.c_str());
2870 0 : delete poElevBand;
2871 0 : return false;
2872 : }
2873 : }
2874 :
2875 4 : auto poValues = poGeoref_metadataLayer->OpenMDArray("values");
2876 2 : if (poValues == nullptr)
2877 : {
2878 0 : CPLError(CE_Failure, CPLE_AppDefined,
2879 : "Cannot find array values of georef_metadata layer %s",
2880 : osGeorefMetadataLayer.c_str());
2881 0 : delete poElevBand;
2882 0 : return false;
2883 : }
2884 2 : if (poValues->GetDimensionCount() != 1)
2885 : {
2886 0 : CPLError(CE_Failure, CPLE_AppDefined,
2887 : "Wrong dimensions for %s/values",
2888 : osGeorefMetadataLayer.c_str());
2889 0 : delete poElevBand;
2890 0 : return false;
2891 : }
2892 2 : if (poValues->GetDataType().GetClass() != GEDTC_COMPOUND)
2893 : {
2894 0 : CPLError(CE_Failure, CPLE_AppDefined,
2895 : "Only compound data type supported for %s/values",
2896 : osGeorefMetadataLayer.c_str());
2897 0 : delete poElevBand;
2898 0 : return false;
2899 : }
2900 :
2901 2 : nRasterXSize = poElevBand->nRasterXSize;
2902 2 : nRasterYSize = poElevBand->nRasterYSize;
2903 2 : SetBand(1, new BAGGeorefMDBand(poValues, poKeys, poElevBand));
2904 : }
2905 : else
2906 : {
2907 26 : nRasterXSize = poElevBand->nRasterXSize;
2908 26 : nRasterYSize = poElevBand->nRasterYSize;
2909 :
2910 26 : SetBand(nNextBand++, poElevBand);
2911 :
2912 : // Try to do the same for the uncertainty band.
2913 : const hid_t hUncertainty =
2914 26 : H5Dopen(GetHDF5Handle(), "/BAG_root/uncertainty");
2915 26 : BAGRasterBand *poUBand = new BAGRasterBand(this, nNextBand);
2916 :
2917 52 : if (hUncertainty >= 0 &&
2918 26 : poUBand->Initialize(hUncertainty, "uncertainty"))
2919 : {
2920 26 : SetBand(nNextBand++, poUBand);
2921 : }
2922 : else
2923 : {
2924 0 : delete poUBand;
2925 : }
2926 :
2927 : // Load other root datasets (such as nominal_elevation)
2928 78 : auto poBAG_root = m_poRootGroup->OpenGroup("BAG_root", nullptr);
2929 26 : if (poBAG_root)
2930 : {
2931 52 : const auto arrayNames = poBAG_root->GetMDArrayNames(nullptr);
2932 153 : for (const auto &arrayName : arrayNames)
2933 : {
2934 127 : if (arrayName != "elevation" && arrayName != "uncertainty")
2935 : {
2936 150 : auto poArray = poBAG_root->OpenMDArray(arrayName, nullptr);
2937 150 : if (poArray && poArray->GetDimensions().size() == 2 &&
2938 18 : poArray->GetDimensions()[0]->GetSize() ==
2939 18 : static_cast<unsigned>(nRasterYSize) &&
2940 10 : poArray->GetDimensions()[1]->GetSize() ==
2941 160 : static_cast<unsigned>(nRasterXSize) &&
2942 10 : poArray->GetDataType().GetClass() == GEDTC_NUMERIC)
2943 : {
2944 2 : hid_t hBandId = GH5DopenNoWarning(
2945 : GetHDF5Handle(),
2946 4 : ("/BAG_root/" + arrayName).c_str());
2947 : BAGRasterBand *const poBand =
2948 2 : new BAGRasterBand(this, nNextBand);
2949 4 : if (hBandId >= 0 &&
2950 2 : poBand->Initialize(hBandId, arrayName.c_str()))
2951 : {
2952 2 : SetBand(nNextBand++, poBand);
2953 : }
2954 : else
2955 : {
2956 0 : delete poBand;
2957 : }
2958 : }
2959 : }
2960 : }
2961 : }
2962 : }
2963 :
2964 81 : SetDescription(poOpenInfo->pszFilename);
2965 :
2966 81 : m_bReportVertCRS = CPLTestBool(CSLFetchNameValueDef(
2967 81 : poOpenInfo->papszOpenOptions, "REPORT_VERTCRS", "YES"));
2968 :
2969 : // Load the XML metadata.
2970 81 : LoadMetadata();
2971 :
2972 81 : if (bResampledGrid)
2973 : {
2974 25 : m_bMask = CPLTestBool(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
2975 : "SUPERGRIDS_MASK", "NO"));
2976 : }
2977 :
2978 81 : if (!m_bMask)
2979 : {
2980 80 : GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
2981 : }
2982 :
2983 : // Load for refinements grids for variable resolution datasets
2984 81 : bool bHasRefinementGrids = false;
2985 81 : if (bOpenSuperGrid || bListSubDS || bResampledGrid || bInterpolate)
2986 : {
2987 : bHasRefinementGrids =
2988 53 : LookForRefinementGrids(poOpenInfo->papszOpenOptions, nY, nX);
2989 55 : if (!bOpenSuperGrid && m_aosSubdatasets.size() == 2 &&
2990 2 : EQUAL(pszMode, "AUTO"))
2991 : {
2992 : outOsSubDsName =
2993 2 : CSLFetchNameValueDef(m_aosSubdatasets, "SUBDATASET_1_NAME", "");
2994 2 : return true;
2995 : }
2996 : }
2997 : else
2998 : {
2999 28 : if (LookForRefinementGrids(poOpenInfo->papszOpenOptions, 0, 0))
3000 : {
3001 10 : GDALDataset::SetMetadataItem("HAS_SUPERGRIDS", "TRUE");
3002 : }
3003 28 : m_aosSubdatasets.Clear();
3004 : }
3005 :
3006 79 : if (!bIsSubdataset && osGeorefMetadataLayer.empty())
3007 : {
3008 65 : auto poGeoref_metadata = m_poRootGroup->OpenGroupFromFullname(
3009 195 : "/BAG_root/georef_metadata", nullptr);
3010 65 : if (poGeoref_metadata)
3011 : {
3012 4 : const auto groupNames = poGeoref_metadata->GetGroupNames(nullptr);
3013 2 : if (!groupNames.empty())
3014 : {
3015 2 : if (m_aosSubdatasets.empty())
3016 : {
3017 1 : const int nIdx = 1;
3018 : m_aosSubdatasets.AddNameValue(
3019 : CPLSPrintf("SUBDATASET_%d_NAME", nIdx),
3020 : CPLSPrintf("BAG:\"%s\":bathymetry_coverage",
3021 1 : GetDescription()));
3022 : m_aosSubdatasets.AddNameValue(
3023 : CPLSPrintf("SUBDATASET_%d_DESC", nIdx),
3024 1 : "Bathymetry gridded data");
3025 : }
3026 6 : for (const auto &groupName : groupNames)
3027 : {
3028 4 : const int nIdx = m_aosSubdatasets.size() / 2 + 1;
3029 : m_aosSubdatasets.AddNameValue(
3030 : CPLSPrintf("SUBDATASET_%d_NAME", nIdx),
3031 : CPLSPrintf("BAG:\"%s\":georef_metadata:%s",
3032 4 : GetDescription(), groupName.c_str()));
3033 : m_aosSubdatasets.AddNameValue(
3034 : CPLSPrintf("SUBDATASET_%d_DESC", nIdx),
3035 : CPLSPrintf("Georeferenced metadata %s",
3036 4 : groupName.c_str()));
3037 : }
3038 : }
3039 : }
3040 : }
3041 :
3042 79 : double dfMinResX = 0.0;
3043 79 : double dfMinResY = 0.0;
3044 79 : double dfMaxResX = 0.0;
3045 79 : double dfMaxResY = 0.0;
3046 79 : if (m_hVarresMetadata >= 0)
3047 : {
3048 61 : if (!GH5_FetchAttribute(m_hVarresMetadata, "min_resolution_x",
3049 122 : dfMinResX) ||
3050 61 : !GH5_FetchAttribute(m_hVarresMetadata, "min_resolution_y",
3051 : dfMinResY))
3052 : {
3053 0 : CPLError(CE_Failure, CPLE_AppDefined,
3054 : "Cannot get min_resolution_x and/or min_resolution_y");
3055 0 : return false;
3056 : }
3057 :
3058 61 : if (!GH5_FetchAttribute(m_hVarresMetadata, "max_resolution_x",
3059 122 : dfMaxResX) ||
3060 61 : !GH5_FetchAttribute(m_hVarresMetadata, "max_resolution_y",
3061 : dfMaxResY))
3062 : {
3063 0 : CPLError(CE_Failure, CPLE_AppDefined,
3064 : "Cannot get max_resolution_x and/or max_resolution_y");
3065 0 : return false;
3066 : }
3067 :
3068 61 : if (!bOpenSuperGrid && !bResampledGrid && !bInterpolate)
3069 : {
3070 24 : GDALDataset::SetMetadataItem("MIN_RESOLUTION_X",
3071 : CPLSPrintf("%f", dfMinResX));
3072 24 : GDALDataset::SetMetadataItem("MIN_RESOLUTION_Y",
3073 : CPLSPrintf("%f", dfMinResY));
3074 24 : GDALDataset::SetMetadataItem("MAX_RESOLUTION_X",
3075 : CPLSPrintf("%f", dfMaxResX));
3076 24 : GDALDataset::SetMetadataItem("MAX_RESOLUTION_Y",
3077 : CPLSPrintf("%f", dfMaxResY));
3078 : }
3079 : }
3080 :
3081 79 : if (bResampledGrid || bInterpolate)
3082 : {
3083 26 : if (!bHasRefinementGrids)
3084 : {
3085 0 : CPLError(CE_Failure, CPLE_AppDefined,
3086 : "No supergrids available. "
3087 : "%s mode not available",
3088 : pszMode);
3089 3 : return false;
3090 : }
3091 :
3092 52 : const char *pszValuePopStrategy = CSLFetchNameValueDef(
3093 26 : poOpenInfo->papszOpenOptions, "VALUE_POPULATION", "MAX");
3094 26 : if (EQUAL(pszValuePopStrategy, "MIN"))
3095 : {
3096 1 : m_ePopulation = BAGDataset::Population::MIN;
3097 : }
3098 25 : else if (EQUAL(pszValuePopStrategy, "MEAN"))
3099 : {
3100 1 : m_ePopulation = BAGDataset::Population::MEAN;
3101 : }
3102 24 : else if (EQUAL(pszValuePopStrategy, "MAX"))
3103 : {
3104 23 : m_ePopulation = BAGDataset::Population::MAX;
3105 : }
3106 : else
3107 : {
3108 1 : m_ePopulation = BAGDataset::Population::COUNT;
3109 1 : bHasNoData = false;
3110 1 : fNoDataValue = 0;
3111 : }
3112 :
3113 : const char *pszResX =
3114 26 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "RESX");
3115 : const char *pszResY =
3116 26 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "RESY");
3117 52 : const char *pszResStrategy = CSLFetchNameValueDef(
3118 26 : poOpenInfo->papszOpenOptions, "RES_STRATEGY", "AUTO");
3119 26 : double dfDefaultResX = 0.0;
3120 26 : double dfDefaultResY = 0.0;
3121 :
3122 : const char *pszResFilterMin =
3123 26 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "RES_FILTER_MIN");
3124 : const char *pszResFilterMax =
3125 26 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "RES_FILTER_MAX");
3126 :
3127 26 : double dfResFilterMin = 0;
3128 26 : if (pszResFilterMin != nullptr)
3129 : {
3130 6 : dfResFilterMin = CPLAtof(pszResFilterMin);
3131 6 : const double dfMaxRes = std::min(dfMaxResX, dfMaxResY);
3132 6 : if (dfResFilterMin >= dfMaxRes)
3133 : {
3134 1 : CPLError(CE_Failure, CPLE_AppDefined,
3135 : "Cannot specified RES_FILTER_MIN >= %g", dfMaxRes);
3136 1 : return false;
3137 : }
3138 5 : GDALDataset::SetMetadataItem("RES_FILTER_MIN",
3139 : CPLSPrintf("%g", dfResFilterMin));
3140 : }
3141 :
3142 25 : double dfResFilterMax = std::numeric_limits<double>::infinity();
3143 25 : if (pszResFilterMax != nullptr)
3144 : {
3145 6 : dfResFilterMax = CPLAtof(pszResFilterMax);
3146 6 : const double dfMinRes = std::min(dfMinResX, dfMinResY);
3147 6 : if (dfResFilterMax < dfMinRes)
3148 : {
3149 2 : CPLError(CE_Failure, CPLE_AppDefined,
3150 : "Cannot specified RES_FILTER_MAX < %g", dfMinRes);
3151 2 : return false;
3152 : }
3153 4 : GDALDataset::SetMetadataItem("RES_FILTER_MAX",
3154 : CPLSPrintf("%g", dfResFilterMax));
3155 : }
3156 :
3157 23 : if (dfResFilterMin >= dfResFilterMax)
3158 : {
3159 0 : CPLError(CE_Failure, CPLE_AppDefined,
3160 : "Cannot specified RES_FILTER_MIN >= RES_FILTER_MAX");
3161 0 : return false;
3162 : }
3163 :
3164 23 : if (EQUAL(pszResStrategy, "AUTO") &&
3165 12 : (pszResFilterMin || pszResFilterMax))
3166 : {
3167 5 : if (pszResFilterMax)
3168 : {
3169 4 : dfDefaultResX = dfResFilterMax;
3170 4 : dfDefaultResY = dfResFilterMax;
3171 : }
3172 : else
3173 : {
3174 1 : dfDefaultResX = dfMaxResX;
3175 1 : dfDefaultResY = dfMaxResY;
3176 : }
3177 : }
3178 18 : else if (EQUAL(pszResStrategy, "AUTO") || EQUAL(pszResStrategy, "MIN"))
3179 : {
3180 12 : dfDefaultResX = dfMinResX;
3181 12 : dfDefaultResY = dfMinResY;
3182 : }
3183 6 : else if (EQUAL(pszResStrategy, "MAX"))
3184 : {
3185 1 : dfDefaultResX = dfMaxResX;
3186 1 : dfDefaultResY = dfMaxResY;
3187 : }
3188 5 : else if (EQUAL(pszResStrategy, "MEAN"))
3189 : {
3190 5 : if (!GetMeanSupergridsResolution(dfDefaultResX, dfDefaultResY))
3191 : {
3192 0 : return false;
3193 : }
3194 : }
3195 :
3196 : const char *pszMinX =
3197 23 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINX");
3198 : const char *pszMinY =
3199 23 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINY");
3200 : const char *pszMaxX =
3201 23 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXX");
3202 : const char *pszMaxY =
3203 23 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXY");
3204 :
3205 23 : double dfMinX = m_dfLowResMinX;
3206 23 : double dfMinY = m_dfLowResMinY;
3207 23 : double dfMaxX = m_dfLowResMaxX;
3208 23 : double dfMaxY = m_dfLowResMaxY;
3209 23 : double dfResX = dfDefaultResX;
3210 23 : double dfResY = dfDefaultResY;
3211 23 : if (pszMinX)
3212 1 : dfMinX = CPLAtof(pszMinX);
3213 23 : if (pszMinY)
3214 1 : dfMinY = CPLAtof(pszMinY);
3215 23 : if (pszMaxX)
3216 1 : dfMaxX = CPLAtof(pszMaxX);
3217 23 : if (pszMaxY)
3218 1 : dfMaxY = CPLAtof(pszMaxY);
3219 23 : if (pszResX)
3220 1 : dfResX = CPLAtof(pszResX);
3221 23 : if (pszResY)
3222 1 : dfResY = CPLAtof(pszResY);
3223 :
3224 23 : if (dfResX <= 0.0 || dfResY <= 0.0)
3225 : {
3226 0 : CPLError(CE_Failure, CPLE_NotSupported,
3227 : "Invalid resolution: %f x %f", dfResX, dfResY);
3228 0 : return false;
3229 : }
3230 23 : const double dfRasterXSize = (dfMaxX - dfMinX) / dfResX;
3231 23 : const double dfRasterYSize = (dfMaxY - dfMinY) / dfResY;
3232 23 : if (dfRasterXSize <= 1 || dfRasterYSize <= 1 ||
3233 23 : dfRasterXSize > INT_MAX || dfRasterYSize > INT_MAX)
3234 : {
3235 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid raster dimension");
3236 0 : return false;
3237 : }
3238 23 : nRasterXSize = static_cast<int>(dfRasterXSize + 0.5);
3239 23 : nRasterYSize = static_cast<int>(dfRasterYSize + 0.5);
3240 23 : m_gt[0] = dfMinX;
3241 23 : m_gt[1] = dfResX;
3242 23 : m_gt[3] = dfMaxY;
3243 23 : m_gt[5] = -dfResY;
3244 23 : if (pszMaxY == nullptr || pszMinY != nullptr)
3245 : {
3246 : // if the constraint is not given by MAXY, we may need to tweak
3247 : // m_gt[3] / maxy, so that we get the requested MINY
3248 : // value
3249 22 : m_gt[3] += dfMinY - (dfMaxY - nRasterYSize * dfResY);
3250 : }
3251 :
3252 23 : const double dfMinRes = std::min(dfMinResX, dfMinResY);
3253 23 : if (dfResFilterMin > dfMinRes)
3254 : {
3255 3 : m_dfResFilterMin = dfResFilterMin;
3256 : }
3257 23 : m_dfResFilterMax = dfResFilterMax;
3258 :
3259 : // Use min/max BAG refinement metadata items only if the
3260 : // GDAL dataset bounding box is equal or larger to the BAG dataset
3261 23 : const bool bInitializeMinMax =
3262 22 : (!m_bMask && m_ePopulation != BAGDataset::Population::COUNT &&
3263 21 : dfMinX <= m_dfLowResMinX && dfMinY <= m_dfLowResMinY &&
3264 45 : dfMaxX >= m_dfLowResMaxX && dfMaxY >= m_dfLowResMaxY);
3265 :
3266 23 : if (bInterpolate)
3267 : {
3268 : // Depth
3269 1 : SetBand(1,
3270 : new BAGInterpolatedBand(this, 1, bHasNoData, fNoDataValue,
3271 1 : bInitializeMinMax));
3272 :
3273 : // Uncertainty
3274 1 : SetBand(2,
3275 : new BAGInterpolatedBand(this, 2, bHasNoData, fNoDataValue,
3276 1 : bInitializeMinMax));
3277 : }
3278 22 : else if (m_bMask || m_ePopulation == BAGDataset::Population::COUNT)
3279 : {
3280 2 : SetBand(1, new BAGResampledBand(this, 1, false, 0.0f, false));
3281 : }
3282 : else
3283 : {
3284 20 : SetBand(1, new BAGResampledBand(this, 1, bHasNoData, fNoDataValue,
3285 20 : bInitializeMinMax));
3286 :
3287 20 : SetBand(2, new BAGResampledBand(this, 2, bHasNoData, fNoDataValue,
3288 20 : bInitializeMinMax));
3289 : }
3290 :
3291 23 : if (GetRasterCount() > 1)
3292 : {
3293 21 : GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL",
3294 : "IMAGE_STRUCTURE");
3295 : }
3296 :
3297 42 : const bool bCanUseLowResAsOvr = nRasterXSize > m_nLowResWidth &&
3298 42 : nRasterYSize > m_nLowResHeight &&
3299 19 : GetRasterCount() > 1;
3300 : const int nMinOvrSize =
3301 23 : bCanUseLowResAsOvr ? 1 + std::min(m_nLowResWidth, m_nLowResHeight)
3302 23 : : 256;
3303 51 : for (int nOvrFactor = 2; nRasterXSize / nOvrFactor >= nMinOvrSize &&
3304 32 : nRasterYSize / nOvrFactor >= nMinOvrSize;
3305 28 : nOvrFactor *= 2)
3306 : {
3307 56 : auto poOvrDS = std::make_unique<BAGDataset>(this, nOvrFactor);
3308 :
3309 84 : for (int i = 1; i <= GetRasterCount(); i++)
3310 : {
3311 56 : if (bInterpolate)
3312 : {
3313 16 : poOvrDS->SetBand(
3314 8 : i, new BAGInterpolatedBand(poOvrDS.get(), i, bHasNoData,
3315 8 : fNoDataValue, false));
3316 : }
3317 : else
3318 : {
3319 96 : poOvrDS->SetBand(
3320 48 : i, new BAGResampledBand(poOvrDS.get(), i, bHasNoData,
3321 48 : fNoDataValue, false));
3322 : }
3323 : }
3324 :
3325 28 : m_apoOverviewDS.emplace_back(std::move(poOvrDS));
3326 : }
3327 :
3328 : // Use the low resolution grid as the last overview level
3329 23 : if (bCanUseLowResAsOvr)
3330 : {
3331 17 : auto poOvrDS = std::make_unique<BAGDataset>(this, m_nLowResWidth,
3332 17 : m_nLowResHeight);
3333 :
3334 17 : poElevBand = new BAGRasterBand(poOvrDS.get(), 1);
3335 : const hid_t hElevation =
3336 17 : H5Dopen(GetHDF5Handle(), "/BAG_root/elevation");
3337 34 : if (hElevation < 0 ||
3338 17 : !poElevBand->Initialize(hElevation, "elevation"))
3339 : {
3340 0 : delete poElevBand;
3341 0 : return false;
3342 : }
3343 17 : poOvrDS->SetBand(1, poElevBand);
3344 :
3345 : const hid_t hUncertainty =
3346 17 : H5Dopen(GetHDF5Handle(), "/BAG_root/uncertainty");
3347 17 : BAGRasterBand *poUBand = new BAGRasterBand(poOvrDS.get(), 2);
3348 34 : if (hUncertainty < 0 ||
3349 17 : !poUBand->Initialize(hUncertainty, "uncertainty"))
3350 : {
3351 0 : delete poUBand;
3352 0 : return false;
3353 : }
3354 17 : poOvrDS->SetBand(2, poUBand);
3355 :
3356 17 : m_apoOverviewDS.emplace_back(std::move(poOvrDS));
3357 23 : }
3358 : }
3359 53 : else if (bOpenSuperGrid)
3360 : {
3361 11 : auto oIter = m_oMapRefinemendGrids.find(nY * m_nLowResWidth + nX);
3362 11 : if (oIter == m_oMapRefinemendGrids.end())
3363 : {
3364 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid subdataset");
3365 2 : return false;
3366 : }
3367 :
3368 9 : m_aosSubdatasets.Clear();
3369 9 : const auto &pSuperGrid = oIter->second;
3370 9 : nRasterXSize = static_cast<int>(pSuperGrid.nWidth);
3371 9 : nRasterYSize = static_cast<int>(pSuperGrid.nHeight);
3372 :
3373 : // Convert from pixel-center convention to corner-pixel convention
3374 : const double dfMinX =
3375 9 : m_gt[0] + nX * m_gt[1] + pSuperGrid.fSWX - pSuperGrid.fResX / 2;
3376 9 : const double dfMinY = m_gt[3] + m_nLowResHeight * m_gt[5] +
3377 9 : nY * -m_gt[5] + pSuperGrid.fSWY -
3378 9 : pSuperGrid.fResY / 2;
3379 9 : const double dfMaxY =
3380 9 : dfMinY + pSuperGrid.nHeight * static_cast<double>(pSuperGrid.fResY);
3381 :
3382 9 : m_gt[0] = dfMinX;
3383 9 : m_gt[1] = pSuperGrid.fResX;
3384 9 : m_gt[3] = dfMaxY;
3385 9 : m_gt[5] = -pSuperGrid.fResY;
3386 9 : m_nSuperGridRefinementStartIndex = pSuperGrid.nIndex;
3387 :
3388 9 : if (!osGeorefMetadataLayer.empty())
3389 : {
3390 4 : auto poGeoref_metadataLayer = m_poRootGroup->OpenGroupFromFullname(
3391 4 : "/BAG_root/georef_metadata/" + osGeorefMetadataLayer, nullptr);
3392 4 : if (poGeoref_metadataLayer == nullptr)
3393 : {
3394 0 : CPLError(CE_Failure, CPLE_AppDefined,
3395 : "Cannot find georef_metadata layer %s",
3396 : osGeorefMetadataLayer.c_str());
3397 0 : return false;
3398 : }
3399 :
3400 8 : auto poKeys = poGeoref_metadataLayer->OpenMDArray("varres_keys");
3401 4 : if (poKeys != nullptr)
3402 : {
3403 2 : auto poDims = poKeys->GetDimensions();
3404 4 : if (poDims.size() != 2 || poDims[0]->GetSize() != 1 ||
3405 2 : poDims[1]->GetSize() != m_nRefinementsSize)
3406 : {
3407 0 : CPLError(CE_Failure, CPLE_AppDefined,
3408 : "Wrong dimensions for %s/varres_keys",
3409 : osGeorefMetadataLayer.c_str());
3410 0 : return false;
3411 : }
3412 4 : if (poKeys->GetDataType().GetClass() != GEDTC_NUMERIC ||
3413 2 : !GDALDataTypeIsInteger(
3414 2 : poKeys->GetDataType().GetNumericDataType()))
3415 : {
3416 0 : CPLError(
3417 : CE_Failure, CPLE_AppDefined,
3418 : "Only integer data type supported for %s/varres_keys",
3419 : osGeorefMetadataLayer.c_str());
3420 0 : return false;
3421 : }
3422 : }
3423 :
3424 8 : auto poValues = poGeoref_metadataLayer->OpenMDArray("values");
3425 4 : if (poValues == nullptr)
3426 : {
3427 0 : CPLError(CE_Failure, CPLE_AppDefined,
3428 : "Cannot find array values of georef_metadata layer %s",
3429 : osGeorefMetadataLayer.c_str());
3430 0 : return false;
3431 : }
3432 4 : if (poValues->GetDimensionCount() != 1)
3433 : {
3434 0 : CPLError(CE_Failure, CPLE_AppDefined,
3435 : "Wrong dimensions for %s/values",
3436 : osGeorefMetadataLayer.c_str());
3437 0 : return false;
3438 : }
3439 4 : if (poValues->GetDataType().GetClass() != GEDTC_COMPOUND)
3440 : {
3441 0 : CPLError(CE_Failure, CPLE_AppDefined,
3442 : "Only compound data type supported for %s/values",
3443 : osGeorefMetadataLayer.c_str());
3444 0 : return false;
3445 : }
3446 4 : SetBand(1, new BAGGeorefMDSuperGridBand(
3447 : poValues, poKeys,
3448 4 : new BAGSuperGridBand(this, 1, bHasNoData,
3449 4 : fNoDataValue)));
3450 : }
3451 : else
3452 : {
3453 15 : for (int i = 0; i < 2; i++)
3454 : {
3455 10 : SetBand(i + 1, new BAGSuperGridBand(this, i + 1, bHasNoData,
3456 10 : fNoDataValue));
3457 : }
3458 :
3459 5 : GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL",
3460 : "IMAGE_STRUCTURE");
3461 : }
3462 :
3463 9 : SetPhysicalFilename(osFilename);
3464 :
3465 9 : m_oMapRefinemendGrids.clear();
3466 : }
3467 :
3468 : // Setup/check for pam .aux.xml.
3469 74 : TryLoadXML();
3470 :
3471 : // Setup overviews.
3472 74 : oOvManager.Initialize(this, poOpenInfo->pszFilename);
3473 :
3474 74 : return true;
3475 : }
3476 :
3477 : /************************************************************************/
3478 : /* GetLayer() */
3479 : /************************************************************************/
3480 :
3481 47 : const OGRLayer *BAGDataset::GetLayer(int idx) const
3482 : {
3483 47 : if (idx != 0)
3484 1 : return nullptr;
3485 46 : return m_poTrackingListLayer.get();
3486 : }
3487 :
3488 : /************************************************************************/
3489 : /* BAGTrackingListLayer */
3490 : /************************************************************************/
3491 :
3492 : class BAGTrackingListLayer final
3493 : : public OGRLayer,
3494 : public OGRGetNextFeatureThroughRaw<BAGTrackingListLayer>
3495 : {
3496 : std::shared_ptr<GDALMDArray> m_poArray{};
3497 : OGRFeatureDefn *m_poFeatureDefn = nullptr;
3498 : int m_nIdx = 0;
3499 :
3500 : OGRFeature *GetNextRawFeature();
3501 :
3502 : CPL_DISALLOW_COPY_ASSIGN(BAGTrackingListLayer)
3503 :
3504 : public:
3505 : explicit BAGTrackingListLayer(const std::shared_ptr<GDALMDArray> &poArray);
3506 : ~BAGTrackingListLayer() override;
3507 :
3508 0 : const OGRFeatureDefn *GetLayerDefn() const override
3509 : {
3510 0 : return m_poFeatureDefn;
3511 : }
3512 :
3513 : void ResetReading() override;
3514 5 : DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(BAGTrackingListLayer)
3515 :
3516 0 : int TestCapability(const char *) const override
3517 : {
3518 0 : return false;
3519 : }
3520 : };
3521 :
3522 : /************************************************************************/
3523 : /* BAGTrackingListLayer() */
3524 : /************************************************************************/
3525 :
3526 45 : BAGTrackingListLayer::BAGTrackingListLayer(
3527 45 : const std::shared_ptr<GDALMDArray> &poArray)
3528 45 : : m_poArray(poArray)
3529 : {
3530 45 : m_poFeatureDefn = new OGRFeatureDefn("tracking_list");
3531 45 : SetDescription(m_poFeatureDefn->GetName());
3532 45 : m_poFeatureDefn->Reference();
3533 45 : m_poFeatureDefn->SetGeomType(wkbNone);
3534 :
3535 45 : const auto &poComponents = poArray->GetDataType().GetComponents();
3536 315 : for (const auto &poComponent : poComponents)
3537 : {
3538 270 : if (poComponent->GetType().GetClass() == GEDTC_NUMERIC)
3539 : {
3540 : OGRFieldType eType;
3541 270 : if (GDALDataTypeIsInteger(
3542 540 : poComponent->GetType().GetNumericDataType()))
3543 180 : eType = OFTInteger;
3544 : else
3545 90 : eType = OFTReal;
3546 540 : OGRFieldDefn oFieldDefn(poComponent->GetName().c_str(), eType);
3547 270 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
3548 : }
3549 : }
3550 45 : }
3551 :
3552 : /************************************************************************/
3553 : /* ~BAGTrackingListLayer() */
3554 : /************************************************************************/
3555 :
3556 90 : BAGTrackingListLayer::~BAGTrackingListLayer()
3557 : {
3558 45 : m_poFeatureDefn->Release();
3559 90 : }
3560 :
3561 : /************************************************************************/
3562 : /* ResetReading() */
3563 : /************************************************************************/
3564 :
3565 3 : void BAGTrackingListLayer::ResetReading()
3566 : {
3567 3 : m_nIdx = 0;
3568 3 : }
3569 :
3570 : /************************************************************************/
3571 : /* GetNextRawFeature() */
3572 : /************************************************************************/
3573 :
3574 5 : OGRFeature *BAGTrackingListLayer::GetNextRawFeature()
3575 : {
3576 5 : if (static_cast<GUInt64>(m_nIdx) >=
3577 5 : m_poArray->GetDimensions()[0]->GetSize())
3578 1 : return nullptr;
3579 :
3580 4 : const auto &oDataType = m_poArray->GetDataType();
3581 4 : std::vector<GByte> abyRow(oDataType.GetSize());
3582 :
3583 4 : const GUInt64 arrayStartIdx = static_cast<GUInt64>(m_nIdx);
3584 4 : const size_t count = 1;
3585 4 : const GInt64 arrayStep = 0;
3586 4 : const GPtrDiff_t bufferStride = 0;
3587 8 : m_poArray->Read(&arrayStartIdx, &count, &arrayStep, &bufferStride,
3588 4 : oDataType, &abyRow[0]);
3589 4 : int iCol = 0;
3590 4 : auto poFeature = new OGRFeature(m_poFeatureDefn);
3591 4 : poFeature->SetFID(m_nIdx);
3592 4 : m_nIdx++;
3593 :
3594 4 : const auto &poComponents = oDataType.GetComponents();
3595 28 : for (const auto &poComponent : poComponents)
3596 : {
3597 24 : if (poComponent->GetType().GetClass() == GEDTC_NUMERIC)
3598 : {
3599 24 : if (GDALDataTypeIsInteger(
3600 48 : poComponent->GetType().GetNumericDataType()))
3601 : {
3602 16 : int nValue = 0;
3603 16 : GDALCopyWords(&abyRow[poComponent->GetOffset()],
3604 16 : poComponent->GetType().GetNumericDataType(), 0,
3605 : &nValue, GDT_Int32, 0, 1);
3606 16 : poFeature->SetField(iCol, nValue);
3607 : }
3608 : else
3609 : {
3610 8 : double dfValue = 0;
3611 8 : GDALCopyWords(&abyRow[poComponent->GetOffset()],
3612 8 : poComponent->GetType().GetNumericDataType(), 0,
3613 : &dfValue, GDT_Float64, 0, 1);
3614 8 : poFeature->SetField(iCol, dfValue);
3615 : }
3616 24 : iCol++;
3617 : }
3618 : }
3619 :
3620 4 : return poFeature;
3621 : }
3622 :
3623 : /************************************************************************/
3624 : /* OpenVector() */
3625 : /************************************************************************/
3626 :
3627 46 : bool BAGDataset::OpenVector()
3628 : {
3629 : auto poTrackingList =
3630 138 : m_poRootGroup->OpenMDArrayFromFullname("/BAG_root/tracking_list");
3631 46 : if (!poTrackingList)
3632 1 : return false;
3633 45 : if (poTrackingList->GetDimensions().size() != 1)
3634 0 : return false;
3635 45 : if (poTrackingList->GetDataType().GetClass() != GEDTC_COMPOUND)
3636 0 : return false;
3637 :
3638 45 : m_poTrackingListLayer.reset(new BAGTrackingListLayer(poTrackingList));
3639 45 : return true;
3640 : }
3641 :
3642 : /************************************************************************/
3643 : /* OpenForCreate() */
3644 : /************************************************************************/
3645 :
3646 4 : GDALDataset *BAGDataset::OpenForCreate(GDALOpenInfo *poOpenInfo, int nXSizeIn,
3647 : int nYSizeIn, int nBandsIn,
3648 : CSLConstList papszCreationOptions)
3649 : {
3650 8 : CPLString osFilename(poOpenInfo->pszFilename);
3651 :
3652 : // Open the file as an HDF5 file.
3653 4 : hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
3654 4 : H5Pset_driver(fapl, HDF5GetFileDriver(), nullptr);
3655 4 : hid_t hHDF5 = H5Fopen(osFilename, H5F_ACC_RDWR, fapl);
3656 4 : H5Pclose(fapl);
3657 4 : if (hHDF5 < 0)
3658 0 : return nullptr;
3659 :
3660 8 : auto poSharedResources = GDAL::HDF5SharedResources::Create(osFilename);
3661 4 : poSharedResources->m_hHDF5 = hHDF5;
3662 :
3663 8 : auto poRootGroup = HDF5Dataset::OpenGroup(poSharedResources);
3664 4 : if (poRootGroup == nullptr)
3665 0 : return nullptr;
3666 :
3667 : // Create a corresponding dataset.
3668 4 : BAGDataset *const poDS = new BAGDataset();
3669 :
3670 4 : poDS->eAccess = poOpenInfo->eAccess;
3671 4 : poDS->m_poRootGroup = std::move(poRootGroup);
3672 4 : poDS->m_poSharedResources = std::move(poSharedResources);
3673 4 : poDS->m_aosCreationOptions = papszCreationOptions;
3674 :
3675 4 : poDS->nRasterXSize = nXSizeIn;
3676 4 : poDS->nRasterYSize = nYSizeIn;
3677 :
3678 4 : const int nBlockSize = std::min(
3679 8 : 4096,
3680 4 : atoi(CSLFetchNameValueDef(papszCreationOptions, "BLOCK_SIZE", "100")));
3681 4 : const int nBlockXSize = std::min(poDS->nRasterXSize, nBlockSize);
3682 4 : const int nBlockYSize = std::min(poDS->nRasterYSize, nBlockSize);
3683 :
3684 10 : for (int i = 0; i < nBandsIn; i++)
3685 : {
3686 6 : auto poBand = new BAGRasterBand(poDS, i + 1);
3687 6 : poBand->nBlockXSize = nBlockXSize;
3688 6 : poBand->nBlockYSize = nBlockYSize;
3689 6 : poBand->eDataType = GDT_Float32;
3690 6 : poBand->m_bHasNoData = true;
3691 6 : poBand->m_fNoDataValue = fDEFAULT_NODATA;
3692 6 : poBand->GDALRasterBand::SetDescription(i == 0 ? "elevation"
3693 : : "uncertainty");
3694 6 : poDS->SetBand(i + 1, poBand);
3695 : }
3696 :
3697 4 : poDS->SetDescription(poOpenInfo->pszFilename);
3698 :
3699 4 : poDS->m_bReportVertCRS = CPLTestBool(CSLFetchNameValueDef(
3700 4 : poOpenInfo->papszOpenOptions, "REPORT_VERTCRS", "YES"));
3701 :
3702 4 : poDS->GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
3703 :
3704 : // Setup/check for pam .aux.xml.
3705 4 : poDS->TryLoadXML();
3706 :
3707 : // Setup overviews.
3708 4 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
3709 :
3710 4 : return poDS;
3711 : }
3712 :
3713 : /************************************************************************/
3714 : /* GetMeanSupergridsResolution() */
3715 : /************************************************************************/
3716 :
3717 5 : bool BAGDataset::GetMeanSupergridsResolution(double &dfResX, double &dfResY)
3718 : {
3719 5 : const int nChunkXSize = m_nChunkXSizeVarresMD;
3720 5 : const int nChunkYSize = m_nChunkYSizeVarresMD;
3721 :
3722 5 : dfResX = 0.0;
3723 5 : dfResY = 0.0;
3724 5 : int nValidSuperGrids = 0;
3725 5 : std::vector<BAGRefinementGrid> rgrids(static_cast<size_t>(nChunkXSize) *
3726 10 : nChunkYSize);
3727 5 : const int county = DIV_ROUND_UP(m_nLowResHeight, nChunkYSize);
3728 5 : const int countx = DIV_ROUND_UP(m_nLowResWidth, nChunkXSize);
3729 10 : for (int y = 0; y < county; y++)
3730 : {
3731 : const int nReqCountY =
3732 5 : std::min(nChunkYSize, m_nLowResHeight - y * nChunkYSize);
3733 10 : for (int x = 0; x < countx; x++)
3734 : {
3735 : const int nReqCountX =
3736 5 : std::min(nChunkXSize, m_nLowResWidth - x * nChunkXSize);
3737 :
3738 : // Create memory space to receive the data.
3739 5 : hsize_t count[2] = {static_cast<hsize_t>(nReqCountY),
3740 5 : static_cast<hsize_t>(nReqCountX)};
3741 5 : const hid_t memspace = H5Screate_simple(2, count, nullptr);
3742 5 : H5OFFSET_TYPE mem_offset[2] = {static_cast<H5OFFSET_TYPE>(0),
3743 : static_cast<H5OFFSET_TYPE>(0)};
3744 5 : if (H5Sselect_hyperslab(memspace, H5S_SELECT_SET, mem_offset,
3745 5 : nullptr, count, nullptr) < 0)
3746 : {
3747 0 : H5Sclose(memspace);
3748 0 : return false;
3749 : }
3750 :
3751 5 : if (ReadVarresMetadataValue(y * nChunkYSize, x * nChunkXSize,
3752 : memspace, rgrids.data(), nReqCountY,
3753 : nReqCountX))
3754 : {
3755 125 : for (int i = 0; i < nReqCountX * nReqCountY; i++)
3756 : {
3757 120 : if (rgrids[i].nWidth > 0)
3758 : {
3759 120 : dfResX += rgrids[i].fResX;
3760 120 : dfResY += rgrids[i].fResY;
3761 120 : nValidSuperGrids++;
3762 : }
3763 : }
3764 : }
3765 5 : H5Sclose(memspace);
3766 : }
3767 : }
3768 :
3769 5 : if (nValidSuperGrids == 0)
3770 : {
3771 0 : CPLError(CE_Failure, CPLE_AppDefined, "No valid supergrids");
3772 0 : return false;
3773 : }
3774 :
3775 5 : dfResX /= nValidSuperGrids;
3776 5 : dfResY /= nValidSuperGrids;
3777 5 : return true;
3778 : }
3779 :
3780 : /************************************************************************/
3781 : /* GetVarresMetadataChunkSizes() */
3782 : /************************************************************************/
3783 :
3784 63 : void BAGDataset::GetVarresMetadataChunkSizes(int &nChunkXSize, int &nChunkYSize)
3785 : {
3786 63 : const hid_t listid = H5Dget_create_plist(m_hVarresMetadata);
3787 63 : nChunkXSize = m_nLowResWidth;
3788 63 : nChunkYSize = std::max(
3789 63 : 1, std::min(10 * 1024 * 1024 / m_nLowResWidth, m_nLowResHeight));
3790 63 : if (listid > 0)
3791 : {
3792 63 : if (H5Pget_layout(listid) == H5D_CHUNKED)
3793 : {
3794 0 : hsize_t panChunkDims[2] = {0, 0};
3795 0 : const int nDimSize = H5Pget_chunk(listid, 2, panChunkDims);
3796 0 : CPL_IGNORE_RET_VAL(nDimSize);
3797 0 : CPLAssert(nDimSize == 2);
3798 0 : nChunkXSize = static_cast<int>(panChunkDims[1]);
3799 0 : nChunkYSize = static_cast<int>(panChunkDims[0]);
3800 : }
3801 :
3802 63 : H5Pclose(listid);
3803 : }
3804 63 : }
3805 :
3806 : /************************************************************************/
3807 : /* GetVarresRefinementChunkSize() */
3808 : /************************************************************************/
3809 :
3810 63 : void BAGDataset::GetVarresRefinementChunkSize(unsigned &nChunkSize)
3811 : {
3812 63 : const hid_t listid = H5Dget_create_plist(m_hVarresRefinements);
3813 63 : nChunkSize = 1024;
3814 63 : if (listid > 0)
3815 : {
3816 63 : if (H5Pget_layout(listid) == H5D_CHUNKED)
3817 : {
3818 53 : hsize_t panChunkDims[2] = {0, 0};
3819 53 : const int nDimSize = H5Pget_chunk(listid, 2, panChunkDims);
3820 53 : CPL_IGNORE_RET_VAL(nDimSize);
3821 53 : CPLAssert(nDimSize == 2);
3822 53 : nChunkSize = static_cast<unsigned>(panChunkDims[1]);
3823 : }
3824 :
3825 63 : H5Pclose(listid);
3826 : }
3827 63 : }
3828 :
3829 : /************************************************************************/
3830 : /* GetRefinementValues() */
3831 : /************************************************************************/
3832 :
3833 107034 : const float *BAGDataset::GetRefinementValues(unsigned nRefinementIndex)
3834 : {
3835 107034 : unsigned nStartIndex = (nRefinementIndex / m_nChunkSizeVarresRefinement) *
3836 107034 : m_nChunkSizeVarresRefinement;
3837 107034 : const auto vPtr = m_oCacheRefinementValues.getPtr(nStartIndex);
3838 107034 : if (vPtr)
3839 107013 : return vPtr->data() + 2 * (nRefinementIndex - nStartIndex);
3840 :
3841 : const unsigned nCachedRefinementCount = std::min(
3842 21 : m_nChunkSizeVarresRefinement, m_nRefinementsSize - nStartIndex);
3843 42 : std::vector<float> values(2 * nCachedRefinementCount);
3844 :
3845 21 : hsize_t countVarresRefinements[2] = {
3846 21 : static_cast<hsize_t>(1), static_cast<hsize_t>(nCachedRefinementCount)};
3847 : const hid_t memspaceVarresRefinements =
3848 21 : H5Screate_simple(2, countVarresRefinements, nullptr);
3849 21 : H5OFFSET_TYPE mem_offset[2] = {static_cast<H5OFFSET_TYPE>(0),
3850 : static_cast<H5OFFSET_TYPE>(0)};
3851 21 : if (H5Sselect_hyperslab(memspaceVarresRefinements, H5S_SELECT_SET,
3852 : mem_offset, nullptr, countVarresRefinements,
3853 21 : nullptr) < 0)
3854 : {
3855 0 : H5Sclose(memspaceVarresRefinements);
3856 0 : return nullptr;
3857 : }
3858 :
3859 21 : H5OFFSET_TYPE offsetRefinement[2] = {
3860 21 : static_cast<H5OFFSET_TYPE>(0), static_cast<H5OFFSET_TYPE>(nStartIndex)};
3861 21 : if (H5Sselect_hyperslab(m_hVarresRefinementsDataspace, H5S_SELECT_SET,
3862 : offsetRefinement, nullptr, countVarresRefinements,
3863 21 : nullptr) < 0)
3864 : {
3865 0 : H5Sclose(memspaceVarresRefinements);
3866 0 : return nullptr;
3867 : }
3868 21 : if (H5Dread(m_hVarresRefinements, m_hVarresRefinementsNative,
3869 : memspaceVarresRefinements, m_hVarresRefinementsDataspace,
3870 42 : H5P_DEFAULT, values.data()) < 0)
3871 : {
3872 0 : H5Sclose(memspaceVarresRefinements);
3873 0 : return nullptr;
3874 : }
3875 21 : H5Sclose(memspaceVarresRefinements);
3876 : const auto &vRef =
3877 21 : m_oCacheRefinementValues.insert(nStartIndex, std::move(values));
3878 21 : return vRef.data() + 2 * (nRefinementIndex - nStartIndex);
3879 : }
3880 :
3881 : /************************************************************************/
3882 : /* ReadVarresMetadataValue() */
3883 : /************************************************************************/
3884 :
3885 336 : bool BAGDataset::ReadVarresMetadataValue(int y, int x, hid_t memspace,
3886 : BAGRefinementGrid *rgrid, int height,
3887 : int width)
3888 : {
3889 336 : constexpr int metadata_elt_size = 3 * 4 + 4 * 4; // 3 uint and 4 float
3890 336 : std::vector<char> buffer(static_cast<size_t>(metadata_elt_size) * height *
3891 672 : width);
3892 :
3893 336 : hsize_t count[2] = {static_cast<hsize_t>(height),
3894 336 : static_cast<hsize_t>(width)};
3895 336 : H5OFFSET_TYPE offset[2] = {static_cast<H5OFFSET_TYPE>(y),
3896 336 : static_cast<H5OFFSET_TYPE>(x)};
3897 336 : if (H5Sselect_hyperslab(m_hVarresMetadataDataspace, H5S_SELECT_SET, offset,
3898 336 : nullptr, count, nullptr) < 0)
3899 : {
3900 0 : CPLError(CE_Failure, CPLE_AppDefined,
3901 : "ReadVarresMetadataValue(): H5Sselect_hyperslab() failed");
3902 0 : return false;
3903 : }
3904 :
3905 336 : if (H5Dread(m_hVarresMetadata, m_hVarresMetadataNative, memspace,
3906 672 : m_hVarresMetadataDataspace, H5P_DEFAULT, buffer.data()) < 0)
3907 : {
3908 0 : CPLError(CE_Failure, CPLE_AppDefined,
3909 : "ReadVarresMetadataValue(): H5Dread() failed");
3910 0 : return false;
3911 : }
3912 :
3913 2319 : for (int i = 0; i < width * height; i++)
3914 : {
3915 1983 : const char *src_ptr = buffer.data() + metadata_elt_size * i;
3916 1983 : memcpy(&rgrid[i].nIndex, src_ptr, 4);
3917 1983 : memcpy(&rgrid[i].nWidth, src_ptr + 4, 4);
3918 1983 : memcpy(&rgrid[i].nHeight, src_ptr + 8, 4);
3919 1983 : memcpy(&rgrid[i].fResX, src_ptr + 12, 4);
3920 1983 : memcpy(&rgrid[i].fResY, src_ptr + 16, 4);
3921 1983 : memcpy(&rgrid[i].fSWX, src_ptr + 20, 4);
3922 1983 : memcpy(&rgrid[i].fSWY, src_ptr + 24, 4);
3923 : }
3924 336 : return true;
3925 : }
3926 :
3927 : /************************************************************************/
3928 : /* LookForRefinementGrids() */
3929 : /************************************************************************/
3930 :
3931 81 : bool BAGDataset::LookForRefinementGrids(CSLConstList l_papszOpenOptions,
3932 : int nYSubDS, int nXSubDS)
3933 :
3934 : {
3935 81 : m_hVarresMetadata = GH5DopenNoWarning(m_poSharedResources->m_hHDF5,
3936 : "/BAG_root/varres_metadata");
3937 81 : if (m_hVarresMetadata < 0)
3938 18 : return false;
3939 63 : m_hVarresRefinements =
3940 63 : H5Dopen(m_poSharedResources->m_hHDF5, "/BAG_root/varres_refinements");
3941 63 : if (m_hVarresRefinements < 0)
3942 0 : return false;
3943 :
3944 63 : m_hVarresMetadataDataType = H5Dget_type(m_hVarresMetadata);
3945 63 : if (H5Tget_class(m_hVarresMetadataDataType) != H5T_COMPOUND)
3946 : {
3947 0 : CPLError(CE_Failure, CPLE_NotSupported,
3948 : "m_hVarresMetadataDataType is not compound");
3949 0 : return false;
3950 : }
3951 :
3952 : const struct
3953 : {
3954 : const char *pszName;
3955 : hid_t eType;
3956 63 : } asMetadataFields[] = {
3957 63 : {"index", H5T_NATIVE_UINT}, {"dimensions_x", H5T_NATIVE_UINT},
3958 126 : {"dimensions_y", H5T_NATIVE_UINT}, {"resolution_x", H5T_NATIVE_FLOAT},
3959 126 : {"resolution_y", H5T_NATIVE_FLOAT}, {"sw_corner_x", H5T_NATIVE_FLOAT},
3960 63 : {"sw_corner_y", H5T_NATIVE_FLOAT},
3961 63 : };
3962 :
3963 63 : if (H5Tget_nmembers(m_hVarresMetadataDataType) !=
3964 : CPL_ARRAYSIZE(asMetadataFields))
3965 : {
3966 0 : CPLError(CE_Failure, CPLE_NotSupported,
3967 : "m_hVarresMetadataDataType has not %u members",
3968 : static_cast<unsigned>(CPL_ARRAYSIZE(asMetadataFields)));
3969 0 : return false;
3970 : }
3971 :
3972 504 : for (unsigned i = 0; i < CPL_ARRAYSIZE(asMetadataFields); i++)
3973 : {
3974 441 : char *pszName = H5Tget_member_name(m_hVarresMetadataDataType, i);
3975 441 : if (strcmp(pszName, asMetadataFields[i].pszName) != 0)
3976 : {
3977 0 : CPLError(CE_Failure, CPLE_NotSupported,
3978 : "asMetadataFields[%u].pszName = %s instead of %s",
3979 : static_cast<unsigned>(i), pszName,
3980 0 : asMetadataFields[i].pszName);
3981 0 : H5free_memory(pszName);
3982 0 : return false;
3983 : }
3984 441 : H5free_memory(pszName);
3985 441 : const hid_t type = H5Tget_member_type(m_hVarresMetadataDataType, i);
3986 441 : const hid_t hNativeType = H5Tget_native_type(type, H5T_DIR_DEFAULT);
3987 441 : bool bTypeOK = H5Tequal(asMetadataFields[i].eType, hNativeType) > 0;
3988 441 : H5Tclose(hNativeType);
3989 441 : H5Tclose(type);
3990 441 : if (!bTypeOK)
3991 : {
3992 0 : CPLError(CE_Failure, CPLE_NotSupported,
3993 : "asMetadataFields[%u].eType is not of expected type", i);
3994 0 : return false;
3995 : }
3996 : }
3997 :
3998 63 : m_hVarresMetadataDataspace = H5Dget_space(m_hVarresMetadata);
3999 63 : if (H5Sget_simple_extent_ndims(m_hVarresMetadataDataspace) != 2)
4000 : {
4001 0 : CPLDebug("BAG",
4002 : "H5Sget_simple_extent_ndims(m_hVarresMetadataDataspace) != 2");
4003 0 : return false;
4004 : }
4005 :
4006 : {
4007 63 : hsize_t dims[2] = {static_cast<hsize_t>(0), static_cast<hsize_t>(0)};
4008 63 : hsize_t maxdims[2] = {static_cast<hsize_t>(0), static_cast<hsize_t>(0)};
4009 :
4010 63 : H5Sget_simple_extent_dims(m_hVarresMetadataDataspace, dims, maxdims);
4011 63 : if (dims[0] != static_cast<hsize_t>(m_nLowResHeight) ||
4012 63 : dims[1] != static_cast<hsize_t>(m_nLowResWidth))
4013 : {
4014 0 : CPLDebug("BAG", "Unexpected dimension for m_hVarresMetadata");
4015 0 : return false;
4016 : }
4017 : }
4018 :
4019 : // We could potentially go beyond but we'd need to make sure that
4020 : // m_oMapRefinemendGrids is indexed by a int64_t
4021 63 : if (m_nLowResWidth > std::numeric_limits<int>::max() / m_nLowResHeight)
4022 : {
4023 0 : CPLError(CE_Failure, CPLE_NotSupported, "Too many refinement grids");
4024 0 : return false;
4025 : }
4026 :
4027 63 : m_hVarresMetadataNative =
4028 63 : H5Tget_native_type(m_hVarresMetadataDataType, H5T_DIR_ASCEND);
4029 :
4030 63 : m_hVarresRefinementsDataType = H5Dget_type(m_hVarresRefinements);
4031 63 : if (H5Tget_class(m_hVarresRefinementsDataType) != H5T_COMPOUND)
4032 : {
4033 0 : CPLError(CE_Failure, CPLE_NotSupported,
4034 : "m_hVarresRefinementsDataType is not compound");
4035 0 : return false;
4036 : }
4037 :
4038 : const struct
4039 : {
4040 : const char *pszName;
4041 : hid_t eType;
4042 63 : } asRefinementsFields[] = {
4043 63 : {"depth", H5T_NATIVE_FLOAT},
4044 63 : {"depth_uncrt", H5T_NATIVE_FLOAT},
4045 63 : };
4046 :
4047 63 : if (H5Tget_nmembers(m_hVarresRefinementsDataType) !=
4048 : CPL_ARRAYSIZE(asRefinementsFields))
4049 : {
4050 0 : CPLError(CE_Failure, CPLE_NotSupported,
4051 : "m_hVarresRefinementsDataType has not %u members",
4052 : static_cast<unsigned>(CPL_ARRAYSIZE(asRefinementsFields)));
4053 0 : return false;
4054 : }
4055 :
4056 189 : for (unsigned i = 0; i < CPL_ARRAYSIZE(asRefinementsFields); i++)
4057 : {
4058 126 : char *pszName = H5Tget_member_name(m_hVarresRefinementsDataType, i);
4059 126 : if (strcmp(pszName, asRefinementsFields[i].pszName) != 0)
4060 : {
4061 0 : CPLError(CE_Failure, CPLE_NotSupported,
4062 : "asRefinementsFields[%u].pszName = %s instead of %s",
4063 : static_cast<unsigned>(i), pszName,
4064 0 : asRefinementsFields[i].pszName);
4065 0 : H5free_memory(pszName);
4066 0 : return false;
4067 : }
4068 126 : H5free_memory(pszName);
4069 126 : const hid_t type = H5Tget_member_type(m_hVarresRefinementsDataType, i);
4070 126 : const hid_t hNativeType = H5Tget_native_type(type, H5T_DIR_DEFAULT);
4071 126 : bool bTypeOK = H5Tequal(asRefinementsFields[i].eType, hNativeType) > 0;
4072 126 : H5Tclose(hNativeType);
4073 126 : H5Tclose(type);
4074 126 : if (!bTypeOK)
4075 : {
4076 0 : CPLError(CE_Failure, CPLE_NotSupported,
4077 : "asRefinementsFields[%u].eType is not of expected type",
4078 : i);
4079 0 : return false;
4080 : }
4081 : }
4082 :
4083 63 : m_hVarresRefinementsDataspace = H5Dget_space(m_hVarresRefinements);
4084 63 : if (H5Sget_simple_extent_ndims(m_hVarresRefinementsDataspace) != 2)
4085 : {
4086 0 : CPLDebug(
4087 : "BAG",
4088 : "H5Sget_simple_extent_ndims(m_hVarresRefinementsDataspace) != 2");
4089 0 : return false;
4090 : }
4091 :
4092 63 : m_hVarresRefinementsNative =
4093 63 : H5Tget_native_type(m_hVarresRefinementsDataType, H5T_DIR_ASCEND);
4094 :
4095 : hsize_t nRefinementsSize;
4096 : {
4097 63 : hsize_t dims[2] = {static_cast<hsize_t>(0), static_cast<hsize_t>(0)};
4098 63 : hsize_t maxdims[2] = {static_cast<hsize_t>(0), static_cast<hsize_t>(0)};
4099 :
4100 63 : H5Sget_simple_extent_dims(m_hVarresRefinementsDataspace, dims, maxdims);
4101 63 : if (dims[0] != 1)
4102 : {
4103 0 : CPLDebug("BAG", "Unexpected dimension for m_hVarresRefinements");
4104 0 : return false;
4105 : }
4106 63 : nRefinementsSize = dims[1];
4107 63 : m_nRefinementsSize = static_cast<unsigned>(nRefinementsSize);
4108 63 : CPLDebug("BAG", "m_nRefinementsSize = %u", m_nRefinementsSize);
4109 : }
4110 :
4111 63 : GetVarresMetadataChunkSizes(m_nChunkXSizeVarresMD, m_nChunkYSizeVarresMD);
4112 63 : CPLDebug("BAG", "m_nChunkXSizeVarresMD = %d, m_nChunkYSizeVarresMD = %d",
4113 : m_nChunkXSizeVarresMD, m_nChunkYSizeVarresMD);
4114 63 : GetVarresRefinementChunkSize(m_nChunkSizeVarresRefinement);
4115 63 : CPLDebug("BAG", "m_nChunkSizeVarresRefinement = %u",
4116 : m_nChunkSizeVarresRefinement);
4117 :
4118 63 : const char *pszMode = CSLFetchNameValueDef(l_papszOpenOptions, "MODE", "");
4119 63 : if (EQUAL(pszMode, "RESAMPLED_GRID") || EQUAL(pszMode, "INTERPOLATED"))
4120 : {
4121 26 : return true;
4122 : }
4123 :
4124 : const char *pszSUPERGRIDS =
4125 37 : CSLFetchNameValue(l_papszOpenOptions, "SUPERGRIDS_INDICES");
4126 :
4127 : struct yx
4128 : {
4129 : int y;
4130 : int x;
4131 :
4132 14 : yx(int yin, int xin) : y(yin), x(xin)
4133 : {
4134 14 : }
4135 :
4136 30 : bool operator<(const yx &other) const
4137 : {
4138 30 : return y < other.y || (y == other.y && x < other.x);
4139 : }
4140 : };
4141 :
4142 74 : std::set<yx> oSupergrids;
4143 37 : int nMinX = 0;
4144 37 : int nMinY = 0;
4145 37 : int nMaxX = m_nLowResWidth - 1;
4146 37 : int nMaxY = m_nLowResHeight - 1;
4147 37 : if (nYSubDS >= 0 && nXSubDS >= 0)
4148 : {
4149 21 : nMinX = nXSubDS;
4150 21 : nMaxX = nXSubDS;
4151 21 : nMinY = nYSubDS;
4152 21 : nMaxY = nYSubDS;
4153 : }
4154 16 : else if (pszSUPERGRIDS)
4155 : {
4156 11 : char chExpectedChar = '(';
4157 11 : bool bNextIsY = false;
4158 11 : bool bNextIsX = false;
4159 11 : bool bHasY = false;
4160 11 : bool bHasX = false;
4161 11 : int nY = 0;
4162 11 : int i = 0;
4163 46 : for (; pszSUPERGRIDS[i]; i++)
4164 : {
4165 39 : if (chExpectedChar && pszSUPERGRIDS[i] != chExpectedChar)
4166 : {
4167 1 : CPLError(
4168 : CE_Warning, CPLE_AppDefined,
4169 : "Invalid formatting for SUPERGRIDS_INDICES at index %d. "
4170 : "Expecting %c, got %c",
4171 1 : i, chExpectedChar, pszSUPERGRIDS[i]);
4172 1 : break;
4173 : }
4174 38 : else if (chExpectedChar == '(')
4175 : {
4176 10 : chExpectedChar = 0;
4177 10 : bNextIsY = true;
4178 : }
4179 28 : else if (chExpectedChar == ',')
4180 : {
4181 2 : chExpectedChar = '(';
4182 : }
4183 : else
4184 : {
4185 26 : CPLAssert(chExpectedChar == 0);
4186 26 : if (bNextIsY && pszSUPERGRIDS[i] >= '0' &&
4187 9 : pszSUPERGRIDS[i] <= '9')
4188 : {
4189 8 : nY = atoi(pszSUPERGRIDS + i);
4190 8 : bNextIsY = false;
4191 8 : bHasY = true;
4192 : }
4193 18 : else if (bNextIsX && pszSUPERGRIDS[i] >= '0' &&
4194 5 : pszSUPERGRIDS[i] <= '9')
4195 : {
4196 4 : int nX = atoi(pszSUPERGRIDS + i);
4197 4 : bNextIsX = false;
4198 4 : oSupergrids.insert(yx(nY, nX));
4199 4 : bHasX = true;
4200 : }
4201 14 : else if ((bHasX || bHasY) && pszSUPERGRIDS[i] >= '0' &&
4202 1 : pszSUPERGRIDS[i] <= '9')
4203 : {
4204 : // ok
4205 : }
4206 14 : else if (bHasY && pszSUPERGRIDS[i] == ',')
4207 : {
4208 7 : bNextIsX = true;
4209 : }
4210 7 : else if (bHasX && bHasY && pszSUPERGRIDS[i] == ')')
4211 : {
4212 4 : chExpectedChar = ',';
4213 4 : bHasX = false;
4214 4 : bHasY = false;
4215 : }
4216 : else
4217 : {
4218 3 : CPLError(CE_Warning, CPLE_AppDefined,
4219 : "Invalid formatting for SUPERGRIDS_INDICES at "
4220 : "index %d. "
4221 : "Got %c",
4222 3 : i, pszSUPERGRIDS[i]);
4223 3 : break;
4224 : }
4225 : }
4226 : }
4227 11 : if (pszSUPERGRIDS[i] == 0 && chExpectedChar != ',')
4228 : {
4229 5 : CPLError(CE_Warning, CPLE_AppDefined,
4230 : "Invalid formatting for SUPERGRIDS_INDICES at index %d.",
4231 : i);
4232 : }
4233 :
4234 11 : bool bFirst = true;
4235 15 : for (const auto &yxPair : oSupergrids)
4236 : {
4237 4 : if (bFirst)
4238 : {
4239 3 : nMinX = yxPair.x;
4240 3 : nMaxX = yxPair.x;
4241 3 : nMinY = yxPair.y;
4242 3 : nMaxY = yxPair.y;
4243 3 : bFirst = false;
4244 : }
4245 : else
4246 : {
4247 1 : nMinX = std::min(nMinX, yxPair.x);
4248 1 : nMaxX = std::max(nMaxX, yxPair.x);
4249 1 : nMinY = std::min(nMinY, yxPair.y);
4250 1 : nMaxY = std::max(nMaxY, yxPair.y);
4251 : }
4252 : }
4253 : }
4254 37 : const char *pszMinX = CSLFetchNameValue(l_papszOpenOptions, "MINX");
4255 37 : const char *pszMinY = CSLFetchNameValue(l_papszOpenOptions, "MINY");
4256 37 : const char *pszMaxX = CSLFetchNameValue(l_papszOpenOptions, "MAXX");
4257 37 : const char *pszMaxY = CSLFetchNameValue(l_papszOpenOptions, "MAXY");
4258 37 : const int nCountBBoxElts = (pszMinX ? 1 : 0) + (pszMinY ? 1 : 0) +
4259 37 : (pszMaxX ? 1 : 0) + (pszMaxY ? 1 : 0);
4260 37 : const bool bHasBoundingBoxFilter =
4261 37 : !(nYSubDS >= 0 && nXSubDS >= 0) && (nCountBBoxElts == 4);
4262 37 : double dfFilterMinX = 0.0;
4263 37 : double dfFilterMinY = 0.0;
4264 37 : double dfFilterMaxX = 0.0;
4265 37 : double dfFilterMaxY = 0.0;
4266 37 : if (nYSubDS >= 0 && nXSubDS >= 0)
4267 : {
4268 : // do nothing
4269 : }
4270 16 : else if (bHasBoundingBoxFilter)
4271 : {
4272 1 : dfFilterMinX = CPLAtof(pszMinX);
4273 1 : dfFilterMinY = CPLAtof(pszMinY);
4274 1 : dfFilterMaxX = CPLAtof(pszMaxX);
4275 1 : dfFilterMaxY = CPLAtof(pszMaxY);
4276 :
4277 1 : nMinX = std::max(nMinX,
4278 1 : static_cast<int>((dfFilterMinX - m_gt[0]) / m_gt[1]));
4279 1 : nMaxX = std::min(nMaxX,
4280 1 : static_cast<int>((dfFilterMaxX - m_gt[0]) / m_gt[1]));
4281 :
4282 1 : nMinY = std::max(
4283 2 : nMinY, static_cast<int>(
4284 1 : (dfFilterMinY - (m_gt[3] + m_nLowResHeight * m_gt[5])) /
4285 1 : -m_gt[5]));
4286 1 : nMaxY = std::min(
4287 1 : nMaxY, static_cast<int>(
4288 1 : (dfFilterMaxY - (m_gt[3] + m_nLowResHeight * m_gt[5])) /
4289 1 : -m_gt[5]));
4290 : }
4291 15 : else if (nCountBBoxElts > 0)
4292 : {
4293 1 : CPLError(CE_Warning, CPLE_AppDefined,
4294 : "Bounding box filter ignored since only part of "
4295 : "MINX, MINY, MAXX and MAXY has been specified");
4296 : }
4297 :
4298 37 : const double dfResFilterMin = CPLAtof(
4299 : CSLFetchNameValueDef(l_papszOpenOptions, "RES_FILTER_MIN", "0"));
4300 37 : const double dfResFilterMax = CPLAtof(
4301 : CSLFetchNameValueDef(l_papszOpenOptions, "RES_FILTER_MAX", "inf"));
4302 :
4303 74 : std::vector<std::string> georefMDLayerNames;
4304 37 : auto poGeoref_metadata = m_poRootGroup->OpenGroupFromFullname(
4305 111 : "/BAG_root/georef_metadata", nullptr);
4306 37 : if (poGeoref_metadata)
4307 : {
4308 18 : const auto groupNames = poGeoref_metadata->GetGroupNames(nullptr);
4309 27 : for (const auto &groupName : groupNames)
4310 : {
4311 18 : georefMDLayerNames.push_back(groupName);
4312 : }
4313 : }
4314 :
4315 : const int nMaxSizeMap =
4316 37 : atoi(CPLGetConfigOption("GDAL_BAG_MAX_SIZE_VARRES_MAP", "50000000"));
4317 37 : const int nChunkXSize = m_nChunkXSizeVarresMD;
4318 37 : const int nChunkYSize = m_nChunkYSizeVarresMD;
4319 37 : std::vector<BAGRefinementGrid> rgrids(static_cast<size_t>(nChunkXSize) *
4320 74 : nChunkYSize);
4321 37 : bool bOK = true;
4322 74 : for (int blockY = nMinY / nChunkYSize; bOK && blockY <= nMaxY / nChunkYSize;
4323 : blockY++)
4324 : {
4325 : int nReqCountY =
4326 37 : std::min(nChunkYSize, m_nLowResHeight - blockY * nChunkYSize);
4327 74 : for (int blockX = nMinX / nChunkXSize;
4328 74 : bOK && blockX <= nMaxX / nChunkXSize; blockX++)
4329 : {
4330 : int nReqCountX =
4331 37 : std::min(nChunkXSize, m_nLowResWidth - blockX * nChunkXSize);
4332 :
4333 : // Create memory space to receive the data.
4334 37 : hsize_t count[2] = {static_cast<hsize_t>(nReqCountY),
4335 37 : static_cast<hsize_t>(nReqCountX)};
4336 37 : const hid_t memspace = H5Screate_simple(2, count, nullptr);
4337 37 : H5OFFSET_TYPE mem_offset[2] = {static_cast<H5OFFSET_TYPE>(0),
4338 : static_cast<H5OFFSET_TYPE>(0)};
4339 37 : if (H5Sselect_hyperslab(memspace, H5S_SELECT_SET, mem_offset,
4340 37 : nullptr, count, nullptr) < 0)
4341 : {
4342 0 : H5Sclose(memspace);
4343 0 : bOK = false;
4344 0 : break;
4345 : }
4346 :
4347 74 : if (!ReadVarresMetadataValue(blockY * nChunkYSize,
4348 37 : blockX * nChunkXSize, memspace,
4349 : rgrids.data(), nReqCountY, nReqCountX))
4350 : {
4351 0 : bOK = false;
4352 0 : H5Sclose(memspace);
4353 0 : break;
4354 : }
4355 37 : H5Sclose(memspace);
4356 :
4357 113 : for (int yInBlock = std::max(0, nMinY - blockY * nChunkYSize);
4358 226 : bOK && yInBlock <= std::min(nReqCountY - 1,
4359 226 : nMaxY - blockY * nChunkYSize);
4360 : yInBlock++)
4361 : {
4362 413 : for (int xInBlock = std::max(0, nMinX - blockX * nChunkXSize);
4363 826 : bOK && xInBlock <= std::min(nReqCountX - 1,
4364 826 : nMaxX - blockX * nChunkXSize);
4365 : xInBlock++)
4366 : {
4367 337 : const int y = yInBlock + blockY * nChunkYSize;
4368 337 : const int x = xInBlock + blockX * nChunkXSize;
4369 : const auto &rgrid =
4370 337 : rgrids[yInBlock * nReqCountX + xInBlock];
4371 337 : if (rgrid.nWidth > 0)
4372 : {
4373 337 : if (rgrid.fResX <= 0 || rgrid.fResY <= 0)
4374 : {
4375 0 : CPLError(CE_Failure, CPLE_NotSupported,
4376 : "Incorrect resolution for supergrid "
4377 : "(%d, %d).",
4378 : static_cast<int>(y), static_cast<int>(x));
4379 0 : bOK = false;
4380 0 : break;
4381 : }
4382 337 : if (rgrid.nIndex + static_cast<GUInt64>(rgrid.nWidth) *
4383 337 : rgrid.nHeight >
4384 : nRefinementsSize)
4385 : {
4386 0 : CPLError(
4387 : CE_Failure, CPLE_NotSupported,
4388 : "Incorrect index / dimensions for supergrid "
4389 : "(%d, %d).",
4390 : static_cast<int>(y), static_cast<int>(x));
4391 0 : bOK = false;
4392 0 : break;
4393 : }
4394 :
4395 337 : if (rgrid.fSWX < 0.0f || rgrid.fSWY < 0.0f ||
4396 : // 0.1 is to deal with numeric imprecisions
4397 337 : rgrid.fSWX +
4398 337 : (rgrid.nWidth - 1 - 0.1) * rgrid.fResX >
4399 1011 : m_gt[1] ||
4400 337 : rgrid.fSWY +
4401 337 : (rgrid.nHeight - 1 - 0.1) * rgrid.fResY >
4402 337 : -m_gt[5])
4403 : {
4404 0 : CPLError(
4405 : CE_Failure, CPLE_NotSupported,
4406 : "Incorrect bounds for supergrid "
4407 : "(%d, %d): %f, %f, %f, %f.",
4408 : static_cast<int>(y), static_cast<int>(x),
4409 0 : rgrid.fSWX, rgrid.fSWY,
4410 0 : rgrid.fSWX + (rgrid.nWidth - 1) * rgrid.fResX,
4411 0 : rgrid.fSWY + (rgrid.nHeight - 1) * rgrid.fResY);
4412 0 : bOK = false;
4413 0 : break;
4414 : }
4415 :
4416 : const float gridRes =
4417 337 : std::max(rgrid.fResX, rgrid.fResY);
4418 337 : if (gridRes < dfResFilterMin ||
4419 337 : gridRes >= dfResFilterMax)
4420 : {
4421 12 : continue;
4422 : }
4423 :
4424 325 : if (static_cast<int>(m_oMapRefinemendGrids.size()) ==
4425 : nMaxSizeMap)
4426 : {
4427 0 : CPLError(
4428 : CE_Failure, CPLE_AppDefined,
4429 : "Size of map of refinement grids has reached "
4430 : "%d entries. "
4431 : "Set the GDAL_BAG_MAX_SIZE_VARRES_MAP "
4432 : "configuration option "
4433 : "to an higher value if you want to allow more",
4434 : nMaxSizeMap);
4435 0 : return false;
4436 : }
4437 :
4438 : try
4439 : {
4440 325 : m_oMapRefinemendGrids[y * m_nLowResWidth + x] =
4441 : rgrid;
4442 : }
4443 0 : catch (const std::exception &e)
4444 : {
4445 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
4446 : "Out of memory adding entries to map of "
4447 : "refinement "
4448 : "grids: %s",
4449 0 : e.what());
4450 0 : return false;
4451 : }
4452 :
4453 325 : const double dfMinX = m_gt[0] + x * m_gt[1] +
4454 325 : rgrid.fSWX - rgrid.fResX / 2;
4455 325 : const double dfMaxX =
4456 : dfMinX +
4457 325 : rgrid.nWidth * static_cast<double>(rgrid.fResX);
4458 : const double dfMinY =
4459 325 : m_gt[3] + m_nLowResHeight * m_gt[5] + y * -m_gt[5] +
4460 325 : rgrid.fSWY - rgrid.fResY / 2;
4461 325 : const double dfMaxY =
4462 : dfMinY +
4463 325 : static_cast<double>(rgrid.nHeight) * rgrid.fResY;
4464 :
4465 335 : if ((oSupergrids.empty() ||
4466 10 : oSupergrids.find(yx(static_cast<int>(y),
4467 10 : static_cast<int>(x))) !=
4468 650 : oSupergrids.end()) &&
4469 319 : (!bHasBoundingBoxFilter ||
4470 16 : (dfMinX >= dfFilterMinX &&
4471 12 : dfMinY >= dfFilterMinY &&
4472 9 : dfMaxX <= dfFilterMaxX &&
4473 : dfMaxY <= dfFilterMaxY)))
4474 : {
4475 : {
4476 : const int nIdx =
4477 305 : m_aosSubdatasets.size() / 2 + 1;
4478 : m_aosSubdatasets.AddNameValue(
4479 : CPLSPrintf("SUBDATASET_%d_NAME", nIdx),
4480 : CPLSPrintf("BAG:\"%s\":supergrid:%d:%d",
4481 305 : GetDescription(),
4482 : static_cast<int>(y),
4483 305 : static_cast<int>(x)));
4484 : m_aosSubdatasets.AddNameValue(
4485 : CPLSPrintf("SUBDATASET_%d_DESC", nIdx),
4486 : CPLSPrintf(
4487 : "Supergrid (y=%d, x=%d) from "
4488 : "(x=%f,y=%f) to "
4489 : "(x=%f,y=%f), resolution (x=%f,y=%f)",
4490 : static_cast<int>(y),
4491 : static_cast<int>(x), dfMinX, dfMinY,
4492 305 : dfMaxX, dfMaxY, rgrid.fResX,
4493 305 : rgrid.fResY));
4494 : }
4495 :
4496 369 : for (const auto &groupName : georefMDLayerNames)
4497 : {
4498 : const int nIdx =
4499 64 : m_aosSubdatasets.size() / 2 + 1;
4500 : m_aosSubdatasets.AddNameValue(
4501 : CPLSPrintf("SUBDATASET_%d_NAME", nIdx),
4502 : CPLSPrintf(
4503 : "BAG:\"%s\":georef_metadata:%s:%d:%d",
4504 64 : GetDescription(), groupName.c_str(),
4505 : static_cast<int>(y),
4506 64 : static_cast<int>(x)));
4507 : m_aosSubdatasets.AddNameValue(
4508 : CPLSPrintf("SUBDATASET_%d_DESC", nIdx),
4509 : CPLSPrintf("Georeferenced metadata %s of "
4510 : "supergrid (y=%d, x=%d)",
4511 : groupName.c_str(),
4512 : static_cast<int>(y),
4513 64 : static_cast<int>(x)));
4514 : }
4515 : }
4516 : }
4517 : }
4518 : }
4519 : }
4520 : }
4521 :
4522 37 : if (!bOK || m_oMapRefinemendGrids.empty())
4523 : {
4524 2 : m_aosSubdatasets.Clear();
4525 2 : m_oMapRefinemendGrids.clear();
4526 2 : return false;
4527 : }
4528 :
4529 35 : CPLDebug("BAG", "variable resolution extensions detected");
4530 35 : return true;
4531 : }
4532 :
4533 : /************************************************************************/
4534 : /* LoadMetadata() */
4535 : /************************************************************************/
4536 :
4537 81 : void BAGDataset::LoadMetadata()
4538 :
4539 : {
4540 : // Load the metadata from the file.
4541 : const hid_t hMDDS =
4542 81 : H5Dopen(m_poSharedResources->m_hHDF5, "/BAG_root/metadata");
4543 81 : const hid_t datatype = H5Dget_type(hMDDS);
4544 81 : const hid_t dataspace = H5Dget_space(hMDDS);
4545 81 : const hid_t native = H5Tget_native_type(datatype, H5T_DIR_ASCEND);
4546 :
4547 81 : const int n_dims = H5Sget_simple_extent_ndims(dataspace);
4548 81 : hsize_t dims[1] = {static_cast<hsize_t>(0)};
4549 81 : hsize_t maxdims[1] = {static_cast<hsize_t>(0)};
4550 :
4551 80 : if (n_dims == 1 && H5Tget_class(native) == H5T_STRING &&
4552 161 : !H5Tis_variable_str(native) && H5Tget_size(native) == 1)
4553 : {
4554 80 : H5Sget_simple_extent_dims(dataspace, dims, maxdims);
4555 :
4556 80 : pszXMLMetadata =
4557 80 : static_cast<char *>(CPLCalloc(static_cast<int>(dims[0] + 1), 1));
4558 :
4559 80 : H5Dread(hMDDS, native, H5S_ALL, dataspace, H5P_DEFAULT, pszXMLMetadata);
4560 : }
4561 :
4562 81 : H5Tclose(native);
4563 81 : H5Sclose(dataspace);
4564 81 : H5Tclose(datatype);
4565 81 : H5Dclose(hMDDS);
4566 :
4567 81 : if (pszXMLMetadata == nullptr || pszXMLMetadata[0] == 0)
4568 1 : return;
4569 :
4570 : // Try to get the geotransform.
4571 80 : CPLXMLNode *psRoot = CPLParseXMLString(pszXMLMetadata);
4572 :
4573 80 : if (psRoot == nullptr)
4574 0 : return;
4575 :
4576 80 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
4577 :
4578 80 : CPLXMLNode *const psGeo = CPLSearchXMLNode(psRoot, "=MD_Georectified");
4579 :
4580 80 : if (psGeo != nullptr)
4581 : {
4582 160 : CPLString osResHeight, osResWidth;
4583 722 : for (const auto *psIter = psGeo->psChild; psIter;
4584 642 : psIter = psIter->psNext)
4585 : {
4586 642 : if (strcmp(psIter->pszValue, "axisDimensionProperties") == 0)
4587 : {
4588 : // since BAG format 1.5 version
4589 160 : const char *pszDim = CPLGetXMLValue(
4590 : psIter,
4591 : "MD_Dimension.dimensionName.MD_DimensionNameTypeCode",
4592 : nullptr);
4593 160 : const char *pszRes = nullptr;
4594 160 : if (pszDim)
4595 : {
4596 152 : pszRes = CPLGetXMLValue(
4597 : psIter, "MD_Dimension.resolution.Measure", nullptr);
4598 : }
4599 : else
4600 : {
4601 : // prior to BAG format 1.5 version
4602 8 : pszDim = CPLGetXMLValue(
4603 : psIter, "MD_Dimension.dimensionName", nullptr);
4604 8 : pszRes = CPLGetXMLValue(
4605 : psIter, "MD_Dimension.resolution.Measure.value",
4606 : nullptr);
4607 : }
4608 :
4609 160 : if (pszDim && EQUAL(pszDim, "row") && pszRes)
4610 : {
4611 80 : osResHeight = pszRes;
4612 : }
4613 80 : else if (pszDim && EQUAL(pszDim, "column") && pszRes)
4614 : {
4615 80 : osResWidth = pszRes;
4616 : }
4617 : }
4618 : }
4619 :
4620 80 : char **papszCornerTokens = CSLTokenizeStringComplex(
4621 : CPLGetXMLValue(psGeo, "cornerPoints.Point.coordinates", ""), " ,",
4622 : FALSE, FALSE);
4623 :
4624 80 : if (CSLCount(papszCornerTokens) == 4)
4625 : {
4626 80 : const double dfLLX = CPLAtof(papszCornerTokens[0]);
4627 80 : const double dfLLY = CPLAtof(papszCornerTokens[1]);
4628 80 : const double dfURX = CPLAtof(papszCornerTokens[2]);
4629 80 : const double dfURY = CPLAtof(papszCornerTokens[3]);
4630 :
4631 80 : double dfResWidth = CPLAtof(osResWidth);
4632 80 : double dfResHeight = CPLAtof(osResHeight);
4633 80 : if (dfResWidth > 0 && dfResHeight > 0)
4634 : {
4635 80 : if (fabs((dfURX - dfLLX) / dfResWidth - m_nLowResWidth) <
4636 0 : 1e-2 &&
4637 0 : fabs((dfURY - dfLLY) / dfResHeight - m_nLowResHeight) <
4638 : 1e-2)
4639 : {
4640 : // Found with
4641 : // https://data.ngdc.noaa.gov/platforms/ocean/nos/coast/H12001-H14000/H12525/BAG/H12525_MB_4m_MLLW_1of2.bag
4642 : // to address issue
4643 : // https://github.com/OSGeo/gdal/issues/1643
4644 0 : CPLError(CE_Warning, CPLE_AppDefined,
4645 : "cornerPoints not consistent with resolution "
4646 : "given in metadata");
4647 : }
4648 80 : else if (fabs((dfURX - dfLLX) / dfResWidth -
4649 80 : (m_nLowResWidth - 1)) < 1e-2 &&
4650 78 : fabs((dfURY - dfLLY) / dfResHeight -
4651 78 : (m_nLowResHeight - 1)) < 1e-2)
4652 : {
4653 : // pixel center convention. OK
4654 : }
4655 : else
4656 : {
4657 2 : CPLDebug("BAG", "cornerPoints not consistent with "
4658 : "resolution given in metadata");
4659 2 : CPLDebug("BAG",
4660 : "Metadata horizontal resolution: %f. "
4661 : "Computed resolution: %f. "
4662 : "Computed width: %f vs %d",
4663 2 : dfResWidth, (dfURX - dfLLX) / (m_nLowResWidth - 1),
4664 2 : (dfURX - dfLLX) / dfResWidth, m_nLowResWidth);
4665 2 : CPLDebug("BAG",
4666 : "Metadata vertical resolution: %f. "
4667 : "Computed resolution: %f. "
4668 : "Computed height: %f vs %d",
4669 : dfResHeight,
4670 2 : (dfURY - dfLLY) / (m_nLowResHeight - 1),
4671 2 : (dfURY - dfLLY) / dfResHeight, m_nLowResHeight);
4672 2 : CPLError(CE_Warning, CPLE_AppDefined,
4673 : "cornerPoints not consistent with resolution "
4674 : "given in metadata");
4675 : }
4676 : }
4677 :
4678 80 : m_gt[0] = dfLLX;
4679 80 : m_gt[1] = dfResWidth;
4680 80 : m_gt[3] = dfLLY + dfResHeight * (m_nLowResHeight - 1);
4681 80 : m_gt[5] = dfResHeight * (-1);
4682 :
4683 : // shift to pixel corner convention
4684 80 : m_gt[0] -= m_gt[1] * 0.5;
4685 80 : m_gt[3] -= m_gt[5] * 0.5;
4686 :
4687 80 : m_dfLowResMinX = m_gt[0];
4688 80 : m_dfLowResMaxX = m_dfLowResMinX + m_nLowResWidth * m_gt[1];
4689 80 : m_dfLowResMaxY = m_gt[3];
4690 80 : m_dfLowResMinY = m_dfLowResMaxY + m_nLowResHeight * m_gt[5];
4691 : }
4692 80 : CSLDestroy(papszCornerTokens);
4693 : }
4694 :
4695 : // Try to get the coordinate system.
4696 80 : if (OGR_SRS_ImportFromISO19115(&m_oSRS, pszXMLMetadata) != OGRERR_NONE)
4697 : {
4698 78 : ParseWKTFromXML(pszXMLMetadata);
4699 : }
4700 :
4701 : // Fetch acquisition date.
4702 80 : CPLXMLNode *const psDateTime = CPLSearchXMLNode(psRoot, "=dateTime");
4703 80 : if (psDateTime != nullptr)
4704 : {
4705 : const char *pszDateTimeValue =
4706 80 : psDateTime->psChild && psDateTime->psChild->eType == CXT_Element
4707 156 : ? CPLGetXMLValue(psDateTime->psChild, nullptr, nullptr)
4708 4 : : CPLGetXMLValue(psDateTime, nullptr, nullptr);
4709 80 : if (pszDateTimeValue)
4710 80 : GDALDataset::SetMetadataItem("BAG_DATETIME", pszDateTimeValue);
4711 : }
4712 :
4713 80 : CPLDestroyXMLNode(psRoot);
4714 : }
4715 :
4716 : /************************************************************************/
4717 : /* ParseWKTFromXML() */
4718 : /************************************************************************/
4719 78 : OGRErr BAGDataset::ParseWKTFromXML(const char *pszISOXML)
4720 : {
4721 78 : CPLXMLNode *const psRoot = CPLParseXMLString(pszISOXML);
4722 :
4723 78 : if (psRoot == nullptr)
4724 0 : return OGRERR_FAILURE;
4725 :
4726 78 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
4727 :
4728 78 : CPLXMLNode *psRSI = CPLSearchXMLNode(psRoot, "=referenceSystemInfo");
4729 78 : if (psRSI == nullptr)
4730 : {
4731 0 : CPLError(CE_Failure, CPLE_AppDefined,
4732 : "Unable to find <referenceSystemInfo> in metadata.");
4733 0 : CPLDestroyXMLNode(psRoot);
4734 0 : return OGRERR_FAILURE;
4735 : }
4736 :
4737 : const char *pszSRCodeString =
4738 78 : CPLGetXMLValue(psRSI,
4739 : "MD_ReferenceSystem.referenceSystemIdentifier."
4740 : "RS_Identifier.code.CharacterString",
4741 : nullptr);
4742 78 : if (pszSRCodeString == nullptr)
4743 : {
4744 2 : CPLDebug("BAG",
4745 : "Unable to find /MI_Metadata/referenceSystemInfo[1]/"
4746 : "MD_ReferenceSystem[1]/referenceSystemIdentifier[1]/"
4747 : "RS_Identifier[1]/code[1]/CharacterString[1] in metadata.");
4748 2 : CPLDestroyXMLNode(psRoot);
4749 2 : return OGRERR_FAILURE;
4750 : }
4751 :
4752 : const char *pszSRCodeSpace =
4753 76 : CPLGetXMLValue(psRSI,
4754 : "MD_ReferenceSystem.referenceSystemIdentifier."
4755 : "RS_Identifier.codeSpace.CharacterString",
4756 : "");
4757 76 : if (!EQUAL(pszSRCodeSpace, "WKT"))
4758 : {
4759 0 : CPLError(CE_Failure, CPLE_AppDefined,
4760 : "Spatial reference string is not in WKT.");
4761 0 : CPLDestroyXMLNode(psRoot);
4762 0 : return OGRERR_FAILURE;
4763 : }
4764 :
4765 76 : if (m_oSRS.importFromWkt(pszSRCodeString) != OGRERR_NONE)
4766 : {
4767 0 : CPLError(CE_Failure, CPLE_AppDefined,
4768 : "Failed parsing WKT string \"%s\".", pszSRCodeString);
4769 0 : CPLDestroyXMLNode(psRoot);
4770 0 : return OGRERR_FAILURE;
4771 : }
4772 :
4773 76 : psRSI = CPLSearchXMLNode(psRSI->psNext, "=referenceSystemInfo");
4774 76 : if (psRSI == nullptr)
4775 : {
4776 0 : CPLError(CE_Failure, CPLE_AppDefined,
4777 : "Unable to find second instance of <referenceSystemInfo> "
4778 : "in metadata.");
4779 0 : CPLDestroyXMLNode(psRoot);
4780 0 : return OGRERR_NONE;
4781 : }
4782 :
4783 : pszSRCodeString =
4784 76 : CPLGetXMLValue(psRSI,
4785 : "MD_ReferenceSystem.referenceSystemIdentifier."
4786 : "RS_Identifier.code.CharacterString",
4787 : nullptr);
4788 76 : if (pszSRCodeString == nullptr)
4789 : {
4790 0 : CPLError(CE_Failure, CPLE_AppDefined,
4791 : "Unable to find /MI_Metadata/referenceSystemInfo[2]/"
4792 : "MD_ReferenceSystem[1]/referenceSystemIdentifier[1]/"
4793 : "RS_Identifier[1]/code[1]/CharacterString[1] in metadata.");
4794 0 : CPLDestroyXMLNode(psRoot);
4795 0 : return OGRERR_NONE;
4796 : }
4797 :
4798 : pszSRCodeSpace =
4799 76 : CPLGetXMLValue(psRSI,
4800 : "MD_ReferenceSystem.referenceSystemIdentifier."
4801 : "RS_Identifier.codeSpace.CharacterString",
4802 : "");
4803 76 : if (!EQUAL(pszSRCodeSpace, "WKT"))
4804 : {
4805 0 : CPLError(CE_Failure, CPLE_AppDefined,
4806 : "Spatial reference string is not in WKT.");
4807 0 : CPLDestroyXMLNode(psRoot);
4808 0 : return OGRERR_NONE;
4809 : }
4810 :
4811 76 : if (m_bReportVertCRS && (STARTS_WITH_CI(pszSRCodeString, "VERTCS") ||
4812 73 : STARTS_WITH_CI(pszSRCodeString, "VERT_CS")))
4813 : {
4814 146 : OGR_SRSNode oVertCRSRootNode;
4815 73 : const char *pszInput = pszSRCodeString;
4816 73 : if (oVertCRSRootNode.importFromWkt(&pszInput) == OGRERR_NONE)
4817 : {
4818 73 : if (oVertCRSRootNode.GetNode("UNIT") == nullptr)
4819 : {
4820 : // UNIT is required
4821 68 : auto poUnits = new OGR_SRSNode("UNIT");
4822 68 : poUnits->AddChild(new OGR_SRSNode("metre"));
4823 68 : poUnits->AddChild(new OGR_SRSNode("1.0"));
4824 68 : oVertCRSRootNode.AddChild(poUnits);
4825 : }
4826 73 : if (oVertCRSRootNode.GetNode("AXIS") == nullptr)
4827 : {
4828 : // If AXIS is missing, add an explicit Depth AXIS
4829 68 : auto poAxis = new OGR_SRSNode("AXIS");
4830 68 : poAxis->AddChild(new OGR_SRSNode("Depth"));
4831 68 : poAxis->AddChild(new OGR_SRSNode("DOWN"));
4832 68 : oVertCRSRootNode.AddChild(poAxis);
4833 : }
4834 :
4835 73 : char *pszVertCRSWKT = nullptr;
4836 73 : oVertCRSRootNode.exportToWkt(&pszVertCRSWKT);
4837 :
4838 146 : OGRSpatialReference oVertCRS;
4839 73 : if (oVertCRS.importFromWkt(pszVertCRSWKT) == OGRERR_NONE)
4840 : {
4841 73 : if (EQUAL(oVertCRS.GetName(), "MLLW"))
4842 : {
4843 60 : oVertCRS.importFromEPSG(5866);
4844 : }
4845 :
4846 146 : OGRSpatialReference oCompoundCRS;
4847 73 : oCompoundCRS.SetCompoundCS(
4848 146 : (CPLString(m_oSRS.GetName()) + " + " + oVertCRS.GetName())
4849 : .c_str(),
4850 73 : &m_oSRS, &oVertCRS);
4851 73 : oCompoundCRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
4852 :
4853 73 : m_oSRS = std::move(oCompoundCRS);
4854 : }
4855 :
4856 73 : CPLFree(pszVertCRSWKT);
4857 : }
4858 : }
4859 :
4860 76 : CPLDestroyXMLNode(psRoot);
4861 :
4862 76 : return OGRERR_NONE;
4863 : }
4864 :
4865 : /************************************************************************/
4866 : /* GetGeoTransform() */
4867 : /************************************************************************/
4868 :
4869 78 : CPLErr BAGDataset::GetGeoTransform(GDALGeoTransform >) const
4870 :
4871 : {
4872 78 : if (m_gt[0] != 0.0 || m_gt[3] != 0.0)
4873 : {
4874 78 : gt = m_gt;
4875 78 : return CE_None;
4876 : }
4877 :
4878 0 : return GDALPamDataset::GetGeoTransform(gt);
4879 : }
4880 :
4881 : /************************************************************************/
4882 : /* GetSpatialRef() */
4883 : /************************************************************************/
4884 :
4885 21 : const OGRSpatialReference *BAGDataset::GetSpatialRef() const
4886 : {
4887 21 : if (!m_oSRS.IsEmpty())
4888 21 : return &m_oSRS;
4889 0 : return GDALPamDataset::GetSpatialRef();
4890 : }
4891 :
4892 : /************************************************************************/
4893 : /* SetGeoTransform() */
4894 : /************************************************************************/
4895 :
4896 3 : CPLErr BAGDataset::SetGeoTransform(const GDALGeoTransform >)
4897 : {
4898 3 : if (eAccess == GA_ReadOnly)
4899 0 : return GDALPamDataset::SetGeoTransform(gt);
4900 :
4901 3 : if (gt[2] != 0 || gt[4] != 0)
4902 : {
4903 0 : CPLError(CE_Failure, CPLE_NotSupported,
4904 : "BAG driver requires a non-rotated geotransform");
4905 0 : return CE_Failure;
4906 : }
4907 3 : m_gt = gt;
4908 3 : return WriteMetadataIfNeeded() ? CE_None : CE_Failure;
4909 : }
4910 :
4911 : /************************************************************************/
4912 : /* SetSpatialRef() */
4913 : /************************************************************************/
4914 :
4915 3 : CPLErr BAGDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
4916 : {
4917 3 : if (eAccess == GA_ReadOnly)
4918 0 : return GDALPamDataset::SetSpatialRef(poSRS);
4919 :
4920 3 : if (poSRS == nullptr || poSRS->IsEmpty())
4921 : {
4922 0 : CPLError(CE_Failure, CPLE_NotSupported,
4923 : "BAG driver requires a valid SRS");
4924 0 : return CE_Failure;
4925 : }
4926 :
4927 3 : m_oSRS = *poSRS;
4928 3 : return WriteMetadataIfNeeded() ? CE_None : CE_Failure;
4929 : }
4930 :
4931 : /************************************************************************/
4932 : /* WriteMetadataIfNeeded() */
4933 : /************************************************************************/
4934 :
4935 6 : bool BAGDataset::WriteMetadataIfNeeded()
4936 : {
4937 6 : if (m_bMetadataWritten)
4938 : {
4939 0 : return true;
4940 : }
4941 6 : if (m_gt == GDALGeoTransform() || m_oSRS.IsEmpty())
4942 : {
4943 3 : return true;
4944 : }
4945 3 : m_bMetadataWritten = true;
4946 :
4947 : CPLString osXMLMetadata = BAGCreator::GenerateMetadata(
4948 3 : nRasterXSize, nRasterYSize, m_gt, m_oSRS.IsEmpty() ? nullptr : &m_oSRS,
4949 9 : m_aosCreationOptions.List());
4950 3 : if (osXMLMetadata.empty())
4951 : {
4952 0 : return false;
4953 : }
4954 :
4955 3 : if (!BAGCreator::CreateAndWriteMetadata(m_poSharedResources->m_hHDF5,
4956 : osXMLMetadata))
4957 : {
4958 0 : return false;
4959 : }
4960 :
4961 3 : return true;
4962 : }
4963 :
4964 : /************************************************************************/
4965 : /* GetMetadataDomainList() */
4966 : /************************************************************************/
4967 :
4968 0 : char **BAGDataset::GetMetadataDomainList()
4969 : {
4970 0 : return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
4971 0 : TRUE, "xml:BAG", nullptr);
4972 : }
4973 :
4974 : /************************************************************************/
4975 : /* GetMetadata() */
4976 : /************************************************************************/
4977 :
4978 67 : char **BAGDataset::GetMetadata(const char *pszDomain)
4979 :
4980 : {
4981 67 : if (pszDomain != nullptr && EQUAL(pszDomain, "xml:BAG"))
4982 : {
4983 5 : apszMDList[0] = pszXMLMetadata;
4984 5 : apszMDList[1] = nullptr;
4985 :
4986 5 : return apszMDList;
4987 : }
4988 :
4989 62 : if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
4990 : {
4991 16 : return m_aosSubdatasets.List();
4992 : }
4993 :
4994 46 : return GDALPamDataset::GetMetadata(pszDomain);
4995 : }
4996 :
4997 : /************************************************************************/
4998 : /* BAGDatasetDriverUnload() */
4999 : /************************************************************************/
5000 :
5001 9 : static void BAGDatasetDriverUnload(GDALDriver *)
5002 : {
5003 9 : HDF5UnloadFileDriver();
5004 9 : }
5005 :
5006 : /************************************************************************/
5007 : /* ~BAGCreator()() */
5008 : /************************************************************************/
5009 :
5010 148 : BAGCreator::~BAGCreator()
5011 : {
5012 74 : Close();
5013 74 : }
5014 :
5015 : /************************************************************************/
5016 : /* Close() */
5017 : /************************************************************************/
5018 :
5019 94 : bool BAGCreator::Close()
5020 : {
5021 94 : bool ret = true;
5022 94 : if (m_bagRoot >= 0)
5023 : {
5024 20 : ret = (H5_CHECK(H5Gclose(m_bagRoot)) >= 0) && ret;
5025 20 : m_bagRoot = -1;
5026 : }
5027 94 : if (m_hdf5 >= 0)
5028 : {
5029 20 : ret = (H5_CHECK(H5Fclose(m_hdf5)) >= 0) && ret;
5030 20 : m_hdf5 = -1;
5031 : }
5032 94 : return ret;
5033 : }
5034 :
5035 : /************************************************************************/
5036 : /* SubstituteVariables() */
5037 : /************************************************************************/
5038 :
5039 4893 : bool BAGCreator::SubstituteVariables(CPLXMLNode *psNode, char **papszDict)
5040 : {
5041 4893 : if (psNode->eType == CXT_Text && psNode->pszValue &&
5042 1617 : strstr(psNode->pszValue, "${"))
5043 : {
5044 735 : CPLString osVal(psNode->pszValue);
5045 735 : size_t nPos = 0;
5046 : while (true)
5047 : {
5048 1470 : nPos = osVal.find("${", nPos);
5049 1470 : if (nPos == std::string::npos)
5050 : {
5051 735 : break;
5052 : }
5053 735 : CPLString osKeyName;
5054 735 : bool bHasDefaultValue = false;
5055 735 : CPLString osDefaultValue;
5056 735 : size_t nAfterKeyName = 0;
5057 14616 : for (size_t i = nPos + 2; i < osVal.size(); i++)
5058 : {
5059 14616 : if (osVal[i] == ':')
5060 : {
5061 378 : osKeyName = osVal.substr(nPos + 2, i - (nPos + 2));
5062 : }
5063 14238 : else if (osVal[i] == '}')
5064 : {
5065 735 : if (osKeyName.empty())
5066 : {
5067 357 : osKeyName = osVal.substr(nPos + 2, i - (nPos + 2));
5068 : }
5069 : else
5070 : {
5071 378 : bHasDefaultValue = true;
5072 : size_t nStartDefaultVal =
5073 378 : nPos + 2 + osKeyName.size() + 1;
5074 756 : osDefaultValue = osVal.substr(nStartDefaultVal,
5075 378 : i - nStartDefaultVal);
5076 : }
5077 735 : nAfterKeyName = i + 1;
5078 735 : break;
5079 : }
5080 : }
5081 735 : if (nAfterKeyName == 0)
5082 : {
5083 0 : CPLError(CE_Failure, CPLE_AppDefined,
5084 : "Invalid variable name in template");
5085 0 : return false;
5086 : }
5087 :
5088 735 : bool bSubstFound = false;
5089 9511 : for (char **papszIter = papszDict;
5090 9511 : !bSubstFound && papszIter && *papszIter; papszIter++)
5091 : {
5092 8776 : if (STARTS_WITH_CI(*papszIter, "VAR_"))
5093 : {
5094 8706 : char *pszKey = nullptr;
5095 : const char *pszValue =
5096 8706 : CPLParseNameValue(*papszIter, &pszKey);
5097 8706 : if (pszKey && pszValue)
5098 : {
5099 8706 : const char *pszVarName = pszKey + strlen("VAR_");
5100 8706 : if (EQUAL(pszVarName, osKeyName))
5101 : {
5102 362 : bSubstFound = true;
5103 724 : osVal = osVal.substr(0, nPos) + pszValue +
5104 1086 : osVal.substr(nAfterKeyName);
5105 : }
5106 8706 : CPLFree(pszKey);
5107 : }
5108 : }
5109 : }
5110 735 : if (!bSubstFound)
5111 : {
5112 373 : if (bHasDefaultValue)
5113 : {
5114 746 : osVal = osVal.substr(0, nPos) + osDefaultValue +
5115 1119 : osVal.substr(nAfterKeyName);
5116 : }
5117 : else
5118 : {
5119 0 : CPLError(CE_Warning, CPLE_AppDefined,
5120 : "%s could not be substituted", osKeyName.c_str());
5121 0 : return false;
5122 : }
5123 : }
5124 735 : }
5125 :
5126 735 : if (!osVal.empty() && osVal[0] == '<' && osVal.back() == '>')
5127 : {
5128 2 : CPLXMLNode *psSubNode = CPLParseXMLString(osVal);
5129 2 : if (psSubNode)
5130 : {
5131 2 : CPLFree(psNode->pszValue);
5132 2 : psNode->eType = psSubNode->eType;
5133 2 : psNode->pszValue = psSubNode->pszValue;
5134 2 : psNode->psChild = psSubNode->psChild;
5135 2 : psSubNode->pszValue = nullptr;
5136 2 : psSubNode->psChild = nullptr;
5137 2 : CPLDestroyXMLNode(psSubNode);
5138 : }
5139 : else
5140 : {
5141 0 : CPLFree(psNode->pszValue);
5142 0 : psNode->pszValue = CPLStrdup(osVal);
5143 : }
5144 : }
5145 : else
5146 : {
5147 733 : CPLFree(psNode->pszValue);
5148 733 : psNode->pszValue = CPLStrdup(osVal);
5149 : }
5150 : }
5151 :
5152 9765 : for (CPLXMLNode *psIter = psNode->psChild; psIter; psIter = psIter->psNext)
5153 : {
5154 4872 : if (!SubstituteVariables(psIter, papszDict))
5155 0 : return false;
5156 : }
5157 4893 : return true;
5158 : }
5159 :
5160 : /************************************************************************/
5161 : /* GenerateMetadata() */
5162 : /************************************************************************/
5163 :
5164 21 : CPLString BAGCreator::GenerateMetadata(int nXSize, int nYSize,
5165 : const GDALGeoTransform >,
5166 : const OGRSpatialReference *poSRS,
5167 : char **papszOptions)
5168 : {
5169 : CPLXMLNode *psRoot;
5170 : CPLString osTemplateFilename =
5171 42 : CSLFetchNameValueDef(papszOptions, "TEMPLATE", "");
5172 21 : if (!osTemplateFilename.empty())
5173 : {
5174 0 : psRoot = CPLParseXMLFile(osTemplateFilename);
5175 : }
5176 : else
5177 : {
5178 : #ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
5179 : #ifdef EMBED_RESOURCE_FILES
5180 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
5181 : #endif
5182 : const char *pszDefaultTemplateFilename =
5183 21 : CPLFindFile("gdal", "bag_template.xml");
5184 21 : if (pszDefaultTemplateFilename == nullptr)
5185 : #endif
5186 : {
5187 : #ifdef EMBED_RESOURCE_FILES
5188 : static const bool bOnce [[maybe_unused]] = []()
5189 : {
5190 : CPLDebug("BAG", "Using embedded bag_template.xml");
5191 : return true;
5192 : }();
5193 : psRoot = CPLParseXMLString(BAGGetEmbeddedTemplateFile());
5194 : #else
5195 0 : CPLError(CE_Failure, CPLE_AppDefined,
5196 : "Cannot find bag_template.xml and TEMPLATE "
5197 : "creation option not specified");
5198 0 : return CPLString();
5199 : #endif
5200 : }
5201 : #ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
5202 : else
5203 : {
5204 21 : psRoot = CPLParseXMLFile(pszDefaultTemplateFilename);
5205 : }
5206 : #endif
5207 : }
5208 21 : if (psRoot == nullptr)
5209 0 : return CPLString();
5210 42 : CPLXMLTreeCloser oCloser(psRoot);
5211 21 : CPL_IGNORE_RET_VAL(oCloser);
5212 :
5213 21 : CPLXMLNode *psMain = psRoot;
5214 42 : for (; psMain; psMain = psMain->psNext)
5215 : {
5216 42 : if (psMain->eType == CXT_Element && !STARTS_WITH(psMain->pszValue, "?"))
5217 : {
5218 21 : break;
5219 : }
5220 : }
5221 21 : if (psMain == nullptr)
5222 : {
5223 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find main XML node");
5224 0 : return CPLString();
5225 : }
5226 :
5227 42 : CPLStringList osOptions(papszOptions, FALSE);
5228 21 : if (osOptions.FetchNameValue("VAR_PROCESS_STEP_DESCRIPTION") == nullptr)
5229 : {
5230 : osOptions.SetNameValue("VAR_PROCESS_STEP_DESCRIPTION",
5231 : CPLSPrintf("Generated by GDAL %s",
5232 21 : GDALVersionInfo("RELEASE_NAME")));
5233 : }
5234 21 : osOptions.SetNameValue("VAR_HEIGHT", CPLSPrintf("%d", nYSize));
5235 21 : osOptions.SetNameValue("VAR_WIDTH", CPLSPrintf("%d", nXSize));
5236 :
5237 : struct tm brokenDown;
5238 21 : CPLUnixTimeToYMDHMS(time(nullptr), &brokenDown);
5239 21 : if (osOptions.FetchNameValue("VAR_DATE") == nullptr)
5240 : {
5241 : osOptions.SetNameValue(
5242 21 : "VAR_DATE", CPLSPrintf("%04d-%02d-%02d", brokenDown.tm_year + 1900,
5243 21 : brokenDown.tm_mon + 1, brokenDown.tm_mday));
5244 : }
5245 21 : if (osOptions.FetchNameValue("VAR_DATETIME") == nullptr)
5246 : {
5247 : osOptions.SetNameValue(
5248 : "VAR_DATETIME",
5249 : CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
5250 21 : brokenDown.tm_year + 1900, brokenDown.tm_mon + 1,
5251 : brokenDown.tm_mday, brokenDown.tm_hour,
5252 21 : brokenDown.tm_min, brokenDown.tm_sec));
5253 : }
5254 :
5255 21 : osOptions.SetNameValue("VAR_RESX", CPLSPrintf("%.17g", gt[1]));
5256 21 : osOptions.SetNameValue("VAR_RESY", CPLSPrintf("%.17g", fabs(gt[5])));
5257 : osOptions.SetNameValue("VAR_RES",
5258 21 : CPLSPrintf("%.17g", std::max(gt[1], fabs(gt[5]))));
5259 :
5260 21 : char *pszProjection = nullptr;
5261 21 : if (poSRS)
5262 21 : poSRS->exportToWkt(&pszProjection);
5263 21 : if (pszProjection == nullptr || EQUAL(pszProjection, ""))
5264 : {
5265 0 : CPLError(CE_Failure, CPLE_NotSupported,
5266 : "BAG driver requires a source dataset with a projection");
5267 : }
5268 21 : osOptions.SetNameValue("VAR_HORIZ_WKT", pszProjection);
5269 21 : CPLFree(pszProjection);
5270 :
5271 42 : OGRSpatialReference oSRS;
5272 21 : if (poSRS)
5273 21 : oSRS = *poSRS;
5274 21 : if (oSRS.IsCompound())
5275 : {
5276 2 : auto node = oSRS.GetRoot();
5277 2 : if (node && node->GetChildCount() == 3)
5278 : {
5279 2 : char *pszHorizWKT = nullptr;
5280 2 : node->GetChild(1)->exportToWkt(&pszHorizWKT);
5281 2 : char *pszVertWKT = nullptr;
5282 2 : node->GetChild(2)->exportToWkt(&pszVertWKT);
5283 :
5284 2 : oSRS.StripVertical();
5285 :
5286 2 : osOptions.SetNameValue("VAR_HORIZ_WKT", pszHorizWKT);
5287 2 : if (osOptions.FetchNameValue("VAR_VERT_WKT") == nullptr)
5288 : {
5289 2 : osOptions.SetNameValue("VAR_VERT_WKT", pszVertWKT);
5290 : }
5291 2 : CPLFree(pszHorizWKT);
5292 2 : CPLFree(pszVertWKT);
5293 : }
5294 : }
5295 :
5296 21 : const char *pszUnits = "m";
5297 21 : if (oSRS.IsProjected())
5298 : {
5299 6 : oSRS.GetLinearUnits(&pszUnits);
5300 6 : if (EQUAL(pszUnits, "metre"))
5301 6 : pszUnits = "m";
5302 : }
5303 : else
5304 : {
5305 15 : pszUnits = "deg";
5306 : }
5307 21 : osOptions.SetNameValue("VAR_RES_UNIT", pszUnits);
5308 :
5309 : // get bounds as pixel center
5310 21 : double dfMinX = gt[0] + gt[1] / 2;
5311 21 : double dfMaxX = dfMinX + (nXSize - 1) * gt[1];
5312 21 : double dfMaxY = gt[3] + gt[5] / 2;
5313 21 : double dfMinY = dfMaxY + (nYSize - 1) * gt[5];
5314 :
5315 21 : if (gt[5] > 0)
5316 : {
5317 1 : std::swap(dfMinY, dfMaxY);
5318 : }
5319 : osOptions.SetNameValue(
5320 : "VAR_CORNER_POINTS",
5321 21 : CPLSPrintf("%.17g,%.17g %.17g,%.17g", dfMinX, dfMinY, dfMaxX, dfMaxY));
5322 :
5323 21 : double adfCornerX[4] = {dfMinX, dfMinX, dfMaxX, dfMaxX};
5324 21 : double adfCornerY[4] = {dfMinY, dfMaxY, dfMaxY, dfMinY};
5325 42 : OGRSpatialReference oSRS_WGS84;
5326 21 : oSRS_WGS84.SetFromUserInput("WGS84");
5327 21 : oSRS_WGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
5328 : OGRCoordinateTransformation *poCT =
5329 21 : OGRCreateCoordinateTransformation(&oSRS, &oSRS_WGS84);
5330 21 : if (!poCT)
5331 0 : return CPLString();
5332 21 : if (!poCT->Transform(4, adfCornerX, adfCornerY))
5333 : {
5334 0 : CPLError(CE_Failure, CPLE_AppDefined,
5335 : "Cannot compute raster extent in geodetic coordinates");
5336 0 : delete poCT;
5337 0 : return CPLString();
5338 : }
5339 21 : delete poCT;
5340 : double dfWest = std::min(std::min(adfCornerX[0], adfCornerX[1]),
5341 21 : std::min(adfCornerX[2], adfCornerX[3]));
5342 : double dfSouth = std::min(std::min(adfCornerY[0], adfCornerY[1]),
5343 21 : std::min(adfCornerY[2], adfCornerY[3]));
5344 : double dfEast = std::max(std::max(adfCornerX[0], adfCornerX[1]),
5345 21 : std::max(adfCornerX[2], adfCornerX[3]));
5346 : double dfNorth = std::max(std::max(adfCornerY[0], adfCornerY[1]),
5347 21 : std::max(adfCornerY[2], adfCornerY[3]));
5348 21 : osOptions.SetNameValue("VAR_WEST_LONGITUDE", CPLSPrintf("%.17g", dfWest));
5349 21 : osOptions.SetNameValue("VAR_SOUTH_LATITUDE", CPLSPrintf("%.17g", dfSouth));
5350 21 : osOptions.SetNameValue("VAR_EAST_LONGITUDE", CPLSPrintf("%.17g", dfEast));
5351 21 : osOptions.SetNameValue("VAR_NORTH_LATITUDE", CPLSPrintf("%.17g", dfNorth));
5352 :
5353 21 : if (!SubstituteVariables(psMain, osOptions.List()))
5354 : {
5355 0 : return CPLString();
5356 : }
5357 :
5358 21 : char *pszXML = CPLSerializeXMLTree(psRoot);
5359 42 : CPLString osXML(pszXML);
5360 21 : CPLFree(pszXML);
5361 21 : return osXML;
5362 : }
5363 :
5364 : /************************************************************************/
5365 : /* CreateAndWriteMetadata() */
5366 : /************************************************************************/
5367 :
5368 19 : bool BAGCreator::CreateAndWriteMetadata(hid_t hdf5,
5369 : const CPLString &osXMLMetadata)
5370 : {
5371 19 : hsize_t dim_init[1] = {1 + osXMLMetadata.size()};
5372 19 : hsize_t dim_max[1] = {H5S_UNLIMITED};
5373 :
5374 19 : hid_t hDataSpace = H5_CHECK(H5Screate_simple(1, dim_init, dim_max));
5375 19 : if (hDataSpace < 0)
5376 0 : return false;
5377 :
5378 19 : hid_t hParams = -1;
5379 19 : hid_t hDataType = -1;
5380 19 : hid_t hDatasetID = -1;
5381 19 : hid_t hFileSpace = -1;
5382 19 : bool ret = false;
5383 : do
5384 : {
5385 19 : hParams = H5_CHECK(H5Pcreate(H5P_DATASET_CREATE));
5386 19 : if (hParams < 0)
5387 0 : break;
5388 :
5389 19 : hsize_t chunk_dims[1] = {1024};
5390 19 : if (H5_CHECK(H5Pset_chunk(hParams, 1, chunk_dims)) < 0)
5391 0 : break;
5392 :
5393 19 : hDataType = H5_CHECK(H5Tcopy(H5T_C_S1));
5394 19 : if (hDataType < 0)
5395 0 : break;
5396 :
5397 19 : hDatasetID = H5_CHECK(H5Dcreate(hdf5, "/BAG_root/metadata", hDataType,
5398 : hDataSpace, hParams));
5399 19 : if (hDatasetID < 0)
5400 0 : break;
5401 :
5402 19 : if (H5_CHECK(H5Dextend(hDatasetID, dim_init)) < 0)
5403 0 : break;
5404 :
5405 19 : hFileSpace = H5_CHECK(H5Dget_space(hDatasetID));
5406 19 : if (hFileSpace < 0)
5407 0 : break;
5408 :
5409 19 : H5OFFSET_TYPE offset[1] = {0};
5410 19 : if (H5_CHECK(H5Sselect_hyperslab(hFileSpace, H5S_SELECT_SET, offset,
5411 19 : nullptr, dim_init, nullptr)) < 0)
5412 : {
5413 0 : break;
5414 : }
5415 :
5416 19 : if (H5_CHECK(H5Dwrite(hDatasetID, hDataType, hDataSpace, hFileSpace,
5417 19 : H5P_DEFAULT, osXMLMetadata.data())) < 0)
5418 : {
5419 0 : break;
5420 : }
5421 :
5422 19 : ret = true;
5423 : } while (0);
5424 :
5425 19 : if (hParams >= 0)
5426 19 : H5_CHECK(H5Pclose(hParams));
5427 19 : if (hDataType >= 0)
5428 19 : H5_CHECK(H5Tclose(hDataType));
5429 19 : if (hFileSpace >= 0)
5430 19 : H5_CHECK(H5Sclose(hFileSpace));
5431 19 : if (hDatasetID >= 0)
5432 19 : H5_CHECK(H5Dclose(hDatasetID));
5433 19 : H5_CHECK(H5Sclose(hDataSpace));
5434 :
5435 19 : return ret;
5436 : }
5437 :
5438 : /************************************************************************/
5439 : /* CreateTrackingListDataset() */
5440 : /************************************************************************/
5441 :
5442 20 : bool BAGCreator::CreateTrackingListDataset()
5443 : {
5444 : typedef struct
5445 : {
5446 : uint32_t row;
5447 : uint32_t col;
5448 : float depth;
5449 : float uncertainty;
5450 : uint8_t track_code;
5451 : uint16_t list_series;
5452 : } TrackingListItem;
5453 :
5454 20 : hsize_t dim_init[1] = {0};
5455 20 : hsize_t dim_max[1] = {H5S_UNLIMITED};
5456 :
5457 20 : hid_t hDataSpace = H5_CHECK(H5Screate_simple(1, dim_init, dim_max));
5458 20 : if (hDataSpace < 0)
5459 0 : return false;
5460 :
5461 20 : hid_t hParams = -1;
5462 20 : hid_t hDataType = -1;
5463 20 : hid_t hDatasetID = -1;
5464 20 : bool ret = false;
5465 : do
5466 : {
5467 20 : hParams = H5_CHECK(H5Pcreate(H5P_DATASET_CREATE));
5468 20 : if (hParams < 0)
5469 0 : break;
5470 :
5471 20 : hsize_t chunk_dims[1] = {10};
5472 20 : if (H5_CHECK(H5Pset_chunk(hParams, 1, chunk_dims)) < 0)
5473 0 : break;
5474 :
5475 : TrackingListItem unusedItem;
5476 : (void)unusedItem.row;
5477 : (void)unusedItem.col;
5478 : (void)unusedItem.depth;
5479 : (void)unusedItem.uncertainty;
5480 : (void)unusedItem.track_code;
5481 : (void)unusedItem.list_series;
5482 20 : hDataType = H5_CHECK(H5Tcreate(H5T_COMPOUND, sizeof(unusedItem)));
5483 20 : if (hDataType < 0)
5484 0 : break;
5485 :
5486 20 : if (H5Tinsert(hDataType, "row", HOFFSET(TrackingListItem, row),
5487 20 : H5T_NATIVE_UINT) < 0 ||
5488 20 : H5Tinsert(hDataType, "col", HOFFSET(TrackingListItem, col),
5489 20 : H5T_NATIVE_UINT) < 0 ||
5490 20 : H5Tinsert(hDataType, "depth", HOFFSET(TrackingListItem, depth),
5491 20 : H5T_NATIVE_FLOAT) < 0 ||
5492 20 : H5Tinsert(hDataType, "uncertainty",
5493 : HOFFSET(TrackingListItem, uncertainty),
5494 20 : H5T_NATIVE_FLOAT) < 0 ||
5495 20 : H5Tinsert(hDataType, "track_code",
5496 : HOFFSET(TrackingListItem, track_code),
5497 60 : H5T_NATIVE_UCHAR) < 0 ||
5498 20 : H5Tinsert(hDataType, "list_series",
5499 : HOFFSET(TrackingListItem, list_series),
5500 20 : H5T_NATIVE_SHORT) < 0)
5501 : {
5502 0 : break;
5503 : }
5504 :
5505 20 : hDatasetID = H5_CHECK(H5Dcreate(m_hdf5, "/BAG_root/tracking_list",
5506 : hDataType, hDataSpace, hParams));
5507 20 : if (hDatasetID < 0)
5508 0 : break;
5509 :
5510 20 : if (H5_CHECK(H5Dextend(hDatasetID, dim_init)) < 0)
5511 0 : break;
5512 :
5513 20 : if (!GH5_CreateAttribute(hDatasetID, "Tracking List Length",
5514 20 : H5T_NATIVE_UINT))
5515 0 : break;
5516 :
5517 20 : if (!GH5_WriteAttribute(hDatasetID, "Tracking List Length", 0U))
5518 0 : break;
5519 :
5520 20 : ret = true;
5521 : } while (0);
5522 :
5523 20 : if (hParams >= 0)
5524 20 : H5_CHECK(H5Pclose(hParams));
5525 20 : if (hDataType >= 0)
5526 20 : H5_CHECK(H5Tclose(hDataType));
5527 20 : if (hDatasetID >= 0)
5528 20 : H5_CHECK(H5Dclose(hDatasetID));
5529 20 : H5_CHECK(H5Sclose(hDataSpace));
5530 :
5531 20 : return ret;
5532 : }
5533 :
5534 : /************************************************************************/
5535 : /* CreateElevationOrUncertainty() */
5536 : /************************************************************************/
5537 :
5538 32 : bool BAGCreator::CreateElevationOrUncertainty(
5539 : GDALDataset *poSrcDS, int nBand, const char *pszDSName,
5540 : const char *pszMaxAttrName, const char *pszMinAttrName, char **papszOptions,
5541 : GDALProgressFunc pfnProgress, void *pProgressData)
5542 : {
5543 32 : const int nYSize = poSrcDS->GetRasterYSize();
5544 32 : const int nXSize = poSrcDS->GetRasterXSize();
5545 :
5546 32 : GDALGeoTransform gt;
5547 32 : poSrcDS->GetGeoTransform(gt);
5548 :
5549 32 : hsize_t dims[2] = {static_cast<hsize_t>(nYSize),
5550 32 : static_cast<hsize_t>(nXSize)};
5551 :
5552 32 : hid_t hDataSpace = H5_CHECK(H5Screate_simple(2, dims, nullptr));
5553 32 : if (hDataSpace < 0)
5554 0 : return false;
5555 :
5556 32 : hid_t hParams = -1;
5557 32 : hid_t hDataType = -1;
5558 32 : hid_t hDatasetID = -1;
5559 32 : hid_t hFileSpace = -1;
5560 32 : bool bDeflate = EQUAL(
5561 : CSLFetchNameValueDef(papszOptions, "COMPRESS", "DEFLATE"), "DEFLATE");
5562 : int nCompressionLevel =
5563 32 : atoi(CSLFetchNameValueDef(papszOptions, "ZLEVEL", "6"));
5564 32 : const int nBlockSize = std::min(
5565 32 : 4096, atoi(CSLFetchNameValueDef(papszOptions, "BLOCK_SIZE", "100")));
5566 32 : const int nBlockXSize = std::min(nXSize, nBlockSize);
5567 32 : const int nBlockYSize = std::min(nYSize, nBlockSize);
5568 32 : bool ret = false;
5569 32 : const float fNoDataValue = fDEFAULT_NODATA;
5570 : do
5571 : {
5572 32 : hDataType = H5_CHECK(H5Tcopy(H5T_NATIVE_FLOAT));
5573 32 : if (hDataType < 0)
5574 0 : break;
5575 :
5576 32 : if (H5_CHECK(H5Tset_order(hDataType, H5T_ORDER_LE)) < 0)
5577 0 : break;
5578 :
5579 32 : hParams = H5_CHECK(H5Pcreate(H5P_DATASET_CREATE));
5580 32 : if (hParams < 0)
5581 0 : break;
5582 :
5583 32 : if (H5_CHECK(H5Pset_fill_time(hParams, H5D_FILL_TIME_ALLOC)) < 0)
5584 0 : break;
5585 :
5586 32 : if (H5_CHECK(H5Pset_fill_value(hParams, hDataType, &fNoDataValue)) < 0)
5587 0 : break;
5588 :
5589 32 : if (H5_CHECK(H5Pset_layout(hParams, H5D_CHUNKED)) < 0)
5590 0 : break;
5591 32 : hsize_t chunk_size[2] = {static_cast<hsize_t>(nBlockYSize),
5592 32 : static_cast<hsize_t>(nBlockXSize)};
5593 32 : if (H5_CHECK(H5Pset_chunk(hParams, 2, chunk_size)) < 0)
5594 0 : break;
5595 :
5596 32 : if (bDeflate)
5597 : {
5598 32 : if (H5_CHECK(H5Pset_deflate(hParams, nCompressionLevel)) < 0)
5599 0 : break;
5600 : }
5601 :
5602 32 : hDatasetID = H5_CHECK(
5603 : H5Dcreate(m_hdf5, pszDSName, hDataType, hDataSpace, hParams));
5604 32 : if (hDatasetID < 0)
5605 0 : break;
5606 :
5607 32 : if (!GH5_CreateAttribute(hDatasetID, pszMaxAttrName, hDataType))
5608 0 : break;
5609 :
5610 32 : if (!GH5_CreateAttribute(hDatasetID, pszMinAttrName, hDataType))
5611 0 : break;
5612 :
5613 32 : hFileSpace = H5_CHECK(H5Dget_space(hDatasetID));
5614 32 : if (hFileSpace < 0)
5615 0 : break;
5616 :
5617 32 : const int nYBlocks =
5618 32 : static_cast<int>(DIV_ROUND_UP(nYSize, nBlockYSize));
5619 32 : const int nXBlocks =
5620 32 : static_cast<int>(DIV_ROUND_UP(nXSize, nBlockXSize));
5621 32 : std::vector<float> afValues(static_cast<size_t>(nBlockYSize) *
5622 32 : nBlockXSize);
5623 32 : ret = true;
5624 32 : const bool bReverseY = gt[5] < 0;
5625 :
5626 32 : float fMin = std::numeric_limits<float>::infinity();
5627 32 : float fMax = -std::numeric_limits<float>::infinity();
5628 :
5629 32 : if (nBand == 1 || poSrcDS->GetRasterCount() == 2)
5630 : {
5631 18 : int bHasNoData = FALSE;
5632 : const double dfSrcNoData =
5633 18 : poSrcDS->GetRasterBand(nBand)->GetNoDataValue(&bHasNoData);
5634 18 : const float fSrcNoData = static_cast<float>(dfSrcNoData);
5635 :
5636 38 : for (int iY = 0; ret && iY < nYBlocks; iY++)
5637 : {
5638 : const int nSrcYOff =
5639 20 : bReverseY ? std::max(0, nYSize - (iY + 1) * nBlockYSize)
5640 1 : : iY * nBlockYSize;
5641 : const int nReqCountY =
5642 20 : std::min(nBlockYSize, nYSize - iY * nBlockYSize);
5643 48 : for (int iX = 0; iX < nXBlocks; iX++)
5644 : {
5645 : const int nReqCountX =
5646 28 : std::min(nBlockXSize, nXSize - iX * nBlockXSize);
5647 :
5648 55 : if (poSrcDS->GetRasterBand(nBand)->RasterIO(
5649 28 : GF_Read, iX * nBlockXSize, nSrcYOff, nReqCountX,
5650 : nReqCountY,
5651 27 : bReverseY ? afValues.data() +
5652 27 : (nReqCountY - 1) * nReqCountX
5653 1 : : afValues.data(),
5654 : nReqCountX, nReqCountY, GDT_Float32, 0,
5655 27 : bReverseY ? -4 * nReqCountX : 0,
5656 28 : nullptr) != CE_None)
5657 : {
5658 0 : ret = false;
5659 0 : break;
5660 : }
5661 :
5662 2376 : for (int i = 0; i < nReqCountY * nReqCountX; i++)
5663 : {
5664 2348 : const float fVal = afValues[i];
5665 4696 : if ((bHasNoData && fVal == fSrcNoData) ||
5666 2348 : std::isnan(fVal))
5667 : {
5668 0 : afValues[i] = fNoDataValue;
5669 : }
5670 : else
5671 : {
5672 2348 : fMin = std::min(fMin, fVal);
5673 2348 : fMax = std::max(fMax, fVal);
5674 : }
5675 : }
5676 :
5677 : H5OFFSET_TYPE offset[2] = {
5678 28 : static_cast<H5OFFSET_TYPE>(iY) *
5679 28 : static_cast<H5OFFSET_TYPE>(nBlockYSize),
5680 28 : static_cast<H5OFFSET_TYPE>(iX) *
5681 28 : static_cast<H5OFFSET_TYPE>(nBlockXSize)};
5682 28 : hsize_t count[2] = {static_cast<hsize_t>(nReqCountY),
5683 28 : static_cast<hsize_t>(nReqCountX)};
5684 28 : if (H5_CHECK(H5Sselect_hyperslab(hFileSpace, H5S_SELECT_SET,
5685 : offset, nullptr, count,
5686 28 : nullptr)) < 0)
5687 : {
5688 0 : ret = false;
5689 0 : break;
5690 : }
5691 :
5692 28 : hid_t hMemSpace = H5Screate_simple(2, count, nullptr);
5693 28 : if (hMemSpace < 0)
5694 0 : break;
5695 :
5696 28 : if (H5_CHECK(H5Dwrite(hDatasetID, H5T_NATIVE_FLOAT,
5697 : hMemSpace, hFileSpace, H5P_DEFAULT,
5698 28 : afValues.data())) < 0)
5699 : {
5700 0 : H5Sclose(hMemSpace);
5701 0 : ret = false;
5702 0 : break;
5703 : }
5704 :
5705 28 : H5Sclose(hMemSpace);
5706 :
5707 28 : if (!pfnProgress(
5708 28 : (static_cast<double>(iY) * nXBlocks + iX + 1) /
5709 28 : (static_cast<double>(nXBlocks) * nYBlocks),
5710 : "", pProgressData))
5711 : {
5712 0 : ret = false;
5713 0 : break;
5714 : }
5715 : }
5716 : }
5717 : }
5718 32 : if (!ret)
5719 0 : break;
5720 :
5721 32 : if (fMin > fMax)
5722 14 : fMin = fMax = fNoDataValue;
5723 :
5724 32 : if (!GH5_WriteAttribute(hDatasetID, pszMaxAttrName, fMax))
5725 0 : break;
5726 :
5727 32 : if (!GH5_WriteAttribute(hDatasetID, pszMinAttrName, fMin))
5728 0 : break;
5729 :
5730 32 : ret = true;
5731 : } while (0);
5732 :
5733 32 : if (hParams >= 0)
5734 32 : H5_CHECK(H5Pclose(hParams));
5735 32 : if (hDataType >= 0)
5736 32 : H5_CHECK(H5Tclose(hDataType));
5737 32 : if (hFileSpace >= 0)
5738 32 : H5_CHECK(H5Sclose(hFileSpace));
5739 32 : if (hDatasetID >= 0)
5740 32 : H5_CHECK(H5Dclose(hDatasetID));
5741 32 : H5_CHECK(H5Sclose(hDataSpace));
5742 :
5743 32 : return ret;
5744 : }
5745 :
5746 : /************************************************************************/
5747 : /* CreateBase() */
5748 : /************************************************************************/
5749 :
5750 22 : bool BAGCreator::CreateBase(const char *pszFilename, char **papszOptions)
5751 : {
5752 22 : hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
5753 22 : H5Pset_driver(fapl, HDF5GetFileDriver(), nullptr);
5754 22 : m_hdf5 = H5Fcreate(pszFilename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl);
5755 22 : H5Pclose(fapl);
5756 22 : if (m_hdf5 < 0)
5757 : {
5758 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create file");
5759 2 : return false;
5760 : }
5761 :
5762 20 : m_bagRoot = H5_CHECK(H5Gcreate(m_hdf5, "/BAG_root", 0));
5763 20 : if (m_bagRoot < 0)
5764 : {
5765 0 : return false;
5766 : }
5767 :
5768 : const char *pszVersion =
5769 20 : CSLFetchNameValueDef(papszOptions, "BAG_VERSION", "1.6.2");
5770 20 : constexpr unsigned knVersionLength = 32;
5771 20 : char szVersion[knVersionLength] = {};
5772 20 : snprintf(szVersion, sizeof(szVersion), "%s", pszVersion);
5773 20 : if (!GH5_CreateAttribute(m_bagRoot, "Bag Version", H5T_C_S1,
5774 40 : knVersionLength) ||
5775 20 : !GH5_WriteAttribute(m_bagRoot, "Bag Version", szVersion))
5776 : {
5777 0 : return false;
5778 : }
5779 :
5780 20 : if (!CreateTrackingListDataset())
5781 : {
5782 0 : return false;
5783 : }
5784 20 : return true;
5785 : }
5786 :
5787 : /************************************************************************/
5788 : /* Create() */
5789 : /************************************************************************/
5790 :
5791 22 : bool BAGCreator::Create(const char *pszFilename, GDALDataset *poSrcDS,
5792 : char **papszOptions, GDALProgressFunc pfnProgress,
5793 : void *pProgressData)
5794 : {
5795 22 : const int nBands = poSrcDS->GetRasterCount();
5796 22 : if (nBands != 1 && nBands != 2)
5797 : {
5798 4 : CPLError(CE_Failure, CPLE_NotSupported,
5799 : "BAG driver doesn't support %d bands. Must be 1 or 2.",
5800 : nBands);
5801 4 : return false;
5802 : }
5803 18 : GDALGeoTransform gt;
5804 18 : if (poSrcDS->GetGeoTransform(gt) != CE_None)
5805 : {
5806 0 : CPLError(CE_Failure, CPLE_NotSupported,
5807 : "BAG driver requires a source dataset with a geotransform");
5808 0 : return false;
5809 : }
5810 18 : if (gt[2] != 0 || gt[4] != 0)
5811 : {
5812 0 : CPLError(CE_Failure, CPLE_NotSupported,
5813 : "BAG driver requires a source dataset with a non-rotated "
5814 : "geotransform");
5815 0 : return false;
5816 : }
5817 :
5818 : CPLString osXMLMetadata =
5819 : GenerateMetadata(poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(),
5820 36 : gt, poSrcDS->GetSpatialRef(), papszOptions);
5821 18 : if (osXMLMetadata.empty())
5822 : {
5823 0 : return false;
5824 : }
5825 :
5826 18 : if (!CreateBase(pszFilename, papszOptions))
5827 : {
5828 2 : return false;
5829 : }
5830 :
5831 16 : if (!CreateAndWriteMetadata(m_hdf5, osXMLMetadata))
5832 : {
5833 0 : return false;
5834 : }
5835 :
5836 16 : void *pScaled = GDALCreateScaledProgress(0, 1. / poSrcDS->GetRasterCount(),
5837 : pfnProgress, pProgressData);
5838 : bool bRet;
5839 16 : bRet = CreateElevationOrUncertainty(
5840 : poSrcDS, 1, "/BAG_root/elevation", "Maximum Elevation Value",
5841 : "Minimum Elevation Value", papszOptions, GDALScaledProgress, pScaled);
5842 16 : GDALDestroyScaledProgress(pScaled);
5843 16 : if (!bRet)
5844 : {
5845 0 : return false;
5846 : }
5847 :
5848 16 : pScaled = GDALCreateScaledProgress(1. / poSrcDS->GetRasterCount(), 1.0,
5849 : pfnProgress, pProgressData);
5850 16 : bRet = CreateElevationOrUncertainty(
5851 : poSrcDS, 2, "/BAG_root/uncertainty", "Maximum Uncertainty Value",
5852 : "Minimum Uncertainty Value", papszOptions, GDALScaledProgress, pScaled);
5853 16 : GDALDestroyScaledProgress(pScaled);
5854 16 : if (!bRet)
5855 : {
5856 0 : return false;
5857 : }
5858 :
5859 16 : return Close();
5860 : }
5861 :
5862 : /************************************************************************/
5863 : /* Create() */
5864 : /************************************************************************/
5865 :
5866 52 : bool BAGCreator::Create(const char *pszFilename, int nBands, GDALDataType eType,
5867 : char **papszOptions)
5868 : {
5869 52 : if (nBands != 1 && nBands != 2)
5870 : {
5871 34 : CPLError(CE_Failure, CPLE_NotSupported,
5872 : "BAG driver doesn't support %d bands. Must be 1 or 2.",
5873 : nBands);
5874 34 : return false;
5875 : }
5876 18 : if (eType != GDT_Float32)
5877 : {
5878 14 : CPLError(CE_Failure, CPLE_NotSupported,
5879 : "BAG driver only supports Float32");
5880 14 : return false;
5881 : }
5882 :
5883 4 : if (!CreateBase(pszFilename, papszOptions))
5884 : {
5885 0 : return false;
5886 : }
5887 :
5888 4 : return Close();
5889 : }
5890 :
5891 : /************************************************************************/
5892 : /* CreateCopy() */
5893 : /************************************************************************/
5894 :
5895 22 : GDALDataset *BAGDataset::CreateCopy(const char *pszFilename,
5896 : GDALDataset *poSrcDS, int /* bStrict */,
5897 : char **papszOptions,
5898 : GDALProgressFunc pfnProgress,
5899 : void *pProgressData)
5900 :
5901 : {
5902 22 : if (!BAGCreator().Create(pszFilename, poSrcDS, papszOptions, pfnProgress,
5903 : pProgressData))
5904 : {
5905 6 : return nullptr;
5906 : }
5907 :
5908 32 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
5909 16 : oOpenInfo.nOpenFlags = GDAL_OF_RASTER;
5910 16 : return Open(&oOpenInfo);
5911 : }
5912 :
5913 : /************************************************************************/
5914 : /* Create() */
5915 : /************************************************************************/
5916 :
5917 52 : GDALDataset *BAGDataset::Create(const char *pszFilename, int nXSize, int nYSize,
5918 : int nBandsIn, GDALDataType eType,
5919 : char **papszOptions)
5920 : {
5921 52 : if (!BAGCreator().Create(pszFilename, nBandsIn, eType, papszOptions))
5922 : {
5923 48 : return nullptr;
5924 : }
5925 :
5926 8 : GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
5927 4 : oOpenInfo.nOpenFlags = GDAL_OF_RASTER;
5928 4 : return OpenForCreate(&oOpenInfo, nXSize, nYSize, nBandsIn, papszOptions);
5929 : }
5930 :
5931 : /************************************************************************/
5932 : /* GDALRegister_BAG() */
5933 : /************************************************************************/
5934 14 : void GDALRegister_BAG()
5935 :
5936 : {
5937 14 : if (!GDAL_CHECK_VERSION("BAG"))
5938 0 : return;
5939 :
5940 14 : if (GDALGetDriverByName(BAG_DRIVER_NAME) != nullptr)
5941 0 : return;
5942 :
5943 14 : GDALDriver *poDriver = new GDALDriver();
5944 :
5945 14 : BAGDriverSetCommonMetadata(poDriver);
5946 :
5947 14 : poDriver->pfnOpen = BAGDataset::Open;
5948 14 : poDriver->pfnUnloadDriver = BAGDatasetDriverUnload;
5949 14 : poDriver->pfnCreateCopy = BAGDataset::CreateCopy;
5950 14 : poDriver->pfnCreate = BAGDataset::Create;
5951 :
5952 14 : GetGDALDriverManager()->RegisterDriver(poDriver);
5953 : }
|