Last bin in histogram has smaller width than others - d3.js

I have a D3.js histogram graph that has an issue where my last x-axis bin is significantly smaller than than the rest of the bins which are equal width. At first I thought this was a axis issue, but the axis is set with the typical width and height range and domains, making me believe it has to do with my bar code, but I'm not sure what would create just one wrong width for a bing, while the other bins are equal width. Provided below is my code. Any help would be appreciated!
Code:
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<style>
#sleep-dist-histo {
stroke-width: 0.5;
stroke: goldenrod;
}
</style>
<script>
var data = [
{ "x": "7.41" },
{ "x": "7.01" },
{ "x": "8.39" },
{ "x": "8.22" },
{ "x": "8.31" },
{ "x": "8.33" },
{ "x": "8.16" },
{ "x": "8.00" },
{ "x": "6.04" },
{ "x": "8.18" },
{ "x": "7.47" },
{ "x": "7.57" },
{ "x": "7.11" },
{ "x": "6.59" },
{ "x": "7.51" },
{ "x": "6.31" },
{ "x": "8.01" },
{ "x": "8.14" },
{ "x": "8.26" },
{ "x": "7.44" },
{ "x": "7.54" },
{ "x": "6.23" },
{ "x": "6.25" },
{ "x": "7.50" },
{ "x": "7.45" },
{ "x": "9.34" },
{ "x": "7.53" },
{ "x": "7.11" },
{ "x": "8.17" },
{ "x": "8.02" },
{ "x": "8.12" },
{ "x": "8.14" },
{ "x": "7.49" },
{ "x": "6.35" },
{ "x": "7.07" },
{ "x": "5.46" },
{ "x": "6.56" },
{ "x": "7.41" },
{ "x": "8.06" },
{ "x": "7.31" },
{ "x": "8.53" },
{ "x": "7.42" },
{ "x": "7.41" },
{ "x": "7.57" },
{ "x": "7.38" },
{ "x": "8.04" },
{ "x": "6.29" },
{ "x": "7.52" },
{ "x": "7.42" },
{ "x": "8.06" },
{ "x": "8.09" },
{ "x": "8.03" },
{ "x": "7.06" },
{ "x": "7.33" },
{ "x": "8.09" },
{ "x": "7.47" },
{ "x": "7.54" },
{ "x": "7.31" },
{ "x": "7.30" },
{ "x": "7.50" },
{ "x": "4.07" },
{ "x": "8.22" },
{ "x": "7.44" }
]
// console.log(data)
var margin = { top: 15, right: 30, bottom: 50, left: 60 }
var width = 800 - margin.left - margin.right;
var height = 800 - margin.top - margin.bottom;
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // translate(margin left, margin top)
// Append x-scale and x-scale tick labels
var xAxisGroup = svg.append("g")
.attr("class", "y-axis")
.attr("transform", "translate(" + 0 + "," + height + ")");
// Set x-scale range
var x = d3.scaleLinear()
.range([0, width]);
// Append x-scale title
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," + (height + margin.bottom) + ")")
.style("text-anchor", "middle")
.text("Time Asleep in Hours (Bins)");
// Set y-scale range
var y = d3.scaleLinear()
.range([height, margin.top]);
// Append y-scale
var yAxisGroup = svg.append("g")
.attr("class", "y-axis");
// Append y-scale title
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Count of Days Asleep");
d3.interval(function(){
update(data)
}, 1000);
update(data);
function update(data){
// X and Y axis domains
x.domain([0, d3.max(data, function(d) { return +d.x })])
var xAxisCall = d3.axisBottom(x);
var yAxisCall = d3.axisLeft(y);
xAxisGroup.call(xAxisCall);
yAxisGroup.call(yAxisCall);
var histogram = d3.histogram()
.value(function(d) { return d.x })
.domain(x.domain())
.thresholds(x.ticks());
var bins = histogram(data);
y.domain([0, d3.max(bins, function(d){ return d.length; })]);
// Append bars
svg.selectAll("bar")
.data(bins)
.enter()
.append("rect")
.attr("x", 1)
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; })
.attr("width", function(d) { return x(d.x1) - x(d.x0) - 1 ; })
.attr("height", function(d) { return height - y(d.length); })
.style("fill", "steelblue")
.attr("id", "sleep-dist-histo");
svg.selectAll("label")
.data(bins)
.enter()
.append("text")
.text(function(d){ return d.length })
.attr("x", function(d){ return (x(d.x0) + x(d.x1)) / 2; })
.attr("y", function(d){ return y(d.length) - 15; })
.attr("class", "label")
.attr("dy", ".71em")
// .style("text-anchor", "end")
;
}
</script>

It is because the domain of x is [0, 9.34], so it ended up with 9.34 instead of 10 (to keep the same width)
Solution:
add .nice() to x to "Extends the domain so that it starts and ends on nice round values. " (See Here);
remove the last element of the new bin data otherwise there will be a zero from 10 to 11
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<style>
#sleep-dist-histo {
stroke-width: 0.5;
stroke: goldenrod;
}
</style>
<script>
var data = [
{ "x": "7.41" },
{ "x": "7.01" },
{ "x": "8.39" },
{ "x": "8.22" },
{ "x": "8.31" },
{ "x": "8.33" },
{ "x": "8.16" },
{ "x": "8.00" },
{ "x": "6.04" },
{ "x": "8.18" },
{ "x": "7.47" },
{ "x": "7.57" },
{ "x": "7.11" },
{ "x": "6.59" },
{ "x": "7.51" },
{ "x": "6.31" },
{ "x": "8.01" },
{ "x": "8.14" },
{ "x": "8.26" },
{ "x": "7.44" },
{ "x": "7.54" },
{ "x": "6.23" },
{ "x": "6.25" },
{ "x": "7.50" },
{ "x": "7.45" },
{ "x": "9.34" },
{ "x": "7.53" },
{ "x": "7.11" },
{ "x": "8.17" },
{ "x": "8.02" },
{ "x": "8.12" },
{ "x": "8.14" },
{ "x": "7.49" },
{ "x": "6.35" },
{ "x": "7.07" },
{ "x": "5.46" },
{ "x": "6.56" },
{ "x": "7.41" },
{ "x": "8.06" },
{ "x": "7.31" },
{ "x": "8.53" },
{ "x": "7.42" },
{ "x": "7.41" },
{ "x": "7.57" },
{ "x": "7.38" },
{ "x": "8.04" },
{ "x": "6.29" },
{ "x": "7.52" },
{ "x": "7.42" },
{ "x": "8.06" },
{ "x": "8.09" },
{ "x": "8.03" },
{ "x": "7.06" },
{ "x": "7.33" },
{ "x": "8.09" },
{ "x": "7.47" },
{ "x": "7.54" },
{ "x": "7.31" },
{ "x": "7.30" },
{ "x": "7.50" },
{ "x": "4.07" },
{ "x": "8.22" },
{ "x": "7.44" }
]
// console.log(data)
var margin = { top: 15, right: 30, bottom: 50, left: 60 }
var width = 800 - margin.left - margin.right;
var height = 800 - margin.top - margin.bottom;
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // translate(margin left, margin top)
// Append x-scale and x-scale tick labels
var xAxisGroup = svg.append("g")
.attr("class", "y-axis")
.attr("transform", "translate(" + 0 + "," + height + ")");
// Set x-scale range
var x = d3.scaleLinear()
.range([0, width]);
// Append x-scale title
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," + (height + margin.bottom) + ")")
.style("text-anchor", "middle")
.text("Time Asleep in Hours (Bins)");
// Set y-scale range
var y = d3.scaleLinear()
.range([height, margin.top]);
// Append y-scale
var yAxisGroup = svg.append("g")
.attr("class", "y-axis");
// Append y-scale title
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Count of Days Asleep");
/* d3.interval(function(){
update(data)
}, 1000);
*/
update(data);
function update(data){
// X and Y axis domains
x.domain([0, d3.max(data, function(d) { return (+d.x) })]).nice()
var xAxisCall = d3.axisBottom(x);
var yAxisCall = d3.axisLeft(y);
xAxisGroup.call(xAxisCall);
yAxisGroup.call(yAxisCall);
var histogram = d3.histogram()
.value(function(d) { return +d.x })
.domain(x.domain())
.thresholds(x.ticks());
var bins = histogram(data);
y.domain([0, d3.max(bins, function(d){ return d.length; })]);
bins = bins.slice(0, bins.length-1)
// Append bars
svg.selectAll("bar")
.data(bins)
.enter()
.append("rect")
.attr("x", 1)
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; })
.attr("width", function(d) { return x(d.x1) - x(d.x0) - 1 ; })
.attr("height", function(d) { return height - y(d.length); })
.style("fill", "steelblue")
.attr("id", "sleep-dist-histo");
svg.selectAll("label")
.data(bins)
.enter()
.append("text")
.text(function(d){ return d.length })
.attr("x", function(d){ return (x(d.x0) + x(d.x1)) / 2; })
.attr("y", function(d){ return y(d.length) - 15; })
.attr("class", "label")
.attr("dy", ".71em")
// .style("text-anchor", "end")
;
}
</script>

Related

Convert string timestamp to hour axis

I am trying to create a scatter plot based on two timestamps in d3, but i'm not sure the proper way to use the d3-time-format methods to properly parse the values in the timestamp format and build a range based on a 24-hour period. So far I built a function to loop through my array and use the d3 methods to convert the string to a d3-readable format, but I can seem to figure out how to format the output so it converts from date format to time format. My questions are does d3 accept time format? And how can I go about converting the date object to a time object? At the moment I have a y-axis that presents ticks for years.
Provided is my full code:
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<style>
div.tooltip {
position: absolute;
text-align: center;
width: 100px;
height: 30px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
</style>
<script>
var data = [
{
"x": "23:19:30",
"y": "08:07:00"
},
{
"x": "22:55:30",
"y": "06:08:00"
},
{
"x": "21:14:30",
"y": "06:13:30"
},
{
"x": "21:24:30",
"y": "06:04:30"
},
{
"x": "21:24:00",
"y": "06:04:00"
},
{
"x": "21:28:00",
"y": "06:09:00"
},
{
"x": "23:23:30",
"y": "08:20:30"
},
{
"x": "23:47:00",
"y": "08:01:30"
},
{
"x": "00:03:00",
"y": "06:49:30"
},
{
"x": "21:46:00",
"y": "06:21:00"
},
{
"x": "21:58:00",
"y": "06:02:30"
},
{
"x": "21:33:00",
"y": "05:56:00"
},
{
"x": "22:33:00",
"y": "06:15:30"
},
{
"x": "23:49:00",
"y": "07:10:30"
},
{
"x": "23:46:30",
"y": "08:35:30"
},
{
"x": "23:15:30",
"y": "05:59:30"
},
{
"x": "21:26:00",
"y": "06:05:00"
},
{
"x": "21:26:30",
"y": "05:54:00"
},
{
"x": "21:06:00",
"y": "05:53:00"
},
{
"x": "21:25:00",
"y": "05:47:30"
},
{
"x": "00:29:30",
"y": "08:59:30"
},
{
"x": "01:14:00",
"y": "08:09:30"
},
{
"x": "23:12:30",
"y": "06:06:30"
},
{
"x": "21:26:00",
"y": "05:52:30"
},
{
"x": "21:18:30",
"y": "05:47:00"
},
{
"x": "20:54:30",
"y": "07:07:30"
},
{
"x": "21:36:00",
"y": "05:53:30"
},
{
"x": "00:28:00",
"y": "08:00:00"
},
{
"x": "23:21:30",
"y": "07:58:30"
},
{
"x": "21:34:00",
"y": "05:51:00"
},
{
"x": "21:23:30",
"y": "05:58:00"
},
{
"x": "21:05:30",
"y": "05:53:00"
},
{
"x": "21:33:30",
"y": "05:39:30"
},
{
"x": "23:49:30",
"y": "06:50:00"
},
{
"x": "01:11:00",
"y": "08:37:30"
},
{
"x": "22:34:30",
"y": "05:15:00"
},
{
"x": "22:49:30",
"y": "05:55:00"
},
{
"x": "22:06:30",
"y": "06:03:00"
},
{
"x": "21:32:30",
"y": "06:01:00"
},
{
"x": "21:49:00",
"y": "05:39:30"
},
{
"x": "22:47:30",
"y": "08:27:30"
},
{
"x": "21:26:30",
"y": "05:51:00"
},
{
"x": "21:47:30",
"y": "05:51:00"
},
{
"x": "21:28:00",
"y": "05:47:30"
},
{
"x": "21:32:00",
"y": "05:47:30"
},
{
"x": "21:13:30",
"y": "05:46:00"
},
{
"x": "23:42:30",
"y": "06:45:00"
},
{
"x": "21:33:00",
"y": "05:48:00"
},
{
"x": "21:45:00",
"y": "05:51:00"
},
{
"x": "21:29:30",
"y": "06:06:00"
},
{
"x": "21:16:00",
"y": "05:43:00"
},
{
"x": "21:14:00",
"y": "05:46:30"
},
{
"x": "00:01:30",
"y": "07:25:30"
},
{
"x": "02:24:00",
"y": "10:35:30"
},
{
"x": "22:29:30",
"y": "07:04:00"
},
{
"x": "21:43:30",
"y": "05:51:00"
},
{
"x": "21:31:30",
"y": "05:45:00"
},
{
"x": "22:16:30",
"y": "05:50:30"
},
{
"x": "21:59:00",
"y": "05:47:00"
},
{
"x": "02:55:30",
"y": "11:15:30"
},
{
"x": "02:57:00",
"y": "07:23:00"
},
{
"x": "21:49:30",
"y": "06:48:30"
},
{
"x": "21:31:30",
"y": "05:26:30"
}
]
// D3 date parser
for (var i=0; i < data.length; i++){
var parser = d3.timeParse("%I:%M:%S")
data[i].x = parser(data[i].x);
data[i].y = parser(data[i].y);
}
console.log(data)
var margin = { top: 10, right: 30, bottom: 30, left: 60 }
var width = 800 - margin.left - margin.right;
var height = 800 - margin.top - margin.bottom;
// Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // translate(margin left, margin top)
var x = d3.scaleTime()
.domain([d3.min(data, function(d) { return d.date }), d3.max(data, function(d) { return d.date })])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(" + 0 + "," + height + ")")
.call(d3.axisBottom(x))
// .tickFormat(d3.time.format);
// text label for the x axis
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," + (height + margin.top + 20) + ")")
.style("text-anchor", "middle")
.text("Date");
var y = d3.scaleTime()
.domain([0, d3.max(data, function(d){ return +d.y })])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// text label for the y axis
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Time Asleep (Minutes)");
// Add line path
svg.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(function(d) { return x(d.date) })
.y(function(d) { return y(d.y) })
);
// Add the scatterplot (data points)
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3)
.attr("cx", function(d){ return x(d.date) })
.attr("cy", function(d){ return y(d.y) })
// Add tooltip on hover
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d.x + "<br/>" + d.label)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 30) + "px")
})
// Remove tooltip after hover
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
</script>
To format the date, use d3.timeParse. This will parse any date string to a Date object. D3 can understand and work with time objects with timeScale.
I've renamed your original data to rawData. We also need to sort the data by the X axis:
var toDate = d3.timeParse("%H:%M:%S")
var data = rawData.map(d => ({
x: toDate(d.x),
y: toDate(d.y),
})).sort((a, b) => d3.descending(a.x, b.x)
To calculate the domain, use d3.extent which will compute the domain automatically.
var x = d3.scaleTime()
.domain(d3.extent(data, d => d.x))
.range([0, width]);
<head></head>
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<style>
div.tooltip {
position: absolute;
text-align: center;
width: 100px;
height: 30px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
</style>
<script>
var rawData = [
{
"x": "23:19:30",
"y": "08:07:00"
},
{
"x": "22:55:30",
"y": "06:08:00"
},
{
"x": "21:14:30",
"y": "06:13:30"
},
{
"x": "21:24:30",
"y": "06:04:30"
},
{
"x": "21:24:00",
"y": "06:04:00"
},
{
"x": "21:28:00",
"y": "06:09:00"
},
{
"x": "23:23:30",
"y": "08:20:30"
},
{
"x": "23:47:00",
"y": "08:01:30"
},
{
"x": "00:03:00",
"y": "06:49:30"
},
{
"x": "21:46:00",
"y": "06:21:00"
},
{
"x": "21:58:00",
"y": "06:02:30"
},
{
"x": "21:33:00",
"y": "05:56:00"
},
{
"x": "22:33:00",
"y": "06:15:30"
},
{
"x": "23:49:00",
"y": "07:10:30"
},
{
"x": "23:46:30",
"y": "08:35:30"
},
{
"x": "23:15:30",
"y": "05:59:30"
},
{
"x": "21:26:00",
"y": "06:05:00"
},
{
"x": "21:26:30",
"y": "05:54:00"
},
{
"x": "21:06:00",
"y": "05:53:00"
},
{
"x": "21:25:00",
"y": "05:47:30"
},
{
"x": "00:29:30",
"y": "08:59:30"
},
{
"x": "01:14:00",
"y": "08:09:30"
},
{
"x": "23:12:30",
"y": "06:06:30"
},
{
"x": "21:26:00",
"y": "05:52:30"
},
{
"x": "21:18:30",
"y": "05:47:00"
},
{
"x": "20:54:30",
"y": "07:07:30"
},
{
"x": "21:36:00",
"y": "05:53:30"
},
{
"x": "00:28:00",
"y": "08:00:00"
},
{
"x": "23:21:30",
"y": "07:58:30"
},
{
"x": "21:34:00",
"y": "05:51:00"
},
{
"x": "21:23:30",
"y": "05:58:00"
},
{
"x": "21:05:30",
"y": "05:53:00"
},
{
"x": "21:33:30",
"y": "05:39:30"
},
{
"x": "23:49:30",
"y": "06:50:00"
},
{
"x": "01:11:00",
"y": "08:37:30"
},
{
"x": "22:34:30",
"y": "05:15:00"
},
{
"x": "22:49:30",
"y": "05:55:00"
},
{
"x": "22:06:30",
"y": "06:03:00"
},
{
"x": "21:32:30",
"y": "06:01:00"
},
{
"x": "21:49:00",
"y": "05:39:30"
},
{
"x": "22:47:30",
"y": "08:27:30"
},
{
"x": "21:26:30",
"y": "05:51:00"
},
{
"x": "21:47:30",
"y": "05:51:00"
},
{
"x": "21:28:00",
"y": "05:47:30"
},
{
"x": "21:32:00",
"y": "05:47:30"
},
{
"x": "21:13:30",
"y": "05:46:00"
},
{
"x": "23:42:30",
"y": "06:45:00"
},
{
"x": "21:33:00",
"y": "05:48:00"
},
{
"x": "21:45:00",
"y": "05:51:00"
},
{
"x": "21:29:30",
"y": "06:06:00"
},
{
"x": "21:16:00",
"y": "05:43:00"
},
{
"x": "21:14:00",
"y": "05:46:30"
},
{
"x": "00:01:30",
"y": "07:25:30"
},
{
"x": "02:24:00",
"y": "10:35:30"
},
{
"x": "22:29:30",
"y": "07:04:00"
},
{
"x": "21:43:30",
"y": "05:51:00"
},
{
"x": "21:31:30",
"y": "05:45:00"
},
{
"x": "22:16:30",
"y": "05:50:30"
},
{
"x": "21:59:00",
"y": "05:47:00"
},
{
"x": "02:55:30",
"y": "11:15:30"
},
{
"x": "02:57:00",
"y": "07:23:00"
},
{
"x": "21:49:30",
"y": "06:48:30"
},
{
"x": "21:31:30",
"y": "05:26:30"
}
]
// D3 date parser
var toDate = d3.timeParse("%H:%M:%S")
var data = rawData.map(d => ({
x: toDate(d.x),
y: toDate(d.y),
})).sort((a, b) => d3.descending(a.x, b.x))
console.log(data[0])
var margin = { top: 10, right: 30, bottom: 30, left: 60 }
var width = 800 - margin.left - margin.right;
var height = 800 - margin.top - margin.bottom;
// Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // translate(margin left, margin top)
var x = d3.scaleTime()
.domain(d3.extent(data, d => d.x))
.range([0, width]);
svg.append("g")
.attr("transform", "translate(" + 0 + "," + height + ")")
.call(d3.axisBottom(x))
// .tickFormat(d3.time.format);
// text label for the x axis
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," + (height + margin.top + 20) + ")")
.style("text-anchor", "middle")
.text("Date");
var y = d3.scaleTime()
.domain(d3.extent(data, d => d.y))
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// text label for the y axis
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Time Asleep (Minutes)");
// Add line path
svg.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(function(d) { return x(d.x) })
.y(function(d) { return y(d.y) })
);
// Add the scatterplot (data points)
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3)
.attr("cx", function(d){ return x(d.x) })
.attr("cy", function(d){ return y(d.y) })
// Add tooltip on hover
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d.x + "<br/>" + d.label)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 30) + "px")
})
// Remove tooltip after hover
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
</script>

Making an existing vertical bar chart horizontal

I have an existing bar chart which works fine.
But I want to make it horizontal by flipping the axis.
Full code:
$(document).ready(function() {
render_chart();
});
function render_chart() {
var stack = d3.stack;
var dataset = {
"categories": ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
"series": ["Group1", "Group2", "Group3"],
"colors": ["#3498db", "#e74c3c", "#2ecc71"],
"layers": [
[{
"y": 1,
"y0": 20,
"month": "Monday"
},
{
"y": 2,
"y0": 18,
"month": "Tuesday"
},
{
"y": 5,
"y0": 18,
"month": "Wednesday"
},
{
"y": 10,
"y0": 20,
"month": "Thursday"
},
{
"y": 14,
"y0": 23,
"month": "Friday"
},
{
"y": 18,
"y0": 24,
"month": "Saturday"
},
{
"y": 20,
"y0": 24,
"month": "Sunday"
}
],
[{
"y": 12,
"y0": 24,
"month": "Monday"
},
{
"y": 14,
"y0": 24,
"month": "Tuesday"
},
{
"y": 13,
"y0": 23,
"month": "Wednesday"
},
{
"y": 16,
"y0": 21,
"month": "Thursday"
},
{
"y": 18,
"y0": 23,
"month": "Friday"
},
{
"y": 19,
"y0": 22,
"month": "Saturday"
},
{
"y": 20,
"y0": 22,
"month": "Sunday"
}
],
[{
"y": 8,
"y0": 24,
"month": "Monday"
},
{
"y": 14,
"y0": 21,
"month": "Tuesday"
},
{
"y": 12,
"y0": 19,
"month": "Wednesday"
},
{
"y": 15,
"y0": 23,
"month": "Thursday"
},
{
"y": 18,
"y0": 21,
"month": "Friday"
},
{
"y": 16,
"y0": 23,
"month": "Saturday"
},
{
"y": 17,
"y0": 24,
"month": "Sunday"
}
]
]
};
n = dataset["series"].length;
m = dataset["layers"].length;
yGroupMax = d3.max(dataset["layers"], function(layer) {
return d3.max(layer, function(d) {
return d.y0;
});
});
yGroupMin = d3.min(dataset["layers"], function(layer) {
return d3.min(layer, function(d) {
return d.y;
});
});
var margin = {
top: 50,
right: 50,
bottom: 50,
left: 100
},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var xScale = d3.scaleBand()
.domain(dataset["categories"])
.rangeRound([0, width])
.padding(.08);
var yScale = d3.scaleLinear()
.domain([yGroupMin, yGroupMax])
.range([height, 0]);
var xAxis = d3.axisBottom(xScale)
.tickSize(7)
.tickPadding(6);
var yAxis = d3.axisLeft(yScale).ticks(24);
var svg = d3.select("#groupchart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var layer = svg.selectAll(".layer")
.data(dataset["layers"])
.enter().append("g")
.attr("class", "layer");
var rect = layer.selectAll("rect")
.data(function(d, i) {
d.map(function(b) {
b.colorIndex = i;
return b;
});
return d;
})
.enter()
.append("rect")
.transition()
.duration(500)
.delay(function(d, i) {
return i * 10;
})
.attr("x", function(d, i, j) {
var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes, j[i].parentNode);
return xScale(d.month) + xScale.bandwidth() / n * k;
})
.attr("width", xScale.bandwidth() / n)
.transition()
.attr("y", function(d) {
return yScale(d.y0);
})
.attr("height", function(d) {
return height - yScale(d.y0 - d.y)
})
.attr("class", "bar")
.style("fill", function(d) {
return dataset["colors"][d.colorIndex];
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.select("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("x", width / 3)
.attr("y", 0)
.attr("dx", ".71em")
.attr("dy", "-.71em")
.text("Grouped Bar Chart Test");
var legend = svg.append("g")
.attr("class", "legend")
legend.selectAll('text')
.data(dataset["colors"])
.enter()
.append("rect")
.attr("x", function(d, i) {
return (i * 120) + (width / 3);
})
.attr("y", (width - margin.right) / 1.6)
.attr("width", 10)
.attr("height", 10)
.style("fill", function(d) {
return d;
})
legend.selectAll('text')
.data(dataset["series"])
.enter()
.append("text")
.attr("x", function(d, i) {
return (i * 120) + (width / 3) + 12;
})
.attr("y", ((width - margin.right) / 1.6) + 9)
.text(function(d) {
return d;
});
var tooltip = d3.select("body")
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'month');
tooltip.append('div')
.attr('class', 'tempRange');
svg.selectAll("rect")
.on('mouseover', function(d) {
if (!d.month) return null;
tooltip.select('.month').html("<b>" + d.month + "</b>");
tooltip.select('.tempRange').html(d.y + ":00 Hours to " + d.y0 + ":00 Hours");
tooltip.style('display', 'block');
tooltip.style('opacity', 2);
})
.on('mousemove', function(d) {
if (!d.month) return null;
tooltip.style('top', (d3.event.layerY + 10) + 'px')
.style('left', (d3.event.layerX - 25) + 'px');
})
.on('mouseout', function() {
tooltip.style('display', 'none');
tooltip.style('opacity', 0);
});
}
.axis path,
.axis line {
fill: none;
font: 10px sans-serif;
stroke: #000;
shape-rendering: crispEdges;
}
.legend {
padding: 5px;
font-size: 15px;
font-family: 'Roboto', sans-serif;
background: yellow;
box-shadow: 2px 2px 1px #888;
}
.tooltip {
background: #eee;
box-shadow: 0 0 5px #999999;
color: #333;
font-size: 12px;
left: 130px;
padding: 10px;
position: absolute;
text-align: center;
top: 95px;
z-index: 10;
display: block;
opacity: 0;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Grouped Bar Graph</title>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<div id="groupchart" class="chart"></div>
</body>
</html>
I tried to do it but I mess it up everytime. When I try to flip the axis by changing the portion below which is just reversing the x and y, it does not appear correctly:
var xAxis = d3.axisBottom(xScale)
.tickSize(7)
.tickPadding(6);
var yAxis = d3.axisLeft(yScale).ticks(24);
I can't figure this out.
Why:
So that I can fit more stuff per group of bars when they are horizontal as they are going to be long and have more space inside.
Here is a reversed (horizontal) version of your chart:
$(document).ready(function() {
render_chart();
});
function render_chart() {
var stack = d3.stack;
var dataset = {
"categories": ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
"series": ["Group1", "Group2", "Group3"],
"colors": ["#3498db", "#e74c3c", "#2ecc71"],
"layers": [
[{
"y": 1,
"y0": 20,
"month": "Monday"
},
{
"y": 2,
"y0": 18,
"month": "Tuesday"
},
{
"y": 5,
"y0": 18,
"month": "Wednesday"
},
{
"y": 10,
"y0": 20,
"month": "Thursday"
},
{
"y": 14,
"y0": 23,
"month": "Friday"
},
{
"y": 18,
"y0": 24,
"month": "Saturday"
},
{
"y": 20,
"y0": 24,
"month": "Sunday"
}
],
[{
"y": 12,
"y0": 24,
"month": "Monday"
},
{
"y": 14,
"y0": 24,
"month": "Tuesday"
},
{
"y": 13,
"y0": 23,
"month": "Wednesday"
},
{
"y": 16,
"y0": 21,
"month": "Thursday"
},
{
"y": 18,
"y0": 23,
"month": "Friday"
},
{
"y": 19,
"y0": 22,
"month": "Saturday"
},
{
"y": 20,
"y0": 22,
"month": "Sunday"
}
],
[{
"y": 8,
"y0": 24,
"month": "Monday"
},
{
"y": 14,
"y0": 21,
"month": "Tuesday"
},
{
"y": 12,
"y0": 19,
"month": "Wednesday"
},
{
"y": 15,
"y0": 23,
"month": "Thursday"
},
{
"y": 18,
"y0": 21,
"month": "Friday"
},
{
"y": 16,
"y0": 23,
"month": "Saturday"
},
{
"y": 17,
"y0": 24,
"month": "Sunday"
}
]
]
};
n = dataset["series"].length;
m = dataset["layers"].length;
xGroupMax = d3.max(dataset["layers"], function(layer) {
return d3.max(layer, function(d) {
return d.y0;
});
});
xGroupMin = d3.min(dataset["layers"], function(layer) {
return d3.min(layer, function(d) {
return d.y;
});
});
var margin = {
top: 50,
right: 50,
bottom: 50,
left: 100
},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var xScale = d3.scaleLinear() //
.domain([xGroupMin, xGroupMax]) //
.range([0, width - margin.left]); //
var yScale = d3.scaleBand() //
.domain(dataset["categories"])
.rangeRound([0, height]) //
.padding(.08);
var xAxis = d3.axisBottom(xScale).ticks(24); //
var yAxis = d3.axisLeft(yScale).tickSize(7).tickPadding(6); //
var svg = d3.select("#groupchart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var layer = svg.selectAll(".layer")
.data(dataset["layers"])
.enter().append("g")
.attr("class", "layer");
var rect = layer.selectAll("rect")
.data(function(d, i) {
d.map(function(b) {
b.colorIndex = i;
return b;
});
return d;
})
.enter()
.append("rect")
.transition()
.duration(500)
.delay(function(d, i) {
return i * 10;
})
.attr("y", function(d, i, j) { //
var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes, j[i].parentNode);
return yScale(d.month) + yScale.bandwidth() / n * k; //
})
.attr("height", yScale.bandwidth() / n) //
.transition()
.attr("x", function(d) { //
return xScale(d.y) //
})
.attr("width", function(d) { //
return xScale(d.y0 - d.y); //
})
.attr("class", "bar")
.style("fill", function(d) {
return dataset["colors"][d.colorIndex];
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.select("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("x", width / 3)
.attr("y", 0)
.attr("dx", ".71em")
.attr("dy", "-.71em")
.text("Grouped Bar Chart Test");
var legend = svg.append("g")
.attr("class", "legend")
legend.selectAll('text')
.data(dataset["colors"])
.enter()
.append("rect")
.attr("x", function(d, i) {
return (i * 120) + (width / 3);
})
.attr("y", (width - margin.right) / 1.6)
.attr("width", 10)
.attr("height", 10)
.style("fill", function(d) {
return d;
})
legend.selectAll('text')
.data(dataset["series"])
.enter()
.append("text")
.attr("x", function(d, i) {
return (i * 120) + (width / 3) + 12;
})
.attr("y", ((width - margin.right) / 1.6) + 9)
.text(function(d) {
return d;
});
var tooltip = d3.select("body")
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'month');
tooltip.append('div')
.attr('class', 'tempRange');
svg.selectAll("rect")
.on('mouseover', function(d) {
if (!d.month) return null;
tooltip.select('.month').html("<b>" + d.month + "</b>");
tooltip.select('.tempRange').html(d.y + ":00 Hours to " + d.y0 + ":00 Hours");
tooltip.style('display', 'block');
tooltip.style('opacity', 2);
})
.on('mousemove', function(d) {
if (!d.month) return null;
tooltip.style('top', (d3.event.layerY + 10) + 'px')
.style('left', (d3.event.layerX - 25) + 'px');
})
.on('mouseout', function() {
tooltip.style('display', 'none');
tooltip.style('opacity', 0);
});
}
.axis path,
.axis line {
fill: none;
font: 10px sans-serif;
stroke: #000;
shape-rendering: crispEdges;
}
.legend {
padding: 5px;
font-size: 15px;
font-family: 'Roboto', sans-serif;
background: yellow;
box-shadow: 2px 2px 1px #888;
}
.tooltip {
background: #eee;
box-shadow: 0 0 5px #999999;
color: #333;
font-size: 12px;
left: 130px;
padding: 10px;
position: absolute;
text-align: center;
top: 95px;
z-index: 10;
display: block;
opacity: 0;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Grouped Bar Graph</title>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id="groupchart" class="chart"></div>
</body>
</html>
This requires a bit more than just switching x and y axes. You'll also have to modify the w/y/width/height attributes of rectangles, as well as the the position of axes.
I've marked the modified lines with //.
Just for the fun (and if like me, starting from a vertical graph, you're lost doing all these inversions of axes/scales), here is another way of doing using a rotation by 90° of the whole graph!:
$(document).ready(function() {
render_chart();
});
function render_chart() {
var stack = d3.stack;
var dataset = {
"categories": ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
"series": ["Group1", "Group2", "Group3"],
"colors": ["#3498db", "#e74c3c", "#2ecc71"],
"layers": [
[{
"y": 1,
"y0": 20,
"month": "Monday"
},
{
"y": 2,
"y0": 18,
"month": "Tuesday"
},
{
"y": 5,
"y0": 18,
"month": "Wednesday"
},
{
"y": 10,
"y0": 20,
"month": "Thursday"
},
{
"y": 14,
"y0": 23,
"month": "Friday"
},
{
"y": 18,
"y0": 24,
"month": "Saturday"
},
{
"y": 20,
"y0": 24,
"month": "Sunday"
}
],
[{
"y": 12,
"y0": 24,
"month": "Monday"
},
{
"y": 14,
"y0": 24,
"month": "Tuesday"
},
{
"y": 13,
"y0": 23,
"month": "Wednesday"
},
{
"y": 16,
"y0": 21,
"month": "Thursday"
},
{
"y": 18,
"y0": 23,
"month": "Friday"
},
{
"y": 19,
"y0": 22,
"month": "Saturday"
},
{
"y": 20,
"y0": 22,
"month": "Sunday"
}
],
[{
"y": 8,
"y0": 24,
"month": "Monday"
},
{
"y": 14,
"y0": 21,
"month": "Tuesday"
},
{
"y": 12,
"y0": 19,
"month": "Wednesday"
},
{
"y": 15,
"y0": 23,
"month": "Thursday"
},
{
"y": 18,
"y0": 21,
"month": "Friday"
},
{
"y": 16,
"y0": 23,
"month": "Saturday"
},
{
"y": 17,
"y0": 24,
"month": "Sunday"
}
]
]
};
n = dataset["series"].length;
m = dataset["layers"].length;
yGroupMax = d3.max(dataset["layers"], function(layer) {
return d3.max(layer, function(d) {
return d.y0;
});
});
yGroupMin = d3.min(dataset["layers"], function(layer) {
return d3.min(layer, function(d) {
return d.y;
});
});
var margin = {
top: 50,
right: 50,
bottom: 50,
left: 100
},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var xScale = d3.scaleBand()
.domain(dataset["categories"])
.rangeRound([0, width])
.padding(.08);
var yScale = d3.scaleLinear()
.domain([yGroupMin, yGroupMax])
.range([height, 0]);
var xAxis = d3.axisBottom(xScale)
.tickSize(7)
.tickPadding(6);
var yAxis = d3.axisLeft(yScale).ticks(24);
var svg = d3.select("#groupchart").append("svg")
.attr("height", width + margin.left + margin.right) //
.attr("width", height + margin.left + margin.bottom) //
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.left + ") rotate(90) translate(0,-400)"); //
var layer = svg.selectAll(".layer")
.data(dataset["layers"])
.enter().append("g")
.attr("class", "layer");
var rect = layer.selectAll("rect")
.data(function(d, i) {
d.map(function(b) {
b.colorIndex = i;
return b;
});
return d;
})
.enter()
.append("rect")
.transition()
.duration(500)
.delay(function(d, i) {
return i * 10;
})
.attr("x", function(d, i, j) {
var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes, j[i].parentNode);
return xScale(d.month) + xScale.bandwidth() / n * k;
})
.attr("width", xScale.bandwidth() / n)
.transition()
.attr("y", function(d) {
return yScale(d.y0);
})
.attr("height", function(d) {
return height - yScale(d.y0 - d.y)
})
.attr("class", "bar")
.style("fill", function(d) {
return dataset["colors"][d.colorIndex];
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text") //
.attr("transform", "rotate(-90) translate(-10,-18)") //
.style("text-anchor", "end"); //
svg.select("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text") //
.attr("transform", "rotate(-90) translate(13,-18)") //
.style("text-anchor", "end"); //
svg.append("text")
.attr("dx", ".71em")
.attr("dy", "-.71em")
.text("Grouped Bar Chart Test (Rien ne va plus)")
.attr("transform", "rotate(-90) translate(-350,-35)"); //
var legend = svg.append("g")
.attr("class", "legend")
legend.selectAll('text')
.data(dataset["colors"])
.enter()
.append("rect")
.attr("x", function(d, i) {
return (i * 120) + (width / 3);
})
.attr("y", (width - margin.right) / 1.6)
.attr("width", 10)
.attr("height", 10)
.style("fill", function(d) {
return d;
})
.attr("transform", "rotate(-90) translate(-600," + (width/2 - 30) + ")"); //
legend.selectAll('text')
.data(dataset["series"])
.enter()
.append("text")
.attr("x", function(d, i) {
return (i * 120) + (width / 3) + 12;
})
.attr("y", ((width - margin.right) / 1.6) + 9)
.text(function(d) {
return d;
})
.attr("transform", "rotate(-90) translate(-600," + (width/2 - 30) + ")"); //
var tooltip = d3.select("body")
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'month');
tooltip.append('div')
.attr('class', 'tempRange');
svg.selectAll("rect")
.on('mouseover', function(d) {
if (!d.month) return null;
tooltip.select('.month').html("<b>" + d.month + "</b>");
tooltip.select('.tempRange').html(d.y + ":00 Hours to " + d.y0 + ":00 Hours");
tooltip.style('display', 'block');
tooltip.style('opacity', 2);
})
.on('mousemove', function(d) {
if (!d.month) return null;
tooltip.style('top', (d3.event.layerY + 10) + 'px')
.style('left', (d3.event.layerX - 25) + 'px');
})
.on('mouseout', function() {
tooltip.style('display', 'none');
tooltip.style('opacity', 0);
});
}
.axis path,
.axis line {
fill: none;
font: 10px sans-serif;
stroke: #000;
shape-rendering: crispEdges;
}
.legend {
padding: 5px;
font-size: 15px;
font-family: 'Roboto', sans-serif;
background: yellow;
box-shadow: 2px 2px 1px #888;
}
.tooltip {
background: #eee;
box-shadow: 0 0 5px #999999;
color: #333;
font-size: 12px;
left: 130px;
padding: 10px;
position: absolute;
text-align: center;
top: 95px;
z-index: 10;
display: block;
opacity: 0;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Grouped Bar Graph</title>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id="groupchart" class="chart"></div>
</body>
</html>
Note: this is of course not recommended.

d3 connect visibility attribute to slider position

hey guys i am currently trying to change the visibility of 4 lines depending on whether the slider position is at the same "year-tick" as i have in my data.
for example: i would want line 3 which has the value "1994" stored to only be visible as long as the slider is on position "1994"
i havent workder with sliders yet, which is why i am having this much trouble getting this to work.
here is a fiddle of my code: https://fiddle.jshell.net/42jdw2Lt/3/
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js">
</script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>
<script>
var width = 1500;
var height = 1500;
var margin = {top: 50, left: 50, right: 50, bottom:50},
height = 650-margin.top-margin.bottom,
width = 1200-margin.left-margin.right;
var svg = d3.select("body").append("svg")
.attr("width", width+margin.left+margin.right)
.attr("height", height+margin.top+margin.bottom)
.append("g")
.attr("transform", "translate("+margin.left+","+margin.top+")")
var strwi = d3.scaleLinear()
.domain([100, 400])
.range([7,35])
var group = svg.append("g")
var series = [
[{"x": 360, "y": 250, "num": 100}, {"x": 520, "y": 400, "num": 100}, {"x":
630, "y": 300, "num": 100, "year": 1991}],
[{"x": 71, "y": 45, "num": 200}, {"x": 32, "y": 39, "num": 200}, {"x": 43,
"y": 70, "num": 200, "year": 1992}],
[{"x": 100, "y": 300, "num": 300}, {"x": 200, "y": 200, "num": 300}, {"x":
300, "y": 200, "num": 300, "year": 1994}],
[{"x": 101, "y": 202, "num": 400}, {"x": 102, "y": 204, "num": 400}, {"x":
103, "y": 215, "num": 400, "year": 1995}]
];
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
group.selectAll(".line")
.data(series)
.enter().append("path")
.attr("class", "line")
// .attr("visibility", "hidden")
.attr("stroke-width", function(d) {return strwi(d); })
.attr("stroke", "black")
.attr("fill", "none")
.attr("d", line);
var data = [1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
2000, 2001, 2002]
var x = d3.scaleLinear()
.domain(d3.extent(data))
.range([0, width])
.clamp(true);
var slider = svg.append("g")
.attr("class", "slider")
.attr("transform", "translate(" + 0 + "," + 300 + ")");
slider.append("line")
.attr("class", "track")
.attr("x1", x.range()[0])
.attr("x2", x.range()[1])
.attr("stroke", "black")
.attr("stroke-width", "4")
.select(function() { return
this.parentNode.appendChild(this.cloneNode(true)); })
.attr("class", "track-inset")
.select(function() { return
this.parentNode.appendChild(this.cloneNode(true)); })
.attr("class", "track-overlay")
.call(d3.drag()
.on("start.interrupt", function() { slider.interrupt(); })
.on("start drag", function() { hue(x.invert(d3.event.x));
}));
slider.insert("g", ".track-overlay")
.attr("class", "ticks")
.selectAll("ticks")
.data(x.ticks(data.length))
.enter().append("text")
.attr("x", x)
.attr("text-anchor", "middle")
.attr("transform", "translate(0," + 30 + ")")
.text(function(d) { return d; })
.exit()
.data(x.ticks(data.length * 2))
.enter().append("circle")
.attr("cx", x)
.attr("r", 3)
.attr("fill", "#c1c7cd");
slider.insert("g", ".track-overlay")
.attr("class", "ticks--cirlces")
.selectAll("ticks--ticks");
var handle = slider.insert("circle", ".track-overlay")
.attr("class", "handle")
.attr("r", 9);
slider.transition() // Gratuitous intro!
.duration(750)
.tween("hue", function() {
var i = d3.interpolate(0, 70);
return function(t) { hue(i(t)); };
});
function hue(h) {
handle.attr("cx", x(h));
d3.select(".text")
.text( (Math.round(h*2)/2).toFixed(1) );
}
</script>
I added the prop year to all your elements in the data selecting all the line paths in a variable called paths so that I can change the opacity when you move your slider like this
var paths = group.selectAll(".line")
.data(series)
.enter().append("path")
.attr("class", "line")
// .attr("visibility", "hidden")
.attr("stroke-width", function(d) {
return strwi(d);
})
.attr("stroke", "black")
.attr("fill", "none")
.attr("d", line);
and then in your hue function, I added this code to change the opacity based on your slider
paths.attr('opacity', function(d) {
if (((Math.round(h * 2) / 2).toFixed(1)) > d[2]['year']) {
return 0;
} else {
return 1;
}
})
Here's a fiddle for you:
var width = 1500;
var height = 1500;
var margin = {
top: 50,
left: 50,
right: 50,
bottom: 50
},
height = 650 - margin.top - margin.bottom,
width = 1200 - margin.left - margin.right;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
var strwi = d3.scaleLinear()
.domain([100, 400])
.range([7, 35])
var group = svg.append("g")
var series = [
[{
"x": 360,
"y": 250,
"num": 100,
"startYear": 1991,
"endYear": 1995
}, {
"x": 520,
"y": 400,
"num": 100,
"startYear": 1991,
"endYear": 1995
}, {
"x": 630,
"y": 300,
"num": 100,
"startYear": 1991,
"endYear": 1995
}],
[{
"x": 71,
"y": 45,
"num": 200,
"startYear": 1992,
"endYear": 1993
}, {
"x": 32,
"y": 39,
"num": 200,
"startYear": 1992,
"endYear": 1993
}, {
"x": 43,
"y": 70,
"num": 200,
"startYear": 1992,
"endYear": 1993
}],
[{
"x": 100,
"y": 300,
"num": 300,
"startYear": 1994,
"endYear": 1996
}, {
"x": 200,
"y": 200,
"num": 300,
"startYear": 1994,
"endYear": 1996
}, {
"x": 300,
"y": 200,
"num": 300,
"startYear": 1994,
"endYear": 1996
}],
[{
"x": 101,
"y": 202,
"num": 400,
"startYear": 1995,
"endYear": 1997
}, {
"x": 102,
"y": 204,
"num": 400,
"startYear": 1995,
"endYear": 1997
}, {
"x": 103,
"y": 215,
"num": 400,
"startYear": 1995,
"endYear": 1997
}]
];
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
});
var paths = group.selectAll(".line")
.data(series)
.enter().append("path")
.attr("class", "line")
.attr('v1',function(d){
return d[2]['startYear'];
})
.attr('v2',function(d) {
return d[2]['endYear'];
})
.attr('opacity',0)
//.attr("visibility", "hidden")
.attr("stroke-width", function(d) {
return strwi(d);
})
.attr("stroke", "black")
.attr("fill", "none")
.attr("d", line);
var data = [1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002]
var x = d3.scaleLinear()
.domain(d3.extent(data))
.range([0, width])
.clamp(true);
var slider = svg.append("g")
.attr("class", "slider")
.attr("transform", "translate(" + 0 + "," + 300 + ")");
slider.insert("g", ".track-overlay")
.attr("class", "ticks")
.selectAll("ticks")
.data(x.ticks(data.length))
.enter().append("text")
.attr("x", x)
.attr("text-anchor", "middle")
.attr("transform", "translate(0," + 30 + ")")
.text(function(d) {
return d;
})
.exit()
.data(x.ticks(data.length * 2))
.enter().append("circle")
.attr("cx", x)
.attr("r", 3)
.attr("fill", "#c1c7cd");
slider.append("line")
.attr("class", "track")
.attr("x1", x.range()[0])
.attr("x2", x.range()[1])
.attr("stroke", "black")
.attr("stroke-width", "4")
.select(function() {
return this.parentNode.appendChild(this.cloneNode(true));
})
.attr("class", "track-inset")
.select(function() {
return this.parentNode.appendChild(this.cloneNode(true));
})
.attr("class", "track-overlay")
.call(d3.drag()
.on("start.interrupt", function() {
slider.interrupt();
})
.on("start drag", function() {
//console.log(d3.event.x)
hue(x.invert(d3.event.x));
}));
slider.insert("g", ".track-overlay")
.attr("class", "ticks--cirlces")
.selectAll("ticks--ticks");
var handle = slider.insert("circle", ".track-overlay")
.attr("class", "handle")
.attr("r", 9);
slider.transition() // Gratuitous intro!
.duration(750)
.tween("hue", function() {
var i = d3.interpolate(0, 70);
return function(t) {
//nsole.log(t)
hue(i(t));
};
});
function hue(h) {
handle.attr("cx", x(h));
d3.select(".text")
.text((Math.round(h * 2) / 2).toFixed(1));
paths
.transition().duration(300)
.attr('opacity', function(d) {
if (((Math.round(h * 2) / 2).toFixed(1)) >= d[2]['startYear'] && ((Math.round(h * 2) / 2).toFixed(1)) <= d[2]['endYear']) {
return 1;
} else {
return 0;
}
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>
Let me know if this is what you wanted or there's something else.

d3 update path with maximum value of area

I display a d3.area(). To show the highest value, I draw a horizontal line with d3.line() at the maximum value of the dataset. This value is calculated using:
var max_out = d3.max(data, function(d){ return +d.out; });
To brush through the graph, I use the setup as in this example:
As soon as the area is brushed, I would also like to move the "maximum line" up and down reflecting the new domain instead of using the whole dataset.
Any pointers?
Let's have a line function definition, in the first time only defining the x attribute:
var line = d3.line()
.x(function(d) { return x(d.date); });
Right after you append your area in the main view, add another path, which will be used to draw your horizontal line above the corresponding area:
focus.append("path")
.attr("class", "line");
Notice that I'm giving it a line class for styling.
We're also gonna draw the line, at first simply using the function you are currently using to determine the peak of your data:
.attr("d", line.y(function () {
return y(max);
}));
... and feed our line generator the right data :):
.datum(data)
To summarize, here's what we got on initialization:
// There's your main view's data area
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
// There's your 'peak' horizontal line
var max = d3.max(data, function(d){ return d.price; });
focus.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line.y(function () {
return y(max);
}));
In the functions that trigger a re-drawing of your elements, you simply have to update the d attribute of your path.line element.
We're gonna simply do the following:
Filter out data points that aren't within the updated domain of your x-axis
Evaluate the maximum value of that sub-set (using the method you described)
Let D3 generate the new value for the d attribute and set it.
That translates into the following:
var lo = x.domain()[0],
hi = x.domain()[1];
var max = d3.max(data.filter(function (d) {
return d.date.getTime() >= lo && d.date.getTime() <= hi;
}), function(d){ return d.price; });
focus.select(".line").attr("d", line.y(function () {
return y(max);
}));
See an example in action in the snippet below or on JSFiddle.
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 110, left: 40},
margin2 = {top: 180, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
height2 = +svg.attr("height") - margin2.top - margin2.bottom;
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var area = d3.area()
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.price); });
var area2 = d3.area()
.x(function(d) { return x2(d.date); })
.y0(height2)
.y1(function(d) { return y2(d.price); });
var line = d3.line()
.x(function(d) { return x(d.date); });
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var data;
(function (_data) {
data = _data;
data.forEach(function (d) {
d.date = new Date(d.date);
});
data = (function uniqBy(a, key) {
var seen = {};
return a.filter(function(item) {
var k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
});
})(data, function (d) {return d.date;});
data.sort(function(a,b) {
return new Date(a.date).getTime() - new Date(b.date).getTime();
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.price; })]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
var max = d3.max(data, function(d){ return d.price; });
focus.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line.y(function () {
return y(max);
}));
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
})([{"date":"1999-12-31T23:00:00.000Z","price":1394.46},{"date":"2000-01-31T23:00:00.000Z","price":1366.42},{"date":"2000-02-29T23:00:00.000Z","price":1498.58},{"date":"2000-03-31T22:00:00.000Z","price":1452.43},{"date":"2000-04-30T22:00:00.000Z","price":1420.6},{"date":"2000-05-31T22:00:00.000Z","price":1454.6},{"date":"2000-06-30T22:00:00.000Z","price":1430.83},{"date":"2000-07-31T22:00:00.000Z","price":1517.68},{"date":"2000-08-31T22:00:00.000Z","price":1436.51},{"date":"2000-09-30T22:00:00.000Z","price":1429.4},{"date":"2000-10-31T23:00:00.000Z","price":1314.95},{"date":"2000-11-30T23:00:00.000Z","price":1320.28},{"date":"2000-12-31T23:00:00.000Z","price":1366.01},{"date":"2001-01-31T23:00:00.000Z","price":1239.94},{"date":"2001-02-28T23:00:00.000Z","price":1160.33},{"date":"2001-03-31T22:00:00.000Z","price":1249.46},{"date":"2001-04-30T22:00:00.000Z","price":1255.82},{"date":"2001-05-31T22:00:00.000Z","price":1224.38},{"date":"2001-06-30T22:00:00.000Z","price":1211.23},{"date":"2001-07-31T22:00:00.000Z","price":1133.58},{"date":"2001-08-31T22:00:00.000Z","price":1040.94},{"date":"2001-09-30T22:00:00.000Z","price":1059.78},{"date":"2001-10-31T23:00:00.000Z","price":1139.45},{"date":"2001-11-30T23:00:00.000Z","price":1148.08},{"date":"2001-12-31T23:00:00.000Z","price":1130.2},{"date":"2002-01-31T23:00:00.000Z","price":1106.73},{"date":"2002-02-28T23:00:00.000Z","price":1147.39},{"date":"2002-03-31T22:00:00.000Z","price":1076.92},{"date":"2002-04-30T22:00:00.000Z","price":1067.14},{"date":"2002-05-31T22:00:00.000Z","price":989.82},{"date":"2002-06-30T22:00:00.000Z","price":911.62},{"date":"2002-07-31T22:00:00.000Z","price":916.07},{"date":"2002-08-31T22:00:00.000Z","price":815.28},{"date":"2002-09-30T22:00:00.000Z","price":885.76},{"date":"2002-10-31T23:00:00.000Z","price":936.31},{"date":"2002-11-30T23:00:00.000Z","price":879.82},{"date":"2002-12-31T23:00:00.000Z","price":855.7},{"date":"2003-01-31T23:00:00.000Z","price":841.15},{"date":"2003-02-28T23:00:00.000Z","price":848.18},{"date":"2003-03-31T22:00:00.000Z","price":916.92},{"date":"2003-04-30T22:00:00.000Z","price":963.59},{"date":"2003-05-31T22:00:00.000Z","price":974.5},{"date":"2003-06-30T22:00:00.000Z","price":990.31},{"date":"2003-07-31T22:00:00.000Z","price":1008.01},{"date":"2003-08-31T22:00:00.000Z","price":995.97},{"date":"2003-09-30T22:00:00.000Z","price":1050.71},{"date":"2003-10-31T23:00:00.000Z","price":1058.2},{"date":"2003-11-30T23:00:00.000Z","price":1111.92},{"date":"2003-12-31T23:00:00.000Z","price":1131.13},{"date":"2004-01-31T23:00:00.000Z","price":1144.94},{"date":"2004-02-29T23:00:00.000Z","price":1126.21},{"date":"2004-03-31T22:00:00.000Z","price":1107.3},{"date":"2004-04-30T22:00:00.000Z","price":1120.68},{"date":"2004-05-31T22:00:00.000Z","price":1140.84},{"date":"2004-06-30T22:00:00.000Z","price":1101.72},{"date":"2004-07-31T22:00:00.000Z","price":1104.24},{"date":"2004-08-31T22:00:00.000Z","price":1114.58},{"date":"2004-09-30T22:00:00.000Z","price":1130.2},{"date":"2004-10-31T23:00:00.000Z","price":1173.82},{"date":"2004-11-30T23:00:00.000Z","price":1211.92},{"date":"2004-12-31T23:00:00.000Z","price":1181.27},{"date":"2005-01-31T23:00:00.000Z","price":1203.6},{"date":"2005-02-28T23:00:00.000Z","price":1180.59},{"date":"2005-03-31T22:00:00.000Z","price":1156.85},{"date":"2005-04-30T22:00:00.000Z","price":1191.5},{"date":"2005-05-31T22:00:00.000Z","price":1191.33},{"date":"2005-06-30T22:00:00.000Z","price":1234.18},{"date":"2005-07-31T22:00:00.000Z","price":1220.33},{"date":"2005-08-31T22:00:00.000Z","price":1228.81},{"date":"2005-09-30T22:00:00.000Z","price":1207.01},{"date":"2005-10-31T23:00:00.000Z","price":1249.48},{"date":"2005-11-30T23:00:00.000Z","price":1248.29},{"date":"2005-12-31T23:00:00.000Z","price":1280.08},{"date":"2006-01-31T23:00:00.000Z","price":1280.66},{"date":"2006-02-28T23:00:00.000Z","price":1294.87},{"date":"2006-03-31T22:00:00.000Z","price":1310.61},{"date":"2006-04-30T22:00:00.000Z","price":1270.09},{"date":"2006-05-31T22:00:00.000Z","price":1270.2},{"date":"2006-06-30T22:00:00.000Z","price":1276.66},{"date":"2006-07-31T22:00:00.000Z","price":1303.82},{"date":"2006-08-31T22:00:00.000Z","price":1335.85},{"date":"2006-09-30T22:00:00.000Z","price":1377.94},{"date":"2006-10-31T23:00:00.000Z","price":1400.63},{"date":"2006-11-30T23:00:00.000Z","price":1418.3},{"date":"2006-12-31T23:00:00.000Z","price":1438.24},{"date":"2007-01-31T23:00:00.000Z","price":1406.82},{"date":"2007-02-28T23:00:00.000Z","price":1420.86},{"date":"2007-03-31T22:00:00.000Z","price":1482.37},{"date":"2007-04-30T22:00:00.000Z","price":1530.62},{"date":"2007-05-31T22:00:00.000Z","price":1503.35},{"date":"2007-06-30T22:00:00.000Z","price":1455.27},{"date":"2007-07-31T22:00:00.000Z","price":1473.99},{"date":"2007-08-31T22:00:00.000Z","price":1526.75},{"date":"2007-09-30T22:00:00.000Z","price":1549.38},{"date":"2007-10-31T23:00:00.000Z","price":1481.14},{"date":"2007-11-30T23:00:00.000Z","price":1468.36},{"date":"2007-12-31T23:00:00.000Z","price":1378.55},{"date":"2008-01-31T23:00:00.000Z","price":1330.63},{"date":"2008-02-29T23:00:00.000Z","price":1322.7},{"date":"2008-03-31T22:00:00.000Z","price":1385.59},{"date":"2008-04-30T22:00:00.000Z","price":1400.38},{"date":"2008-05-31T22:00:00.000Z","price":1280},{"date":"2008-06-30T22:00:00.000Z","price":1267.38},{"date":"2008-07-31T22:00:00.000Z","price":1282.83},{"date":"2008-08-31T22:00:00.000Z","price":1166.36},{"date":"2008-09-30T22:00:00.000Z","price":968.75},{"date":"2008-10-31T23:00:00.000Z","price":896.24},{"date":"2008-11-30T23:00:00.000Z","price":903.25},{"date":"2008-12-31T23:00:00.000Z","price":825.88},{"date":"2009-01-31T23:00:00.000Z","price":735.09},{"date":"2009-02-28T23:00:00.000Z","price":797.87},{"date":"2009-03-31T22:00:00.000Z","price":872.81},{"date":"2009-04-30T22:00:00.000Z","price":919.14},{"date":"2009-05-31T22:00:00.000Z","price":919.32},{"date":"2009-06-30T22:00:00.000Z","price":987.48},{"date":"2009-07-31T22:00:00.000Z","price":1020.62},{"date":"2009-08-31T22:00:00.000Z","price":1057.08},{"date":"2009-09-30T22:00:00.000Z","price":1036.19},{"date":"2009-10-31T23:00:00.000Z","price":1095.63},{"date":"2009-11-30T23:00:00.000Z","price":1115.1},{"date":"2009-12-31T23:00:00.000Z","price":1073.87},{"date":"2010-01-31T23:00:00.000Z","price":1104.49},{"date":"2010-02-28T23:00:00.000Z","price":1140.45 }]);
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.select(".area").attr("d", area);
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
var lo = x.domain()[0],
hi = x.domain()[1];
var max = d3.max(data.filter(function (d) {
return d.date.getTime() >= lo && d.date.getTime() <= hi;
}), function(d){ return d.price; });
focus.select(".line").attr("d", line.y(function () {
return y(max);
}));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.select(".area").attr("d", area);
focus.select(".axis--x").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
var lo = x.domain()[0],
hi = x.domain()[1];
var max = d3.max(data.filter(function (d) {
return d.date.getTime() >= lo && d.date.getTime() <= hi;
}), function(d){ return d.price; });
focus.select(".line").attr("d", line.y(function () {
return y(max);
}));
}
function type(d) {
d.date = parseDate(d.date);
d.price = +d.price;
return d;
}
.area {
fill: steelblue;
clip-path: url(#clip);
}
.line {
stroke: red;
clip-path: url(#clip);
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500" height="250"></svg>
Something like this:
focus.append("line")
.attr("class", "peak")
.style("stroke", "orange")
.attr("stroke-width", 3);
function setPeak(){
var maxY = {
x: null,
y: -1e100
};
data.forEach(function(d){
if (d.date >= x.domain()[0] &&
d.date <= x.domain()[1] &&
d.price > maxY.y){
maxY.y = d.price;
maxY.x = d.date;
}
});
d3.select(".peak")
.attr("x1", x(maxY.x))
.attr("x2", x(maxY.x))
.attr("y1", 0)
.attr("y2", height);
}
setPeak();
Running code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.area {
fill: steelblue;
clip-path: url(#clip);
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
</style>
<svg width="400" height="300"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 110,
left: 40
},
margin2 = {
top: 230,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
height2 = +svg.attr("height") - margin2.top - margin2.bottom;
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
.on("brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var area = d3.area()
.curve(d3.curveMonotoneX)
.x(function(d) {
return x(d.date);
})
.y0(height)
.y1(function(d) {
return y(d.price);
});
var area2 = d3.area()
.curve(d3.curveMonotoneX)
.x(function(d) {
return x2(d.date);
})
.y0(height2)
.y1(function(d) {
return y2(d.price);
});
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
//d3.csv("data.csv", type, function(error, data) {
// if (error) throw error;
var data = [{
"date": "1999-12-31T23:00:00.000Z",
"price": 1394.46
}, {
"date": "2000-01-31T23:00:00.000Z",
"price": 1366.42
}, {
"date": "2000-02-29T23:00:00.000Z",
"price": 1498.58
}, {
"date": "2000-03-31T22:00:00.000Z",
"price": 1452.43
}, {
"date": "2000-04-30T22:00:00.000Z",
"price": 1420.6
}, {
"date": "2000-05-31T22:00:00.000Z",
"price": 1454.6
}, {
"date": "2000-06-30T22:00:00.000Z",
"price": 1430.83
}, {
"date": "2000-07-31T22:00:00.000Z",
"price": 1517.68
}, {
"date": "2000-08-31T22:00:00.000Z",
"price": 1436.51
}, {
"date": "2000-09-30T22:00:00.000Z",
"price": 1429.4
}, {
"date": "2000-10-31T23:00:00.000Z",
"price": 1314.95
}, {
"date": "2000-11-30T23:00:00.000Z",
"price": 1320.28
}, {
"date": "2000-12-31T23:00:00.000Z",
"price": 1366.01
}, {
"date": "2001-01-31T23:00:00.000Z",
"price": 1239.94
}, {
"date": "2001-02-28T23:00:00.000Z",
"price": 1160.33
}, {
"date": "2001-03-31T22:00:00.000Z",
"price": 1249.46
}, {
"date": "2001-04-30T22:00:00.000Z",
"price": 1255.82
}, {
"date": "2001-05-31T22:00:00.000Z",
"price": 1224.38
}, {
"date": "2001-06-30T22:00:00.000Z",
"price": 1211.23
}, {
"date": "2001-07-31T22:00:00.000Z",
"price": 1133.58
}, {
"date": "2001-08-31T22:00:00.000Z",
"price": 1040.94
}, {
"date": "2001-09-30T22:00:00.000Z",
"price": 1059.78
}, {
"date": "2001-10-31T23:00:00.000Z",
"price": 1139.45
}, {
"date": "2001-11-30T23:00:00.000Z",
"price": 1148.08
}, {
"date": "2001-12-31T23:00:00.000Z",
"price": 1130.2
}, {
"date": "2002-01-31T23:00:00.000Z",
"price": 1106.73
}, {
"date": "2002-02-28T23:00:00.000Z",
"price": 1147.39
}, {
"date": "2002-03-31T22:00:00.000Z",
"price": 1076.92
}, {
"date": "2002-04-30T22:00:00.000Z",
"price": 1067.14
}, {
"date": "2002-05-31T22:00:00.000Z",
"price": 989.82
}, {
"date": "2002-06-30T22:00:00.000Z",
"price": 911.62
}, {
"date": "2002-07-31T22:00:00.000Z",
"price": 916.07
}, {
"date": "2002-08-31T22:00:00.000Z",
"price": 815.28
}, {
"date": "2002-09-30T22:00:00.000Z",
"price": 885.76
}, {
"date": "2002-10-31T23:00:00.000Z",
"price": 936.31
}, {
"date": "2002-11-30T23:00:00.000Z",
"price": 879.82
}, {
"date": "2002-12-31T23:00:00.000Z",
"price": 855.7
}, {
"date": "2003-01-31T23:00:00.000Z",
"price": 841.15
}, {
"date": "2003-02-28T23:00:00.000Z",
"price": 848.18
}, {
"date": "2003-03-31T22:00:00.000Z",
"price": 916.92
}, {
"date": "2003-04-30T22:00:00.000Z",
"price": 963.59
}, {
"date": "2003-05-31T22:00:00.000Z",
"price": 974.5
}, {
"date": "2003-06-30T22:00:00.000Z",
"price": 990.31
}, {
"date": "2003-07-31T22:00:00.000Z",
"price": 1008.01
}, {
"date": "2003-08-31T22:00:00.000Z",
"price": 995.97
}, {
"date": "2003-09-30T22:00:00.000Z",
"price": 1050.71
}, {
"date": "2003-10-31T23:00:00.000Z",
"price": 1058.2
}, {
"date": "2003-11-30T23:00:00.000Z",
"price": 1111.92
}, {
"date": "2003-12-31T23:00:00.000Z",
"price": 1131.13
}, {
"date": "2004-01-31T23:00:00.000Z",
"price": 1144.94
}, {
"date": "2004-02-29T23:00:00.000Z",
"price": 1126.21
}, {
"date": "2004-03-31T22:00:00.000Z",
"price": 1107.3
}, {
"date": "2004-04-30T22:00:00.000Z",
"price": 1120.68
}, {
"date": "2004-05-31T22:00:00.000Z",
"price": 1140.84
}, {
"date": "2004-06-30T22:00:00.000Z",
"price": 1101.72
}, {
"date": "2004-07-31T22:00:00.000Z",
"price": 1104.24
}, {
"date": "2004-08-31T22:00:00.000Z",
"price": 1114.58
}, {
"date": "2004-09-30T22:00:00.000Z",
"price": 1130.2
}, {
"date": "2004-10-31T23:00:00.000Z",
"price": 1173.82
}, {
"date": "2004-11-30T23:00:00.000Z",
"price": 1211.92
}, {
"date": "2004-12-31T23:00:00.000Z",
"price": 1181.27
}, {
"date": "2005-01-31T23:00:00.000Z",
"price": 1203.6
}, {
"date": "2005-02-28T23:00:00.000Z",
"price": 1180.59
}, {
"date": "2005-03-31T22:00:00.000Z",
"price": 1156.85
}, {
"date": "2005-04-30T22:00:00.000Z",
"price": 1191.5
}, {
"date": "2005-05-31T22:00:00.000Z",
"price": 1191.33
}, {
"date": "2005-06-30T22:00:00.000Z",
"price": 1234.18
}, {
"date": "2005-07-31T22:00:00.000Z",
"price": 1220.33
}, {
"date": "2005-08-31T22:00:00.000Z",
"price": 1228.81
}, {
"date": "2005-09-30T22:00:00.000Z",
"price": 1207.01
}, {
"date": "2005-10-31T23:00:00.000Z",
"price": 1249.48
}, {
"date": "2005-11-30T23:00:00.000Z",
"price": 1248.29
}, {
"date": "2005-12-31T23:00:00.000Z",
"price": 1280.08
}, {
"date": "2006-01-31T23:00:00.000Z",
"price": 1280.66
}, {
"date": "2006-02-28T23:00:00.000Z",
"price": 1294.87
}, {
"date": "2006-03-31T22:00:00.000Z",
"price": 1310.61
}, {
"date": "2006-04-30T22:00:00.000Z",
"price": 1270.09
}, {
"date": "2006-05-31T22:00:00.000Z",
"price": 1270.2
}, {
"date": "2006-06-30T22:00:00.000Z",
"price": 1276.66
}, {
"date": "2006-07-31T22:00:00.000Z",
"price": 1303.82
}, {
"date": "2006-08-31T22:00:00.000Z",
"price": 1335.85
}, {
"date": "2006-09-30T22:00:00.000Z",
"price": 1377.94
}, {
"date": "2006-10-31T23:00:00.000Z",
"price": 1400.63
}, {
"date": "2006-11-30T23:00:00.000Z",
"price": 1418.3
}, {
"date": "2006-12-31T23:00:00.000Z",
"price": 1438.24
}, {
"date": "2007-01-31T23:00:00.000Z",
"price": 1406.82
}, {
"date": "2007-02-28T23:00:00.000Z",
"price": 1420.86
}, {
"date": "2007-03-31T22:00:00.000Z",
"price": 1482.37
}, {
"date": "2007-04-30T22:00:00.000Z",
"price": 1530.62
}, {
"date": "2007-05-31T22:00:00.000Z",
"price": 1503.35
}, {
"date": "2007-06-30T22:00:00.000Z",
"price": 1455.27
}, {
"date": "2007-07-31T22:00:00.000Z",
"price": 1473.99
}, {
"date": "2007-08-31T22:00:00.000Z",
"price": 1526.75
}, {
"date": "2007-09-30T22:00:00.000Z",
"price": 1549.38
}, {
"date": "2007-10-31T23:00:00.000Z",
"price": 1481.14
}, {
"date": "2007-11-30T23:00:00.000Z",
"price": 1468.36
}, {
"date": "2007-12-31T23:00:00.000Z",
"price": 1378.55
}, {
"date": "2008-01-31T23:00:00.000Z",
"price": 1330.63
}, {
"date": "2008-02-29T23:00:00.000Z",
"price": 1322.7
}, {
"date": "2008-03-31T22:00:00.000Z",
"price": 1385.59
}, {
"date": "2008-04-30T22:00:00.000Z",
"price": 1400.38
}, {
"date": "2008-05-31T22:00:00.000Z",
"price": 1280
}, {
"date": "2008-06-30T22:00:00.000Z",
"price": 1267.38
}, {
"date": "2008-07-31T22:00:00.000Z",
"price": 1282.83
}, {
"date": "2008-08-31T22:00:00.000Z",
"price": 1166.36
}, {
"date": "2008-09-30T22:00:00.000Z",
"price": 968.75
}, {
"date": "2008-10-31T23:00:00.000Z",
"price": 896.24
}, {
"date": "2008-11-30T23:00:00.000Z",
"price": 903.25
}, {
"date": "2008-12-31T23:00:00.000Z",
"price": 825.88
}, {
"date": "2009-01-31T23:00:00.000Z",
"price": 735.09
}, {
"date": "2009-02-28T23:00:00.000Z",
"price": 797.87
}, {
"date": "2009-03-31T22:00:00.000Z",
"price": 872.81
}, {
"date": "2009-04-30T22:00:00.000Z",
"price": 919.14
}, {
"date": "2009-05-31T22:00:00.000Z",
"price": 919.32
}, {
"date": "2009-06-30T22:00:00.000Z",
"price": 987.48
}, {
"date": "2009-07-31T22:00:00.000Z",
"price": 1020.62
}, {
"date": "2009-08-31T22:00:00.000Z",
"price": 1057.08
}, {
"date": "2009-09-30T22:00:00.000Z",
"price": 1036.19
}, {
"date": "2009-10-31T23:00:00.000Z",
"price": 1095.63
}, {
"date": "2009-11-30T23:00:00.000Z",
"price": 1115.1
}, {
"date": "2009-12-31T23:00:00.000Z",
"price": 1073.87
}, {
"date": "2010-01-31T23:00:00.000Z",
"price": 1104.49
}, {
"date": "2010-02-28T23:00:00.000Z",
"price": 1140.45
}];
data = data.map(type);
console.log(data)
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.price;
})]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
focus.append("line")
.attr("class", "peak")
.style("stroke", "orange")
.attr("stroke-width", 3);
//});
function setPeak(){
var maxY = {
x: null,
y: -1e100
};
data.forEach(function(d){
if (d.date >= x.domain()[0] &&
d.date <= x.domain()[1] &&
d.price > maxY.y){
maxY.y = d.price;
maxY.x = d.date;
}
});
d3.select(".peak")
.attr("x1", x(maxY.x))
.attr("x2", x(maxY.x))
.attr("y1", 0)
.attr("y2", height);
}
setPeak();
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.select(".area").attr("d", area);
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
setPeak();
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.select(".area").attr("d", area);
focus.select(".axis--x").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
function type(d) {
d.date = new Date(d.date);
d.price = +d.price;
return d;
}
</script>

d3.js path doesn't respect domain and range

There must be something obvious I'm missing here.
I'm trying to draw a simple line and this is my javascript:
// CRASH DATA
var lineData = [
{ "x": 0, "y": 0.5},
{ "x": 2, "y": 0.1},
{ "x": 4, "y": -0.5},
{ "x": 6, "y": -0.8},
{ "x": 8, "y": -0.9},
{ "x": 10, "y": -0.10},
{ "x": 12, "y": -0.10},
{ "x": 14, "y": -0.11},
{ "x": 16, "y": -0.10},
{ "x": 18, "y": -0.9},
{ "x": 20, "y": -0.7},
{ "x": 22, "y": -0.6},
{ "x": 24, "y": -0.5},
{ "x": 26, "y": -0.3},
{ "x": 28, "y": -0.1},
{ "x": 30, "y": 0.2},
{ "x": 32, "y": 0.4},
{ "x": 34, "y": 0.8},
{ "x": 36, "y": 0.8},
{ "x": 38, "y": 0.7},
{ "x": 40, "y": 0.4},
{ "x": 42, "y": 0.4},
{ "x": 44, "y": 0.4},
{ "x": 46, "y": 0.2},
{ "x": 48, "y": 0.1},
{ "x": 50, "y": 0}
];
//DRAW TRAJECTORY
function draw(data){
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
//This is the accessor function we talked about above
var lineFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.x; }));
y.domain(d3.extent(data, function(d) { return d.y; }));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
svg.append("path") // Add the lineFunction path.
.attr("class", "line")
.attr("d", lineFunction(data));
};
//PUT EVERYTHING ON SCREEN
$( document ).ready(function() {
draw(lineData);
});
And here is the outcome:
You aren't actually using the scales you define. Your line function should be
var lineFunction = d3.svg.line()
.x(function(d) { return x(d.x); })
.y(function(d) { return y(d.y); })
.interpolate("linear");
Complete example here.

Resources