Skip to content

Commit 222ed47

Browse files
committed
options to display text on heatmaps
- add texttemplate and textfont
1 parent 0408836 commit 222ed47

File tree

4 files changed

+183
-2
lines changed

4 files changed

+183
-2
lines changed

src/traces/heatmap/attributes.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ var scatterAttrs = require('../scatter/attributes');
44
var baseAttrs = require('../../plots/attributes');
55
var axisHoverFormat = require('../../plots/cartesian/axis_format_attributes').axisHoverFormat;
66
var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplateAttrs;
7+
var texttemplateAttrs = require('../../plots/template_attributes').texttemplateAttrs;
78
var colorScaleAttrs = require('../../components/colorscale/attributes');
89

910
var extendFlat = require('../../lib/extend').extendFlat;
@@ -116,6 +117,11 @@ module.exports = extendFlat({
116117
zhoverformat: axisHoverFormat('z', 1),
117118

118119
hovertemplate: hovertemplateAttrs(),
120+
texttemplate: texttemplateAttrs({editType: 'plot'}, {
121+
keys: ['x', 'y', 'z', 'text']
122+
}),
123+
textfont: extendFlat({}, scatterAttrs.textfont, {arrayOk: false}),
124+
119125
showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false})
120126
}, {
121127
transforms: undefined

src/traces/heatmap/defaults.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
2727
coerce('text');
2828
coerce('hovertext');
2929
coerce('hovertemplate');
30+
coerce('texttemplate');
31+
32+
var fontDflt = Lib.extendFlat({}, layout.font, {color: 'white'});
33+
Lib.coerceFont(coerce, 'textfont', fontDflt);
3034

3135
handleStyleDefaults(traceIn, traceOut, coerce, layout);
3236

src/traces/heatmap/plot.js

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,26 @@ var d3 = require('@plotly/d3');
44
var tinycolor = require('tinycolor2');
55

66
var Registry = require('../../registry');
7+
var Drawing = require('../../components/drawing');
8+
var Axes = require('../../plots/cartesian/axes');
79
var Lib = require('../../lib');
10+
var svgTextUtils = require('../../lib/svg_text_utils');
11+
var formatLabels = require('../scatter/format_labels');
12+
var extractOpts = require('../../components/colorscale').extractOpts;
813
var makeColorScaleFuncFromTrace = require('../../components/colorscale').makeColorScaleFuncFromTrace;
914
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
15+
var alignmentConstants = require('../../constants/alignment');
16+
var LINE_SPACING = alignmentConstants.LINE_SPACING;
17+
18+
var labelClass = 'label';
19+
20+
function selectLabels(plotGroup) {
21+
return plotGroup.selectAll('g.' + labelClass);
22+
}
23+
24+
function removeLabels(plotGroup) {
25+
selectLabels(plotGroup).remove();
26+
}
1027

1128
module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
1229
var xa = plotinfo.xaxis;
@@ -31,7 +48,7 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
3148
var xrev = false;
3249
var yrev = false;
3350

34-
var left, right, temp, top, bottom, i;
51+
var left, right, temp, top, bottom, i, j;
3552

3653
// TODO: if there are multiple overlapping categorical heatmaps,
3754
// or if we allow category sorting, then the categories may not be
@@ -112,6 +129,8 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
112129
if(isOffScreen) {
113130
var noImage = plotGroup.selectAll('image').data([]);
114131
noImage.exit().remove();
132+
133+
removeLabels(plotGroup);
115134
return;
116135
}
117136

@@ -167,7 +186,7 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
167186
var gcount = 0;
168187
var bcount = 0;
169188

170-
var xb, j, xi, v, row, c;
189+
var xb, xi, v, row, c;
171190

172191
function setColor(v, pixsize) {
173192
if(v !== undefined) {
@@ -332,6 +351,106 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
332351
y: top,
333352
'xlink:href': canvas.toDataURL('image/png')
334353
});
354+
355+
removeLabels(plotGroup);
356+
357+
var texttemplate = trace.texttemplate;
358+
if(texttemplate) {
359+
// dummy axis for formatting the z value
360+
var cOpts = extractOpts(trace);
361+
var dummyAx = {
362+
type: 'linear',
363+
range: [cOpts.min, cOpts.max],
364+
_separators: xa._separators,
365+
_numFormat: xa._numFormat
366+
};
367+
368+
var xOff = 0;
369+
var yOff = 0;
370+
371+
var allX = cd0.xCenter;
372+
if(!allX) {
373+
allX = cd0.x;
374+
xOff = (allX[1] - allX[0]) / 2 || 0;
375+
}
376+
377+
var allY = cd0.yCenter;
378+
if(!allY) {
379+
allY = cd0.y;
380+
yOff = (allY[1] - allY[0]) / 2 || 0;
381+
}
382+
383+
var allZ = cd0.z;
384+
385+
var textData = [];
386+
for(i = 0; i < m; i++) {
387+
var yVal = allY[i] + xOff;
388+
var _y = Math.round(ya.c2p(yVal));
389+
if(0 > _y || _y > ya._length) continue;
390+
391+
for(j = 0; j < n; j++) {
392+
var xVal = allX[j] + yOff;
393+
var _x = Math.round(xa.c2p(xVal));
394+
if(0 > _x || _x > xa._length) continue;
395+
396+
var obj = formatLabels({
397+
x: xVal,
398+
y: yVal
399+
}, trace, gd._fullLayout);
400+
401+
obj.x = xVal;
402+
obj.y = yVal;
403+
404+
var zVal = allZ[i][j];
405+
if(zVal === undefined) {
406+
obj.z = '';
407+
obj.zLabel = '';
408+
} else {
409+
obj.z = zVal;
410+
obj.zLabel = Axes.tickText(dummyAx, zVal, 'hover').text;
411+
}
412+
413+
var theText = cd0.text && cd0.text[i] && cd0.text[i][j];
414+
if(theText === undefined || theText === false) theText = '';
415+
obj.text = theText;
416+
417+
var _t = Lib.texttemplateString(texttemplate, obj, gd._fullLayout._d3locale, obj, trace._meta || {});
418+
if(!_t) continue;
419+
420+
textData.push({
421+
t: _t,
422+
x: _x,
423+
y: _y
424+
});
425+
}
426+
}
427+
428+
var font = trace.textfont;
429+
var xFn = function(d) { return d.x; };
430+
var yFn = function(d) {
431+
var nlines = d.t.split('<br>').length;
432+
return d.y - font.size * ((nlines * LINE_SPACING) / 2 - 1);
433+
};
434+
435+
var labels = selectLabels(plotGroup).data(textData);
436+
437+
labels
438+
.enter()
439+
.append('g')
440+
.classed(labelClass, 1)
441+
.append('text')
442+
.attr('text-anchor', 'middle')
443+
.each(function(d) {
444+
var thisLabel = d3.select(this);
445+
446+
thisLabel
447+
.attr('data-notex', 1)
448+
.call(svgTextUtils.positionText, xFn(d), yFn(d))
449+
.call(Drawing.font, font.family, font.size, font.color)
450+
.text(d.t)
451+
.call(svgTextUtils.convertToTspans, gd);
452+
});
453+
}
335454
});
336455
};
337456

test/plot-schema.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26630,11 +26630,63 @@
2663026630
"editType": "calc",
2663126631
"valType": "data_array"
2663226632
},
26633+
"textfont": {
26634+
"arrayOk": false,
26635+
"color": {
26636+
"arrayOk": true,
26637+
"editType": "style",
26638+
"valType": "color"
26639+
},
26640+
"colorsrc": {
26641+
"description": "Sets the source reference on Chart Studio Cloud for `color`.",
26642+
"editType": "none",
26643+
"valType": "string"
26644+
},
26645+
"description": "Sets the text font.",
26646+
"editType": "calc",
26647+
"family": {
26648+
"arrayOk": true,
26649+
"description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*.",
26650+
"editType": "calc",
26651+
"noBlank": true,
26652+
"strict": true,
26653+
"valType": "string"
26654+
},
26655+
"familysrc": {
26656+
"description": "Sets the source reference on Chart Studio Cloud for `family`.",
26657+
"editType": "none",
26658+
"valType": "string"
26659+
},
26660+
"role": "object",
26661+
"size": {
26662+
"arrayOk": true,
26663+
"editType": "calc",
26664+
"min": 1,
26665+
"valType": "number"
26666+
},
26667+
"sizesrc": {
26668+
"description": "Sets the source reference on Chart Studio Cloud for `size`.",
26669+
"editType": "none",
26670+
"valType": "string"
26671+
}
26672+
},
2663326673
"textsrc": {
2663426674
"description": "Sets the source reference on Chart Studio Cloud for `text`.",
2663526675
"editType": "none",
2663626676
"valType": "string"
2663726677
},
26678+
"texttemplate": {
26679+
"arrayOk": true,
26680+
"description": "Template string used for rendering the information text that appear on points. Note that this will override `textinfo`. Variables are inserted using %{variable}, for example \"y: %{y}\". Numbers are formatted using d3-format's syntax %{variable:d3-format}, for example \"Price: %{y:$.2f}\". https://github.com/d3/d3-format/tree/v1.4.5#d3-format for details on the formatting syntax. Dates are formatted using d3-time-format's syntax %{variable|d3-time-format}, for example \"Day: %{2019-01-01|%A}\". https://github.com/d3/d3-time-format/tree/v2.2.3#locale_format for details on the date formatting syntax. Every attributes that can be specified per-point (the ones that are `arrayOk: true`) are available. variables `x`, `y`, `z` and `text`.",
26681+
"dflt": "",
26682+
"editType": "plot",
26683+
"valType": "string"
26684+
},
26685+
"texttemplatesrc": {
26686+
"description": "Sets the source reference on Chart Studio Cloud for `texttemplate`.",
26687+
"editType": "none",
26688+
"valType": "string"
26689+
},
2663826690
"transforms": {
2663926691
"items": {
2664026692
"transform": {

0 commit comments

Comments
 (0)