Coverage Report

Created: 2025-07-11 06:33

/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
63
{
63
63
    if (type == Georef_Metadata && name.empty())
64
0
        throw NameRequired{};
65
66
63
    std::string nameLower{name};
67
63
    std::transform(begin(nameLower), end(nameLower), begin(nameLower),
68
63
        [](char c) noexcept {
69
0
            return static_cast<char>(std::tolower(c));
70
0
        });
71
72
63
    std::shared_ptr<Layer> foundLayer = nullptr;
73
69
    for (auto layer : layers) {
74
69
        auto pDescriptor = layer->getDescriptor();
75
69
        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
69
    }
91
92
63
    if (foundLayer == nullptr) return {};
93
94
0
    return std::shared_ptr<Layer>(foundLayer);
95
63
}
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
242
{
114
242
    const std::regex expression{R"(\.)"};  // match any period
115
116
242
    auto b = std::sregex_token_iterator{begin(versionStr), end(versionStr),
117
242
        expression, -1};
118
242
    const auto e = std::sregex_token_iterator{};
119
120
121
242
    uint32_t version = 0;
122
242
    uint32_t multiplier = 1'000'000;
123
124
777
    for (auto iter = b; iter != e; ++iter)
125
589
    {
126
589
        try
127
589
        {
128
589
            version += std::stoul(*iter) * multiplier;
129
589
        }
130
589
        catch(...)
131
589
        {
132
54
            return 0;
133
54
        }
134
135
535
        multiplier /= 1000;
136
535
    }
137
138
188
    return version;
139
242
}
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
358
{
163
358
    const ::H5::DataSet h5DataSet = h5file.openDataSet(dataSetName);
164
358
    const ::H5::Attribute attribute = h5DataSet.openAttribute(attributeName);
165
166
358
    T value{};
167
358
    attribute.read(attribute.getDataType(), &value);
168
169
358
    return value;
170
358
}
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
242
{
242
242
    const ::H5::Group h5group = h5file.openGroup(groupName);
243
242
    const ::H5::Attribute attribute = h5group.openAttribute(attributeName);
244
245
242
    std::string value;
246
242
    attribute.read(attribute.getDataType(), value);
247
248
242
    return value;
249
242
}
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
332
{
267
332
#ifdef NDEBUG
268
332
    ::H5::Exception::dontPrint();
269
332
#endif
270
271
332
    std::shared_ptr<Dataset> pDataset{new Dataset};
272
332
    try
273
332
    {
274
332
        pDataset->readDataset(fileName, openMode);
275
332
    } catch (H5::FileIException &fileExcept)
276
332
    {
277
130
        std::cerr << "\nUnable to open BAG file: " << fileName << " due to error: " << fileExcept.getCDetailMsg();
278
130
        return nullptr;
279
130
    }
280
281
202
    return pDataset;
282
332
}
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
202
void Dataset::close() {
314
202
    if (m_pH5file) {
315
202
        m_pH5file->close();
316
202
        m_pH5file.reset(nullptr);
317
202
    }
318
202
}
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
239
{
332
239
    m_layers.push_back(std::move(newLayer));
333
334
239
    const auto& layer = m_layers.back();
335
336
239
    m_descriptor.addLayerDescriptor(*layer->getDescriptor());
337
338
239
    return *layer;
339
239
}
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
24
{
695
24
    return m_descriptor;
696
24
}
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.06k
{
715
1.06k
    return *m_pH5file;
716
1.06k
}
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
24
{
797
24
    std::vector<std::shared_ptr<const Layer>> layers;
798
24
    layers.reserve(m_layers.size());
799
800
24
    for (auto&& layer : m_layers)
801
42
        layers.push_back(std::static_pointer_cast<const Layer>(layer));
802
803
24
    return layers;
804
24
}
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
179
{
863
179
    try
864
179
    {
865
179
        const auto info = getAttributeInfo(type);
866
179
        const auto& thePath = path.empty() ? info.path : path;
867
868
179
        return {true,
869
179
            readAttributeFromDataSet<float>(*m_pH5file, thePath, info.minName),
870
179
            readAttributeFromDataSet<float>(*m_pH5file, thePath, info.maxName)};
871
179
    }
872
179
    catch(const UnsupportedSimpleLayerType&)
873
179
    {
874
0
        return {false, 0.f, 0.f};  // No min/max attributes.
875
0
    }
876
179
}
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
268
{
885
268
    return static_cast<uint32_t>(m_layers.size());
886
268
}
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
24
{
943
24
    return *m_pTrackingList;
944
24
}
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
63
{
963
63
    return std::dynamic_pointer_cast<VRMetadata>(BAG::getLayer(m_layers, VarRes_Metadata));
964
63
}
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
48
{
1050
48
    const auto x = m_pMetadata->llCornerX() +
1051
48
        (row * m_pMetadata->rowResolution());
1052
1053
48
    const auto y = m_pMetadata->llCornerY() +
1054
48
        (column * m_pMetadata->columnResolution());
1055
1056
48
    return {x, y};
1057
48
}
1058
1059
1060
2.87k
hid_t DopenProtector2(hid_t loc_id, const char *name, hid_t dapl_id) {
1061
2.87k
    hid_t id = -1;
1062
2.87k
    try {
1063
2.87k
        id = H5Dopen2(loc_id, name, dapl_id);
1064
2.87k
    } catch (std::exception& e) {
1065
0
        std::cerr << e.what();
1066
0
        id = -1;
1067
0
    }
1068
2.87k
    return id;
1069
2.87k
}
1070
1071
119
hid_t GopenProtector2(hid_t loc_id, const char *name, hid_t dapl_id) {
1072
119
    hid_t id = -1;
1073
119
    try {
1074
119
        id = H5Gopen2(loc_id, name, dapl_id);
1075
119
    } catch (std::exception& e) {
1076
0
        std::cerr << e.what();
1077
0
        id = -1;
1078
0
    }
1079
119
    return id;
1080
119
}
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
332
{
1098
332
    signal(SIGABRT, handleAbrt);
1099
332
    m_pH5file = std::unique_ptr<::H5::H5File, DeleteH5File>(new ::H5::H5File{
1100
332
        fileName.c_str(),
1101
332
        (openMode == BAG_OPEN_READONLY) ? H5F_ACC_RDONLY : H5F_ACC_RDWR},
1102
332
        DeleteH5File{});
1103
1104
332
    m_pMetadata = std::make_unique<Metadata>(*this);
1105
1106
332
    m_descriptor = Descriptor{*m_pMetadata};
1107
332
    m_descriptor.setReadOnly(openMode == BAG_OPEN_READONLY);
1108
332
    m_descriptor.setVersion(readStringAttributeFromGroup(*m_pH5file,
1109
332
        ROOT_PATH, BAG_VERSION_NAME));
1110
1111
332
    const auto bagGroup = m_pH5file->openGroup(ROOT_PATH);
1112
1113
    // Look for the simple layers.
1114
332
    for (auto layerType : {Elevation, Uncertainty, Hypothesis_Strength,
1115
332
        Num_Hypotheses, Shoal_Elevation, Std_Dev, Num_Soundings,
1116
332
        Average_Elevation, Nominal_Elevation})
1117
2.17k
    {
1118
2.17k
        const std::string internalPath = Layer::getInternalPath(layerType);
1119
2.17k
        if (internalPath.empty())
1120
0
            continue;
1121
2.17k
        hid_t id = DopenProtector2(bagGroup.getLocId(), internalPath.c_str(),
1122
2.17k
            H5P_DEFAULT);
1123
2.17k
        if (id < 0)
1124
1.99k
            continue;
1125
1126
179
        H5Dclose(id);
1127
1128
179
        auto layerDesc = SimpleLayerDescriptor::open(*this, layerType);
1129
179
        this->addLayer(SimpleLayer::open(*this, *layerDesc));
1130
179
    }
1131
1132
332
    const auto bagVersion = getNumericalVersion(m_descriptor.getVersion());
1133
1134
    // If the BAG is version 1.5+ ...
1135
332
    if (bagVersion >= 1'005'000)
1136
127
    {
1137
127
        hid_t id = DopenProtector2(bagGroup.getLocId(), NODE_GROUP_PATH, H5P_DEFAULT);
1138
127
        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
127
        id = DopenProtector2(bagGroup.getLocId(), ELEVATION_SOLUTION_GROUP_PATH,
1153
127
            H5P_DEFAULT);
1154
127
        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
127
    }
1175
1176
    // Read optional VR
1177
332
    hid_t id = DopenProtector2(bagGroup.getLocId(), VR_TRACKING_LIST_PATH, H5P_DEFAULT);
1178
332
    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
332
    m_pTrackingList = std::unique_ptr<TrackingList>(new TrackingList{*this});
1206
1207
    // Read optional Surface Corrections
1208
332
    id = DopenProtector2(bagGroup.getLocId(), VERT_DATUM_CORR_PATH, H5P_DEFAULT);
1209
332
    if (id >= 0)
1210
26
    {
1211
26
        H5Dclose(id);
1212
1213
26
        auto descriptor = SurfaceCorrectionsDescriptor::open(*this);
1214
26
        this->addLayer(SurfaceCorrections::open(*this, *descriptor));
1215
26
    }
1216
1217
    // If the BAG is version 2.0+ ...
1218
332
    if (bagVersion >= 2'000'000)
1219
119
    {
1220
        // Add all existing GeorefMetadataLayers
1221
119
        id = GopenProtector2(bagGroup.getLocId(), GEOREF_METADATA_PATH, H5P_DEFAULT);
1222
119
        if (id >= 0)
1223
86
        {
1224
86
            H5Gclose(id);
1225
1226
            // Look for any subgroups of the GEOREF_METADATA_PATH group.
1227
86
            const auto group = m_pH5file->openGroup(GEOREF_METADATA_PATH);
1228
86
            const hsize_t numObjects = group.getNumObjs();
1229
1230
171
            for (auto i=0; i<numObjects; ++i)
1231
85
            {
1232
85
                try
1233
85
                {
1234
85
                    const auto name = group.getObjnameByIdx(i);
1235
1236
85
                    auto descriptor = GeorefMetadataLayerDescriptor::open(*this, name);
1237
85
                    this->addLayer(GeorefMetadataLayer::open(*this, *descriptor));
1238
85
                }
1239
85
                catch(...)
1240
85
                {}
1241
85
            }
1242
86
        }
1243
119
    }
1244
332
}
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
242
{
1253
242
    delete ptr;
1254
242
}
1255
1256
}   //namespace BAG
1257