Coverage Report

Created: 2026-09-01 07:08

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