Coverage Report

Created: 2025-11-09 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bag/api/bag_metadata_import.cpp
Line
Count
Source
1
//************************************************************************
2
//
3
//      Open Navigation Surface Working Group, 2013
4
//
5
//************************************************************************
6
#include "bag_errors.h"
7
#include "bag_legacy_crs.h"
8
#include "bag_metadata_import.h"
9
#include "bag_metadatatypes.h"
10
11
#include <cstring>
12
#include <iostream>
13
#include <libxml/parser.h>
14
#include <libxml/xmlschemas.h>
15
#include <libxml/xpath.h>
16
#include <libxml/xpathInternals.h>
17
#include <sstream>
18
#include <string>
19
#include <sys/stat.h>
20
#include <sys/types.h>
21
#include <vector>
22
23
24
namespace {
25
26
//! The location of the BAG home folder.
27
static std::string bagHomeFolder;
28
29
//! The relative path from the BAG home folder, to the Bag schema file.
30
static std::string ons_schema_location = "ISO19139/bag/bag.xsd";
31
32
//! Utility class to convert an encoded XML string.
33
class EncodedString final
34
{
35
public:
36
    //************************************************************************
37
    //! Constructor
38
    /*!
39
    \param doc
40
        \li The XML document that the string is from.
41
    \param string
42
        \li The string to be converted.
43
    */
44
    //************************************************************************
45
    EncodedString(xmlDoc& doc, const char* string) noexcept
46
15.1k
    {
47
15.1k
        this->m_pEncodedString = xmlEncodeEntitiesReentrant(&doc,
48
15.1k
            reinterpret_cast<const xmlChar*>(string));
49
15.1k
    }
50
51
    EncodedString(const EncodedString&) = delete;
52
    EncodedString(EncodedString&&) = delete;
53
    EncodedString& operator=(const EncodedString&) = delete;
54
    EncodedString& operator=(EncodedString&&) = delete;
55
56
    //************************************************************************
57
    //! Destructor.
58
    //************************************************************************
59
    ~EncodedString() noexcept
60
15.1k
    {
61
15.1k
        xmlFree(this->m_pEncodedString);
62
15.1k
    }
63
64
    //************************************************************************
65
    //! Conversion operator.
66
    /*!
67
    \return
68
        \li The encoded string.
69
    */
70
    //************************************************************************
71
    operator xmlChar*() const noexcept
72
15.1k
    {
73
15.1k
        return this->m_pEncodedString;
74
15.1k
    }
75
76
private:
77
    //! The encoded string.
78
    xmlChar* m_pEncodedString = nullptr;
79
};
80
81
//************************************************************************
82
//! Convert a string to a double value.
83
/*!
84
\param value
85
    \li The input string to be converted.
86
\return
87
    \li The double value.
88
*/
89
//************************************************************************
90
double toDouble(const std::string& value)
91
1.10k
{
92
1.10k
    std::stringstream lineStream;
93
1.10k
    (void)lineStream.imbue(std::locale::classic());
94
1.10k
    lineStream << value;
95
96
1.10k
    double dblValue = 0.0;
97
1.10k
    lineStream >> dblValue;
98
99
1.10k
    return dblValue;
100
1.10k
}
101
102
//************************************************************************
103
//! Get the name of a node.
104
/*!
105
\param value
106
    \li The input string to be converted.
107
\return
108
    \li The node name.
109
*/
110
//************************************************************************
111
std::string getNodeName(const xmlNode& node)
112
207
{
113
207
    std::string name;
114
115
    // append the namespace prefix
116
207
    const xmlNs* nameSpace = node.ns;
117
207
    if (nameSpace)
118
207
    {
119
207
        name = reinterpret_cast<const char*>(nameSpace->prefix);
120
207
        name += ':';
121
207
    }
122
123
207
    name += reinterpret_cast<const char*>(node.name);
124
125
207
    return name;
126
207
}
127
128
//************************************************************************
129
//! Find all the nodes matching the supplied xpath pattern.
130
/*!
131
\param relativeNode
132
    \li The node to start searching from.
133
\param searchString
134
    \li The pattern to be searched for.  The string conforms to the xpath
135
        searching syntax.  For detailed information, see http://www.w3schools.com/xpath/xpath_syntax.asp
136
\return
137
    \li The list of XML nodes that match \e searchString
138
*/
139
//************************************************************************
140
std::vector<xmlNode*> findNodes(
141
    xmlNode& relativeNode,
142
    const char* searchString)
143
14.9k
{
144
    //Get the root node of the document.
145
14.9k
    const xmlNode* pRoot = xmlDocGetRootElement(relativeNode.doc);
146
14.9k
    if (!pRoot)
147
0
        return {};
148
149
    //If the xPath context has not been initialized yet, do it now.
150
14.9k
    xmlXPathContext* pContext = xmlXPathNewContext(relativeNode.doc);
151
14.9k
    if (!pContext)
152
0
        return {};
153
154
14.9k
    pContext->node = &relativeNode;
155
156
    //Register any namespaces with the xPath context.
157
14.9k
    const xmlNs* xmlNameSpace = pRoot->nsDef;
158
159
117k
    while (xmlNameSpace)
160
103k
    {
161
103k
        if (xmlNameSpace->prefix)
162
103k
        {
163
103k
            const int ret = xmlXPathRegisterNs(pContext, xmlNameSpace->prefix,
164
103k
                xmlNameSpace->href);
165
103k
            if (ret != 0)
166
0
            {
167
                //Error
168
0
                 xmlXPathFreeContext(pContext);
169
0
                 return {};
170
0
            }
171
103k
        }
172
173
103k
        xmlNameSpace = xmlNameSpace->next;
174
103k
    }
175
176
    //Encode the specified search string.
177
14.9k
    const EncodedString encodedSearch{*relativeNode.doc, searchString};
178
179
    //Evaluate the expression.
180
14.9k
    xmlXPathObject* pPathObject = xmlXPathEvalExpression(encodedSearch, pContext);
181
14.9k
    if (!pPathObject)
182
1.95k
    {
183
        //Error
184
1.95k
        xmlXPathFreeContext(pContext);
185
1.95k
        return {};
186
1.95k
    }
187
188
12.9k
    std::vector<xmlNode*> retList;
189
190
    //Add each value that was returned.
191
12.9k
    if (pPathObject->nodesetval)
192
11.7k
    {
193
11.7k
        retList.reserve(pPathObject->nodesetval->nodeNr);
194
195
23.2k
        for (int i = 0; i < pPathObject->nodesetval->nodeNr; i++)
196
11.5k
            retList.push_back(pPathObject->nodesetval->nodeTab[i]);
197
11.7k
    }
198
199
12.9k
    xmlXPathFreeObject(pPathObject);
200
12.9k
    xmlXPathFreeContext(pContext);
201
202
12.9k
    return retList;
203
14.9k
}
204
205
//************************************************************************
206
//! Find a single node matching the given xpath pattern.
207
/*!
208
\param relativeNode
209
    \li The node to start searching from.
210
\param searchString
211
    \li The pattern to be searched for.  The string conforms to the xpath
212
        searching syntax.  For detailed information, see http://www.w3schools.com/xpath/xpath_syntax.asp
213
\return
214
    \li The first XML node that matches \e searchString
215
*/
216
//************************************************************************
217
xmlNode* findNode(
218
    xmlNode& relativeNode,
219
    const char* searchString)
220
13.9k
{
221
13.9k
    auto retList = findNodes(relativeNode, searchString);
222
13.9k
    if (retList.empty())
223
3.32k
        return nullptr;
224
225
10.5k
    return retList.front();
226
13.9k
}
227
228
//************************************************************************
229
//! Get the named property from an XML node.
230
/*!
231
\param current
232
    \li The node to retrieve the property from.
233
\param propertyName
234
    \li The name of the property to be retrieved.
235
\return
236
    \li The property matching \e propertyName from \e current.
237
*/
238
//************************************************************************
239
std::string getProperty(
240
    const xmlNode& current,
241
    const char* propertyName)
242
160
{
243
     // Retrieve the property.
244
160
    xmlChar* temp = xmlGetProp(&current, EncodedString{*current.doc,
245
160
        propertyName});
246
160
    if (!temp)
247
5
        return {};
248
249
155
    const std::string value(reinterpret_cast<char*>(temp));
250
251
    // Free the memory allocated by xmlGetProp().
252
155
    xmlFree(temp);
253
254
155
    return value;
255
160
}
256
257
//************************************************************************
258
//! Get the contents of an XML node.
259
/*!
260
\param current
261
    \li The node to retrieve the contents from.
262
\return
263
    \li The contents of \e current.
264
*/
265
//************************************************************************
266
std::string getContents(const xmlNode& current)
267
8.77k
{
268
8.77k
    std::string contents;
269
270
    // Get the children of the current element.
271
8.77k
    const xmlNode* text = current.children;
272
273
    // Concatenate all the text elements.
274
17.6k
    while (text)
275
8.86k
    {
276
8.86k
        if (text->type == XML_TEXT_NODE && text->content)
277
8.77k
            contents += reinterpret_cast<const char*>(text->content);
278
279
8.86k
        text = text->next;
280
8.86k
    }
281
282
8.77k
    return contents;
283
8.77k
}
284
285
//************************************************************************
286
//! Get the named property from an XML node as a string.
287
/*!
288
\param node
289
    \li The node to begin searching from.
290
\param searchPath
291
    \li The pattern to be searched for.  The string conforms to the xpath
292
        searching syntax.  For detailed information, see http://www.w3schools.com/xpath/xpath_syntax.asp
293
\param propertyName
294
    \li The name of the property to be retrieved from the node matching
295
        \e searchPath
296
\return
297
    \li The specified property as a string value.  NULL is returned if
298
        a node matching \e searchPath cannot be found.
299
*/
300
//************************************************************************
301
char* getPropertyAsString(
302
    xmlNode& node,
303
    const char* searchPath,
304
    const char* propertyName)
305
207
{
306
207
    const xmlNode* pNode = findNode(node, searchPath);
307
207
    if (!pNode)
308
47
        return nullptr;
309
310
160
    const std::string value = getProperty(*pNode, propertyName);
311
312
160
    char* result = new char[value.size() + 1];
313
160
    strcpy(result, value.c_str());
314
160
    result[value.size()] = '\0';
315
316
160
    return result;
317
318
207
}
319
320
//************************************************************************
321
//! Get the contents of an XML node as a string.
322
/*!
323
\param node
324
    \li The node to begin searching from.
325
\param searchPath
326
    \li The pattern to be searched for.  The string conforms to the xpath
327
        searching syntax.  For detailed information, see http://www.w3schools.com/xpath/xpath_syntax.asp
328
\return
329
    \li The contents of \e node as a string value.  NULL is returned if
330
        a node matching \e searchPath cannot be found.
331
*/
332
//************************************************************************
333
std::string getContentsAsString(
334
    xmlNode& node,
335
    const char* searchPath)
336
9.77k
{
337
9.77k
    const xmlNode* pNode = findNode(node, searchPath);
338
9.77k
    if (!pNode)
339
2.69k
        return {};
340
341
7.07k
    return getContents(*pNode);
342
9.77k
}
343
344
char* copyString(const char* source)
345
9.77k
{
346
9.77k
    if (!source || strlen(source) == 0)
347
2.69k
        return nullptr;
348
349
7.07k
    char* result = new char[strlen(source) + 1];
350
7.07k
    strcpy(result, source);
351
352
7.07k
    return result;
353
9.77k
}
354
355
char* getContentsAsCharStar(
356
    xmlNode& node,
357
    const char* searchPath)
358
9.77k
{
359
9.77k
    return copyString(getContentsAsString(node, searchPath).c_str());
360
9.77k
}
361
362
//************************************************************************
363
//! Get the contents of an XML node as an integer.
364
/*!
365
\param node
366
    \li The node to begin searching from.
367
\param searchPath
368
    \li The pattern to be searched for.  The string conforms to the xpath
369
        searching syntax.  For detailed information, see http://www.w3schools.com/xpath/xpath_syntax.asp
370
\return
371
    \li The contents of \e node as an integer value.  0 is returned if
372
        a node matching \e searchPath cannot be found.
373
*/
374
//************************************************************************
375
int32_t getContentsAsInt(
376
    xmlNode& node,
377
    const char* searchPath)
378
414
{
379
414
    auto* pNode = findNode(node, searchPath);
380
414
    if (!pNode)
381
93
        return 0;
382
383
321
    return static_cast<int32_t>(toDouble(getContents(*pNode)));
384
414
}
385
386
//************************************************************************
387
//! Get the contents of an XML node as a 64 bit floating point value.
388
/*!
389
\param node
390
    \li The node to begin searching from.
391
\param searchPath
392
    \li The pattern to be searched for.  The string conforms to the xpath
393
        searching syntax.  For detailed information, see http://www.w3schools.com/xpath/xpath_syntax.asp
394
\return
395
    \li The contents of \e node as a 64 bit floating point value.
396
        0.0 is returned if a node matching \e searchPath cannot be found.
397
*/
398
//************************************************************************
399
double getContentsAsFloat(
400
    xmlNode& node,
401
    const char* searchPath)
402
1.24k
{
403
1.24k
    const xmlNode* pNode = findNode(node, searchPath);
404
1.24k
    if (!pNode)
405
459
        return 0.0;
406
407
783
    return toDouble(getContents(*pNode));
408
1.24k
}
409
410
//************************************************************************
411
//! Get the contents of an XML node as a boolean value.
412
/*!
413
\param node
414
    \li The node to begin searching from.
415
\param searchPath
416
    \li The pattern to be searched for.  The string conforms to the xpath
417
        searching syntax.  For detailed information, see http://www.w3schools.com/xpath/xpath_syntax.asp
418
\return
419
    \li The contents of \e node as boolean value.  False is returned if a node
420
    matching \e searchPath cannot be found.
421
*/
422
//************************************************************************
423
bool getContentsAsBool(
424
    xmlNode& node,
425
    const char* searchPath)
426
414
{
427
414
    const xmlNode* pNode = findNode(node, searchPath);
428
414
    if (!pNode)
429
28
        return false;
430
431
386
    const std::string value = getContents(*pNode);
432
433
    // Prevent repeated memory allocations.
434
386
    static std::string kZero{"0"};
435
386
    static std::string kFalse{"false"};
436
437
386
    return (value != kZero && value != kFalse);
438
414
}
439
440
}  // namespace
441
442
namespace BAG {
443
444
//************************************************************************
445
//! Get the current setting for the BAG_HOME directory.
446
/*!
447
\return
448
    \li If the user has called bagSetHomeFolder(), then that
449
        value is returned, otherwise, getenv("BAG_HOME") is
450
        returned.
451
*/
452
//************************************************************************
453
std::string bagGetHomeFolder()
454
0
{
455
    //If the BAG home folder has been set, then return it.
456
0
    if (!bagHomeFolder.empty())
457
0
        return bagHomeFolder;
458
459
    //Else we will return the environment variable.
460
0
    const char* bagHome = getenv("BAG_HOME");
461
0
    if (bagHome)
462
0
        return std::string{bagHome};
463
464
0
    return {};
465
0
}
466
467
//************************************************************************
468
//! Set the location of the BAG 'home' directory.
469
/*!
470
\param homeFolder
471
    \li The new location for the BAG home directory.
472
*/
473
//************************************************************************
474
void bagSetHomeFolder(const char* homeFolder)
475
0
{
476
0
    bagHomeFolder = homeFolder;
477
0
}
478
479
//************************************************************************
480
//! Decode a BagResponsibleParty from the supplied XML node.
481
/*!
482
\param node
483
    \li The XML node containing the responsible party information.
484
\param responsibleParty
485
    \li Modified to contain the responsible party information from \e node.
486
\param schemaVersion
487
    \li The version of the schema stored in \e node.
488
\return
489
    \li True if the information was found and decoded properly, False if
490
        an error occurs.
491
*/
492
//************************************************************************
493
bool decodeResponsibleParty(
494
    xmlNode& node,
495
    BagResponsibleParty& responsibleParty,
496
    uint16_t schemaVersion)
497
530
{
498
530
    if (schemaVersion == 1)
499
0
    {
500
        //smXML:CI_ResponsibleParty/individualName
501
0
        responsibleParty.individualName = getContentsAsCharStar(node,
502
0
            "smXML:CI_ResponsibleParty/individualName");
503
504
        //smXML:CI_ResponsibleParty/organisationName
505
0
        responsibleParty.organisationName = getContentsAsCharStar(node,
506
0
            "smXML:CI_ResponsibleParty/organisationName");
507
508
        //smXML:CI_ResponsibleParty/positionName
509
0
        responsibleParty.positionName = getContentsAsCharStar(node,
510
0
            "smXML:CI_ResponsibleParty/positionName");
511
512
        //smXML:CI_ResponsibleParty/role
513
0
        responsibleParty.role = getContentsAsCharStar(node,
514
0
            "smXML:CI_ResponsibleParty/role");
515
0
    }
516
530
    else if (schemaVersion == 2)
517
530
    {
518
        //gmd:CI_ResponsibleParty/gmd:individualName
519
530
        responsibleParty.individualName = getContentsAsCharStar(node,
520
530
            "gmd:CI_ResponsibleParty/gmd:individualName/gco:CharacterString");
521
522
        //gmd:CI_ResponsibleParty/gmd:organisationName
523
530
        responsibleParty.organisationName = getContentsAsCharStar(node,
524
530
            "gmd:CI_ResponsibleParty/gmd:organisationName/gco:CharacterString");
525
526
        //gmd:CI_ResponsibleParty/gmd:positionName
527
530
        responsibleParty.positionName = getContentsAsCharStar(node,
528
530
            "gmd:CI_ResponsibleParty/gmd:positionName/gco:CharacterString");
529
530
        //gmd:CI_ResponsibleParty/gmd:role
531
530
        responsibleParty.role = getContentsAsCharStar(node,
532
530
            "gmd:CI_ResponsibleParty/gmd:role/gmd:CI_RoleCode");
533
530
    }
534
535
530
    return true;
536
530
}
537
538
//************************************************************************
539
//! Decode a BagLegalConstraints from the supplied XML node.
540
/*!
541
\param node
542
    \li The XML node containing the legal constraints information.
543
\param legalConstraints
544
    \li Modified to contain the legal constraints information from \e node.
545
\param schemaVersion
546
    \li The version of the schema stored in \e node.
547
\return
548
    \li True if the information was found and decoded properly, False if
549
        an error occurs.
550
*/
551
//************************************************************************
552
bool decodeLegalConstraints(
553
    xmlNode& node,
554
    BagLegalConstraints& legalConstraints,
555
    uint16_t schemaVersion)
556
207
{
557
207
    if (schemaVersion == 1)
558
0
    {
559
        //smXML:MD_LegalConstraints/useConstraints
560
0
        legalConstraints.useConstraints = getContentsAsCharStar(node,
561
0
            "smXML:MD_LegalConstraints/useConstraints");
562
563
        //smXML:MD_LegalConstraints/otherConstraints
564
0
        legalConstraints.otherConstraints = getContentsAsCharStar(node,
565
0
            "smXML:MD_LegalConstraints/otherConstraints");
566
0
    }
567
207
    else if (schemaVersion == 2)
568
207
    {
569
        //gmd:MD_LegalConstraints/gmd:useConstraints
570
207
        legalConstraints.useConstraints = getContentsAsCharStar(node,
571
207
            "gmd:MD_LegalConstraints/gmd:useConstraints/gmd:MD_RestrictionCode");
572
573
        //gmd:MD_LegalConstraints/gmd:otherConstraints
574
207
        legalConstraints.otherConstraints = getContentsAsCharStar(node,
575
207
            "gmd:MD_LegalConstraints/gmd:otherConstraints/gco:CharacterString");
576
207
    }
577
578
207
    return true;
579
207
}
580
581
//************************************************************************
582
//! Decode a BagSecurityConstraints from the supplied XML node.
583
/*!
584
\param node
585
    \li The XML node containing the security constraints information.
586
\param securityConstraints
587
    \li Modified to contain the security constraints information from \e node.
588
\param schemaVersion
589
    \li The version of the schema stored in \e node.
590
\return
591
    \li True if the information was found and decoded properly, False if
592
        an error occurs.
593
*/
594
//************************************************************************
595
bool decodeSecurityConstraints(
596
    xmlNode& node,
597
    BagSecurityConstraints& securityConstraints,
598
    uint16_t schemaVersion)
599
207
{
600
207
    if (schemaVersion == 1)
601
0
    {
602
        //smXML:MD_SecurityConstraints/classification
603
0
        securityConstraints.classification = getContentsAsCharStar(node,
604
0
            "smXML:MD_SecurityConstraints/classification");
605
606
        //smXML:MD_SecurityConstraints/userNote
607
0
        securityConstraints.userNote = getContentsAsCharStar(node,
608
0
            "smXML:MD_SecurityConstraints/userNote");
609
0
    }
610
207
    else if (schemaVersion == 2)
611
207
    {
612
        //gmd:MD_SecurityConstraints/gmd:classification
613
207
        securityConstraints.classification = getContentsAsCharStar(node,
614
207
            "gmd:MD_SecurityConstraints/gmd:classification/gmd:MD_ClassificationCode");
615
616
        //gmd:MD_SecurityConstraints/gmd:userNote
617
207
        securityConstraints.userNote = getContentsAsCharStar(node,
618
207
            "gmd:MD_SecurityConstraints/gmd:userNote/gco:CharacterString");
619
207
    }
620
621
207
    return true;
622
207
}
623
624
//************************************************************************
625
//! Decode a BagSource from the supplied XML node.
626
/*!
627
\param node
628
    \li The XML node containing the source information.
629
\param sourceInfo
630
    \li Modified to contain the source information from \e node.
631
\param schemaVersion
632
    \li The version of the schema stored in \e node.
633
\return
634
    \li True if the information was found and decoded properly, False if
635
        an error occurs.
636
*/
637
//************************************************************************
638
bool decodeSourceInfo(
639
    xmlNode& node,
640
    BagSource& sourceInfo,
641
    uint16_t schemaVersion)
642
206
{
643
206
    if (schemaVersion == 1)
644
0
    {
645
        //smXML:LI_Source/description
646
0
        sourceInfo.description = getContentsAsCharStar(node,
647
0
            "smXML:LI_Source/description");
648
0
    }
649
206
    else if (schemaVersion == 2)
650
206
    {
651
        //gmd:LI_Source/gmd:description
652
206
        sourceInfo.description = getContentsAsCharStar(node,
653
206
            "gmd:LI_Source/gmd:description/gco:CharacterString");
654
655
        //gmd:LI_Source/gmd:sourceCitation/gmd:CI_Citation/gmd:title
656
206
        sourceInfo.title = getContentsAsCharStar(node,
657
206
            "gmd:LI_Source/gmd:sourceCitation/gmd:CI_Citation/gmd:title/gco:CharacterString");
658
659
        //gmd:LI_Source/gmd:sourceCitation/gmd:CI_Citation/gmd:CI_Date/gmd:date
660
206
        sourceInfo.date = getContentsAsCharStar(node,
661
206
            "gmd:LI_Source/gmd:sourceCitation/gmd:CI_Citation/gmd:date/gmd:CI_Date/gmd:date/gco:Date");
662
663
        //gmd:LI_Source/gmd:sourceCitation/gmd:CI_Citation/gmd:CI_Date/gmd:dateType
664
206
        sourceInfo.dateType = getContentsAsCharStar(node,
665
206
            "gmd:LI_Source/gmd:sourceCitation/gmd:CI_Citation/gmd:date/gmd:CI_Date/gmd:dateType/gmd:CI_DateTypeCode");
666
667
        //gmd:LI_Source/gmd:sourceCitation/gmd:CI_Citation/gmd:citedResponsibleParty
668
206
        const auto partyNodes = findNodes(node,
669
206
            "gmd:LI_Source/gmd:sourceCitation/gmd:CI_Citation/gmd:citedResponsibleParty");
670
206
        if (!partyNodes.empty())
671
0
        {
672
0
            sourceInfo.numberOfResponsibleParties =
673
0
                static_cast<uint32_t>(partyNodes.size());
674
0
            sourceInfo.responsibleParties = new BagResponsibleParty[sourceInfo.numberOfResponsibleParties];
675
676
0
            for (uint32_t i = 0; i < sourceInfo.numberOfResponsibleParties; i++)
677
0
            {
678
0
                initResponsibleParty(sourceInfo.responsibleParties[i]);
679
0
                if (!decodeResponsibleParty(*partyNodes[i],
680
0
                    sourceInfo.responsibleParties[i], schemaVersion))
681
0
                    return false;
682
0
            }
683
0
        }
684
206
    }
685
686
206
    return true;
687
206
}
688
689
//************************************************************************
690
//! Decode a BagProcessStep from the supplied XML node.
691
/*!
692
\param node
693
    \li The XML node containing the process information.
694
\param processStep
695
    \li Modified to contain the process information from \e node.
696
\param schemaVersion
697
    \li The version of the schema stored in \e node.
698
\return
699
    \li True if the information was found and decoded properly, False if
700
        an error occurs.
701
*/
702
//************************************************************************
703
bool decodeProcessStep(
704
    xmlNode& node,
705
    BagProcessStep& processStep,
706
    uint16_t schemaVersion)
707
206
{
708
206
    if (schemaVersion == 1)
709
0
    {
710
        //smXML:BAG_ProcessStep/description
711
0
        processStep.description = getContentsAsCharStar(node,
712
0
            "smXML:BAG_ProcessStep/description");
713
714
        //smXML:BAG_ProcessStep/dateTime
715
0
        processStep.dateTime = getContentsAsCharStar(node,
716
0
            "smXML:BAG_ProcessStep/dateTime");
717
718
        //smXML:BAG_ProcessStep/trackingId
719
0
        processStep.trackingId = getContentsAsCharStar(node,
720
0
            "smXML:BAG_ProcessStep/trackingId");
721
722
        //smXML:BAG_ProcessStep/processor
723
0
        const auto processorNodes = findNodes(node,
724
0
            "smXML:BAG_ProcessStep/processor");
725
0
        if (!processorNodes.empty())
726
0
        {
727
0
            processStep.numberOfProcessors =
728
0
                static_cast<uint32_t>(processorNodes.size());
729
0
            processStep.processors =
730
0
                new BagResponsibleParty[processStep.numberOfProcessors];
731
732
0
            for (uint32_t i = 0; i < processStep.numberOfProcessors; i++)
733
0
            {
734
0
                initResponsibleParty(processStep.processors[i]);
735
0
                if (!decodeResponsibleParty(*processorNodes[i],
736
0
                    processStep.processors[i], schemaVersion))
737
0
                    return false;
738
0
            }
739
0
        }
740
741
        //Sources are stored at the same level as the process step, so go up a level.
742
0
        const auto sourceNodes = findNodes(node,
743
0
            "parent::*/source");
744
0
        if (!sourceNodes.empty())
745
0
        {
746
0
            processStep.numberOfSources =
747
0
                static_cast<uint32_t>(sourceNodes.size());
748
0
            processStep.lineageSources =
749
0
                new BagSource[processStep.numberOfSources];
750
751
0
            for (uint32_t i = 0; i < processStep.numberOfSources; i++)
752
0
            {
753
0
                initSourceInfo(processStep.lineageSources[i]);
754
0
                if (!decodeSourceInfo(*sourceNodes[i],
755
0
                    processStep.lineageSources[i], schemaVersion))
756
0
                    return false;
757
0
            }
758
0
        }
759
0
    }
760
206
    else if (schemaVersion == 2)
761
206
    {
762
        //bag:BAG_ProcessStep/gmd:description
763
206
        processStep.description = getContentsAsCharStar(node,
764
206
            "//gmd:description/gco:CharacterString");
765
766
        //bag:BAG_ProcessStep/gmd:dateTime
767
206
        processStep.dateTime = getContentsAsCharStar(node,
768
206
            "//gmd:dateTime/gco:DateTime");
769
770
        //bag:BAG_ProcessStep/bag:trackingId
771
206
        processStep.trackingId = getContentsAsCharStar(node,
772
206
            "//bag:trackingId/gco:CharacterString");
773
774
        //bag:BAG_ProcessStep/gmd:processor
775
206
        const auto processorNodes = findNodes(node,
776
206
            "//gmd:processor");
777
206
        if (!processorNodes.empty())
778
206
        {
779
206
            processStep.numberOfProcessors =
780
206
                static_cast<uint32_t>(processorNodes.size());
781
206
            processStep.processors =
782
206
                new BagResponsibleParty[processStep.numberOfProcessors];
783
784
412
            for (uint32_t i = 0; i < processStep.numberOfProcessors; i++)
785
206
            {
786
206
                initResponsibleParty(processStep.processors[i]);
787
206
                if (!decodeResponsibleParty(*processorNodes[i],
788
206
                    processStep.processors[i], schemaVersion))
789
0
                    return false;
790
206
            }
791
206
        }
792
793
        //bag:BAG_ProcessStep/gmd:source
794
206
        const auto sourceNodes = findNodes(node, "//gmd:source");
795
206
        if (!sourceNodes.empty())
796
206
        {
797
206
            processStep.numberOfSources =
798
206
                static_cast<uint32_t>(sourceNodes.size());
799
206
            processStep.lineageSources =
800
206
                new BagSource[processStep.numberOfSources];
801
802
412
            for (uint32_t i = 0; i < processStep.numberOfSources; i++)
803
206
            {
804
206
                initSourceInfo(processStep.lineageSources[i]);
805
206
                if (!decodeSourceInfo(*sourceNodes[i],
806
206
                    processStep.lineageSources[i], schemaVersion))
807
0
                    return false;
808
206
            }
809
206
        }
810
206
    }
811
812
206
    return true;
813
206
}
814
815
//************************************************************************
816
//! Decode a BagDataQuality from the supplied XML node.
817
/*!
818
\param node
819
    \li The XML node containing the data quality information.
820
\param dataQualityInfo
821
    \li Modified to contain the data quality information from \e node.
822
\param schemaVersion
823
    \li The version of the schema stored in \e node.
824
\return
825
    \li True if the information was found and decoded properly, False if
826
        an error occurs.
827
*/
828
//************************************************************************
829
bool decodeDataQualityInfo(
830
    xmlNode& node,
831
    BagDataQuality& dataQualityInfo,
832
    uint16_t schemaVersion)
833
207
{
834
207
    if (schemaVersion == 1)
835
0
    {
836
        //smXML:DQ_DataQuality/scope/smXML:DQ_Scope/level
837
0
        dataQualityInfo.scope = getContentsAsCharStar(node,
838
0
            "smXML:DQ_DataQuality/scope/smXML:DQ_Scope/level");
839
840
        //smXML:DQ_DataQuality/lineage/smXML:LI_Lineage/processStep
841
0
        const auto stepNodes = findNodes(node,
842
0
            "smXML:DQ_DataQuality/lineage/smXML:LI_Lineage/processStep");
843
0
        if (!stepNodes.empty())
844
0
        {
845
0
            dataQualityInfo.numberOfProcessSteps =
846
0
                static_cast<uint32_t>(stepNodes.size());
847
0
            dataQualityInfo.lineageProcessSteps =
848
0
                new BagProcessStep[dataQualityInfo.numberOfProcessSteps];
849
850
0
            for (uint32_t i = 0; i < dataQualityInfo.numberOfProcessSteps; i++)
851
0
            {
852
0
                initProcessStep(dataQualityInfo.lineageProcessSteps[i]);
853
0
                if (!decodeProcessStep(*stepNodes[i],
854
0
                    dataQualityInfo.lineageProcessSteps[i], schemaVersion))
855
0
                    return false;
856
0
            }
857
0
        }
858
0
    }
859
207
    else if (schemaVersion == 2)
860
207
    {
861
        //gmd:DQ_DataQuality/gmd:scope/gmd:DQ_Scope/gmd:level
862
207
        delete[] dataQualityInfo.scope;
863
207
        dataQualityInfo.scope = getContentsAsCharStar(node,
864
207
            "gmd:DQ_DataQuality/gmd:scope/gmd:DQ_Scope/gmd:level/gmd:MD_ScopeCode");
865
866
        //gmd:DQ_DataQuality/gmd:lineage/gmd:LI_Lineage/gmd:processStep
867
207
        const auto stepNodes = findNodes(node,
868
207
            "gmd:DQ_DataQuality/gmd:lineage/gmd:LI_Lineage/gmd:processStep");
869
207
        if (!stepNodes.empty())
870
206
        {
871
206
            dataQualityInfo.numberOfProcessSteps =
872
206
                static_cast<uint32_t>(stepNodes.size());
873
206
            dataQualityInfo.lineageProcessSteps =
874
206
                new BagProcessStep[dataQualityInfo.numberOfProcessSteps];
875
876
412
            for (uint32_t i = 0; i < dataQualityInfo.numberOfProcessSteps; i++)
877
206
            {
878
206
                initProcessStep(dataQualityInfo.lineageProcessSteps[i]);
879
206
                if (!decodeProcessStep(*stepNodes[i],
880
206
                    dataQualityInfo.lineageProcessSteps[i], schemaVersion))
881
0
                    return false;
882
206
            }
883
206
        }
884
207
    }
885
886
207
    return true;
887
207
}
888
889
//************************************************************************
890
//! Decode a BagSpatialRepresentation from the supplied XML node.
891
/*!
892
\param node
893
    \li The XML node containing the spatial representation information.
894
\param spatialRepresentationInfo
895
    \li Modified to contain the spatial representation information from \e node.
896
\param schemaVersion
897
    \li The version of the schema stored in \e node.
898
\return
899
    \li True if the information was found and decoded properly, False if
900
        an error occurs.
901
*/
902
//************************************************************************
903
bool decodeSpatialRepresentationInfo(
904
    xmlNode& node,
905
    BagSpatialRepresentation& spatialRepresentationInfo,
906
    uint16_t schemaVersion)
907
207
{
908
207
    if (schemaVersion == 1)
909
0
    {
910
        //smXML:MD_Georectified/axisDimensionProperties/smXML:MD_Dimension/dimensionSize
911
0
        spatialRepresentationInfo.numberOfRows = getContentsAsInt(node,
912
0
            "smXML:MD_Georectified/axisDimensionProperties/smXML:MD_Dimension[dimensionName='row']/dimensionSize");
913
914
        //smXML:MD_Georectified/axisDimensionProperties/smXML:MD_Dimension/resolution/smXML:Measure
915
0
        spatialRepresentationInfo.rowResolution = getContentsAsFloat(node,
916
0
            "smXML:MD_Georectified/axisDimensionProperties/smXML:MD_Dimension[dimensionName='row']/resolution/smXML:Measure/smXML:value");
917
918
        //smXML:MD_Georectified/axisDimensionProperties/smXML:MD_Dimension/dimensionSize
919
0
        spatialRepresentationInfo.numberOfColumns = getContentsAsInt(node,
920
0
            "smXML:MD_Georectified/axisDimensionProperties/smXML:MD_Dimension[dimensionName='column']/dimensionSize");
921
922
        //smXML:MD_Georectified/axisDimensionProperties/smXML:MD_Dimension/resolution/smXML:Measure
923
0
        spatialRepresentationInfo.columnResolution = getContentsAsFloat(node,
924
0
            "smXML:MD_Georectified/axisDimensionProperties/smXML:MD_Dimension[dimensionName='column']/resolution/smXML:Measure/smXML:value");
925
926
        //smXML:MD_Georectified/cellGeometry
927
0
        spatialRepresentationInfo.cellGeometry = getContentsAsCharStar(node,
928
0
            "smXML:MD_Georectified/cellGeometry");
929
930
        //smXML:MD_Georectified/transformationParameterAvailability
931
0
        spatialRepresentationInfo.transformationParameterAvailability =
932
0
            getContentsAsBool(node, "smXML:MD_Georectified/transformationParameterAvailability");
933
934
        //smXML:MD_Georectified/checkPointAvailability
935
0
        spatialRepresentationInfo.checkPointAvailability = getContentsAsBool(
936
0
            node, "smXML:MD_Georectified/checkPointAvailability");
937
938
        //smXML:MD_Georectified/cornerPoints/gml:Point
939
0
        {
940
0
            const xmlNode* pNode = findNode(node,
941
0
                "smXML:MD_Georectified/cornerPoints/gml:Point/gml:coordinates");
942
0
            if (!pNode)
943
0
                return false;
944
945
            //Get the encoded corner values.
946
0
            const std::string value = getContents(*pNode);
947
948
            //Decode the extents
949
0
            (void)sscanf(value.c_str(), "%lf,%lf %lf,%lf",
950
0
                &spatialRepresentationInfo.llCornerX,
951
0
                &spatialRepresentationInfo.llCornerY,
952
0
                &spatialRepresentationInfo.urCornerX,
953
0
                &spatialRepresentationInfo.urCornerY);
954
0
        }
955
956
        //smXML:MD_Georectified/transformationDimensionDescription
957
0
        spatialRepresentationInfo.transformationDimensionDescription =
958
0
            getContentsAsCharStar(node,
959
0
                "smXML:MD_Georectified/transformationDimensionDescription");
960
961
        //smXML:MD_Georectified/transformationDimensionMapping
962
0
        spatialRepresentationInfo.transformationDimensionMapping =
963
0
            getContentsAsCharStar(node,
964
0
                "smXML:MD_Georectified/transformationDimensionMapping");
965
966
0
    }
967
207
    else if (schemaVersion == 2)
968
207
    {
969
        //gmd:MD_Georectified/gmd:axisDimensionProperties/gmd:MD_Dimension/gmd:dimensionSize
970
207
        spatialRepresentationInfo.numberOfRows = getContentsAsInt(node,
971
207
            "gmd:MD_Georectified/gmd:axisDimensionProperties/gmd:MD_Dimension/gmd:dimensionName/gmd:MD_DimensionNameTypeCode[@codeListValue='row']/parent::*/parent::*/gmd:dimensionSize/gco:Integer");
972
973
        //gmd:MD_Georectified/gmd:axisDimensionProperties/gmd:MD_Dimension/gmd:resolution
974
207
        spatialRepresentationInfo.rowResolution = getContentsAsFloat(node,
975
207
            "gmd:MD_Georectified/gmd:axisDimensionProperties/gmd:MD_Dimension/gmd:dimensionName/gmd:MD_DimensionNameTypeCode[@codeListValue='row']/parent::*/parent::*/gmd:resolution/gco:Measure");
976
977
        //gmd:MD_Georectified/gmd:axisDimensionProperties/gmd:MD_Dimension/gmd:dimensionSize
978
207
        spatialRepresentationInfo.numberOfColumns = getContentsAsInt(node,
979
207
            "gmd:MD_Georectified/gmd:axisDimensionProperties/gmd:MD_Dimension/gmd:dimensionName/gmd:MD_DimensionNameTypeCode[@codeListValue='column']/parent::*/parent::*/gmd:dimensionSize/gco:Integer");
980
981
        //gmd:MD_Georectified/gmd:axisDimensionProperties/gmd:MD_Dimension/gmd:resolution
982
207
        spatialRepresentationInfo.columnResolution = getContentsAsFloat(node,
983
207
            "gmd:MD_Georectified/gmd:axisDimensionProperties/gmd:MD_Dimension/gmd:dimensionName/gmd:MD_DimensionNameTypeCode[@codeListValue='column']/parent::*/parent::*/gmd:resolution/gco:Measure");
984
985
        //gmd:MD_Georectified/gmd:axisDimensionProperties/gmd:MD_Dimension/gmd:resolution[@uom]
986
207
        spatialRepresentationInfo.resolutionUnit = getPropertyAsString(node,
987
207
            "gmd:MD_Georectified/gmd:axisDimensionProperties/gmd:MD_Dimension/gmd:dimensionName/gmd:MD_DimensionNameTypeCode[@codeListValue='column']/parent::*/parent::*/gmd:resolution/gco:Measure", "uom");
988
989
        //gmd:MD_Georectified/gmd:cellGeometry
990
207
        delete[] spatialRepresentationInfo.cellGeometry;
991
207
        spatialRepresentationInfo.cellGeometry = getContentsAsCharStar(node,
992
207
            "gmd:MD_Georectified/gmd:cellGeometry/gmd:MD_CellGeometryCode");
993
994
        //gmd:MD_Georectified/gmd:transformationParameterAvailability
995
207
        spatialRepresentationInfo.transformationParameterAvailability =
996
207
            getContentsAsBool(node, "gmd:MD_Georectified/gmd:transformationParameterAvailability/gco:Boolean");
997
998
        //gmd:MD_Georectified/gmd:checkPointAvailability
999
207
        spatialRepresentationInfo.checkPointAvailability = getContentsAsBool(
1000
207
            node, "gmd:MD_Georectified/gmd:checkPointAvailability/gco:Boolean");
1001
1002
        //gmd:MD_Georectified/gmd:cornerPoints/gml:Point
1003
207
        {
1004
207
            const xmlNode *pNode = findNode(node,
1005
207
                "gmd:MD_Georectified/gmd:cornerPoints/gml:Point/gml:coordinates");
1006
207
            if (!pNode)
1007
0
                return false;
1008
1009
            //Get the encoded corner values.
1010
207
            const std::string value = getContents(*pNode);
1011
1012
            //Decode the extents
1013
207
            (void)sscanf(value.c_str(), "%lf,%lf %lf,%lf",
1014
207
                &spatialRepresentationInfo.llCornerX,
1015
207
                &spatialRepresentationInfo.llCornerY,
1016
207
                &spatialRepresentationInfo.urCornerX,
1017
207
                &spatialRepresentationInfo.urCornerY);
1018
207
        }
1019
207
    }
1020
1021
207
    return true;
1022
207
}
1023
1024
//************************************************************************
1025
//! Decode a BagIdentification from the supplied XML node.
1026
/*!
1027
\param node
1028
    \li The XML node containing the data identification information.
1029
\param dataIdentificationInfo
1030
    \li Modified to contain the data identification information from \e node.
1031
\param schemaVersion
1032
    \li The version of the schema stored in \e node.
1033
\return
1034
    \li True if the information was found and decoded properly, False if
1035
        an error occurs.
1036
*/
1037
//************************************************************************
1038
bool decodeDataIdentificationInfo(
1039
    xmlNode& node,
1040
    BagIdentification& dataIdentificationInfo,
1041
    uint16_t schemaVersion)
1042
207
{
1043
207
    if (schemaVersion == 1)
1044
0
    {
1045
        //smXML:BAG_DataIdentification/citation/smXML:CI_Citation/title
1046
0
        dataIdentificationInfo.title = getContentsAsCharStar(node,
1047
0
            "smXML:BAG_DataIdentification/citation/smXML:CI_Citation/title");
1048
1049
        //smXML:BAG_DataIdentification/citation/smXML:CI_Citation/date/smXML:CI_Date/date
1050
0
        dataIdentificationInfo.date = getContentsAsCharStar(node,
1051
0
            "smXML:BAG_DataIdentification/citation/smXML:CI_Citation/date/smXML:CI_Date/date");
1052
1053
        //smXML:BAG_DataIdentification/citation/smXML:CI_Citation/date/smXML:CI_Date/dateType
1054
0
        dataIdentificationInfo.dateType = getContentsAsCharStar(node,
1055
0
            "smXML:BAG_DataIdentification/citation/smXML:CI_Citation/date/smXML:CI_Date/dateType");
1056
1057
        //smXML:BAG_DataIdentification/citation/smXML:CI_Citation/citedResponsibleParty"
1058
0
        const auto partyNodes = findNodes(node,
1059
0
            "smXML:BAG_DataIdentification/citation/smXML:CI_Citation/citedResponsibleParty");
1060
0
        if (!partyNodes.empty())
1061
0
        {
1062
0
            dataIdentificationInfo.numberOfResponsibleParties =
1063
0
                static_cast<uint32_t>(partyNodes.size());
1064
0
            dataIdentificationInfo.responsibleParties =
1065
0
                new BagResponsibleParty[dataIdentificationInfo.numberOfResponsibleParties];
1066
1067
0
            for (uint32_t i = 0; i < dataIdentificationInfo.numberOfResponsibleParties; i++)
1068
0
            {
1069
0
                initResponsibleParty(dataIdentificationInfo.responsibleParties[i]);
1070
0
                if (!decodeResponsibleParty(*partyNodes[i],
1071
0
                    dataIdentificationInfo.responsibleParties[i], schemaVersion))
1072
0
                    return false;
1073
0
            }
1074
0
        }
1075
1076
        //smXML:BAG_DataIdentification/abstract
1077
0
        dataIdentificationInfo.abstractString = getContentsAsCharStar(node,
1078
0
            "smXML:BAG_DataIdentification/abstract");
1079
1080
        //smXML:BAG_DataIdentification/status
1081
0
        dataIdentificationInfo.status = getContentsAsCharStar(node,
1082
0
            "smXML:BAG_DataIdentification/status");
1083
1084
        //smXML:BAG_DataIdentification/spatialRepresentationType
1085
0
        dataIdentificationInfo.spatialRepresentationType = getContentsAsCharStar(
1086
0
            node, "smXML:BAG_DataIdentification/spatialRepresentationType");
1087
1088
        //smXML:BAG_DataIdentification/language
1089
0
        dataIdentificationInfo.language = getContentsAsCharStar(node,
1090
0
            "smXML:BAG_DataIdentification/language");
1091
1092
        //Doesn't appear to be set, so always set to utf8
1093
0
        dataIdentificationInfo.characterSet = copyString("utf8");
1094
1095
        //smXML:BAG_DataIdentification/topicCategory
1096
0
        dataIdentificationInfo.topicCategory = getContentsAsCharStar(node,
1097
0
            "smXML:BAG_DataIdentification/topicCategory");
1098
1099
        //smXML:BAG_DataIdentification/extent/smXML:EX_Extent/geographicElement/smXML:EX_GeographicBoundingBox/
1100
0
        dataIdentificationInfo.westBoundingLongitude = getContentsAsFloat(node,
1101
0
            "smXML:BAG_DataIdentification/extent/smXML:EX_Extent/geographicElement/smXML:EX_GeographicBoundingBox/westBoundLongitude");
1102
0
        dataIdentificationInfo.eastBoundingLongitude = getContentsAsFloat(node,
1103
0
            "smXML:BAG_DataIdentification/extent/smXML:EX_Extent/geographicElement/smXML:EX_GeographicBoundingBox/eastBoundLongitude");
1104
0
        dataIdentificationInfo.southBoundingLatitude = getContentsAsFloat(node,
1105
0
            "smXML:BAG_DataIdentification/extent/smXML:EX_Extent/geographicElement/smXML:EX_GeographicBoundingBox/southBoundLatitude");
1106
0
        dataIdentificationInfo.northBoundingLatitude = getContentsAsFloat(node,
1107
0
            "smXML:BAG_DataIdentification/extent/smXML:EX_Extent/geographicElement/smXML:EX_GeographicBoundingBox/northBoundLatitude");
1108
1109
        //smXML:BAG_DataIdentification/verticalUncertaintyType
1110
0
        dataIdentificationInfo.verticalUncertaintyType = getContentsAsCharStar(
1111
0
            node, "smXML:BAG_DataIdentification/verticalUncertaintyType");
1112
1113
        //smXML:BAG_DataIdentification/depthCorrectionType
1114
0
        dataIdentificationInfo.depthCorrectionType = getContentsAsCharStar(node,
1115
0
            "smXML:BAG_DataIdentification/depthCorrectionType");
1116
1117
        //smXML:BAG_DataIdentification/nodeGroupType
1118
0
        dataIdentificationInfo.nodeGroupType = getContentsAsCharStar(node,
1119
0
            "smXML:BAG_DataIdentification/nodeGroupType");
1120
1121
        //smXML:BAG_DataIdentification/elevationSolutionGroupType
1122
0
        dataIdentificationInfo.elevationSolutionGroupType = getContentsAsCharStar(
1123
0
            node, "smXML:BAG_DataIdentification/elevationSolutionGroupType");
1124
0
    }
1125
207
    else if (schemaVersion == 2)
1126
207
    {
1127
        //bag:BAG_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:title
1128
207
        dataIdentificationInfo.title = getContentsAsCharStar(node,
1129
207
            "bag:BAG_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:title/gco:CharacterString");
1130
1131
        //bag:BAG_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:CI_Date/gmd:date
1132
207
        dataIdentificationInfo.date = getContentsAsCharStar(node,
1133
207
            "bag:BAG_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:date/gmd:CI_Date/gmd:date/gco:Date");
1134
1135
        //bag:BAG_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:CI_Date/gmd:dateType
1136
207
        dataIdentificationInfo.dateType = getContentsAsCharStar(node,
1137
207
            "bag:BAG_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:date/gmd:CI_Date/gmd:dateType/gmd:CI_DateTypeCode");
1138
1139
        //bag:BAG_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:citedResponsibleParty
1140
207
        const auto partyNodes = findNodes(node,
1141
207
            "bag:BAG_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:citedResponsibleParty");
1142
207
        if (!partyNodes.empty())
1143
117
        {
1144
117
            dataIdentificationInfo.numberOfResponsibleParties =
1145
117
                static_cast<uint32_t>(partyNodes.size());
1146
117
            dataIdentificationInfo.responsibleParties =
1147
117
                new BagResponsibleParty[dataIdentificationInfo.numberOfResponsibleParties];
1148
1149
234
            for (uint32_t i = 0; i < dataIdentificationInfo.numberOfResponsibleParties; i++)
1150
117
            {
1151
117
                initResponsibleParty(dataIdentificationInfo.responsibleParties[i]);
1152
117
                if (!decodeResponsibleParty(*partyNodes[i],
1153
117
                    dataIdentificationInfo.responsibleParties[i], schemaVersion))
1154
0
                    return false;
1155
117
            }
1156
117
        }
1157
1158
        //bag:BAG_DataIdentification/gmd:abstract
1159
207
        dataIdentificationInfo.abstractString = getContentsAsCharStar(node,
1160
207
            "bag:BAG_DataIdentification/gmd:abstract/gco:CharacterString");
1161
1162
        //bag:BAG_DataIdentification/gmd:status
1163
207
        dataIdentificationInfo.status = getContentsAsCharStar(node,
1164
207
            "bag:BAG_DataIdentification/gmd:status/gmd:MD_ProgressCode");
1165
1166
        //bag:BAG_DataIdentification/gmd:spatialRepresentationType
1167
207
        dataIdentificationInfo.spatialRepresentationType = getContentsAsCharStar(
1168
207
            node, "bag:BAG_DataIdentification/gmd:spatialRepresentationType/gmd:MD_SpatialRepresentationTypeCode");
1169
1170
        //bag:BAG_DataIdentification/gmd:language
1171
207
        dataIdentificationInfo.language = getContentsAsCharStar(node,
1172
207
            "bag:BAG_DataIdentification/gmd:language/gmd:LanguageCode");
1173
1174
        //bag:BAG_DataIdentification/gmd:characterSet
1175
207
        dataIdentificationInfo.characterSet = getContentsAsCharStar(node,
1176
207
            "bag:BAG_DataIdentification/gmd:characterSet/gmd:MD_CharacterSetCode");
1177
1178
        //bag:BAG_DataIdentification/gmd:topicCategory
1179
207
        dataIdentificationInfo.topicCategory = getContentsAsCharStar(node,
1180
207
            "bag:BAG_DataIdentification/gmd:topicCategory/gmd:MD_TopicCategoryCode");
1181
1182
        //bag:BAG_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicBoundingBox/
1183
207
        dataIdentificationInfo.westBoundingLongitude = getContentsAsFloat(node,
1184
207
            "bag:BAG_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicBoundingBox/gmd:westBoundLongitude/gco:Decimal");
1185
207
        dataIdentificationInfo.eastBoundingLongitude = getContentsAsFloat(node,
1186
207
            "bag:BAG_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicBoundingBox/gmd:eastBoundLongitude/gco:Decimal");
1187
207
        dataIdentificationInfo.southBoundingLatitude = getContentsAsFloat(node,
1188
207
            "bag:BAG_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicBoundingBox/gmd:southBoundLatitude/gco:Decimal");
1189
207
        dataIdentificationInfo.northBoundingLatitude = getContentsAsFloat(node,
1190
207
            "bag:BAG_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicBoundingBox/gmd:northBoundLatitude/gco:Decimal");
1191
1192
        //bag:BAG_DataIdentification/bag:verticalUncertaintyType
1193
207
        dataIdentificationInfo.verticalUncertaintyType = getContentsAsCharStar(
1194
207
            node, "bag:BAG_DataIdentification/bag:verticalUncertaintyType/bag:BAG_VertUncertCode");
1195
1196
        //bag:BAG_DataIdentification/bag:depthCorrectionType
1197
207
        dataIdentificationInfo.depthCorrectionType = getContentsAsCharStar(node,
1198
207
            "bag:BAG_DataIdentification/bag:depthCorrectionType/bag:BAG_DepthCorrectCode");
1199
1200
        //bag:BAG_DataIdentification/bag:nodeGroupType
1201
207
        dataIdentificationInfo.nodeGroupType = getContentsAsCharStar(node,
1202
207
            "bag:BAG_DataIdentification/bag:nodeGroupType/bag:BAG_OptGroupCode");
1203
1204
        //bag:BAG_DataIdentification/bag:elevationSolutionGroupType
1205
207
        dataIdentificationInfo.elevationSolutionGroupType = getContentsAsCharStar(
1206
207
            node, "bag:BAG_DataIdentification/bag:elevationSolutionGroupType/bag:BAG_OptGroupCode");
1207
207
    }
1208
1209
207
    return true;
1210
207
}
1211
1212
//************************************************************************
1213
//! Decode a BagReferenceSystem from a BagSpatialRepresentation, the narrowly defined as the parsing of an EPSG code
1214
//! from the transformationDimensionDescription tag
1215
/*!
1216
\param spatialRepresentationInfo
1217
    \li Spatial representation information input.
1218
\param referenceSystemInfo
1219
    \li Modified to contain the reference system information from \e spatialRepresentationInfo->transformationDimensionDescription.
1220
\param schemaVersion
1221
    \li The version of the schema stored in \e node.
1222
\return
1223
    \li True if the information was found and decoded properly, False if
1224
        the optional tag is not present in the header or an error occurs.
1225
*/
1226
//************************************************************************
1227
bool decodeReferenceSystemInfoFromSpatial(
1228
    const BagSpatialRepresentation* spatialRepresentationInfo,
1229
    BagReferenceSystem* referenceSystemInfo,
1230
    uint16_t /*schemaVersion*/)
1231
0
{
1232
0
    if ( spatialRepresentationInfo && referenceSystemInfo )
1233
0
    {
1234
        // Is the TransformationDimensionDescription parameter present?
1235
0
        if ( spatialRepresentationInfo->transformationDimensionDescription )
1236
0
        {
1237
            // Find the assignment operator
1238
0
            auto* equal = strchr(
1239
0
                spatialRepresentationInfo->transformationDimensionDescription, '=');
1240
0
            if (equal != nullptr)
1241
0
            {
1242
0
                ++equal;
1243
0
                const auto epsg = atoi(equal);
1244
1245
                // Proceed to create a string containing the EPSG authority
1246
                // code, and set the type to "EPSG".
1247
0
                if (epsg > 0 && strncmp(
1248
0
                    spatialRepresentationInfo->transformationDimensionDescription, "EPSG", 4) == 0)
1249
0
                {
1250
0
                    char buffer[2048];
1251
1252
0
                    sprintf(buffer, "%d", epsg);
1253
0
                    referenceSystemInfo->definition = copyString(buffer);
1254
0
                    referenceSystemInfo->type = copyString("EPSG");
1255
1256
0
                    return true;
1257
0
                }
1258
0
            }
1259
0
        }
1260
0
    }
1261
1262
0
    return false;
1263
0
}
1264
1265
1266
//************************************************************************
1267
//! Decode a BagReferenceSystem from the supplied XML node.
1268
/*!
1269
\param node
1270
    \li The XML node containing the reference system information.
1271
\param referenceSystemInfo
1272
    \li Modified to contain the reference system information from \e node.
1273
\param schemaVersion
1274
    \li The version of the schema stored in \e node.
1275
\return
1276
    \li True if the information was found and decoded properly, False if
1277
        an error occurs.
1278
*/
1279
//************************************************************************
1280
bool decodeReferenceSystemInfo(
1281
    xmlNode& node,
1282
    BagReferenceSystem& referenceSystemInfo,
1283
    uint16_t schemaVersion)
1284
414
{
1285
414
    if (schemaVersion == 1)
1286
0
    {
1287
        //If I have an ellipsoid, then this must be horizontal.
1288
0
        auto ellipsoid = getContentsAsString(node, "smXML:MD_CRS/ellipsoid/smXML:RS_Identifier/code");
1289
0
        if (!ellipsoid.empty())
1290
0
        {
1291
0
            auto projectionId = getContentsAsString(node, "smXML:MD_CRS/projection/smXML:RS_Identifier/code");
1292
0
            auto datumId = getContentsAsString(node, "smXML:MD_CRS/datum/smXML:RS_Identifier/code");
1293
1294
0
            BagLegacyReferenceSystem v1Def;
1295
0
            memset(&v1Def, 0, sizeof(BagLegacyReferenceSystem));
1296
1297
            //Store the projection information.
1298
0
            v1Def.coordSys = bagCoordsys(projectionId.c_str());
1299
1300
            //Store the ellipsoid information.
1301
0
            strncpy(v1Def.geoParameters.ellipsoid, ellipsoid.c_str(), 256);
1302
1303
            //Store the datum information.
1304
0
            v1Def.geoParameters.datum = bagDatumID(datumId.c_str());
1305
1306
0
            v1Def.geoParameters.zone = getContentsAsInt(node,
1307
0
                "smXML:MD_CRS/projectionParameters/smXML:MD_ProjectionParameters/zone");
1308
0
            v1Def.geoParameters.std_parallel_1 = getContentsAsFloat(node,
1309
0
                "smXML:MD_CRS/projectionParameters/smXML:MD_ProjectionParameters/standardParallel[1]");
1310
0
            v1Def.geoParameters.std_parallel_2 = getContentsAsFloat(node,
1311
0
                "smXML:MD_CRS/projectionParameters/smXML:MD_ProjectionParameters/standardParallel[2]");
1312
0
            v1Def.geoParameters.central_meridian = getContentsAsFloat(node,
1313
0
                "smXML:MD_CRS/projectionParameters/smXML:MD_ProjectionParameters/longitudeOfCentralMeridian");
1314
0
            v1Def.geoParameters.origin_latitude = getContentsAsFloat(node,
1315
0
                "smXML:MD_CRS/projectionParameters/smXML:MD_ProjectionParameters/latitudeOfProjectionOrigin");
1316
0
            v1Def.geoParameters.false_easting = getContentsAsFloat(node,
1317
0
                "smXML:MD_CRS/projectionParameters/smXML:MD_ProjectionParameters/falseEasting");
1318
0
            v1Def.geoParameters.false_northing = getContentsAsFloat(node,
1319
0
                "smXML:MD_CRS/projectionParameters/smXML:MD_ProjectionParameters/falseNorthing");
1320
0
            const double scaleFactAtEq = getContentsAsFloat(node,
1321
0
                "smXML:MD_CRS/projectionParameters/smXML:MD_ProjectionParameters/scaleFactorAtEquator");
1322
0
            v1Def.geoParameters.longitude_of_centre = getContentsAsFloat(node,
1323
0
                "smXML:MD_CRS/projectionParameters/smXML:MD_ProjectionParameters/longitudeOfProjectionCenter");
1324
0
            v1Def.geoParameters.latitude_of_centre = getContentsAsFloat(node,
1325
0
                "smXML:MD_CRS/projectionParameters/smXML:MD_ProjectionParameters/latitudeOfProjectionCenter");
1326
0
            v1Def.geoParameters.longitude_down_from_pole = getContentsAsFloat(
1327
0
                node, "smXML:MD_CRS/projectionParameters/smXML:MD_ProjectionParameters/straightVerticalLongitudeFromPole");
1328
0
            const double scaleAtProjOrigin = getContentsAsFloat(node,
1329
0
                "smXML:MD_CRS/projectionParameters/smXML:MD_ProjectionParameters/scaleFactorAtProjectionOrigin");
1330
1331
            /* dhf */
1332
            /* scaleFactAtEq - for mercator */
1333
            /* scaleAtCenterLine - for oblique mercator (not supported) */
1334
            /* scaleAtProjOrigin - for polar stereographic & transverse mercator */
1335
0
            if (v1Def.coordSys == CoordinateType::Mercator)
1336
0
              v1Def.geoParameters.scale_factor = scaleFactAtEq;
1337
0
            if (v1Def.coordSys == CoordinateType::Transverse_Mercator ||
1338
0
                v1Def.coordSys == CoordinateType::Polar_Stereo)
1339
0
              v1Def.geoParameters.scale_factor = scaleAtProjOrigin;
1340
1341
0
            char buffer[2048];
1342
0
            const BagError error = bagLegacyToWkt(v1Def, buffer, sizeof(buffer),
1343
0
                nullptr, 0);
1344
0
            if (error)
1345
0
                return false;
1346
1347
0
            referenceSystemInfo.definition = copyString(buffer);
1348
0
            referenceSystemInfo.type = copyString("WKT");
1349
0
        }
1350
        //Else it must be vertical.
1351
0
        else
1352
0
        {
1353
0
            auto datum = getContentsAsString(node, "smXML:MD_CRS/datum/smXML:RS_Identifier/code");
1354
1355
0
            BagLegacyReferenceSystem system;
1356
0
            strncpy(system.geoParameters.vertical_datum, datum.c_str(), 256);
1357
1358
0
            char buffer[1024];
1359
0
            const BagError error = bagLegacyToWkt(system, nullptr, 0, buffer,
1360
0
                sizeof(buffer));
1361
0
            if (error)
1362
0
                return false;
1363
1364
0
            referenceSystemInfo.definition = copyString(buffer);
1365
0
            referenceSystemInfo.type = copyString("WKT");
1366
0
        }
1367
0
    }
1368
414
    else if (schemaVersion == 2)
1369
414
    {
1370
        //gmd:MD_ReferenceSystem/gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:code
1371
414
        referenceSystemInfo.definition = getContentsAsCharStar(node,
1372
414
            "gmd:MD_ReferenceSystem/gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:code/gco:CharacterString");
1373
1374
        //gmd:MD_ReferenceSystem/gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:codeSpace
1375
414
        referenceSystemInfo.type = getContentsAsCharStar(node,
1376
414
            "gmd:MD_ReferenceSystem/gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:codeSpace/gco:CharacterString");
1377
414
    }
1378
1379
414
    return true;
1380
414
}
1381
1382
//************************************************************************
1383
//! Validate an XML document against the current BAG metadata schema.
1384
/*!
1385
\param metadataDocument
1386
    \li The document to be validated.
1387
\return
1388
    \li BAG_SUCCESS if the document passes schema validation, a BAG error
1389
        code if an error occurs.
1390
*/
1391
//************************************************************************
1392
BagError validateSchema(xmlDoc& metadataDocument)
1393
0
{
1394
    //Get the location of the BAG home directory.
1395
0
    const std::string bagHome = bagGetHomeFolder();
1396
1397
0
    if (bagHome.empty())
1398
0
        return BAG_METADTA_NO_HOME;
1399
1400
    //Build the full path to the schema location.
1401
0
    std::string schemaFile(bagHome);
1402
0
    schemaFile += "/";
1403
0
    schemaFile += ons_schema_location;
1404
1405
    // make sure the main schema file exists.
1406
0
    struct stat fStat;
1407
0
    if (stat(schemaFile.c_str(), &fStat))
1408
0
        return BAG_METADTA_SCHEMA_FILE_MISSING;
1409
1410
    // Open the schema.
1411
0
    xmlDoc *pSchemaDoc = xmlParseFile(schemaFile.c_str());
1412
0
    if (!pSchemaDoc)
1413
0
        return BAG_METADTA_PARSE_FAILED;
1414
1415
    // Parse the schema.
1416
0
    xmlSchemaParserCtxt *pContext = xmlSchemaNewDocParserCtxt(pSchemaDoc);
1417
0
    if (!pContext)
1418
0
    {
1419
0
        xmlFreeDoc(pSchemaDoc);
1420
1421
0
        return BAG_METADTA_SCHEMA_SETUP_FAILED;
1422
0
    }
1423
1424
    // Initialize the schema object.
1425
0
    xmlSchema *pSchema = xmlSchemaParse(pContext);
1426
0
    if (!pSchema)
1427
0
    {
1428
0
        xmlSchemaFreeParserCtxt(pContext);
1429
0
        xmlFreeDoc(pSchemaDoc);
1430
1431
0
        return BAG_METADTA_SCHEMA_SETUP_FAILED;
1432
0
    }
1433
1434
    // Create the validation object.
1435
0
    xmlSchemaValidCtxt *pValidationContext = xmlSchemaNewValidCtxt(pSchema);
1436
0
    if (!pValidationContext)
1437
0
    {
1438
0
        xmlSchemaFree(pSchema);
1439
0
        xmlSchemaFreeParserCtxt(pContext);
1440
0
        xmlFreeDoc(pSchemaDoc);
1441
1442
0
        return BAG_METADTA_SCHEMA_VALIDATION_SETUP_FAILED;
1443
0
    }
1444
1445
    // Validate the document.
1446
0
    const int result = xmlSchemaValidateDoc(pValidationContext, &metadataDocument);
1447
1448
0
    xmlSchemaFreeValidCtxt(pValidationContext);
1449
0
    xmlSchemaFree(pSchema);
1450
0
    xmlSchemaFreeParserCtxt(pContext);
1451
0
    xmlFreeDoc(pSchemaDoc);
1452
1453
0
    return (result == 0) ? BAG_SUCCESS : BAG_METADTA_VALIDATE_FAILED;
1454
0
}
1455
1456
//************************************************************************
1457
//! Import the BAG_METADATA from a version 1 schema.
1458
/*!
1459
    Imports the metadata from a 'version 1' BAG schema.  Version 1 schemas
1460
    were used in BAG versions 1.0 - 1.4.  Version 2 schemas are used in
1461
    BAG version 1.5 and newer.
1462
1463
\param document
1464
    \li The XML document containing the 'version 1' schema.
1465
\param metadata
1466
    \li Modified to contain the BAG metadata information from \e document.
1467
        Input should not be NULL.
1468
\return
1469
    \li BAG_SUCCESS if the information is successfully extracted from \e document,
1470
        an error code otherwise.
1471
*/
1472
//************************************************************************
1473
BagError bagImportMetadataFromXmlV1(
1474
    const xmlDoc& document,
1475
    BagMetadata& metadata)
1476
0
{
1477
0
    xmlNode* pRoot = xmlDocGetRootElement(&document);
1478
0
    if (!pRoot)
1479
0
        return BAG_METADTA_NOT_INITIALIZED;
1480
1481
    //gmd:language
1482
0
    metadata.language = getContentsAsCharStar(*pRoot, "/smXML:MD_Metadata/language");
1483
1484
    //gmd:characterSet
1485
0
    metadata.characterSet = copyString("eng");
1486
1487
    //gmd:hierarchyLevel
1488
0
    metadata.hierarchyLevel = copyString("dataset");
1489
1490
    //gmd:contact
1491
0
    {
1492
0
        auto* pNode = findNode(*pRoot, "/smXML:MD_Metadata/contact");
1493
0
        if (!pNode)
1494
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1495
1496
0
        if (!decodeResponsibleParty(*pNode, *metadata.contact, 1))
1497
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1498
0
    }
1499
1500
    //gmd:dateStamp
1501
0
    metadata.dateStamp = getContentsAsCharStar(*pRoot,
1502
0
        "/smXML:MD_Metadata/dateStamp");
1503
1504
    //gmd:metadataStandardName
1505
0
    metadata.metadataStandardName = getContentsAsCharStar(*pRoot,
1506
0
        "/smXML:MD_Metadata/metadataStandardName");
1507
1508
    //gmd:metadataStandardVersion
1509
0
    metadata.metadataStandardVersion = getContentsAsCharStar(*pRoot,
1510
0
        "/smXML:MD_Metadata/metadataStandardVersion");
1511
1512
    //gmd:spatialRepresentationInfo
1513
0
    {
1514
0
        auto* pNode = findNode(*pRoot,
1515
0
            "/smXML:MD_Metadata/spatialRepresentationInfo");
1516
0
        if (!pNode)
1517
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1518
1519
0
        if (!decodeSpatialRepresentationInfo(*pNode,
1520
0
            *metadata.spatialRepresentationInfo, 1))
1521
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1522
0
    }
1523
1524
    //gmd:referenceSystemInfo (horizontal)
1525
0
    {
1526
0
        auto* pNode = findNode(*pRoot,
1527
0
            "/smXML:MD_Metadata/referenceSystemInfo[1]");
1528
0
        if (!pNode)
1529
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1530
1531
0
        if (!decodeReferenceSystemInfo(*pNode, *metadata.horizontalReferenceSystem, 1))
1532
0
        {
1533
            // If the reference system could not be identified from this block
1534
            // of code look for an EPSG code in the spatial reference system
1535
0
            if (!decodeReferenceSystemInfoFromSpatial(
1536
0
                metadata.spatialRepresentationInfo,
1537
0
                metadata.horizontalReferenceSystem, 1))
1538
0
                return BAG_METADTA_MISSING_MANDATORY_ITEM;
1539
0
        }
1540
0
    }
1541
1542
    //gmd:referenceSystemInfo (vertical)
1543
0
    {
1544
0
        auto* pNode = findNode(*pRoot,
1545
0
            "/smXML:MD_Metadata/referenceSystemInfo[2]");
1546
0
        if (!pNode)
1547
0
        {
1548
            //If we could not find the vertical coordinate system node, then
1549
            //lets look in the other location.
1550
0
            auto* pNode2 = findNode(*pRoot,
1551
0
                "/smXML:MD_Metadata/referenceSystemInfo[1]//verticalDatum");
1552
0
            if (!pNode2)
1553
0
                return BAG_METADTA_MISSING_MANDATORY_ITEM;
1554
1555
0
            const auto datum = getContentsAsString(*pNode2,
1556
0
                "smXML:RS_Identifier/code");
1557
0
            if (datum.empty())
1558
0
                return BAG_METADTA_MISSING_MANDATORY_ITEM;
1559
1560
0
            BagLegacyReferenceSystem system;
1561
0
            strncpy(system.geoParameters.vertical_datum, datum.c_str(), 256);
1562
1563
0
            char buffer[1024];
1564
0
            BagError error = bagLegacyToWkt(system, nullptr, 0, buffer,
1565
0
                sizeof(buffer));
1566
0
            if (error)
1567
0
                return error;
1568
1569
0
            metadata.verticalReferenceSystem->definition = copyString(buffer);
1570
0
            metadata.verticalReferenceSystem->type = copyString("WKT");
1571
0
        }
1572
0
        else if (!decodeReferenceSystemInfo(*pNode,
1573
0
            *metadata.verticalReferenceSystem, 1))
1574
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1575
0
    }
1576
1577
    //Because the previous metadata profile did not store the unit of measure
1578
    //for the spatial representation element, we will use the spatial reference
1579
    //system to figure it out.
1580
0
    {
1581
        //Get the WKT horizontal reference system string.
1582
0
        const std::string horizontalWKT{
1583
0
            metadata.horizontalReferenceSystem->definition};
1584
1585
        //If we have a projection, then it must be metres (its all we supported in the past)
1586
0
        if (horizontalWKT.find("PROJCS[") >= 0)
1587
0
            metadata.spatialRepresentationInfo->resolutionUnit = copyString("Metre");
1588
        //Else it must be geographic
1589
0
        else
1590
0
            metadata.spatialRepresentationInfo->resolutionUnit = copyString("Degree");
1591
0
    }
1592
1593
    //gmd:identificationInfo
1594
0
    {
1595
0
        auto* pNode = findNode(*pRoot, "/smXML:MD_Metadata/identificationInfo");
1596
0
        if (!pNode)
1597
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1598
1599
0
        if (!decodeDataIdentificationInfo(*pNode, *metadata.identificationInfo, 1))
1600
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1601
0
    }
1602
1603
    //gmd:dataQualityInfo
1604
0
    {
1605
0
        auto* pNode = findNode(*pRoot, "/smXML:MD_Metadata/dataQualityInfo");
1606
0
        if (!pNode)
1607
0
        {
1608
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1609
0
        }
1610
1611
0
        if (!decodeDataQualityInfo(*pNode, *metadata.dataQualityInfo, 1))
1612
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1613
0
    }
1614
1615
    //gmd:metadataConstraints (legal)
1616
0
    {
1617
0
        auto* pNode = findNode(*pRoot,
1618
0
            "/smXML:MD_Metadata/metadataConstraints/smXML:MD_LegalConstraints/parent::*");
1619
0
        if (!pNode)
1620
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1621
1622
0
        if (!decodeLegalConstraints(*pNode, *metadata.legalConstraints, 1))
1623
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1624
0
    }
1625
1626
    //gmd:metadataConstraints (security)
1627
0
    {
1628
0
        auto* pNode = findNode(*pRoot,
1629
0
            "/smXML:MD_Metadata/metadataConstraints/smXML:MD_SecurityConstraints/parent::*");
1630
0
        if (!pNode)
1631
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1632
1633
0
        if (!decodeSecurityConstraints(*pNode, *metadata.securityConstraints, 1))
1634
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1635
0
    }
1636
1637
0
    return BAG_SUCCESS;
1638
0
}
1639
1640
//************************************************************************
1641
//! Import the BAG_METADATA from a version 2 schema.
1642
/*!
1643
    Imports the metadata from a 'version 2' BAG schema.  Version 2 schemas
1644
    are used in BAG version 1.5 and newer.
1645
1646
\param document
1647
    \li The XML document containing the 'version 2' schema.
1648
\param metadata
1649
    \li Modified to contain the BAG metadata information from \e document.
1650
        Input should not be NULL.
1651
\return
1652
    \li BAG_SUCCESS if the information is successfully extracted from \e document,
1653
        an error code otherwise.
1654
*/
1655
//************************************************************************
1656
BagError bagImportMetadataFromXmlV2(
1657
    const xmlDoc& document,
1658
    BagMetadata& metadata)
1659
207
{
1660
207
    auto* pRoot = xmlDocGetRootElement(&document);
1661
207
    if (!pRoot)
1662
0
        return BAG_METADTA_NOT_INITIALIZED;
1663
1664
    //gmd:fileIdentifier
1665
207
    delete[] metadata.fileIdentifier;
1666
207
    metadata.fileIdentifier = getContentsAsCharStar(*pRoot,
1667
207
        "/gmi:MI_Metadata/gmd:fileIdentifier/gco:CharacterString");
1668
1669
    //gmd:language
1670
207
    delete[] metadata.language;
1671
207
    metadata.language = getContentsAsCharStar(*pRoot,
1672
207
        "/gmi:MI_Metadata/gmd:language/gmd:LanguageCode");
1673
1674
    //gmd:characterSet
1675
207
    delete[] metadata.characterSet;
1676
207
    metadata.characterSet = getContentsAsCharStar(*pRoot,
1677
207
        "/gmi:MI_Metadata/gmd:characterSet/gmd:MD_CharacterSetCode");
1678
1679
    //gmd:hierarchyLevel
1680
207
    delete[] metadata.hierarchyLevel;
1681
207
    metadata.hierarchyLevel = getContentsAsCharStar(*pRoot,
1682
207
        "/gmi:MI_Metadata/gmd:hierarchyLevel/gmd:MD_ScopeCode");
1683
1684
    //gmd:contact
1685
207
    {
1686
207
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:contact");
1687
207
        if (!pNode)
1688
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1689
1690
207
        if (!decodeResponsibleParty(*pNode, *metadata.contact, 2))
1691
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1692
207
    }
1693
1694
    //gmd:dateStamp
1695
207
    delete[] metadata.dateStamp;
1696
207
    metadata.dateStamp = getContentsAsCharStar(*pRoot,
1697
207
        "/gmi:MI_Metadata/gmd:dateStamp/gco:Date");
1698
1699
    //gmd:metadataStandardName
1700
207
    delete[] metadata.metadataStandardName;
1701
207
    metadata.metadataStandardName = getContentsAsCharStar(*pRoot,
1702
207
        "/gmi:MI_Metadata/gmd:metadataStandardName/gco:CharacterString");
1703
1704
    //gmd:metadataStandardVersion
1705
207
    delete[] metadata.metadataStandardVersion;
1706
207
    metadata.metadataStandardVersion = getContentsAsCharStar(*pRoot,
1707
207
        "/gmi:MI_Metadata/gmd:metadataStandardVersion/gco:CharacterString");
1708
1709
    //gmd:spatialRepresentationInfo
1710
207
    {
1711
207
        auto* pNode = findNode(*pRoot,
1712
207
            "/gmi:MI_Metadata/gmd:spatialRepresentationInfo/gmd:MD_Georectified/parent::*");
1713
207
        if (!pNode)
1714
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1715
1716
207
        if (!decodeSpatialRepresentationInfo(*pNode, *metadata.spatialRepresentationInfo, 2))
1717
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1718
207
    }
1719
1720
    //gmd:referenceSystemInfo (horizontal)
1721
207
    {
1722
207
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:referenceSystemInfo[1]");
1723
207
        if (!pNode)
1724
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1725
1726
207
        if (!decodeReferenceSystemInfo(*pNode, *metadata.horizontalReferenceSystem, 2))
1727
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1728
207
    }
1729
1730
    //gmd:referenceSystemInfo (vertical)
1731
207
    {
1732
207
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:referenceSystemInfo[2]");
1733
207
        if (!pNode)
1734
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1735
1736
207
        if (!decodeReferenceSystemInfo(*pNode, *metadata.verticalReferenceSystem, 2))
1737
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1738
207
    }
1739
1740
    //gmd:identificationInfo
1741
207
    {
1742
207
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:identificationInfo");
1743
207
        if (!pNode)
1744
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1745
1746
207
        if (!decodeDataIdentificationInfo(*pNode, *metadata.identificationInfo, 2))
1747
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1748
207
    }
1749
1750
    //gmd:dataQualityInfo
1751
207
    {
1752
207
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:dataQualityInfo");
1753
207
        if (!pNode)
1754
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1755
1756
207
        if (!decodeDataQualityInfo(*pNode, *metadata.dataQualityInfo, 2))
1757
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1758
207
    }
1759
1760
    //gmd:metadataConstraints (legal)
1761
207
    {
1762
207
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:metadataConstraints/gmd:MD_LegalConstraints/parent::*");
1763
207
        if (!pNode)
1764
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1765
1766
207
        if (!decodeLegalConstraints(*pNode, *metadata.legalConstraints, 2))
1767
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1768
207
    }
1769
1770
    //gmd:metadataConstraints (security)
1771
207
    {
1772
207
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:metadataConstraints/gmd:MD_SecurityConstraints/parent::*");
1773
207
        if (!pNode)
1774
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1775
1776
207
        if (!decodeSecurityConstraints(*pNode, *metadata.securityConstraints, 2))
1777
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1778
207
    }
1779
1780
207
    return BAG_SUCCESS;
1781
207
}
1782
1783
//************************************************************************
1784
//! Import the BAG_METADATA from an XML document.
1785
/*!
1786
    Imports the BAG metadata information from any supported schema version.
1787
1788
\param document
1789
    \li The XML document containing the 'version 2' schema.
1790
\param metadata
1791
    \li Modified to contain the BAG metadata information from \e document.
1792
        Input should not be NULL.
1793
\param doValidation
1794
    \li True to perform the schema validation (conformance test).  False
1795
        to simply extract the data.
1796
\return
1797
    \li BAG_SUCCESS on success.  A BAG error code is returned if there
1798
        is an error, or if schema validation fails (only if \e doValidation
1799
        is True).
1800
*/
1801
//************************************************************************
1802
BagError bagImportMetadataFromXml(
1803
    xmlDoc& document,
1804
    BagMetadata& metadata,
1805
    bool doValidation)
1806
207
{
1807
207
    const xmlNode* pRoot = xmlDocGetRootElement(&document);
1808
207
    if (!pRoot)
1809
0
        return BAG_METADTA_EMPTY_DOCUMENT;
1810
1811
207
    if (doValidation)
1812
0
    {
1813
0
        const BagError ret = validateSchema(document);
1814
0
        if (ret)
1815
0
            return ret;
1816
0
    }
1817
1818
    //Get the name of the root node.
1819
207
    const std::string rootName = getNodeName(*pRoot);
1820
1821
    //We will use the root node's name to figure out what version of the schema we are working with.
1822
207
    const uint16_t schemaVersion = (rootName == "smXML:MD_Metadata") ? 1 : 2;
1823
1824
207
    return (schemaVersion == 1) ?
1825
0
        bagImportMetadataFromXmlV1(document, metadata) :
1826
207
        bagImportMetadataFromXmlV2(document, metadata);
1827
207
}
1828
1829
//************************************************************************
1830
//! Import the BAG_METADATA from an XML document stored in a character buffer.
1831
/*!
1832
    Imports the BAG metadata information from any supported schema version.
1833
1834
\param xmlBuffer
1835
    \li The character buffer containing the XML document to be used for
1836
        import.  Input should not be NULL.
1837
\param bufferSize
1838
    \li The size in bytes of the \e xmlBuffer.
1839
\param metadata
1840
    \li Modified to contain the BAG metadata information from \e xmlBuffer.
1841
        Input should not be NULL.
1842
\param doValidation
1843
    \li True to perform the schema validation (conformance test).  False
1844
        to simply extract the data.
1845
\return
1846
    \li BAG_SUCCESS if the information is successfully extracted from \e xmlBuffer,
1847
        an error code otherwise.  If \e doValidation is True, an error will be returned
1848
        if the validation fails.
1849
*/
1850
//************************************************************************
1851
BagError bagImportMetadataFromXmlBuffer(
1852
    const char* xmlBuffer,
1853
    int bufferSize,
1854
    BagMetadata& metadata,
1855
    bool doValidation)
1856
207
{
1857
207
    xmlDoc* pDocument = xmlParseMemory(xmlBuffer, bufferSize);
1858
207
    if (!pDocument)
1859
0
        return BAG_METADTA_NOT_INITIALIZED;
1860
1861
207
    const BagError err = bagImportMetadataFromXml(*pDocument, metadata, doValidation);
1862
207
    xmlFreeDoc(pDocument);
1863
207
    return err;
1864
207
}
1865
1866
//************************************************************************
1867
//! Import the BAG_METADATA from an XML file.
1868
/*!
1869
    Imports the BAG metadata information from any supported schema version.
1870
1871
\param fileName
1872
    \li The fully qualified name of the XML file to be read.
1873
\param metadata
1874
    \li Modified to contain the BAG metadata information from \e fileName.
1875
        Input should not be NULL.
1876
\param doValidation
1877
    \li True to perform the schema validation (conformance test).  False
1878
        to simply extract the data.
1879
\return
1880
    \li BAG_SUCCESS if the information is successfully extracted from \e xmlBuffer,
1881
        an error code otherwise.  If \e doValidation is True, an error will be returned
1882
        if the validation fails.
1883
*/
1884
//************************************************************************
1885
BagError bagImportMetadataFromXmlFile(
1886
    const char* fileName,
1887
    BagMetadata& metadata,
1888
    bool doValidation)
1889
0
{
1890
0
    xmlDoc* pDocument = xmlParseFile(fileName);
1891
0
    if (!pDocument)
1892
0
        return BAG_METADTA_NOT_INITIALIZED;
1893
1894
0
    const BagError err = bagImportMetadataFromXml(*pDocument, metadata, doValidation);
1895
0
    xmlFreeDoc(pDocument);
1896
0
    return err;
1897
0
}
1898
1899
}  // namespace BAG
1900