mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-27 00:33:48 -05:00
405 lines
18 KiB
Python
405 lines
18 KiB
Python
# -*- coding: ISO-8859-15 -*-
|
|
# =============================================================================
|
|
# Copyright (c) 2004, 2006 Sean C. Gillies
|
|
# Copyright (c) 2007 STFC <http://www.stfc.ac.uk>
|
|
#
|
|
# Authors :
|
|
# Dominic Lowe <d.lowe@rl.ac.uk>
|
|
#
|
|
# Contact email: d.lowe@rl.ac.uk
|
|
# =============================================================================
|
|
|
|
##########NOTE: Does not conform to new interfaces yet #################
|
|
|
|
from wcsBase import WCSBase, WCSCapabilitiesReader, ServiceException
|
|
from owslib.util import openURL, testXMLValue
|
|
from urllib import urlencode
|
|
from urllib2 import urlopen
|
|
from owslib.etree import etree
|
|
import os, errno
|
|
from owslib.coverage import wcsdecoder
|
|
from owslib.crs import Crs
|
|
|
|
import logging
|
|
from owslib.util import log
|
|
|
|
def ns(tag):
|
|
return '{http://www.opengis.net/wcs/1.1}'+tag
|
|
|
|
class WebCoverageService_1_1_0(WCSBase):
|
|
"""Abstraction for OGC Web Coverage Service (WCS), version 1.1.0
|
|
Implements IWebCoverageService.
|
|
"""
|
|
|
|
def __getitem__(self, name):
|
|
''' check contents dictionary to allow dict like access to service layers'''
|
|
if name in self.__getattribute__('contents').keys():
|
|
return self.__getattribute__('contents')[name]
|
|
else:
|
|
raise KeyError, "No content named %s" % name
|
|
|
|
def __init__(self,url,xml, cookies):
|
|
self.version='1.1.0'
|
|
self.url = url
|
|
self.cookies=cookies
|
|
# initialize from saved capability document or access the server
|
|
reader = WCSCapabilitiesReader(self.version)
|
|
if xml:
|
|
self._capabilities = reader.readString(xml)
|
|
else:
|
|
self._capabilities = reader.read(self.url)
|
|
|
|
# check for exceptions
|
|
se = self._capabilities.find('{http://www.opengis.net/ows/1.1}Exception')
|
|
|
|
if se is not None:
|
|
err_message = str(se.text).strip()
|
|
raise ServiceException(err_message, xml)
|
|
|
|
#build metadata objects:
|
|
|
|
#serviceIdentification metadata
|
|
elem=self._capabilities.find('{http://www.opengis.net/wcs/1.1/ows}ServiceIdentification')
|
|
if elem is None:
|
|
elem=self._capabilities.find('{http://www.opengis.net/ows/1.1}ServiceIdentification')
|
|
self.identification=ServiceIdentification(elem)
|
|
|
|
#serviceProvider
|
|
elem=self._capabilities.find('{http://www.opengis.net/ows/1.1}ServiceProvider')
|
|
self.provider=ServiceProvider(elem)
|
|
|
|
#serviceOperations
|
|
self.operations = []
|
|
for elem in self._capabilities.findall('{http://www.opengis.net/wcs/1.1/ows}OperationsMetadata/{http://www.opengis.net/wcs/1.1/ows}Operation/'):
|
|
self.operations.append(Operation(elem))
|
|
|
|
# exceptions - ***********TO DO *************
|
|
self.exceptions = [f.text for f \
|
|
in self._capabilities.findall('Capability/Exception/Format')]
|
|
|
|
# serviceContents: our assumption is that services use a top-level layer
|
|
# as a metadata organizer, nothing more.
|
|
self.contents = {}
|
|
top = self._capabilities.find('{http://www.opengis.net/wcs/1.1}Contents/{http://www.opengis.net/wcs/1.1}CoverageSummary')
|
|
for elem in self._capabilities.findall('{http://www.opengis.net/wcs/1.1}Contents/{http://www.opengis.net/wcs/1.1}CoverageSummary/{http://www.opengis.net/wcs/1.1}CoverageSummary'):
|
|
cm=ContentMetadata(elem, top, self)
|
|
self.contents[cm.id]=cm
|
|
|
|
if self.contents=={}:
|
|
#non-hierarchical.
|
|
top=None
|
|
for elem in self._capabilities.findall('{http://www.opengis.net/wcs/1.1}Contents/{http://www.opengis.net/wcs/1.1}CoverageSummary'):
|
|
cm=ContentMetadata(elem, top, self)
|
|
#make the describeCoverage requests to populate the supported formats/crs attributes
|
|
self.contents[cm.id]=cm
|
|
|
|
def items(self):
|
|
'''supports dict-like items() access'''
|
|
items=[]
|
|
for item in self.contents:
|
|
items.append((item,self.contents[item]))
|
|
return items
|
|
|
|
#TO DECIDE: Offer repackaging of coverageXML/Multipart MIME output?
|
|
#def getData(self, directory='outputdir', outputfile='coverage.nc', **kwargs):
|
|
#u=self.getCoverageRequest(**kwargs)
|
|
##create the directory if it doesn't exist:
|
|
#try:
|
|
#os.mkdir(directory)
|
|
#except OSError, e:
|
|
## Ignore directory exists error
|
|
#if e.errno <> errno.EEXIST:
|
|
#raise
|
|
##elif wcs.version=='1.1.0':
|
|
##Could be multipart mime or XML Coverages document, need to use the decoder...
|
|
#decoder=wcsdecoder.WCSDecoder(u)
|
|
#x=decoder.getCoverages()
|
|
#if type(x) is wcsdecoder.MpartMime:
|
|
#filenames=x.unpackToDir(directory)
|
|
##print 'Files from 1.1.0 service written to %s directory'%(directory)
|
|
#else:
|
|
#filenames=x
|
|
#return filenames
|
|
|
|
#TO DO: Handle rest of the WCS 1.1.0 keyword parameters e.g. GridCRS etc.
|
|
def getCoverage(self, identifier=None, bbox=None, time=None, format = None, store=False, rangesubset=None, gridbaseCRS=None, gridtype=None, gridCS=None, gridorigin=None, gridoffsets=None, method='Get',**kwargs):
|
|
"""Request and return a coverage from the WCS as a file-like object
|
|
note: additional **kwargs helps with multi-version implementation
|
|
core keyword arguments should be supported cross version
|
|
example:
|
|
cvg=wcs.getCoverageRequest(identifier=['TuMYrRQ4'], time=['2792-06-01T00:00:00.0'], bbox=(-112,36,-106,41),format='application/netcdf', store='true')
|
|
|
|
is equivalent to:
|
|
http://myhost/mywcs?SERVICE=WCS&REQUEST=GetCoverage&IDENTIFIER=TuMYrRQ4&VERSION=1.1.0&BOUNDINGBOX=-180,-90,180,90&TIMESEQUENCE=2792-06-01T00:00:00.0&FORMAT=application/netcdf
|
|
|
|
if store = true, returns a coverages XML file
|
|
if store = false, returns a multipart mime
|
|
"""
|
|
if log.isEnabledFor(logging.DEBUG):
|
|
log.debug('WCS 1.1.0 DEBUG: Parameters passed to GetCoverage: identifier=%s, bbox=%s, time=%s, format=%s, rangesubset=%s, gridbaseCRS=%s, gridtype=%s, gridCS=%s, gridorigin=%s, gridoffsets=%s, method=%s, other_arguments=%s'%(identifier, bbox, time, format, rangesubset, gridbaseCRS, gridtype, gridCS, gridorigin, gridoffsets, method, str(kwargs)))
|
|
|
|
if method == 'Get':
|
|
method='{http://www.opengis.net/wcs/1.1/ows}Get'
|
|
try:
|
|
base_url = next((m.get('url') for m in self.getOperationByName('GetCoverage').methods if m.get('type').lower() == method.lower()))
|
|
except StopIteration:
|
|
base_url = self.url
|
|
|
|
|
|
#process kwargs
|
|
request = {'version': self.version, 'request': 'GetCoverage', 'service':'WCS'}
|
|
assert len(identifier) > 0
|
|
request['identifier']=identifier
|
|
#request['identifier'] = ','.join(identifier)
|
|
if bbox:
|
|
request['boundingbox']=','.join([repr(x) for x in bbox])
|
|
if time:
|
|
request['timesequence']=','.join(time)
|
|
request['format']=format
|
|
request['store']=store
|
|
|
|
#rangesubset: untested - require a server implementation
|
|
if rangesubset:
|
|
request['RangeSubset']=rangesubset
|
|
|
|
#GridCRS structure: untested - require a server implementation
|
|
if gridbaseCRS:
|
|
request['gridbaseCRS']=gridbaseCRS
|
|
if gridtype:
|
|
request['gridtype']=gridtype
|
|
if gridCS:
|
|
request['gridCS']=gridCS
|
|
if gridorigin:
|
|
request['gridorigin']=gridorigin
|
|
if gridoffsets:
|
|
request['gridoffsets']=gridoffsets
|
|
|
|
#anything else e.g. vendor specific parameters must go through kwargs
|
|
if kwargs:
|
|
for kw in kwargs:
|
|
request[kw]=kwargs[kw]
|
|
|
|
#encode and request
|
|
data = urlencode(request)
|
|
|
|
u=openURL(base_url, data, method, self.cookies)
|
|
return u
|
|
|
|
|
|
def getOperationByName(self, name):
|
|
"""Return a named operation item."""
|
|
for item in self.operations:
|
|
if item.name == name:
|
|
return item
|
|
raise KeyError, "No operation named %s" % name
|
|
|
|
class Operation(object):
|
|
"""Abstraction for operation metadata
|
|
Implements IOperationMetadata.
|
|
"""
|
|
def __init__(self, elem):
|
|
self.name = elem.get('name')
|
|
self.formatOptions = [f.text for f in elem.findall('{http://www.opengis.net/wcs/1.1/ows}Parameter/{http://www.opengis.net/wcs/1.1/ows}AllowedValues/{http://www.opengis.net/wcs/1.1/ows}Value')]
|
|
methods = []
|
|
for verb in elem.findall('{http://www.opengis.net/wcs/1.1/ows}DCP/{http://www.opengis.net/wcs/1.1/ows}HTTP/*'):
|
|
url = verb.attrib['{http://www.w3.org/1999/xlink}href']
|
|
methods.append((verb.tag, {'url': url}))
|
|
self.methods = dict(methods)
|
|
|
|
class ServiceIdentification(object):
|
|
""" Abstraction for ServiceIdentification Metadata
|
|
implements IServiceIdentificationMetadata"""
|
|
def __init__(self,elem):
|
|
self.service="WCS"
|
|
self.version="1.1.0"
|
|
self.title=testXMLValue(elem.find('{http://www.opengis.net/ows}Title'))
|
|
if self.title is None: #may have used the wcs ows namespace:
|
|
self.title=testXMLValue(elem.find('{http://www.opengis.net/wcs/1.1/ows}Title'))
|
|
|
|
self.abstract=testXMLValue(elem.find('{http://www.opengis.net/ows}Abstract'))
|
|
if self.abstract is None:#may have used the wcs ows namespace:
|
|
self.abstract=testXMLValue(elem.find('{http://www.opengis.net/wcs/1.1/ows}Abstract'))
|
|
if elem.find('{http://www.opengis.net/ows}Abstract') is not None:
|
|
self.abstract=elem.find('{http://www.opengis.net/ows}Abstract').text
|
|
else:
|
|
self.abstract = None
|
|
self.keywords = [f.text for f in elem.findall('{http://www.opengis.net/ows}Keywords/{http://www.opengis.net/ows}Keyword')]
|
|
#self.link = elem.find('{http://www.opengis.net/wcs/1.1}Service/{http://www.opengis.net/wcs/1.1}OnlineResource').attrib.get('{http://www.w3.org/1999/xlink}href', '')
|
|
|
|
if elem.find('{http://www.opengis.net/wcs/1.1/ows}Fees') is not None:
|
|
self.fees=elem.find('{http://www.opengis.net/wcs/1.1/ows}Fees').text
|
|
else:
|
|
self.fees=None
|
|
|
|
if elem.find('{http://www.opengis.net/wcs/1.1/ows}AccessConstraints') is not None:
|
|
self.accessConstraints=elem.find('{http://www.opengis.net/wcs/1.1/ows}AccessConstraints').text
|
|
else:
|
|
self.accessConstraints=None
|
|
|
|
|
|
class ServiceProvider(object):
|
|
""" Abstraction for ServiceProvider metadata
|
|
implements IServiceProviderMetadata """
|
|
def __init__(self,elem):
|
|
name=elem.find('{http://www.opengis.net/ows}ProviderName')
|
|
if name is not None:
|
|
self.name=name.text
|
|
else:
|
|
self.name=None
|
|
#self.contact=ServiceContact(elem.find('{http://www.opengis.net/ows}ServiceContact'))
|
|
self.contact =ContactMetadata(elem)
|
|
self.url=self.name # no obvious definitive place for url in wcs, repeat provider name?
|
|
|
|
class ContactMetadata(object):
|
|
''' implements IContactMetadata'''
|
|
def __init__(self, elem):
|
|
try:
|
|
self.name = elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}IndividualName').text
|
|
except AttributeError:
|
|
self.name = None
|
|
|
|
try:
|
|
self.organization=elem.find('{http://www.opengis.net/ows}ProviderName').text
|
|
except AttributeError:
|
|
self.organization = None
|
|
|
|
try:
|
|
self.address = elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}ContactInfo/{http://www.opengis.net/ows}Address/{http://www.opengis.net/ows}DeliveryPoint').text
|
|
except AttributeError:
|
|
self.address = None
|
|
try:
|
|
self.city= elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}ContactInfo/{http://www.opengis.net/ows}Address/{http://www.opengis.net/ows}City').text
|
|
except AttributeError:
|
|
self.city = None
|
|
|
|
try:
|
|
self.region= elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}ContactInfo/{http://www.opengis.net/ows}Address/{http://www.opengis.net/ows}AdministrativeArea').text
|
|
except AttributeError:
|
|
self.region = None
|
|
|
|
try:
|
|
self.postcode= elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}ContactInfo/{http://www.opengis.net/ows}Address/{http://www.opengis.net/ows}PostalCode').text
|
|
except AttributeError:
|
|
self.postcode = None
|
|
|
|
try:
|
|
self.country= elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}ContactInfo/{http://www.opengis.net/ows}Address/{http://www.opengis.net/ows}Country').text
|
|
except AttributeError:
|
|
self.country = None
|
|
|
|
try:
|
|
self.email = elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}ContactInfo/{http://www.opengis.net/ows}Address/{http://www.opengis.net/ows}ElectronicMailAddress').text
|
|
except AttributeError:
|
|
self.email = None
|
|
|
|
class ContentMetadata(object):
|
|
"""Abstraction for WCS ContentMetadata
|
|
Implements IContentMetadata
|
|
"""
|
|
def __init__(self, elem, parent, service):
|
|
"""Initialize."""
|
|
#TODO - examine the parent for bounding box info.
|
|
|
|
self._service=service
|
|
self._elem=elem
|
|
self._parent=parent
|
|
self.id=self._checkChildAndParent('{http://www.opengis.net/wcs/1.1}Identifier')
|
|
self.description =self._checkChildAndParent('{http://www.opengis.net/wcs/1.1}Description')
|
|
self.title =self._checkChildAndParent('{http://www.opengis.net/ows}Title')
|
|
self.abstract =self._checkChildAndParent('{http://www.opengis.net/ows}Abstract')
|
|
|
|
#keywords.
|
|
self.keywords=[]
|
|
for kw in elem.findall('{http://www.opengis.net/ows}Keywords/{http://www.opengis.net/ows}Keyword'):
|
|
if kw is not None:
|
|
self.keywords.append(kw.text)
|
|
|
|
#also inherit any keywords from parent coverage summary (if there is one)
|
|
if parent is not None:
|
|
for kw in parent.findall('{http://www.opengis.net/ows}Keywords/{http://www.opengis.net/ows}Keyword'):
|
|
if kw is not None:
|
|
self.keywords.append(kw.text)
|
|
|
|
self.boundingBox=None #needed for iContentMetadata harmonisation
|
|
self.boundingBoxWGS84 = None
|
|
b = elem.find('{http://www.opengis.net/ows}WGS84BoundingBox')
|
|
if b is not None:
|
|
lc=b.find('{http://www.opengis.net/ows}LowerCorner').text
|
|
uc=b.find('{http://www.opengis.net/ows}UpperCorner').text
|
|
self.boundingBoxWGS84 = (
|
|
float(lc.split()[0]),float(lc.split()[1]),
|
|
float(uc.split()[0]), float(uc.split()[1]),
|
|
)
|
|
|
|
# bboxes - other CRS
|
|
self.boundingboxes = []
|
|
for bbox in elem.findall('{http://www.opengis.net/ows}BoundingBox'):
|
|
if bbox is not None:
|
|
try:
|
|
lc=b.find('{http://www.opengis.net/ows}LowerCorner').text
|
|
uc=b.find('{http://www.opengis.net/ows}UpperCorner').text
|
|
boundingBox = (
|
|
float(lc.split()[0]),float(lc.split()[1]),
|
|
float(uc.split()[0]), float(uc.split()[1]),
|
|
b.attrib['crs'])
|
|
self.boundingboxes.append(boundingBox)
|
|
except:
|
|
pass
|
|
|
|
#others not used but needed for iContentMetadata harmonisation
|
|
self.styles=None
|
|
self.crsOptions=None
|
|
|
|
#SupportedCRS
|
|
self.supportedCRS=[]
|
|
for crs in elem.findall('{http://www.opengis.net/wcs/1.1}SupportedCRS'):
|
|
self.supportedCRS.append(Crs(crs.text))
|
|
|
|
|
|
#SupportedFormats
|
|
self.supportedFormats=[]
|
|
for format in elem.findall('{http://www.opengis.net/wcs/1.1}SupportedFormat'):
|
|
self.supportedFormats.append(format.text)
|
|
|
|
#grid is either a gml:Grid or a gml:RectifiedGrid if supplied as part of the DescribeCoverage response.
|
|
def _getGrid(self):
|
|
grid=None
|
|
#TODO- convert this to 1.1 from 1.0
|
|
#if not hasattr(self, 'descCov'):
|
|
#self.descCov=self._service.getDescribeCoverage(self.id)
|
|
#gridelem= self.descCov.find(ns('CoverageOffering/')+ns('domainSet/')+ns('spatialDomain/')+'{http://www.opengis.net/gml}RectifiedGrid')
|
|
#if gridelem is not None:
|
|
#grid=RectifiedGrid(gridelem)
|
|
#else:
|
|
#gridelem=self.descCov.find(ns('CoverageOffering/')+ns('domainSet/')+ns('spatialDomain/')+'{http://www.opengis.net/gml}Grid')
|
|
#grid=Grid(gridelem)
|
|
return grid
|
|
grid=property(_getGrid, None)
|
|
|
|
|
|
|
|
#time limits/postions require a describeCoverage request therefore only resolve when requested
|
|
def _getTimeLimits(self):
|
|
timelimits=[]
|
|
for elem in self._service.getDescribeCoverage(self.id).findall(ns('CoverageDescription/')+ns('Domain/')+ns('TemporalDomain/')+ns('TimePeriod/')):
|
|
subelems=elem.getchildren()
|
|
timelimits=[subelems[0].text,subelems[1].text]
|
|
return timelimits
|
|
timelimits=property(_getTimeLimits, None)
|
|
|
|
#TODO timepositions property
|
|
def _getTimePositions(self):
|
|
return []
|
|
timepositions=property(_getTimePositions, None)
|
|
|
|
def _checkChildAndParent(self, path):
|
|
''' checks child coverage summary, and if item not found checks higher level coverage summary'''
|
|
try:
|
|
value = self._elem.find(path).text
|
|
except:
|
|
try:
|
|
value = self._parent.find(path).text
|
|
except:
|
|
value = None
|
|
return value
|