"""Simple map visualization."""
from typing import Optional, List
import matplotlib.pyplot as plt
import numpy as np
import canopy as cp
from canopy.visualization.multiple_figs import create_wrapper_from_locals
from canopy.visualization.visualization_helpers import handle_figure_output
from .map_helpers import (
get_metadata,
safe_make_raster,
calculate_bins,
plot_map
)
[docs]
def make_simple_map(
field: cp.Field,
layer: str,
categorical: bool = False,
output_file: Optional[str] = None,
timeop: str = 'av',
cb_label: Optional[str] = None,
title: Optional[str] = None,
unit: Optional[str] = None,
n_classes: int = 4,
classification: List[float] | str = "linear",
palette: Optional[str] = None,
custom_palette: Optional[str] = None,
orientation: str = 'horizontal',
cb_label_rotation: float = 0,
extend: str = "neither",
proj: str = "Robinson",
force_zero: bool = False,
dark_mode: bool = False,
transparent: bool = False,
stats_annotation: bool = False,
x_fig: float = 10,
y_fig: float = 10,
subfig=None,
return_fig: bool = False,
) -> Optional[plt.Figure]:
"""
Create a map from a given Field object (apply time reduction) and save it to a file.
Parameters
----------
field : cp.Field
Field object.
layer : str
Layer name to display.
categorical : bool, optional
Set to True for categorical data mapping. Default False.
output_file : str, optional
File path for saving the plot.
timeop : str, optional
The reduction operation. Either 'sum' or 'av'. Default is 'av'.
cb_label : str, optional
Label of the colour bar, if not provided canopy will try to
retrieve the name of the variable in the metadata.
unit : str, optional
Unit of the variable, if not provided canopy will try to retrieve
the unit of the variable in the metadata.
title : str, optional
Title of the map.
n_classes : int, optional
Number of discrete color classes to use. Default is 4.
classification : List[float] | str, optional
Method to classify the data into different classes.
One of 'linear', 'quantile', 'jenks', 'std'
(https://gisgeography.com/choropleth-maps-data-classification/)
or a list, e.g. [0,2,4,8].
Default is 'linear'.
palette : str, optional
Seaborn color palette to use for the line colors
(https://seaborn.pydata.org/tutorial/color_palettes.html,
recommended palette are in https://colorbrewer2.org).
custom_palette : str, optional
Path of custom color palette .txt file to use.
orientation: str, optional
Orientation of the legend. Either 'horizontal' or 'vertical'.
Default is 'horizontal'.
cb_label_rotation : float, optional
Rotation angle in degrees for the colorbar tick labels. Default is 0.
extend : str, optional
Extend colourbar to maximum and minimum value. One of 'neither',
'min', 'max' or 'both'. Default is 'neither'.
proj : str, optional
Cartopy projection to use for the map
(https://scitools.org.uk/cartopy/docs/v0.15/crs/projections.html).
Default is 'Robinson'.
force_zero : bool, optional
If True, force the first (or the closest in diff_map) bin to zero.
Default is False.
dark_mode : bool, optional
If True, apply dark mode styling. Default is False.
transparent: bool, optional
If True, make the figure transparent. Default is False.
stats_annotation : bool, optional
If True, adds a text box annotation on the bottom-left of the map
displaying the mean and standard deviation of the raster values
over the entire domain. Default is False.
x_fig : float, optional
Width of the figure in inches. Default is 10.
y_fig : float, optional
Height of the figure in inches. Default is 10.
subfig : matplotlib.figure.SubFigure, optional
If provided, the plot will be created in this subfigure instead of creating a new figure.
This is used by multiple_figs() to combine multiple plots.
User can also provide a plt.figure.subfigure object
(https://matplotlib.org/stable/gallery/subplots_axes_and_figures/subfigures.html)
return_fig : bool, optional
If True, return a callable wrapper function instead of creating the plot immediately.
This wrapper can be used with multiple_figs(). Default is False.
"""
# If return_fig is True, create a wrapper function and return it
if return_fig:
return create_wrapper_from_locals(make_simple_map, locals())
if field.grid.is_reduced("lat") and field.grid.is_reduced("lon"):
raise ValueError(
"Field has reduced latitude and longitude. Map requires unreduced spatial dimensions."
)
# Retrieve metadata
cb_label, unit = get_metadata(field, cb_label, unit)
# If classification is provided as a list, update n_classes to match its length minus one
if isinstance(classification, list) and len(classification)-1 != n_classes:
n_classes = len(classification) - 1
if categorical:
raster = safe_make_raster(field, layer, timeop=timeop)
n_classes = len(raster.keys) if raster.keys else 0
bins = np.arange(n_classes + 1)
keys = raster.keys
else:
raster = safe_make_raster(field, layer, timeop=timeop)
bins = calculate_bins(raster.vmap, n_classes, classification, force_zero)
keys = False
fig = plot_map(raster.x, raster.y, raster.vmap, categorical, keys, output_file,
cb_label, title, unit, n_classes, bins, palette, custom_palette,
orientation, cb_label_rotation, extend, proj, dark_mode, stats_annotation,
x_fig, y_fig, nonsig_mask=None, hist=not categorical, subfig=subfig)
return handle_figure_output(fig, output_file=output_file, transparent=transparent, subfig=subfig)