Search code examples
python-3.xlabelcartopygridlines

specify the lat/lon label location in cartopy (remove at some sides)


The new capability in Cartopy 0.18.0 to add lat/lon labels for any map projection is excellent. It's a great addition to this package. For some maps, especially in polar regions, the lat/lon labels can be very crowded. Here is an example.

from matplotlib import pyplot as plt
import numpy as np
import cartopy.crs as ccrs

pcproj = ccrs.PlateCarree()
lon0 = -150
mapproj = ccrs.LambertAzimuthalEqualArea(
    central_longitude=lon0,central_latitude=75,
    )
XLIM = 600e3; YLIM=700e3
dm =5; dp=2

fig = plt.figure(0,(7,7))
ax  = fig.add_axes([0.1,0.1,0.85,0.9],projection=mapproj)
ax.set_extent([-XLIM,XLIM,-YLIM,YLIM],crs=mapproj)
ax.coastlines(resolution='50m',color='.5',linewidth=1.5)
lon_grid = np.arange(-180,181,dm)
lat_grid = np.arange(-80,86,dp)
gl = ax.gridlines(draw_labels=True,
                  xlocs=lon_grid,ylocs=lat_grid,
                  x_inline=False,y_inline=False,
                  color='k',linestyle='dotted')
gl.rotate_labels = False

Here is the output plot: I can't embed image yet, so here is the link

What I am looking for is to have lat labels on the left and right sides and lon labels at the bottom, with no labels at the top. This can be easily done in Basemap using a list of flags. I am wondering if this is possible with cartopy now. Several failed attempts:

  1. I came across a Github open issue for cartopy on a similar topic, but the suggested method does not work for this case. Adding gl.ylocator = mticker.FixedLocator(yticks) does nothing and adding gl.xlocator = mticker.FixedLocator(xticks) gets rid of most of lon labels except the 180 line on left and right sides but all the other lon labels are missing. The 80N lat label is still on the top, see here. After a more careful read of that thread, it seems it is still an ongoing effort for future cartopy releases.
  2. Using gl.top_labels=False does not work either.
  3. Setting y_inline to True makes the lat labels completely gone. I guess this might be because of axes extent I used. The lat labels might be on some longitude lines outside of the box. This is a separate issue, about how to specify the longitude lines/locations of the inline labels.

Right now, I have chosen to turn off the labels. Any suggestions and temporary solutions will be appreciated. At this point, the maps such as the examples above are useful for quicklooks but not ready for any formal use.

UPDATE: Based on @swatchai 's suggestion, there is a temporary workaround below:

# --- add _labels attribute to gl
plt.draw()

# --- tol is adjusted based on the positions of the labels relative to the borders.
tol = 20
for ea in gl._labels:
    pos = ea[2].get_position()
    t_label = ea[2].get_text()
    # --- remove lon labels on the sides
    if abs(abs(pos[0])-XLIM)<tol:
        if 'W' in t_label or 'E' in t_label or '180°' in t_label:
            print(t_label)
            ea[2].set_text('')
    # --- remove labels on top 
    if abs(pos[1]-YLIM)<tol:
        ea[2].set_text('')

This is almost what I wanted except that the 74N labels are missing because it is close to the 170W labels on the sides and cartopy chose 170W label instead of 74N. So I need a little more simple tweaks to put it back there.


Solution

  • This could be a workaround for your project until a better solution comes up.

    # more code above this line
    
    # this suppresses drawing labels on top edges
    # only longitude_labels disappear, some latitude_labels persist
    gl.top_labels=False
    
    # workaround here to manipulate the remaining labels
    plt.draw()  #enable the use of ._lables()
    for ea in gl._labels:
        #here, ea[2] is a Text object
        #print(ea)
        if '80°N'==ea[2].get_text():
            # set it a blank string
            ea[2].set_text("")
    
    ax.set_title("No Labels on Top Edge");
    plt.show()
    

    The output plot:

    outputplot