Coverage Report

Created: 2025-08-03 06:36

/src/bag/api/bag_dataset.cpp
Line
Count
Source (jump to first uncovered line)
1
2
#include "bag_attributeinfo.h"
3
#include "bag_georefmetadatalayer.h"
4
#include "bag_georefmetadatalayerdescriptor.h"
5
#include "bag_dataset.h"
6
#include "bag_exceptions.h"
7
#include "bag_interleavedlegacylayer.h"
8
#include "bag_interleavedlegacylayerdescriptor.h"
9
#include "bag_metadataprofiles.h"
10
#include "bag_metadata_export.h"
11
#include "bag_private.h"
12
#include "bag_simplelayer.h"
13
#include "bag_simplelayerdescriptor.h"
14
#include "bag_surfacecorrections.h"
15
#include "bag_surfacecorrectionsdescriptor.h"
16
#include "bag_version.h"
17
#include "bag_vrmetadata.h"
18
#include "bag_vrmetadatadescriptor.h"
19
#include "bag_vrnode.h"
20
#include "bag_vrnodedescriptor.h"
21
#include "bag_vrrefinements.h"
22
#include "bag_vrrefinementsdescriptor.h"
23
24
#include <iostream>
25
#include <algorithm>
26
#include <array>
27
#include <cctype>
28
#include <H5Cpp.h>
29
#include <H5Exception.h>
30
#include <map>
31
#include <memory>
32
#include <regex>
33
#include <string>
34
#include <memory>
35
#include <csignal>
36
37
38
namespace BAG {
39
40
using std::cerr;
41
42
namespace {
43
44
//! Find a layer by type and case-insensitive name.
45
/*!
46
\param layers
47
    The layers to search.
48
\param type
49
    The type of layer to find.
50
\param name
51
    The case-insensitive name of the layer to find.
52
    This is optional unless looking for a georeferenced metadata layer.
53
54
\return
55
    The found layer.
56
    nullptr if not found.
57
*/
58
std::shared_ptr<Layer> getLayer(
59
    const std::vector<std::shared_ptr<Layer>>& layers,
60
    LayerType type,
61
    const std::string& name = {})
62
69
{
63
69
    if (type == Georef_Metadata && name.empty())
64
0
        throw NameRequired{};
65
66
69
    std::string nameLower{name};
67
69
    std::transform(begin(nameLower), end(nameLower), begin(nameLower),
68
69
        [](char c) noexcept {
69
0
            return static_cast<char>(std::tolower(c));
70
0
        });
71
72
69
    std::shared_ptr<Layer> foundLayer = nullptr;
73
75
    for (auto layer : layers) {
74
75
        auto pDescriptor = layer->getDescriptor();
75
75
        if (type == pDescriptor->getLayerType()) {
76
0
            if (nameLower.empty()) {
77
0
                foundLayer = layer;
78
0
                break;
79
0
            }
80
0
            std::string foundNameLower{pDescriptor->getName()};
81
0
            std::transform(begin(foundNameLower), end(foundNameLower), begin(foundNameLower),
82
0
                           [](char c) noexcept {
83
0
                               return static_cast<char>(std::tolower(c));
84
0
                           });
85
0
            if (foundNameLower == nameLower) {
86
0
                foundLayer = layer;
87
0
                break;
88
0
            }
89
0
        }
90
75
    }
91
92
69
    if (foundLayer == nullptr) return {};
93
94
0
    return std::shared_ptr<Layer>(foundLayer);
95
69
}
96
97
//! Get the numerical version.
98
/*!
99
    Expecting "major" (2) or "major.minor" (2.0) or "major.minor.patch" (2.0.0),
100
    which will be turned into a number such that:
101
102
        major * 1,000,000 + minor * 1,000 + patch is returned.
103
104
\param versionStr
105
    The version as a string.  Eg., "2.1.4".
106
107
\return
108
    The numerical representation of the version.
109
    0 if the version string cannot be parsed.
110
*/
111
uint32_t getNumericalVersion(
112
    const std::string& versionStr)
113
280
{
114
280
    const std::regex expression{R"(\.)"};  // match any period
115
116
280
    auto b = std::sregex_token_iterator{begin(versionStr), end(versionStr),
117
280
        expression, -1};
118
280
    const auto e = std::sregex_token_iterator{};
119
120
121
280
    uint32_t version = 0;
122
280
    uint32_t multiplier = 1'000'000;
123
124
874
    for (auto iter = b; iter != e; ++iter)
125
665
    {
126
665
        try
127
665
        {
128
665
            version += std::stoul(*iter) * multiplier;
129
665
        }
130
665
        catch(...)
131
665
        {
132
71
            return 0;
133
71
        }
134
135
594
        multiplier /= 1000;
136
594
    }
137
138
209
    return version;
139
280
}
140
141
//! A type alias to use SFINAE to check if a type is not a std::string.
142
template <typename T>
143
using IsNotString = std::enable_if_t<!std::is_same<T, std::string>::value>;
144
145
//! Helper to read a non-string attribute from an HDF5 DataSet.
146
/*!
147
\param h5file
148
    The HDF5 file to read.
149
\param dataSetName
150
    The name of the HDF5 DataSet.
151
\param attributeName
152
    The name of the attribute.
153
154
\return
155
    The attribute value.
156
*/
157
template <typename T, typename = IsNotString<T>>
158
T readAttributeFromDataSet(
159
    const ::H5::H5File& h5file,
160
    const std::string& dataSetName,
161
    const std::string& attributeName)
162
400
{
163
400
    const ::H5::DataSet h5DataSet = h5file.openDataSet(dataSetName);
164
400
    const ::H5::Attribute attribute = h5DataSet.openAttribute(attributeName);
165
166
400
    T value{};
167
400
    attribute.read(attribute.getDataType(), &value);
168
169
400
    return value;
170
400
}
171
172
//! Helper to read a string attribute from an HDF5 DataSet.
173
/*!
174
\param h5file
175
    The HDF5 file to read.
176
\param dataSetName
177
    The name of the HDF5 DataSet.
178
\param attributeName
179
    The name of the attribute.
180
181
\return
182
    The attribute value as a string.
183
*/
184
std::string readStringAttributeFromDataSet(
185
    const ::H5::H5File& h5file,
186
    const std::string& dataSetName,
187
    const std::string& attributeName)
188
0
{
189
0
    const ::H5::DataSet h5DataSet = h5file.openDataSet(dataSetName);
190
0
    const ::H5::Attribute attribute = h5DataSet.openAttribute(attributeName);
191
0
192
0
    std::string value;
193
0
    attribute.read(attribute.getDataType(), value);
194
0
195
0
    return value;
196
0
}
197
198
//! Helper to read a non-string attribute from an HDF5 Group.
199
/*!
200
\param h5file
201
    The HDF5 file to read.
202
\param groupName
203
    The name of the HDF5 Group.
204
\param attributeName
205
    The name of the attribute.
206
207
\return
208
    The attribute value.
209
*/
210
template <typename T, typename = IsNotString<T>>
211
T readAttributeFromGroup(
212
    const ::H5::H5File& h5file,
213
    const std::string& groupName,
214
    const std::string& attributeName)
215
{
216
    const ::H5::Group h5group = h5file.openGroup(groupName);
217
    const ::H5::Attribute attribute = h5group.openAttribute(attributeName);
218
219
    T value{};
220
    attribute.read(attribute.getDataType(), &value);
221
222
    return value;
223
}
224
225
//! Helper to read a string attribute from an HDF5 Group.
226
/*!
227
\param h5file
228
    The HDF5 file to read.
229
\param groupName
230
    The name of the HDF5 Group.
231
\param attributeName
232
    The name of the attribute.
233
234
\return
235
    The attribute value as a string.
236
*/
237
std::string readStringAttributeFromGroup(
238
    const ::H5::H5File& h5file,
239
    const std::string& groupName,
240
    const std::string& attributeName)
241
280
{
242
280
    const ::H5::Group h5group = h5file.openGroup(groupName);
243
280
    const ::H5::Attribute attribute = h5group.openAttribute(attributeName);
244
245
280
    std::string value;
246
280
    attribute.read(attribute.getDataType(), value);
247
248
280
    return value;
249
280
}
250
251
}  // namespace
252
253
//! Open an existing BAG.
254
/*!
255
\param fileName
256
    The name of the BAG.
257
\param openMode
258
    The mode to open the BAG with.
259
260
\return
261
    The BAG Dataset.
262
*/
263
std::shared_ptr<Dataset> Dataset::open(
264
    const std::string& fileName,
265
    OpenMode openMode)
266
383
{
267
383
#ifdef NDEBUG
268
383
    ::H5::Exception::dontPrint();
269
383
#endif
270
271
383
    std::shared_ptr<Dataset> pDataset{new Dataset};
272
383
    try
273
383
    {
274
383
        pDataset->readDataset(fileName, openMode);
275
383
    } catch (H5::FileIException &fileExcept)
276
383
    {
277
151
        std::cerr << "\nUnable to open BAG file: " << fileName << " due to error: " << fileExcept.getCDetailMsg();
278
151
        return nullptr;
279
151
    }
280
281
232
    return pDataset;
282
383
}
283
284
//! Create a BAG.
285
/*!
286
\param fileName
287
    The name of the BAG.
288
\param metadata
289
    The metadata describing the BAG.
290
    This parameter will be moved, and not usable after.
291
\param chunkSize
292
    The chunk size the HDF5 DataSet will use.
293
\param compressionLevel
294
    The compression level the HDF5 DataSet will use.
295
296
\return
297
    The BAG Dataset.
298
*/
299
std::shared_ptr<Dataset> Dataset::create(
300
    const std::string& fileName,
301
    Metadata&& metadata,
302
    uint64_t chunkSize,
303
    int compressionLevel)
304
0
{
305
0
    std::shared_ptr<Dataset> pDataset{new Dataset};
306
0
    pDataset->createDataset(fileName, std::move(metadata), chunkSize,
307
0
        compressionLevel);
308
309
0
    return pDataset;
310
0
}
311
312
//! Close a BAG dataset. Closes the underlying HDF5 file.
313
232
void Dataset::close() {
314
232
    if (m_pH5file) {
315
232
        m_pH5file->close();
316
232
        m_pH5file.reset(nullptr);
317
232
    }
318
232
}
319
320
321
//! Add a layer to this dataset.
322
/*!
323
\param newLayer
324
    The new layer.
325
326
\return
327
    A reference to the new layer.
328
*/
329
Layer& Dataset::addLayer(
330
    std::shared_ptr<Layer> newLayer) &
331
266
{
332
266
    m_layers.push_back(std::move(newLayer));
333
334
266
    const auto& layer = m_layers.back();
335
336
266
    m_descriptor.addLayerDescriptor(*layer->getDescriptor());
337
338
266
    return *layer;
339
266
}
340
341
//! Create a georeferenced metadata layer.
342
/*!
343
\param keyType
344
    The type of key the georeferenced metadata layer will use.
345
    Valid values are: DT_UINT8, DT_UINT16, DT_UINT32 or DT_UINT64
346
\param name
347
    The name of the simple layer this georeferenced metadata layer has metadata for.
348
\param definition
349
    The list of fields defining a record of the georeferenced metadata layer.
350
\param chunkSize
351
    The chunk size the HDF5 DataSet will use.
352
\param compressionLevel
353
    The compression level the HDF5 DataSet will use.
354
355
\return
356
    The new georeferenced metadata layer.
357
*/
358
GeorefMetadataLayer& Dataset::createGeorefMetadataLayer(
359
            DataType keyType,
360
            GeorefMetadataProfile profile,
361
            const std::string& name,
362
            const RecordDefinition& definition,
363
            uint64_t chunkSize,
364
            int compressionLevel) &
365
0
{
366
0
    if (m_descriptor.isReadOnly())
367
0
        throw ReadOnlyError{};
368
369
0
    std::string nameLower{name};
370
0
    std::transform(begin(nameLower), end(nameLower), begin(nameLower),
371
0
        [](char c) noexcept {
372
0
            return static_cast<char>(std::tolower(c));
373
0
        });
374
375
    // Make sure a corresponding simple layer exists.
376
0
    const bool simpleLayerExists = std::any_of(cbegin(m_layers), cend(m_layers),
377
0
        [&nameLower](const std::shared_ptr<Layer>& layer) {
378
0
            auto pDescriptor = layer->getDescriptor();
379
380
0
            const auto layerType = pDescriptor->getLayerType();
381
382
            // Skip non-simple layers.
383
0
            if (layerType == Georef_Metadata || layerType == VarRes_Metadata ||
384
0
                layerType == VarRes_Refinement || layerType == VarRes_Node)
385
0
                return false;
386
387
0
            std::string simpleNameLower{pDescriptor->getName()};
388
0
            std::transform(begin(simpleNameLower), end(simpleNameLower), begin(simpleNameLower),
389
0
                [](char c) noexcept {
390
0
                    return static_cast<char>(std::tolower(c));
391
0
                });
392
393
0
            return simpleNameLower == nameLower;
394
0
        });
395
0
    if (!simpleLayerExists)
396
0
        throw LayerNotFound{};
397
398
    // Make sure the georeferenced metadata layer does not already exist.
399
0
    if (this->getGeorefMetadataLayer(name))
400
0
        throw LayerExists{};
401
402
    // Create the group if it does not exist.
403
0
    const auto id = H5Gopen2(m_pH5file->getId(), GEOREF_METADATA_PATH, H5P_DEFAULT);
404
0
    if (id == -1)
405
0
        m_pH5file->createGroup(GEOREF_METADATA_PATH);
406
0
    else
407
0
        H5Gclose(id);
408
409
0
    return dynamic_cast<GeorefMetadataLayer&>(this->addLayer(GeorefMetadataLayer::create(
410
0
        keyType, name, profile, *this, definition, chunkSize, compressionLevel)));
411
0
}
412
413
//! Convenience method for creating a georeferenced metadata layer with a known metadata profile.
414
//! Will use the RecordDefinition appropriate to the known profile.
415
/*!
416
\param profile
417
    The metadata profile to assign to the georeferenced metadata layer.
418
\param name
419
    The name of the simple layer this georeferenced metadata layer has metadata for.
420
\param chunkSize
421
    The chunk size the HDF5 DataSet will use.
422
\param compressionLevel
423
    The compression level the HDF5 DataSet will use.
424
\param keyType
425
    The type of key the georeferenced metadata layer will use.
426
    Valid values are: DT_UINT8, DT_UINT16, DT_UINT32 or DT_UINT64
427
    Default value: DT_UINT16
428
429
\return
430
    The new georeferenced metadata layer.
431
432
\throws
433
    UknownMetadataProfile if profile is not a known metadata profile
434
*/
435
GeorefMetadataLayer& Dataset::createGeorefMetadataLayer(
436
        GeorefMetadataProfile profile,
437
        const std::string& name,
438
        uint64_t chunkSize,
439
        int compressionLevel,
440
        DataType keyType) &
441
0
{
442
0
    BAG::RecordDefinition definition = METADATA_DEFINITION_UNKNOWN;
443
444
0
    try {
445
0
        definition = kGeorefMetadataProfileMapKnownRecordDefinition.at(profile);
446
0
    } catch (const std::out_of_range&) {
447
0
        throw UknownMetadataProfile{kGeorefMetadataProfileMapString.at(profile)};
448
0
    }
449
450
0
    return createGeorefMetadataLayer(keyType, profile,
451
0
                                     name, definition, chunkSize, compressionLevel);
452
0
}
453
454
//! Create a new Dataset.
455
/*!
456
\param fileName
457
    The name of the new BAG.
458
\param metadata
459
    The metadata to be used by the BAG.
460
\param chunkSize
461
    The chunk size the HDF5 DataSet will use.
462
\param compressionLevel
463
    The compression level the HDF5 DataSet will use.
464
*/
465
void Dataset::createDataset(
466
    const std::string& fileName,
467
    Metadata&& metadata,
468
    uint64_t chunkSize,
469
    int compressionLevel)
470
0
{
471
0
#ifdef NDEBUG
472
0
    ::H5::Exception::dontPrint();
473
0
#endif
474
475
0
    m_pH5file = std::unique_ptr<::H5::H5File, DeleteH5File>(new ::H5::H5File{
476
0
        fileName.c_str(), H5F_ACC_EXCL}, DeleteH5File{});
477
478
    // Group: BAG_root
479
0
    {
480
0
        auto h5bagGroup = m_pH5file->createGroup(ROOT_PATH);
481
482
0
        const auto versionAttType = ::H5::StrType{0, BAG_VERSION_LENGTH};
483
0
        const ::H5::DataSpace versionAttDataSpace{};
484
0
        auto versionAtt = h5bagGroup.createAttribute(BAG_VERSION_NAME,
485
0
            versionAttType, versionAttDataSpace);
486
487
0
        versionAtt.write(versionAttType, BAG_VERSION);
488
0
    }
489
490
    // Metadata
491
0
    metadata.createH5dataSet(*this);
492
0
    metadata.write();
493
0
    m_pMetadata = std::make_unique<Metadata>(std::move(metadata));
494
495
0
    m_descriptor = Descriptor{*m_pMetadata};
496
0
    m_descriptor.setReadOnly(false);
497
0
    m_descriptor.setVersion(BAG_VERSION);
498
499
    // TrackingList
500
0
    m_pTrackingList = std::unique_ptr<TrackingList>(new TrackingList{*this,
501
0
        compressionLevel});
502
503
    // Mandatory Layers
504
    // Elevation
505
0
    this->addLayer(SimpleLayer::create(*this, Elevation, chunkSize,
506
0
        compressionLevel));
507
508
    // Uncertainty
509
0
    this->addLayer(SimpleLayer::create(*this, Uncertainty, chunkSize,
510
0
        compressionLevel));
511
0
}
512
513
//! Create an optional simple layer.
514
/*!
515
\param type
516
    The type of layer to create.
517
    The layer cannot currently exist.
518
\param chunkSize
519
    The chunk size the HDF5 DataSet will use.
520
\param compressionLevel
521
    The compression level the HDF5 DataSet will use.
522
523
\return
524
    The new layer.
525
*/
526
Layer& Dataset::createSimpleLayer(
527
    LayerType type,
528
    uint64_t chunkSize,
529
    int compressionLevel) &
530
0
{
531
0
    if (m_descriptor.isReadOnly())
532
0
        throw ReadOnlyError{};
533
534
    // Make sure it doesn't already exist.
535
0
    if (BAG::getLayer(m_layers, type))
536
0
        throw LayerExists{};
537
538
0
    switch (type)
539
0
    {
540
0
    case Elevation:  //[[fallthrough]];
541
0
    case Uncertainty:  //[[fallthrough]];
542
0
    case Hypothesis_Strength:  //[[fallthrough]];
543
0
    case Num_Hypotheses:  //[[fallthrough]];
544
0
    case Shoal_Elevation:  //[[fallthrough]];
545
0
    case Std_Dev:  //[[fallthrough]];
546
0
    case Num_Soundings:  //[[fallthrough]];
547
0
    case Average_Elevation:  //[[fallthrough]];
548
0
    case Nominal_Elevation:
549
0
        return this->addLayer(SimpleLayer::create(*this, type, chunkSize,
550
0
            compressionLevel));
551
0
    case Surface_Correction:  //[[fallthrough]];
552
0
    case Georef_Metadata:  //[[fallthrough]];
553
0
    default:
554
0
        throw UnsupportedLayerType{};
555
0
    }
556
0
}
557
558
//! Create optional surface corrections layer.
559
/*!
560
\param type
561
    The type of topography.
562
    Gridded (BAG_SURFACE_GRID_EXTENTS) or sparse (BAG_SURFACE_IRREGULARLY_SPACED).
563
\param numCorrectors
564
    The number of correctors to use (1-10).
565
\param chunkSize
566
    The chunk size the HDF5 DataSet will use.
567
\param compressionLevel
568
    The compression level the HDF5 DataSet will use.
569
570
\return
571
    The new surface corrections layer.
572
*/
573
SurfaceCorrections& Dataset::createSurfaceCorrections(
574
    BAG_SURFACE_CORRECTION_TOPOGRAPHY type,
575
    uint8_t numCorrectors,
576
    uint64_t chunkSize,
577
    int compressionLevel) &
578
0
{
579
0
    if (m_descriptor.isReadOnly())
580
0
        throw ReadOnlyError{};
581
582
    // Make sure surface corrections do not already exist.
583
0
    if (this->getSurfaceCorrections())
584
0
        throw LayerExists{};
585
586
0
    return dynamic_cast<SurfaceCorrections&>(this->addLayer(
587
0
        SurfaceCorrections::create(*this, type, numCorrectors, chunkSize,
588
0
            compressionLevel)));
589
0
}
590
591
//! Create optional variable resolution layers.
592
/*!
593
\param chunkSize
594
    The chunk size the HDF5 DataSet will use.
595
\param compressionLevel
596
    The compression level the HDF5 DataSet will use.
597
*/
598
void Dataset::createVR(
599
    uint64_t chunkSize,
600
    int compressionLevel,
601
    bool createNode)
602
0
{
603
0
    if (m_descriptor.isReadOnly())
604
0
        throw ReadOnlyError{};
605
606
    // Make sure VR layers do not already exist.
607
0
    if (this->getVRMetadata())
608
0
        throw LayerExists{};
609
610
    //TODO Consider a try/catch to undo partial creation.
611
612
0
    m_pVRTrackingList = std::make_unique<VRTrackingList>(
613
0
        *this, compressionLevel);
614
615
0
    this->addLayer(VRMetadata::create(*this, chunkSize, compressionLevel));
616
0
    this->addLayer(VRRefinements::create(*this, chunkSize, compressionLevel));
617
618
0
    if (createNode)
619
0
        this->addLayer(VRNode::create(*this, chunkSize, compressionLevel));
620
0
}
621
622
//! Convert a geographic location to grid position.
623
/*!
624
\param x
625
    The X of the geographic location.
626
\param y
627
    The Y of the geographic location.
628
629
\return
630
    The grid position (row, column).
631
*/
632
std::tuple<uint32_t, uint32_t> Dataset::geoToGrid(
633
    double x,
634
    double y) const noexcept
635
0
{
636
0
    const auto row = static_cast<uint32_t>((x - m_pMetadata->llCornerX()) /
637
0
        m_pMetadata->rowResolution());
638
0
    const auto column = static_cast<uint32_t>((y - m_pMetadata->llCornerY()) /
639
0
        m_pMetadata->columnResolution());
640
641
0
    return {row, column};
642
0
}
643
644
//! Retrieve an optional georeferenced metadata layer by name.
645
/*!
646
\param name
647
    The name of the simple layer the georeferenced metadata layer has metadata for.
648
649
\return
650
    The specified georeferenced metadata layer, if it exists.  nullptr otherwise
651
*/
652
std::shared_ptr<GeorefMetadataLayer> Dataset::getGeorefMetadataLayer(
653
    const std::string& name) & noexcept
654
0
{
655
0
    return std::dynamic_pointer_cast<GeorefMetadataLayer>(BAG::getLayer(m_layers, Georef_Metadata, name));
656
0
}
657
658
//! Retrieve an optional georeferenced metadata layer by name.
659
/*!
660
\param name
661
    The name of the simple layer the georeferenced metadata layer has metadata for.
662
663
\return
664
 x   The specified georeferenced metadata layer, if it exists.  nullptr otherwise
665
*/
666
std::shared_ptr<const GeorefMetadataLayer> Dataset::getGeorefMetadataLayer(const std::string& name) const & noexcept
667
0
{
668
0
    return std::dynamic_pointer_cast<const GeorefMetadataLayer>(BAG::getLayer(m_layers, Georef_Metadata, name));
669
0
}
670
671
//! Retrieve all the georeferenced metadata layers.
672
/*!
673
\return
674
    All the georeferenced metadata layers.
675
*/
676
std::vector<std::shared_ptr<GeorefMetadataLayer>> Dataset::getGeorefMetadataLayers() & noexcept
677
0
{
678
0
    std::vector<std::shared_ptr<GeorefMetadataLayer>> layers;
679
680
0
    for (const auto& layer : m_layers)
681
0
        if (layer->getDescriptor()->getLayerType() == Georef_Metadata) {
682
0
            layers.emplace_back(std::dynamic_pointer_cast<GeorefMetadataLayer>(layer));
683
0
        }
684
685
0
    return layers;
686
0
}
687
688
//! Retrieve the dataset's descriptor.
689
/*!
690
\return
691
    The dataset's descriptor.
692
*/
693
Descriptor& Dataset::getDescriptor() & noexcept
694
23
{
695
23
    return m_descriptor;
696
23
}
697
698
//! Retrieve the dataset's descriptor.
699
/*!
700
\return
701
    The dataset's descriptor.
702
*/
703
const Descriptor& Dataset::getDescriptor() const & noexcept
704
0
{
705
0
    return m_descriptor;
706
0
}
707
708
//! Retrieve the HDF5 file that contains this BAG.
709
/*!
710
\return
711
    The HDF5 file that contains to this BAG.
712
*/
713
::H5::H5File& Dataset::getH5file() const & noexcept
714
1.21k
{
715
1.21k
    return *m_pH5file;
716
1.21k
}
717
718
//! Retrieve a layer by its unique id.
719
/*!
720
    Retrieve a layer by its unique id.  If it is not found, an InvalidLayerId
721
    exception is thrown.
722
723
\param id
724
    The unique id of the layer.
725
726
\return
727
    The layer specified by the id.
728
*/
729
Layer& Dataset::getLayer(uint32_t id) &
730
0
{
731
0
    if (id >= m_layers.size())
732
0
        throw InvalidLayerId{};
733
734
0
    return *m_layers[id];
735
0
}
736
737
//! Retrieve a layer by its unique id.
738
/*!
739
    Retrieve a layer by its unique id.  If it is not found, an InvalidLayerId
740
    exception is thrown.
741
742
\param id
743
    The unique id of the layer.
744
745
\return
746
    The layer specified by the id.
747
*/
748
const Layer& Dataset::getLayer(uint32_t id) const &
749
0
{
750
0
    if (id >= m_layers.size())
751
0
        throw InvalidLayerId{};
752
753
0
    return *m_layers[id];
754
0
}
755
756
//! Retrieve a layer based on type and case-insensitive name.
757
/*!
758
\param type
759
    The layer type.
760
\param name
761
    The optional, case-insensitive name.
762
    If the layer type is Georef_Metadata, the name must be the simple layer it refers to.
763
764
\return
765
    The specified layer.
766
    nullptr if no layer is found.
767
*/
768
std::shared_ptr<Layer> Dataset::getLayer(
769
    LayerType type,
770
    const std::string& name) &
771
0
{
772
0
    return BAG::getLayer(m_layers, type, name);
773
0
}
774
775
//! Retrieve a layer based on type and case-insensitive name.
776
/*!
777
\param type
778
    The layer type.
779
\param name
780
    The optional, case-insensitive name.
781
    If the layer type is Georef_Metadata, the name must be the simple layer it refers to.
782
*/
783
std::shared_ptr<const Layer> Dataset::getLayer(
784
    LayerType type,
785
    const std::string& name) const &
786
0
{
787
0
    return std::shared_ptr<const Layer>{BAG::getLayer(m_layers, type, name)};
788
0
}
789
790
//! Retrieve all the layers.
791
/*!
792
\return
793
    All the layers.
794
*/
795
std::vector<std::shared_ptr<const Layer>> Dataset::getLayers() const &
796
23
{
797
23
    std::vector<std::shared_ptr<const Layer>> layers;
798
23
    layers.reserve(m_layers.size());
799
800
23
    for (auto&& layer : m_layers)
801
43
        layers.push_back(std::static_pointer_cast<const Layer>(layer));
802
803
23
    return layers;
804
23
}
805
806
//! Retrieve the layer types.
807
/*!
808
\return
809
    The layer types.
810
    If multiple georeferenced metadata layers exist, Compound_Layer will only be present once.
811
*/
812
std::vector<LayerType> Dataset::getLayerTypes() const
813
0
{
814
0
    std::vector<LayerType> types;
815
0
    types.reserve(m_layers.size());
816
817
0
    bool georefMetadataLayerAdded = false;
818
819
0
    for (auto&& layer : m_layers)
820
0
    {
821
0
        const auto type = layer->getDescriptor()->getLayerType();
822
0
        if (type == Georef_Metadata)
823
0
        {
824
0
            if (georefMetadataLayerAdded)
825
0
                continue;
826
0
            else
827
0
                georefMetadataLayerAdded = true;
828
0
        }
829
830
0
        types.push_back(type);
831
0
    }
832
833
0
    return types;
834
0
}
835
836
//! Retrieve the metadta.
837
/*!
838
\return
839
    The metadata.
840
*/
841
const Metadata& Dataset::getMetadata() const & noexcept
842
0
{
843
0
    return *m_pMetadata;
844
0
}
845
846
//! Retrieve the minimum and maximum values of a simple layer.
847
/*!
848
\param type
849
    The type of simple layer.
850
\param path
851
    The optional internal HDF5 path to use.
852
853
\return
854
    A tuple of results.
855
    The first value in the tuple indicates whether a value was found or not.
856
    If the first value is true, the second value is the minimum, and the third value is the maximum.
857
    If the first value is false, the second and third values are undefined.
858
*/
859
std::tuple<bool, float, float> Dataset::getMinMax(
860
    LayerType type,
861
    const std::string& path) const
862
200
{
863
200
    try
864
200
    {
865
200
        const auto info = getAttributeInfo(type);
866
200
        const auto& thePath = path.empty() ? info.path : path;
867
868
200
        return {true,
869
200
            readAttributeFromDataSet<float>(*m_pH5file, thePath, info.minName),
870
200
            readAttributeFromDataSet<float>(*m_pH5file, thePath, info.maxName)};
871
200
    }
872
200
    catch(const UnsupportedSimpleLayerType&)
873
200
    {
874
0
        return {false, 0.f, 0.f};  // No min/max attributes.
875
0
    }
876
200
}
877
878
//! Retrieve the next unique layer id.
879
/*!
880
\return
881
    The next unique layer id.
882
*/
883
uint32_t Dataset::getNextId() const noexcept
884
302
{
885
302
    return static_cast<uint32_t>(m_layers.size());
886
302
}
887
888
//! Retrieve the specified simple layer.
889
/*!
890
\param type
891
    The layer type.
892
893
\return
894
    The specified simple l ayer.
895
    nullptr if the layer does not exist.
896
*/
897
std::shared_ptr<SimpleLayer> Dataset::getSimpleLayer(LayerType type) & noexcept
898
0
{
899
0
    return std::dynamic_pointer_cast<SimpleLayer>(BAG::getLayer(m_layers, type));
900
0
}
901
902
//! Retrieve the specified simple layer.
903
/*!
904
\param type
905
    The layer type.
906
907
\return
908
    The specified simple l ayer.
909
    nullptr if the layer does not exist.
910
*/
911
std::shared_ptr<const SimpleLayer> Dataset::getSimpleLayer(LayerType type) const & noexcept
912
0
{
913
0
    return std::dynamic_pointer_cast<const SimpleLayer>(BAG::getLayer(m_layers, type));
914
0
}
915
916
//! Retrieve the optional surface corrections layer.
917
/*!
918
\return
919
    The optional surface corrections layer.
920
*/
921
std::shared_ptr<SurfaceCorrections> Dataset::getSurfaceCorrections() & noexcept
922
0
{
923
0
    return std::dynamic_pointer_cast<SurfaceCorrections>(BAG::getLayer(m_layers, Surface_Correction));
924
0
}
925
926
//! Retrieve the optional surface corrections layer.
927
/*!
928
\return
929
    The optional surface corrections layer.
930
*/
931
std::shared_ptr<const SurfaceCorrections> Dataset::getSurfaceCorrections() const & noexcept
932
0
{
933
0
    return std::dynamic_pointer_cast<const SurfaceCorrections>(BAG::getLayer(m_layers, Surface_Correction));
934
0
}
935
936
//! Retrieve the tracking list.
937
/*!
938
\return
939
    The tracking list.
940
*/
941
TrackingList& Dataset::getTrackingList() & noexcept
942
23
{
943
23
    return *m_pTrackingList;
944
23
}
945
946
//! Retrieve the tracking list.
947
/*!
948
\return
949
    The tracking list.
950
*/
951
const TrackingList& Dataset::getTrackingList() const & noexcept
952
0
{
953
0
    return *m_pTrackingList;
954
0
}
955
956
//! Retrieve the optional variable resolution metadata.
957
/*!
958
\return
959
    The optional variable resolution metadata.
960
*/
961
std::shared_ptr<VRMetadata> Dataset::getVRMetadata() & noexcept
962
69
{
963
69
    return std::dynamic_pointer_cast<VRMetadata>(BAG::getLayer(m_layers, VarRes_Metadata));
964
69
}
965
966
//! Retrieve the optional variable resolution metadata.
967
/*!
968
\return
969
    The optional variable resolution metadata.
970
*/
971
std::shared_ptr<const VRMetadata> Dataset::getVRMetadata() const & noexcept
972
0
{
973
0
    return std::dynamic_pointer_cast<const VRMetadata>(BAG::getLayer(m_layers, VarRes_Metadata));
974
0
}
975
976
//! Retrieve the optional variable resolution node group.
977
/*!
978
\return
979
    The optional variable resolution node group.
980
*/
981
std::shared_ptr<VRNode> Dataset::getVRNode() & noexcept
982
0
{
983
0
    return std::dynamic_pointer_cast<VRNode>(BAG::getLayer(m_layers, VarRes_Node));
984
0
}
985
986
//! Retrieve the optional variable resolution node group.
987
/*!
988
\return
989
    The optional variable resolution node group.
990
*/
991
std::shared_ptr<const VRNode> Dataset::getVRNode() const & noexcept
992
0
{
993
0
    return std::dynamic_pointer_cast<const VRNode>(BAG::getLayer(m_layers, VarRes_Node));
994
0
}
995
996
//! Retrieve the optional variable resolution refinements.
997
/*!
998
\return
999
    The optional variable resolution refinements.
1000
*/
1001
std::shared_ptr<VRRefinements> Dataset::getVRRefinements() & noexcept
1002
0
{
1003
0
    return std::dynamic_pointer_cast<VRRefinements>(BAG::getLayer(m_layers, VarRes_Refinement));
1004
0
}
1005
1006
//! Retrieve the optional variable resolution refinements.
1007
/*!
1008
\return
1009
    The optional variable resolution refinements.
1010
*/
1011
std::shared_ptr<const VRRefinements> Dataset::getVRRefinements() const & noexcept
1012
0
{
1013
0
    return std::dynamic_pointer_cast<const VRRefinements>(BAG::getLayer(m_layers, VarRes_Refinement));
1014
0
}
1015
1016
//! Retrieve the optional variable resolution tracking list.
1017
/*!
1018
\return
1019
    The optional variable resolution tracking list.
1020
*/
1021
std::shared_ptr<VRTrackingList> Dataset::getVRTrackingList() & noexcept
1022
0
{
1023
0
    return std::shared_ptr<VRTrackingList>{m_pVRTrackingList};
1024
0
}
1025
1026
//! Retrieve the optional variable resolution tracking list.
1027
/*!
1028
\return
1029
    The optional variable resolution tracking list.
1030
*/
1031
std::shared_ptr<const VRTrackingList> Dataset::getVRTrackingList() const & noexcept
1032
0
{
1033
0
    return std::static_pointer_cast<const VRTrackingList>(m_pVRTrackingList);
1034
0
}
1035
1036
//! Convert a grid position to a geographic location.
1037
/*!
1038
\param row
1039
    The grid row.
1040
\param column
1041
    The grid column.
1042
1043
\return
1044
    The geographic position.
1045
*/
1046
std::tuple<double, double> Dataset::gridToGeo(
1047
    uint32_t row,
1048
    uint32_t column) const noexcept
1049
46
{
1050
46
    const auto x = m_pMetadata->llCornerX() +
1051
46
        (row * m_pMetadata->rowResolution());
1052
1053
46
    const auto y = m_pMetadata->llCornerY() +
1054
46
        (column * m_pMetadata->columnResolution());
1055
1056
46
    return {x, y};
1057
46
}
1058
1059
1060
3.31k
hid_t DopenProtector2(hid_t loc_id, const char *name, hid_t dapl_id) {
1061
3.31k
    hid_t id = -1;
1062
3.31k
    try {
1063
3.31k
        id = H5Dopen2(loc_id, name, dapl_id);
1064
3.31k
    } catch (std::exception& e) {
1065
0
        std::cerr << e.what();
1066
0
        id = -1;
1067
0
    }
1068
3.31k
    return id;
1069
3.31k
}
1070
1071
130
hid_t GopenProtector2(hid_t loc_id, const char *name, hid_t dapl_id) {
1072
130
    hid_t id = -1;
1073
130
    try {
1074
130
        id = H5Gopen2(loc_id, name, dapl_id);
1075
130
    } catch (std::exception& e) {
1076
0
        std::cerr << e.what();
1077
0
        id = -1;
1078
0
    }
1079
130
    return id;
1080
130
}
1081
1082
0
void handleAbrt(int signum) {
1083
0
    std::cerr << "\nUnrecoverable HDF5 Error \n";
1084
0
    exit(signum);
1085
0
}
1086
1087
//! Read an existing BAG.
1088
/*!
1089
\param fileName
1090
    The name of the BAG.
1091
\param openMode
1092
    The mode to open the BAG with.
1093
*/
1094
void Dataset::readDataset(
1095
    const std::string& fileName,
1096
    OpenMode openMode)
1097
383
{
1098
383
    signal(SIGABRT, handleAbrt);
1099
383
    m_pH5file = std::unique_ptr<::H5::H5File, DeleteH5File>(new ::H5::H5File{
1100
383
        fileName.c_str(),
1101
383
        (openMode == BAG_OPEN_READONLY) ? H5F_ACC_RDONLY : H5F_ACC_RDWR},
1102
383
        DeleteH5File{});
1103
1104
383
    m_pMetadata = std::make_unique<Metadata>(*this);
1105
1106
383
    m_descriptor = Descriptor{*m_pMetadata};
1107
383
    m_descriptor.setReadOnly(openMode == BAG_OPEN_READONLY);
1108
383
    m_descriptor.setVersion(readStringAttributeFromGroup(*m_pH5file,
1109
383
        ROOT_PATH, BAG_VERSION_NAME));
1110
1111
383
    const auto bagGroup = m_pH5file->openGroup(ROOT_PATH);
1112
1113
    // Look for the simple layers.
1114
383
    for (auto layerType : {Elevation, Uncertainty, Hypothesis_Strength,
1115
383
        Num_Hypotheses, Shoal_Elevation, Std_Dev, Num_Soundings,
1116
383
        Average_Elevation, Nominal_Elevation})
1117
2.52k
    {
1118
2.52k
        const std::string internalPath = Layer::getInternalPath(layerType);
1119
2.52k
        if (internalPath.empty())
1120
0
            continue;
1121
2.52k
        hid_t id = DopenProtector2(bagGroup.getLocId(), internalPath.c_str(),
1122
2.52k
            H5P_DEFAULT);
1123
2.52k
        if (id < 0)
1124
2.32k
            continue;
1125
1126
200
        H5Dclose(id);
1127
1128
200
        auto layerDesc = SimpleLayerDescriptor::open(*this, layerType);
1129
200
        this->addLayer(SimpleLayer::open(*this, *layerDesc));
1130
200
    }
1131
1132
383
    const auto bagVersion = getNumericalVersion(m_descriptor.getVersion());
1133
1134
    // If the BAG is version 1.5+ ...
1135
383
    if (bagVersion >= 1'005'000)
1136
140
    {
1137
140
        hid_t id = DopenProtector2(bagGroup.getLocId(), NODE_GROUP_PATH, H5P_DEFAULT);
1138
140
        if (id >= 0)
1139
0
        {
1140
0
            H5Dclose(id);
1141
1142
            // Hypothesis_Strength
1143
0
            auto layerDesc = InterleavedLegacyLayerDescriptor::open(*this,
1144
0
                Hypothesis_Strength, NODE);
1145
0
            this->addLayer(InterleavedLegacyLayer::open(*this, *layerDesc));
1146
1147
            // Num_Hypotheses
1148
0
            layerDesc = InterleavedLegacyLayerDescriptor::open(*this, Num_Hypotheses,
1149
0
                NODE);
1150
0
            this->addLayer(InterleavedLegacyLayer::open(*this, *layerDesc));
1151
0
        }
1152
140
        id = DopenProtector2(bagGroup.getLocId(), ELEVATION_SOLUTION_GROUP_PATH,
1153
140
            H5P_DEFAULT);
1154
140
        if (id >= 0)
1155
0
        {
1156
0
            H5Dclose(id);
1157
1158
            // Shoal_Elevation
1159
0
            auto layerDesc = InterleavedLegacyLayerDescriptor::open(*this,
1160
0
                Shoal_Elevation, ELEVATION);
1161
1162
0
            this->addLayer(InterleavedLegacyLayer::open(*this, *layerDesc));
1163
1164
            // Std_Dev
1165
0
            layerDesc = InterleavedLegacyLayerDescriptor::open(*this, Std_Dev,
1166
0
                ELEVATION);
1167
0
            this->addLayer(InterleavedLegacyLayer::open(*this, *layerDesc));
1168
1169
            // Num_Soundings
1170
0
            layerDesc = InterleavedLegacyLayerDescriptor::open(*this, Num_Soundings,
1171
0
                ELEVATION);
1172
0
            this->addLayer(InterleavedLegacyLayer::open(*this, *layerDesc));
1173
0
        }
1174
140
    }
1175
1176
    // Read optional VR
1177
383
    hid_t id = DopenProtector2(bagGroup.getLocId(), VR_TRACKING_LIST_PATH, H5P_DEFAULT);
1178
383
    if (id >= 0)
1179
0
    {
1180
0
        H5Dclose(id);
1181
1182
0
        m_pVRTrackingList = std::make_shared<VRTrackingList>(*this);
1183
1184
0
        {
1185
0
            auto descriptor = VRMetadataDescriptor::open(*this);
1186
0
            this->addLayer(VRMetadata::open(*this, *descriptor));
1187
0
        }
1188
1189
0
        {
1190
0
            auto descriptor = VRRefinementsDescriptor::open(*this);
1191
0
            this->addLayer(VRRefinements::open(*this, *descriptor));
1192
0
        }
1193
1194
        // optional VRNodeLayer
1195
0
        id = DopenProtector2(bagGroup.getLocId(), VR_NODE_PATH, H5P_DEFAULT);
1196
0
        if (id >= 0)
1197
0
        {
1198
0
            H5Dclose(id);
1199
1200
0
            auto descriptor = VRNodeDescriptor::open(*this);
1201
0
            this->addLayer(VRNode::open(*this, *descriptor));
1202
0
        }
1203
0
    }
1204
1205
383
    m_pTrackingList = std::unique_ptr<TrackingList>(new TrackingList{*this});
1206
1207
    // Read optional Surface Corrections
1208
383
    id = DopenProtector2(bagGroup.getLocId(), VERT_DATUM_CORR_PATH, H5P_DEFAULT);
1209
383
    if (id >= 0)
1210
33
    {
1211
33
        H5Dclose(id);
1212
1213
33
        auto descriptor = SurfaceCorrectionsDescriptor::open(*this);
1214
33
        this->addLayer(SurfaceCorrections::open(*this, *descriptor));
1215
33
    }
1216
1217
    // If the BAG is version 2.0+ ...
1218
383
    if (bagVersion >= 2'000'000)
1219
130
    {
1220
        // Add all existing GeorefMetadataLayers
1221
130
        id = GopenProtector2(bagGroup.getLocId(), GEOREF_METADATA_PATH, H5P_DEFAULT);
1222
130
        if (id >= 0)
1223
87
        {
1224
87
            H5Gclose(id);
1225
1226
            // Look for any subgroups of the GEOREF_METADATA_PATH group.
1227
87
            const auto group = m_pH5file->openGroup(GEOREF_METADATA_PATH);
1228
87
            const hsize_t numObjects = group.getNumObjs();
1229
1230
173
            for (auto i=0; i<numObjects; ++i)
1231
86
            {
1232
86
                try
1233
86
                {
1234
86
                    const auto name = group.getObjnameByIdx(i);
1235
1236
86
                    auto descriptor = GeorefMetadataLayerDescriptor::open(*this, name);
1237
86
                    this->addLayer(GeorefMetadataLayer::open(*this, *descriptor));
1238
86
                }
1239
86
                catch(...)
1240
86
                {}
1241
86
            }
1242
87
        }
1243
130
    }
1244
383
}
1245
1246
//! Custom deleter to not expose the HDF5 dependency to the user.
1247
/*!
1248
\param ptr
1249
    The H5File to be deleted.
1250
*/
1251
void Dataset::DeleteH5File::operator()(::H5::H5File* ptr) noexcept
1252
280
{
1253
280
    delete ptr;
1254
280
}
1255
1256
}   //namespace BAG
1257