Skip to content

Commit

Permalink
Handle zero ranges correctly in regrid operation (#2869)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr authored and jlstevens committed Sep 17, 2018
1 parent e58aa7b commit a96f75f
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 8 deletions.
4 changes: 2 additions & 2 deletions holoviews/core/data/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,14 @@ def values(cls, dataset, dim, expanded=True, flat=True, compute=True):
l, b, r, t = dataset.bounds.lbrt()
dim2, dim1 = dataset.data.shape[:2]
xdate, ydate = isinstance(l, util.datetime_types), isinstance(b, util.datetime_types)
if l == r:
if l == r or dim1 == 0:
xlin = np.full((dim1,), l, dtype=('datetime64[us]' if xdate else 'float'))
elif xdate:
xlin = util.date_range(l, r, dim1, dataset._time_unit)
else:
xstep = float(r - l)/dim1
xlin = np.linspace(l+(xstep/2.), r-(xstep/2.), dim1)
if b == t:
if b == t or dim2 == 0:
ylin = np.full((dim2,), b, dtype=('datetime64[us]' if ydate else 'float'))
elif ydate:
ylin = util.date_range(b, t, dim2, dataset._time_unit)
Expand Down
2 changes: 2 additions & 0 deletions holoviews/core/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -1690,6 +1690,8 @@ def bound_range(vals, density, time_unit='us'):
assumed to be evenly spaced. Density is rounded to machine precision
using significant digits reported by sys.float_info.dig.
"""
if not len(vals):
return(np.nan, np.nan, density, False)
low, high = vals.min(), vals.max()
invert = False
if len(vals) > 1 and vals[0] > vals[1]:
Expand Down
6 changes: 5 additions & 1 deletion holoviews/element/raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,11 @@ def __init__(self, data, kdims=None, vdims=None, bounds=None, extents=None,
if (data is None
or (isinstance(data, (list, tuple)) and not data)
or (isinstance(data, np.ndarray) and data.size == 0)):
data = np.zeros((2, 2))
data = data if isinstance(data, np.ndarray) and data.ndim == 2 else np.zeros((0, 0))
bounds = 0
if not xdensity: xdensity = 1
if not ydensity: ydensity = 1

if rtol is not None:
params['rtol'] = rtol
else:
Expand Down
25 changes: 20 additions & 5 deletions holoviews/operation/datashader.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ def _process(self, element, key=None):
x, y = element.kdims
coords = tuple(element.dimension_values(d, expanded=False) for d in [x, y])
info = self._get_sampling(element, x, y)
(x_range, y_range), _, (width, height), (xtype, ytype) = info
(x_range, y_range), (xs, ys), (width, height), (xtype, ytype) = info

# Disable upsampling by clipping size and ranges
(xstart, xend), (ystart, yend) = (x_range, y_range)
Expand All @@ -592,21 +592,33 @@ def _process(self, element, key=None):
if isinstance(y0, datetime_types):
y0, y1 = dt_to_int(y0, 'ns'), dt_to_int(y1, 'ns')
exspan, eyspan = (x1-x0), (y1-y0)
width = min([int((xspan/exspan) * len(coords[0])), width])
height = min([int((yspan/eyspan) * len(coords[1])), height])
if np.isfinite(exspan) and exspan > 0:
width = min([int((xspan/exspan) * len(coords[0])), width])
else:
width = 0
if np.isfinite(eyspan) and eyspan > 0:
height = min([int((yspan/eyspan) * len(coords[1])), height])
else:
height = 0
xunit = float(xspan)/width if width else 0
yunit = float(yspan)/height if height else 0
xs, ys = (np.linspace(xstart+xunit/2., xend-xunit/2., width),
np.linspace(ystart+yunit/2., yend-yunit/2., height))

# Compute bounds (converting datetimes)
if xtype == 'datetime':
xstart, xend = (np.array([xstart, xend])/10e5).astype('datetime64[us]')
xs = (xs/10e5).astype('datetime64[us]')
if ytype == 'datetime':
ystart, yend = (np.array([ystart, yend])/10e5).astype('datetime64[us]')
ys = (ys/10e5).astype('datetime64[us]')
bbox = BoundingBox(points=[(xstart, ystart), (xend, yend)])

params = dict(bounds=bbox)
if width == 0 or height == 0:
if width == 0: params['xdensity'] = 1
if height == 0: params['ydensity'] = 1
return element.clone([], **params)
return element.clone((xs, ys, np.zeros((height, width))), **params)

cvs = ds.Canvas(plot_width=width, plot_height=height,
x_range=x_range, y_range=y_range)
Expand Down Expand Up @@ -881,7 +893,10 @@ def to_xarray(cls, element):
data += tuple(element.dimension_values(vd, flat=False)
for vd in element.vdims)
dtypes = [dt for dt in element.datatype if dt != 'xarray']
return element.clone(data, datatype=['xarray']+dtypes)
return element.clone(data, datatype=['xarray']+dtypes,
bounds=element.bounds,
xdensity=element.xdensity,
ydensity=element.ydensity)


def _process(self, element, key=None):
Expand Down
3 changes: 3 additions & 0 deletions holoviews/plotting/bokeh/raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ def get_data(self, element, ranges, style):
b, t = t, b
dh, dw = t-b, r-l

if 0 in img.shape:
img = np.zeros((1, 1), dtype=np.uint32)

data = dict(image=[img], x=[l], y=[b], dw=[dw], dh=[dh])
return (data, mapping, style)

Expand Down
9 changes: 9 additions & 0 deletions tests/operation/testdatashader.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,15 @@ def test_regrid_disabled_expand(self):
dynamic=False)
self.assertEqual(regridded, img)

def test_regrid_zero_range(self):
ls = np.linspace(0, 10, 200)
xx, yy = np.meshgrid(ls, ls)
img = Image(np.sin(xx)*np.cos(yy), bounds=(0, 0, 1, 1))
regridded = regrid(img, x_range=(-1, -0.5), y_range=(-1, -0.5), dynamic=False)
expected = Image(np.zeros((0, 0)), bounds=(0, 0, 0, 0), xdensity=1, ydensity=1)
self.assertEqual(regridded, expected)



@attr(optional=1)
class DatashaderRasterizeTests(ComparisonTestCase):
Expand Down

0 comments on commit a96f75f

Please sign in to comment.