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