@@ -1431,9 +1431,9 @@ def __init__(self, boundaries, ncolors, clip=False, *, extend='neither'):
14311431 Parameters
14321432 ----------
14331433 boundaries : array-like
1434- Monotonically increasing sequence of boundaries
1434+ Monotonically increasing sequence of at least 2 boundaries.
14351435 ncolors : int
1436- Number of colors in the colormap to be used
1436+ Number of colors in the colormap to be used.
14371437 clip : bool, optional
14381438 If clip is ``True``, out of range values are mapped to 0 if they
14391439 are below ``boundaries[0]`` or mapped to ``ncolors - 1`` if they
@@ -1472,38 +1472,56 @@ def __init__(self, boundaries, ncolors, clip=False, *, extend='neither'):
14721472 self .vmax = boundaries [- 1 ]
14731473 self .boundaries = np .asarray (boundaries )
14741474 self .N = len (self .boundaries )
1475+ if self .N < 2 :
1476+ raise ValueError ("You must provide at least 2 boundaries "
1477+ f"(1 region) but you passed in { boundaries !r} " )
14751478 self .Ncmap = ncolors
14761479 self .extend = extend
14771480
1478- self ._N = self .N - 1 # number of colors needed
1481+ self ._n_regions = self .N - 1 # number of colors needed
14791482 self ._offset = 0
14801483 if extend in ('min' , 'both' ):
1481- self ._N += 1
1484+ self ._n_regions += 1
14821485 self ._offset = 1
14831486 if extend in ('max' , 'both' ):
1484- self ._N += 1
1485- if self ._N > self .Ncmap :
1486- raise ValueError (f"There are { self ._N } color bins including "
1487- f" extensions, but ncolors = { ncolors } ; "
1488- " ncolors must equal or exceed the number of "
1489- "bins" )
1487+ self ._n_regions += 1
1488+ if self ._n_regions > self .Ncmap :
1489+ raise ValueError (f"There are { self ._n_regions } color bins "
1490+ "including extensions, but ncolors = "
1491+ f" { ncolors } ; ncolors must equal or exceed the "
1492+ "number of bins" )
14901493
14911494 def __call__ (self , value , clip = None ):
14921495 if clip is None :
14931496 clip = self .clip
14941497
14951498 xx , is_scalar = self .process_value (value )
14961499 mask = np .ma .getmaskarray (xx )
1500+ # Fill masked values a value above the upper boundary
14971501 xx = np .atleast_1d (xx .filled (self .vmax + 1 ))
14981502 if clip :
14991503 np .clip (xx , self .vmin , self .vmax , out = xx )
15001504 max_col = self .Ncmap - 1
15011505 else :
15021506 max_col = self .Ncmap
1507+ # this gives us the bins in the lookup table in the range
1508+ # [0, _n_regions - 1] (the offset is baked in in the init)
15031509 iret = np .digitize (xx , self .boundaries ) - 1 + self ._offset
1504- if self .Ncmap > self ._N :
1505- scalefac = (self .Ncmap - 1 ) / (self ._N - 1 )
1506- iret = (iret * scalefac ).astype (np .int16 )
1510+ # if we have more colors than regions, stretch the region
1511+ # index computed above to full range of the color bins. This
1512+ # will make use of the full range (but skip some of the colors
1513+ # in the middle) such that the first region is mapped to the
1514+ # first color and the last region is mapped to the last color.
1515+ if self .Ncmap > self ._n_regions :
1516+ if self ._n_regions == 1 :
1517+ # special case the 1 region case, pick the middle color
1518+ iret [iret == 0 ] = (self .Ncmap - 1 ) // 2
1519+ else :
1520+ # otherwise linearly remap the values from the region index
1521+ # to the color index spaces
1522+ iret = (self .Ncmap - 1 ) / (self ._n_regions - 1 ) * iret
1523+ # cast to 16bit integers in all cases
1524+ iret = iret .astype (np .int16 )
15071525 iret [xx < self .vmin ] = - 1
15081526 iret [xx >= self .vmax ] = max_col
15091527 ret = np .ma .array (iret , mask = mask )
0 commit comments