def plot_eai_exp_geom(imp_geom, centered=False, figsize=(9, 13), **kwargs):
    """
    Plot the average impact per exposure polygon.

    Parameters
    ----------
    imp_geom : Impact
        Impact instance with imp_geom set (i.e. computed from exposures with
                                           polygons)
    centered : bool, optional
        Center the plot. The default is False.
    figsize : (float, float), optional
        Figure size. The default is (9, 13).
    **kwargs : dict
        Keyword arguments for GeoDataFrame.plot()

    Returns
    -------
    ax:
        matplotlib axes instance

    """
    kwargs['figsize'] = figsize
    if 'legend_kwds' not in kwargs:
        kwargs['legend_kwds'] = {
            'label': f"Impact [{imp_geom.unit}]",
            'orientation': "horizontal"
        }
    if 'legend' not in kwargs:
        kwargs['legend'] = True
    gdf_plot = gpd.GeoDataFrame(imp_geom.geom_exp)
    gdf_plot['impact'] = imp_geom.eai_exp
    if centered:
        # pylint: disable=abstract-class-instantiated
        xmin, xmax = u_coord.lon_bounds(imp_geom.coord_exp[:, 1])
        proj_plot = ccrs.PlateCarree(central_longitude=0.5 * (xmin + xmax))
        gdf_plot = gdf_plot.to_crs(proj_plot)
    return gdf_plot.plot(column='impact', **kwargs)
Ejemplo n.º 2
0
def geo_im_from_array(array_sub,
                      coord,
                      var_name,
                      title,
                      proj=None,
                      smooth=True,
                      axes=None,
                      figsize=(9, 13),
                      adapt_fontsize=True,
                      **kwargs):
    """Image(s) plot defined in array(s) over input coordinates.

    Parameters
    ----------
    array_sub : np.array(1d or 2d) or list(np.array)
        Each array (in a row or in  the list) are values at each point in corresponding
        geo_coord that are ploted in one subplot.
    coord : 2d np.array
        (lat, lon) for each point in a row. The same grid is used for all subplots.
    var_name : str or list(str)
        label to be shown in the colorbar. If one provided, the same is used for all subplots.
        Otherwise provide as many as subplots in array_sub.
    title : str or list(str)
        subplot title. If one provided, the same is used for all subplots.
        Otherwise provide as many as subplots in array_sub.
    proj : ccrs, optional
        coordinate reference system used in coordinates, by default None
    smooth : bool, optional
        smooth plot to RESOLUTIONxRESOLUTION, by default True
    axes : Axes or ndarray(Axes), optional
        by default None
    figsize : tuple, optional
        figure size for plt.subplots, by default (9, 13)
    adapt_fontsize : bool, optional
        If set to true, the size of the fonts will be adapted to the size of the figure. Otherwise
        the default matplotlib font size is used. Default is True.
    **kwargs
        arbitrary keyword arguments for pcolormesh matplotlib function

    Returns
    -------
    cartopy.mpl.geoaxes.GeoAxesSubplot

    Raises
    ------
    ValueError
    """

    # Generate array of values used in each subplot
    num_im, list_arr = _get_collection_arrays(array_sub)
    list_tit = to_list(num_im, title, 'title')
    list_name = to_list(num_im, var_name, 'var_name')
    list_coord = to_list(num_im, coord, 'geo_coord')

    is_reg, height, width = u_coord.grid_is_regular(coord)
    extent = _get_borders(coord, proj_limits=(-360, 360, -90, 90))
    mid_lon = 0
    if not proj:
        mid_lon = 0.5 * sum(extent[:2])
        proj = ccrs.PlateCarree(central_longitude=mid_lon)
    if 'vmin' not in kwargs:
        kwargs['vmin'] = np.nanmin(array_sub)
    if 'vmax' not in kwargs:
        kwargs['vmax'] = np.nanmax(array_sub)
    if axes is None:
        if isinstance(proj, ccrs.PlateCarree):
            # use different projections for plot and data to shift the central lon in the plot
            xmin, xmax = u_coord.lon_bounds(
                np.concatenate([c[:, 1] for c in list_coord]))
            proj_plot = ccrs.PlateCarree(central_longitude=0.5 * (xmin + xmax))
        _, axes, fontsize = make_map(num_im,
                                     proj=proj_plot,
                                     figsize=figsize,
                                     adapt_fontsize=adapt_fontsize)
    else:
        fontsize = None
    axes_iter = axes
    if not isinstance(axes, np.ndarray):
        axes_iter = np.array([[axes]])

    if 'cmap' not in kwargs:
        kwargs['cmap'] = CMAP_RASTER

    # Generate each subplot
    for array_im, axis, tit, name in zip(list_arr, axes_iter.flatten(),
                                         list_tit, list_name):
        if coord.shape[0] != array_im.size:
            raise ValueError("Size mismatch in input array: %s != %s." %
                             (coord.shape[0], array_im.size))
        if smooth or not is_reg:
            # Create regular grid where to interpolate the array
            grid_x, grid_y = np.mgrid[
                extent[0]:extent[1]:complex(0, RESOLUTION),
                extent[2]:extent[3]:complex(0, RESOLUTION)]
            grid_im = griddata((coord[:, 1], coord[:, 0]), array_im,
                               (grid_x, grid_y))
        else:
            grid_x = coord[:, 1].reshape((width, height)).transpose()
            grid_y = coord[:, 0].reshape((width, height)).transpose()
            grid_im = np.array(array_im.reshape((width, height)).transpose())
            if grid_y[0, 0] > grid_y[0, -1]:
                grid_y = np.flip(grid_y)
                grid_im = np.flip(grid_im, 1)
            grid_im = np.resize(grid_im, (height, width, 1))
        axis.set_extent(
            (extent[0] - mid_lon, extent[1] - mid_lon, extent[2], extent[3]),
            crs=proj)

        # Add coastline to axis
        add_shapes(axis)
        # Create colormesh, colorbar and labels in axis
        cbax = make_axes_locatable(axis).append_axes('right',
                                                     size="6.5%",
                                                     pad=0.1,
                                                     axes_class=plt.Axes)
        img = axis.pcolormesh(grid_x - mid_lon,
                              grid_y,
                              np.squeeze(grid_im),
                              transform=proj,
                              **kwargs)
        cbar = plt.colorbar(img, cax=cbax, orientation='vertical')
        cbar.set_label(name)
        axis.set_title("\n".join(wrap(tit)))
        if fontsize:
            cbar.ax.tick_params(labelsize=fontsize)
            cbar.ax.yaxis.get_offset_text().set_fontsize(fontsize)
            for item in [axis.title, cbar.ax.xaxis.label, cbar.ax.yaxis.label]:
                item.set_fontsize(fontsize)

    plt.tight_layout()
    return axes
Ejemplo n.º 3
0
def compute_windfields(track, centroids, model, metric="equirect"):
    """Compute 1-minute sustained winds (in m/s) at 10 meters above ground

    In a first step, centroids within reach of the track are determined so that wind fields will
    only be computed and returned for those centroids.

    Parameters
    ----------
    track : xr.Dataset
        Track infomation.
    centroids : 2d np.array
        Each row is a centroid [lat, lon].
        Centroids that are not within reach of the track are ignored.
    model : int
        Holland model selection according to MODEL_VANG.
    metric : str, optional
        Specify an approximation method to use for earth distances: "equirect" (faster) or
        "geosphere" (more accurate). See `dist_approx` function in `climada.util.coordinates`.
        Default: "equirect".

    Returns
    -------
    windfields : np.array of shape (npositions, nreachable, 2)
        Directional wind fields for each track position on those centroids within reach
        of the TC track.
    reachable_centr_idx : np.array of shape (nreachable,)
        List of indices of input centroids within reach of the TC track.
    """
    # copies of track data
    # Note that max wind records are not used in the Holland wind field models!
    t_lat, t_lon, t_tstep, t_rad, t_env, t_cen = [
        track[ar].values.copy() for ar in [
            'lat', 'lon', 'time_step', 'radius_max_wind',
            'environmental_pressure', 'central_pressure'
        ]
    ]

    # start with the assumption that no centroids are within reach
    npositions = t_lat.shape[0]
    reachable_centr_idx = np.zeros((0, ), dtype=np.int64)
    windfields = np.zeros((npositions, 0, 2), dtype=np.float64)

    # the wind field model requires at least two track positions because translational speed
    # as well as the change in pressure are required
    if npositions < 2:
        return windfields, reachable_centr_idx

    # normalize longitude values (improves performance of `dist_approx` and `_close_centroids`)
    mid_lon = 0.5 * sum(u_coord.lon_bounds(t_lon))
    u_coord.lon_normalize(t_lon, center=mid_lon)
    u_coord.lon_normalize(centroids[:, 1], center=mid_lon)

    # restrict to centroids within rectangular bounding boxes around track positions
    track_centr_msk = _close_centroids(t_lat, t_lon, centroids)
    track_centr = centroids[track_centr_msk]
    nreachable = track_centr.shape[0]
    if nreachable == 0:
        return windfields, reachable_centr_idx

    # compute distances and vectors to all centroids
    [d_centr], [v_centr] = u_coord.dist_approx(t_lat[None],
                                               t_lon[None],
                                               track_centr[None, :, 0],
                                               track_centr[None, :, 1],
                                               log=True,
                                               normalize=False,
                                               method=metric)

    # exclude centroids that are too far from or too close to the eye
    close_centr_msk = (d_centr < CENTR_NODE_MAX_DIST_KM) & (d_centr > 1e-2)
    if not np.any(close_centr_msk):
        return windfields, reachable_centr_idx
    v_centr_normed = np.zeros_like(v_centr)
    v_centr_normed[close_centr_msk] = v_centr[close_centr_msk] / d_centr[
        close_centr_msk, None]

    # make sure that central pressure never exceeds environmental pressure
    pres_exceed_msk = (t_cen > t_env)
    t_cen[pres_exceed_msk] = t_env[pres_exceed_msk]

    # extrapolate radius of max wind from pressure if not given
    t_rad[:] = estimate_rmw(t_rad, t_cen) * NM_TO_KM

    # translational speed of track at every node
    v_trans = _vtrans(t_lat, t_lon, t_tstep, metric=metric)
    v_trans_norm = v_trans[0]

    # adjust pressure at previous track point
    prev_pres = t_cen[:-1].copy()
    msk = (prev_pres < 850)
    prev_pres[msk] = t_cen[1:][msk]

    # compute b-value and derive (absolute) angular velocity
    if model == MODEL_VANG['H1980']:
        # convert recorded surface winds to gradient-level winds without translational influence
        t_vmax = track.max_sustained_wind.values.copy()
        t_gradient_winds = np.fmax(
            0, t_vmax - v_trans_norm) / GRADIENT_LEVEL_TO_SURFACE_WINDS
        hol_b = _B_holland_1980(t_gradient_winds[1:], t_env[1:], t_cen[1:])
        v_ang_norm = _stat_holland_1980(d_centr[1:], t_rad[1:], hol_b,
                                        t_env[1:], t_cen[1:], t_lat[1:],
                                        close_centr_msk[1:])
        v_ang_norm *= GRADIENT_LEVEL_TO_SURFACE_WINDS
    elif model == MODEL_VANG['H08']:
        # this model doesn't use the recorded surface winds
        hol_b = _bs_holland_2008(v_trans_norm[1:], t_env[1:], t_cen[1:],
                                 prev_pres, t_lat[1:], t_tstep[1:])
        v_ang_norm = _stat_holland_1980(d_centr[1:], t_rad[1:], hol_b,
                                        t_env[1:], t_cen[1:], t_lat[1:],
                                        close_centr_msk[1:])
    elif model == MODEL_VANG['H10']:
        # this model doesn't use the recorded surface winds
        hol_b = _bs_holland_2008(v_trans_norm[1:], t_env[1:], t_cen[1:],
                                 prev_pres, t_lat[1:], t_tstep[1:])
        t_vmax = _v_max_s_holland_2008(t_env[1:], t_cen[1:], hol_b)
        hol_x = _x_holland_2010(d_centr[1:], t_rad[1:], t_vmax, hol_b,
                                close_centr_msk[1:])
        v_ang_norm = _stat_holland_2010(d_centr[1:], t_vmax, t_rad[1:], hol_b,
                                        close_centr_msk[1:], hol_x)
    else:
        raise NotImplementedError

    # vectorial angular velocity
    hemisphere = 'N'
    if np.count_nonzero(t_lat < 0) > np.count_nonzero(t_lat > 0):
        hemisphere = 'S'
    v_ang_rotate = [1.0, -1.0] if hemisphere == 'N' else [-1.0, 1.0]
    v_ang_dir = np.array(v_ang_rotate)[..., :] * v_centr_normed[1:, :, ::-1]
    v_ang = np.zeros_like(v_ang_dir)
    v_ang[close_centr_msk[1:]] = v_ang_norm[close_centr_msk[1:], None] \
                                 * v_ang_dir[close_centr_msk[1:]]

    # Influence of translational speed decreases with distance from eye.
    # The "absorbing factor" is according to the following paper (see Fig. 7):
    #
    #   Mouton, F., & Nordbeck, O. (1999). Cyclone Database Manager. A tool
    #   for converting point data from cyclone observations into tracks and
    #   wind speed profiles in a GIS. UNED/GRID-Geneva.
    #   https://unepgrid.ch/en/resource/19B7D302
    #
    t_rad_bc = np.broadcast_arrays(t_rad[:, None], d_centr)[0]
    v_trans_corr = np.zeros_like(d_centr)
    v_trans_corr[close_centr_msk] = np.fmin(
        1, t_rad_bc[close_centr_msk] / d_centr[close_centr_msk])

    # add angular and corrected translational velocity vectors
    v_full = v_trans[1][1:, None, :] * v_trans_corr[1:, :, None] + v_ang
    v_full[np.isnan(v_full)] = 0

    windfields = np.zeros((npositions, nreachable, 2), dtype=np.float64)
    windfields[1:, :, :] = v_full
    [reachable_centr_idx] = track_centr_msk.nonzero()
    return windfields, reachable_centr_idx
Ejemplo n.º 4
0
def _plot_scattered_data(method,
                         array_sub,
                         geo_coord,
                         var_name,
                         title,
                         pop_name=False,
                         buffer=BUFFER,
                         extend='neither',
                         proj=ccrs.PlateCarree(),
                         shapes=True,
                         axes=None,
                         figsize=(9, 13),
                         adapt_fontsize=True,
                         **kwargs):
    """Function for internal use in `geo_scatter_from_array` (when called with method="scatter")
    and `geo_bin_from_array` (when called with method="hexbin"). See the docstrings of the
    respective functions for more information on the parameters."""

    # Generate array of values used in each subplot
    num_im, list_arr = _get_collection_arrays(array_sub)
    list_tit = to_list(num_im, title, 'title')
    list_name = to_list(num_im, var_name, 'var_name')
    list_coord = to_list(num_im, geo_coord, 'geo_coord')

    if 'cmap' not in kwargs:
        kwargs['cmap'] = CMAP_EXPOSURES

    if axes is None:
        proj_plot = proj
        if isinstance(proj, ccrs.PlateCarree):
            # use different projections for plot and data to shift the central lon in the plot
            xmin, xmax = u_coord.lon_bounds(
                np.concatenate([c[:, 1] for c in list_coord]))
            proj_plot = ccrs.PlateCarree(central_longitude=0.5 * (xmin + xmax))
        _, axes, fontsize = make_map(num_im,
                                     proj=proj_plot,
                                     figsize=figsize,
                                     adapt_fontsize=adapt_fontsize)
    else:
        fontsize = None
    axes_iter = axes
    if not isinstance(axes, np.ndarray):
        axes_iter = np.array([[axes]])

    # Generate each subplot
    for array_im, axis, tit, name, coord in \
    zip(list_arr, axes_iter.flatten(), list_tit, list_name, list_coord):
        if coord.shape[0] != array_im.size:
            raise ValueError("Size mismatch in input array: %s != %s." %
                             (coord.shape[0], array_im.size))

        # Binned image with coastlines
        if isinstance(proj, ccrs.PlateCarree):
            xmin, ymin, xmax, ymax = u_coord.latlon_bounds(coord[:, 0],
                                                           coord[:, 1],
                                                           buffer=buffer)
            extent = (xmin, xmax, ymin, ymax)
        else:
            extent = _get_borders(coord,
                                  buffer=buffer,
                                  proj_limits=proj.x_limits + proj.y_limits)
        axis.set_extent((extent), proj)

        if shapes:
            add_shapes(axis)
        if pop_name:
            add_populated_places(axis, extent, proj, fontsize)

        if method == "hexbin":
            if 'gridsize' not in kwargs:
                kwargs['gridsize'] = min(int(array_im.size / 2), MAX_BINS)
            mappable = axis.hexbin(coord[:, 1],
                                   coord[:, 0],
                                   C=array_im,
                                   transform=proj,
                                   **kwargs)
        else:
            mappable = axis.scatter(coord[:, 1],
                                    coord[:, 0],
                                    c=array_im,
                                    transform=proj,
                                    **kwargs)

        # Create colorbar in this axis
        cbax = make_axes_locatable(axis).append_axes('right',
                                                     size="6.5%",
                                                     pad=0.1,
                                                     axes_class=plt.Axes)
        cbar = plt.colorbar(mappable,
                            cax=cbax,
                            orientation='vertical',
                            extend=extend)
        cbar.set_label(name)
        axis.set_title("\n".join(wrap(tit)))
        if fontsize:
            cbar.ax.tick_params(labelsize=fontsize)
            cbar.ax.yaxis.get_offset_text().set_fontsize(fontsize)
            for item in [axis.title, cbar.ax.xaxis.label, cbar.ax.yaxis.label]:
                item.set_fontsize(fontsize)
    plt.tight_layout()
    return axes
Ejemplo n.º 5
0
def geo_bin_from_array(array_sub,
                       geo_coord,
                       var_name,
                       title,
                       pop_name=True,
                       buffer=BUFFER,
                       extend='neither',
                       proj=ccrs.PlateCarree(),
                       axes=None,
                       figsize=(9, 13),
                       **kwargs):
    """Plot array values binned over input coordinates.

    Parameters
    ----------
    array_sub : np.array(1d or 2d) or list(np.array)
        Each array (in a row or in  the list) are values at each point in corresponding
        geo_coord that are binned in one subplot.
    geo_coord : 2d np.array or list(2d np.array)
        (lat, lon) for each point in a row. If one provided, the same grid is used for all
        subplots. Otherwise provide as many as subplots in array_sub.
    var_name : str or list(str)
        label to be shown in the colorbar. If one provided, the same is used for all subplots.
        Otherwise provide as many as subplots in array_sub.
    title : str or list(str)
        subplot title. If one provided, the same is used for all subplots.
        Otherwise provide as many as subplots in array_sub.
    pop_name : bool, optional
        add names of the populated places, by default True
    buffer : float, optional
        border to add to coordinates, by default BUFFER
    extend : str, optional
        extend border colorbar with arrows.
        [ 'neither' | 'both' | 'min' | 'max' ], by default 'neither'
    proj : ccrs, optional
        coordinate reference system of the given data, by default ccrs.PlateCarree()
    axes : Axes or ndarray(Axes), optional
        by default None
    figsize : tuple, optional
        figure size for plt.subplots, by default (9, 13)
    **kwargs
        arbitrary keyword arguments for hexbin matplotlib function

    Returns
    -------
    cartopy.mpl.geoaxes.GeoAxesSubplot

    Raises
    ------
    ValueError
    """

    # Generate array of values used in each subplot
    num_im, list_arr = _get_collection_arrays(array_sub)
    list_tit = to_list(num_im, title, 'title')
    list_name = to_list(num_im, var_name, 'var_name')
    list_coord = to_list(num_im, geo_coord, 'geo_coord')

    if 'cmap' not in kwargs:
        kwargs['cmap'] = 'Wistia'

    if axes is None:
        proj_plot = proj
        if isinstance(proj, ccrs.PlateCarree):
            # use different projections for plot and data to shift the central lon in the plot
            xmin, xmax = u_coord.lon_bounds(
                np.concatenate([c[:, 1] for c in list_coord]))
            proj_plot = ccrs.PlateCarree(central_longitude=0.5 * (xmin + xmax))
        _, axes = make_map(num_im, proj=proj_plot, figsize=figsize)

    if not isinstance(axes, np.ndarray):
        axes_iter = np.array([[axes]])

    # Generate each subplot
    for array_im, axis, tit, name, coord in \
    zip(list_arr, axes_iter.flatten(), list_tit, list_name, list_coord):
        if coord.shape[0] != array_im.size:
            raise ValueError("Size mismatch in input array: %s != %s." %
                             (coord.shape[0], array_im.size))

        # Binned image with coastlines
        if isinstance(proj, ccrs.PlateCarree):
            xmin, ymin, xmax, ymax = u_coord.latlon_bounds(coord[:, 0],
                                                           coord[:, 1],
                                                           buffer=buffer)
            extent = (xmin, xmax, ymin, ymax)
        else:
            extent = _get_borders(coord,
                                  buffer=buffer,
                                  proj_limits=proj.x_limits + proj.y_limits)
        axis.set_extent((extent), proj)
        add_shapes(axis)
        if pop_name:
            add_populated_places(axis, extent, proj)

        if 'gridsize' not in kwargs:
            kwargs['gridsize'] = min(int(array_im.size / 2), MAX_BINS)
        hex_bin = axis.hexbin(coord[:, 1],
                              coord[:, 0],
                              C=array_im,
                              transform=proj,
                              **kwargs)

        # Create colorbar in this axis
        cbax = make_axes_locatable(axis).append_axes('right',
                                                     size="6.5%",
                                                     pad=0.1,
                                                     axes_class=plt.Axes)
        cbar = plt.colorbar(hex_bin,
                            cax=cbax,
                            orientation='vertical',
                            extend=extend)
        cbar.set_label(name)
        axis.set_title(tit)

    return axes