Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Hierarchical Data Format Release 5 (HDF5)
4 : * Purpose: Read S102 bathymetric datasets.
5 : * Author: Even Rouault <even dot rouault at spatialys dot com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys dot com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "hdf5dataset.h"
15 : #include "hdf5drivercore.h"
16 : #include "gh5_convenience.h"
17 : #include "rat.h"
18 : #include "s100.h"
19 :
20 : #include "gdal_priv.h"
21 : #include "gdal_proxy.h"
22 : #include "gdal_rat.h"
23 :
24 : #include <cmath>
25 : #include <limits>
26 :
27 : /************************************************************************/
28 : /* S102Dataset */
29 : /************************************************************************/
30 :
31 : class S102Dataset final : public S100BaseDataset
32 : {
33 : bool OpenQuality(GDALOpenInfo *poOpenInfo,
34 : const std::shared_ptr<GDALGroup> &poRootGroup);
35 :
36 : public:
37 22 : explicit S102Dataset(const std::string &osFilename)
38 22 : : S100BaseDataset(osFilename)
39 : {
40 22 : }
41 :
42 : static GDALDataset *Open(GDALOpenInfo *);
43 : };
44 :
45 : /************************************************************************/
46 : /* S102RasterBand */
47 : /************************************************************************/
48 :
49 : class S102RasterBand : public GDALProxyRasterBand
50 : {
51 : friend class S102Dataset;
52 : std::unique_ptr<GDALDataset> m_poDS{};
53 : GDALRasterBand *m_poUnderlyingBand = nullptr;
54 : double m_dfMinimum = std::numeric_limits<double>::quiet_NaN();
55 : double m_dfMaximum = std::numeric_limits<double>::quiet_NaN();
56 :
57 : public:
58 31 : explicit S102RasterBand(std::unique_ptr<GDALDataset> &&poDSIn)
59 62 : : m_poDS(std::move(poDSIn)),
60 31 : m_poUnderlyingBand(m_poDS->GetRasterBand(1))
61 : {
62 31 : eDataType = m_poUnderlyingBand->GetRasterDataType();
63 31 : m_poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
64 31 : }
65 :
66 : GDALRasterBand *
67 23 : RefUnderlyingRasterBand(bool /*bForceOpen*/ = true) const override
68 : {
69 23 : return m_poUnderlyingBand;
70 : }
71 :
72 8 : double GetMinimum(int *pbSuccess = nullptr) override
73 : {
74 8 : if (pbSuccess)
75 8 : *pbSuccess = !std::isnan(m_dfMinimum);
76 8 : return m_dfMinimum;
77 : }
78 :
79 8 : double GetMaximum(int *pbSuccess = nullptr) override
80 : {
81 8 : if (pbSuccess)
82 8 : *pbSuccess = !std::isnan(m_dfMaximum);
83 8 : return m_dfMaximum;
84 : }
85 :
86 4 : const char *GetUnitType() override
87 : {
88 4 : return "metre";
89 : }
90 : };
91 :
92 : /************************************************************************/
93 : /* S102GeoreferencedMetadataRasterBand */
94 : /************************************************************************/
95 :
96 : class S102GeoreferencedMetadataRasterBand : public GDALProxyRasterBand
97 : {
98 : friend class S102Dataset;
99 :
100 : std::unique_ptr<GDALDataset> m_poDS{};
101 : GDALRasterBand *m_poUnderlyingBand = nullptr;
102 : std::unique_ptr<GDALRasterAttributeTable> m_poRAT{};
103 :
104 : public:
105 4 : explicit S102GeoreferencedMetadataRasterBand(
106 : std::unique_ptr<GDALDataset> &&poDSIn,
107 : std::unique_ptr<GDALRasterAttributeTable> &&poRAT)
108 8 : : m_poDS(std::move(poDSIn)),
109 4 : m_poUnderlyingBand(m_poDS->GetRasterBand(1)),
110 8 : m_poRAT(std::move(poRAT))
111 : {
112 4 : eDataType = m_poUnderlyingBand->GetRasterDataType();
113 4 : m_poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
114 4 : }
115 :
116 : GDALRasterBand *
117 6 : RefUnderlyingRasterBand(bool /*bForceOpen*/ = true) const override
118 : {
119 6 : return m_poUnderlyingBand;
120 : }
121 :
122 2 : GDALRasterAttributeTable *GetDefaultRAT() override
123 : {
124 2 : return m_poRAT.get();
125 : }
126 : };
127 :
128 : /************************************************************************/
129 : /* Open() */
130 : /************************************************************************/
131 :
132 26 : GDALDataset *S102Dataset::Open(GDALOpenInfo *poOpenInfo)
133 :
134 : {
135 : // Confirm that this appears to be a S102 file.
136 26 : if (!S102DatasetIdentify(poOpenInfo))
137 0 : return nullptr;
138 :
139 : HDF5_GLOBAL_LOCK();
140 :
141 26 : if (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)
142 : {
143 2 : return HDF5Dataset::OpenMultiDim(poOpenInfo);
144 : }
145 :
146 : // Confirm the requested access is supported.
147 24 : if (poOpenInfo->eAccess == GA_Update)
148 : {
149 0 : ReportUpdateNotSupportedByDriver("S102");
150 0 : return nullptr;
151 : }
152 :
153 48 : std::string osFilename(poOpenInfo->pszFilename);
154 24 : bool bIsSubdataset = false;
155 24 : bool bIsQuality = false;
156 24 : if (STARTS_WITH(poOpenInfo->pszFilename, "S102:"))
157 : {
158 : const CPLStringList aosTokens(
159 11 : CSLTokenizeString2(poOpenInfo->pszFilename, ":",
160 11 : CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
161 :
162 11 : if (aosTokens.size() == 2)
163 : {
164 1 : osFilename = aosTokens[1];
165 : }
166 10 : else if (aosTokens.size() == 3)
167 : {
168 10 : bIsSubdataset = true;
169 10 : osFilename = aosTokens[1];
170 10 : if (EQUAL(aosTokens[2], "BathymetryCoverage"))
171 : {
172 : // Default dataset
173 : }
174 12 : else if (EQUAL(aosTokens[2], "QualityOfSurvey") || // < v3
175 4 : EQUAL(aosTokens[2], "QualityOfBathymetryCoverage")) // v3
176 : {
177 6 : bIsQuality = true;
178 : }
179 : else
180 : {
181 2 : CPLError(CE_Failure, CPLE_NotSupported,
182 : "Unsupported subdataset component: '%s'. Expected "
183 : "'QualityOfSurvey'",
184 : aosTokens[2]);
185 2 : return nullptr;
186 : }
187 : }
188 : else
189 : {
190 0 : return nullptr;
191 : }
192 : }
193 :
194 44 : auto poDS = std::make_unique<S102Dataset>(osFilename);
195 22 : if (!poDS->Init())
196 0 : return nullptr;
197 :
198 22 : const auto &poRootGroup = poDS->m_poRootGroup;
199 : auto poBathymetryCoverage01 = poRootGroup->OpenGroupFromFullname(
200 66 : "/BathymetryCoverage/BathymetryCoverage.01");
201 22 : if (!poBathymetryCoverage01)
202 : {
203 0 : CPLError(CE_Failure, CPLE_AppDefined,
204 : "S102: Cannot find /BathymetryCoverage/BathymetryCoverage.01");
205 0 : return nullptr;
206 : }
207 :
208 22 : const bool bNorthUp = CPLTestBool(
209 22 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "NORTH_UP", "YES"));
210 :
211 22 : if (bIsQuality)
212 : {
213 6 : if (!poDS->OpenQuality(poOpenInfo, poRootGroup))
214 2 : return nullptr;
215 :
216 : // Setup/check for pam .aux.xml.
217 4 : poDS->SetDescription(osFilename.c_str());
218 4 : poDS->TryLoadXML();
219 :
220 : // Setup overviews.
221 4 : poDS->oOvManager.Initialize(poDS.get(), osFilename.c_str());
222 :
223 4 : return poDS.release();
224 : }
225 :
226 : // Compute geotransform
227 16 : poDS->m_bHasGT = S100GetGeoTransform(poBathymetryCoverage01.get(),
228 16 : poDS->m_adfGeoTransform, bNorthUp);
229 :
230 48 : auto poGroup001 = poBathymetryCoverage01->OpenGroup("Group_001");
231 16 : if (!poGroup001)
232 : {
233 0 : CPLError(CE_Failure, CPLE_AppDefined,
234 : "S102: Cannot find "
235 : "/BathymetryCoverage/BathymetryCoverage.01/Group_001");
236 0 : return nullptr;
237 : }
238 48 : auto poValuesArray = poGroup001->OpenMDArray("values");
239 16 : if (!poValuesArray || poValuesArray->GetDimensionCount() != 2)
240 : {
241 0 : CPLError(CE_Failure, CPLE_AppDefined,
242 : "S102: Cannot find "
243 : "/BathymetryCoverage/BathymetryCoverage.01/Group_001/values");
244 0 : return nullptr;
245 : }
246 16 : const auto &oType = poValuesArray->GetDataType();
247 16 : if (oType.GetClass() != GEDTC_COMPOUND)
248 : {
249 0 : CPLError(CE_Failure, CPLE_AppDefined,
250 : "S102: Wrong type for "
251 : "/BathymetryCoverage/BathymetryCoverage.01/Group_001/values");
252 0 : return nullptr;
253 : }
254 16 : const auto &oComponents = oType.GetComponents();
255 16 : if (oComponents.size() == 0 || oComponents[0]->GetName() != "depth")
256 : {
257 0 : CPLError(CE_Failure, CPLE_AppDefined,
258 : "S102: Wrong type for "
259 : "/BathymetryCoverage/BathymetryCoverage.01/Group_001/values");
260 0 : return nullptr;
261 : }
262 :
263 16 : if (bNorthUp)
264 15 : poValuesArray = poValuesArray->GetView("[::-1,...]");
265 :
266 48 : auto poDepth = poValuesArray->GetView("[\"depth\"]");
267 :
268 : // Mandatory in v2.2
269 16 : bool bCSIsElevation = false;
270 48 : auto poVerticalCS = poRootGroup->GetAttribute("verticalCS");
271 16 : if (poVerticalCS && poVerticalCS->GetDataType().GetClass() == GEDTC_NUMERIC)
272 : {
273 6 : const auto nVal = poVerticalCS->ReadAsInt();
274 6 : if (nVal == 6498) // Depth metre
275 : {
276 : // nothing to do
277 : }
278 0 : else if (nVal == 6499) // Height metre
279 : {
280 0 : bCSIsElevation = true;
281 : }
282 : else
283 : {
284 0 : CPLError(CE_Warning, CPLE_NotSupported, "Unsupported verticalCS=%d",
285 : nVal);
286 : }
287 : }
288 :
289 : const bool bUseElevation =
290 16 : EQUAL(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
291 : "DEPTH_OR_ELEVATION", "DEPTH"),
292 : "ELEVATION");
293 31 : const bool bInvertDepth = (bUseElevation && !bCSIsElevation) ||
294 15 : (!bUseElevation && bCSIsElevation);
295 16 : const double dfDepthNoData = poDepth->GetNoDataValueAsDouble();
296 33 : auto poDepthDS = [&poDepth, bInvertDepth, dfDepthNoData]()
297 : {
298 16 : if (bInvertDepth)
299 : {
300 1 : auto poInverted = poDepth->GetUnscaled(-1, 0, dfDepthNoData);
301 : return std::unique_ptr<GDALDataset>(
302 1 : poInverted->AsClassicDataset(1, 0));
303 : }
304 : else
305 : {
306 : return std::unique_ptr<GDALDataset>(
307 15 : poDepth->AsClassicDataset(1, 0));
308 : }
309 32 : }();
310 :
311 16 : poDS->nRasterXSize = poDepthDS->GetRasterXSize();
312 16 : poDS->nRasterYSize = poDepthDS->GetRasterYSize();
313 :
314 : // Create depth (or elevation) band
315 16 : auto poDepthBand = new S102RasterBand(std::move(poDepthDS));
316 16 : poDepthBand->SetDescription(bUseElevation ? "elevation" : "depth");
317 :
318 48 : auto poMinimumDepth = poGroup001->GetAttribute("minimumDepth");
319 32 : if (poMinimumDepth &&
320 32 : poMinimumDepth->GetDataType().GetClass() == GEDTC_NUMERIC)
321 : {
322 16 : const double dfVal = poMinimumDepth->ReadAsDouble();
323 16 : if (dfVal != dfDepthNoData)
324 : {
325 13 : if (bInvertDepth)
326 1 : poDepthBand->m_dfMaximum = -dfVal;
327 : else
328 12 : poDepthBand->m_dfMinimum = dfVal;
329 : }
330 : }
331 :
332 48 : auto poMaximumDepth = poGroup001->GetAttribute("maximumDepth");
333 32 : if (poMaximumDepth &&
334 32 : poMaximumDepth->GetDataType().GetClass() == GEDTC_NUMERIC)
335 : {
336 16 : const double dfVal = poMaximumDepth->ReadAsDouble();
337 16 : if (dfVal != dfDepthNoData)
338 : {
339 16 : if (bInvertDepth)
340 1 : poDepthBand->m_dfMinimum = -dfVal;
341 : else
342 15 : poDepthBand->m_dfMaximum = dfVal;
343 : }
344 : }
345 :
346 16 : poDS->SetBand(1, poDepthBand);
347 :
348 : const bool bHasUncertainty =
349 16 : oComponents.size() >= 2 && oComponents[1]->GetName() == "uncertainty";
350 16 : if (bHasUncertainty)
351 : {
352 : // Create uncertainty band
353 45 : auto poUncertainty = poValuesArray->GetView("[\"uncertainty\"]");
354 : const double dfUncertaintyNoData =
355 15 : poUncertainty->GetNoDataValueAsDouble();
356 : auto poUncertaintyDS =
357 30 : std::unique_ptr<GDALDataset>(poUncertainty->AsClassicDataset(1, 0));
358 :
359 15 : auto poUncertaintyBand = new S102RasterBand(std::move(poUncertaintyDS));
360 15 : poUncertaintyBand->SetDescription("uncertainty");
361 :
362 : auto poMinimumUncertainty =
363 45 : poGroup001->GetAttribute("minimumUncertainty");
364 30 : if (poMinimumUncertainty &&
365 30 : poMinimumUncertainty->GetDataType().GetClass() == GEDTC_NUMERIC)
366 : {
367 15 : const double dfVal = poMinimumUncertainty->ReadAsDouble();
368 15 : if (dfVal != dfUncertaintyNoData)
369 : {
370 15 : poUncertaintyBand->m_dfMinimum = dfVal;
371 : }
372 : }
373 :
374 : auto poMaximumUncertainty =
375 45 : poGroup001->GetAttribute("maximumUncertainty");
376 30 : if (poMaximumUncertainty &&
377 30 : poMaximumUncertainty->GetDataType().GetClass() == GEDTC_NUMERIC)
378 : {
379 15 : const double dfVal = poMaximumUncertainty->ReadAsDouble();
380 15 : if (dfVal != dfUncertaintyNoData)
381 : {
382 15 : poUncertaintyBand->m_dfMaximum = dfVal;
383 : }
384 : }
385 :
386 15 : poDS->SetBand(2, poUncertaintyBand);
387 : }
388 :
389 16 : poDS->GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
390 :
391 48 : auto poGroupQuality = poRootGroup->OpenGroup("QualityOfSurvey");
392 16 : const bool bIsNamedQualityOfSurvey = poGroupQuality != nullptr;
393 16 : if (!bIsNamedQualityOfSurvey)
394 : {
395 : // S102 v3 now uses QualityOfBathymetryCoverage instead of QualityOfSurvey
396 14 : poGroupQuality = poRootGroup->OpenGroup("QualityOfBathymetryCoverage");
397 : }
398 16 : if (!bIsSubdataset && poGroupQuality)
399 : {
400 3 : const char *pszNameOfQualityGroup = bIsNamedQualityOfSurvey
401 3 : ? "QualityOfSurvey"
402 : : "QualityOfBathymetryCoverage";
403 3 : auto poGroupQuality01 = poGroupQuality->OpenGroup(
404 9 : CPLSPrintf("%s.01", pszNameOfQualityGroup));
405 3 : if (poGroupQuality01)
406 : {
407 3 : poDS->GDALDataset::SetMetadataItem(
408 : "SUBDATASET_1_NAME",
409 : CPLSPrintf("S102:\"%s\":BathymetryCoverage",
410 : osFilename.c_str()),
411 : "SUBDATASETS");
412 3 : poDS->GDALDataset::SetMetadataItem(
413 : "SUBDATASET_1_DESC", "Bathymetric gridded data", "SUBDATASETS");
414 :
415 3 : poDS->GDALDataset::SetMetadataItem(
416 : "SUBDATASET_2_NAME",
417 : CPLSPrintf("S102:\"%s\":%s", osFilename.c_str(),
418 : pszNameOfQualityGroup),
419 : "SUBDATASETS");
420 3 : poDS->GDALDataset::SetMetadataItem(
421 : "SUBDATASET_2_DESC",
422 : CPLSPrintf("Georeferenced metadata %s", pszNameOfQualityGroup),
423 : "SUBDATASETS");
424 : }
425 : }
426 :
427 : // Setup/check for pam .aux.xml.
428 16 : poDS->SetDescription(osFilename.c_str());
429 16 : poDS->TryLoadXML();
430 :
431 : // Setup overviews.
432 16 : poDS->oOvManager.Initialize(poDS.get(), osFilename.c_str());
433 :
434 16 : return poDS.release();
435 : }
436 :
437 : /************************************************************************/
438 : /* OpenQuality() */
439 : /************************************************************************/
440 :
441 6 : bool S102Dataset::OpenQuality(GDALOpenInfo *poOpenInfo,
442 : const std::shared_ptr<GDALGroup> &poRootGroup)
443 : {
444 6 : const bool bNorthUp = CPLTestBool(
445 6 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "NORTH_UP", "YES"));
446 :
447 6 : const char *pszNameOfQualityGroup = "QualityOfSurvey";
448 18 : auto poGroupQuality = poRootGroup->OpenGroup(pszNameOfQualityGroup);
449 6 : if (!poGroupQuality)
450 : {
451 4 : pszNameOfQualityGroup = "QualityOfBathymetryCoverage";
452 4 : poGroupQuality = poRootGroup->OpenGroup(pszNameOfQualityGroup);
453 4 : if (!poGroupQuality)
454 : {
455 2 : CPLError(CE_Failure, CPLE_AppDefined,
456 : "Cannot find group /QualityOfSurvey or "
457 : "/QualityOfBathymetryCoverage");
458 2 : return false;
459 : }
460 : }
461 :
462 : const std::string osQuality01Name =
463 12 : std::string(pszNameOfQualityGroup).append(".01");
464 8 : const std::string osQuality01FullName = std::string("/")
465 4 : .append(pszNameOfQualityGroup)
466 4 : .append("/")
467 8 : .append(osQuality01Name);
468 8 : auto poGroupQuality01 = poGroupQuality->OpenGroup(osQuality01Name);
469 4 : if (!poGroupQuality01)
470 : {
471 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
472 : osQuality01FullName.c_str());
473 0 : return false;
474 : }
475 :
476 8 : if (auto poStartSequence = poGroupQuality01->GetAttribute("startSequence"))
477 : {
478 0 : const char *pszStartSequence = poStartSequence->ReadAsString();
479 0 : if (pszStartSequence && !EQUAL(pszStartSequence, "0,0"))
480 : {
481 0 : CPLError(CE_Failure, CPLE_AppDefined,
482 : "startSequence (=%s) != 0,0 is not supported",
483 : pszStartSequence);
484 0 : return false;
485 : }
486 : }
487 :
488 : // Compute geotransform
489 4 : m_bHasGT = S100GetGeoTransform(poGroupQuality01.get(), m_adfGeoTransform,
490 : bNorthUp);
491 :
492 12 : auto poGroup001 = poGroupQuality01->OpenGroup("Group_001");
493 4 : if (!poGroup001)
494 : {
495 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s/Group_001",
496 : osQuality01FullName.c_str());
497 0 : return false;
498 : }
499 :
500 12 : auto poValuesArray = poGroup001->OpenMDArray("values");
501 4 : if (!poValuesArray)
502 : {
503 0 : CPLError(CE_Failure, CPLE_AppDefined,
504 : "Cannot find array "
505 : "%s/Group_001/values",
506 : osQuality01FullName.c_str());
507 0 : return false;
508 : }
509 :
510 : {
511 4 : const auto &oType = poValuesArray->GetDataType();
512 6 : if (oType.GetClass() == GEDTC_NUMERIC &&
513 2 : oType.GetNumericDataType() == GDT_UInt32)
514 : {
515 : // ok
516 : }
517 4 : else if (oType.GetClass() == GEDTC_COMPOUND &&
518 4 : oType.GetComponents().size() == 1 &&
519 2 : oType.GetComponents()[0]->GetType().GetClass() ==
520 4 : GEDTC_NUMERIC &&
521 2 : oType.GetComponents()[0]->GetType().GetNumericDataType() ==
522 : GDT_UInt32)
523 : {
524 : // seen in a S102 v3 product (102DE00CA22_UNC_MD.H5), although
525 : // I believe this is non-conformant.
526 :
527 : // Escape potentials single-quote and double-quote with back-slash
528 2 : CPLString osEscapedCompName(oType.GetComponents()[0]->GetName());
529 4 : osEscapedCompName.replaceAll("\\", "\\\\")
530 4 : .replaceAll("'", "\\'")
531 2 : .replaceAll("\"", "\\\"");
532 :
533 : // Gets a view with that single component extracted.
534 4 : poValuesArray = poValuesArray->GetView(
535 4 : std::string("['").append(osEscapedCompName).append("']"));
536 2 : if (!poValuesArray)
537 0 : return false;
538 : }
539 : else
540 : {
541 0 : CPLError(CE_Failure, CPLE_NotSupported,
542 : "Unsupported data type for %s",
543 0 : poValuesArray->GetFullName().c_str());
544 0 : return false;
545 : }
546 : }
547 :
548 4 : if (poValuesArray->GetDimensionCount() != 2)
549 : {
550 0 : CPLError(CE_Failure, CPLE_NotSupported,
551 : "Unsupported number of dimensions for %s",
552 0 : poValuesArray->GetFullName().c_str());
553 0 : return false;
554 : }
555 :
556 : auto poFeatureAttributeTable =
557 12 : poGroupQuality->OpenMDArray("featureAttributeTable");
558 4 : if (!poFeatureAttributeTable)
559 : {
560 0 : CPLError(CE_Failure, CPLE_AppDefined,
561 : "Cannot find array /%s/featureAttributeTable",
562 : pszNameOfQualityGroup);
563 0 : return false;
564 : }
565 :
566 : {
567 4 : const auto &oType = poFeatureAttributeTable->GetDataType();
568 4 : if (oType.GetClass() != GEDTC_COMPOUND)
569 : {
570 0 : CPLError(CE_Failure, CPLE_NotSupported,
571 : "Unsupported data type for %s",
572 0 : poFeatureAttributeTable->GetFullName().c_str());
573 0 : return false;
574 : }
575 :
576 4 : const auto &poComponents = oType.GetComponents();
577 4 : if (poComponents.size() >= 1 && poComponents[0]->GetName() != "id")
578 : {
579 0 : CPLError(CE_Failure, CPLE_AppDefined,
580 : "Missing 'id' component in %s",
581 0 : poFeatureAttributeTable->GetFullName().c_str());
582 0 : return false;
583 : }
584 : }
585 :
586 4 : if (bNorthUp)
587 2 : poValuesArray = poValuesArray->GetView("[::-1,...]");
588 :
589 : auto poDS =
590 8 : std::unique_ptr<GDALDataset>(poValuesArray->AsClassicDataset(1, 0));
591 4 : if (!poDS)
592 0 : return false;
593 :
594 4 : nRasterXSize = poDS->GetRasterXSize();
595 4 : nRasterYSize = poDS->GetRasterYSize();
596 :
597 : auto poRAT =
598 8 : HDF5CreateRAT(poFeatureAttributeTable, /* bFirstColIsMinMax = */ true);
599 : auto poBand = std::make_unique<S102GeoreferencedMetadataRasterBand>(
600 4 : std::move(poDS), std::move(poRAT));
601 4 : SetBand(1, poBand.release());
602 :
603 4 : return true;
604 : }
605 :
606 : /************************************************************************/
607 : /* S102DatasetDriverUnload() */
608 : /************************************************************************/
609 :
610 6 : static void S102DatasetDriverUnload(GDALDriver *)
611 : {
612 6 : HDF5UnloadFileDriver();
613 6 : }
614 :
615 : /************************************************************************/
616 : /* GDALRegister_S102() */
617 : /************************************************************************/
618 11 : void GDALRegister_S102()
619 :
620 : {
621 11 : if (!GDAL_CHECK_VERSION("S102"))
622 0 : return;
623 :
624 11 : if (GDALGetDriverByName(S102_DRIVER_NAME) != nullptr)
625 0 : return;
626 :
627 11 : GDALDriver *poDriver = new GDALDriver();
628 :
629 11 : S102DriverSetCommonMetadata(poDriver);
630 11 : poDriver->pfnOpen = S102Dataset::Open;
631 11 : poDriver->pfnUnloadDriver = S102DatasetDriverUnload;
632 :
633 11 : GetGDALDriverManager()->RegisterDriver(poDriver);
634 : }
|