Using D3 Scales to convert colour to number

Viewed 242

I would like to have this colour scheme as my input domain: enter image description here

And a value between 0 and 1 as my output range. However, I am not sure which type of scale to use or how to add the scheme as an input domain.

The code below is the opposite of what I am trying to do.

let scaleSequential1 = d3.scaleSequential()
  .domain([0, 1])
  .interpolator(d3.interpolateViridis);

console.log( scaleSequential1(0) ); //#440154
console.log( scaleSequential1(0.5) ); //#21918c
console.log( scaleSequential1(1) ); //#fde725
2 Answers

Following up on my comment, there is no direct way to do this. You can hack it up if you like this. If you are going to call it frequently I'd wrap in in a closure to spare the .map...

function invertViridis(color){
  return d3.range(0, 1.01, 0.01).map(i => d3.interpolateViridis(i)).indexOf(color) / 100;
}

console.log(invertViridis("#440154"));
console.log(invertViridis("#3b528b"));
console.log(invertViridis("#21918c"));
console.log(invertViridis("#5ec962"));
console.log(invertViridis("#fde725"));

function invertViridisClosure(){
  var r = d3.range(0, 1.01, 0.01).map(i => d3.interpolateViridis(i));
  return function(color) {
    return r.indexOf(color) / 100;
  }
}

let f = invertViridisClosure();
console.log(f("#440154"));
console.log(f("#3b528b"));
console.log(f("#21918c"));
console.log(f("#5ec962"));
console.log(f("#fde725"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

I'll suggest a different approach, which deals with D3 source code.

If you look at the d3.interpolateViridis source, you'll see that the colours are just a long string, which is passed to this function:

export default function(specifier) {
  var n = specifier.length / 6 | 0, colors = new Array(n), i = 0;
  while (i < n) colors[i] = "#" + specifier.slice(i * 6, ++i * 6);
  return colors;
}

Then, the above function returns an array of 256 colours.

Therefore, my approach is just using that function (here renamed as getColours) to create our colours array:

const colorsArray = getColors(colorsString);

With that array in hand we can just use indexOf and a basic linear scale, since you explicitly asked for a D3 scale in your question's title. However, we can ditch the scales and use a vanilla JavaScript function:

function invert(color) {
    return colorsArray.indexOf(color) / (colorsArray.length - 1);
}

Here is the demo:

const colorsString = "44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725";

const colorsArray = getColors(colorsString);

function invert(color) {
  return colorsArray.indexOf(color) / (colorsArray.length - 1);
}

console.log(invert("#440154"));
console.log(invert("#21918c"));
console.log(invert("#fde725"));

function getColors(specifier) {
  var n = specifier.length / 6 | 0,
    colors = new Array(n),
    i = 0;
  while (i < n) colors[i] = "#" + specifier.slice(i * 6, ++i * 6);
  return colors;
}

Have in ind that, because 256 is an even number, there is no colour that will return exactly 0.5. The other answer has a colour returning a nice and beautiful 0.5 because it uses d3.range(0, 1.01, 0.01), which produces an array with an odd number of elements (101 elements).

Related