From: Timothy W. H. <hi...@me...> - 2010-11-01 20:03:19
|
Hello, I have a 2D numpy masked array of geo-located data -- with some data missing -- that I wish to plot on a map. Basemap provides a nice tool to do this, but I am stumped trying to get the colorscheme I want. My data are only physically meaningful on land, so I am using Basemap.maskoceans() to mask out "wet" locations (oceans, lakes, etc.) Trouble is, now both water as well as actual missing data show up in the missing data color. I want to have blue water, some other (bright) color for missing data, and a nice-looking color transition (matplotlib.cm.Blues or something similar) for the valid data over land (values from 0 to 50). The Cookbook example at <http://www.scipy.org/Cookbook/Matplotlib/Plotting_Images_with_Special_Values> addresses my problem, but I cannot get it to work. After changing instances of matplotlib.numerix to numpy, I get a long list of exceptions, the last of which is TypeError: __call__() got an unexpected keyword argument 'bytes'. This has to do with sentinelNorm, I think, but I'm not sure how to fix it. Eventually I would like to sub-classify missing data by the type of missing input that caused a missing value, but for now a single missing data color is enough. The code below does almost what I want- I just need to figure out how to make the water blue. I have also messed around with matplotlib.cm.BoundaryNorm to create a colormap/normalization to handle my data, but I am getting hung up initializing the cmap, counting bin edges, etc. I also tacked my test code for that... Any help greatly appreciated! Thanks, Tim -- Timothy W. Hilton PhD Candidate, Department of Meteorology The Pennsylvania State University 503 Walker Building, University Park, PA 16802 hi...@me... #-------------------------------------------------- # amost right... import numpy as np import numpy.ma as ma from mpl_toolkits.basemap import Basemap, maskoceans import matplotlib.pyplot as plt import matplotlib.colors if __name__=="__main__": # setup a basemap instance & draw a map m_aeqd = Basemap(width=9e5,height=9e5,projection='aeqd', lat_0=28.46,lon_0=360-80.67, resolution='i', area_thresh=1000, rsphere=6371007.181000) col_water='#B9D3EE' #SlateGray2 col_land ='#1C1C1C' m_aeqd.drawmapboundary(fill_color=col_water) # draw coasts and fill continents. m_aeqd.drawcoastlines(linewidth=0.5) m_aeqd.fillcontinents(color=col_land,lake_color=col_water, zorder=0) # create a 100 x 100 pseudodata array with valid data between 0 and 50 and some "missing" data (less than zero) n=100 X = ma.masked_less(np.random.random_integers(-1, 50, (n,n)), 0) plot_mid = np.mean((m_aeqd.llcrnrx, m_aeqd.urcrnrx)) Xu, Xv = np.meshgrid(np.arange(stop=plot_mid + 500*n, start=plot_mid - 500*n, step=1000), np.arange(stop=m_aeqd.urcrnry, start=m_aeqd.urcrnry - 1000*n, step=1000)) Xlon, Xlat = m_aeqd(Xu, Xv, inverse=True) #setup a colormap and plot the data cmap = matplotlib.cm.get_cmap("Blues", 25) #mask oceans ocean_mask = maskoceans(Xlon, Xlat, X) #now I'm stumped how to incorporate oceans_mask into the color map oceans_cmap = matplotlib.colors.ListedColormap((col_land, "#000000"), name="oceans", N=2) cmap.set_bad(color="#FF0000") #show missing vals in bright red m_aeqd.pcolormesh(Xu, Xv, ocean_mask, cmap=cmap) plt.colorbar() #it seems like I could do another pcolormesh call with #ocean_mask.mask and oceans_cmap; I'd need to set the #transparency. It seems like the more elegant solution is to #devise cmap to account for ocean_mask.mask (water) separately #from X.mask (data that are actually missing). I'm not sure how #do that though. |
From: Friedrich R. <fri...@gm...> - 2010-11-06 21:52:56
|
2010/11/1 Timothy W. Hilton <hi...@me...>: > [...] > > I want to have blue water, some other (bright) color for missing data, > and a nice-looking color transition (matplotlib.cm.Blues or something > similar) for the valid data over land (values from 0 to 50). The > Cookbook example at > <http://www.scipy.org/Cookbook/Matplotlib/Plotting_Images_with_Special_Values> > addresses my problem, but I cannot get it to work. After changing > instances of matplotlib.numerix to numpy, I get a long list of > exceptions, the last of which is > TypeError: __call__() got an unexpected keyword argument 'bytes'. > This has to do with sentinelNorm, I think, but I'm not sure how to fix it. I think I would tackle this by writing a Norm that doesn't change negative values, and you might mask then the oceans by -0.5, and the nans by -1.5. Then, you might create a colormap comprised of the ocean color for [-1, 0] and the nan color for [-2, -1], and for the normal normed range [0, 1] the normal Blues cm. Have a look at cm.py and _cm.py how it works. Basically, you can specify for all sections of the colormap the left and right color. So you can mix discrete maps with continuous ones, because the continuous ones are just linearly interpolated with matching colors for left/right at each boundary position. Looking at the code will clarify things a lot I believe. I don't know what went wrong with the cb example you said. From a quick look, it seems to have "sentinel rgb values", but this is not what we want, right? > Eventually I would like to sub-classify missing data by the type of > missing input that caused a missing value, but for now a single > missing data color is enough. That would be possible with the approach above, by just adding sections below zero. _cm.py: Definitions of colormaps, like Blues. cm.py: among other things, how to load such specifications. colors.py: Defines Colormaps, and Norms. Have a look at both of them, esp. at :class:`Normalize`. I would subclass the Norm mentioned above from Normalize. I hope this helps you, Friedrich |
From: Timothy W. H. <hi...@me...> - 2010-12-09 00:11:45
|
Hi Friedrich, Many thanks for your detailed response. I've had to turn my attention to other things in the past few weeks, but I am back to this task now. I've implemented the Norm that you suggested by subclassing Normalize; that was a great suggestion. Now I have a two dimensional array where water has value -1.5, missing data have -0.5, and valid data over land have values in [0, 1]. After poking around in cm.py, _cm.py, and colors.py, I understand better how to define a colormap, but I am not sure how to map negative values like -0.5 or -1.5 to a color -- I believe the x values in the color dictionary must be positive? I can imagine various schemes to sidestep this by assigning water and missing values positive discrete values (say, 0.1 and 0.2 or something) and then putting valid data in the remaining portion of [0,1] -- maybe [0.3, 1] or something. But there's an elegance to valid data going into [0, 1] and other invalid data getting values outside of that range... I think I'd lose a little readability in giving that up. Anyway, thanks a whole lot for your helpful suggestions. Best, Tim On Sat, Nov 2010, 06 at 10:52:48PM +0100, Friedrich Romstedt wrote: > 2010/11/1 Timothy W. Hilton <hi...@me...>: > > [...] > > > > I want to have blue water, some other (bright) color for missing data, > > and a nice-looking color transition (matplotlib.cm.Blues or something > > similar) for the valid data over land (values from 0 to 50). The > > Cookbook example at > > <http://www.scipy.org/Cookbook/Matplotlib/Plotting_Images_with_Special_Values> > > addresses my problem, but I cannot get it to work. After changing > > instances of matplotlib.numerix to numpy, I get a long list of > > exceptions, the last of which is > > TypeError: __call__() got an unexpected keyword argument 'bytes'. > > This has to do with sentinelNorm, I think, but I'm not sure how to fix it. > > I think I would tackle this by writing a Norm that doesn't change > negative values, and you might mask then the oceans by -0.5, and the > nans by -1.5. Then, you might create a colormap comprised of the > ocean color for [-1, 0] and the nan color for [-2, -1], and for the > normal normed range [0, 1] the normal Blues cm. Have a look at cm.py > and _cm.py how it works. Basically, you can specify for all sections > of the colormap the left and right color. So you can mix discrete > maps with continuous ones, because the continuous ones are just > linearly interpolated with matching colors for left/right at each > boundary position. Looking at the code will clarify things a lot I > believe. > > I don't know what went wrong with the cb example you said. From a > quick look, it seems to have "sentinel rgb values", but this is not > what we want, right? > > > Eventually I would like to sub-classify missing data by the type of > > missing input that caused a missing value, but for now a single > > missing data color is enough. > > That would be possible with the approach above, by just adding > sections below zero. > > _cm.py: Definitions of colormaps, like Blues. > cm.py: among other things, how to load such specifications. > colors.py: Defines Colormaps, and Norms. Have a look at both of them, > esp. at :class:`Normalize`. I would subclass the Norm mentioned above > from Normalize. > > I hope this helps you, > Friedrich > |