[GIS] Adding custom names to a GeoPandas legend

geopandaslegendmatplotlibpython

I have a shapefile which has an attribute table with a column I would like to make a map/plot of. The attribute values are numerical (integer). I have made two dicts to map the colors and names I want to these integers.

Palette = {0: 'black',
       20: '#FFBB22',
       30: '#FFFF4C',
       40: '#F096FF',
       80: '#0032C8',
       90: '#0096A0',
       112: '#009900',
       114: '#00CC00',
       116: '#007800',
       124: '#A0DC00',
       126:'#648C00'}

names  = {0: 'NAN',
       20: 'Shrubs',
       30: 'Herbaceous',
       40: 'Cultivated',
       80: 'Permanent Water',
       90: 'Herbaceous Wetland',
       112: 'Closed Forest: Evergreen',
       114: 'Closed Forest: Deciduous broad leaf',
       116: 'Closed forest: Other',
       124: 'Open forest: Deciduous broad leaf',
       126:'Open forest: Other'}

However, while I can map the colors to the right values, I cannot get the legend to show the right names. The legend comes up empty and I get a message that No handles with labels found to put in legend.

My code is:

fig, ax = plt.subplots(figsize=(5, 5))

# Loop through each attribute value and assign each
# with the correct color & width specified in the dictionary
for ctype, data in map_df.groupby('landcovermode'):
    color = Palette[ctype]
    label = names[ctype]
    data.plot(color=color,
      ax=ax,
      label=label,legend=True)


# Place legend in the lower right hand corner of the plot
ax.legend(loc='lower right',
  fontsize=15,
  frameon=True)

ax.set_axis_off()

How do I get the legend to read my labels from the dict?

Best Answer

As discussed here, matplotlib doesn't support the automatic creation of legend handlers for polygons. Your code works fine when using Point or LineString geometries. The solution would be to create a custom legend handle (ref), which could be done the following way:

from matplotlib.patches import Patch

fig, ax = plt.subplots(figsize=(5,5))
pmarks = []
for ctype, data in map_df.groupby('landcovermode'):
    color = Palette[ctype]
    label = names[ctype]
    data.plot(ax=ax, color=color)
    pmarks.append(Patch(facecolor=color, label=label))

handles, _ = ax.get_legend_handles_labels()
ax.legend(handles=[*handles,*pmarks], loc='lower right')

ax.set_axis_off()

This will be the output using some random test data:

polygons_with_legend

Related Question