Source code for nowcast.figures.shared

# Copyright 2016-2019 Doug Latornell, 43ravens
#
# 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.
"""A collection of constants and  functions for use by figure modules in the
:kbd:`nowcast.figures` namespace(s).

.. note::
    These constants and functions are intended for use *only* by :kbd:`nowcast.figures`
    modules in order to avoid coupling between figure generation code and code
    used for other purposes.
    If you find that you want to use one of these functions outside of those
    namespaces please talk refactor the function into a tools package.
"""
from types import SimpleNamespace

import arrow
from mpl_toolkits.basemap import Basemap
import numpy

#: Dictionary of map parameters namespace objects.
#: Items are intended for use as :kbd:`map_params` arguments in
#: :py:func:`nowcast.figures.shared.projected_lon_lat_axes` calls.
#: Each item must have the following attributes:
#:
#: :kbd:`projection`
#:    (:py:obj:`str`)
#:    Map projection to use.
#:    Please see https://matplotlib.org/basemap/users/mapsetup.html for
#:    the list of available projections.
#:    Look for the value of the  :kbd:`projection` argument in the
#:    :py:func:`~mpl_toolkits.basemap.Basemap` calls in the code examples.
#: :kbd:`ll_lon`
#:    (:py:obj:`float` or :py:obj:`int`)
#:    Longitude of the lower left corner of the map region.
#: :kbd:`ur_lon`
#:    (:py:obj:`float` or :py:obj:`int`)
#:    Longitude of the upper right corner of the map region.
#: :kbd:`ll_lat`
#:    (:py:obj:`float` or :py:obj:`int`)
#:    Latitude of the lower left corner of the map region.
#: :kbd:`ur_lat`
#:    (:py:obj:`float` or :py:obj:`int`)
#:    Latitude of the upper right corner of the map region.
#: :kbd:`lon_0_offset`
#:    (:py:obj:`float` or :py:obj:`int`)
#:    Central longitude offset.
#:    This is a tuning value that is used in combination with the map
#:    region corner values above to position the map region withing the
#:    plotting axes.
#:    Unfortunately,
#:    there is no evident algorithm to find its value.
#:    The recommended process is to make trial-and-error plots of a
#:    a model variable field and the bathymetry,
#:    starting with large map region,
#:    and gradually reducing the size of the map region and adjusting
#:    the :kbd:`lon_0_offset` value.
#: :kbd:`meridians`
#:    (:py:class:`numpy.ndarray`)
#:    Array of longitudes to mark with grid lines and axis tick labels.
#: :kbd:`parallels`
#:    (:py:class:`numpy.ndarray`)
#:    Array of latitudes to mark with grid lines and axis tick labels.
MAP_PARAMS = {
    "full domain": SimpleNamespace(
        projection="lcc",
        ll_lon=-68.3,
        ur_lon=-53.45,
        ll_lat=37.425,
        ur_lat=49.1,
        lon_0_offset=391.8,
        meridians=numpy.arange(-72, -50, 2),
        parallels=numpy.arange(38, 50, 1),
    )
}


[docs]def localize_time(data_array, time_coord="time_counter", local_datetime=None): """Offset :kbd:`data_array` times to account for local time zone difference from UTC and add :kbd:`tz_name` attribute to :kbd:`data_array`. .. note:: This function is intended for use just before presentation/output of :kbd:`data_array`. It is strongly recommended to do all date/time calculations in UTC to avoid time change issues. :param data_array: Data array or dataset object to adjust time values of. :type data_array: :py:class:`xarray.DataArray` or :py:class:`xarray.Dataset` :param str time_coord: Optional name of the time coordinate. :param local_datetime: Optional timezone-aware local date/time to use as basis to calculate offset from UTC. The 1st element of :kbd:`data_array` is used when :kbd:`local_datetime` is :py:class:`None`. :type local_datetime: :py:class:`arrow.Arrow` """ time_values = getattr(data_array, time_coord).values if local_datetime is None: local_datetime = arrow.get(str(time_values[0])).to("local") tz_offset = local_datetime.tzinfo.utcoffset(local_datetime.datetime) tz_name = local_datetime.tzinfo.tzname(local_datetime.datetime) numpy_offset = numpy.timedelta64(tz_offset.days, "D") + numpy.timedelta64( tz_offset.seconds, "s" ) data_array[time_coord] = time_values + numpy_offset data_array.attrs["tz_name"] = tz_name
[docs]def projected_lon_lat_axes( ax, map_params, bathy, theme, meridians_labels=(False, False, True, True), parallels_labels=(True, True, False, False), ): """Use Basemap (https://matplotlib.org/basemap/) to calculate transformed coordinates for :kbd:`ax` according to the projection and other parameters in :kbd:`map_params`. The :kbd:`(x, y)` arrays that are returned are the transformed coordinates to use for plotting on :kbd:`ax`. :kbd:`ax` will be labelled with longitudes and latitudes on the sides indicated by :kbd:`meridians_labels` and :kbd:`parallels_labels` and it will show a longitude/latitude grid. :param ax: Axes to calculate transformed coordinates for. :type ax: :py:class:`matplotlib.axes.Axes` :param map_params: Namespace of map parameters to use for calculation of transformed coordinates. Please see :py:data:`nowcast.figures.shared.MAP_PARAMS` for definitions of the namespace attributes, and examples. :type map_params: :py:class:`types.SimpleNamespace` :param bathy: A bathymetry dataset that provides the longitude and latitudes arrays on which variables to be plotted are calculated. The longitude and latitude variables names in the dataset are assumed to be :kbd:`nav_lon` and :kbd:`nav_lat`. .. note:: This dataset *must* be neither masked nor scaled. That is accomplished by using the :kbd:`mask_and_scale=False` argument in :py:func:`xarray.open_dataset`. :type bathy: :py:class:`xarray.Dataset` :param theme: Module-like object that defines the style elements for the figure. See :py:mod:`nowcast.figures.website_theme` for an example. :type theme: :py:mod:`nowcast.figures.website_theme` :param meridians_labels: Axes spines on which to show longitude labels. :type meridians_labels: 4-tuple of booleans (top, bottom, left, right) :param parallels_labels: Axes spines on which to show latitude labels. :type parallels_labels: 4-tuple of booleans (top, bottom, left, right) :return: Transformed x and y coordinates to use for plotting on :kbd:`ax`. :rtype: 2-tuple of :py:class:`numpy.ndarray` """ central_lon = ( (map_params.ur_lon - map_params.ll_lon) / 2 + map_params.ll_lon + map_params.lon_0_offset ) central_lat = (map_params.ur_lat - map_params.ll_lat) / 2 + map_params.ll_lat m = Basemap( ax=ax, projection=map_params.projection, lon_0=central_lon, lat_0=central_lat, llcrnrlon=map_params.ll_lon, urcrnrlon=map_params.ur_lon, llcrnrlat=map_params.ll_lat, urcrnrlat=map_params.ur_lat, ) # lon/lat grid m.drawmeridians( map_params.meridians, labels=meridians_labels, textcolor=theme.COLOURS["text"]["axis"], fontproperties=theme.FONTS["axis small"], ) m.drawparallels( map_params.parallels, labels=parallels_labels, textcolor=theme.COLOURS["text"]["axis"], fontproperties=theme.FONTS["axis small"], ) x, y = m(bathy.nav_lon.values, bathy.nav_lat.values) return x, y