3

I'm trying to produce a similar version of this image using Python:

Original Image

I'm close but can't quite figure out how to modify a matplotlib colormap to make values <0.4 go to white. I tried masking those values and using set_bad but I ended up with a real blocky appearance, losing the nice smooth contours seen in the original image.

  • Result with continuous colormap (problem: no white):

    Image with all colors

  • Result with set_bad (problem: no smooth transition to white):

    Image with masked values

Code so far:

from netCDF4 import Dataset as NetCDFFile
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
nc = NetCDFFile('C:/myfile1.nc')
nc1 = NetCDFFile('C:/myfile2.nc')
lat = nc.variables['lat'][:]
lon = nc.variables['lon'][:]
time = nc.variables['time'][:]
uwnd = nc.variables['uwnd'][:]
vwnd = nc1.variables['vwnd'][:]
map = Basemap(llcrnrlon=180.,llcrnrlat=0.,urcrnrlon=340.,urcrnrlat=80.)
lons,lats = np.meshgrid(lon,lat)
x,y = map(lons,lats)
speed = np.sqrt(uwnd*uwnd+vwnd*vwnd)
#speed = np.ma.masked_where(speed < 0.4, speed)
#cmap = plt.cm.jet
#cmap.set_bad(color='white')
levels = np.arange(0.0,3.0,0.1)
ticks = np.arange(0.0,3.0,0.2)
cs = map.contourf(x,y,speed[0],levels, cmap='jet')
vw = plt.quiver(x,y,speed)
cbar = plt.colorbar(cs, orientation='horizontal', cmap='jet', spacing='proportional',ticks=ticks)
cbar.set_label('850 mb Vector Wind Anomalies (m/s)')
map.drawcoastlines()
map.drawparallels(np.arange(20,80,20),labels=[1,1,0,0], linewidth=0.5)
map.drawmeridians(np.arange(200,340,20),labels=[0,0,0,1], linewidth=0.5)
#plt.show()
plt.savefig('phase8_850wind_anom.png',dpi=600)
4
  • If there shouldn't be any levels below 0.4, why does levels start at 0? Commented Mar 19, 2018 at 15:54
  • Because I missed my stupid mistake.. ;) Thanks for catching that! Commented Mar 19, 2018 at 16:16
  • So if this solves the issue, you may just delete the question, as this is not likely to help anyone else. if your issue is not solved, you should update your code with the actual problem. Commented Mar 19, 2018 at 16:23
  • @ImportanceOfBeingErnest - The silly mistake on my part was the main issue but I think the custom colormap solution proposed below will ultimately give me the solution I was looking for. Commented Mar 19, 2018 at 17:11

1 Answer 1

13

The answer to get the result smooth lies in constructing your own colormap. To do this one has to create an RGBA-matrix: a matrix with on each row the amount (between 0 and 1) of Red, Green, Blue, and Alpha (transparency; 0 means that the pixel does not have any coverage information and is transparent).

As an example the distance to some point is plotted in two dimensions. Then:

  • For any distance higher than some critical value, the colors will be taken from a standard colormap.
  • For any distance lower than some critical value, the colors will linearly go from white to the first color of the previously mentioned map.

The choices depend fully on what you want to show. The colormaps and their sizes depend on your problem. For example, you can choose different types of interpolation: linear, exponential, ...; single- or multi-color colormaps; etc..

The code:

import numpy             as np
import matplotlib        as mpl
import matplotlib.pyplot as plt

from mpl_toolkits.axes_grid1 import make_axes_locatable

# create colormap
# ---------------

# create a colormap that consists of
# - 1/5 : custom colormap, ranging from white to the first color of the colormap
# - 4/5 : existing colormap

# set upper part: 4 * 256/4 entries
upper = mpl.cm.jet(np.arange(256))

# set lower part: 1 * 256/4 entries
# - initialize all entries to 1 to make sure that the alpha channel (4th column) is 1
lower = np.ones((int(256/4),4))
# - modify the first three columns (RGB):
#   range linearly between white (1,1,1) and the first color of the upper colormap
for i in range(3):
  lower[:,i] = np.linspace(1, upper[0,i], lower.shape[0])

# combine parts of colormap
cmap = np.vstack(( lower, upper ))

# convert to matplotlib colormap
cmap = mpl.colors.ListedColormap(cmap, name='myColorMap', N=cmap.shape[0])

# show some example
# -----------------

# open a new figure
fig, ax = plt.subplots()

# some data to plot: distance to point at (50,50)
x,y = np.meshgrid(np.linspace(0,99,100),np.linspace(0,99,100))
z   = (x-50)**2. + (y-50)**2.

# plot data, apply colormap, set limit such that our interpretation is correct
im = ax.imshow(z, interpolation='nearest', cmap=cmap, clim=(0,5000))

# add a colorbar to the bottom of the image
div  = make_axes_locatable(ax)
cax  = div.append_axes('bottom', size='5%', pad=0.4)
cbar = plt.colorbar(im, cax=cax, orientation='horizontal')

# save/show the image
plt.savefig('so.png')
plt.show()

The result:

enter image description here

Sign up to request clarification or add additional context in comments.

5 Comments

To the downvoter: This seems to be exactly what the OP described... What did I miss?? Ok, it is only partly tailored to the problem, but the OP did not give a minimal example neither.
Tom -- the custom colormap did solve my issue but I'm ending up with the last 4 intervals at the top end as all the same color. How do I change that so that there's some color separation at the upper end? Here's my current output: ibb.co/ji2gsx
@bayouwxman Nice that it is what you wanted! About having the same colors: Sorry that was my mistake, I accidentally oversimplified the number of colors in the colormap. The colormap is actually encoded in 256 colors, so one should read it with this amount of colors. I have edited my answer, such that it is right now. It should also work for your problem.
thanks again! That fixed the upper end of the colormap. Now I just need to figure out how to get the quiver line working to plot wind vectors. Curious about this line: lower = np.ones((int(256/4),4)) I know np.ones returns an array of ones, but what does 'int' accomplish? Return whole integers?
@bayouwxman You are right, int(256/4) makes sure that you get an integer number of rows the matrix. Note that the matrix is initialized to 1 to have the alpha in the 4th color to one. The RGB columns are modified. I'll add this in comments. (BTW If this is exactly your answer you should probably accept it)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.