Module snowpat.SnowLense.plotting

Expand source code
import matplotlib.pyplot as plt
from snowpat.snowpackreader import Snowpack
from numpy import ndarray, append
from typing import Dict
import matplotlib.colors as mcolors
import matplotlib as mpl
import importlib.resources as pkg_resources

import numpy as np

from .plot_helpers_snowpack import grain_type_color, adjustData, _add_crust_lines

CMAP = mcolors.ListedColormap(grain_type_color)
NORM = mcolors.BoundaryNorm([0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5], 9)

# h, T, grain type, wl, hardness, ssif, layer_middle
NEEDED_VARS = ["0501", "0503", "0513", "0530", "0534", "0604", "layer middle", "layer thickness"]

def plotProfile(profile:Snowpack, out:str = None, ind_mfcrust = True, standardized_limits:bool=True)->plt.Figure:
    data = _aggregate_data(profile)
    if not data:
        return None
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.yaxis.tick_right()
    ax.yaxis.set_label_position("right")
    ax.set_ylabel("Height (cm)")

    if standardized_limits:
        ymax = 250 if data["0501"][-1] < 250 else data["0501"][-1]
        ax.set_ylim(0, ymax)
    
    mpl.rcParams['hatch.linewidth'] = 2.0
    
    _plot_hardness(ax, data, ind_mfcrust, profile.isNewton, profile.old_hardness)
    _plot_temperature(ax, data, standardized_limits)
    _plot_stability_index(ax, data)
    
    plt.close(fig)
    if out:
        fig.savefig(out)
    return fig

def _aggregate_data(profile:Snowpack) -> Dict[str, ndarray]:
    data:Dict[str,ndarray] = {}
    for var in NEEDED_VARS:
        data[var] = profile.get_param(var)
        try:
            data["layer middle"] = profile.data["layer middle"]
        except KeyError:
            data["layer middle"] = None
        if var == "0501" and data[var].size < 2:
            RuntimeWarning("Profile data is too small to plot")
            return None
    return data 

def _plot_temperature(ax:plt.Axes, data:Dict[str,ndarray], standardized_limits:bool):
    T = data["0503"]
    if data["layer middle"] is None:
        T = append(T, T[-1])
        z = data["0501"]
    else:
        z = data["layer middle"]
    ax2 = ax.twiny()
    ax2.plot(T, z, color="red", zorder=3)
    ax2.set_xlabel("Temperature (C)")
    
    if standardized_limits:
        Tmin = T.min()
        xmin = -20 if Tmin > -20 else Tmin
        ax2.set_xlim(xmin, 0)
    
    ax.xaxis.tick_top()
    ax.xaxis.set_label_position("top")
    ax2.xaxis.tick_bottom()
    ax2.xaxis.set_label_position("bottom")


    
def _plot_hardness(ax:plt.Axes, data:Dict[str,ndarray], ind_meltcrust:bool, isNewton:bool = False, old_hardness:bool = True):
    grain_type = data["0513"]
    hardness = data["0534"]
    
    grain_type = adjustData("0513", grain_type)
    
    if old_hardness:
        if all(hardness < 0): # TODO: possibly remove this, if change is accepted
            hardness = hardness*-1

    hatch_mask = ["///" if grain == 7.2 and ind_meltcrust else "" for grain in grain_type]
    # Plot the horizontal bar plot with hash lines when grain type is 7.2
    bars = ax.barh(data["layer middle"], hardness, data["layer thickness"]*2, color=CMAP(NORM(grain_type)), hatch=hatch_mask, zorder=1)
  
    if isNewton:
        ax.set_xlabel("Hardness (N)")
        ax.invert_xaxis()
    else:
        ax.set_xlabel("Hardness (Id)")
        ax.invert_xaxis()
        if np.abs(hardness).max() > 5:
            xticks = ["Fist", "4F", "1F", "Pencil", "Knife", "Ice"]
            ax.set_xticks([1, 2, 3, 4, 5, 6])
            ax.set_xticklabels(xticks)
        else:
            xticks = ["Fist", "4F", "1F", "Pencil", "Knife"]
            ax.set_xticks([1, 2, 3, 4, 5])
            ax.set_xticklabels(xticks)
            
        
            
    
    
def _plot_stability_index(ax:plt.Axes, data:Dict[str,ndarray]):
    ssi = data["0604"]
    wl = data["0530"]
    sk38 = wl[-1]
    z_sk38 = wl[-2]
    
    layer_boundaries = data["0501"]
    
    # find the index where z_sk38 is located
    layer = next((i for i, boundary in enumerate(layer_boundaries) if boundary > z_sk38), 0)
    
    SSI = ssi[layer] if layer < len(ssi) else ssi[-1]
    if sk38 < 0.45 and SSI < 1.32:
        stability = "cl: Poor"
    elif sk38 < 0.45 and SSI >= 1.32:
        stability = "cl: Fair"
    else:
        stability = "cl: Good"

    ymin, ymax = ax.get_ylim()
    
    z_sk38 = (z_sk38 - ymin) / (ymax - ymin)

    # Draw a horizontal arrow at height z
    ax.annotate('', xy=(1, z_sk38), xytext=(1.2, z_sk38),
                arrowprops=dict(facecolor='black', shrink=0.05, width=0.5, headwidth=5),
                xycoords='axes fraction', textcoords='axes fraction',
                horizontalalignment='right', verticalalignment='center',
                zorder=1)  # Add this line here
        
    # Add stability above the arrow
    ax.annotate(stability, xy=(1.1, z_sk38), xytext=(1.1, z_sk38+0.03),
                xycoords='axes fraction', textcoords='axes fraction',
                horizontalalignment='left', verticalalignment='center',
                zorder=1)  # Add this line here

    # Add sk38 value below the arrow
    ax.annotate("sk38: "+str(sk38), xy=(1.1, z_sk38), xytext=(1.1, z_sk38-0.03),
                xycoords='axes fraction', textcoords='axes fraction',
                horizontalalignment='left', verticalalignment='center',
                zorder=1)  # Add this line here
    
def show_logo():
    with pkg_resources.path('snowpat.resources', 'snowpat.png') as logo_path:
        logo = plt.imread(str(logo_path))
        plt.imshow(logo)
        plt.show()

Functions

def plotProfile(profile: snowpat.snowpackreader.Snowpack.Snowpack, out: str = None, ind_mfcrust=True, standardized_limits: bool = True) ‑> matplotlib.figure.Figure
Expand source code
def plotProfile(profile:Snowpack, out:str = None, ind_mfcrust = True, standardized_limits:bool=True)->plt.Figure:
    data = _aggregate_data(profile)
    if not data:
        return None
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.yaxis.tick_right()
    ax.yaxis.set_label_position("right")
    ax.set_ylabel("Height (cm)")

    if standardized_limits:
        ymax = 250 if data["0501"][-1] < 250 else data["0501"][-1]
        ax.set_ylim(0, ymax)
    
    mpl.rcParams['hatch.linewidth'] = 2.0
    
    _plot_hardness(ax, data, ind_mfcrust, profile.isNewton, profile.old_hardness)
    _plot_temperature(ax, data, standardized_limits)
    _plot_stability_index(ax, data)
    
    plt.close(fig)
    if out:
        fig.savefig(out)
    return fig
Expand source code
def show_logo():
    with pkg_resources.path('snowpat.resources', 'snowpat.png') as logo_path:
        logo = plt.imread(str(logo_path))
        plt.imshow(logo)
        plt.show()