I have written a routine to draw vertical cross sections from atmospheric model output. An example is shown below. What I would like to do, is to show two vertical axes: on the left I display presure values on a log scale, and on the right I show altitudes in km. I thought it would be nice to show the altitudes at the locations of the model levels - this is why they are irregularly spaced. All works nicely, except that the labels on the right overlap near the bottom. I found out that I can hide specific labels using ax2.get_yticklabels()[index].set_visible(False)
. My problem is: how do I determine which labels (indices) I want to hide? I believe it should be possible to find out where the tick labels are positioned (in axis or figure coordinates). Then I could use a threshold distance as in
yp = -1
for t in ax2.get_yticklabels():
y = t.get_position().y0 # this doesn't yield any useful bbox!
if y-yp < threshold:
t.set_visible(False)
else:
yp = y
Unfortunately, I haven't found a way to get the label coordinates. Any hints?
Here is the example figure:
And here is the complete code that does the plotting (data is a 2-D array, x are latitudes, and y are pressure values):
def plotZM(data, x, y, plotOpt=None):
"""Create a zonal mean contour plot of one variable
plotOpt is a dictionary with plotting options:
'scale_factor': multiply values with this factor before plotting
'units': a units label for the colorbar
'levels': use list of values as contour intervals
'title': a title for the plot
"""
if plotOpt is None: plotOpt = {}
# create figure and axes
fig = plt.figure()
ax1 = fig.add_subplot(111)
# scale data if requested
scale_factor = plotOpt.get('scale_factor', 1.0)
pdata = data * scale_factor
# determine contour levels to be used; default: linear spacing, 20 levels
clevs = plotOpt.get('levels', np.linspace(data.min(), data.max(), 20))
# map contour values to colors
norm=matplotlib.colors.BoundaryNorm(clevs, ncolors=256, clip=False)
# draw the (filled) contours
contour = ax1.contourf(x, y, pdata, levels=clevs, norm=norm)
# add a title
title = plotOpt.get('title', 'Vertical cross section')
ax1.set_title(title) # optional keyword: fontsize="small"
# add colorbar
# Note: use of the ticks keyword forces colorbar to draw all labels
fmt = matplotlib.ticker.FormatStrFormatter("%g")
cbar = fig.colorbar(contour, ax=ax1, orientation='horizontal', shrink=0.8,
ticks=clevs, format=fmt)
cbar.set_label(plotOpt.get('units', ''))
for t in cbar.ax.get_xticklabels():
t.set_fontsize("x-small")
# change font size of x labels
xlabels = ax1.get_xticklabels()
for t in xlabels:
t.set_fontsize("x-small")
# set up y axes: log pressure labels on the left y axis, altitude labels
# according to model levels on the right y axis
ax1.set_ylabel("Pressure [hPa]")
ax1.set_yscale('log')
ax1.set_ylim(y.max(), y.min())
subs = [1,2,5]
print "y_max/y_min = ", y.max()/y.min()
if y.max()/y.min() < 30.:
subs = [1,2,3,4,5,6,7,8,9]
loc = matplotlib.ticker.LogLocator(base=10., subs=subs)
ax1.yaxis.set_major_locator(loc)
fmt = matplotlib.ticker.FormatStrFormatter("%g")
ax1.yaxis.set_major_formatter(fmt)
ylabels = ax1.get_yticklabels()
for t in ylabels:
t.set_fontsize("x-small")
# calculate altitudes from pressure values (use fixed scale height)
z0 = 8.400 # scale height for pressure_to_altitude conversion [km]
altitude = z0 * np.log(1015.23/y)
# add second y axis for altitude scale
ax2 = ax1.twinx()
ax2.set_ylabel("Altitude [km]")
ax2.set_ylim(altitude.min(), altitude.max())
ax2.set_yticks(altitude)
fmt = matplotlib.ticker.FormatStrFormatter("%6.1f")
ax2.yaxis.set_major_formatter(fmt)
# tweak altitude labels
ylabels = ax2.get_yticklabels()
for i,t in enumerate(ylabels):
t.set_fontsize("x-small")
# show plot
plt.show()