Coverage Report

Created: 2026-08-13 07:18

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
21.3k
    {
47
21.3k
        this->m_pEncodedString = xmlEncodeEntitiesReentrant(&doc,
48
21.3k
            reinterpret_cast<const xmlChar*>(string));
49
21.3k
    }
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
21.3k
    {
61
21.3k
        xmlFree(this->m_pEncodedString);
62
21.3k
    }
63
64
    //************************************************************************
65
    //! Conversion operator.
66
    /*!
67
    \return
68
        \li The encoded string.
69
    */
70
    //************************************************************************
71
    operator xmlChar*() const noexcept
72
21.3k
    {
73
21.3k
        return this->m_pEncodedString;
74
21.3k
    }
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.52k
{
92
1.52k
    std::stringstream lineStream;
93
1.52k
    (void)lineStream.imbue(std::locale::classic());
94
1.52k
    lineStream << value;
95
96
1.52k
    double dblValue = 0.0;
97
1.52k
    lineStream >> dblValue;
98
99
1.52k
    return dblValue;
100
1.52k
}
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
292
{
113
292
    std::string name;
114
115
    // append the namespace prefix
116
292
    const xmlNs* nameSpace = node.ns;
117
292
    if (nameSpace)
118
292
    {
119
292
        name = reinterpret_cast<const char*>(nameSpace->prefix);
120
292
        name += ':';
121
292
    }
122
123
292
    name += reinterpret_cast<const char*>(node.name);
124
125
292
    return name;
126
292
}
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
21.0k
{
144
    //Get the root node of the document.
145
21.0k
    const xmlNode* pRoot = xmlDocGetRootElement(relativeNode.doc);
146
21.0k
    if (!pRoot)
147
0
        return {};
148
149
    //If the xPath context has not been initialized yet, do it now.
150
21.0k
    xmlXPathContext* pContext = xmlXPathNewContext(relativeNode.doc);
151
21.0k
    if (!pContext)
152
0
        return {};
153
154
21.0k
    pContext->node = &relativeNode;
155
156
    //Register any namespaces with the xPath context.
157
21.0k
    const xmlNs* xmlNameSpace = pRoot->nsDef;
158
159
165k
    while (xmlNameSpace)
160
144k
    {
161
144k
        if (xmlNameSpace->prefix)
162
144k
        {
163
144k
            const int ret = xmlXPathRegisterNs(pContext, xmlNameSpace->prefix,
164
144k
                xmlNameSpace->href);
165
144k
            if (ret != 0)
166
0
            {
167
                //Error
168
0
                 xmlXPathFreeContext(pContext);
169
0
                 return {};
170
0
            }
171
144k
        }
172
173
144k
        xmlNameSpace = xmlNameSpace->next;
174
144k
    }
175
176
    //Encode the specified search string.
177
21.0k
    const EncodedString encodedSearch{*relativeNode.doc, searchString};
178
179
    //Evaluate the expression.
180
21.0k
    xmlXPathObject* pPathObject = xmlXPathEvalExpression(encodedSearch, pContext);
181
21.0k
    if (!pPathObject)
182
3.05k
    {
183
        //Error
184
3.05k
        xmlXPathFreeContext(pContext);
185
3.05k
        return {};
186
3.05k
    }
187
188
18.0k
    std::vector<xmlNode*> retList;
189
190
    //Add each value that was returned.
191
18.0k
    if (pPathObject->nodesetval)
192
16.2k
    {
193
16.2k
        retList.reserve(pPathObject->nodesetval->nodeNr);
194
195
32.3k
        for (int i = 0; i < pPathObject->nodesetval->nodeNr; i++)
196
16.0k
            retList.push_back(pPathObject->nodesetval->nodeTab[i]);
197
16.2k
    }
198
199
18.0k
    xmlXPathFreeObject(pPathObject);
200
18.0k
    xmlXPathFreeContext(pContext);
201
202
18.0k
    return retList;
203
21.0k
}
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
19.6k
{
221
19.6k
    auto retList = findNodes(relativeNode, searchString);
222
19.6k
    if (retList.empty())
223
4.90k
        return nullptr;
224
225
14.7k
    return retList.front();
226
19.6k
}
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
230
{
243
     // Retrieve the property.
244
230
    xmlChar* temp = xmlGetProp(&current, EncodedString{*current.doc,
245
230
        propertyName});
246
230
    if (!temp)
247
7
        return {};
248
249
223
    const std::string value(reinterpret_cast<char*>(temp));
250
251
    // Free the memory allocated by xmlGetProp().
252
223
    xmlFree(temp);
253
254
223
    return value;
255
230
}
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
12.1k
{
268
12.1k
    std::string contents;
269
270
    // Get the children of the current element.
271
12.1k
    const xmlNode* text = current.children;
272
273
    // Concatenate all the text elements.
274
24.4k
    while (text)
275
12.2k
    {
276
12.2k
        if (text->type == XML_TEXT_NODE && text->content)
277
12.1k
            contents += reinterpret_cast<const char*>(text->content);
278
279
12.2k
        text = text->next;
280
12.2k
    }
281
282
12.1k
    return contents;
283
12.1k
}
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
292
{
306
292
    const xmlNode* pNode = findNode(node, searchPath);
307
292
    if (!pNode)
308
62
        return nullptr;
309
310
230
    const std::string value = getProperty(*pNode, propertyName);
311
312
230
    char* result = new char[value.size() + 1];
313
230
    strcpy(result, value.c_str());
314
230
    result[value.size()] = '\0';
315
316
230
    return result;
317
318
292
}
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
13.7k
{
337
13.7k
    const xmlNode* pNode = findNode(node, searchPath);
338
13.7k
    if (!pNode)
339
3.98k
        return {};
340
341
9.79k
    return getContents(*pNode);
342
13.7k
}
343
344
char* copyString(const char* source)
345
13.7k
{
346
13.7k
    if (!source || strlen(source) == 0)
347
3.98k
        return nullptr;
348
349
9.79k
    char* result = new char[strlen(source) + 1];
350
9.79k
    strcpy(result, source);
351
352
9.79k
    return result;
353
13.7k
}
354
355
char* getContentsAsCharStar(
356
    xmlNode& node,
357
    const char* searchPath)
358
13.7k
{
359
13.7k
    return copyString(getContentsAsString(node, searchPath).c_str());
360
13.7k
}
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
584
{
379
584
    auto* pNode = findNode(node, searchPath);
380
584
    if (!pNode)
381
134
        return 0;
382
383
450
    return static_cast<int32_t>(toDouble(getContents(*pNode)));
384
584
}
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.75k
{
403
1.75k
    const xmlNode* pNode = findNode(node, searchPath);
404
1.75k
    if (!pNode)
405
675
        return 0.0;
406
407
1.07k
    return toDouble(getContents(*pNode));
408
1.75k
}
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
584
{
427
584
    const xmlNode* pNode = findNode(node, searchPath);
428
584
    if (!pNode)
429
47
        return false;
430
431
537
    const std::string value = getContents(*pNode);
432
433
    // Prevent repeated memory allocations.
434
537
    static std::string kZero{"0"};
435
537
    static std::string kFalse{"false"};
436
437
537
    return (value != kZero && value != kFalse);
438
584
}
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
743
{
498
743
    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
743
    else if (schemaVersion == 2)
517
743
    {
518
        //gmd:CI_ResponsibleParty/gmd:individualName
519
743
        responsibleParty.individualName = getContentsAsCharStar(node,
520
743
            "gmd:CI_ResponsibleParty/gmd:individualName/gco:CharacterString");
521
522
        //gmd:CI_ResponsibleParty/gmd:organisationName
523
743
        responsibleParty.organisationName = getContentsAsCharStar(node,
524
743
            "gmd:CI_ResponsibleParty/gmd:organisationName/gco:CharacterString");
525
526
        //gmd:CI_ResponsibleParty/gmd:positionName
527
743
        responsibleParty.positionName = getContentsAsCharStar(node,
528
743
            "gmd:CI_ResponsibleParty/gmd:positionName/gco:CharacterString");
529
530
        //gmd:CI_ResponsibleParty/gmd:role
531
743
        responsibleParty.role = getContentsAsCharStar(node,
532
743
            "gmd:CI_ResponsibleParty/gmd:role/gmd:CI_RoleCode");
533
743
    }
534
535
743
    return true;
536
743
}
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
292
{
557
292
    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
292
    else if (schemaVersion == 2)
568
292
    {
569
        //gmd:MD_LegalConstraints/gmd:useConstraints
570
292
        legalConstraints.useConstraints = getContentsAsCharStar(node,
571
292
            "gmd:MD_LegalConstraints/gmd:useConstraints/gmd:MD_RestrictionCode");
572
573
        //gmd:MD_LegalConstraints/gmd:otherConstraints
574
292
        legalConstraints.otherConstraints = getContentsAsCharStar(node,
575
292
            "gmd:MD_LegalConstraints/gmd:otherConstraints/gco:CharacterString");
576
292
    }
577
578
292
    return true;
579
292
}
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
292
{
600
292
    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
292
    else if (schemaVersion == 2)
611
292
    {
612
        //gmd:MD_SecurityConstraints/gmd:classification
613
292
        securityConstraints.classification = getContentsAsCharStar(node,
614
292
            "gmd:MD_SecurityConstraints/gmd:classification/gmd:MD_ClassificationCode");
615
616
        //gmd:MD_SecurityConstraints/gmd:userNote
617
292
        securityConstraints.userNote = getContentsAsCharStar(node,
618
292
            "gmd:MD_SecurityConstraints/gmd:userNote/gco:CharacterString");
619
292
    }
620
621
292
    return true;
622
292
}
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
292
{
643
292
    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
292
    else if (schemaVersion == 2)
650
292
    {
651
        //gmd:LI_Source/gmd:description
652
292
        sourceInfo.description = getContentsAsCharStar(node,
653
292
            "gmd:LI_Source/gmd:description/gco:CharacterString");
654
655
        //gmd:LI_Source/gmd:sourceCitation/gmd:CI_Citation/gmd:title
656
292
        sourceInfo.title = getContentsAsCharStar(node,
657
292
            "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
292
        sourceInfo.date = getContentsAsCharStar(node,
661
292
            "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
292
        sourceInfo.dateType = getContentsAsCharStar(node,
665
292
            "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
292
        const auto partyNodes = findNodes(node,
669
292
            "gmd:LI_Source/gmd:sourceCitation/gmd:CI_Citation/gmd:citedResponsibleParty");
670
292
        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
292
    }
685
686
292
    return true;
687
292
}
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
291
{
708
291
    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
291
    else if (schemaVersion == 2)
761
291
    {
762
        //bag:BAG_ProcessStep/gmd:description
763
291
        processStep.description = getContentsAsCharStar(node,
764
291
            "//gmd:description/gco:CharacterString");
765
766
        //bag:BAG_ProcessStep/gmd:dateTime
767
291
        processStep.dateTime = getContentsAsCharStar(node,
768
291
            "//gmd:dateTime/gco:DateTime");
769
770
        //bag:BAG_ProcessStep/bag:trackingId
771
291
        processStep.trackingId = getContentsAsCharStar(node,
772
291
            "//bag:trackingId/gco:CharacterString");
773
774
        //bag:BAG_ProcessStep/gmd:processor
775
291
        const auto processorNodes = findNodes(node,
776
291
            "//gmd:processor");
777
291
        if (!processorNodes.empty())
778
291
        {
779
291
            processStep.numberOfProcessors =
780
291
                static_cast<uint32_t>(processorNodes.size());
781
291
            processStep.processors =
782
291
                new BagResponsibleParty[processStep.numberOfProcessors];
783
784
583
            for (uint32_t i = 0; i < processStep.numberOfProcessors; i++)
785
292
            {
786
292
                initResponsibleParty(processStep.processors[i]);
787
292
                if (!decodeResponsibleParty(*processorNodes[i],
788
292
                    processStep.processors[i], schemaVersion))
789
0
                    return false;
790
292
            }
791
291
        }
792
793
        //bag:BAG_ProcessStep/gmd:source
794
291
        const auto sourceNodes = findNodes(node, "//gmd:source");
795
291
        if (!sourceNodes.empty())
796
291
        {
797
291
            processStep.numberOfSources =
798
291
                static_cast<uint32_t>(sourceNodes.size());
799
291
            processStep.lineageSources =
800
291
                new BagSource[processStep.numberOfSources];
801
802
583
            for (uint32_t i = 0; i < processStep.numberOfSources; i++)
803
292
            {
804
292
                initSourceInfo(processStep.lineageSources[i]);
805
292
                if (!decodeSourceInfo(*sourceNodes[i],
806
292
                    processStep.lineageSources[i], schemaVersion))
807
0
                    return false;
808
292
            }
809
291
        }
810
291
    }
811
812
291
    return true;
813
291
}
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
292
{
834
292
    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
292
    else if (schemaVersion == 2)
860
292
    {
861
        //gmd:DQ_DataQuality/gmd:scope/gmd:DQ_Scope/gmd:level
862
292
        delete[] dataQualityInfo.scope;
863
292
        dataQualityInfo.scope = getContentsAsCharStar(node,
864
292
            "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
292
        const auto stepNodes = findNodes(node,
868
292
            "gmd:DQ_DataQuality/gmd:lineage/gmd:LI_Lineage/gmd:processStep");
869
292
        if (!stepNodes.empty())
870
291
        {
871
291
            dataQualityInfo.numberOfProcessSteps =
872
291
                static_cast<uint32_t>(stepNodes.size());
873
291
            dataQualityInfo.lineageProcessSteps =
874
291
                new BagProcessStep[dataQualityInfo.numberOfProcessSteps];
875
876
582
            for (uint32_t i = 0; i < dataQualityInfo.numberOfProcessSteps; i++)
877
291
            {
878
291
                initProcessStep(dataQualityInfo.lineageProcessSteps[i]);
879
291
                if (!decodeProcessStep(*stepNodes[i],
880
291
                    dataQualityInfo.lineageProcessSteps[i], schemaVersion))
881
0
                    return false;
882
291
            }
883
291
        }
884
292
    }
885
886
292
    return true;
887
292
}
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
292
{
908
292
    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
292
    else if (schemaVersion == 2)
968
292
    {
969
        //gmd:MD_Georectified/gmd:axisDimensionProperties/gmd:MD_Dimension/gmd:dimensionSize
970
292
        spatialRepresentationInfo.numberOfRows = getContentsAsInt(node,
971
292
            "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
292
        spatialRepresentationInfo.rowResolution = getContentsAsFloat(node,
975
292
            "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
292
        spatialRepresentationInfo.numberOfColumns = getContentsAsInt(node,
979
292
            "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
292
        spatialRepresentationInfo.columnResolution = getContentsAsFloat(node,
983
292
            "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
292
        spatialRepresentationInfo.resolutionUnit = getPropertyAsString(node,
987
292
            "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
292
        delete[] spatialRepresentationInfo.cellGeometry;
991
292
        spatialRepresentationInfo.cellGeometry = getContentsAsCharStar(node,
992
292
            "gmd:MD_Georectified/gmd:cellGeometry/gmd:MD_CellGeometryCode");
993
994
        //gmd:MD_Georectified/gmd:transformationParameterAvailability
995
292
        spatialRepresentationInfo.transformationParameterAvailability =
996
292
            getContentsAsBool(node, "gmd:MD_Georectified/gmd:transformationParameterAvailability/gco:Boolean");
997
998
        //gmd:MD_Georectified/gmd:checkPointAvailability
999
292
        spatialRepresentationInfo.checkPointAvailability = getContentsAsBool(
1000
292
            node, "gmd:MD_Georectified/gmd:checkPointAvailability/gco:Boolean");
1001
1002
        //gmd:MD_Georectified/gmd:cornerPoints/gml:Point
1003
292
        {
1004
292
            const xmlNode *pNode = findNode(node,
1005
292
                "gmd:MD_Georectified/gmd:cornerPoints/gml:Point/gml:coordinates");
1006
292
            if (!pNode)
1007
0
                return false;
1008
1009
            //Get the encoded corner values.
1010
292
            const std::string value = getContents(*pNode);
1011
1012
            //Decode the extents
1013
292
            (void)sscanf(value.c_str(), "%lf,%lf %lf,%lf",
1014
292
                &spatialRepresentationInfo.llCornerX,
1015
292
                &spatialRepresentationInfo.llCornerY,
1016
292
                &spatialRepresentationInfo.urCornerX,
1017
292
                &spatialRepresentationInfo.urCornerY);
1018
292
        }
1019
292
    }
1020
1021
292
    return true;
1022
292
}
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
292
{
1043
292
    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
292
    else if (schemaVersion == 2)
1126
292
    {
1127
        //bag:BAG_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:title
1128
292
        dataIdentificationInfo.title = getContentsAsCharStar(node,
1129
292
            "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
292
        dataIdentificationInfo.date = getContentsAsCharStar(node,
1133
292
            "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
292
        dataIdentificationInfo.dateType = getContentsAsCharStar(node,
1137
292
            "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
292
        const auto partyNodes = findNodes(node,
1141
292
            "bag:BAG_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:citedResponsibleParty");
1142
292
        if (!partyNodes.empty())
1143
159
        {
1144
159
            dataIdentificationInfo.numberOfResponsibleParties =
1145
159
                static_cast<uint32_t>(partyNodes.size());
1146
159
            dataIdentificationInfo.responsibleParties =
1147
159
                new BagResponsibleParty[dataIdentificationInfo.numberOfResponsibleParties];
1148
1149
318
            for (uint32_t i = 0; i < dataIdentificationInfo.numberOfResponsibleParties; i++)
1150
159
            {
1151
159
                initResponsibleParty(dataIdentificationInfo.responsibleParties[i]);
1152
159
                if (!decodeResponsibleParty(*partyNodes[i],
1153
159
                    dataIdentificationInfo.responsibleParties[i], schemaVersion))
1154
0
                    return false;
1155
159
            }
1156
159
        }
1157
1158
        //bag:BAG_DataIdentification/gmd:abstract
1159
292
        dataIdentificationInfo.abstractString = getContentsAsCharStar(node,
1160
292
            "bag:BAG_DataIdentification/gmd:abstract/gco:CharacterString");
1161
1162
        //bag:BAG_DataIdentification/gmd:status
1163
292
        dataIdentificationInfo.status = getContentsAsCharStar(node,
1164
292
            "bag:BAG_DataIdentification/gmd:status/gmd:MD_ProgressCode");
1165
1166
        //bag:BAG_DataIdentification/gmd:spatialRepresentationType
1167
292
        dataIdentificationInfo.spatialRepresentationType = getContentsAsCharStar(
1168
292
            node, "bag:BAG_DataIdentification/gmd:spatialRepresentationType/gmd:MD_SpatialRepresentationTypeCode");
1169
1170
        //bag:BAG_DataIdentification/gmd:language
1171
292
        dataIdentificationInfo.language = getContentsAsCharStar(node,
1172
292
            "bag:BAG_DataIdentification/gmd:language/gmd:LanguageCode");
1173
1174
        //bag:BAG_DataIdentification/gmd:characterSet
1175
292
        dataIdentificationInfo.characterSet = getContentsAsCharStar(node,
1176
292
            "bag:BAG_DataIdentification/gmd:characterSet/gmd:MD_CharacterSetCode");
1177
1178
        //bag:BAG_DataIdentification/gmd:topicCategory
1179
292
        dataIdentificationInfo.topicCategory = getContentsAsCharStar(node,
1180
292
            "bag:BAG_DataIdentification/gmd:topicCategory/gmd:MD_TopicCategoryCode");
1181
1182
        //bag:BAG_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicBoundingBox/
1183
292
        dataIdentificationInfo.westBoundingLongitude = getContentsAsFloat(node,
1184
292
            "bag:BAG_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicBoundingBox/gmd:westBoundLongitude/gco:Decimal");
1185
292
        dataIdentificationInfo.eastBoundingLongitude = getContentsAsFloat(node,
1186
292
            "bag:BAG_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicBoundingBox/gmd:eastBoundLongitude/gco:Decimal");
1187
292
        dataIdentificationInfo.southBoundingLatitude = getContentsAsFloat(node,
1188
292
            "bag:BAG_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicBoundingBox/gmd:southBoundLatitude/gco:Decimal");
1189
292
        dataIdentificationInfo.northBoundingLatitude = getContentsAsFloat(node,
1190
292
            "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
292
        dataIdentificationInfo.verticalUncertaintyType = getContentsAsCharStar(
1194
292
            node, "bag:BAG_DataIdentification/bag:verticalUncertaintyType/bag:BAG_VertUncertCode");
1195
1196
        //bag:BAG_DataIdentification/bag:depthCorrectionType
1197
292
        dataIdentificationInfo.depthCorrectionType = getContentsAsCharStar(node,
1198
292
            "bag:BAG_DataIdentification/bag:depthCorrectionType/bag:BAG_DepthCorrectCode");
1199
1200
        //bag:BAG_DataIdentification/bag:nodeGroupType
1201
292
        dataIdentificationInfo.nodeGroupType = getContentsAsCharStar(node,
1202
292
            "bag:BAG_DataIdentification/bag:nodeGroupType/bag:BAG_OptGroupCode");
1203
1204
        //bag:BAG_DataIdentification/bag:elevationSolutionGroupType
1205
292
        dataIdentificationInfo.elevationSolutionGroupType = getContentsAsCharStar(
1206
292
            node, "bag:BAG_DataIdentification/bag:elevationSolutionGroupType/bag:BAG_OptGroupCode");
1207
292
    }
1208
1209
292
    return true;
1210
292
}
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
                    snprintf(buffer, 2048, "%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
584
{
1285
584
    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
584
    else if (schemaVersion == 2)
1369
584
    {
1370
        //gmd:MD_ReferenceSystem/gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:code
1371
584
        referenceSystemInfo.definition = getContentsAsCharStar(node,
1372
584
            "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
584
        referenceSystemInfo.type = getContentsAsCharStar(node,
1376
584
            "gmd:MD_ReferenceSystem/gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:codeSpace/gco:CharacterString");
1377
584
    }
1378
1379
584
    return true;
1380
584
}
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
292
{
1660
292
    auto* pRoot = xmlDocGetRootElement(&document);
1661
292
    if (!pRoot)
1662
0
        return BAG_METADTA_NOT_INITIALIZED;
1663
1664
    //gmd:fileIdentifier
1665
292
    delete[] metadata.fileIdentifier;
1666
292
    metadata.fileIdentifier = getContentsAsCharStar(*pRoot,
1667
292
        "/gmi:MI_Metadata/gmd:fileIdentifier/gco:CharacterString");
1668
1669
    //gmd:language
1670
292
    delete[] metadata.language;
1671
292
    metadata.language = getContentsAsCharStar(*pRoot,
1672
292
        "/gmi:MI_Metadata/gmd:language/gmd:LanguageCode");
1673
1674
    //gmd:characterSet
1675
292
    delete[] metadata.characterSet;
1676
292
    metadata.characterSet = getContentsAsCharStar(*pRoot,
1677
292
        "/gmi:MI_Metadata/gmd:characterSet/gmd:MD_CharacterSetCode");
1678
1679
    //gmd:hierarchyLevel
1680
292
    delete[] metadata.hierarchyLevel;
1681
292
    metadata.hierarchyLevel = getContentsAsCharStar(*pRoot,
1682
292
        "/gmi:MI_Metadata/gmd:hierarchyLevel/gmd:MD_ScopeCode");
1683
1684
    //gmd:contact
1685
292
    {
1686
292
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:contact");
1687
292
        if (!pNode)
1688
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1689
1690
292
        if (!decodeResponsibleParty(*pNode, *metadata.contact, 2))
1691
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1692
292
    }
1693
1694
    //gmd:dateStamp
1695
292
    delete[] metadata.dateStamp;
1696
292
    metadata.dateStamp = getContentsAsCharStar(*pRoot,
1697
292
        "/gmi:MI_Metadata/gmd:dateStamp/gco:Date");
1698
1699
    //gmd:metadataStandardName
1700
292
    delete[] metadata.metadataStandardName;
1701
292
    metadata.metadataStandardName = getContentsAsCharStar(*pRoot,
1702
292
        "/gmi:MI_Metadata/gmd:metadataStandardName/gco:CharacterString");
1703
1704
    //gmd:metadataStandardVersion
1705
292
    delete[] metadata.metadataStandardVersion;
1706
292
    metadata.metadataStandardVersion = getContentsAsCharStar(*pRoot,
1707
292
        "/gmi:MI_Metadata/gmd:metadataStandardVersion/gco:CharacterString");
1708
1709
    //gmd:spatialRepresentationInfo
1710
292
    {
1711
292
        auto* pNode = findNode(*pRoot,
1712
292
            "/gmi:MI_Metadata/gmd:spatialRepresentationInfo/gmd:MD_Georectified/parent::*");
1713
292
        if (!pNode)
1714
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1715
1716
292
        if (!decodeSpatialRepresentationInfo(*pNode, *metadata.spatialRepresentationInfo, 2))
1717
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1718
292
    }
1719
1720
    //gmd:referenceSystemInfo (horizontal)
1721
292
    {
1722
292
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:referenceSystemInfo[1]");
1723
292
        if (!pNode)
1724
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1725
1726
292
        if (!decodeReferenceSystemInfo(*pNode, *metadata.horizontalReferenceSystem, 2))
1727
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1728
292
    }
1729
1730
    //gmd:referenceSystemInfo (vertical)
1731
292
    {
1732
292
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:referenceSystemInfo[2]");
1733
292
        if (!pNode)
1734
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1735
1736
292
        if (!decodeReferenceSystemInfo(*pNode, *metadata.verticalReferenceSystem, 2))
1737
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1738
292
    }
1739
1740
    //gmd:identificationInfo
1741
292
    {
1742
292
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:identificationInfo");
1743
292
        if (!pNode)
1744
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1745
1746
292
        if (!decodeDataIdentificationInfo(*pNode, *metadata.identificationInfo, 2))
1747
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1748
292
    }
1749
1750
    //gmd:dataQualityInfo
1751
292
    {
1752
292
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:dataQualityInfo");
1753
292
        if (!pNode)
1754
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1755
1756
292
        if (!decodeDataQualityInfo(*pNode, *metadata.dataQualityInfo, 2))
1757
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1758
292
    }
1759
1760
    //gmd:metadataConstraints (legal)
1761
292
    {
1762
292
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:metadataConstraints/gmd:MD_LegalConstraints/parent::*");
1763
292
        if (!pNode)
1764
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1765
1766
292
        if (!decodeLegalConstraints(*pNode, *metadata.legalConstraints, 2))
1767
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1768
292
    }
1769
1770
    //gmd:metadataConstraints (security)
1771
292
    {
1772
292
        auto* pNode = findNode(*pRoot, "/gmi:MI_Metadata/gmd:metadataConstraints/gmd:MD_SecurityConstraints/parent::*");
1773
292
        if (!pNode)
1774
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1775
1776
292
        if (!decodeSecurityConstraints(*pNode, *metadata.securityConstraints, 2))
1777
0
            return BAG_METADTA_MISSING_MANDATORY_ITEM;
1778
292
    }
1779
1780
292
    return BAG_SUCCESS;
1781
292
}
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
292
{
1807
292
    const xmlNode* pRoot = xmlDocGetRootElement(&document);
1808
292
    if (!pRoot)
1809
0
        return BAG_METADTA_EMPTY_DOCUMENT;
1810
1811
292
    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
292
    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
292
    const uint16_t schemaVersion = (rootName == "smXML:MD_Metadata") ? 1 : 2;
1823
1824
292
    return (schemaVersion == 1) ?
1825
0
        bagImportMetadataFromXmlV1(document, metadata) :
1826
292
        bagImportMetadataFromXmlV2(document, metadata);
1827
292
}
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
292
{
1857
292
    xmlDoc* pDocument = xmlParseMemory(xmlBuffer, bufferSize);
1858
292
    if (!pDocument)
1859
0
        return BAG_METADTA_NOT_INITIALIZED;
1860
1861
292
    const BagError err = bagImportMetadataFromXml(*pDocument, metadata, doValidation);
1862
292
    xmlFreeDoc(pDocument);
1863
292
    return err;
1864
292
}
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