Source code for pylawr.plot.layer.radarfield
#!/bin/env python
# -*- coding: utf-8 -*-
#
# Created on 04.09.17
#
# Created for pattern
#
#
# Copyright (C) {2017}
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# System modules
import logging
# External modules
import xarray as xr
import cartopy.crs as ccrs
import numpy as np
# Internal modules
from .base import BaseLayer
from pylawr.grid.base import BaseGrid
# Workaround for plotter, see: https://github.com/SciTools/cartopy/issues/1120
from matplotlib.axes import Axes
from cartopy.mpl.geoaxes import GeoAxes
GeoAxes._pcolormesh_patched = Axes.pcolormesh
logger = logging.getLogger(__name__)
[docs]class RadarFieldLayer(BaseLayer):
"""
Gridded data, like radar data, can be plotted with this layer. The given
radar field is plotted on a subplot, which can be specified during
plotting. A given grid is used to specify the boundaries for the radar
field.
Parameters
----------
radar_field : :py:class:`xarray.DataArray` or :py:class:`numpy.ndarray`
This array is plotted with this layer. It has to be castable to a
one- or two-dimensional :py:class:`numpy.ndarray`. The array needs
the same shape as the grid to be plottable. Missing values can be
marked as :py:class:`numpy.nan` and are masked during plotting. If
no grid is given, the radar field need to have a grid specified in
:py:attr:`radar_field.lawr.grid <pylawr.field.RadarField.grid>`.
grid : child of :py:class:`pylawr.grid.base.BaseGrid` or None
This grid is used to infer the boundaries for the radar field. For
this the grid need to have
:py:attr:`pylawr.grid.base.BaseGrid.lat_lon_bounds`. If no grid is
given, the grid will be inferred from radar field. The grid need to
have the same shape as the squeezed radar field. Default is None.
zorder : int
The z-order defines the drawing order of the layers. If no z-order
is set, the z-order will be set during plotting based on layer order
in subplot. Default is 0.
**settings
Variable keyword arguments dict, which is passed during plotting to
:py:meth:`~matplotlib.axes.Axes.pcolormesh` and which sets the
plotting behaviour of this layer. All keyword arguments except
`transform` can be used.
Attributes
----------
zorder : int
The z-order defines the drawing order of the layers. If no z-order
is set, the z-order will be set during plotting based on layer order
in subplot.
settings : dict
The settings for this layer. It is passed to
:py:meth:`~matplotlib.axes.Axes.pcolormesh`. These settings can be
accessed directly and via a dict like interface of this layer. All
possible kwargs of :py:meth:`~matplotlib.axes.Axes.pcolormesh` can
set to this dictionary.
field : :py:class:`xarray.DataArray` or :py:class:`numpy.ndarray`
This array is plotted with this layer. It is castable to a one- or
two-dimensional :py:class:`numpy.ndarray` and has during plotting
the same shape as the grid. Missing values are marked as
:py:class:`numpy.nan` and are masked during plotting.
grid : child of :py:class:`pylawr.grid.base.BaseGrid` or None
This grid is used to infer the boundaries for the radar field with
:py:attr:`pylawr.grid.base.BaseGrid.lat_lon_bounds`. The grid has
the same shape as the radar_field. If the grid is None, then the
grid will be inferred during plotting from
:py:attr:`radar_field.lawr.grid <pylawr.field.RadarField.grid>`.
plot_store : :py:class:`matplotlib.collections.QuadMesh` or None
The plotted element of this layer. This is the storage for the
returned element of :py:meth:`~matplotlib.axes.Axes.pcolormesh`.
If this storage is None, this layer was not plotted yet.
"""
def __init__(self, radar_field, grid=None, zorder=0, **settings):
super().__init__(zorder, **settings)
self._field = None
self._grid = None
self._transform = ccrs.PlateCarree()
self.grid = grid
self.field = radar_field
self.plot_store = None
@property
def field(self):
return self._field
@field.setter
def field(self, field):
if not isinstance(field, xr.DataArray) and \
not isinstance(field, np.ndarray):
raise TypeError('The given radar field is not a valid '
'``xarray.DataArray`` or ``np.array``')
if field.squeeze().ndim > 2:
raise ValueError('The given radar field is not castable into one '
'or two dimensions and cannot be plotted.')
self._field = field
@property
def grid(self):
return self._grid
@grid.setter
def grid(self, grid):
if isinstance(grid, BaseGrid) or grid is None:
self._grid = grid
else:
raise TypeError(
'The given grid has to be a valid pylawr grid or None. The '
'given grid type was: {0:s}'.format(str(type(grid)))
)
[docs] def _get_checked_grid(self):
"""
Get the grid for plotting purpose. If grid is not set, the grid will be
inferred from radar field. The grid is checked against the radar field
if the shapes match.
Returns
-------
grid : child of :py:class:`pylawr.grid.base.BaseGrid`
The inferred and checked grid. This grid has the same shape as the
radar field.
"""
grid = self._grid
if grid is None:
try:
grid = self.field.lawr.grid
except (TypeError, AttributeError):
raise AttributeError('No grid is set and the grid is not '
'accessible, please set the grid instead')
if hasattr(self.field, 'lawr'):
self.field.lawr.check_grid(grid=grid)
return grid
[docs] def plot(self, ax):
"""
This method plots the radar field onto given axes with set zorder. For
plotting :py:meth:`~matplotlib.axes.Axes.pcolormesh` is called based on
given radar field and inferred grid. All nan-values of the given radar
field are masked.
Parameters
----------
ax : :py:class:`matplotlib.axes.Axes`
This layer is plotted with set zorder on this subplot.
"""
grid = self._get_checked_grid()
lat_bounds, lon_bounds = grid.lat_lon_bounds
try:
numpy_data = self.field.values.squeeze()
except AttributeError:
numpy_data = self.field.squeeze()
mask = ~np.isfinite(numpy_data)
masked_data = np.ma.array(data=numpy_data, mask=mask)
self.plot_store = ax.pcolormesh(
lon_bounds, lat_bounds, masked_data, transform=self._transform,
zorder=self.zorder, **self.settings
)
self._collection.append(self.plot_store)