/src/bag/api/bag_metadata_export.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //************************************************************************ |
2 | | // |
3 | | // Open Navigation Surface Working Group, 2013 |
4 | | // |
5 | | //************************************************************************ |
6 | | #include "bag_metadata_export.h" |
7 | | |
8 | | #include <cstring> |
9 | | #include <iostream> |
10 | | #include <libxml/parser.h> |
11 | | #include <sstream> |
12 | | #include <string> |
13 | | |
14 | | |
15 | | namespace BAG { |
16 | | |
17 | | namespace { |
18 | | |
19 | | //! Utility class to convert an encoded XML string. |
20 | | class EncodedString final |
21 | | { |
22 | | public: |
23 | | //************************************************************************ |
24 | | //! Constructor |
25 | | /*! |
26 | | \param doc |
27 | | \li The XML document that the string is from. |
28 | | \param string |
29 | | \li The string to be converted. |
30 | | */ |
31 | | //************************************************************************ |
32 | | EncodedString(xmlDoc &doc, const char *string) |
33 | 0 | { |
34 | 0 | this->m_pEncodedString = xmlEncodeEntitiesReentrant(&doc, |
35 | 0 | reinterpret_cast<const xmlChar*>(string)); |
36 | 0 | } |
37 | | |
38 | | //************************************************************************ |
39 | | //! Destructor. |
40 | | //************************************************************************ |
41 | | ~EncodedString() |
42 | 0 | { |
43 | 0 | xmlFree(this->m_pEncodedString); |
44 | 0 | } |
45 | | |
46 | | //************************************************************************ |
47 | | //! Conversion operator. |
48 | | /*! |
49 | | \return |
50 | | \li The encoded string. |
51 | | */ |
52 | | //************************************************************************ |
53 | | operator xmlChar*() const |
54 | 0 | { |
55 | 0 | return this->m_pEncodedString; |
56 | 0 | } |
57 | | |
58 | | private: |
59 | | //! The encoded string. |
60 | | xmlChar* m_pEncodedString = nullptr; |
61 | | }; |
62 | | |
63 | 0 | #define XMLCast(value) reinterpret_cast<const xmlChar*>(value) |
64 | | |
65 | | //************************************************************************ |
66 | | //! Add a CharacterString node to the supplied parent XML node. |
67 | | /*! |
68 | | \param parentNode |
69 | | \li The parent XML node to be modified. |
70 | | \param content |
71 | | \li The content to be added to \e parentNode. |
72 | | */ |
73 | | //************************************************************************ |
74 | | xmlNode* addCharacterNode(xmlNode &parentNode, const char *content) |
75 | 0 | { |
76 | 0 | const auto pGcoNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gco")); |
77 | | |
78 | | //Create the CharacterString node. |
79 | 0 | xmlNode *pCharacterNode = xmlNewChild(&parentNode, pGcoNamespace, XMLCast("CharacterString"), EncodedString(*parentNode.doc, content)); |
80 | 0 | return pCharacterNode; |
81 | 0 | } |
82 | | |
83 | | //************************************************************************ |
84 | | //! Add a Date node to the supplied parent XML node. |
85 | | /*! |
86 | | \param parentNode |
87 | | \li The parent XML node to be modified. |
88 | | \param content |
89 | | \li The content to be added to \e parentNode. |
90 | | */ |
91 | | //************************************************************************ |
92 | | xmlNode* addDateNode(xmlNode &parentNode, const char *content) |
93 | 0 | { |
94 | 0 | const xmlNsPtr pGcoNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gco")); |
95 | | |
96 | | //If the content is nullptr, then we will just add a nilReason. |
97 | 0 | if (content == nullptr) |
98 | 0 | { |
99 | 0 | xmlSetProp(&parentNode, XMLCast("gco:nilReason"), XMLCast("unknown")); |
100 | 0 | return nullptr; |
101 | 0 | } |
102 | | |
103 | | //Create the Date node. |
104 | 0 | xmlNode *pDateNode = xmlNewChild(&parentNode, pGcoNamespace, XMLCast("Date"), EncodedString(*parentNode.doc, content)); |
105 | 0 | return pDateNode; |
106 | 0 | } |
107 | | |
108 | | //************************************************************************ |
109 | | //! Add a DateTime node to the supplied parent XML node. |
110 | | /*! |
111 | | \param parentNode |
112 | | \li The parent XML node to be modified. |
113 | | \param content |
114 | | \li The content to be added to \e parentNode. |
115 | | */ |
116 | | //************************************************************************ |
117 | | xmlNode* addDateTimeNode(xmlNode &parentNode, const char *content) |
118 | 0 | { |
119 | 0 | const xmlNsPtr pGcoNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gco")); |
120 | | |
121 | | //Create the DateTime node. |
122 | 0 | xmlNode *pDateTimeNode = xmlNewChild(&parentNode, pGcoNamespace, XMLCast("DateTime"), EncodedString(*parentNode.doc, content)); |
123 | 0 | return pDateTimeNode; |
124 | 0 | } |
125 | | |
126 | | //************************************************************************ |
127 | | //! Add a Code value to the supplied parent XML node. |
128 | | /*! |
129 | | \param parentNode |
130 | | \li The parent XML node to be modified. |
131 | | \param codeNameSpace |
132 | | \li The namespace to which the code belongs |
133 | | \param codeName |
134 | | \li The code name. |
135 | | \param url |
136 | | \li The url that manages houses code list. |
137 | | \param value |
138 | | \li The actual value from the code list. |
139 | | */ |
140 | | //************************************************************************ |
141 | | xmlNode* addCodeListNode(xmlNode &parentNode, const char *codeNameSpace, const char *codeName, |
142 | | const char *url, const char *value, bool appendValueToUrl = true) |
143 | 0 | { |
144 | 0 | const xmlNsPtr pNamespace = xmlSearchNs(parentNode.doc, &parentNode, EncodedString(*parentNode.doc, codeNameSpace)); |
145 | | |
146 | | //Create the codeList node. |
147 | 0 | xmlNode *pCodeNode = xmlNewChild(&parentNode, pNamespace, EncodedString(*parentNode.doc, codeName), EncodedString(*parentNode.doc, value)); |
148 | |
|
149 | 0 | std::stringstream fullUrlStream; |
150 | 0 | fullUrlStream << url; |
151 | |
|
152 | 0 | if (appendValueToUrl) |
153 | 0 | fullUrlStream << "#" << codeName; |
154 | |
|
155 | 0 | xmlSetProp(pCodeNode, XMLCast("codeList"), EncodedString(*parentNode.doc, fullUrlStream.str().c_str())); |
156 | 0 | xmlSetProp(pCodeNode, XMLCast("codeListValue"), EncodedString(*parentNode.doc, value)); |
157 | |
|
158 | 0 | return pCodeNode; |
159 | 0 | } |
160 | | |
161 | | //************************************************************************ |
162 | | //! Add a Decimal node to the supplied parent XML node. |
163 | | /*! |
164 | | \param parentNode |
165 | | \li The parent XML node to be modified. |
166 | | \param value |
167 | | \li The content to be added to \e parentNode. |
168 | | */ |
169 | | //************************************************************************ |
170 | | xmlNode* addDecimalNode(xmlNode &parentNode, double value) |
171 | 0 | { |
172 | 0 | std::stringstream lineStream; |
173 | 0 | lineStream.imbue(std::locale::classic()); |
174 | 0 | lineStream << value; |
175 | |
|
176 | 0 | const xmlNsPtr pGcoNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gco")); |
177 | | |
178 | | //Create the Decimal node. |
179 | 0 | xmlNode *pDecimalNode = xmlNewChild(&parentNode, pGcoNamespace, XMLCast("Decimal"), XMLCast(lineStream.str().c_str())); |
180 | 0 | return pDecimalNode; |
181 | 0 | } |
182 | | |
183 | | //************************************************************************ |
184 | | //! Add a Integer node to the supplied parent XML node. |
185 | | /*! |
186 | | \param parentNode |
187 | | \li The parent XML node to be modified. |
188 | | \param value |
189 | | \li The content to be added to \e parentNode. |
190 | | */ |
191 | | //************************************************************************ |
192 | | xmlNode* addIntegerNode(xmlNode &parentNode, int value) |
193 | 0 | { |
194 | 0 | std::stringstream lineStream; |
195 | 0 | lineStream.imbue(std::locale::classic()); |
196 | 0 | lineStream << value; |
197 | |
|
198 | 0 | const xmlNsPtr pGcoNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gco")); |
199 | | |
200 | | //Create the Integer node. |
201 | 0 | xmlNode *pIntegerNode = xmlNewChild(&parentNode, pGcoNamespace, XMLCast("Integer"), XMLCast(lineStream.str().c_str())); |
202 | 0 | return pIntegerNode; |
203 | 0 | } |
204 | | |
205 | | //************************************************************************ |
206 | | //! Add a Measure node to the supplied parent XML node. |
207 | | /*! |
208 | | \param parentNode |
209 | | \li The parent XML node to be modified. |
210 | | \param value |
211 | | \li The content to be added to \e parentNode. |
212 | | */ |
213 | | //************************************************************************ |
214 | | xmlNode* addMeasureNode(xmlNode &parentNode, const char *uomName, double value) |
215 | 0 | { |
216 | 0 | std::stringstream lineStream; |
217 | 0 | lineStream.imbue(std::locale::classic()); |
218 | 0 | lineStream << value; |
219 | |
|
220 | 0 | const xmlNsPtr pGcoNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gco")); |
221 | | |
222 | | //Create the Measure node. |
223 | 0 | xmlNode *pMeasureNode = xmlNewChild(&parentNode, pGcoNamespace, XMLCast("Measure"), XMLCast(lineStream.str().c_str())); |
224 | 0 | xmlSetProp(pMeasureNode, XMLCast("uom"), EncodedString(*parentNode.doc, uomName)); |
225 | 0 | return pMeasureNode; |
226 | 0 | } |
227 | | |
228 | | //************************************************************************ |
229 | | //! Add an MD_Dimension node to the supplied parent XML node. |
230 | | /*! |
231 | | \param parentNode |
232 | | \li The parent XML node to be modified. |
233 | | \param name |
234 | | \li The name for the MD_DimensionNameTypeCode |
235 | | \param size |
236 | | \li The number of dimensions to be added. |
237 | | \param resolution |
238 | | \li The resolution of the dimension. |
239 | | \param resolutionUnit |
240 | | \li The units of \e resolution. |
241 | | */ |
242 | | //************************************************************************ |
243 | | xmlNode* addDimension(xmlNode &parentNode, const char *name, unsigned int size, |
244 | | double resolution, const char *resolutionUnit) |
245 | 0 | { |
246 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gmd")); |
247 | |
|
248 | 0 | xmlNode *pDimNode = xmlNewChild(&parentNode, pGmdNamespace, XMLCast("axisDimensionProperties"), nullptr); |
249 | 0 | pDimNode = xmlNewChild(pDimNode, pGmdNamespace, XMLCast("MD_Dimension"), nullptr); |
250 | | |
251 | | //dimensionName |
252 | 0 | { |
253 | 0 | xmlNode *pNode = xmlNewChild(pDimNode, pGmdNamespace, XMLCast("dimensionName"), nullptr); |
254 | 0 | addCodeListNode(*pNode, "gmd", "MD_DimensionNameTypeCode", |
255 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", name); |
256 | 0 | } |
257 | | |
258 | | //dimensionSize |
259 | 0 | { |
260 | 0 | xmlNode *pNode = xmlNewChild(pDimNode, pGmdNamespace, XMLCast("dimensionSize"), nullptr); |
261 | 0 | addIntegerNode(*pNode, size); |
262 | 0 | } |
263 | | |
264 | | //resolution |
265 | 0 | { |
266 | 0 | xmlNode *pNode = xmlNewChild(pDimNode, pGmdNamespace, XMLCast("resolution"), nullptr); |
267 | 0 | addMeasureNode(*pNode, resolutionUnit, resolution); |
268 | 0 | } |
269 | |
|
270 | 0 | return pDimNode; |
271 | 0 | } |
272 | | |
273 | | //************************************************************************ |
274 | | //! Add a Boolean node to the supplied parent XML node. |
275 | | /*! |
276 | | \param parentNode |
277 | | \li The parent XML node to be modified. |
278 | | \param value |
279 | | \li The content to be added to \e parentNode. |
280 | | */ |
281 | | //************************************************************************ |
282 | | xmlNode* addBooleanNode(xmlNode &parentNode, bool value) |
283 | 0 | { |
284 | 0 | const std::string valString(value ? "1" : "0"); |
285 | |
|
286 | 0 | const xmlNsPtr pGcoNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gco")); |
287 | | |
288 | | //Create the Integer node. |
289 | 0 | xmlNode *pBoolNode = xmlNewChild(&parentNode, pGcoNamespace, XMLCast("Boolean"), XMLCast(valString.c_str())); |
290 | 0 | return pBoolNode; |
291 | 0 | } |
292 | | |
293 | | } // namespace |
294 | | |
295 | | |
296 | | //************************************************************************ |
297 | | //! Create a new XML document and configure it for the BAG metadata profile. |
298 | | /*! |
299 | | \return |
300 | | \li The new XML document. |
301 | | */ |
302 | | //************************************************************************ |
303 | | xmlDoc* createNewDocument() |
304 | 0 | { |
305 | 0 | const char version[] = "1.0"; |
306 | 0 | const char rootName[] = "MI_Metadata"; |
307 | 0 | const char rootNameSpaceUrl[] = "http://www.isotc211.org/2005/gmi"; |
308 | | |
309 | | //Create the new document. |
310 | 0 | xmlDoc *pDocument = xmlNewDoc(XMLCast(version)); |
311 | | |
312 | | //Create the root node and assign to the document. |
313 | 0 | xmlNodePtr pRoot = xmlNewDocNode(pDocument, nullptr, XMLCast(rootName), nullptr); |
314 | 0 | xmlDocSetRootElement(pDocument, pRoot); |
315 | | |
316 | | //Create the root namespace and assign to the root. |
317 | 0 | xmlNs *pRootNameSpace = xmlNewNs(pRoot, XMLCast(rootNameSpaceUrl), XMLCast("gmi")); |
318 | 0 | xmlSetNs(pRoot, pRootNameSpace); |
319 | | |
320 | | //Add the rest of the required namespaces. |
321 | 0 | xmlNewNs(pRoot, XMLCast("http://www.isotc211.org/2005/gmd"), XMLCast("gmd")); |
322 | 0 | xmlNewNs(pRoot, XMLCast("http://www.w3.org/2001/XMLSchema-instance"), XMLCast("xsi")); |
323 | 0 | xmlNewNs(pRoot, XMLCast("http://www.opengis.net/gml/3.2"), XMLCast("gml")); |
324 | 0 | xmlNewNs(pRoot, XMLCast("http://www.isotc211.org/2005/gco"), XMLCast("gco")); |
325 | 0 | xmlNewNs(pRoot, XMLCast("http://www.w3.org/1999/xlink"), XMLCast("xlink")); |
326 | 0 | xmlNewNs(pRoot, XMLCast("http://www.opennavsurf.org/schema/bag"), XMLCast("bag")); |
327 | |
|
328 | 0 | return pDocument; |
329 | 0 | } |
330 | | |
331 | | //************************************************************************ |
332 | | //! Add the BagResponsibleParty information to the parent XML node. |
333 | | /*! |
334 | | \param parentNode |
335 | | \li The parent XML node to be modified. |
336 | | \param responsiblePartyStruct |
337 | | \li The structure to be added to \e parentNode. |
338 | | \return |
339 | | \li True if the structure is added, False if an error occurs. |
340 | | */ |
341 | | //************************************************************************ |
342 | | bool addResponsibleParty(xmlNode &parentNode, const BagResponsibleParty &responsiblePartyStruct) |
343 | 0 | { |
344 | | /* Criteria for this node is that "role must be supplied and at least one of the following fileds must be supplied. */ |
345 | 0 | if (responsiblePartyStruct.individualName == nullptr && |
346 | 0 | responsiblePartyStruct.organisationName == nullptr && |
347 | 0 | responsiblePartyStruct.positionName == nullptr) |
348 | 0 | { |
349 | 0 | return false; |
350 | 0 | } |
351 | | |
352 | | /* If "role" is not populated, don't create the element. "role" is a required element of the schema. */ |
353 | 0 | if (responsiblePartyStruct.role == nullptr) |
354 | 0 | { |
355 | 0 | fprintf(stderr, "ERROR: The \"role\" is required in order to create the CI_ResponsibleParty node.\n"); |
356 | 0 | return false; |
357 | 0 | } |
358 | | |
359 | | //Find the gmd namespace. |
360 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gmd")); |
361 | | |
362 | | //Create the CI_ResponsibleParty node. |
363 | 0 | xmlNode *pPartyNode = xmlNewChild(&parentNode, pGmdNamespace, XMLCast("CI_ResponsibleParty"), nullptr); |
364 | | |
365 | | /* If an individual name has been supplied, Create the individual node and populate it. */ |
366 | 0 | if (responsiblePartyStruct.individualName != nullptr) |
367 | 0 | { |
368 | 0 | xmlNode *pNode = xmlNewChild(pPartyNode, pGmdNamespace, XMLCast("individualName"), nullptr); |
369 | 0 | addCharacterNode(*pNode, (char*)responsiblePartyStruct.individualName); |
370 | 0 | } |
371 | | |
372 | | /* If an organisation name has been supplied, Create the organisation node and populate it. */ |
373 | 0 | if (responsiblePartyStruct.organisationName != nullptr) |
374 | 0 | { |
375 | 0 | xmlNode *pNode = xmlNewChild(pPartyNode, pGmdNamespace, XMLCast("organisationName"), nullptr); |
376 | 0 | addCharacterNode(*pNode, (char*)responsiblePartyStruct.organisationName); |
377 | 0 | } |
378 | | |
379 | | /* If a postiion name has been supplied, Create the position node and populate it. */ |
380 | 0 | if (responsiblePartyStruct.positionName != nullptr) |
381 | 0 | { |
382 | 0 | xmlNode *pNode = xmlNewChild(pPartyNode, pGmdNamespace, XMLCast("positionName"), nullptr); |
383 | 0 | addCharacterNode(*pNode, (char*)responsiblePartyStruct.positionName); |
384 | 0 | } |
385 | | |
386 | | //Create the role node and populate it. |
387 | 0 | xmlNode *pNode = xmlNewChild(pPartyNode, pGmdNamespace, XMLCast("role"), nullptr); |
388 | 0 | addCodeListNode(*pNode, "gmd", "CI_RoleCode", |
389 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", (char*)responsiblePartyStruct.role); |
390 | |
|
391 | 0 | return true; |
392 | 0 | } |
393 | | |
394 | | //************************************************************************ |
395 | | //! Add the citation information to the parent XML node. |
396 | | /*! |
397 | | \param parentNode |
398 | | \li The parent XML node to be modified. |
399 | | \param title |
400 | | \li The title of the person for the citation. |
401 | | \param date |
402 | | \li The date of the citation. |
403 | | \param dateType |
404 | | \li The type of date in \e date. |
405 | | \param responsibleParties |
406 | | \li The array of responsible parties. |
407 | | \param numberOfParties |
408 | | \li The number of elements in \e responsibleParties. |
409 | | \return |
410 | | \li True if the structure is added, False if an error occurs. |
411 | | */ |
412 | | //************************************************************************ |
413 | | bool addCitation(xmlNode &parentNode, const char *title, const char *date, const char *dateType, |
414 | | const BagResponsibleParty *responsibleParties, uint32_t numberOfParties) |
415 | 0 | { |
416 | | //CI_citation is optional, so if no title was given just return. |
417 | 0 | if (!title) |
418 | 0 | return true; |
419 | | |
420 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gmd")); |
421 | | |
422 | | //Create the CI_Citation node. |
423 | 0 | xmlNode *pCitationNode = xmlNewChild(&parentNode, pGmdNamespace, XMLCast("CI_Citation"), nullptr); |
424 | | |
425 | | //Add the title |
426 | 0 | { |
427 | | //Create the title node. |
428 | 0 | xmlNode *pTitleNode = xmlNewChild(pCitationNode, pGmdNamespace, XMLCast("title"), nullptr); |
429 | | |
430 | | //Set the title value. |
431 | 0 | addCharacterNode(*pTitleNode, title); |
432 | 0 | } |
433 | | |
434 | | //Add the date |
435 | 0 | { |
436 | | //Create the date nodes. |
437 | 0 | xmlNode *pDateNode = xmlNewChild(pCitationNode, pGmdNamespace, XMLCast("date"), nullptr); |
438 | 0 | pDateNode = xmlNewChild(pDateNode, pGmdNamespace, XMLCast("CI_Date"), nullptr); |
439 | | |
440 | | //Create the date node. |
441 | 0 | xmlNode *pDateNode2 = xmlNewChild(pDateNode, pGmdNamespace, XMLCast("date"), nullptr); |
442 | | |
443 | | //Set the date value. |
444 | 0 | addDateNode(*pDateNode2, date); |
445 | | |
446 | | //Create the dateType node. |
447 | 0 | xmlNode *pDateTypeNode = xmlNewChild(pDateNode, pGmdNamespace, XMLCast("dateType"), nullptr); |
448 | | |
449 | | //Set the date type value. |
450 | 0 | addCodeListNode(*pDateTypeNode, "gmd", "CI_DateTypeCode", |
451 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", dateType); |
452 | 0 | } |
453 | | |
454 | | //Add the responsible parties |
455 | 0 | { |
456 | 0 | for (uint32_t r = 0; r < numberOfParties; r++) |
457 | 0 | { |
458 | | //Create the citedResponsibleParty node. |
459 | 0 | xmlNode *pPartyNode = xmlNewChild(pCitationNode, pGmdNamespace, XMLCast("citedResponsibleParty"), nullptr); |
460 | |
|
461 | 0 | const bool ret = addResponsibleParty(*pPartyNode, responsibleParties[r]); |
462 | 0 | if (!ret) |
463 | 0 | { |
464 | 0 | fprintf(stderr, "ERROR: responsibleParties[%d]: At least one of the following fields must be supplied. individualName, organisationName, postionName.\n", r); |
465 | 0 | return false; |
466 | 0 | } |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | 0 | return true; |
471 | 0 | } |
472 | | |
473 | | //************************************************************************ |
474 | | //! Add the BagIdentification information to the parent XML node. |
475 | | /*! |
476 | | \param parentNode |
477 | | \li The parent XML node to be modified. |
478 | | \param identificationInfo |
479 | | \li The identification information to be added to \e parentNode |
480 | | \return |
481 | | \li True if the structure is added, False if an error occurs. |
482 | | */ |
483 | | //************************************************************************ |
484 | | bool addDataIdentification(xmlNode &parentNode, const BagIdentification &identificationInfo) |
485 | 0 | { |
486 | | /* Check for the required fields. If they are not present, return nullptr. */ |
487 | 0 | if (identificationInfo.abstractString == nullptr || |
488 | 0 | identificationInfo.language == nullptr || |
489 | 0 | identificationInfo.verticalUncertaintyType == nullptr) |
490 | 0 | { |
491 | 0 | fprintf(stderr, "ERROR: can not create BAG identificationInfo. Missing one or more required fields... abstract, language or verticalUncertaintyType. \n"); |
492 | 0 | return false; |
493 | 0 | } |
494 | | |
495 | | //Find the gmd namespace. |
496 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gmd")); |
497 | 0 | const xmlNsPtr pBagNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("bag")); |
498 | | |
499 | | //Create the identificationInfo node. |
500 | 0 | xmlNode *pIdentInfoNode = xmlNewChild(&parentNode, pGmdNamespace, XMLCast("identificationInfo"), nullptr); |
501 | | |
502 | | //Create the BAG_DataIdentification node. |
503 | 0 | xmlNode *pBagIdentInfoNode = xmlNewChild(pIdentInfoNode, pBagNamespace, XMLCast("BAG_DataIdentification"), nullptr); |
504 | | |
505 | | //Citation |
506 | 0 | { |
507 | | //Create the citation node. |
508 | 0 | xmlNode *pCitationNode = xmlNewChild(pBagIdentInfoNode, pGmdNamespace, XMLCast("citation"), nullptr); |
509 | | |
510 | | //Add the citation info. |
511 | 0 | const bool ret = addCitation(*pCitationNode, identificationInfo.title, |
512 | 0 | identificationInfo.date, |
513 | 0 | identificationInfo.dateType, |
514 | 0 | identificationInfo.responsibleParties, |
515 | 0 | identificationInfo.numberOfResponsibleParties); |
516 | 0 | if (!ret) |
517 | 0 | return false; |
518 | 0 | } |
519 | | |
520 | | //Abstract |
521 | 0 | { |
522 | | //Add the abstract. |
523 | 0 | xmlNode *pAbstractNode = xmlNewChild(pBagIdentInfoNode, pGmdNamespace, XMLCast("abstract"), nullptr); |
524 | | |
525 | | //Set the value. |
526 | 0 | addCharacterNode(*pAbstractNode, identificationInfo.abstractString); |
527 | 0 | } |
528 | | |
529 | | //Status (Optional) |
530 | 0 | if (identificationInfo.status != nullptr) |
531 | 0 | { |
532 | | //Add the status. |
533 | 0 | xmlNode *pStatusNode = xmlNewChild(pBagIdentInfoNode, pGmdNamespace, XMLCast("status"), nullptr); |
534 | | |
535 | | //Set the value. |
536 | 0 | addCodeListNode(*pStatusNode, "gmd", "MD_ProgressCode", |
537 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", identificationInfo.status); |
538 | 0 | } |
539 | | |
540 | | //spatialRepresentationType (Optional) |
541 | 0 | if (identificationInfo.spatialRepresentationType != nullptr) |
542 | 0 | { |
543 | | //Add the spatialRepresentationType. |
544 | 0 | xmlNode *pTypeNode = xmlNewChild(pBagIdentInfoNode, pGmdNamespace, XMLCast("spatialRepresentationType"), nullptr); |
545 | | |
546 | | //Set the value. |
547 | 0 | addCodeListNode(*pTypeNode, "gmd", "MD_SpatialRepresentationTypeCode", |
548 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", identificationInfo.spatialRepresentationType); |
549 | 0 | } |
550 | | |
551 | | //language |
552 | 0 | { |
553 | | //Add the language. |
554 | 0 | xmlNode *pLanguageNode = xmlNewChild(pBagIdentInfoNode, pGmdNamespace, XMLCast("language"), nullptr); |
555 | | |
556 | | //Set the value. |
557 | 0 | addCodeListNode(*pLanguageNode, "gmd", "LanguageCode", |
558 | 0 | "http://www.loc.gov/standards/iso639-2/", identificationInfo.language, false); |
559 | 0 | } |
560 | | |
561 | | //characterSet |
562 | 0 | { |
563 | | //Add the characterSet. |
564 | 0 | xmlNode *pCharacterNode = xmlNewChild(pBagIdentInfoNode, pGmdNamespace, XMLCast("characterSet"), nullptr); |
565 | | |
566 | | //Set the value. |
567 | 0 | addCodeListNode(*pCharacterNode, "gmd", "MD_CharacterSetCode", |
568 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", identificationInfo.characterSet); |
569 | 0 | } |
570 | | |
571 | | //topicCategory |
572 | 0 | { |
573 | | //Add the topicCategory. |
574 | 0 | xmlNode *pTopicNode = xmlNewChild(pBagIdentInfoNode, pGmdNamespace, XMLCast("topicCategory"), nullptr); |
575 | | |
576 | | //Create the MD_TopicCategoryCode node. |
577 | 0 | xmlNode *pCodeNode = xmlNewChild(pTopicNode, pGmdNamespace, XMLCast("MD_TopicCategoryCode"), |
578 | 0 | EncodedString(*parentNode.doc, identificationInfo.topicCategory)); |
579 | | |
580 | | //Set the value. |
581 | 0 | addCodeListNode(*pCodeNode, "gmd", "MD_TopicCategoryCode", |
582 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", identificationInfo.topicCategory); |
583 | 0 | } |
584 | | |
585 | | //extent (Optional) |
586 | 0 | if ( identificationInfo.westBoundingLongitude != double(INIT_VALUE) && |
587 | 0 | identificationInfo.eastBoundingLongitude != double(INIT_VALUE) && |
588 | 0 | identificationInfo.southBoundingLatitude != double(INIT_VALUE) && |
589 | 0 | identificationInfo.northBoundingLatitude != double(INIT_VALUE) ) |
590 | 0 | { |
591 | | //Add the extent. |
592 | 0 | xmlNode *pExtentNode = xmlNewChild(pBagIdentInfoNode, pGmdNamespace, XMLCast("extent"), nullptr); |
593 | | |
594 | | //Add the EX_Extent |
595 | 0 | xmlNode *pExtentNode2 = xmlNewChild(pExtentNode, pGmdNamespace, XMLCast("EX_Extent"), nullptr); |
596 | | |
597 | | //Add the geographicElement |
598 | 0 | xmlNode *pGeoNode = xmlNewChild(pExtentNode2, pGmdNamespace, XMLCast("geographicElement"), nullptr); |
599 | | |
600 | | //Add the EX_GeographicBoundingBox |
601 | 0 | xmlNode *pGeoNode2 = xmlNewChild(pGeoNode, pGmdNamespace, XMLCast("EX_GeographicBoundingBox"), nullptr); |
602 | | |
603 | | //Add the westBoundLongitude |
604 | 0 | xmlNode *pCoordNode = xmlNewChild(pGeoNode2, pGmdNamespace, XMLCast("westBoundLongitude"), nullptr); |
605 | 0 | addDecimalNode(*pCoordNode, identificationInfo.westBoundingLongitude); |
606 | | |
607 | | //Add the eastBoundLongitude |
608 | 0 | pCoordNode = xmlNewChild(pGeoNode2, pGmdNamespace, XMLCast("eastBoundLongitude"), nullptr); |
609 | 0 | addDecimalNode(*pCoordNode, identificationInfo.eastBoundingLongitude); |
610 | | |
611 | | //Add the southBoundLatitude |
612 | 0 | pCoordNode = xmlNewChild(pGeoNode2, pGmdNamespace, XMLCast("southBoundLatitude"), nullptr); |
613 | 0 | addDecimalNode(*pCoordNode, identificationInfo.southBoundingLatitude); |
614 | | |
615 | | //Add the northBoundLatitude |
616 | 0 | pCoordNode = xmlNewChild(pGeoNode2, pGmdNamespace, XMLCast("northBoundLatitude"), nullptr); |
617 | 0 | addDecimalNode(*pCoordNode, identificationInfo.northBoundingLatitude); |
618 | 0 | } |
619 | | |
620 | | //verticalUncertaintyType |
621 | 0 | { |
622 | | //Add the verticalUncertaintyType. |
623 | 0 | xmlNode *pVertUncertNode = xmlNewChild(pBagIdentInfoNode, pBagNamespace, XMLCast("verticalUncertaintyType"), nullptr); |
624 | | |
625 | | //Set the value. |
626 | 0 | addCodeListNode(*pVertUncertNode, "bag", "BAG_VertUncertCode", |
627 | 0 | "http://www.opennavsurf.org/schema/bag/bagCodelists.xml", (const char*)identificationInfo.verticalUncertaintyType); |
628 | 0 | } |
629 | | |
630 | | //depthCorrectionType (Optional) |
631 | 0 | if (identificationInfo.depthCorrectionType != nullptr) |
632 | 0 | { |
633 | | //Add the depthCorrectionType. |
634 | 0 | xmlNode *pDepthCorrNode = xmlNewChild(pBagIdentInfoNode, pBagNamespace, XMLCast("depthCorrectionType"), nullptr); |
635 | | |
636 | | //Set the value. |
637 | 0 | addCodeListNode(*pDepthCorrNode, "bag", "BAG_DepthCorrectCode", |
638 | 0 | "http://www.opennavsurf.org/schema/bag/bagCodelists.xml", (const char*)identificationInfo.depthCorrectionType); |
639 | 0 | } |
640 | | |
641 | | //elevationSolutionGroupType (Optional) |
642 | 0 | if (identificationInfo.elevationSolutionGroupType != nullptr) |
643 | 0 | { |
644 | | //Add the depthCorrectionType. |
645 | 0 | xmlNode *pElevNode = xmlNewChild(pBagIdentInfoNode, pBagNamespace, XMLCast("elevationSolutionGroupType"), nullptr); |
646 | | |
647 | | //Set the value. |
648 | 0 | addCodeListNode(*pElevNode, "bag", "BAG_OptGroupCode", |
649 | 0 | "http://www.opennavsurf.org/schema/bag/bagCodelists.xml", (const char*)identificationInfo.elevationSolutionGroupType); |
650 | 0 | } |
651 | | |
652 | | //nodeGroupType (Optional) |
653 | 0 | if (identificationInfo.nodeGroupType != nullptr) |
654 | 0 | { |
655 | | //Add the depthCorrectionType. |
656 | 0 | xmlNode *pNodeGroupNode = xmlNewChild(pBagIdentInfoNode, pBagNamespace, XMLCast("nodeGroupType"), nullptr); |
657 | | |
658 | | //Set the value. |
659 | 0 | addCodeListNode(*pNodeGroupNode, "bag", "BAG_OptGroupCode", |
660 | 0 | "http://www.opennavsurf.org/schema/bag/bagCodelists.xml", (const char*)identificationInfo.nodeGroupType); |
661 | 0 | } |
662 | |
|
663 | 0 | return true; |
664 | 0 | } |
665 | | |
666 | | //************************************************************************ |
667 | | //! Add the BagSecurityConstraints information to the parent XML node. |
668 | | /*! |
669 | | \param parentNode |
670 | | \li The parent XML node to be modified. |
671 | | \param securityConstraints |
672 | | \li The security constraint information to be added to \e parentNode |
673 | | \return |
674 | | \li True if the structure is added, False if an error occurs. |
675 | | */ |
676 | | //************************************************************************ |
677 | | bool addSecurityConstraints(xmlNode &parentNode, const BagSecurityConstraints &securityConstraints) |
678 | 0 | { |
679 | | /* If either the classification or the distribution statement is not supplied, the node should not be created.*/ |
680 | 0 | if (securityConstraints.classification == nullptr || |
681 | 0 | securityConstraints.userNote == nullptr) |
682 | 0 | { |
683 | 0 | fprintf(stderr, "ERROR: creating security constraints. Classification and Distribution statement must be supplied!.\n"); |
684 | 0 | return false; |
685 | 0 | } |
686 | | |
687 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gmd")); |
688 | | |
689 | | //Create the metadataConstraints node. |
690 | 0 | xmlNode *pConstraintNode = xmlNewChild(&parentNode, pGmdNamespace, XMLCast("metadataConstraints"), nullptr); |
691 | | |
692 | | //Create the MD_SecurityConstraints node. |
693 | 0 | xmlNode *pConstraintNode2 = xmlNewChild(pConstraintNode, pGmdNamespace, XMLCast("MD_SecurityConstraints"), nullptr); |
694 | | |
695 | | //classification |
696 | 0 | { |
697 | | //Create the title node. |
698 | 0 | xmlNode *pNode = xmlNewChild(pConstraintNode2, pGmdNamespace, XMLCast("classification"), nullptr); |
699 | 0 | addCodeListNode(*pNode, "gmd", "MD_ClassificationCode", |
700 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", (char*)securityConstraints.classification); |
701 | 0 | } |
702 | | |
703 | | //userNote |
704 | 0 | { |
705 | | //Create the title node. |
706 | 0 | xmlNode *pNode = xmlNewChild(pConstraintNode2, pGmdNamespace, XMLCast("userNote"), nullptr); |
707 | | |
708 | | //Set the title value. |
709 | 0 | addCharacterNode(*pNode, (char*)securityConstraints.userNote); |
710 | 0 | } |
711 | |
|
712 | 0 | return true; |
713 | 0 | } |
714 | | |
715 | | //************************************************************************ |
716 | | //! Add the BagLegalConstraints information to the parent XML node. |
717 | | /*! |
718 | | \param parentNode |
719 | | \li The parent XML node to be modified. |
720 | | \param legalConstraints |
721 | | \li The legal constraint information to be added to \e parentNode |
722 | | \return |
723 | | \li True if the structure is added, False if an error occurs. |
724 | | */ |
725 | | //************************************************************************ |
726 | | bool addLegalConstraints(xmlNode &parentNode, const BagLegalConstraints &legalConstraints) |
727 | 0 | { |
728 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gmd")); |
729 | | |
730 | | //Create the metadataConstraints node. |
731 | 0 | xmlNode *pConstraintNode = xmlNewChild(&parentNode, pGmdNamespace, XMLCast("metadataConstraints"), nullptr); |
732 | | |
733 | | //Create the MD_LegalConstraints node. |
734 | 0 | xmlNode *pConstraintNode2 = xmlNewChild(pConstraintNode, pGmdNamespace, XMLCast("MD_LegalConstraints"), nullptr); |
735 | | |
736 | | //useConstraints |
737 | 0 | { |
738 | | //Create the useConstraints node. |
739 | 0 | xmlNode *pNode = xmlNewChild(pConstraintNode2, pGmdNamespace, XMLCast("useConstraints"), nullptr); |
740 | 0 | addCodeListNode(*pNode, "gmd", "MD_RestrictionCode", |
741 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", (char*)legalConstraints.useConstraints); |
742 | 0 | } |
743 | | |
744 | | //otherConstraints |
745 | 0 | { |
746 | | //Create the title node. |
747 | 0 | xmlNode *pNode = xmlNewChild(pConstraintNode2, pGmdNamespace, XMLCast("otherConstraints"), nullptr); |
748 | | |
749 | | //Set the title value. |
750 | 0 | addCharacterNode(*pNode, (char*)legalConstraints.otherConstraints); |
751 | 0 | } |
752 | |
|
753 | 0 | return true; |
754 | 0 | } |
755 | | |
756 | | //************************************************************************ |
757 | | //! Add the BagSource information to the parent XML node. |
758 | | /*! |
759 | | \param parentNode |
760 | | \li The parent XML node to be modified. |
761 | | \param source |
762 | | \li The process source information to be added to \e parentNode |
763 | | \return |
764 | | \li True if the structure is added, False if an error occurs. |
765 | | */ |
766 | | //************************************************************************ |
767 | | bool addProcessSource(xmlNode &parentNode, const BagSource &source) |
768 | 0 | { |
769 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gmd")); |
770 | | |
771 | | //The description is required. |
772 | 0 | if (source.description == nullptr) |
773 | 0 | { |
774 | 0 | fprintf(stderr, "ERROR: source description not supplied.\n"); |
775 | 0 | return false; |
776 | 0 | } |
777 | | |
778 | | //Create the source node. |
779 | 0 | xmlNode *pSourceNode = xmlNewChild(&parentNode, pGmdNamespace, XMLCast("source"), nullptr); |
780 | | |
781 | | //Create the LI_Source node. |
782 | 0 | xmlNode *pSourceNode2 = xmlNewChild(pSourceNode, pGmdNamespace, XMLCast("LI_Source"), nullptr); |
783 | | |
784 | | //description |
785 | 0 | { |
786 | | //Create the description node. |
787 | 0 | xmlNode *pNode = xmlNewChild(pSourceNode2, pGmdNamespace, XMLCast("description"), nullptr); |
788 | | |
789 | | //Set the title value. |
790 | 0 | addCharacterNode(*pNode, (char*)source.description); |
791 | 0 | } |
792 | | |
793 | | //sourceCitation |
794 | 0 | { |
795 | | //Create the sourceCitation node. |
796 | 0 | xmlNode *pNode = xmlNewChild(pSourceNode2, pGmdNamespace, XMLCast("sourceCitation"), nullptr); |
797 | |
|
798 | 0 | const bool ret = addCitation(*pNode, (char*)source.title, (char*)source.date, (char*)source.dateType, |
799 | 0 | source.responsibleParties, source.numberOfResponsibleParties); |
800 | 0 | if (!ret) |
801 | 0 | return false; |
802 | 0 | } |
803 | | |
804 | 0 | return true; |
805 | 0 | } |
806 | | |
807 | | //************************************************************************ |
808 | | //! Add the BagProcessStep information to the parent XML node. |
809 | | /*! |
810 | | \param parentNode |
811 | | \li The parent XML node to be modified. |
812 | | \param processInfo |
813 | | \li The process step information to be added to \e parentNode |
814 | | \return |
815 | | \li True if the structure is added, False if an error occurs. |
816 | | */ |
817 | | //************************************************************************ |
818 | | bool addProcessStep(xmlNode &parentNode, const BagProcessStep &processInfo) |
819 | 0 | { |
820 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gmd")); |
821 | 0 | const xmlNsPtr pBagNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("bag")); |
822 | |
|
823 | 0 | xmlNode *pProcessStepNode = xmlNewChild(&parentNode, pGmdNamespace, XMLCast("processStep"), nullptr); |
824 | 0 | pProcessStepNode = xmlNewChild(pProcessStepNode, pBagNamespace, XMLCast("BAG_ProcessStep"), nullptr); |
825 | | |
826 | | //description |
827 | 0 | { |
828 | | //Create the description node. |
829 | 0 | xmlNode *pNode = xmlNewChild(pProcessStepNode, pGmdNamespace, XMLCast("description"), nullptr); |
830 | 0 | addCharacterNode(*pNode, (char*)processInfo.description); |
831 | 0 | } |
832 | | |
833 | | //dateTime |
834 | 0 | { |
835 | | //Create the dateTime node. |
836 | 0 | xmlNode *pNode = xmlNewChild(pProcessStepNode, pGmdNamespace, XMLCast("dateTime"), nullptr); |
837 | 0 | addDateTimeNode(*pNode, (char*)processInfo.dateTime); |
838 | 0 | } |
839 | | |
840 | | //processor |
841 | 0 | for (uint32_t i = 0; i < processInfo.numberOfProcessors; i++) |
842 | 0 | { |
843 | | //Create the processor node. |
844 | 0 | xmlNode *pNode = xmlNewChild(pProcessStepNode, pGmdNamespace, XMLCast("processor"), nullptr); |
845 | 0 | addResponsibleParty(*pNode, processInfo.processors[i]); |
846 | 0 | } |
847 | | |
848 | | //source |
849 | 0 | for (uint32_t i = 0; i < processInfo.numberOfSources; i++) |
850 | 0 | { |
851 | 0 | const bool ret = addProcessSource(*pProcessStepNode, processInfo.lineageSources[i]); |
852 | 0 | if (!ret) |
853 | 0 | return false; |
854 | 0 | } |
855 | | |
856 | | //trackingId |
857 | 0 | { |
858 | | //Create the trackingId node. |
859 | 0 | xmlNode *pNode = xmlNewChild(pProcessStepNode, pBagNamespace, XMLCast("trackingId"), nullptr); |
860 | 0 | addCharacterNode(*pNode, (char*)processInfo.trackingId); |
861 | 0 | } |
862 | |
|
863 | 0 | return true; |
864 | 0 | } |
865 | | |
866 | | //************************************************************************ |
867 | | //! Add the BagDataQuality information to the parent XML node. |
868 | | /*! |
869 | | \param parentNode |
870 | | \li The parent XML node to be modified. |
871 | | \param dataQuality |
872 | | \li The data quality information to be added to \e parentNode |
873 | | \return |
874 | | \li True if the structure is added, False if an error occurs. |
875 | | */ |
876 | | //************************************************************************ |
877 | | bool addDataQuality(xmlNode &parentNode, const BagDataQuality &dataQuality) |
878 | 0 | { |
879 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gmd")); |
880 | | |
881 | | //Create the dataQualityInfo node. |
882 | 0 | xmlNode *pQualityNode = xmlNewChild(&parentNode, pGmdNamespace, XMLCast("dataQualityInfo"), nullptr); |
883 | | |
884 | | //Create the DQ_DataQuality node. |
885 | 0 | xmlNode *pQualityNode2 = xmlNewChild(pQualityNode, pGmdNamespace, XMLCast("DQ_DataQuality"), nullptr); |
886 | | |
887 | | //scope |
888 | 0 | { |
889 | 0 | xmlNode *pNode = xmlNewChild(pQualityNode2, pGmdNamespace, XMLCast("scope"), nullptr); |
890 | 0 | pNode = xmlNewChild(pNode, pGmdNamespace, XMLCast("DQ_Scope"), nullptr); |
891 | 0 | pNode = xmlNewChild(pNode, pGmdNamespace, XMLCast("level"), nullptr); |
892 | 0 | addCodeListNode(*pNode, "gmd", "MD_ScopeCode", |
893 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", (char*)dataQuality.scope); |
894 | 0 | } |
895 | | |
896 | | //lineage |
897 | 0 | { |
898 | 0 | xmlNode *pLineageNode = xmlNewChild(pQualityNode2, pGmdNamespace, XMLCast("lineage"), nullptr); |
899 | 0 | pLineageNode = xmlNewChild(pLineageNode, pGmdNamespace, XMLCast("LI_Lineage"), nullptr); |
900 | | |
901 | | //Add each process step. |
902 | 0 | for (uint32_t i = 0; i < dataQuality.numberOfProcessSteps; i++) |
903 | 0 | { |
904 | 0 | const bool ret = addProcessStep(*pLineageNode, dataQuality.lineageProcessSteps[i]); |
905 | 0 | if (!ret) |
906 | 0 | return false; |
907 | 0 | } |
908 | 0 | } |
909 | | |
910 | 0 | return true; |
911 | 0 | } |
912 | | |
913 | | //************************************************************************ |
914 | | //! Add the BagSpatialRepresentation information to the parent XML node. |
915 | | /*! |
916 | | \param parentNode |
917 | | \li The parent XML node to be modified. |
918 | | \param spatialRepresentationInfo |
919 | | \li The spatial representation information to be added to \e parentNode |
920 | | \return |
921 | | \li True if the structure is added, False if an error occurs. |
922 | | */ |
923 | | //************************************************************************ |
924 | | bool addSpatialRepresentation(xmlNode &parentNode, const BagSpatialRepresentation &spatialRepresentationInfo) |
925 | 0 | { |
926 | | /* Check for required elements. If do not exist, return nullptr*/ |
927 | | |
928 | | /* Must have specified cellGeometry, transformationParameterAvailability,and checkPointAvailability */ |
929 | | /* If any of the four corner points equal the INIT_VALUE, this indicates the points have not been populated by the user. */ |
930 | |
|
931 | 0 | if (spatialRepresentationInfo.cellGeometry == nullptr) |
932 | 0 | { |
933 | 0 | fprintf(stderr, "ERROR: spatialRepresentationInfo.cellGeometry, transformationParameterAvailability,checkPointAvailability must be supplied\n"); |
934 | 0 | return false; |
935 | 0 | } |
936 | | |
937 | 0 | if (spatialRepresentationInfo.llCornerX == (double)INIT_VALUE || |
938 | 0 | spatialRepresentationInfo.llCornerY == (double)INIT_VALUE || |
939 | 0 | spatialRepresentationInfo.urCornerX == (double)INIT_VALUE || |
940 | 0 | spatialRepresentationInfo.urCornerY == (double)(INIT_VALUE)) |
941 | 0 | { |
942 | |
|
943 | 0 | fprintf(stderr, "ERROR: All four spatialRepresentationInfo corner points must be supplied. \n"); |
944 | 0 | return false; |
945 | |
|
946 | 0 | } |
947 | | |
948 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gmd")); |
949 | 0 | const xmlNsPtr pGmlNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gml")); |
950 | | |
951 | | //Create the spatialRepresentationInfo node. |
952 | 0 | xmlNode *pSpatialRepNode = xmlNewChild(&parentNode, pGmdNamespace, XMLCast("spatialRepresentationInfo"), nullptr); |
953 | | |
954 | | //Create the MD_Georectified node. |
955 | 0 | xmlNode *pGeoNode = xmlNewChild(pSpatialRepNode, pGmdNamespace, XMLCast("MD_Georectified"), nullptr); |
956 | | |
957 | | //numberOfDimensions |
958 | 0 | { |
959 | 0 | xmlNode *pNode = xmlNewChild(pGeoNode, pGmdNamespace, XMLCast("numberOfDimensions"), nullptr); |
960 | 0 | addIntegerNode(*pNode, 2); |
961 | 0 | } |
962 | | |
963 | | //axisDimensionProperties |
964 | 0 | addDimension(*pGeoNode, "row", spatialRepresentationInfo.numberOfRows, spatialRepresentationInfo.rowResolution, |
965 | 0 | spatialRepresentationInfo.resolutionUnit); |
966 | 0 | addDimension(*pGeoNode, "column", spatialRepresentationInfo.numberOfColumns, spatialRepresentationInfo.columnResolution, |
967 | 0 | spatialRepresentationInfo.resolutionUnit); |
968 | | |
969 | | //cellGeometry |
970 | 0 | { |
971 | 0 | xmlNode *pNode = xmlNewChild(pGeoNode, pGmdNamespace, XMLCast("cellGeometry"), nullptr); |
972 | 0 | addCodeListNode(*pNode, "gmd", "MD_CellGeometryCode", |
973 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", (char*)spatialRepresentationInfo.cellGeometry); |
974 | 0 | } |
975 | | |
976 | | //transformationParameterAvailability |
977 | 0 | { |
978 | 0 | xmlNode *pNode = xmlNewChild(pGeoNode, pGmdNamespace, XMLCast("transformationParameterAvailability"), nullptr); |
979 | 0 | addBooleanNode(*pNode, spatialRepresentationInfo.transformationParameterAvailability); |
980 | 0 | } |
981 | | |
982 | | //checkPointAvailability |
983 | 0 | { |
984 | 0 | xmlNode *pNode = xmlNewChild(pGeoNode, pGmdNamespace, XMLCast("checkPointAvailability"), nullptr); |
985 | 0 | addBooleanNode(*pNode, spatialRepresentationInfo.checkPointAvailability); |
986 | 0 | } |
987 | | |
988 | | //cornerPoints |
989 | 0 | { |
990 | 0 | xmlNode *pCornerNode = xmlNewChild(pGeoNode, pGmdNamespace, XMLCast("cornerPoints"), nullptr); |
991 | |
|
992 | 0 | xmlNode *pPointNode = xmlNewChild(pCornerNode, pGmlNamespace, XMLCast("Point"), nullptr); |
993 | 0 | xmlSetProp(pPointNode, XMLCast("gml:id"), XMLCast("id1")); |
994 | |
|
995 | 0 | char pointsString[88]; |
996 | 0 | sprintf(pointsString, "%.12lf,%.12lf %.12lf,%.12lf", spatialRepresentationInfo.llCornerX, spatialRepresentationInfo.llCornerY, spatialRepresentationInfo.urCornerX, spatialRepresentationInfo.urCornerY); |
997 | |
|
998 | 0 | xmlNode *pCoordNode = xmlNewChild(pPointNode, pGmlNamespace, XMLCast("coordinates"), EncodedString(*parentNode.doc, pointsString)); |
999 | 0 | xmlSetProp(pCoordNode, XMLCast("decimal"), XMLCast(".")); |
1000 | 0 | xmlSetProp(pCoordNode, XMLCast("cs"), XMLCast(",")); |
1001 | 0 | xmlSetProp(pCoordNode, XMLCast("ts"), XMLCast(" ")); |
1002 | 0 | } |
1003 | | |
1004 | | //pointInPixel |
1005 | 0 | { |
1006 | 0 | xmlNode *pNode = xmlNewChild(pGeoNode, pGmdNamespace, XMLCast("pointInPixel"), nullptr); |
1007 | 0 | xmlNewChild(pNode, pGmdNamespace, XMLCast("MD_PixelOrientationCode"), XMLCast("center")); |
1008 | 0 | } |
1009 | |
|
1010 | 0 | return true; |
1011 | 0 | } |
1012 | | |
1013 | | //************************************************************************ |
1014 | | //! Add the BagReferenceSystem information to the parent XML node. |
1015 | | /*! |
1016 | | \param parentNode |
1017 | | \li The parent XML node to be modified. |
1018 | | \param system |
1019 | | \li The reference system information to be added to \e parentNode |
1020 | | \return |
1021 | | \li True if the structure is added, False if an error occurs. |
1022 | | */ |
1023 | | //************************************************************************ |
1024 | | bool addReferenceSystem(xmlNode &parentNode, const BagReferenceSystem &system) |
1025 | 0 | { |
1026 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gmd")); |
1027 | | //const xmlNsPtr pGcoNamespace = xmlSearchNs(parentNode.doc, &parentNode, XMLCast("gco")); |
1028 | |
|
1029 | 0 | xmlNode *pNode = xmlNewChild(&parentNode, pGmdNamespace, XMLCast("referenceSystemInfo"), nullptr); |
1030 | 0 | pNode = xmlNewChild(pNode, pGmdNamespace, XMLCast("MD_ReferenceSystem"), nullptr); |
1031 | 0 | pNode = xmlNewChild(pNode, pGmdNamespace, XMLCast("referenceSystemIdentifier"), nullptr); |
1032 | 0 | pNode = xmlNewChild(pNode, pGmdNamespace, XMLCast("RS_Identifier"), nullptr); |
1033 | |
|
1034 | 0 | xmlNode *pCodeNode = xmlNewChild(pNode, pGmdNamespace, XMLCast("code"), nullptr); |
1035 | 0 | addCharacterNode(*pCodeNode, system.definition); |
1036 | |
|
1037 | 0 | xmlNode *pCodeNameNode = xmlNewChild(pNode, pGmdNamespace, XMLCast("codeSpace"), nullptr); |
1038 | 0 | addCharacterNode(*pCodeNameNode, system.type); |
1039 | |
|
1040 | 0 | return true; |
1041 | 0 | } |
1042 | | |
1043 | | //************************************************************************ |
1044 | | //! Export the Metadata to a string. |
1045 | | /*! |
1046 | | \param metadata |
1047 | | \li The metadata information to be exported. |
1048 | | \return |
1049 | | \li The metadata as XML in a string. |
1050 | | */ |
1051 | | //************************************************************************ |
1052 | | std::string exportMetadataToXML( |
1053 | | const BagMetadata& metadata) |
1054 | 0 | { |
1055 | | //Create a new xml document. |
1056 | 0 | xmlDoc *pDocument = createNewDocument(); |
1057 | | |
1058 | | //Get the document's root node. |
1059 | 0 | xmlNode *pRoot = xmlDocGetRootElement(pDocument); |
1060 | | |
1061 | | //Add the fileIdentifier |
1062 | 0 | { |
1063 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(pDocument, pRoot, XMLCast("gmd")); |
1064 | | |
1065 | | //Create the fileIdentifier node. |
1066 | 0 | xmlNode *pNode = xmlNewChild(pRoot, pGmdNamespace, XMLCast("fileIdentifier"), nullptr); |
1067 | 0 | addCharacterNode(*pNode, metadata.fileIdentifier); |
1068 | 0 | } |
1069 | | |
1070 | | //Add the language |
1071 | 0 | { |
1072 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(pDocument, pRoot, XMLCast("gmd")); |
1073 | | |
1074 | | //Create the language node. |
1075 | 0 | xmlNode *pNode = xmlNewChild(pRoot, pGmdNamespace, XMLCast("language"), nullptr); |
1076 | 0 | addCodeListNode(*pNode, "gmd", "LanguageCode", |
1077 | 0 | "http://www.loc.gov/standards/iso639-2/", metadata.language, false); |
1078 | 0 | } |
1079 | | |
1080 | | //Add the characterSet |
1081 | 0 | { |
1082 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(pDocument, pRoot, XMLCast("gmd")); |
1083 | | |
1084 | | //Create the characterSet node. |
1085 | 0 | xmlNode *pNode = xmlNewChild(pRoot, pGmdNamespace, XMLCast("characterSet"), nullptr); |
1086 | 0 | addCodeListNode(*pNode, "gmd", "MD_CharacterSetCode", |
1087 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", |
1088 | 0 | metadata.characterSet); |
1089 | 0 | } |
1090 | | |
1091 | | //Add the hierarchyLevel. |
1092 | 0 | { |
1093 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(pDocument, pRoot, XMLCast("gmd")); |
1094 | | |
1095 | | //Create the hierarchyLevel node. |
1096 | 0 | xmlNode *pNode = xmlNewChild(pRoot, pGmdNamespace, XMLCast("hierarchyLevel"), nullptr); |
1097 | 0 | addCodeListNode(*pNode, "gmd", "MD_ScopeCode", |
1098 | 0 | "http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml", |
1099 | 0 | metadata.hierarchyLevel); |
1100 | 0 | } |
1101 | | |
1102 | | //Add the contact |
1103 | 0 | { |
1104 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(pDocument, pRoot, XMLCast("gmd")); |
1105 | | |
1106 | | //Create the characterSet node. |
1107 | 0 | xmlNode *pNode = xmlNewChild(pRoot, pGmdNamespace, XMLCast("contact"), nullptr); |
1108 | 0 | if (!addResponsibleParty(*pNode, *metadata.contact)) |
1109 | 0 | { |
1110 | 0 | xmlFreeDoc(pDocument); |
1111 | 0 | return {}; |
1112 | 0 | } |
1113 | 0 | } |
1114 | | |
1115 | | //Add the dateStamp |
1116 | 0 | { |
1117 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(pDocument, pRoot, XMLCast("gmd")); |
1118 | | |
1119 | | //Create the hierarchyLevel node. |
1120 | 0 | xmlNode *pNode = xmlNewChild(pRoot, pGmdNamespace, XMLCast("dateStamp"), nullptr); |
1121 | 0 | addDateNode(*pNode, metadata.dateStamp); |
1122 | 0 | } |
1123 | | |
1124 | | //Add the metadataStandardName |
1125 | 0 | { |
1126 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(pDocument, pRoot, XMLCast("gmd")); |
1127 | | |
1128 | | //Create the metadataStandardName node. |
1129 | 0 | xmlNode *pNode = xmlNewChild(pRoot, pGmdNamespace, XMLCast("metadataStandardName"), nullptr); |
1130 | 0 | addCharacterNode(*pNode, metadata.metadataStandardName); |
1131 | 0 | } |
1132 | | |
1133 | | //Add the metadataStandardVersion |
1134 | 0 | { |
1135 | 0 | const xmlNsPtr pGmdNamespace = xmlSearchNs(pDocument, pRoot, XMLCast("gmd")); |
1136 | | |
1137 | | //Create the metadataStandardVersion node. |
1138 | 0 | xmlNode *pNode = xmlNewChild(pRoot, pGmdNamespace, XMLCast("metadataStandardVersion"), nullptr); |
1139 | 0 | addCharacterNode(*pNode, metadata.metadataStandardVersion); |
1140 | 0 | } |
1141 | | |
1142 | | //Add the spatialRepresentationInfo |
1143 | 0 | if (!addSpatialRepresentation(*pRoot, *metadata.spatialRepresentationInfo)) |
1144 | 0 | { |
1145 | 0 | xmlFreeDoc(pDocument); |
1146 | 0 | return {}; |
1147 | 0 | } |
1148 | | |
1149 | | //Add the horizontal referenceSystemInfo |
1150 | 0 | if (!addReferenceSystem(*pRoot, *metadata.horizontalReferenceSystem)) |
1151 | 0 | { |
1152 | 0 | xmlFreeDoc(pDocument); |
1153 | 0 | return {}; |
1154 | 0 | } |
1155 | | |
1156 | | //Add the vertical referenceSystemInfo |
1157 | 0 | if (!addReferenceSystem(*pRoot, *metadata.verticalReferenceSystem)) |
1158 | 0 | { |
1159 | 0 | xmlFreeDoc(pDocument); |
1160 | 0 | return {}; |
1161 | 0 | } |
1162 | | |
1163 | | //Add the data identification information. |
1164 | 0 | if (!addDataIdentification(*pRoot, *metadata.identificationInfo)) |
1165 | 0 | { |
1166 | 0 | xmlFreeDoc(pDocument); |
1167 | 0 | return {}; |
1168 | 0 | } |
1169 | | |
1170 | | //Add the data quality information. |
1171 | 0 | if (!addDataQuality(*pRoot, *metadata.dataQualityInfo)) |
1172 | 0 | { |
1173 | 0 | xmlFreeDoc(pDocument); |
1174 | 0 | return {}; |
1175 | 0 | } |
1176 | | |
1177 | | //Add the legal constraint information. |
1178 | 0 | if (!addLegalConstraints(*pRoot, *metadata.legalConstraints)) |
1179 | 0 | { |
1180 | 0 | xmlFreeDoc(pDocument); |
1181 | 0 | return {}; |
1182 | 0 | } |
1183 | | |
1184 | | //Add the security constraint information. |
1185 | 0 | if (!addSecurityConstraints(*pRoot, *metadata.securityConstraints)) |
1186 | 0 | { |
1187 | 0 | xmlFreeDoc(pDocument); |
1188 | 0 | return {}; |
1189 | 0 | } |
1190 | | |
1191 | | //Export to the buffer. |
1192 | 0 | int bufferSize = 0; |
1193 | 0 | xmlChar *pBuffer = nullptr; |
1194 | 0 | xmlDocDumpMemoryEnc(pDocument, &pBuffer, &bufferSize, "UTF-8"); |
1195 | 0 | xmlFreeDoc(pDocument); |
1196 | | |
1197 | | //If nothing was exported, then just return. |
1198 | 0 | if (bufferSize == 0) |
1199 | 0 | return {}; |
1200 | | |
1201 | | //Copy the buffer to our output string and add a null terminator. |
1202 | 0 | std::string result{reinterpret_cast<char*>(pBuffer), static_cast<size_t>(bufferSize)}; |
1203 | |
|
1204 | 0 | xmlFree(pBuffer); |
1205 | 0 | return result; |
1206 | 0 | } |
1207 | | |
1208 | | } // namespace BAG |
1209 | | |