0

I am trying to create a graphic where I overlay multiple contour plots on a single image. So I want to have colorbars for each of the plots, as well as a legend indicating what each contour represents. However Matplotlib will not allow me to create a separate legend for my contour plots. Simple example:

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



def create_contour(i,j):
    colors = ["red","green","blue"]
    hatches = ['-','+','x','//','*']
    fig = plt.figure()
    ax = plt.axes(projection=ccrs.PlateCarree())
    ax.set_extent((-15.0,15.0,-15.0,15.0))
    delta = 0.25
    x = np.arange(-3.0,3.0,delta)
    y = np.arange(-2.0,2.0,delta)
    X, Y = np.meshgrid(x, y)
    data = np.full(np.shape(X), 1.0)
    plot = ax.contourf(X,Y,data, levels = [float(i),float(i+1)], hatch=[hatches[j]], colors = colors[i], label="label")
    plt.legend(handles=[plot], labels=["label"])
    plt.savefig("figure_"+str(i)+".png")

create_contour(1,3)

When I run this, I get the following message:

UserWarning: Legend does not support (matplotlib.contour.QuadContourSet object at 0x7fa69df7cac8) instances. A proxy artist may be used instead. See: http://matplotlib.org/users/legend_guide.html#creating-artists-specifically-for-adding-to-the-legend-aka-proxy-artists "aka-proxy-artists".format(orig_handle)

But as far as I can tell, I am following those directions as closely as possible, the only difference being that they do not use contourf in the example.

Any help would be greatly appreciated.

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
D. Lef
  • 239
  • 3
  • 13
  • A contourf plot consists of several contoured regions. It is hence not suitable for a single legend entry, or at least it's not obvious how such legend would look like. Can you describe the kind of legend you have in mind? Is it three rectangles (red, green blue)? In that case, the first proxy artist would be a rectangle `plt.Rectangle((0,0),1,1, color="red")` and so on. Or do you want a single legend entry consisting of all 3 rectangles? Or other shapes? – ImportanceOfBeingErnest Dec 07 '18 at 19:17
  • For example, I might give each set of contours a particular hatch, then the legend entry for each might ultimately consist of a square with the hatch over a white background. As a preliminary step, I was trying to make a single layer contour and create the legend, as that is the part that I currently can't do. Does that make more sense? Thank you – D. Lef Dec 07 '18 at 19:45
  • So, `plt.Rectangle((0,0),1,1, hatch="\\")`? – ImportanceOfBeingErnest Dec 07 '18 at 20:03
  • The data will not necessarily be rectangular. – D. Lef Dec 07 '18 at 20:06
  • 1
    So the legend should have the same shape as the contour? (Like [this](https://i.stack.imgur.com/MxOXg.png)?) That would be more complicated, but still possible. – ImportanceOfBeingErnest Dec 07 '18 at 20:09
  • I've updated the example a bit. So the idea here is that the contour can be colored and hatched independently by the function that creates it. I want the legend to show which hatch corresponds to which set of contours. – D. Lef Dec 07 '18 at 21:55
  • Yes, then it's as easy as I initially proposed. Use `plt.Rectangle((0,0),1,1, hatch=hatches[j])` as proxy-artist. – ImportanceOfBeingErnest Dec 07 '18 at 21:59

1 Answers1

3

The comments to your question look like they have solved the question (by making custom patches and passing those through to the legend). There is also an example that I added many years ago to the matplotlib documentation to do something similar (about the same time I added contour hatching to matplotlib): https://matplotlib.org/examples/pylab_examples/contourf_hatching.html#pylab-examples-contourf-hatching

It is such a reasonable request that there is even a method on the contour set to give you legend proxies out of the box: ContourSet.legend_elements.

So your example might look something like:

%matplotlib inline

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


fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection=ccrs.PlateCarree())
ax.coastlines('10m')

y = np.linspace(40.0, 60.0, 30)
x = np.linspace(-10.0, 10.0, 40)
X, Y = np.meshgrid(x, y)
data = 2*np.cos(2*X**2/Y) - np.sin(Y**X)

cs = ax.contourf(X, Y, data, 3,
                 hatches=['//','+','x','o'],
                 alpha=0.5)
artists, labels = cs.legend_elements()

plt.legend(handles=artists, labels=labels)

plt.show()

enter image description here

pelson
  • 21,252
  • 4
  • 92
  • 99