create logarithmic color scale with specified steps

I need to do color scale that represents the same scale used in mapbox. The mapbox expression is this:

'fill-color': [
  'interpolate',
  ['linear'],
  ['log10', value],
  0, "white",
  Math.log10(1_000), "yellow",
  Math.log10(100_000), "orange",
  Math.log10(1_000_000), "red",
  Math.log10(60_000_000), "blue"
]

that means:

  • for very low value (log10(value) = 0), the color is white
  • at the first threshold (log10(1_000)), the color becomes yellow
  • at the mid threshold (log10(100_000)), the color transitions to orange
  • at the high threshold (log10(1_000_000)), the color changes to red
  • for the highest values (log10(60_000_000)), the color becomes blue.
    This creates a smooth transition between these colors based on values levels.

I would like to create a color scale in d3, so this is what I did:

  • I created the following scales to create the ticks:
const logScale = d3.scaleLog([1, 60_000_000], [0, 100]).base(10);
const logIncompleteTicks = logScale.ticks(TICKS_NUMBER);
const logTicks = _.uniq([0, ...logIncompleteTicks, 60_000_000]);
const logTickPositionScale = d3.scaleLinear([0, 60_000_000], [SCALE_HEIGHT, 0]);
  • now I need to create the background of the scale so the color scales. I created this:
const colorScale = d3.scaleLinear([0, 1_000, 100_000, 1_000_000, 60_000_000], ["white", "yellow", "orange", "red", "blue"]);

but I don’t know how to use it and I am also not sure that what I’ve done since now is correct, if it represents the same scale used in mapbox..

Here the full code:

import * as d3 from "d3";
import _ from "lodash-es";

const TICKS_NUMBER = 8;
const X = 0;
const Y = 0;
const SCALE_CONTAINER_WIDTH = 200;
const SCALE_CONTAINER_HEIGHT = 550;
const SCALE_WIDTH = 100;
const SCALE_HEIGHT = 530;

export default function App() {
  const logScale = d3.scaleLog([1, 60_000_000], [0, 100]).base(10);
  const logIncompleteTicks = logScale.ticks(TICKS_NUMBER);
  const logTicks = _.uniq([0, ...logIncompleteTicks, 60_000_000]);
  const logTickPositionScale = d3.scaleLinear(
    [0, 60_000_000],
    [SCALE_HEIGHT, 0]
  );
  const colorScale = d3.scaleLinear(
    [0, 1_000, 100_000, 1_000_000, 60_000_000],
    ["white", "yellow", "orange", "red", "blue"]
  );

  return (
    <div>
      <div
        style={{
          width: SCALE_CONTAINER_WIDTH,
          height: SCALE_CONTAINER_HEIGHT,
          display: "flex",
          justifyContent: "end",
          alignItems: "center",
          border: "2px solid red",
        }}
      >
        <svg
          x={X}
          y={Y}
          width={SCALE_WIDTH}
          height={SCALE_HEIGHT}
          overflow="visible"
        >
          <rect
            x={X}
            y={Y}
            width={SCALE_WIDTH}
            height={SCALE_HEIGHT}
            fill="white"
            stroke="black"
          />
          <g>
            {logTicks.map((logTick) => {
              const y = logTickPositionScale(logTick);

              return (
                <g key={logTick} transform={`translate(0, ${y})`}>
                  <line x1={0} y1={0} x2={SCALE_WIDTH} y2={0} stroke="black" />
                  <text
                    x={-5}
                    y={0}
                    textAnchor="end"
                    dominantBaseline="middle"
                    fontSize={12}
                  >
                    {logTick}
                  </text>
                </g>
              );
            })}
          </g>
        </svg>
      </div>
    </div>
  );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>

the result is:
scale tmp