diff --git a/examples/color/colorbar_basics.py b/examples/color/colorbar_basics.py index 64bed1449da3..e1815b1da6f3 100644 --- a/examples/color/colorbar_basics.py +++ b/examples/color/colorbar_basics.py @@ -32,8 +32,9 @@ fig.colorbar(pos, ax=ax1) # repeat everything above for the negative data +# you can specify location, anchor and shrink the colorbar neg = ax2.imshow(Zneg, cmap='Reds_r', interpolation='none') -fig.colorbar(neg, ax=ax2) +fig.colorbar(neg, ax=ax2, location='right', anchor=(0, 0.3), shrink=0.7) # Plot both positive and negative values between +/- 1.2 pos_neg_clipped = ax3.imshow(Z, cmap='RdBu', vmin=-1.2, vmax=1.2, diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 505daaf41341..5f329d6ed4f5 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -1509,35 +1509,51 @@ def make_axes_gridspec(parent, *, location=None, orientation=None, kw['orientation'] = loc_settings['orientation'] location = kw['ticklocation'] = loc_settings['location'] - pad = loc_settings["pad"] + anchor = kw.pop('anchor', loc_settings['anchor']) + panchor = kw.pop('panchor', loc_settings['panchor']) + pad = kw.pop('pad', loc_settings["pad"]) wh_space = 2 * pad / (1 - pad) - # for shrinking - pad_s = (1 - shrink) * 0.5 - wh_ratios = [pad_s, shrink, pad_s] - - if location == "left": - gs = parent.get_subplotspec().subgridspec( - 1, 2, wspace=wh_space, width_ratios=[fraction, 1-fraction-pad]) - ss_main = gs[1] - ss_cb = gs[0].subgridspec(3, 1, hspace=0, height_ratios=wh_ratios)[1] - elif location == "right": - gs = parent.get_subplotspec().subgridspec( - 1, 2, wspace=wh_space, width_ratios=[1-fraction-pad, fraction]) - ss_main = gs[0] - ss_cb = gs[1].subgridspec(3, 1, hspace=0, height_ratios=wh_ratios)[1] - elif location == "top": - gs = parent.get_subplotspec().subgridspec( - 2, 1, hspace=wh_space, height_ratios=[fraction, 1-fraction-pad]) - ss_main = gs[1] - ss_cb = gs[0].subgridspec(1, 3, wspace=0, width_ratios=wh_ratios)[1] - aspect = 1 / aspect - else: # "bottom" - gs = parent.get_subplotspec().subgridspec( - 2, 1, hspace=wh_space, height_ratios=[1-fraction-pad, fraction]) - ss_main = gs[0] - ss_cb = gs[1].subgridspec(1, 3, wspace=0, width_ratios=wh_ratios)[1] - aspect = 1 / aspect + if location in ('left', 'right'): + # for shrinking + height_ratios = [ + (1-anchor[1])*(1-shrink), shrink, anchor[1]*(1-shrink)] + + if location == 'left': + gs = parent.get_subplotspec().subgridspec( + 1, 2, wspace=wh_space, + width_ratios=[fraction, 1-fraction-pad]) + ss_main = gs[1] + ss_cb = gs[0].subgridspec( + 3, 1, hspace=0, height_ratios=height_ratios)[1] + else: + gs = parent.get_subplotspec().subgridspec( + 1, 2, wspace=wh_space, + width_ratios=[1-fraction-pad, fraction]) + ss_main = gs[0] + ss_cb = gs[1].subgridspec( + 3, 1, hspace=0, height_ratios=height_ratios)[1] + else: + # for shrinking + width_ratios = [ + anchor[0]*(1-shrink), shrink, (1-anchor[0])*(1-shrink)] + + if location == 'bottom': + gs = parent.get_subplotspec().subgridspec( + 2, 1, hspace=wh_space, + height_ratios=[1-fraction-pad, fraction]) + ss_main = gs[0] + ss_cb = gs[1].subgridspec( + 1, 3, wspace=0, width_ratios=width_ratios)[1] + aspect = 1 / aspect + else: + gs = parent.get_subplotspec().subgridspec( + 2, 1, hspace=wh_space, + height_ratios=[fraction, 1-fraction-pad]) + ss_main = gs[1] + ss_cb = gs[0].subgridspec( + 1, 3, wspace=0, width_ratios=width_ratios)[1] + aspect = 1 / aspect parent.set_subplotspec(ss_main) parent.set_anchor(loc_settings["panchor"]) diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index dbdd6b776fbb..3e9d14e4643f 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -622,3 +622,85 @@ def test_colorbar_int(clim): im = ax.imshow([[*map(np.int16, clim)]]) fig.colorbar(im) assert (im.norm.vmin, im.norm.vmax) == clim + + +def test_anchored_cbar_position_using_specgrid(): + data = np.arange(1200).reshape(30, 40) + levels = [0, 200, 400, 600, 800, 1000, 1200] + shrink = 0.5 + anchor_y = 0.3 + # right + fig, ax = plt.subplots() + cs = ax.contourf(data, levels=levels) + cbar = plt.colorbar( + cs, ax=ax, use_gridspec=True, + location='right', anchor=(1, anchor_y), shrink=shrink) + + # the bottom left corner of one ax is (x0, y0) + # the top right corner of one ax is (x1, y1) + # p0: the vertical / horizontal position of anchor + x0, y0, x1, y1 = ax.get_position().extents + cx0, cy0, cx1, cy1 = cbar.ax.get_position().extents + p0 = (y1 - y0) * anchor_y + y0 + + np.testing.assert_allclose( + [cy1, cy0], + [y1 * shrink + (1 - shrink) * p0, p0 * (1 - shrink) + y0 * shrink]) + + # left + fig, ax = plt.subplots() + cs = ax.contourf(data, levels=levels) + cbar = plt.colorbar( + cs, ax=ax, use_gridspec=True, + location='left', anchor=(1, anchor_y), shrink=shrink) + + # the bottom left corner of one ax is (x0, y0) + # the top right corner of one ax is (x1, y1) + # p0: the vertical / horizontal position of anchor + x0, y0, x1, y1 = ax.get_position().extents + cx0, cy0, cx1, cy1 = cbar.ax.get_position().extents + p0 = (y1 - y0) * anchor_y + y0 + + np.testing.assert_allclose( + [cy1, cy0], + [y1 * shrink + (1 - shrink) * p0, p0 * (1 - shrink) + y0 * shrink]) + + # top + shrink = 0.5 + anchor_x = 0.3 + fig, ax = plt.subplots() + cs = ax.contourf(data, levels=levels) + cbar = plt.colorbar( + cs, ax=ax, use_gridspec=True, + location='top', anchor=(anchor_x, 1), shrink=shrink) + + # the bottom left corner of one ax is (x0, y0) + # the top right corner of one ax is (x1, y1) + # p0: the vertical / horizontal position of anchor + x0, y0, x1, y1 = ax.get_position().extents + cx0, cy0, cx1, cy1 = cbar.ax.get_position().extents + p0 = (x1 - x0) * anchor_x + x0 + + np.testing.assert_allclose( + [cx1, cx0], + [x1 * shrink + (1 - shrink) * p0, p0 * (1 - shrink) + x0 * shrink]) + + # bottom + shrink = 0.5 + anchor_x = 0.3 + fig, ax = plt.subplots() + cs = ax.contourf(data, levels=levels) + cbar = plt.colorbar( + cs, ax=ax, use_gridspec=True, + location='bottom', anchor=(anchor_x, 1), shrink=shrink) + + # the bottom left corner of one ax is (x0, y0) + # the top right corner of one ax is (x1, y1) + # p0: the vertical / horizontal position of anchor + x0, y0, x1, y1 = ax.get_position().extents + cx0, cy0, cx1, cy1 = cbar.ax.get_position().extents + p0 = (x1 - x0) * anchor_x + x0 + + np.testing.assert_allclose( + [cx1, cx0], + [x1 * shrink + (1 - shrink) * p0, p0 * (1 - shrink) + x0 * shrink])