Skip to content

Instantly share code, notes, and snippets.

@dmeliza
Last active February 7, 2023 00:33
Show Gist options
  • Save dmeliza/3251476 to your computer and use it in GitHub Desktop.
Save dmeliza/3251476 to your computer and use it in GitHub Desktop.
matplotlib: add scale bars to axes
# -*- coding: utf-8 -*-
# -*- mode: python -*-
# Adapted from mpl_toolkits.axes_grid1
# LICENSE: Python Software Foundation (http://docs.python.org/license.html)
from matplotlib.offsetbox import AnchoredOffsetbox
class AnchoredScaleBar(AnchoredOffsetbox):
def __init__(self, transform, sizex=0, sizey=0, labelx=None, labely=None, loc=4,
pad=0.1, borderpad=0.1, sep=2, prop=None, barcolor="black", barwidth=None,
**kwargs):
"""
Draw a horizontal and/or vertical bar with the size in data coordinate
of the give axes. A label will be drawn underneath (center-aligned).
- transform : the coordinate frame (typically axes.transData)
- sizex,sizey : width of x,y bar, in data units. 0 to omit
- labelx,labely : labels for x,y bars; None to omit
- loc : position in containing axes
- pad, borderpad : padding, in fraction of the legend font size (or prop)
- sep : separation between labels and bars in points.
- **kwargs : additional arguments passed to base class constructor
"""
from matplotlib.patches import Rectangle
from matplotlib.offsetbox import AuxTransformBox, VPacker, HPacker, TextArea, DrawingArea
bars = AuxTransformBox(transform)
if sizex:
bars.add_artist(Rectangle((0,0), sizex, 0, ec=barcolor, lw=barwidth, fc="none"))
if sizey:
bars.add_artist(Rectangle((0,0), 0, sizey, ec=barcolor, lw=barwidth, fc="none"))
if sizex and labelx:
self.xlabel = TextArea(labelx)
bars = VPacker(children=[bars, self.xlabel], align="center", pad=0, sep=sep)
if sizey and labely:
self.ylabel = TextArea(labely)
bars = HPacker(children=[self.ylabel, bars], align="center", pad=0, sep=sep)
AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad,
child=bars, prop=prop, frameon=False, **kwargs)
def add_scalebar(ax, matchx=True, matchy=True, hidex=True, hidey=True, **kwargs):
""" Add scalebars to axes
Adds a set of scale bars to *ax*, matching the size to the ticks of the plot
and optionally hiding the x and y axes
- ax : the axis to attach ticks to
- matchx,matchy : if True, set size of scale bars to spacing between ticks
if False, size should be set using sizex and sizey params
- hidex,hidey : if True, hide x-axis and y-axis of parent
- **kwargs : additional arguments passed to AnchoredScaleBars
Returns created scalebar object
"""
def f(axis):
l = axis.get_majorticklocs()
return len(l)>1 and (l[1] - l[0])
if matchx:
kwargs['sizex'] = f(ax.xaxis)
kwargs['labelx'] = str(kwargs['sizex'])
if matchy:
kwargs['sizey'] = f(ax.yaxis)
kwargs['labely'] = str(kwargs['sizey'])
sb = AnchoredScaleBar(ax.transData, **kwargs)
ax.add_artist(sb)
if hidex : ax.xaxis.set_visible(False)
if hidey : ax.yaxis.set_visible(False)
if hidex and hidey: ax.set_frame_on(False)
return sb
@mac389
Copy link

mac389 commented Feb 28, 2013

This is awesome.

@esiabri
Copy link

esiabri commented Jun 5, 2014

I agree! :)
Thanks

@baldwint
Copy link

Thanks for this, I've used it for a few months now. It looks like it is adapted from AnchoredSizeBar ( in mpl_toolkits.axes_grid1.anchored_artists). That one can only make a horizontal bar, this one can make both horizontal and vertical bars.

Is axes_grid2 a real thing, or is that a typo of axes_grid1?

@dmeliza
Copy link
Author

dmeliza commented Aug 23, 2016

Glad it's been of use! axes_grid2 is a typo; thanks for catching it.

@dmeliza
Copy link
Author

dmeliza commented Sep 19, 2017

The last revision should fix issues with the bars not displaying in recent versions of matplotlib. Plus now you can customize color and width

@NickleDave
Copy link

Hi @dmeliza thank you for sharing this -- it's helpful!

Not sure if you're updating this but thought I'd let you know that minimumdescent (like in line 32) is now deprecated:
https://matplotlib.org/stable/api/api_changes.html#minimumdescent-parameter-property-of-textarea

Looks like it won't affect this since you set it to False anyway, but removing the parameter avoids a warning.

@dmeliza
Copy link
Author

dmeliza commented Oct 6, 2021

Thanks! Have not been using this myself any more, but appreciate the fix should I need to dust off my matplotlib skillz. Removed the offending parameter.

@NickleDave
Copy link

Excellent, thanks!

@arashgmn
Copy link

Amazing! Thanks, @dmeliza!

@KisungShin-koreaUniv
Copy link

AWESOME! Thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment