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.