Hi all
is there a way to add a scale bar in a corner of the plots ?
Thanks
Tom
Hi @ThomasLecocq. There's no simple way to do this right now. If there were, would it need to be interactive, or would a static version be OK to start with?
I'd say a static version would be a great start.. :)
I think @pp-mo might have looked at this.
I think @pp-mo might have looked at this.
Found it !
(wasn't easy)
But it is quite clunky, and also rather old...
Take a look here : http://nbviewer.ipython.org/github/pp-mo/iris_example_code/blob/cartopy_scalebar/map_scalebar.ipynb
:+1:
I'd really like to see something like this too.
anybody working on this feature?
There's an existing library for adding a scalebar to matplotlib that might be of help: https://pypi.python.org/pypi/matplotlib-scalebar.
This function may be useful:
# plot a scale bar with 4 subdivisions on the left side of the map
def scale_bar_left(ax, bars=4, length=None, location=(0.1, 0.05), linewidth=3, col='black'):
"""
ax is the axes to draw the scalebar on.
bars is the number of subdivisions of the bar (black and white chunks)
length is the length of the scalebar in km.
location is left side of the scalebar in axis coordinates.
(ie. 0 is the left side of the plot)
linewidth is the thickness of the scalebar.
color is the color of the scale bar
"""
# Get the limits of the axis in lat long
llx0, llx1, lly0, lly1 = ax.get_extent(ccrs.PlateCarree())
# Make tmc aligned to the left of the map,
# vertically at scale bar location
sbllx = llx0 + (llx1 - llx0) * location[0]
sblly = lly0 + (lly1 - lly0) * location[1]
tmc = ccrs.TransverseMercator(sbllx, sblly)
# Get the extent of the plotted area in coordinates in metres
x0, x1, y0, y1 = ax.get_extent(tmc)
# Turn the specified scalebar location into coordinates in metres
sbx = x0 + (x1 - x0) * location[0]
sby = y0 + (y1 - y0) * location[1]
# Calculate a scale bar length if none has been given
# (Theres probably a more pythonic way of rounding the number but this works)
if not length:
length = (x1 - x0) / 5000 # in km
ndim = int(np.floor(np.log10(length))) # number of digits in number
length = round(length, -ndim) # round to 1sf
# Returns numbers starting with the list
def scale_number(x):
if str(x)[0] in ['1', '2', '5']:
return int(x)
else:
return scale_number(x - 10 ** ndim)
length = scale_number(length)
# Generate the x coordinate for the ends of the scalebar
bar_xs = [sbx, sbx + length * 1000 / bars]
# Plot the scalebar chunks
barcol = 'white'
for i in range(0, bars):
# plot the chunk
ax.plot(bar_xs, [sby, sby], transform=tmc, color=barcol, linewidth=linewidth)
# alternate the colour
if barcol == 'white':
barcol = 'dimgrey'
else:
barcol = 'white'
# Generate the x coordinate for the number
bar_xt = sbx + i * length * 1000 / bars
# Plot the scalebar label for that chunk
ax.text(bar_xt, sby, str(round(i * length / bars)), transform=tmc,
horizontalalignment='center', verticalalignment='bottom',
color=col)
# work out the position of the next chunk of the bar
bar_xs[0] = bar_xs[1]
bar_xs[1] = bar_xs[1] + length * 1000 / bars
# Generate the x coordinate for the last number
bar_xt = sbx + length * 1000
# Plot the last scalebar label
ax.text(bar_xt, sby, str(round(length)), transform=tmc,
horizontalalignment='center', verticalalignment='bottom',
color=col)
# Plot the unit label below the bar
bar_xt = sbx + length * 1000 / 2
bar_yt = y0 + (y1 - y0) * (location[1] / 4)
ax.text(bar_xt, bar_yt, 'km', transform=tmc, horizontalalignment='center',
verticalalignment='bottom', color=col)
I wrote something more flexible and accurate using the new geodesic module: https://stackoverflow.com/a/50674451/2676166 . I'm modifying it to allow alternating colours like most scale bars have, and a few other things. When it's working I'll make a pull request.
Most helpful comment
I wrote something more flexible and accurate using the new geodesic module: https://stackoverflow.com/a/50674451/2676166 . I'm modifying it to allow alternating colours like most scale bars have, and a few other things. When it's working I'll make a pull request.