Skip to content

Commit

Permalink
Merge pull request #1057 from efiring/contour_norm_scaling
Browse files Browse the repository at this point in the history
Contour norm scaling
  • Loading branch information
efiring committed Aug 11, 2012
2 parents 50ae794 + 0890246 commit 41822d5
Show file tree
Hide file tree
Showing 12 changed files with 20,367 additions and 170 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG
@@ -1,9 +1,17 @@
2012-08-05 When a norm is passed to contourf, either or both of the
vmin, vmax attributes of that norm are now respected.
Formerly they were respected only if both were
specified. In addition, vmin and/or vmax can now
be passed to contourf directly as kwargs. - EF

2012-07-24 Contourf handles the extend kwarg by mapping the extended
ranges outside the normed 0-1 range so that they are
handled by colormap colors determined by the set_under
and set_over methods. Previously the extended ranges
were mapped to 0 or 1 so that the "under" and "over"
colormap colors were ignored. - EF
colormap colors were ignored. This change also increases
slightly the color contrast for a given set of contour
levels. - EF

2012-06-24 Make use of mathtext in tick labels configurable - DSD

Expand Down
32 changes: 16 additions & 16 deletions lib/matplotlib/axes.py
Expand Up @@ -3754,7 +3754,7 @@ def vlines(self, x, ymin, ymax, colors='k', linestyles='solid',
linestyles=linestyles, label=label)
self.add_collection(coll)
coll.update(kwargs)

if len(x) > 0:
minx = min( x )
maxx = max( x )
Expand Down Expand Up @@ -6955,16 +6955,18 @@ def pcolor(self, *args, **kwargs):
A :class:`matplotlib.colors.Colormap` instance. If *None*, use
rc settings.
norm: [ *None* | Normalize ]
*norm*: [ *None* | Normalize ]
An :class:`matplotlib.colors.Normalize` instance is used
to scale luminance data to 0,1. If *None*, defaults to
:func:`normalize`.
*vmin*/*vmax*: [ *None* | scalar ]
*vmin* and *vmax* are used in conjunction with *norm* to
normalize luminance data. If either are *None*, the min
and max of the color array *C* is used. If you pass a
*norm* instance, *vmin* and *vmax* will be ignored.
normalize luminance data. If either is *None*, it
is autoscaled to the respective min or max
of the color array *C*. If not *None*, *vmin* or
*vmax* passed in here override any pre-existing values
supplied in the *norm* instance.
*shading*: [ 'flat' | 'faceted' ]
If 'faceted', a black grid is drawn around each rectangle; if
Expand Down Expand Up @@ -7121,10 +7123,8 @@ def pcolor(self, *args, **kwargs):
if norm is not None: assert(isinstance(norm, mcolors.Normalize))
collection.set_cmap(cmap)
collection.set_norm(norm)
if vmin is not None or vmax is not None:
collection.set_clim(vmin, vmax)
else:
collection.autoscale_None()
collection.set_clim(vmin, vmax)
collection.autoscale_None()
self.grid(False)

x = X.compressed()
Expand Down Expand Up @@ -7167,9 +7167,11 @@ def pcolormesh(self, *args, **kwargs):
*vmin*/*vmax*: [ *None* | scalar ]
*vmin* and *vmax* are used in conjunction with *norm* to
normalize luminance data. If either are *None*, the min
and max of the color array *C* is used. If you pass a
*norm* instance, *vmin* and *vmax* will be ignored.
normalize luminance data. If either is *None*, it
is autoscaled to the respective min or max
of the color array *C*. If not *None*, *vmin* or
*vmax* passed in here override any pre-existing values
supplied in the *norm* instance.
*shading*: [ 'flat' | 'gouraud' ]
'flat' indicates a solid color for each quad. When
Expand Down Expand Up @@ -7235,10 +7237,8 @@ def pcolormesh(self, *args, **kwargs):
if norm is not None: assert(isinstance(norm, mcolors.Normalize))
collection.set_cmap(cmap)
collection.set_norm(norm)
if vmin is not None or vmax is not None:
collection.set_clim(vmin, vmax)
else:
collection.autoscale_None()
collection.set_clim(vmin, vmax)
collection.autoscale_None()

self.grid(False)

Expand Down
74 changes: 46 additions & 28 deletions lib/matplotlib/colorbar.py
Expand Up @@ -255,7 +255,7 @@ def __init__(self, ax, cmap=None,
self.filled = filled
self.extendfrac = extendfrac
self.solids = None
self.lines = None
self.lines = list()
self.outline = None
self.patch = None
self.dividers = None
Expand Down Expand Up @@ -467,14 +467,25 @@ def _add_solids(self, X, Y, C):
)
self.ax.add_collection(self.dividers)

def add_lines(self, levels, colors, linewidths):
def add_lines(self, levels, colors, linewidths, erase=True):
'''
Draw lines on the colorbar.
*colors* and *linewidths* must be scalars or
sequences the same length as *levels*.
Set *erase* to False to add lines without first
removing any previously added lines.
'''
N = len(levels)
dummy, y = self._locate(levels)
if len(y) != N:
raise ValueError("levels are outside colorbar range")
y = self._locate(levels)
nlevs = len(levels)
igood = (y < 1.001) & (y > -0.001)
y = y[igood]
if cbook.iterable(colors):
colors = np.asarray(colors)[igood]
if cbook.iterable(linewidths):
linewidths = np.asarray(linewidths)[igood]
N = len(y)
x = np.array([0.0, 1.0])
X, Y = np.meshgrid(x,y)
if self.orientation == 'vertical':
Expand All @@ -483,9 +494,10 @@ def add_lines(self, levels, colors, linewidths):
xy = [zip(Y[i], X[i]) for i in xrange(N)]
col = collections.LineCollection(xy, linewidths=linewidths)

if self.lines:
self.lines.remove()
self.lines = col
if erase and self.lines:
for lc in self.lines.pop():
lc.remove()
self.lines.append(col)
col.set_color(colors)
self.ax.add_collection(col)

Expand Down Expand Up @@ -528,7 +540,10 @@ def _ticker(self):
locator.axis.get_minpos = lambda : intv[0]
formatter.axis.get_minpos = lambda : intv[0]
b = np.array(locator())
b, ticks = self._locate(b)
ticks = self._locate(b)
inrange = (ticks > -0.001) & (ticks < 1.001)
ticks = ticks[inrange]
b = b[inrange]
formatter.set_locs(b)
ticklabels = [formatter(t, i) for i, t in enumerate(b)]
offset_string = formatter.get_offset()
Expand Down Expand Up @@ -746,37 +761,36 @@ def _mesh(self):

def _locate(self, x):
'''
Given a possible set of color data values, return the ones
within range, together with their corresponding colorbar
data coordinates.
Given a set of color data values, return their
corresponding colorbar data coordinates.
'''
if isinstance(self.norm, (colors.NoNorm, colors.BoundaryNorm)):
b = self._boundaries
xn = x
xout = x
else:
# Do calculations using normalized coordinates so
# as to make the interpolation more accurate.
b = self.norm(self._boundaries, clip=False).filled()
# We do our own clipping so that we can allow a tiny
# bit of slop in the end point ticks to allow for
# floating point errors.
xn = self.norm(x, clip=False).filled()
in_cond = (xn > -0.001) & (xn < 1.001)
xn = np.compress(in_cond, xn)
xout = np.compress(in_cond, x)
# The rest is linear interpolation with clipping.
# The rest is linear interpolation with extrapolation at ends.
y = self._y
N = len(b)
ii = np.minimum(np.searchsorted(b, xn), N-1)
i0 = np.maximum(ii - 1, 0)
ii = np.searchsorted(b, xn)
i0 = ii - 1
itop = (ii == N)
ibot = (ii == 0)
i0[itop] -= 1
ii[itop] -= 1
i0[ibot] += 1
ii[ibot] += 1

#db = b[ii] - b[i0]
db = np.take(b, ii) - np.take(b, i0)
db = np.where(i0==ii, 1.0, db)
#dy = y[ii] - y[i0]
dy = np.take(y, ii) - np.take(y, i0)
z = np.take(y, i0) + (xn-np.take(b,i0))*dy/db
return xout, z

return z

def set_alpha(self, alpha):
self.alpha = alpha
Expand Down Expand Up @@ -834,10 +848,13 @@ def on_mappable_changed(self, mappable):
self.set_clim(mappable.get_clim())
self.update_normal(mappable)

def add_lines(self, CS):
def add_lines(self, CS, erase=True):
'''
Add the lines from a non-filled
:class:`~matplotlib.contour.ContourSet` to the colorbar.
Set *erase* to False if these lines should be added to
any pre-existing lines.
'''
if not isinstance(CS, contour.ContourSet) or CS.filled:
raise ValueError('add_lines is only for a ContourSet of lines')
Expand All @@ -851,7 +868,8 @@ def add_lines(self, CS):
#tcolors = [col.get_colors()[0] for col in CS.collections]
#tlinewidths = [col.get_linewidth()[0] for lw in CS.collections]
#print 'tlinewidths:', tlinewidths
ColorbarBase.add_lines(self, CS.levels, tcolors, tlinewidths)
ColorbarBase.add_lines(self, CS.levels, tcolors, tlinewidths,
erase=erase)

def update_normal(self, mappable):
'''
Expand Down Expand Up @@ -884,7 +902,7 @@ def update_bruteforce(self, mappable):
self.outline = None
self.patch = None
self.solids = None
self.lines = None
self.lines = list()
self.dividers = None
self.set_alpha(mappable.get_alpha())
self.cmap = mappable.cmap
Expand Down
7 changes: 3 additions & 4 deletions lib/matplotlib/colors.py
Expand Up @@ -510,11 +510,10 @@ def __call__(self, X, alpha=None, bytes=False):
xa = np.array([X])
else:
vtype = 'array'
xma = ma.array(X, copy=False)
mask_bad = xma.mask
xa = xma.data.copy() # Copy here to avoid side effects.
xma = ma.array(X, copy=True) # Copy here to avoid side effects.
mask_bad = xma.mask # Mask will be used below.
xa = xma.filled() # Fill to avoid infs, etc.
del xma
# masked values are substituted below; no need to fill them here

if xa.dtype.char in np.typecodes['Float']:
# Treat 1.0 as slightly less than 1.
Expand Down

0 comments on commit 41822d5

Please sign in to comment.