d3 v4 drag line chart with x and y axes - javascript

I'm new to d3.js. I wanted to drag a line chart using its points. It is working fine without the x and y axes.
I have used this example as reference: https://bl.ocks.org/mbostock/4342190
With the axes to the line chart the line is not plotting correcly. Please have a look into the snippet below.
Thanks in advance.
<!DOCTYPE html>
<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;
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 + ')')
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", "steelblue")
.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) {
d3.select(this)
.attr('cx', d[0] = d3.event.x)
.attr('cy', d[1] = d3.event.y)
focus.select('path').attr('d', line);
}
function dragended(d) {
d3.select(this).classed('active', false);
}
</script>

d3.event is holding pixel positions, but your plot is driven by user-space coordinates. So, you need to convert those pixel positions to user-space. You can use your scales invert method to do so.
function dragged(d) {
d[0] = x.invert(d3.event.x); // convert to user-space
d[1] = y.invert(d3.event.y);
d3.select(this)
.attr('cx', x(d[0])) // back to pixels
.attr('cy', y(d[1]))
focus.select('path').attr('d', line); //line does pixel conversion too
}
Running code:
<!DOCTYPE html>
<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;
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 + ')')
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", "steelblue")
.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>

Related

How to add grid lines to a Chart in Javascript

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>

Can not show tooltip on a bar chart - d3js / javascript

I do not know why the tooltip does not appear. Here is my code:
// set the dimensions and margins of the graph
var margin = { top: 20, right: 30, bottom: 50, left: 90 }
var width = 460 - margin.left - margin.right
var height = 2000 - margin.top - margin.bottom;
const dataUrl = "https://raw.githubusercontent.com/yushinglui/IV/main/course_distance_v2.csv"
//fetch the data
d3.csv(dataUrl)
.then((data) => {
// append the svg object to the body of the page
var svg = d3.select("#graph-1")
.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 + ")");
// Add X axis
var x = d3.scaleLinear()
.domain([0, 60])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end");
// Y axis
var y = d3.scaleBand()
.range([0, height])
.domain(data.map(function (d) { return d.CourseCode; }))
.padding(0.1);
svg.append("g")
.call(d3.axisLeft(y))
// create tooltip element
const tooltip = d3.select("body")
.append("div")
.attr("class", "d3-tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.style("padding", "15px")
.style("background", "rgba(0,0,0,0.6)")
.style("border-radius", "5px")
.style("color", "#fff")
.text("a simple tooltip");
//Bars
let bars = svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function (d) { return 1; })
.attr("y", function (d) { return y(d.CourseCode); })
.attr("width", function (d) { return x(d.AverageDistance); })
.attr("height", function (d) { return y.bandwidth(); })
.attr("fill", "orange");
.on("mouseover", function (d, i) {
tooltip.html(`CourseName: ${d.CourseName}`).style("visibility", "visible");
d3.select(this)
.attr("fill", "orangered");
})
.on("mousemove", function () {
tooltip
.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px");
})
.on("mouseout", function () {
tooltip.html(``).style("visibility", "hidden");
d3.select(this).attr("fill", "orange");
});
//axis labels
svg.append('text')
.attr('x', -(height / 2))
.attr('y', width - 410)
.attr('transform', 'rotate(-90)')
.attr('text-anchor', 'middle')
.text('Course Code');
svg.append('text')
.attr('x', 150)
.attr('y', 1970)
.attr('transform', 'rotate()')
.attr('text-anchor', 'middle')
.text('Average Distance (Miles)');
//text labels on bars
svg.selectAll(null)
.data(data)
.enter()
.append("text")
.text(function (d) { return d.AverageDistance; })
.attr("x", function (d) { return x(d.AverageDistance) + 15; })
.attr("y", function (d) { console.log(d); return y(d.CourseCode) + y.bandwidth() * (0.5 + 0.1); })
.attr("font-family", "sans-serif")
.attr("font-size", "12px")
.attr("fill", "black")
.attr("text-anchor", "middle");
})
my reference website:
https://perials.github.io/responsive-bar-chart-with-d3/
https://observablehq.com/#bsaienko/animated-bar-chart-with-tooltip
https://blockbuilder.org/1Cr18Ni9/bfadecc96183c48d13b7b90bcf358a61
http://bl.ocks.org/katirg/5f168b5c884b1f9c36a5

Focus not displaying the graph properly D3 javascript

Here is the template for my Django where I am visualizing training using D3:
.line {
fill: none;
stroke: gray;
stroke-width: 2px;
}
<meta charset="utf-8">
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var real = {{values.real0|safe}}, pred = {{values.got0|safe}};
var margin = {top: 20, right: 20, bottom: 110, left: 50},
margin2 = {top: 430, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var x = d3.scaleLinear().range([0, width]).domain([0, Object.keys(real).length]),
x2 = d3.scaleLinear().range([0, width]).domain([0, Object.keys(real).length]),
y = d3.scaleLinear().range([height, 0]).domain([0, 1]),
y2 = d3.scaleLinear().range([height2, 0]).domain([0, 1]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush", brushed);
var svg = d3.select("body").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 formain = d3.line()
.x(function(d,i) { return x(i); })
.y(function(d) { return y(d); });
var forbrush = d3.line()
.x(function(d,i) { return x2(i); })
.y(function(d) { return y2(d); });
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 + ")");
// Real starts
var color = d3.scaleLinear()
.domain([0, 0.5, 1])
.range(["red", "dodgerblue", "lime"]);
// x.domain(d3.extent(data, function(d) { return d.date; }));
// y.domain([0, d3.max(data, function(d) { return d.price; })+200]);
// x2.domain(x.domain());
// y2.domain(y.domain());
// append scatter plot to main chart area
var dots = focus.append("g");
dots.attr("clip-path", "url(#clip)");
dots.selectAll("dot")
.data(real)
.enter().append("circle")
.attr('class', 'dot')
.attr("r",5)
.style("opacity", .5)
.attr("cx", function(d,i) { return x(i); })
.attr("cy", function(d) { return y(d); })
.attr("fill",(function (d) { return color(d) }));
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);
focus.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text(Object.keys({{values|safe}}));
// console.log(Object.keys({{values|safe}}));
svg.append("text")
.attr("transform",
"translate(" + ((width + margin.right + margin.left)/2) + " ," +
(height + margin.top + margin.bottom) + ")")
.style("text-anchor", "middle")
.text("index");
// append scatter plot to brush chart area
var dots = context.append("g");
dots.attr("clip-path", "url(#clip)");
dots.selectAll("dot")
.data(real)
.enter().append("circle")
.attr('class', 'dotContext')
.attr("r",3)
.style("opacity", .5)
.attr("cx", function(d,i) { return x2(i); })
.attr("cy", function(d) { return y2(d); })
.attr("fill",(function (d) { return color(d) }));
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("path")
.data([real])
.attr("class", "line")
.attr("d", formain);
context.append("path")
.data([real])
.attr("class", "line")
.attr("d", forbrush);
//create brush function redraw scatterplot with selection
function brushed() {
var selection = d3.event.selection;
x.domain(selection.map(x2.invert, x2));
focus.selectAll(".dot")
.attr("cx", function(d,i) { return x(i); })
.attr("cy", function(d) { return y(d); });
context.selectAll(".line")
.attr("cx", function(d,i) { return x(i); })
.attr("cy", function(d) { return y(d); });
focus.select(".axis--x").call(xAxis);
context.select(".axis--x").call(xAxis2);
}
</script>
The output I received is something like the following:
What I want is the the magnifier focus should display the respective contents of the line and the dots. Plus I want to have the line in the background and dots on the foreground.
Please help me modify the sample for my use. There is some attribute I guess I am missing.
The sample csv need is : Sample Csv
Try to change the few thing and it will work. see below:
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 + ")");
focus.append("path")
.data([real])
.attr("class", "line")
.attr("d", formain);
context.append("path")
.data([real])
.attr("class", "line")
.attr("d", forbrush);
Place it just as mentioned.
Change the brushed() function like the following:
function brushed() {
var selection = d3.event.selection;
x.domain(selection.map(x2.invert, x2));
focus.selectAll(".dot")
.attr("cx", function(d,i) { return x(i); })
.attr("cy", function(d) { return y(d); });
focus.selectAll(".line")
.attr("d",formain)
}
See the output of mine. It worked.:
Hope this will help you.

Scale d3 x axis(time) to zero

I am making a chart using d3 and time data.
However, its scale is wrong.
It is a simple example.
Code is below:
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = svg.attr("width"),
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseTime = d3.timeParse("%H:%M:%S");
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var line = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
d3.csv("data.csv", function(d) {
d.date = parseTime(d.date);
d.close = +parseFloat(d.close);
return d;
}, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.select(".domain");
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("dy", "0.71em")
.attr("y",2)
.attr("text-anchor", "end");
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
});
Data is:
date,close
00:00:00,0.00
03:00:00,10
06:00:00,20
09:00:00,30
12:00:00,40
15:00:00,50
18:00:00,60
21:00:00,70
The point midnight,0 is wrong. It should be at 0,0.
Instead, it generates this:
How can it fit the chart correctly (starting at 0,0)?
Link to the Codepen:
https://codepen.io/jaqueline-passos/pen/mGpexG/

Trying to user scatterplots in a d3 chart with brush/zoom

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));
}

Categories