@@ -4,9 +4,27 @@ var d3 = require('@plotly/d3');
4
4
var tinycolor = require ( 'tinycolor2' ) ;
5
5
6
6
var Registry = require ( '../../registry' ) ;
7
+ var Drawing = require ( '../../components/drawing' ) ;
8
+ var Axes = require ( '../../plots/cartesian/axes' ) ;
7
9
var 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 ;
8
14
var makeColorScaleFuncFromTrace = require ( '../../components/colorscale' ) . makeColorScaleFuncFromTrace ;
9
15
var 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
+ }
10
28
11
29
module . exports = function ( gd , plotinfo , cdheatmaps , heatmapLayer ) {
12
30
var xa = plotinfo . xaxis ;
@@ -16,6 +34,8 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
16
34
var plotGroup = d3 . select ( this ) ;
17
35
var cd0 = cd [ 0 ] ;
18
36
var trace = cd0 . trace ;
37
+ var xGap = trace . xgap || 0 ;
38
+ var yGap = trace . ygap || 0 ;
19
39
20
40
var z = cd0 . z ;
21
41
var x = cd0 . x ;
@@ -31,7 +51,7 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
31
51
var xrev = false ;
32
52
var yrev = false ;
33
53
34
- var left , right , temp , top , bottom , i ;
54
+ var left , right , temp , top , bottom , i , j , k ;
35
55
36
56
// TODO: if there are multiple overlapping categorical heatmaps,
37
57
// or if we allow category sorting, then the categories may not be
@@ -112,6 +132,8 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
112
132
if ( isOffScreen ) {
113
133
var noImage = plotGroup . selectAll ( 'image' ) . data ( [ ] ) ;
114
134
noImage . exit ( ) . remove ( ) ;
135
+
136
+ removeLabels ( plotGroup ) ;
115
137
return ;
116
138
}
117
139
@@ -167,7 +189,7 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
167
189
var gcount = 0 ;
168
190
var bcount = 0 ;
169
191
170
- var xb , j , xi , v , row , c ;
192
+ var xb , xi , v , row , c ;
171
193
172
194
function setColor ( v , pixsize ) {
173
195
if ( v !== undefined ) {
@@ -278,8 +300,6 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
278
300
} else { // zsmooth = false -> filling potentially large bricks works fastest with fillRect
279
301
// gaps do not need to be exact integers, but if they *are* we will get
280
302
// cleaner edges by rounding at least one edge
281
- var xGap = trace . xgap ;
282
- var yGap = trace . ygap ;
283
303
var xGapLeft = Math . floor ( xGap / 2 ) ;
284
304
var yGapTop = Math . floor ( yGap / 2 ) ;
285
305
@@ -332,6 +352,185 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
332
352
y : top ,
333
353
'xlink:href' : canvas . toDataURL ( 'image/png' )
334
354
} ) ;
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
+ }
335
534
} ) ;
336
535
} ;
337
536
0 commit comments