How can I create standalone scale that is aware of Observable plot's width, margins, etc?
scale.apply()
method.apply()
function returns values in the range [0,1], not in terms of the plot's dimensions (width, margin, etc).plot.scale
, which returns values in terms of the plot's dimensions.plot.scale
because the scale.apply()
function is needed to define the marks that are part of the options when creating the plot. (Sort of a chicken-and-egg problem.)plot.scale
, then again to define the plot that uses plot.scale
I have created a simple sample demonstrating the problem below:
const data = [{
text: 'Weekend',
utc: new Date('2024-06-16'), end: new Date('2024-06-17'),
},{
text: 'Weekdays',
utc: new Date('2024-06-17'), end: new Date('2024-06-22'),
}, {
text: 'Weekend',
utc: new Date('2024-06-22'), end: new Date('2024-06-23'),
}]
// Construct scale:
const scale = Plot.scale({x: {domain: [new Date('2024-06-16'), new Date('2024-06-23')]}})
// Simple plot:
const plot = Plot.plot({
height: 150,
width: 800,
marginRight: 12,
marginLeft: 12,
marks: [
Plot.rectY(data, {
x1: (d) => d.utc,
x2: (d) => d.end,
y: 1,
fill: (d) => d.text == 'Weekend' ? 'lightgray': 'beige'
}),
Plot.text(data, {
x: (d) => (Number(d.utc) + Number(d.end))/2,
text: 'text'
}),
],
});
function compareScales(value) {
console.log({
value,
' scale': scale.apply(value),
'plot.scale': plot.scale('x').apply(value)
})
}
compareScales(new Date('2024-06-16'))
compareScales(new Date('2024-06-17'))
compareScales(new Date('2024-06-23'))
const div = document.querySelector('#myplot');
div.append(plot);
#myplot {
height: 200px;
}
<!DOCTYPE html>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script src="https://cdn.jsdelivr.net/npm/@observablehq/[email protected]"></script>
<div id="myplot"></div>
When you create your "standalone" scale just give it the same range
as your plot.
const width = 800,
marginLeft = 12,
marginRight = 12;
// construct scale
const scale = Plot.scale({
x: {
domain: [ new Date('2024-06-16'), new Date('2024-06-23') ],
range: [ marginLeft, width - marginRight ] //<-- range matching plot
},
});
Running example:
<!DOCTYPE html>
<html>
<head>
<style>
#myplot {
height: 200px;
}
</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script src="https://cdn.jsdelivr.net/npm/@observablehq/[email protected]"></script>
<div id="myplot"></div>
<script>
const data = [{
text: 'Weekend',
utc: new Date('2024-06-16'),
end: new Date('2024-06-17'),
},
{
text: 'Weekdays',
utc: new Date('2024-06-17'),
end: new Date('2024-06-22'),
},
{
text: 'Weekend',
utc: new Date('2024-06-22'),
end: new Date('2024-06-23'),
},
];
const width = 800,
marginLeft = 12,
marginRight = 12;
// Construct scale:
const scale = Plot.scale({
x: {
domain: [new Date('2024-06-16'), new Date('2024-06-23')],
range: [marginLeft, width - marginRight]
},
});
// Simple plot:
const plot = Plot.plot({
height: 150,
width: width,
marginRight: marginRight,
marginLeft: marginLeft,
marks: [
Plot.rectY(data, {
x1: (d) => d.utc,
x2: (d) => d.end,
y: 1,
fill: (d) => (d.text == 'Weekend' ? 'lightgray' : 'beige'),
}),
Plot.text(data, {
x: (d) => (Number(d.utc) + Number(d.end)) / 2,
text: 'text',
}),
],
});
function compareScales(value) {
console.log({
value,
' scale': scale.apply(value),
'plot.scale': plot.scale('x').apply(value)
})
}
compareScales(new Date('2024-06-16'))
compareScales(new Date('2024-06-17'))
compareScales(new Date('2024-06-23'))
const div = document.querySelector('#myplot');
div.append(plot);
</script>
</body>
</html>