Coverage Report

Created: 2025-07-11 06:33

/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