D3 - loading xml data from file, using scales for barchart - javascript

I'm following this great tutorial on d3 here
http://chimera.labs.oreilly.com/books/1230000000345/ch09.html#_modernizing_the_bar_chart
which introduces a bar chart in this chapter using scales. Now I wanted to load my data from an xml file but of course my code doesn't work. I'm guessing that in creating xScale and yScale variables I don't get the length of the dataset
Here is the xml file
<data>
<value>5</value>
<value>10</value>
<value>53</value>
<value>19</value>
<value>61</value>
<value>25</value>
<value>22</value>
<value>18</value>
<value>15</value>
<value>13</value>
<value>11</value>
<value>12</value>
<value>15</value>
<value>20</value>
<value>18</value>
<value>17</value>
<value>16</value>
<value>18</value>
<value>23</value>
<value>25</value>
</data>
And the code itself
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vjezba - 9.poglavlje - barchart</title>
<script type="text/javascript" src="d3/d3.v3.js"></script>
<style type="text/css">
/* No style rules here yet */
</style>
</head>
<body>
<script type="text/javascript">
d3.xml("values.xml","application/xml", function(dataset) {
var w = 600;
var h = 250;
//var dataset = [ 5, 10, 53, 19, 61, 25, 22, 18, 15, 13,11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
//radi jednakog razmaka stupova
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset)])
.range([0, h]);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create bars
svg.selectAll("rect")
.data(dataset.documentElement.getElementsByTagName("value"))
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
});
/*
//Create labels
svg.selectAll("text")
.data(xml.documentElement.getElementsByTagName("value"))
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
*/
</script>
</body>

Simple mistake. You need to use the "d.value" instead of "d". Your sample data is just an array of numbers, but your JSON data is an array of objects with a "value" attribute.
Try this to see your example:
http://jsfiddle.net/5edkw/1/
Since I need code to post jsfiddle links...here's a snippet of what you already know:
.attr("height", function(d) {
return yScale(d.value);
})
The "/0/" version is the original you posted to pastebin.

Related

Graph is no longer appearing when I use d3.json to access json data

I'm very new to D3.js - please can you help? I have successfully created a bar chart and used json data within the code, but when I removed it to create a separate json file and inserted d3.json("sales.json").then(function(data){ graph as before};) it no longer works. It's such a simple line of code to insert and matches the code I've found online so I can't see why it isn't working. Can anyone help please? Thanks.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="L4F - Axes and scales ">
<title>SVG</title>
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
</head>
<body>
<h1> Sales data </h1>
<script>
d3.json("sales.json").then(function(data){
var w = 500;
var h = 500;
var padding = 2;
var lmargin = 50;
var bmargin = 50;
var tmargin = 50;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h)
.style("background", "white");
var plotArea = svg.append('g');
function colourPicker(v){
if (v<=20) {return "#666666";}
else if (v>20) {return "#ff0033";}
}
plotArea.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x",function(d, i){return i * ((w - lmargin)/dataset.length) + lmargin;})
.attr("y",function (d){ return h - (d.s*4) - bmargin;})
.attr("width", (w-lmargin)/dataset.length - padding)
.attr("height", function(d){return d.s*4;})
.attr("fill", function(d){ return colourPicker(d.s);})
plotArea.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {return (d.m+" £"+d.s);})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {return i*((w - lmargin)/dataset.length)+((w - lmargin)/dataset.length - padding)/2;})
.attr("y", function(d) { return h-(d.s*4)+14 - bmargin;})
.attr("font-family", "sans-serif")
.attr("font-size", 12)
.attr("fill", "white")
var maxY = d3.max(dataset.map(function(d){return (d.s);}))
var yScale = d3.scaleLinear()
.domain([0, maxY*4])
.range([h-maxY*4, 0]);
var yAxis = d3.axisLeft()
.scale(yScale);
var yAxisGroup = svg.append('g')
.attr('transform', 'translate('+lmargin+','+(bmargin)+')')
.call(yAxis);
yAxisGroup.append("text")
.text("Sales")
.attr("y", h/2 - bmargin)
.attr("x", lmargin/2)
.attr("transform", "translate(-230,230) rotate(270)")
.style("fill", "black")
.attr("font-size", "14")
.attr("text-anchor", "middle");
};)
</script>
</body>
</html>
And my json file looks like this..
[
{
"s":25,
"m":"Jan"
},
{
"s":10,
"m":"Feb"
},
{
"s":15,
"m":"Mar"
}
]

Change the colours of the bars at mouseout in svg-tooltip.html

My question is based on a little modification in the code shown below. I code in Python, and HTML/CSS are new to me. I have the following code.
In the code below , modify the code such that on mouse out the values of the bars remain there, but they are white in color.
How can I accomplish the same. Many thanks. I am learning this.
//Width and height
var w = 600;
var h = 250;
var dataset = [5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25
];
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, w])
.paddingInner(0.05);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, h]);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create bars
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => h - yScale(d))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(d))
.attr("fill", d => "rgb(100, 0, " + (d * 10) + ")")
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("x")) + xScale.bandwidth() / 2;
var yPosition = parseFloat(d3.select(this).attr("y")) + 14;
//Create the tooltip label
svg.append("text")
.attr("id", "tooltip")
.attr("x", xPosition)
.attr("y", yPosition)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("font-weight", "bold")
.attr("fill", "black")
.text(d);
})
.on("mouseout", () => d3.select("#tooltip").remove());
//Remove the tooltip
rect:hover {
fill: orange;
}
<script src="//d3js.org/d3.v5.min.js" charset="utf-8"></script>

update d3 chart with new data

I want to update the bar chart with new data and I looked over this question:
How to update d3.js bar chart with new data
But it is not working for me. Probably because I don't know where to put the exit().remove() functions within my code. I tried putting this line
svg.selectAll("rect").exit().remove();
below the create bars section but it just removes all of the labels. Then, if I put it right after the create labels portion it removes the chart entirely. How can I get the update button change the chart with new data?
function draw(data) {
//Width and height
var w = 250;
var h = 250;
var xScale = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(data)])
.range([0, h]);
//Create SVG element
var svg = d3.select("#chart")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create bars
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", "steelblue");
//Create labels
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
};
function update() {
var data = [ 25, 22, 18, 15, 13 ];
draw(data);
}
var data = [ 21, 3, 5, 21, 15 ];
window.onload = draw(data);
With d3v3 (as I can see from your code you use this version) you should update your chart this way:
In update function set the new domain for yScale:
function update(newData) {
yScale.domain([0, d3.max(newData)]);
After that, apply new selection with selectAll("rect").data(newData), store selection in rects variable and set new value for appropriate attributes (if you do not want animation effect, remove .transition() .duration(300)):
var rects = d3.select("#chart svg")
.selectAll("rect")
.data(newData);
// enter selection
rects
.enter().append("rect");
// update selection
rects
.transition()
.duration(300)
.attr("y", function(d) {
return h - yScale(d);
})
.attr("height", function(d) {
return yScale(d);
})
Exit selection with exit method:
rects
.exit().remove();
Do the same way with text. I rewrite your code, look at the example in a hidden snippet below:
var myData = [21, 3, 5, 21, 15];
//Width and height
var w = 250;
var h = 250;
var yScale = null;
function draw(initialData) {
var xScale = d3.scale.ordinal()
.domain(d3.range(initialData.length))
.rangeRoundBands([0, w], 0.05);
yScale = d3.scale.linear()
.domain([0, d3.max(initialData)])
.range([0, h]);
//Create SVG element
var svg = d3.select("#chart")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(initialData)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", "steelblue");
svg.selectAll("text")
.data(initialData)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
}
function update(newData) {
yScale.domain([0, d3.max(newData)]);
var rects = d3.select("#chart svg")
.selectAll("rect")
.data(newData);
// enter selection
rects
.enter().append("rect");
// update selection
rects
.transition()
.duration(300)
.attr("y", function(d) {
return h - yScale(d);
})
.attr("height", function(d) {
return yScale(d);
})
// exit selection
rects
.exit().remove();
var texts = d3.select("#chart svg")
.selectAll("text")
.data(newData);
// enter selection
texts
.enter().append("rect");
// update selection
texts
.transition()
.duration(300)
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.text(function(d) {
return d;
})
// exit selection
texts
.exit().remove();
}
window.onload = draw(myData);
setInterval(function() {
var data = d3.range(5).map(function() {
return parseInt(Math.random() * 20 + 1);
});
update(data);
}, 3000)
<div id="chart"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>

How to make a clickable transition bar graph in d3 v4?

How to make a clickable transition bar graph in d3 v4?
Current code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: A bar chart that transitions to new data!</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style type="text/css">
/* No style rules here yet */
</style>
</head>
<body>
<p>Click on this text to update the chart with new data values (once).</p>
<script type="text/javascript">
//Width and height
var w = 600;
var h = 250;
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
var xScale = d3.scaleBand()
.rangeRound([0, w])
.padding(0.05);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, h]);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create bars
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.bandwidth())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
//Create labels
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.bandwidth() / 2;
})
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
//On click, update with new data
d3.select("p")
.on("click", function() {
//New values for dataset
dataset = [ 11, 12, 15, 20, 18, 17, 16, 18, 23, 25,
5, 10, 13, 19, 21, 25, 22, 18, 15, 13 ];
//Update all rects
svg.selectAll("rect")
.data(dataset)
.transition() // <-- This makes it a smooth transition!
.attr("y", function(d) {
return h - yScale(d);
})
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
//Update all labels
svg.selectAll("text")
.data(dataset)
.text(function(d) {
return d;
})
.attr("x", function(d, i) {
return xScale(i) + xScale.band() / 2;
})
.attr("y", function(d) {
return h - yScale(d) + 14;
});
});
</script>
</body>
</html>
Code above from:
https://github.com/alignedleft/d3-book/blob/master/chapter_09/05_transition.html
http://examples.oreilly.com/0636920026938/chapter_09/05_transition.html
I am getting errors after switching the code to v4. I fixed the errors I knew about but now I am getting these one errors in the JavaScript console:
Error: attribute x: Expected length, "NaN".
You are not setting the domain of the x scale:
var xScale = d3.scaleBand()
.domain(d3.range(dataset.lengh))
.rangeRound([0, w])
.padding(0.1);
Also, pay attention to xScale.band(), which doesn't exist: it should be xScale.bandwidth() instead.
Here is a working code, with the domain:
var w = 600;
var h = 250;
var dataset = [5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25
];
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, w])
.padding(0.1);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, h]);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create bars
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.bandwidth())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
//Create labels
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.bandwidth() / 2;
})
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
//On click, update with new data
d3.select("p")
.on("click", function() {
//New values for dataset
dataset = [11, 12, 15, 20, 18, 17, 16, 18, 23, 25,
5, 10, 13, 19, 21, 25, 22, 18, 15, 13
];
//Update all rects
svg.selectAll("rect")
.data(dataset)
.transition() // <-- This makes it a smooth transition!
.attr("y", function(d) {
return h - yScale(d);
})
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
//Update all labels
svg.selectAll("text")
.data(dataset)
.text(function(d) {
return d;
})
.attr("y", function(d) {
return h - yScale(d) + 14;
});
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<p>Click on this text to update the chart with new data values (once).</p>

How do i add a fixed text beside bar chart using D3

I am trying to add a text beside a Bar chart . it will be look like
two bar chart both of them will have a text in their right side. I tried in many ways but couldn't find or may i didn't understand because i am beginners in D3 . This is my first question in stackoverflow .
I wrote this
var w = 500;
var h = 100;
var barPadding = 5;
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
//Create SVG element Men 2010
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return h - (d * 4);}
)
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return d*4; //Just the data value
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
///for the fixed text
var svgContainertext = d3.select("body").append("svg")
.attr("width", 100)
.attr("height", 20);
svgContainertext.append("g")
.selectAll("text")
.append("text")
.text("2010")
.attr("x",20)
.attr("y",10)
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black");
But the code is not showing the result. I am new to D3 .
Thanks in advance .
Your text is not showing because you ad the text after .selectAll("text"), which results in an empty selection, hence nowhere to add your text.
Furthermore, the text you are adding is in a new svg, and is not linked anyhow to your chart. I created a plunk to show how to put the text in the same svg: http://plnkr.co/edit/EHVB65sn7Oc67s7woKrj?p=preview
Updated code:
var w = 500;
var h = 100;
var barPadding = 5;
var textWidth = 30;
var dataset = [5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25
];
//Create SVG element Men 2010
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * ((w - textWidth) / dataset.length);
})
.attr("y", function(d) {
return h - (d * 4);
})
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return d * 4; //Just the data value
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
});
///for the fixed text
svg.append("g")
.append("text")
.text("2010")
.attr("x", w - textWidth)
.attr("y", h / 2)
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black");
What I did is use the same svg to append the text (in a group). I also reduced the space available for the bar chart using a textWidth variable, allowing to view the text and position it properly.
Removing .selectAll("text") will show the text 2010. I made a JSFiddle to show this.
If you want to read about selections, I suggest you have a read of this article
basically it is not working because the .selectAll('text') returns an empty selection and after that you append a text node to nothing.
It works for the rects drawn earlier because the .data() method is used on that selection.
var svgContainertext = d3.select("body").append("svg")
.attr("width", 100)
.attr("height", 20);
svgContainertext.append("g")
.append("text")
.text("2010")
.attr("x",20)
.attr("y",10)
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black");

Categories