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>