How can i set each text label vertically in d3? - javascript

Please, have a look. I feel like i'm somewhere near,after searching stackoverflow.
But still can set each label(it's date) vertically so it's readable.
https://codepen.io/DeanWinchester88/pen/XWgjjeW
const svg = d3.select("body")
.append("svg")
.attr("width",w)
.attr("height",h)
.attr("style", "outline: thin solid red;");
svg.selectAll("rect")
.data(dataSet)
.enter()
.append("rect")
.attr("x", (d,i) => i * 10)
.attr("y", (d) => ( h - (d[1]/10 ) ) )
.attr("width", 8)
.attr("height", (d) => d[1] /10 )
.attr("class", 'bar')
.attr("data-date",(d) => d[0])
.attr("data-gdp",(d) => d[1])
.append("title")
.text((d) => d)
svg.selectAll("text")
.data(dataSet)
.enter()
.append("text")
.text((d) => d)
.attr("x", (d,i) => i * 10)
.attr("y", (d) => ( h - (d[1]/10 ) ))
// .attr("transform", "rotate(-5)")
.attr('transform', (d,i)=>{
return 'translate( i * 10, (d) => ( h - (d[1]/10 ) )) rotate(125)';})
});

You can achieve this with just a little tweak. Instead of setting the x and y attribute, we can add that to the transform instead. So we move the object to the position that we want, then rotate it.
That is:
.attr("transform", (d,i) => "translate(" + i*10 + "," + (h-(d[1]/10)) + "),rotate(90)");
Remove the x and y lines before adding that.
That will show the text overlapping with the bars. To have the text above the bar we can just add:
.attr("text-anchor", "end")
You can also change the font-size, to ensure the text doesn't overlap with the other text:
text {
font-size: 12px;
}
The final result is:
let dataSet;
let data;
// let pizda =[1,5,8,15,35,76,36,
function readTextFile(file, callback) {
var rawFile = new XMLHttpRequest();
rawFile.overrideMimeType("application/json");
rawFile.open("GET", file, true);
rawFile.onreadystatechange = function() {
if (rawFile.readyState === 4 && rawFile.status == "200") {
callback(rawFile.responseText);
}
}
rawFile.send(null);
}
//usage:
readTextFile("https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json", function(text){
data = JSON.parse(text);
dataSet = data["data"]
console.log(dataSet)
const w = 3400;
const h = 2000;
const svg = d3.select("body")
.append("svg")
.attr("width",w)
.attr("height",h)
.attr("style", "outline: thin solid red;");
svg.selectAll("rect")
.data(dataSet)
.enter()
.append("rect")
.attr("x", (d,i) => i * 10)
.attr("y", (d) => ( h - (d[1]/10 ) ) )
.attr("width", 8)
.attr("height", (d) => d[1] /10 )
.attr("class", 'bar')
.attr("data-date",(d) => d[0])
.attr("data-gdp",(d) => d[1])
.append("title")
.text((d) => d)
svg.selectAll("text")
.data(dataSet)
.enter()
.append("text")
.text((d) => d)
.attr("text-anchor", "end")
.attr("transform", (d,i) => "translate(" + i*10 + "," + (h-(d[1]/10)) + "),rotate(90)");
});
.bar:hover {
fill: black;
}
.bar {
margin: 6px;
fill: #a87a44;
}
text {
font-size: 12px;
}
<html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title id = "title">Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
</head>
<body>
</body>
</html>

Related

Stacked bar chart: how to make diverging and vertically centered?

I am using D3 to make a stacked bar chart (for more artistic purposes than scientific). I want to design my stacked bar chart to be centered around one group, with half above and half below an invisible line, and have the other two groups be on either side of the line.
Currently, my graph looks like this
But I want it to look more like this
My code is here:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Stacked Bar</title>
</head>
<body>
<div class="container">
<div id="chart"></div>
</div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
const width = 860,
height = 400,
margin = { top: 40, right: 30, bottom: 20, left: 20 };
const svg = d3
.select("#chart")
.append("svg")
.attr("viewBox", [0, 0, width, height]);
d3.csv("test.csv").then((data) => {
let x = d3
.scaleBand(
data.map((d) => d.Time),
[margin.left, width - margin.right]
)
.padding([0.2]);
let y = d3.scaleLinear([0, 500], [height - margin.bottom, margin.top]);
svg
.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x));
svg.append("g").attr("transform", `translate(${margin.left},0)`);
// .call(d3.axisLeft(y).tickSize(-width + margin.left + margin.right));
//protein,carbs,fiber
const subgroups = data.columns.slice(1);
const color = d3.scaleOrdinal(subgroups, [
"#e41a1c",
"#377eb8",
"#4daf4a",
]);
const stackedData = d3.stack().keys(subgroups)(data);
console.log(stackedData);
svg
.append("g")
.selectAll("g")
.data(stackedData)
.join("g")
.attr("fill", (d) => color(d.key))
.selectAll("rect")
.data((d) => d)
.join("rect")
.attr("x", (d) => x(d.data.Time))
.attr("y", (d) => y(d[1]))
.attr("height", (d) => y(d[0]) - y(d[1]))
.attr("width", x.bandwidth());
let legendGroup = svg
.selectAll(".legend-group")
.data(subgroups)
.join("g")
.attr("class", "legend-group");
legendGroup
.append("circle")
.attr("cx", (d, i) => 10 + i * 75)
.attr("cy", 10)
.attr("r", 3)
.attr("fill", (d, i) => color(i));
legendGroup
.append("text")
.attr("x", (d, i) => 20 + i * 75)
.attr("y", 15)
.text((d, i) => subgroups[i]);
});
</script>
</body>
</html>
and csv:
Time,team1,team2,middle
0,5,2,70
1,10,13,89
2,4,15,110
3,6,16,145
4,12,2,167
5,42,3,111
6,6,4,108
7,7,5,92
8,8,34,140
9,12,89,190
10,22,90,398
11,42,91,459
12,60,23,256
13,69,13,253
14,43,11,188
15,42,7,167
16,21,9,124
17,16,12,156
18,7,14,167
19,12,13,188
Does anyone know how I could vertically center each line around the middle group? Is this something to do in the data pre-processing or in the graph making itself?
You have to use the correct offset, in this case d3.offsetWiggle:
const stackedData = d3.stack().offset(d3.stackOffsetWiggle)
In this solution I'm flattening the stacked data and getting the extent, which I'll pass to the y scale:
const flatData = stackedData.flat(2);
y.domain(d3.extent(flatData));
Finally, I'm just moving the x axis to the middle of the y range. Also, I'm hardcoding the stack keys, but making the sequence programatically is trivial, as well as some other details you'll have to adjust.
Here's the result:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Stacked Bar</title>
</head>
<body>
<div class="container">
<div id="chart"></div>
</div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
const width = 860,
height = 400,
margin = {
top: 40,
right: 30,
bottom: 20,
left: 20
};
const svg = d3
.select("#chart")
.append("svg")
.attr("viewBox", [0, 0, width, height]);
const csv = `Time,team1,team2,middle
0,5,2,70
1,10,13,89
2,4,15,110
3,6,16,145
4,12,2,167
5,42,3,111
6,6,4,108
7,7,5,92
8,8,34,140
9,12,89,190
10,22,90,398
11,42,91,459
12,60,23,256
13,69,13,253
14,43,11,188
15,42,7,167
16,21,9,124
17,16,12,156
18,7,14,167
19,12,13,188`;
const data = d3.csvParse(csv);
let x = d3
.scaleBand(
data.map((d) => d.Time), [margin.left, width - margin.right]
)
.padding([0.2]);
let y = d3.scaleLinear().range([height - margin.bottom, margin.top]);
svg.append("g").attr("transform", `translate(${margin.left},0)`);
// .call(d3.axisLeft(y).tickSize(-width + margin.left + margin.right));
//protein,carbs,fiber
const subgroups = ["team1", "middle", "team2"];
const color = d3.scaleOrdinal(subgroups, [
"#377eb8",
"#4daf4a",
"#e41a1c"
]);
const stackedData = d3.stack().offset(d3.stackOffsetWiggle).order(d3.stackOrderNone).keys(subgroups)(data);
const flatData = stackedData.flat(2);
y.domain(d3.extent(flatData));
svg
.append("g")
.selectAll("g")
.data(stackedData)
.join("g")
.attr("fill", (d) => color(d.key))
.selectAll("rect")
.data((d) => d)
.join("rect")
.attr("x", (d) => x(d.data.Time))
.attr("y", (d) => y(d[1]))
.attr("height", (d) => y(d[0]) - y(d[1]))
.attr("width", x.bandwidth());
svg
.append("g")
.attr("transform", `translate(0,${margin.top + (height - margin.bottom)/2})`)
.call(d3.axisBottom(x));
let legendGroup = svg
.selectAll(".legend-group")
.data(subgroups)
.join("g")
.attr("class", "legend-group");
legendGroup
.append("circle")
.attr("cx", (d, i) => 10 + i * 75)
.attr("cy", 10)
.attr("r", 3)
.attr("fill", (d, i) => color(i));
legendGroup
.append("text")
.attr("x", (d, i) => 20 + i * 75)
.attr("y", 15)
.text((d, i) => subgroups[i]);
</script>
</body>
</html>

How to make information box and attach it with chart in d3.js

Is it possible to create a chart with d3.js as given in the attached picture? Especially the information box. The context is from a specific start and end date if the data is missing on date place a dot instead of a bar in the chart. The difficulty I am facing is to attach the information box with the dots using a line using d3.js.
The whole chart should be implemented using (SVG) d3.js.
Can anyone give a solution example with any dataset?
This is just an example, hopefully it will be enough to get you started.
const url = "https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json"
const margin = {
top: 30,
left: 40,
right: 40,
bottom: 100
};
const width = 800;
const height = 300;
const svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
const g = svg.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
const notice = svg.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top + height})`)
notice.append('rect')
.classed('notice-box', true)
.attr('x', 0)
.attr('y', margin.top)
.attr('width', width)
.attr('height', margin.bottom - margin.top);
const warning = notice.append('text')
.attr('x', 10)
.attr('y', margin.top + 30);
const format = d3.timeFormat("Q%q %Y")
const setWarning = (data) => {
warning.text(`Missing data for ${data.map(d => format(d.date)).join(', ')}`);
notice.selectAll('line')
.data(data)
.enter()
.append('line')
.attr('stroke', 'black')
.attr('x1', d => x(d.date))
.attr('y1', margin.top)
.attr('x2', d => x(d.date))
.attr('y2', y(0) - height);
notice.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('fill', 'black')
.attr('r', 3)
.attr('cx', d => x(d.date))
.attr('cy', y(0) - height);
}
var x = d3.scaleTime().range([0, width]);
const y = d3.scaleLinear().range([height, 0]);
// Since v5 d3.json is promise-based, hence the change.
d3.json(url)
.then(response => response.data)
.then(data => data.map(([date, value]) => ({
date: new Date(date),
value: value
})))
.then(data => {
data.filter(({
date
}) => date.getFullYear() >= 2000 && date.getFullYear() <= 2005 && date.getMonth() === 0)
.forEach(d => d.value = null);
x.domain(d3.extent(data.map(({
date
}) => date)));
const barWidth = width / data.length;
y.domain([0, d3.max(data.map(({
value
}) => value))]);
g.append('g')
.call(d3.axisBottom().scale(x))
.attr('id', 'x-axis')
.attr('transform', `translate(0, ${height})`);
g.append('g')
.call(d3.axisLeft(y))
.attr('id', 'y-axis');
g.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', d => x(d.date))
.attr('y', d => y(d.value))
.attr('width', barWidth)
.attr('height', d => height - y(d.value))
.style('fill', '#33adff');
const missing = data.filter(({
value
}) => value === null);
setWarning(missing);
});
#y-axis path {
stroke: black;
stroke-width: 1;
fill: none;
}
#x-axis path {
stroke: black;
stroke-width: 1;
fill: none;
}
.notice-box {
fill: none;
stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg></svg>

D3.js add circles to every other point on a line graph

I've got a line chart that needs circles on every other point for column A and C (not for column B). I've struggled to figure out how to do it. This is my line chart without the circles:
date,A=count,A=rank,B=count,B=rank,C=count,C=rank
2016-11-01,60588,213,51915,46,41200,10
2016-12-01,73344,216,58536,47,41230,10
2017-01-01,64164,219,53203,50,51220,12
2017-02-01,85295,224,34047,52,61000,15
2017-03-01,86089,226,44636,54,71200,16
2017-04-01,96871,230,55281,55,71000,10
2017-05-01,97622,234,85879,55,67900,10
I've tried dozens of solutions and I'm very stuck! Here is one of the things I've tried:
linesAndDots.selectAll("line-circle")
.data(data)
.enter().append("circle")
.attr("class", "data-circle")
.attr("r", 5)
.attr("cx", function(d) { return xScale(d.date); })
.attr("cy", function(d) { return yScale(d.measurement); });
But that is giving back NaN for the cx and cy values.
My full code looks like this:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
path.line-0 {
fill: none;
stroke: #1F77B4;
}
path.line-1 {
fill: none;
stroke: #FF7F0E;
}
</style>
</head>
<!-- Body tag is where we will append our SVG and SVG objects-->
<body>
</body>
<!-- Load in the d3 library -->
<script src="lib/d3.v5.min.js"></script>
<div id="svgdiv"></div>
<script>
//------------------------1. PREPARATION------------------------//
//-----------------------------SVG------------------------------//
var columns=['A=count','B=count'];
var columnsB=['A=rank','B=rank'];
var width = 960;
var height = 500;
var margin = 5;
var padding = 5;
var adj = 75;
// we are appending SVG first
var svg = d3.select("body").append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "-"
+ adj + " -"
+ adj + " "
+ (width + adj *3) + " "
+ (height + adj*3))
.style("padding", padding)
.style("margin", margin)
.classed("svg-content", true);
//-----------------------------DATA-----------------------------//
var timeConv = d3.timeParse("%Y-%m-%d");
var formatTime = d3.timeFormat("%b %y")
var dataset = d3.csv("toShare.csv");
dataset.then(function(data) {
console.log(data.columns.slice(1))
var slices = columns.map(function(id) {
return {
id: id,
values: data.map(function(d){
return {
date: timeConv(d.date),
measurement: +d[id]
};
})
};
})
//----------------------------SCALES----------------------------//
var xScale = d3.scaleTime().range([0,width]);
var yScale = d3.scaleLinear().rangeRound([height, 0]);
xScale.domain(d3.extent(data, function(d){
return timeConv(d.date)}));
yScale.domain([(0), d3.max(slices, function(c) {
return d3.max(c.values, function(d) {
return d.measurement + 4; });
})
]);
//-----------------------------AXES-----------------------------//
var yaxis = d3.axisLeft()
.ticks(9)
.scale(yScale);
var xaxis = d3.axisBottom()
.ticks(7)
.scale(xScale);
//----------------------------LINES-----------------------------//
var line = d3.line()
.x(function(d) { return xScale(d.date); })
.y(function(d) { return yScale(d.measurement); });
let id = 0;
var ids = function () {
return "line-"+id++;
}
//-------------------------2. DRAWING---------------------------//
//-----------------------------AXES-----------------------------//
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xaxis)
.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
50 + ")")
.style("text-anchor", "middle")
.text("Month");
svg.append("g")
.attr("class", "axis")
.call(yaxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - adj)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Running Total");
//----------------------------LINES-----------------------------//
var linesAndDots = svg.selectAll("lines")
.data(slices)
.enter()
.append("g");
linesAndDots.append("path")
.attr("class", ids)
.attr("d", function(d) { return line(d.values); });
linesAndDots.selectAll("line-circle")
.data(data)
.enter().append("circle")
.attr("class", "data-circle")
.attr("r", 5)
.attr("cx", function(d) {
console.log("id", id)
return 5; })
.attr("cy", function(d) { return 40; });
//Add the label on the right
linesAndDots.append("text")
.attr("class", ids)
.datum(function(d) {
return {
id: d.id,
value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) {
return "translate(" + (xScale(d.value.date) + 10)
+ "," + (yScale(d.value.measurement) + 5 ) + ")"; })
.attr("x", 5)
.text(function(d) { return d.id.replace("=count", ""); });
});
</script>
</body>
Thanks for the help!
The problem can be inspected by seeing what d really is in the cx and cy function.
cx's problem: you didnt parse the date string into a Date object like you did for the slices data; cy's problem: the data item has no measurement key.
You have used both data and slices. To make the code more consistent, I fix the code of circle drawing using slices too.
linesAndDots
.selectAll(".data-circle")
.data(d=>d.values) // `d` now is the one of the two entries of `slices`,
//and we want to use the `values` array of each entry.
.enter()
.append("circle")
.attr("class", "data-circle")
.attr("r", 5)
.attr("cx", function(d) {
return xScale(d.date);
})
.attr("cy", function(d) {
return yScale(d.measurement)
});
A demo here

Unable to append path to svg

I am trying to add a regression line to a scatter plot. I am using the below code as an example for scatter plot.
http://bl.ocks.org/majetisiri/57da501b3182bd08d17402261c7187f7
I am appending the path to svg as explained here:
Plot regression line on a scatter plot from regression coefficients
But the regression line is not visible.
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<!-- Example based on http://bl.ocks.org/mbostock/3887118 -->
<!-- Tooltip example from http://www.d3noob.org/2013/01/adding-tooltips-to-d3js-graph.html -->
<style>
body {
font: 11px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
}
.tooltip {
position: absolute;
width: 200px;
height: 28px;
pointer-events: none;
}
h1 {
text-align: center;
}
h2 {
text-align: left;
}
</style>
<body>
<p><span><label for="y-axis">Select y-axis</label></span>
<select id="y-value">
<option value="FLFPR">Female LFPR</option>
<option value="lnGDP">Log GDP per capita</option>
<option value="Fertility">Fertility rate</option>
</select>
<p><span><label for="x-axis">Select x-axis</label></span>
<select id="x-value">
<option value="FLFPR">Female LFPR</option>
<option value="lnGDP">Log GDP per capita</option>
<option value="Fertility">Fertility rate</option>
</select>
<button onclick="setGraph()">submit</button>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="https://unpkg.com/d3-regression#1.2.3/dist/d3-regression.min.js"></script>
<script>
function drawGraph(xText, yText) {
$('svg').remove();
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
/*
* value accessor - returns the value to encode for a given data object.
* scale - maps value to a visual display encoding, such as a pixel position.
* map function - maps from data value to display value
* axis - sets up axis
*/
// setup x
var xValue = function(d) { return d[xText];}, // data -> value
xScale = d3.scale.linear().range([0, width]), // value -> display
xMap = function(d) { return xScale(xValue(d));}, // data -> display
xAxis = d3.svg.axis().scale(xScale).orient("bottom");
// setup y
var yValue = function(d) { return d[yText];}, // data -> value
yScale = d3.scale.linear().range([height, 0]), // value -> display
yMap = function(d) { return yScale(yValue(d));}, // data -> display
yAxis = d3.svg.axis().scale(yScale).orient("left");
// setup fill color
var cValue = function(d) { return d.IG;},
color = d3.scale.category20();
// add the graph canvas to the body of the webpage
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 + ")");
// add the tooltip area to the webpage
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// load data
d3.csv("scatter2.csv", function(error, data) {
// change string (from CSV) into number format
data.forEach(function(d) {
d[yText] = +d[yText];
d[xText] = +d[xText];
//console.log (d.School);
//console.dir (d);
});
// don't want dots overlapping axis, so add in buffer to data domain
xScale.domain([d3.min(data, xValue)-1, d3.max(data, xValue)+1]);
yScale.domain([d3.min(data, yValue)-1, d3.max(data, yValue)+1]);
// x-axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text(xText);
// y-axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(yText);
// draw dots
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 6.6)
.attr("opacity", 0.9)
.style("stroke", function(d) { return color(cValue(d));})
.attr("cx", xMap)
.attr("cy", yMap)
.style("fill", function(d) { return color(cValue(d));})
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(d["Player"] + "<br/> " + d.School + "<br/>(" + xValue(d)
+ ", " + yValue(d) + ")")
.style("left", (d3.event.pageX + 10) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
// draw legend
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(10," + (i+7) * 20 + ")"; });
// draw legend colored rectangles
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
// draw legend text
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d;})
// get regression coefficients
regData = data.map(item => ({x: item[xText], y: item[yText]}));
res = drawRegressionLine(regData)
console.log("regression results")
console.log(res)
var line = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");
// var line = d3.svg.line()
// .x(function(d) { return xScale(d['x']); })
// .y(function(d) { return yScale(d['y']); });
// var lineFunction = d3.svg.line()
// .x(function(d) { return d.x; })
// .y(function(d) { return d.y; })
// .interpolate("linear");
var regLine = svg.append("path")
.datum(res)
.attr("d", line)
.style("stroke", "steelblue")
.style("stroke-width", "6px");
// var lineGraph = svg.append("path")
// .attr("d", line(res))
// .attr("stroke", "blue")
// .attr("stroke-width", 2)
// .attr("fill", "black");
});
}
// draw regression line
function drawRegressionLine(regData) {
console.log("beginning")
console.log("inside draw regression lilne")
linearRegression = d3.regressionLinear()
.x(d => d.x)
.y(d => d.y);
res = linearRegression(regData)
return res;
}
// drawGraph('Passing TD', 'Rushing TD');
function setGraph() {
console.log("inside set graph")
console.log($('#x-value').val())
drawGraph($('#x-value').val(), $('#y-value').val());
}
</script>
</body>
</html>
Please help me find what is wrong with the code.
It looks like you are not passing the your linear regression into your x and y scale.
Try:
var line = d3.svg.line()
.x(function(d) { return xScale(d[0]); })
.y(function(d) { return yScale(d[1]); })
.interpolate("linear");

How do you clear an SVG in d3.js in javascript

I just created my first test program in d3.js. It works ok so far. It creates rects to illustrate data it reads from a .csv file and it loads a new dataset if the user picks different data. But it writes it on top of what is already there,
This code snippet writes new graphs without clearing what is already there
barsM = svg.selectAll(".bar").data(dataMale).enter();
barsF = svg.selectAll(".bar").data(dataFemale).enter()
barsM.append("rect")
.attr("class", "bar1")
.attr("x", function (d) { return x(d.age_group); })
.attr("width", x.rangeBand() / 2)
.attr("y", function (d) { return y(d.mean * 100); })
.attr("height", function (d, i, j) { return height - y(d.mean *100); });
barsF.append("rect")
.attr("class", "bar2")
.attr("x", function (d) { return x(d.age_group) + x.rangeBand() / 2; })
.attr("width", x.rangeBand() / 2)
.attr("y", function (d) { return y(d.mean * 100); })
.attr("height", function (d, i, j) { return height - y(d.mean * 100); });
You can see the program in action here
http://www.gelsana.com/IHME/blog/
How do I clear out the graphs? I do not understand the code for using datum instead of data.
I would assume that this would work
svg.selectAll(".bar").data(data).exit().remove();
barsM = svg.selectAll(".bar").data(dataMale).enter();
barsF = svg.selectAll(".bar").data(dataFemale).enter();
or this
svg.selectAll(".bar").data(dataMale).exit().remove();
svg.selectAll(".bar").data(dataFemale).exit().remove();
barsM = svg.selectAll(".bar").data(dataMale).enter();
barsF = svg.selectAll(".bar").data(dataFemale).enter();
I would think that if there is an append,there would be a remove. But staring at this code and pondering how to put the right code before this block did not yield any results
barsM.append("rect")
.attr("class", "bar1")
.attr("x", function (d) { return x(d.age_group); })
.attr("width", x.rangeBand() / 2)
.attr("y", function (d) { return y(d.mean * 100); })
.attr("height", function (d, i, j) { return height - y(d.mean *100); });
barsF.append("rect")
.attr("class", "bar2")
.attr("x", function (d) { return x(d.age_group) + x.rangeBand() / 2; })
.attr("width", x.rangeBand() / 2)
.attr("y", function (d) { return y(d.mean * 100); })
.attr("height", function (d, i, j) { return height - y(d.mean * 100); });
When I tried this
svg.selectAll("*").remove();
It removed the SVG and it did not come back. I assume this is because I set the margins and size of the thing in the javascript and so using this trick would involve rewriting and moving the code I have all around.
Here is the entire javascript file. Please tell me what to change to make this work. The code snippits and the refresh button content to clearing out the svg is not working.
var margin = {top: 20, right: 50, bottom: 100, left: 75},
width = 740 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear().domain([300, 1100]).range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxisLeft = d3.svg.axis().scale(y).ticks(4).orient("left");
var svg = d3.select("#chart-svg").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "graph")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var _location = "USA";
var _year = "1990";
var _metric = "obese";
function loadCountry(inputcountry)
{
//d3.selectAll("svg > *").remove();
_location = inputcountry;
load();
}
function loadYear(inputyear)
{
//d3.selectAll("svg > *").remove();
_year = inputyear;
load();
}
function loadMetric(inputmetrice)
{
_metric = inputmetrice;
load();
}
var headers = [ "Male", "Female"];
function load() {
d3.csv("../database/IHME_GBD_2013_OBESITY_PREVALENCE_1990_2013_Y2014M10D08.CSV", type, function (error, data)
{
var dataMale = data.filter(function (d) {
return (d.location == _location) &&
(d.year == _year) &&
(d.metric == _metric) &&
(d.sex_id == 1)
});
var dataFemale = data.filter(function (d) {
return (d.location == _location) &&
(d.year == _year) &&
(d.metric == _metric) &&
(d.sex_id == 2)
});
x.domain(data.map(function (d) { return d.age_group; }));
y.domain([0, d3.max(data, function (d) { return d.mean * 100; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.16em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
svg.append("g")
.attr("class", "y axis axisLeft")
.attr("transform", "translate(0,0)")
.call(yAxisLeft)
.append("text")
.attr("y", 6)
.attr("dy", "-2em")
.style("text-anchor", "end")
.text("Mean");
svg.selectAll(".bar").data(data).exit().remove();
barsM = svg.selectAll(".bar").data(dataMale).enter();
barsF = svg.selectAll(".bar").data(dataFemale).enter();
barsM.append("rect")
.attr("class", "bar1")
.attr("x", function (d) { return x(d.age_group); })
.attr("width", x.rangeBand() / 2)
.attr("y", function (d) { return y(d.mean * 100); })
.attr("height", function (d, i, j) { return height - y(d.mean *100); });
barsF.append("rect")
.attr("class", "bar2")
.attr("x", function (d) { return x(d.age_group) + x.rangeBand() / 2; })
.attr("width", x.rangeBand() / 2)
.attr("y", function (d) { return y(d.mean * 100); })
.attr("height", function (d, i, j) { return height - y(d.mean * 100); });
var color = d3.scale.ordinal()
.domain([0, 1])
.range(["#ff0000", "#0000ff"]);
var legend = svg.selectAll(".legend")
.data(headers.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function (d, i) { return "translate(-20," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function (d) { return d; });
var tooltip = svg.append("g")
.attr("class", "tooltip");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "red")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
});
function type(d) {
d.mean = +d.mean;
return d;
}
}
EDIT:
I tried a solution offered here and it did not work
Here is the url
http://www.gelsana.com/IHME/echonax/
The HTML
<html>
<head>
<link rel="stylesheet" type="text/css" href="stylefile.css">
</head>
<body>
<script type="text/javascript" src="http://d3js.org/d3.v2.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
<script type="text/javascript" src="thejsfile.js"></script>
<svg id="graphContainer" class="graphContainer">
<circle r="10" cx="50" cy="50" ></circle>
</svg>
<button>
remove svg contents
</button>
</body>
</html>
Here is the javascript file
var svg = d3.select('svg');
var btn = d3.select('button');
btn.on('click', ()=>{
svg.selectAll("*").remove();
});
and here is the css file
svg{
height:500px;
width:500px;
background: gray;
}
path.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
}
circle {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
}
text {
font: 10px sans-serif;
pointer-events: none;
}
text.shadow {
stroke: #fff;
stroke-width: 3px;
stroke-opacity: .8;
}
body {
background-color: white;
margin: 0px;
}
.graphContainer {
text-shadow: -1px -1px 0 white, 1px -1px 0 white, -1px 1px 0 white, 1px 1px 0 white;
}
When I loaded this on the internet and clicked the button, it did not do anything.
I think my next step will try to find this example code you talk of that used this update cycle. As PatelGatnan said, I think I am missing the Enter and Exit parts
These additions did nothing. The program still does not refresh after a new selection.
As mentioned in the comment by #PavelGatnar you should use the enter/update/exit pattern. But to answer your question you clear the contents of the svg (everything under svg) with:
d3.select(".graph").selectAll("*").remove();
Example: https://jsfiddle.net/rqqko9hd/3/
adding this function and calling it whenever a control was called fixed the issue. The graph is reset and completely redrawn. It would be nice if the graph was animated and the bars moved whenever a new selection was made, but this was good enough:
function resetchart()
{
d3.select("svg").remove();
svg = d3.select("#chart-svg").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "graph")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
}

Categories

Resources