Source code for obspy.clients.fdsn.routing.eidaws_routing_client
# -*- coding: utf-8 -*-
"""
Routing client for the EIDAWS routing service.
:copyright:
The ObsPy Development Team (devs@obspy.org)
:license:
GNU Lesser General Public License, Version 3
(https://www.gnu.org/copyleft/lesser.html)
"""
import collections
from ..client import get_bulk_string
from ..header import FDSNNoDataException
from .routing_client import (
BaseRoutingClient, _assert_attach_response_not_in_kwargs,
_assert_filename_not_in_kwargs)
[docs]class EIDAWSRoutingClient(BaseRoutingClient):
"""
Routing client for the EIDAWS routing service.
http://www.orfeus-eu.org/data/eida/webservices/routing/
For waveform queries it will first launch a station query, get the
station information at each data center with additional constraints
(e.g. latitude/longitude/...) and use that information for the final
waveform query. This means that with ObsPy the EIDA routing client
behaves very similar to the IRIS federator routing client.
"""
[docs] def __init__(self, url="http://www.orfeus-eu.org/eidaws/routing/1",
include_providers=None, exclude_providers=None,
debug=False, timeout=120, **kwargs):
"""
Initialize an EIDAWS router client.
All parameters except ``url`` are passed on to the
:class:`~obspy.clients.fdsn.routing.routing_client.BaseRoutingClient`
parent class
:param url: The URL of the routing service.
:type url: str
"""
BaseRoutingClient.__init__(self, debug=debug, timeout=timeout,
include_providers=include_providers,
exclude_providers=exclude_providers,
**kwargs)
self._url = url
[docs] @_assert_filename_not_in_kwargs
@_assert_attach_response_not_in_kwargs
def get_waveforms_bulk(self, bulk, **kwargs):
"""
Get waveforms from multiple data centers.
Arguments are the same as in
:meth:`obspy.clients.fdsn.client.Client.get_waveforms_bulk()`.
Any additional ``**kwargs`` are passed on to each individual service's
dataselect service if the service supports them (otherwise they are
silently ignored for that particular fdsnws endpoint).
The ``filename`` and ``attach_response`` parameters of the single
provider FDSN client are not supported.
This can route on a number of different parameters, please see the
web site of the `EIDAWS Routing Service
<http://www.orfeus-eu.org/data/eida/webservices/routing/>`_
for details.
"""
# Multi-step procedure - first get the stations to be able to use
# more query parameters - and then construct the waveform string
# from it.
#
# This has to be done for each time interval - otherwise it will get
# a lot more complicated. I guess in most cases people will use bulk
# requests for the same time span so it should be fine.
# Group by time interval - utilize the existing get_bulk_string()
# method to not have to deal with various different inputs.
_tmp_bulk_str = get_bulk_string(bulk, {})
if hasattr(_tmp_bulk_str, "decode"):
_tmp_bulk_str = _tmp_bulk_str.decode()
# Parse and split.
bulk_per_time_interval = collections.defaultdict(list)
for line in _tmp_bulk_str.splitlines():
# Cannot really happen - just a safety measure.
if not line: # pragma: no cover
continue
item = line.split()
bulk_per_time_interval[(item[-2], item[-1])].append(item)
# Build up the new bulk string for each found time interval by
# querying the station services.
new_bulk = []
for t, _b in bulk_per_time_interval.items():
# channel level and text to keep it fast.
inv = self.get_stations_bulk(_b, format="text",
level="channel", **kwargs)
for c in sorted(set(inv.get_contents()["channels"])):
new_bulk.append(c.split("."))
new_bulk[-1].extend(t)
# no available data, show appropriate error message and raise
if not new_bulk:
msg = ('No data available for request (requested time window '
'might be out of bounds of valid station epochs).')
raise FDSNNoDataException(msg)
# Finally get the waveforms by getting the routes and downloading
# everytyhing. Don't directly pass in the initializer as the order
# would not be guaranteed across all Python version.
arguments = collections.OrderedDict()
arguments["service"] = "dataselect"
arguments["format"] = "post"
bulk_str = get_bulk_string(new_bulk, arguments)
r = self._download(self._url + "/query", data=bulk_str,
content_type='text/plain')
split = self._split_routing_response(
r.content.decode() if hasattr(r.content, "decode") else r.content)
return self._download_waveforms(split, **kwargs)
[docs] @_assert_filename_not_in_kwargs
def get_stations(self, **kwargs):
"""
Get stations from multiple data centers.
Only the ``network``, ``station``, ``location``, ``channel``,
``starttime``, and ``endtime`` parameters are used for the actual
routing. These and all other arguments are then just passed on to each
single fdsnws station implementation.
Arguments are the same as in
:meth:`obspy.clients.fdsn.client.Client.get_stations()`.
Any additional ``**kwargs`` are passed on to each individual service's
station service if the service supports them (otherwise they are
silently ignored for that particular fdsnws endpoint).
The ``filename`` parameter of the single provider FDSN client is not
supported for practical reasons.
This can route on a number of different parameters, please see the
web site of the `EIDAWS Routing Service
<http://www.orfeus-eu.org/data/eida/webservices/routing/>`_
for details.
"""
return super(EIDAWSRoutingClient, self).get_stations(**kwargs)
[docs] @_assert_filename_not_in_kwargs
def get_stations_bulk(self, bulk, **kwargs):
"""
Bulk station download from multiple stations.
Arguments are the same as in
:meth:`obspy.clients.fdsn.client.Client.get_stations_bulk()`.
Any additional ``**kwargs`` are passed on to each individual service's
station service if the service supports them (otherwise they are
silently ignored for that particular fdsnws endpoint).
The ``filename`` parameter of the single provider FDSN client is not
supported for practical reasons.
This can route on a number of different parameters, please see the
web site of the `EIDAWS Routing Service
<http://www.orfeus-eu.org/data/eida/webservices/routing/>`_
for details.
"""
arguments = collections.OrderedDict()
arguments["service"] = "station"
arguments["format"] = "post"
arguments["alternative"] = "false"
bulk_str = get_bulk_string(bulk, arguments)
r = self._download(self._url + "/query", data=bulk_str,
content_type='text/plain')
split = self._split_routing_response(
r.content.decode() if hasattr(r.content, "decode") else r.content)
return self._download_stations(split, **kwargs)
[docs] @staticmethod
def _split_routing_response(data):
"""
Splits the routing responses per data center for the EIDAWS output.
Returns a dictionary with the keys being the root URLs of the fdsnws
endpoints and the values the data payloads for that endpoint.
:param data: The return value from the EIDAWS routing service.
"""
split = collections.defaultdict(list)
current_key = None
for line in data.splitlines():
line = line.strip()
if not line:
continue
if "http" in line and "fdsnws" in line:
current_key = line[:line.rfind("/fdsnws")]
continue
split[current_key].append(line)
return {k: "\n".join(v) for k, v in split.items()}