While conditional formatting is a familiar tool for improving table-based presentations of data, its application within a Node and Edge interface introduces some increased complexity. The sheer volume of data can easily result in clutter that obscures vital patterns and trends. Therefore, enhancing the usability of data visualisations without compromising their accuracy and clarity poses a significant challenge.
Attempt #1 Scaling
For the first attempt, I generated a colour in hexadecimal format based on a given value relative to a maximum value (defaulting to 9999 but this would need to be dynamic based on the actual data).
It calculates a scale from 0 to 1 based on where the value falls between a fixed minimum value of 1 and the maximum value. It then uses this scale to determine the red component (increasing with value) and the green component (decreasing with value), setting the blue component to a constant 50. The resulting RGB values are converted to a hexadecimal string and returned as the color code.
export function getColorBasedOnValue(value, maxValue = 9999) {
const minValue = 1;
const scale = (value - minValue) / (maxValue - minValue);
const red = Math.min(255, Math.floor(255 * scale));
const green = Math.max(0, Math.floor(200 * (1 - scale)));
const blue = 50;
return `#${red.toString(16).padStart(2, "0")}${green
.toString(16)
.padStart(2, "0")}${blue.toString(16).padStart(2, "0")}`;
}
This worked as a POC, but we really wanted to have a selection based on the brand colours rather than just red, green divergence.
Generating the colour pallets
Selecting the ideal shades required some going back and forth testing at different scale of data.. Ultimately, I opted for simplicity, adjusting the colours in 20% increments to create distinct layers of dark and light. This method ensures that each colour variation is both noticeable and harmonious, enhancing the overall clarity of the visualisation. I found d3-scale-chromatic and ColorBrewer's tooling really helpful inspiration for the project.
Result
The function I ended up with selects a colour from the predefined palettes based on a given minimum and maximum value. It calculates an index into the selectedPalette
array by dividing the range of possible values into segments corresponding to the number of colours in the palette. This index is based on where the value falls within these segments. It then ensures this index is within the bounds of the array to avoid errors. The function finally returns the hexadecimal colour code from the palette corresponding to the calculated index.
export function getColorBasedOnValue(value, maxValue = 9999, selectedPalette) {
const hexValues = selectedPalette;
// Calculate the index for the hex value based on the input value
const segment = (maxValue - 1) / (hexValues.length - 1);
let index = Math.min(hexValues.length - 1, Math.floor((value - 1) / segment));
// Ensure index is within the bounds of the hexValues array
index = Math.max(0, Math.min(index, hexValues.length - 1));
// Return the hex value corresponding to the calculated index
return hexValues[index];
}