I am making a multi-line chart and I have implemented a brush to be able to zoom into a specific domain on the x-axis. However, when I zoom in I want the y-axis to scale along so that its domain goes from [0, maxY]
, where maxY
is the maximum y-value for the current selection on the x-axis. To generate the lines I am using d3.line()
(which has the connection between the x and y values). This is how I currently calculate the maxY
value:
//Find max and min values in data to set the domain of the y-axis
var maxArray = updatedData.map(function(variable){
//First map the values in the array to a new array
var valuesArray = variable.values.map(function(d){
return d.value;
})
//Find max value in array
return Math.max(...valuesArray);
});
var maxY = Math.max(...maxArray);
And here is where I set the scales and create the d3.line()
:
var xScale = d3.scaleTime()
.range([0, chartWidth]);
var yScale = d3.scaleLinear()
.domain([0, maxY])
.range([chartHeight, 0]);
var brush = d3.brushX()
.on("end", brushend);
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) {return xScale(d.date)})
.y(function(d) {return yScale(d.value)})
//Save this to be able to zoom back out
var originalDomain = [new Date(data[0].Timestamp), new Date(data[data.length-1].Timestamp)];
xScale.domain(originalDomain);
Here is the code where I set the new xScale.domain()
and zoom in on that interval (which is called when the brushing is ended):
function brushend(){
//sourceEvent - the underlying input event, such as mousemove or touchmove.
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var brushInterval = d3.event.selection; //The interval of the current brushed selection
//If the function is called with no selection: ignore
if(!brushInterval) return;
//Enable reset button
resetButton.attr("disabled", null)
.on("click", resetAxis);
var newDomain = brushInterval.map(xScale.invert, xScale);
//TODO: Find max and min values in data to set the domain of the y-axis
xScale.domain(newDomain);
chart.selectAll(".line")
.transition()
.duration(1000)
.attr("d", function(d){ return line(d.values)});
chart.select(".x-axis")
.transition()
.duration(1000)
.call(xAxis);
//Remove the visual brush
d3.select(".brush").call(brush.move, null);
}
What I would like to do is to find the maximum y-value in the currently selected domain. I know that I can filter the data values to remove the ones that are not in the currently selected domain and then calculate the maximum value from them (like I did for the original domain). But it seems like there should be an easier solution to this. I didn't find any function in the documentation for d3.line()
that could calculate max values.
Is there any easy way to calculate max value from d3.line()
?
Thanks
There is not really an easier solution to this as you somehow have to filter the values to only take into account the ones which are in your selected x domain. However, using two nested calls to d3.max()
you can at least give it a pleasant look and spare some iterations by avoiding an additional call to .filter()
. Since d3.max()
will ignore null
values you can use it to filter your values by returning null
if the current datum is outside of the x domain's boundaries. To get the maximum value you can use something like the following:
const maxY = xDomain => d3.max(updatedData, variable =>
d3.max(
variable.values,
v => v.Timestamp >= xDomain[0] && v.Timestamp <= xDomain[1] ? v.value : null
)
);
Have a look at the following snippet for a working demo:
var updatedData = [{
values: [{Timestamp:0, value:1},{Timestamp:1, value:5},{Timestamp:2, value:10},{Timestamp:3, value:3},{Timestamp:4, value:30}]
}, {
values: [{Timestamp:0, value:19},{Timestamp:1, value:12},{Timestamp:2, value:13},{Timestamp:3, value:8},{Timestamp:4, value:50}]
}];
const maxY = xDomain => d3.max(updatedData, variable =>
d3.max(
variable.values,
v => (!xDomain || v.Timestamp >= xDomain[0] && v.Timestamp <= xDomain[1]) ? v.value : null
)
);
console.log(maxY()); // Default, check all values: max 50
console.log(maxY([1,3])); // Max 13
console.log(maxY([0,3])); // Max 19
<script src="https://d3js.org/d3.v4.js"></script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With