From 9011d50997260f3aafa1ef713e82ebd3ed4b1ae1 Mon Sep 17 00:00:00 2001 From: Vincent Fazio Date: Wed, 3 Jan 2024 16:37:33 +1100 Subject: [PATCH] Improved ISO 19115 Part 3 XML parsing - Added ArcGIS synthetic sample - Added tests for DQ_DataQuality, MD_FeatureCatalogueDescription and MD_Bands - Ability to parse older mdb v1.0 XML --- owslib/iso_3.py | 588 +-- tests/resources/iso3_examples/README.txt | 1 + .../resources/iso3_examples/arcgis-sample.xml | 3762 +++++++++++++++++ tests/test_iso3_parsing.py | 194 +- 4 files changed, 4249 insertions(+), 296 deletions(-) create mode 100644 tests/resources/iso3_examples/arcgis-sample.xml diff --git a/owslib/iso_3.py b/owslib/iso_3.py index 482d8925..0e413585 100755 --- a/owslib/iso_3.py +++ b/owslib/iso_3.py @@ -25,7 +25,7 @@ # ISO 19115 Part 3 XML namespaces -namespaces = { +NAMESPACES_V2 = { "mdb":"http://standards.iso.org/iso/19115/-3/mdb/2.0", "cat":"http://standards.iso.org/iso/19115/-3/cat/1.0", "gfc":"http://standards.iso.org/iso/19110/gfc/1.1", @@ -57,6 +57,38 @@ "xsi":"http://www.w3.org/2001/XMLSchema-instance" } +NAMESPACES_V1 = { + "xsi":"http://www.w3.org/2001/XMLSchema-instance", + "cat":"http://standards.iso.org/iso/19115/-3/cat/1.0", + "cit":"http://standards.iso.org/iso/19115/-3/cit/1.0", + "gcx":"http://standards.iso.org/iso/19115/-3/gcx/1.0", + "gex":"http://standards.iso.org/iso/19115/-3/gex/1.0", + "lan":"http://standards.iso.org/iso/19115/-3/lan/1.0", + "srv":"http://standards.iso.org/iso/19115/-3/srv/2.0", + "mac":"http://standards.iso.org/iso/19115/-3/mac/1.0", + "mas":"http://standards.iso.org/iso/19115/-3/mas/1.0", + "mcc":"http://standards.iso.org/iso/19115/-3/mcc/1.0", + "mco":"http://standards.iso.org/iso/19115/-3/mco/1.0", + "mda":"http://standards.iso.org/iso/19115/-3/mda/1.0", + "mdb":"http://standards.iso.org/iso/19115/-3/mdb/1.0", + "mdt":"http://standards.iso.org/iso/19115/-3/mdt/1.0", + "mex":"http://standards.iso.org/iso/19115/-3/mex/1.0", + "mrl":"http://standards.iso.org/iso/19115/-3/mrl/1.0", + "mds":"http://standards.iso.org/iso/19115/-3/mds/1.0", + "mmi":"http://standards.iso.org/iso/19115/-3/mmi/1.0", + "mpc":"http://standards.iso.org/iso/19115/-3/mpc/1.0", + "mrc":"http://standards.iso.org/iso/19115/-3/mrc/1.0", + "mrd":"http://standards.iso.org/iso/19115/-3/mrd/1.0", + "mri":"http://standards.iso.org/iso/19115/-3/mri/1.0", + "mrs":"http://standards.iso.org/iso/19115/-3/mrs/1.0", + "msr":"http://standards.iso.org/iso/19115/-3/msr/1.0", + "mdq":"http://standards.iso.org/iso/19157/-2/mdq/1.0", + "dqc":"http://standards.iso.org/iso/19157/-2/dqc/1.0", + "gco":"http://standards.iso.org/iso/19115/-3/gco/1.0", + "gml":"http://www.opengis.net/gml/3.2", + "xlink":"http://www.w3.org/1999/xlink" +} + class printable(): """ A super class used to roughly pretty print class members @@ -100,6 +132,7 @@ def __init__(self, md=None): :param md: etree.Element root """ + self.namespaces = NAMESPACES_V2 if md is None: self.md = None self.xml = None @@ -129,86 +162,91 @@ def __init__(self, md=None): else: # part of a larger document self.xml = etree.tostring(md) - val = md.find(util.nspath_eval('mdb:metadataIdentifier/mcc:MD_Identifier/mcc:code/gco:CharacterString', namespaces)) + # Test mdb version + if md.find(util.nspath_eval('mdb:metadataIdentifier', NAMESPACES_V2)) is None and \ + md.find(util.nspath_eval('mdb:metadataIdentifier', NAMESPACES_V1)) is not None: + self.namespaces = NAMESPACES_V1 + + val = md.find(util.nspath_eval('mdb:metadataIdentifier/mcc:MD_Identifier/mcc:code/gco:CharacterString', self.namespaces)) self.identifier = util.testXMLValue(val) - val = md.find(util.nspath_eval('mdb:parentMetadata/cit:CI_Citation/cit:identifier/mcc:MD_Identifier/mcc:code/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('mdb:parentMetadata/cit:CI_Citation/cit:identifier/mcc:MD_Identifier/mcc:code/gco:CharacterString', self.namespaces)) self.parentidentifier = util.testXMLValue(val) - val = md.find(util.nspath_eval('lan:language/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('lan:language/gco:CharacterString', self.namespaces)) self.language = util.testXMLValue(val) - val = md.find(util.nspath_eval('mdb:identificationInfo/mri:MD_DataIdentification/mri:citation/cit:CI_Citation/cit:identifier/mcc:MD_Identifier/mcc:code/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('mdb:identificationInfo/mri:MD_DataIdentification/mri:citation/cit:CI_Citation/cit:identifier/mcc:MD_Identifier/mcc:code/gco:CharacterString', self.namespaces)) self.dataseturi = util.testXMLValue(val) - val = md.find(util.nspath_eval('mdb:defaultLocale/lan:PT_Locale/lan:language/lan:LanguageCode', namespaces)) + val = md.find(util.nspath_eval('mdb:defaultLocale/lan:PT_Locale/lan:language/lan:LanguageCode', self.namespaces)) self.languagecode = util.testXMLAttribute(val, 'codeListValue') - val = md.find(util.nspath_eval('mdb:dateInfo/cit:CI_Date/cit:date/gco:DateTime', namespaces)) + val = md.find(util.nspath_eval('mdb:dateInfo/cit:CI_Date/cit:date/gco:DateTime', self.namespaces)) self.datestamp = util.testXMLValue(val) val = md.find( - util.nspath_eval('mdb:defaultLocale/lan:PT_Locale/lan:characterEncoding/lan:MD_CharacterSetCode', namespaces)) + util.nspath_eval('mdb:defaultLocale/lan:PT_Locale/lan:characterEncoding/lan:MD_CharacterSetCode', self.namespaces)) self.charset = util.testXMLAttribute(val, 'codeListValue') val = md.find( - util.nspath_eval('mdb:metadataScope/mdb:MD_MetadataScope/mdb:resourceScope/mcc:MD_ScopeCode', namespaces)) + util.nspath_eval('mdb:metadataScope/mdb:MD_MetadataScope/mdb:resourceScope/mcc:MD_ScopeCode', self.namespaces)) self.hierarchy = util.testXMLAttribute(val, 'codeListValue') self.contact = [] - for i in md.findall(util.nspath_eval('mdb:contact/cit:CI_Responsibility', namespaces)): - o = CI_Responsibility(i) + for i in md.findall(util.nspath_eval('mdb:contact/cit:CI_Responsibility', self.namespaces)): + o = CI_Responsibility(self.namespaces, i) self.contact.append(o) self.datetimestamp = self.datestamp - val = md.find(util.nspath_eval('mdb:metadataStandard/cit:CI_Citation/cit:title/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('mdb:metadataStandard/cit:CI_Citation/cit:title/gco:CharacterString', self.namespaces)) self.stdname = util.testXMLValue(val) - val = md.find(util.nspath_eval('mdb:metadataStandard/cit:CI_Citation/cit:edition/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('mdb:metadataStandard/cit:CI_Citation/cit:edition/gco:CharacterString', self.namespaces)) self.stdver = util.testXMLValue(val) self.locales = [] - for i in md.findall(util.nspath_eval('mdb:defaultLocale/lan:PT_Locale', namespaces)): - self.locales.append(PT_Locale(i)) + for i in md.findall(util.nspath_eval('mdb:defaultLocale/lan:PT_Locale', self.namespaces)): + self.locales.append(PT_Locale(self.namespaces, i)) - val = md.find(util.nspath_eval('mdb:referenceSystemInfo/mrs:MD_ReferenceSystem', namespaces)) + val = md.find(util.nspath_eval('mdb:referenceSystemInfo/mrs:MD_ReferenceSystem', self.namespaces)) if val is not None: - self.referencesystem = MD_ReferenceSystem(val) + self.referencesystem = MD_ReferenceSystem(self.namespaces, val) else: self.referencesystem = None self.identification = [] - for idinfo in md.findall(util.nspath_eval('mdb:identificationInfo', namespaces)): + for idinfo in md.findall(util.nspath_eval('mdb:identificationInfo', self.namespaces)): if len(idinfo) > 0: val = list(idinfo)[0] tagval = util.xmltag_split(val.tag) if tagval == 'MD_DataIdentification': - self.identification.append(MD_DataIdentification(val, 'dataset')) + self.identification.append(MD_DataIdentification(self.namespaces, val, 'dataset')) elif tagval == 'MD_ServiceIdentification': - self.identification.append(MD_DataIdentification(val, 'service')) + self.identification.append(MD_DataIdentification(self.namespaces, val, 'service')) elif tagval == 'SV_ServiceIdentification': - self.identification.append(SV_ServiceIdentification(val)) + self.identification.append(SV_ServiceIdentification(self.namespaces, val)) self.contentinfo = [] for contentinfo in md.findall( - util.nspath_eval('mdb:contentInfo/mrc:MD_FeatureCatalogueDescription', namespaces)): - self.contentinfo.append(MD_FeatureCatalogueDescription(contentinfo)) + util.nspath_eval('mdb:contentInfo/mrc:MD_FeatureCatalogueDescription', self.namespaces)): + self.contentinfo.append(MD_FeatureCatalogueDescription(self.namespaces, contentinfo)) for contentinfo in md.findall( - util.nspath_eval('mdb:contentInfo/mrc:MD_ImageDescription', namespaces)): - self.contentinfo.append(MD_ImageDescription(contentinfo)) + util.nspath_eval('mdb:contentInfo/mrc:MD_ImageDescription', self.namespaces)): + self.contentinfo.append(MD_ImageDescription(self.namespaces, contentinfo)) - val = md.find(util.nspath_eval('mdb:distributionInfo/mrd:MD_Distribution', namespaces)) + val = md.find(util.nspath_eval('mdb:distributionInfo/mrd:MD_Distribution', self.namespaces)) if val is not None: - self.distribution = MD_Distribution(val) + self.distribution = MD_Distribution(self.namespaces, val) else: self.distribution = None - val = md.find(util.nspath_eval('mdb:dataQualityInfo/mdq:DQ_DataQuality', namespaces)) + val = md.find(util.nspath_eval('mdb:dataQualityInfo/mdq:DQ_DataQuality', self.namespaces)) if val is not None: - self.dataquality = DQ_DataQuality(val) + self.dataquality = DQ_DataQuality(self.namespaces, val) else: self.dataquality = None @@ -246,12 +284,14 @@ class PT_Locale(printable): """ Process PT_Locale """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ Parses PT_Locale XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: PT_Locale etree.Element """ + self.namespaces = namespaces if md is None: self.id = None self.languagecode = None @@ -259,9 +299,9 @@ def __init__(self, md=None): else: self.id = md.attrib.get('id') self.languagecode = md.find( - util.nspath_eval('lan:language/lan:LanguageCode', namespaces)).attrib.get('codeListValue') + util.nspath_eval('lan:language/lan:LanguageCode', self.namespaces)).attrib.get('codeListValue') self.charset = md.find( - util.nspath_eval('lan:characterEncoding/lan:MD_CharacterSetCode', namespaces)).attrib.get( + util.nspath_eval('lan:characterEncoding/lan:MD_CharacterSetCode', self.namespaces)).attrib.get( 'codeListValue') @@ -269,45 +309,49 @@ def __init__(self, md=None): class CI_Date(printable): """ Process CI_Date """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ Parses CI_Date XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: CI_Date etree.Element """ + self.namespaces = namespaces if md is None: self.date = None self.type = None else: - val = md.find(util.nspath_eval('cit:date/gco:Date', namespaces)) + val = md.find(util.nspath_eval('cit:date/gco:Date', self.namespaces)) if val is not None: self.date = util.testXMLValue(val) else: - val = md.find(util.nspath_eval('cit:date/gco:DateTime', namespaces)) + val = md.find(util.nspath_eval('cit:date/gco:DateTime', self.namespaces)) if val is not None: self.date = util.testXMLValue(val) else: self.date = None - val = md.find(util.nspath_eval('cit:dateType/cit:CI_DateTypeCode', namespaces)) + val = md.find(util.nspath_eval('cit:dateType/cit:CI_DateTypeCode', self.namespaces)) self.type = _testCodeListValue(val) class CI_Responsibility(printable): """ Process CI_Responsibility """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ Parses CI_Responsibility XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: CI_Responsibility etree.Element """ + self.namespaces = namespaces + self.phone = None + self.fax = None if md is None: self.name = None self.organization = None self.position = None - self.phone = None - self.fax = None self.address = None self.city = None self.region = None @@ -317,142 +361,150 @@ def __init__(self, md=None): self.onlineresource = None self.role = None else: - val = md.find(util.nspath_eval('cit:party/cit:CI_Organisation/cit:individual/cit:CI_Individual/cit:name/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('cit:party/cit:CI_Organisation/cit:individual/cit:CI_Individual/cit:name/gco:CharacterString', self.namespaces)) self.name = util.testXMLValue(val) - val = md.find(util.nspath_eval('cit:party/cit:CI_Organisation/cit:name/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('cit:party/cit:CI_Organisation/cit:name/gco:CharacterString', self.namespaces)) self.organization = util.testXMLValue(val) - val = md.find(util.nspath_eval('cit:party/cit:CI_Organisation/cit:individual/cit:CI_Individual/cit:positionName/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('cit:party/cit:CI_Organisation/cit:individual/cit:CI_Individual/cit:positionName/gco:CharacterString', self.namespaces)) self.position = util.testXMLValue(val) # Telephone - val_list = md.xpath('cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:phone/cit:CI_Telephone[cit:numberType/cit:CI_TelephoneTypeCode/@codeListValue="voice"]/cit:number/gco:CharacterString', namespaces=namespaces) + val_list = md.xpath('cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:phone/cit:CI_Telephone[cit:numberType/cit:CI_TelephoneTypeCode/@codeListValue="voice"]/cit:number/gco:CharacterString', namespaces=self.namespaces) if len(val_list) > 0: self.phone = util.testXMLValue(val_list[0]) # Facsimile (Telephone and fax are differentiated by telephone type codes) - val_list = md.xpath('cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:phone/cit:CI_Telephone[cit:numberType/cit:CI_TelephoneTypeCode/@codeListValue="facsimile"]/cit:number/gco:CharacterString', namespaces=namespaces) + val_list = md.xpath('cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:phone/cit:CI_Telephone[cit:numberType/cit:CI_TelephoneTypeCode/@codeListValue="facsimile"]/cit:number/gco:CharacterString', namespaces=self.namespaces) if len(val_list) > 0: self.fax = util.testXMLValue(val_list[0]) val = md.find(util.nspath_eval( 'cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:address/cit:CI_Address/cit:deliveryPoint/gco:CharacterString', - namespaces)) + self.namespaces)) self.address = util.testXMLValue(val) val = md.find(util.nspath_eval( - 'cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:address/cit:CI_Address/cit:city/gco:CharacterString', namespaces)) + 'cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:address/cit:CI_Address/cit:city/gco:CharacterString', self.namespaces)) self.city = util.testXMLValue(val) val = md.find(util.nspath_eval( 'cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:address/cit:CI_Address/cit:administrativeArea/gco:CharacterString', - namespaces)) + self.namespaces)) self.region = util.testXMLValue(val) val = md.find(util.nspath_eval( 'cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:address/cit:CI_Address/cit:postalCode/gco:CharacterString', - namespaces)) + self.namespaces)) self.postcode = util.testXMLValue(val) val = md.find(util.nspath_eval( 'cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:address/cit:CI_Address/cit:country/gco:CharacterString', - namespaces)) + self.namespaces)) self.country = util.testXMLValue(val) val = md.find(util.nspath_eval( 'cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:address/cit:CI_Address/cit:electronicMailAddress/gco:CharacterString', - namespaces)) + self.namespaces)) self.email = util.testXMLValue(val) val = md.find(util.nspath_eval( - 'cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:onlineResource/cit:CI_OnlineResource', namespaces)) + 'cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:onlineResource/cit:CI_OnlineResource', self.namespaces)) if val is not None: - self.onlineresource = CI_OnlineResource(val) + self.onlineresource = CI_OnlineResource(self.namespaces, val) else: self.onlineresource = None - val = md.find(util.nspath_eval('cit:role/cit:CI_RoleCode', namespaces)) + val = md.find(util.nspath_eval('cit:role/cit:CI_RoleCode', self.namespaces)) self.role = _testCodeListValue(val) class Keyword(printable): """ Class for complex keywords, with labels and URLs """ - def __init__(self, kw=None): + def __init__(self, namespaces, kw=None): """ Parses keyword Element + :param namespaces: dict of XML namespaces, key is namespace, val is path :param kw: keyword 'gco:CharacterString' or 'gcx:Anchor' etree.Element """ + self.namespaces = namespaces if kw is None: self.name = None self.url = None else: self.name = util.testXMLValue(kw) - self.url = kw.attrib.get(util.nspath_eval('xlink:href', namespaces)) + self.url = kw.attrib.get(util.nspath_eval('xlink:href', self.namespaces)) class MD_Keywords(printable): """ Class for the metadata MD_Keywords element """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ Parses keyword Element + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: keyword etree.Element """ - if md is None: - self.keywords = [] - self.type = None - self.thesaurus = None - self.kwdtype_codeList = 'http://standards.iso.org/iso/19115/-3/resources/Codelist/gmxCodelists.xml#MD_KeywordTypeCode' - else: - self.keywords = [] - val = md.findall(util.nspath_eval('mri:keyword/gco:CharacterString', namespaces)) + self.namespaces = namespaces + self.thesaurus = None + self.keywords = [] + self.type = None + self.kwdtype_codeList = 'http://standards.iso.org/iso/19115/-3/resources/Codelist/gmxCodelists.xml#MD_KeywordTypeCode' + + if md is not None: + val = md.findall(util.nspath_eval('mri:keyword/gco:CharacterString', self.namespaces)) if len(val) == 0: - val = md.findall(util.nspath_eval('mri:keyword/gcx:Anchor', namespaces)) + val = md.findall(util.nspath_eval('mri:keyword/gcx:Anchor', self.namespaces)) for word in val: - self.keywords.append(Keyword(word)) - self.type = None - val = md.find(util.nspath_eval('mri:type/mri:MD_KeywordTypeCode', namespaces)) + self.keywords.append(Keyword(self.namespaces, word)) + + val = md.find(util.nspath_eval('mri:type/mri:MD_KeywordTypeCode', self.namespaces)) self.type = util.testXMLAttribute(val, 'codeListValue') - self.thesaurus = None - val = md.find(util.nspath_eval('mri:thesaurusName/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('mri:thesaurusName/gco:CharacterString', self.namespaces)) if val is not None: self.thesaurus = {} - title = val.find(util.nspath_eval('cit:title/gco:CharacterString', namespaces)) + title = val.find(util.nspath_eval('cit:title/gco:CharacterString', self.namespaces)) self.thesaurus['title'] = util.testXMLValue(title) self.thesaurus['url'] = None if self.thesaurus['title'] is None: # try gmx:Anchor - t = val.find(util.nspath_eval('cit:title/gcx:Anchor', namespaces)) + t = val.find(util.nspath_eval('cit:title/gcx:Anchor', self.namespaces)) if t is not None: self.thesaurus['title'] = util.testXMLValue(t) - self.thesaurus['url'] = t.attrib.get(util.nspath_eval('xlink:href', namespaces)) + self.thesaurus['url'] = t.attrib.get(util.nspath_eval('xlink:href', self.namespaces)) - date_ = val.find(util.nspath_eval('cit:date/cit:CI_Date/cit:date/gco:Date', namespaces)) + date_ = val.find(util.nspath_eval('cit:date/cit:CI_Date/cit:date/gco:Date', self.namespaces)) self.thesaurus['date'] = util.testXMLValue(date_) datetype = val.find( - util.nspath_eval('cit:date/cit:CI_Date/cit:dateType/cit:CI_DateTypeCode', namespaces)) + util.nspath_eval('cit:date/cit:CI_Date/cit:dateType/cit:CI_DateTypeCode', self.namespaces)) self.thesaurus['datetype'] = util.testXMLAttribute(datetype, 'codeListValue') class MD_DataIdentification(printable): """ Process MD_DataIdentification """ - def __init__(self, md=None, identtype=None): + def __init__(self, namespaces, md=None, identtype=None): """ Parses MD_DataIdentification XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: MD_DataIdentification etree.Element :param identtype: identitication type e.g. 'dataset' if MD_DataIdentification, 'service' if MD_ServiceIdentification """ + self.namespaces = namespaces self.aggregationinfo = None + self.bbox = None + self.temporalextent_start = None + self.temporalextent_end = None + self.extent = None if md is None: self.identtype = None self.title = None @@ -487,26 +539,22 @@ def __init__(self, md=None, identtype=None): self.keywords = [] self.topiccategory = [] self.supplementalinformation = None - self.extent = None - self.bbox = None - self.temporalextent_start = None - self.temporalextent_end = None self.spatialrepresentationtype = [] else: self.identtype = identtype val = md.find(util.nspath_eval( - 'mri:citation/cit:CI_Citation/cit:title/gco:CharacterString', namespaces)) + 'mri:citation/cit:CI_Citation/cit:title/gco:CharacterString', self.namespaces)) self.title = util.testXMLValue(val) val = md.find(util.nspath_eval( - 'mri:citation/cit:CI_Citation/cit:alternateTitle/gco:CharacterString', namespaces)) + 'mri:citation/cit:CI_Citation/cit:alternateTitle/gco:CharacterString', self.namespaces)) self.alternatetitle = util.testXMLValue(val) self.uricode = [] for end_tag in ['gco:CharacterString', 'gcx:Anchor']: _values = md.findall(util.nspath_eval( f"mri:citation/cit:CI_Citation/cit:identifier/mcc:MD_Identifier/mcc:code/{end_tag}", - namespaces)) + self.namespaces)) for i in _values: val = util.testXMLValue(i) if val is not None: @@ -515,7 +563,7 @@ def __init__(self, md=None, identtype=None): self.uricodespace = [] for i in md.findall(util.nspath_eval( 'mri:citation/cit:CI_Citation/cit:identifier/mcc:MD_Identifier/mcc:codeSpace/gco:CharacterString', - namespaces)): + self.namespaces)): val = util.testXMLValue(i) if val is not None: self.uricodespace.append(val) @@ -523,13 +571,13 @@ def __init__(self, md=None, identtype=None): self.date = [] self.datetype = [] - for i in md.findall(util.nspath_eval('mri:citation/cit:CI_Citation/cit:date/cit:CI_Date', namespaces)): - self.date.append(CI_Date(i)) + for i in md.findall(util.nspath_eval('mri:citation/cit:CI_Citation/cit:date/cit:CI_Date', self.namespaces)): + self.date.append(CI_Date(self.namespaces, i)) self.uselimitation = [] self.uselimitation_url = [] uselimit_values = md.findall(util.nspath_eval( - 'mri:resourceConstraints/mco:MD_LegalConstraints/mco:useLimitation/gco:CharacterString>', namespaces)) + 'mri:resourceConstraints/mco:MD_LegalConstraints/mco:useLimitation/gco:CharacterString>', self.namespaces)) for i in uselimit_values: val = util.testXMLValue(i) if val is not None: @@ -538,7 +586,7 @@ def __init__(self, md=None, identtype=None): self.accessconstraints = [] for i in md.findall(util.nspath_eval( 'mri:resourceConstraints/mco:MD_LegalConstraints/mco:accessConstraints/mco:MD_RestrictionCode', - namespaces)): + self.namespaces)): val = _testCodeListValue(i) if val is not None: self.accessconstraints.append(val) @@ -549,7 +597,7 @@ def __init__(self, md=None, identtype=None): for end_tag in ['gco:CharacterString', 'gcx:Anchor']: for i in md.findall(util.nspath_eval( f"mri:resourceConstraints/mco:MD_LegalConstraints/mco:otherConstraints/{end_tag}", - namespaces)): + self.namespaces)): val = util.testXMLValue(i) if val is not None: self.otherconstraints.append(val) @@ -557,7 +605,7 @@ def __init__(self, md=None, identtype=None): self.securityconstraints = [] for i in md.findall(util.nspath_eval( 'mri:resourceConstraints/mco:MD_SecurityConstraints/mco:classification/mco:MD_ClassificationCode', - namespaces)): + self.namespaces)): val = _testCodeListValue(i) if val is not None: self.securityconstraints.append(val) @@ -565,7 +613,7 @@ def __init__(self, md=None, identtype=None): self.useconstraints = [] for i in md.findall(util.nspath_eval( 'mri:resourceConstraints/mco:MD_LegalConstraints/mco:useConstraints/mco:MD_RestrictionCode', - namespaces)): + self.namespaces)): val = _testCodeListValue(i) if val is not None: self.useconstraints.append(val) @@ -573,7 +621,7 @@ def __init__(self, md=None, identtype=None): self.denominators = [] for i in md.findall(util.nspath_eval( 'mri:spatialResolution/mri:MD_Resolution/mri:equivalentScale/mri:MD_RepresentativeFraction/mri:denominator/gco:Integer', - namespaces)): + self.namespaces)): val = util.testXMLValue(i) if val is not None: self.denominators.append(val) @@ -581,20 +629,20 @@ def __init__(self, md=None, identtype=None): self.distance = [] self.uom = [] for i in md.findall(util.nspath_eval( - 'mri:spatialResolution/mri:MD_Resolution/mri:distance/gco:Distance', namespaces)): + 'mri:spatialResolution/mri:MD_Resolution/mri:distance/gco:Distance', self.namespaces)): val = util.testXMLValue(i) if val is not None: self.distance.append(val) self.uom.append(i.get("uom")) self.resourcelanguagecode = [] - for i in md.findall(util.nspath_eval('lan:language/lan:LanguageCode', namespaces)): + for i in md.findall(util.nspath_eval('lan:language/lan:LanguageCode', self.namespaces)): val = _testCodeListValue(i) if val is not None: self.resourcelanguagecode.append(val) self.resourcelanguage = [] - for i in md.findall(util.nspath_eval('lan:language/gco:CharacterString', namespaces)): + for i in md.findall(util.nspath_eval('lan:language/gco:CharacterString', self.namespaces)): val = util.testXMLValue(i) if val is not None: self.resourcelanguage.append(val) @@ -603,11 +651,11 @@ def __init__(self, md=None, identtype=None): self.publisher = [] self.contributor = [] self.funder = [] - for val in md.findall(util.nspath_eval('mri:pointOfContact/cit:CI_Responsibility', namespaces)): - role = val.find(util.nspath_eval('cit:role/cit:CI_RoleCode', namespaces)) + for val in md.findall(util.nspath_eval('mri:pointOfContact/cit:CI_Responsibility', self.namespaces)): + role = val.find(util.nspath_eval('cit:role/cit:CI_RoleCode', self.namespaces)) if role is not None: clv = _testCodeListValue(role) - rp = CI_Responsibility(val) + rp = CI_Responsibility(self.namespaces, val) if clv == 'originator': self.creator.append(rp) elif clv == 'publisher': @@ -617,55 +665,55 @@ def __init__(self, md=None, identtype=None): elif clv == 'funder': self.funder.append(rp) - val = md.find(util.nspath_eval('cit:CI_Citation/cit:edition/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('cit:CI_Citation/cit:edition/gco:CharacterString', self.namespaces)) self.edition = util.testXMLValue(val) - val = md.find(util.nspath_eval('mri:abstract/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('mri:abstract/gco:CharacterString', self.namespaces)) self.abstract = util.testXMLValue(val) - val = md.find(util.nspath_eval('mri:abstract/gcx:Anchor', namespaces)) + val = md.find(util.nspath_eval('mri:abstract/gcx:Anchor', self.namespaces)) self.abstract_url = None if val is not None: self.abstract = util.testXMLValue(val) - self.abstract_url = val.attrib.get(util.nspath_eval('xlink:href', namespaces)) + self.abstract_url = val.attrib.get(util.nspath_eval('xlink:href', self.namespaces)) - val = md.find(util.nspath_eval('mri:purpose/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('mri:purpose/gco:CharacterString', self.namespaces)) self.purpose = util.testXMLValue(val) - self.status = _testCodeListValue(md.find(util.nspath_eval('mri:status/mri:MD_ProgressCode', namespaces))) + self.status = _testCodeListValue(md.find(util.nspath_eval('mri:status/mri:MD_ProgressCode', self.namespaces))) self.graphicoverview = [] for val in md.findall(util.nspath_eval( - 'mri:graphicOverview/mcc:MD_BrowseGraphic/mcc:fileName/gco:CharacterString', namespaces)): + 'mri:graphicOverview/mcc:MD_BrowseGraphic/mcc:fileName/gco:CharacterString', self.namespaces)): if val is not None: val2 = util.testXMLValue(val) if val2 is not None: self.graphicoverview.append(val2) self.contact = [] - for i in md.findall(util.nspath_eval('mri:pointOfContact/cit:CI_Responsibility', namespaces)): - o = CI_Responsibility(i) + for i in md.findall(util.nspath_eval('mri:pointOfContact/cit:CI_Responsibility', self.namespaces)): + o = CI_Responsibility(self.namespaces, i) self.contact.append(o) self.spatialrepresentationtype = [] for val in md.findall(util.nspath_eval( - 'mri:spatialRepresentationType/mcc:MD_SpatialRepresentationTypeCode', namespaces)): + 'mri:spatialRepresentationType/mcc:MD_SpatialRepresentationTypeCode', self.namespaces)): val = util.testXMLAttribute(val, 'codeListValue') if val: self.spatialrepresentationtype.append(val) self.keywords = [] - for mdkw in md.findall(util.nspath_eval('mri:descriptiveKeywords/mri:MD_Keywords', namespaces)): - self.keywords.append(MD_Keywords(mdkw)) + for mdkw in md.findall(util.nspath_eval('mri:descriptiveKeywords/mri:MD_Keywords', self.namespaces)): + self.keywords.append(MD_Keywords(self.namespaces, mdkw)) self.topiccategory = [] - for i in md.findall(util.nspath_eval('mri:topicCategory/mri:MD_TopicCategoryCode', namespaces)): + for i in md.findall(util.nspath_eval('mri:topicCategory/mri:MD_TopicCategoryCode', self.namespaces)): val = util.testXMLValue(i) if val is not None: self.topiccategory.append(val) - val = md.find(util.nspath_eval('mri:supplementalInformation/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('mri:supplementalInformation/gco:CharacterString', self.namespaces)) self.supplementalinformation = util.testXMLValue(val) # There may be multiple geographicElement, create an extent @@ -674,70 +722,74 @@ def __init__(self, md=None, identtype=None): val = None val2 = None val3 = None - extents = md.findall(util.nspath_eval('mri:extent', namespaces)) + extents = md.findall(util.nspath_eval('mri:extent', self.namespaces)) for extent in extents: # Parse bounding box and vertical extents if val is None: - for e in extent.findall(util.nspath_eval('gex:EX_Extent/gex:geographicElement', namespaces)): - if e.find(util.nspath_eval('gex:EX_GeographicBoundingBox', namespaces)) is not None or \ - e.find(util.nspath_eval('gex:EX_BoundingPolygon', namespaces)) is not None: + for e in extent.findall(util.nspath_eval('gex:EX_Extent/gex:geographicElement', self.namespaces)): + if e.find(util.nspath_eval('gex:EX_GeographicBoundingBox', self.namespaces)) is not None or \ + e.find(util.nspath_eval('gex:EX_BoundingPolygon', self.namespaces)) is not None: val = e break - vert_elem = extent.find(util.nspath_eval('gex:EX_Extent/gex:verticalElement', namespaces)) - self.extent = EX_Extent(val, vert_elem) + vert_elem = extent.find(util.nspath_eval('gex:EX_Extent/gex:verticalElement', self.namespaces)) + self.extent = EX_Extent(self.namespaces, val, vert_elem) self.bbox = self.extent.boundingBox # for backwards compatibility # Parse temporal extent begin if val2 is None: val2 = extent.find(util.nspath_eval( 'gex:EX_Extent/gex:temporalElement/gex:EX_TemporalExtent/gex:extent/gml:TimePeriod/gml:beginPosition', - namespaces)) + self.namespaces)) self.temporalextent_start = util.testXMLValue(val2) # Parse temporal extent end if val3 is None: val3 = extent.find(util.nspath_eval( 'gex:EX_Extent/gex:temporalElement/gex:EX_TemporalExtent/gex:extent/gml:TimePeriod/gml:endPosition', - namespaces)) + self.namespaces)) self.temporalextent_end = util.testXMLValue(val3) class MD_Distributor(printable): """ Process MD_Distributor """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ Parses MD_Distributor XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: MD_Distributor etree.Element """ + self.namespaces = namespaces if md is None: self.contact = None self.online = [] else: self.contact = None val = md.find(util.nspath_eval( - 'mrd:MD_Distributor/mrd:distributorContact/cit:CI_Responsibility', namespaces)) + 'mrd:MD_Distributor/mrd:distributorContact/cit:CI_Responsibility', self.namespaces)) if val is not None: - self.contact = CI_Responsibility(val) + self.contact = CI_Responsibility(self.namespaces, val) self.online = [] for ol in md.findall(util.nspath_eval( 'mrd:MD_Distributor/mrd:distributorTransferOptions/mrd:MD_DigitalTransferOptions/cit:onLine/cit:CI_OnlineResource', - namespaces)): - self.online.append(CI_OnlineResource(ol)) + self.namespaces)): + self.online.append(CI_OnlineResource(self.namespaces, ol)) class MD_Distribution(printable): """ Process MD_Distribution """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ Parses MD_Distribution XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: MD_Distribution etree.Element """ + self.namespaces = namespaces if md is None: self.format = None self.version = None @@ -745,78 +797,82 @@ def __init__(self, md=None): self.online = [] else: val = md.find(util.nspath_eval( - 'mrd:distributionFormat/mrd:MD_Format/mrd:formatSpecificationCitation/cit:CI_Citation/cit:title/gco:CharacterString', namespaces)) + 'mrd:distributionFormat/mrd:MD_Format/mrd:formatSpecificationCitation/cit:CI_Citation/cit:title/gco:CharacterString', self.namespaces)) self.format = util.testXMLValue(val) val = md.find(util.nspath_eval( - 'mrd:distributionFormat/mrd:MD_Format/mrd:formatSpecificationCitation/cit:CI_Citation/cit:edition/gco:CharacterString', namespaces)) + 'mrd:distributionFormat/mrd:MD_Format/mrd:formatSpecificationCitation/cit:CI_Citation/cit:edition/gco:CharacterString', self.namespaces)) self.version = util.testXMLValue(val) self.distributor = [] - for dist in md.findall(util.nspath_eval('mrd:distributor', namespaces)): - self.distributor.append(MD_Distributor(dist)) + for dist in md.findall(util.nspath_eval('mrd:distributor', self.namespaces)): + self.distributor.append(MD_Distributor(self.namespaces, dist)) self.online = [] for ol in md.findall(util.nspath_eval( 'mrd:transferOptions/mrd:MD_DigitalTransferOptions/mrd:onLine/cit:CI_OnlineResource', - namespaces)): - self.online.append(CI_OnlineResource(ol)) + self.namespaces)): + self.online.append(CI_OnlineResource(self.namespaces, ol)) class DQ_DataQuality(printable): """ Process DQ_DataQuality """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ - Parses DQ_DataQuality XML subtree + Parse a portion of DQ_DataQuality XML subtree only taking the first value found + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: DQ_DataQuality etree.Element """ - if md is None: - self.conformancetitle = [] - self.conformancedate = [] - self.conformancedatetype = [] - self.conformancedegree = [] - self.lineage = None - self.lineage_url = None - self.specificationtitle = None - self.specificationdate = [] - else: - val = md.find(util.nspath_eval( - 'mdq:evaluationProcedure/cit:CI_Citation/cit:title/gco:CharacterString', namespaces)) - self.conformancetitle = util.testXMLValue(val) - - val = md.find(util.nspath_eval( - 'mdq:evaluationProcedure/cit:CI_Citation/cit:date', namespaces)) - self.conformancedate = util.testXMLValue(val) - - val = md.find(util.nspath_eval( - 'mdq:evaluationProcedure/cit:CI_Citation/cit:date/cit:CI_DateTypeCode', namespaces)) - self.conformancedatetype = util.testXMLValue(val) - - val = md.find(util.nspath_eval( - 'mdq:result/mdq:DQ_QuantitativeResult/mdq:value/gco:Record', namespaces)) - self.conformancedegree = util.testXMLValue(val) - - val = md.find(util.nspath_eval( - 'mdb:resourceLineage/mrl:LI_Lineage/mrl:statement/gco:CharacterString', namespaces)) - self.lineage = util.testXMLValue(val) - - val = md.find(util.nspath_eval('mdb:resourceLineage/mrl:LI_Lineage/mrl:statement/gcx:Anchor', namespaces)) - if val is not None: - self.lineage = util.testXMLValue(val) - self.lineage_url = val.attrib.get(util.nspath_eval('xlink:href', namespaces)) + self.namespaces = namespaces + self.conformancetitle = [] + self.conformancedate = [] + self.conformancedatetype = [] + self.conformancedegree = [] + self.lineage = None + self.lineage_url = None + self.specificationtitle = None + self.specificationdate = [] + if md is not None: + for conftitle in md.xpath( + 'mdq:report/*/mdq:evaluationMethod/mdq:DQ_EvaluationMethod/mdq:evaluationProcedure/cit:CI_Citation/cit:title/gco:CharacterString', + namespaces=self.namespaces): + self.conformancetitle.append(util.testXMLValue(conftitle)) + + for confdate in md.xpath( + 'mdq:report/*/mdq:evaluationMethod/mdq:DQ_EvaluationMethod/mdq:evaluationProcedure/cit:CI_Citation/cit:date/cit:CI_Date/cit:date/gco:DateTime', + namespaces=self.namespaces): + self.conformancedate.append(util.testXMLValue(confdate)) + + for confdatetype in md.xpath( + 'mdq:report/*/mdq:evaluationMethod/mdq:DQ_EvaluationMethod/mdq:evaluationProcedure/cit:CI_Citation/cit:date/cit:CI_Date/cit:dateType/cit:CI_DateTypeCode', + namespaces=self.namespaces): + print(f"{confdatetype=}") + self.conformancedatetype.append(util.testXMLValue(confdatetype)) + + for confdegree in md.xpath( + 'mdq:report/*/mdq:result/mdq:DQ_QuantitativeResult/mdq:value/gco:Record', + namespaces=self.namespaces): + self.conformancedegree.append(util.testXMLValue(confdegree)) + + lins = md.xpath( + 'mdq:lineage/mrl:LI_Lineage/mrl:statement/*[self::gco:CharacterString or self::gcx:Anchor]', + namespaces=self.namespaces) + if len(lins) > 0: + self.lineage = util.testXMLValue(lins[0]) + self.lineage_url = lins[0].attrib.get(util.nspath_eval('xlink:href', self.namespaces)) val = md.find(util.nspath_eval( 'mdq:report/mdq:DQ_DomainConsistency/mdq:result/mdq:DQ_ConformanceResult/mdq:specification/cit:CI_Citation/cit:title/gco:CharacterString', - namespaces)) + self.namespaces)) self.specificationtitle = util.testXMLValue(val) self.specificationdate = [] for i in md.findall(util.nspath_eval( - 'mdq:report/mdq:DQ_DomainConsistency/mdq:result/mdq:DQ_ConformanceResult/mdq:specification/cit:CI_Citation/cit:date/cit:CI_Date', - namespaces)): + 'mdq:report/mdq:DQ_DomainConsistency/mdq:result/mdq:DQ_ConformanceResult/mdq:specification/cit:CI_Citation/cit:date/cit:CI_Date/cit:date/gco:DateTime', + self.namespaces)): val = util.testXMLValue(i) if val is not None: self.specificationdate.append(val) @@ -825,13 +881,15 @@ def __init__(self, md=None): class SV_ServiceIdentification(MD_DataIdentification, printable): """ Process SV_ServiceIdentification """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ Parses SV_ServiceIdentification XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: SV_ServiceIdentification etree.Element """ - super().__init__(md, 'service') + super().__init__(namespaces, md, 'service') + self.namespaces = namespaces if md is None: self.type = None @@ -841,58 +899,60 @@ def __init__(self, md=None): self.operations = [] self.operateson = [] else: - val = md.xpath('srv:serviceType/*[self::gco:LocalName or self::gco:ScopedName]', namespaces=namespaces) + val = md.xpath('srv:serviceType/*[self::gco:LocalName or self::gco:ScopedName]', namespaces=self.namespaces) if len(val) > 0: self.type = util.testXMLValue(val[0]) - val = md.find(util.nspath_eval('srv:serviceTypeVersion/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('srv:serviceTypeVersion/gco:CharacterString', self.namespaces)) self.version = util.testXMLValue(val) val = md.find(util.nspath_eval( - 'srv:accessProperties/mrd:MD_StandardOrderProcess/mrd:fees/gco:CharacterString', namespaces)) + 'srv:accessProperties/mrd:MD_StandardOrderProcess/mrd:fees/gco:CharacterString', self.namespaces)) self.fees = util.testXMLValue(val) self.couplingtype = _testCodeListValue(md.find(util.nspath_eval( - 'srv:couplingType/srv:SV_CouplingType', namespaces))) + 'srv:couplingType/srv:SV_CouplingType', self.namespaces))) self.operations = [] - for i in md.findall(util.nspath_eval('srv:containsOperations', namespaces)): + for i in md.findall(util.nspath_eval('srv:containsOperations', self.namespaces)): tmp = {} val = i.find(util.nspath_eval( - 'srv:SV_OperationMetadata/srv:operationName/gco:CharacterString', namespaces)) + 'srv:SV_OperationMetadata/srv:operationName/gco:CharacterString', self.namespaces)) tmp['name'] = util.testXMLValue(val) tmp['dcplist'] = [] - for d in i.findall(util.nspath_eval('srv:SV_OperationMetadata/srv:DCP', namespaces)): - tmp2 = _testCodeListValue(d.find(util.nspath_eval('srv:DCPList', namespaces))) + for d in i.findall(util.nspath_eval('srv:SV_OperationMetadata/srv:DCP', self.namespaces)): + tmp2 = _testCodeListValue(d.find(util.nspath_eval('srv:DCPList', self.namespaces))) tmp['dcplist'].append(tmp2) tmp['connectpoint'] = [] - for d in i.findall(util.nspath_eval('srv:SV_OperationMetadata/srv:connectPoint', namespaces)): - tmp3 = d.find(util.nspath_eval('cit:CI_OnlineResource', namespaces)) - tmp['connectpoint'].append(CI_OnlineResource(tmp3)) + for d in i.findall(util.nspath_eval('srv:SV_OperationMetadata/srv:connectPoint', self.namespaces)): + tmp3 = d.find(util.nspath_eval('cit:CI_OnlineResource', self.namespaces)) + tmp['connectpoint'].append(CI_OnlineResource(self.namespaces, tmp3)) self.operations.append(tmp) self.operateson = [] - for i in md.findall(util.nspath_eval('srv:operatesOn', namespaces)): + for i in md.findall(util.nspath_eval('srv:operatesOn', self.namespaces)): tmp = {} tmp['uuidref'] = i.attrib.get('uuidref') - tmp['href'] = i.attrib.get(util.nspath_eval('xlink:href', namespaces)) - tmp['title'] = i.attrib.get(util.nspath_eval('xlink:title', namespaces)) + tmp['href'] = i.attrib.get(util.nspath_eval('xlink:href', self.namespaces)) + tmp['title'] = i.attrib.get(util.nspath_eval('xlink:title', self.namespaces)) self.operateson.append(tmp) class CI_OnlineResource(printable): """ Process CI_OnlineResource """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ Parses CI_OnlineResource XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: CI_OnlineResource etree.Element """ + self.namespaces = namespaces if md is None: self.url = None self.protocol = None @@ -900,68 +960,72 @@ def __init__(self, md=None): self.description = None self.function = None else: - val = md.find(util.nspath_eval('cit:linkage/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('cit:linkage/gco:CharacterString', self.namespaces)) self.url = util.testXMLValue(val) - val = md.find(util.nspath_eval('cit:protocol/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('cit:protocol/gco:CharacterString', self.namespaces)) self.protocol = util.testXMLValue(val) - val = md.find(util.nspath_eval('cit:name/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('cit:name/gco:CharacterString', self.namespaces)) self.name = util.testXMLValue(val) - val = md.find(util.nspath_eval('cit:description/gco:CharacterString', namespaces)) + val = md.find(util.nspath_eval('cit:description/gco:CharacterString', self.namespaces)) self.description = util.testXMLValue(val) self.function = _testCodeListValue(md.find(util.nspath_eval( - 'cit:function/cit:CI_OnLineFunctionCode', namespaces))) + 'cit:function/cit:CI_OnLineFunctionCode', self.namespaces))) class EX_GeographicBoundingBox(printable): """ Process gex:EX_GeographicBoundingBox """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ Parses EX_GeographicBoundingBox XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: EX_GeographicBoundingBox etree.Element """ + self.namespaces = namespaces if md is None: self.minx = None self.maxx = None self.miny = None self.maxy = None else: - val = md.find(util.nspath_eval('gex:westBoundLongitude/gco:Decimal', namespaces)) + val = md.find(util.nspath_eval('gex:westBoundLongitude/gco:Decimal', self.namespaces)) self.minx = util.testXMLValue(val) - val = md.find(util.nspath_eval('gex:eastBoundLongitude/gco:Decimal', namespaces)) + val = md.find(util.nspath_eval('gex:eastBoundLongitude/gco:Decimal', self.namespaces)) self.maxx = util.testXMLValue(val) - val = md.find(util.nspath_eval('gex:southBoundLatitude/gco:Decimal', namespaces)) + val = md.find(util.nspath_eval('gex:southBoundLatitude/gco:Decimal', self.namespaces)) self.miny = util.testXMLValue(val) - val = md.find(util.nspath_eval('gex:northBoundLatitude/gco:Decimal', namespaces)) + val = md.find(util.nspath_eval('gex:northBoundLatitude/gco:Decimal', self.namespaces)) self.maxy = util.testXMLValue(val) class EX_Polygon(printable): """ Process gml32:Polygon """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ Parses EX_Polygon XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: EX_Polygon etree.Element """ + self.namespaces = namespaces if md is None: self.exterior_ring = None self.interior_rings = [] else: - linear_ring = md.find(util.nspath_eval('gml32:Polygon/gml32:exterior/gml32:LinearRing', namespaces)) + linear_ring = md.find(util.nspath_eval('gml32:Polygon/gml32:exterior/gml32:LinearRing', self.namespaces)) if linear_ring is not None: self.exterior_ring = self._coordinates_for_ring(linear_ring) - interior_ring_elements = md.findall(util.nspath_eval('gml32:Polygon/gml32:interior', namespaces)) + interior_ring_elements = md.findall(util.nspath_eval('gml32:Polygon/gml32:interior', self.namespaces)) self.interior_rings = [] for iring_element in interior_ring_elements: - linear_ring = iring_element.find(util.nspath_eval('gml32:LinearRing', namespaces)) + linear_ring = iring_element.find(util.nspath_eval('gml32:LinearRing', self.namespaces)) self.interior_rings.append(self._coordinates_for_ring(linear_ring)) def _coordinates_for_ring(self, linear_ring): @@ -971,7 +1035,7 @@ def _coordinates_for_ring(self, linear_ring): :returns: coordinate list of float tuples """ coordinates = [] - positions = linear_ring.findall(util.nspath_eval('gml32:pos', namespaces)) + positions = linear_ring.findall(util.nspath_eval('gml32:pos', self.namespaces)) for pos in positions: tokens = pos.text.split() coords = tuple([float(t) for t in tokens]) @@ -982,36 +1046,40 @@ def _coordinates_for_ring(self, linear_ring): class EX_BoundingPolygon(printable): """ Process EX_BoundingPolygon """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ Parses EX_BoundingPolygon XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: EX_BoundingPolygon etree.Element """ + self.namespaces = namespaces if md is None: self.is_extent = None self.polygons = [] else: - val = md.find(util.nspath_eval('gex:extentTypeCode', namespaces)) + val = md.find(util.nspath_eval('gex:extentTypeCode', self.namespaces)) self.is_extent = util.testXMLValue(val) - md_polygons = md.findall(util.nspath_eval('gex:polygon', namespaces)) + md_polygons = md.findall(util.nspath_eval('gex:polygon', self.namespaces)) self.polygons = [] for val in md_polygons: - self.polygons.append(EX_Polygon(val)) + self.polygons.append(EX_Polygon(self.namespaces, val)) class EX_Extent(printable): """ Process EX_Extent """ - def __init__(self, md=None, vert_elem=None): + def __init__(self, namespaces, md=None, vert_elem=None): """ Parses EX_Extent XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: EX_Extent etree.Element :param vert_elem: vertical extent 'gex:verticalElement' etree.Element """ + self.namespaces = namespaces self.boundingBox = None self.boundingPolygon = None self.description_code = None @@ -1019,17 +1087,17 @@ def __init__(self, md=None, vert_elem=None): self.vertExtMax = None if md is not None: # Parse bounding box - bboxElement = md.find(util.nspath_eval('gex:EX_GeographicBoundingBox', namespaces)) + bboxElement = md.find(util.nspath_eval('gex:EX_GeographicBoundingBox', self.namespaces)) if bboxElement is not None: - self.boundingBox = EX_GeographicBoundingBox(bboxElement) + self.boundingBox = EX_GeographicBoundingBox(self.namespaces, bboxElement) - polygonElement = md.find(util.nspath_eval('gex:EX_BoundingPolygon', namespaces)) + polygonElement = md.find(util.nspath_eval('gex:EX_BoundingPolygon', self.namespaces)) if polygonElement is not None: - self.boundingPolygon = EX_BoundingPolygon(polygonElement) + self.boundingPolygon = EX_BoundingPolygon(self.namespaces, polygonElement) code = md.find(util.nspath_eval( 'gex:EX_GeographicDescription/gex:geographicIdentifier/mcc:MD_Identifier/mcc:code/gco:CharacterString', - namespaces)) + self.namespaces)) self.description_code = util.testXMLValue(code) # Parse vertical extent @@ -1037,51 +1105,53 @@ def __init__(self, md=None, vert_elem=None): # Get vertical extent max vertext_max = vert_elem.find(util.nspath_eval( 'gex:EX_VerticalExtent/gex:maximumValue/gco:Real', - namespaces)) + self.namespaces)) self.vertExtMax = util.testXMLValue(vertext_max) # Get vertical extent min vertext_min = vert_elem.find(util.nspath_eval( 'gex:EX_VerticalExtent/gex:minimumValue/gco:Real', - namespaces)) + self.namespaces)) self.vertExtMin = util.testXMLValue(vertext_min) class MD_ReferenceSystem(printable): """ Process MD_ReferenceSystem """ - def __init__(self, md=None): + def __init__(self, namespaces, md=None): """ Parses MD_ReferenceSystem XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param md: MD_ReferenceSystem etree.Element """ + self.namespaces = namespaces if md is None: self.code = None self.codeSpace = None self.version = None else: val = md.find(util.nspath_eval( - 'mrs:referenceSystemIdentifier/mcc:MD_Identifier/mcc:code/gco:CharacterString', namespaces)) + 'mrs:referenceSystemIdentifier/mcc:MD_Identifier/mcc:code/gco:CharacterString', self.namespaces)) if val is not None: self.code = util.testXMLValue(val) else: val = md.find(util.nspath_eval( - 'mrs:referenceSystemIdentifier/mcc:MD_Identifier/mcc:code/gcx:Anchor', namespaces)) + 'mrs:referenceSystemIdentifier/mcc:MD_Identifier/mcc:code/gcx:Anchor', self.namespaces)) if val is not None: self.code = util.testXMLValue(val) else: self.code = None val = md.find(util.nspath_eval( - 'mrs:referenceSystemIdentifier/mcc:MD_Identifier/mcc:codeSpace/gco:CharacterString', namespaces)) + 'mrs:referenceSystemIdentifier/mcc:MD_Identifier/mcc:codeSpace/gco:CharacterString', self.namespaces)) if val is not None: self.codeSpace = util.testXMLValue(val) else: self.codeSpace = None val = md.find(util.nspath_eval( - 'mrs:referenceSystemIdentifier/mcc:MD_Identifier/mcc:version/gco:CharacterString', namespaces)) + 'mrs:referenceSystemIdentifier/mcc:MD_Identifier/mcc:version/gco:CharacterString', self.namespaces)) if val is not None: self.version = util.testXMLValue(val) else: @@ -1089,7 +1159,7 @@ def __init__(self, md=None): def _testCodeListValue(elpath): - """ Get gco:CodeListValue_Type attribute, else get text content + """ Get codeListValue attribute, else get text content :param elpath: Element path :returns: 'codeListValue' attribute of Element or text value or None if elpath is None @@ -1107,12 +1177,16 @@ def _testCodeListValue(elpath): class MD_FeatureCatalogueDescription(printable): """Process mrc:MD_FeatureCatalogueDescription """ - def __init__(self, fcd=None): + def __init__(self, namespaces, fcd=None): """ Parses MD_FeatureCatalogueDescription XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param fcd: MD_FeatureCatalogueDescription etree.Element """ + self.namespaces = namespaces + self.featuretypenames = [] + self.featurecatalogues = [] if fcd is None: self.xml = None self.compliancecode = None @@ -1128,33 +1202,34 @@ def __init__(self, fcd=None): self.xml = etree.tostring(fcd) self.compliancecode = None - comp = fcd.find(util.nspath_eval('mrc:complianceCode/gco:Boolean', namespaces)) + comp = fcd.find(util.nspath_eval('mrc:complianceCode/gco:Boolean', self.namespaces)) val = util.testXMLValue(comp) if val is not None: self.compliancecode = util.getTypedValue('boolean', val) self.language = [] - for i in fcd.findall(util.nspath_eval('lan:language/gco:CharacterString', namespaces)): - val = util.testXMLValue(i) + for i in fcd.findall(util.nspath_eval('mrc:locale/lan:PT_Locale/lan:language/lan:LanguageCode', self.namespaces)): + val = _testCodeListValue(i) if val is not None: self.language.append(val) self.includedwithdataset = None - comp = fcd.find(util.nspath_eval('mrc:includedWithDataset/gco:Boolean', namespaces)) + comp = fcd.find(util.nspath_eval('mrc:includedWithDataset/gco:Boolean', self.namespaces)) val = util.testXMLValue(comp) if val is not None: self.includedwithdataset = util.getTypedValue('boolean', val) self.featuretypenames = [] - for i in fcd.findall(util.nspath_eval('mrc:featureTypes/mrc:MD_FeatureTypeInfo/mrc:featureTypeName/gml:CodeType', namespaces)): - val = util.testXMLValue(i) - if val is not None: + for name in fcd.xpath('mrc:featureTypes/mrc:MD_FeatureTypeInfo/mrc:featureTypeName/*[self::gco:LocalName or self::gco:ScopedName]', + namespaces=self.namespaces): + val = util.testXMLValue(name) + if ValueError is not None: self.featuretypenames.append(val) # Gather feature catalogue titles self.featurecatalogues = [] for cit in fcd.findall(util.nspath_eval( - 'mrc:featureCatalogueCitation/cit:CI_Citation/cit:title/gco:CharacterString', namespaces)): + 'mrc:featureCatalogueCitation/cit:CI_Citation/cit:title/gco:CharacterString', self.namespaces)): val = util.testXMLValue(cit) if val is not None: self.featurecatalogues.append(val) @@ -1162,61 +1237,64 @@ def __init__(self, fcd=None): class MD_ImageDescription(printable): """Process mrc:MD_ImageDescription """ - def __init__(self, img_desc=None): + def __init__(self, namespaces, img_desc=None): """ Parses MD_ImageDescription XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param img_desc: MD_ImageDescription etree.Element """ + self.namespaces = namespaces self.type = 'image' self.bands = [] if img_desc is None: - self.attribute_description = None - self.cloud_cover = None - self.processing_level = None + self.attributedescription = None + self.cloudcover = None + self.processinglevel = None else: - val = img_desc.find(util.nspath_eval('mrc:attributeDescription/gco:RecordType', namespaces)) - self.attribute_description = util.testXMLValue(val) + attdesc = img_desc.find(util.nspath_eval('mrc:attributeDescription/gco:RecordType', self.namespaces)) + self.attributedescription = util.testXMLValue(attdesc) - val = img_desc.find(util.nspath_eval('mrc:attributeGroup/mrc:MD_CoverageContentTypeCode', namespaces)) - self.type = util.testXMLAttribute(val, 'codeListValue') + ctype = img_desc.find(util.nspath_eval('mrc:attributeGroup/mrc:MD_AttributeGroup/mrc:contentType/mrc:MD_CoverageContentTypeCode', self.namespaces)) + self.type = util.testXMLAttribute(ctype, 'codeListValue') - val = img_desc.find(util.nspath_eval('mrc:cloudCoverPercentage/gco:Real', namespaces)) - self.cloud_cover = util.testXMLValue(val) + cloudcov = img_desc.find(util.nspath_eval('mrc:cloudCoverPercentage/gco:Real', self.namespaces)) + self.cloudcover = util.testXMLValue(cloudcov) - val = img_desc.find(util.nspath_eval( - 'mrc:processingLevelCode/mcc:MD_Identifier/mcc:code/gco:CharacterString', namespaces)) - self.processing_level = util.testXMLValue(val) + proclvl = img_desc.find(util.nspath_eval( + 'mrc:processingLevelCode/mcc:MD_Identifier/mcc:code/gco:CharacterString', self.namespaces)) + self.processinglevel = util.testXMLValue(proclvl) - for i in img_desc.findall(util.nspath_eval('mrc:attributeGroup/mrc:MD_Band', namespaces)): - bid = util.testXMLAttribute(i, 'id') - self.bands.append(MD_Band(i, bid)) + for i in img_desc.findall(util.nspath_eval('mrc:attributeGroup/mrc:MD_AttributeGroup/mrc:attribute/mrc:MD_Band', self.namespaces)): + self.bands.append(MD_Band(self.namespaces, i)) class MD_Band(printable): """Process mrc:MD_Band """ - def __init__(self, band, band_id=None): + def __init__(self, namespaces, band): """ Parses MD_Band XML subtree + :param namespaces: dict of XML namespaces, key is namespace, val is path :param band: MD_Band etree.Element - :param band_id: optional id attribute """ + self.namespaces = namespaces if band is None: self.id = None self.units = None self.min = None self.max = None else: - self.id = band_id + seq_id = band.find(util.nspath_eval('mrc:sequenceIdentifier/gco:MemberName/gco:aName/gco:CharacterString', self.namespaces)) + self.id = util.testXMLValue(seq_id) - val = band.find(util.nspath_eval('mrc:units/gml:UnitDefinition/gml:identifier', namespaces)) - self.units = util.testXMLValue(val) + units = band.find(util.nspath_eval('mrc:units/gml:UnitDefinition/gml:identifier', self.namespaces)) + self.units = util.testXMLValue(units) - val = band.find(util.nspath_eval('mrc:minValue/gco:Real', namespaces)) - self.min = util.testXMLValue(val) + bmin = band.find(util.nspath_eval('mrc:minValue/gco:Real', self.namespaces)) + self.min = util.testXMLValue(bmin) - val = band.find(util.nspath_eval('mrc:maxValue/gco:Real', namespaces)) - self.max = util.testXMLValue(val) + bmax = band.find(util.nspath_eval('mrc:maxValue/gco:Real', self.namespaces)) + self.max = util.testXMLValue(bmax) diff --git a/tests/resources/iso3_examples/README.txt b/tests/resources/iso3_examples/README.txt index 6071f2dd..7e7dec36 100644 --- a/tests/resources/iso3_examples/README.txt +++ b/tests/resources/iso3_examples/README.txt @@ -7,3 +7,4 @@ Acknowledged sources: https://portal.auscope.org.au/geonetwork: auscope-3d-model.xml https://metawal.wallonie.be/geonetwork/: metawal.wallonie.be-catchments.xml, metawal.wallonie.be-srv.xml +https://github.com/Esri/arcgis-pro-metadata-toolkit: arcgis-sample.xml \ No newline at end of file diff --git a/tests/resources/iso3_examples/arcgis-sample.xml b/tests/resources/iso3_examples/arcgis-sample.xml new file mode 100644 index 00000000..399cf17c --- /dev/null +++ b/tests/resources/iso3_examples/arcgis-sample.xml @@ -0,0 +1,3762 @@ + + + + + + Metadata > Details > File Identifier + + + + + + + eng + + + US + + + utf8 + + + + + + + + service + + + Metadata > Details > Hierarchy Level Name + + + + + + + pointOfContact + + + + + Metadata > Contacts > Contact > Organization + + + + + + + Metadata > Contacts > Contact > Contact Information > Phone + + + voice + + + + + + + Metadata > Contacts > Contact > Contact Information > Phone + TDD/TTY + + + voice + + + + + + + Metadata > Contacts > Contact > Contact Information > Fax + + + facsimile + + + + + + + Metadata > Contacts > Contact > Contact Information > Address + + + Metadata > Contacts > Contact > Contact Information > City + + + Metadata > Contacts > Contact > Contact Information > State + + + Metadata > Contacts > Contact > Contact Information > Postal Code + + + US + + + Metadata > Contacts > Contact > Contact Information > Email + + + + + + + http://Metadata_Contacts_Contact_Contact_Information_Online_Resource_Linkage + + + Metadata > Contacts > Contact > Contact Information > Online Resource > Protocol + + + Metadata > Contacts > Contact > Contact Information > Online Resource > Profile + + + Metadata > Contacts > Contact > Contact Information > Online Resource > Name + + + Metadata > Contacts > Contact > Contact Information > Online Resource > Description + + + information + + + + + Metadata > Contacts > Contact > Contact Information > Hours + + + Metadata > Contacts > Contact > Contact Information > Instructions + + + + + + + Metadata > Contacts > Contact > Name + + + Metadata > Contacts > Contact > Position + + + + + + + + + + + 2021-12-07T00:00:00 + + + revision + + + + + + + ISO 19115-3 Geographic Information - Metadata - Part 1: Fundamentals + + + 2014 + + + + + + + fre + + + FR + + + utf8 + + + + + + + fullSurfaceGraph + + + + + complex + + + 27249 + + + + + + + + + 1 + + + + + track + + + 35 + + + 1.0 + + + + + point + + + true + + + + + + + 3 + + + + + row + + + 150 + + + 5.0 + + + + + + + column + + + 259 + + + 10.0 + + + + + + + vertical + + + 20 + + + 0.1 + + + + + area + + + true + + + true + + + Resource > Spatial Data Representation > Georectified > Check Point Description + + + + Resource > Spatial Data Representation > Georectified > Corner Point > Description + + Resource > Spatial Data Representation > Georectified > Corner Point > Identifier + 1.0 1.0 + + + + + Resource > Spatial Data Representation > Georectified > Corner Point > Description + + Resource > Spatial Data Representation > Georectified > Corner Point > Identifier + 7.0 7.0 + + + + + Resource > Spatial Data Representation > Georectified > Center Point > Description + + Resource > Spatial Data Representation > Georectified > Center Point > Identifier + 4.0 4.0 + + + + upperLeft + + + Resource > Spatial Data Representation > Georectified > Transformation Dimension Description + + + Resource > Spatial Data Representation > Georectified > Transformation Dimension Mapping + + + + + + + 1 + + + + + time + + + 140 + + + 1 + + + + + point + + + true + + + true + + + true + + + Resource > Spatial Data Representation > Georeferenceable > Orientation Parameter Description + + + Resource > Spatial Data Representation > Georeferenceable > Georeferenced Parameters + + + + + Resource > Spatial Data Representation > Georeferenceable > Parameter Citation > Titles > Title + + + + + 2011-01-01T00:00:00 + + + revision + + + + + + + publisher + + + + + Resource > Spatial Data Representation > Georeferenceable > Parameter Citation > Contact > Organization + + + + + + + Resource > Spatial Data Representation > Georeferenceable > Parameter Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Resource_Spatial_Data_Representation_Georeferenceable_Parameter_Citation_Online_Resource_Linkage + + + Resource > Spatial Data Representation > Georeferenceable > Parameter Citation > Online Resource > Protocol + + + + + + + + + + + + + + + Resource > Spatial Reference > Reference System > Authority Citation > Titles > Title + + + + + 2011-07-01T00:00:00 + + + publication + + + + + + + publisher + + + + + Resource > Spatial Reference > Reference System > Authority Citation > Contact > Organization + + + + + + + Resource > Spatial Reference > Reference System > Authority Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Resource_Spatial_Reference_Reference_System_Authority_Citation_Online_Resource_Linkage + + + Resource > Spatial Reference > Reference System > Authority Citation > Online Resource > Protocol + + + + + + + Resource > Spatial Reference > Reference System > Code + + + Resource > Spatial Reference > Reference System > Code Space + + + Resource > Spatial Reference > Reference System > Version + + + + + + + + + + + Overview > Item Description > Title + Overview > Citation > Titles > Title + + + Overview > Citation > Titles > Alternate Title + + + + + 1994-01-01T06:00:00 + + + publication + + + + + + + 1993-01-01T23:59:59 + + + creation + + + + + + + 1995-01-01T18:30:59 + + + revision + + + + + + + 2011-09-01T00:00:00 + + + notAvailable + + + + + + + 2011-09-03T00:00:00 + + + adopted + + + + + + + 2011-09-02T00:00:00 + + + inforceDate + + + + + + + 2011-09-04T00:00:00 + + + deprecated + + + + + + + 2011-09-05T00:00:00 + + + superseded + + + + + Overview > Citation > Edition > Edition + + + 2011-01-01T00:00:00 + + + + + + + Overview > Citation > Identifier > Authority Citation > Titles > Title + + + + + 2010-01-01T00:00:00 + + + publication + + + + + + + publisher + + + + + Overview > Citation > Identifier > Authority Citation > Contact > Organization + + + + + + + Overview > Citation > Identifier > Authority Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Overview_Citation_Identifier_Authority_Citation_Online_Resource_Linkage + + + Overview > Citation > Identifier > Authority Citation > Online Resource > Protocol + + + + + + + Overview > Citation > Identifier > Code + + + + + + + Metadata > Details > Dataset URI (NAP Function Code=Web Map Service) + + + + + + + originator + + + + + Overview > Citation Contacts > Contact > Organization (Role=Originator) + + + + + + + Overview > Citation Contacts > Contact > Contact Information > Phone (Role=Originator) + + + voice + + + + + + + Overview > Citation Contacts > Contact > Contact Information > Address (Role=Originator) + + + Overview > Citation Contacts > Contact > Contact Information > City (Role=Originator) + + + Overview > Citation Contacts > Contact > Contact Information > State (Role=Originator) + + + Overview > Citation Contacts > Contact > Contact Information > Postal Code (Role=Originator) + + + US + + + Overview > Citation Contacts > Contact > Contact Information > Email (Role=Originator) + + + + + + + + + Overview > Citation Contacts > Contact > Name (Role=Originator) + + + Overview > Citation Contacts > Contact > Position (Role=Originator) + + + + + + + + + + + publisher + + + + + Overview > Citation Contacts > Contact > Organization (Role=Publisher) + + + + + + + Overview > Citation Contacts > Contact > Contact Information > Address (Role=Publisher) + + + Overview > Citation Contacts > Contact > Contact Information > City (Role=Publisher) + + + Overview > Citation Contacts > Contact > Contact Information > State (Role=Publisher) + + + Overview > Citation Contacts > Contact > Contact Information > Postal Code (Role=Publisher) + + + US + + + Overview > Citation Contacts > Contact > Contact Information > Email (Role=Publisher) + + + + + + + + + Overview > Citation Contacts > Contact > Name (Role=Publisher) + + + Overview > Citation Contacts > Contact > Position (Role=Publisher) + + + + + + + + + + + rightsHolder + + + + + Overview > Citation Contacts > Contact > Organization (NAP Role=Rights Holder) + + + + + + + Overview > Citation Contacts > Contact > Contact Information > Phone (NAP Role=Rights Holder) + + + voice + + + + + + + Overview > Citation Contacts > Contact > Contact Information > Address (NAP Role=Rights Holder) + + + Overview > Citation Contacts > Contact > Contact Information > City (NAP Role=Rights Holder) + + + Overview > Citation Contacts > Contact > Contact Information > State (NAP Role=Rights Holder) + + + Overview > Citation Contacts > Contact > Contact Information > Postal Code (NAP Role=Rights Holder) + + + US + + + Overview > Citation Contacts > Contact > Contact Information > Email (NAP Role=Rights Holder) + + + + + + + + + Overview > Citation Contacts > Contact > Name (NAP Role=Rights Holder) + + + Overview > Citation Contacts > Contact > Position (NAP Role=Rights Holder) + + + + + + + + + mapDigital + + + + + Overview > Citation > Series > Name + + + Overview > Citation > Series > Issue + + + Overview > Citation > Series > Page + + + + + Overview > Citation > Other Details + + + Overview > Citation > ISBN + + + Overview > Citation > ISSN + + + + + http://Resource_Distribution_Digital_Transfer_Options_Online_Resource_Linkage + + + Resource > Distribution > Digital Transfer Options > Online Resource > Protocol + + + + + + + Overview > Item Description > Description/Abstract + + + Overview > Item Description > Summary/Purpose + + + Overview > Item Description > Credits + Resource > Details > Credit + + + onGoing + + + + + pointOfContact + + + + + Resource > Points of Contact > Contact > Name + + + + + + + Resource > Points of Contact > Contact > Contact Information > Phone + + + voice + + + + + + + Resource > Points of Contact > Contact > Contact Information > Address + + + Resource > Points of Contact > Contact > Contact Information > City + + + Resource > Points of Contact > Contact > Contact Information > State + + + Resource > Points of Contact > Contact > Contact Information > Zip + + + US + + + Resource > Points of Contact > Contact > Contact Information > Email + + + + + + + http://Resource_Points_of_Contact_Contact_Contact_Information_Online_Resource_Linkage + + + Resource > Points of Contact > Contact > Contact Information > Online Resource > Protocol + + + + + + + + + + + vector + + + grid + + + + + + + 24000 + + + + + + + + + 0.001 + + + + + geoscientificInformation + + + imageryBaseMapsEarthCover + + + + + Resource > Extents > Extent > Description Bounding boxes created on the Extents page do not have the esriExtentType attribute. + + + + + true + + + -155 + + + -135 + + + 35 + + + 42 + + + + + + + true + + + + + + + Resource > Extents > Extent > Geographic Description > Authority Citation > Titles > Title + + + + + 2011-01-01T00:00:00 + + + publication + + + + + + + publisher + + + + + Resource > Extents > Extent > Geographic Description > Authority Citation > Contact > Organization + + + + + + + Resource > Extents > Extent > Geographic Description > Authority Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Resource_Extents_Extent_Geographic_Description_Authority_Citation_Online_Resource_Linkage + + + Resource > Extents > Extent > Geographic Description > Authority Citation > Online Resource > Protocol + + + + + + + Resource > Extents > Extent > Geographic Description > Code + + + + + + + + + + 2011-01-01T00:00:01 + 2011-12-31T23:59:59 + + + + + + + + 0.01 + + + 599.99 + + + + + + + + + + A bounding box created on the Item Description page has the esriExtentType attribute set with the value "search". The Item Description page only supports editing a bounding box with that attribute. The bounding box added by the synchronization process has the esriExtentType attribute set with the value "search". This bounding box is preferred when exporting to a standard that only allows one bounding box, such as the FGDC CSDGM. + + + + + true + + + -180 + + + 180 + + + -90 + + + 90 + + + + + + + + + asNeeded + + + + + 2012-01-01T00:00:00 + + + revision + + + + + P36DT12H + + + + + service + + + + + Resource > Maintenance > Scope Description > Dataset + + + + + + + Resource > Maintenance > Maintenance Note + + + + + custodian + + + + + Resource > Maintenance > Contact > Name + + + + + + + Resource > Maintenance > Contact > Contact Information > Email + + + + + + + + + + + + + + + Resource > Details > Browse Graphic > File Name + + + Resource > Details > Browse Graphic > Description + + + Resource > Details > Browse Graphic > File Type + + + + + + + + + Resource > Details > Distribution Format > Format Name + + + Resource > Details > Distribution Format > Specification + + + Resource > Details > Distribution Format > Format Version + + + + + Resource > Details > Distribution Format > Amendment Number + + + Resource > Details > Distribution Format > Decompression Technique + + + + + + + Overview > Topics & Keywords > Place Keywords > Keyword1 + + + Overview > Topics & Keywords > Place Keywords > Keyword2 + + + place + + + + + Overview > Topics & Keywords > Place Keywords > Thesaurus Citation > Titles > Title + + + + + 2010-01-01T00:00:00 + + + publication + + + + + + + publisher + + + + + Overview > Topics & Keywords > Place Keywords > Thesaurus Citation > Contact > Organization + + + + + + + Overview > Topics & Keywords > Place Keywords > Thesaurus Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Overview_Topics_Keywords_Place_Keywords_Thesaurus_Citation_Online_Resource_Linkage + + + Overview > Topics & Keywords > Place Keywords > Thesaurus Citation > Online Resource > Protocol + + + + + + + + + + + Overview > Topics & Keywords > Stratum Keywords > Keyword1 + + + Overview > Topics & Keywords > Stratum Keywords > Keyword2 + + + stratum + + + + + Overview > Topics & Keywords > Stratum Keywords > Thesaurus Citation > Titles > Title + + + + + 1988-08-26T00:00:00 + + + revision + + + + + + + publisher + + + + + Overview > Topics & Keywords > Stratum Keywords > Thesaurus Citation > Contact > Organization + + + + + + + Overview > Topics & Keywords > Stratum Keywords > Thesaurus Citation > Contact > Contact Information > Email + + + + + + + + + + + + + + + + + Overview > Topics & Keywords > Temporal Keywords > Keyword1 + + + Overview > Topics & Keywords > Temporal Keywords > Keyword2 + + + temporal + + + + + Overview > Topics & Keywords > Temporal Keywords > Thesaurus Citation > Titles > Title + + + + + 1996-08-22T00:00:00 + + + revision + + + + + + + publisher + + + + + Overview > Topics & Keywords > Temporal Keywords > Thesaurus Citation > Contact > Organization + + + + + + + Overview > Topics & Keywords > Temporal Keywords > Thesaurus Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Overview_Topics_Keywords_Temporal_Keywords_Thesaurus_Citation_Online_Resource_Linkage + + + Overview > Topics & Keywords > Temporal Keywords > Thesaurus Citation > Online Resource > Protocol + + + + + + + + + + + Overview > Topics & Keywords > Theme Keywords > Keyword1 + + + Overview > Topics & Keywords > Theme Keywords > Keyword2 + + + theme + + + + + Overview > Topics & Keywords > Theme Keywords > Thesaurus Citation > Titles > Title + + + + + 2010-01-01T00:00:00 + + + publication + + + + + + + publisher + + + + + Overview > Topics & Keywords > Theme Keywords > Thesaurus Citation > Contact > Organization + + + + + + + Overview > Topics & Keywords > Theme Keywords > Thesaurus Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Overview_Topics_Keywords_Theme_Keywords_Thesaurus_Citation_Online_Resource_Linkage + + + Overview > Topics & Keywords > Theme Keywords > Thesaurus Citation > Online Resource > Protocol + + + + + + + + + + + Overview > Topics & Keywords > Discipline Keywords > Keyword1 + + + Overview > Topics & Keywords > Discipline Keywords > Keyword2 + + + discipline + + + + + Overview > Topics & Keywords > Discipline Keywords > Thesaurus Citation > Titles > Title + + + + + 2007-02-12T00:00:00 + + + revision + + + + + + + publisher + + + + + Overview > Topics & Keywords > Discipline Keywords > Thesaurus Citation > Contact > Organization + + + + + + + Overview > Topics & Keywords > Discipline Keywords > Thesaurus Citation > Contact > Contact Information > Email + + + + + + + + + + + + + + + + + Overview > Topics & Keywords > Other Keywords > Keyword1 + + + Overview > Topics & Keywords > Other Keywords > Keyword2 + + + + + Overview > Topics & Keywords > Other Keywords > Thesaurus Citation > Titles > Title + + + + + 2010-05-10T00:00:00 + + + revision + + + + + + + publisher + + + + + Overview > Topics & Keywords > Other Keywords > Thesaurus Citation > Contact > Organization + + + + + + + Overview > Topics & Keywords > Other Keywords > Thesaurus Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Overview_Topics_Keywords_Other_Keywords_Thesaurus_Citation_Online_Resource_Linkage + + + Overview > Topics & Keywords > Other Keywords > Thesaurus Citation > Online Resource > Protocol + + + + + + + + + + + Overview > Topics & Keywords > Subtopic Keywords > Keyword1 + + + Overview > Topics & Keywords > Subtopic Keywords > Keyword2 + + + subTopicCategory + + + + + Overview > Topics & Keywords > Subtopic Keywords > Thesaurus Citation > Titles > Title + + + + + 2011-10-14T00:00:00 + + + creation + + + + + + + publisher + + + + + Overview > Topics & Keywords > Subtopic Keywords > Thesaurus Citation > Contact > Organization + + + + + + + Overview > Topics & Keywords > Subtopic Keywords > Thesaurus Citation > Contact > Contact Information > Email + + + + + + + + + + + + + + + + + Overview > Topics & Keywords > Product Keywords > Keyword1 + + + Overview > Topics & Keywords > Product Keywords > Keyword2 + + + product + + + + + Overview > Topics & Keywords > Product Keywords > Thesaurus Citation > Titles > Title + + + + + 2011-10-13T00:00:00 + + + creation + + + + + + + publisher + + + + + Overview > Topics & Keywords > Product Keywords > Thesaurus Citation > Contact > Organization + + + + + + + Overview > Topics & Keywords > Product Keywords > Thesaurus Citation > Contact > Contact Information > Email + + + + + + + + + + + + + + + + + Live Data and Maps + + + + + + + + Resource > Details > Usage > Specific Usage + + + + 2011-10-01T10:00:00 + + + + Resource > Details > Usage > Limitations + + + + + user + + + + + Resource > Details > Usage > Contact > Organization + + + + + + + Resource > Details > Usage > Contact > Contact Information > Email + + + + + + + + + Resource > Details > Usage > Contact > Position + + + + + + + + + + + + + Overview > Item Description > Use Limitation + Resource > Constraints > General Constraints > Use Limitation + + + + + + + Resource > Constraints > Legal Constraints > Use Limitation + + + license + + + otherRestrictions + + + Resource > Constraints > Legal Constraints > Other Constraints + + + + + + + Resource > Constraints > Security Constraints > Use Limitation + + + restricted + + + Resource > Constraints > Security Constraints > User Note + + + Resource > Constraints > Security Constraints > Classification System + + + Resource > Constraints > Security Constraints > Handling Description + + + + + + + + + Resource > References > Aggregate Information > Dataset Citation > Titles > Title (Association=Cross Reference) + + + + + 2006-06-01T00:00:00 + + + publication + + + + + + + originator + + + + + Resource > References > Aggregate Information > Dataset Citation > Contact > Organization (Association=Cross Reference) + + + + + + + Resource > References > Aggregate Information > Dataset Citation > Contact > Contact Information > Email (Association=Cross Reference) + + + + + + + + + Resource > References > Aggregate Information > Dataset Citation > Contact > Name (Association=Cross Reference) + + + + + + + + + + + http://Resource_References_Aggregate_Information_Dataset_Citation_Online_Resource_Linkage_Association=Cross_Reference + + + Resource > References > Aggregate Information > Dataset Citation > Online Resource > Protocol (Association=Cross Reference) + + + + + + + crossReference + + + investigation + + + + + + + + + Resource > References > Aggregate Information > Dataset Citation > Titles > Title (Association=Larger Work) + + + + + 2003-09-18T00:00:00 + + + publication + + + + + + + originator + + + + + Resource > References > Aggregate Information > Dataset Citation > Contact > Organization (Association=Larger Work) + + + + + + + Resource > References > Aggregate Information > Dataset Citation > Contact > Contact Information > Email (Association=Larger Work) + + + + + + + + + Resource > References > Aggregate Information > Dataset Citation > Contact > Name (Association=Larger Work) + + + + + + + + + + + http://Resource_References_Aggregate_Information_Dataset_Citation_Online_Resource_Linkage_Association=Larger_Work + + + Resource > References > Aggregate Information > Dataset Citation > Online Resource > Protocol (Association=Larger Work) + + + + + + + largerWorkCitation + + + + + + + + + Overview > Citation > Titles > Collective Title + + + + + collectiveTitle + + + + + Resource > Service Details > Service Type > Name + + + Resource > Service Details > Service Type Version + + + + + Resource > Service Details > Access Properties > Fees + + + 2011-11-01T06:00:00 + + + Resource > Service Details > Access Properties > Ordering Instructions + + + Resource > Service Details > Access Properties > Turnaround + + + + + loose + + + + + Resource > Service Details > Coupled Resource > Identifier + + + + + + Resource > Service Details > Coupled Resource > Operation Name + + + + + + + + + + + + + true + + + + + fre + + + CA + + + utf8 + + + + + true + + + + + Resource > Content > Feature Catalogue > Feature Type > Name + + + + + + + Resource > Content > Feature Catalogue > Feature Catalogue Citation > Titles > Title + + + + + 2011-03-15T00:00:00 + + + creation + + + + + + + publisher + + + + + Resource > Content > Feature Catalogue > Feature Catalogue Citation > Contact > Organization + + + + + + + Resource > Content > Feature Catalogue > Feature Catalogue Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Resource_Content_Feature_Catalogue_Feature_Catalogue_Citation_Online_Resource_Linkage + + + Resource > Content > Feature Catalogue > Feature Catalogue Citation > Online Resource > Protocol + + + + + + + + + + + Resource > Content > Coverage Description > Attribute Description + + + + + physicalMeasurement + + + + + + + Resource > Content > Coverage Description > Range Dimension > Sequence Identifier + + + + + Resource > Content > Coverage Description > Range Dimension > Sequence Identifier > Type + + + + + + + Resource > Content > Coverage Description > Range Dimension > Descriptor + + + + + + + + + + + Resource > Content > Image Description > Attribute Description + + + + + + + Resource > Content > Image Description > Processing Level Code > Authority Citation > Titles > Title + + + + + 2008-09-18T00:00:00 + + + revision + + + + + + + publisher + + + + + Resource > Content > Image Description > Processing Level Code > Authority Citation > Contact > Organization + + + + + + + Resource > Content > Image Description > Processing Level Code > Authority Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Resource_Content_Image_Description_Processing_Level_Code_Authority_Citation_Online_Resource_Linkage + + + Resource > Content > Image Description > Processing Level Code > Authority Citation > Online Resource > Protocol + + + + + + + Resource > Content > Image Description > Processing Level Code > Code + + + + + + + image + + + + + + + Resource > Content > Image Description > Band > Sequence Identifier + + + + + Resource > Content > Image Description > Band > Sequence Identifier > Type + + + + + + + Resource > Content > Image Description > Band > Descriptor + + + 255.99 + + + 0.01 + + + + Unified Code of Units of Measure + m + + + + 0.98 + + + 19.3 + + + 8 + + + 225.4 + + + 4 + + + + + + + 78.5 + + + 135.5 + + + shadow + + + + + + + Resource > Content > Image Description > Quality Code > Authority Citation > Titles > Title + + + + + 2008-09-18T00:00:00 + + + revision + + + + + + + publisher + + + + + Resource > Content > Image Description > Quality Code > Authority Citation > Contact > Organization + + + + + + + Resource > Content > Image Description > Quality Code > Authority Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Resource_Content_Image_Description_Quality_Code_Authority_Citation_Online_Resource_Linkage + + + Resource > Content > Image Description > Quality Code > Authority Citation > Online Resource > Protocol + + + + + + + Resource > Content > Image Description > Quality Code > Code + + + + + 6.5 + + + 2 + + + true + + + true + + + true + + + true + + + true + + + + + + + + + + + Resource > Distribution > Distribution Format > Format Name + + + Resource > Distribution > Distribution Format > Format Version + + + + + + + + + + + Resource > Distribution > Distributor > Distribution Format > Format Name + + + Resource > Distribution > Distributor > Distribution Format > Specification + + + Resource > Distribution > Distributor > Distribution Format > Format Version + + + + + Resource > Distribution > Distributor > Distribution Format > Amendment Number + + + Resource > Distribution > Distributor > Distribution Format > Decompression Technique + + + + + + + + + distributor + + + + + Resource > Distribution > Distributor > Organization + + + + + + + Resource > Distribution > Distributor > Contact Information > Phone + + + voice + + + + + + + Resource > Distribution > Distributor > Contact Information > Address + + + Resource > Distribution > Distributor > Contact Information > City + + + Resource > Distribution > Distributor > Contact Information > State + + + Resource > Distribution > Distributor > Contact Information > Postal Code + + + US + + + Resource > Distribution > Distributor > Contact Information > Email + + + + + + + + + + + + + Resource > Distribution > Distributor > Ordering Process > Fees + + + 2011-10-15T13:00:00 + + + Resource > Distribution > Distributor > Ordering Process > Ordering Instructions + + + Resource > Distribution > Distributor > Ordering Process > Turnaround + + + + + + + + + Resource > Distribution > Distributor > Distribution Format > Format Name + + + Resource > Distribution > Distributor > Distribution Format > Specification + + + Resource > Distribution > Distributor > Distribution Format > Format Version + + + + + Resource > Distribution > Distributor > Distribution Format > Amendment Number + + + Resource > Distribution > Distributor > Distribution Format > Decompression Technique + + + + + + + Resource > Distribution > Distributor > Digital Transfer Options > Units of Distribution + + + 1 + + + + + http://Resource_Distribution_Distributor_Digital_Transfer_Options_Online_Resource_Linkage + + + Resource > Distribution > Distributor > Digital Transfer Options > Online Resource > Protocol + + + Resource > Distribution > Distributor > Digital Transfer Options > Online Resource > Profile + + + Resource > Distribution > Distributor > Digital Transfer Options > Online Resource > Name + + + Resource > Distribution > Distributor > Digital Transfer Options > Online Resource > Description (not a geoportal content type, e.g. Offline Data) + + + download + + + + + + + + + dvdRom + + + + + 1 + + + Resource > Distribution > Distributor > Digital Transfer Options > Offline Medium > Density Units (dvdRom Medium Name) + + + 1 + + + iso9660 + + + Resource > Distribution > Distributor > Digital Transfer Options > Offline Medium > Medium Note (dvdRom Medium Name) + + + + + + + + + + + + + hardcopy + + + + + Resource > Distribution > Distributor > Digital Transfer Options > Offline Medium > Medium Note (hardcopy Medium Name) + + + + + + + + + + + + + USBFlashDrive + + + + + UDF + + + Resource > Distribution > Distributor > Digital Transfer Options > Offline Medium > Medium Note (NAP USBFlashDrive Medium Name + UDF Format Code) + + + + + + + + + + + 2.12 + + + + + http://Resource_Distribution_Digital_Transfer_Options_Online_Resource_Linkage + + + Resource > Distribution > Digital Transfer Options > Online Resource > Protocol + + + + + + + + + + + + + service + + + + + Resource > Data Quality > Extent > Description + + + + + + 2011-10-12T00:00:00 + + + + + + + + + + Resource > Data Quality > Level Description > Dataset + + + + + + + + + + + + + + + Resource > Data Quality > Report > Measure > Identifier > Authority Citation > Titles > Title (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + 2011-08-31T00:00:00 + + + revision + + + + + + + publisher + + + + + Resource > Data Quality > Report > Measure > Identifier > Authority Citation > Contact > Organization (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + + + Resource > Data Quality > Report > Measure > Identifier > Authority Citation > Contact > Contact Information > Email (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + + + + + + + + + http://Resource_Data_Quality_Report_Measure_Identifier_Authority_Citation_Online_Resource_Linkage_Type=Absolute_External_Positional_Accuracy_Dimension=Horizontal + + + Resource > Data Quality > Report > Measure > Identifier > Authority Citation > Online Resource > Protocol (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + + + Resource > Data Quality > Report > Measure > Identifier > Code (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + Resource > Data Quality > Report > Measure > Name (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + Resource > Data Quality > Report > Measure > Description (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + + + 2011-10-12T00:00:00 + + + Resource > Data Quality > Report > Evaluation Method > Description (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + Resource > Data Quality > Report > Evaluation Method > Procedure Citation > Titles > Title (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + 2011-08-01T00:00:00 + + + revision + + + + + + + publisher + + + + + Resource > Data Quality > Report > Evaluation Method > Procedure Citation > Contact > Organization (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + + + Resource > Data Quality > Report > Evaluation Method > Procedure Citation > Contact > Contact Information > Email (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + + + + + + + + + http://Resource_Data_Quality_Report_Evaluation_Method_Procedure_Citation_Online_Resource_Linkage_Type=Absolute_External_Positional_Accuracy_Dimension=Horizontal + + + Resource > Data Quality > Report > Evaluation Method > Procedure Citation > Online Resource > Protocol (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + + + indirect + + + + + + + + + Resource > Data Quality > Report > Conformance Result > Specification > Titles > Title (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + 2009-06-20T00:00:00 + + + creation + + + + + + + publisher + + + + + Resource > Data Quality > Report > Conformance Result > Specification > Contact > Organization (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + + + Resource > Data Quality > Report > Conformance Result > Specification > Contact > Contact Information > Email (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + + + + + + + + + http://Resource_Data_Quality_Report_Conformance_Result_Specification_Online_Resource_Linkage_Type=Absolute_External_Positional_Accuracy_Dimension=Horizontal + + + Resource > Data Quality > Report > Conformance Result > Specification > Online Resource > Protocol (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + + + Resource > Data Quality > Report > Conformance Result > Explanation (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + true + + + + + + + Resource > Data Quality > Report > Quantitative Result > Value (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + Unified Code of Units of Measure + m + + + + Resource > Data Quality > Report > Quantitative Result > Value Type (Type=Absolute External Positional Accuracy + Dimension=Horizontal) + + + + + + + + + + + Resource > Data Quality > Report > Measure > Description (Type=Absolute External Positional Accuracy + Dimension=Vertical) + + + + + + + Resource > Data Quality > Report > Evaluation Method > Description (Type=Absolute External Positional Accuracy + Dimension=Vertical) + + + + + + + Resource > Data Quality > Report > Quantitative Result > Value (Type=Absolute External Positional Accuracy + Dimension=Vertical) + + + + Unified Code of Units of Measure + m + + + + + + + + + + + + Resource > Data Quality > Report > Measure > Description (Type=Completeness Omission) + + + + + + + Resource > Data Quality > Report > Quantitative Result > Value (Type=Completeness Omission) + + + + Unified Code of Units of Measure + m + + + + + + + + + + + + Resource > Data Quality > Report > Measure > Description (Type=Conceptual Consistency) + + + + + + + Resource > Data Quality > Report > Quantitative Result > Value (Type=Conceptual Consistency) + + + + Unified Code of Units of Measure + m + + + + + + + + + + + + Resource > Data Quality > Report > Measure > Description (Type=Quantitative Attribute Accuracy) + + + + + + + Resource > Data Quality > Report > Evaluation Method > Description (Type=Quantitative Attribute Accuracy) + + + + + + + Resource > Data Quality > Report > Quantitative Result > Value (Type=Quantitative Attribute Accuracy) + + + + Unified Code of Units of Measure + Cel + + + + + + + + + + + + + + Resource > Data Quality > Report > Conformance Result > Specification > Titles > Title (Type=Domain Consistency) + + + + + 2010-07-01T00:00:00 + + + creation + + + + + + + publisher + + + + + Resource > Data Quality > Report > Conformance Result > Specification > Contact > Organization (Type=Domain Consistency) + + + + + + + Resource > Data Quality > Report > Conformance Result > Specification > Contact > Contact Information > Email (Type=Domain Consistency) + + + + + + + + + + + + + http://Resource_Data_Quality_Report_Conformance_Result_Specification_Online_Resource_Linkage_Type=Domain_Consistency + + + Resource > Data Quality > Report > Conformance Result > Specification > Online Resource > Protocol (Type=Domain Consistency) + + + + + + + Resource > Data Quality > Report > Conformance Result > Explanation (Type=Domain Consistency) + + + true + + + + + + + + + + + Resource > Lineage > Statement + + + + + service + + + + + Resource > Data Quality > Extent > Description + + + + + + 2011-10-12T00:00:00 + + + + + + + + + + Resource > Data Quality > Level Description > Dataset + + + + + + + + + Resource > Lineage > Data Source > Source Description + + + + + + + 250000 + + + + + + + + + + + + + Resource > Lineage > Data Source > Reference System > Authority Citation > Titles > Title + + + + + 2011-06-15T00:00:00 + + + revision + + + + + + + custodian + + + + + Resource > Lineage > Data Source > Reference System > Authority Citation > Contact > Organization + + + + + + + Resource > Lineage > Data Source > Reference System > Authority Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Resource_Lineage_Data_Source_Reference_System_Authority_Citation_Online_Resource_Linkage + + + Resource > Lineage > Data Source > Reference System > Authority Citation > Online Resource > Protocol + + + + + + + Resource > Lineage > Data Source > Reference System > Code + + + Resource > Lineage > Data Source > Reference System > Code Space + + + Resource > Lineage > Data Source > Reference System > Version + + + + + + + + + Resource > Lineage > Data Source > Source Citation > Titles > Title + + + Resource > Lineage > Data Source > Source Citation > Titles > Alternate Title + + + + + 2011-07-15T00:00:00 + + + publication + + + + + + + originator + + + + + Resource > Lineage > Data Source > Source Citation > Contact > Organization + + + + + + + Resource > Lineage > Data Source > Source Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Resource_Lineage_Data_Source_Source_Citation_Online_Resource_Linkage + + + Resource > Lineage > Data Source > Source Citation > Online Resource > Protocol + + + + + + + + + Resource > Lineage > Data Source > Source Extent > Description + + + + + + 2011-06-06T00:00:00 + + + + + + + + + 2011-07-14T14:20:00 + + + + + + + + + 2011-08-05T06:05:00 + + + + + + + + + + + + Resource > Lineage > Process Step > Process Description + + + Resource > Lineage > Process Step > Rationale + + + + 2011-10-01T16:10:32 + + + + + + processor + + + + + Resource > Lineage > Process Step > Processor > Organization + + + + + + + Resource > Lineage > Process Step > Processor > Contact Information > Phone + + + voice + + + + + + + Resource > Lineage > Process Step > Processor > Contact Information > Address + + + Resource > Lineage > Process Step > Processor > Contact Information > City + + + Resource > Lineage > Process Step > Processor > Contact Information > State + + + Resource > Lineage > Process Step > Processor > Contact Information > Postal Code + + + US + + + Resource > Lineage > Process Step > Processor > Contact Information > Email + + + + + + + + + Resource > Lineage > Process Step > Processor > Name + + + + + + + + + + + Resource > Lineage > Process Step > Data Source > Source Description (Source Type=Used) + + + + + + + + + Resource > Lineage > Process Step > Data Source > Reference System > Authority Citation > Titles > Title (Source Type=Used) + + + + + 2012-07-20T00:00:00 + + + publication + + + + + + + resourceProvider + + + + + Resource > Lineage > Process Step > Data Source > Reference System > Authority Citation > Contact > Organization (Source Type=Used) + + + + + + + Resource > Lineage > Process Step > Data Source > Reference System > Authority Citation > Contact > Contact Information > Email (Source Type=Used) + + + + + + + + + + + + + http://Resource_Lineage_Process_Step_Data_Source_Reference_System_Authority_Citation_Online_Resource_Linkage_Source_Type=Used + + + Resource > Lineage > Process Step > Data Source > Reference System > Authority Citation > Online Resource > Protocol (Source Type=Used) + + + + + + + Resource > Lineage > Process Step > Data Source > Reference System > Code (Source Type=Used) + + + Resource > Lineage > Process Step > Data Source > Reference System > Code Space (Source Type=Used) + + + Resource > Lineage > Process Step > Data Source > Reference System > Version (Source Type=Used) + + + + + + + + + Resource > Lineage > Process Step > Data Source > Source Citation > Titles > Title (Source Type=Used) + + + Resource > Lineage > Process Step > Data Source > Source Citation > Titles > Alternate Title (Source Type=Used) + + + + + 2011-09-14T00:00:00 + + + revision + + + + + + + publisher + + + + + Resource > Lineage > Process Step > Data Source > Source Citation > Contact > Organization (Source Type=Used Role=Publisher) + + + + + + + Resource > Lineage > Process Step > Data Source > Source Citation > Contact > Contact Information > City (Source Type=Used Role=Publisher) + + + Resource > Lineage > Process Step > Data Source > Source Citation > Contact > Contact Information > State (Source Type=Used Role=Publisher) + + + US + + + Resource > Lineage > Process Step > Data Source > Source Citation > Contact > Contact Information > Email (Source Type=Used Role=Publisher) + + + + + + + + + + + + + originator + + + + + Resource > Lineage > Process Step > Data Source > Source Citation > Contact > Organization (Source Type=Used Role=Originator) + + + + + + + Resource > Lineage > Process Step > Data Source > Source Citation > Contact > Contact Information > Email (Source Type=Used Role=Originator) + + + + + + + + + + + + + http://Resource_Lineage_Process_Step_Data_Source_Source_Citation_Online_Resource_Linkage_Source_Type=Used + + + Resource > Lineage > Process Step > Data Source > Source Citation > Online Resource > Protocol (Source Type=Used) + + + + + + + + + + + true + + + -100 + + + -80 + + + 21.5 + + + 42.5 + + + + + + + + + + + Resource > Lineage > Process Step > Data Source > Source Description (Source Type=Produced) + + + + + Resource > Lineage > Process Step > Data Source > Source Citation > Titles > Title (Source Type=Produced) + + + Resource > Lineage > Process Step > Data Source > Source Citation > Titles > Alternate Title (Source Type=Produced) + + + + + 2011-10-14T00:00:00 + + + creation + + + + + + + originator + + + + + Resource > Lineage > Process Step > Data Source > Source Citation > Contact > Organization (Source Type=Produced Role=Originator) + + + + + + + Resource > Lineage > Process Step > Data Source > Source Citation > Contact > Contact Information > Email (Source Type=Produced Role=Originator) + + + + + + + + + + + + + + + + + + + + + + + Resource > References > Portrayal Citation > Titles > Title + + + + + 2008-01-01T00:00:00 + + + revision + + + + + + + custodian + + + + + Resource > References > Portrayal Citation > Contact > Organization + + + + + + + Resource > References > Portrayal Citation > Contact > Contact Information > Email + + + + + + + + + Resource > References > Portrayal Citation > Contact > Position + + + + + + + + + + + http://Resource_References_Portrayal_Citation_Online_Resource_Linkage + + + Resource > References > Portrayal Citation > Online Resource > Protocol + + + + + + + + + + + Metadata > Constraints > General Constraints > Use Limitation + + + + + + + Metadata > Constraints > Legal Constraints > Use Limitation + + + otherRestrictions + + + restricted + + + Metadata > Constraints > Legal Constraints > Other Constraints + + + + + + + Metadata > Constraints > Security Constraints > Use Limitation + + + unclassified + + + Metadata > Constraints > Security Constraints > User Note + + + Metadata > Constraints > Security Constraints > Classification System + + + Metadata > Constraints > Security Constraints > Handling Description + + + + + + + + + Resource > References > Application Schema Information > Citation > Titles > Title + + + + + 2011-04-08T00:00:00 + + + creation + + + + + + + publisher + + + + + Resource > References > Application Schema Information > Citation > Contact > Organization + + + + + + + Resource > References > Application Schema Information > Citation > Contact > Contact Information > Email + + + + + + + + + + + + + http://Resource_References_Application_Schema_Information_Citation_Online_Resource_Linkage + + + Resource > References > Application Schema Information > Citation > Online Resource > Protocol + + + + + + + Resource > References > Application Schema Information > Schema Language + + + Resource > References > Application Schema Information > Constraint Language + + + Resource > References > Application Schema Information > ASCII + + + + + Resource > References > Application Schema Information > Graphics File Source + + + Resource > References > Application Schema Information > Graphics File + + + + + + + Resource > References > Application Schema Information > Software Development File Source + + + Resource > References > Application Schema Information > Software Development File + + + + + Resource > References > Application Schema Information > Software Development File Format + + + + + + + unknown + + + + + 2012-11-01T12:00:00 + + + revision + + + + + P1Y2M3DT4H5M6S + + + + + service + + + + + Metadata > Maintenance > Scope Description > Attributes + + + + + + + Metadata > Maintenance > Scope Description > Features + + + + + + + Metadata > Maintenance > Scope Description > Feature Instances + + + + + + + Metadata > Maintenance > Scope Description > Attribute Instances + + + + + + + Metadata > Maintenance > Scope Description > Dataset + + + + + + + Metadata > Maintenance > Scope Description > Other Instances + + + + + + + Last metadata review date: 20211207 + + + Metadata > Maintenance > Maintenance Note + + + + + custodian + + + + + Metadata > Maintenance > Contact > Organization + + + + + + + Metadata > Maintenance > Contact > Contact Information > Email + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/test_iso3_parsing.py b/tests/test_iso3_parsing.py index dc294cce..3d46fde6 100644 --- a/tests/test_iso3_parsing.py +++ b/tests/test_iso3_parsing.py @@ -28,6 +28,7 @@ # ================================================================= """ Unit tests for pycsw.core.mdb This tests its ability to parse ISO19115-3 XML + """ from pathlib import Path @@ -46,7 +47,16 @@ pytestmark = pytest.mark.unit +@pytest.fixture +def ns(): + """ Create a V2 namespace + """ + md = MD_Metadata() + return md.namespaces + def test_md_metadata_empty(): + """ Test empty MD_Metadata + """ mdb = MD_Metadata() assert mdb.md == None assert mdb.xml == None @@ -70,19 +80,25 @@ def test_md_metadata_empty(): assert mdb.dataquality == None assert mdb.acquisition == None -def test_pt_locale_empty(): - loc = PT_Locale() +def test_pt_locale_empty(ns): + """ Test empty PT_Locale + """ + loc = PT_Locale(ns) assert loc.id == None assert loc.languagecode == None assert loc.charset == None -def test_ci_date_empty(): - dat = CI_Date() +def test_ci_date_empty(ns): + """ Test empty CI_Date + """ + dat = CI_Date(ns) assert dat.date == None assert dat.type == None -def test_ci_responsibility_empty(): - resp = CI_Responsibility() +def test_ci_responsibility_empty(ns): + """ Test empty + """ + resp = CI_Responsibility(ns) assert resp.name == None assert resp.organization == None assert resp.position == None @@ -97,20 +113,26 @@ def test_ci_responsibility_empty(): assert resp.onlineresource == None assert resp.role == None -def test_keyword_empty(): - keyw = Keyword() +def test_keyword_empty(ns): + """ Test empty Keyword + """ + keyw = Keyword(ns) assert keyw.name == None assert keyw.url == None -def test_md_keywords_empty(): - mdk = MD_Keywords() +def test_md_keywords_empty(ns): + """ Test empty MD_Keywords + """ + mdk = MD_Keywords(ns) assert mdk.keywords == [] assert mdk.type == None assert mdk.thesaurus == None assert mdk.kwdtype_codeList == 'http://standards.iso.org/iso/19115/-3/resources/Codelist/gmxCodelists.xml#MD_KeywordTypeCode' -def test_md_dataidentification_empty(): - mdi = MD_DataIdentification() +def test_md_dataidentification_empty(ns): + """ Test empty MD_DataIdentification + """ + mdi = MD_DataIdentification(ns) mdi.identtype == None assert mdi.title == None assert mdi.alternatetitle == None @@ -150,20 +172,26 @@ def test_md_dataidentification_empty(): assert mdi.temporalextent_end == None assert mdi.spatialrepresentationtype == [] -def test_md_distributor_empty(): - mdd = MD_Distributor() +def test_md_distributor_empty(ns): + """ Test empty MD_Distributor + """ + mdd = MD_Distributor(ns) assert mdd.contact == None assert mdd.online == [] -def test_md_distribution_empty(): - mdd = MD_Distribution() +def test_md_distribution_empty(ns): + """ Test empty MD_Distribution + """ + mdd = MD_Distribution(ns) assert mdd.format == None assert mdd.version == None assert mdd.distributor == [] assert mdd.online == [] -def test_dq_dataquality_empty(): - dqd = DQ_DataQuality() +def test_dq_dataquality_empty(ns): + """ Test empty DQ_DataQuality + """ + dqd = DQ_DataQuality(ns) assert dqd.conformancetitle == [] assert dqd.conformancedate == [] assert dqd.conformancedatetype == [] @@ -173,8 +201,10 @@ def test_dq_dataquality_empty(): assert dqd.specificationtitle == None assert dqd.specificationdate == [] -def test_sv_serviceidentification_empty(): - svs = SV_ServiceIdentification() +def test_sv_serviceidentification_empty(ns): + """ Test empty SV_ServiceIdentification + """ + svs = SV_ServiceIdentification(ns) assert svs.type == None assert svs.version == None assert svs.fees == None @@ -182,47 +212,61 @@ def test_sv_serviceidentification_empty(): assert svs.operations == [] assert svs.operateson == [] -def test_ci_onlineresource_empty(): - cio = CI_OnlineResource() +def test_ci_onlineresource_empty(ns): + """ Test empty CI_OnlineResource + """ + cio = CI_OnlineResource(ns) assert cio.url == None assert cio.protocol == None assert cio.name == None assert cio.description == None assert cio.function == None -def test_ex_geographicboundingbox_empty(): - exg = EX_GeographicBoundingBox() +def test_ex_geographicboundingbox_empty(ns): + """ Test empty EX_GeographicBoundingBox + """ + exg = EX_GeographicBoundingBox(ns) assert exg.minx == None assert exg.maxx == None assert exg.miny == None assert exg.maxy == None -def test_ex_polygon_empty(): - exp = EX_Polygon() +def test_ex_polygon_empty(ns): + """ Test empty EX_Polygon + """ + exp = EX_Polygon(ns) assert exp.exterior_ring == None assert exp.interior_rings == [] -def test_ex_boundingpolygon_empty(): - exb = EX_BoundingPolygon() +def test_ex_boundingpolygon_empty(ns): + """ Test empty EX_BoundingPolygon + """ + exb = EX_BoundingPolygon(ns) assert exb.is_extent == None assert exb.polygons == [] -def test_ex_extent_empty(): - exe = EX_Extent() +def test_ex_extent_empty(ns): + """ Test empty EX_Extent + """ + exe = EX_Extent(ns) assert exe.boundingBox == None assert exe.boundingPolygon == None assert exe.description_code == None assert exe.vertExtMin == None assert exe.vertExtMax == None -def test_md_referencesystem_empty(): - mdr = MD_ReferenceSystem() +def test_md_referencesystem_empty(ns): + """ Test empty MD_ReferenceSystem + """ + mdr = MD_ReferenceSystem(ns) assert mdr.code == None assert mdr.codeSpace == None assert mdr.version == None -def test_md_featurecataloguedescription_empty(): - mdf = MD_FeatureCatalogueDescription() +def test_md_featurecataloguedescription_empty(ns): + """ Test empty MD_FeatureCatalogueDescription + """ + mdf = MD_FeatureCatalogueDescription(ns) assert mdf.xml == None assert mdf.compliancecode == None assert mdf.language == [] @@ -230,16 +274,20 @@ def test_md_featurecataloguedescription_empty(): assert mdf.featuretypenames == [] assert mdf.featurecatalogues == [] -def test_md_imagedescription_empty(): - mdi = MD_ImageDescription() +def test_md_imagedescription_empty(ns): + """ Test empty MD_ImageDescription + """ + mdi = MD_ImageDescription(ns) assert mdi.type == 'image' assert mdi.bands == [] - assert mdi.attribute_description == None - assert mdi.cloud_cover == None - assert mdi.processing_level == None + assert mdi.attributedescription == None + assert mdi.cloudcover == None + assert mdi.processinglevel == None -def test_md_band_empty(): - mdb = MD_Band(None) +def test_md_band_empty(ns): + """ Test empty MD_Band + """ + mdb = MD_Band(ns, None) assert mdb.id == None assert mdb.units == None assert mdb.min == None @@ -249,6 +297,8 @@ def test_md_band_empty(): @pytest.fixture def bmd(): """ Create an MD_Metadata instance from Belgian ISO 19115 Part 3 XML sample + + Source: https://metawal.wallonie.be/geonetwork """ belgian_sample = str(Path(__file__).parent.parent / "tests" / "resources" / "iso3_examples" / "metawal.wallonie.be-catchments.xml") with open(belgian_sample, "r") as f_d: @@ -391,6 +441,8 @@ def test_identification_keywords(bmd): def amd(): """ Create an MD_Metadata instance from AuScope 3D Models ISO 19115 Part 3 XML sample + + Source: https://portal.auscope.org.au/geonetwork """ aust_sample = str(Path(__file__).parent.parent / "tests" / "resources" / "iso3_examples" / "auscope-3d-model.xml") with open(aust_sample, "r") as f_d: @@ -417,6 +469,8 @@ def test_aus(amd): def smd(): """ Create an MD_Metadata instance from Belgian health & safety ISO 19115 Part 3 XML services sample + + Source: https://metawal.wallonie.be/geonetwork """ belgian_srv_sample = str(Path(__file__).parent.parent / "tests" / "resources" / "iso3_examples" / "metawal.wallonie.be-srv.xml") with open(belgian_srv_sample, "r") as f_d: @@ -444,3 +498,61 @@ def test_service(smd): assert(rec_9['uuidref'] == '401a1ac7-7222-4cf8-a7bb-f68090614056') assert(rec_9['title'] == '[Brouillon] INSPIRE - Bruit des aéroports wallons (Charleroi et Liège) - Plan d’exposition au bruit en Wallonie (BE)') assert(rec_9['href'] == 'https://metawal.wallonie.be/geonetwork/srv/api/records/401a1ac7-7222-4cf8-a7bb-f68090614056') + +@pytest.fixture +def emd(): + """ + Create a MD_Metadata instance from ESRI ArcGIS ISO 19115 Part 3 XML artificial sample + This uses the older mdb v1 namespaces and has elements not present in other samples + e.g. MD_Band + + Source: https://github.com/Esri/arcgis-pro-metadata-toolkit + """ + arcgis_sample = str(Path(__file__).parent.parent / "tests" / "resources" / "iso3_examples" / "arcgis-sample.xml") + with open(arcgis_sample, "r") as f_d: + xml_list = f_d.readlines() + xml_str = ''.join(xml_list) + xml_bytes = bytes(xml_str, encoding='utf-8') + exml = etree.fromstring(xml_bytes) + assert exml is not None + return MD_Metadata(exml) + +def test_md_featurecataloguedesc(emd): + """ Tests MD_FeatureCatalogueDescription + """ + assert emd is not None + print(emd.format_me()) + cont_info = emd.contentinfo[0] + assert cont_info.compliancecode == True + assert cont_info.includedwithdataset == True + assert cont_info.featurecatalogues[0] == "Resource > Content > Feature Catalogue > Feature Catalogue Citation > Titles > Title" + assert cont_info.featuretypenames[0] == "Resource > Content > Feature Catalogue > Feature Type > Name" + assert cont_info.language == ['fre'] + +def test_md_imagedescription(emd): + """ Tests MD_ImageDescription and MD_Band + """ + assert emd is not None + img_desc = emd.contentinfo[1] + assert img_desc.type == 'image' + assert img_desc.attributedescription == "Resource > Content > Image Description > Attribute Description" + assert img_desc.cloudcover == '6.5' + assert img_desc.processinglevel == "Resource > Content > Image Description > Processing Level Code > Code" + assert img_desc.bands[0].id == "Resource > Content > Image Description > Band > Sequence Identifier" + assert img_desc.bands[0].units == "Unified Code of Units of Measure" + assert img_desc.bands[0].max == '255.99' + assert img_desc.bands[0].min == '0.01' + +def test_dq_dataquality(emd): + """ Tests DQ_DataQuality + """ + assert emd is not None + dq = emd.dataquality + assert dq.conformancetitle[0][:90] == "Resource > Data Quality > Report > Evaluation Method > Procedure Citation > Titles > Title" + assert dq.conformancedate[0] == '2011-08-01T00:00:00' + assert dq.conformancedatetype[0] == 'revision' + assert dq.conformancedegree[0][:62] == 'Resource > Data Quality > Report > Quantitative Result > Value' + assert dq.lineage == None # emd does not have lineage within DQ_DataQuality + assert dq.lineage_url == None + assert dq.specificationtitle == 'Resource > Data Quality > Report > Conformance Result > Specification > Titles > Title (Type=Domain Consistency)' + assert dq.specificationdate[0] == '2010-07-01T00:00:00'