1# -*- coding: utf-8 -*- 2# ================================================================= 3# 4# Authors: Tom Kralidis <tomkralidis@gmail.com> 5# Angelos Tzotsos <tzotsos@gmail.com> 6# 7# Copyright (c) 2015 Tom Kralidis 8# Copyright (c) 2015 Angelos Tzotsos 9# 10# Permission is hereby granted, free of charge, to any person 11# obtaining a copy of this software and associated documentation 12# files (the "Software"), to deal in the Software without 13# restriction, including without limitation the rights to use, 14# copy, modify, merge, publish, distribute, sublicense, and/or sell 15# copies of the Software, and to permit persons to whom the 16# Software is furnished to do so, subject to the following 17# conditions: 18# 19# The above copyright notice and this permission notice shall be 20# included in all copies or substantial portions of the Software. 21# 22# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 24# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 26# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 27# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 28# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 29# OTHER DEALINGS IN THE SOFTWARE. 30# 31# ================================================================= 32 33import os 34from pycsw.core import config, util 35from pycsw.core.etree import etree 36from pycsw.plugins.profiles import profile 37 38CODELIST = 'http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml' 39CODESPACE = 'ISOTC211/19115' 40 41class APISO(profile.Profile): 42 ''' APISO class ''' 43 def __init__(self, model, namespaces, context): 44 self.context = context 45 46 self.namespaces = { 47 'apiso': 'http://www.opengis.net/cat/csw/apiso/1.0', 48 'gco': 'http://www.isotc211.org/2005/gco', 49 'gmd': 'http://www.isotc211.org/2005/gmd', 50 'srv': 'http://www.isotc211.org/2005/srv', 51 'xlink': 'http://www.w3.org/1999/xlink' 52 } 53 54 self.inspire_namespaces = { 55 'inspire_ds': 'http://inspire.ec.europa.eu/schemas/inspire_ds/1.0', 56 'inspire_common': 'http://inspire.ec.europa.eu/schemas/common/1.0' 57 } 58 59 60 self.repository = { 61 'gmd:MD_Metadata': { 62 'outputschema': 'http://www.isotc211.org/2005/gmd', 63 'queryables': { 64 'SupportedISOQueryables': { 65 'apiso:Subject': {'xpath': 'gmd:identificationInfo/gmd:MD_Identification/gmd:descriptiveKeywords/gmd:MD_Keywords/gmd:keyword/gco:CharacterString|gmd:identificationInfo/gmd:MD_DataIdentification/gmd:topicCategory/gmd:MD_TopicCategoryCode', 'dbcol': self.context.md_core_model['mappings']['pycsw:Keywords']}, 66 'apiso:Title': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:title/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:Title']}, 67 'apiso:Abstract': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:abstract/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:Abstract']}, 68 'apiso:Format': {'xpath': 'gmd:distributionInfo/gmd:MD_Distribution/gmd:distributionFormat/gmd:MD_Format/gmd:name/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:Format']}, 69 'apiso:Identifier': {'xpath': 'gmd:fileIdentifier/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:Identifier']}, 70 'apiso:Modified': {'xpath': 'gmd:dateStamp/gco:Date', 'dbcol': self.context.md_core_model['mappings']['pycsw:Modified']}, 71 'apiso:Type': {'xpath': 'gmd:hierarchyLevel/gmd:MD_ScopeCode', 'dbcol': self.context.md_core_model['mappings']['pycsw:Type']}, 72 'apiso:BoundingBox': {'xpath': 'apiso:BoundingBox', 'dbcol': self.context.md_core_model['mappings']['pycsw:BoundingBox']}, 73 'apiso:CRS': {'xpath': 'concat("urn:ogc:def:crs:","gmd:referenceSystemInfo/gmd:MD_ReferenceSystem/gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:codeSpace/gco:CharacterString",":","gmd:referenceSystemInfo/gmd:MD_ReferenceSystem/gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:version/gco:CharacterString",":","gmd:referenceSystemInfo/gmd:MD_ReferenceSystem/gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:code/gco:CharacterString")', 'dbcol': self.context.md_core_model['mappings']['pycsw:CRS']}, 74 'apiso:AlternateTitle': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:alternateTitle/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:AlternateTitle']}, 75 'apiso:RevisionDate': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:date/gmd:CI_Date[gmd:dateType/gmd:CI_DateTypeCode/@codeListValue="revision"]/gmd:date/gco:Date', 'dbcol': self.context.md_core_model['mappings']['pycsw:RevisionDate']}, 76 'apiso:CreationDate': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:date/gmd:CI_Date[gmd:dateType/gmd:CI_DateTypeCode/@codeListValue="creation"]/gmd:date/gco:Date', 'dbcol': self.context.md_core_model['mappings']['pycsw:CreationDate']}, 77 'apiso:PublicationDate': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:date/gmd:CI_Date[gmd:dateType/gmd:CI_DateTypeCode/@codeListValue="publication"]/gmd:date/gco:Date', 'dbcol': self.context.md_core_model['mappings']['pycsw:PublicationDate']}, 78 'apiso:OrganisationName': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:pointOfContact/gmd:CI_ResponsibleParty/gmd:organisationName/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:OrganizationName']}, 79 'apiso:HasSecurityConstraints': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:resourceConstraints/gmd:MD_SecurityConstraints', 'dbcol': self.context.md_core_model['mappings']['pycsw:SecurityConstraints']}, 80 'apiso:Language': {'xpath': 'gmd:language/gmd:LanguageCode|gmd:language/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:Language']}, 81 'apiso:ParentIdentifier': {'xpath': 'gmd:parentIdentifier/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:ParentIdentifier']}, 82 'apiso:KeywordType': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:descriptiveKeywords/gmd:MD_Keywords/gmd:type/gmd:MD_KeywordTypeCode', 'dbcol': self.context.md_core_model['mappings']['pycsw:KeywordType']}, 83 'apiso:TopicCategory': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:topicCategory/gmd:MD_TopicCategoryCode', 'dbcol': self.context.md_core_model['mappings']['pycsw:TopicCategory']}, 84 'apiso:ResourceLanguage': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:identifier/gmd:code/gmd:MD_LanguageTypeCode', 'dbcol': self.context.md_core_model['mappings']['pycsw:ResourceLanguage']}, 85 'apiso:GeographicDescriptionCode': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:geographicElement/gmd:EX_GeographicDescription/gmd:geographicIdentifier/gmd:MD_Identifier/gmd:code/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:GeographicDescriptionCode']}, 86 'apiso:Denominator': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:spatialResolution/gmd:MD_Resolution/gmd:equivalentScale/gmd:MD_RepresentativeFraction/gmd:denominator/gco:Integer', 'dbcol': self.context.md_core_model['mappings']['pycsw:Denominator']}, 87 'apiso:DistanceValue': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:spatialResolution/gmd:MD_Resolution/gmd:distance/gco:Distance', 'dbcol': self.context.md_core_model['mappings']['pycsw:DistanceValue']}, 88 'apiso:DistanceUOM': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:spatialResolution/gmd:MD_Resolution/gmd:distance/gco:Distance/@uom', 'dbcol': self.context.md_core_model['mappings']['pycsw:DistanceUOM']}, 89 'apiso:TempExtent_begin': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:temporalElement/gmd:EX_TemporalExtent/gmd:extent/gml:TimePeriod/gml:beginPosition', 'dbcol': self.context.md_core_model['mappings']['pycsw:TempExtent_begin']}, 90 'apiso:TempExtent_end': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:temporalElement/gmd:EX_TemporalExtent/gmd:extent/gml:TimePeriod/gml:endPosition', 'dbcol': self.context.md_core_model['mappings']['pycsw:TempExtent_end']}, 91 'apiso:AnyText': {'xpath': '//', 'dbcol': self.context.md_core_model['mappings']['pycsw:AnyText']}, 92 'apiso:ServiceType': {'xpath': 'gmd:identificationInfo/srv:SV_ServiceIdentification/srv:serviceType/gco:LocalName', 'dbcol': self.context.md_core_model['mappings']['pycsw:ServiceType']}, 93 'apiso:ServiceTypeVersion': {'xpath': 'gmd:identificationInfo/srv:SV_ServiceIdentification/srv:serviceTypeVersion/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:ServiceTypeVersion']}, 94 'apiso:Operation': {'xpath': 'gmd:identificationInfo/srv:SV_ServiceIdentification/srv:containsOperations/srv:SV_OperationMetadata/srv:operationName/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:Operation']}, 95 'apiso:CouplingType': {'xpath': 'gmd:identificationInfo/srv:SV_ServiceIdentification/srv:couplingType/srv:SV_CouplingType', 'dbcol': self.context.md_core_model['mappings']['pycsw:CouplingType']}, 96 'apiso:OperatesOn': {'xpath': 'gmd:identificationInfo/srv:SV_ServiceIdentification/srv:operatesOn/gmd:MD_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:identifier/gmd:RS_Identifier/gmd:code/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:OperatesOn']}, 97 'apiso:OperatesOnIdentifier': {'xpath': 'gmd:identificationInfo/srv:SV_ServiceIdentification/srv:coupledResource/srv:SV_CoupledResource/srv:identifier/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:OperatesOnIdentifier']}, 98 'apiso:OperatesOnName': {'xpath': 'gmd:identificationInfo/srv:SV_ServiceIdentification/srv:coupledResource/srv:SV_CoupledResource/srv:operationName/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:OperatesOnName']}, 99 }, 100 'AdditionalQueryables': { 101 'apiso:Degree': {'xpath': 'gmd:dataQualityInfo/gmd:DQ_DataQuality/gmd:report/gmd:DQ_DomainConsistency/gmd:result/gmd:DQ_ConformanceResult/gmd:pass/gco:Boolean', 'dbcol': self.context.md_core_model['mappings']['pycsw:Degree']}, 102 'apiso:AccessConstraints': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:resourceConstraints/gmd:MD_LegalConstraints/gmd:accessConstraints/gmd:MD_RestrictionCode', 'dbcol': self.context.md_core_model['mappings']['pycsw:AccessConstraints']}, 103 'apiso:OtherConstraints': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:resourceConstraints/gmd:MD_LegalConstraints/gmd:otherConstraints/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:OtherConstraints']}, 104 'apiso:Classification': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:resourceConstraints/gmd:MD_LegalConstraints/gmd:accessConstraints/gmd:MD_ClassificationCode', 'dbcol': self.context.md_core_model['mappings']['pycsw:Classification']}, 105 'apiso:ConditionApplyingToAccessAndUse': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:useLimitation/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:ConditionApplyingToAccessAndUse']}, 106 'apiso:Lineage': {'xpath': 'gmd:dataQualityInfo/gmd:DQ_DataQuality/gmd:lineage/gmd:LI_Lineage/gmd:statement/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:Lineage']}, 107 'apiso:ResponsiblePartyRole': {'xpath': 'gmd:contact/gmd:CI_ResponsibleParty/gmd:role/gmd:CI_RoleCode', 'dbcol': self.context.md_core_model['mappings']['pycsw:ResponsiblePartyRole']}, 108 'apiso:SpecificationTitle': {'xpath': 'gmd:dataQualityInfo/gmd:DQ_DataQuality/gmd:report/gmd:DQ_DomainConsistency/gmd:result/gmd:DQ_ConformanceResult/gmd:specification/gmd:CI_Citation/gmd:title/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:SpecificationTitle']}, 109 'apiso:SpecificationDate': {'xpath': 'gmd:dataQualityInfo/gmd:DQ_DataQuality/gmd:report/gmd:DQ_DomainConsistency/gmd:result/gmd:DQ_ConformanceResult/gmd:specification/gmd:CI_Citation/gmd:date/gmd:CI_Date/gmd:date/gco:Date', 'dbcol': self.context.md_core_model['mappings']['pycsw:SpecificationDate']}, 110 'apiso:SpecificationDateType': {'xpath': 'gmd:dataQualityInfo/gmd:DQ_DataQuality/gmd:report/gmd:DQ_DomainConsistency/gmd:result/gmd:DQ_ConformanceResult/gmd:specification/gmd:CI_Citation/gmd:date/gmd:CI_Date/gmd:dateType/gmd:CI_DateTypeCode', 'dbcol': self.context.md_core_model['mappings']['pycsw:SpecificationDateType']}, 111 'apiso:Creator': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:pointOfContact/gmd:CI_ResponsibleParty/gmd:organisationName[gmd:role/gmd:CI_RoleCode/@codeListValue="originator"]/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:Creator']}, 112 'apiso:Publisher': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:pointOfContact/gmd:CI_ResponsibleParty/gmd:organisationName[gmd:role/gmd:CI_RoleCode/@codeListValue="publisher"]/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:Publisher']}, 113 'apiso:Contributor': {'xpath': 'gmd:identificationInfo/gmd:MD_DataIdentification/gmd:pointOfContact/gmd:CI_ResponsibleParty/gmd:organisationName[gmd:role/gmd:CI_RoleCode/@codeListValue="contributor"]/gco:CharacterString', 'dbcol': self.context.md_core_model['mappings']['pycsw:Contributor']}, 114 'apiso:Relation': {'xpath': 'gmd:identificationInfo/gmd:MD_Data_Identification/gmd:aggregationInfo', 'dbcol': self.context.md_core_model['mappings']['pycsw:Relation']} 115 } 116 }, 117 'mappings': { 118 'csw:Record': { 119 # map APISO queryables to DC queryables 120 'apiso:Title': 'dc:title', 121 'apiso:Creator': 'dc:creator', 122 'apiso:Subject': 'dc:subject', 123 'apiso:Abstract': 'dct:abstract', 124 'apiso:Publisher': 'dc:publisher', 125 'apiso:Contributor': 'dc:contributor', 126 'apiso:Modified': 'dct:modified', 127 #'apiso:Date': 'dc:date', 128 'apiso:Type': 'dc:type', 129 'apiso:Format': 'dc:format', 130 'apiso:Language': 'dc:language', 131 'apiso:Relation': 'dc:relation', 132 'apiso:AccessConstraints': 'dc:rights', 133 } 134 } 135 } 136 } 137 138 profile.Profile.__init__(self, 139 name='apiso', 140 version='1.0.0', 141 title='ISO Metadata Application Profile', 142 url='http://portal.opengeospatial.org/files/?artifact_id=21460', 143 namespace=self.namespaces['gmd'], 144 typename='gmd:MD_Metadata', 145 outputschema=self.namespaces['gmd'], 146 prefixes=['apiso', 'gmd'], 147 model=model, 148 core_namespaces=namespaces, 149 added_namespaces=self.namespaces, 150 repository=self.repository['gmd:MD_Metadata']) 151 152 def extend_core(self, model, namespaces, config): 153 ''' Extend core configuration ''' 154 155 # update INSPIRE vars 156 self.context.namespaces.update(self.inspire_namespaces) 157 158 # update harvest resource types with WMS, since WMS is not a typename, 159 if 'Harvest' in model['operations']: 160 model['operations']['Harvest']['parameters']['ResourceType']['values'].append('http://www.isotc211.org/schemas/2005/gmd/') 161 162 # set INSPIRE config 163 if config.has_section('metadata:inspire') and config.has_option('metadata:inspire', 'enabled') and config.get('metadata:inspire', 'enabled') == 'true': 164 self.inspire_config = {} 165 self.inspire_config['languages_supported'] = config.get('metadata:inspire', 'languages_supported') 166 self.inspire_config['default_language'] = config.get('metadata:inspire', 'default_language') 167 self.inspire_config['date'] = config.get('metadata:inspire', 'date') 168 self.inspire_config['gemet_keywords'] = config.get('metadata:inspire', 'gemet_keywords') 169 self.inspire_config['conformity_service'] = config.get('metadata:inspire', 'conformity_service') 170 self.inspire_config['contact_name'] = config.get('metadata:inspire', 'contact_name') 171 self.inspire_config['contact_email'] = config.get('metadata:inspire', 'contact_email') 172 self.inspire_config['temp_extent'] = config.get('metadata:inspire', 'temp_extent') 173 else: 174 self.inspire_config = None 175 176 self.ogc_schemas_base = config.get('server', 'ogc_schemas_base') 177 self.url = config.get('server', 'url') 178 179 def check_parameters(self, kvp): 180 '''Check for Language parameter in GetCapabilities request''' 181 182 if self.inspire_config is not None: 183 result = None 184 if 'language' not in kvp: 185 self.inspire_config['current_language'] = self.inspire_config['default_language'] 186 else: 187 if kvp['language'] not in self.inspire_config['languages_supported'].split(','): 188 text = 'Requested Language not supported, Supported languages: %s' % self.inspire_config['languages_supported'] 189 return {'error': 'true', 'locator': 'language', 'code': 'InvalidParameterValue', 'text': text} 190 else: 191 self.inspire_config['current_language'] = kvp['language'] 192 return None 193 return None 194 return None 195 196 def get_extendedcapabilities(self): 197 ''' Add child to ows:OperationsMetadata Element ''' 198 199 if self.inspire_config is not None: 200 201 ex_caps = etree.Element( 202 util.nspath_eval('inspire_ds:ExtendedCapabilities', self.inspire_namespaces)) 203 204 ex_caps.attrib[util.nspath_eval('xsi:schemaLocation', self.context.namespaces)] = \ 205 '%s %s/inspire_ds.xsd' % \ 206 (self.inspire_namespaces['inspire_ds'], self.inspire_namespaces['inspire_ds']) 207 208 # Resource Locator 209 res_loc = etree.SubElement(ex_caps, 210 util.nspath_eval('inspire_common:ResourceLocator', self.inspire_namespaces)) 211 212 etree.SubElement(res_loc, 213 util.nspath_eval('inspire_common:URL', self.inspire_namespaces)).text = '%sservice=CSW&version=2.0.2&request=GetCapabilities' % (util.bind_url(self.url)) 214 215 etree.SubElement(res_loc, 216 util.nspath_eval('inspire_common:MediaType', self.inspire_namespaces)).text = 'application/xml' 217 218 # Resource Type 219 etree.SubElement(ex_caps, 220 util.nspath_eval('inspire_common:ResourceType', self.inspire_namespaces)).text = 'service' 221 222 # Temporal Reference 223 temp_ref = etree.SubElement(ex_caps, 224 util.nspath_eval('inspire_common:TemporalReference', self.inspire_namespaces)) 225 226 temp_extent = etree.SubElement(temp_ref, 227 util.nspath_eval('inspire_common:TemporalExtent', self.inspire_namespaces)) 228 229 val = self.inspire_config['temp_extent'].split('/') 230 231 if len(val) == 1: 232 etree.SubElement(temp_extent, 233 util.nspath_eval('inspire_common:IndividualDate', self.inspire_namespaces)).text = val[0] 234 235 else: 236 interval_dates = etree.SubElement(temp_extent, 237 util.nspath_eval('inspire_common:IntervalOfDates', self.inspire_namespaces)) 238 239 etree.SubElement(interval_dates, 240 util.nspath_eval('inspire_common:StartingDate', self.inspire_namespaces)).text = val[0] 241 242 etree.SubElement(interval_dates, 243 util.nspath_eval('inspire_common:EndDate', self.inspire_namespaces)).text = val[1] 244 245 # Conformity - service 246 cfm = etree.SubElement(ex_caps, 247 util.nspath_eval('inspire_common:Conformity', self.inspire_namespaces)) 248 249 spec = etree.SubElement(cfm, 250 util.nspath_eval('inspire_common:Specification', self.inspire_namespaces)) 251 252 spec.attrib[util.nspath_eval('xsi:type', self.context.namespaces)] = 'inspire_common:citationInspireInteroperabilityRegulation_eng' 253 254 etree.SubElement(spec, 255 util.nspath_eval('inspire_common:Title', self.inspire_namespaces)).text = 'COMMISSION REGULATION (EU) No 1089/2010 of 23 November 2010 implementing Directive 2007/2/EC of the European Parliament and of the Council as regards interoperability of spatial data sets and services' 256 257 etree.SubElement(spec, 258 util.nspath_eval('inspire_common:DateOfPublication', self.inspire_namespaces)).text = '2010-12-08' 259 260 etree.SubElement(spec, 261 util.nspath_eval('inspire_common:URI', self.inspire_namespaces)).text = 'OJ:L:2010:323:0011:0102:EN:PDF' 262 263 spec_loc = etree.SubElement(spec, 264 util.nspath_eval('inspire_common:ResourceLocator', self.inspire_namespaces)) 265 266 etree.SubElement(spec_loc, 267 util.nspath_eval('inspire_common:URL', self.inspire_namespaces)).text = 'http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=OJ:L:2010:323:0011:0102:EN:PDF' 268 269 etree.SubElement(spec_loc, 270 util.nspath_eval('inspire_common:MediaType', self.inspire_namespaces)).text = 'application/pdf' 271 272 spec = etree.SubElement(cfm, 273 util.nspath_eval('inspire_common:Degree', self.inspire_namespaces)).text = self.inspire_config['conformity_service'] 274 275 # Metadata Point of Contact 276 poc = etree.SubElement(ex_caps, 277 util.nspath_eval('inspire_common:MetadataPointOfContact', self.inspire_namespaces)) 278 279 etree.SubElement(poc, 280 util.nspath_eval('inspire_common:OrganisationName', self.inspire_namespaces)).text = self.inspire_config['contact_name'] 281 282 etree.SubElement(poc, 283 util.nspath_eval('inspire_common:EmailAddress', self.inspire_namespaces)).text = self.inspire_config['contact_email'] 284 285 # Metadata Date 286 etree.SubElement(ex_caps, 287 util.nspath_eval('inspire_common:MetadataDate', self.inspire_namespaces)).text = self.inspire_config['date'] 288 289 # Spatial Data Service Type 290 etree.SubElement(ex_caps, 291 util.nspath_eval('inspire_common:SpatialDataServiceType', self.inspire_namespaces)).text = 'discovery' 292 293 # Mandatory Keyword 294 mkey = etree.SubElement(ex_caps, 295 util.nspath_eval('inspire_common:MandatoryKeyword', self.inspire_namespaces)) 296 297 mkey.attrib[util.nspath_eval('xsi:type', self.context.namespaces)] = 'inspire_common:classificationOfSpatialDataService' 298 299 etree.SubElement(mkey, 300 util.nspath_eval('inspire_common:KeywordValue', self.inspire_namespaces)).text = 'infoCatalogueService' 301 302 # Gemet Keywords 303 304 for gkw in self.inspire_config['gemet_keywords'].split(','): 305 gkey = etree.SubElement(ex_caps, 306 util.nspath_eval('inspire_common:Keyword', self.inspire_namespaces)) 307 308 gkey.attrib[util.nspath_eval('xsi:type', self.context.namespaces)] = 'inspire_common:inspireTheme_eng' 309 310 ocv = etree.SubElement(gkey, 311 util.nspath_eval('inspire_common:OriginatingControlledVocabulary', self.inspire_namespaces)) 312 313 etree.SubElement(ocv, 314 util.nspath_eval('inspire_common:Title', self.inspire_namespaces)).text = 'GEMET - INSPIRE themes' 315 316 etree.SubElement(ocv, 317 util.nspath_eval('inspire_common:DateOfPublication', self.inspire_namespaces)).text = '2008-06-01' 318 319 etree.SubElement(gkey, 320 util.nspath_eval('inspire_common:KeywordValue', self.inspire_namespaces)).text = gkw 321 322 # Languages 323 slang = etree.SubElement(ex_caps, 324 util.nspath_eval('inspire_common:SupportedLanguages', self.inspire_namespaces)) 325 326 dlang = etree.SubElement(slang, 327 util.nspath_eval('inspire_common:DefaultLanguage', self.inspire_namespaces)) 328 329 etree.SubElement(dlang, 330 util.nspath_eval('inspire_common:Language', self.inspire_namespaces)).text = self.inspire_config['default_language'] 331 332 for l in self.inspire_config['languages_supported'].split(','): 333 lang = etree.SubElement(slang, 334 util.nspath_eval('inspire_common:SupportedLanguage', self.inspire_namespaces)) 335 336 etree.SubElement(lang, 337 util.nspath_eval('inspire_common:Language', self.inspire_namespaces)).text = l 338 339 clang = etree.SubElement(ex_caps, 340 util.nspath_eval('inspire_common:ResponseLanguage', self.inspire_namespaces)) 341 etree.SubElement(clang, 342 util.nspath_eval('inspire_common:Language', self.inspire_namespaces)).text = self.inspire_config['current_language'] 343 344 return ex_caps 345 346 def get_schemacomponents(self): 347 ''' Return schema components as lxml.etree.Element list ''' 348 349 node1 = etree.Element( 350 util.nspath_eval('csw:SchemaComponent', self.context.namespaces), 351 schemaLanguage='XMLSCHEMA', targetNamespace=self.namespace, 352 parentSchema='gmd.xsd') 353 354 schema_file = os.path.join(self.context.pycsw_home, 'plugins', 355 'profiles', 'apiso', 'schemas', 'ogc', 356 'iso', '19139', '20060504', 'gmd', 357 'identification.xsd') 358 359 schema = etree.parse(schema_file, self.context.parser).getroot() 360 361 node1.append(schema) 362 363 node2 = etree.Element( 364 util.nspath_eval('csw:SchemaComponent', self.context.namespaces), 365 schemaLanguage='XMLSCHEMA', targetNamespace=self.namespace, 366 parentSchema='gmd.xsd') 367 368 schema_file = os.path.join(self.context.pycsw_home, 'plugins', 369 'profiles', 'apiso', 'schemas', 'ogc', 370 'iso', '19139', '20060504', 'srv', 371 'serviceMetadata.xsd') 372 373 schema = etree.parse(schema_file, self.context.parser).getroot() 374 375 node2.append(schema) 376 377 return [node1, node2] 378 379 def check_getdomain(self, kvp): 380 '''Perform extra profile specific checks in the GetDomain request''' 381 return None 382 383 def write_record(self, result, esn, outputschema, queryables, caps=None): 384 ''' Return csw:SearchResults child as lxml.etree.Element ''' 385 typename = util.getqattr(result, self.context.md_core_model['mappings']['pycsw:Typename']) 386 is_iso_anyway = False 387 388 xml_blob = util.getqattr(result, self.context.md_core_model['mappings']['pycsw:XML']) 389 390 #xml_blob_decoded = bytes.fromhex(xml_blob[2:]).decode('utf-8') 391 392 if isinstance(xml_blob, bytes): 393 iso_string = b'<gmd:MD_Metadata>' 394 else: 395 iso_string = '<gmd:MD_Metadata>' 396 397 if caps is None and xml_blob is not None and xml_blob.startswith(iso_string): 398 is_iso_anyway = True 399 400 if (esn == 'full' and (typename == 'gmd:MD_Metadata' or is_iso_anyway)): 401 # dump record as is and exit 402 return etree.fromstring(xml_blob, self.context.parser) 403 404 node = etree.Element(util.nspath_eval('gmd:MD_Metadata', self.namespaces)) 405 node.attrib[util.nspath_eval('xsi:schemaLocation', self.context.namespaces)] = \ 406 '%s %s/csw/2.0.2/profiles/apiso/1.0.0/apiso.xsd' % (self.namespace, self.ogc_schemas_base) 407 408 # identifier 409 idval = util.getqattr(result, self.context.md_core_model['mappings']['pycsw:Identifier']) 410 411 identifier = etree.SubElement(node, util.nspath_eval('gmd:fileIdentifier', self.namespaces)) 412 etree.SubElement(identifier, util.nspath_eval('gco:CharacterString', self.namespaces)).text = idval 413 414 if esn in ['summary', 'full']: 415 # language 416 val = util.getqattr(result, queryables['apiso:Language']['dbcol']) 417 418 lang = etree.SubElement(node, util.nspath_eval('gmd:language', self.namespaces)) 419 etree.SubElement(lang, util.nspath_eval('gco:CharacterString', self.namespaces)).text = val 420 421 # hierarchyLevel 422 mtype = util.getqattr(result, queryables['apiso:Type']['dbcol']) or None 423 424 if mtype is not None: 425 if mtype == 'http://purl.org/dc/dcmitype/Dataset': 426 mtype = 'dataset' 427 hierarchy = etree.SubElement(node, util.nspath_eval('gmd:hierarchyLevel', self.namespaces)) 428 hierarchy.append(_write_codelist_element('gmd:MD_ScopeCode', mtype, self.namespaces)) 429 430 if esn in ['summary', 'full']: 431 # contact 432 contact = etree.SubElement(node, util.nspath_eval('gmd:contact', self.namespaces)) 433 if caps is not None: 434 CI_resp = etree.SubElement(contact, util.nspath_eval('gmd:CI_ResponsibleParty', self.namespaces)) 435 if hasattr(caps.provider.contact, 'name'): 436 ind_name = etree.SubElement(CI_resp, util.nspath_eval('gmd:individualName', self.namespaces)) 437 etree.SubElement(ind_name, util.nspath_eval('gco:CharacterString', self.namespaces)).text = caps.provider.contact.name 438 if hasattr(caps.provider.contact, 'organization'): 439 if caps.provider.contact.organization is not None: 440 org_val = caps.provider.contact.organization 441 else: 442 org_val = caps.provider.name 443 org_name = etree.SubElement(CI_resp, util.nspath_eval('gmd:organisationName', self.namespaces)) 444 etree.SubElement(org_name, util.nspath_eval('gco:CharacterString', self.namespaces)).text = org_val 445 if hasattr(caps.provider.contact, 'position'): 446 pos_name = etree.SubElement(CI_resp, util.nspath_eval('gmd:positionName', self.namespaces)) 447 etree.SubElement(pos_name, util.nspath_eval('gco:CharacterString', self.namespaces)).text = caps.provider.contact.position 448 contact_info = etree.SubElement(CI_resp, util.nspath_eval('gmd:contactInfo', self.namespaces)) 449 ci_contact = etree.SubElement(contact_info, util.nspath_eval('gmd:CI_Contact', self.namespaces)) 450 if hasattr(caps.provider.contact, 'phone'): 451 phone = etree.SubElement(ci_contact, util.nspath_eval('gmd:phone', self.namespaces)) 452 ci_phone = etree.SubElement(phone, util.nspath_eval('gmd:CI_Telephone', self.namespaces)) 453 voice = etree.SubElement(ci_phone, util.nspath_eval('gmd:voice', self.namespaces)) 454 etree.SubElement(voice, util.nspath_eval('gco:CharacterString', self.namespaces)).text = caps.provider.contact.phone 455 if hasattr(caps.provider.contact, 'fax'): 456 fax = etree.SubElement(ci_phone, util.nspath_eval('gmd:facsimile', self.namespaces)) 457 etree.SubElement(fax, util.nspath_eval('gco:CharacterString', self.namespaces)).text = caps.provider.contact.fax 458 address = etree.SubElement(ci_contact, util.nspath_eval('gmd:address', self.namespaces)) 459 ci_address = etree.SubElement(address, util.nspath_eval('gmd:CI_Address', self.namespaces)) 460 if hasattr(caps.provider.contact, 'address'): 461 delivery_point = etree.SubElement(ci_address, util.nspath_eval('gmd:deliveryPoint', self.namespaces)) 462 etree.SubElement(delivery_point, util.nspath_eval('gco:CharacterString', self.namespaces)).text = caps.provider.contact.address 463 if hasattr(caps.provider.contact, 'city'): 464 city = etree.SubElement(ci_address, util.nspath_eval('gmd:city', self.namespaces)) 465 etree.SubElement(city, util.nspath_eval('gco:CharacterString', self.namespaces)).text = caps.provider.contact.city 466 if hasattr(caps.provider.contact, 'region'): 467 admin_area = etree.SubElement(ci_address, util.nspath_eval('gmd:administrativeArea', self.namespaces)) 468 etree.SubElement(admin_area, util.nspath_eval('gco:CharacterString', self.namespaces)).text = caps.provider.contact.region 469 if hasattr(caps.provider.contact, 'postcode'): 470 postal_code = etree.SubElement(ci_address, util.nspath_eval('gmd:postalCode', self.namespaces)) 471 etree.SubElement(postal_code, util.nspath_eval('gco:CharacterString', self.namespaces)).text = caps.provider.contact.postcode 472 if hasattr(caps.provider.contact, 'country'): 473 country = etree.SubElement(ci_address, util.nspath_eval('gmd:country', self.namespaces)) 474 etree.SubElement(country, util.nspath_eval('gco:CharacterString', self.namespaces)).text = caps.provider.contact.country 475 if hasattr(caps.provider.contact, 'email'): 476 email = etree.SubElement(ci_address, util.nspath_eval('gmd:electronicMailAddress', self.namespaces)) 477 etree.SubElement(email, util.nspath_eval('gco:CharacterString', self.namespaces)).text = caps.provider.contact.email 478 479 contact_url = None 480 if hasattr(caps.provider, 'url'): 481 contact_url = caps.provider.url 482 if hasattr(caps.provider.contact, 'url') and caps.provider.contact.url is not None: 483 contact_url = caps.provider.contact.url 484 485 if contact_url is not None: 486 online_resource = etree.SubElement(ci_contact, util.nspath_eval('gmd:onlineResource', self.namespaces)) 487 gmd_linkage = etree.SubElement(online_resource, util.nspath_eval('gmd:linkage', self.namespaces)) 488 etree.SubElement(gmd_linkage, util.nspath_eval('gmd:URL', self.namespaces)).text = contact_url 489 490 if hasattr(caps.provider.contact, 'role'): 491 role = etree.SubElement(CI_resp, util.nspath_eval('gmd:role', self.namespaces)) 492 role_val = caps.provider.contact.role 493 if role_val is None: 494 role_val = 'pointOfContact' 495 etree.SubElement(role, util.nspath_eval('gmd:CI_RoleCode', self.namespaces), codeListValue=role_val, codeList='%s#CI_RoleCode' % CODELIST).text = role_val 496 else: 497 val = util.getqattr(result, queryables['apiso:OrganisationName']['dbcol']) 498 if val: 499 CI_resp = etree.SubElement(contact, util.nspath_eval('gmd:CI_ResponsibleParty', self.namespaces)) 500 org_name = etree.SubElement(CI_resp, util.nspath_eval('gmd:organisationName', self.namespaces)) 501 etree.SubElement(org_name, util.nspath_eval('gco:CharacterString', self.namespaces)).text = val 502 503 # date 504 val = util.getqattr(result, queryables['apiso:Modified']['dbcol']) 505 date = etree.SubElement(node, util.nspath_eval('gmd:dateStamp', self.namespaces)) 506 if val and val.find('T') != -1: 507 dateel = 'gco:DateTime' 508 else: 509 dateel = 'gco:Date' 510 etree.SubElement(date, util.nspath_eval(dateel, self.namespaces)).text = val 511 512 metadatastandardname = 'ISO19115' 513 metadatastandardversion = '2003/Cor.1:2006' 514 515 if mtype == 'service': 516 metadatastandardname = 'ISO19119' 517 metadatastandardversion = '2005/PDAM 1' 518 519 # metadata standard name 520 standard = etree.SubElement(node, util.nspath_eval('gmd:metadataStandardName', self.namespaces)) 521 etree.SubElement(standard, util.nspath_eval('gco:CharacterString', self.namespaces)).text = metadatastandardname 522 523 # metadata standard version 524 standardver = etree.SubElement(node, util.nspath_eval('gmd:metadataStandardVersion', self.namespaces)) 525 etree.SubElement(standardver, util.nspath_eval('gco:CharacterString', self.namespaces)).text = metadatastandardversion 526 527 # title 528 val = util.getqattr(result, queryables['apiso:Title']['dbcol']) or '' 529 identification = etree.SubElement(node, util.nspath_eval('gmd:identificationInfo', self.namespaces)) 530 531 if mtype == 'service': 532 restagname = 'srv:SV_ServiceIdentification' 533 else: 534 restagname = 'gmd:MD_DataIdentification' 535 536 resident = etree.SubElement(identification, util.nspath_eval(restagname, self.namespaces), id=idval) 537 tmp2 = etree.SubElement(resident, util.nspath_eval('gmd:citation', self.namespaces)) 538 tmp3 = etree.SubElement(tmp2, util.nspath_eval('gmd:CI_Citation', self.namespaces)) 539 tmp4 = etree.SubElement(tmp3, util.nspath_eval('gmd:title', self.namespaces)) 540 etree.SubElement(tmp4, util.nspath_eval('gco:CharacterString', self.namespaces)).text = val 541 542 # creation date 543 val = util.getqattr(result, queryables['apiso:CreationDate']['dbcol']) 544 if val is not None: 545 tmp3.append(_write_date(val, 'creation', self.namespaces)) 546 # publication date 547 val = util.getqattr(result, queryables['apiso:PublicationDate']['dbcol']) 548 if val is not None: 549 tmp3.append(_write_date(val, 'publication', self.namespaces)) 550 # revision date 551 val = util.getqattr(result, queryables['apiso:RevisionDate']['dbcol']) 552 if val is not None: 553 tmp3.append(_write_date(val, 'revision', self.namespaces)) 554 555 if esn in ['summary', 'full']: 556 # abstract 557 val = util.getqattr(result, queryables['apiso:Abstract']['dbcol']) or '' 558 tmp = etree.SubElement(resident, util.nspath_eval('gmd:abstract', self.namespaces)) 559 etree.SubElement(tmp, util.nspath_eval('gco:CharacterString', self.namespaces)).text = val 560 561 # keywords 562 kw = util.getqattr(result, queryables['apiso:Subject']['dbcol']) 563 if kw is not None: 564 md_keywords = etree.SubElement(resident, util.nspath_eval('gmd:descriptiveKeywords', self.namespaces)) 565 md_keywords.append(write_keywords(kw, self.namespaces)) 566 567 # spatial resolution 568 val = util.getqattr(result, queryables['apiso:Denominator']['dbcol']) 569 if val: 570 tmp = etree.SubElement(resident, util.nspath_eval('gmd:spatialResolution', self.namespaces)) 571 tmp2 = etree.SubElement(tmp, util.nspath_eval('gmd:MD_Resolution', self.namespaces)) 572 tmp3 = etree.SubElement(tmp2, util.nspath_eval('gmd:equivalentScale', self.namespaces)) 573 tmp4 = etree.SubElement(tmp3, util.nspath_eval('gmd:MD_RepresentativeFraction', self.namespaces)) 574 tmp5 = etree.SubElement(tmp4, util.nspath_eval('gmd:denominator', self.namespaces)) 575 etree.SubElement(tmp5, util.nspath_eval('gco:Integer', self.namespaces)).text = str(val) 576 577 # resource language 578 val = util.getqattr(result, queryables['apiso:ResourceLanguage']['dbcol']) 579 tmp = etree.SubElement(resident, util.nspath_eval('gmd:language', self.namespaces)) 580 etree.SubElement(tmp, util.nspath_eval('gco:CharacterString', self.namespaces)).text = val 581 582 # topic category 583 val = util.getqattr(result, queryables['apiso:TopicCategory']['dbcol']) 584 if val: 585 for v in val.split(','): 586 tmp = etree.SubElement(resident, util.nspath_eval('gmd:topicCategory', self.namespaces)) 587 etree.SubElement(tmp, util.nspath_eval('gmd:MD_TopicCategoryCode', self.namespaces)).text = val 588 589 # bbox extent 590 val = util.getqattr(result, queryables['apiso:BoundingBox']['dbcol']) 591 bboxel = write_extent(val, self.namespaces) 592 if bboxel is not None and mtype != 'service': 593 resident.append(bboxel) 594 595 # service identification 596 597 if mtype == 'service': 598 # service type 599 # service type version 600 val = util.getqattr(result, queryables['apiso:ServiceType']['dbcol']) 601 val2 = util.getqattr(result, queryables['apiso:ServiceTypeVersion']['dbcol']) 602 if val is not None: 603 tmp = etree.SubElement(resident, util.nspath_eval('srv:serviceType', self.namespaces)) 604 etree.SubElement(tmp, util.nspath_eval('gco:LocalName', self.namespaces)).text = val 605 tmp = etree.SubElement(resident, util.nspath_eval('srv:serviceTypeVersion', self.namespaces)) 606 etree.SubElement(tmp, util.nspath_eval('gco:CharacterString', self.namespaces)).text = val2 607 608 kw = util.getqattr(result, queryables['apiso:Subject']['dbcol']) 609 if kw is not None: 610 srv_keywords = etree.SubElement(resident, util.nspath_eval('srv:keywords', self.namespaces)) 611 srv_keywords.append(write_keywords(kw, self.namespaces)) 612 613 if bboxel is not None: 614 bboxel.tag = util.nspath_eval('srv:extent', self.namespaces) 615 resident.append(bboxel) 616 617 val = util.getqattr(result, queryables['apiso:CouplingType']['dbcol']) 618 if val is not None: 619 couplingtype = etree.SubElement(resident, util.nspath_eval('srv:couplingType', self.namespaces)) 620 etree.SubElement(couplingtype, util.nspath_eval('srv:SV_CouplingType', self.namespaces), codeListValue=val, codeList='%s#SV_CouplingType' % CODELIST).text = val 621 622 if esn in ['summary', 'full']: 623 # all service resources as coupled resources 624 coupledresources = util.getqattr(result, queryables['apiso:OperatesOn']['dbcol']) 625 operations = util.getqattr(result, queryables['apiso:Operation']['dbcol']) 626 627 if coupledresources: 628 for val2 in coupledresources.split(','): 629 coupledres = etree.SubElement(resident, util.nspath_eval('srv:coupledResource', self.namespaces)) 630 svcoupledres = etree.SubElement(coupledres, util.nspath_eval('srv:SV_CoupledResource', self.namespaces)) 631 opname = etree.SubElement(svcoupledres, util.nspath_eval('srv:operationName', self.namespaces)) 632 etree.SubElement(opname, util.nspath_eval('gco:CharacterString', self.namespaces)).text = _get_resource_opname(operations) 633 sid = etree.SubElement(svcoupledres, util.nspath_eval('srv:identifier', self.namespaces)) 634 etree.SubElement(sid, util.nspath_eval('gco:CharacterString', self.namespaces)).text = val2 635 636 # service operations 637 if operations: 638 for i in operations.split(','): 639 oper = etree.SubElement(resident, util.nspath_eval('srv:containsOperations', self.namespaces)) 640 tmp = etree.SubElement(oper, util.nspath_eval('srv:SV_OperationMetadata', self.namespaces)) 641 642 tmp2 = etree.SubElement(tmp, util.nspath_eval('srv:operationName', self.namespaces)) 643 etree.SubElement(tmp2, util.nspath_eval('gco:CharacterString', self.namespaces)).text = i 644 645 tmp3 = etree.SubElement(tmp, util.nspath_eval('srv:DCP', self.namespaces)) 646 etree.SubElement(tmp3, util.nspath_eval('srv:DCPList', self.namespaces), codeList='%s#DCPList' % CODELIST, codeListValue='HTTPGet').text = 'HTTPGet' 647 648 tmp4 = etree.SubElement(tmp, util.nspath_eval('srv:DCP', self.namespaces)) 649 etree.SubElement(tmp4, util.nspath_eval('srv:DCPList', self.namespaces), codeList='%s#DCPList' % CODELIST, codeListValue='HTTPPost').text = 'HTTPPost' 650 651 connectpoint = etree.SubElement(tmp, util.nspath_eval('srv:connectPoint', self.namespaces)) 652 onlineres = etree.SubElement(connectpoint, util.nspath_eval('gmd:CI_OnlineResource', self.namespaces)) 653 linkage = etree.SubElement(onlineres, util.nspath_eval('gmd:linkage', self.namespaces)) 654 etree.SubElement(linkage, util.nspath_eval('gmd:URL', self.namespaces)).text = util.getqattr(result, self.context.md_core_model['mappings']['pycsw:Source']) 655 656 # operates on resource(s) 657 if coupledresources: 658 for i in coupledresources.split(','): 659 operates_on = etree.SubElement(resident, util.nspath_eval('srv:operatesOn', self.namespaces), uuidref=i) 660 operates_on.attrib[util.nspath_eval('xlink:href', self.namespaces)] = '%sservice=CSW&version=2.0.2&request=GetRecordById&outputschema=http://www.isotc211.org/2005/gmd&id=%s-%s' % (util.bind_url(self.url), idval, i) 661 662 rlinks = util.getqattr(result, self.context.md_core_model['mappings']['pycsw:Links']) 663 664 if rlinks: 665 distinfo = etree.SubElement(node, util.nspath_eval('gmd:distributionInfo', self.namespaces)) 666 distinfo2 = etree.SubElement(distinfo, util.nspath_eval('gmd:MD_Distribution', self.namespaces)) 667 transopts = etree.SubElement(distinfo2, util.nspath_eval('gmd:transferOptions', self.namespaces)) 668 dtransopts = etree.SubElement(transopts, util.nspath_eval('gmd:MD_DigitalTransferOptions', self.namespaces)) 669 670 for link in rlinks.split('^'): 671 linkset = link.split(',') 672 online = etree.SubElement(dtransopts, util.nspath_eval('gmd:onLine', self.namespaces)) 673 online2 = etree.SubElement(online, util.nspath_eval('gmd:CI_OnlineResource', self.namespaces)) 674 675 linkage = etree.SubElement(online2, util.nspath_eval('gmd:linkage', self.namespaces)) 676 etree.SubElement(linkage, util.nspath_eval('gmd:URL', self.namespaces)).text = linkset[-1] 677 678 protocol = etree.SubElement(online2, util.nspath_eval('gmd:protocol', self.namespaces)) 679 etree.SubElement(protocol, util.nspath_eval('gco:CharacterString', self.namespaces)).text = linkset[2] 680 681 name = etree.SubElement(online2, util.nspath_eval('gmd:name', self.namespaces)) 682 etree.SubElement(name, util.nspath_eval('gco:CharacterString', self.namespaces)).text = linkset[0] 683 684 desc = etree.SubElement(online2, util.nspath_eval('gmd:description', self.namespaces)) 685 etree.SubElement(desc, util.nspath_eval('gco:CharacterString', self.namespaces)).text = linkset[1] 686 687 return node 688 689def write_keywords(keywords, nsmap): 690 """generate gmd:MD_Keywords construct""" 691 md_keywords = etree.Element(util.nspath_eval('gmd:MD_Keywords', nsmap)) 692 for kw in keywords.split(','): 693 keyword = etree.SubElement(md_keywords, util.nspath_eval('gmd:keyword', nsmap)) 694 etree.SubElement(keyword, util.nspath_eval('gco:CharacterString', nsmap)).text = kw 695 return md_keywords 696 697def write_extent(bbox, nsmap): 698 ''' Generate BBOX extent ''' 699 700 if bbox is not None: 701 try: 702 bbox2 = util.wkt2geom(bbox) 703 except: 704 return None 705 extent = etree.Element(util.nspath_eval('gmd:extent', nsmap)) 706 ex_extent = etree.SubElement(extent, util.nspath_eval('gmd:EX_Extent', nsmap)) 707 ge = etree.SubElement(ex_extent, util.nspath_eval('gmd:geographicElement', nsmap)) 708 gbb = etree.SubElement(ge, util.nspath_eval('gmd:EX_GeographicBoundingBox', nsmap)) 709 west = etree.SubElement(gbb, util.nspath_eval('gmd:westBoundLongitude', nsmap)) 710 east = etree.SubElement(gbb, util.nspath_eval('gmd:eastBoundLongitude', nsmap)) 711 south = etree.SubElement(gbb, util.nspath_eval('gmd:southBoundLatitude', nsmap)) 712 north = etree.SubElement(gbb, util.nspath_eval('gmd:northBoundLatitude', nsmap)) 713 714 etree.SubElement(west, util.nspath_eval('gco:Decimal', nsmap)).text = str(bbox2[0]) 715 etree.SubElement(south, util.nspath_eval('gco:Decimal', nsmap)).text = str(bbox2[1]) 716 etree.SubElement(east, util.nspath_eval('gco:Decimal', nsmap)).text = str(bbox2[2]) 717 etree.SubElement(north, util.nspath_eval('gco:Decimal', nsmap)).text = str(bbox2[3]) 718 return extent 719 return None 720 721def _write_date(dateval, datetypeval, nsmap): 722 date1 = etree.Element(util.nspath_eval('gmd:date', nsmap)) 723 date2 = etree.SubElement(date1, util.nspath_eval('gmd:CI_Date', nsmap)) 724 date3 = etree.SubElement(date2, util.nspath_eval('gmd:date', nsmap)) 725 if dateval.find('T') != -1: 726 dateel = 'gco:DateTime' 727 else: 728 dateel = 'gco:Date' 729 etree.SubElement(date3, util.nspath_eval(dateel, nsmap)).text = dateval 730 datetype = etree.SubElement(date2, util.nspath_eval('gmd:dateType', nsmap)) 731 datetype.append(_write_codelist_element('gmd:CI_DateTypeCode', datetypeval, nsmap)) 732 return date1 733 734def _get_resource_opname(operations): 735 for op in operations.split(','): 736 if op in ['GetMap', 'GetFeature', 'GetCoverage', 'GetObservation']: 737 return op 738 return None 739 740def _write_codelist_element(codelist_element, codelist_value, nsmap): 741 namespace, codelist = codelist_element.split(':') 742 743 element = etree.Element(util.nspath_eval(codelist_element, nsmap), 744 codeSpace=CODESPACE, codeList='%s#%s' % (CODELIST, codelist), 745 codeListValue=codelist_value) 746 747 element.text = codelist_value 748 749 return element 750