proplot
proplot copied to clipboard
Understanding autolayout and colorbar placement
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.
whereas what I aim to achieve is something similar to:
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
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.
This makes intuitive also more sense than my prior hacky approach. Perhaps this is a non-issue ;-)!
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()
fig.show()
fig.show()
Interesting. Thanks for the additional info.