import matplotlib
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import cartopy.feature as cpf
def map_hydro(ax, data, shp, cmap, bnds, **kwargs):
"""
Parameters
----------
ax: cartopy.mpl.geoaxes.GeoAxes | cartopy.mpl.geoaxes.GeoAxesSubplot
Created by initialising a matplotlib plot with the 'projection' argument.
data: xr.DataArray
Data to be plotted. Requires a 'station_id' coordinate.
shp: geopandas.geodataframe.GeoDataFrame
Spatial information data. Indexes should match 'station_id'
cmap:
Colormap to use.
bnds: list
Boundaries to use for matplotlib.colors.BoundaryNorm.
"""
# Add data to the shapefile
data["station_id"] = data.station_id.astype(str)
da2 = data.to_dataframe().set_index("station_id")
i = da2.index.intersection(shp.index)
shp["val"] = da2.loc[i][data.name]
# Change CRS of the shapefile, for cartopy/geopandas compatibility
crs_proj4 = ax.projection.proj4_init
df_ae = shp.to_crs(crs_proj4)
# add the background
ax.add_feature(cpf.LAND, color="#f0f0f0")
ax.add_feature(cpf.OCEAN, color="#cfd3d4")
ax.add_feature(cpf.STATES, edgecolor="black", linestyle="dotted")
ax.add_feature(cpf.RIVERS, color="#cfd3d4")
ax.add_feature(cpf.LAKES, color="#cfd3d4")
# Normalize colormap to match the Atlas boundaries
norm = matplotlib.colors.BoundaryNorm(boundaries=bnds, ncolors=256)
# Plot data using geopandas
df_ae.plot(column="val", ax=ax, norm=norm, cmap=cmap, **kwargs)
def get_atlas_cmap(indicator, plot_type):
"""
Creates a hard-coded colormap that matches the colors and boundaries of the 2022 Hydroclimatic Atlas.
Parameters
----------
indicator: str
Indicator to be plotted.
plot_type: str
One of [Direction, Ampleur, Dispersion]
Returns
-------
cmap
matplotlib.colors.LinearSegmentedColormap
bnds: list
Boundaries used by matplotlib.colors.BoundaryNorm
"""
cmap = _atlas_cmap(plot_type)
if plot_type == "Direction":
bnds = [0, 0.10, 0.33, 0.66, 0.90, 1.00]
elif plot_type == "Ampleur":
bnds = {
"Q1MAX2AN": [-999, -9, -5.4, -1.8, 1.8, 5.4, 9, 999],
"Q1MAX20AN": [-999, -13, -7.8, -2.6, 2.6, 7.8, 13, 999],
"Q1MAX100AN": [-999, -15, -9, -3, 3, 9, 15, 999],
"Q1MAX350AN": [-999, -18, -10.8, -3.6, 3.6, 10.8, 18, 999],
"Q7MIN2AN": [-999, -23, -13.8, -4.6, 4.6, 13.8, 23, 999],
"Q7MIN2EA": [-999, -36, -21.6, -7.2, 7.2, 21.6, 36, 999],
"QMOYAN": [-999, -8, -4.8, -1.6, 1.6, 4.8, 8, 999],
"QMOYEA": [-999, -12, -7.2, -2.4, 2.4, 7.2, 12, 999],
"QMOYHP": [-999, -17, -10.2, -3.4, 3.4, 10.2, 17, 999]
}
bnds = bnds[indicator]
elif plot_type == "Dispersion":
bnds = {
"Q1MAX2AN": [0, 4, 8, 12, 15, 19, 23, 999],
"Q1MAX20AN": [0, 5, 10, 15, 21, 26, 31, 999],
"Q1MAX100AN": [0, 7, 14, 21, 28, 35, 42, 999],
"Q1MAX350AN": [0, 9, 17, 26, 35, 43, 52, 999],
"Q7MIN2AN": [0, 6, 11, 17, 23, 28, 34, 999],
"Q7MIN2EA": [0, 5, 10, 16, 21, 26, 31, 999],
"QMOYAN": [0, 2, 4, 6, 8, 10, 12, 999],
"QMOYEA": [0, 4, 8, 12, 16, 20, 24, 999],
"QMOYHP": [0, 2, 4, 6, 8, 10, 11, 999]
}
bnds = bnds[indicator]
else:
raise ValueError()
return cmap, bnds
def _atlas_cmap(plot_type):
""" Creates the color scheme."""
if plot_type == "Direction":
red = [i / 255 for i in (255, 28, 12)]
orange = [i / 255 for i in (255, 178, 15)]
white = [i / 255 for i in (255, 255, 255)]
l_blue = [i / 255 for i in (19, 191, 255)]
blue = [i / 255 for i in (11, 34, 224)]
cmap = [red, orange, white, l_blue, blue]
elif plot_type in ["Ampleur", "Dispersion"]:
red = [i / 255 for i in (236, 60, 60)]
orange = [i / 255 for i in (255, 190, 60)]
yellow = [i / 255 for i in (255, 255, 61)]
white = [i / 255 for i in (255, 255, 255)]
cyan = [i / 255 for i in (85, 237, 255)]
l_blue = [i / 255 for i in (61, 140, 221)]
blue = [i / 255 for i in (60, 64, 221)]
cmap = [red, orange, yellow, white, cyan, l_blue, blue]
else:
raise ValueError(f"'plot_type' should be one of [Direction, Ampleur, Dispersion], received {plot_type}.")
return matplotlib.colors.LinearSegmentedColormap.from_list("custom_cmap", colors=cmap, N=256)