Related
I'm new to Javascript, and through exploring various websites I have created a draggable points line chart. The data points are movable and the line connecting the dots is made invisible. The data points will move vertically only. I want to have grid lines with this chart. I tried but have not able to achieve it. Can someone help me with adding gridlines to this chart? I have attached the code that I have build.
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.0/d3.min.js"></script>
<!DOCTYPE html>
<style>
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
</style>
<svg width="500" height="350"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
function make_x_axis() {
return d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5)
}
function make_y_axis() {
return d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
}
let points = d3.range(1, 10).map(function(i) {
return [i * width / 10, 50 + Math.random() * (height - 100)];
});
var x = d3.scaleLinear()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var line = d3.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
let drag = d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended);
svg.append('rect')
.attr('class', 'zoom')
.attr('cursor', 'move')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat("")
)
svg.append("g")
.attr("class", "grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat("")
)
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(points, function(d) { return d[0]; }));
y.domain(d3.extent(points, function(d) { return d[1]; }));
focus.append("path")
.datum(points)
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
focus.selectAll('circle')
.data(points)
.enter()
.append('circle')
.attr('r', 5.0)
.attr('cx', function(d) { return x(d[0]); })
.attr('cy', function(d) { return y(d[1]); })
.style('cursor', 'pointer')
.style('fill', 'steelblue');
focus.selectAll('circle')
.call(drag);
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);
function dragstarted(d) {
d3.select(this).raise().classed('active', true);
}
function dragged(d) {
//d[0] = x.invert(d3.event.x);
d[1] = y.invert(d3.event.y);
d3.select(this)
//.attr('cx', x(d[0]))
.attr('cy', y(d[1]))
focus.select('path').attr('d', line);
}
function dragended(d) {
d3.select(this).classed('active', false);
}
</script>
The part you're using to append the grid is from v3 of d3.js. d3v4 added d3.axisLeft() and d3.axisBottom(), etc. See this post.
Also, the translate coordinates you're using for the grid is not correct, I've adjusted some of the values so it fits the graph.
Here's a working example:
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.0/d3.min.js"></script>-->
<!DOCTYPE html>
<style>
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
</style>
<svg width="500" height="350"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
function make_x_axis() {
return d3.axisBottom(x)
// .scale(x)
// .orient("bottom")
.ticks(5)
}
function make_y_axis() {
return d3.axisLeft(y)
// .scale(y)
// .orient("left")
.ticks(5)
}
let points = d3.range(1, 10).map(function(i) {
return [i * width / 10, 50 + Math.random() * (height - 100)];
});
var x = d3.scaleLinear()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var line = d3.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
let drag = d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended);
svg.append('rect')
.attr('class', 'zoom')
.attr('cursor', 'move')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// svg.append("g")
// .attr("class", "grid")
// .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// .call(make_x_axis()
// .tickSize(-height, 0, 0)
// .tickFormat("")
// )
//
// svg.append("g")
// .attr("class", "grid")
// .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// // .attr("transform", "translate(0," + (height + margin.top) + ")")
// .call(make_y_axis()
// .tickSize(-width, 0, 0)
// .tickFormat("")
// )
svg.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left}, ${height + margin.top})`)
.call(make_x_axis()
.tickSize(-height)
.tickFormat("")
)
// add the Y gridlines
svg.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left}, ${margin.top})`)
.call(make_y_axis()
.tickSize(-width)
.tickFormat("")
)
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(points, function(d) { return d[0]; }));
y.domain(d3.extent(points, function(d) { return d[1]; }));
focus.append("path")
.datum(points)
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
focus.selectAll('circle')
.data(points)
.enter()
.append('circle')
.attr('r', 5.0)
.attr('cx', function(d) { return x(d[0]); })
.attr('cy', function(d) { return y(d[1]); })
.style('cursor', 'pointer')
.style('fill', 'steelblue');
focus.selectAll('circle')
.call(drag);
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);
function dragstarted(d) {
d3.select(this).raise().classed('active', true);
}
function dragged(d) {
//d[0] = x.invert(d3.event.x);
d[1] = y.invert(d3.event.y);
d3.select(this)
//.attr('cx', x(d[0]))
.attr('cy', y(d[1]))
focus.select('path').attr('d', line);
}
function dragended(d) {
d3.select(this).classed('active', false);
}
</script>
In case you're wondering why the lines are "broken", it's because of the white best-fitting line overlapping with the grid.
Line revealed:
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.0/d3.min.js"></script>-->
<!DOCTYPE html>
<style>
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
</style>
<svg width="500" height="350"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
function make_x_axis() {
return d3.axisBottom(x)
// .scale(x)
// .orient("bottom")
.ticks(5)
}
function make_y_axis() {
return d3.axisLeft(y)
// .scale(y)
// .orient("left")
.ticks(5)
}
let points = d3.range(1, 10).map(function(i) {
return [i * width / 10, 50 + Math.random() * (height - 100)];
});
var x = d3.scaleLinear()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var line = d3.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
let drag = d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended);
svg.append('rect')
.attr('class', 'zoom')
.attr('cursor', 'move')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// svg.append("g")
// .attr("class", "grid")
// .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// .call(make_x_axis()
// .tickSize(-height, 0, 0)
// .tickFormat("")
// )
//
// svg.append("g")
// .attr("class", "grid")
// .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// // .attr("transform", "translate(0," + (height + margin.top) + ")")
// .call(make_y_axis()
// .tickSize(-width, 0, 0)
// .tickFormat("")
// )
svg.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left}, ${height + margin.top})`)
.call(make_x_axis()
.tickSize(-height)
.tickFormat("")
)
// add the Y gridlines
svg.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left}, ${margin.top})`)
.call(make_y_axis()
.tickSize(-width)
.tickFormat("")
)
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(points, function(d) { return d[0]; }));
y.domain(d3.extent(points, function(d) { return d[1]; }));
focus.append("path")
.datum(points)
.attr("fill", "none")
.attr("stroke", "black") // modified
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
focus.selectAll('circle')
.data(points)
.enter()
.append('circle')
.attr('r', 5.0)
.attr('cx', function(d) { return x(d[0]); })
.attr('cy', function(d) { return y(d[1]); })
.style('cursor', 'pointer')
.style('fill', 'steelblue');
focus.selectAll('circle')
.call(drag);
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);
function dragstarted(d) {
d3.select(this).raise().classed('active', true);
}
function dragged(d) {
//d[0] = x.invert(d3.event.x);
d[1] = y.invert(d3.event.y);
d3.select(this)
//.attr('cx', x(d[0]))
.attr('cy', y(d[1]))
focus.select('path').attr('d', line);
}
function dragended(d) {
d3.select(this).classed('active', false);
}
</script>
am working on multi line chart with two lines and with brush and zoom in d3 v4, when i brush my only one line is moving my another line remains constant. Since i have just started learning this on my own i don't have idea on what changes i have to make so that my both lines moves when i brush. Any kind of suggestions would be of a great help.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
</style>
<svg width="960" height="500"></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: 430, 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 parseDate = d3.timeParse("%Y-%m-%d");
var x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
//brush
x2 = d3.scaleTime().range([0, width]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y),
//brush
xAxis2 = d3.axisBottom(x2);
//slider that grey selection one
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 line = d3.line()
.x(function (d) { return x(d.date); })
.y(function (d) { return y(d.extra); });
var line1 = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.Speed); });
//slider line
var line2 = d3.line()
.x(function (d) { return x2(d.date); })
.y(function (d) { return y2(d.Speed); });
var clip = svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("x", 0)
.attr("y", 0);
var Line_chart = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("clip-path", "url(#clip)");
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/morley.csv", type, function (error, data) {
if (error) throw error;
data.sort(function(a,b){
return a.date-b.date
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) {
return Math.max(d.Speed, d.extra); })]);
//slider
x2.domain(x.domain());
y2.domain(y.domain());
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);
Line_chart.append("path")
.datum(data)
.attr("class", "line")
.style("stroke", "green")
.attr("d", line);
Line_chart.append("path")
.datum(data)
.attr("class", "line")
.style("stroke", "blue")
.attr("d", line1);
//slider line
context.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line2);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
//slider
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);
});
//selection in slider
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return;
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
Line_chart.select(".line").attr("d", line);
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return;
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
Line_chart.select(".line").attr("d", line);
focus.select(".axis--x").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
function type(d) {
d.date = parseDate(d.date);
d.extra = +d.extra;
d.Speed=+d.Speed
return d;
}
</script>
In the brushed() and zoomed() functions, you are only updating the green line (extra VS. date). You have to identify each line (for example with a specific class), and update both using their respective d3.line functions:
Line_chart.append("path")
.datum(data)
.attr("class", "line line-extra") // <-- class added here
.style("stroke", "green")
.attr("d", line);
Line_chart.append("path")
.datum(data)
.attr("class", "line line-speed") // <-- and here
.style("stroke", "blue")
.attr("d", line1);
[...]
function updateLines() {
Line_chart.select(".line-extra").attr("d", line);
Line_chart.select(".line-speed").attr("d", line1);
}
function brushed() {
[...]
updateLines();
}
function zoomed() {
[...]
updateLines();
}
I currently trying to learn d3, and wanted to do a small project but was running into an issue. I have been using this block as a base: Brush & Zoom
and replace the area fill with a scatterplot data I had. I was able to get the points to render but when moving the zoom/brush on the second axis, the x-axis for the .focus chart transforms but my scatterplots didn't. I tried a number of ways including to see if I was appending it to the right svg element but in my limited understanding of D3, I'm not too sure what is wrong. My sample code is as follows:
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.area {
fill: #FDB827;
clip-path: url(#clip);
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
</style>
<svg width="960" height="500"></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: 430, 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 parseDate = d3.timeParse("%b %Y");
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.PTS); });
var area2 = d3.area()
.curve(d3.curveMonotoneX)
.x(function(d) { return x2(d.date); })
.y0(height2)
.y1(function(d) { return y2(d.PTS); });
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/kobe-playoff1.csv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.PTS; })]);
x2.domain(x.domain());
y2.domain(y.domain());
let fixedData ={};
let arr = [];
data.forEach((d)=>{
fixedData["date"] = d.date;
fixedData["PTS"] = d.PTS;
arr.push(fixedData);
fixedData = {};
})
//
focus.append("path")
.datum(arr)
.attr("class", "area")
.attr("d", area);
// draw dots
focus.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "brush")
// .attr("class", "brush")
.attr("r", (d)=>{return (d.PTS)})
.attr("cx", (d)=>{return x(d.date)})
.attr("cy", (d)=>{return y(d.PTS)})
.style("fill", "#552583");
// .on("mouseover", function(d) {
// tooltip.transition()
// .duration(200)
// .style("opacity", .9);
// tooltip.html(d["date"] + "<br/> (" + xValue(d)
// + ", " + yValue(d) + ")")
// .style("left", (d3.event.pageX + 5) + "px")
// .style("top", (d3.event.pageY - 28) + "px");
// })
// .on("mouseout", function(d) {
// tooltip.transition()
// .duration(500)
// .style("opacity", 0);
// });
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());
focus.append("g")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
});
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));
}
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 = parseDate(d.date);
// d.price = +d.price;
// d.date = new Date(d.Date);
d.date = Date.parse(d.Date);
d.PTS = +d.PTS;
return d;
}
</script>
The sample data I am using is as follows:
Rk,G,Date,Age,Tm,,Opp,,GS,MP,FG,FGA,FG%,3P,3PA,3P%,FT,FTA,FT%,ORB,DRB,TRB,AST,STL,BLK,TOV,PF,PTS,GmSc
1,1,1997-04-25,18-245,LAL,,POR,W (+18),0,1:00,1,1,1.000,0,0,,0,0,,0,0,0,0,0,0,0,0,2,1.7
2,2,1997-04-27,18-247,LAL,,POR,W (+14),0,5:00,1,3,.333,0,1,.000,4,4,1.000,0,0,0,0,0,0,0,1,6,3.9
3,3,1997-04-30,18-250,LAL,#,POR,L (-8),0,27:00,7,13,.538,2,3,.667,6,8,.750,0,4,4,2,1,0,4,5,22,12.5
4,4,1997-05-02,18-252,LAL,#,POR,W (+4),0,6:00,0,0,,0,0,,0,0,,0,0,0,0,0,0,0,0,0,0.0
5,5,1997-05-04,18-254,LAL,#,UTA,L (-16),0,14:00,1,7,.143,1,5,.200,0,1,.000,1,1,2,3,0,1,0,5,3,-0.1
6,6,1997-05-06,18-256,LAL,#,UTA,L (-2),0,4:00,1,1,1.000,0,0,,0,0,,0,0,0,1,0,0,1,2,2,0.6
7,7,1997-05-08,18-258,LAL,,UTA,W (+20),0,19:00,3,7,.429,0,2,.000,13,14,.929,0,1,1,3,1,1,3,3,19,14.8
8,8,1997-05-10,18-260,LAL,,UTA,L (-15),0,28:00,3,9,.333,3,6,.500,0,0,,0,2,2,0,0,0,5,4,9,-2.1
9,9,1997-05-12,18-262,LAL,#,UTA,L (-5),0,29:00,4,14,.286,0,6,.000,3,3,1.000,0,2,2,2,1,0,1,3,11,3.6
Any help would be greatly appreciated! Thank you.
In your brushed and zoomed functions, you need to select your circles and apply dynamic attributes. But before you do that, you need to rename the class for your circles. Something like:
// draw dots
focus.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "circle")
...
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);
focus.selectAll(".circle")
.attr("r", (d)=>{return (d.PTS)})
.attr("cx", (d)=>{return x(d.date)})
.attr("cy", (d)=>{return y(d.PTS)})
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
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);
focus.selectAll(".circle")
.attr("r", (d)=>{return (d.PTS)})
.attr("cx", (d)=>{return x(d.date)})
.attr("cy", (d)=>{return y(d.PTS)})
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
I'm trying to get a D3.JS scrollable line chart to automatically readjust the axes based on the user's current view.
To give an example, the intended behaviour can be seen on Google Finance.
In the first chart, you can see the price peaking at around 5485:
In the second chart, I have scrolled forward and you can see that the chart has automatically re-scaled to accommodate the new peak at 5510:
Note that this behaviour is different to simply making the chart responsive, which can easily be achieved by putting the entire chart, including data outside the user's view, in a responsive container, e.g.:
.svg-container {
display: inline-block;
position: relative;
width: 100%;
padding-bottom: 65%;
vertical-align: top;
overflow: hidden;
}
In order to solve this problem, I would need to distinguish between the entirety of the price history and the segment the user is currently viewing, then set y-axis range. How can I achieve this?
Not sure I understand the question, but it seems like you just want to scale the y-axis as the selected data range changes. Building off this classic example, I've modified it so that as you brush or zoom the y-axis domain changes to match the current selection. The key code is this in the brushed callback:
// set new y domain based on selection
y.domain([0, d3.max(data, function(d) {
if (d.x < x.domain()[0] || d.x > x.domain()[1]) return 0;
else return d.y;
})]);
// redraw y axis
focus.select(".axis--y").call(yAxis);
Complete example:
<!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="960" height="500"></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: 430,
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.scaleLinear().range([0, width]),
x2 = d3.scaleLinear().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.x);
})
.y0(height)
.y1(function(d) {
return y(d.y);
});
var area2 = d3.area()
.curve(d3.curveMonotoneX)
.x(function(d) {
return x2(d.x);
})
.y0(height2)
.y1(function(d) {
return y2(d.y);
});
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 = [];
for (var i = 0; i < 100; i++) {
data.push({
x: i,
y: i > 50 ? Math.random() * 100 : Math.random() * 50
});
}
x.domain(d3.extent(data, function(d) {
return d.x;
}));
y.domain([0, d3.max(data, function(d) {
return d.y;
})]);
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);
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);
y.domain([0, d3.max(data, function(d) {
if (d.x < x.domain()[0] || d.x > x.domain()[1]) return 0;
else return d.y;
})]);
focus.select(".axis--y").call(yAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
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);
y.domain([0, d3.max(data, function(d) {
if (d.x < x.domain()[0] || d.x > x.domain()[1]) return 0;
else return d.y;
})]);
focus.select(".axis--y").call(yAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
</script>
I have implemented the Drawing Focus + Context via Brushing diagram to display the chi-square results, but the drawing of the results in d3 are strange (check image below at 7 AM).
Also below the image I have included the code which draws the diagram.
ChiSquare Image
var chiSquare = function(config,data,d3){
var margin = {top: 10, right: 10, bottom: 100, left: 40},
margin2 = {top: 430, right: 10, bottom: 20, left: 40},
width = 1127 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var parseDate = d3.time.format("%b %Y").parse;
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom"),
xAxis2 = d3.svg.axis().scale(x2).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left");
var brush = d3.svg.brush()
.x(x2)
.on("brush", brushed);
var area = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x(d.timestamp_unix); })
.y0(height)
.y1(function(d) { return y(d.chiSquare); });
var area2 = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x2(d.timestamp_unix); })
.y0(height2)
.y1(function(d) {return y2(d.chiSquare); });
var svg = d3.select(config.selector).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
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 + ")");
x.domain(d3.extent(data,(function(d) { return d.timestamp_unix; })));
y.domain([0, d3.max(data, (function(d) { return d.chiSquare; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
focus.select(".area").attr("d", area);
focus.select(".x.axis").call(xAxis);
}
function type(d) {
d.timestamp_unix = parseDate(d.timestamp_unix);
d.chiSquare = +d.chiSquare;
return d;
}
//end class
}
I can't see anything wrong in the code, so I assume that the problem is from D3js but I don't know where to look, any ideas?