Coverage Report

Created: 2026-03-17 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bag/api/bag_metadata.cpp
Line
Count
Source
1
2
#include "bag_dataset.h"
3
#include "bag_errors.h"
4
#include "bag_exceptions.h"
5
#include "bag_layer.h"
6
#include "bag_metadata.h"
7
#include "bag_metadata_export.h"
8
#include "bag_metadata_import.h"
9
#include "bag_private.h"
10
11
#include <fstream>
12
#include <H5Cpp.h>
13
#include <iostream>
14
15
16
namespace BAG
17
{
18
19
//! The HDF5 chunk size of the metadata.
20
constexpr hsize_t kMetadataChunkSize = 1024;
21
22
//! The default constructor.
23
Metadata::Metadata() noexcept
24
0
    : m_pMetaStruct(std::make_unique<BagMetadata>())
25
0
{
26
    // Can throw, but only if a new fails.  std::terminate() is fine then.
27
0
    bagInitMetadata(*m_pMetaStruct);
28
0
}
29
30
//! Constructor
31
/*!
32
\param pDataset
33
    A shared pointer to the BAG Dataset the metadata belongs to.
34
*/
35
Metadata::Metadata(std::shared_ptr<Dataset> pDataset)
36
0
    : Metadata(*pDataset)
37
0
{    
38
0
}
39
40
//! Constructor.
41
/*!
42
\param dataset
43
    The BAG Dataset the metadata belongs to.
44
*/
45
Metadata::Metadata(Dataset& dataset)
46
28
    : m_pBagDataset(dataset.shared_from_this())
47
28
    , m_pMetaStruct(std::make_unique<BagMetadata>())
48
28
{
49
28
    bagInitMetadata(*m_pMetaStruct);
50
51
28
    const auto& h5file = dataset.getH5file();
52
53
28
    try
54
28
    {
55
28
        m_pH5dataSet = std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
56
28
            new ::H5::DataSet{h5file.openDataSet(METADATA_PATH)}, DeleteH5dataSet{});
57
28
    }
58
28
    catch(...)
59
28
    {
60
0
        throw MetadataNotFound{};
61
0
    }
62
63
28
    H5std_string buffer;
64
28
    const ::H5::StrType stringType{*m_pH5dataSet};
65
28
    m_pH5dataSet->read(buffer, stringType);
66
67
28
    this->loadFromBuffer(buffer);
68
28
}
69
70
//! Destructor.
71
Metadata::~Metadata() noexcept
72
28
{
73
28
    if (m_pMetaStruct)
74
28
    {
75
28
        try
76
28
        {
77
28
            bagFreeMetadata(*m_pMetaStruct);
78
28
        }
79
28
        catch(...)  // Prevent any exceptions from escaping.
80
28
        {}
81
28
    }
82
28
}
83
84
85
//! Retrieve the column resolution.
86
/*!
87
\return
88
    The column resolution.
89
*/
90
double Metadata::columnResolution() const noexcept
91
82
{
92
82
    return m_pMetaStruct->spatialRepresentationInfo->columnResolution;
93
82
}
94
95
//! Retrieve the number of columns.
96
/*!
97
\return
98
    The number of columns.
99
*/
100
uint32_t Metadata::columns() const noexcept
101
28
{
102
28
    return m_pMetaStruct->spatialRepresentationInfo->numberOfColumns;
103
28
}
104
105
//! Create an HDF5 DataSet to store the metadata.
106
/*!
107
\param dataset
108
    The BAG Dataset this layer belongs to.
109
*/
110
void Metadata::createH5dataSet(
111
    const Dataset& dataset)
112
0
{
113
0
    m_pBagDataset = dataset.shared_from_this();
114
115
0
    const auto& h5file = dataset.getH5file();
116
117
0
    const auto buffer = exportMetadataToXML(this->getStruct());
118
0
    const hsize_t xmlLength{buffer.size()};
119
0
    const hsize_t kUnlimitedSize = H5F_UNLIMITED;
120
0
    const ::H5::DataSpace h5dataSpace{1, &xmlLength, &kUnlimitedSize};
121
122
0
    const ::H5::DSetCreatPropList h5createPropList{};
123
0
    h5createPropList.setChunk(1, &kMetadataChunkSize);
124
125
0
    m_pH5dataSet = std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
126
0
        new ::H5::DataSet{h5file.createDataSet(METADATA_PATH,
127
0
            ::H5::PredType::C_S1, h5dataSpace, h5createPropList)},
128
0
            DeleteH5dataSet{});
129
130
0
    m_pH5dataSet->extend(&xmlLength);
131
0
}
132
133
//! Retrieve the C structure this class wraps.
134
/*!
135
\return
136
    The C structure this class wraps.
137
*/
138
const BagMetadata& Metadata::getStruct() const & noexcept
139
0
{
140
0
    return *m_pMetaStruct;
141
0
}
142
143
//! Retrieve the length of the XML in this metadata.
144
/*!
145
\return
146
    The length of the XML in this metadata.
147
*/
148
size_t Metadata::getXMLlength() const noexcept
149
0
{
150
0
    return m_xmlLength;
151
0
}
152
153
//! Retrieve a copy of the horizontal reference system as WKT.
154
/*!
155
\return
156
    A copy of the horizontal reference system as WKT.
157
    The string is empty if it is not WKT.
158
*/
159
std::string Metadata::horizontalReferenceSystemAsWKT() const
160
28
{
161
28
    const auto* type = m_pMetaStruct->horizontalReferenceSystem->type;
162
28
    if (!type || type != std::string{"WKT"})
163
0
        return {};
164
165
28
    return m_pMetaStruct->horizontalReferenceSystem->definition;
166
28
}
167
168
//! Retrieve the lower left corner's X value.
169
/*!
170
\return
171
    The lower left corner's X value.
172
*/
173
double Metadata::llCornerX() const noexcept
174
110
{
175
110
    return m_pMetaStruct->spatialRepresentationInfo->llCornerX;
176
110
}
177
178
//! Retrieve the lower left corner's Y value.
179
/*!
180
\return
181
    The lower left corner's Y value.
182
*/
183
double Metadata::llCornerY() const noexcept
184
110
{
185
110
    return m_pMetaStruct->spatialRepresentationInfo->llCornerY;
186
110
}
187
188
//! Populate the metadata from the XML in the specified file.
189
/*!
190
    Read the XML from the specified file.  An ErrorLoadingMetadata exception is
191
    thrown if an error occurs.
192
193
\param fileName
194
    The XML file to read into this class.
195
*/
196
void Metadata::loadFromFile(const std::string& fileName)
197
0
{
198
0
    const BagError err = bagImportMetadataFromXmlFile(fileName.c_str(),
199
0
        *m_pMetaStruct, false);
200
0
    if (err != BAG_SUCCESS)
201
0
        throw ErrorLoadingMetadata{err};
202
203
0
    std::ifstream ifs{fileName, std::ios_base::in|std::ios_base::binary};
204
0
    ifs.seekg(0, std::ios_base::end);
205
0
    m_xmlLength = ifs.tellg();
206
0
}
207
208
//! Populate the metadata from the XML in the specified buffer.
209
/*!
210
    Read the XML from the specified buffer.  An ErrorLoadingMetadata exception
211
    is thrown if an error occurs.
212
213
\param xmlBuffer
214
    The XML buffer.
215
*/
216
void Metadata::loadFromBuffer(const std::string& xmlBuffer)
217
28
{
218
28
    BagError err;
219
    // BAG stores its metadata XML document in HDF5 as a null-terminated C-style string. In recent
220
    // versions of libxml2 (ca. 1.12+), the xmlParseMemory() function blows up if there are any
221
    // characters (including whitespace) between the closing '>' of the closing XML element of the
222
    // metadata document and the null termination character. Since we don't know what version of
223
    // libxml2 people are linking against, let's strip all trailing characters, which have no semantic
224
    // meaning as far as the XML parser is concerned, before trying to load the metadata XML document.
225
28
    auto pos = xmlBuffer.rfind('>');
226
28
    if (pos != std::string::npos) {
227
28
        auto xmlBuffer_stripped = xmlBuffer.substr(0, pos+1);
228
28
        err = bagImportMetadataFromXmlBuffer(xmlBuffer_stripped.c_str(),
229
28
        static_cast<int>(xmlBuffer_stripped.size()), *m_pMetaStruct, false);
230
28
    } else {
231
        // This branch is only needed if the metadata XML doesn't contain an XML element, i.e.
232
        // if it isn't a well-formed XML document.
233
0
        err = bagImportMetadataFromXmlBuffer(xmlBuffer.c_str(),
234
0
        static_cast<int>(xmlBuffer.size()), *m_pMetaStruct, false);
235
0
    }
236
237
28
    if (err != BAG_SUCCESS)
238
0
        throw ErrorLoadingMetadata{err};
239
240
28
    m_xmlLength = xmlBuffer.size();
241
28
}
242
243
//! Retrieve the row resolution.
244
/*!
245
\return
246
    The row resolution.
247
*/
248
double Metadata::rowResolution() const noexcept
249
82
{
250
82
    return m_pMetaStruct->spatialRepresentationInfo->rowResolution;
251
82
}
252
253
//! Retrieve the number of rows.
254
/*!
255
\return
256
    The number of rows.
257
*/
258
uint32_t Metadata::rows() const noexcept
259
28
{
260
28
    return m_pMetaStruct->spatialRepresentationInfo->numberOfRows;
261
28
}
262
263
//! Retrieve the upper right corner's X value.
264
/*!
265
\return
266
    The upper right corner's X value.
267
*/
268
double Metadata::urCornerX() const noexcept
269
28
{
270
28
    return m_pMetaStruct->spatialRepresentationInfo->urCornerX;
271
28
}
272
273
//! Retrieve the upper right corner's Y value.
274
/*!
275
\return
276
    The upper right corner's Y value.
277
*/
278
double Metadata::urCornerY() const noexcept
279
28
{
280
28
    return m_pMetaStruct->spatialRepresentationInfo->urCornerY;
281
28
}
282
283
//! Retrieve a copy of the vertical reference system as WKT.
284
/*!
285
\return
286
    A copy of the vertical reference system as WKT.
287
    The string is empty if it is not WKT.
288
*/
289
std::string Metadata::verticalReferenceSystemAsWKT() const
290
28
{
291
28
    const auto* type = m_pMetaStruct->verticalReferenceSystem->type;
292
28
    if (!type || type != std::string{"WKT"})
293
0
        return {};
294
295
28
    return m_pMetaStruct->verticalReferenceSystem->definition;
296
28
}
297
298
//! Write the metadata to the HDF5 file.
299
void Metadata::write() const
300
0
{
301
0
    const auto buffer = exportMetadataToXML(this->getStruct());
302
303
0
    const hsize_t bufferLen = buffer.size();
304
0
    const hsize_t kMaxSize = H5F_UNLIMITED;
305
0
    const ::H5::DataSpace h5dataSpace{1, &bufferLen, &kMaxSize};
306
307
0
    m_pH5dataSet->write(buffer, ::H5::PredType::C_S1, h5dataSpace);
308
0
}
309
310
}   //namespace BAG
311
312