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

Skip to content

Positioning floating_axes.FloatingSubplot #19832

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

Open
w2naf opened this issue Mar 31, 2021 · 21 comments · May be fixed by #30099
Open

Positioning floating_axes.FloatingSubplot #19832

w2naf opened this issue Mar 31, 2021 · 21 comments · May be fixed by #30099

Comments

@w2naf
Copy link

w2naf commented Mar 31, 2021

Bug report

Bug summary

In a previous version of matplotlib, I was able to reposition and stretch a curvilinear coordinate axis created with floating_axes.FloatingSubplot after the axis has been created using Dividers and ax.set_axes_locator(). Now in matplotlib version 3.3.4 I can only get the axis to translate, but not stretch.

Code for reproduction

#!/usr/bin/env python3
from matplotlib import pyplot as plt
from matplotlib.transforms import Affine2D, Transform
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib.projections import polar
from mpl_toolkits.axisartist.grid_finder import FixedLocator, DictFormatter
import mpl_toolkits.axes_grid1.axes_size as Size
from mpl_toolkits.axes_grid1 import Divider

fig = plt.figure()

# Generate first axis using normal methods.
ax0 = fig.add_subplot(111)
ax0.set_xticks([0,500,1000,1500,2000])

# Generate a curvilear axis for the second axis.
angle_ticks     = [(0, '0'), (0.079, '500'), (0.157, '1000'), (0.235, '1500'), (0.314, '2000')]
grid_locator1   = FixedLocator([v for v, s in angle_ticks])
tick_formatter1 = DictFormatter(dict(angle_ticks))

alt_ticks       = [(6371.0, '0'), (6671.0, '300'), (6971.0, '600'), (7271.0, '900'), (7571.0, '1200')]
grid_locator2   = FixedLocator([v for v, s in alt_ticks])
tick_formatter2 = DictFormatter(dict(alt_ticks))

tr_rotate       = Affine2D().rotate(1.414)
tr_shift        = Affine2D().translate(0, 6371)
tr              = polar.PolarTransform() + tr_rotate

grid_helper = \
    floating_axes.GridHelperCurveLinear(tr, extremes=(0, 0.314, 6371, 7871),
                                        grid_locator1=grid_locator1,
                                        grid_locator2=grid_locator2,
                                        tick_formatter1=tick_formatter1,
                                        tick_formatter2=tick_formatter2,)

ax1 = floating_axes.FloatingSubplot(fig, 111, grid_helper=grid_helper)
ax1.invert_xaxis()
fig.add_subplot(ax1, transform=tr)

horiz   = [Size.Scaled(1.0)]
vert    = [Size.Scaled(1.0)]
ax0_div = Divider(fig,[0.0, 0.5, 1.0, 0.45],horiz,vert,aspect=False) # Rectangular Coordinates
ax1_div = Divider(fig,[0.0, 0.0, 1.0, 0.45],horiz,vert,aspect=False) # Curvilinear Coordinates

ax0_loc = ax0_div.new_locator(nx=0,ny=0) # Rectangular Coordinates
ax1_loc = ax1_div.new_locator(nx=0,ny=0) # Curvilinear Coordinates

ax0.set_axes_locator(ax0_loc) # Rectangular Coordinates
ax1.set_axes_locator(ax1_loc) # Curvilinear Coordinates

fig.savefig('bug_report.png',bbox_inches='tight')

Actual outcome

bug_report

Expected outcome

I expected to be able to stretch the curvilinear coordinate system so that its x-axis is aligned with the rectangular coordinate system.

Matplotlib version

  • Operating system: Ubuntu 18.04 LTS
  • Matplotlib version: 3.3.4
  • Matplotlib backend: Qt5Agg
  • Python version: 3.7.7 (default, May 7 2020, 21:25:33) [GCC 7.3.0]

Matplotlib installed with anacond default linux installer.

@jklymak
Copy link
Member

jklymak commented Mar 31, 2021

Do you know what version used to behave differently? That will make it easier to track down.

@w2naf
Copy link
Author

w2naf commented Mar 31, 2021 via email

@QuLogic
Copy link
Member

QuLogic commented Mar 31, 2021

I cannot get any different result even all the way back to 2.1.2. If it did work that way, it hasn't for a very very long time.

@QuLogic
Copy link
Member

QuLogic commented Mar 31, 2021

I also don't see anything in this code that's supposed to do a stretch in the x direction?

@w2naf
Copy link
Author

w2naf commented Mar 31, 2021

The figure used to be stretched/scaled when ax1.set_axes_locator(ax1_loc) was applied. Below are two versions of the same figure generated with the same code. The only difference I can tell is the matplotlib version used. I think the API must have changed, but I can't figure out how to get back to my old result.

Good Figure Generated on April 10, 2020

20170821 2000_10000kHz_txWWV_rxWA9VNJ_iri2016

Bad Figure Generated with Matplotlib v3.3.4

20170821 2000_10000kHz_txWWV_rxWA9VNJ_iri2016

@jklymak
Copy link
Member

jklymak commented Mar 31, 2021

I mean, its pretty obvious that those are not the same figure regardless of the axes positioning issue. That makes it more than a little suspicious you have two versions of your code floating around.

However, if you are sure the reproducible snippet above used to work, simply make virtual environments with old versions of matplotlib until it works. But given @QuLogic tried that back to 2.1.2, it seems unlikely that will show the problem.

@w2naf
Copy link
Author

w2naf commented Mar 31, 2021

Hi, @jklymak. You are right... I guess I should have said I believe the location portion of the code is the same. Let me try to do what you said with the virtual environments, and if I can show it is reproducible, I'll report back. Thank you for your help.

@QuLogic
Copy link
Member

QuLogic commented Mar 31, 2021

The figure used to be stretched/scaled when ax1.set_axes_locator(ax1_loc) was applied.

This only allocates space for the Axes; if the Axes itself has a different aspect ratio, it won't be stretched to fill the spce.

@jklymak
Copy link
Member

jklymak commented Mar 31, 2021

... right, your aspect is being set by your extremes. If you make the "vertical" range far narrower it fills out the bottom axes. However, I can never get it to exactly match the width of the bottom axes. I somewhat suspect someone went through and tuned that by hand.

@w2naf
Copy link
Author

w2naf commented Apr 1, 2021

Hi again,

I'm able to demonstrate that my code breaks between MPL v3.2.0 and v3.3.0. I created clean conda virtual environments and installed only the minimal packages in each. The only difference between them is the matplotlib version. I then re-ran my code in both environments. Please see below.

What do you think?

Matplotlib v3.2.2 Result

mpl_3 2 2

Matplotlib v3.3.0 Result

mpl_3 3 0

@jklymak
Copy link
Member

jklymak commented Apr 1, 2021

Great but we need a minimal example to test. I don't think your example code does this.

@w2naf
Copy link
Author

w2naf commented Apr 1, 2021

Hi, @jklymak . I fixed the minimal example show it shows the problem now. The problem only appears when we include a parasite axis with aux_ax = ax1.get_aux_axes(tr).

Minimal working example:

#!/usr/bin/env python3
from matplotlib import pyplot as plt
from matplotlib.transforms import Affine2D, Transform
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib.projections import polar
from mpl_toolkits.axisartist.grid_finder import FixedLocator, DictFormatter
import mpl_toolkits.axes_grid1.axes_size as Size
from mpl_toolkits.axes_grid1 import Divider

fig = plt.figure(figsize=(15,10))

# Generate first axis using normal methods.
ax0 = fig.add_subplot(111)
ax0.set_xticks([0,4000,8000,12000,16000])

# Generate a curvilear axis for the second axis.
angle_ticks     = [(0, '0'), (0.1, '4000'), (0.2, '8000'), (0.3, '12000'), (0.4, '16000')]
grid_locator1   = FixedLocator([v for v, s in angle_ticks])
tick_formatter1 = DictFormatter(dict(angle_ticks))

alt_ticks       = [(38226.0, '0'), (38586.0, '120'), (38946.0, '240'), (39306.0, '360'), (39666.0, '480')]
grid_locator2   = FixedLocator([v for v, s in alt_ticks])
tick_formatter2 = DictFormatter(dict(alt_ticks))

tr_rotate       = Affine2D().rotate(1.3615)
tr_shift        = Affine2D().translate(0, 38226.)
tr              = polar.PolarTransform() + tr_rotate

grid_helper = \
    floating_axes.GridHelperCurveLinear(tr, extremes=(0, 0.4, 38226.0, 40026.),
                                        grid_locator1=grid_locator1,
                                        grid_locator2=grid_locator2,
                                        tick_formatter1=tick_formatter1,
                                        tick_formatter2=tick_formatter2,)

ax1 = floating_axes.FloatingSubplot(fig, 111, grid_helper=grid_helper)
ax1.invert_xaxis()
fig.add_subplot(ax1, transform=tr)

# Adding this parasite axis causes the problem:
# create a parasite axes whose transData in RA, cz
aux_ax          = ax1.get_aux_axes(tr)

horiz   = [Size.Scaled(1.0)]
vert    = [Size.Scaled(1.0)]
ax0_div = Divider(fig,[0.035, 0.35, 0.88, 0.20],horiz,vert,aspect=False) # Rectangular Coordinates
ax1_div = Divider(fig,[0.000, 0.50, 0.95, 0.45],horiz,vert,aspect=False) # Curvilinear Coordinates

ax0_loc = ax0_div.new_locator(nx=0,ny=0) # Rectangular Coordinates
ax1_loc = ax1_div.new_locator(nx=0,ny=0) # Curvilinear Coordinates

ax0.set_axes_locator(ax0_loc) # Rectangular Coordinates
ax1.set_axes_locator(ax1_loc) # Curvilinear Coordinates

fig.savefig('bug_report.png',bbox_inches='tight')

Matplotlib v3.2.2 Result

bug_report-mpl_3 2 2

Matplotlib v3.3.0 Result

bug_report-mpl_3 3 0

@jklymak
Copy link
Member

jklymak commented Apr 1, 2021

Hmmm, well this is an issue with bbox_inches='tight'. If you turn that off, it seems to work fine (plus or minus not making a tight box properly). That there might be a bad interaction here is not too surprising. This bisects to c0ab395 #16463

@w2naf
Copy link
Author

w2naf commented Apr 1, 2021

Hi, @jklymak. It looks like you are right. What do we do now?

@jklymak
Copy link
Member

jklymak commented Apr 1, 2021

Make your axes a bit smaller, and don't use bbox_inches='tight'? Thats your workaround...

@w2naf
Copy link
Author

w2naf commented Apr 1, 2021

Thanks, @jklymak. I can do that. I might also try setting bbox_inches manually to a static value. Out of curiosity, how long do you think it will be until the root problem gets fixed in a code release?

@jklymak
Copy link
Member

jklymak commented Apr 1, 2021

This is not a well-maintained nor well-tested corner of the library. Presumably an eager user of these features would need to step up and figure out the problem!

@w2naf
Copy link
Author

w2naf commented Apr 2, 2021 via email

@anntzer
Copy link
Contributor

anntzer commented May 17, 2021

fwiw, this bisects to c0ab395.

Copy link

github-actions bot commented Dec 8, 2023

This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

@github-actions github-actions bot added the status: inactive Marked by the “Stale” Github Action label Dec 8, 2023
@anntzer
Copy link
Contributor

anntzer commented Dec 9, 2023

This mostly seems to have been fixed since the original report, except for the fact that bbox_inches tight appears to still leave too much room below the second axes. This part gets fixed with

diff --git i/lib/mpl_toolkits/axes_grid1/parasite_axes.py w/lib/mpl_toolkits/axes_grid1/parasite_axes.py
index 2a2b5957e8..18190639d3 100644
--- i/lib/mpl_toolkits/axes_grid1/parasite_axes.py
+++ w/lib/mpl_toolkits/axes_grid1/parasite_axes.py
@@ -70,6 +70,9 @@ class ParasiteAxesBase:

     # end of aux_transform support

+    def get_axes_locator(self):
+        return self._parent_axes.get_axes_locator()
+

 parasite_axes_class_factory = cbook._make_class_factory(
     ParasiteAxesBase, "{}Parasite")

otherwise, updating the parent's axes_locator (as done in the original example) leads to the parent and the parasite's locator getting out of sync, and thus the parasite misreporting its bbox for tightbboxing purposes.

This could probably use a test to avoid getting re-broken...

@anntzer anntzer removed the status: inactive Marked by the “Stale” Github Action label Dec 9, 2023
@anntzer anntzer added the status: has patch patch suggested, PR still needed label Apr 25, 2024
@anntzer anntzer linked a pull request May 23, 2025 that will close this issue
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants