@@ -1431,9 +1431,9 @@ def __init__(self, boundaries, ncolors, clip=False, *, extend='neither'):
1431
1431
Parameters
1432
1432
----------
1433
1433
boundaries : array-like
1434
- Monotonically increasing sequence of boundaries
1434
+ Monotonically increasing sequence of at least 2 boundaries.
1435
1435
ncolors : int
1436
- Number of colors in the colormap to be used
1436
+ Number of colors in the colormap to be used.
1437
1437
clip : bool, optional
1438
1438
If clip is ``True``, out of range values are mapped to 0 if they
1439
1439
are below ``boundaries[0]`` or mapped to ``ncolors - 1`` if they
@@ -1472,38 +1472,56 @@ def __init__(self, boundaries, ncolors, clip=False, *, extend='neither'):
1472
1472
self .vmax = boundaries [- 1 ]
1473
1473
self .boundaries = np .asarray (boundaries )
1474
1474
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} " )
1475
1478
self .Ncmap = ncolors
1476
1479
self .extend = extend
1477
1480
1478
- self ._N = self .N - 1 # number of colors needed
1481
+ self ._n_regions = self .N - 1 # number of colors needed
1479
1482
self ._offset = 0
1480
1483
if extend in ('min' , 'both' ):
1481
- self ._N += 1
1484
+ self ._n_regions += 1
1482
1485
self ._offset = 1
1483
1486
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" )
1490
1493
1491
1494
def __call__ (self , value , clip = None ):
1492
1495
if clip is None :
1493
1496
clip = self .clip
1494
1497
1495
1498
xx , is_scalar = self .process_value (value )
1496
1499
mask = np .ma .getmaskarray (xx )
1500
+ # Fill masked values a value above the upper boundary
1497
1501
xx = np .atleast_1d (xx .filled (self .vmax + 1 ))
1498
1502
if clip :
1499
1503
np .clip (xx , self .vmin , self .vmax , out = xx )
1500
1504
max_col = self .Ncmap - 1
1501
1505
else :
1502
1506
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)
1503
1509
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 )
1507
1525
iret [xx < self .vmin ] = - 1
1508
1526
iret [xx >= self .vmax ] = max_col
1509
1527
ret = np .ma .array (iret , mask = mask )
0 commit comments