Skip to content

Commit b556ed5

Browse files
authoredSep 20, 2024··
Support for ISO 19115 Part 3 XML (#933)
* Added initial code to ingest ISO 19115-3 XML * Add ISO 19115-3 XML plugin * Remove old files * Rename profile * Add support for output of vertical extent and funder * Fix bugs in output code * Fix incorrect namespace issue * Get address info from contacts when GetCapabilities not supplied * Add funder parsing, fix vert extent path * Add initial tests * Fix numerous path bugs * Fix errors in constraints and spatial resolution * Allowed anchor tag for identifiers * Updated processing of image description and bands * Add functional tests * Add funder column to db * Fix error im gml namespace path * Add mdb unit test * Cleaning up and adding comments * Add mdb function comments * Add db columns for vert extent * Add function comments to profile code * Fix vert extent and gml paths in tests * Fix services bugs and add tests * Update functional tests for new mdb XML test record * Update comments and readme for ISO 19115 Part 3 XML * Add mdb transaction tests * Fixes: - Missing contact name in converted records - Add more tests * Update due to functionality moved to OWSLib * Updated tests and modifications for changed config format * Update expected test responses after OWSLib funder changes * Restore correct version of cite.db * Remove cite.db from pull request * Remove unnecessary trailing comma in setup.py * Renamed func. test suites from 'mdb' to 'iso19115p3' * Remove 'Funder' field and tests * Correct the profile name in the iso19115p3 test suite * Update iso19115p3 func. tests for profile name change
1 parent 91353f4 commit b556ed5

File tree

157 files changed

+26197
-25
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

157 files changed

+26197
-25
lines changed
 

‎pycsw/core/config.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ def __init__(self, prefix='csw30'):
8282
'gmd': 'http://www.isotc211.org/2005/gmd',
8383
'gml': 'http://www.opengis.net/gml',
8484
'gml32': 'http://www.opengis.net/gml/3.2',
85+
'mdb': 'http://standards.iso.org/iso/19115/-3/mdb/2.0',
8586
'ogc': 'http://www.opengis.net/ogc',
8687
'os': 'http://a9.com/-/spec/opensearch/1.1/',
8788
'ows': 'http://www.opengis.net/ows',
@@ -118,7 +119,7 @@ def __init__(self, prefix='csw30'):
118119
'pycsw:Metadata': 'metadata',
119120
# raw metadata payload type, xml as default for now
120121
'pycsw:MetadataType': 'metadata_type',
121-
# bag of metadata element and attributes ONLY, no XML tages
122+
# bag of metadata element and attributes ONLY, no XML tags
122123
'pycsw:AnyText': 'anytext',
123124
'pycsw:Language': 'language',
124125
'pycsw:Title': 'title',
@@ -134,6 +135,8 @@ def __init__(self, prefix='csw30'):
134135
'pycsw:Type': 'type',
135136
# geometry, specified in OGC WKT
136137
'pycsw:BoundingBox': 'wkt_geometry',
138+
'pycsw:VertExtentMin': 'vert_extent_min',
139+
'pycsw:VertExtentMax': 'vert_extent_max',
137140
'pycsw:CRS': 'crs',
138141
'pycsw:AlternateTitle': 'title_alternate',
139142
'pycsw:RevisionDate': 'date_revision',

‎pycsw/core/metadata.py

+37-15
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ def _parse_metadata(context, repos, record):
162162
return [_parse_dc(context, repos, exml)]
163163
elif root == '{%s}DIF' % context.namespaces['dif']: # DIF
164164
pass # TODO
165+
elif root == '{%s}MD_Metadata' % context.namespaces['mdb']:
166+
# ISO 19115p3 XML
167+
return [_parse_iso(context, repos, exml)]
165168
else:
166169
raise RuntimeError('Unsupported metadata format')
167170

@@ -1364,23 +1367,29 @@ def get_value_by_language(pt_group, language, pt_type='text'):
13641367
return recobj
13651368

13661369
def _parse_iso(context, repos, exml):
1370+
""" Parses ISO 19139, ISO 19115p3 """
13671371

13681372
from owslib.iso import MD_ImageDescription, MD_Metadata, SV_ServiceIdentification
13691373
from owslib.iso_che import CHE_MD_Metadata
13701374

13711375
recobj = repos.dataset()
13721376
bbox = None
13731377
links = []
1378+
mdmeta_ns = 'gmd'
13741379

13751380
if exml.tag == '{http://www.geocat.ch/2008/che}CHE_MD_Metadata':
13761381
md = CHE_MD_Metadata(exml)
1382+
elif exml.tag == '{http://standards.iso.org/iso/19115/-3/mdb/2.0}MD_Metadata':
1383+
from owslib.iso3 import MD_Metadata
1384+
md = MD_Metadata(exml)
1385+
mdmeta_ns = 'mdb'
13771386
else:
13781387
md = MD_Metadata(exml)
13791388

13801389
md_identification = md.identification[0]
13811390

13821391
_set(context, recobj, 'pycsw:Identifier', md.identifier)
1383-
_set(context, recobj, 'pycsw:Typename', 'gmd:MD_Metadata')
1392+
_set(context, recobj, 'pycsw:Typename', f'{mdmeta_ns}:MD_Metadata')
13841393
_set(context, recobj, 'pycsw:Schema', context.namespaces['gmd'])
13851394
_set(context, recobj, 'pycsw:MdSource', 'local')
13861395
_set(context, recobj, 'pycsw:InsertDate', util.get_today_and_now())
@@ -1394,7 +1403,7 @@ def _parse_iso(context, repos, exml):
13941403
_set(context, recobj, 'pycsw:Modified', md.datestamp)
13951404
_set(context, recobj, 'pycsw:Source', md.dataseturi)
13961405

1397-
if md.referencesystem is not None:
1406+
if md.referencesystem is not None and md.referencesystem.code is not None:
13981407
try:
13991408
code_ = 'urn:ogc:def:crs:EPSG::%d' % int(md.referencesystem.code)
14001409
except ValueError:
@@ -1421,11 +1430,21 @@ def _parse_iso(context, repos, exml):
14211430
elif len(md_identification.resourcelanguagecode) > 0:
14221431
_set(context, recobj, 'pycsw:ResourceLanguage', md_identification.resourcelanguagecode[0])
14231432

1433+
# Geographic bounding box
14241434
if hasattr(md_identification, 'bbox'):
14251435
bbox = md_identification.bbox
14261436
else:
14271437
bbox = None
14281438

1439+
# Vertical extent of a bounding box
1440+
if hasattr(md_identification, 'extent'):
1441+
if hasattr(md_identification.extent, 'vertExtMin') and \
1442+
md_identification.extent.vertExtMin is not None:
1443+
_set(context, recobj, 'pycsw:VertExtentMin', md_identification.extent.vertExtMin)
1444+
if hasattr(md_identification.extent, 'vertExtMax') and \
1445+
md_identification.extent.vertExtMax is not None:
1446+
_set(context, recobj, 'pycsw:VertExtentMax', md_identification.extent.vertExtMax)
1447+
14291448
if (hasattr(md_identification, 'keywords') and
14301449
len(md_identification.keywords) > 0):
14311450
all_keywords = [item for sublist in md_identification.keywords for item in sublist.keywords if item is not None]
@@ -1435,14 +1454,17 @@ def _parse_iso(context, repos, exml):
14351454
json.dumps([t for t in md_identification.keywords if t.thesaurus is not None],
14361455
default=lambda o: o.__dict__))
14371456

1457+
# Creator
14381458
if (hasattr(md_identification, 'creator') and
14391459
len(md_identification.creator) > 0):
14401460
all_orgs = set([item.organization for item in md_identification.creator if hasattr(item, 'organization') and item.organization is not None])
14411461
_set(context, recobj, 'pycsw:Creator', ';'.join(all_orgs))
1462+
# Publisher
14421463
if (hasattr(md_identification, 'publisher') and
14431464
len(md_identification.publisher) > 0):
14441465
all_orgs = set([item.organization for item in md_identification.publisher if hasattr(item, 'organization') and item.organization is not None])
14451466
_set(context, recobj, 'pycsw:Publisher', ';'.join(all_orgs))
1467+
# Contributor
14461468
if (hasattr(md_identification, 'contributor') and
14471469
len(md_identification.contributor) > 0):
14481470
all_orgs = set([item.organization for item in md_identification.contributor if hasattr(item, 'organization') and item.organization is not None])
@@ -1527,18 +1549,18 @@ def _parse_iso(context, repos, exml):
15271549

15281550
_set(context, recobj, 'pycsw:Keywords', keywords)
15291551

1530-
bands = []
1531-
for band in ci.bands:
1532-
band_info = {
1533-
'id': band.id,
1534-
'units': band.units,
1535-
'min': band.min,
1536-
'max': band.max
1537-
}
1538-
bands.append(band_info)
1552+
bands = []
1553+
for band in ci.bands:
1554+
band_info = {
1555+
'id': band.id,
1556+
'units': band.units,
1557+
'min': band.min,
1558+
'max': band.max
1559+
}
1560+
bands.append(band_info)
15391561

1540-
if len(bands) > 0:
1541-
_set(context, recobj, 'pycsw:Bands', json.dumps(bands))
1562+
if len(bands) > 0:
1563+
_set(context, recobj, 'pycsw:Bands', json.dumps(bands))
15421564

15431565
if hasattr(md, 'acquisition') and md.acquisition is not None:
15441566
platform = md.acquisition.platforms[0]
@@ -1557,10 +1579,10 @@ def _parse_iso(context, repos, exml):
15571579
if hasattr(md, 'distribution'):
15581580
dist_links = []
15591581
if hasattr(md.distribution, 'online'):
1560-
LOGGER.debug('Scanning for gmd:transferOptions element(s)')
1582+
LOGGER.debug(f'Scanning for {mdmeta_ns}:transferOptions element(s)')
15611583
dist_links.extend(md.distribution.online)
15621584
if hasattr(md.distribution, 'distributor'):
1563-
LOGGER.debug('Scanning for gmd:distributorTransferOptions element(s)')
1585+
LOGGER.debug(f'Scanning for {mdmeta_ns}:distributorTransferOptions element(s)')
15641586
for dist_member in md.distribution.distributor:
15651587
dist_links.extend(dist_member.online)
15661588
for link in dist_links:

0 commit comments

Comments
 (0)