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