@@ -4,9 +4,27 @@ var d3 = require('@plotly/d3');
44var tinycolor = require ( 'tinycolor2' ) ;
55
66var Registry = require ( '../../registry' ) ;
7+ var Drawing = require ( '../../components/drawing' ) ;
8+ var Axes = require ( '../../plots/cartesian/axes' ) ;
79var Lib = require ( '../../lib' ) ;
10+ var svgTextUtils = require ( '../../lib/svg_text_utils' ) ;
11+ var formatLabels = require ( '../scatter/format_labels' ) ;
12+ var Color = require ( '../../components/color' ) ;
13+ var extractOpts = require ( '../../components/colorscale' ) . extractOpts ;
814var makeColorScaleFuncFromTrace = require ( '../../components/colorscale' ) . makeColorScaleFuncFromTrace ;
915var xmlnsNamespaces = require ( '../../constants/xmlns_namespaces' ) ;
16+ var alignmentConstants = require ( '../../constants/alignment' ) ;
17+ var LINE_SPACING = alignmentConstants . LINE_SPACING ;
18+
19+ var labelClass = 'heatmap-label' ;
20+
21+ function selectLabels ( plotGroup ) {
22+ return plotGroup . selectAll ( 'g.' + labelClass ) ;
23+ }
24+
25+ function removeLabels ( plotGroup ) {
26+ selectLabels ( plotGroup ) . remove ( ) ;
27+ }
1028
1129module . exports = function ( gd , plotinfo , cdheatmaps , heatmapLayer ) {
1230 var xa = plotinfo . xaxis ;
@@ -16,6 +34,8 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
1634 var plotGroup = d3 . select ( this ) ;
1735 var cd0 = cd [ 0 ] ;
1836 var trace = cd0 . trace ;
37+ var xGap = trace . xgap || 0 ;
38+ var yGap = trace . ygap || 0 ;
1939
2040 var z = cd0 . z ;
2141 var x = cd0 . x ;
@@ -31,7 +51,7 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
3151 var xrev = false ;
3252 var yrev = false ;
3353
34- var left , right , temp , top , bottom , i ;
54+ var left , right , temp , top , bottom , i , j , k ;
3555
3656 // TODO: if there are multiple overlapping categorical heatmaps,
3757 // or if we allow category sorting, then the categories may not be
@@ -112,6 +132,8 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
112132 if ( isOffScreen ) {
113133 var noImage = plotGroup . selectAll ( 'image' ) . data ( [ ] ) ;
114134 noImage . exit ( ) . remove ( ) ;
135+
136+ removeLabels ( plotGroup ) ;
115137 return ;
116138 }
117139
@@ -167,7 +189,7 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
167189 var gcount = 0 ;
168190 var bcount = 0 ;
169191
170- var xb , j , xi , v , row , c ;
192+ var xb , xi , v , row , c ;
171193
172194 function setColor ( v , pixsize ) {
173195 if ( v !== undefined ) {
@@ -278,8 +300,6 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
278300 } else { // zsmooth = false -> filling potentially large bricks works fastest with fillRect
279301 // gaps do not need to be exact integers, but if they *are* we will get
280302 // cleaner edges by rounding at least one edge
281- var xGap = trace . xgap ;
282- var yGap = trace . ygap ;
283303 var xGapLeft = Math . floor ( xGap / 2 ) ;
284304 var yGapTop = Math . floor ( yGap / 2 ) ;
285305
@@ -332,6 +352,185 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
332352 y : top ,
333353 'xlink:href' : canvas . toDataURL ( 'image/png' )
334354 } ) ;
355+
356+ removeLabels ( plotGroup ) ;
357+
358+ var texttemplate = trace . texttemplate ;
359+ if ( texttemplate ) {
360+ // dummy axis for formatting the z value
361+ var cOpts = extractOpts ( trace ) ;
362+ var dummyAx = {
363+ type : 'linear' ,
364+ range : [ cOpts . min , cOpts . max ] ,
365+ _separators : xa . _separators ,
366+ _numFormat : xa . _numFormat
367+ } ;
368+
369+ var aHistogram2dContour = trace . type === 'histogram2dcontour' ;
370+ var aContour = trace . type === 'contour' ;
371+ var iStart = aContour ? 1 : 0 ;
372+ var iStop = aContour ? m - 1 : m ;
373+ var jStart = aContour ? 1 : 0 ;
374+ var jStop = aContour ? n - 1 : n ;
375+
376+ var textData = [ ] ;
377+ for ( i = iStart ; i < iStop ; i ++ ) {
378+ var yVal ;
379+ if ( aContour ) {
380+ yVal = cd0 . y [ i ] ;
381+ } else if ( aHistogram2dContour ) {
382+ if ( i === 0 || i === m - 1 ) continue ;
383+ yVal = cd0 . y [ i ] ;
384+ } else if ( cd0 . yCenter ) {
385+ yVal = cd0 . yCenter [ i ] ;
386+ } else {
387+ if ( i + 1 === m && cd0 . y [ i + 1 ] === undefined ) continue ;
388+ yVal = ( cd0 . y [ i ] + cd0 . y [ i + 1 ] ) / 2 ;
389+ }
390+
391+ var _y = Math . round ( ya . c2p ( yVal ) ) ;
392+ if ( 0 > _y || _y > ya . _length ) continue ;
393+
394+ for ( j = jStart ; j < jStop ; j ++ ) {
395+ var xVal ;
396+ if ( aContour ) {
397+ xVal = cd0 . x [ j ] ;
398+ } else if ( aHistogram2dContour ) {
399+ if ( j === 0 || j === n - 1 ) continue ;
400+ xVal = cd0 . x [ j ] ;
401+ } else if ( cd0 . xCenter ) {
402+ xVal = cd0 . xCenter [ j ] ;
403+ } else {
404+ if ( j + 1 === n && cd0 . x [ j + 1 ] === undefined ) continue ;
405+ xVal = ( cd0 . x [ j ] + cd0 . x [ j + 1 ] ) / 2 ;
406+ }
407+
408+ var _x = Math . round ( xa . c2p ( xVal ) ) ;
409+ if ( 0 > _x || _x > xa . _length ) continue ;
410+
411+ var obj = formatLabels ( {
412+ x : xVal ,
413+ y : yVal
414+ } , trace , gd . _fullLayout ) ;
415+
416+ obj . x = xVal ;
417+ obj . y = yVal ;
418+
419+ var zVal = cd0 . z [ i ] [ j ] ;
420+ if ( zVal === undefined ) {
421+ obj . z = '' ;
422+ obj . zLabel = '' ;
423+ } else {
424+ obj . z = zVal ;
425+ obj . zLabel = Axes . tickText ( dummyAx , zVal , 'hover' ) . text ;
426+ }
427+
428+ var theText = cd0 . text && cd0 . text [ i ] && cd0 . text [ i ] [ j ] ;
429+ if ( theText === undefined || theText === false ) theText = '' ;
430+ obj . text = theText ;
431+
432+ var _t = Lib . texttemplateString ( texttemplate , obj , gd . _fullLayout . _d3locale , obj , trace . _meta || { } ) ;
433+ if ( ! _t ) continue ;
434+
435+ var lines = _t . split ( '<br>' ) ;
436+ var nL = lines . length ;
437+ var nC = 0 ;
438+ for ( k = 0 ; k < nL ; k ++ ) {
439+ nC = Math . max ( nC , lines [ k ] . length ) ;
440+ }
441+
442+ textData . push ( {
443+ l : nL , // number of lines
444+ c : nC , // maximum number of chars in a line
445+ t : _t , // text
446+ x : _x ,
447+ y : _y ,
448+ z : zVal
449+ } ) ;
450+ }
451+ }
452+
453+ var font = trace . textfont ;
454+ var fontFamily = font . family ;
455+ var fontSize = font . size ;
456+
457+ if ( ! fontSize || fontSize === 'auto' ) {
458+ var minW = Infinity ;
459+ var minH = Infinity ;
460+ var maxL = 0 ;
461+ var maxC = 0 ;
462+
463+ for ( k = 0 ; k < textData . length ; k ++ ) {
464+ var d = textData [ k ] ;
465+ maxL = Math . max ( maxL , d . l ) ;
466+ maxC = Math . max ( maxC , d . c ) ;
467+
468+ if ( k < textData . length - 1 ) {
469+ var nextD = textData [ k + 1 ] ;
470+ var dx = Math . abs ( nextD . x - d . x ) ;
471+ var dy = Math . abs ( nextD . y - d . y ) ;
472+
473+ if ( dx ) minW = Math . min ( minW , dx ) ;
474+ if ( dy ) minH = Math . min ( minH , dy ) ;
475+ }
476+ }
477+
478+ if (
479+ ! isFinite ( minW ) ||
480+ ! isFinite ( minH )
481+ ) {
482+ fontSize = 12 ;
483+ } else {
484+ minW -= xGap ;
485+ minH -= yGap ;
486+
487+ minW /= maxC ;
488+ minH /= maxL ;
489+
490+ minW /= LINE_SPACING / 2 ;
491+ minH /= LINE_SPACING ;
492+
493+ fontSize = Math . min (
494+ Math . floor ( minW ) ,
495+ Math . floor ( minH )
496+ ) ;
497+ }
498+ }
499+ if ( fontSize <= 0 || ! isFinite ( fontSize ) ) return ;
500+
501+ var xFn = function ( d ) { return d . x ; } ;
502+ var yFn = function ( d ) {
503+ return d . y - fontSize * ( ( d . l * LINE_SPACING ) / 2 - 1 ) ;
504+ } ;
505+
506+ var labels = selectLabels ( plotGroup ) . data ( textData ) ;
507+
508+ labels
509+ . enter ( )
510+ . append ( 'g' )
511+ . classed ( labelClass , 1 )
512+ . append ( 'text' )
513+ . attr ( 'text-anchor' , 'middle' )
514+ . each ( function ( d ) {
515+ var thisLabel = d3 . select ( this ) ;
516+
517+ var fontColor = font . color ;
518+ if ( ! fontColor || fontColor === 'auto' ) {
519+ fontColor = Color . contrast (
520+ 'rgba(' +
521+ sclFunc ( d . z ) . join ( ) +
522+ ')'
523+ ) ;
524+ }
525+
526+ thisLabel
527+ . attr ( 'data-notex' , 1 )
528+ . call ( svgTextUtils . positionText , xFn ( d ) , yFn ( d ) )
529+ . call ( Drawing . font , fontFamily , fontSize , fontColor )
530+ . text ( d . t )
531+ . call ( svgTextUtils . convertToTspans , gd ) ;
532+ } ) ;
533+ }
335534 } ) ;
336535} ;
337536
0 commit comments