Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contour norm scaling #1057

Merged
merged 6 commits into from Aug 11, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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