Skip to content

Instantly share code, notes, and snippets.

@rclark
Last active December 19, 2015 02:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rclark/5886908 to your computer and use it in GitHub Desktop.
Save rclark/5886908 to your computer and use it in GitHub Desktop.
CKAN + CSW code
from sqlalchemy import Column, Table, String, Text, Integer, types, ForeignKey
from sqlalchemy.orm import relationship
from ckan import model
from ckan.model import meta, Package
from shapely.geometry import asShape
import json
import logging
log = logging.getLogger(__name__)
class CswRecord(object):
"""
Class representing CSW Records that are stored in the database and served via CSW
"""
def __init__(self, package_id=None, **kwargs):
self.package_id = package_id
self.identifier = kwargs.get("identifier", None)
self.typename = kwargs.get("typename", None)
self.schema = kwargs.get("schema", None)
self.mdsource = kwargs.get("mdsource", None)
self.insert_date = kwargs.get("insert_date", None)
self.xml = kwargs.get("xml", None)
self.anytext = kwargs.get("anytext", None)
self.language = kwargs.get("language", None)
self.type = kwargs.get("type", None)
self.title = kwargs.get("title", None)
self.title_alternate = kwargs.get("title_alternate", None)
self.abstract = kwargs.get("abstract", None)
self.keywords = kwargs.get("keywords", None)
self.keywordstype = kwargs.get("keywordstype", None)
self.parentidentifier = kwargs.get("parentidentifier", None)
self.relation = kwargs.get("relation", None)
self.time_begin = kwargs.get("time_begin", None)
self.time_end = kwargs.get("time_end", None)
self.topicategory = kwargs.get("topicategory", None)
self.resourcelanguage = kwargs.get("resourcelanguage", None)
self.creator = kwargs.get("creator", None)
self.publisher = kwargs.get("publisher", None)
self.contributor = kwargs.get("contributor", None)
self.organization = kwargs.get("organization", None)
self.securityconstraints = kwargs.get("securityconstraints", None)
self.accessconstraints = kwargs.get("accessconstraints", None)
self.otherconstraints = kwargs.get("otherconstraints", None)
self.date = kwargs.get("date", None)
self.date_revision = kwargs.get("date_revision", None)
self.date_creation = kwargs.get("date_creation", None)
self.date_publication = kwargs.get("date_publication", None)
self.date_modified = kwargs.get("date_modified", None)
self.format = kwargs.get("format", None)
self.source = kwargs.get("source", None)
self.crs = kwargs.get("crs", None)
self.geodescode = kwargs.get("geodescode", None)
self.distancevalue = kwargs.get("distancevalue", None)
self.distanceuom = kwargs.get("distanceuom", None)
self.wkt_geometry = kwargs.get("wkt_geometry", None)
self.servicetype = kwargs.get("servicetype", None)
self.servicetypeversion = kwargs.get("servicetypeversion", None)
self.operation = kwargs.get("operation", None)
self.couplingtype = kwargs.get("couplingtype", None)
self.operateson = kwargs.get("operateson", None)
self.operatesonidentifier = kwargs.get("operatesonidentifier", None)
self.operatesoname = kwargs.get("operatesoname", None)
self.degree = kwargs.get("degree", None)
self.classification = kwargs.get("classification", None)
self.conditionapplyingtoaccessanduse = kwargs.get("conditionapplyingtoaccessanduse", None)
self.lineage = kwargs.get("lineage", None)
self.responsiblepartyrole = kwargs.get("responsiblepartyrole", None)
self.specificationtitle = kwargs.get("specificationtitle", None)
self.specificationdate = kwargs.get("specificationdate", None)
self.specificationdatetype = kwargs.get("specificationdatetype", None)
self.links = kwargs.get("links", None)
@classmethod
def format_links(cls, resources):
'''Format links for pycsw usage: "name,description,protocol,url[^,,,[^,,,]]"'''
links = []
for resource in resources:
link = "%s," % resource["name"]
link += "%s," % resource["description"]
link += "%s," % resource["protocol"]
link += "%s" % resource["url"]
links.append(link)
return "^".join(links)
@classmethod
def format_keywords(cls, iso_package):
keywords = [ keyword.name for keyword in iso_package.dataset_info["keywords"] ]
if iso_package.dataset_info["extent_keyword"]:
keywords.append(iso_package.dataset_info["extent_keyword"])
return ",".join(keywords)
@classmethod
def from_iso_package(cls, iso_package):
"""Create an instance of CswRecord from an ckanext.ngds.metadata.model.iso_package:IsoPackage"""
creation_args = {
"package_id": iso_package.ckan_package.id,
"identifier": iso_package.metadata_info["id"],
"typename": "gmd:MD_Metadata",
"schema": "http://www.isotc211.org/2005/gmd",
"mdsource": "local",
"insert_date": iso_package.metadata_info["updated"].replace(microsecond=0).isoformat(),
"xml": iso_package.to_iso_xml(),
"anytext": "",
"language": iso_package.metadata_info["language"],
"type": iso_package.dataset_info["category"],
"title": iso_package.dataset_info["title"],
"abstract": iso_package.dataset_info["abstract"],
"keywords": cls.format_keywords(iso_package),
"keywordstype": "theme",
"parentIdentifier": None,
"relation": "",
"time_begin": None,
"time_end": None,
"topicategory": iso_package.dataset_info["topic"],
"resourcelanguage": iso_package.dataset_info["language"],
"creator": "creator",
"publisher": "publisher",
"contributor": "contributor",
"organization": None,
"securityconstraints": None,
"accessconstraints": iso_package.dataset_info["usage"],
"otherconstraints": None,
"date": iso_package.metadata_info["updated"].replace(microsecond=0).isoformat(),
"date_revision": None,
"date_creation": None,
"date_publication": iso_package.dataset_info["publication_date"].replace(microsecond=0).isoformat(),
"date_modified": None,
"format": None,
"source": None,
"crs": None,
"geodescode": None,
"denominator": None,
"distancevalue": None,
"distanceuom": None,
"wkt_geometry": asShape(json.loads(iso_package.dataset_info["extent"])).wkt,
"servicetype": None,
"servicetypeversion": None,
"operation": None,
"couplingtype": None,
"operateson": None,
"operatesonidentifier": None,
"degree": None,
"conditionapplyingtoaccessanduse": None,
"lineage": None,
"responsiblepartyrole": "distributor", # huh?
"specificationtitle": None,
"specificationdate": None,
"spcificationdatetype": None,
"links": cls.format_links(iso_package.resources)
}
record = cls(**creation_args)
return record
def define_tables():
"""
Create in-memory representation of the tables, configure mappings to
python classes, and return the tables
Table generation code is lifted from pycsw
"""
csw_record = Table(
"csw_record", meta.metadata,
Column('package_id', types.UnicodeText, ForeignKey("package.id")), # Implicit Foreign Key to the package
# core; nothing happens without these
Column('identifier', String(256), primary_key=True),
Column('typename', String(32),
default='csw:Record', nullable=False, index=True),
Column('schema', String(256),
default='http://www.opengis.net/cat/csw/2.0.2', nullable=False,
index=True),
Column('mdsource', String(256), default='local', nullable=False,
index=True),
Column('insert_date', String(20), nullable=False, index=True),
Column('xml', Text, nullable=False),
Column('anytext', Text, nullable=False),
Column('language', String(32), index=True),
# identification
Column('type', String(128), index=True),
Column('title', types.UnicodeText, index=True),
Column('title_alternate', types.UnicodeText, index=True),
Column('abstract', types.UnicodeText, index=True),
Column('keywords', types.UnicodeText, index=True),
Column('keywordstype', String(256), index=True),
Column('parentidentifier', String(32), index=True),
Column('relation', String(256), index=True),
Column('time_begin', String(20), index=True),
Column('time_end', String(20), index=True),
Column('topicategory', String(32), index=True),
Column('resourcelanguage', String(32), index=True),
# attribution
Column('creator', String(256), index=True),
Column('publisher', String(256), index=True),
Column('contributor', String(256), index=True),
Column('organization', String(256), index=True),
# security
Column('securityconstraints', String(256), index=True),
Column('accessconstraints', String(256), index=True),
Column('otherconstraints', String(256), index=True),
# date
Column('date', String(20), index=True),
Column('date_revision', String(20), index=True),
Column('date_creation', String(20), index=True),
Column('date_publication', String(20), index=True),
Column('date_modified', String(20), index=True),
Column('format', String(128), index=True),
Column('source', String(1024), index=True),
# geospatial
Column('crs', String(256), index=True),
Column('geodescode', String(256), index=True),
Column('denominator', Integer, index=True),
Column('distancevalue', Integer, index=True),
Column('distanceuom', String(8), index=True),
Column('wkt_geometry', Text),
# service
Column('servicetype', String(32), index=True),
Column('servicetypeversion', String(32), index=True),
Column('operation', String(256), index=True),
Column('couplingtype', String(8), index=True),
Column('operateson', String(32), index=True),
Column('operatesonidentifier', String(32), index=True),
Column('operatesoname', String(32), index=True),
# additional
Column('degree', String(8), index=True),
Column('classification', String(32), index=True),
Column('conditionapplyingtoaccessanduse', String(256), index=True),
Column('lineage', String(32), index=True),
Column('responsiblepartyrole', String(32), index=True),
Column('specificationtitle', String(32), index=True),
Column('specificationdate', String(20), index=True),
Column('specificationdatetype', String(20), index=True),
# distribution
# links: format "name,description,protocol,url[^,,,[^,,,]]"
Column('links', Text, index=True),
)
# Map the table to the class...
meta.mapper(
CswRecord,
csw_record,
properties={
"package": relationship(Package)
}
)
# put the CswRecord class intp CKAN model for later reference
model.CswRecord = CswRecord
return csw_record
def db_setup():
"""Create tables in the database"""
# These tables will already be defined in memory if the csw plugin is enabled.
# IConfigurer will make a call to define_tables()
csw_record = meta.metadata.tables.get("csw_record", None)
if csw_record == None:
# The tables have not been defined. Its likely that the plugin is not enabled in the CKAN .ini file
log.debug("Could not create CSW tables. Please make sure that you've added the csw plugin to your CKAN config .ini file.")
else:
log.debug('CSW tables defined in memory')
# Alright. Create the tables.
from ckanext.ngds.base.commands.ngds_tables import create_tables
create_tables([csw_record], log)
"""
Thanks to OpenDataCatalog for inspiration and guidance on how to wrap pycsw
into a python function.
https://github.com/azavea/Open-Data-Catalog
"""
import os.path
from ConfigParser import SafeConfigParser
from ckan.lib.base import BaseController
from pylons import config as ckan_config
from pycsw import server
# Configuration parameters required for pycsw server.
# Especially the metadata:main section should be end-user configurable
CONFIGURATION = {
'server': {
'home': '.',
'mimetype': 'application/xml; charset=UTF-8',
'encoding': 'UTF-8',
'language': 'en-US',
'maxrecords': '10',
'profiles': 'apiso,dif,fgdc,atom,ebrim',
},
'repository': {
'database': ckan_config["sqlalchemy.url"], # read from configuration
'table': 'csw_record'
},
'metadata:main': { # Read all from configuration
'identification_title': ckan_config.get("ngds.csw.title", 'NGDS CSW'),
'identification_abstract': ckan_config.get("ngds.csw.abstract", 'NGDS is awesome'),
'identification_keywords': ckan_config.get("ngds.csw.keywords", 'ngds,csw,ogc,catalog'),
'identification_keywords_type': ckan_config.get("ngds.csw.keywords_type", 'theme'),
'identification_fees': ckan_config.get("ngds.csw.fees", 'None'),
'identification_accessconstraints': ckan_config.get("ngds.csw.accessconstraints", 'None'),
'provider_name': ckan_config.get("ngds.csw.provider.name", 'Roger Mebowitz'),
'provider_url': ckan_config.get("ngds.csw.provider.url", 'http://geothermaldatasystem.org'),
'contact_name': ckan_config.get("ngds.csw.contact.name", 'Roger Mebowitz'),
'contact_position': ckan_config.get("ngds.csw.contact.position", 'Roger Mebowitz'),
'contact_address': ckan_config.get("ngds.csw.contact.address", '416 W. Congress St. Ste. 100'),
'contact_city': ckan_config.get("ngds.csw.contact.city", 'Tucson'),
'contact_stateorprovince': ckan_config.get("ngds.csw.contact.state", 'Arizona'),
'contact_postalcode': ckan_config.get("ngds.csw.contact.zip", '85701'),
'contact_country': ckan_config.get("ngds.csw.contact.country", 'United States of America'),
'contact_phone': ckan_config.get("ngds.csw.contact.phone", '+01-xxx-xxx-xxxx'),
'contact_fax': ckan_config.get("ngds.csw.contact.fax", '+01-xxx-xxx-xxxx'),
'contact_email': ckan_config.get("ngds.csw.contact.email", 'nothing@false.com'),
'contact_url': ckan_config.get("ngds.csw.contact.url", 'http://geothermaldatasystem.org'),
'contact_hours': ckan_config.get("ngds.csw.contact.hours", '0800h - 1600h EST'),
'contact_instructions': ckan_config.get("ngds.csw.contact.instructions", 'During hours of service. Off on weekends.'),
'contact_role': ckan_config.get("ngds.csw.contact.role", 'pointOfContact'),
},
'metadata:inspire': { # from configuration
'enabled': 'false',
'languages_supported': 'eng',
'default_language': 'eng',
'date': '2012-06-11',
'gemet_keywords': 'Utility and governmental services',
'conformity_service': 'notEvaluated',
'contact_name': ckan_config.get("ngds.csw.contact.name", 'Roger Mebowitz'),
'contact_email': ckan_config.get("ngds.csw.contact.email", 'nothing@false.com'),
'temp_extent': '2012-06-11/2031-06-11',
}
}
class CswController(BaseController):
def csw(self, *args, **kwargs):
"""Wrapper around pycsw for dispatching CSW requests"""
# Cycle through the CONFIGURATION dictionary and push the params into the config object
config = SafeConfigParser()
for section, options in CONFIGURATION.iteritems():
config.add_section(section)
for k, v in options.iteritems():
config.set(section, k, v)
# Calculate and insert the server URL into the config
server_url = 'http://%s%s' % \
(kwargs["environ"]['HTTP_HOST'],
kwargs["environ"]['PATH_INFO'])
config.set('server', 'url', server_url)
# Make a copy of the WSGI request environment and add parameters
env = kwargs["environ"].copy()
query = kwargs["environ"]["QUERY_STRING"]
if query != "":
absolute_uri = "%s?%s" % (server_url, query)
else:
absolute_uri = server_url
env.update({
'local.app_root': os.path.dirname(__file__),
'REQUEST_URI': absolute_uri
})
# Create an instance of pycsw's CSW class to handle the request
csw = server.Csw(config, env)
# Run the request
content = csw.dispatch_wsgi()
# Set the response Content-type, and return the result
kwargs["pylons"].response.content_type = csw.contenttype
return content
<gmd:MD_Metadata xmlns:gml='http://www.opengis.net/gml' xmlns:xlink='http://www.w3.org/1999/xlink'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:gmd='http://www.isotc211.org/2005/gmd'
xmlns:gco='http://www.isotc211.org/2005/gco'
xsi:schemaLocation='http://www.isotc211.org/2005/gmd http://schemas.opengis.net/csw/2.0.2/profiles/apiso/1.0.0/apiso.xsd'>
<gmd:fileIdentifier>
<gco:CharacterString>{{ id }}</gco:CharacterString>
</gmd:fileIdentifier>
<gmd:language>
<gco:CharacterString>eng</gco:CharacterString>
</gmd:language>
<gmd:characterSet>
<gmd:MD_CharacterSetCode
codeList='http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#MD_CharacterSetCode'
codeListValue='utf8'>UTF-8
</gmd:MD_CharacterSetCode>
</gmd:characterSet>
<!-- >>>>>>>>>>>>>>>>>>>>> Dataset Category <<<<<<<<<<<<<<<<<<<<<<<<< -->
<gmd:hierarchyLevel>
<gmd:MD_ScopeCode
codeList='http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#MD_ScopeCode'
codeListValue='Dataset'>Dataset
</gmd:MD_ScopeCode>
</gmd:hierarchyLevel>
<gmd:hierarchyLevelName>
<gco:CharacterString>Dataset</gco:CharacterString>
</gmd:hierarchyLevelName>
<!-- >>>>>>>>>>>>>>>>>>>>> Dataset Category <<<<<<<<<<<<<<<<<<<<<<<<< -->
<!-- >>>>>>>>>>>>>>>>>>>>> Maintainer <<<<<<<<<<<<<<<<<<<<<<<<< -->
<gmd:contact>
<gmd:CI_ResponsibleParty>
<gmd:individualName>
<gco:CharacterString>No Name Was Given</gco:CharacterString>
</gmd:individualName>
<gmd:organisationName>
<gco:CharacterString>No Organization Name Was Given</gco:CharacterString>
</gmd:organisationName>
<gmd:contactInfo>
<gmd:CI_Contact>
<gmd:phone>
<gmd:CI_Telephone>
<gmd:voice>
<gco:CharacterString>No Phone Number Was Given</gco:CharacterString>
</gmd:voice>
</gmd:CI_Telephone>
</gmd:phone>
<gmd:address>
<gmd:CI_Address>
<gmd:deliveryPoint>
<gco:CharacterString>No Street Address Was Given</gco:CharacterString>
</gmd:deliveryPoint>
<gmd:city>
<gco:CharacterString>No City Was Given</gco:CharacterString>
</gmd:city>
<gmd:administrativeArea>
<gco:CharacterString>No State Was Given</gco:CharacterString>
</gmd:administrativeArea>
<gmd:postalCode>
<gco:CharacterString>No Zip Was Given</gco:CharacterString>
</gmd:postalCode>
<gmd:electronicMailAddress>
<gco:CharacterString>No email Was Given</gco:CharacterString>
</gmd:electronicMailAddress>
</gmd:CI_Address>
</gmd:address>
</gmd:CI_Contact>
</gmd:contactInfo>
<gmd:role>
<gmd:CI_RoleCode
codeList='http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_RoleCode'
codeListValue='pointOfContact'>pointOfContact
</gmd:CI_RoleCode>
</gmd:role>
</gmd:CI_ResponsibleParty>
</gmd:contact>
<!-- >>>>>>>>>>>>>>>>>>>>> Maintainer <<<<<<<<<<<<<<<<<<<<<<<<< -->
<gmd:dateStamp>
<gco:DateTime>{{ metadata_modified }}</gco:DateTime>
</gmd:dateStamp>
<gmd:metadataStandardName>
<gco:CharacterString>ISO-USGIN</gco:CharacterString>
</gmd:metadataStandardName>
<gmd:metadataStandardVersion>
<gco:CharacterString>1.2</gco:CharacterString>
</gmd:metadataStandardVersion>
<!-- >>>>>>>>>>>>>>>>>>>>> Dataset URI <<<<<<<<<<<<<<<<<<<<<<<<< -->
<gmd:dataSetURI>
<gco:CharacterString>http://repository.usgin.org/uri_gin/usgin/dlio/528</gco:CharacterString>
</gmd:dataSetURI>
<!-- >>>>>>>>>>>>>>>>>>>>> Dataset URI <<<<<<<<<<<<<<<<<<<<<<<<< -->
<gmd:identificationInfo>
<gmd:MD_DataIdentification>
<gmd:citation>
<gmd:CI_Citation>
<gmd:title>
<gco:CharacterString>{{ title }}</gco:CharacterString>
</gmd:title>
<!-- >>>>>>>>>>>>>>>>>>>>> Publication Date <<<<<<<<<<<<<<<<<<<<<<<<< -->
<gmd:date>
<gmd:CI_Date>
<gmd:date>
<gco:DateTime>2012-05-02T12:00:00</gco:DateTime>
</gmd:date>
<gmd:dateType>
<gmd:CI_DateTypeCode
codeList='http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_DateTypeCode'
codeListValue='publication'>publication
</gmd:CI_DateTypeCode>
</gmd:dateType>
</gmd:CI_Date>
</gmd:date>
<!-- >>>>>>>>>>>>>>>>>>>>> Publication Date <<<<<<<<<<<<<<<<<<<<<<<<< -->
<!-- >>>>>>>>>>>>>>>>>>>>> Creators <<<<<<<<<<<<<<<<<<<<<<<<< -->
<gmd:citedResponsibleParty>
<gmd:CI_ResponsibleParty>
<gmd:individualName>
<gco:CharacterString>Ann C. Arnold and Denise Hills</gco:CharacterString>
</gmd:individualName>
<gmd:organisationName>
<gco:CharacterString>Geological Survey of Alabama</gco:CharacterString>
</gmd:organisationName>
<gmd:contactInfo>
<gmd:CI_Contact>
<gmd:phone>
<gmd:CI_Telephone>
<gmd:voice>
<gco:CharacterString>205-247-3694</gco:CharacterString>
</gmd:voice>
</gmd:CI_Telephone>
</gmd:phone>
<gmd:address>
<gmd:CI_Address>
<gmd:deliveryPoint>
<gco:CharacterString>420 Hackberry Lane</gco:CharacterString>
</gmd:deliveryPoint>
<gmd:city>
<gco:CharacterString>Tuscaloosa</gco:CharacterString>
</gmd:city>
<gmd:administrativeArea>
<gco:CharacterString>Alabama</gco:CharacterString>
</gmd:administrativeArea>
<gmd:postalCode>
<gco:CharacterString>35401</gco:CharacterString>
</gmd:postalCode>
<gmd:electronicMailAddress>
<gco:CharacterString>dhills@gsa.state.al.us</gco:CharacterString>
</gmd:electronicMailAddress>
</gmd:CI_Address>
</gmd:address>
</gmd:CI_Contact>
</gmd:contactInfo>
<gmd:role>
<gmd:CI_RoleCode
codeList='http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_RoleCode'
codeListValue='originator'>originator
</gmd:CI_RoleCode>
</gmd:role>
</gmd:CI_ResponsibleParty>
</gmd:citedResponsibleParty>
<!-- >>>>>>>>>>>>>>>>>>>>> Creators <<<<<<<<<<<<<<<<<<<<<<<<< -->
</gmd:CI_Citation>
</gmd:citation>
<gmd:abstract>
<gco:CharacterString>{{ notes }}</gco:CharacterString>
</gmd:abstract>
<!-- >>>>>>>>>>>>>>>>>>>>> Status <<<<<<<<<<<<<<<<<<<<<<<<< -->
<gmd:status>
<gmd:MD_ProgressCode
codeList='http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#MD_ProgressCode'
codeListValue='completed'>completed
</gmd:MD_ProgressCode>
</gmd:status>
<!-- >>>>>>>>>>>>>>>>>>>>> Status <<<<<<<<<<<<<<<<<<<<<<<<< -->
<gmd:descriptiveKeywords>
<gmd:MD_Keywords>
{% for tag in tags %}
<gmd:keyword>
<gco:CharacterString>{{ tag.display_name }}</gco:CharacterString>
</gmd:keyword>
{% endfor %}
<gmd:type>
<gmd:MD_KeywordTypeCode
codeList='http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#MD_KeywordTypeCode'
codeListValue='theme'>theme
</gmd:MD_KeywordTypeCode>
</gmd:type>
</gmd:MD_Keywords>
</gmd:descriptiveKeywords>
<!-- >>>>>>>>>>>>>>>>>>>>> Facets <<<<<<<<<<<<<<<<<<<<<<<<< -->
<gmd:descriptiveKeywords>
<gmd:MD_Keywords>
<gmd:keyword>
<gco:CharacterString>Alabama Geological Survey</gco:CharacterString>
</gmd:keyword>
<gmd:thesaurusName xlink:href='/metadata/collection/'>
<gmd:CI_Citation>
<gmd:title>
<gco:CharacterString>Server Collections</gco:CharacterString>
</gmd:title>
<gmd:date>
<gmd:CI_Date>
<gmd:date>
<gco:Date>2012-06-06</gco:Date>
</gmd:date>
<gmd:dateType>
<gmd:CI_DateTypeCode
codeList='http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_DateTypeCode'
codeListValue='publication'></gmd:CI_DateTypeCode>
</gmd:dateType>
</gmd:CI_Date>
</gmd:date>
</gmd:CI_Citation>
</gmd:thesaurusName>
</gmd:MD_Keywords>
</gmd:descriptiveKeywords>
<!-- >>>>>>>>>>>>>>>>>>>>> Facets <<<<<<<<<<<<<<<<<<<<<<<<< -->
<gmd:language>
<gco:CharacterString>eng</gco:CharacterString>
</gmd:language>
<gmd:topicCategory>
<gmd:MD_TopicCategoryCode>geoscientificInformation</gmd:MD_TopicCategoryCode>
</gmd:topicCategory>
{% if extent != {} %}
<gmd:extent>
<gmd:EX_Extent>
<gmd:geographicElement>
<gmd:EX_GeographicBoundingBox>
<gmd:westBoundLongitude>
<gco:Decimal>{{ extent.west }}</gco:Decimal>
</gmd:westBoundLongitude>
<gmd:eastBoundLongitude>
<gco:Decimal>{{ extent.east }}</gco:Decimal>
</gmd:eastBoundLongitude>
<gmd:southBoundLatitude>
<gco:Decimal>{{ extent.south }}</gco:Decimal>
</gmd:southBoundLatitude>
<gmd:northBoundLatitude>
<gco:Decimal>{{ extent.north }}</gco:Decimal>
</gmd:northBoundLatitude>
</gmd:EX_GeographicBoundingBox>
</gmd:geographicElement>
</gmd:EX_Extent>
</gmd:extent>
{% endif %}
</gmd:MD_DataIdentification>
</gmd:identificationInfo>
<gmd:distributionInfo>
<gmd:MD_Distribution>
<!-- >>>>>>>>>>>>>>>>>>>>> Distributors <<<<<<<<<<<<<<<<<<<<<<<<< -->
<gmd:distributor>
<gmd:MD_Distributor>
<gmd:distributorContact>
<gmd:CI_ResponsibleParty>
<gmd:individualName>
<gco:CharacterString>No Name Was Given</gco:CharacterString>
</gmd:individualName>
<gmd:organisationName>
<gco:CharacterString>Arizona Geological Survey</gco:CharacterString>
</gmd:organisationName>
<gmd:contactInfo>
<gmd:CI_Contact>
<gmd:phone>
<gmd:CI_Telephone>
<gmd:voice>
<gco:CharacterString>520-770-3500</gco:CharacterString>
</gmd:voice>
</gmd:CI_Telephone>
</gmd:phone>
<gmd:address>
<gmd:CI_Address>
<gmd:deliveryPoint>
<gco:CharacterString>416 W. Congress St., Suite 100
</gco:CharacterString>
</gmd:deliveryPoint>
<gmd:city>
<gco:CharacterString>Tucson</gco:CharacterString>
</gmd:city>
<gmd:administrativeArea>
<gco:CharacterString>AZ</gco:CharacterString>
</gmd:administrativeArea>
<gmd:postalCode>
<gco:CharacterString>85701</gco:CharacterString>
</gmd:postalCode>
<gmd:electronicMailAddress>
<gco:CharacterString>metadata@azgs.az.gov</gco:CharacterString>
</gmd:electronicMailAddress>
</gmd:CI_Address>
</gmd:address>
</gmd:CI_Contact>
</gmd:contactInfo>
<gmd:role>
<gmd:CI_RoleCode
codeList='http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_RoleCode'
codeListValue='distributor'>distributor
</gmd:CI_RoleCode>
</gmd:role>
</gmd:CI_ResponsibleParty>
</gmd:distributorContact>
</gmd:MD_Distributor>
</gmd:distributor>
<!-- >>>>>>>>>>>>>>>>>>>>> Distributors <<<<<<<<<<<<<<<<<<<<<<<<< -->
{% for resource in resources %}
<gmd:transferOptions>
<gmd:MD_DigitalTransferOptions>
<gmd:onLine>
<gmd:CI_OnlineResource>
<gmd:linkage>
<gmd:URL>{{ resource.url }}</gmd:URL>
</gmd:linkage>
<gmd:protocol></gmd:protocol>
<gmd:name>
<gco:CharacterString>{{ resource.name }}</gco:CharacterString>
</gmd:name>
<gmd:description>{{ resource.description }}</gmd:description>
<gmd:function>
<gmd:CI_OnLineFunctionCode codeListValue='375'
codeList='http://www.fgdc.gov/nap/metadata/register/registerItemClasses.html#IC_88'>
download
</gmd:CI_OnLineFunctionCode>
</gmd:function>
</gmd:CI_OnlineResource>
</gmd:onLine>
</gmd:MD_DigitalTransferOptions>
</gmd:transferOptions>
{% endfor %}
</gmd:MD_Distribution>
</gmd:distributionInfo>
</gmd:MD_Metadata>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment