Source code for seismic.plot.plot_correlation

'''
:copyright:
   The SeisMIC development team (makus@gfz-potsdam.de).
:license:
    GNU Lesser General Public License, Version 3
    (https://www.gnu.org/copyleft/lesser.html)
:author:
   Peter Makus (makus@gfz-potsdam.de)

Created: Monday, 19th July 2021 11:37:54 am
Last Modified: Monday, 4th March 2024 02:42:42 pm
'''
import os
from typing import Tuple, Optional, List
import datetime

import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib.dates import date2num
import numpy as np
from obspy.core.utcdatetime import UTCDateTime
from obspy import Trace, Stream

from seismic.plot.plot_utils import set_mpl_params, remove_topright, remove_all


[docs]def plot_ctr( corr: Trace, tlim: list or tuple = None, ax: plt.Axes = None, outputdir: str = None, clean: bool = False) -> plt.Axes: set_mpl_params() # Get figure/axes dimensions if ax is None: width, height = 10, 2.5 fig = plt.figure(figsize=(width, height)) ax = plt.gca() axtmp = None else: fig = plt.gcf() bbox = ax.get_window_extent().transformed( fig.dpi_scale_trans.inverted()) width, height = bbox.width, bbox.height axtmp = ax # The ratio ensures that the text # is perfectly distanced from top left/right corner ratio = width/height times = corr.times() ydata = corr.data # Plot stuff into axes ax.plot(times, ydata, 'k', lw=1) # Set limits if tlim is None: ax.set_xlim(times[0], times[-1]) else: ax.set_xlim(tlim) ax.set_ylim([-1, 1]) # Removes top/right axes spines. If you want the whole thing, comment # or remove remove_topright() # Plot RF only if clean: remove_all() else: ax.set_xlabel("Lag Time [s]") ax.set_ylabel("Correlation") # Start time in station stack does not make sense text = '%s - %s\n%s' % ( corr.stats.corr_start.strftime('%Y/%m/%d %H:%M'), corr.stats.corr_end.strftime('%Y/%m/%d %H:%M'), corr.get_id()) # corr.stats.corr_start.isoformat(sep=" ") + "\n" + corr.get_id() ax.text(0.995, 1.0-0.005*ratio, text, transform=ax.transAxes, horizontalalignment="right", verticalalignment="top") # Only use tight layout if not part of plot. if axtmp is None: plt.tight_layout() # Hide minor ticks on y-axis plt.tick_params(axis='y', which='minor', length=0) # Outout the receiver function as pdf using # its station name and starttime if outputdir is not None: filename = os.path.join( outputdir, corr.get_id() + "_" + corr.stats.starttime.strftime('%Y%m%dT%H%M%S') + ".pdf") plt.savefig(filename, format="pdf") return ax
[docs]def plot_cst( cst: Stream, sort_by: str = 'corr_start', timelimits: list or tuple or None = None, ylimits: list or tuple or None = None, scalingfactor: float = 2.0, ax: plt.Axes = None, linewidth: float = 0.25, outputfile: str or None = None, title: str or None = None, type: str = 'heatmap', cmap: str = 'seismic', vmin: float = None, vmax: float = None, **kwargs): """ Creates a section or heat plot of all correlations in this stream. :param cst: Input CorrelationStream :type cst: :class:`~seismic.correlate.stream.CorrStream` :param sort_by: Which parameter to plot against. Can be either ``corr_start`` or ``distance``, defaults to 'corr_start'. :type sort_by: str, optional :param timelimits: xlimits (lag time) in seconds, defaults to None :type timelimits: Tuple[float, float], optional :param ylimits: limits for Y-axis (either a :class:`datetime.datetime` or float in km (if plotted against distance)), defaults to None. :type ylimits: Tuple[float, float], optional :param scalingfactor: Which factor to scale the Correlations with. Play around with this if you want to make amplitudes bigger or smaller, defaults to None (automatically chosen). :type scalingfactor: float, optional :param ax: Plot in existing axes? Defaults to None :type ax: plt.Axes, optional :param linewidth: Width of the lines to plot, defaults to 0.25 :type linewidth: float, optional :param outputfile: Save the plot? defaults to None :type outputfile: str, optional :param title: Title of the plot, defaults to None :type title: str, optional :param type: can be set to 'heat' for a heatmap or 'section' for a wiggle type plot. Default to `heat` :type type: str, optional :param cmap: Decides about colormap if type == 'heatmap'. Defaults to 'inferno'. :type cmap: str, optional :return: returns the axes object. :rtype: `matplotlib.pyplot.Axes` """ set_mpl_params() scalingfactor = scalingfactor or 1 # Create figure if no axes is specified if ax is None: plt.figure(figsize=(8, 6)) ax = plt.axes() # zorder=9999999 if sort_by == 'distance': sort_by = 'dist' cst.sort(keys=[sort_by, 'network', 'station', 'channel']) if not cst.count(): raise KeyError('No Correlations with the desired properties found.') # Plot traces if sort_by == 'corr_start': scalingfactor *= 1e5 if type == 'section': times = sect_plot_corr_start(cst, ax, scalingfactor, linewidth) elif type == 'heatmap': times = heat_plot_corr_start( cst, ax, cmap=cmap, vmin=vmin, vmax=vmax) else: raise NotImplementedError( 'Unknown or not implemented plot type %s.' % type) elif sort_by == 'dist': if type == 'section': scalingfactor *= 4 times = sect_plot_dist( cst, ax, scalingfactor, linewidth, **kwargs) elif type == 'heatmap': times = heat_plot_corr_dist( cst, ax, cmap=cmap, vmin=vmin, vmax=vmax, **kwargs) else: raise NotImplementedError('Unknown sorting method %s.' % sort_by) plt.tick_params(axis='y', which='minor', length=0) # Set limits if ylimits: plt.ylim(ylimits) if timelimits is None: plt.xlim(times[0], times[-1]) else: plt.xlim(timelimits) plt.xlabel(r"$\tau$ [s]") plt.title(title) # Set output directory if outputfile is not None: plt.savefig(outputfile, dpi=300, transparent=True) return ax
[docs]def plot_corr_bulk( corr_bulk, timelimits: Optional[Tuple[datetime.datetime, datetime.datetime]] = None, ylimits: Optional[Tuple[datetime.datetime, datetime.datetime]] = None, clim: Optional[Tuple[float, float]] = None, plot_colorbar: bool = False, outputfile: Optional[str] = None, title: Optional[str] = None, ax: Optional[plt.Axes] = None) -> plt.Axes: """ Plots a :class:`~seismic.correlate.stream.CorrBulk` object. :param corr_bulk: The CorrBulk to plot. :type corr_bulk: :class:`~seismic.correlate.stream.CorrBulk` :param timelimits: Limits time axis, defaults to None :type timelimits: Optional[Tuple[datetime.datetime, datetime.datetime]], optional :param ylimits: Limits of y-axis, defaults to None :type ylimits: Optional[Tuple[datetime.datetime, datetime.datetime]], optional :param clim: Limits of Colobar, defaults to None :type clim: Optional[Tuple[float, float]], optional :param plot_colorbar: add colorbar to plot, defaults to False :type plot_colorbar: bool, optional :param outputfile: save file to, defaults to None :type outputfile: Optional[str], optional :param title: Title, defaults to None :type title: Optional[str], optional :param ax: Axis to plot into, defaults to None :type ax: Optional[plt.Axes], optional :return: The current axis :rtype: plt.Axes """ set_mpl_params() # check for constant sampling if any(np.diff(corr_bulk.stats.corr_start, 2)): raise NotImplementedError( 'Correlation times have to be sampled at equal intervals.') # Create figure if no axes is specified if ax is None: plt.figure(figsize=(8, 6)) ax = plt.axes() # zorder=9999999 extent = [ corr_bulk.stats.start_lag, corr_bulk.stats.end_lag, date2num(corr_bulk.stats.corr_start[-1].datetime), date2num(corr_bulk.stats.corr_start[0].datetime)] im = ax.imshow( corr_bulk.data, extent=extent, aspect='auto', cmap='seismic') ax.yaxis_date() ax.figure.autofmt_xdate(rotation=45) ax.set_xlabel(r'$\tau$ [s]') # Set limits if ylimits: ax.set_ylim(date2num(ylimits[0]), date2num(ylimits[1])) if timelimits: ax.set_xlim(timelimits) if title: ax.set_title(title) if clim: im.set_clim(clim) if plot_colorbar: plt.colorbar(im, orientation='vertical') if outputfile: plt.savefig(outputfile) else: plt.show() return ax
[docs]def sect_plot_corr_start( cst: Stream, ax: plt.Axes, scalingfactor: float, linewidth: float) -> np.ndarray: for ii, ctr in enumerate(cst): ydata = ctr.data times = ctr.times() ytmp = ctr.stats['corr_start'].timestamp ctmp = ydata * scalingfactor + ytmp ctmp = np.array([UTCDateTime(t).datetime for t in ctmp]) ax.plot(times, ctmp, 'k', lw=linewidth, zorder=-ii + 0.1) ax.yaxis.set_major_locator(mpl.dates.AutoDateLocator()) ax.yaxis.set_major_formatter(mpl.dates.DateFormatter('%d %h %y')) ax.invert_yaxis() return times
[docs]def heat_plot_corr_start( cst: Stream, ax: plt.Axes, cmap: str, vmin: float, vmax: float): data = np.empty((cst.count(), cst[0].stats.npts)) # y grid y = [] for ii, ctr in enumerate(cst): data[ii, :] = ctr.data y.append(ctr.stats['corr_start'].datetime) times = ctr.times() ds = plt.pcolormesh( times, np.array(y), data, shading='auto', cmap=cmap, vmin=vmin, vmax=vmax) plt.colorbar( ds, label='Correlation Coefficient', shrink=.6, orientation='horizontal') ax.yaxis.set_major_locator(mpl.dates.AutoDateLocator()) ax.yaxis.set_major_formatter(mpl.dates.DateFormatter('%d %h %y')) ax.invert_yaxis() return times
[docs]def sect_plot_dist( cst: Stream, ax: plt.Axes, scalingfactor: float, linewidth: float, plot_reference_v: bool = False, ref_v: List[float] = [1, 2, 3]) -> np.ndarray: # Sort by azimuth. W on left side of plot for ii, ctr in enumerate(cst): ydata = ctr.data times = ctr.times() if ctr.stats.az >= 180: times = -1*times ytmp = ydata * scalingfactor + ctr.stats.dist ax.plot(times, ytmp, 'k', lw=linewidth, zorder=-ii + 0.1) if plot_reference_v: yref = np.array([ax.get_ylim()[1], 0, ax.get_ylim()[1]]) [plt.plot( [-yref[0]/rv, 0, yref[0]/rv], yref, linewidth=.5, zorder=0, label=f'{rv}'+r'$\frac{km}{s}$') for rv in ref_v] plt.legend() plt.ylabel(r"Distance [km]") # Set label locations. # step = round((cst[-1].stats.dist - cst[0].stats.dist)/10000) # plt.yticks(np.arange(0, ctr.stats.dist/1000+step, step, dtype=int)) plt.yticks(np.linspace(0, ctr.stats.dist, 10, dtype=int)) return times
[docs]def heat_plot_corr_dist( cst: Stream, ax: plt.Axes, cmap: str, vmin: float, vmax: float, plot_reference_v: bool = False, ref_v: List[float] = [1, 2, 3]): data = np.empty((cst.count(), cst[0].stats.npts)) # y grid y = np.zeros((len(cst),)) # x coords times = cst[0].times() for ii, ctr in enumerate(cst): if ctr.stats.az >= 180: data[ii, :] = np.flip(ctr.data) else: data[ii, :] = ctr.data y[ii] = ctr.stats.dist ds = plt.pcolormesh( times, y, data, shading='auto', cmap=cmap, vmin=vmin, vmax=vmax) if plot_reference_v: yref = np.array([ax.get_ylim()[1], 0, ax.get_ylim()[1]]) [plt.plot( [-yref[0]/rv, 0, yref[0]/rv], yref, linewidth=.5, zorder=99, label=f'{rv}'+r'$\frac{km}{s}$') for rv in ref_v] plt.legend() plt.colorbar( ds, label='Correlation Coefficient', shrink=.6, orientation='horizontal') plt.yticks(np.linspace(0, ctr.stats.dist, 10, dtype=int)) ax.set_ylabel(r"Distance [km]") return times