Source code for pyClimat.plot_utils

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Jul 29 18:50:11 2021

@author: dboateng
This module contains all the utilities used in the Climat_plots
"""
# Import modules

import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.patches as mpatches
import matplotlib.colors as colors
import matplotlib.path as mpath
from cycler import cycler
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.mpl.ticker import (LongitudeFormatter, LatitudeFormatter,
                                LatitudeLocator)
from cartopy.util import add_cyclic_point
from matplotlib import rc
import matplotlib.colors


# defining image sizes and colors 
# A4 paper size: 210 mm X 297 mm

cm = 0.3937   # 1 cm in inch for plot size
pt = 1/72.27  # pt in inch from latex geometry package
textwidth = 345*pt
big_width = textwidth + 2*3*cm

# colors
orange = 'orangered'
lightblue = 'teal'
brown = 'sienna'
red = '#a41a36'
blue = '#006c9e'
green = '#55a868'
purple = '#8172b2'
lightbrown = '#ccb974'
pink = 'fuchsia'
lightgreen = 'lightgreen'
skyblue = "skyblue"
tomato = "tomato"
gold = "gold"
magenta = "magenta"
black = "black"
grey = "grey"
golden = "darkgoldenrod"
cyan = "cyan"
olive = "olive"

#divergence colors 
RdBu_r = plt.cm.RdBu_r
RdBu = plt.cm.RdBu
Blues = plt.cm.Blues
Spectral_r = plt.cm.Spectral_r
terrain = plt.cm.terrain
viridis = plt.cm.viridis
BwR_r = plt.cm.bwr_r
BwR = plt.cm.bwr
Seismic = plt.cm.seismic
Reds = plt.cm.Reds
Greys = plt.cm.Greys
RdGy = plt.cm.RdGy
PiYG = plt.cm.PiYG_r
PuOr = plt.cm.PuOr
BrBG = plt.cm.BrBG
RdYlBu = plt.cm.RdYlBu
RdYlBu_r = plt.cm.RdYlBu_r
RdYlGn = plt.cm.RdYlGn
Spectral = plt.cm.Spectral
Spectral_r = plt.cm.Spectral_r
YlGnBu = plt.cm.YlGnBu
winter = plt.cm.winter
PuBu = plt.cm.PuBu
PuBu_r = plt.cm.PuBu_r

# defining plot styles (which contains fonts and backgrouds)
# plt.style.use (can be seaborn, dark_background, fivethirtyeight, bmh



[docs]def apply_style(fontsize=20, style=None, linewidth=2): """ Parameters ---------- fontsize : TYPE, optional DESCRIPTION. The default is 10. style : TYPE, optional DESCRIPTION. The default is "bmh". ["seaborn", "fivethirtyeight",] Returns ------- None. """ if style is not None: plt.style.use(style) rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']}) mpl.rc('text', usetex=True) mpl.rc('xtick', labelsize=fontsize) mpl.rc('ytick', labelsize=fontsize) mpl.rc('legend', fontsize=fontsize) mpl.rc('axes', labelsize=fontsize) mpl.rc('lines', linewidth=linewidth) mpl.rc("font", weight="bold")
# defining function for selecting background domain for cartopy
[docs]def plot_background(p, domain=None, use_AlbersEqualArea=None,ax=None, left_labels=True, bottom_labels=True): """ This funtion defines the plotting domain and also specifies the background. It requires the plot handle from xarray.plot.imshow and other optional arguments Parameters ------------- p: TYPE: plot handle DESCRIPTION: the plot handle after plotting with xarray.plot.imshow domian = TYPE:str DESCRIPTION: defines the domain size, eg. "Europe", "Asia", "Africa" "South America", "Alaska", "Tibet Plateau" or "Himalaya", "Eurosia", "New Zealand", default: global """ p.axes.set_global() # setting global axis p.axes.coastlines(resolution = "50m") # add coastlines outlines to the current axis p.axes.add_feature(cfeature.BORDERS, edgecolor="black", linewidth = 0.3) #adding country boarder lines #setting domain size if domain is not None: if domain == "Europe": # Europe minLon = -15 maxLon = 40 minLat = 35 maxLat = 65 elif domain == "South America": # South America minLon = -83 maxLon = -32 minLat = -25 maxLat = 15 elif domain == "Tibetan Plateau" or domain == "Himalayas": #Tibet Plateau/Himalayas minLon = 40 maxLon = 120 minLat = 0 maxLat = 60 elif domain == "Eurasia": # Eurasia minLon = -18 maxLon = 164 minLat = 20 maxLat = 77 elif domain == "Cascades": # Cascades minLon = -129 maxLon = -120 minLat = 45 maxLat = 52 elif domain == "Alaska": # Alaska minLon = -165 maxLon = -125 minLat = 52 maxLat = 68 elif domain == "Africa": # Africa minLon = -30 maxLon = 55 minLat = -35 maxLat = 40 elif domain == "New Zealand": # New Zealand minLon = 165 maxLon = 180 minLat = -47 maxLat = -34 elif domain == "Olympic Mnts": # Olympic Mnt's minLon = -126 maxLon = -118 minLat = 43 maxLat = 52 elif domain == "NH": # Northen Hemisphere minLon = -80 maxLon = 60 minLat = 20 maxLat = 80 else: print("ERROR: invalid geographical domain passed in options") p.axes.set_extent([minLon, maxLon, minLat, maxLat], ccrs.PlateCarree()) if domain is None: p.axes.set_extent([-180, 180, -90, 90], ccrs.PlateCarree()) if use_AlbersEqualArea == True: vertices = [(lon, 0) for lon in range(-100, 31, 1)] vertices += [(lon, 90) for lon in range(30, -101,-1)] boundary= mpath.Path(vertices) p.axes.set_boundary(boundary, transform=ccrs.PlateCarree()) #ax.set_global() # setting global axi gl= ax.gridlines(crs = ccrs.PlateCarree(), draw_labels = True, linewidth = 1, edgecolor = "gray", linestyle = "--", color="gray", alpha=0.5) else: # adding gridlines gl= p.axes.gridlines(crs = ccrs.PlateCarree(), draw_labels = True, linewidth = 1, edgecolor = "gray", linestyle = "--", color="gray", alpha=0.5) gl.top_labels = False # labesl at top gl.right_labels = False if left_labels == True: gl.left_labels = True else: gl.left_labels = False if bottom_labels == True: gl.bottom_labels =True else: gl.bottom_labels = False gl.xformatter = LongitudeFormatter() # axis formatter gl.yformatter = LatitudeFormatter() gl.xlabel_style = {"fontsize": 20, "color": "black", "fontweight": "semibold"} #axis style gl.ylabel_style = {"fontsize": 20, "color": "black", "fontweight": "semibold"}
# for divergence plots
[docs]class MidpointNormalize(colors.Normalize): """ At the moment its a bug to use divergence colormap and set the colorbar range midpoint to zero if both vmax and vmin has different magnitude. This might be possible in future development in matplotlib through colors.offsetNorm(). This class was original developed by Joe Kingto and modified by Daniel Boateng. It sets the divergence color bar to a scale of 0-1 by dividing the midpoint to 0.5 Use this class at your own risk since its non-standard practice for quantitative data. """ def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False): self.midpoint = midpoint colors.Normalize.__init__(self, vmin, vmax, clip) def __call__(self, value, clip=None): # I'm ignoring masked values and all kinds of edge cases to make a # simple example... x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1] return np.ma.masked_array(np.interp(value, x, y))
[docs]class FixedPointNormalized(matplotlib.colors.Normalize): def __init__(self, vmin=None, vmax=None, sealevel=0, color_val=0.21875, clip=False): self.sealevel = sealevel self.color_val = color_val matplotlib.colors.Normalize.__init__(self, vmin, vmax, clip) def __call__(self, value, clip=None): x,y = [self.vmin, self.sealevel, self.vmax], [0, self.color_val, 1] return np.ma.masked_array(np.interp(value, x, y))
colors_ocean = plt.cm.terrain(np.linspace(0, 0.17, 56)) colors_land = plt.cm.terrain(np.linspace(0.25, 1, 200)) colors_combined = np.vstack((colors_ocean, colors_land)) cut_terrain_map = matplotlib.colors.LinearSegmentedColormap.from_list('cut_terrain', colors_combined)