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

Skip to content

savefig messes up coordinate transformation of annotate #4375

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

Closed
PiQuer opened this issue Apr 23, 2015 · 14 comments
Closed

savefig messes up coordinate transformation of annotate #4375

PiQuer opened this issue Apr 23, 2015 · 14 comments

Comments

@PiQuer
Copy link

PiQuer commented Apr 23, 2015

I want to annotate a data point with an inset axis (matplotlib-1.4.3, Linux):

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1.inset_locator import inset_axes

x=np.linspace(0,10,200)
y=np.exp(-x)*np.sin(x)

fig,ax = plt.subplots(figsize=(3.39,3))

ax.plot(x,y)

axins=inset_axes(ax, width=0.6, height=0.6, bbox_to_anchor=(6, 0.2),bbox_transform=ax.transData)
axins.axes.get_xaxis().set_visible(False)
axins.axes.get_yaxis().set_visible(False)
ax.annotate("",xy=(x[150],y[150]), xycoords=ax.transData,
        xytext=(1, 0), textcoords=axins.transAxes,
        arrowprops=dict(arrowstyle="->"))

fig.savefig('example.pdf')
plt.show()

This is the correct plot shown on screen:
shown
This is what savefig produces:
example

Apparently savefig changes the coordinates (1,0) from the inset axes to the parent axes for some reason.

@PiQuer
Copy link
Author

PiQuer commented Apr 23, 2015

Strangely enough, when commenting out the savefig, what is shown interactively on screen is the wrong version, until plt.draw() is called explicitely. After that, also savefig works correctly, so this is a workaround.

@tacaswell tacaswell added this to the next point release milestone Apr 23, 2015
@tacaswell tacaswell self-assigned this Apr 23, 2015
@adrn
Copy link
Contributor

adrn commented Jul 29, 2015

@PiQuer are you saying that explicitly calling draw() before savefig() is a workaround?

I'm running in to this exact issue, I think, see my griping here.

@adrn
Copy link
Contributor

adrn commented Jul 29, 2015

BTW different weirdness happens whether I save as PDF or PNG

@WeatherGod
Copy link
Member

attn: @mdboom. From the looks of it, it seems like the transform stack of
the annotated object (or the inset axes) is getting modified at each draw?

On Wed, Jul 29, 2015 at 8:29 AM, Adrian Price-Whelan <
[email protected]> wrote:

BTW different weirdness happens whether I save as PDF or PNG


Reply to this email directly or view it on GitHub
#4375 (comment)
.

@tacaswell
Copy link
Member

I wonder if this is fixed by #4843

@tacaswell
Copy link
Member

no 😞 not fixed that branch.

@tacaswell
Copy link
Member

Maybe (?) related to #2831 (comment)

@tacaswell
Copy link
Member

@adrn That sort of griping is not the most effective at making devs want to help you. Without seeing your code I suspect you are using display-space units at some point which you should not be doing. Please open a new issue or send a note to the mailing list.

@PiQuer Does this work reliably on any version of mpl? Trying to sort out if this is a regression or a long standing bug.

I suspect this issues is related to the use of the inset axes which has some non-trivial logic in how it's transforms get managed. The inset axes has it's transforms defined in terms of the transforms of the parent axes. Currently the details of those transforms get set up (somehow, have not traced the code path yet) as part of the draw process off the inset axes. When it is initialized it's transforms have some default value. The annotation in the above code is a child of the inset axes. The way the draw tree is worked is that the parent axes, then all it's children, then the inset axes, then all of it's children are drawn. The source of the error here is that the annotation is drawn before the inset axes is drawn, and hence before it's transforms have been updated to reflect that it is an inset axes. The simplest work around is to just make the annotation belong to the inset axes so it is always drawn after the transfroms have been properly updated:

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1.inset_locator import inset_axes

x=np.linspace(0,10,200)
y=np.exp(-x)*np.sin(x)

fig,ax = plt.subplots(figsize=(3.39,3))

ax.plot(x,y)

axins=inset_axes(ax, width=0.6, height=0.6, bbox_to_anchor=(6, 0.2),bbox_transform=ax.transData)
axins.axes.get_xaxis().set_visible(False)
axins.axes.get_yaxis().set_visible(False)
axins.annotate("",xy=(x[150],y[150]), xycoords=ax.transData,
        xytext=(1, 0), textcoords=axins.transAxes,
        arrowprops=dict(arrowstyle="->"), clip_on=False)

fig.savefig('example.pdf')
plt.show()

(the clip_on=False is not really needed, but I don't understand why off the top of my head)

Removing this from the next point release milestone because:

  • there is a simple workaround
  • the root cause of this may not be fixable with out major changes to axes_grid, the draw process, or both.

cc @leejjoon

@tacaswell tacaswell modified the milestones: proposed next point release, next point release Aug 6, 2015
@tacaswell tacaswell removed their assignment Aug 6, 2015
@PiQuer
Copy link
Author

PiQuer commented Aug 6, 2015

I will try to test in a couple of days, at the moment unfortunately I have no time to do it.

@tacaswell tacaswell modified the milestones: 2.1 (next point release), 2.2 (next next feature release) Oct 3, 2017
@leejjoon
Copy link
Contributor

I know this is rather old, but I will make a quick comment as this is still open.
I would say this is more like a design issue with the draw process. We can easily reproduce a similar behavior with two subplots where an annotation in the 1st subplot refers the the transAxes of the second subplot. I think we need to split the drawing process into two: first update the geometry of all the artists, and then draw. Or maybe three: update their extent, update location, and then draw. We may need some dependency tracking scheme with their geometry.

Maybe, we'd better create a new general issue for the discussion (and assemble all the related issues under it)?

@jklymak
Copy link
Member

jklymak commented Jul 23, 2018

FWIW:

import matplotlib.pyplot as plt
import numpy as np

x=np.linspace(0,10,200)
y=np.exp(-x)*np.sin(x)

fig,ax = plt.subplots(figsize=(3.39,3))

ax.plot(x,y)

axins = ax.inset_axes([0.4, 0.5, 0.3, 0.3])
axins.axes.get_xaxis().set_visible(False)
axins.axes.get_yaxis().set_visible(False)
ax.annotate("",xy=(x[150],y[150]), xycoords=ax.transData,
        xytext=(1, 0), textcoords=axins.transAxes,
        arrowprops=dict(arrowstyle="->"))

fig.savefig('example.pdf')
plt.show()

Works fine on master.
example.pdf

@leejjoon
Copy link
Contributor

I guess this works because the inset location is given in normalized axes coordinate, so its location is unchanged during its draw process. If you put 'axins.set_aspect(1.)', you will see that the location of the arrow becomes incorrect.

@jklymak
Copy link
Member

jklymak commented Jul 23, 2018

Yes, agreed - had the same problem w/ inset axes locators.

Lets start a new issue with as simple as possible a failing case and maybe we can work out a fix. But I think you are correct that the issue is the draw process sometimes needs to iterate, or at least be done twice before complicated layouts will work.

Calling apply_aspect (which calls the axes locator) seems to fix things.

A dangerous idea is for when the figure gets drawn call apply_aspect on all the children axes before the rest of the draw gets done. The danger is that apply_aspect is expensive, but I don't think its that expensive.

import matplotlib.pyplot as plt
import numpy as np

x=np.linspace(0,10,200)
y=np.exp(-x)*np.sin(x)

fig,ax = plt.subplots(figsize=(3.39,3))

ax.plot(x,y)

axins = ax.inset_axes([0.4, 0.5, 0.3, 0.3])
axins.xaxis.set_visible(False)
axins.yaxis.set_visible(False)
axins.set_aspect(0.2)
ax.annotate("",xy=(x[150],y[150]), xycoords=ax.transData,
        xytext=(1, 0), textcoords=axins.transAxes,
        arrowprops=dict(arrowstyle="->"))
axins.apply_aspect()

fig.savefig('example.pdf')
plt.show()

example.pdf

@ImportanceOfBeingErnest
Copy link
Member

Closing because this seems to be fixed by #11753. At least the pdf and screen output are now the same.

@story645 story645 removed this from the future releases milestone Oct 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants