Question

I'm trying to produce a plot which uses both contour and contourf where they both use the same colormap, plotting the same data. However, contourf is only used to plot data which is 'significant' in some way (using a masked array). Meanwhile, contour is used to plot all the data. The aim is to produce a plot where all the information is available, but the eye is drawn to the more important areas.

I nearly have this working as I would like, but I am finding that the color of the contour lines is slightly different from the color of the filled contours from contourf.

I'm guessing that the differences come from the fact that either the contour colors are actually half-way between the contour colors (which would make sense, as the contour lines are defined at a value, e.g. 1, 2, etc, and the filled contours are between 1 and 2, i.e. with a 'value' of 1.5 etc).

I am defining my colormap as

cmap = cm.coolwarm
cnorm=clrs.Normalize(cmap,clip=False)
cmap.set_under(color=cmap(0.0),alpha=1.0)
cmap.set_over(color=cmap(1.0),alpha=1.0)

my contour levels, used for both contour and contourf are

clevs = [-3.,-2.,-1.,1.,2.,3.]

The contour lines are plotted as

cplot=map.contour(x,y,diff,clevs,\
                      cmap=cmap,\
                      norm=cnorm,\
                      extend='both')

and the filled contours are plotted as

cplot=map.contourf(x,y,true_mask,clevs,cmap=cmap,\
                       norm=cnorm,
                       extend='both')

Is there a straight-forward way to have the colors of the contour lines 'match' those of the filled contours, i.e. the line at 1 is the color of the 1-2 filled contour, the line at 2 is the color of the 2-3 filled contour, the line at -1 have the color of the -2--1 filled contour etc.?

Many thanks for your help.

Was it helpful?

Solution

I think that one possible solution here is to create a new colormap for the call to contour. If the original colormap is defined as

cmap = matplotlib.cm.coolwarm
cnorm=matplotlib.colors.Normalize(cmap,clip=False)
cmap.set_under(color=cmap(0.0),alpha=1.0)
cmap.set_over(color=cmap(1.0),alpha=1.0)

and the contour levels to plot as

clevs = [-3.,-2.,-1.,1.,2.,3.]

then the new colormap can be created by

cw=matplotlib.cm.get_cmap('coolwarm',8*(len(clevs)+1))
cw_vals=cw(np.arange(8*(len(clevs)+1)))
new_cw_vals=np.zeros([len(clevs),cw_vals.shape[1]],dtype=np.float128)
new_cw_vals_t = np.float128(cw_vals[4::8,:])
new_cw_vals_b = np.float128(cw_vals[12::8,:])
for i in np.arange(new_cw_vals.shape[0]):
    if clevs[i] < 0.0:
        new_cw_vals[i,:]=np.float32(new_cw_vals_t[i,:])
    else:
        new_cw_vals[i,:]=np.float32(new_cw_vals_b[i,:])
newcmap = matplotlib.colors.LinearSegmentedColormap.from_list("newcw", new_cw_vals)
newcnorm=matplotlib.colors.Normalize(newcmap,clip=False)

I had to put a shift in to allow for the fact that I'm not plotting the 0 line.

which is then used in the call to contour

cplot=map.contour(x,y,diff,clevs,
                      cmap=newcmap,
                      norm=newcnorm,
                      vmin=vmin,vmax=vmax)

Essentially I am creating a colormap with 8 times the number of points, and then picking out the mid-points. It isn't quite perfect though, the colors are ever-so-slightly off from the colors from contourf. This may be down to rounding differences (the colormap values seem to be float32). It is also a bit specific to the values used in clevs, although it could easily be changed for other values.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top