Coverage Report

Created: 2025-08-11 06:23

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