import matplotlib.cm as cm
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import numpy as np
import pandas as pd
from matplotlib.lines import Line2D
from ..data_utils import scaling_columnDF
from .colors import kindlmann, rand_cmap
pd.set_option("display.precision", 3)
[docs]
class Plot:
"""
Class for creating a basic matplotlib plot with a title and customizable settings.
Parameters
----------
figsize: tuple
Tuple of width and height for the figure.
black_background: bool
Whether to set the background color as black.
title: str
Title for the plot.
Attributes
----------
fig: matplotlib.figure.Figure
Figure object for the plot.
ax: matplotlib.axes object
Axes object for the plot.
title: str
Title for the plot.
text_color: str
Color for text elements, white if black_background is True and black otherwise.
font_size_primary: int
Font size for the title.
font_size_secondary: int
Font size for secondary elements.
"""
[docs]
def __init__(self, figsize, black_background, fontsize, title=None):
self.font_size_primary = fontsize
self.font_size_secondary = fontsize * 0.90
self.fig, self.ax = plt.subplots(1, figsize=figsize)
self.title = title
self.ax.axis("equal")
self.black_background = black_background
self.text_color = "white" if black_background else "black"
rect = self.fig.patch
rect.set_facecolor("black" if black_background else "white")
self.fig.tight_layout(pad=3.0)
if title is not None:
self.fig.suptitle(
title,
color=self.text_color,
fontsize=self.font_size_primary,
fontfamily="Times New Roman",
)
[docs]
class MultiPlot:
"""Create a grid of static GeoDataFrame plots.
Parameters
----------
ncols : int
Number of subplot columns.
nrows : int
Number of subplot rows.
figsize : tuple, optional
Matplotlib figure size.
**kwargs
Additional plotting options.
Attributes
----------
fig : matplotlib.figure.Figure
Figure created for the plot grid.
axes : numpy.ndarray
Matplotlib axes used by the plot grid.
"""
[docs]
def __init__(self, figsize, nrows, ncols, black_background, fontsize, title=None):
"""
Initializes the MultiPlot class.
Parameters
----------
figsize: tuple
The size of the figure (width, height) in inches.
nrows: int
The number of rows in the grid layout.
ncols: int
The number of columns in the grid layout.
black_background : bool
Specifies whether the plot has a black background (True) or white background (False).
fontsize: int
The font size to be used in the plot.
title: str or None
The title of the figure. If None, no title is displayed.
"""
self.ncols = ncols
self.fig, self.grid = plt.subplots(nrows=nrows, ncols=ncols, figsize=figsize)
self.black_background = black_background
self.text_color = "white" if black_background else "black"
rect = self.fig.patch
rect.set_facecolor("black" if black_background else "white")
self.font_size_primary = fontsize
self.font_size_secondary = fontsize * 0.90
self.fig.tight_layout()
if title is not None:
self.fig.suptitle(
title,
color=self.text_color,
fontsize=self.font_size_primary,
fontfamily="Times New Roman",
ha="center",
va="center",
)
[docs]
def plot_gdf(
gdf,
column=None,
title=None,
black_background=True,
figsize=(15, 15),
scheme=None,
bins=None,
classes=None,
norm=None,
cmap=None,
color=None,
alpha=None,
geometry_size=1.0,
geometry_size_column=None,
geometry_size_factor=None,
legend=False,
fontsize=15,
cbar=False,
cbar_ticks=5,
cbar_max_symbol=False,
cbar_min_max=False,
cbar_shrink=0.75,
axes_frame=False,
base_map_gdf=None,
base_map_color=None,
base_map_alpha=0.4,
base_map_geometry_size=1.1,
base_map_zorder=0,
):
"""
It plots the geometries of a single GeoDataFrame, coloring on the bases of the values contained in column, using a given scheme.
When only column is provided (no scheme), a categorical map is depicted.
When no column is provided, a plain map is shown.
The other parameters regulate colorbar, legend, base map.
Use this function for plotting in relation to maximum one column, one GeoDataFrame.
Parameters
----------
gdf: GeoDataFrame
GeoDataFrame to be plotted.
column: str
Column on which the plot is based.
title: str
Title of the plot.
black_background: bool
Specifies whether the plot has a black background (True) or white background (False).
fig_size: float
Size of the figure's side extent.
scheme: str
Classification method. Choose amongst the options listed at https://pysal.org/mapclassify/api.html.
bins: list
Bins defined by the user.
classes: int
Number of classes for categorizing the data when scheme is not "None".
norm: array
A class that specifies a desired data normalization into a [min, max] interval.
cmap: str or matplotlib.colors.LinearSegmentedColormap
Color map for the plot. See matplotlib colormaps for a list of possible values or pass a colormap.
color: str
Categorical color applied to all geometries when not using a column to color them.
alpha: float
Alpha value of the plotted layer.
geometry_size: float
Point size value when plotting a Point GeoDataFrame or Width value when plotting LineString GeoDataFrame.
geometry_size_columns: List of str
The column name in the GeoDataFrame to be used for scaling for manipulating the geometry size.
geometry_size_factor: float
Rescaling factor for the column provided, if any. The column is rescaled from 0 to 1, and the
geometry_size_factor is used to rescale the marker size accordingly
(e.g., rescaled variable's value [0-1] * factor) when plotting a Point GeoDataFrame.
legend: bool
When True, show the legend.
fontsize: int
Font size.
cbar: bool
If True, show the colorbar; otherwise, don't. When True, the legend is not shown.
cbar_ticks: int
Number of ticks along the colorbar.
cbar_max_symbol: bool
If True, show the ">" next to the highest tick's label in the colorbar (useful when normalizing).
cbar_min_max: bool
If True, only show the ">" and "<" as labels of the lowest and highest ticks' the colorbar.
cbar_shrink:
Fraction by which to multiply the size of the colorbar.
axes_frame: bool
If True, show the axes' frame.
base_map_gdf: GeoDataFrame
Additional layer to use as a base map.
base_map_color: str
Color applied to all geometries of the base map.
base_map_alpha: float
Base map's alpha value.
base_map_geometry_size: float
Base map's marker size when the base map is a Point GeoDataFrame.
base_map_zorder: int
Z-order of the layer. If 0, plots first, thus main GeoDataFrame on top; if 1, plots last, thus on top.
Returns
-------
fig: matplotlib.figure.Figure object
the resulting figure
"""
plot = Plot(figsize=figsize, black_background=black_background, title=title, fontsize=fontsize)
_fig, ax = plot.fig, plot.ax
ax.set_aspect("equal")
_set_axes_frame(axes_frame, ax, black_background, plot.text_color)
zorder = 0
if cbar:
legend = False
if cbar & (norm is None):
min_value = gdf[column].min()
max_value = gdf[column].max()
norm = plt.Normalize(vmin=min_value, vmax=max_value)
if base_map_gdf is not None and not base_map_gdf.empty:
_plot_base_map(
gdf=base_map_gdf,
ax=ax,
color=base_map_color,
geometry_size=base_map_geometry_size,
alpha=base_map_alpha,
zorder=base_map_zorder,
)
if base_map_zorder == 0:
zorder = 1
if geometry_size_column is None:
geometry_size_column = column
_plot_on_ax(
ax,
gdf,
column=column,
scheme=scheme,
bins=bins,
classes=classes,
norm=norm,
cmap=cmap,
color=color,
alpha=alpha,
geometry_size=geometry_size,
geometry_size_column=geometry_size_column,
geometry_size_factor=geometry_size_factor,
zorder=zorder,
legend=legend,
)
if cbar:
_generate_colorbar(
plot=plot,
cmap=cmap,
norm=norm,
cbar_ticks=cbar_ticks,
cbar_max_symbol=cbar_max_symbol,
cbar_min_max=cbar_min_max,
cbar_shrink=cbar_shrink,
)
elif legend:
_generate_legend_ax(ax, plot)
return plot.fig
[docs]
def plot_grid_gdfs_column(
gdfs=None,
column=None,
ncols=1,
nrows=1,
main_title=None,
titles=None,
black_background=True,
figsize=(15, 15),
scheme=None,
bins=None,
classes=None,
norm=None,
cmap=None,
color=None,
alpha=None,
geometry_size=None,
geometry_size_columns=None,
geometry_size_factor=None,
legend=False,
fontsize=15,
cbar=False,
cbar_ticks=5,
cbar_max_symbol=False,
cbar_min_max=False,
cbar_shrink=0.75,
axes_frame=False,
):
"""
It plots the geometries of different GeoDataFrames, coloring on the bases of the values contained in the provided column, using a given scheme.
When only column is provided (no scheme), a categorical map is depicted.
When no column is provided, a plain map is shown.
The other parameters regulate colorbar, legend, etc (no basemap here).
Use this function for plotting in relation to maximum one column, for multiple GeoDataFrames.
Parameters
----------
gdfs: list of GeoDataFrame
The list of GeoDataFrames to be plotted.
column: str
Column on which the plot is based.
ncols: int
The number of desired columns for organising the subplots.
nrows: int
The number of desired rows for organising the subplots.
main_title: str
The main plot title.
titles: list of str
The list of titles, one per axes (and column, when provided).
black_background: boolean
Black background or white.
fig_size: float
Size figure extent.
scheme: str
Classification method. Choose amongst the options listed at https://pysal.org/mapclassify/api.html.
bins: list
Bins defined by the user.
classes: int
Number of classes for categorizing the data when scheme is not "None".
norm: array
A class that specifies a desired data normalization into a [min, max] interval.
cmap: str or matplotlib.colors.LinearSegmentedColormap
Color map for the plot. See matplotlib colormaps for a list of possible values or pass a colormap.
color: str
Categorical color applied to all geometries when not using a column to color them.
alpha: float
Alpha value of the plotted layer.
geometry_size: float
Point size value when plotting a Point GeoDataFrame or Width value when plotting LineString GeoDataFrame.
geometry_size_columns: List of str
The column name(s) in the GeoDataFrames to be used for scaling the geometry size.
geometry_size_factor: float
Rescaling factor for the column provided, if any. The column is rescaled from 0 to 1, and the
geometry_size_factor is used to rescale the marker size accordingly
(e.g., rescaled variable's value [0-1] * factor) when plotting a Point GeoDataFrame.
legend: bool
When True, show the legend.
fontsize: int
Font size.
cbar: bool
If True, show the colorbar; otherwise, don't. When True, the legend is not shown.
cbar_ticks: int
Number of ticks along the colorbar.
cbar_max_symbol: bool
If True, show the ">" next to the highest tick's label in the colorbar (useful when normalizing).
cbar_min_max: bool
If True, only show the ">" and "<" as labels of the lowest and highest ticks' the colorbar.
cbar_shrink:
Fraction by which to multiply the size of the colorbar.
axes_frame: bool
If True, show the axes' frame.
Returns
-------
fig: matplotlib.figure.Figure object
the resulting figure
"""
if geometry_size_columns is None:
geometry_size_columns = []
if titles is None:
titles = []
if gdfs is None:
gdfs = []
if (len(gdfs) + 1 != ncols * nrows) & (len(gdfs) != ncols * nrows):
raise ValueError("Please provide an appropriate combination of nrows and ncols")
multiPlot = MultiPlot(
figsize=figsize,
nrows=nrows,
ncols=ncols,
black_background=black_background,
fontsize=fontsize,
title=main_title,
)
if (cbar) & (norm is None):
min_value = min([gdf[column].min() for gdf in gdfs])
max_value = max([gdf[column].max() for gdf in gdfs])
norm = plt.Normalize(vmin=min_value, vmax=max_value)
unique_categories = []
if legend and scheme is None:
# Step 1: Collect unique categories
unique_categories = set()
for gdf in gdfs:
unique_categories = unique_categories.union(set(gdf[column].unique()))
for n, ax in enumerate(multiPlot.grid.flat):
if n > len(gdfs) - 1:
ax.set_visible(False)
continue
gdf = gdfs[n]
parameters = {
"ax": ax,
"n": n,
"multiPlot": multiPlot,
"column": column,
"gdf": gdf,
"titles": titles,
"scheme": scheme,
"bins": bins,
"classes": classes,
"norm": norm,
"cmap": cmap,
"color": color,
"alpha": alpha,
"one_legend": True,
"legend": legend,
"axes_frame": axes_frame,
"geometry_size": geometry_size,
"geometry_size_columns": geometry_size_columns,
"geometry_size_factor": geometry_size_factor,
"unique_categories": unique_categories,
}
_subplot(**parameters)
if (cbar) & (not legend):
_generate_colorbar(
plot=multiPlot,
cmap=cmap,
norm=norm,
cbar_ticks=cbar_ticks,
cbar_max_symbol=cbar_max_symbol,
cbar_min_max=cbar_min_max,
cbar_shrink=cbar_shrink,
)
return multiPlot.fig
[docs]
def plot_grid_gdf_columns(
gdf,
columns=None,
ncols=1,
nrows=1,
main_title=None,
titles=None,
black_background=True,
figsize=(15, 15),
scheme=None,
bins=None,
classes=None,
norm=None,
cmap=None,
color=None,
alpha=None,
geometry_size=None,
geometry_size_columns=None,
geometry_size_factor=None,
legend=False,
fontsize=15,
cbar=False,
cbar_ticks=5,
cbar_max_symbol=False,
cbar_min_max=False,
cbar_shrink=0.75,
axes_frame=False,
):
"""
It plots the geometries of a GeoDataFrame, coloring on the bases of the values contained in two or more provided columns, using a given scheme.
When no columns are provided, the function raises an error.
The other parameters regulate colorbar, legend, etc (no basemap here).
Use this function for plotting in relation to more than one column, for a single GeoDataFrame.
Parameters
----------
gdf: GeoDataFrame
GeoDataFrame to be plotted.
columns: list of str
The list of column on which the plot is based.
ncols: int
The number of desired columns for organising the subplots.
nrows: int
The number of desired rows for organising the subplots.
main_title: str
The main plot title.
titles: list of str
Title of the plot.
black_background: boolean
Black background or white.
fig_size: float
Size figure extent.
scheme: str
Classification method. Choose amongst the options listed at https://pysal.org/mapclassify/api.html.
bins: list
Bins defined by the user.
classes: int
Number of classes for categorizing the data when scheme is not "None".
norm: array
A class that specifies a desired data normalization into a [min, max] interval.
cmap: str or matplotlib.colors.LinearSegmentedColormap
Color map for the plot. See matplotlib colormaps for a list of possible values or pass a colormap.
color: str
Categorical color applied to all geometries when not using a column to color them.
alpha: float
Alpha value of the plotted layer.
geometry_size: float
Point size value when plotting a Point GeoDataFrame or Width value when plotting LineString GeoDataFrame.
geometry_size_columns: List of str
The column name(s) in the GeoDataFrame to be used for scaling the geometry size.
geometry_size_factor: float
Rescaling factor for the column provided, if any. The column is rescaled from 0 to 1, and the
geometry_size_factor is used to rescale the marker size accordingly
(e.g., rescaled variable's value [0-1] * factor) when plotting a Point GeoDataFrame.
legend: bool
When True, show the legend.
fontsize: int
Font size.
cbar: bool
If True, show the colorbar; otherwise, don't. When True, the legend is not shown.
cbar_ticks: int
Number of ticks along the colorbar.
cbar_max_symbol: bool
If True, show the ">" next to the highest tick's label in the colorbar (useful when normalizing).
cbar_min_max: bool
If True, only show the ">" and "<" as labels of the lowest and highest ticks' the colorbar.
cbar_shrink:
Fraction by which to multiply the size of the colorbar.
axes_frame: bool
If True, show the axes' frame.
Returns
-------
fig: matplotlib.figure.Figure object
the resulting figure
"""
if geometry_size_columns is None:
geometry_size_columns = []
if titles is None:
titles = []
if columns is None:
columns = []
if len(columns) == 0:
raise ValueError(
"Provide a list of columns to plot the geometries on. For a plain plot, use plot_gdf"
)
if (len(columns) + 1 != ncols * nrows) & (len(columns) != ncols * nrows):
raise ValueError("Please provide an appropriate combination of nrows and ncols")
multiPlot = MultiPlot(
figsize=figsize,
nrows=nrows,
ncols=ncols,
black_background=black_background,
fontsize=fontsize,
title=main_title,
)
if (cbar) & (norm is None):
min_value = min([gdf[column].min() for column in columns])
max_value = max([gdf[column].max() for column in columns])
norm = plt.Normalize(vmin=min_value, vmax=max_value)
one_legend = scheme == "User_Defined"
for n, ax in enumerate(multiPlot.grid.flat):
if n > len(columns) - 1:
ax.set_visible(False)
continue
column = columns[n]
parameters = {
"ax": ax,
"n": n,
"multiPlot": multiPlot,
"column": column,
"gdf": gdf,
"titles": titles,
"scheme": scheme,
"bins": bins,
"classes": classes,
"norm": norm,
"cmap": cmap,
"color": color,
"alpha": alpha,
"one_legend": one_legend,
"legend": legend,
"axes_frame": axes_frame,
"geometry_size": geometry_size,
"geometry_size_columns": geometry_size_columns,
"geometry_size_factor": geometry_size_factor,
}
_subplot(**parameters)
if (cbar) & (not legend):
_generate_colorbar(
plot=multiPlot,
cmap=cmap,
norm=norm,
cbar_ticks=cbar_ticks,
cbar_max_symbol=cbar_max_symbol,
cbar_min_max=cbar_min_max,
cbar_shrink=cbar_shrink,
)
return multiPlot.fig
def _plot_on_ax(
ax,
gdf,
column=None,
scheme=None,
bins=None,
classes=7,
norm=None,
cmap=None,
color="red",
alpha=1.0,
geometry_size=1.0,
geometry_size_column=None,
geometry_size_factor=None,
legend=False,
zorder=0,
):
"""
Parameters
----------
ax: matplotlib.axes object
The axes object on which to create the _subplot.
gdf: GeoDataFrame
GeoDataFrame to be plotted.
column: str
Column on which the plot is based.
scheme: str
Classification method. Choose amongst the options listed at https://pysal.org/mapclassify/api.html.
bins: list
Bins defined by the user.
classes: int
Number of classes for categorizing the data when scheme is not "None".
norm: array
A class that specifies a desired data normalization into a [min, max] interval.
cmap: str or matplotlib.colors.LinearSegmentedColormap
Color map for the plot. See matplotlib colormaps for a list of possible values or pass a colormap.
color: str
Categorical color applied to all geometries when not using a column to color them.
alpha: float
Alpha value of the plotted layer.
geometry_size: float
Point size value when plotting a Point GeoDataFrame or Width value when plotting LineString GeoDataFrame.
geometry_size_columns: str
The column name in the GeoDataFrame to be used for scaling the geometry size.
geometry_size_factor: float
Rescaling factor for the column provided, if any. The column is rescaled from 0 to 1, and the
geometry_size_factor is used to rescale the marker size accordingly
(e.g., rescaled variable's value [0-1] * factor) when plotting a Point GeoDataFrame.
legend: bool
When True, show the legend.
zorder: int
Zorder of this layer; e.g. if 0, plots first, thus main GeoDataFrame on top; if 1, plots last, thus on top.
"""
gdf = gdf.copy()
categorical = not (norm is not None) | (scheme is not None)
if not categorical:
color = None
if cmap is None:
cmap = kindlmann()
if norm is not None:
scheme = None
if gdf[column].dtype != "O":
gdf = gdf.reindex(gdf[column].abs().sort_values(ascending=True).index)
else:
gdf[column] = gdf[column].astype(float)
elif (column is not None) & (cmap is None):
cmap = rand_cmap(len(gdf[column].unique()))
if len(gdf[column].unique()) == 1:
legend = False
cmap = None
color = "red"
c_k = dict(k=classes) if bins is None else dict(bins=bins, k=len(bins))
scheme = "User_Defined" if bins is not None else scheme
parameters = {
"ax": ax,
"column": column,
"classification_kwds": c_k,
"scheme": scheme,
"norm": norm,
"cmap": cmap,
"categorical": categorical,
"color": color,
"alpha": alpha,
"legend": legend,
"zorder": zorder,
}
geometry_type = gdf.iloc[0].geometry.geom_type
if geometry_type == "Point":
if geometry_size_factor is not None:
if "_sc" not in column:
gdf[column + "_sc"] = scaling_columnDF(gdf[column])
geometry_factor_column = column + "_sc"
else:
geometry_factor_column = column
# Define base marker size
base_marker_size = 0.40
# Apply exponential scaling to marker sizes
geometry_size = np.where(
gdf[geometry_factor_column] >= 0.20,
base_marker_size * np.exp(gdf[geometry_factor_column] * geometry_size_factor),
base_marker_size,
)
parameters["markersize"] = geometry_size
elif geometry_type == "LineString":
if geometry_size_factor is not None:
geometry_size = [
(abs(value) * geometry_size_factor)
if (abs(value) * geometry_size_factor) > 1.1
else 1.1
for value in gdf[geometry_size_column]
]
sub_parameters = {"linewidth": geometry_size, "capstyle": "round", "joinstyle": "round"}
parameters.update(sub_parameters)
else:
parameters["edgecolor"] = "none"
gdf.plot(**parameters)
def _subplot(
ax,
n,
multiPlot,
gdf,
column,
titles,
scheme,
bins,
classes,
norm,
cmap,
color,
alpha,
geometry_size,
geometry_size_columns,
geometry_size_factor,
one_legend,
legend,
axes_frame,
unique_categories=None,
):
"""
Create a _subplot with a map plot on the given axes.
Parameters
----------
ax: matplotlib.axes object
The axes object on which to create the _subplot.
n: int
The index of the _subplot.
multiPlot: MultiPlot object
The MultiPlot object controlling the plot settings.
gdf: GeoDataFrame
The GeoDataFrame containing the data to plot.
column: str
The column name in the GeoDataFrame to be used for plotting.
titles: str or sequence
The title(s) of the _subplot(s).
scheme: str
The classification scheme to use for mapping the data.
bins: int or sequence or pandas.IntervalIndex
The number of bins to use for the classification or the bin intervals.
classes: int or sequence
The number of classes to use for the classification or the class intervals.
geometry_size: float
Marker size value when plotting a Point GeoDataFrame or line width value when plotting LineString GeoDataFrame.
geometry_size_columns: List of str
The column name(s) in the GeoDataFrame to be used for scaling the geometry size.
geometry_size_factor: float
The factor by which to scale the geometry size.
norm: Normalize or str
The normalization scheme to use for mapping values to colors.
cmap: str or Colormap
The colormap to use for mapping values to colors.
color: str
Categorical color applied to all geometries when not using a column to color them.
alpha: float
Alpha value of the plotted layer.
legend: bool
If True, show the legend; otherwise, don't.
axes_frame: bool
Flag indicating whether to draw axes frame or not.
"""
if unique_categories is None:
unique_categories = []
ax.set_aspect("equal")
_set_axes_frame(axes_frame, ax, multiPlot.black_background, multiPlot.text_color)
if titles:
ax.set_title(
titles[n],
loc="center",
fontfamily="Times New Roman",
fontsize=multiPlot.font_size_primary,
color=multiPlot.text_color,
pad=15,
)
geometry_size_column = column
if geometry_size_columns:
geometry_size_column = geometry_size_columns[n]
legend_ax = False
legend_fig = False
if legend and scheme is not None:
legend_ax = (n == 1 and scheme == "User_Defined") or (scheme != "User_Defined")
legend_fig = n == 1 and scheme == "User_Defined"
elif legend: # categorical legend
legend_ax = n == 1
legend_fig = n == 1
_plot_on_ax(
ax,
gdf,
column=column,
scheme=scheme,
bins=bins,
classes=classes,
norm=norm,
cmap=cmap,
color=color,
alpha=alpha,
legend=legend_ax,
geometry_size=geometry_size,
geometry_size_column=geometry_size_column,
geometry_size_factor=geometry_size_factor,
)
if legend_fig and one_legend:
_generate_legend_fig(ax, multiPlot, gdf, unique_categories, cmap)
elif legend_ax:
_generate_legend_ax(ax, multiPlot)
multiPlot.fig.tight_layout() # Adjust layout after adding elements
def _plot_base_map(gdf=None, ax=None, color=None, geometry_size=None, alpha=0.5, zorder=0):
"""
It plots the geometries of a GeoDataFrame, coloring on the basis of the values contained in the provided columns, using a given scheme.
If only column is provided, a categorical map is depicted.
If no column is provided, a plain map is shown.
Parameters
----------
gdf: GeoDataFrame
The GeoDataFrame containing the map data.
ax: matplotlib.axes object
The axes object on which to create the _subplot.
color: str
The color to use for mapping
geometry_size: float
Point size value when plotting a Point GeoDataFrame or Width value when plotting LineString GeoDataFrame.
alpha: float
Alpha value of the plotted layer.
zorder: str
The order of the map elements with respect to other elements in the axis, default value is 0.
"""
if (gdf.iloc[0].geometry.geom_type == "LineString") or (
gdf.iloc[0].geometry.geom_type == "MultiLineString"
):
gdf.plot(ax=ax, color=color, linewidth=geometry_size, alpha=alpha, zorder=zorder)
if gdf.iloc[0].geometry.geom_type == "Point":
gdf.plot(ax=ax, color=color, markersize=geometry_size, alpha=alpha, zorder=zorder)
if (gdf.iloc[0].geometry.geom_type == "Polygon") or (
gdf.iloc[0].geometry.geom_type == "MultiPolygon"
):
gdf.plot(ax=ax, color=color, alpha=alpha, zorder=zorder)
def _generate_legend_fig(ax, plot, gdf, unique_categories=None, cmap=None):
"""
It generates the legend for an entire figure.
Parameters
----------
ax: matplotlib.axes object
The axes object on which to create the _subplot.
plot: Plot, MultiPlot Object
The Plot object.
gdf: GeoDataFrame
GeoDataFrame containing the geometries.
column: str
Column on which the legend is based.
unique_categories: list, optional
List of unique categories for categorical data.
cmap: str or Colormap, optional
Colormap to use for categorical data.
"""
if unique_categories is None:
unique_categories = []
leg = ax.get_legend()
if len(unique_categories) == 0:
handles = leg.legend_handles
labels = [t.get_text() for t in leg.texts]
else:
handles = []
labels = []
geometry_type = gdf.iloc[0].geometry.geom_type
for i, cat in enumerate(unique_categories):
color = plt.get_cmap(cmap)(i / len(unique_categories))
if geometry_type in ["Polygon", "MultiPolygon"]:
patch = mpatches.Patch(color=color, label=str(cat))
elif geometry_type in ["LineString", "MultiLineString"]:
patch = Line2D([0], [0], color=color, lw=4, label=str(cat))
elif geometry_type == "Point":
patch = Line2D(
[0],
[0],
marker="o",
color=color,
markerfacecolor=color,
markersize=10,
label=str(cat),
)
handles.append(patch)
labels.append(str(cat))
fig_leg = plot.fig.legend(handles=handles, labels=labels, loc=5, borderaxespad=0)
ax.get_legend().remove()
plt.setp(
fig_leg.texts,
family="Times New Roman",
fontsize=plot.font_size_secondary,
color=plot.text_color,
va="center",
)
fig_leg.get_frame().set_linewidth(0.0) # remove legend border
fig_leg.set_zorder(102)
fig_leg.get_frame().set_facecolor("none")
for handle in fig_leg.legend_handles:
if isinstance(handle, mpatches.Patch):
handle.set_linewidth(0) # or other properties specific to patches
elif not isinstance(handle, Line2D):
handle._legmarker.set_markersize(15)
else:
break
def _generate_legend_ax(ax, plot):
"""
It generate the legend for an axes.
Parameters
----------
ax: matplotlib.axes object
The axes object on which to create the _subplot.
plot: Plot, MultiPlot Object
The Plot object.
"""
leg = ax.get_legend()
plt.setp(
leg.texts,
family="Times New Roman",
fontsize=plot.font_size_secondary,
color=plot.text_color,
va="center",
)
bbox_legend = leg.get_window_extent()
bbox_axes = ax.get_window_extent()
legend_height = bbox_legend.y1 - bbox_legend.y0
axes_height = bbox_axes.y1 - bbox_axes.y0
vertical_position = 0.5 + (legend_height / 2 / axes_height)
leg.set_bbox_to_anchor((1.0, vertical_position))
leg.get_frame().set_linewidth(0.0) # remove legend border
leg.set_zorder(102)
for handle in leg.legend_handles:
if not isinstance(handle, Line2D):
handle._legmarker.set_markersize(12)
else:
break
leg.get_frame().set_facecolor("none")
def _generate_colorbar(
plot=None,
cmap=None,
norm=None,
cbar_ticks=5,
cbar_max_symbol=False,
cbar_min_max=False,
cbar_shrink=0.95,
):
"""
It plots a colorbar, given some settings.
Parameters
----------
plot: Plot, MultiPlot Object
The Plot object.
cmap: str or matplotlib.colors.LinearSegmentedColormap
Color map for the plot. See matplotlib colormaps for a list of possible values or pass a colormap.
norm: array
A class that specifies a desired data normalisation into a [min, max] interval.
cbar_ticks: int
Number of ticks along the colorbar.
cbar_max_symbol: bool
If True, show the ">" next to the highest tick's label in the colorbar (useful when normalizing).
cbar_min_max: bool
If True, only show the ">" and "<" as labels of the lowest and highest ticks' the colorbar.
cbar_shrink:
Fraction by which to multiply the size of the colorbar.
"""
ax = plot.ax if isinstance(plot, Plot) else plot.grid
cb = plot.fig.colorbar(cm.ScalarMappable(norm=norm, cmap=cmap), ax=ax, shrink=cbar_shrink)
tick_locator = ticker.MaxNLocator(nbins=cbar_ticks)
cb.locator = tick_locator
cb.update_ticks()
cb.outline.set_visible(False)
ticks = list(cb.get_ticks())
for t in ticks:
if (t == ticks[-1]) & (t != norm.vmax):
ticks[-1] = norm.vmax
if cbar_min_max:
ticks = [norm.vmin, norm.vmax]
cb.set_ticks(ticks)
cb.ax.set_yticklabels([round(t, 1) for t in ticks])
if cbar_max_symbol:
cb.ax.set_yticklabels(
[round(t, 1) if t < norm.vmax else "> " + str(round(t, 1)) for t in cb.ax.get_yticks()]
)
plt.setp(
plt.getp(cb.ax, "yticklabels"),
color=plot.text_color,
fontfamily="Times New Roman",
fontsize=plot.font_size_secondary,
)
def _set_axes_frame(axes_frame=False, ax=None, black_background=False, text_color="black"):
"""
It draws the axis frame.
Parameters
----------
axes_frame: bool
Flag indicating whether to draw axes frame or not.
ax: matplotlib.axes object
The axes object on which to create the _subplot.
black_background: boolean
It indicates whether the background color is black.
text_color: str
The text color.
"""
if not axes_frame:
ax.set_axis_off()
return
ax.xaxis.set_ticklabels([])
ax.yaxis.set_ticklabels([])
ax.tick_params(axis="both", which="both", length=0)
for spine in ax.spines:
ax.spines[spine].set_color(text_color)
if black_background:
ax.set_facecolor("black")