/src/bag/api/bag_metadata.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 291 | : m_pBagDataset(dataset.shared_from_this()) |
47 | 291 | , m_pMetaStruct(std::make_unique<BagMetadata>()) |
48 | 291 | { |
49 | 291 | bagInitMetadata(*m_pMetaStruct); |
50 | | |
51 | 291 | const auto& h5file = dataset.getH5file(); |
52 | | |
53 | 291 | try |
54 | 291 | { |
55 | 291 | m_pH5dataSet = std::unique_ptr<::H5::DataSet, DeleteH5dataSet>( |
56 | 291 | new ::H5::DataSet{h5file.openDataSet(METADATA_PATH)}, DeleteH5dataSet{}); |
57 | 291 | } |
58 | 291 | catch(...) |
59 | 291 | { |
60 | 0 | throw MetadataNotFound{}; |
61 | 0 | } |
62 | | |
63 | 291 | H5std_string buffer; |
64 | 291 | const ::H5::StrType stringType{*m_pH5dataSet}; |
65 | 291 | m_pH5dataSet->read(buffer, stringType); |
66 | | |
67 | 291 | this->loadFromBuffer(buffer); |
68 | 291 | } |
69 | | |
70 | | //! Destructor. |
71 | | Metadata::~Metadata() noexcept |
72 | 291 | { |
73 | 291 | if (m_pMetaStruct) |
74 | 291 | { |
75 | 291 | try |
76 | 291 | { |
77 | 291 | bagFreeMetadata(*m_pMetaStruct); |
78 | 291 | } |
79 | 291 | catch(...) // Prevent any exceptions from escaping. |
80 | 291 | {} |
81 | 291 | } |
82 | 291 | } |
83 | | |
84 | | |
85 | | //! Retrieve the column resolution. |
86 | | /*! |
87 | | \return |
88 | | The column resolution. |
89 | | */ |
90 | | double Metadata::columnResolution() const noexcept |
91 | 339 | { |
92 | 339 | return m_pMetaStruct->spatialRepresentationInfo->columnResolution; |
93 | 339 | } |
94 | | |
95 | | //! Retrieve the number of columns. |
96 | | /*! |
97 | | \return |
98 | | The number of columns. |
99 | | */ |
100 | | uint32_t Metadata::columns() const noexcept |
101 | 291 | { |
102 | 291 | return m_pMetaStruct->spatialRepresentationInfo->numberOfColumns; |
103 | 291 | } |
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 | 291 | { |
161 | 291 | const auto* type = m_pMetaStruct->horizontalReferenceSystem->type; |
162 | 291 | if (!type || type != std::string{"WKT"}) |
163 | 14 | return {}; |
164 | | |
165 | 277 | return m_pMetaStruct->horizontalReferenceSystem->definition; |
166 | 291 | } |
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 | 630 | { |
175 | 630 | return m_pMetaStruct->spatialRepresentationInfo->llCornerX; |
176 | 630 | } |
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 | 630 | { |
185 | 630 | return m_pMetaStruct->spatialRepresentationInfo->llCornerY; |
186 | 630 | } |
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 | 291 | { |
218 | 291 | 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 | 291 | auto pos = xmlBuffer.rfind('>'); |
226 | 291 | if (pos != std::string::npos) { |
227 | 291 | auto xmlBuffer_stripped = xmlBuffer.substr(0, pos+1); |
228 | 291 | err = bagImportMetadataFromXmlBuffer(xmlBuffer_stripped.c_str(), |
229 | 291 | static_cast<int>(xmlBuffer_stripped.size()), *m_pMetaStruct, false); |
230 | 291 | } 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 | 291 | if (err != BAG_SUCCESS) |
238 | 0 | throw ErrorLoadingMetadata{err}; |
239 | | |
240 | 291 | m_xmlLength = xmlBuffer.size(); |
241 | 291 | } |
242 | | |
243 | | //! Retrieve the row resolution. |
244 | | /*! |
245 | | \return |
246 | | The row resolution. |
247 | | */ |
248 | | double Metadata::rowResolution() const noexcept |
249 | 339 | { |
250 | 339 | return m_pMetaStruct->spatialRepresentationInfo->rowResolution; |
251 | 339 | } |
252 | | |
253 | | //! Retrieve the number of rows. |
254 | | /*! |
255 | | \return |
256 | | The number of rows. |
257 | | */ |
258 | | uint32_t Metadata::rows() const noexcept |
259 | 291 | { |
260 | 291 | return m_pMetaStruct->spatialRepresentationInfo->numberOfRows; |
261 | 291 | } |
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 | 291 | { |
270 | 291 | return m_pMetaStruct->spatialRepresentationInfo->urCornerX; |
271 | 291 | } |
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 | 291 | { |
280 | 291 | return m_pMetaStruct->spatialRepresentationInfo->urCornerY; |
281 | 291 | } |
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 | 291 | { |
291 | 291 | const auto* type = m_pMetaStruct->verticalReferenceSystem->type; |
292 | 291 | if (!type || type != std::string{"WKT"}) |
293 | 48 | return {}; |
294 | | |
295 | 243 | return m_pMetaStruct->verticalReferenceSystem->definition; |
296 | 291 | } |
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 | | |