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

Skip to content

Axes.inset_axes: enable Axes subclass creation #22608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 7, 2022

Conversation

rcomer
Copy link
Member

@rcomer rcomer commented Mar 5, 2022

PR Summary

Closes #22630

Modify Axes.inset_axes so it can accept the polar, projection and axes_class keywords, and therefore return subclasses of Axes. My target use-case is with Cartopy, so we can now adapt this gallery example to do something like

import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import numpy as np

def sample_data(shape=(73, 145)):
    """Return ``lons``, ``lats`` and ``data`` of some fake data."""
    nlats, nlons = shape
    lats = np.linspace(-np.pi / 2, np.pi / 2, nlats)
    lons = np.linspace(0, 2 * np.pi, nlons)
    lons, lats = np.meshgrid(lons, lats)
    wave = 0.75 * (np.sin(2 * lats) ** 8) * np.cos(4 * lons)
    mean = 0.5 * np.cos(2 * lats) * ((np.sin(2 * lats)) ** 2 + 2)

    lats = np.rad2deg(lats)
    lons = np.rad2deg(lons)
    data = wave + mean

    return lons, lats, data


lons, lats, data = sample_data()

ax = plt.subplot(111, projection=ccrs.PlateCarree())
ax.contourf(lons, lats, data)
ax.coastlines()

axins = ax.inset_axes([0.5, 0.03, 0.47, 0.47], projection=ccrs.PlateCarree())
axins.contourf(lons, lats, data)
axins.set_xlim(-10, 5)
axins.set_ylim(50, 60)
axins.coastlines()

box, lines = ax.indicate_inset_zoom(axins, edgecolor="black")

plt.show()

cartopy_inset

PR Checklist

Tests and Styling

  • Has pytest style unit tests (and pytest passes). I'm unsure what extra tests should be added for this. Advice welcome!
  • Is Flake 8 compliant (install flake8-docstrings and run flake8 --docstring-convention=all).

Documentation

  • New features are documented, with examples if plot related.
  • New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).
  • [N/A] API changes documented in doc/api/next_api_changes/ (follow instructions in README.rst there).
  • Documentation is sphinx and numpydoc compliant (the docs should build without error).

@rcomer rcomer changed the title Use fig.add_axes within Axes.inset_axes Use Figure.add_axes within Axes.inset_axes Mar 5, 2022
@rcomer
Copy link
Member Author

rcomer commented Mar 5, 2022

Looks like I have broken one test consistently across platforms. I will have to come back to this another day.

@jklymak
Copy link
Member

jklymak commented Mar 5, 2022

Inset axes are explicitly children of their parent axes so please don't also add the figure as a parent. I'm not quite sure what the best way to get a projection into an inset_axes is...

@greglucas
Copy link
Contributor

This seems like a useful thing to have available in some form. Might be worth having a look at #22060 and whether https://github.com/matplotlib/matplotview would work with GeoAxes too...

Ahh, the axes being a child of the figure or not would actually explain why mpl_toolkit inset_axes warn with tight_layout() (because they are added!), but standard inset_axes don't. (xref: #22611)

parent_axes.figure.add_axes(inset_axes)

The easiest way might be to allow axes_class to be passed in to the inset_axes() method, similar to mpl_toolkits and figure's add_axes methods.

axes_class : `matplotlib.axes.Axes` type, default: `.HostAxes`
The type of the newly created inset axes.
axes_kwargs : dict, optional
Keyword arguments to pass to the constructor of the inset axes.
Valid arguments include:

However, there might be some more surgery needed outside of AxesGrid to support the projection or other keyword args.

axes_class : subclass type of `~.axes.Axes`, optional
The `.axes.Axes` subclass that is instantiated. This parameter
is incompatible with *projection* and *polar*. See
:ref:`axisartist_users-guide-index` for examples.

@rcomer
Copy link
Member Author

rcomer commented Mar 6, 2022

Thanks for the feedback. I did have a hunch that it couldn't really be this simple!

Inset axes are explicitly children of their parent axes so please don't also add the figure as a parent.

I'm afraid I don't have the background knowledge to understand this. Is there somewhere I can read up on the matplotlib parent/child relationships and why they matter? I guess the comment about tight_layout gives a clue.

matplotlibview

Looks useful - thanks! I will have a play with that at some point.

The easiest way might be to allow axes_class to be passed in to the inset_axes() method, similar to mpl_toolkits and figure's add_axes methods.

I'm not so keen on that API as it seems inconsistent with how the user is usually encouraged to create their Axes instances. But I guess it was done in those other functions for a reason.

Anyway it seems that whatever the answer is, it's not this. So for now I will just close this.

@rcomer rcomer closed this Mar 6, 2022
@jklymak
Copy link
Member

jklymak commented Mar 6, 2022

@rcomer, maybe convert this to an issue? I think it is reasonable to ask that inset_axes accepts projections, I'm just not sure off the top of my head how to do it (I have limited Matplotlib (and internet) bandwidth at the moment.

@rcomer
Copy link
Member Author

rcomer commented Mar 9, 2022

maybe convert this to an issue?

Issue now at #22630. Thanks!

@rcomer rcomer reopened this Mar 18, 2022
@rcomer rcomer marked this pull request as draft March 18, 2022 16:12
@rcomer
Copy link
Member Author

rcomer commented Mar 18, 2022

The important functionality seems to be contained within Figure._process_projection_requirements, so I tried using that directly. The Cartopy example still works, so I just wanted to see how it would fly with the CI.

@rcomer rcomer closed this Mar 25, 2022
@jklymak
Copy link
Member

jklymak commented Mar 25, 2022

@rcomer, did this not work? It looks like something similar should work, and it is certainly reasonable functionality...

@rcomer
Copy link
Member Author

rcomer commented Mar 25, 2022

@jklymak yes the CI all seemed fine and it works for my Cartopy example. However at #22630 @mylasem said they were working on it, so I thought I would leave it to them for now. I'd be happy to pick this up again at some point if @mylasem's version doesn't land for whatever reason.

@jklymak
Copy link
Member

jklymak commented Mar 25, 2022

In general, Matplotlib does not have a "reserve an issue" policy, and this PR predates @mylasem 's comment. I think they should work within the context of this PR if at all possible.

@rcomer rcomer reopened this Mar 31, 2022
@rcomer rcomer changed the title Use Figure.add_axes within Axes.inset_axes Axes.inset_axes: enable Axes subclass creation Mar 31, 2022
@rcomer
Copy link
Member Author

rcomer commented Mar 31, 2022

OK, since @mylasem has not commented further and I had some time, I have:

  • Added some basic tests to verify that the expected type is returned for our three methods of specifying it.
  • Updated the Axes.inset_axes docstring with the keyword descriptions - this was a straight copy/paste from the Figure.add_axes docstring, and then added the word "inset".
  • Updated the what's new entry (and the PR title and OP) to reflect that we're not going via Figure.add_axes.

@@ -6614,6 +6614,30 @@ def test_zoom_inset():
axin1.get_position().get_points(), xx, rtol=1e-4)


def test_inset_polar():
from matplotlib.projections.polar import PolarAxes
Copy link
Member Author

@rcomer rcomer Mar 31, 2022

Choose a reason for hiding this comment

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

I imported the class within the test as I noticed another test in this module does that. If that's not necessary, then I think we could parametrize these three tests into one.

Copy link
Member

Choose a reason for hiding this comment

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

I do not see any reason to do this, I think pulling all of the imports to the top is OK.

Copy link
Member Author

Choose a reason for hiding this comment

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

I moved the imports to the top, but in the end couldn't convince myself it was worth parametrizing the two small tests that are still the same: since they're so small there isn't much repetition going on and I suspect readability is better as they are than with a parametrized version.

@rcomer rcomer marked this pull request as ready for review March 31, 2022 09:33
@tacaswell tacaswell added this to the v3.6.0 milestone Apr 1, 2022
@rcomer rcomer closed this Apr 4, 2022
@rcomer rcomer reopened this Apr 4, 2022
Copy link
Member

@tacaswell tacaswell left a comment

Choose a reason for hiding this comment

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

I am 👍 with this going in as-is.

Moving to the mpl20 style would be good as would refactoring the test, but neither is worth holding up the PR over.

@rcomer
Copy link
Member Author

rcomer commented Apr 4, 2022

Thanks @tacaswell, I am happy to follow those up tomorrow when I am back at my keyboard. Should I also squash the commits down to one?

@tacaswell
Copy link
Member

Should I also squash the commits down to one?

Yes please (and if you replace the image CI without squashing CI will fail).

@timhoffm timhoffm merged commit 9caa261 into matplotlib:main Apr 7, 2022
@rcomer
Copy link
Member Author

rcomer commented Apr 7, 2022

Thanks All for your guidance!

@rcomer rcomer deleted the inset-add_axes branch April 7, 2022 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[ENH]: enable passing of projection keyword to Axes.inset_axes
6 participants