Coverage Report

Created: 2025-06-20 06:09

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