diff --git a/.travis.yml b/.travis.yml index 6a7e23e4e..579ada463 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,8 @@ install: - if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then pip install -r requirements-2.6.txt; fi - if [ "$LXML" == "true" ]; then pip install lxml; fi script: - - python -m pytest -s + - python -m pytest + - pep8 owslib/wmts.py notifications: irc: - "irc.freenode.org#geopython" diff --git a/CREDITS.txt b/CREDITS.txt index 1e4946d43..31cf18047 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -10,3 +10,4 @@ * Christian Ledermann * Sean Cowan * Kyle Wilcox + * Angelos Tzotsos diff --git a/LICENSE.txt b/LICENSE.txt index 2f1ad0751..33e4ab53a 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -10,7 +10,7 @@ modification, are permitted provided that the following conditions are met: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of the University of North Carolina nor the names of + * Neither the name of the Ancient World Mapping Center nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/README.txt b/README.md similarity index 100% rename from README.txt rename to README.md diff --git a/docs/en/index.rst b/docs/en/index.rst index cc34d446b..946c8bef7 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -5,11 +5,11 @@ OWSLib |release| documentation .. toctree:: :maxdepth: 2 -.. image:: http://www.ohloh.net/p/owslib/widgets/project_partner_badge.gif +.. image:: https://www.openhub.net/p/owslib/widgets/project_partner_badge.gif :width: 193px :height: 33px :alt: OWSLib - :target: http://www.ohloh.net/p/owslib?ref=WidgetProjectPartnerBadge + :target: https://www.openhub.net/p/owslib?ref=WidgetProjectPartnerBadge :Author: Tom Kralidis :Contact: tomkralidis at gmail.com @@ -62,7 +62,7 @@ Standards Support +-------------------+---------------------+ | `WMTS`_ | 1.0.0 | +-------------------+---------------------+ -| `WaterML`_ | 1.0, 1.1 | +| `WaterML`_ | 1.0, 1.1, 2.0 | +-------------------+---------------------+ Installation @@ -71,7 +71,7 @@ Installation Requirements ------------ -OWSLib requires a Python interpreter, as well as `ElementTree `_ or `lxml `_ for XML parsing. +OWSLib requires a Python interpreter, as well as `ElementTree `_ or `lxml `_ for XML parsing. Install ------- @@ -81,6 +81,8 @@ PyPI: .. code-block:: bash $ easy_install OWSLib + # pip works too + $ pip install OWSLib Git: @@ -110,6 +112,15 @@ RedHat Enterprise Linux $ wget -O /etc/yum.repos.d/RHEL-CBS.repo http://download.opensuse.org/repositories/Application:/Geo/RHEL_6/Application:Geo.repo $ yum install owslib +Fedora: + +.. note:: + + As of Fedora 20, OWSLib is part of the Fedora core package collection + +.. code-block:: bash + $ yum install OWSLib + Usage ===== @@ -220,7 +231,9 @@ Search for bird data: .. code-block:: python - >>> csw.getrecords(keywords=['birds'], maxrecords=20) + >>> from owslib.fes import PropertyIsEqualTo, PropertyIsLike, BBox + >>> birds_query = PropertyIsEqualTo('csw:AnyText', 'birds') + >>> csw.getrecords(constraints=[birds_query], maxrecords=20) >>> csw.results {'matches': 101, 'nextrecord': 21, 'returned': 20} >>> for rec in csw.records: @@ -252,16 +265,19 @@ Search for bird data in Canada: .. code-block:: python - >>> csw.getrecords(keywords=['birds'],bbox=[-141,42,-52,84]) + >>> bbox_query = BBox([-141,42,-52,84]) + >>> csw.getrecords(constraints=[birds_query, bbox_query]) >>> csw.results {'matches': 3, 'nextrecord': 0, 'returned': 3} >>> -Search for 'birds' or 'fowl' +Search for keywords like 'birds' or 'fowl' .. code-block:: python - >>> csw.getrecords(keywords=['birds', 'fowl']) + >>> birds_query_like = PropertyIsLike('dc:subject', '%birds%') + >>> fowl_query_like = PropertyIsLike('dc:subject', '%fowl%') + >>> csw.getrecords(constraints=[birds_query_like, fowl_query_like]) >>> csw.results {'matches': 107, 'nextrecord': 11, 'returned': 10} >>> @@ -503,8 +519,8 @@ You can also submit a pre-made request encoded as WPS XML: Output URL=http://cida.usgs.gov/climate/gdp/process/RetrieveResultServlet?id=5103866488472745994OUTPUT.f80e2a78-96a9-4343-9777-be60fac5b256 -SOS ---- +SOS 1.0 +------- GetCapabilities @@ -514,6 +530,16 @@ GetObservation .. include:: ../../tests/doctests/sos_10_ndbc_getobservation.txt +SOS 2.0 +------- +Examples of service metadata and GetObservation + +.. include:: ../../tests/doctests/sos_20_52n_geoviqua.txt + +Using the GetObservation response decoder for O&M and WaterML2.0 results + +.. include:: ../../tests/doctests/sos_20_timeseries_decoder_ioos.txt + SensorML -------- .. include:: ../../tests/doctests/sml_ndbc_station.txt @@ -661,6 +687,8 @@ Credits .. _`OGC WMC`: http://www.opengeospatial.org/standards/wmc .. _`OGC WPS`: http://www.opengeospatial.org/standards/wps .. _`OGC SOS`: http://www.opengeospatial.org/standards/sos +.. _`OGC O&M`: http://www.opengeospatial.org/standards/om +.. _`OGC WaterML2.0`: http://www.opengeospatial.org/standards/waterml .. _`OGC SensorML`: http://www.opengeospatial.org/standards/sensorml .. _`OGC CSW`: http://www.opengeospatial.org/standards/cat .. _`OGC WMTS`: http://www.opengeospatial.org/standards/wmts @@ -674,4 +702,4 @@ Credits .. _`freenode`: http://freenode.net/ .. _`ohloh`: http://www.ohloh.net/p/OWSLib .. _`CIA.vc`: http://cia.vc/stats/project/OWSLib -.. _`WaterML`: http://his.cuahsi.org/wofws.html#waterml +.. _`WaterML1.0`: http://his.cuahsi.org/wofws.html#waterml diff --git a/docs/publish.sh b/docs/publish.sh index 0520fc301..ce47e421b 100755 --- a/docs/publish.sh +++ b/docs/publish.sh @@ -4,8 +4,8 @@ THIS_DIR=`pwd` make clean && make html -git clone git@github.com:geopython/OWSLib.git /tmp/OWSLib -cd /tmp/OWSLib +git clone git@github.com:geopython/OWSLib.git /tmp/OWSLib-$$ +cd /tmp/OWSLib-$$ git checkout gh-pages /bin/cp -rp $THIS_DIR/build/html/en/* . git add . @@ -13,4 +13,4 @@ git commit -am "update live docs [ci skip]" git push origin gh-pages cd $THIS_DIR -rm -fr /tmp/OWSLib +rm -fr /tmp/OWSLib-$$ diff --git a/owslib/csw.py b/owslib/csw.py index 80ec92534..d24a39982 100644 --- a/owslib/csw.py +++ b/owslib/csw.py @@ -9,11 +9,13 @@ """ CSW request and response processor """ +import base64 +import inspect import warnings import StringIO import random from urllib import urlencode -from urllib2 import urlopen +from urllib2 import Request, urlopen from owslib.util import OrderedDict @@ -39,7 +41,8 @@ def get_namespaces(): class CatalogueServiceWeb: """ csw request class """ - def __init__(self, url, lang='en-US', version='2.0.2', timeout=10, skip_caps=False): + def __init__(self, url, lang='en-US', version='2.0.2', timeout=10, skip_caps=False, + username=None, password=None): """ Construct and process a GetCapabilities request @@ -52,6 +55,8 @@ def __init__(self, url, lang='en-US', version='2.0.2', timeout=10, skip_caps=Fal - version: version (default is '2.0.2') - timeout: timeout in seconds - skip_caps: whether to skip GetCapabilities processing on init (default is False) + - username: username for HTTP basic authentication + - password: password for HTTP basic authentication """ @@ -59,6 +64,8 @@ def __init__(self, url, lang='en-US', version='2.0.2', timeout=10, skip_caps=Fal self.lang = lang self.version = version self.timeout = timeout + self.username = username + self.password = password self.service = 'CSW' self.exceptionreport = None self.owscommon = ows.OwsCommon('1.0.0') @@ -511,7 +518,7 @@ def _parseinsertresult(self): def _parserecords(self, outputschema, esn): if outputschema == namespaces['gmd']: # iso 19139 - for i in self._exml.findall('.//'+util.nspath_eval('gmd:MD_Metadata', namespaces)): + for i in self._exml.findall('.//'+util.nspath_eval('gmd:MD_Metadata', namespaces)) or self._exml.findall('.//'+util.nspath_eval('gmi:MI_Metadata', namespaces)): val = i.find(util.nspath_eval('gmd:fileIdentifier/gco:CharacterString', namespaces)) identifier = self._setidentifierkey(util.testXMLValue(val)) self.records[identifier] = MD_Metadata(i) @@ -584,14 +591,21 @@ def _invoke(self): # do HTTP request if isinstance(self.request, basestring): # GET KVP - self.response = urlopen(self.request, timeout=self.timeout).read() + req = Request(self.request) + if self.username is not None and self.password is not None: + base64string = base64.encodestring('%s:%s' % (self.username, self.password))[:-1] + req.add_header('Authorization', 'Basic %s' % base64string) + self.response = urlopen(req, timeout=self.timeout).read() else: xml_post_url = self.url # Get correct POST URL based on Operation list. # If skip_caps=True, then self.operations has not been set, so use # default URL. if hasattr(self, 'operations'): - for op in self.operations: + caller = inspect.stack()[1][3] + if caller == 'getrecords2': caller = 'getrecords' + try: + op = self.get_operation_by_name(caller) post_verbs = filter(lambda x: x.get('type').lower() == 'post', op.methods) if len(post_verbs) > 1: # Filter by constraints. We must match a PostEncoding of "XML" @@ -602,6 +616,8 @@ def _invoke(self): xml_post_url = post_verbs[0].get('url') elif len(post_verbs) == 1: xml_post_url = post_verbs[0].get('url') + except: # no such luck, just go with xml_post_url + pass self.request = cleanup_namespaces(self.request) # Add any namespaces used in the "typeNames" attribute of the @@ -616,7 +632,7 @@ def _invoke(self): self.request = util.element_to_string(self.request, encoding='utf-8') - self.response = util.http_post(xml_post_url, self.request, self.lang, self.timeout) + self.response = util.http_post(xml_post_url, self.request, self.lang, self.timeout, self.username, self.password) # parse result see if it's XML self._exml = etree.parse(StringIO.StringIO(self.response)) diff --git a/owslib/feature/__init__.py b/owslib/feature/__init__.py index 5a9b224b8..2de607476 100644 --- a/owslib/feature/__init__.py +++ b/owslib/feature/__init__.py @@ -119,7 +119,10 @@ def getGETGetFeatureRequest(self, typename=None, filter=None, bbox=None, feature request['query'] = str(filter) if typename: typename = [typename] if type(typename) == type("") else typename - request['typename'] = ','.join(typename) + if self.version == "2.0.0": + request['typename'] = ','.join(typename) + else: + request['typenames'] = ','.join(typename) if propertyname: request['propertyname'] = ','.join(propertyname) if featureversion: diff --git a/owslib/feature/wfs200.py b/owslib/feature/wfs200.py index d805ded96..2659d38d9 100644 --- a/owslib/feature/wfs200.py +++ b/owslib/feature/wfs200.py @@ -255,7 +255,7 @@ def _getStoredQueries(self, timeout=30): request = {'service': 'WFS', 'version': self.version, 'request': 'ListStoredQueries'} encoded_request = urlencode(request) - u = urlopen(base_url + encoded_request, timeout=timeout) + u = urlopen(base_url, data=encoded_request, timeout=timeout) tree=etree.fromstring(u.read()) tempdict={} for sqelem in tree[:]: @@ -275,7 +275,7 @@ def _getStoredQueries(self, timeout=30): base_url = self.url request = {'service': 'WFS', 'version': self.version, 'request': 'DescribeStoredQueries'} encoded_request = urlencode(request) - u = urlopen(base_url + encoded_request, timeout=timeout) + u = urlopen(base_url, data=encoded_request, timeout=timeout) tree=etree.fromstring(u.read()) tempdict2={} for sqelem in tree[:]: diff --git a/owslib/iso.py b/owslib/iso.py index f719a4651..e8a81d27d 100644 --- a/owslib/iso.py +++ b/owslib/iso.py @@ -247,9 +247,9 @@ def __init__(self, md=None, identtype=None): self.distance = [] self.uom = [] self.resourcelanguage = [] - self.creator = None - self.publisher = None - self.originator = None + self.creator = [] + self.publisher = [] + self.contributor = [] self.edition = None self.abstract = None self.purpose = None @@ -347,17 +347,20 @@ def __init__(self, md=None, identtype=None): if val is not None: self.resourcelanguage.append(val) - val = md.find(util.nspath_eval('gmd:pointOfContact/gmd:CI_ResponsibleParty/gmd:organisationName', namespaces)) - if val is not None: - val2 = val.find(util.nspath_eval('gmd:role/gmd:CI_RoleCode', namespaces)) - if val2 is not None: - clv = _testCodeListValue(val) + self.creator = [] + self.publisher = [] + self.contributor = [] + for val in md.findall(util.nspath_eval('gmd:pointOfContact/gmd:CI_ResponsibleParty', namespaces)): + role = val.find(util.nspath_eval('gmd:role/gmd:CI_RoleCode', namespaces)) + if role is not None: + clv = _testCodeListValue(role) + rp = CI_ResponsibleParty(val) if clv == 'originator': - self.creator = util.testXMLValue(val) + self.creator.append(rp) elif clv == 'publisher': - self.publisher = util.testXMLValue(val) - elif clv == 'contributor': - self.originator = util.testXMLValue(val) + self.publisher.append(rp) + elif clv == 'author': + self.contributor.append(rp) val = md.find(util.nspath_eval('gmd:edition/gco:CharacterString', namespaces)) self.edition = util.testXMLValue(val) @@ -545,6 +548,8 @@ def __init__(self, md=None): self.operations = [] self.operateson = [] else: + val=md.find(util.nspath_eval('gmd:citation/gmd:CI_Citation/gmd:title/gco:CharacterString', namespaces)) + self.title=util.testXMLValue(val) self.identtype = 'service' val = md.find(util.nspath_eval('srv:serviceType/gco:LocalName', namespaces)) self.type = util.testXMLValue(val) @@ -555,6 +560,9 @@ def __init__(self, md=None): val = md.find(util.nspath_eval('srv:accessProperties/gmd:MD_StandardOrderProcess/gmd:fees/gco:CharacterString', namespaces)) self.fees = util.testXMLValue(val) + val = md.find(util.nspath_eval('srv:accessProperties/gmd:MD_StandardOrderProcess/gmd:fees/gco:CharacterString', namespaces)) + self.fees = util.testXMLValue(val) + val = md.find(util.nspath_eval('srv:extent/gmd:EX_Extent', namespaces)) if val is not None: diff --git a/owslib/namespaces.py b/owslib/namespaces.py index cbf5a4df9..2ec8f876d 100644 --- a/owslib/namespaces.py +++ b/owslib/namespaces.py @@ -15,6 +15,7 @@ class Namespaces(object): 'fgdc' : 'http://www.opengis.net/cat/csw/csdgm', 'gco' : 'http://www.isotc211.org/2005/gco', 'gmd' : 'http://www.isotc211.org/2005/gmd', + 'gmi' : 'http://www.isotc211.org/2005/gmi', 'gml' : 'http://www.opengis.net/gml', 'gml311': 'http://www.opengis.net/gml', 'gml32' : 'http://www.opengis.net/gml/3.2', @@ -42,6 +43,7 @@ class Namespaces(object): 'swe20' : 'http://www.opengis.net/swe/2.0', 'swes' : 'http://www.opengis.net/swes/2.0', 'tml' : 'ttp://www.opengis.net/tml', + 'wml2' : 'http://www.opengis.net/waterml/2.0', 'wfs' : 'http://www.opengis.net/wfs', 'wfs20' : 'http://www.opengis.net/wfs/2.0', 'wcs' : 'http://www.opengis.net/wcs', diff --git a/owslib/ows.py b/owslib/ows.py index cb5ade533..24bf51cc0 100644 --- a/owslib/ows.py +++ b/owslib/ows.py @@ -162,6 +162,8 @@ def __repr__(self): class OperationsMetadata(object): """Initialize an OWS OperationMetadata construct""" def __init__(self, elem, namespace=DEFAULT_OWS_NAMESPACE): + if 'name' not in elem.attrib: # This is not a valid element + return self.name = elem.attrib['name'] self.formatOptions = ['text/xml'] parameters = [] diff --git a/owslib/swe/observation/om.py b/owslib/swe/observation/om.py new file mode 100644 index 000000000..1196bde68 --- /dev/null +++ b/owslib/swe/observation/om.py @@ -0,0 +1,109 @@ +# -*- coding: ISO-8859-15 -*- +# ============================================================================= +# Copyright (c) 2014 Pete Taylor +# +# Authors : Pete Taylor +# +# Contact email: peterataylor@gmail.com +# ============================================================================= + +from owslib.util import nspath_eval, extract_time +from owslib.namespaces import Namespaces +from owslib.util import testXMLAttribute, testXMLValue + +def get_namespaces(): + ns = Namespaces() + return ns.get_namespaces(["swe20", "xlink", "sos20", "om20", "gml32", + "xsi"]) +namespaces = get_namespaces() + +def nspv(path): + return nspath_eval(path, namespaces) + +class TimePeriod(object): + ''' Basic class for gml TimePeriod ''' + def __init__(self, start, end): + self.start = start + self.end = end + def __str__(self): + return ("start: " + str(self.start) + " " + + "end: " + str(self.end)) + +class OM_Observation(object): + ''' The base OM_Observation type, of which there may be many + specialisations, e.g. MesaurementObservation, SWE Observation, WML2 etc. + Currently assumes that many properties are xlink only (not inline). + ''' + def __init__(self, element): + self.type = testXMLAttribute(element.find(nspv( + "om20:type")), nspv("xlink:href")) + + self.featureOfInterest = testXMLAttribute(element.find(nspv( + "om20:featureOfInterest")), nspv("xlink:href")) + + self.observedProperty = testXMLAttribute(element.find(nspv( + "om20:observedProperty")), nspv("xlink:href")) + + self.procedure = testXMLAttribute(element.find(nspv( + "om20:procedure")), nspv("xlink:href")) + + ''' Determine if phenom time is instant or a period. This + depend on the type of observation -- this could be split out ''' + instant_element = element.find(nspv( + "om20:phenomenonTime/gml32:TimeInstant")) + + if instant_element is not None: + self.phenomenonTime = extract_time(instant_element) + else: + start = extract_time(element.find(nspv( + "om20:phenomenonTime/gml32:TimePeriod/gml32:beginPosition"))) + end = extract_time(element.find(nspv( + "om20:phenomenonTime/gml32:TimePeriod/gml32:endPosition"))) + self.phenomenonTime = TimePeriod(start, end) + + self.resultTime = extract_time(element.find(nspv( + "om20:resultTime/gml32:TimeInstant/gml32:timePosition"))) + + self.result = element.find(nspv("om20:result")) + + def get_result(self): + ''' This will handle different result types using specialised + observation types ''' + return self.result + +class MeasurementObservation(OM_Observation): + ''' Specialised observation type that has a measurement (value + uom) + as result type + ''' + def __init__(self, element): + super(MeasurementObservation, self).__init__(element) + self._parse_result() + + def _parse_result(self): + ''' Parse the result property, extracting the value + and unit of measure ''' + if self.result is not None: + uom = testXMLAttribute(self.result, "uom") + value_str = testXMLValue(self.result) + try: + value = float(value_str) + except: + raise ValueError("Error parsing measurement value") + self.result = Measurement(value, uom) + + def get_result(self): + return self.result + +class Result(object): + ''' Base class for different OM_Observation result types ''' + def __init__(self, element): + pass + +class Measurement(Result): + ''' A single measurement (value + uom) ''' + def __init__(self, value, uom): + super(Measurement, self).__init__(None) + self.value = value + self.uom = uom + def __str__(self): + return str(self.value) + "(" + self.uom + ")" diff --git a/owslib/swe/observation/sos100.py b/owslib/swe/observation/sos100.py index 776ca58fd..698c53aad 100644 --- a/owslib/swe/observation/sos100.py +++ b/owslib/swe/observation/sos100.py @@ -10,7 +10,7 @@ def get_namespaces(): n = Namespaces() - ns = n.get_namespaces(["ogc","sml","gml","sos","swe","xlink"]) + ns = n.get_namespaces(["ogc","sml","gml","om","sos","swe","xlink"]) ns["ows"] = n.get_namespace("ows110") return ns namespaces = get_namespaces() @@ -30,7 +30,7 @@ def __new__(self,url, version, xml=None, username=None, password=None): def __getitem__(self,id): ''' check contents dictionary to allow dict like access to service observational offerings''' - if name in self.__getattribute__('contents').keys(): + if id in self.__getattribute__('contents').keys(): return self.__getattribute__('contents')[id] else: raise KeyError, "No Observational Offering with id: %s" % id @@ -67,17 +67,17 @@ def getOperationByName(self, name): raise KeyError("No operation named %s" % name) def _build_metadata(self): - """ + """ Set up capabilities metadata objects """ # ows:ServiceIdentification metadata service_id_element = self._capabilities.find(nspath_eval('ows:ServiceIdentification', namespaces)) self.identification = ows.ServiceIdentification(service_id_element) - + # ows:ServiceProvider metadata service_provider_element = self._capabilities.find(nspath_eval('ows:ServiceProvider', namespaces)) self.provider = ows.ServiceProvider(service_provider_element) - + # ows:OperationsMetadata metadata self.operations=[] for elem in self._capabilities.findall(nspath_eval('ows:OperationsMetadata/ows:Operation', namespaces)): @@ -116,14 +116,22 @@ def describe_sensor(self, outputFormat=None, assert isinstance(procedure, str) request['procedure'] = procedure + url_kwargs = {} + if 'timeout' in kwargs: + url_kwargs['timeout'] = kwargs.pop('timeout') # Client specified timeout value + # Optional Fields if kwargs: for kw in kwargs: request[kw]=kwargs[kw] - - data = urlencode(request) - response = openURL(base_url, data, method, username=self.username, password=self.password).read() + data = urlencode(request) + + + response = openURL(base_url, data, method, username=self.username, password=self.password, **url_kwargs).read() + + + tr = etree.fromstring(response) if tr.tag == nspath_eval("ows:ExceptionReport", namespaces): @@ -169,25 +177,29 @@ def get_observation(self, responseFormat=None, if eventTime is not None: request['eventTime'] = eventTime + url_kwargs = {} + if 'timeout' in kwargs: + url_kwargs['timeout'] = kwargs.pop('timeout') # Client specified timeout value + if kwargs: for kw in kwargs: request[kw]=kwargs[kw] - data = urlencode(request) + data = urlencode(request) - response = openURL(base_url, data, method, username=self.username, password=self.password).read() + response = openURL(base_url, data, method, username=self.username, password=self.password, **kwargs).read() try: tr = etree.fromstring(response) if tr.tag == nspath_eval("ows:ExceptionReport", namespaces): raise ows.ExceptionReport(tr) else: - return response + return response except ows.ExceptionReport: raise except BaseException: return response - def get_operation_by_name(self, name): + def get_operation_by_name(self, name): """ Return a Operation item by name, case insensitive """ @@ -254,7 +266,7 @@ def __init__(self, element): def __str__(self): return 'Offering id: %s, name: %s' % (self.id, self.name) - + class SosCapabilitiesReader(object): def __init__(self, version="1.0.0", url=None, username=None, password=None): self.version = version @@ -304,4 +316,3 @@ def read_string(self, st): if not isinstance(st, str): raise ValueError("String must be of type string, not %s" % type(st)) return etree.fromstring(st) - diff --git a/owslib/swe/observation/sos200.py b/owslib/swe/observation/sos200.py index 08ab1410b..b1d7f9f28 100644 --- a/owslib/swe/observation/sos200.py +++ b/owslib/swe/observation/sos200.py @@ -5,12 +5,14 @@ from owslib import ows from owslib.crs import Crs from owslib.fes import FilterCapabilities200 -from owslib.util import openURL, testXMLValue, nspath_eval, nspath, extract_time +from owslib.util import openURL, testXMLValue, testXMLAttribute, nspath_eval, nspath, extract_time from owslib.namespaces import Namespaces +from owslib.swe.observation.om import MeasurementObservation +from owslib.swe.observation.waterml2 import MeasurementTimeseriesObservation def get_namespaces(): n = Namespaces() - ns = n.get_namespaces(["fes","ogc","om","gml32","sml","swe20","swes","xlink"]) + ns = n.get_namespaces(["fes","ogc","xsi", "om20","gml32","sml","swe20","swes","xlink"]) ns["ows"] = n.get_namespace("ows110") ns["sos"] = n.get_namespace("sos20") return ns @@ -32,7 +34,7 @@ def __new__(self,url, version, xml=None, username=None, password=None): def __getitem__(self,id): ''' check contents dictionary to allow dict like access to service observational offerings''' - if name in self.__getattribute__('contents').keys(): + if id in self.__getattribute__('contents').keys(): return self.__getattribute__('contents')[id] else: raise KeyError, "No Observational Offering with id: %s" % id @@ -56,8 +58,8 @@ def __init__(self, url, version='2.0.0', xml=None, username=None, password=None) # Avoid building metadata if the response is an Exception se = self._capabilities.find(nspath_eval('ows:ExceptionReport', namespaces)) - if se is not None: - raise ows.ExceptionReport(se) + if se is not None: + raise ows.ExceptionReport(se) # build metadata objects self._build_metadata() @@ -70,17 +72,17 @@ def getOperationByName(self, name): raise KeyError("No operation named %s" % name) def _build_metadata(self): - """ + """ Set up capabilities metadata objects """ # ows:ServiceIdentification metadata service_id_element = self._capabilities.find(nspath_eval('ows:ServiceIdentification', namespaces)) self.identification = ows.ServiceIdentification(service_id_element) - + # ows:ServiceProvider metadata service_provider_element = self._capabilities.find(nspath_eval('ows:ServiceProvider', namespaces)) self.provider = ows.ServiceProvider(service_provider_element) - + # ows:OperationsMetadata metadata self.operations= [] for elem in self._capabilities.findall(nspath_eval('ows:OperationsMetadata/ows:Operation', namespaces)): @@ -119,14 +121,18 @@ def describe_sensor(self, outputFormat=None, assert isinstance(procedure, str) request['procedure'] = procedure + url_kwargs = {} + if 'timeout' in kwargs: + url_kwargs['timeout'] = kwargs.pop('timeout') # Client specified timeout value + # Optional Fields if kwargs: for kw in kwargs: request[kw]=kwargs[kw] - - data = urlencode(request) - response = openURL(base_url, data, method, username=self.username, password=self.password).read() + data = urlencode(request) + + response = openURL(base_url, data, method, username=self.username, password=self.password, **url_kwargs).read() tr = etree.fromstring(response) if tr.tag == nspath_eval("ows:ExceptionReport", namespaces): @@ -139,6 +145,7 @@ def get_observation(self, responseFormat=None, observedProperties=None, eventTime=None, method='Get', + timeout=30, **kwargs): """ Parameters @@ -151,7 +158,10 @@ def get_observation(self, responseFormat=None, anything else e.g. vendor specific parameters """ - base_url = self.get_operation_by_name('GetObservation').methods[method]['url'] + # Pluck out the get observation URL for HTTP method - methods is an + # array of dicts + methods = self.get_operation_by_name('GetObservation').methods + base_url = [ m['url'] for m in methods if m['type'] == method][0] request = {'service': 'SOS', 'version': self.version, 'request': 'GetObservation'} @@ -169,25 +179,29 @@ def get_observation(self, responseFormat=None, if eventTime is not None: request['temporalFilter'] = eventTime + url_kwargs = {} + if 'timeout' in kwargs: + url_kwargs['timeout'] = kwargs.pop('timeout') # Client specified timeout value + if kwargs: for kw in kwargs: request[kw]=kwargs[kw] - data = urlencode(request) + data = urlencode(request) - response = openURL(base_url, data, method, username=self.username, password=self.password).read() + response = openURL(base_url, data, method, username=self.username, password=self.password, **url_kwargs).read() try: tr = etree.fromstring(response) if tr.tag == nspath_eval("ows:ExceptionReport", namespaces): raise ows.ExceptionReport(tr) else: - return response + return response except ows.ExceptionReport: raise except BaseException: return response - def get_operation_by_name(self, name): + def get_operation_by_name(self, name): """ Return a Operation item by name, case insensitive """ @@ -254,7 +268,7 @@ def __init__(self, element): def __str__(self): return 'Offering id: %s, name: %s' % (self.id, self.name) - + class SosCapabilitiesReader(object): def __init__(self, version="2.0.0", url=None, username=None, password=None): self.version = version @@ -305,3 +319,51 @@ def read_string(self, st): raise ValueError("String must be of type string, not %s" % type(st)) return etree.fromstring(st) +class SOSGetObservationResponse(object): + """ The base response type from SOS2.0. Container for OM_Observation + objects. + """ + def __init__(self, element): + obs_data = element.findall( + nspath_eval("sos:observationData/om20:OM_Observation", namespaces)) + self.observations = [] + decoder = ObservationDecoder() + for obs in obs_data: + parsed_obs = decoder.decode_observation(obs) + self.observations.append(parsed_obs) + + def __iter__(self): + for obs in self.observations: + yield obs + + def __getitem__(self, index): + return self.observations[index] + +class ObservationDecoder(object): + """ Class to handle decoding different Observation types. + The decode method inspects the type of om:result element and + returns the appropriate observation type, which handles parsing + of the result. + """ + def decode_observation(self, element): + """ Returns a parsed observation of the appropriate type, + by inspecting the result element. + + 'element' input is the XML tree of the OM_Observation object + """ + result_element = element.find(nspath_eval("om20:result", namespaces)) + if len(result_element) == 0: + result_type = testXMLAttribute( + result_element, nspath_eval("xsi:type", namespaces)) + else: + result_type = list(result_element)[0].tag + + if result_type.find('MeasureType') != -1: + return MeasurementObservation(element) + elif (result_type == + '{http://www.opengis.net/waterml/2.0}MeasurementTimeseries'): + return MeasurementTimeseriesObservation(element) + else: + raise NotImplementedError('Result type of %s not supported' + % result_type) + diff --git a/owslib/swe/observation/waterml2.py b/owslib/swe/observation/waterml2.py new file mode 100644 index 000000000..9dcbd9d3f --- /dev/null +++ b/owslib/swe/observation/waterml2.py @@ -0,0 +1,142 @@ +# -*- coding: ISO-8859-15 -*- +# ============================================================================= +# Copyright (c) 2014 Pete Taylor +# +# Authors : Pete Taylor +# +# Contact email: peterataylor@gmail.com +# ============================================================================= +from owslib.util import nspath_eval +from owslib.namespaces import Namespaces +from owslib.util import testXMLAttribute, testXMLValue +from owslib.swe.common import Quantity +from dateutil import parser +from owslib.swe.observation.om import OM_Observation, Result + +def get_namespaces(): + ns = Namespaces() + return ns.get_namespaces(["swe20", "xlink", "sos20", "om20", "gml32", + "xsi", "wml2"]) +namespaces = get_namespaces() + +def nspv(path): + return nspath_eval(path, namespaces) + +class MeasurementTimeseriesObservation(OM_Observation): + ''' A timeseries observation that has a measurement timeseries as + result. An implementation of the WaterML2 + MeasurementTimeseriesObservation. ''' + def __init__(self, element): + super(MeasurementTimeseriesObservation, self).__init__(element) + self._parse_result() + + def _parse_result(self): + ''' Parse the result element of the observation type ''' + if self.result is not None: + result = self.result.find(nspv( + "wml2:MeasurementTimeseries")) + self.result = MeasurementTimeseries(result) + + def get_result(self): + return self.result + +class Timeseries(Result): + ''' Generic time-series class ''' + def __init__(self, element): + super(Timeseries, self).__init__(element) + +class MeasurementTimeseries(Timeseries): + ''' A WaterML2.0 timeseries of measurements, with per-value metadata. ''' + def __init__(self, element): + super(MeasurementTimeseries, self).__init__(element) + + self.defaultTVPMetadata = TVPMeasurementMetadata(element.find( + nspv("wml2:defaultPointMetadata/wml2:DefaultTVPMeasurementMetadata"))) + + elems = element.findall(nspv("wml2:point")) + self.points = [] + for point in elems: + self.points.append(TimeValuePair(point)) + + def __iter__(self): + for point in self.points: + yield point + + def __len__(self): + return len(self.points) + + def _parse_metadata(self, element): + ''' Parse metadata elements relating to timeseries: + TS: baseTime, spacing, commentBlock, parameter + MTS: startAnchor, endAnchor, cumulative, accAnchor/Length, maxGap + ''' + pass + +class TimeValuePair(object): + ''' A time-value pair as specified by WaterML2.0 + Currently no support for tvp metadata. + ''' + def __init__(self, element): + date_str = testXMLValue( + element.find(nspv("wml2:MeasurementTVP/wml2:time"))) + try: + self.datetime = parser.parse(date_str) + except: + raise ValueError("Error parsing datetime string: %s" % date_str) + + value_str = testXMLValue(element.find(nspv( + "wml2:MeasurementTVP/wml2:value"))) + try: + self.value = float(value_str) + except: + raise ValueError( + "Error parsing time series point value: %s" % value_str) + def __str__(self): + return str(self.datetime) + "," + str(self.value) + +class TVPMetadata(object): + def __init__(self, element): + ''' Base time-value pair metadata. Still to do: + - relatedObservation + ''' + self.quality = testXMLAttribute(element.find(nspv( + "wml2:quality")), nspv("xlink:href")) + self.nilReason = testXMLAttribute(element.find(nspv( + "wml2:nilReason")), nspv("xlink:href")) + self.comment = testXMLValue(element.find(nspv( + "wml2:comment"))) + self.qualifier = testXMLValue(element.find(nspv( + "wml2:qualifier")), nspv("xlink:href")) + self.processing = testXMLValue(element.find(nspv( + "wml2:processing")), nspv("xlink:href")) + self.source = testXMLValue(element.find(nspv( + "wml2:source")), nspv("xlink:href")) + +class TVPMeasurementMetadata(TVPMetadata): + ''' Measurement specific metadata. Still to do: + - aggregationDuration + ''' + def __init__(self, element): + super(TVPMeasurementMetadata, self).__init__(element) + + self.uom = testXMLAttribute(element.find(nspv( + "wml2:uom")), "code") + self.interpolationType = testXMLAttribute(element.find(nspv( + "wml2:interpolationType")), nspv("xlink:href")) + self.censoredReason = testXMLAttribute(element.find(nspv( + "wml2:censoredReason")), "xlink:href") + + accuracy = testXMLValue(element.find(nspv("wml2:accuracy"))) + if accuracy is not None: + self.accuracy = Quantity(element) + +class MeasurementTimeseriesDomainRange(Timeseries): + ''' Class to implement domain range timeseries encoding ''' + def __init__(self, element): + super(MeasurementTimeseriesDomainRange, self, element).__init__() + +class MonitoringPoint(object): + ''' A WaterML2.0 Monitoring Point, which is a specialised O&M SamplingPoint + ''' + def __init__(self, element): + pass diff --git a/owslib/util.py b/owslib/util.py index da3b34645..d70089287 100644 --- a/owslib/util.py +++ b/owslib/util.py @@ -7,6 +7,7 @@ # Contact email: tomkralidis@gmail.com # ============================================================================= +import base64 import sys from dateutil import parser from datetime import datetime @@ -338,7 +339,7 @@ def testXMLAttribute(element, attribute): return None -def http_post(url=None, request=None, lang='en-US', timeout=10): +def http_post(url=None, request=None, lang='en-US', timeout=10, username=None, password=None): """ Invoke an HTTP POST request @@ -364,6 +365,9 @@ def http_post(url=None, request=None, lang='en-US', timeout=10): r.add_header('Accept-Encoding', 'gzip,deflate') r.add_header('Host', u.netloc) + if username is not None and password is not None: + base64string = base64.encodestring('%s:%s' % (username, password))[:-1] + r.add_header('Authorization', 'Basic %s' % base64string) try: up = urllib2.urlopen(r,timeout=timeout); except TypeError: diff --git a/owslib/wms.py b/owslib/wms.py index 045c3880f..e726b5c3f 100644 --- a/owslib/wms.py +++ b/owslib/wms.py @@ -1,4 +1,4 @@ -# -*- coding: ISO-8859-15 -*- +# -*- coding: iso-8859-15 -*- # ============================================================================= # Copyright (c) 2004, 2006 Sean C. Gillies # Copyright (c) 2005 Nuxeo SARL @@ -366,7 +366,8 @@ def __init__(self, elem, parent=None, index=0, parse_remote_metadata=False, time sh = elem.find('ScaleHint') self.scaleHint = None if sh is not None: - self.scaleHint = {'min': sh.attrib['min'], 'max': sh.attrib['max']} + if 'min' in sh.attrib and 'max' in sh.attrib: + self.scaleHint = {'min': sh.attrib['min'], 'max': sh.attrib['max']} attribution = elem.find('Attribution') if attribution is not None: diff --git a/owslib/wmts.py b/owslib/wmts.py index a40233419..eff4e04b5 100644 --- a/owslib/wmts.py +++ b/owslib/wmts.py @@ -16,13 +16,16 @@ Abstract -------- -The wmts module of the OWSlib package provides client-side functionality for fetching tiles from an OGC Web Map Tile Service (WMTS) +The wmts module of the OWSlib package provides client-side functionality +for fetching tiles from an OGC Web Map Tile Service (WMTS) Disclaimer ---------- -PLEASE NOTE: the owslib wmts module should be considered in early-beta state: it has been tested against only one WMTS server (NASA EODSIS). -More extensive testing is needed and feedback (to bradh@frogmouth.net) would be appreciated. +PLEASE NOTE: the owslib wmts module should be considered in early-beta +state: it has been tested against only one WMTS server (NASA EODSIS). +More extensive testing is needed and feedback (to bradh@frogmouth.net) +would be appreciated. """ @@ -37,16 +40,48 @@ from ows import ServiceProvider, ServiceIdentification, OperationsMetadata +_OWS_NS = '{http://www.opengis.net/ows/1.1}' _WMTS_NS = '{http://www.opengis.net/wmts/1.0}' +_XLINK_NS = '{http://www.w3.org/1999/xlink}' + +_ABSTRACT_TAG = _OWS_NS + 'Abstract' +_IDENTIFIER_TAG = _OWS_NS + 'Identifier' +_LOWER_CORNER_TAG = _OWS_NS + 'LowerCorner' +_OPERATIONS_METADATA_TAG = _OWS_NS + 'OperationsMetadata' +_SERVICE_IDENTIFICATION_TAG = _OWS_NS + 'ServiceIdentification' +_SERVICE_PROVIDER_TAG = _OWS_NS + 'ServiceProvider' +_SUPPORTED_CRS_TAG = _OWS_NS + 'SupportedCRS' +_TITLE_TAG = _OWS_NS + 'Title' +_UPPER_CORNER_TAG = _OWS_NS + 'UpperCorner' +_WGS84_BOUNDING_BOX_TAG = _OWS_NS + 'WGS84BoundingBox' + +_CONTENTS_TAG = _WMTS_NS + 'Contents' +_FORMAT_TAG = _WMTS_NS + 'Format' +_INFO_FORMAT_TAG = _WMTS_NS + 'InfoFormat' +_LAYER_TAG = _WMTS_NS + 'Layer' +_LAYER_REF_TAG = _WMTS_NS + 'LayerRef' +_MATRIX_HEIGHT_TAG = _WMTS_NS + 'MatrixHeight' +_MATRIX_WIDTH_TAG = _WMTS_NS + 'MatrixWidth' +_MAX_TILE_COL_TAG = _WMTS_NS + 'MaxTileCol' +_MAX_TILE_ROW_TAG = _WMTS_NS + 'MaxTileRow' +_MIN_TILE_COL_TAG = _WMTS_NS + 'MinTileCol' +_MIN_TILE_ROW_TAG = _WMTS_NS + 'MinTileRow' +_RESOURCE_URL_TAG = _WMTS_NS + 'ResourceURL' +_SCALE_DENOMINATOR_TAG = _WMTS_NS + 'ScaleDenominator' +_SERVICE_METADATA_URL_TAG = _WMTS_NS + 'ServiceMetadataURL' +_STYLE_TAG = _WMTS_NS + 'Style' +_THEME_TAG = _WMTS_NS + 'Theme' +_THEMES_TAG = _WMTS_NS + 'Themes' +_TILE_HEIGHT_TAG = _WMTS_NS + 'TileHeight' _TILE_MATRIX_SET_LINK_TAG = _WMTS_NS + 'TileMatrixSetLink' _TILE_MATRIX_SET_TAG = _WMTS_NS + 'TileMatrixSet' _TILE_MATRIX_SET_LIMITS_TAG = _WMTS_NS + 'TileMatrixSetLimits' _TILE_MATRIX_LIMITS_TAG = _WMTS_NS + 'TileMatrixLimits' _TILE_MATRIX_TAG = _WMTS_NS + 'TileMatrix' -_MIN_TILE_ROW_TAG = _WMTS_NS + 'MinTileRow' -_MAX_TILE_ROW_TAG = _WMTS_NS + 'MaxTileRow' -_MIN_TILE_COL_TAG = _WMTS_NS + 'MinTileCol' -_MAX_TILE_COL_TAG = _WMTS_NS + 'MaxTileCol' +_TILE_WIDTH_TAG = _WMTS_NS + 'TileWidth' +_TOP_LEFT_CORNER_TAG = _WMTS_NS + 'TopLeftCorner' + +_HREF_TAG = _XLINK_NS + 'href' class ServiceException(Exception): @@ -75,35 +110,57 @@ class WebMapTileService(object): Implements IWebMapService. """ - def __getitem__(self,name): - ''' check contents dictionary to allow dict like access to service layers''' + 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 + raise KeyError("No content named %s" % name) + + def __init__(self, url, version='1.0.0', xml=None, username=None, + password=None, parse_remote_metadata=False, + vendor_kwargs=None): + """Initialize. + Parameters + ---------- + url : string + Base URL for the WMTS service. + version : string + Optional WMTS version. Defaults to '1.0.0'. + xml : string + Optional XML content to use as the content for the initial + GetCapabilities request. Typically only used for testing. + username : string + Optional user name for authentication. + password : string + Optional password for authentication. + parse_remote_metadata: string + Currently unused. + vendor_kwargs : dict + Optional vendor-specific parameters to be included in all + requests. - def __init__(self, url, version='1.0.0', xml=None, - username=None, password=None, parse_remote_metadata=False - ): - """Initialize.""" + """ self.url = url self.username = username self.password = password self.version = version + self.vendor_kwargs = vendor_kwargs self._capabilities = None # Authentication handled by Reader - reader = WMTSCapabilitiesReader( - self.version, url=self.url, un=self.username, pw=self.password - ) + reader = WMTSCapabilitiesReader(self.version, url=self.url, + un=self.username, pw=self.password) if xml: # read from stored xml self._capabilities = reader.readString(xml) else: # read from server - self._capabilities = reader.read(self.url) + self._capabilities = reader.read(self.url, self.vendor_kwargs) - # avoid building capabilities metadata if the response is a ServiceExceptionReport + # Avoid building capabilities metadata if the response is a + # ServiceExceptionReport. # TODO: check if this needs a namespace se = self._capabilities.find('ServiceException') if se is not None: @@ -118,70 +175,78 @@ def _getcapproperty(self): reader = WMTSCapabilitiesReader( self.version, url=self.url, un=self.username, pw=self.password ) - self._capabilities = ServiceMetadata(reader.read(self.url)) + xml = reader.read(self.url, self.vendor_kwargs) + self._capabilities = ServiceMetadata(xml) return self._capabilities def _buildMetadata(self, parse_remote_metadata=False): ''' set up capabilities metadata objects ''' - #serviceIdentification metadata - serviceident=self._capabilities.find('{http://www.opengis.net/ows/1.1}ServiceIdentification') - self.identification=ServiceIdentification(serviceident) + # serviceIdentification metadata + serviceident = self._capabilities.find(_SERVICE_IDENTIFICATION_TAG) + self.identification = ServiceIdentification(serviceident) - #serviceProvider metadata - serviceprov=self._capabilities.find('{http://www.opengis.net/ows/1.1}ServiceProvider') - self.provider=ServiceProvider(serviceprov) + # serviceProvider metadata + serviceprov = self._capabilities.find(_SERVICE_PROVIDER_TAG) + self.provider = ServiceProvider(serviceprov) - #serviceOperations metadata - self.operations=[] - for elem in self._capabilities.find('{http://www.opengis.net/ows/1.1}OperationsMetadata')[:]: + # serviceOperations metadata + self.operations = [] + for elem in self._capabilities.find(_OPERATIONS_METADATA_TAG)[:]: self.operations.append(OperationsMetadata(elem)) - #serviceContents metadata: our assumption is that services use a top-level - #layer as a metadata organizer, nothing more. - self.contents={} - caps = self._capabilities.find('{http://www.opengis.net/wmts/1.0}Contents') + # serviceContents metadata: our assumption is that services use + # a top-level layer as a metadata organizer, nothing more. + self.contents = {} + caps = self._capabilities.find(_CONTENTS_TAG) def gather_layers(parent_elem, parent_metadata): - for index, elem in enumerate(parent_elem.findall('{http://www.opengis.net/wmts/1.0}Layer')): - cm = ContentMetadata(elem, parent=parent_metadata, index=index+1, parse_remote_metadata=parse_remote_metadata) + for index, elem in enumerate(parent_elem.findall(_LAYER_TAG)): + cm = ContentMetadata( + elem, parent=parent_metadata, index=index+1, + parse_remote_metadata=parse_remote_metadata) if cm.id: if cm.id in self.contents: - raise KeyError('Content metadata for layer "%s" already exists' % cm.id) + raise KeyError('Content metadata for layer "%s" ' + 'already exists' % cm.id) self.contents[cm.id] = cm gather_layers(elem, cm) gather_layers(caps, None) self.tilematrixsets = {} - for elem in caps.findall('{http://www.opengis.net/wmts/1.0}TileMatrixSet'): + for elem in caps.findall(_TILE_MATRIX_SET_TAG): tms = TileMatrixSet(elem) if tms.identifier: if tms.identifier in self.tilematrixsets: - raise KeyError('TileMatrixSet with identifier "%s" already exists' % tms.identifier) + raise KeyError('TileMatrixSet with identifier "%s" ' + 'already exists' % tms.identifier) self.tilematrixsets[tms.identifier] = tms self.themes = {} - for elem in self._capabilities.findall('{http://www.opengis.net/wmts/1.0}Themes/{http://www.opengis.net/wmts/1.0}Theme'): + for elem in self._capabilities.findall(_THEMES_TAG + '/' + _THEME_TAG): theme = Theme(elem) if theme.identifier: if theme.identifier in self.themes: - raise KeyError('Theme with identifier "%s" already exists' % theme.identifier) + raise KeyError('Theme with identifier "%s" already exists' + % theme.identifier) self.themes[theme.identifier] = theme - serviceMetadataURL = self._capabilities.find('{http://www.opengis.net/wmts/1.0}ServiceMetadataURL') + serviceMetadataURL = self._capabilities.find(_SERVICE_METADATA_URL_TAG) if serviceMetadataURL is not None: - self.serviceMetadataURL = serviceMetadataURL.attrib['{http://www.w3.org/1999/xlink}href'] + self.serviceMetadataURL = serviceMetadataURL.attrib[_HREF_TAG] else: self.serviceMetadataURL = None def items(self): '''supports dict-like items() access''' - items=[] + items = [] for item in self.contents: - items.append((item,self.contents[item])) + items.append((item, self.contents[item])) return items - def buildTileRequest(self, layer=None, style=None, format=None, tilematrixset=None, tilematrix=None, row=None, column=None, **kwargs): + def buildTileRequest(self, layer=None, style=None, format=None, + tilematrixset=None, tilematrix=None, row=None, + column=None, **kwargs): """Return the URL-encoded parameters for a GetTile request. Parameters @@ -252,12 +317,14 @@ def buildTileRequest(self, layer=None, style=None, format=None, tilematrixset=No request.append(('FORMAT', format)) for key, value in kwargs.iteritems(): - request.append(key, value) + request.append((key, value)) data = urlencode(request, True) return data - def gettile(self, base_url=None, layer=None, style=None, format=None, tilematrixset=None, tilematrix=None, row=None, column=None, **kwargs): + def gettile(self, base_url=None, layer=None, style=None, format=None, + tilematrixset=None, tilematrix=None, row=None, column=None, + **kwargs): """Return a tile from the WMTS. Returns the tile image as a file-like object. @@ -303,15 +370,27 @@ def gettile(self, base_url=None, layer=None, style=None, format=None, tilematrix >>> out.close() """ - data = self.buildTileRequest(layer, style, format, tilematrixset, tilematrix, row, column, **kwargs) + vendor_kwargs = self.vendor_kwargs or {} + vendor_kwargs.update(kwargs) + data = self.buildTileRequest(layer, style, format, tilematrixset, + tilematrix, row, column, **vendor_kwargs) if base_url is None: base_url = self.url try: - get_verbs = filter(lambda x: x.get('type').lower() == 'get', self.getOperationByName('GetTile').methods) + get_verbs = filter( + lambda x: x.get('type').lower() == 'get', + self.getOperationByName('GetTile').methods) if len(get_verbs) > 1: # Filter by constraints - base_url = next(x for x in filter(list, ([pv.get('url') for const in pv.get('constraints') if 'kvp' in map(lambda x: x.lower(), const.values)] for pv in get_verbs if pv.get('constraints'))))[0] + base_url = next( + x for x in filter( + list, + ([pv.get('url') + for const in pv.get('constraints') + if 'kvp' in map( + lambda x: x.lower(), const.values)] + for pv in get_verbs if pv.get('constraints'))))[0] elif len(get_verbs) == 1: base_url = get_verbs[0].get('url') except StopIteration: @@ -341,74 +420,80 @@ def getOperationByName(self, name): for item in self.operations: if item.name == name: return item - raise KeyError, "No operation named %s" % name + raise KeyError("No operation named %s" % name) + class TileMatrixSet(object): '''Holds one TileMatrixSet''' def __init__(self, elem): - if elem.tag != '{http://www.opengis.net/wmts/1.0}TileMatrixSet': + if elem.tag != _TILE_MATRIX_SET_TAG: raise ValueError('%s should be a TileMatrixSet' % (elem,)) - self.identifier = testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Identifier')).strip() - self.crs = testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}SupportedCRS')).strip() - if (self.crs == None) or (self.identifier == None): + self.identifier = testXMLValue(elem.find(_IDENTIFIER_TAG)).strip() + self.crs = testXMLValue(elem.find(_SUPPORTED_CRS_TAG)).strip() + if self.crs is None or self.identifier is None: raise ValueError('%s incomplete TileMatrixSet' % (elem,)) self.tilematrix = {} - for tilematrix in elem.findall('{http://www.opengis.net/wmts/1.0}TileMatrix'): + for tilematrix in elem.findall(_TILE_MATRIX_TAG): tm = TileMatrix(tilematrix) if tm.identifier: if tm.identifier in self.tilematrix: - raise KeyError('TileMatrix with identifier "%s" already exists' % tm.identifier) + raise KeyError('TileMatrix with identifier "%s" ' + 'already exists' % tm.identifier) self.tilematrix[tm.identifier] = tm + class TileMatrix(object): '''Holds one TileMatrix''' def __init__(self, elem): - if elem.tag != '{http://www.opengis.net/wmts/1.0}TileMatrix': + if elem.tag != _TILE_MATRIX_TAG: raise ValueError('%s should be a TileMatrix' % (elem,)) - self.identifier = testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Identifier')).strip() - sd = testXMLValue(elem.find('{http://www.opengis.net/wmts/1.0}ScaleDenominator')) + self.identifier = testXMLValue(elem.find(_IDENTIFIER_TAG)).strip() + sd = testXMLValue(elem.find(_SCALE_DENOMINATOR_TAG)) if sd is None: raise ValueError('%s is missing ScaleDenominator' % (elem,)) self.scaledenominator = float(sd) - tl = testXMLValue(elem.find('{http://www.opengis.net/wmts/1.0}TopLeftCorner')) + tl = testXMLValue(elem.find(_TOP_LEFT_CORNER_TAG)) if tl is None: raise ValueError('%s is missing TopLeftCorner' % (elem,)) (lon, lat) = tl.split(" ") self.topleftcorner = (float(lon), float(lat)) - width = testXMLValue(elem.find('{http://www.opengis.net/wmts/1.0}TileWidth')) - height = testXMLValue(elem.find('{http://www.opengis.net/wmts/1.0}TileHeight')) + width = testXMLValue(elem.find(_TILE_WIDTH_TAG)) + height = testXMLValue(elem.find(_TILE_HEIGHT_TAG)) if (width is None) or (height is None): - raise ValueError('%s is missing TileWidth and/or TileHeight' % (elem,)) + msg = '%s is missing TileWidth and/or TileHeight' % (elem,) + raise ValueError(msg) self.tilewidth = int(width) self.tileheight = int(height) - mw = testXMLValue(elem.find('{http://www.opengis.net/wmts/1.0}MatrixWidth')) - mh = testXMLValue(elem.find('{http://www.opengis.net/wmts/1.0}MatrixHeight')) + mw = testXMLValue(elem.find(_MATRIX_WIDTH_TAG)) + mh = testXMLValue(elem.find(_MATRIX_HEIGHT_TAG)) if (mw is None) or (mh is None): - raise ValueError('%s is missing MatrixWidth and/or MatrixHeight' % (elem,)) + msg = '%s is missing MatrixWidth and/or MatrixHeight' % (elem,) + raise ValueError(msg) self.matrixwidth = int(mw) self.matrixheight = int(mh) + class Theme: """ Abstraction for a WMTS theme """ def __init__(self, elem): - if elem.tag != '{http://www.opengis.net/wmts/1.0}Theme': + if elem.tag != _THEME_TAG: raise ValueError('%s should be a Theme' % (elem,)) - self.identifier = testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Identifier')).strip() - title = testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Title')) + self.identifier = testXMLValue(elem.find(_IDENTIFIER_TAG)).strip() + title = testXMLValue(elem.find(_TITLE_TAG)) if title is not None: self.title = title.strip() else: self.title = None - abstract = testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Abstract')) + abstract = testXMLValue(elem.find(_ABSTRACT_TAG)) if abstract is not None: self.abstract = abstract.strip() else: self.abstract = None self.layerRefs = [] - layerRefs = elem.findall('{http://www.opengis.net/wmts/1.0}LayerRef') + layerRefs = elem.findall(_LAYER_REF_TAG) for layerRef in layerRefs: if layerRef.text is not None: self.layerRefs.append(layerRef.text) @@ -465,9 +550,9 @@ def from_elements(link_elements): set_limits_elements = link_element.findall( _TILE_MATRIX_SET_LIMITS_TAG) if set_limits_elements: - raise ValueError('Multiple instances of TileMatrixSet' - ' plus TileMatrixSetLimits in %s' % - link_element) + raise ValueError('Multiple instances of TileMatrixSet' + ' plus TileMatrixSetLimits in %s' % + link_element) for matrix_set_element in matrix_set_elements: uri = matrix_set_element.text.strip() links.append(TileMatrixSetLink(uri)) @@ -475,12 +560,15 @@ def from_elements(link_elements): uri = matrix_set_elements[0].text.strip() tilematrixlimits = {} - path = '%s/%s' % (_TILE_MATRIX_SET_LIMITS_TAG, _TILE_MATRIX_LIMITS_TAG) + path = '%s/%s' % (_TILE_MATRIX_SET_LIMITS_TAG, + _TILE_MATRIX_LIMITS_TAG) for limits_element in link_element.findall(path): tml = TileMatrixLimits(limits_element) if tml.tilematrix: if tml.tilematrix in tilematrixlimits: - raise KeyError('TileMatrixLimits with tileMatrix "%s" already exists' % tml.tilematrix) + msg = ('TileMatrixLimits with tileMatrix "%s" ' + 'already exists' % tml.tilematrix) + raise KeyError(msg) tilematrixlimits[tml.tilematrix] = tml links.append(TileMatrixSetLink(uri, tilematrixlimits)) @@ -506,8 +594,9 @@ class ContentMetadata: Implements IContentMetadata. """ - def __init__(self, elem, parent=None, index=0, parse_remote_metadata=False): - if elem.tag != '{http://www.opengis.net/wmts/1.0}Layer': + def __init__(self, elem, parent=None, index=0, + parse_remote_metadata=False): + if elem.tag != _LAYER_TAG: raise ValueError('%s should be a Layer' % (elem,)) self.parent = parent @@ -516,25 +605,26 @@ def __init__(self, elem, parent=None, index=0, parse_remote_metadata=False): else: self.index = str(index) - self.id = self.name = testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Identifier')) + self.id = self.name = testXMLValue(elem.find(_IDENTIFIER_TAG)) # title is mandatory property self.title = None - title = testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Title')) + title = testXMLValue(elem.find(_TITLE_TAG)) if title is not None: self.title = title.strip() - self.abstract = testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Abstract')) + self.abstract = testXMLValue(elem.find(_ABSTRACT_TAG)) # bboxes - b = elem.find('{http://www.opengis.net/ows/1.1}WGS84BoundingBox') + b = elem.find(_WGS84_BOUNDING_BOX_TAG) self.boundingBox = None if b is not None: - lc = b.find("{http://www.opengis.net/ows/1.1}LowerCorner") - uc = b.find("{http://www.opengis.net/ows/1.1}UpperCorner") + lc = b.find(_LOWER_CORNER_TAG) + uc = b.find(_UPPER_CORNER_TAG) ll = [float(s) for s in lc.text.split()] ur = [float(s) for s in uc.text.split()] - self.boundingBoxWGS84 = (ll[0],ll[1],ur[0],ur[1]) - # TODO: there is probably some more logic here, and it should probably be shared code + self.boundingBoxWGS84 = (ll[0], ll[1], ur[0], ur[1]) + # TODO: there is probably some more logic here, and it should + # probably be shared code self._tilematrixsets = [f.text.strip() for f in elem.findall(_TILE_MATRIX_SET_LINK_TAG + '/' + @@ -552,32 +642,32 @@ def __init__(self, elem, parent=None, index=0, parse_remote_metadata=False): self.tilematrixsetlinks[tmsl.tilematrixset] = tmsl self.resourceURLs = [] - for resourceURL in elem.findall('{http://www.opengis.net/wmts/1.0}ResourceURL'): + for resourceURL in elem.findall(_RESOURCE_URL_TAG): resource = {} for attrib in ['format', 'resourceType', 'template']: resource[attrib] = resourceURL.attrib[attrib] self.resourceURLs.append(resource) - #Styles + # Styles self.styles = {} - for s in elem.findall('{http://www.opengis.net/wmts/1.0}Style'): + for s in elem.findall(_STYLE_TAG): style = {} isdefaulttext = s.attrib.get('isDefault') style['isDefault'] = (isdefaulttext == "true") - identifier = s.find('{http://www.opengis.net/ows/1.1}Identifier') + identifier = s.find(_IDENTIFIER_TAG) if identifier is None: raise ValueError('%s missing identifier' % (s,)) - title = s.find('{http://www.opengis.net/ows/1.1}Title') + title = s.find(_TITLE_TAG) if title is not None: style['title'] = title.text self.styles[identifier.text] = style - self.formats = [f.text for f in elem.findall('{http://www.opengis.net/wmts/1.0}Format')] + self.formats = [f.text for f in elem.findall(_FORMAT_TAG)] - self.infoformats = [f.text for f in elem.findall('{http://www.opengis.net/wmts/1.0}InfoFormat')] + self.infoformats = [f.text for f in elem.findall(_INFO_FORMAT_TAG)] self.layers = [] - for child in elem.findall('{http://www.opengis.net/wmts/1.0}Layer'): + for child in elem.findall(_LAYER_TAG): self.layers.append(ContentMetadata(child, self)) @property @@ -606,37 +696,41 @@ def __init__(self, version='1.0.0', url=None, un=None, pw=None): self.username = un self.password = pw - def capabilities_url(self, service_url): + def capabilities_url(self, service_url, vendor_kwargs=None): """Return a capabilities url """ - qs = [] - if service_url.find('?') != -1: - qs = urlparse.parse_qsl(service_url.split('?')[1]) - - params = [x[0] for x in qs] - - if 'service' not in params: - qs.append(('service', 'WMTS')) - if 'request' not in params: - qs.append(('request', 'GetCapabilities')) - if 'version' not in params: - qs.append(('version', self.version)) - - urlqs = urlencode(tuple(qs)) - return service_url.split('?')[0] + '?' + urlqs - - def read(self, service_url): + # Ensure the 'service', 'request', and 'version' parameters, + # and any vendor-specific parameters are included in the URL. + pieces = urlparse.urlparse(service_url) + args = urlparse.parse_qs(pieces.query) + if 'service' not in args: + args['service'] = 'WMTS' + if 'request' not in args: + args['request'] = 'GetCapabilities' + if 'version' not in args: + args['version'] = self.version + if vendor_kwargs: + args.update(vendor_kwargs) + query = urlencode(args, doseq=True) + pieces = urlparse.ParseResult(pieces.scheme, pieces.netloc, + pieces.path, pieces.params, + query, pieces.fragment) + return urlparse.urlunparse(pieces) + + def read(self, service_url, vendor_kwargs=None): """Get and parse a WMTS capabilities document, returning an elementtree instance service_url is the base url, to which is appended the service, - version, and request parameters + version, and request parameters. Optional vendor-specific + parameters can also be supplied as a dict. """ - getcaprequest = self.capabilities_url(service_url) + getcaprequest = self.capabilities_url(service_url, vendor_kwargs) - #now split it up again to use the generic openURL function... - spliturl=getcaprequest.split('?') - u = openURL(spliturl[0], spliturl[1], method='Get', username = self.username, password = self.password) + # now split it up again to use the generic openURL function... + spliturl = getcaprequest.split('?') + u = openURL(spliturl[0], spliturl[1], method='Get', + username=self.username, password=self.password) return etree.fromstring(u.read()) def readString(self, st): @@ -645,5 +739,6 @@ def readString(self, st): string should be an XML capabilities document """ if not isinstance(st, str): - raise ValueError("String must be of type string, not %s" % type(st)) + msg = 'String must be of type string, not %s' % type(st) + raise ValueError(msg) return etree.fromstring(st) diff --git a/owslib/wps.py b/owslib/wps.py index fb16ea620..3479fab5f 100644 --- a/owslib/wps.py +++ b/owslib/wps.py @@ -88,7 +88,7 @@ from owslib.ows import DEFAULT_OWS_NAMESPACE, ServiceIdentification, ServiceProvider, OperationsMetadata from time import sleep from owslib.util import (testXMLValue, build_get_url, dump, getTypedValue, - getNamespace, element_to_string, nspath, openURL, nspath_eval) + getNamespace, element_to_string, nspath, openURL, nspath_eval, log) from xml.dom.minidom import parseString from owslib.namespaces import Namespaces @@ -196,8 +196,7 @@ def getcapabilities(self, xml=None): else: self._capabilities = reader.readFromUrl(self.url, username=self.username, password=self.password) - if self.verbose==True: - print element_to_string(self._capabilities) + log.debug(element_to_string(self._capabilities)) # populate the capabilities metadata obects from the XML tree self._parseCapabilitiesMetadata(self._capabilities) @@ -217,8 +216,7 @@ def describeprocess(self, identifier, xml=None): # read from server rootElement = reader.readFromUrl(self.url, identifier) - if self.verbose==True: - print element_to_string(rootElement) + log.info(element_to_string(rootElement)) # build metadata objects return self._parseProcessMetadata(rootElement) @@ -236,15 +234,15 @@ def execute(self, identifier, inputs, output=None, request=None, response=None): """ # instantiate a WPSExecution object - print 'Executing WPS request...' + log.info('Executing WPS request...') execution = WPSExecution(version=self.version, url=self.url, username=self.username, password=self.password, verbose=self.verbose) # build XML request from parameters if request is None: requestElement = execution.buildRequest(identifier, inputs, output) - request = etree.tostring( requestElement ) - if self.verbose==True: - print request + request = etree.tostring( requestElement ) + execution.request = request + log.debug(request) # submit the request to the live server if response is None: @@ -252,8 +250,7 @@ def execute(self, identifier, inputs, output=None, request=None, response=None): else: response = etree.fromstring(response) - if self.verbose==True: - print etree.tostring(response) + log.debug(etree.tostring(response)) # parse response execution.parseResponse(response) @@ -358,8 +355,7 @@ def _readFromUrl(self, url, data, method='Get', username=None, password=None): if method == 'Get': # full HTTP request url request_url = build_get_url(url, data) - if self.verbose==True: - print request_url + log.debug(request_url) # split URL into base url and query string to use utility function spliturl=request_url.split('?') @@ -581,21 +577,20 @@ def checkStatus(self, url=None, response=None, sleepSecs=60): # override status location if url is not None: self.statusLocation = url - print '\nChecking execution status... (location=%s)' % self.statusLocation + log.info('\nChecking execution status... (location=%s)' % self.statusLocation) response = reader.readFromUrl(self.statusLocation, username=self.username, password=self.password) else: response = reader.readFromString(response) # store latest response self.response = etree.tostring(response) - if self.verbose==True: - print self.response + log.debug(self.response) self.parseResponse(response) # sleep given number of seconds if self.isComplete()==False: - print 'Sleeping %d seconds...' % sleepSecs + log.info('Sleeping %d seconds...' % sleepSecs) sleep(sleepSecs) @@ -654,7 +649,7 @@ def getOutput(self, filepath=None): out = open(filepath, 'wb') out.write(content) out.close() - print 'Output written to file: %s' %filepath + log.info('Output written to file: %s' %filepath) else: raise Exception("Execution not successfully completed: status=%s" % self.status) @@ -700,12 +695,12 @@ def parseResponse(self, response): self._parseExceptionReport(response) else: - print 'Unknown Response' + log.debug('Unknown Response') - # print status, errors - print 'Execution status=%s' % self.status - print 'Percent completed=%s' % self.percentCompleted - print 'Status message=%s' % self.statusMessage + # log status, errors + log.info('Execution status=%s' % self.status) + log.info('Percent completed=%s' % self.percentCompleted) + log.info('Status message=%s' % self.statusMessage) for error in self.errors: dump(error) @@ -1026,7 +1021,6 @@ def __init__(self, outputElement): if complexDataElement is not None: self.dataType = "ComplexData" self.mimeType = complexDataElement.get('mimeType') - #print etree.tostring(complexDataElement) if complexDataElement.text is not None and complexDataElement.text.strip() is not '': self.data.append(complexDataElement.text.strip()) for child in complexDataElement: @@ -1051,7 +1045,7 @@ def retrieveData(self, username=None, password=None): # a) 'http://cida.usgs.gov/climate/gdp/process/RetrieveResultServlet?id=1318528582026OUTPUT.601bb3d0-547f-4eab-8642-7c7d2834459e' # b) 'http://rsg.pml.ac.uk/wps/wpsoutputs/outputImage-11294Bd6l2a.tif' - print 'Output URL=%s' % url + log.info('Output URL=%s' % url) if '?' in url: spliturl=url.split('?') u = openURL(spliturl[0], spliturl[1], method='Get', username = username, password = password) @@ -1091,7 +1085,7 @@ def writeToDisk(self, path=None, username=None, password=None): out = open(self.filePath, 'wb') out.write(content) out.close() - print 'Output written to file: %s' %self.filePath + log.info('Output written to file: %s' %self.filePath) class WPSException: @@ -1336,7 +1330,7 @@ def monitorExecution(execution, sleepSecs=3, download=False, filepath=None): while execution.isComplete()==False: execution.checkStatus(sleepSecs=sleepSecs) - print 'Execution status: %s' % execution.status + log.info('Execution status: %s' % execution.status) if execution.isSucceded(): if download: @@ -1344,10 +1338,10 @@ def monitorExecution(execution, sleepSecs=3, download=False, filepath=None): else: for output in execution.processOutputs: if output.reference is not None: - print 'Output URL=%s' % output.reference + log.info('Output URL=%s' % output.reference) else: for ex in execution.errors: - print 'Error: code=%s, locator=%s, text=%s' % (ex.code, ex.locator, ex.text) + log.error('Error: code=%s, locator=%s, text=%s' % (ex.code, ex.locator, ex.text)) def printValue(value): ''' diff --git a/requirements-dev.txt b/requirements-dev.txt index e2ad8c1c0..d51e4fc26 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,4 @@ +pep8 pytest pytest-cov Pillow diff --git a/setup.py b/setup.py index c185517c7..3aa423683 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def run_tests(self): errno = pytest.main(self.test_args) sys.exit(errno) -readme = open('README.txt').read() +readme = open('README.md').read() reqs = [line.strip() for line in open('requirements.txt')] if sys.version[:3] < '2.7': @@ -29,7 +29,7 @@ def run_tests(self): author_email = 'sean.gillies@gmail.com', maintainer = 'Tom Kralidis', maintainer_email = 'tomkralidis@gmail.com', - url = 'https://geopython.github.io/OWSLib', + url = 'http://geopython.github.io/OWSLib', install_requires = reqs, cmdclass = {'test': PyTest}, packages = find_packages(exclude=["docs", "etc", "examples", "tests"]), diff --git a/tests/doctests/csw_uuid_constrain.txt b/tests/doctests/csw_uuid_constrain.txt index 69b3bbd04..e9ca0edd6 100644 --- a/tests/doctests/csw_uuid_constrain.txt +++ b/tests/doctests/csw_uuid_constrain.txt @@ -66,7 +66,9 @@ Initialize CSW client ########################################################## >>> filter = fes.PropertyIsNull(propertyname='sys.siteuuid') >>> c.getrecords2([filter]) - >>> c.results == {'matches': 2, 'nextrecord': 0, 'returned': 2} + >>> c.results['matches'] < 1 + True + >>> c.results['nextrecord'] == 0 True diff --git a/tests/doctests/sos_10_getcapabilities.txt b/tests/doctests/sos_10_getcapabilities.txt index 08776d7dd..fd759649d 100644 --- a/tests/doctests/sos_10_getcapabilities.txt +++ b/tests/doctests/sos_10_getcapabilities.txt @@ -112,7 +112,7 @@ OperationsMetadata >>> getcap = ndbc.get_operation_by_name('GetCapabilities') >>> isinstance(getcap, OperationsMetadata) True - + Get by name (case insensitive) >>> getcap = ndbc.get_operation_by_name('getcapabilities') @@ -174,6 +174,16 @@ Contents >>> len(contents) 848 +Dict access __getitem__ + >>> ndbc['station-46084'].name + 'urn:ioos:station:wmo:46084' + + >>> ndbc['rubbishrubbish'].name + Traceback (most recent call last): + ... + KeyError: 'No Observational Offering with id: rubbishrubbish' + + Network >>> network = contents['network-all'] >>> network.id @@ -181,10 +191,10 @@ Network >>> network.name 'urn:ioos:network:noaa.nws.ndbc:all' - + >>> network.description 'All stations on the NDBC SOS server' - + >>> srs = network.srs >>> isinstance(srs, Crs) True @@ -204,14 +214,14 @@ Network 'urn:ogc:def:crs:EPSG::4326' >>> bbsrs.getcode() 'EPSG:4326' - + >>> bp = network.begin_position >>> isinstance(bp, datetime) True >>> ep = network.end_position >>> isinstance(ep, datetime) True - + >>> network.result_model 'om:Observation' @@ -251,7 +261,7 @@ Station >>> station.description "Zeke's Basin, North Carolina" - + >>> srs = station.srs >>> isinstance(srs, Crs) True @@ -261,7 +271,7 @@ Station 'EPSG:4326' >>> cast_tuple_int_list(station.bbox) [-77, 33, -77, 33] - + >>> bbsrs = station.bbox_srs >>> isinstance(bbsrs, Crs) True diff --git a/tests/doctests/sos_20_52N_demo.txt b/tests/doctests/sos_20_52N_demo.txt new file mode 100644 index 000000000..b52097d51 --- /dev/null +++ b/tests/doctests/sos_20_52N_demo.txt @@ -0,0 +1,59 @@ +# Tests using the 52North demo service + +>>> from owslib.sos import SensorObservationService +>>> service = SensorObservationService('http://sensorweb.demo.52north.org/52n-sos-webapp/sos/kvp',version='2.0.0') +>>> for content in service.contents: +... print content +... +test1 +http://www.52north.org/test/offering/8 +http://www.52north.org/test/offering/9 +myOfferingXYT +http://www.example.org/offering/http://www.52north.org/test/procedure/9x/observations +http://www.52north.org/test/offering/2 +http://www.52north.org/test/offering/3 +http://www.52north.org/test/offering/1 +http://www.52north.org/test/offering/6 +http://www.52north.org/test/offering/7 +http://www.52north.org/test/offering/4 +http://www.52north.org/test/offering/5 +http://www.example.org/offering/http://www.52north.org/test/procedure/9x4/observations +http://www.52north.org/test/offering/29 +asdafagfa +http://www.52north.org/test/offering/19 +http://www.52north.org/test/offering/SHUSIV_1 +z1 +http://www.52north.org/test/offering/22 + +>>> id = service.identification + +# Check basic service metadata +>>> id.service +'OGC:SOS' + +>>> id.title +'52N SOS' + +>>> provider=service.provider +>>> provider.name +'52North' + +>>> len(service.operations) +13 + +# Check allowed params for get FOI +>>> get_foi=service.get_operation_by_name('GetFeatureOfInterest') +>>> get_foi.parameters['featureOfInterest']['values'] +['http://www.52north.org/test/featureOfInterest/1', 'http://www.52north.org/test/featureOfInterest/19', 'http://www.52north.org/test/featureOfInterest/2', 'http://www.52north.org/test/featureOfInterest/3', 'http://www.52north.org/test/featureOfInterest/4', 'http://www.52north.org/test/featureOfInterest/5', 'http://www.52north.org/test/featureOfInterest/6', 'http://www.52north.org/test/featureOfInterest/7', 'http://www.52north.org/test/featureOfInterest/8', 'http://www.52north.org/test/featureOfInterest/9'] + +# Check allowed params for get observation +>>> get_obs=service.get_operation_by_name('GetObservation') + +get_obs.parameters['responseFormat']['values'] +['http://www.opengis.net/om/2.0'] + +# Get observation call + +# Get latest value using O&M reponse format +#'\n\n \n \n \n \n \n 2014-07-10T04:31:58.000Z\n \n \n \n \n \n \n 17.2\n \n \n' +>>> latest_response = service.get_observation('http://www.opengis.net/om/2.0', ['http://www.52north.org/test/offering/1'], ['http://www.52north.org/test/observableProperty/1'], eventTime='om:phenomenonTime,latest', timeout=120) diff --git a/tests/doctests/sos_20_52n_geoviqua.txt b/tests/doctests/sos_20_52n_geoviqua.txt new file mode 100644 index 000000000..175672538 --- /dev/null +++ b/tests/doctests/sos_20_52n_geoviqua.txt @@ -0,0 +1,70 @@ +# SOS version 2.0 tests using the 52North 'Geoviqua' service + +# Imports +>>> from owslib.sos import SensorObservationService + +# Setup +>>> service = SensorObservationService('http://geoviqua.dev.52north.org/SOS-Q/sos/kvp',version='2.0.0') + +>>> for content in service.contents: +... print content +... +http://www.52north.org/test/offering/9 +http://kli.uni-muenster.de/stations/rks/observations +http://geoviqua.dev.52north.org/procedures/ws2500 +http://kli.uni-muenster.de/stations/hbs/observations +http://geoviqua.dev.52north.org/procedures/ws2500-internal +http://geoviqua.dev.52north.org/procedures/WXT520 + +>>> id = service.identification + +# Check basic service metadata +>>> id.service +'OGC:SOS' + +>>> id.title +'52N SOS for GeoViQua Project' + +>>> id.keywords +['citizen science', 'climate', 'quality', 'testbed', 'weather'] + +>>> provider=service.provider +>>> provider.name +'52North' + +>>> provider.contact.name +u'Eike Hinderk J\xfcrrens' +>>> provider.contact.position +'Software Developer' + +>>> len(service.operations) +16 + +# Check allowed params for get FOI +>>> get_foi=service.get_operation_by_name('GetFeatureOfInterest') +>>> get_foi.parameters['featureOfInterest']['values'] +['http://52north.org/fac/internal/it-is/srv-01', 'http://geoviqua.dev.52north.org/stations/ELV-WS2500', 'http://geoviqua.dev.52north.org/stations/Vaisala-WXT520', 'http://kli.uni-muenster.de/stations/hbs', 'http://kli.uni-muenster.de/stations/rks', 'http://www.52north.org/test/featureOfInterest/9'] + +# Check allowed params for get observation +>>> get_obs=service.get_operation_by_name('GetObservation') + +>>> get_obs.parameters['responseFormat']['values'] +['application/json', 'http://www.opengis.net/om/2.0', 'http://www.opengis.net/waterml-dr/2.0', 'http://www.opengis.net/waterml/2.0'] + +# Get observation call +# These can time out depending on the state of the service + +# Get latest value - no checks at this stage +#'\n\n \n \n \n \n \n 2014-07-10T04:31:58.000Z\n \n \n \n \n \n \n 17.2\n \n \n' +>>> latest_response = service.get_observation('http://www.opengis.net/om/2.0', ['http://geoviqua.dev.52north.org/procedures/WXT520'], ['http://geoviqua.dev.52north.org/properties/AirTemperature'], eventTime='om:phenomenonTime,latest', timeout=120) + +# Specific time period request +#'\n\n \n \n \n \n \n 2014-07-01T00:01:42.000Z\n \n \n \n \n \n \n 12.2\n \n \n \n \n \n \n 2014-07-01T01:01:42.000Z\n \n \n \n \n \n \n 12.1\n \n \n \n \n \n \n 2014-07-01T00:46:42.000Z\n \n \n \n \n \n \n 11.9\n \n \n \n \n \n \n 2014-07-01T01:46:41.000Z\n \n \n \n \n \n \n 12.3\n \n \n \n \n \n \n 2014-07-01T00:16:42.000Z\n \n \n \n \n \n \n 12.3\n \n \n \n \n \n \n 2014-07-01T01:16:41.000Z\n \n \n \n \n \n \n 12.5\n \n \n \n \n \n \n 2014-07-01T00:31:42.000Z\n \n \n \n \n \n \n 12.0\n \n \n \n \n \n \n 2014-07-01T01:31:41.000Z\n \n \n \n \n \n \n 12.3\n \n \n' +>>> time_range_response = service.get_observation('http://www.opengis.net/om/2.0', ['http://geoviqua.dev.52north.org/procedures/WXT520'], ['http://geoviqua.dev.52north.org/properties/AirTemperature'], eventTime='om:phenomenonTime,2014-07-01T00:00:00Z/2014-07-01T02:00:00Z') + +# WaterML2.0 domain-range request +>>> wml2_dr_response = service.get_observation('http://www.opengis.net/waterml-dr/2.0', ['http://geoviqua.dev.52north.org/procedures/WXT520'], ['http://geoviqua.dev.52north.org/properties/AirTemperature'], eventTime='om:phenomenonTime,2014-07-01T00:00:00Z/2014-07-01T02:00:00Z') + +# WaterML2.0 interleaved time-series response +>>> wml2_response = service.get_observation('http://www.opengis.net/waterml/2.0', ['http://geoviqua.dev.52north.org/procedures/WXT520'], ['http://geoviqua.dev.52north.org/properties/AirTemperature'], eventTime='om:phenomenonTime,2014-07-01T00:00:00Z/2014-07-01T01:30:00Z') + diff --git a/tests/doctests/sos_20_timeseries_decoder_ioos.txt b/tests/doctests/sos_20_timeseries_decoder_ioos.txt new file mode 100644 index 000000000..6d9a35a4a --- /dev/null +++ b/tests/doctests/sos_20_timeseries_decoder_ioos.txt @@ -0,0 +1,57 @@ +# SOS version 2.0 tests using the 52North installation for IOOS: http://ioossos.axiomalaska.com/ + +# Imports +>>> from owslib.sos import SensorObservationService +>>> from owslib.swe.observation.sos200 import SOSGetObservationResponse +>>> from owslib.etree import etree + +# Setup +#>>> service = SensorObservationService('http://geoviqua.dev.52north.org/SOS-Q/sos/kvp',version='2.0.0') +>>> service = SensorObservationService('http://ioossos.axiomalaska.com/52n-sos-ioos-stable/sos/kvp',version='2.0.0') + +# http://ioossos.axiomalaska.com/52n-sos-ioos-stable/sos/kvp?service=SOS&request=GetObservation&namespaces=xmlns(om%2Chttp%3A%2F%2Fwww.opengis.net%2Fom%2F2.0)&temporalFilter=om%3AphenomenonTime%2Clatest&version=2.0.0 + +# Check allowed params for get observation +>>> get_obs=service.get_operation_by_name('GetObservation') + +>>> response = service.get_observation(responseFormat='http://www.opengis.net/om/2.0', offerings=['urn:ioos:station:test:8'], observedProperties=['http://mmisw.org/ont/cf/parameter/sea_water_temperature'], timeout=60) + +>>> xml_tree = etree.fromstring(response) + +>>> parsed_response = SOSGetObservationResponse(xml_tree) + +>>> type(parsed_response) + + +>>> o=parsed_response.observations[0] + +## Value changes each call so can't be tested +## >>> o.get_result().value + +>>> o.get_result().uom +'urn:ogc:def:uom:udunits:2:Cel' + +# This O&M structure of the results splits each point into an O&M object, resulting in 400 results +>>> len(parsed_response.observations) +400 +>>> type(parsed_response.observations[0]) + + +# Get observation for a specific offering (in this case corresponds to a station) and observed property (sea water temperature) +>>> response = service.get_observation(responseFormat='http://www.opengis.net/waterml/2.0', offerings=['urn:ioos:station:test:8'], observedProperties=['http://mmisw.org/ont/cf/parameter/sea_water_temperature'], timeout=60) +>>> xml_tree = etree.fromstring(response) +>>> parsed_response = SOSGetObservationResponse(xml_tree) +>>> type(parsed_response) + + +>>> len(parsed_response.observations) +20 +>>> type(parsed_response.observations[0]) + +>>> type(parsed_response.observations[0].get_result()) + + +>>> measurement_timeseries = parsed_response.observations[0].get_result() +>>> len(measurement_timeseries) +20 + diff --git a/tests/doctests/wfs_MapServerWFSCapabilities.txt b/tests/doctests/wfs_MapServerWFSCapabilities.txt index 80d736def..ccae23375 100644 --- a/tests/doctests/wfs_MapServerWFSCapabilities.txt +++ b/tests/doctests/wfs_MapServerWFSCapabilities.txt @@ -21,19 +21,19 @@ Service Identification: '1.0' >>> wfs.identification.title - 'Bird Studies Canada WMS/WFS Server' + 'Atlas of the Cryosphere: Southern Hemisphere' >>> wfs.identification.abstract - 'Bird Studies Canada WMS/WFS Server for bird distribution and abundance data, and related information. Bird Studies Canada gratefully acknowledges the support of Environment Canada - Canadian Information System for the Environment in developing this service.' + "The National Snow and Ice Data Center (NSIDC) Atlas of the Cryosphere is a map server that provides data and information pertinent to the frozen regions of Earth, including monthly climatologies of sea ice extent and concentration, snow cover extent, and snow water equivalent, in addition to glacier outlines, ice sheet elevation and accumulation, and more. In order to support polar projections, the Atlas is divided into two separate map servers: one for the Northern Hemisphere and one for the Southern Hemisphere. In addition to providing map images and source data through Open Geospatial Consortium, Inc. (OGC) protocols (WMS, WFS, and WCS), a dynamic web interface for exploring these data is also available at http://nsidc.org/data/atlas. If you have questions, comments or suggestions, please contact NSIDC User Services at +1.303.492.6199 or nsidc@nsidc.org. The development of this map server application was supported by NASA's Earth Observing System (EOS) Program under contract NAS5-03099 and was developed using MapServer, an Open Source development environment for building spatially-enabled internet applications. To cite the Atlas of the Cryosphere: Maurer, J. 2007. Atlas of the Cryosphere. Boulder, Colorado USA: National Snow and Ice Data Center. Digital media. Available at http://nsidc.org/data/atlas/." >>> wfs.identification.keywords - ['\n birds\n distribution\n abundance\n conservation\n sites\n monitoring\n populations\n canada\n '] + ['\n Antarctica\n Cryosphere\n Earth Science\n Fronts\n Glacial Landforms/Processes\n Glaciers\n Ice Sheets\n Oceans\n Ocean Circulation\n Ocean Currents\n Polar\n Southern Hemisphere\n '] >>> wfs.identification.accessconstraints - 'None' + 'none' >>> wfs.identification.fees - 'None' + 'none' Service Provider: @@ -41,7 +41,7 @@ Service Provider: 'MapServer WFS' >>> wfs.provider.url - 'http://geodiscover.cgdi.ca/gdp/search?action=entrySummary&entryType=webService&entryId=3920&entryLang=en&portal=gdp' + 'http://nsidc.org' #TODO: test contact info: #>>> wfs.provider.contact.name @@ -50,25 +50,25 @@ Service Provider: Test available content layers >>> sorted(wfs.contents.keys()) - ['BBS_PT', 'CBC_PT', 'CBC_PY', 'CLLS', 'IBA', 'MMP', 'OBBA_BLOCK', 'OBBA_REGION', 'OBBA_SQUARE', 'OWLS'] + ['antarctic_coastline', 'antarctic_continent', 'antarctic_ice_cores', 'antarctic_ice_shelves_fill', 'antarctic_ice_shelves_outline', 'antarctic_islands', 'antarctic_islands_coastlines', 'antarctic_megadunes', 'antarctic_polar_front', 'antarctic_research_stations', 'antarctica_country_border', 'antarctica_elevation_contours', 'antarctica_islands_coastlines', 'coastlines_excluding_antarctica', 'country_borders_excluding_antarctica', 'glacier_outlines', 'glaciers', 'international_date_line', 'land_excluding_antarctica', 'south_pole_geographic', 'south_pole_geomagnetic', 'south_pole_inaccessibility', 'south_pole_magnetic', 'south_pole_of_cold', 'south_poles_wfs'] >>> sorted([wfs[layer].title for layer in wfs.contents]) - ['Breeding Bird Survey Route Start Points', 'Canadian Christmas Bird Count Locations', 'Canadian Christmas Bird Count Locations', 'Canadian Important Bird Areas', 'Canadian Lakes Loon Survey Locations', 'Marsh Monitoring Program Route Locations', 'Nocturnal Owl Survey Locations', 'Ontario Breeding Bird Atlas 10 km Squares', 'Ontario Breeding Bird Atlas 100 km Blocks', 'Ontario Breeding Bird Atlas Administrative Regions'] + ['Antarctic Polar Front', 'Antarctic coastline (includes ice shelves)', 'Antarctic continent', 'Antarctic grounding line (excludes ice shelves)', 'Antarctic ice core locations', 'Antarctic ice shelves', 'Antarctic island coastlines', 'Antarctic island coastlines', 'Antarctic islands', 'Antarctic megadunes', 'Antarctic permanent research stations', 'Antarctic suface elevation contours', 'Antarctica border', 'International Date Line', 'South Pole of Cold', 'South Pole of Inaccessibility', 'South Pole, Geographic', 'South Pole, Geomagnetic', 'South Pole, Magnetic', 'South Poles', 'coastlines (excluding Antarctica)', 'countries (excluding Antarctica)', 'glacier outlines', 'glaciers', 'land (excluding Antarctica)'] Test single item accessor - >>> wfs['IBA'].title - 'Canadian Important Bird Areas' + >>> wfs['glaciers'].title + 'glaciers' - >>> wfs['IBA'].boundingBox + >>> wfs['glaciers'].boundingBox - >>> cast_tuple_int_list(wfs['IBA'].boundingBoxWGS84) - [-141, 41, -52, 78] + >>> cast_tuple_int_list(wfs['glaciers'].boundingBoxWGS84) + [-11887400000, -850889000, 557154000, 262891000] - >>> map(lambda x: x.getcode(), wfs['IBA'].crsOptions) - ['EPSG:4326'] + >>> map(lambda x: x.getcode(), wfs['glaciers'].crsOptions) + ['EPSG:3031'] - >>> wfs['IBA'].verbOptions + >>> wfs['glaciers'].verbOptions ['{http://www.opengis.net/wfs}Query'] Expect a KeyError for invalid names @@ -84,11 +84,11 @@ Test operations ['DescribeFeatureType', 'GetCapabilities', 'GetFeature'] >>> x = sorted(wfs.getOperationByName('GetFeature').methods, key=itemgetter('type')) - >>> x == [{'type': 'Get', 'url': 'http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?'}, {'type': 'Post', 'url': 'http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?'}] + >>> x == [{'type': 'Get', 'url': 'http://nsidc.org/cgi-bin/atlas_south?'}, {'type': 'Post', 'url': 'http://nsidc.org/cgi-bin/atlas_south?'}] True >>> sorted(wfs.getOperationByName('GetFeature').formatOptions) - ['{http://www.opengis.net/wfs}GML2', '{http://www.opengis.net/wfs}GML3'] + ['{http://www.opengis.net/wfs}GML2'] Test exceptions @@ -98,8 +98,7 @@ Test exceptions Lastly, test the getcapabilities method - >>> wfs = WebFeatureService('http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?', version='1.0') + >>> wfs = WebFeatureService('http://nsidc.org/cgi-bin/atlas_south?', version='1.0') >>> xml = wfs.getcapabilities().read() >>> xml.find(' 0 True - diff --git a/tests/doctests/wfs_MapServerWFSFeature.txt b/tests/doctests/wfs_MapServerWFSFeature.txt index dcf9e1074..2aad82c18 100644 --- a/tests/doctests/wfs_MapServerWFSFeature.txt +++ b/tests/doctests/wfs_MapServerWFSFeature.txt @@ -5,8 +5,8 @@ Imports Test the getfeature method - >>> wfs = WebFeatureService('http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?', version='1.0.0') - >>> response = wfs.getfeature(typename=['IBA'], maxfeatures=5) + >>> wfs = WebFeatureService('http://nsidc.org/cgi-bin/atlas_south?', version='1.0.0') + >>> response = wfs.getfeature(typename=['glaciers'], maxfeatures=5) >>> response.read().find(' 0 True diff --git a/tests/doctests/wps_execute.txt b/tests/doctests/wps_execute.txt index 8763eb1e5..1b3244b4d 100644 --- a/tests/doctests/wps_execute.txt +++ b/tests/doctests/wps_execute.txt @@ -3,8 +3,9 @@ This test does not execute any live HTTP request, rather it parses XML files con Imports - >>> from tests.utils import resource_file + >>> from tests.utils import resource_file, setup_logging >>> from owslib.wps import WebProcessingService + >>> logger = setup_logging('INFO') Initialize WPS client diff --git a/tests/doctests/wps_execute_invalid_request.txt b/tests/doctests/wps_execute_invalid_request.txt index f3c2b57e5..86584eab1 100644 --- a/tests/doctests/wps_execute_invalid_request.txt +++ b/tests/doctests/wps_execute_invalid_request.txt @@ -3,8 +3,9 @@ This test does not execute any live HTTP request, rather it parses XML files con Imports - >>> from tests.utils import resource_file + >>> from tests.utils import resource_file, setup_logging >>> from owslib.wps import WebProcessingService + >>> logger = setup_logging('INFO') Initialize WPS client diff --git a/tests/doctests/wps_response6.txt b/tests/doctests/wps_response6.txt index 5a8bf537f..cb21e5a78 100644 --- a/tests/doctests/wps_response6.txt +++ b/tests/doctests/wps_response6.txt @@ -1,7 +1,8 @@ Instantiate WPS - >>> from tests.utils import resource_file, compare_xml + >>> from tests.utils import resource_file, compare_xml, setup_logging >>> from owslib.wps import WebProcessingService + >>> logger = setup_logging('INFO') >>> wps = WebProcessingService('http://rsg.pml.ac.uk/wps/vector.cgi') Execute face WPS invocation diff --git a/tests/resources/mapserver-wfs-cap.xml b/tests/resources/mapserver-wfs-cap.xml index 577be9b79..ac056ab8d 100644 --- a/tests/resources/mapserver-wfs-cap.xml +++ b/tests/resources/mapserver-wfs-cap.xml @@ -1,25 +1,35 @@ - - + + - + MapServer WFS - Bird Studies Canada WMS/WFS Server - Bird Studies Canada WMS/WFS Server for bird distribution and abundance data, and related information. Bird Studies Canada gratefully acknowledges the support of Environment Canada - Canadian Information System for the Environment in developing this service. + Atlas of the Cryosphere: Southern Hemisphere + The National Snow and Ice Data Center (NSIDC) Atlas of the Cryosphere is a map server that provides data and information pertinent to the frozen regions of Earth, including monthly climatologies of sea ice extent and concentration, snow cover extent, and snow water equivalent, in addition to glacier outlines, ice sheet elevation and accumulation, and more. In order to support polar projections, the Atlas is divided into two separate map servers: one for the Northern Hemisphere and one for the Southern Hemisphere. In addition to providing map images and source data through Open Geospatial Consortium, Inc. (OGC) protocols (WMS, WFS, and WCS), a dynamic web interface for exploring these data is also available at http://nsidc.org/data/atlas. If you have questions, comments or suggestions, please contact NSIDC User Services at +1.303.492.6199 or nsidc@nsidc.org. The development of this map server application was supported by NASA's Earth Observing System (EOS) Program under contract NAS5-03099 and was developed using MapServer, an Open Source development environment for building spatially-enabled internet applications. To cite the Atlas of the Cryosphere: Maurer, J. 2007. Atlas of the Cryosphere. Boulder, Colorado USA: National Snow and Ice Data Center. Digital media. Available at http://nsidc.org/data/atlas/. - birds - distribution - abundance - conservation - sites - monitoring - populations - canada + Antarctica + Cryosphere + Earth Science + Fronts + Glacial Landforms/Processes + Glaciers + Ice Sheets + Oceans + Ocean Circulation + Ocean Currents + Polar + Southern Hemisphere - http://geodiscover.cgdi.ca/gdp/search?action=entrySummary&entryType=webService&entryId=3920&entryLang=en&portal=gdp - None - None + http://nsidc.org + none + none @@ -27,44 +37,42 @@ - + - + - - + - + - - + - + @@ -76,197 +84,408 @@ - IBA - Canadian Important Bird Areas - This layer represents *generalized* boundaries of Important Bird Areas (IBA) in Canada. IBAs, in Canada and elsewhere, are evaluated and designated on the basis of internationally criteria. The polygons contained in the dataset meet these criteria and are therefore deemed significant at the national, continental, or global level. In Canada, IBAs are not legally recognized entities and therefore do not have any regulatory status. Nevertheless, they are extremely useful for conservation planning for birds. Polygon boundaries were digitized on-screen in ArcView GIS 3.2 by using NTDB 1:50K or 1:250K digital layers for reference. If NTDB coverage was not available (e.g., far north), the Digital Chart of the World (1:1M) was used. The boundaries of IBAs in this dataset are intentionally generalized to reflect the importance of the surrounding ecosystem on the values present within IBAs. For example, small islands supporting colonies of seabirds might be buffered by 1 or 2 km to form an IBA. Boundaries for a very small minority of IBAs were available in paper form as part of IBA conservation plans that had been developed previously. In such cases, we visually estimated their boundaries against the NTDB or other base layers. Many IBAs are already recognized as other types of protected areas, e.g., National Wildlife Areas, National and Provincial Parks, Migratory Bird Sanctuaries, conservation reserves, and so on. In these cases, we used digital boundaries from other sources (e.g., WWF Canada's Designated Areas Database, Environment Canada's Canadian Conservation Areas Database) as reference points when digitizing IBA boundaries. Please cite as follows: Bird Studies Canada and Nature Canada. 2004. Important Bird Areas of Canada Database. Port Rowan, Ontario: Bird Studies Canada. To access the Canadian IBA directory: http://www.bsc-eoc.org/iba/ibasites.html. Full metadata for this layer: http://www.bsc-eoc.org/website/metadata/caniba_poly.xml + antarctic_ice_shelves_fill + Antarctic ice shelves + Bohlander, J. and T. Scambos. 2007. Antarctic coastlines and grounding line derived from MODIS Mosaic of Antarctica (MOA). Boulder, Colorado USA: National Snow and Ice Data Center. Digital media. Accessed 24 April 2008. Background: These outlines were hand-digitized from the Moderate-resolution Imaging Spectroradiometer (MODIS) mosaic of Antarctica (MOA) image map produced and distributed by the National Snow and Ice Data Center (NSIDC) at: http://nsidc.org/data/nsidc-0280.html. The coastline includes the ice shelves surrounding Antarctica while the grounding line excludes them. Coastlines for islands surrounding Antarctica are provided separately from the rest of Antarctica's grounding line. - iba - birdlife - birds - conservation - sites - canada + Antarctica + Coastlines + Cryosphere + Ice Sheets + Ice Shelves + Polar + Shorelines - EPSG:4326 - - http://www.bsc-eoc.org/website/metadata/caniba_poly.xml + EPSG:3031 + - CBC_PT - Canadian Christmas Bird Count Locations - A winter survey of bird populations conducted during the Christmas period - December 14 to January 5th inclusive. More than 1800 counts are conducted each year across Canada, the United States and Latin America. On a selected day within the count period, local birders count as many birds as possible within a 24 km (15 mile) diameter circle. The Christmas Bird Count had long been organized on the continental scale by the New York-based National Audubon Society. In 2000, Audubon finalized an agreement with Bird Studies Canada that would see BSC partner with Audubon to coordinate CBCs in Canada. This layer represents the centre point of all the Christmas Bird Counts conducted in Canada. An accompanying layer (CBC_PY) contains the 24 km circles. + antarctic_continent + Antarctic continent + Bohlander, J. and T. Scambos. 2007. Antarctic coastlines and grounding line derived from MODIS Mosaic of Antarctica (MOA). Boulder, Colorado USA: National Snow and Ice Data Center. Digital media. Accessed 24 April 2008. Background: These outlines were hand-digitized from the Moderate-resolution Imaging Spectroradiometer (MODIS) mosaic of Antarctica (MOA) image map produced and distributed by the National Snow and Ice Data Center (NSIDC) at: http://nsidc.org/data/nsidc-0280.html. The coastline includes the ice shelves surrounding Antarctica while the grounding line excludes them. Coastlines for islands surrounding Antarctica are provided separately from the rest of Antarctica's grounding line. - birds - winter - survey - population - volunteers - cbc - christmas - canada + Antarctica + Coastlines + Cryosphere + Ice Sheets + Ice Shelves + Polar + Shorelines - EPSG:4326 - + EPSG:3031 + - CBC_PY - Canadian Christmas Bird Count Locations - A winter survey of bird populations conducted during the Christmas period - December 14 to January 5th inclusive. More than 1800 counts are conducted each year across Canada, the United States and Latin America. On a selected day within the count period, local birders count as many birds as possible within a 24 km (15 mile) diameter circle. The Christmas Bird Count had long been organized on the continental scale by the New York-based National Audubon Society. In 2000, Audubon finalized an agreement with Bird Studies Canada that would see BSC partner with Audubon to coordinate CBCs in Canada. This layer represents the 24 km circles surveyed as part of each Christmas Bird Count conducted in Canada. An accompanying layer (CBC_PT) contains the centre point of the circles. + antarctic_islands + Antarctic islands + Bohlander, J. and T. Scambos. 2007. Antarctic coastlines and grounding line derived from MODIS Mosaic of Antarctica (MOA). Boulder, Colorado USA: National Snow and Ice Data Center. Digital media. Accessed 24 April 2008. Background: These outlines were hand-digitized from the Moderate-resolution Imaging Spectroradiometer (MODIS) mosaic of Antarctica (MOA) image map produced and distributed by the National Snow and Ice Data Center (NSIDC) at: http://nsidc.org/data/nsidc-0280.html. The coastline includes the ice shelves surrounding Antarctica while the grounding line excludes them. Coastlines for islands surrounding Antarctica are provided separately from the rest of Antarctica's grounding line. - birds - winter - survey - population - volunteers - cbc - christmas - canada + Antarctica + Coastlines + Cryosphere + Ice Sheets + Ice Shelves + Polar + Shorelines - EPSG:4326 - + EPSG:3031 + - MMP - Marsh Monitoring Program Route Locations - A Spring survey of birds and amphibians carried out primarily within the Great Lakes region of North America. + land_excluding_antarctica + land (excluding Antarctica) + Center for International Earth Science Information Network (CIESIN), Columbia University; and Centro Internacional de Agricultura Tropical (CIAT). 2005. Gridded Population of the World Version 3 (GPWv3): Coastlines. Palisades, NY, USA: Socioeconomic Data and Applications Center (SEDAC), Columbia University. Available at http://sedac.ciesin.columbia.edu/gpw. 19 January 2007. Background: Coastlines derived from the Gridded Population of the World (GPW) land area grid at 2.5 arc-minute resolution. Permanent ice and all but large lakes have been merged with neighbouring polygons to make a layer more appropriate for cartographic visualization of the data. Does not include Antarctica. - birds - waterbirds - amphibians - marsh - wetland - survey - populations - volunteers - canada - united states - great lakes - AOC + Coastlines + Global + Shorelines - EPSG:4326 - + EPSG:3031 + - CLLS - Canadian Lakes Loon Survey Locations - A volunteer-based program that monitors the effects of acid rain and other human disturbance on loon breeding and presence on Canada's cottage lakes and other waterbodies. + antarctica_elevation_contours + Antarctic suface elevation contours + Liu, H., K. Jezek, B. Li, and Z. Zhao. 2001. Radarsat Antarctic Mapping Project digital elevation model version 2. Boulder, CO, USA: National Snow and Ice Data Center. Digital media. Available at http://nsidc.org/data/nsidc-0082.html. 01 November 2006. Background: The high-resolution Radarsat Antarctic Mapping Project (RAMP) Digital Elevation Model (DEM) combines topographic data from a variety of sources to provide consistent coverage of all of Antarctica. Version 2 improves upon the original version by incorporating new topographic data, error corrections, extended coverage, and other modifications. The DEM incorporates topographic data from satellite radar altimetry, airborne radar surveys, the recently-updated Antarctic Digital Database (version 2), and large-scale topographic maps from the U.S. Geological Survey (USGS) and the Australian Antarctic Division. Data were collected between the 1940s and present, with most collected during the 1980s and 1990s. Although the RAMP DEM was created to aid in processing RAMP radar data, it does not utilize any RAMP radar data. The Atlas of the Cryosphere uses the RAMP DEM gridded at 1 km. - birds - Gavia immer - loons - lakes - survey - populations - volunteers - canada - acid rain - disturbance - ecosystem health + Antarctica + Cryosphere + Earth Science + Ice Sheets + Ice Sheet Elevation + Polar - EPSG:4326 - + EPSG:3031 + + http://nsidc.org/cgi-bin/get_metadata.pl?id=nsidc-0082&format=fgdc - OBBA_SQUARE - Ontario Breeding Bird Atlas 10 km Squares - A sampling unit used by the Ontario Breeding Bird Atlas to collect breeding evidence data for all bird species within the Province of Ontario. Squares are based upon the Universal Transverse Mercator (UTM) grid system, NAD83, and measure 10Km by 10Km except near UTM zone boundaries, where they are smaller. + glaciers + glaciers + National Imagery and Mapping Agency (NIMA). 1992. VMAP_1V10 - Vector Map Level 0 (Digital Chart of the World). Bethesda, MD, USA: National Imagery and Mapping Agency (NIMA). Available at http://www.maproom.psu.edu/dcw/ and http://webgis.wr.usgs.gov/globalgis/. 01 September 2000. Background: The primary source for this database is the U.S. Defense Mapping Agency's (DMA) Operational Navigation Chart (ONC) 1:1,000,000 scale paper map series produced by the United States, Australia, Canada, and the United Kingdom. These charts were designed to meet the needs of pilots and air crews in medium-and low-altitude en route navigation and to support military operational planning, intelligence briefings, and other needs. Level 0 (low resolution) coverage is global, and is entirely in the public domain. The National Imagery and Mapping Agency (NIMA) is a federal agency of the United States Government and is now known as the National Geospatial-Intelligence Agency (NGA). - birds - distribution - abundance - breeding range - survey - populations - volunteers - Ontario + Antarctica + Cryosphere + Earth Science + Global + Glaciers + Polar + Southern Hemisphere - EPSG:4326 - + EPSG:3031 + - OBBA_REGION - Ontario Breeding Bird Atlas Administrative Regions - An administrative unit that is used for local bird atlas co-ordination, which is based on bioregions and access. Each region is assigned a Regional Coordinator who administers bird surveys within their region. + glacier_outlines + glacier outlines + Armstrong, R., B. Raup, S.J.S. Khalsa, R. Barry, J. Kargel, C. Helm, and H. Kiefer. 2005. GLIMS glacier database. Boulder, CO, USA: National Snow and Ice Data Center. Available at http://nsidc.org/data/nsidc-0272.html. 24 August 2006. Background: Global Land Ice Measurements from Space (GLIMS) is an international project with the goal of surveying a majority of the world's estimated 160,000 glaciers. GLIMS uses data collected primarily by the Advanced Spaceborne Thermal Emission and Reflection Radiometer (ASTER) instrument aboard the Terra satellite and the LANDSAT Enhanced Thematic Mapper Plus (ETM+), along with historical observations. The GLIMS project is currently creating a unique glacier inventory, storing information about the extent and rates of change of all the world's glacial resources. GLIMS consists of many institutions called Regional Centers, who produce glacier analyses for their particular region. The GLIMS Glacier Database provides students, educators, scientists, and the public with reliable glacier data from these analyses. New glacier data are continually being added to the database. - birds - distribution - abundance - breeding range - survey - populations - volunteers - Ontario + Antarctica + Cryosphere + Earth Science + Global + Glaciers + Polar + Southern Hemisphere - EPSG:4326 - + EPSG:3031 + + http://nsidc.org/cgi-bin/get_metadata.pl?id=nsidc-0272&format=fgdc - OBBA_BLOCK - Ontario Breeding Bird Atlas 100 km Blocks - A sampling unit used by the Ontario Breeding Bird Atlas to collect breeding evidence data for all bird species within the Province of Ontario. Blocks are based upon the Universal Transverse Mercator (UTM) grid system, NAD83, and measure 100Km by 100Km except near UTM zone boundaries, where they are smaller. + coastlines_excluding_antarctica + coastlines (excluding Antarctica) + Center for International Earth Science Information Network (CIESIN), Columbia University; and Centro Internacional de Agricultura Tropical (CIAT). 2005. Gridded Population of the World Version 3 (GPWv3): Coastlines. Palisades, NY, USA: Socioeconomic Data and Applications Center (SEDAC), Columbia University. Available at http://sedac.ciesin.columbia.edu/gpw. 19 January 2007. Background: Coastlines derived from the Gridded Population of the World (GPW) land area grid at 2.5 arc-minute resolution. Permanent ice and all but large lakes have been merged with neighbouring polygons to make a layer more appropriate for cartographic visualization of the data. Does not include Antarctica. - birds - distribution - abundance - breeding range - survey - populations - volunteers - Ontario + Coastlines + Global + Shorelines - EPSG:4326 - + EPSG:3031 + - OWLS - Nocturnal Owl Survey Locations - A volunteer-based program that monitors the abundance of owl species across Canada by means of broadcast surveys. Ontario survey locations are currently available through this service. + antarctic_ice_shelves_outline + Antarctic coastline (includes ice shelves) + Bohlander, J. and T. Scambos. 2007. Antarctic coastlines and grounding line derived from MODIS Mosaic of Antarctica (MOA). Boulder, Colorado USA: National Snow and Ice Data Center. Digital media. Accessed 24 April 2008. Background: These outlines were hand-digitized from the Moderate-resolution Imaging Spectroradiometer (MODIS) mosaic of Antarctica (MOA) image map produced and distributed by the National Snow and Ice Data Center (NSIDC) at: http://nsidc.org/data/nsidc-0280.html. The coastline includes the ice shelves surrounding Antarctica while the grounding line excludes them. Coastlines for islands surrounding Antarctica are provided separately from the rest of Antarctica's grounding line. - birds - owls - survey - populations - volunteers - canada - broadcast + Antarctica + Coastlines + Cryosphere + Ice Sheets + Polar + Shorelines - EPSG:4326 - + EPSG:3031 + - BBS_PT - Breeding Bird Survey Route Start Points - The Breeding Bird Survey, initiated in 1966, is one of the oldest surveys of breeding birds in North America. It is conducted primarily by volunteers, who follow a predetermined, roadside route each year at the height of the breeding season. This layer portrays the start point for each BBS route in Canada. + antarctic_coastline + Antarctic grounding line (excludes ice shelves) + Bohlander, J. and T. Scambos. 2007. Antarctic coastlines and grounding line derived from MODIS Mosaic of Antarctica (MOA). Boulder, Colorado USA: National Snow and Ice Data Center. Digital media. Accessed 24 April 2008. Background: These outlines were hand-digitized from the Moderate-resolution Imaging Spectroradiometer (MODIS) mosaic of Antarctica (MOA) image map produced and distributed by the National Snow and Ice Data Center (NSIDC) at: http://nsidc.org/data/nsidc-0280.html. The coastline includes the ice shelves surrounding Antarctica while the grounding line excludes them. Coastlines for islands surrounding Antarctica are provided separately from the rest of Antarctica's grounding line. - birds - breeding - survey - roadside - populations - volunteers - canada + Antarctica + Coastlines + Cryosphere + Ice Sheets + Polar + Shorelines - EPSG:4326 - + EPSG:3031 + + + + antarctic_islands_coastlines + Antarctic island coastlines + Bohlander, J. and T. Scambos. 2007. Antarctic coastlines and grounding line derived from MODIS Mosaic of Antarctica (MOA). Boulder, Colorado USA: National Snow and Ice Data Center. Digital media. Accessed 24 April 2008. Background: These outlines were hand-digitized from the Moderate-resolution Imaging Spectroradiometer (MODIS) mosaic of Antarctica (MOA) image map produced and distributed by the National Snow and Ice Data Center (NSIDC) at: http://nsidc.org/data/nsidc-0280.html. The coastline includes the ice shelves surrounding Antarctica while the grounding line excludes them. Coastlines for islands surrounding Antarctica are provided separately from the rest of Antarctica's grounding line. + + Antarctica + Coastlines + Cryosphere + Ice Sheets + Polar + Shorelines + + EPSG:3031 + + + + country_borders_excluding_antarctica + countries (excluding Antarctica) + Center for International Earth Science Information Network (CIESIN), Columbia University; and Centro Internacional de Agricultura Tropical (CIAT). 2005. Gridded Population of the World Version 3 (GPWv3): National Boundaries. Palisades, NY, USA: Socioeconomic Data and Applications Center (SEDAC), Columbia University. Available at http://sedac.ciesin.columbia.edu/gpw. 19 January 2007. Background: National boundaries derived from the Gridded Population of the World (GPW) country-level land area grids at 2.5 arc-minute resolution. Permanent ice and all but large lakes have been merged with neighbouring polygons to make a layer more appropriate for cartographic visualization of the data. Does not include Antarctica. + + Boundaries + Global + Political Divisions + + EPSG:3031 + + + + antarctica_country_border + Antarctica border + Bohlander, J. and T. Scambos. 2007. Antarctic coastlines and grounding line derived from MODIS Mosaic of Antarctica (MOA). Boulder, Colorado USA: National Snow and Ice Data Center. Digital media. Accessed 24 April 2008. Background: These outlines were hand-digitized from the Moderate-resolution Imaging Spectroradiometer (MODIS) mosaic of Antarctica (MOA) image map produced and distributed by the National Snow and Ice Data Center (NSIDC) at: http://nsidc.org/data/nsidc-0280.html. The coastline includes the ice shelves surrounding Antarctica while the grounding line excludes them. Coastlines for islands surrounding Antarctica are provided separately from the rest of Antarctica's grounding line. + + Antarctica + Coastlines + Cryosphere + Ice Sheets + Polar + Shorelines + + EPSG:3031 + + + + antarctica_islands_coastlines + Antarctic island coastlines + Bohlander, J. and T. Scambos. 2007. Antarctic coastlines and grounding line derived from MODIS Mosaic of Antarctica (MOA). Boulder, Colorado USA: National Snow and Ice Data Center. Digital media. Accessed 24 April 2008. Background: These outlines were hand-digitized from the Moderate-resolution Imaging Spectroradiometer (MODIS) mosaic of Antarctica (MOA) image map produced and distributed by the National Snow and Ice Data Center (NSIDC) at: http://nsidc.org/data/nsidc-0280.html. The coastline includes the ice shelves surrounding Antarctica while the grounding line excludes them. Coastlines for islands surrounding Antarctica are provided separately from the rest of Antarctica's grounding line. + + Antarctica + Coastlines + Cryosphere + Ice Sheets + Polar + Shorelines + + EPSG:3031 + + + + antarctic_polar_front + Antarctic Polar Front + Orsi, A. and Ryan, U. 2001. Locations of the various fronts in the Southern Ocean. Kingston, Tasmania, Australia: Australian Antarctic Data Centre. Digital media. Available at http://aadc-maps.aad.gov.au/aadc/metadata/metadata_redirect.cfm?md=AMD/AU/southern_ocean_fronts. 28 September 2006. Background: Hydrographic observations were used in this study of the Southern Ocean to improve our knowledge of large-scale aspects of the Antarctic Circumpolar Current (ACC). The Antarctic Polar Front, also known as the Antarctic Convergence, is the southern front of the Antarctic Circumpolar Current that separates the Antarctic Zone in the south from the Polar Frontal Zone in the north. The Polar Front is taken by many to delineate the actual northern boundary of the Antarctic Zone. It is characterized by sea surface temperatures near 5-6 deg C and a salinity minimum of 33.8-34.0 psu produced by high rainfall. + + Antarctica + Cryosphere + Earth Science + Fronts + Oceans + Ocean Circulation + Ocean Currents + Polar + Southern Ocean + + EPSG:3031 + + + + international_date_line + International Date Line + National Geographic Society. 1992. National Geographic Atlas of the World, Revised Sixth Edition. Washington, D.C. USA: National Geographic Society. Compiled by J. Maurer. 2007. Boulder, Colorado USA: National Snow and Ice Data Center. Digital Media. Background: The International Date Line (IDL), also known as just the Date Line, is an imaginary line on the surface of the Earth opposite the Prime Meridian which offsets the date as one travels east or west across it. Roughly along 180 deg longitude, with diversions to pass around some territories and island groups, it corresponds to the time zone boundary separating +12 and -12 hours GMT (UT1). Crossing the IDL travelling east results in a day or 24 hours being subtracted, and crossing west results in a day being added. + + Boundaries + Date Line + International Date Line + + EPSG:3031 + + + + antarctic_megadunes + Antarctic megadunes + Bohlander, J. and T. Scambos. 2005. Outlines of Antarctic megadunes regions. Boulder, CO, USA: National Snow and Ice Data Center. Background: Unlike snow dunes that are piles of drifted snow, Antarctic megadunes are long, undulating waves in the surface of the ice sheet that are 2 to 4 meters (6.5 to 13 feet) high and 2 to 5 kilometers (1 to 3 miles) apart. They are slightly rounded at their crests and are so subtle that a person on the ground cannot see the pattern. Regions of megadunes on the Antarctic ice sheet have been identified and outlined using satellite remote sensing imagery. + + Antarctica + Cryosphere + Earth Science + Glacial Landforms/Processes + Ice Sheets + Megadunes + Polar + + EPSG:3031 + + + + antarctic_research_stations + Antarctic permanent research stations + Wikipedia contributors. 24 January 2007. List of research stations in Antarctica. Wikipedia, The Free Encyclopedia. Available at http://en.wikipedia.org/wiki/List_of_research_stations_in_Antarctica. 24 January 2007. Background: A number of governments maintain permanent research stations throughout Antarctica. Many of the stations are staffed around the year. A total of 30 countries (as of October 2006), all signatory to the Antarctic Treaty, operate seasonal (summer) and year-round research stations on the continent and in its surrounding oceans. The population of persons doing and supporting science on the continent and its nearby islands varies from approximately 4,000 persons during the summer season to 1,000 persons during winter. + + Antarctica + Cryosphere + Earth Science + Polar + Research Stations + + EPSG:3031 + + + + antarctic_ice_cores + Antarctic ice core locations + Maurer, J. compiler. 2009. Deep ice core locations. Boulder, Colorado USA: National Snow and Ice Data Center. Digital media. Accessed 03 May 2009. Background: Labels the locations of several deep ice cores from the Antarctic ice sheet, including: the European Project for Ice Coring in Antarctica (EPICA) Dome C; Siple Dome, West Antarctic Ice Sheet (WAIS) Dome A; Vostok; WAIS Divide; Byrd; Taylor Dome; Dome Fuji; Dome B; Komsomolskaya; and Talos Dome Ice Core (TALDICE). + + Antarctica + Cryosphere + Earth Science + Ice Core Records + Polar + + EPSG:3031 + + + + south_pole_geographic + South Pole, Geographic + Labels the location of the South Pole (90 deg S, 0 deg), also referred to as the Geographic South Pole. + + Antarctica + Geographic South Pole + Polar + South Pole + + EPSG:3031 + + + + south_pole_magnetic + South Pole, Magnetic + McClean, S. 24 January 2007. Geomagnetism Frequently Asked Questions. National Geophysical Data Center. http://www.ngdc.noaa.gov/seg/geomag/faqgeom.shtml#q4b. 24 January 2007. Background: Labels the location of the Magnetic South Pole, which in 2005 was computed to be 64.53 deg S, 137.86 deg E by the World Magnetic Model (WMM). The Earth's magnetic poles are the two points on the earth's surface at which magnetic meridians converge; the horizontal component of the magnetic field of the earth becomes zero at this point; also called the dip pole. The magnetic poles migrate over time. + + Antarctica + Earth Science + Geomagnetism + Magnetic Field + Magnetic South Pole + Polar + South Pole + + EPSG:3031 + + + + south_pole_geomagnetic + South Pole, Geomagnetic + McClean, S. 24 January 2007. Geomagnetism Frequently Asked Questions. National Geophysical Data Center. http://www.ngdc.noaa.gov/seg/geomag/faqgeom.shtml#q4b. 24 January 2007. Background: Labels the location of the Geomagnetic South Pole, which in 2005 was computed to be 79.74 deg S, 108.22 deg E by the World Magnetic Model (WMM). The Earth's geomagnetic poles are the points of intersection of the Earth's surface with the axis of a simple magnetic dipole that best approximates the Earth's actual, more complex magnetic field. If the Earth's magnetic field were a perfect dipole, then the field lines would be vertical at the geomagnetic poles and they would therefore coincide with the magnetic poles: however, the dipole approximation is in fact far from perfect, so in reality the magnetic and geomagnetic poles lie some distance apart. The geomagnetic poles migrate over time. + + Antarctica + Earth Science + Geomagnetic Indices + Geomagnetism + Geomagnetic South Pole + Polar + South Pole + + EPSG:3031 + + + + south_pole_inaccessibility + South Pole of Inaccessibility + Wikipedia contributors. 20 January 2007. South Pole. Wikipedia, The Free Encyclopedia. Available at http://en.wikipedia.org/w/index.php?title=South_Pole&oldid=101993204. 24 January 2007. Background: Labels the location of the South Pole of Inaccessibility (85.83 deg S, 65.78 deg E), which is the point on the Antarctic continent farthest from the Southern Ocean. This pole was reached on December 14, 1958 by the 3rd Soviet Antarctic Expedition, led by Yevgeny Tolstikov. At that point they established a temporary station named Polyus Nedostupnosti. + + Antarctica + Polar + South Pole + South Pole of Inaccessibility + + EPSG:3031 + + + + south_pole_of_cold + South Pole of Cold + Wikipedia contributors. 23 January 2007. Pole of Cold. Wikipedia, The Free Encyclopedia. Available at http://en.wikipedia.org/w/index.php?title=Pole_of_Cold&oldid=102629381. 24 January 2007. Background: The Poles of Cold are the places in the Northern and Southern hemispheres where the lowest air temperature was recorded. In the Southern hemisphere, the Pole of Cold is in Antarctica near the Russian Antarctic station Vostok at 78.47.S, 106.8.E where a temperature of -89.2.C (-129.8.F) was recorded on July 21, 1983. + + Antarctica + Cold Pole + Cryosphere + Earth Science + Polar + South Pole + South Pole of Cold + + EPSG:3031 + + + + south_poles_wfs + South Poles + Labels the location of various types of South Poles: geographic, geomagnetic, magnetic, pole of cold, and pole of inaccessibility. Citations: McClean, S. 2007. Geomagnetism frequently asked questions. Boulder, Colorado USA: National Geophysical Data Center (NGDC). Available at http://www.ngdc.noaa.gov/geomag/faqgeom.shtml. Accessed 24 January 2007; Wikipedia contributors. 2007. Pole of Cold. Wikipedia, The Free Encyclopedia. Available at http://en.wikipedia.org/wiki/Pole_of_Cold. Accessed 24 January 2007; Wikipedia contributors. 2007. South Pole. Wikipedia, The Free Encyclopedia. Available at http://en.wikipedia.org/w/index.php?title=South_Pole&oldid=101993204. Accessed 24 Janu +ary 2007. + + Antarctica + Cold Pole + Geographic South Pole + Geomagnetic Indices + Geomagnetism + Geomagnetic South Pole + Magnetic Field + Magnetic South Pole + Polar + South Pole + South Pole of Cold + South Pole of Inaccessibility + + EPSG:3031 + + + + + + + + - + - - - + + + - \ No newline at end of file + diff --git a/tests/resources/sos_52n_get_observation_ioos.xml b/tests/resources/sos_52n_get_observation_ioos.xml new file mode 100644 index 000000000..1059fe367 --- /dev/null +++ b/tests/resources/sos_52n_get_observation_ioos.xml @@ -0,0 +1,318 @@ + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 25.12 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 16.37 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 16.49 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 20.49 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 6.35 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 12.05 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 8.82 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 11.5 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 24.76 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 17.74 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 6.03 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 20.86 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 13.63 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 8.47 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 8.08 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 30.38 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 13.18 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 6.82 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 18.35 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 19.34 + + + + + + + + 2014-07-07T22:00:00.000Z + + + + + + + 11.58 + + + diff --git a/tests/resources/sos_52n_get_observation_ioos_wml2.xml b/tests/resources/sos_52n_get_observation_ioos_wml2.xml new file mode 100644 index 000000000..02d6ab424 --- /dev/null +++ b/tests/resources/sos_52n_get_observation_ioos_wml2.xml @@ -0,0 +1,3216 @@ + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T08:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 16.02 + + + + + 2014-07-07T04:00:00.000Z + 12.1 + + + + + 2014-07-07T05:00:00.000Z + 30.7 + + + + + 2014-07-07T06:00:00.000Z + 23.61 + + + + + 2014-07-07T07:00:00.000Z + 28.21 + + + + + 2014-07-07T08:00:00.000Z + 26.93 + + + + + 2014-07-07T09:00:00.000Z + 24.6 + + + + + 2014-07-07T10:00:00.000Z + 6.19 + + + + + 2014-07-07T11:00:00.000Z + 15.18 + + + + + 2014-07-07T12:00:00.000Z + 15.58 + + + + + 2014-07-07T13:00:00.000Z + 7.33 + + + + + 2014-07-07T14:00:00.000Z + 11.64 + + + + + 2014-07-07T15:00:00.000Z + 16.41 + + + + + 2014-07-07T16:00:00.000Z + 31.85 + + + + + 2014-07-07T17:00:00.000Z + 7.18 + + + + + 2014-07-07T18:00:00.000Z + 13.33 + + + + + 2014-07-07T19:00:00.000Z + 9.13 + + + + + 2014-07-07T20:00:00.000Z + 5.17 + + + + + 2014-07-07T21:00:00.000Z + 31.29 + + + + + 2014-07-07T22:00:00.000Z + 12.6 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T09:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 17.29 + + + + + 2014-07-07T04:00:00.000Z + 21.07 + + + + + 2014-07-07T05:00:00.000Z + 14.85 + + + + + 2014-07-07T06:00:00.000Z + 5.87 + + + + + 2014-07-07T07:00:00.000Z + 6.71 + + + + + 2014-07-07T08:00:00.000Z + 20.69 + + + + + 2014-07-07T09:00:00.000Z + 23.59 + + + + + 2014-07-07T10:00:00.000Z + 20.53 + + + + + 2014-07-07T11:00:00.000Z + 17.84 + + + + + 2014-07-07T12:00:00.000Z + 10.11 + + + + + 2014-07-07T13:00:00.000Z + 17.43 + + + + + 2014-07-07T14:00:00.000Z + 11.97 + + + + + 2014-07-07T15:00:00.000Z + 13.8 + + + + + 2014-07-07T16:00:00.000Z + 21.56 + + + + + 2014-07-07T17:00:00.000Z + 26.72 + + + + + 2014-07-07T18:00:00.000Z + 10.73 + + + + + 2014-07-07T19:00:00.000Z + 23.63 + + + + + 2014-07-07T20:00:00.000Z + 29.27 + + + + + 2014-07-07T21:00:00.000Z + 21.93 + + + + + 2014-07-07T22:00:00.000Z + 13.19 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T22:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 19.21 + + + + + 2014-07-07T04:00:00.000Z + 27.72 + + + + + 2014-07-07T05:00:00.000Z + 6.77 + + + + + 2014-07-07T06:00:00.000Z + 14.93 + + + + + 2014-07-07T07:00:00.000Z + 7.7 + + + + + 2014-07-07T08:00:00.000Z + 16.77 + + + + + 2014-07-07T09:00:00.000Z + 12.88 + + + + + 2014-07-07T10:00:00.000Z + 11.9 + + + + + 2014-07-07T11:00:00.000Z + 16.92 + + + + + 2014-07-07T12:00:00.000Z + 21.88 + + + + + 2014-07-07T13:00:00.000Z + 14.66 + + + + + 2014-07-07T14:00:00.000Z + 27.46 + + + + + 2014-07-07T15:00:00.000Z + 21.68 + + + + + 2014-07-07T16:00:00.000Z + 22.62 + + + + + 2014-07-07T17:00:00.000Z + 16.59 + + + + + 2014-07-07T18:00:00.000Z + 30.91 + + + + + 2014-07-07T19:00:00.000Z + 31.27 + + + + + 2014-07-07T20:00:00.000Z + 17.6 + + + + + 2014-07-07T21:00:00.000Z + 18.61 + + + + + 2014-07-07T22:00:00.000Z + 7.83 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T20:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 27.83 + + + + + 2014-07-07T04:00:00.000Z + 19.45 + + + + + 2014-07-07T05:00:00.000Z + 23.65 + + + + + 2014-07-07T06:00:00.000Z + 25.66 + + + + + 2014-07-07T07:00:00.000Z + 28.93 + + + + + 2014-07-07T08:00:00.000Z + 17.23 + + + + + 2014-07-07T09:00:00.000Z + 13.27 + + + + + 2014-07-07T10:00:00.000Z + 27.39 + + + + + 2014-07-07T11:00:00.000Z + 20.37 + + + + + 2014-07-07T12:00:00.000Z + 19.72 + + + + + 2014-07-07T13:00:00.000Z + 31.6 + + + + + 2014-07-07T14:00:00.000Z + 11.44 + + + + + 2014-07-07T15:00:00.000Z + 10.9 + + + + + 2014-07-07T16:00:00.000Z + 9.89 + + + + + 2014-07-07T17:00:00.000Z + 20.37 + + + + + 2014-07-07T18:00:00.000Z + 12.04 + + + + + 2014-07-07T19:00:00.000Z + 10.34 + + + + + 2014-07-07T20:00:00.000Z + 28.26 + + + + + 2014-07-07T21:00:00.000Z + 16.02 + + + + + 2014-07-07T22:00:00.000Z + 29.9 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T15:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 28.98 + + + + + 2014-07-07T04:00:00.000Z + 28.57 + + + + + 2014-07-07T05:00:00.000Z + 18.68 + + + + + 2014-07-07T06:00:00.000Z + 9.2 + + + + + 2014-07-07T07:00:00.000Z + 18.53 + + + + + 2014-07-07T08:00:00.000Z + 27.17 + + + + + 2014-07-07T09:00:00.000Z + 7.84 + + + + + 2014-07-07T10:00:00.000Z + 20.1 + + + + + 2014-07-07T11:00:00.000Z + 26.82 + + + + + 2014-07-07T12:00:00.000Z + 11.9 + + + + + 2014-07-07T13:00:00.000Z + 17.86 + + + + + 2014-07-07T14:00:00.000Z + 19.04 + + + + + 2014-07-07T15:00:00.000Z + 10.73 + + + + + 2014-07-07T16:00:00.000Z + 25.74 + + + + + 2014-07-07T17:00:00.000Z + 25.47 + + + + + 2014-07-07T18:00:00.000Z + 19.13 + + + + + 2014-07-07T19:00:00.000Z + 15.55 + + + + + 2014-07-07T20:00:00.000Z + 20.51 + + + + + 2014-07-07T21:00:00.000Z + 31.11 + + + + + 2014-07-07T22:00:00.000Z + 9.9 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T16:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 24.69 + + + + + 2014-07-07T04:00:00.000Z + 19.84 + + + + + 2014-07-07T05:00:00.000Z + 9.28 + + + + + 2014-07-07T06:00:00.000Z + 31.31 + + + + + 2014-07-07T07:00:00.000Z + 21.24 + + + + + 2014-07-07T08:00:00.000Z + 27.43 + + + + + 2014-07-07T09:00:00.000Z + 13.33 + + + + + 2014-07-07T10:00:00.000Z + 21.53 + + + + + 2014-07-07T11:00:00.000Z + 9.77 + + + + + 2014-07-07T12:00:00.000Z + 19.13 + + + + + 2014-07-07T13:00:00.000Z + 6.43 + + + + + 2014-07-07T14:00:00.000Z + 27.81 + + + + + 2014-07-07T15:00:00.000Z + 15.89 + + + + + 2014-07-07T16:00:00.000Z + 31.14 + + + + + 2014-07-07T17:00:00.000Z + 26.5 + + + + + 2014-07-07T18:00:00.000Z + 27.83 + + + + + 2014-07-07T19:00:00.000Z + 28.75 + + + + + 2014-07-07T20:00:00.000Z + 18.53 + + + + + 2014-07-07T21:00:00.000Z + 11.78 + + + + + 2014-07-07T22:00:00.000Z + 8.32 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T17:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 18.5 + + + + + 2014-07-07T04:00:00.000Z + 31.23 + + + + + 2014-07-07T05:00:00.000Z + 23.38 + + + + + 2014-07-07T06:00:00.000Z + 26.73 + + + + + 2014-07-07T07:00:00.000Z + 12.25 + + + + + 2014-07-07T08:00:00.000Z + 26.52 + + + + + 2014-07-07T09:00:00.000Z + 19.07 + + + + + 2014-07-07T10:00:00.000Z + 9.6 + + + + + 2014-07-07T11:00:00.000Z + 31.53 + + + + + 2014-07-07T12:00:00.000Z + 10.02 + + + + + 2014-07-07T13:00:00.000Z + 18.97 + + + + + 2014-07-07T14:00:00.000Z + 19.22 + + + + + 2014-07-07T15:00:00.000Z + 10.02 + + + + + 2014-07-07T16:00:00.000Z + 12.91 + + + + + 2014-07-07T17:00:00.000Z + 18.78 + + + + + 2014-07-07T18:00:00.000Z + 5.16 + + + + + 2014-07-07T19:00:00.000Z + 6.64 + + + + + 2014-07-07T20:00:00.000Z + 26.07 + + + + + 2014-07-07T21:00:00.000Z + 13.51 + + + + + 2014-07-07T22:00:00.000Z + 24.85 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T03:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 22.1 + + + + + 2014-07-07T04:00:00.000Z + 23.03 + + + + + 2014-07-07T05:00:00.000Z + 25.29 + + + + + 2014-07-07T06:00:00.000Z + 19.8 + + + + + 2014-07-07T07:00:00.000Z + 6.16 + + + + + 2014-07-07T08:00:00.000Z + 30.26 + + + + + 2014-07-07T09:00:00.000Z + 18.62 + + + + + 2014-07-07T10:00:00.000Z + 22.15 + + + + + 2014-07-07T11:00:00.000Z + 5.21 + + + + + 2014-07-07T12:00:00.000Z + 27.8 + + + + + 2014-07-07T13:00:00.000Z + 11.12 + + + + + 2014-07-07T14:00:00.000Z + 15.33 + + + + + 2014-07-07T15:00:00.000Z + 8.56 + + + + + 2014-07-07T16:00:00.000Z + 6.2 + + + + + 2014-07-07T17:00:00.000Z + 14.12 + + + + + 2014-07-07T18:00:00.000Z + 17.22 + + + + + 2014-07-07T19:00:00.000Z + 19.38 + + + + + 2014-07-07T20:00:00.000Z + 11.82 + + + + + 2014-07-07T21:00:00.000Z + 28.2 + + + + + 2014-07-07T22:00:00.000Z + 5.37 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T10:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 8.42 + + + + + 2014-07-07T04:00:00.000Z + 29.5 + + + + + 2014-07-07T05:00:00.000Z + 25.59 + + + + + 2014-07-07T06:00:00.000Z + 25.67 + + + + + 2014-07-07T07:00:00.000Z + 7.33 + + + + + 2014-07-07T08:00:00.000Z + 22.03 + + + + + 2014-07-07T09:00:00.000Z + 17.81 + + + + + 2014-07-07T10:00:00.000Z + 9.57 + + + + + 2014-07-07T11:00:00.000Z + 23.75 + + + + + 2014-07-07T12:00:00.000Z + 15.99 + + + + + 2014-07-07T13:00:00.000Z + 30.87 + + + + + 2014-07-07T14:00:00.000Z + 8.51 + + + + + 2014-07-07T15:00:00.000Z + 21.36 + + + + + 2014-07-07T16:00:00.000Z + 10.3 + + + + + 2014-07-07T17:00:00.000Z + 19.65 + + + + + 2014-07-07T18:00:00.000Z + 23.82 + + + + + 2014-07-07T19:00:00.000Z + 19.1 + + + + + 2014-07-07T20:00:00.000Z + 11.35 + + + + + 2014-07-07T21:00:00.000Z + 15.89 + + + + + 2014-07-07T22:00:00.000Z + 23.61 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T05:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 21.57 + + + + + 2014-07-07T04:00:00.000Z + 18.44 + + + + + 2014-07-07T05:00:00.000Z + 18.51 + + + + + 2014-07-07T06:00:00.000Z + 25.67 + + + + + 2014-07-07T07:00:00.000Z + 21.28 + + + + + 2014-07-07T08:00:00.000Z + 10.18 + + + + + 2014-07-07T09:00:00.000Z + 19.54 + + + + + 2014-07-07T10:00:00.000Z + 30.95 + + + + + 2014-07-07T11:00:00.000Z + 31.93 + + + + + 2014-07-07T12:00:00.000Z + 20.07 + + + + + 2014-07-07T13:00:00.000Z + 30.61 + + + + + 2014-07-07T14:00:00.000Z + 17.44 + + + + + 2014-07-07T15:00:00.000Z + 21.51 + + + + + 2014-07-07T16:00:00.000Z + 15.05 + + + + + 2014-07-07T17:00:00.000Z + 27.24 + + + + + 2014-07-07T18:00:00.000Z + 18.05 + + + + + 2014-07-07T19:00:00.000Z + 17.87 + + + + + 2014-07-07T20:00:00.000Z + 21.47 + + + + + 2014-07-07T21:00:00.000Z + 7.11 + + + + + 2014-07-07T22:00:00.000Z + 24.13 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T08:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 23.49 + + + + + 2014-07-07T04:00:00.000Z + 23.9 + + + + + 2014-07-07T05:00:00.000Z + 29.34 + + + + + 2014-07-07T06:00:00.000Z + 31.41 + + + + + 2014-07-07T07:00:00.000Z + 6.78 + + + + + 2014-07-07T08:00:00.000Z + 13.3 + + + + + 2014-07-07T09:00:00.000Z + 30.09 + + + + + 2014-07-07T10:00:00.000Z + 26.95 + + + + + 2014-07-07T11:00:00.000Z + 8.05 + + + + + 2014-07-07T12:00:00.000Z + 11.48 + + + + + 2014-07-07T13:00:00.000Z + 10.08 + + + + + 2014-07-07T14:00:00.000Z + 16.21 + + + + + 2014-07-07T15:00:00.000Z + 14.11 + + + + + 2014-07-07T16:00:00.000Z + 8.33 + + + + + 2014-07-07T17:00:00.000Z + 23.32 + + + + + 2014-07-07T18:00:00.000Z + 9.01 + + + + + 2014-07-07T19:00:00.000Z + 17.02 + + + + + 2014-07-07T20:00:00.000Z + 11.4 + + + + + 2014-07-07T21:00:00.000Z + 19.17 + + + + + 2014-07-07T22:00:00.000Z + 11.62 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T08:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 24.01 + + + + + 2014-07-07T04:00:00.000Z + 27.08 + + + + + 2014-07-07T05:00:00.000Z + 13.63 + + + + + 2014-07-07T06:00:00.000Z + 9.36 + + + + + 2014-07-07T07:00:00.000Z + 19.41 + + + + + 2014-07-07T08:00:00.000Z + 18.93 + + + + + 2014-07-07T09:00:00.000Z + 30.37 + + + + + 2014-07-07T10:00:00.000Z + 17.17 + + + + + 2014-07-07T11:00:00.000Z + 23.97 + + + + + 2014-07-07T12:00:00.000Z + 14.91 + + + + + 2014-07-07T13:00:00.000Z + 20.07 + + + + + 2014-07-07T14:00:00.000Z + 15.28 + + + + + 2014-07-07T15:00:00.000Z + 8.84 + + + + + 2014-07-07T16:00:00.000Z + 28.72 + + + + + 2014-07-07T17:00:00.000Z + 24.88 + + + + + 2014-07-07T18:00:00.000Z + 16.35 + + + + + 2014-07-07T19:00:00.000Z + 14.72 + + + + + 2014-07-07T20:00:00.000Z + 23.2 + + + + + 2014-07-07T21:00:00.000Z + 18.53 + + + + + 2014-07-07T22:00:00.000Z + 25.08 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T22:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 23.57 + + + + + 2014-07-07T04:00:00.000Z + 22.05 + + + + + 2014-07-07T05:00:00.000Z + 7.66 + + + + + 2014-07-07T06:00:00.000Z + 30.06 + + + + + 2014-07-07T07:00:00.000Z + 18.5 + + + + + 2014-07-07T08:00:00.000Z + 25.37 + + + + + 2014-07-07T09:00:00.000Z + 12.31 + + + + + 2014-07-07T10:00:00.000Z + 12.56 + + + + + 2014-07-07T11:00:00.000Z + 15.15 + + + + + 2014-07-07T12:00:00.000Z + 24.46 + + + + + 2014-07-07T13:00:00.000Z + 13.1 + + + + + 2014-07-07T14:00:00.000Z + 9.94 + + + + + 2014-07-07T15:00:00.000Z + 30.43 + + + + + 2014-07-07T16:00:00.000Z + 14.84 + + + + + 2014-07-07T17:00:00.000Z + 14.28 + + + + + 2014-07-07T18:00:00.000Z + 16.52 + + + + + 2014-07-07T19:00:00.000Z + 10.54 + + + + + 2014-07-07T20:00:00.000Z + 9.6 + + + + + 2014-07-07T21:00:00.000Z + 12.85 + + + + + 2014-07-07T22:00:00.000Z + 12.85 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T16:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 24.86 + + + + + 2014-07-07T04:00:00.000Z + 5.66 + + + + + 2014-07-07T05:00:00.000Z + 14.37 + + + + + 2014-07-07T06:00:00.000Z + 6.63 + + + + + 2014-07-07T07:00:00.000Z + 28.4 + + + + + 2014-07-07T08:00:00.000Z + 15.26 + + + + + 2014-07-07T09:00:00.000Z + 22.82 + + + + + 2014-07-07T10:00:00.000Z + 16.72 + + + + + 2014-07-07T11:00:00.000Z + 20.94 + + + + + 2014-07-07T12:00:00.000Z + 31.97 + + + + + 2014-07-07T13:00:00.000Z + 24.83 + + + + + 2014-07-07T14:00:00.000Z + 17.77 + + + + + 2014-07-07T15:00:00.000Z + 28.44 + + + + + 2014-07-07T16:00:00.000Z + 17.97 + + + + + 2014-07-07T17:00:00.000Z + 12.75 + + + + + 2014-07-07T18:00:00.000Z + 27.94 + + + + + 2014-07-07T19:00:00.000Z + 22.06 + + + + + 2014-07-07T20:00:00.000Z + 19.03 + + + + + 2014-07-07T21:00:00.000Z + 24.45 + + + + + 2014-07-07T22:00:00.000Z + 30.68 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T09:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 24.11 + + + + + 2014-07-07T04:00:00.000Z + 6.78 + + + + + 2014-07-07T05:00:00.000Z + 25.14 + + + + + 2014-07-07T06:00:00.000Z + 18.24 + + + + + 2014-07-07T07:00:00.000Z + 9.66 + + + + + 2014-07-07T08:00:00.000Z + 14.95 + + + + + 2014-07-07T09:00:00.000Z + 26.63 + + + + + 2014-07-07T10:00:00.000Z + 11.3 + + + + + 2014-07-07T11:00:00.000Z + 13.38 + + + + + 2014-07-07T12:00:00.000Z + 27.72 + + + + + 2014-07-07T13:00:00.000Z + 24.73 + + + + + 2014-07-07T14:00:00.000Z + 28.05 + + + + + 2014-07-07T15:00:00.000Z + 22.69 + + + + + 2014-07-07T16:00:00.000Z + 20.35 + + + + + 2014-07-07T17:00:00.000Z + 29.89 + + + + + 2014-07-07T18:00:00.000Z + 17.99 + + + + + 2014-07-07T19:00:00.000Z + 14.12 + + + + + 2014-07-07T20:00:00.000Z + 17.9 + + + + + 2014-07-07T21:00:00.000Z + 8.07 + + + + + 2014-07-07T22:00:00.000Z + 23.12 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T11:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 9.51 + + + + + 2014-07-07T04:00:00.000Z + 25.8 + + + + + 2014-07-07T05:00:00.000Z + 8.94 + + + + + 2014-07-07T06:00:00.000Z + 28.45 + + + + + 2014-07-07T07:00:00.000Z + 5.63 + + + + + 2014-07-07T08:00:00.000Z + 27.63 + + + + + 2014-07-07T09:00:00.000Z + 5.66 + + + + + 2014-07-07T10:00:00.000Z + 26.87 + + + + + 2014-07-07T11:00:00.000Z + 13.58 + + + + + 2014-07-07T12:00:00.000Z + 23.86 + + + + + 2014-07-07T13:00:00.000Z + 8.17 + + + + + 2014-07-07T14:00:00.000Z + 17.62 + + + + + 2014-07-07T15:00:00.000Z + 24.65 + + + + + 2014-07-07T16:00:00.000Z + 31.44 + + + + + 2014-07-07T17:00:00.000Z + 11.75 + + + + + 2014-07-07T18:00:00.000Z + 14.89 + + + + + 2014-07-07T19:00:00.000Z + 6.75 + + + + + 2014-07-07T20:00:00.000Z + 29.57 + + + + + 2014-07-07T21:00:00.000Z + 31.56 + + + + + 2014-07-07T22:00:00.000Z + 23.48 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T21:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 8.27 + + + + + 2014-07-07T04:00:00.000Z + 31.19 + + + + + 2014-07-07T05:00:00.000Z + 29.1 + + + + + 2014-07-07T06:00:00.000Z + 25.76 + + + + + 2014-07-07T07:00:00.000Z + 26.27 + + + + + 2014-07-07T08:00:00.000Z + 16.13 + + + + + 2014-07-07T09:00:00.000Z + 6.6 + + + + + 2014-07-07T10:00:00.000Z + 31.49 + + + + + 2014-07-07T11:00:00.000Z + 12.88 + + + + + 2014-07-07T12:00:00.000Z + 27.45 + + + + + 2014-07-07T13:00:00.000Z + 18.49 + + + + + 2014-07-07T14:00:00.000Z + 23.2 + + + + + 2014-07-07T15:00:00.000Z + 23.6 + + + + + 2014-07-07T16:00:00.000Z + 22.95 + + + + + 2014-07-07T17:00:00.000Z + 10.4 + + + + + 2014-07-07T18:00:00.000Z + 23.77 + + + + + 2014-07-07T19:00:00.000Z + 18.44 + + + + + 2014-07-07T20:00:00.000Z + 31.04 + + + + + 2014-07-07T21:00:00.000Z + 15.04 + + + + + 2014-07-07T22:00:00.000Z + 9.17 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T19:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 25.59 + + + + + 2014-07-07T04:00:00.000Z + 16.33 + + + + + 2014-07-07T05:00:00.000Z + 19.27 + + + + + 2014-07-07T06:00:00.000Z + 18.93 + + + + + 2014-07-07T07:00:00.000Z + 19.69 + + + + + 2014-07-07T08:00:00.000Z + 8.89 + + + + + 2014-07-07T09:00:00.000Z + 19.63 + + + + + 2014-07-07T10:00:00.000Z + 14.75 + + + + + 2014-07-07T11:00:00.000Z + 28.14 + + + + + 2014-07-07T12:00:00.000Z + 5.51 + + + + + 2014-07-07T13:00:00.000Z + 20.53 + + + + + 2014-07-07T14:00:00.000Z + 17.34 + + + + + 2014-07-07T15:00:00.000Z + 17.74 + + + + + 2014-07-07T16:00:00.000Z + 6.22 + + + + + 2014-07-07T17:00:00.000Z + 12.68 + + + + + 2014-07-07T18:00:00.000Z + 9.69 + + + + + 2014-07-07T19:00:00.000Z + 26.4 + + + + + 2014-07-07T20:00:00.000Z + 14.55 + + + + + 2014-07-07T21:00:00.000Z + 24.41 + + + + + 2014-07-07T22:00:00.000Z + 15.51 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T12:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 29.92 + + + + + 2014-07-07T04:00:00.000Z + 26.48 + + + + + 2014-07-07T05:00:00.000Z + 6.49 + + + + + 2014-07-07T06:00:00.000Z + 19.43 + + + + + 2014-07-07T07:00:00.000Z + 15.16 + + + + + 2014-07-07T08:00:00.000Z + 31.95 + + + + + 2014-07-07T09:00:00.000Z + 5.42 + + + + + 2014-07-07T10:00:00.000Z + 29.29 + + + + + 2014-07-07T11:00:00.000Z + 18.57 + + + + + 2014-07-07T12:00:00.000Z + 8.46 + + + + + 2014-07-07T13:00:00.000Z + 30.95 + + + + + 2014-07-07T14:00:00.000Z + 25.0 + + + + + 2014-07-07T15:00:00.000Z + 11.16 + + + + + 2014-07-07T16:00:00.000Z + 18.52 + + + + + 2014-07-07T17:00:00.000Z + 10.48 + + + + + 2014-07-07T18:00:00.000Z + 27.07 + + + + + 2014-07-07T19:00:00.000Z + 20.67 + + + + + 2014-07-07T20:00:00.000Z + 22.8 + + + + + 2014-07-07T21:00:00.000Z + 19.01 + + + + + 2014-07-07T22:00:00.000Z + 8.63 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T17:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 25.34 + + + + + 2014-07-07T04:00:00.000Z + 19.0 + + + + + 2014-07-07T05:00:00.000Z + 12.5 + + + + + 2014-07-07T06:00:00.000Z + 12.2 + + + + + 2014-07-07T07:00:00.000Z + 5.18 + + + + + 2014-07-07T08:00:00.000Z + 27.62 + + + + + 2014-07-07T09:00:00.000Z + 16.82 + + + + + 2014-07-07T10:00:00.000Z + 26.73 + + + + + 2014-07-07T11:00:00.000Z + 26.58 + + + + + 2014-07-07T12:00:00.000Z + 16.83 + + + + + 2014-07-07T13:00:00.000Z + 19.48 + + + + + 2014-07-07T14:00:00.000Z + 28.34 + + + + + 2014-07-07T15:00:00.000Z + 11.9 + + + + + 2014-07-07T16:00:00.000Z + 14.12 + + + + + 2014-07-07T17:00:00.000Z + 27.6 + + + + + 2014-07-07T18:00:00.000Z + 24.27 + + + + + 2014-07-07T19:00:00.000Z + 26.15 + + + + + 2014-07-07T20:00:00.000Z + 30.48 + + + + + 2014-07-07T21:00:00.000Z + 8.24 + + + + + 2014-07-07T22:00:00.000Z + 21.34 + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 2014-07-07T22:00:00.000Z + + + + + 2014-07-07T03:00:00.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-07T03:00:00.000Z + 20.22 + + + + + 2014-07-07T04:00:00.000Z + 27.24 + + + + + 2014-07-07T05:00:00.000Z + 17.9 + + + + + 2014-07-07T06:00:00.000Z + 21.85 + + + + + 2014-07-07T07:00:00.000Z + 8.65 + + + + + 2014-07-07T08:00:00.000Z + 18.29 + + + + + 2014-07-07T09:00:00.000Z + 21.38 + + + + + 2014-07-07T10:00:00.000Z + 13.54 + + + + + 2014-07-07T11:00:00.000Z + 16.48 + + + + + 2014-07-07T12:00:00.000Z + 23.36 + + + + + 2014-07-07T13:00:00.000Z + 25.78 + + + + + 2014-07-07T14:00:00.000Z + 21.41 + + + + + 2014-07-07T15:00:00.000Z + 6.63 + + + + + 2014-07-07T16:00:00.000Z + 26.72 + + + + + 2014-07-07T17:00:00.000Z + 17.85 + + + + + 2014-07-07T18:00:00.000Z + 30.51 + + + + + 2014-07-07T19:00:00.000Z + 16.42 + + + + + 2014-07-07T20:00:00.000Z + 26.14 + + + + + 2014-07-07T21:00:00.000Z + 15.22 + + + + + 2014-07-07T22:00:00.000Z + 26.41 + + + + + + + diff --git a/tests/resources/sos_52n_getobservation_wml2_response.xml b/tests/resources/sos_52n_getobservation_wml2_response.xml new file mode 100644 index 000000000..44decbf90 --- /dev/null +++ b/tests/resources/sos_52n_getobservation_wml2_response.xml @@ -0,0 +1,72 @@ + + + + + + + 2014-07-01T00:01:42.000Z + 2014-07-01T01:16:41.000Z + + + + + 2014-07-01T00:01:42.000Z + + + + + + + + + + + + + + + + + + + + + 2014-07-01T00:01:42.000Z + 12.2 + + + + + 2014-07-01T00:16:42.000Z + 12.3 + + + + + 2014-07-01T00:31:42.000Z + 12.0 + + + + + 2014-07-01T00:46:42.000Z + 11.9 + + + + + 2014-07-01T01:01:42.000Z + 12.1 + + + + + 2014-07-01T01:16:41.000Z + 12.5 + + + + + + + diff --git a/tests/utils.py b/tests/utils.py index 72110af10..18bc3bf17 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,7 +1,19 @@ +import logging import os +import sys from owslib.etree import etree from urlparse import urlparse +def setup_logging(loglevel='INFO'): + """Helper function to setup logging for tests""" + logger = logging.getLogger('owslib') + logger.setLevel(getattr(logging, loglevel)) + sh = logging.StreamHandler(sys.stdout) + formatter = logging.Formatter('%(message)s') + sh.setFormatter(formatter) + logger.addHandler(sh) + return logger + def resource_file(filepath): return os.path.join(test_directory(), 'resources', filepath)