Skip to content

Commit 50bbb7e

Browse files
authored
Merge pull request #4325 from plotly/image-nan_ms
Skip non-numeric values in image trace plot
2 parents 405b9ed + d6abde9 commit 50bbb7e

File tree

9 files changed

+161
-61
lines changed

9 files changed

+161
-61
lines changed

src/traces/image/attributes.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,19 @@ module.exports = extendFlat({
3535
values: cm,
3636
dflt: 'rgb',
3737
role: 'info',
38-
editType: 'plot',
38+
editType: 'calc',
3939
description: 'Color model used to map the numerical color components described in `z` into colors.'
4040
},
4141
zmin: {
4242
valType: 'info_array',
4343
items: [
44-
{valType: 'number', editType: 'plot'},
45-
{valType: 'number', editType: 'plot'},
46-
{valType: 'number', editType: 'plot'},
47-
{valType: 'number', editType: 'plot'}
44+
{valType: 'number', editType: 'calc'},
45+
{valType: 'number', editType: 'calc'},
46+
{valType: 'number', editType: 'calc'},
47+
{valType: 'number', editType: 'calc'}
4848
],
4949
role: 'info',
50-
editType: 'plot',
50+
editType: 'calc',
5151
description: [
5252
'Array defining the lower bound for each color component.',
5353
'Note that the default value will depend on the colormodel.',
@@ -57,13 +57,13 @@ module.exports = extendFlat({
5757
zmax: {
5858
valType: 'info_array',
5959
items: [
60-
{valType: 'number', editType: 'plot'},
61-
{valType: 'number', editType: 'plot'},
62-
{valType: 'number', editType: 'plot'},
63-
{valType: 'number', editType: 'plot'}
60+
{valType: 'number', editType: 'calc'},
61+
{valType: 'number', editType: 'calc'},
62+
{valType: 'number', editType: 'calc'},
63+
{valType: 'number', editType: 'calc'}
6464
],
6565
role: 'info',
66-
editType: 'plot',
66+
editType: 'calc',
6767
description: [
6868
'Array defining the higher bound for each color component.',
6969
'Note that the default value will depend on the colormodel.',

src/traces/image/calc.js

+46
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
'use strict';
1010

11+
var Lib = require('../../lib');
12+
var constants = require('./constants');
13+
var isNumeric = require('fast-isnumeric');
1114
var Axes = require('../../plots/cartesian/axes');
1215
var maxRowLength = require('../../lib').maxRowLength;
1316

@@ -28,6 +31,7 @@ module.exports = function calc(gd, trace) {
2831
if(ya && ya.type === 'log') for(i = 0; i < h; i++) yrange.push(y0 + i * trace.dy);
2932
trace._extremes[xa._id] = Axes.findExtremes(xa, xrange);
3033
trace._extremes[ya._id] = Axes.findExtremes(ya, yrange);
34+
trace._scaler = makeScaler(trace);
3135

3236
var cd0 = {
3337
x0: x0,
@@ -38,3 +42,45 @@ module.exports = function calc(gd, trace) {
3842
};
3943
return [cd0];
4044
};
45+
46+
function scale(zero, ratio, min, max) {
47+
return function(c) {
48+
return Lib.constrain((c - zero) * ratio, min, max);
49+
};
50+
}
51+
52+
function constrain(min, max) {
53+
return function(c) { return Lib.constrain(c, min, max);};
54+
}
55+
56+
// Generate a function to scale color components according to zmin/zmax and the colormodel
57+
function makeScaler(trace) {
58+
var colormodel = trace.colormodel;
59+
var n = colormodel.length;
60+
var cr = constants.colormodel[colormodel];
61+
62+
trace._sArray = [];
63+
// Loop over all color components
64+
for(var k = 0; k < n; k++) {
65+
if(cr.min[k] !== trace.zmin[k] || cr.max[k] !== trace.zmax[k]) {
66+
trace._sArray.push(scale(
67+
trace.zmin[k],
68+
(cr.max[k] - cr.min[k]) / (trace.zmax[k] - trace.zmin[k]),
69+
cr.min[k],
70+
cr.max[k]
71+
));
72+
} else {
73+
trace._sArray.push(constrain(cr.min[k], cr.max[k]));
74+
}
75+
}
76+
77+
return function(pixel) {
78+
var c = pixel.slice(0, n);
79+
for(var k = 0; k < n; k++) {
80+
var ck = c[k];
81+
if(!isNumeric(ck)) return false;
82+
c[k] = trace._sArray[k](ck);
83+
}
84+
return c;
85+
};
86+
}

src/traces/image/constants.js

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
109
'use strict';
1110

1211
module.exports = {

src/traces/image/defaults.js

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
109
'use strict';
1110

1211
var Lib = require('../../lib');

src/traces/image/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module.exports = {
1212
attributes: require('./attributes'),
1313
supplyDefaults: require('./defaults'),
1414
calc: require('./calc'),
15-
plot: require('./plot').plot,
15+
plot: require('./plot'),
1616
style: require('./style'),
1717
hoverPoints: require('./hover'),
1818
eventData: require('./event_data'),

src/traces/image/plot.js

+9-46
Original file line numberDiff line numberDiff line change
@@ -7,55 +7,13 @@
77
*/
88

99
'use strict';
10+
1011
var d3 = require('d3');
1112
var Lib = require('../../lib');
1213
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
1314
var constants = require('./constants');
1415

15-
module.exports = {};
16-
17-
// Generate a function to scale color components according to zmin/zmax and the colormodel
18-
var scaler = function(trace) {
19-
var colormodel = trace.colormodel;
20-
var n = colormodel.length;
21-
var cr = constants.colormodel[colormodel];
22-
23-
function scale(zero, factor, min, max) {
24-
return function(c) {
25-
c = (c - zero) * factor;
26-
c = Lib.constrain(c, min, max);
27-
return c;
28-
};
29-
}
30-
31-
function constrain(min, max) {
32-
return function(c) { return Lib.constrain(c, min, max);};
33-
}
34-
35-
var s = [];
36-
// Loop over all color components
37-
for(var k = 0; k < n; k++) {
38-
if(cr.min[k] !== trace.zmin[k] || cr.max[k] !== trace.zmax[k]) {
39-
s.push(scale(
40-
trace.zmin[k],
41-
(cr.max[k] - cr.min[k]) / (trace.zmax[k] - trace.zmin[k]),
42-
cr.min[k],
43-
cr.max[k]
44-
));
45-
} else {
46-
s.push(constrain(cr.min[k], cr.max[k]));
47-
}
48-
}
49-
50-
return function(pixel) {
51-
var c = pixel.slice(0, n);
52-
for(var k = 0; k < n; k++) {
53-
c[k] = s[k](c[k]);
54-
}
55-
return c;
56-
};
57-
};
58-
module.exports.plot = function(gd, plotinfo, cdimage, imageLayer) {
16+
module.exports = function plot(gd, plotinfo, cdimage, imageLayer) {
5917
var xa = plotinfo.xaxis;
6018
var ya = plotinfo.yaxis;
6119

@@ -129,10 +87,10 @@ module.exports.plot = function(gd, plotinfo, cdimage, imageLayer) {
12987
canvas.width = imageWidth;
13088
canvas.height = imageHeight;
13189
var context = canvas.getContext('2d');
90+
13291
var ipx = function(i) {return Lib.constrain(Math.round(xa.c2p(x0 + i * dx) - left), 0, imageWidth);};
13392
var jpx = function(j) {return Lib.constrain(Math.round(ya.c2p(y0 + j * dy) - top), 0, imageHeight);};
13493

135-
trace._scaler = scaler(trace);
13694
var fmt = constants.colormodel[trace.colormodel].fmt;
13795
var c;
13896
for(i = 0; i < cd0.w; i++) {
@@ -142,7 +100,12 @@ module.exports.plot = function(gd, plotinfo, cdimage, imageLayer) {
142100
var jpx0 = jpx(j); var jpx1 = jpx(j + 1);
143101
if(jpx1 === jpx0 || isNaN(jpx1) || isNaN(jpx0) || !z[j][i]) continue;
144102
c = trace._scaler(z[j][i]);
145-
context.fillStyle = trace.colormodel + '(' + fmt(c).join(',') + ')';
103+
if(c) {
104+
context.fillStyle = trace.colormodel + '(' + fmt(c).join(',') + ')';
105+
} else {
106+
// Return a transparent pixel
107+
context.fillStyle = 'rgba(0,0,0,0)';
108+
}
146109
context.fillRect(ipx0, jpx0, ipx1 - ipx0, jpx1 - jpx0);
147110
}
148111
}

src/traces/image/style.js

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
109
'use strict';
1110

1211
var d3 = require('d3');
19.5 KB
Loading
+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
{
2+
"data": [{
3+
"type": "image",
4+
"hoverinfo": "all",
5+
"z": [
6+
[
7+
[255, 0, 0],
8+
[191, 0, 0],
9+
[127, 0, 0]
10+
],
11+
[
12+
[0, 255, 0],
13+
[0, 191, 0],
14+
[0, 127, 0]
15+
],
16+
[
17+
[0, 0, 255],
18+
[0, 0, 191],
19+
[0, 0, 127]
20+
]
21+
]
22+
}, {
23+
"yaxis": "y2",
24+
"xaxis": "x2",
25+
"type": "image",
26+
"hoverinfo": "all",
27+
"z": [
28+
[
29+
[255, 0, 0],
30+
[191, 0, null],
31+
[127, 0, 0]
32+
],
33+
[
34+
[0, 255, 0],
35+
[false, 191, 0],
36+
[0, 127, 0]
37+
],
38+
[
39+
[0, 0, 255],
40+
[0, true, 191],
41+
[0, 0, 127]
42+
]
43+
]
44+
}, {
45+
"yaxis": "y3",
46+
"xaxis": "x3",
47+
"type": "image",
48+
"hoverinfo": "all",
49+
"z": [
50+
[
51+
[255, 0, 0],
52+
[191, 0, 0],
53+
[-10000, 0, 0]
54+
],
55+
[
56+
[0, 255, 0],
57+
[0, "", 0],
58+
[0, 127, 0]
59+
],
60+
[
61+
[0, 0, 10000],
62+
[0, 0, 191],
63+
[0, 0, 127]
64+
]
65+
]
66+
}, {
67+
"yaxis": "y4",
68+
"xaxis": "x4",
69+
"type": "image",
70+
"hoverinfo": "all",
71+
"z": [
72+
[
73+
[255, 0],
74+
[191, 0, null],
75+
[127, 0, ""]
76+
],
77+
[
78+
[true, 255, 0],
79+
[false, 191, 0],
80+
["text", 127, 0]
81+
],
82+
[]
83+
]
84+
}],
85+
"layout": {
86+
"width": 600,
87+
"height": 600,
88+
"grid": {
89+
"rows": 2,
90+
"columns": 2,
91+
"pattern": "independent"
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)