Skip to content

Commit d5e650d

Browse files
authored
[iso19139:2007] add more generic char/anchor check (#954)
* add more generic char/anchor check, add xxx_url property to keep backwards compat. * match list length xx with xx_url (don't remove None, set as '')
1 parent 7d92dba commit d5e650d

File tree

2 files changed

+131
-70
lines changed

2 files changed

+131
-70
lines changed

owslib/iso.py

Lines changed: 99 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,41 @@ def get_namespaces():
2727

2828
namespaces = get_namespaces()
2929

30+
def testFirstCharOrAnchor(md,xpath):
31+
""" function which checks if the first matching element is either charstring or anchor, if anchor, returns {name, url}, else {name} """
32+
r = {'name': '', 'url': ''}
33+
val = md.find(util.nspath_eval(xpath+'/gco:CharacterString', namespaces))
34+
r['name'] = util.testXMLValue(val)
35+
if r['name'] in [None,'']:
36+
val = md.find(util.nspath_eval(xpath+'/gmx:Anchor', namespaces))
37+
if val is not None:
38+
r['name'] = util.testXMLValue(val)
39+
r['url'] = val.attrib.get(util.nspath_eval('xlink:href', namespaces))
40+
return r
41+
42+
def testAllCharOrAnchor(md,xpath,aslist=True):
43+
"""
44+
function which checks for each matching element if it is charstring or anchor, returns {name, uri}
45+
the aslist parameter indicates to return 2 lists or single list of objects
46+
"""
47+
ro = []
48+
for i in md.findall(util.nspath_eval(xpath+'/gco:CharacterString', namespaces)):
49+
r = {'name': '', 'url': ''}
50+
r['name'] = util.testXMLValue(i)
51+
ro.append(r)
52+
for i in md.findall(util.nspath_eval(xpath+'/gmx:Anchor', namespaces)):
53+
r = {'name': '', 'url': ''}
54+
if i is not None:
55+
r['name'] = util.testXMLValue(i)
56+
r['url'] = i.attrib.get(util.nspath_eval('xlink:href', namespaces))
57+
ro.append(r)
58+
if aslist:
59+
return ro
60+
else:
61+
return {'name': [i.get('name', '') for i in ro],
62+
'url': [i.get('url', '') for i in ro]}
63+
64+
3065

3166
class MD_Metadata(object):
3267
""" Process gmd:MD_Metadata """
@@ -223,7 +258,9 @@ def __init__(self, md=None):
223258

224259
if md is None:
225260
self.name = None
261+
self.name_url = None
226262
self.organization = None
263+
self.organization_url = None
227264
self.position = None
228265
self.phone = None
229266
self.fax = None
@@ -236,11 +273,14 @@ def __init__(self, md=None):
236273
self.onlineresource = None
237274
self.role = None
238275
else:
239-
val = md.find(util.nspath_eval('gmd:individualName/gco:CharacterString', namespaces))
240-
self.name = util.testXMLValue(val)
241276

242-
val = md.find(util.nspath_eval('gmd:organisationName/gco:CharacterString', namespaces))
243-
self.organization = util.testXMLValue(val)
277+
frm = testFirstCharOrAnchor(md,'gmd:individualName')
278+
self.name = frm['name']
279+
self.name_url = frm['url']
280+
281+
frm = testFirstCharOrAnchor(md,'gmd:organisationName')
282+
self.organization = frm['name']
283+
self.organization_url = frm['url']
244284

245285
val = md.find(util.nspath_eval('gmd:positionName/gco:CharacterString', namespaces))
246286
self.position = util.testXMLValue(val)
@@ -495,22 +535,9 @@ def __init__(self, md=None, identtype=None):
495535
if val is not None:
496536
self.classification.append(val)
497537

498-
self.otherconstraints = []
499-
for i in md.findall(util.nspath_eval(
500-
'gmd:resourceConstraints/gmd:MD_LegalConstraints/gmd:otherConstraints/gco:CharacterString',
501-
namespaces)):
502-
val = util.testXMLValue(i)
503-
if val is not None:
504-
self.otherconstraints.append(val)
505-
for i in md.findall(util.nspath_eval(
506-
'gmd:resourceConstraints/gmd:MD_LegalConstraints/gmd:otherConstraints/gmx:Anchor',
507-
namespaces)):
508-
val = util.testXMLAttribute(i, util.nspath('href', namespaces["xlink"]))
509-
if val is None:
510-
val = util.testXMLValue(i)
511-
if val is not None:
512-
self.otherconstraints.append(val)
513-
538+
ocs = testAllCharOrAnchor(md,'gmd:resourceConstraints/gmd:MD_LegalConstraints/gmd:otherConstraints', False)
539+
self.otherconstraints = ocs['name']
540+
self.otherconstraints_url = ocs['url']
514541

515542
self.securityconstraints = []
516543
for i in md.findall(util.nspath_eval(
@@ -688,25 +715,32 @@ class MD_Distribution(object):
688715
def __init__(self, md=None):
689716
if md is None:
690717
self.format = None
718+
self.format_url = None
691719
self.version = None
720+
self.version_url = None
721+
self.specification = None
722+
self.specification_url = None
692723
self.distributor = []
693724
self.online = []
694725
pass
695726
else:
696-
val = md.find(util.nspath_eval(
697-
'gmd:distributionFormat/gmd:MD_Format/gmd:name/gco:CharacterString', namespaces))
698-
self.format = util.testXMLValue(val)
727+
frm = testFirstCharOrAnchor(md,'gmd:distributionFormat/gmd:MD_Format/gmd:name')
728+
self.format = frm['name']
729+
self.format_url = frm['url']
730+
731+
vrs = testFirstCharOrAnchor(md,'gmd:distributionFormat/gmd:MD_Format/gmd:version')
732+
self.version = vrs['name']
733+
self.version_url = vrs['url']
699734

700-
val = md.find(util.nspath_eval(
701-
'gmd:distributionFormat/gmd:MD_Format/gmd:version/gco:CharacterString', namespaces))
702-
self.version = util.testXMLValue(val)
735+
spc = testFirstCharOrAnchor(md,'gmd:distributionFormat/gmd:MD_Format/gmd:specification')
736+
self.specification = spc['name']
737+
self.specification_url = spc['url']
703738

704739
self.distributor = []
705740
for dist in md.findall(util.nspath_eval('gmd:distributor', namespaces)):
706741
self.distributor.append(MD_Distributor(dist))
707742

708743
self.online = []
709-
710744
for ol in md.findall(util.nspath_eval(
711745
'gmd:transferOptions/gmd:MD_DigitalTransferOptions/gmd:onLine/gmd:CI_OnlineResource',
712746
namespaces)):
@@ -718,6 +752,7 @@ class DQ_DataQuality(object):
718752
def __init__(self, md=None):
719753
if md is None:
720754
self.conformancetitle = []
755+
self.conformancetitle_url = []
721756
self.conformancedate = []
722757
self.conformancedatetype = []
723758
self.conformancedegree = []
@@ -726,13 +761,9 @@ def __init__(self, md=None):
726761
self.specificationtitle = None
727762
self.specificationdate = []
728763
else:
729-
self.conformancetitle = []
730-
for i in md.findall(util.nspath_eval(
731-
'gmd:report/gmd:DQ_DomainConsistency/gmd:result/gmd:DQ_ConformanceResult/gmd:specification/gmd:CI_Citation/gmd:title/gco:CharacterString',
732-
namespaces)):
733-
val = util.testXMLValue(i)
734-
if val is not None:
735-
self.conformancetitle.append(val)
764+
cts = testAllCharOrAnchor(md,'gmd:report/gmd:DQ_DomainConsistency/gmd:result/gmd:DQ_ConformanceResult/gmd:specification/gmd:CI_Citation/gmd:title', False)
765+
self.conformancetitle = cts['name']
766+
self.conformancetitle_url = cts['url']
736767

737768
self.conformancedate = []
738769
for i in md.findall(util.nspath_eval(
@@ -758,14 +789,9 @@ def __init__(self, md=None):
758789
if val is not None:
759790
self.conformancedegree.append(val)
760791

761-
val = md.find(util.nspath_eval(
762-
'gmd:lineage/gmd:LI_Lineage/gmd:statement/gco:CharacterString', namespaces))
763-
self.lineage = util.testXMLValue(val)
764-
765-
val = md.find(util.nspath_eval('gmd:lineage/gmd:LI_Lineage/gmd:statement/gmx:Anchor', namespaces))
766-
if val is not None:
767-
self.lineage = util.testXMLValue(val)
768-
self.lineage_url = val.attrib.get(util.nspath_eval('xlink:href', namespaces))
792+
lng = testFirstCharOrAnchor(md, 'gmd:lineage/gmd:LI_Lineage/gmd:statement')
793+
self.lineage = lng['name']
794+
self.lineage_url = lng['url']
769795

770796
val = md.find(util.nspath_eval(
771797
'gmd:report/gmd:DQ_DomainConsistency/gmd:result/gmd:DQ_ConformanceResult/gmd:specification/gmd:CI_Citation/gmd:title/gco:CharacterString',
@@ -843,18 +869,33 @@ def __init__(self, md=None):
843869
if md is None:
844870
self.url = None
845871
self.protocol = None
872+
self.protocol_url = None
846873
self.name = None
874+
self.name_url = None
847875
self.description = None
876+
self.description_url = None
848877
self.function = None
878+
self.applicationprofile = None
879+
self.applicationprofile_url = None
849880
else:
850881
val = md.find(util.nspath_eval('gmd:linkage/gmd:URL', namespaces))
851882
self.url = util.testXMLValue(val)
852883

853-
val = md.find(util.nspath_eval('gmd:protocol/gco:CharacterString', namespaces))
854-
self.protocol = util.testXMLValue(val)
884+
val = testFirstCharOrAnchor(md,'gmd:protocol')
885+
self.protocol = val['name']
886+
self.protocol_url = val['url']
855887

856-
val = md.find(util.nspath_eval('gmd:name/gco:CharacterString', namespaces))
857-
self.name = util.testXMLValue(val)
888+
val = testFirstCharOrAnchor(md,'gmd:name')
889+
self.name = val['name']
890+
self.name_url = val['url']
891+
892+
val = testFirstCharOrAnchor(md,'gmd:description')
893+
self.description = val['name']
894+
self.description_url = val['url']
895+
896+
val = testFirstCharOrAnchor(md,'gmd:applicationProfile')
897+
self.applicationprofile = val['name']
898+
self.applicationprofile_url = val['url']
858899

859900
val = md.find(util.nspath_eval('gmd:description/gco:CharacterString', namespaces))
860901
self.description = util.testXMLValue(val)
@@ -954,33 +995,23 @@ class MD_ReferenceSystem(object):
954995
def __init__(self, md=None):
955996
if md is None:
956997
self.code = None
998+
self.code_url = None
957999
self.codeSpace = None
1000+
self.codeSpace_url = None
9581001
self.version = None
1002+
self.version_url = None
9591003
else:
960-
val = md.find(util.nspath_eval(
961-
'gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:code/gco:CharacterString', namespaces))
962-
if val is None:
963-
val = md.find(util.nspath_eval(
964-
'gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:code/gmx:Anchor', namespaces))
965-
if val is not None:
966-
self.code = util.testXMLValue(val)
967-
else:
968-
self.code = None
1004+
val = testFirstCharOrAnchor(md,'gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:code')
1005+
self.code_url = val['url']
1006+
self.code = val['name']
9691007

970-
val = md.find(util.nspath_eval(
971-
'gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:codeSpace/gco:CharacterString', namespaces))
972-
if val is not None:
973-
self.codeSpace = util.testXMLValue(val)
974-
else:
975-
self.codeSpace = None
976-
977-
val = md.find(util.nspath_eval(
978-
'gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:version/gco:CharacterString', namespaces))
979-
if val is not None:
980-
self.version = util.testXMLValue(val)
981-
else:
982-
self.version = None
1008+
val = testFirstCharOrAnchor(md,'gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:codeSpace')
1009+
self.codeSpace_url = val['url']
1010+
self.codeSpace = val['name']
9831011

1012+
val = testFirstCharOrAnchor(md,'gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:version')
1013+
self.version_url = val['url']
1014+
self.version = val['name']
9841015

9851016
def _testCodeListValue(elpath):
9861017
""" get gco:CodeListValue_Type attribute, else get text content """

tests/test_iso_parsing.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def test_md_parsing_dov():
138138
assert_list(iden.classification, 0)
139139

140140
assert_list(iden.otherconstraints, 2)
141-
assert iden.otherconstraints[
141+
assert iden.otherconstraints_url[
142142
1] == "https://inspire.ec.europa.eu/metadata-codelist/ConditionsApplyingToAccessAndUse/noConditionsApply"
143143
assert iden.otherconstraints[
144144
0] == "Data beschikbaar voor hergebruik volgens de " \
@@ -611,7 +611,37 @@ def test_md_indentifier_anchor():
611611
md = MD_Metadata(md_resource)
612612
assert type(md) is MD_Metadata
613613
assert md.referencesystem.code == 'ETRS89-GRS80'
614+
assert md.referencesystem.code_url == 'http://www.opengis.net/def/crs/EPSG/0/4937'
615+
614616
iden = md.identification[0]
615617
assert_list(iden.uricode, 1)
616618
assert iden.uricode[0] == 'https://www.nationaalgeoregister.nl/geonetwork/srv/metadata/f44dac86-2228-412f-8355-e56446ca9933'
617-
619+
assert iden.contact[0].organization_url == 'http://standaarden.overheid.nl/owms/terms/Ministerie_van_Defensie'
620+
assert iden.keywords[0].keywords[0].url == 'http://www.eionet.europa.eu/gemet/nl/inspire-theme/am'
621+
assert_list(iden.otherconstraints, 3)
622+
assert_list(iden.otherconstraints_url, 3)
623+
assert iden.otherconstraints[0] == 'Geen beperkingen'
624+
assert iden.otherconstraints_url[0] == 'http://creativecommons.org/publicdomain/mark/1.0/deed.nl'
625+
assert iden.otherconstraints[2] == 'Geen beperkingen voor publieke toegang'
626+
assert iden.otherconstraints_url[2] == 'http://inspire.ec.europa.eu/metadata-codelist/LimitationsOnPublicAccess/noLimitations'
627+
628+
dist = md.distribution
629+
assert dist.format_url == 'http://www.iana.org/assignments/media-types/application/gml+xml'
630+
assert dist.format == 'gml+xml'
631+
assert dist.version == 'GML, version 3.2.1'
632+
assert dist.specification_url == 'http://inspire.ec.europa.eu/id/document/tg/hy'
633+
assert dist.specification == 'Data specificatie hydrografie'
634+
635+
assert dist.online[0].protocol == 'OGC:WMS'
636+
assert dist.online[0].protocol_url == 'http://www.opengis.net/def/serviceType/ogc/wms'
637+
assert dist.online[0].applicationprofile == 'view'
638+
assert dist.online[0].applicationprofile_url == 'http://inspire.ec.europa.eu/metadata-codelist/SpatialDataServiceType/view'
639+
640+
assert dist.online[2].protocol == 'INSPIRE Atom'
641+
assert dist.online[2].protocol_url == 'https://tools.ietf.org/html/rfc4287'
642+
assert dist.online[2].applicationprofile == 'download'
643+
assert dist.online[2].applicationprofile_url == 'http://inspire.ec.europa.eu/metadata-codelist/SpatialDataServiceType/download'
644+
645+
assert md.dataquality.lineage == 'Ministerie van Defensie, Koninklijke Marine, Dienst der Hydrografie'
646+
assert md.dataquality.conformancetitle[0] == 'VERORDENING (EU) Nr. 1089/2010 VAN DE COMMISSIE van 23 november 2010 ter uitvoering van Richtlijn 2007/2/EG van het Europees Parlement en de Raad betreffende de interoperabiliteit van verzamelingen ruimtelijke gegevens en van diensten met betrekking tot ruimtelijke gegevens'
647+
assert md.dataquality.conformancedegree[0] == 'true'

0 commit comments

Comments
 (0)