Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Updated custom_projection_example.py to work with v1.2 and newer #1579

Merged
merged 2 commits into from

3 participants

Joe Kington Phil Elson Michael Droettboom
Joe Kington

custom_projection_example.py is currently broken with matplotlib v1.2.x.

Compare the custom_projection_example.py run with matplotlib 1.1 Example with 1.1

with the custom_projection_example.py run with matplotlib 1.2 Example with 1.2

Joe Kington

I should have added this information here instead of on the mailing list. At any rate, here's the rationale for the changes, beyond simply fixing the example:

At some point transforms.Transform was slightly refactored. (Particularly,
this commit:
8bbe2e5 )

It's certainly makes things more logical and consistent, but it changed what methods need to be overridden when subclassing
Transform.

It looks like Phil caught the changes for the polar and geo projections, but the custom projection example was never updated. This led to the example failing for newer (1.2.0 and master) versions.

However, it's worth elaborating on the changes to the Transform class in the comments in the example.

With versions 1.1.x and older, one had to directly implement a
transform method when subclassing transforms.Transform,
otherwise a NotImplemented error would be raised.

With versions 1.2.x and newer, the preferred way appears to be to implement
things in a separate transform_affine or transform_non_affine method and
not explicitly implement a transform method.

If you implement the non-affine portion directly in the transform method
without overriding transform_non_affine, it leads to strange drawing bugs
with v1.2 that did not occur with older versions. (And thus the example being broken.)

On the other hand, for compatibility with versions 1.1 and older, you have
to explicitly implement the transform method as well, otherwise you'll get
the NotImplemented error.

Therefore, now one needs to explicitly implement both the
transform_non_affine and transform methods of a custom non-affine transform
for compatibility with 1.1 and older as well as 1.2 and newer.

Similarly, one needs to implement both the transform_path_non_affine and the
transform_path methods for compatibility with newer and older versions of
matplotlib.

examples/api/custom_projection_example.py
((7 lines not shown))
ipath = path.interpolated(path._interpolation_steps)
return Path(self.transform(ipath.vertices), ipath.codes)
+ transform_path_non_affine.__doc__ = \
+ Transform.transform_path_non_affine.__doc__
+
+ # Once again, for compatibility with matplotlib v1.1 and older, we need
+ # to explicitly override ``transform_path``. With v1.2 and newer, only
+ # overriding the ``transform_path_non_affine`` method is sufficient.
+ transform_path = transform_path_non_affine
Phil Elson Collaborator
pelson added a note

We could do a version specific override here. Something like:

if matplotlib.__version__ < '1.2':
    transform_path = ...
    ...

Good point! Also a version-specific conditional would potentially be more future-proof, as well as "stretching the legs" of the new functionality. I'll add that here directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Phil Elson
Collaborator

Thanks for doing this - much appreciated! I have commented that we might consider applying the compatibility code only if the version requires it, that way we are stretching the legs of the new functionality for newer versions, whereas we can still run the code for older. What do you think?

Phil Elson
Collaborator

Other than that :+1:

Phil Elson
Collaborator

P.S. Once we are happy with this, we will want to apply it to the v1.2.x branch.

Michael Droettboom
Owner

Thanks for catching this. +1 (and I agree with all of @pelson's comments).

Joe Kington

I agree, the version conditional makes the intent more clear, in case someone doesn't read the comments closely. Also, it's arguably more future-proof than re-implementing the default transform method. Done in joferkington@2a73c12. Thanks!

Phil Elson pelson merged commit c13f629 into from
Phil Elson
Collaborator

Thanks @joferkington - keep up the great work!

Joe Kington

Thanks!

Michael Droettboom
Owner

Cherry-picked to v1.2.x and will update the online docs shortly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 28 additions and 6 deletions.
  1. +28 −6 examples/api/custom_projection_example.py
34 examples/api/custom_projection_example.py
View
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+import matplotlib
from matplotlib.axes import Axes
from matplotlib.patches import Circle
from matplotlib.path import Path
@@ -385,9 +386,10 @@ class HammerTransform(Transform):
output_dims = 2
is_separable = False
- def transform(self, ll):
+ def transform_non_affine(self, ll):
"""
- Override the transform method to implement the custom transform.
+ Override the transform_non_affine method to implement the custom
+ transform.
The input and output are Nx2 numpy arrays.
"""
@@ -411,10 +413,25 @@ def transform(self, ll):
# differently-sized array, any transform that requires
# changing the length of the data array must happen within
# ``transform_path``.
- def transform_path(self, path):
- vertices = path.vertices
+ def transform_path_non_affine(self, path):
ipath = path.interpolated(path._interpolation_steps)
return Path(self.transform(ipath.vertices), ipath.codes)
+ transform_path_non_affine.__doc__ = \
+ Transform.transform_path_non_affine.__doc__
+
+ if matplotlib.__version__ < '1.2':
+ # Note: For compatibility with matplotlib v1.1 and older, you'll
+ # need to explicitly implement a ``transform`` method as well.
+ # Otherwise a ``NotImplementedError`` will be raised. This isn't
+ # necessary for v1.2 and newer, however.
+ transform = transform_non_affine
+
+ # Similarly, we need to explicitly override ``transform_path`` if
+ # compatibility with older matplotlib versions is needed. With v1.2
+ # and newer, only overriding the ``transform_path_non_affine``
+ # method is sufficient.
+ transform_path = transform_path_non_affine
+ transform_path.__doc__ = Transform.transform_path.__doc__
def inverted(self):
return HammerAxes.InvertedHammerTransform()
@@ -425,7 +442,7 @@ class InvertedHammerTransform(Transform):
output_dims = 2
is_separable = False
- def transform(self, xy):
+ def transform_non_affine(self, xy):
x = xy[:, 0:1]
y = xy[:, 1:2]
@@ -435,7 +452,12 @@ def transform(self, xy):
longitude = 2 * np.arctan((z*x) / (2.0 * (2.0*z*z - 1.0)))
latitude = np.arcsin(y*z)
return np.concatenate((longitude, latitude), 1)
- transform.__doc__ = Transform.transform.__doc__
+ transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__
+
+ # As before, we need to implement the "transform" method for
+ # compatibility with matplotlib v1.1 and older.
+ if matplotlib.__version__ < '1.2':
+ transform = transform_non_affine
def inverted(self):
# The inverse of the inverse is the original transform... ;)
Something went wrong with that request. Please try again.