Esempio n. 1
0
def annotation(ax, text, loc='upper right',fontsize=8):
    """Put a general annotation in the plot."""
    at = AnchoredText('%s'% text, prop=dict(size=fontsize), frameon=True, loc=loc)
    at.patch.set_boxstyle("round,pad=0.,rounding_size=0.1")
    at.zorder = 10
    ax.add_artist(at)
    return(at)
Esempio n. 2
0
def annotation_run(ax, time, loc='upper right',fontsize=8):
    """Put annotation of the run obtaining it from the
    time array passed to the function."""
    at = AnchoredText('Run %s'% time.strftime('%Y%m%d %H UTC'), 
                       prop=dict(size=fontsize), frameon=True, loc=loc)
    at.patch.set_boxstyle("round,pad=0.,rounding_size=0.1")
    at.zorder = 10
    ax.add_artist(at)
    return(at)
Esempio n. 3
0
def annotation_forecast(ax, time, loc='upper left',fontsize=8, local=True):
    """Put annotation of the forecast time."""
    if local: # convert to local time
        time = convert_timezone(time)
        at = AnchoredText('Valid %s' % time.strftime('%A %d %b %Y at %H (Berlin)'), 
                       prop=dict(size=fontsize), frameon=True, loc=loc)
    else:
        at = AnchoredText('Forecast for %s' % time.strftime('%A %d %b %Y at %H UTC'), 
                       prop=dict(size=fontsize), frameon=True, loc=loc)
    at.patch.set_boxstyle("round,pad=0.,rounding_size=0.1")
    at.zorder = 10
    ax.add_artist(at)
    return(at) 
Esempio n. 4
0
def plastic_measurements(plastic_type='count',
                         output_path=None,
                         plot_style='plot_tools/plot.mplstyle'):
    time_cki, lon_cki, lat_cki, n_plastic_cki, kg_plastic_cki = get_cki_plastic_measurements(
    )
    time_ci, lon_ci, lat_ci, n_plastic_ci, kg_plastic_ci = get_christmas_plastic_measurements(
    )
    n_plastic_month_cki = np.zeros(12)
    n_months_cki = np.zeros(12)
    n_plastic_month_ci = np.zeros(12)
    n_months_ci = np.zeros(12)
    if plastic_type == 'count':
        samples_cki = n_plastic_cki
        samples_ci = n_plastic_ci
        ylabel = 'Plastic items [#]'
        ylim = [0, 140000]
        yticks = np.arange(0, 140000, 20000)
        ylim_studies = [0, 14]
        yticks_studies = np.arange(0, 14, 2)
    elif plastic_type == 'mass':
        samples_cki = kg_plastic_cki
        samples_ci = kg_plastic_ci
        ylabel = 'Plastic weight [kg]'
        ylim = [0, 4000]
        yticks = np.arange(0, 4000, 500)
        ylim_studies = [0, 16]
        yticks_studies = np.arange(0, 16, 2)
    else:
        raise ValueError(
            f'Unknown plastic type requested: {plastic_type}. Valid values are: count and mass.'
        )
    for i, t in enumerate(time_cki):
        n_months_cki[t.month - 1] += 1
        if plastic_type == 'count':
            n_plastic_month_cki[t.month - 1] = np.nansum(
                [n_plastic_month_cki[t.month - 1], n_plastic_cki[i]])
        elif plastic_type == 'mass':
            n_plastic_month_cki[t.month - 1] = np.nansum(
                [n_plastic_month_cki[t.month - 1], kg_plastic_cki[i]])
    for i, t in enumerate(time_ci):
        n_months_ci[t.month - 1] += 1
        if plastic_type == 'count':
            n_plastic_month_ci[t.month - 1] = np.nansum(
                [n_plastic_month_ci[t.month - 1], n_plastic_ci[i]])
        elif plastic_type == 'mass':
            n_plastic_month_ci[t.month - 1] = np.nansum(
                [n_plastic_month_ci[t.month - 1], kg_plastic_ci[i]])
    (main_colors_cki, main_sizes_cki,
     main_edgewidths_cki) = _get_marker_colors_sizes_for_samples(
         samples_cki, plastic_type)
    (main_colors_ci, main_sizes_ci,
     main_edgewidths_ci) = _get_marker_colors_sizes_for_samples(
         samples_ci, plastic_type)

    plt.style.use(plot_style)
    fig = plt.figure(figsize=(8, 6))

    months = np.arange(1, 13, 1)
    colors = get_months_colors()
    color = '#adadad'
    land_color = '#cfcfcf'
    # (a) seasonal distribution measured plastic CKI
    ax1 = plt.subplot(2, 2, 1)
    ax1.bar(months - 0.2,
            n_plastic_month_cki,
            width=0.4,
            color=colors,
            edgecolor='k',
            zorder=5)
    ax1.set_xticks(months)
    ax1.set_xticklabels([
        'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct',
        'Nov', 'Dec'
    ])
    ax1.set_ylim(ylim)
    ax1.set_yticks(yticks)
    ax1.set_ylabel(ylabel)
    anchored_text1 = AnchoredText(
        f'(a) Plastic samples from Cocos Keeling Islands (CKI)',
        loc='upper left',
        borderpad=0.0)
    ax1.add_artist(anchored_text1)
    # number of sampling studies per month
    ax2 = ax1.twinx()
    ax2.grid(False)
    ax2.bar(months + 0.2,
            n_months_cki,
            width=0.4,
            color=color,
            edgecolor='k',
            zorder=6)
    ax2.set_ylim(ylim_studies)
    ax2.set_yticks(yticks_studies)
    ax2.set_ylabel('Sampling studies [#]')
    ax2.spines['right'].set_color(color)
    ax2.tick_params(axis='y', colors=color)
    ax2.yaxis.label.set_color(color)
    # (b) seasonal distribution measured plastic CI
    ax3 = plt.subplot(2, 2, 3)
    ax3.bar(months - 0.2,
            n_plastic_month_ci,
            width=0.4,
            color=colors,
            edgecolor='k',
            zorder=5)
    ax3.set_xticks(months)
    ax3.set_xticklabels([
        'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct',
        'Nov', 'Dec'
    ])
    ax3.set_ylim(ylim)
    ax3.set_yticks(yticks)
    ax3.set_ylabel(ylabel)
    anchored_text2 = AnchoredText(
        f'(b) Plastic samples from Christmas Island (CI)',
        loc='upper left',
        borderpad=0.0)
    ax3.add_artist(anchored_text2)
    # number of sampling studies per month
    ax4 = ax3.twinx()
    ax4.grid(False)
    ax4.bar(months + 0.2,
            n_months_ci,
            width=0.4,
            color=color,
            edgecolor='k',
            zorder=6)
    ax4.set_ylim(ylim_studies)
    ax4.set_yticks(yticks_studies)
    ax4.set_ylabel('Sampling studies [#]')
    ax4.spines['right'].set_color(color)
    ax4.tick_params(axis='y', colors=color)
    ax4.yaxis.label.set_color(color)
    # (c) map of measured plastic CKI
    ax5 = plt.subplot(2, 2, 2, projection=ccrs.PlateCarree())
    lon_range_cki, lat_range_cki = get_cki_box_lon_lat_range()
    mplot1 = _iot_basic_map(ax5,
                            lon_range=lon_range_cki,
                            lat_range=lat_range_cki,
                            dlon=0.2,
                            dlat=0.1)
    l_nonan_cki = ~np.isnan(samples_cki)
    mplot1.points(lon_cki[l_nonan_cki],
                  lat_cki[l_nonan_cki],
                  facecolor=main_colors_cki,
                  edgecolor='k',
                  marker='o',
                  markersize=np.array(main_sizes_cki) * 5,
                  edgewidth=main_edgewidths_cki)
    anchored_text3 = AnchoredText(f'(c) Sampled plastics on CKI',
                                  loc='upper left',
                                  borderpad=0.0)
    anchored_text3.zorder = 8
    ax5.add_artist(anchored_text3)
    # (d) map of measured plastic CI
    ax6 = plt.subplot(2, 2, 4, projection=ccrs.PlateCarree())
    lon_range_ci, lat_range_ci = get_christmas_box_lon_lat_range()
    mplot2 = _iot_basic_map(ax6,
                            lon_range=lon_range_ci,
                            lat_range=lat_range_ci,
                            dlon=0.2,
                            dlat=0.1)
    l_nonan_ci = ~np.isnan(samples_ci)
    mplot2.points(lon_ci[l_nonan_ci],
                  lat_ci[l_nonan_ci],
                  facecolor=main_colors_ci,
                  edgecolor='k',
                  marker='o',
                  markersize=np.array(main_sizes_ci) * 5,
                  edgewidth=main_edgewidths_ci)
    anchored_text4 = AnchoredText(f'(d) Sampled plastics on CI',
                                  loc='upper left',
                                  borderpad=0.0)
    ax6.add_artist(anchored_text4)
    # legend
    legend_entries = _get_legend_entries_samples(plastic_type)
    ax5.legend(handles=legend_entries,
               loc='upper left',
               title=ylabel,
               bbox_to_anchor=(1.1, 1.0))
    # save
    if output_path:
        plt.savefig(output_path, bbox_inches='tight', dpi=300)
    plt.show()
Esempio n. 5
0
def _spectrogram(
    tr,
    starttime,
    endtime,
    is_infrasound,
    rescale,
    spec_win_dur,
    db_lim,
    freq_lim,
    log,
    is_local_time,
    resolution,
):
    """
    Make a combination waveform and spectrogram plot for an infrasound or
    seismic signal.

    Args:
        tr (:class:`~obspy.core.trace.Trace`): Input data, usually starts
            before `starttime` and ends after `endtime` (this function expects
            the response to be removed!)
        starttime (:class:`~obspy.core.utcdatetime.UTCDateTime`): Start time
        endtime (:class:`~obspy.core.utcdatetime.UTCDateTime`): End time
        is_infrasound (bool): `True` if infrasound, `False` if seismic
        rescale (int or float): Scale waveforms by this factor for plotting
        spec_win_dur (int or float): See docstring for :func:`~sonify.sonify`
        db_lim (tuple or str): See docstring for :func:`~sonify.sonify`
        freq_lim (tuple): Tuple defining frequency limits for spectrogram plot
        log (bool): See docstring for :func:`~sonify.sonify`
        is_local_time (bool): Passed to :class:`_UTCDateFormatter`
        resolution (str): See docstring for :func:`~sonify.sonify`

    Returns:
        Tuple of (`fig`, `spec_line`, `wf_line`, `time_box`, `wf_progress`)
    """

    if is_infrasound:
        ylab = 'Pressure (Pa)'
        clab = f'Power (dB rel. [{REFERENCE_PRESSURE * 1e6:g} µPa]$^2$ Hz$^{{-1}}$)'
        ref_val = REFERENCE_PRESSURE
    else:
        ylab = 'Velocity (µm s$^{-1}$)'
        if REFERENCE_VELOCITY == 1:
            clab = (
                f'Power (dB rel. {REFERENCE_VELOCITY:g} [m s$^{{-1}}$]$^2$ Hz$^{{-1}}$)'
            )
        else:
            clab = (
                f'Power (dB rel. [{REFERENCE_VELOCITY:g} m s$^{{-1}}$]$^2$ Hz$^{{-1}}$)'
            )
        ref_val = REFERENCE_VELOCITY

    fs = tr.stats.sampling_rate
    nperseg = int(spec_win_dur * fs)  # Samples
    nfft = np.power(2, int(np.ceil(np.log2(nperseg))) + 1)  # Pad fft with zeroes

    f, t, sxx = signal.spectrogram(
        tr.data, fs, window='hann', nperseg=nperseg, noverlap=nperseg // 2, nfft=nfft
    )

    # [dB rel. (ref_val <ref_val_unit>)^2 Hz^-1]
    sxx_db = 10 * np.log10(sxx / (ref_val**2))

    t_mpl = tr.stats.starttime.matplotlib_date + (t / mdates.SEC_PER_DAY)

    # Ensure a 16:9 aspect ratio
    fig = Figure(figsize=(FIGURE_WIDTH, (9 / 16) * FIGURE_WIDTH))

    # width_ratios effectively controls the colorbar width
    gs = GridSpec(2, 2, figure=fig, height_ratios=[2, 1], width_ratios=[40, 1])

    spec_ax = fig.add_subplot(gs[0, 0])
    wf_ax = fig.add_subplot(gs[1, 0], sharex=spec_ax)  # Share x-axis with spec
    cax = fig.add_subplot(gs[0, 1])

    wf_lw = 0.5
    wf_ax.plot(tr.times('matplotlib'), tr.data * rescale, '#b0b0b0', linewidth=wf_lw)
    wf_progress = wf_ax.plot(np.nan, np.nan, 'black', linewidth=wf_lw)[0]
    wf_ax.set_ylabel(ylab)
    wf_ax.grid(linestyle=':')
    max_value = np.abs(tr.copy().trim(starttime, endtime).data).max() * rescale
    wf_ax.set_ylim(-max_value, max_value)

    im = spec_ax.pcolormesh(
        t_mpl, f, sxx_db, cmap='inferno', shading='nearest', rasterized=True
    )

    spec_ax.set_ylabel('Frequency (Hz)')
    spec_ax.grid(linestyle=':')
    spec_ax.set_ylim(freq_lim)
    if log:
        spec_ax.set_yscale('log')

    # Tick locating and formatting
    locator = mdates.AutoDateLocator()
    wf_ax.xaxis.set_major_locator(locator)
    wf_ax.xaxis.set_major_formatter(_UTCDateFormatter(locator, is_local_time))
    fig.autofmt_xdate()

    # "Crop" x-axis!
    wf_ax.set_xlim(starttime.matplotlib_date, endtime.matplotlib_date)

    # Initialize animated stuff
    line_kwargs = dict(x=starttime.matplotlib_date, color='forestgreen', linewidth=1)
    spec_line = spec_ax.axvline(**line_kwargs)
    wf_line = wf_ax.axvline(ymin=0.01, clip_on=False, zorder=10, **line_kwargs)
    time_box = AnchoredText(
        s=starttime.strftime('%H:%M:%S'),
        pad=0.2,
        loc='lower right',
        bbox_to_anchor=[1, 1],
        bbox_transform=wf_ax.transAxes,
        borderpad=0,
        prop=dict(color='forestgreen'),
    )
    offset_px = -0.0025 * RESOLUTIONS[resolution][1]  # Resolution-independent!
    time_box.txt._text.set_y(offset_px)  # [pixels] Vertically center text
    time_box.zorder = 12  # This should place it on the very top; see below
    time_box.patch.set_linewidth(matplotlib.rcParams['axes.linewidth'])
    wf_ax.add_artist(time_box)

    # Adjustments to ensure time marker line is zordered properly
    # 9 is below marker; 11 is above marker
    spec_ax.spines['bottom'].set_zorder(9)
    wf_ax.spines['top'].set_zorder(9)
    for side in 'bottom', 'left', 'right':
        wf_ax.spines[side].set_zorder(11)

    # Pick smart limits rounded to nearest 10
    if db_lim == 'smart':
        db_min = np.percentile(sxx_db, 20)
        db_max = sxx_db.max()
        db_lim = (np.ceil(db_min / 10) * 10, np.floor(db_max / 10) * 10)

    # Clip image to db_lim if provided (doesn't clip if db_lim=None)
    im.set_clim(db_lim)

    # Automatically determine whether to show triangle extensions on colorbar
    # (kind of adopted from xarray)
    if db_lim:
        min_extend = sxx_db.min() < db_lim[0]
        max_extend = sxx_db.max() > db_lim[1]
    else:
        min_extend = False
        max_extend = False
    if min_extend and max_extend:
        extend = 'both'
    elif min_extend:
        extend = 'min'
    elif max_extend:
        extend = 'max'
    else:
        extend = 'neither'

    fig.colorbar(im, cax, extend=extend, extendfrac=EXTENDFRAC, label=clab)

    spec_ax.set_title(tr.id)

    fig.tight_layout()
    fig.subplots_adjust(hspace=0, wspace=0.05)

    # Finnicky formatting to get extension triangles (if they exist) to extend
    # above and below the vertical extent of the spectrogram axes
    pos = cax.get_position()
    triangle_height = EXTENDFRAC * pos.height
    ymin = pos.ymin
    height = pos.height
    if min_extend and max_extend:
        ymin -= triangle_height
        height += 2 * triangle_height
    elif min_extend and not max_extend:
        ymin -= triangle_height
        height += triangle_height
    elif max_extend and not min_extend:
        height += triangle_height
    else:
        pass
    cax.set_position([pos.xmin, ymin, pos.width, height])

    # Move offset text around and format it more nicely, see
    # https://github.com/matplotlib/matplotlib/blob/710fce3df95e22701bd68bf6af2c8adbc9d67a79/lib/matplotlib/ticker.py#L677
    magnitude = wf_ax.yaxis.get_major_formatter().orderOfMagnitude
    if magnitude:  # I.e., if offset text is present
        wf_ax.yaxis.get_offset_text().set_visible(False)  # Remove original text
        sf = ScalarFormatter(useMathText=True)
        sf.orderOfMagnitude = magnitude  # Formatter needs to know this!
        sf.locs = [47]  # Can't be an empty list
        wf_ax.text(
            0.002,
            0.95,
            sf.get_offset(),  # Let the ScalarFormatter do the formatting work
            transform=wf_ax.transAxes,
            ha='left',
            va='top',
        )

    return fig, spec_line, wf_line, time_box, wf_progress
Esempio n. 6
0
 def add_subtitle(self, subtitle, location='upper left'):
     anchored_text = AnchoredText(subtitle, loc=location, borderpad=0.0)
     anchored_text.zorder = 15
     self.ax.add_artist(anchored_text)