Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Hierarchical Data Format Release 5 (HDF5)
4 : * Purpose: Read S111 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_frmts.h"
21 : #include "gdal_priv.h"
22 : #include "gdal_proxy.h"
23 : #include "gdal_rat.h"
24 :
25 : #include <limits>
26 : #include <map>
27 :
28 : /************************************************************************/
29 : /* S111Dataset */
30 : /************************************************************************/
31 :
32 14 : class S111Dataset final : public S100BaseDataset
33 : {
34 : public:
35 7 : explicit S111Dataset(const std::string &osFilename)
36 7 : : S100BaseDataset(osFilename)
37 : {
38 7 : }
39 :
40 : ~S111Dataset() override;
41 :
42 : static GDALDataset *Open(GDALOpenInfo *);
43 : };
44 :
45 : S111Dataset::~S111Dataset() = default;
46 :
47 : /************************************************************************/
48 : /* S111RasterBand */
49 : /************************************************************************/
50 :
51 : class S111RasterBand final : public GDALProxyRasterBand
52 : {
53 : friend class S111Dataset;
54 : std::unique_ptr<GDALDataset> m_poDS{};
55 : GDALRasterBand *m_poUnderlyingBand = nullptr;
56 : std::string m_osUnitType{};
57 : std::unique_ptr<GDALRasterAttributeTable> m_poRAT{};
58 :
59 : CPL_DISALLOW_COPY_ASSIGN(S111RasterBand)
60 :
61 : public:
62 8 : explicit S111RasterBand(std::unique_ptr<GDALDataset> &&poDSIn)
63 16 : : m_poDS(std::move(poDSIn)),
64 8 : m_poUnderlyingBand(m_poDS->GetRasterBand(1))
65 : {
66 8 : eDataType = m_poUnderlyingBand->GetRasterDataType();
67 8 : m_poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
68 8 : }
69 :
70 : GDALRasterBand *
71 : RefUnderlyingRasterBand(bool /*bForceOpen*/ = true) const override;
72 :
73 4 : const char *GetUnitType() override
74 : {
75 4 : return m_osUnitType.c_str();
76 : }
77 :
78 1 : GDALRasterAttributeTable *GetDefaultRAT() override
79 : {
80 1 : return m_poRAT.get();
81 : }
82 :
83 0 : char **GetMetadata(const char *pszDomain) override
84 : {
85 : // Short-circuit GDALProxyRasterBand...
86 0 : return GDALRasterBand::GetMetadata(pszDomain);
87 : }
88 : };
89 :
90 : GDALRasterBand *
91 12 : S111RasterBand::RefUnderlyingRasterBand(bool /*bForceOpen*/) const
92 : {
93 12 : return m_poUnderlyingBand;
94 : }
95 :
96 : /************************************************************************/
97 : /* Open() */
98 : /************************************************************************/
99 :
100 8 : GDALDataset *S111Dataset::Open(GDALOpenInfo *poOpenInfo)
101 :
102 : {
103 : // Confirm that this appears to be a S111 file.
104 8 : if (!S111DatasetIdentify(poOpenInfo))
105 0 : return nullptr;
106 :
107 : HDF5_GLOBAL_LOCK();
108 :
109 8 : if (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)
110 : {
111 1 : return HDF5Dataset::OpenMultiDim(poOpenInfo);
112 : }
113 :
114 : // Confirm the requested access is supported.
115 7 : if (poOpenInfo->eAccess == GA_Update)
116 : {
117 0 : ReportUpdateNotSupportedByDriver("S111");
118 0 : return nullptr;
119 : }
120 :
121 14 : std::string osFilename(poOpenInfo->pszFilename);
122 14 : std::string osFeatureInstance = "SurfaceCurrent.01";
123 14 : std::string osGroup;
124 7 : if (STARTS_WITH(poOpenInfo->pszFilename, "S111:"))
125 : {
126 : const CPLStringList aosTokens(
127 5 : CSLTokenizeString2(poOpenInfo->pszFilename, ":",
128 5 : CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
129 :
130 5 : if (aosTokens.size() == 2)
131 : {
132 0 : osFilename = aosTokens[1];
133 : }
134 5 : else if (aosTokens.size() == 3)
135 : {
136 3 : osFilename = aosTokens[1];
137 3 : osGroup = aosTokens[2];
138 : }
139 2 : else if (aosTokens.size() == 4)
140 : {
141 2 : osFilename = aosTokens[1];
142 2 : osFeatureInstance = aosTokens[2];
143 2 : osGroup = aosTokens[3];
144 : }
145 : else
146 : {
147 0 : return nullptr;
148 : }
149 : }
150 :
151 14 : auto poDS = std::make_unique<S111Dataset>(osFilename);
152 7 : if (!poDS->Init())
153 0 : return nullptr;
154 :
155 7 : const auto &poRootGroup = poDS->m_poRootGroup;
156 :
157 21 : auto poVerticalCS = poRootGroup->GetAttribute("verticalCS");
158 7 : if (poVerticalCS && poVerticalCS->GetDataType().GetClass() == GEDTC_NUMERIC)
159 : {
160 7 : const int nVerticalCS = poVerticalCS->ReadAsInt();
161 7 : if (nVerticalCS == 6498)
162 7 : poDS->GDALDataset::SetMetadataItem(
163 : "VERTICAL_CS_MEANING", "depth, meters, orientation down");
164 0 : else if (nVerticalCS == 6499)
165 0 : poDS->GDALDataset::SetMetadataItem(
166 : "VERTICAL_CS_MEANING", "height, meters, orientation up");
167 :
168 14 : poDS->GDALDataset::SetMetadataItem("verticalCS",
169 14 : std::to_string(nVerticalCS).c_str());
170 : }
171 :
172 21 : auto poSurfaceCurrent = poRootGroup->OpenGroup("SurfaceCurrent");
173 7 : if (!poSurfaceCurrent)
174 : {
175 0 : CPLError(CE_Failure, CPLE_AppDefined,
176 : "Cannot find /SurfaceCurrent group");
177 0 : return nullptr;
178 : }
179 :
180 : auto poDataCodingFormat =
181 21 : poSurfaceCurrent->GetAttribute("dataCodingFormat");
182 14 : if (!poDataCodingFormat ||
183 7 : poDataCodingFormat->GetDataType().GetClass() != GEDTC_NUMERIC)
184 : {
185 0 : CPLError(CE_Failure, CPLE_AppDefined,
186 : "Cannot find /SurfaceCurrent/dataCodingFormat attribute");
187 0 : return nullptr;
188 : }
189 7 : const int nDataCodingFormat = poDataCodingFormat->ReadAsInt();
190 7 : if (nDataCodingFormat != 2)
191 : {
192 0 : CPLError(CE_Failure, CPLE_NotSupported,
193 : "dataCodingFormat=%d is not supported by the S111 driver",
194 : nDataCodingFormat);
195 0 : return nullptr;
196 : }
197 :
198 : // Read additional metadata
199 21 : for (const char *pszAttrName :
200 : {"methodCurrentsProduct", "minDatasetCurrentSpeed",
201 28 : "maxDatasetCurrentSpeed"})
202 : {
203 63 : auto poAttr = poSurfaceCurrent->GetAttribute(pszAttrName);
204 21 : if (poAttr)
205 : {
206 14 : const char *pszVal = poAttr->ReadAsString();
207 14 : if (pszVal)
208 : {
209 14 : poDS->GDALDataset::SetMetadataItem(pszAttrName, pszVal);
210 : }
211 : }
212 : }
213 :
214 7 : int nNumInstances = 1;
215 7 : if (osGroup.empty())
216 : {
217 6 : auto poNumInstances = poSurfaceCurrent->GetAttribute("numInstances");
218 3 : if (poNumInstances &&
219 3 : poNumInstances->GetDataType().GetClass() == GEDTC_NUMERIC)
220 : {
221 1 : nNumInstances = poNumInstances->ReadAsInt();
222 : }
223 : }
224 7 : if (nNumInstances != 1)
225 : {
226 2 : CPLStringList aosSubDSList;
227 1 : int iSubDS = 0;
228 2 : for (const std::string &featureInstanceName :
229 5 : poSurfaceCurrent->GetGroupNames())
230 : {
231 : auto poFeatureInstance =
232 4 : poSurfaceCurrent->OpenGroup(featureInstanceName);
233 2 : if (poFeatureInstance)
234 : {
235 4 : GDALMajorObject mo;
236 : // Read first vertical datum from root group and let the
237 : // coverage override it.
238 2 : S100ReadVerticalDatum(&mo, poRootGroup.get());
239 2 : S100ReadVerticalDatum(&mo, poFeatureInstance.get());
240 :
241 4 : const auto aosGroupNames = poFeatureInstance->GetGroupNames();
242 4 : for (const auto &osSubGroup : aosGroupNames)
243 : {
244 2 : if (auto poSubGroup =
245 4 : poFeatureInstance->OpenGroup(osSubGroup))
246 : {
247 2 : ++iSubDS;
248 : aosSubDSList.SetNameValue(
249 : CPLSPrintf("SUBDATASET_%d_NAME", iSubDS),
250 : CPLSPrintf("S111:\"%s\":%s:%s", osFilename.c_str(),
251 : featureInstanceName.c_str(),
252 2 : osSubGroup.c_str()));
253 :
254 4 : std::string verticalDatum;
255 : const char *pszValue =
256 2 : mo.GetMetadataItem(S100_VERTICAL_DATUM_MEANING);
257 2 : if (pszValue)
258 : {
259 2 : verticalDatum = ", vertical datum ";
260 2 : verticalDatum += pszValue;
261 : pszValue =
262 2 : mo.GetMetadataItem(S100_VERTICAL_DATUM_ABBREV);
263 2 : if (pszValue)
264 : {
265 2 : verticalDatum += " (";
266 2 : verticalDatum += pszValue;
267 2 : verticalDatum += ')';
268 : }
269 : }
270 : else
271 : {
272 : pszValue =
273 0 : mo.GetMetadataItem(S100_VERTICAL_DATUM_NAME);
274 0 : if (pszValue)
275 : {
276 0 : verticalDatum = ", vertical datum ";
277 0 : verticalDatum += pszValue;
278 : }
279 : }
280 :
281 4 : std::string osSubDSDesc;
282 : const auto poTimePoint =
283 6 : poSubGroup->GetAttribute("timePoint");
284 2 : if (poTimePoint)
285 : {
286 2 : const char *pszVal = poTimePoint->ReadAsString();
287 2 : if (pszVal)
288 : {
289 2 : osSubDSDesc = "Values for feature instance ";
290 2 : osSubDSDesc += featureInstanceName;
291 2 : osSubDSDesc += verticalDatum;
292 2 : osSubDSDesc += " at timestamp ";
293 2 : osSubDSDesc += pszVal;
294 : }
295 : }
296 2 : if (osSubDSDesc.empty())
297 : {
298 0 : osSubDSDesc = "Values for feature instance ";
299 0 : osSubDSDesc += featureInstanceName;
300 0 : osSubDSDesc += verticalDatum;
301 0 : osSubDSDesc += " and group ";
302 0 : osSubDSDesc += osSubGroup;
303 : }
304 :
305 : aosSubDSList.SetNameValue(
306 : CPLSPrintf("SUBDATASET_%d_DESC", iSubDS),
307 2 : osSubDSDesc.c_str());
308 : }
309 : }
310 : }
311 : }
312 :
313 1 : poDS->GDALDataset::SetMetadata(aosSubDSList.List(), "SUBDATASETS");
314 :
315 : // Setup/check for pam .aux.xml.
316 1 : poDS->SetDescription(osFilename.c_str());
317 1 : poDS->TryLoadXML();
318 :
319 : // Setup overviews.
320 1 : poDS->oOvManager.Initialize(poDS.get(), osFilename.c_str());
321 :
322 1 : return poDS.release();
323 : }
324 :
325 12 : auto poFeatureInstance = poSurfaceCurrent->OpenGroup(osFeatureInstance);
326 6 : if (!poFeatureInstance)
327 : {
328 0 : CPLError(CE_Failure, CPLE_AppDefined,
329 : "Cannot find /SurfaceCurrent/%s group",
330 : osFeatureInstance.c_str());
331 0 : return nullptr;
332 : }
333 :
334 : // Read additional metadata
335 24 : for (const char *pszAttrName :
336 : {"timeRecordInterval", "dateTimeOfFirstRecord", "dateTimeOfLastRecord",
337 30 : "numberOfTimes"})
338 : {
339 72 : auto poAttr = poFeatureInstance->GetAttribute(pszAttrName);
340 24 : if (poAttr)
341 : {
342 24 : const char *pszVal = poAttr->ReadAsString();
343 24 : if (pszVal)
344 : {
345 24 : poDS->GDALDataset::SetMetadataItem(pszAttrName, pszVal);
346 : }
347 : }
348 : }
349 :
350 6 : if (auto poDataDynamicity =
351 18 : poFeatureInstance->GetAttribute("dataDynamicity"))
352 : {
353 2 : if (poDataDynamicity->GetDataType().GetClass() == GEDTC_NUMERIC)
354 : {
355 2 : const int nVal = poDataDynamicity->ReadAsInt();
356 : const std::map<int, const char *> oDataDynamicityMap = {
357 : {1, "Observation"},
358 : {2, "Astronomical prediction"},
359 : {3, "Analysis or hybrid method"},
360 : {4, "Hydrodynamic model hindcast"},
361 : {5, "Hydrodynamic model forecast"},
362 : {6, "Observed minus predicted"},
363 : {7, "Observed minus analysis"},
364 : {8, "Observed minus hindcast"},
365 : {9, "Observed minus forecast"},
366 : {10, "Forecast minus predicted"},
367 4 : };
368 2 : const auto oIter = oDataDynamicityMap.find(nVal);
369 2 : if (oIter != oDataDynamicityMap.end())
370 2 : poDS->GDALDataset::SetMetadataItem("DATA_DYNAMICITY_MEANING",
371 2 : oIter->second);
372 : }
373 : }
374 :
375 : // Read optional uncertainty array
376 13 : if (auto poUncertainty = poFeatureInstance->OpenMDArray("uncertainty"))
377 : {
378 1 : auto &apoDims = poUncertainty->GetDimensions();
379 2 : if (poUncertainty->GetDataType().GetClass() == GEDTC_COMPOUND &&
380 2 : apoDims.size() == 1 && apoDims[0]->GetSize() == 2)
381 : {
382 : const auto &oComponents =
383 1 : poUncertainty->GetDataType().GetComponents();
384 2 : if (oComponents.size() == 2 &&
385 2 : oComponents[0]->GetName() == "name" &&
386 2 : oComponents[0]->GetType().GetClass() == GEDTC_STRING &&
387 3 : oComponents[1]->GetName() == "value" &&
388 1 : oComponents[1]->GetType().GetNumericDataType() == GDT_Float64)
389 : {
390 3 : auto poName = poUncertainty->GetView("[\"name\"]");
391 3 : auto poValue = poUncertainty->GetView("[\"value\"]");
392 1 : if (poName && poValue)
393 : {
394 1 : char *apszStr[2] = {nullptr, nullptr};
395 1 : double adfVals[2] = {0, 0};
396 1 : GUInt64 arrayStartIdx[] = {0};
397 1 : size_t count[] = {2};
398 1 : GInt64 arrayStep[] = {1};
399 1 : GPtrDiff_t bufferStride[] = {1};
400 2 : if (poName->Read(arrayStartIdx, count, arrayStep,
401 1 : bufferStride, oComponents[0]->GetType(),
402 2 : apszStr) &&
403 2 : poValue->Read(arrayStartIdx, count, arrayStep,
404 1 : bufferStride, oComponents[1]->GetType(),
405 : adfVals))
406 : {
407 3 : for (int i = 0; i < 2; ++i)
408 : {
409 4 : std::string osName = apszStr[i];
410 2 : if (osName[0] >= 'a' && osName[0] <= 'z')
411 2 : osName[0] = osName[0] - 'a' + 'A';
412 2 : osName = "uncertainty" + osName;
413 2 : poDS->GDALDataset::SetMetadataItem(
414 : osName.c_str(), CPLSPrintf("%f", adfVals[i]));
415 : }
416 : }
417 1 : VSIFree(apszStr[0]);
418 1 : VSIFree(apszStr[1]);
419 : }
420 : }
421 : }
422 : }
423 :
424 12 : if (auto poStartSequence = poFeatureInstance->GetAttribute("startSequence"))
425 : {
426 6 : const char *pszStartSequence = poStartSequence->ReadAsString();
427 6 : if (pszStartSequence && !EQUAL(pszStartSequence, "0,0"))
428 : {
429 0 : CPLError(CE_Failure, CPLE_AppDefined,
430 : "startSequence (=%s) != 0,0 is not supported",
431 : pszStartSequence);
432 0 : return nullptr;
433 : }
434 : }
435 :
436 6 : if (!S100GetNumPointsLongitudinalLatitudinal(
437 6 : poFeatureInstance.get(), poDS->nRasterXSize, poDS->nRasterYSize))
438 : {
439 0 : return nullptr;
440 : }
441 :
442 : // Potentially override vertical datum
443 6 : S100ReadVerticalDatum(poDS.get(), poFeatureInstance.get());
444 :
445 6 : const bool bNorthUp = CPLTestBool(
446 6 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "NORTH_UP", "YES"));
447 :
448 : // Compute geotransform
449 12 : poDS->m_bHasGT =
450 6 : S100GetGeoTransform(poFeatureInstance.get(), poDS->m_gt, bNorthUp);
451 :
452 6 : if (osGroup.empty())
453 : {
454 2 : const auto aosGroupNames = poFeatureInstance->GetGroupNames();
455 1 : int iSubDS = 1;
456 2 : for (const auto &osSubGroup : aosGroupNames)
457 : {
458 2 : if (auto poSubGroup = poFeatureInstance->OpenGroup(osSubGroup))
459 : {
460 1 : poDS->GDALDataset::SetMetadataItem(
461 : CPLSPrintf("SUBDATASET_%d_NAME", iSubDS),
462 : CPLSPrintf("S111:\"%s\":%s", osFilename.c_str(),
463 : osSubGroup.c_str()),
464 : "SUBDATASETS");
465 2 : std::string osSubDSDesc = "Values for group ";
466 1 : osSubDSDesc += osSubGroup;
467 2 : const auto poTimePoint = poSubGroup->GetAttribute("timePoint");
468 1 : if (poTimePoint)
469 : {
470 1 : const char *pszVal = poTimePoint->ReadAsString();
471 1 : if (pszVal)
472 : {
473 1 : osSubDSDesc = "Values at timestamp ";
474 1 : osSubDSDesc += pszVal;
475 : }
476 : }
477 1 : poDS->GDALDataset::SetMetadataItem(
478 : CPLSPrintf("SUBDATASET_%d_DESC", iSubDS),
479 : osSubDSDesc.c_str(), "SUBDATASETS");
480 1 : ++iSubDS;
481 : }
482 : }
483 : }
484 : else
485 : {
486 5 : auto poGroup = poFeatureInstance->OpenGroup(osGroup);
487 5 : if (!poGroup)
488 : {
489 1 : CPLError(CE_Failure, CPLE_AppDefined,
490 : "Cannot find /SurfaceCurrent/%s/%s group",
491 : osFeatureInstance.c_str(), osGroup.c_str());
492 1 : return nullptr;
493 : }
494 :
495 8 : auto poValuesArray = poGroup->OpenMDArray("values");
496 4 : if (!poValuesArray)
497 : {
498 0 : CPLError(CE_Failure, CPLE_AppDefined,
499 : "Cannot find /SurfaceCurrent/%s/%s/values array",
500 : osFeatureInstance.c_str(), osGroup.c_str());
501 0 : return nullptr;
502 : }
503 :
504 4 : if (poValuesArray->GetDimensionCount() != 2)
505 : {
506 0 : CPLError(CE_Failure, CPLE_AppDefined,
507 : "Wrong dimension count for %s",
508 0 : poValuesArray->GetFullName().c_str());
509 0 : return nullptr;
510 : }
511 :
512 4 : const auto &oType = poValuesArray->GetDataType();
513 4 : if (oType.GetClass() != GEDTC_COMPOUND)
514 : {
515 0 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong data type for %s",
516 0 : poValuesArray->GetFullName().c_str());
517 0 : return nullptr;
518 : }
519 :
520 4 : const auto &oComponents = oType.GetComponents();
521 8 : if (!(oComponents.size() == 2 &&
522 8 : ((oComponents[0]->GetName() == "surfaceCurrentSpeed" &&
523 4 : oComponents[0]->GetType().GetNumericDataType() == GDT_Float32 &&
524 8 : oComponents[1]->GetName() == "surfaceCurrentDirection" &&
525 4 : oComponents[1]->GetType().GetNumericDataType() ==
526 0 : GDT_Float32) ||
527 : // S111US_20170829.0100_W078.N44_F2_loofs_type2.h5 has direction first...
528 0 : (oComponents[0]->GetName() == "surfaceCurrentDirection" &&
529 0 : oComponents[0]->GetType().GetNumericDataType() == GDT_Float32 &&
530 0 : oComponents[1]->GetName() == "surfaceCurrentSpeed" &&
531 0 : oComponents[1]->GetType().GetNumericDataType() ==
532 : GDT_Float32))))
533 : {
534 0 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong data type for %s",
535 0 : poValuesArray->GetFullName().c_str());
536 0 : return nullptr;
537 : }
538 :
539 4 : const auto &apoDims = poValuesArray->GetDimensions();
540 4 : if (apoDims[0]->GetSize() != static_cast<unsigned>(poDS->nRasterYSize))
541 : {
542 0 : CPLError(CE_Failure, CPLE_AppDefined,
543 : "numPointsLatitudinal(=%d) doesn't match first dimension "
544 : "size of %s (=%d)",
545 0 : poDS->nRasterYSize, poValuesArray->GetFullName().c_str(),
546 0 : static_cast<int>(apoDims[0]->GetSize()));
547 0 : return nullptr;
548 : }
549 4 : if (apoDims[1]->GetSize() != static_cast<unsigned>(poDS->nRasterXSize))
550 : {
551 0 : CPLError(CE_Failure, CPLE_AppDefined,
552 : "numPointsLongitudinal(=%d) doesn't match second "
553 : "dimension size of %s (=%d)",
554 0 : poDS->nRasterXSize, poValuesArray->GetFullName().c_str(),
555 0 : static_cast<int>(apoDims[1]->GetSize()));
556 0 : return nullptr;
557 : }
558 :
559 4 : if (bNorthUp)
560 3 : poValuesArray = poValuesArray->GetView("[::-1,...]");
561 :
562 : // Create surfaceCurrentSpeed band
563 : auto poSurfaceCurrentSpeed =
564 12 : poValuesArray->GetView("[\"surfaceCurrentSpeed\"]");
565 : auto poSurfaceCurrentSpeedDS = std::unique_ptr<GDALDataset>(
566 8 : poSurfaceCurrentSpeed->AsClassicDataset(1, 0));
567 : auto poSurfaceCurrentSpeedBand = std::make_unique<S111RasterBand>(
568 8 : std::move(poSurfaceCurrentSpeedDS));
569 4 : poSurfaceCurrentSpeedBand->SetDescription("surfaceCurrentSpeed");
570 4 : poSurfaceCurrentSpeedBand->m_osUnitType = "knots";
571 :
572 : // From S-111 v1.2 table 9.1 (Speed ranges) and 9.2 (Colour schemas)
573 8 : auto poRAT = std::make_unique<GDALDefaultRasterAttributeTable>();
574 4 : poRAT->CreateColumn("speed_band", GFT_Integer, GFU_Generic);
575 4 : poRAT->CreateColumn("min_speed", GFT_Real, GFU_Min);
576 4 : poRAT->CreateColumn("width_band", GFT_Real, GFU_Generic);
577 4 : poRAT->CreateColumn("color", GFT_String, GFU_Generic);
578 4 : poRAT->CreateColumn("red", GFT_Integer, GFU_RedMin);
579 4 : poRAT->CreateColumn("green", GFT_Integer, GFU_GreenMin);
580 4 : poRAT->CreateColumn("blue", GFT_Integer, GFU_BlueMin);
581 :
582 : const struct
583 : {
584 : int nSpeedBand;
585 : double dfMinSpeed;
586 : double dfWidthBand;
587 : const char *pszColor;
588 : int nRed;
589 : int nGreen;
590 : int nBlue;
591 4 : } aoRatValues[] = {
592 : {1, 0.0, 0.5, "purple", 118, 82, 226},
593 : {2, 0.5, 0.5, "dark blue", 72, 152, 211},
594 : {3, 1.0, 1.0, "light blue", 97, 203, 229},
595 : {4, 2.0, 1.0, "dark green", 109, 188, 69},
596 : {5, 3.0, 2.0, "light green", 180, 220, 0},
597 : {6, 5.0, 2.0, "yellow green", 205, 193, 0},
598 : {7, 7.0, 3.0, "orange", 248, 167, 24},
599 : {8, 10.0, 3.0, "pink", 247, 162, 157},
600 : {9, 13.0, 86.0, "red", 255, 30, 30},
601 : };
602 :
603 4 : int iRow = 0;
604 40 : for (const auto &oRecord : aoRatValues)
605 : {
606 36 : int iCol = 0;
607 36 : poRAT->SetValue(iRow, iCol++, oRecord.nSpeedBand);
608 36 : poRAT->SetValue(iRow, iCol++, oRecord.dfMinSpeed);
609 36 : poRAT->SetValue(iRow, iCol++, oRecord.dfWidthBand);
610 36 : poRAT->SetValue(iRow, iCol++, oRecord.pszColor);
611 36 : poRAT->SetValue(iRow, iCol++, oRecord.nRed);
612 36 : poRAT->SetValue(iRow, iCol++, oRecord.nGreen);
613 36 : poRAT->SetValue(iRow, iCol++, oRecord.nBlue);
614 36 : ++iRow;
615 : }
616 :
617 4 : poSurfaceCurrentSpeedBand->m_poRAT = std::move(poRAT);
618 :
619 4 : poDS->SetBand(1, poSurfaceCurrentSpeedBand.release());
620 :
621 : // Create surfaceCurrentDirection band
622 : auto poSurfaceCurrentDirection =
623 12 : poValuesArray->GetView("[\"surfaceCurrentDirection\"]");
624 : auto poSurfaceCurrentDirectionDS = std::unique_ptr<GDALDataset>(
625 8 : poSurfaceCurrentDirection->AsClassicDataset(1, 0));
626 : auto poSurfaceCurrentDirectionBand = std::make_unique<S111RasterBand>(
627 8 : std::move(poSurfaceCurrentDirectionDS));
628 4 : poSurfaceCurrentDirectionBand->SetDescription(
629 4 : "surfaceCurrentDirection");
630 4 : poSurfaceCurrentDirectionBand->m_osUnitType = "degree";
631 4 : poSurfaceCurrentDirectionBand->GDALRasterBand::SetMetadataItem(
632 : "ANGLE_CONVENTION", "From true north, clockwise");
633 4 : poDS->SetBand(2, poSurfaceCurrentDirectionBand.release());
634 : }
635 :
636 5 : poDS->GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
637 :
638 : // Setup/check for pam .aux.xml.
639 5 : poDS->SetDescription(osFilename.c_str());
640 5 : poDS->TryLoadXML();
641 :
642 : // Setup overviews.
643 5 : poDS->oOvManager.Initialize(poDS.get(), osFilename.c_str());
644 :
645 5 : return poDS.release();
646 : }
647 :
648 : /************************************************************************/
649 : /* S111DatasetDriverUnload() */
650 : /************************************************************************/
651 :
652 6 : static void S111DatasetDriverUnload(GDALDriver *)
653 : {
654 6 : HDF5UnloadFileDriver();
655 6 : }
656 :
657 : /************************************************************************/
658 : /* GDALRegister_S111() */
659 : /************************************************************************/
660 11 : void GDALRegister_S111()
661 :
662 : {
663 11 : if (!GDAL_CHECK_VERSION("S111"))
664 0 : return;
665 :
666 11 : if (GDALGetDriverByName(S111_DRIVER_NAME) != nullptr)
667 0 : return;
668 :
669 11 : GDALDriver *poDriver = new GDALDriver();
670 :
671 11 : S111DriverSetCommonMetadata(poDriver);
672 11 : poDriver->pfnOpen = S111Dataset::Open;
673 11 : poDriver->pfnUnloadDriver = S111DatasetDriverUnload;
674 :
675 11 : GetGDALDriverManager()->RegisterDriver(poDriver);
676 : }
|