cartopy
cartopy copied to clipboard
Adding tick marks to plot axes
Is there an easy way to have tick marks shown on plot axes, without needing to specify the tick positions every time (e.g. in code where you might plot data for different world regions and so can't hard code the tick positions)? I think this is important to have, particularly when making multi-panel figures and not wanting to put the tick labels on all of them to reduce whitespace - then tick marks allow easy reading of the axis values on each panel.
In cartopy 0.20.1, the following gets me a plot with axes with no tick marks:
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import numpy as np
proj=ccrs.PlateCarree()
fig, ax = plt.subplots(1, 1, subplot_kw={'projection':proj})
ax.coastlines()
gl=ax.gridlines(draw_labels=True)
Solutions to get the tick marks that I found by searching use ax.set_xticks and ax.set_yticks e.g.:
gl.bottom_labels=False
ax.set_xticks([-180,-120,-60,0,60,120,180])
(My tick marks point outwards since I have "xtick.direction : out" in my matplotlibrc file.)
Is there a way to get the tick marks to show without needing to specify what the tick values should be? I tried extracting the values from gl.xlabel_artists, but I couldn't see how to get them put in the right place with ax.set_xticks (the labels appear in the wrong places on the x-axis - I've not understood why). It would also be nice to be able to more easily keep cartopy's tick labels.
If there isn't a straightforward way to do this, this would be a very useful feature to have.
Just to note further that if using a central longitude of 180 degrees, the plotted x ticks don't come out in the correct places using axis.set_xticks with hard coded values either with the method I'm using (proceeding along similar lines to the one here - using "ccrs.PlateCarree(central_longitude=180)" in the argument to ax.set_xticks doesn't help). (Edit - I've realised that the problem here is not using cartopy.mpl.gridliner.LongitudeFormatter - see my subsequent post below.)
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import numpy as np
proj=ccrs.PlateCarree(central_longitude=180)
fig, ax = plt.subplots(1, 1, subplot_kw={'projection':proj})
ax.coastlines()
gl=ax.gridlines(draw_labels=True)
gl.bottom_labels=False
ax.set_xticks([0,60,120,180,-120,-60], crs=ccrs.PlateCarree())
I experimented a bit more and found something that seemed to work for PlateCarree projections - I've not tested it for others. I thought others may find it useful. I included 4 panels to illustrate a use case of having multiple panels with little whitespace in between, where tick marks allow coordinate values to be read off (very useful when there are enough panels to fill a whole page/screen). However, this assumes that all panels use the same axis ranges.
import cartopy.crs as ccrs
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
import matplotlib.pyplot as plt
import numpy as np
#Function for plotting tick marks
def cartopy_plot_tickmarks(ax,labels,axis):
assert axis in ['x','y']
ticks=[]
values=[float(txt.get_text().split('°')[0]) for txt in labels]
directions=[txt.get_text().split('°')[1] for txt in labels] #what comes after the degree symbol
for i,txt in enumerate(labels):
value=values[i]
if directions[i] in ['W','S']:
ticks+=[-value]
else: #for 'E', 'N' and '' (e.g. 180 degrees)
ticks+=[value]
if axis=='x':
ax.set_xticks(ticks, crs=ccrs.PlateCarree())
ax.set_xticklabels(['' for i in range(len(ticks))]) #make new ticks have blank labels to not overplot cartopy's
elif axis=='y':
ax.set_yticks(ticks, crs=ccrs.PlateCarree())
ax.set_yticklabels(['' for i in range(len(ticks))])
proj=ccrs.PlateCarree(central_longitude=45) #to show this working for a non-zero central_longitude
nrows=2
ncolumns=2
fig, axarr = plt.subplots(nrows, ncolumns, figsize=(10, 6), subplot_kw={'projection':proj})
#Making plots and adding cartopy labels to left and bottom panels
for i in range(nrows*ncolumns):
row_ind=i//ncolumns
col_ind=i%ncolumns
ax=axarr[row_ind, col_ind]
ax.coastlines()
ax.set_extent([-100,100,-45,70], proj) #just to show it working with axes different from the default
draw_labels=[]
if row_ind==nrows-1:
draw_labels+=['bottom']
if col_ind==0:
draw_labels+=['left']
gl=ax.gridlines(draw_labels=draw_labels)
gl.xlines = False #removing gridlines
gl.ylines = False
if row_ind==nrows-1 and col_ind==0: #saving gridliner that has both left and bottom labels for getting tick values below
gl_save=gl
lon_formatter = LongitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
lat_formatter = LatitudeFormatter()
ax.yaxis.set_major_formatter(lat_formatter)
#getting the cartopy tick information
plt.draw()
xlabels=gl_save.xlabel_artists
ylabels=gl_save.ylabel_artists
#adding tick marks at label locations
for i in range(nrows*ncolumns):
row_ind=i//ncolumns
col_ind=i%ncolumns
ax=axarr[row_ind, col_ind]
cartopy_plot_tickmarks(ax,xlabels,'x')
cartopy_plot_tickmarks(ax,ylabels,'y')
fig.subplots_adjust(hspace=0.05, wspace=0.05) #removing whitespace
Hi @PAGWatson, I copied exactly your script - adding only plt.show() in the end. However, the resulting plot does not have any ticks. (Tick labels are there, but no tick marks.) Debugging shows that xlabels=gl_save.xlabel_artists and yabels=gl_save.ylabel_artists return empty lists. Can you let me know which cartopy and matplotlib versions you are on so that I can try to figure out what is going on? I haven't found any other, flexible solution to get ticks without having grid lines...
@gesape Just checked it still works with cartopy 0.21.1 and matplotlib 3.7.1.