Search code examples
matplotlibcartopy

Adding gridlines using Cartopy


I'm trying to add gridlines to a map I made using Cartopy, however, when I use the example code from the cartopy documentation, it doesn't display what I want and I can't figure out how to manipulate it to do so.

def plotMap():

    proj = ccrs.Mercator(central_longitude=180, min_latitude=15, 
    max_latitude=55)

    fig, ax = plt.subplots(subplot_kw=dict(projection=proj), figsize=(12,12))

    ax.set_extent([255 ,115, 0, 60], crs=ccrs.PlateCarree())

    ax.add_feature(cfeature.LAND, facecolor='0.3')
    ax.add_feature(cfeature.LAKES, alpha=0.9)  
    ax.add_feature(cfeature.BORDERS, zorder=10)
    ax.add_feature(cfeature.COASTLINE, zorder=10)


    #(http://www.naturalearthdata.com/features/)
    states_provinces = cfeature.NaturalEarthFeature(
            category='cultural',  name='admin_1_states_provinces_lines',
            scale='50m', facecolor='none')
    ax.add_feature(states_provinces, edgecolor='black', zorder=10)

    #ax.gridlines(xlocs=grids_ma, ylocs=np.arange(-80,90,20), zorder=21, 
    draw_labels=True ) 
    ax.gridlines(crs=ccrs.PlateCarree(), linewidth=2, color='black', 
    draw_labels=True, alpha=0.5, linestyle='--')
    ax.xlabels_top = False
    ax.ylabels_left = False
    ax.ylabels_right=True
    ax.xlines = True
    ax.xlocator = mticker.FixedLocator([-160, -140, -120, 120, 140, 160, 180,])
    ax.xformatter = LONGITUDE_FORMATTER
    ax.yformatter = LATITUDE_FORMATTER
    ax.xlabel_style = {'size': 15, 'color': 'gray'}
    ax.xlabel_style = {'color': 'red', 'weight': 'bold'}


    return fig, ax

I've attached a picture of the output. For reference, I only want the longitude gridlines to start at the left of my domain and end at the right side, preferably being spaced every 20 degrees. Ideally the same for latitude lines as well. Bad gridline plot


Solution

  • Update: Cartopy's gridliner dateline issue noted below has been fixed at some point, so either of the below code solutions should now give the desired outcome.

    Is the example you are following the one at the bottom of this page? If so, you are attempting to set attributes on the GeoAxes (ax) instance which should be set on the GridLiner (gl) instance:

    import cartopy
    import cartopy.crs as ccrs
    import cartopy.feature as cfeature
    import matplotlib.pyplot as plt
    import matplotlib.ticker as mticker
    
    from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
    
    def plotMap():    
        proj = ccrs.Mercator(central_longitude=180, min_latitude=15, 
        max_latitude=55)
    
        fig, ax = plt.subplots(subplot_kw=dict(projection=proj), figsize=(12,12))
    
        ax.set_extent([255 ,115, 0, 60], crs=ccrs.PlateCarree())
    
        ax.add_feature(cfeature.LAND, facecolor='0.3')
        ax.add_feature(cfeature.LAKES, alpha=0.9)  
        ax.add_feature(cfeature.BORDERS, zorder=10)
        ax.add_feature(cfeature.COASTLINE, zorder=10)
    
        states_provinces = cfeature.NaturalEarthFeature(
                category='cultural',  name='admin_1_states_provinces_lines',
                scale='50m', facecolor='none')
        ax.add_feature(states_provinces, edgecolor='black', zorder=10)   
        
        gl = ax.gridlines(crs=ccrs.PlateCarree(), linewidth=2, color='black', alpha=0.5, linestyle='--', draw_labels=True)
        gl.xlabels_top = False
        gl.ylabels_left = False
        gl.ylabels_right=True
        gl.xlines = True
        gl.xlocator = mticker.FixedLocator([120, 140, 160, 180, -160, -140, -120])
        gl.ylocator = mticker.FixedLocator([0, 20, 40, 60])
        gl.xformatter = LONGITUDE_FORMATTER
        gl.yformatter = LATITUDE_FORMATTER
        gl.xlabel_style = {'color': 'red', 'weight': 'bold'}
    

    This produces the following map. The gridliner doesn't seem to be coping with the dateline. I do not know if there is a way around this, but there is a note at the top of the above linked documentation to say that there are currently known limitations with this class, so maybe not.

    enter image description here

    An alternative is to set the various labels and their styles directly with matplotlib. Note that you have to set the ticklabels separately from the ticks, otherwise you get labels corresponding to the Mercator coordinate reference system:

    import cartopy.mpl.ticker as cticker
    
    def plotMap2():
        proj = ccrs.Mercator(central_longitude=180, min_latitude=15, 
        max_latitude=55)
    
        fig, ax = plt.subplots(subplot_kw=dict(projection=proj), figsize=(12,12))
    
        ax.set_extent([255 ,115, 0, 60], crs=ccrs.PlateCarree())
    
        ax.add_feature(cfeature.LAND, facecolor='0.3')
        ax.add_feature(cfeature.LAKES, alpha=0.9)  
        ax.add_feature(cfeature.BORDERS, zorder=10)
        ax.add_feature(cfeature.COASTLINE, zorder=10)
    
        states_provinces = cfeature.NaturalEarthFeature(
                category='cultural',  name='admin_1_states_provinces_lines',
                scale='50m', facecolor='none')
        ax.add_feature(states_provinces, edgecolor='black', zorder=10)
    
        ax.set_xticks([120., 140., 160., 180., -160., -140., -120.], crs=ccrs.PlateCarree())
        ax.set_xticklabels([120., 140., 160., 180., -160., -140., -120.], color='red', weight='bold')
        ax.set_yticks([20, 40], crs=ccrs.PlateCarree())
        ax.set_yticklabels([20, 40])
        ax.yaxis.tick_right()
        
        lon_formatter = cticker.LongitudeFormatter()
        lat_formatter = cticker.LatitudeFormatter()
        ax.xaxis.set_major_formatter(lon_formatter)
        ax.yaxis.set_major_formatter(lat_formatter)
        ax.grid(linewidth=2, color='black', alpha=0.5, linestyle='--')
    

    enter image description here