proplot icon indicating copy to clipboard operation
proplot copied to clipboard

Understanding autolayout and colorbar placement

Open cvanelteren opened this issue 2 years ago • 1 comments

Description

I have been using proplot for the last few months and most of the time it takes off the heavy lifting that is a bi tricky to do in native matplotlib. However, sometimes, the autolayout feature fails without finding a trivial way to prevent the layout from changing. What is the problem? I am trying to add some annotation to a complex plot I am making (see simplified snippet below), and I am trying to understand how proplot computes the layout for a figure. Are there any resources on the rationale or more info on what went into the underlying design? I have browsed a bit around in the source code (e.g. here) to understand what proplot is doing.

In my case I aim to provide a simple annotation next to a figure with a colorbar. The problem is that hte autoscale feature produces a layout where the colorbar is removed from the main axes.

image whereas what I aim to achieve is something similar to: image

Steps to reproduce

# import proplot as plt
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# add colorbar proplot
norm = plt.pyplot.cm.colors.Normalize(vmin = 0, vmax = 1)
s = plt.pyplot.cm.ScalarMappable(norm = norm, cmap = "jet")

# add colorbar matplotlib
# norm = plt.cm.colors.Normalize(vmin = 0, vmax = 1)
# s = plt.cm.ScalarMappable(norm = norm, cmap = "jet")
cbar = fig.colorbar(s)
# cbar = ax.colorbar(s)

# add annotation
bbox = cbar.ax.get_window_extent()
c = bbox._transform.inverted().transform([bbox.x1, 0])
x = c[0] * 4
start = (x, 0.8)
end = (x, 1)

p=dict(arrowstyle="<-",
    connectionstyle="arc3",
    lw= 2)

ax.annotate("test", start, xytext = end,
            xycoords = cbar.ax.transAxes,
            ha = "center", va = "center",
            arrowprops = p)
fig.show()

Equivalent matplotlib code
import matplotlib.pyplot as plt
import matplotlib
print(matplotlib.__version__)
fig, ax = plt.subplots()

# add colorbar matplotlib
norm = plt.cm.colors.Normalize(vmin = 0, vmax = 1)
s = plt.cm.ScalarMappable(norm = norm, cmap = "jet")
cbar = fig.colorbar(s)
# cbar = ax.colorbar(s)

# add annotation
bbox = cbar.ax.get_window_extent()
c = bbox._transform.inverted().transform([bbox.x1, 0])
x = c[0] * 4
start = (x, 0.8)
end = (x, 1)

p=dict(arrowstyle="<-",
    connectionstyle="arc3",
    lw= 2)

ax.annotate("test", start, xytext = end,
            xycoords = cbar.ax.transAxes,
            ha = "center", va = "center",
            arrowprops = p)
fig.show()

Proplot version

0.9.5

cvanelteren avatar Jul 28 '22 15:07 cvanelteren

For posterity, I ended up using this

import proplot as plt
fig, ax = plt.subplots()
# add colorbar proplot
norm = plt.pyplot.cm.colors.Normalize(vmin = 0, vmax = 1)
s = plt.pyplot.cm.ScalarMappable(norm = norm, cmap = "jet")

cbar = ax.colorbar(s)

# add annotation
x = 3 # heuristic
start = (x, 0.8)
end = (x, 1)

p=dict(arrowstyle="<-",
    connectionstyle="arc3",
    lw= 2)

cbar.ax.annotate("test", start, xytext = end,
            xycoords = cbar.ax.transAxes,
            ha = "center", va = "center",
            arrowprops = p)
fig.show()

which produces. image

This makes intuitive also more sense than my prior hacky approach. Perhaps this is a non-issue ;-)!

cvanelteren avatar Aug 05 '22 13:08 cvanelteren

Good fix! This was tight-layout related -- setting tight=False and/or using ax.annotate(..., in_layout=False), then adding space to the edge of the figure with e.g. pplt.subplots(right=3), would also work. But your fix is simpler.

Basically, since you were calling the original annotate function from the main axes, proplot's tight layout algorithm was trying to offset the colorbar away from the text (matplotlib's own tight layout algorithm would have done something similar if you had multiple subplots in the figure). However, since the annotation position is relative to the colorbar, it got moved again after the colorbar was moved, producing that weird result.

Strangely, if I re-draw the figure repeatedly with fig.show() you can see the arrow "chasing" the colorbar away.

...
fig.show()

iTerm2 4xfhru tmps3vgvymu

fig.show()

iTerm2 3PKISY tmph_9e_6yt

fig.show()

iTerm2 Ilplo7 tmp5ptm8ud2

lukelbd avatar Mar 29 '23 08:03 lukelbd

Interesting. Thanks for the additional info.

cvanelteren avatar Mar 29 '23 13:03 cvanelteren