Creating a Figure Module¶
This section discusses the elements of a nowcast.figures
module.
We’ll use nowcast.figures.day_avg_tracer
as the basis of the example.
The example focuses on the structure of the module and the functions it contains,
as well as the interfaces between those and the rest of the GoMSS_Nowcast package.
There are some very strong opinions in this section about what function names to use, and how to break the code that creates a figure up into functions. They are here because we have learned the hard way that figure generation code quickly evolves into hard to read and maintain globs with fragile interconnections. Please follow the methodology in this section, but do feel free to discuss it with the group so that we can try to improve.
The DevelopDayAvgTracer notebook in notebooks/figures/
was used to develop our example figure module’s functions.
You can take that approach if you wish,
or you can develop directly in a module.
Of course,
the ultimate goal is to produce a module.
Once you’ve got a code module,
you should create a notebook that tests it in the nowcast context.
The TestDayAvgTracer notebook in notebooks/figures/
is an example for the nowcast.figures.day_avg_tracer
module.
Example Module¶
First we’ll show the day_avg_tracer
module structure as a whole,
and then we’ll look at each section in detail.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | # Copyright 2016-2019 Doug Latornell (43ravens)
# and the GoMSS Nowcast project contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Produce a figure that shows surface values of a tracer field for the full GoMSS
model domain. The values are day averages. They are displayed as filled colour contours.
The axes grid and tick labels are an angled lon/lat grid using a Lambert Conformal Conic
map projection.
Testing notebook for this module is
https://nbviewer.jupyter.org/urls/bitbucket.org/gomss-nowcast/gomss_nowcast/raw/default/notebooks/figures/TestDayAvgTracer.ipynb
Development notebook for this module is
https://nbviewer.jupyter.org/urls/bitbucket.org/gomss-nowcast/gomss_nowcast/raw/default/notebooks/figures/DevelopDayAvgTracer.ipynb
"""
from types import SimpleNamespace
import matplotlib.pyplot as plt
import numpy
import xarray
import nowcast.figures.website_theme
from nowcast.figures import shared
def make_figure(
results_archive,
run_date,
var,
cmap,
bathy,
figsize=(16, 9),
theme=nowcast.figures.website_theme,
):
"""Plot colour contours of day averaged tracer variable surface values for the
full GoMSS model domain on an angled lon/lat grid using a Lambert Conformal Conic
map projection.
:param results_archive: Path of directory tree in which NEMO model results are stored.
:type results_archive: :py:class:`pathlib.Path`
:param run_date: Run date to produce the figure for.
:type run_date: :py:class:`arrow.Arrow`
:param str var: Name of NEMO results variable to display in figure.
:param cmap: Colour map to use for filled contours in figure.
:type cmap: :py:class:`matplotlib.colors.ListedColormap`
:param bathy: GoMSS NEMO model bathymetry.
:type bathy: :py:class:`xarray.Dataset`
:param 2-tuple figsize: Figure size (width, height) in inches.
:param theme: Module-like object that defines the style elements for the
figure. See :py:mod:`nowcast.figures.website_theme` for an
example.
:return: :py:class:`matplotlib.figure.Figure`
"""
plot_data = _prep_plot_data(results_archive, run_date, bathy)
fig, ax_surface, x, y = _prep_fig_axes(figsize, plot_data, theme)
contour_set, isobath = _plot_tracer_surface(
ax_surface, x, y, plot_data, var, cmap, theme
)
_surface_axes_labels(
ax_surface, plot_data, var, contour_set, isobath, theme
)
return fig
def _prep_plot_data(results_archive, run_date, bathy):
"""
:param :py:class:`pathlib.Path` results_archive:
:param :py:class:`arrow.Arrow` run_date:
:param :py:class:`xarray.Dataset` bathy:
:return: :py:class:`types.SimpleNamespace`
"""
ddmmmyy = run_date.format('DDMMMYY').lower()
yyyymmdd = run_date.format('YYYYMMDD')
dataset_path = (
results_archive / ddmmmyy /
f'GoMSS_NOWCAST_1d_{yyyymmdd}_{yyyymmdd}_grid_T.nc'
)
day_avg_tracers = xarray.open_dataset(dataset_path)
shared.localize_time(day_avg_tracers)
return SimpleNamespace(
day_avg_tracers=day_avg_tracers,
bathy=bathy,
tz_name=day_avg_tracers.attrs['tz_name'],
)
def _prep_fig_axes(figsize, plot_data, theme):
"""
:param 2-tuple figsize: Figure size (width, height) in inches.
:param :py:class:`types.SimpleNamespace` plot_data:
:param :py:mod:`nowcast.figures.website_theme` theme:
:return: :py:class:`matplotlib.figure.Figure`, :py:class:`matplotlib.axes.Axes`,
:py:class:`numpy.ndarray`, :py:class:`numpy.ndarray`
"""
fig, ax_surface = plt.subplots(
figsize=figsize, facecolor=theme.COLOURS['figure']['facecolor']
)
x, y = shared.projected_lon_lat_axes(
ax_surface, shared.MAP_PARAMS['full domain'], plot_data.bathy, theme
)
return fig, ax_surface, x, y
def _plot_tracer_surface(ax, x, y, plot_data, var, cmap, theme):
"""
:param :py:class:`matplotlib.axes.Axes` ax:
:param :py:class:`numpy.ndarray` x:
:param :py:class:`numpy.ndarray` y:
:param :py:class:`types.SimpleNamespace` plot_data:
:param str var:
:param :py:class:`matplotlib.colors.ListedColormap` cmap:
:param :py:mod:`nowcast.figures.website_theme` theme:
:return: :py:class:`matplotlib.contour.QuadContourSet`,
:py:class:`matplotlib.contour.QuadContourSet`
"""
tracer = (
plot_data.day_avg_tracers.data_vars[var]
.isel(time_counter=0, deptht=0)
)
# tracer as filled contours
contour_set = ax.contourf(
x,
y,
tracer,
cmap=cmap,
levels=numpy.linspace(
numpy.floor(tracer.where(tracer > 0).min()),
numpy.floor(tracer.where(tracer > 0).max()), 20
),
extend='max',
)
# land
ax.contourf(
x,
y,
plot_data.bathy.Bathymetry,
levels=(-0.01, 0.01),
colors=theme.COLOURS['land'],
)
# coastline
ax.contour(
x,
y,
plot_data.bathy.Bathymetry,
levels=(-0.01, 0.01),
colors=theme.COLOURS['coastline'],
)
# 1000m isobath
isobath = ax.contour(
x,
y,
plot_data.bathy.Bathymetry,
levels=(1000,),
colors=theme.COLOURS['contour lines']['1000m isobath'],
)
return contour_set, isobath
def _surface_axes_labels(ax, plot_data, var, contour_set, isobath, theme):
"""
:param :py:class:`matplotlib.axes.Axes` ax:
:param :py:class:`types.SimpleNamespace` plot_data:
:param str var:
:param :py:class:`matplotlib.contour.QuadContourSet` contour_set:
:param :py:class:`matplotlib.contour.QuadContourSet` isobath:
:param :py:mod:`nowcast.figures.website_theme` theme:
:return: None
"""
# Colour bar labels
cbar = plt.colorbar(contour_set, ax=ax)
cbar.ax.axes.tick_params(
labelcolor=theme.COLOURS['cbar']['tick labels'],
labelsize=theme.FONTS['cbar']['tick labels'].get_size(),
)
long_name = plot_data.day_avg_tracers.data_vars[var].long_name
units = plot_data.day_avg_tracers.data_vars[var].units
cbar.set_label(
f'Surface {long_name.title()} [{units}]',
color=theme.COLOURS['cbar']['label'],
fontproperties=theme.FONTS['cbar']['label'],
)
plt.clabel(isobath, fmt={isobath.levels[0]: f'{isobath.levels[0]:.0f} m'})
# Axes title
time = plot_data.day_avg_tracers.temp.time_counter
year = time.dt.year.values[0]
month = time.dt.month.values[0]
day = time.dt.day.values[0]
ax.set_title(
f'{year}-{month:02d}-{day:02d}\n\n',
color=theme.COLOURS['text']['figure title'],
fontproperties=theme.FONTS['figure title'],
fontsize=theme.FONTS['figure title'].get_size(),
)
# Axes aspect ratio from latitude
ax.set_aspect(
1 /
numpy.cos(plot_data.day_avg_tracers.nav_lat.median() * numpy.pi / 180),
)
# Axes element colours
theme.set_axis_colors(ax)
|
Summary of Functions in a Figure Module¶
The function that the nowcast.workers.make_plots
worker will call is named make_figure()
.
More details in make_figure() Function section.
make_figure()
starts by calling 2 other functions:
_prep_plot_data()
to do all of the extraction and preparatory processing of the data that will be plotted in the figure’s axes objects.All of the slicing of the plot data from the dataset objects passed into the :
make_figure()
, or loaded within_prep_plot_data()
, and any calculations that are required, should be done in_prep_plot_data()
so that the variables it returns are ready to be passed into plotting methods. More details in the _prep_plot_data() Function section.
_prep_fig_axes()
creates the figure and axes objects that the variables will be plotted on. If the variables will be plotted on a projected lon/lat axes (as is the case inday_avg_tracer
), The x and y arrays of longitudes and latitudes transformed to axes coordinates are also calculated in_prep_fig_axes()
. More details in the _prep_fig_axes() Function section.
make_figure()
then calls a function whose name starts with _plot_()
for each of the axes objects returned by _prep_fig_axes()
.
If the processing in the _prep_plot_data()
,
_prep_fig_axes()
,
or _plot_*()
functions is long or complicated,
it may be broken up into additional functions that those functions call.
An example is:
Axis labelling and prettifying code like
nowcast.figures.day_avg_tracer._surface_axes_labels()
The following sub-sections go through the example module above section by section to discuss its details.
Copyright Notice¶
At the top of the file is our standard project copyright header block:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # Copyright 2016-2019 Doug Latornell (43ravens)
# and the GoMSS Nowcast project contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
|
The copyright notice is marked as comments with the # character at the beginning of lines rather than enclosing the block in triple quotes so that it will not be interpreted by code introspection tools as the module docstring.
The exception to using the above notice is if the module contains code that we have copied from somewhere. In that case the copyright ownership needs to be changed to make appropriate attribution. The license notice may also need to be changed if the code is released under a license other than Apache 2.0. If you have questions about the attribution and licensing of a piece of code, please talk to Doug.
Module Docstring¶
The module docstring will appear at top of the automatically generated module documentation
(nowcast.figures.day_avg_tracer
in this case).
15 16 17 18 19 20 21 22 23 24 25 | """Produce a figure that shows surface values of a tracer field for the full GoMSS
model domain. The values are day averages. They are displayed as filled colour contours.
The axes grid and tick labels are an angled lon/lat grid using a Lambert Conformal Conic
map projection.
Testing notebook for this module is
https://nbviewer.jupyter.org/urls/bitbucket.org/gomss-nowcast/gomss_nowcast/raw/default/notebooks/figures/TestDayAvgTracer.ipynb
Development notebook for this module is
https://nbviewer.jupyter.org/urls/bitbucket.org/gomss-nowcast/gomss_nowcast/raw/default/notebooks/figures/DevelopDayAvgTracer.ipynb
"""
|
Imports¶
Next come the imports:
26 27 28 29 30 31 32 33 | from types import SimpleNamespace
import matplotlib.pyplot as plt
import numpy
import xarray
import nowcast.figures.website_theme
from nowcast.figures import shared
|
The Python standard library imports,
and those from 3rd party libraries like matplotlib
,
numpy
,
etc.,
will vary from one figure module to another.
However,
the
32 | import nowcast.figures.website_theme
|
import must be present in every figure module.
nowcast.figures.website_theme
provides the definition of colours and fonts that figure modules must use in order to ensure consistency from one to the next,
and with the gomss.ocean.dal.ca site styling.
See nowcast.figures.website_theme Module for more details about the website_theme
module.
Notes About Imports¶
Only import things that you are actually using in your module. Installing a static analysis tool like flake8 and enabling your editor to use it to highlight problem code will help you to write well-styled code, including identifying unused imports for you.
Never use:
from something import *
Imports should be grouped:
Python standard library
Other installed libraries
Other GoMSS project libraries
The library that the module is part of
The groups should be separated by an empty line, and the imports should be sorted alphabetically within the groups.
make_figure()
Function¶
The first function in the module is the function that will be called by the nowcast.workers.make_plots
worker to return a matplotlib.figure.Figure
object.
This function is always named make_figure()
.
It is also the module’s only public function.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | def make_figure(
results_archive,
run_date,
var,
cmap,
bathy,
figsize=(16, 9),
theme=nowcast.figures.website_theme,
):
"""Plot colour contours of day averaged tracer variable surface values for the
full GoMSS model domain on an angled lon/lat grid using a Lambert Conformal Conic
map projection.
:param results_archive: Path of directory tree in which NEMO model results are stored.
:type results_archive: :py:class:`pathlib.Path`
:param run_date: Run date to produce the figure for.
:type run_date: :py:class:`arrow.Arrow`
:param str var: Name of NEMO results variable to display in figure.
:param cmap: Colour map to use for filled contours in figure.
:type cmap: :py:class:`matplotlib.colors.ListedColormap`
:param bathy: GoMSS NEMO model bathymetry.
:type bathy: :py:class:`xarray.Dataset`
:param 2-tuple figsize: Figure size (width, height) in inches.
:param theme: Module-like object that defines the style elements for the
figure. See :py:mod:`nowcast.figures.website_theme` for an
example.
:return: :py:class:`matplotlib.figure.Figure`
"""
plot_data = _prep_plot_data(results_archive, run_date, bathy)
fig, ax_surface, x, y = _prep_fig_axes(figsize, plot_data, theme)
contour_set, isobath = _plot_tracer_surface(
ax_surface, x, y, plot_data, var, cmap, theme
)
_surface_axes_labels(
ax_surface, plot_data, var, contour_set, isobath, theme
)
return fig
|
Function Signature¶
The function signature
36 37 38 39 40 41 42 43 44 | def make_figure(
results_archive,
run_date,
var,
cmap,
bathy,
figsize=(16, 9),
theme=nowcast.figures.website_theme,
):
|
includes all of the code objects that figure module needs from the nowcast.workers.make_plots
worker.
In this case:
results_archive
: the path of the directory tree where the NEMO model results are storedrun_date
: the NEMO run date to produce the figure forvar
: the NEMO results variable to display in the figurecmap
: the colour map to use for the filled contours in the figurebathy
the model bathymetry dataset
The signature ends with the default-values keyword arguments figsize and theme.
The figsize 2-tuple give the width and height of the figure, but more importantly its aspect ratio. Choose values that are appropriate to the information presented in the figure. If you don’t have a good reason to choose something else, use figsize=(16, 9) because that matches the aspect ration of wide displays that most people use to view web sites (even phones in landscape orientation).
The theme should be defaulted to nowcast.figures.website_theme
, a module that provides colours and font specifications that fit with the gomss.ocean.dal.ca site styling and provide consistency among the figures.
Function Docstring¶
The function docstring
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | """Plot colour contours of day averaged tracer variable surface values for the
full GoMSS model domain on an angled lon/lat grid using a Lambert Conformal Conic
map projection.
:param results_archive: Path of directory tree in which NEMO model results are stored.
:type results_archive: :py:class:`pathlib.Path`
:param run_date: Run date to produce the figure for.
:type run_date: :py:class:`arrow.Arrow`
:param str var: Name of NEMO results variable to display in figure.
:param cmap: Colour map to use for filled contours in figure.
:type cmap: :py:class:`matplotlib.colors.ListedColormap`
:param bathy: GoMSS NEMO model bathymetry.
:type bathy: :py:class:`xarray.Dataset`
:param 2-tuple figsize: Figure size (width, height) in inches.
:param theme: Module-like object that defines the style elements for the
figure. See :py:mod:`nowcast.figures.website_theme` for an
example.
:return: :py:class:`matplotlib.figure.Figure`
"""
|
includes description and type information for each of the function arguments. Those are written using Sphinx Info Field List markup so that they render nicely in the automatically generated module documentation.
Simple, 1-word type information can be included in the :arg ...: role, for example:
:param str var: Name of NEMO results variable to display in figure.
More complicated type information should go in a separate :type ...: role like:
:param results_archive: Path of directory tree in which NEMO model results are stored.
:type results_archive: :py:class:`pathlib.Path`
Function Code¶
The function code does 4 things:
Call a module-private function
_prep_plot_data()
to prepare the collection of objects that contain the data that will be plotted in the figure:71
plot_data = _prep_plot_data(results_archive, run_date, bathy)
Call a module-private function
_prep_fig_axes()
:72
fig, ax_surface, x, y = _prep_fig_axes(figsize, plot_data, theme)
That function returns:
a
matplotlib.figure.Figure
objecta
matplotlib.axes.Axes
objecta pair of
numpy.ndarray
objects
The
_prep_fig_axes()
function accept arguments named figsize and theme. figsize provides the size and shape of the figure area. theme provides thenowcast.figures.website_theme
WebsiteTheme module which defines things like the figure and axes background colours.The axes object(s) returned by
_prep_fig_axes()
should be given meaningful name(s) as shown above rather than:fig, (ax1, ax2, ax2, ax4) = _prep_fig_axes(figsize, theme)
For each axes object returned by
_prep_fig_axes()
, call a module-private function whose name starts with_plot_()
is called to draw all the things on the axes:73 74 75 76 77 78
contour_set, isobath = _plot_tracer_surface( ax_surface, x, y, plot_data, var, cmap, theme ) _surface_axes_labels( ax_surface, plot_data, var, contour_set, isobath, theme )
In
day_avg_tracer
we have separated the axes labeling and prettifying code into a separate function,_surface_axes_labels()
.Return the
matplotlib.figure.Figure
object to thenowcast.workers.make_plots
worker:79
return fig
_prep_plot_data()
Function¶
The _prep_plot_data()
function is responsible for all of the extraction and preparatory processing of the data that will be plotted in the figure’s axes object(s).
All of the slicing of the plot data from the dataset objects passed into the make_figure() Function,
and any calculations that are required should be done in _prep_plot_data()
so that the variables it returns are ready to be passed into plotting methods.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | def _prep_plot_data(results_archive, run_date, bathy):
"""
:param :py:class:`pathlib.Path` results_archive:
:param :py:class:`arrow.Arrow` run_date:
:param :py:class:`xarray.Dataset` bathy:
:return: :py:class:`types.SimpleNamespace`
"""
ddmmmyy = run_date.format('DDMMMYY').lower()
yyyymmdd = run_date.format('YYYYMMDD')
dataset_path = (
results_archive / ddmmmyy /
f'GoMSS_NOWCAST_1d_{yyyymmdd}_{yyyymmdd}_grid_T.nc'
)
day_avg_tracers = xarray.open_dataset(dataset_path)
shared.localize_time(day_avg_tracers)
return SimpleNamespace(
day_avg_tracers=day_avg_tracers,
bathy=bathy,
tz_name=day_avg_tracers.attrs['tz_name'],
)
|
The type definitions in the function docstring are optional, but can be very helpful to your future self and other people reading your code, especially if they are using an IDE (Integrated Development Environment) like PyCharm, or an editor like Visual Studio Code, that understands Python type annotations.
_prep_plot_data()
should return a types.SimpleNamespace
so that the various data objects to be plotted can be easily accessed using dotted notation;
e.g. plot_data.day_avg_tracers
.
Notes About Returning SimpleNamespace
from Functions¶
If you are writing a function that returns more than one value,
consider returning the collection of values as a SimpleNamespace.
If your function returns more than 3 values,
definitely return them as a SimpleNamespace
.
SimpleNamespace
objects that have fields accessible by attribute lookup
(dotted notation).
They also have a helpful string representation which lists the namespace contents in a name=value format.
>>> p = SimpleNamespace(x=11, y=22)
>>> p.x + p.y # fields accessible by name
33
>>> p # readable string representation with a name=value style
namespace(x=11, y=22)
Using the _prep_plot_data() Function function code as an example:
98 99 100 101 102 | return SimpleNamespace(
day_avg_tracers=day_avg_tracers,
bathy=bathy,
tz_name=day_avg_tracers.attrs['tz_name'],
)
|
Returning a SimpleNamespace
lets us call _prep_plot_data()
like:
plot_data = _prep_plot_data(results_archive, run_date, bathy)
and we can access the name of the time zone to which the model results have been localized as:
plot_data.tz_name
This makes for compact and easy to understand code that our future selves and others will appreciate when they read our code.
_prep_fig_axes()
Function¶
The _prep_fig_axes()
function accepts arguments named figsize,
plot_data,
and theme.
figsize provides the size and shape of the figure area.
:plot_data provides access to the model bathymetry that is needed to set up the projected longitude/latitude axes object and transformed x and y coordinates to use for plotting.
theme provides the nowcast.figures.website_theme
WebsiteTheme module which defines things like the figure and axes background colours.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | def _prep_fig_axes(figsize, plot_data, theme):
"""
:param 2-tuple figsize: Figure size (width, height) in inches.
:param :py:class:`types.SimpleNamespace` plot_data:
:param :py:mod:`nowcast.figures.website_theme` theme:
:return: :py:class:`matplotlib.figure.Figure`, :py:class:`matplotlib.axes.Axes`,
:py:class:`numpy.ndarray`, :py:class:`numpy.ndarray`
"""
fig, ax_surface = plt.subplots(
figsize=figsize, facecolor=theme.COLOURS['figure']['facecolor']
)
x, y = shared.projected_lon_lat_axes(
ax_surface, shared.MAP_PARAMS['full domain'], plot_data.bathy, theme
)
return fig, ax_surface, x, y
|
As in the _prep_plot_data() Function, the type definitions in the function docstring are optional.
plt.subplots()
creates the matplotlib.figure.Figure
object for us,
and the matplotlib.axes.Axes
object within it that we will plot our model tracer field on.
The figsize parameter specifies the figure size and aspect ratio.
A colour to match the web page background colour is used as the figure facecolor
: theme.COLOURS['figure']['facecolor']
.
nowcast.figures.shared.projected_lon_lat_axes()
uses Basemap to calculate transformed coordinates for the axes object according to the projection and other parameters in shared.MAP_PARAMS['full domain']
.
The (x, y) arrays that are returned are the transformed coordinates to use for plotting on the axes.
The axes will be labelled with longitudes and latitudes and it will show a longitude/latitude grid.
The function returns:
the
matplotlib.figure.Figure
objectthe
matplotlib.axes.Axes
objectthe transformed coordinates for plotting as
numpy.ndarray
objects
Axes Plotting Functions¶
After preparing the plot data, and setting up the figure and axes objects, our example make_figure() Function calls the _plot_tracer_surface() Function.
Plotting functions generally accept:
a
matplotlib.axes.Axes
object as their 1st argument, called ax by conventionthe
SimpleNamespace
object that was returned by the_prep_plot_data()
function, called plot_data by conventionthe
nowcast.figures.website_theme
module as their last argument, called theme by convention
They may accept other arguments as necessary.
The job of the _plot_*()
functions is to act on the matplotlib.axes.Axes
object
(ax)
so they may or may not return anything.
_plot_tracer_surface()
Function¶
The _plot_tracer_surface()
function in our example plots colour contours of the surface values of a tracer for the full domain.
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | def _plot_tracer_surface(ax, x, y, plot_data, var, cmap, theme):
"""
:param :py:class:`matplotlib.axes.Axes` ax:
:param :py:class:`numpy.ndarray` x:
:param :py:class:`numpy.ndarray` y:
:param :py:class:`types.SimpleNamespace` plot_data:
:param str var:
:param :py:class:`matplotlib.colors.ListedColormap` cmap:
:param :py:mod:`nowcast.figures.website_theme` theme:
:return: :py:class:`matplotlib.contour.QuadContourSet`,
:py:class:`matplotlib.contour.QuadContourSet`
"""
tracer = (
plot_data.day_avg_tracers.data_vars[var]
.isel(time_counter=0, deptht=0)
)
# tracer as filled contours
contour_set = ax.contourf(
x,
y,
tracer,
cmap=cmap,
levels=numpy.linspace(
numpy.floor(tracer.where(tracer > 0).min()),
numpy.floor(tracer.where(tracer > 0).max()), 20
),
extend='max',
)
# land
ax.contourf(
x,
y,
plot_data.bathy.Bathymetry,
levels=(-0.01, 0.01),
colors=theme.COLOURS['land'],
)
# coastline
ax.contour(
x,
y,
plot_data.bathy.Bathymetry,
levels=(-0.01, 0.01),
colors=theme.COLOURS['coastline'],
)
# 1000m isobath
isobath = ax.contour(
x,
y,
plot_data.bathy.Bathymetry,
levels=(1000,),
colors=theme.COLOURS['contour lines']['1000m isobath'],
)
return contour_set, isobath
|
As in the _prep_plot_data() Function, the type definitions in the function docstring are optional.
It returns two matplotlib.contour.QuadContourSet
objects:
the surface tracer field filled contours so that they can be used to construct a colour bar for the figure
the 1000m isobath contour line so that it can be labelled
for the separate _surface_axes_labels()
to use to “make the axes pretty”:
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | def _surface_axes_labels(ax, plot_data, var, contour_set, isobath, theme):
"""
:param :py:class:`matplotlib.axes.Axes` ax:
:param :py:class:`types.SimpleNamespace` plot_data:
:param str var:
:param :py:class:`matplotlib.contour.QuadContourSet` contour_set:
:param :py:class:`matplotlib.contour.QuadContourSet` isobath:
:param :py:mod:`nowcast.figures.website_theme` theme:
:return: None
"""
# Colour bar labels
cbar = plt.colorbar(contour_set, ax=ax)
cbar.ax.axes.tick_params(
labelcolor=theme.COLOURS['cbar']['tick labels'],
labelsize=theme.FONTS['cbar']['tick labels'].get_size(),
)
long_name = plot_data.day_avg_tracers.data_vars[var].long_name
units = plot_data.day_avg_tracers.data_vars[var].units
cbar.set_label(
f'Surface {long_name.title()} [{units}]',
color=theme.COLOURS['cbar']['label'],
fontproperties=theme.FONTS['cbar']['label'],
)
plt.clabel(isobath, fmt={isobath.levels[0]: f'{isobath.levels[0]:.0f} m'})
# Axes title
time = plot_data.day_avg_tracers.temp.time_counter
year = time.dt.year.values[0]
month = time.dt.month.values[0]
day = time.dt.day.values[0]
ax.set_title(
f'{year}-{month:02d}-{day:02d}\n\n',
color=theme.COLOURS['text']['figure title'],
fontproperties=theme.FONTS['figure title'],
fontsize=theme.FONTS['figure title'].get_size(),
)
# Axes aspect ratio from latitude
ax.set_aspect(
1 /
numpy.cos(plot_data.day_avg_tracers.nav_lat.median() * numpy.pi / 180),
)
# Axes element colours
theme.set_axis_colors(ax)
|
As in the _prep_plot_data() Function, the type definitions in the function docstring are optional.
This function shows:
how text colours and fonts are obtained from theme
how date components are extracted from the
xarray.DataArray.dt
attribute of a model results time coordinatehow those components are formatted into a string for the axes title
how to fine tune the aspect ratio of the axes according to the median latitude of the field that is being displayed
It finishes with a call to the theme.set_axis_colors()
convenience function to set the colours of axis labels,
ticks,
and spines so that they are consistent with the web site theme.
Automatic Module Documentation Generation¶
When you create a new figure module don’t forget to add it to the GoMSS_Nowcast/docs/workers.rst
file so that documentation will be generated for it.
For our example,
the content added to GoMSS_Nowcast/docs/workers.rst
is:
.. _nowcast.figures.day_avg_tracer:
:py:mod:`nowcast.figures.day_avg_tracer` Module
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: nowcast.figures.day_avg_tracer
:members:
Automatic Code Formatting¶
The GoMSS_Nowcast package uses the yapf code formatting tool to maintain a coding style that is very close to PEP 8.
yapf is installed as part of the Nowcast Figures Development Environment setup.
Before each commit of your figure module please run yapf to automatically format your code.
For our example day_avg_tracer
module the command would be:
$ yapf --in-place nowcast/figures/day_avg_tracer.py