import warnings
from typing import List, Union
import matplotlib.pylab as pl
import matplotlib.pyplot as plt
import numpy
import shapely
[docs]
def generate_2d_scatter_doughnut(num_points: float, radius: float) -> numpy.ndarray:
"""Generate a 2d doughnut shape of points distributed using gaussian noise.
Parameters
----------
num_points: :class:`float`
The total number of points that make up the doughnut shape.
radius: :class:`float`
The radius of the circle around which the points are scattered
Returns
-------
:class:`numpy.ndarray`
The points that make up the doughnut shape
"""
# Generate angles uniformly spaced around a circle
angles = numpy.linspace(0, 2 * numpy.pi, num_points)
# Generate coordinates of the points
x_coords = radius * numpy.cos(angles)
y_coords = radius * numpy.sin(angles)
# Apply Gaussian noise to the coordinates
numpy.random.seed(0)
x_coords += numpy.random.normal(0, 1, num_points)
y_coords += numpy.random.normal(0, 1, num_points)
return numpy.array([x_coords, y_coords]).T
[docs]
def plot_polygons(
geoms: List[shapely.Polygon],
colors: Union[numpy.ndarray, str] = None,
cmap: str = "viridis",
filled: bool = True,
ax=plt,
**kwargs,
):
"""Plot polygons on a map and color them based on the supplied ``values``.
.. warning ::
This function is a workaround to have matplotlib plot arbitrary shapes.
It is not performant and thus not suitable for large amounts of data.
Moreover, sometimes the shapes don't seem to align well in the corners.
This is a matplotlib visualization issue, likely related to the resolution of the figure.
The jagged corners tend to disappear when zooming in closely.
..
Parameters
----------
geoms: List[shapely.Polygon]
A list of shapely polygons to draw
colors: `Union[numpy.ndarray, str]`
If a string is supplied, the string is assumed to be the name of a matplotlib color, e.g. 'green'.
If colors is a numpy array or a list,
these values will be used to color the supplied ``geoms`` according the the supplied ``cmap``.
If None, the all polygons will be black.
cmap: :class:`str`
The matplitlib complient colormap name to use
Will be ignored if the 'values' argument not is supplied.
filled: :class:`bool`
Whether only the outline of the polygon should be drawn (False) or the polygon should be filled (True)
ax: `matplotlib.axes.Axes` (optional)
The matplotlib axis object to plot on.
If an axis object is supplied, the plot will be edited in-place.
Default: `matplotlib.pyplot`
Returns
-------
None
"""
# ravel geoms if necessary
if isinstance(geoms, numpy.ndarray):
geoms = geoms.ravel()
if isinstance(colors, str) or colors is None:
colors = [colors if colors else "black"] * len(geoms)
if all(isinstance(c, str) for c in colors):
pass # already got passed a list of color names
else:
# create colormap that matches our values
cmap = getattr(pl.cm, cmap)
values = colors
vmin = numpy.nanmin(values)
values_normalized = values - vmin
vmax = numpy.nanmax(values_normalized)
values_normalized = values_normalized / vmax
colors = cmap(
values_normalized
).squeeze() # squeeze to remove empty axes (when values is pandas series)
colors[numpy.all(colors == 0, axis=1)] += 1 # turn black (nodata) to white
# plot each cell as a polygon with color
for geom, color in zip(geoms, colors):
if filled:
ax.fill(*geom.exterior.xy, color=color, **kwargs)
else:
ax.plot(*geom.exterior.xy, color=color, **kwargs)