1

I have found some elevation data as a matrix. I plotted the data with a contourf plot with matplotlib. This looks like top left of the figure. I would like to reuse these contours later in other parts of the code. At some point, I want to redraw only one given contour at a time. For that, I use the simple plt.fill function and take the polygons from the contours segments. Top right is the first contour, bottom left is second one, and bottom right is third... etc. Now you see the issue. It seems that the polygons that are given as output from plt.contourf are not closed. Or something else happens but I'm not sure what. Do you have any idea how I could solve this?

The code I use is given here:

import matplotlib.pyplot as plt
import matplotlib.cm as cm
    
%matplotlib inline

im = Image.open('someImage.tif')

imTif = np.array(im)     
im_df = pd.DataFrame(imTif)

Here I process a bit the image I have, but this doesn't matter much. In the end I just have a matrix of numbers

lenY, lenX = im_df.shape
fig1 = plt.figure()
ax1 = fig1.add_subplot(2,2,1)
contour_set = ax1.contourf(np.arange(0,lenX), -np.arange(0,lenY), im_df, cmap='terrain', \
                          levels=levels)
plt.colorbar(contour_set)

xi, xf = ax1.get_xlim()
yi, yf = ax1.get_ylim()

cnorm = plt.Normalize(vmin=levels[0],vmax=levels[-1])
clevels = [levels[0]] + list(0.5*(levels[1:]+levels[:-1])) + [levels[-1]]
colors = plt.cm.terrain(cnorm(clevels))

segments = contour_set.allsegs

for contour_nb in range(0,3):
    ax = fig1.add_subplot(2,2,2+contour_nb)
    for polygon in segments[contour_nb]:
        xs, ys = zip(*polygon)
        ax.fill(xs,ys,color=colors[contour_nb+1])
    
    ax.set_xlim(xi, xf)
    ax.set_ylim(yi, yf)

enter image description here

mwoua
  • 403
  • 5
  • 13

1 Answers1

0

I too experienced this problem, which can be understood by also examining contour_set.allkinds. contour_set.allsegs contains all the coordinates that enclose a region. This includes holes! contour_set.allkinds describes what role each coordinate fulfills (start, line or end-point). Using fill works for objects with one hole, but for objects with multiple interiors you will need to try a different approach. Contourf uses path collections:

   import matplotlib.pyplot as plt
   import matplotlib.collections as col
   plt.style.use('seaborn-white')
   import numpy as np

   # Generate data
   # Obtained from:https://jakevdp.github.io/PythonDataScienceHandbook/04.04-density-and-contour-plots.html
   def f(x, y):
       return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)

   x = np.linspace(0, 5, 50)
   y = np.linspace(0, 5, 40)
   X, Y = np.meshgrid(x, y)
   Z = f(X, Y)

   # Filled contour plot
   fig, ax = plt.subplots(1,2, figsize=(10,5))
   contours = ax[0].contourf(X, Y, Z)

   # Replicate filled contour plot
   for i in range(8):
     paths = contours.collections[i].get_paths()
     fc = contours.axes.collections[i].properties().get('facecolor')
     for p in paths:
       test = col.PathCollection([p], facecolors=fc)
       ax[1].add_collection(test)

   ax[1].set_xlim([0, 5])
   ax[1].set_ylim([0, 5]);

Note that the path collections from contourf are grouped per contour value. Specifying a specific contour can be done by selecting it after .get_paths().

See plot