# -*- coding: utf-8 -*-
"""
Calculations for 3D ray paths.
:author:
Matthias Meschede
:copyright:
The ObsPy Development Team (devs@obspy.org)
:license:
GNU Lesser General Public License, Version 3
(https://www.gnu.org/copyleft/lesser.html)
"""
import warnings
import numpy as np
import obspy.geodetics.base as geodetics
[docs]
def get_ray_paths(inventory, catalog, phase_list=['P'],
coordinate_system='XYZ', taup_model='iasp91'):
"""
This function returns lat, lon, depth coordinates from an event
location to all stations in the inventory object
:param inventory: an obspy station inventory
:param catalog: an obspy event catalog
:param phase_list: a list of seismic phase names that is passed to taup
:param coordinate_system: can be either 'XYZ' or 'RTP'.
:param taup_model: the taup model for which the greatcircle paths are
computed
:returns: a list of tuples
``[(gcircle, phase_name, station_label, event_timestamp,
event_magnitude, event_id, origin_id), ...]``. ``gcircle`` is an array
of shape ``[3, npoints]`` with the path coordinates. ``phase_name`` is
the name of the seismic phase, ``station_label`` is the name of the
station and network that belongs to the path. ``event_timestamp``,
``event_magnitude``, ``event_id`` and ``origin_id`` describe the event
that belongs to the path.
"""
# GEOGRAPHICLIB is mandatory for this function
if not geodetics.HAS_GEOGRAPHICLIB:
raise ImportError('Geographiclib not found but required by ray path '
'routine')
stlats = []
stlons = []
stlabels = []
for network in inventory:
for station in network:
label_ = ".".join((network.code, station.code))
if station.latitude is None or station.longitude is None:
msg = ("Station '%s' does not have latitude/longitude "
"information and will not be plotted." % label_)
warnings.warn(msg)
continue
stlats.append(station.latitude)
stlons.append(station.longitude)
stlabels.append(label_)
# make a big list of event coordinates and names
# this part should be included as a subroutine of catalog that extracts
# a list of event properties. E.g. catalog.extract(['latitiude',
# 'longitude', 'depth', 'mag', 'focal_mechanism') The same should be done
# for an inventory with stations
evlats = []
evlons = []
evdepths = []
event_ids = []
origin_ids = []
magnitudes = []
times = []
for event in catalog:
if not event.origins:
msg = ("Event '%s' does not have an origin and will not be "
"plotted." % str(event.resource_id))
warnings.warn(msg)
continue
if not event.magnitudes:
msg = ("Event '%s' does not have a magnitude and will not be "
"plotted." % str(event.resource_id))
warnings.warn(msg)
continue
origin = event.preferred_origin() or event.origins[0]
evlats.append(origin.latitude)
evlons.append(origin.longitude)
if not origin.get('depth'):
# XXX do we really want to include events without depth???
origin.depth = 0.
evdepths.append(origin.get('depth') * 1e-3)
magnitude = event.preferred_magnitude() or event.magnitudes[0]
mag = magnitude.mag
event_ids.append(str(event.resource_id))
origin_ids.append(str(origin.resource_id))
magnitudes.append(mag)
times.append(origin.time.timestamp)
# initialize taup model if it is not provided
if isinstance(taup_model, str):
from obspy.taup import TauPyModel
model = TauPyModel(model=taup_model)
else:
model = taup_model
# now loop through all stations and source combinations
r_earth = model.model.radius_of_planet
greatcircles = []
for stlat, stlon, stlabel in zip(stlats, stlons, stlabels):
for evlat, evlon, evdepth_km, time, magnitude, event_id, origin_id \
in zip(evlats, evlons, evdepths, times, magnitudes, event_ids,
origin_ids):
arrivals = model.get_ray_paths_geo(
evdepth_km, evlat, evlon, stlat, stlon, phase_list=phase_list,
resample=True)
if len(arrivals) == 0:
continue
for arr in arrivals:
radii = (r_earth - arr.path['depth']) / r_earth
thetas = np.radians(90. - arr.path['lat'])
phis = np.radians(arr.path['lon'])
if coordinate_system == 'RTP':
gcircle = np.array([radii, thetas, phis])
if coordinate_system == 'XYZ':
gcircle = np.array([radii * np.sin(thetas) * np.cos(phis),
radii * np.sin(thetas) * np.sin(phis),
radii * np.cos(thetas)])
greatcircles.append((gcircle, arr.name, stlabel, time,
magnitude, event_id, origin_id))
return greatcircles