#!/usr/bin/env python
#-*- coding:utf-8 -*-
#
# This file is part of the PyNCulture project, which aims at providing tools to
# easily generate complex neuronal cultures.
# Copyright (C) 2017 SENeC Initiative
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# These tools are inspired by Sean Gillies' `descartes` library, that you can
# find here: https://pypi.python.org/pypi/descartes. They are released under
# a BSD license.
""" Plotting functions for shapely objects """
import numpy as np
from matplotlib.patches import PathPatch
from matplotlib.path import Path
[docs]def plot_shape(shape, axis=None, m='', mc="#999999", fc="#8888ff",
ec="#444444", alpha=0.5, **kwargs):
'''
Plot a shape (set the `axis` aspect to 1 to respect the proportions).
Parameters
----------
shape : :class:`Shape`
Shape to plot.
axis : :class:`matplotlib.axes.Axes` instance, optional (default: None)
Axis on which the shape should be plotted. By default, a new figure
is created.
m : str, optional (default: invisible)
Marker to plot the shape's vertices, matplotlib syntax.
mc : str, optional (default: "#999999")
Color of the markers.
fc : str, optional (default: "#8888ff")
Color of the shape's interior.
ec : str, optional (default: "#444444")
Color of the shape's edges.
alpha : float, optional (default: 0.5)
Opacity of the shape's interior.
kwargs: keywords arguments for :class:`matplotlib.patches.PathPatch`
'''
if axis is None:
import matplotlib.pyplot as plt
fig, axis = plt.subplots()
_plot_coords(axis, shape.exterior, m, mc, ec)
for path in shape.interiors:
_plot_coords(axis, path.coords, m, mc, ec)
patch = _make_patch(shape, color=fc, alpha=alpha, zorder=0, **kwargs)
axis.add_patch(patch)
axis.set_aspect(1)
def _make_patch(shape, **kwargs):
'''
Construct a matplotlib patch from a geometric object
Parameters
----------
shape: :class:`NetGrowth.geometry.Shape`
may be a Shapely or GeoJSON-like object with or without holes.
kwargs: keywords arguments for :class:`matplotlib.patches.PathPatch`
Returns
-------
an instance of :class:`matplotlib.patches.PathPatch`.
Example
-------
(using Shapely Point and a matplotlib axes):
>>> b = Point(0, 0).buffer(1.0)
>>> patch = PolygonPatch(b, fc='blue', ec='blue', alpha=0.5)
>>> axis.add_patch(patch)
Modified from `descartes` by Sean Gillies (BSD license).
'''
vertices = np.concatenate(
[np.asarray(shape.exterior.coords)[:, :2]] +
[np.asarray(h.coords)[:, :2] for h in shape.interiors])
instructions = np.concatenate(
[_path_instructions(shape.exterior)] +
[_path_instructions(h) for h in shape.interiors])
path = Path(vertices, instructions)
return PathPatch(path, **kwargs)
def _path_instructions(ob):
'''
Give instructions to build path from vertices.
'''
# The codes will be all "LINETO" commands, except for "MOVETO"s at the
# beginning of each subpath
n = len(ob.coords)
vals = np.ones(n, dtype=Path.code_type) * Path.LINETO
vals[0] = Path.MOVETO
return vals
def _plot_coords(ax, ob, m, mc, ec):
x, y = ob.xy
ax.plot(x, y, m, ls='-', c=ec, markerfacecolor=mc, zorder=1)