Source code for quakemigrate.plot.phase_picks

# -*- coding: utf-8 -*-
"""
Module to produce a summary plot for the phase picking.

:copyright:
    2020, QuakeMigrate developers.
:license:
    GNU General Public License, Version 3
    (https://www.gnu.org/licenses/gpl-3.0.html)

"""

import matplotlib.pyplot as plt
import numpy as np


[docs]def pick_summary(event, station, signal, picks, onsets, ttimes, window): """ Plot figure showing the filtered traces for each data component and the characteristic functions calculated from them (P and S) for each station. The search window to make a phase pick is displayed, along with the dynamic pick threshold (defined as a percentile of the background noise level), the phase pick time and its uncertainty (if made) and the Gaussian fit to the characteristic function. Parameters ---------- event : str Unique identifier for the event. station : str Station code. signal : `numpy.ndarray` of int Seismic data for the Z N and E components. picks : pandas DataFrame object Phase pick times with columns ["Name", "Phase", "ModelledTime", "PickTime", "PickError", "SNR"] Each row contains the phase pick from one station/phase. onsets : `numpy.ndarray` of float Onset functions for each seismic phase, shape(nstations, nsamples). ttimes : list, [int, int] Modelled phase travel times. window : list, [int, int] Indices specifying the window within which the pick was made. Returns ------- fig : `matplotlib.Figure` object Figure showing basic phase picking information. """ fig = plt.figure(figsize=(30, 15)) # Create plot axes, ordering: [Z data, N data, E data, P onset, S onset] for i in [2, 1, 3, 4, 5]: ax = fig.add_subplot(3, 2, i+1) ax.set_ylim([-1.1, 1.1]) axes = fig.axes # Share P-pick x-axes and set title axes[0].get_shared_x_axes().join(axes[0], axes[3]) axes[0].set_xticklabels([]) axes[0].set_yticklabels([]) axes[0].yaxis.set_ticks_position('none') axes[0].set_title("P phase", fontsize=22, fontweight="bold") # Share S-pick x-axes and set title for ax in axes[1:3]: ax.get_shared_x_axes().join(ax, axes[4]) ax.set_xticklabels([]) ax.set_yticklabels([]) ax.yaxis.set_ticks_position('none') axes[1].set_title("S phase", fontsize=22, fontweight="bold") # --- Grab event information once --- otime = event.otime times = event.data.times(type="utcdatetime") # --- Plot data signal --- # Trim data to just around phase picks min_t = otime + 0.5 * ttimes[0] max_t = otime + 1.5 * ttimes[1] # Find indices for window in which to get normalising factor, then plot min_t_idx = np.argmin([abs(t - min_t) for t in times]) max_t_idx = np.argmin([abs(t - max_t) for t in times]) times = event.data.times(type="matplotlib") for i, ax in enumerate(axes[:3]): # Funky maths to assign the signal data to correct axes y = signal[(i+2) % 3, :] # Get normalising factor within window norm = np.max(abs(y[min_t_idx:max_t_idx+1])) ax.plot(times, y / norm, c="k", lw=0.5, zorder=1) for i, (ax, ph) in enumerate(zip(axes[3:], ["P", "S"])): y = onsets[i] win = window[ph] # Get normalising factor within window norm = np.max(abs(y[win[0]:win[1]+1])) ax.plot(times, y / norm, c="k", lw=0.5, zorder=1) for ax in axes: ax.set_xlim([min_t.datetime, max_t.datetime]) # --- Plot labels and fix limits --- shift = (max_t - min_t) * 0.01 # Fractional shift for text label for comp, ax in zip(["Z", "N", "E"], axes[:3]): ax.text((min_t + shift).datetime, 0.9, f"{station}.BH{comp}", ha="left", va="center", zorder=2, fontsize=18) ax.set_ylim([-1.1, 1.1]) ax.set_yticks(np.arange(-1, 1.5, 0.5)) for ph, ax in zip(["P", "S"], axes[3:]): ax.text((min_t + shift).datetime, 1., f"{ph} onset", ha="left", va="center", zorder=2, fontsize=18) ax.set_ylim([-0.1, 1.1]) ax.set_yticks(np.arange(0., 1.2, 0.2)) # --- Plot predicted arrival times --- for i, ax in enumerate(axes): model_pick = otime + ttimes[0] if i % 3 == 0 else otime + ttimes[1] ax.axvline(model_pick.datetime, alpha=0.9, c="k", label="Modelled pick time") # --- Plot picks and summary information --- text = fig.add_subplot(3, 2, 1) text.text(0.5, 0.8, f"Event: {event.uid}\nStation: {station}", ha="center", va="center", fontsize=22, fontweight="bold") for i, pick in picks.iterrows(): # Pick lines if pick["Phase"] == "P": c1, c2 = "#F03B20", "gray" else: c1, c2 = "gray", "#3182BD" if pick["PickTime"] != -1: for j, ax in enumerate(axes): clr = c1 if j % 3 == 0 else c2 _plot_phase_pick(ax, pick, clr) # Summary text text.text(0.1+i*0.5, 0.6, f"{pick.Phase} phase", ha="center", va="center", fontsize=20, fontweight="bold") pick_info = (f"Pick time: {pick.PickTime}\n" f"Pick error: {pick.PickError:5.3f} s\n" f"Pick SNR: {pick.SNR:5.3f}") text.text(0.05+i*0.5, 0.45, pick_info, ha="left", va="center", fontsize=18) text.set_axis_off() for ax in axes[3:5]: ax.legend() fig.tight_layout(pad=1, w_pad=0) plt.subplots_adjust(hspace=0) return fig
def _plot_phase_pick(ax, pick, clr): """ Plot the phase pick time with the associated uncertainty. Parameters ---------- ax : `matplotlib.Axes` object Axes on which to plot the pick. pick : `pandas.DataFrame` object Contains information on the phase pick. clr : str Colour to use when plotting the phase pick. """ pick_time, pick_err = pick["PickTime"], pick["PickError"] ax.axvline((pick_time - pick_err/2).datetime, ls="--", c=clr) ax.axvline((pick_time + pick_err/2).datetime, ls="--", c=clr) ax.axvline((pick_time).datetime, c=clr, label=f"{pick.Phase} pick time")