Skip to content

Commit 6061128

Browse files
feat(heatmap): add min/max to heatmap color domain
1 parent a503302 commit 6061128

File tree

6 files changed

+115
-11
lines changed

6 files changed

+115
-11
lines changed

packages/core/src/components/essentials/color-scale-legend.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { legend as legendConfigs } from '@/configuration'
55
import { ColorLegendType, Events, RenderTypes } from '@/interfaces/enums'
66
import { Legend } from './legend'
77
import { DOMUtils } from '@/services/essentials/dom-utils'
8-
import { getDomain } from '@/services/color-scale-utils'
8+
import { getDomain, type ColorDomainOptions } from '@/services/color-scale-utils'
99
import type { ChartModel } from '@/model'
1010

1111
export class ColorScaleLegend extends Legend {
@@ -107,7 +107,14 @@ export class ColorScaleLegend extends Legend {
107107
}
108108

109109
const customColorsEnabled = !isEmpty(customColors)
110-
const domain = getDomain(this.model.getDisplayData())
110+
111+
// Get custom color domain options for heatmap charts
112+
const customDomain: ColorDomainOptions | undefined = getProperty(
113+
options,
114+
this.chartType,
115+
'colorDomain'
116+
)
117+
const domain = getDomain(this.model.getDisplayData(), customDomain)
111118

112119
const useDefaultBarWidth = !(width <= legendConfigs.color.barWidth)
113120
const barWidth = useDefaultBarWidth ? legendConfigs.color.barWidth : width

packages/core/src/interfaces/charts.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,22 @@ export interface HeatmapChartOptions extends AxisChartOptions {
579579
title?: string
580580
type?: ColorLegendType | string
581581
}
582+
/**
583+
* Optional explicit domain for the color scale.
584+
* When not specified, the domain is automatically calculated from the data values.
585+
*/
586+
colorDomain?: {
587+
/**
588+
* Explicit minimum value for the color scale.
589+
* Values below this will use the minimum color.
590+
*/
591+
min?: number
592+
/**
593+
* Explicit maximum value for the color scale.
594+
* Values above this will use the maximum color.
595+
*/
596+
max?: number
597+
}
582598
}
583599
}
584600

packages/core/src/model/heatmap.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { getProperty } from '@/tools'
44
import { AxisFlavor, ScaleTypes } from '@/interfaces/enums'
55
import { getColorScale } from '@/services'
66
import { ChartModelCartesian } from './cartesian-charts'
7+
import type { ColorDomainOptions } from '@/services/color-scale-utils'
78

89
/** The gauge chart model layer */
910
export class HeatmapModel extends ChartModelCartesian {
@@ -37,9 +38,18 @@ export class HeatmapModel extends ChartModelCartesian {
3738
}
3839
}
3940

41+
/**
42+
* Get the custom color domain options from chart configuration
43+
* @returns ColorDomainOptions or undefined
44+
*/
45+
getColorDomainOptions(): ColorDomainOptions | undefined {
46+
const options = this.getOptions()
47+
return getProperty(options, 'heatmap', 'colorDomain')
48+
}
49+
4050
/**
4151
* Get min and maximum value of the display data
42-
* @returns Array consisting of smallest and largest values in data
52+
* @returns Array consisting of smallest and largest values in data
4353
*/
4454
getValueDomain() {
4555
const limits = extent(this.getDisplayData(), (d: any) => d.value)
@@ -53,7 +63,8 @@ export class HeatmapModel extends ChartModelCartesian {
5363
domain[0] = 0
5464
} else if (domain[0] === 0 && domain[1] === 0) {
5565
// Range cannot be between 0 and 0 (itself)
56-
return [0, 1]
66+
domain[0] = 0
67+
domain[1] = 1
5768
}
5869

5970
// Ensure the median of the range is 0 if domain extends into both negative & positive
@@ -65,6 +76,17 @@ export class HeatmapModel extends ChartModelCartesian {
6576
}
6677
}
6778

79+
// Apply custom domain overrides from options if provided
80+
const customDomain = this.getColorDomainOptions()
81+
if (customDomain) {
82+
if (typeof customDomain.min === 'number') {
83+
domain[0] = customDomain.min
84+
}
85+
if (typeof customDomain.max === 'number') {
86+
domain[1] = customDomain.max
87+
}
88+
}
89+
6890
return domain
6991
}
7092

@@ -299,11 +321,12 @@ export class HeatmapModel extends ChartModelCartesian {
299321
}
300322
}
301323

302-
// Save scale type
324+
// Save scale type with custom domain support
303325
this._colorScale = scaleQuantize()
304326
.domain(domain as [number, number])
305327
.range(colorPairing)
306328
const colorOptions = getProperty(this.getOptions(), 'color')
307-
this._colorScale = getColorScale(this.getDisplayData(), colorOptions)
329+
const customDomain = this.getColorDomainOptions()
330+
this._colorScale = getColorScale(this.getDisplayData(), colorOptions, customDomain)
308331
}
309332
}

packages/core/src/services/color-scale-utils.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,21 @@ import { extent, scaleLinear, scaleQuantize } from 'd3'
22
import { isEmpty } from 'lodash-es'
33
import { getProperty } from '@/tools'
44

5-
export function getDomain(data: any) {
5+
/**
6+
* Custom domain configuration for color scales
7+
*/
8+
export interface ColorDomainOptions {
9+
min?: number
10+
max?: number
11+
}
12+
13+
/**
14+
* Calculate the domain for a color scale from data values
15+
* @param data - Array of data objects with value property
16+
* @param customDomain - Optional explicit min/max values to override calculated domain
17+
* @returns [min, max] domain array
18+
*/
19+
export function getDomain(data: any, customDomain?: ColorDomainOptions) {
620
const limits = extent(data, (d: any) => d.value)
721
const domain = scaleLinear()
822
.domain(limits as [number, number])
@@ -14,7 +28,8 @@ export function getDomain(data: any) {
1428
domain[0] = 0
1529
} else if (domain[0] === 0 && domain[1] === 0) {
1630
// Range cannot be between 0 and 0 (itself)
17-
return [0, 1]
31+
domain[0] = 0
32+
domain[1] = 1
1833
}
1934

2035
// Ensure the median of the range is 0 if domain extends into both negative & positive
@@ -26,17 +41,27 @@ export function getDomain(data: any) {
2641
}
2742
}
2843

44+
// Apply custom domain overrides if provided
45+
if (customDomain) {
46+
if (typeof customDomain.min === 'number') {
47+
domain[0] = customDomain.min
48+
}
49+
if (typeof customDomain.max === 'number') {
50+
domain[1] = customDomain.max
51+
}
52+
}
53+
2954
return domain
3055
}
3156

32-
export function getColorScale(displayData: any, colorOptions: any) {
57+
export function getColorScale(displayData: any, colorOptions: any, customDomain?: ColorDomainOptions) {
3358
const customColors = getProperty(colorOptions, 'gradient', 'colors')
3459
const customColorsEnabled = !isEmpty(customColors)
3560

3661
let colorPairingOption = getProperty(colorOptions, 'pairing', 'option')
3762

3863
// If domain consists of negative and positive values, use diverging palettes
39-
const domain = getDomain(displayData)
64+
const domain = getDomain(displayData, customDomain)
4065
const colorScheme = domain[0] < 0 && domain[1] > 0 ? 'diverge' : 'mono'
4166

4267
// Use default color pairing options if not in defined range

packages/docs/src/lib/heatmap/index.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,30 @@ const heatmapMissingDataOptions: HeatmapChartOptions = {
112112
height: '400px'
113113
}
114114

115+
const heatmapCustomColorDomainOptions: HeatmapChartOptions = {
116+
title: 'Heatmap (Custom color domain)',
117+
axes: {
118+
bottom: {
119+
title: 'Letters',
120+
mapsTo: 'letter',
121+
scaleType: ScaleTypes.LABELS
122+
},
123+
left: {
124+
title: 'Months',
125+
mapsTo: 'month',
126+
scaleType: ScaleTypes.LABELS
127+
}
128+
},
129+
heatmap: {
130+
colorLegend: { title: 'Score (0, 150)' },
131+
colorDomain: {
132+
min: 0,
133+
max: 150
134+
}
135+
},
136+
height: '400px'
137+
}
138+
115139
const heatmapData: ChartTabularData = [
116140
{
117141
letter: 'A',
@@ -1302,6 +1326,11 @@ export const examples: Example[] = [
13021326
data: heatmapMissingData,
13031327
tags: ['test']
13041328
},
1329+
{
1330+
options: heatmapCustomColorDomainOptions,
1331+
data: heatmapData,
1332+
tags: ['test']
1333+
},
13051334
{
13061335
options: heatmapDomainOptions,
13071336
data: heatmapData,

packages/docs/src/searchIndex.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,11 @@ export default [
552552
'Heatmap (Missing data)',
553553
'Letters',
554554
'Months',
555-
'Legend title'
555+
'Legend title',
556+
'Heatmap (Custom color domain)',
557+
'Letters',
558+
'Months',
559+
'Score (0-150)'
556560
]
557561
},
558562
{

0 commit comments

Comments
 (0)