/
raster.py
229 lines (180 loc) · 7.95 KB
/
raster.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
from __future__ import absolute_import, division, unicode_literals
import numpy as np
import param
from ...core.util import cartesian_product, dimension_sanitizer, isfinite
from ...element import Raster, RGB, HSV
from .element import ElementPlot, ColorbarPlot
from .styles import line_properties, fill_properties, mpl_to_bokeh
from .util import bokeh_version, colormesh
class RasterPlot(ColorbarPlot):
clipping_colors = param.Dict(default={'NaN': 'transparent'})
show_legend = param.Boolean(default=False, doc="""
Whether to show legend for the plot.""")
style_opts = ['cmap', 'alpha']
_nonvectorized_styles = style_opts
_plot_methods = dict(single='image')
def _hover_opts(self, element):
xdim, ydim = element.kdims
tooltips = [(xdim.pprint_label, '$x'), (ydim.pprint_label, '$y')]
if bokeh_version >= '0.12.16' and not isinstance(element, (RGB, HSV)):
vdims = element.vdims
tooltips.append((vdims[0].pprint_label, '@image'))
for vdim in vdims[1:]:
vname = dimension_sanitizer(vdim.name)
tooltips.append((vdim.pprint_label, '@{0}'.format(vname)))
return tooltips, {}
def __init__(self, *args, **kwargs):
super(RasterPlot, self).__init__(*args, **kwargs)
if self.hmap.type == Raster:
self.invert_yaxis = not self.invert_yaxis
def get_data(self, element, ranges, style):
mapping = dict(image='image', x='x', y='y', dw='dw', dh='dh')
val_dim = element.vdims[0]
style['color_mapper'] = self._get_colormapper(val_dim, element, ranges, style)
if self.static_source:
return {}, mapping, style
if type(element) is Raster:
l, b, r, t = element.extents
if self.invert_axes:
l, b, r, t = b, l, t, r
else:
l, b, r, t = element.bounds.lbrt()
if self.invert_axes:
l, b, r, t = b, l, t, r
if self.invert_xaxis:
l, r = r, l
if self.invert_yaxis:
b, t = t, b
dh, dw = t-b, r-l
data = dict(x=[l], y=[b], dw=[dw], dh=[dh])
for i, vdim in enumerate(element.vdims, 2):
if i > 2 and 'hover' not in self.handles:
continue
img = element.dimension_values(i, flat=False)
if img.dtype.kind == 'b':
img = img.astype(np.int8)
if 0 in img.shape:
img = np.array([[np.NaN]])
if ((self.invert_axes and not type(element) is Raster) or
(not self.invert_axes and type(element) is Raster)):
img = img.T
if self.invert_xaxis:
img = img[:, ::-1]
if self.invert_yaxis:
img = img[::-1]
key = 'image' if i == 2 else dimension_sanitizer(vdim.name)
data[key] = [img]
return (data, mapping, style)
class RGBPlot(ElementPlot):
style_opts = ['alpha']
_nonvectorized_styles = style_opts
_plot_methods = dict(single='image_rgba')
def get_data(self, element, ranges, style):
mapping = dict(image='image', x='x', y='y', dw='dw', dh='dh')
if self.static_source:
return {}, mapping, style
img = np.dstack([element.dimension_values(d, flat=False)
for d in element.vdims])
if img.ndim == 3:
if img.shape[2] == 3: # alpha channel not included
alpha = np.ones(img.shape[:2])
if img.dtype.name == 'uint8':
alpha = (alpha*255).astype('uint8')
img = np.dstack([img, alpha])
if img.dtype.name != 'uint8':
img = (img*255).astype(np.uint8)
N, M, _ = img.shape
#convert image NxM dtype=uint32
img = img.view(dtype=np.uint32).reshape((N, M))
# Ensure axis inversions are handled correctly
l, b, r, t = element.bounds.lbrt()
if self.invert_axes:
img = img.T
l, b, r, t = b, l, t, r
if self.invert_xaxis:
l, r = r, l
img = img[:, ::-1]
if self.invert_yaxis:
img = img[::-1]
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)
class HSVPlot(RGBPlot):
def get_data(self, element, ranges, style):
return super(HSVPlot, self).get_data(element.rgb, ranges, style)
class QuadMeshPlot(ColorbarPlot):
clipping_colors = param.Dict(default={'NaN': 'transparent'})
show_legend = param.Boolean(default=False, doc="""
Whether to show legend for the plot.""")
style_opts = ['cmap', 'color'] + line_properties + fill_properties
_nonvectorized_styles = style_opts
_plot_methods = dict(single='quad')
def get_data(self, element, ranges, style):
x, y, z = element.dimensions()[:3]
if self.invert_axes: x, y = y, x
cmapper = self._get_colormapper(z, element, ranges, style)
cmapper = {'field': z.name, 'transform': cmapper}
irregular = element.interface.irregular(element, x)
if irregular:
mapping = dict(xs='xs', ys='ys', fill_color=cmapper)
else:
mapping = {'left': 'left', 'right': 'right',
'fill_color': cmapper,
'top': 'top', 'bottom': 'bottom'}
if self.static_source:
return {}, mapping, style
x, y = dimension_sanitizer(x.name), dimension_sanitizer(y.name)
zdata = element.dimension_values(z, flat=False)
if irregular:
dims = element.kdims
if self.invert_axes: dims = dims[::-1]
X, Y = [element.interface.coords(element, d, expanded=True, edges=True)
for d in dims]
X, Y = colormesh(X, Y)
zvals = zdata.T.flatten() if self.invert_axes else zdata.flatten()
XS, YS = [], []
mask = []
xc, yc = [], []
for xs, ys, zval in zip(X, Y, zvals):
xs, ys = xs[:-1], ys[:-1]
if isfinite(zval) and all(isfinite(xs)) and all(isfinite(ys)):
XS.append(list(xs))
YS.append(list(ys))
mask.append(True)
if 'hover' in self.handles:
xc.append(xs.mean())
yc.append(ys.mean())
else:
mask.append(False)
data = {'xs': XS, 'ys': YS, z.name: zvals[np.array(mask)]}
if 'hover' in self.handles:
data[x] = np.array(xc)
data[y] = np.array(yc)
else:
xc, yc = (element.interface.coords(element, x, edges=True, ordered=True),
element.interface.coords(element, y, edges=True, ordered=True))
x0, y0 = cartesian_product([xc[:-1], yc[:-1]], copy=True)
x1, y1 = cartesian_product([xc[1:], yc[1:]], copy=True)
zvals = zdata.flatten() if self.invert_axes else zdata.T.flatten()
data = {'left': x0, 'right': x1, dimension_sanitizer(z.name): zvals,
'bottom': y0, 'top': y1}
if 'hover' in self.handles and not self.static_source:
data[x] = element.dimension_values(x)
data[y] = element.dimension_values(y)
return data, mapping, style
def _init_glyph(self, plot, mapping, properties):
"""
Returns a Bokeh glyph object.
"""
properties = mpl_to_bokeh(properties)
properties = dict(properties, **mapping)
if 'xs' in mapping:
renderer = plot.patches(**properties)
else:
renderer = plot.quad(**properties)
if self.colorbar and 'color_mapper' in self.handles:
self._draw_colorbar(plot, self.handles['color_mapper'])
return renderer, renderer.glyph