Skip to content
Commits on Source (5)
include *.txt
include LICENSE
include *.rst
recursive-include owslib
......@@ -121,6 +121,26 @@ configure your application to use the log messages like so:
>>> # Add formatting and handlers as needed
>>> owslib_log.setLevel(logging.DEBUG)
Releasing
---------
.. code-block:: bash
# update version
vi VERSION.txt
vi owslib/__init__.py
git commit -m 'update release version' VERSION.txt owslib/__init__.py
# push changes
git push origin master
git tag -a x.y.z -m 'tagging OWSLib release x.y.z'
# push tag
git push --tags
# update live docs
cd docs
make html
./publish.sh
# update on PyPI (must be a maintainer)
python setup.py sdist upload
Support
-------
......
owslib (0.17.0-2) UNRELEASED; urgency=medium
owslib (0.17.1-1) unstable; urgency=medium
* Team upload.
* New upstream release.
* Bump Standards-Version to 4.3.0, no changes.
* Update copyright holders.
-- Bas Couwenberg <sebastic@debian.org> Tue, 25 Dec 2018 22:52:37 +0100
-- Bas Couwenberg <sebastic@debian.org> Sun, 13 Jan 2019 09:30:10 +0100
owslib (0.17.0-1) unstable; urgency=medium
......
......@@ -4,41 +4,23 @@ Upstream-Name: OWSLib
Source: https://github.com/geopython/owslib
Files: *
Copyright: 2006-2016 OWSLib Contributors
* Sean Gillies <sgillies@frii.com>
* Julien Anguenot <ja@nuxeo.com>
* Kai Lautaportti <kai.lautaportti@hexagonit.fi>
* Dominic Lowe <D.Lowe@rl.ac.uk>
* Jo Walsh <jo@frot.org>
* Tom Kralidis <tomkralidis@gmail.com>
* Jachym Cepicky <jachym.cepicky@gmail.com>
* Luca Cinquini <luca.cinquini@jpl.nasa.gov>
* Brad Hards <bradh@frogmouth.net>
* Christian Ledermann <christian.ledermann@gmail.com>
* Sean Cowan <scowan@asascience.com>
* Kyle Wilcox <wilcox.kyle@gmail.com>
* Angelos Tzotsos <gcpp.kalxas@gmail.com>
* Brian Miles <selimnairb@gmail.com>
* Eliott Sales de Andrade
* David Blodgett <dblodgett@usgs.gov>
* Thomas Kluyver
* Kai Lautaportti
* Richard Hattersley
* Christian Ledermann <christian.ledermann@gmail.com>
* Luke Campbell <luke.s.campbell@gmail.com>
* Luís de Sousa <luis.a.de.sousa@gmail.com>
* Julien Enselme <jujens+github@jujens.eu>
* Dave Foster <dave.foster@gmail.com>
* Nuxeo SARL <http://nuxeo.com>
* STFC <http://www.stfc.ac.uk>
* Pete Taylor
Copyright: 2005, Nuxeo SARL <http:nuxeo.com>
2004-2006, Sean C. Gillies
2006, Ancient World Mapping Center
2007, 2009, STFC <http:www.stfc.ac.uk>
2012, Brad Hards <bradh@frogmouth.net>
2013, Christian Ledermann <christian.ledermann@gmail.com>
2014, Pete Taylor
2012, 2015, Jachym Cepicky
2015, Luís de Sousa
2008-2011, 2013, 2015, 2018, Tom Kralidis
2018, Luca Cinquini
License: BSD-3-Clause
Files: debian/*
Copyright: 2014-2016
* Angelos Tzotsos <gcpp.kalxas@gmail.com>
* Johan Van de Wauw <johan.vandewauw@gmail.com>
* Bas Couwenberg <sebastic@xs4all.nl>
Copyright: 2014-2016, Angelos Tzotsos <gcpp.kalxas@gmail.com>
2014-2016, Johan Van de Wauw <johan.vandewauw@gmail.com>
2014-2016, Bas Couwenberg <sebastic@xs4all.nl>
License: BSD-3-Clause
License: BSD-3-Clause
......@@ -65,4 +47,3 @@ License: BSD-3-Clause
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
......@@ -34,7 +34,7 @@ Standards Support
+===================+=====================+
| `OGC WMS`_ | 1.1.1 |
+-------------------+---------------------+
| `OGC WFS`_ | 1.0.0, 1.1.0, 2.0.0 |
| `OGC WFS`_ | 1.0.0, 1.1.0, 2.0.0, 3.0 |
+-------------------+---------------------+
| `OGC WCS`_ | 1.0.0, 1.1.0 |
+-------------------+---------------------+
......@@ -285,6 +285,35 @@ services)
>>> response = wfs20.getfeature(storedQueryID='urn:ogc:def:query:OGC-WFS::GetFeatureById', storedQueryParams={'ID':'gmd_ex.1'})
WFS 3.0
-------
WFS 3.0 is a clean break from the traditional OGC service architecture
(RESTful, JSON, OpenAPI) and as such OWSLib the code follows the same pattern.
.. code-block:: python
>>> from owslib.wfs import WebFeatureService
>>> w = WebFeatureService('https://geo.kralidis.ca/pygeoapi, version='3.0')
>>> w.url
'http://geo.kralidis.ca/pygeoapi/'
>>> w.version
'3.0'
>>> conformance = w.conformance()
{u'conformsTo': [u'http://www.opengis.net/spec/wfs-1/3.0/req/core', u'http://www.opengis.net/spec/wfs-1/3.0/req/oas30', u'http://www.opengis.net/spec/wfs-1/3.0/req/html', u'http://www.opengis.net/spec/wfs-1/3.0/req/geojson']}
>>> collections = w.collections()
>>> len(collections)
3
>>> lakes = w.collection('lakes')
>>> lakes['name']
'lakes'
>>> lakes['title']
'Large Lakes'
>>> lakes['description']
'lakes of the world, public domain'
>>> lakes_query = w.collection_items('lakes')
>>> lakes_query['features'][0]['properties']
{u'scalerank': 0, u'name_alt': None, u'admin': None, u'featureclass': u'Lake', u'id': 0, u'name': u'Lake Baikal'}
WCS
---
......
......@@ -16,8 +16,9 @@ def multiple_outputs():
processid = 'dummyprocess'
inputs = [("input1", '1'), ("input2", '2')]
# list of tuple (output identifier, asReference attribute)
outputs = [("output1",True), ("output2",False)]
# list of tuple (output identifier, asReference attribute, mimeType attribute)
# when asReference or mimeType is None - the wps service will use its default option
outputs = [("output1",True,'some/mime-type'), ("output2",False,None)]
execution = wps.execute(processid, inputs, output=outputs)
monitorExecution(execution)
......@@ -47,8 +48,9 @@ def complex_input_with_reference():
processid = 'wordcount'
textdoc = ComplexDataInput("http://www.gutenberg.org/files/28885/28885-h/28885-h.htm") # alice in wonderland
inputs = [("text", textdoc)]
# list of tuple (output identifier, asReference attribute)
outputs = [("output",True)]
# list of tuple (output identifier, asReference attribute, mimeType attribute)
# when asReference or mimeType is None - the wps service will use its default option
outputs = [("output",True,'some/mime-type')]
execution = wps.execute(processid, inputs, output=outputs)
monitorExecution(execution)
......@@ -72,8 +74,9 @@ def complex_input_with_content():
processid = 'wordcount'
textdoc = ComplexDataInput("ALICE was beginning to get very tired ...") # alice in wonderland
inputs = [("text", textdoc)]
# list of tuple (output identifier, asReference attribute)
outputs = [("output",True)]
# list of tuple (output identifier, asReference attribute, mimeType attribute)
# when asReference or mimeType is None - the wps service will use its default option
outputs = [("output",True,'some/mime-type')]
execution = wps.execute(processid, inputs, output=outputs)
monitorExecution(execution)
......
from __future__ import (absolute_import, division, print_function)
__version__ = '0.17.0'
__version__ = '0.17.1'
# =============================================================================
# Copyright (c) 2018 Tom Kralidis
#
# Authors : Tom Kralidis <tomkralidis@gmail.com>
#
# Contact email: tomkralidis@gmail.com
# =============================================================================
import json
import logging
from six.moves.urllib.parse import urljoin
import requests
from owslib import __version__
LOGGER = logging.getLogger(__name__)
REQUEST_HEADERS = {
'User-Agent': 'OWSLib {} (https://geopython.github.io/OWSLib)'.format(
__version__)
}
class WebFeatureService_3_0_0(object):
"""Abstraction for OGC Web Feature Service (WFS) version 3.0"""
def __init__(self, url, version, json_, timeout=30, username=None,
password=None):
"""
initializer; implements Requirement 1 (/req/core/root-op)
@type url: string
@param url: url of WFS root document
@type json_: string
@param json_: json object
@param timeout: time (in seconds) after which requests should timeout
@param username: service authentication username
@param password: service authentication password
@return: initialized WebFeatureService_3_0_0 object
"""
if '?' in url:
self.url, self.url_query_string = url.split('?')
else:
self.url = url.rstrip('/') + '/'
self.url_query_string = None
self.version = version
self.json_ = json_
self.timeout = timeout
self.username = username
self.password = password
if json_ is not None: # static JSON string
self.links = json.loads(json_)['links']
else:
response = requests.get(url, headers=REQUEST_HEADERS).json()
self.links = response['links']
def conformance(self):
"""
implements Requirement 5 (/req/core/conformance-op)
@returns: conformance object
"""
url = self._build_url('conformance')
LOGGER.debug('Request: {}'.format(url))
response = requests.get(url, headers=REQUEST_HEADERS).json()
return response
def collections(self):
"""
implements Requirement 9 (/req/core/collections-op)
@returns: collections object
"""
url = self._build_url('collections')
LOGGER.debug('Request: {}'.format(url))
response = requests.get(url, headers=REQUEST_HEADERS).json()
return response['collections']
def collection(self, collection_name):
"""
implements Requirement 15 (/req/core/sfc-md-op)
@type collection_name: string
@param collection_name: name of collection
@returns: feature collection metadata
"""
path = 'collections/{}'.format(collection_name)
url = self._build_url(path)
LOGGER.debug('Request: {}'.format(url))
response = requests.get(url, headers=REQUEST_HEADERS).json()
return response
def collection_items(self, collection_name, **kwargs):
"""
implements Requirement 17 (/req/core/fc-op)
@type collection_name: string
@param collection_name: name of collection
@type bbox: list
@param bbox: list of minx,miny,maxx,maxy
@type time: string
@param time: time extent or time instant
@type limit: int
@param limit: limit number of features
@type startindex: int
@param startindex: start position of results
@returns: feature results
"""
if 'bbox' in kwargs:
kwargs['bbox'] = ','.join(kwargs['bbox'])
path = 'collections/{}/items'.format(collection_name)
url = self._build_url(path)
LOGGER.debug('Request: {}'.format(url))
response = requests.get(url, headers=REQUEST_HEADERS,
params=kwargs).json()
return response
def collection_item(self, collection_name, identifier):
"""
implements Requirement 30 (/req/core/f-op)
@type collection_name: string
@param collection_name: name of collection
@type identifier: string
@param identifier: feature identifier
@returns: single feature result
"""
path = 'collections/{}/items/{}'.format(collection_name, identifier)
url = self._build_url(path)
LOGGER.debug('Request: {}'.format(url))
response = requests.get(url, headers=REQUEST_HEADERS).json()
return response
def _build_url(self, path=None):
"""
helper function to build a WFS 3.0 URL
@type path: string
@param path: path of WFS URL
@returns: fully constructed URL path
"""
url = self.url
if self.url_query_string is not None:
LOGGER.debug('base URL has a query string')
url = urljoin(url, path)
url = '?'.join([url, self.url_query_string])
else:
url = urljoin(url, path)
LOGGER.debug('URL: {}'.format(url))
return url
......@@ -269,7 +269,7 @@ class WebMapService_1_1_1(object):
u = openURL(base_url, data, method, username=self.username, password=self.password, timeout=timeout or self.timeout)
# check for service exceptions, and return
if u.info()['Content-Type'].split(';')[0] in ['application/vnd.ogc.se_xml']:
if u.info().get('Content-Type', '').split(';')[0] in ['application/vnd.ogc.se_xml']:
se_xml = u.read()
se_tree = etree.fromstring(se_xml)
err_message = six.text_type(se_tree.find('ServiceException').text).strip()
......@@ -377,7 +377,10 @@ class ServiceProvider(object):
self.name=name.text
else:
self.name=None
self.url=self._root.find('OnlineResource').attrib.get('{http://www.w3.org/1999/xlink}href', '')
self.url = None
online_resource = self._root.find('OnlineResource')
if online_resource is not None:
self.url = online_resource.attrib.get('{http://www.w3.org/1999/xlink}href', '')
#contact metadata
contact = self._root.find('ContactInformation')
## sometimes there is a contact block that is empty, so make
......
......@@ -315,7 +315,7 @@ class WebMapService_1_3_0(object):
headers[k.lower()] = v
# handle the potential charset def
if headers['content-type'].split(';')[0] in ['application/vnd.ogc.se_xml', 'text/xml']:
if headers.get('content-type', '').split(';')[0] in ['application/vnd.ogc.se_xml', 'text/xml']:
se_xml = u.read()
se_tree = etree.fromstring(se_xml)
err_message = six.text_type(se_tree.find(nspath('ServiceException', OGC_NAMESPACE)).text).strip()
......@@ -411,7 +411,10 @@ class ServiceProvider(object):
self.name = name.text
else:
self.name = None
self.url = self._root.find(nspath('OnlineResource', WMS_NAMESPACE)).attrib.get('{http://www.w3.org/1999/xlink}href', '')
self.url = None
online_resource = self._root.find(nspath('OnlineResource', WMS_NAMESPACE))
if online_resource is not None:
self.url = online_resource.attrib.get('{http://www.w3.org/1999/xlink}href', '')
# contact metadata
contact = self._root.find(nspath('ContactInformation', WMS_NAMESPACE))
# sometimes there is a contact block that is empty, so make
......
......@@ -15,12 +15,13 @@ Web Feature Server (WFS) methods and metadata. Factory function.
from __future__ import (absolute_import, division, print_function)
from .feature import wfs100, wfs110, wfs200
from .feature import wfs100, wfs110, wfs200, wfs300
from .util import clean_ows_url
def WebFeatureService(url, version='1.0.0', xml=None, parse_remote_metadata=False,
timeout=30, username=None, password=None):
def WebFeatureService(url, version='1.0.0', xml=None, json_=None,
parse_remote_metadata=False, timeout=30, username=None,
password=None):
''' wfs factory function, returns a version specific WebFeatureService object
@type url: string
......@@ -32,7 +33,7 @@ def WebFeatureService(url, version='1.0.0', xml=None, parse_remote_metadata=Fals
@param timeout: time (in seconds) after which requests should timeout
@param username: service authentication username
@param password: service authentication password
@return: initialized WebFeatureService_2_0_0 object
@return: initialized WebFeatureService object (version dependent)
'''
clean_url = clean_ows_url(url)
......@@ -52,3 +53,8 @@ def WebFeatureService(url, version='1.0.0', xml=None, parse_remote_metadata=Fals
timeout=timeout,
username=username,
password=password)
elif version in ['3.0', '3.0.0']:
return wfs300.WebFeatureService_3_0_0(clean_url, version, json_,
timeout=timeout,
username=username,
password=password)
......@@ -121,6 +121,7 @@ try: # Python 3
except ImportError: # Python 2
from urlparse import urlparse
import six
# namespace definition
n = Namespaces()
......@@ -160,7 +161,7 @@ def is_reference(val):
"""
try:
parsed = urlparse(val)
is_ref = parsed.scheme != ''
is_ref = bool(parsed.scheme)
except Exception:
is_ref = False
return is_ref
......@@ -270,7 +271,9 @@ class WebProcessingService(object):
def describeprocess(self, identifier, xml=None):
"""
Requests a process document from a WPS service and populates the process metadata.
Returns the process object.
Returns the process object or a list of process objects.
:param str identifier: The process id. If `all`, return a list of all processes available.
"""
# read capabilities document
......@@ -287,7 +290,12 @@ class WebProcessingService(object):
log.info(element_to_string(rootElement))
# build metadata objects
return self._parseProcessMetadata(rootElement)
processes = self._parseProcessMetadata(rootElement)
if identifier == 'all':
return processes
else:
return processes[0]
def execute(self, identifier, inputs, output=None, mode=ASYNC, lineage=False, request=None, response=None):
"""
......@@ -296,10 +304,12 @@ class WebProcessingService(object):
retrieve the result.
:param str identifier: the requested process identifier
:param inputs: list of process inputs as (key, value) tuples (where value is either a string for LiteralData, or
an object for ComplexData)
:param output: optional identifier for process output reference (if not provided, output will be
embedded in the response)
:param inputs: list of process inputs as (input_identifier, value) tuples (where value is either a string
for LiteralData, or an object for ComplexData).
:param output: optional list of process outputs as tuples (output_identifier, as_ref, mime_type).
`as_ref` can be True (as reference),
False (embedded in response) or None (use service default).
`mime_type` should be text or None (use service default)
:param mode: execution mode: SYNC, ASYNC or AUTO. Default: ASYNC
:param lineage: if lineage is "true", the Execute operation response shall include the DataInputs and
OutputDefinitions elements.
......@@ -341,11 +351,11 @@ class WebProcessingService(object):
raise KeyError("No operation named %s" % name)
def _parseProcessMetadata(self, rootElement):
"""
Method to parse a <ProcessDescriptions> XML element and returned the constructed Process object
"""
"""Return a list of Process objects parsed from a <ProcessDescriptions> XML element."""
processDescriptionElement = rootElement.find('ProcessDescription')
processDescriptionElements = rootElement.findall('ProcessDescription')
processes = []
for processDescriptionElement in processDescriptionElements:
process = Process(processDescriptionElement, verbose=self.verbose)
# override existing processes in object metadata, if existing already
......@@ -358,10 +368,12 @@ class WebProcessingService(object):
if not found:
self.processes.append(process)
return process
processes.append(process)
return processes
def _parseCapabilitiesMetadata(self, root):
''' Sets up capabilities metadata objects '''
"""Set up capabilities metadata objects."""
# reset metdata
self.operations = []
......@@ -425,7 +437,6 @@ class WebProcessingService(object):
class WPSReader(object):
"""
Superclass for reading a WPS document into a lxml.etree infoset.
"""
......@@ -550,7 +561,7 @@ class WPSExecuteReader(WPSReader):
headers=headers, verify=verify, cert=cert)
class WPSExecution():
class WPSExecution(object):
"""
Class that represents a single WPS process executed on a remote WPS service.
......@@ -598,8 +609,9 @@ class WPSExecution():
and the object must contain a 'getXml()' method that returns an XML infoset to be included in
the WPS request
:param output: array of outputs which should be returned:
expressed as tuples (key, as_ref) where key is the output identifier and as_ref is True
expressed as tuples (key, as_ref, mime_mype) where key is the output identifier and as_ref is True
if output should be returned as reference.
as_ref and mimeType may be null for using server's default value
:param mode: execution mode: SYNC, ASYNC or AUTO.
:param lineage: if lineage is "true", the Execute operation response shall include the DataInputs and
OutputDefinitions elements.
......@@ -687,7 +699,7 @@ class WPSExecution():
# <wps:ResponseForm>
# <wps:ResponseDocument storeExecuteResponse="true" status="true" lineage="false">
# <wps:Output asReference="true">
# <wps:Output asReference="true" mimeType="application/json">
# <ows:Identifier>OUTPUT</ows:Identifier>
# </wps:Output>
# </wps:ResponseDocument>
......@@ -703,30 +715,38 @@ class WPSExecution():
'lineage': str(lineage).lower()})
# keeping backward compability of output parameter
if isinstance(output, str):
self._add_output(
responseDocumentElement, output, asReference=True)
self._add_output(responseDocumentElement, output)
elif isinstance(output, list):
for (identifier, as_reference) in output:
for ouputTuple in output:
# tuple (identifier, as_reference) for backward compatibility
if(len(ouputTuple) == 2):
(identifier, as_reference) = ouputTuple
mime_type = None
else:
(identifier, as_reference, mime_type) = ouputTuple
self._add_output(
responseDocumentElement, identifier, asReference=as_reference)
responseDocumentElement, identifier, asReference=as_reference, mimeType=mime_type)
else:
raise Exception(
'output parameter is neither string nor list. output=%s' % output)
return root
def _add_output(self, element, identifier, asReference=False):
outputElement = etree.SubElement(
element, nspath_eval('wps:Output', namespaces),
attrib={'asReference': str(asReference).lower()})
def _add_output(self, element, identifier, asReference=None, mimeType=None):
output_element = etree.SubElement(
element, nspath_eval('wps:Output', namespaces))
if isinstance(mimeType, str):
output_element.attrib['mimeType'] = mimeType
if isinstance(asReference, bool):
output_element.attrib['asReference'] = str(asReference).lower()
# outputIdentifierElement
etree.SubElement(
outputElement, nspath_eval('ows:Identifier', namespaces)).text = identifier
output_element, nspath_eval('ows:Identifier', namespaces)).text = identifier
# wait for 60 seconds by default
def checkStatus(self, url=None, response=None, sleepSecs=60):
"""
Method to check the status of a job execution.
In the process, this method will upadte the object 'response' attribute.
In the process, this method will update the object 'response' attribute.
:param str url: optional 'statusLocation' URL retrieved from a previous WPS Execute response document.
If not provided, the current 'statusLocation' URL will be used.
......@@ -923,6 +943,9 @@ class WPSExecution():
element = root.find(nspath('Status', ns=wpsns))
self.creationTime = testXMLAttribute(element, 'creationTime')
# get progress info
if self.status == 'ProcessSucceeded':
self.percentCompleted = 100
else:
try:
percentCompleted = int(statusEl.get('percentCompleted'))
self.percentCompleted = percentCompleted
......@@ -1323,7 +1346,7 @@ class Output(InputOutput):
if literalDataElement.text is not None and literalDataElement.text.strip() is not '':
self.data.append(literalDataElement.text.strip())
bboxDataElement = dataElement.find(nspath('BoundingBox', ns=namespaces['ows']))
if not bboxDataElement:
if bboxDataElement is not None:
# TODO: just a workaround for data-inputs in lineage
bboxDataElement = dataElement.find(nspath('BoundingBoxData', ns=namespaces['wps']))
if bboxDataElement is not None:
......@@ -1444,10 +1467,20 @@ class Process(object):
wpsns = getNamespace(elem)
def get_bool_attribute(elem, attribute):
property = elem.get(attribute, '').lower()
if property == 'true':
value = True
elif property == 'false':
value = False
else:
value = None
return value
# <ProcessDescription statusSupported="true" storeSupported="true" ns0:processVersion="1.0.0">
self.processVersion = elem.get(nspath('processVersion', ns=wpsns))
self.statusSupported = bool(elem.get("statusSupported"))
self.storeSupported = bool(elem.get("storeSupported"))
self.statusSupported = get_bool_attribute(elem, "statusSupported")
self.storeSupported = get_bool_attribute(elem, "storeSupported")
self.identifier = None
self.title = None
self.abstract = None
......@@ -1581,8 +1614,14 @@ class ComplexDataInput(IComplexDataInput, ComplexData):
"""
<wps:Reference xlink:href="http://somewhere/test.xml"/>
"""
refElement = etree.Element(nspath_eval('wps:Reference', namespaces),
attrib={nspath_eval("xlink:href", namespaces): self.value})
attrib = {nspath_eval("xlink:href", namespaces): self.value}
if self.encoding:
attrib['encoding'] = self.encoding
if self.schema:
attrib['schema'] = self.schema
if self.mimeType:
attrib['mimeType'] = self.mimeType
refElement = etree.Element(nspath_eval('wps:Reference', namespaces), attrib)
return refElement
def complexDataRaw(self):
......
-r requirements.txt
flake8
pytest
pytest>=3.6
pytest-cov
Pillow
tox
......
This diff is collapsed.
<wps100:Execute xmlns:wps100="http://www.opengis.net/wps/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" service="WPS" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsExecute_request.xsd"><ows110:Identifier xmlns:ows110="http://www.opengis.net/ows/1.1">wordcount</ows110:Identifier><wps100:DataInputs><wps100:Input><ows110:Identifier xmlns:ows110="http://www.opengis.net/ows/1.1">text</ows110:Identifier><wps100:Reference xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://emu.readthedocs.org/en/latest/index.html"/></wps100:Input></wps100:DataInputs><wps100:ResponseForm><wps100:ResponseDocument lineage="false" status="false" storeExecuteResponse="false"><wps100:Output asReference="true"><ows110:Identifier xmlns:ows110="http://www.opengis.net/ows/1.1">output</ows110:Identifier></wps100:Output></wps100:ResponseDocument></wps100:ResponseForm></wps100:Execute>
<wps100:Execute xmlns:wps100="http://www.opengis.net/wps/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" service="WPS" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsExecute_request.xsd"><ows110:Identifier xmlns:ows110="http://www.opengis.net/ows/1.1">wordcount</ows110:Identifier><wps100:DataInputs><wps100:Input><ows110:Identifier xmlns:ows110="http://www.opengis.net/ows/1.1">text</ows110:Identifier><wps100:Reference xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://emu.readthedocs.org/en/latest/index.html"/></wps100:Input></wps100:DataInputs><wps100:ResponseForm><wps100:ResponseDocument lineage="false" status="false" storeExecuteResponse="false"><wps100:Output asReference="false"><ows110:Identifier xmlns:ows110="http://www.opengis.net/ows/1.1">output</ows110:Identifier></wps100:Output></wps100:ResponseDocument></wps100:ResponseForm></wps100:Execute>
from tests.utils import service_ok
import pytest
from owslib.wfs import WebFeatureService
SERVICE_URL = 'https://www.ldproxy.nrw.de/rest/services/kataster/?f=json'
@pytest.mark.online
@pytest.mark.skipif(not service_ok(SERVICE_URL),
reason='service is unreachable')
def test_wfs3_ldproxy():
w = WebFeatureService(SERVICE_URL, version='3.0')
assert w.url == 'https://www.ldproxy.nrw.de/rest/services/kataster/'
assert w.version == '3.0'
assert w.url_query_string == 'f=json'
conformance = w.conformance()
assert len(conformance['conformsTo']) == 5
from tests.utils import service_ok
import pytest
from owslib.wfs import WebFeatureService
SERVICE_URL = 'http://geo.kralidis.ca/pygeoapi'
@pytest.mark.online
@pytest.mark.skipif(not service_ok(SERVICE_URL),
reason='service is unreachable')
def test_wfs3_pygeoapi():
w = WebFeatureService(SERVICE_URL, version='3.0')
assert w.url == 'http://geo.kralidis.ca/pygeoapi/'
assert w.version == '3.0'
assert w.url_query_string is None
conformance = w.conformance()
assert len(conformance['conformsTo']) == 4
collections = w.collections()
assert len(collections) == 3
lakes = w.collection('lakes')
assert lakes['name'] == 'lakes'
assert lakes['title'] == 'Large Lakes'
assert lakes['description'] == 'lakes of the world, public domain'
lakes_query = w.collection_items('lakes', limit=0)
assert lakes_query['numberMatched'] == 25
assert lakes_query['numberReturned'] == 0
assert len(lakes_query['features']) == 0
import pytest
from tests.utils import service_ok
from owslib.wms import WebMapService
......@@ -5,9 +6,8 @@ from owslib.util import ServiceException
from owslib.util import ResponseWrapper
import pytest
SERVICE_URL = 'http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-t.cgi'
NCWMS2_URL = "http://wms.stccmop.org:8080/ncWMS2/wms"
@pytest.mark.online
......@@ -36,7 +36,7 @@ def test_wms_getmap_111_service_exception():
"""GetMap 1.1.1 ServiceException for an invalid CRS"""
wms = WebMapService(SERVICE_URL, version='1.1.1')
try:
rsp = wms.getmap(
wms.getmap(
layers=['nexrad_base_reflect'],
styles=['default'],
srs='EPSG:4328',
......@@ -74,7 +74,7 @@ def test_wms_getmap_130_service_exception():
"""GetMap 1.3.0 ServiceException for an invalid CRS"""
wms = WebMapService(SERVICE_URL, version='1.3.0')
try:
rsp = wms.getmap(
wms.getmap(
layers=['nexrad_base_reflect'],
styles=['default'],
srs='EPSG:4328',
......@@ -120,3 +120,34 @@ def test_getmap_130_national_map():
assert "height=300" in wms.request
assert "format=image%2Fpng" in wms.request
assert "transparent=TRUE" in wms.request
@pytest.mark.online
@pytest.mark.skipif(not service_ok(NCWMS2_URL), reason="WMS service is unreachable")
def test_ncwms2():
"""Test with an ncWMS2 server.
"""
# Note that this does not exercise the bug in https://github.com/geopython/OWSLib/issues/556
wms = WebMapService(NCWMS2_URL, version='1.3.0')
rsp = wms.getmap(
layers=['f33_thredds/min_temp'],
styles=['default'],
srs='CRS:84',
bbox=(-124.17, 46.02, -123.29, 46.38),
size=(256, 256),
format='image/png',
transparent=True,
mode='32bit',
)
assert type(rsp) is ResponseWrapper
assert "service=WMS" in wms.request
assert "version=1.3.0" in wms.request
assert "request=GetMap" in wms.request
assert "layers=f33_thredds/min_temp" in wms.request
assert "styles=default" in wms.request
assert "crs=CRS%3A84" in wms.request
assert "width=256" in wms.request
assert "height=256" in wms.request
assert "format=image%2Fpng" in wms.request
assert "transparent=TRUE" in wms.request
......@@ -2,7 +2,7 @@ import pytest
from tests.utils import resource_file
from owslib.wps import WebProcessingService, WPSExecution, Process
from owslib.wps import WebProcessingService, WPSExecution, Process, is_reference
from owslib.etree import etree
......@@ -39,6 +39,12 @@ def test_wps_process_representation(wps):
assert str(p) == 'WPS Process: CDMSSubsetVariable, title=Writes a text file and returns an output.'
def test_wps_process_properties(wps):
p = wps.processes[0]
assert p.statusSupported is None
assert p.storeSupported is None
def test_wps_process_with_invalid_identifer():
p = Process(etree.Element('invalid'))
assert repr(p) == '<owslib.wps.Process >'
......@@ -69,3 +75,10 @@ def test_wps_response_with_lineage():
assert outp.title == 'Test Report'
assert outp.abstract == 'Compliance checker test report.'
assert outp.reference.startswith('http://localhost:8090/wpsoutputs')
def test_is_reference():
assert is_reference('http://testing.org')
assert is_reference(b'http://testing.org')
assert not is_reference('mumbo jumbo')
assert not is_reference(b'mumbo jumbo')