Source code for bokeh.plotting._geo_feature

#-----------------------------------------------------------------------------
# Copyright (c) Anaconda, Inc., and Bokeh Contributors.
# All rights reserved.
#
# The full license is in the file LICENSE.txt, distributed with this software.
#-----------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# Boilerplate
#-----------------------------------------------------------------------------
from __future__ import annotations

import logging # isort:skip
log = logging.getLogger(__name__)

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------

# Standard library imports
from dataclasses import dataclass

# External imports
import numpy as np

# Bokeh imports
from ..colors import ColorLike

try:
    import cartopy.crs as ccrs
    import cartopy.feature as cfeature
except ModuleNotFoundError as e:
    raise ModuleNotFoundError(
        "The module 'bokeh.plotting._geo_feature' requires the optional dependency 'Cartopy'."
        "To use this module, please install 'Cartopy'.",
    ) from e


#-----------------------------------------------------------------------------
# General API
#-----------------------------------------------------------------------------

[docs] @dataclass class GEO_FEATURE_COLORS: LAND: ColorLike = "#EFEFDB" STATES: ColorLike = "#EFEFDB" RIVERS: ColorLike = "#9FDBF3" OCEAN: ColorLike = "#9FDBF3" LAKES: ColorLike = "#9FDBF3" BORDERS: ColorLike = "#CCCCCC" MAP_BOUNDARYS: ColorLike = "#000000" COASTLINES: ColorLike = "#000000"
[docs] def add_line_geometries(p, projection, geometries_collection, **line_kwargs): """ Adds line geometries to a map with respect to a given projection. The lines are added by the :func:`~bokeh.plotting.figure.multi_line` function. Args: p (Plot): Object which should be extended. projection (cartopy.crs.Projection): Cartopy projection for a geographic map. scale (str, "110m"): Scale of the feature resolution. Valid strings are "110m", "50m" and "10m". .. note:: This functions allows all parameters and keyword arguments defined by the :func:`~bokeh.plotting.figure.multi_line` function. Example: .. bokeh-plot:: :source-position: above import cartopy.crs as ccrs import cartopy.feature as cfeature from bokeh.plotting import figure, show from bokeh.plotting._geo_feature import add_line_geometries p = figure() p = add_line_geometries(p, ccrs.PlateCarree(), cfeature.BORDERS) show(p) """ xs, ys = _collect_line_geometries(projection, geometries_collection) p.multi_line(xs, ys, **line_kwargs) return p
[docs] def add_polygon_geometries(p, projection, geometries_collection, **poly_kwargs): """ Adds polygon geometries to a map with respect to a given projection. The polygons are added by the :func:`~bokeh.plotting.figure.multi_polygons` function to allow holes and islands. To draw the border of the geometries, set `draw_polygon_border` to `True`. Args: p (Plot): Object which should be extended. projection (cartopy.crs.Projection): Cartopy projection for a geographic map. scale (str, "110m"): Scale of the feature resolution. Valid strings are "110m", "50m" and "10m". Keyword Arguments: draw_polygon_border (bool, False): Enables the plotting of the geometry border. draw_polygon_color (str, "black"): Sets the color of the geometry border. .. note:: This functions allows all parameters and keyword arguments defined by the :func:`~bokeh.plotting.figure.multi_polygons` function. Example: .. bokeh-plot:: :source-position: above import cartopy.crs as ccrs import cartopy.feature as cfeature from bokeh.plotting import figure, show from bokeh.plotting._geo_feature import add_polygon_geometries p = figure() p = add_polygon_geometries(p, ccrs.PlateCarree(), cfeature.LAND) show(p) """ draw_border = poly_kwargs.pop("draw_polygon_border", False) border_color= poly_kwargs.pop("polygon_border_color", GEO_FEATURE_COLORS.BORDERS) xs, ys, poly_kwargs = _collect_polygon_geometries(projection, geometries_collection, **poly_kwargs) p.multi_polygons(xs, ys, **poly_kwargs) if draw_border: xs, ys = _collect_lines_from_polygons(xs, ys) p.multi_line(xs, ys, color=border_color) return p
[docs] def add_borders(p, projection, scale="110m", **line_kwargs): """ Adds the borders of countries to a map with respect to a given projection. Args: p (Plot): Object which should be extended. projection (cartopy.crs.Projection): Cartopy projection for a geographic map. scale (str, "110m"): Scale of the feature resolution. Valid strings are "110m", "50m" and "10m". .. note:: This functions allows all parameters and keyword arguments defined by the :func:`~bokeh.plotting.figure.multi_line` function. Example: .. bokeh-plot:: :source-position: above import cartopy.crs as ccrs from bokeh.plotting import figure, show from bokeh.plotting._geo_feature import add_borders p = figure() p = add_borders(p, ccrs.PlateCarree()) show(p) """ line_kwargs.setdefault("color", GEO_FEATURE_COLORS.BORDERS) borders = cfeature.BORDERS.with_scale(scale) return add_line_geometries(p, projection, borders, **line_kwargs)
[docs] def add_coastlines(p, projection, scale="110m", **line_kwargs): """ Adds coastlines to a map with respect to a given projection. Args: p (Plot): Object which should be extended. projection (cartopy.crs.Projection): Cartopy projection for a geographic map. scale (str, "110m"): Scale of the feature resolution. Valid strings are "110m", "50m" and "10m". .. note:: This functions allows all parameters and keyword arguments defined by the :func:`~bokeh.plotting.figure.multi_line` function. Example: .. bokeh-plot:: :source-position: above import cartopy.crs as ccrs from bokeh.plotting import figure, show from bokeh.plotting._geo_feature import add_coastlines p = figure() p = add_coastlines(p, ccrs.PlateCarree()) show(p) """ line_kwargs.setdefault("color", GEO_FEATURE_COLORS.COASTLINES) coastline = cfeature.COASTLINE.with_scale(scale) return add_line_geometries(p, projection, coastline, **line_kwargs)
[docs] def add_land(p, projection, scale="110m", **poly_kwargs): """ Adds land geometries including islands to a map with respect to a given projection. Args: p (Plot): Object which should be extended. projection (cartopy.crs.Projection): Cartopy projection for a geographic map. scale (str, "110m"): Scale of the feature resolution. Valid strings are "110m", "50m" and "10m". Keyword Arguments: draw_polygon_border (bool, False): Enables the plotting of the geometry border. draw_polygon_color (str, "black"): Sets the color of the geometry border. .. note:: This functions allows all parameters and keyword arguments defined by the :func:`~bokeh.plotting.figure.multi_polygons` function. Example: .. bokeh-plot:: :source-position: above import cartopy.crs as ccrs from bokeh.plotting import figure, show from bokeh.plotting._geo_feature import add_land p = figure() p = add_land(p, ccrs.PlateCarree()) show(p) """ poly_kwargs.setdefault("color", GEO_FEATURE_COLORS.LAND) land = cfeature.LAND.with_scale(scale) return add_polygon_geometries(p, projection, land, **poly_kwargs)
[docs] def add_lakes(p, projection, scale="110m", **poly_kwargs): """ Adds lakes to a map with respect to a given projection. Args: p (Plot): Object which should be extended. projection (cartopy.crs.Projection): Cartopy projection for a geographic map. scale (str, "110m"): Scale of the feature resolution. Valid strings are "110m", "50m" and "10m". Keyword Arguments: draw_polygon_border (bool, False): Enables the plotting of the geometry border. draw_polygon_color (str, "black"): Sets the color of the geometry border. .. note:: This functions allows all parameters and keyword arguments defined by the :func:`~bokeh.plotting.figure.multi_polygons` function. Example: .. bokeh-plot:: :source-position: above import cartopy.crs as ccrs from bokeh.plotting import figure, show from bokeh.plotting._geo_feature import add_lakes p = figure() p = add_lakes(p, ccrs.PlateCarree()) show(p) """ poly_kwargs.setdefault("color", GEO_FEATURE_COLORS.LAKES) lakes = cfeature.LAKES.with_scale(scale) return add_polygon_geometries(p, projection, lakes, **poly_kwargs)
[docs] def add_ocean(p, projection, scale="110m", **poly_kwargs): """ Adds ocean to a map with respect to a given projection. Args: p (Plot): Object which should be extended. projection (cartopy.crs.Projection): Cartopy projection for a geographic map. scale (str, "110m"): Scale of the feature resolution. Valid strings are "110m", "50m" and "10m". Keyword Arguments: draw_polygon_border (bool, False): Enables the plotting of the geometry border. draw_polygon_color (str, "black"): Sets the color of the geometry border. .. note:: This functions allows all parameters and keyword arguments defined by the :func:`~bokeh.plotting.figure.multi_polygons` function. Example: .. bokeh-plot:: :source-position: above import cartopy.crs as ccrs from bokeh.plotting import figure, show from bokeh.plotting._geo_feature import add_ocean p = figure() p = add_ocean(p, ccrs.PlateCarree()) show(p) """ poly_kwargs.setdefault("color", GEO_FEATURE_COLORS.OCEAN) ocean = cfeature.OCEAN.with_scale(scale) return add_polygon_geometries(p, projection, ocean, **poly_kwargs)
[docs] def add_rivers(p, projection, scale="110m", **line_kwargs): """ Adds rivers to a map with respect to a given projection. Args: p (Plot): Object which should be extended. projection (cartopy.crs.Projection): Cartopy projection for a geographic map. scale (str, "110m"): Scale of the feature resolution. Valid strings are "110m", "50m" and "10m". .. note:: This functions allows all parameters and keyword arguments defined by the :func:`~bokeh.plotting.figure.multi_line` function. Example: .. bokeh-plot:: :source-position: above import cartopy.crs as ccrs from bokeh.plotting import figure, show from bokeh.plotting._geo_feature import add_rivers p = figure() p = add_rivers(p, ccrs.PlateCarree()) show(p) """ line_kwargs.setdefault("color", GEO_FEATURE_COLORS.RIVERS) rivers = cfeature.RIVERS.with_scale(scale) return add_line_geometries(p, projection, rivers, **line_kwargs)
[docs] def add_projection_boundary(p, projection, **line_kwargs): """ Adds the boundary of a given projection to a map. Args: p (Plot): Object which should be extended. projection (cartopy.crs.Projection): Cartopy projection for a geographic map. scale (str, "110m"): Scale of the feature resolution. Valid strings are "110m", "50m" and "10m". .. note:: This functions allows all parameters and keyword arguments defined by the :func:`~bokeh.plotting.figure.line` function. Example: .. bokeh-plot:: :source-position: above import cartopy.crs as ccrs from bokeh.plotting import figure, show from bokeh.plotting._geo_feature import add_projection_boundary p = figure() p = add_projection_boundary(p, ccrs.EckertIII()) show(p) """ line_kwargs.setdefault("color", GEO_FEATURE_COLORS.MAP_BOUNDARYS) x, y = np.array(projection.boundary.xy) p.line(x, y, **line_kwargs) return p
[docs] def add_provinces(p, projection, scale="110m", **line_kwargs): """ Adds the borders of provinces to a map with respect to a given projection. Args: p (Plot): Object which should be extended. projection (cartopy.crs.Projection): Cartopy projection for a geographic map. scale (str, "110m"): Scale of the feature resolution. Valid strings are "110m", "50m" and "10m". .. note:: This functions allows all parameters and keyword arguments defined by the :func:`~bokeh.plotting.figure.multi_line` function. Example: .. bokeh-plot:: :source-position: above import cartopy.crs as ccrs from bokeh.plotting import figure, show from bokeh.plotting._geo_feature import add_provinces p = figure() p = add_provinces(p, ccrs.PlateCarree()) show(p) """ line_kwargs.setdefault("color", GEO_FEATURE_COLORS.BORDERS) provinces = cfeature.NaturalEarthFeature('cultural', 'admin_1_states_provinces_lines', scale) return add_line_geometries(p, projection, provinces, **line_kwargs)
[docs] def add_states(p, projection, scale="110m", **poly_kwargs): """ Adds states and provinces as multi-polygons to a map for a given projection. Args: p (Plot): Object which should be extended. projection (cartopy.crs.Projection): Cartopy projection for a geographic map. scale (str, "110m"): Scale of the feature resolution. Valid strings are "110m", "50m" and "10m". Keyword Arguments: draw_polygon_border (bool, False): Enables the plotting of the geometry border. draw_polygon_color (str, "black"): Sets the color of the geometry border. .. note:: This functions allows all parameters and keyword arguments defined by the :func:`~bokeh.plotting.figure.multi_polygons` function. Example: .. bokeh-plot:: :source-position: above import cartopy.crs as ccrs from bokeh.plotting import figure, show from bokeh.plotting._geo_feature import add_states p = figure() p = add_states(p, ccrs.PlateCarree(), draw_polygon_border=True) show(p) """ poly_kwargs.setdefault("color", GEO_FEATURE_COLORS.STATES) states = cfeature.STATES.with_scale(scale) return add_polygon_geometries(p, projection, states, **poly_kwargs)
#----------------------------------------------------------------------------- # Private API #----------------------------------------------------------------------------- def _collect_line_geometries(projection, geometries_collection): """ Collects the x and y elements for line geometries of a NaturalEarthFeature and transforms the coordinates to fit to a given projection. Args: projection (catopy.crs.Projection): cartopy Projection geometries_collection (catopy.feature.NaturalEarthFeature): collection of line geometries given by a cartopy NaturalEarthFeature Example: .. code-block:: python import cartopy.crs as ccrs import cartopy.feature as cfeature from bokeh.plotting._geo_feature import _collect_line_geometries xs, ys = _collect_line_geometries(ccrs.Mollweide(), cfeature.BORDERS) """ xs = [] ys = [] for geometry in geometries_collection.geometries(): lines = projection.project_geometry(geometry, src_crs=ccrs.PlateCarree()) for line in lines.geoms: x, y = np.array(line.xy) xs.append(x) ys.append(y) return xs, ys def _collect_polygon_geometries(projection, geometries_collection, **kwargs): """ Collects the x and y elements for multi-polygon geometries of a NaturalEarthFeature and transforms the coordinates to fit to a given projection. If a color palette of list of colors is given, for each geometry the consecutive color is selected and the Keyword Arguments are returned with an updated list for colors. Args: projection (catopy.crs.Projection): cartopy Projection geometries_collection (catopy.feature.NaturalEarthFeature): collection of multi-polygon geometries given by a cartopy NaturalEarthFeature Keyword Arguments: color (str | list[str]): color or palette Example: .. code-block:: python import cartopy.crs as ccrs import cartopy.feature as cfeature from bokeh.plotting._geo_feature import _collect_polygon_geometries xs, ys, _ = _collect_polygon_geometries(ccrs.Mollweide(), cfeature.LAKES) """ xs = [] ys = [] color = kwargs.get("color") color_selection = isinstance(color, (list, tuple)) selected_colors = [] if color_selection: n_color = len(color) for i, geometry in enumerate(geometries_collection.geometries()): projected_geometrie = projection.project_geometry(geometry, src_crs=ccrs.PlateCarree()) for geom in projected_geometrie.geoms: x, y = np.array(geom.exterior.xy) x_geom = [x] y_geom = [y] for interior in geom.interiors: x, y = np.array(interior.coords.xy) x_geom.append(x) y_geom.append(y) xs.append([x_geom]) ys.append([y_geom]) if color_selection: selected_colors.append(color[i%n_color]) if color_selection: kwargs.update(color=selected_colors) return xs, ys, kwargs def _collect_lines_from_polygons(polygon_xs, polygon_ys): """ Collects the outline borders of a multi-polygon and returns it as a list. This can safe some time, if multi-polygons are already transformed to fit to a projection and afterwards the borders are needed. Args: polygon_xs (list[list[np.array]]): collected polygons for the x coordinate polygon_ys (list[list[np.array]]): collected polygons for the y coordinate """ line_xs = [] line_ys = [] for xs, ys in zip(polygon_xs, polygon_ys): line_xs.append(xs[0][0]) line_ys.append(ys[0][0]) return line_xs, line_ys #----------------------------------------------------------------------------- # Code #-----------------------------------------------------------------------------