Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Prev Previous commit
Next Next commit
Added documentation, supported add_axes regarding _as_mpl_axes.
  • Loading branch information
Phil Elson committed Feb 6, 2012
commit 756dd8c1c6a5d9b2c725f5cac72e202c30a12d4e
31 changes: 27 additions & 4 deletions doc/devel/add_new_projection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ in :mod:`matplotlib.scale` that may be used as starting points.
Creating a new projection
=========================

Adding a new projection consists of defining a subclass of
:class:`matplotlib.axes.Axes`, that includes the following elements:
Adding a new projection consists of defining a projection axes which
subclasses :class:`matplotlib.axes.Axes` and includes the following
elements:

- A transformation from data coordinates into display coordinates.

Expand All @@ -98,11 +99,33 @@ Adding a new projection consists of defining a subclass of
- Set up interactive panning and zooming. This is left as an
"advanced" feature left to the reader, but there is an example of
this for polar plots in :mod:`matplotlib.projections.polar`.

- Optionally define the class attribute ``NAME`` which can be
registered with :func:`matplotlib.projections.register_projection`
and used for simple projection instantiation such as
``plt.axes(projection=NAME)``, where ``NAME`` is a string.

- Any additional methods for additional convenience or features.

Once the class is defined, it must be registered with matplotlib
so that the user can select it.
Once the projection axes is defined, it can be used in one of two ways:

- By defining the class attribute ``NAME``, the projection axes can be
registered with :func:`matplotlib.projections.register_projection`
and subsequently simply invoked by name::

plt.axes(projection=NAME)

- For more complex, parameterisable projections, a generic "projection"
object may be defined which includes the method ``_as_mpl_axes``.
``_as_mpl_axes`` should take no arguments and return the projection's
axes subclass and a dictionary of additional arguments to pass to the
subclass' ``__init__`` method. Subsequently a parameterised projection
can be initialised with::

plt.axes(projection=MyProjection(param1=param1_value))

where MyProjection is an object which implements a ``_as_mpl_axes`` method.


A full-fledged and heavily annotated example is in
:file:`examples/api/custom_projection_example.py`. The polar plot
Expand Down
27 changes: 19 additions & 8 deletions lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,8 +702,16 @@ def add_axes(self, *args, **kwargs):
projection)
projection = 'polar'

if isinstance(projection, basestring) or projection is None:
projection_class = get_projection_class(projection)
elif hasattr(projection, '_as_mpl_axes'):
projection_class, extra_kwargs = projection._as_mpl_axes()
kwargs.update(**extra_kwargs)
else:
TypeError('projection must be a string, None or implement a _as_mpl_axes method. Got %r' % projection)

a = projection_factory(projection, self, rect, **kwargs)

self._axstack.add(key, a)
self.sca(a)
return a
Expand All @@ -714,10 +722,11 @@ def add_subplot(self, *args, **kwargs):
Add a subplot. Examples:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you are at it, could you add double colons here so that sphinx can render the following properly?


fig.add_subplot(111)
fig.add_subplot(1,1,1) # equivalent but more general
fig.add_subplot(212, axisbg='r') # add subplot with red background
fig.add_subplot(111, polar=True) # add a polar subplot
fig.add_subplot(sub) # add Subplot instance sub
fig.add_subplot(1,1,1) # equivalent but more general
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lines exceed 75 characters wide. Please follow PEP 8.

fig.add_subplot(212, axisbg='r') # add subplot with red background
fig.add_subplot(111, projection='polar') # add a polar subplot
fig.add_subplot(111, polar=True) # add a polar subplot
fig.add_subplot(sub) # add Subplot instance sub

*kwargs* are legal :class:`!matplotlib.axes.Axes` kwargs plus
*projection*, which chooses a projection type for the axes.
Expand Down Expand Up @@ -751,18 +760,20 @@ def add_subplot(self, *args, **kwargs):
ispolar = kwargs.pop('polar', False)
projection = kwargs.pop('projection', None)
if ispolar:
if projection is not None and projection != 'polar':
if projection is not None:
raise ValueError(
"polar=True, yet projection='%s'. " +
"polar=True, yet projection=%r. " +
"Only one of these arguments should be supplied." %
projection)
projection = 'polar'

if isinstance(projection, basestring) or projection is None:
projection_class = get_projection_class(projection)
else:
elif hasattr(projection, '_as_mpl_axes'):
projection_class, extra_kwargs = projection._as_mpl_axes()
kwargs.update(**extra_kwargs)
else:
TypeError('projection must be a string, None or implement a _as_mpl_axes method. Got %r' % projection)

# Remake the key without projection kwargs:
key = self._make_key(*args, **kwargs)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem right here... in add_axes(), the key is made in the beginning without the extra_kwargs, but here, the key could be made with the extra kwargs, possibly preventing the ability to find an existing axes. This key making needs to be moved up to an earlier spot in the function.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but, then again, we see that there are already changes made to kwargs... must find a judicious spot for the keymaker.

Expand Down
17 changes: 13 additions & 4 deletions lib/matplotlib/projections/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,19 @@ def get_projection_class(projection=None):
if projection is None:
projection = 'rectilinear'

try:
return projection_registry.get_projection_class(projection)
except KeyError:
raise ValueError("Unknown projection '%s'" % projection)
if isinstance(projection, basestring):
try:
return projection_registry.get_projection_class(projection)
except KeyError:
raise ValueError("Unknown projection '%s'" % projection)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't be getting rid of a public function (even if it probably doesn't get used elsewhere) without a deprecation notice and an addendum in the api_changes.

elif hasattr(projection, '_as_mpl_axes'):
projection_class, extra_kwargs = projection._as_mpl_axes()
kwargs.update(**extra_kwargs)
else:
TypeError('projection must be a string, None or implement a _as_mpl_axes method. Got %r' % projection)



def projection_factory(projection, figure, rect, **kwargs):
"""
Expand Down
12 changes: 11 additions & 1 deletion lib/matplotlib/projections/polar.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,4 +757,14 @@ def drag_pan(self, button, key, x, y):
# return mpath.Path(result, codes)
# transform_path_non_affine = transform_path


class Polar(object):
"""
Represents the polar projection, a Polar instance can be used to initialise
a polar plot in the standard ways, for example::

plt.axes(projection=Polar())

"""
def _as_mpl_axes(self):
# implement the matplotlib axes interface
return PolarAxes, {}