HokieWx
HokieWx

Reputation: 37

How to add a legend to cartopy plot?

I'm currently working with some data that involves the land use type (i.e., forested, desert, water, etc.) that comes up as different colors on my cartopy maps. I want to add a legend or equivalent that shows which land type each color represents, but can't seem to get it to work. The land use type data plotting on top of the cartopy map requires me using pcolormesh. Is there any way to get a legend of sorts onto the cartopy plot that shows which color each land use type is?

Below is my cartopy portion of my code:

proj = ccrs.LambertConformal(central_longitude=cLon, central_latitude=cLat)
res = '10m'
fig_1 = plt.figure(figsize=(20,20))
ax_1 = plt.subplot(1,1,1,projection=proj)
ax_1.set_extent([lonW,lonE,latS,latN])
ax_1.add_feature(cfeature.LAND.with_scale(res))
ax_1.add_feature(cfeature.OCEAN.with_scale(res))
ax_1.add_feature(cfeature.COASTLINE.with_scale(res))
ax_1.add_feature(cfeature.LAKES.with_scale(res), alpha = 0.5)
ax_1.add_feature(cfeature.STATES.with_scale(res));
ax_1.set_title('Land Use Type: Western United States', fontsize=20)
ax_1.pcolormesh(cat_land_lons, cat_land_lats, cat_land, transform=ccrs.PlateCarree(), zorder=3)
ax_1.legend(['Forested', 'Water', 'Desert', 'Farmland', 'Urban'])

Upvotes: 0

Views: 1789

Answers (1)

Rutger Kassies
Rutger Kassies

Reputation: 64463

It difficult to be specific without knowing what your data looks like. But given some example Landcover data and assigned colors. Things might be a little different if your LCC data is not consecutive like this.

lc_colors = {
    'Forested': "g", # value=0
    'Water': "b",    # value=1
    'Desert': "y",   # value=2
    'Farmland': "c", # value=3
    'Urban': "r",    # value=4
}

yy, xx = np.mgrid[35:45:1, -120:-110:1]
zz = np.random.randint(0, len(lc_colors), xx.shape)

You can define a colormap and normalizer:

cmap = mpl.colors.LinearSegmentedColormap.from_list("lcc", list(lc_colors.values()))

bounds = np.arange(len(lc_colors)+1)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)

Using proxy artists for the legend:

import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.patches as mpatches
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import numpy as np

proj = ccrs.LambertConformal(central_longitude=-110, central_latitude=40)

fig, ax = plt.subplots(figsize=(6,6), facecolor="w", dpi=86, subplot_kw=dict(projection=proj))
ax.set_title('Land Use Type: Western United States', fontsize=15)

im = ax.pcolormesh(xx, yy, zz, transform=ccrs.PlateCarree(), cmap=cmap, norm=norm)
ax.set_extent([-125, -95, 30, 50], crs=ccrs.PlateCarree())

res = '110m'
ax.add_feature(cfeature.LAND.with_scale(res))
ax.add_feature(cfeature.OCEAN.with_scale(res))
ax.add_feature(cfeature.COASTLINE.with_scale(res))
ax.add_feature(cfeature.LAKES.with_scale(res), alpha=0.5)
ax.add_feature(cfeature.STATES.with_scale(res), lw=0.5, alpha=.5)

labels, handles = zip(*[(k, mpatches.Rectangle((0, 0), 1, 1, facecolor=v)) for k,v in lc_colors.items()])
ax.legend(handles, labels, loc=4, framealpha=1)

enter image description here

Or instead of using the last two lines from the snippet above, use the result from pcolormesh with a colorbar:

cb = fig.colorbar(im, ax=ax, shrink=.3, aspect=8)
cb.set_ticks(bounds[:-1]+0.5)
cb.set_ticklabels(lc_classes.keys())

enter image description here

Using a proper legend with the proxy artists is probably better from a dataviz perspective, since a colorbar might suggest some sort of order to the classes.

Upvotes: 2

Related Questions