-
Notifications
You must be signed in to change notification settings - Fork 368
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Scale bar #490
Comments
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. |
Found it ! |
👍 |
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. |
The pull request looks to be nearly ready... seems like it might be coming soon? |
Dear Theroggy, which Pull Request are you referring to? If you see some missing attributes, or potential errors, let us know so we may help solve them, once for all. Sincerely, |
Hey PhilipeRLeal, I was referring to #1728 |
Hey all, since there has not been a lot of progress on this in the last year, I thought I mention what I've created for EOmaps (... an interactivity-layer on top of matplotlib/cartopy I've been developing for quite some time now) Initially I've tried to have a look at the existing pull-requet (#1728) but since it shows 28 changed files I got lost and did not investigate this any further... I'm not sure what the status of #1728 is and whether or not it is feasible to propose a completely new implementation... anyway, here's what I have:
The scalebar supports the following features:
... and here's how it looks like: |
Dear all, I would like to know whether anyone needs any help or can give some support for adhering to RaphaelQuast's solution. Sincerely, |
In general, it looks great. Without even a draft PR, I can't evaluate what the consequences of trying to add it would be. |
Before any attempt to merge the two packages (or at least the scalebar functionality into cartopy's), I believe that one must decide how the scalebar should be provided to the user. There seem to be two possibilities here: a) the scalebar will be added as an extension module to cartopy, and, therefore, one would have to import it directly from cartopy package, and instantiate the class with respect to an already instantiated matplotlib.geoaxes object; b) the scalebar instance would be instantiated from a matplotlib.geoaxes extention method, therefore, directly from the geoaxes instance itself. From a Geographic Information System (GIS) point of view, the second approach (option b) seems more logical; after all, a scalebar is an object linked (or makes reference) to a map (in this case, the matplotlib.geoaxes), and not the other way around. Assuming that this option (b) is the one to go, I believe that the scalebar integration into cartopy's package will be straightforward if the SOLID principles are followed correctly. Please, let me know your thoughts in this regard. |
Option (b) sounds perfect. Simpler and probably easier to use for random people like me. I hope my post revives the discussion (I haven't found a scalebar in cartopy yet...) |
This package seems to work well for simple cases where you can specify the pixel size in SI units, and it adapts when you zoom in to the plot https://github.com/ppinard/matplotlib-scalebar |
Hi all
is there a way to add a scale bar in a corner of the plots ?
Thanks
Tom
The text was updated successfully, but these errors were encountered: