Why is my xAxis not sorting in d3? - sorting

Yesterday I posted a question about a bar graph that I didn't know how to sort in alphabetical order. #Mark_M very kindly answered and provided me whith a good solution that normally should perfectly work.
Here is the snippet:
var xAxis = d3.axisBottom(xScale).tickSize(0)
var sortBars = function() {
// Change domain based on sorted data
xScale.domain(data.sort(function(a, b) {
return d3.ascending(a.Region, b.Region);
})
.map(function(d) {return d.Region})
)
// Redraw Rects with new position
svg.selectAll("rect")
.transition()
.duration(1000)
.attr("x", function(d, i) {return xScale(d.Region)})
// Redraw x Axis
axeX.transition()
.duration(1000)
.call(xAxis)
The "rect" are indeed sorting, however the x Axis (here called axeX) doesn't change at all. It seems that the calling of xAxis (last line) doesn't work.
Any idea of what's going on here ?
Here is the full code, the sorting part is at the very end:
<!DOCTYPE html>
<html>
<head>
<meta charset = "utf-8">
<title>D3 Test</title>
</head>
<body>
<style>
div.tooltip {position: absolute;
text-align: center;
width: none;
height: none;
padding: none;
font: 12px futura;
color: thistle;
background: none;
border: 0px;
border-radius: 8px;
pointer-events: none;}
svg {background-color: none;}
.tooltip {font: 10px futura;
color: thistle;}
.axisX line{stroke: white;}
.axisX path{stroke: white;}
.axisX text{fill: black;}
.axisY line{stroke: black;}
.axisY path{stroke: white;}
.horizontalGrid line {stroke: lightgrey;
stroke-opacity: 0;
shape-rendering: crispEdges;}
.horizontalGrid path {stroke-width: 0;}
</style>
<script type ="text/javascript" src = d3/d3.js></script>
<script>
//Variables; dimensions du canevas
var margin = {top: 40, right: 20, bottom: 130, left: 120},
svgWidth = 960 - margin.left - margin.right,
svgHeight = 400 - margin.top - margin.bottom,
barPadding = 2;
//Variables: domaines de l'Axe X et Y
var xScale = d3.scaleBand().range([0, svgWidth]).padding(0.1), //scaleBand pour des proportions
yScale = d3.scaleLinear().range([svgHeight, 0]); //scaleLinear pour des unités (noms, lettres)
//Variable: échelle de couleur divisée en 80 nuances entre deux couleurs
var colorScale = d3.scaleLinear().domain([0,80]).range(["white", "mediumturquoise"]);
//Variable: création du canevas dans <body>
var svg = d3.select("body")
.append("svg")
.attr("width", svgWidth + margin.left + margin.right)
.attr("height", svgHeight + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Variable: petit encadré qui apparait au passage de la souris
var div = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("visibility", "hidden");
var absoluteView = false
//Variable: format des nombre lors de la transition de chargement des nombres -> https://github.com/d3/d3-format
var format = d3.format("d"); //d = notation décimale arrondie aux entiers
var xAxis = d3.axisBottom(xScale).tickSize(0)
//Biding:
d3.csv("/Users/daumardlouis/Desktop/geid3.csv", function(error, data) {if (error) throw error;
data.forEach(function(d) {d.Classification = +d.Classification;});
xScale.domain(data.map(function(d) {return d.Region}));
yScale.domain([0, d3.max(data,function(d) {return d.Classification})]);
var xAxis = d3.axisBottom(xScale).tickSize(0) //marqueur d'échelle de taille 0
//Définition des barres
rect = svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {return xScale(d.Region)})
.attr("width", svgWidth / data.length - barPadding)
.attr("y", svgHeight) // écart de l'axe Y = hauteur du canevas SVG
.attr("height", 0) // hauteur de départ des barres à 0 pour l'effet de transition
.attr("fill", function (d) {return colorScale (d.Classification)})
.style("opacity", 0.9)
.on("mouseover",function(d) {d3.select(this).style("fill", "thistle").style("opacity", 0.5); // rempli de couleur la barre au passage de la souris
div.transition() // affiche le div en 0.2 secondes avec opacité de .5
.duration(200)
.style("visibility", "hidden");
div.html(d.Classification) // affiche une donnée ou un texte dans le div
.style("left", (d3.event.pageX - 50) + "px")
.style("top", (d3.event.pageY - 70) + "px"); // la hauteur du div par rapport à la souris (attention en html la hauteur est inversée)
if(!absoluteView) {
var xPos = parseFloat(d3.select(this).attr("x")) + 6;
var yPos = parseFloat(d3.select(this).attr("y")) + 2;
var height = parseFloat(d3.select(this).attr("height"));
svg.append("text")
.attr("x", xPos)
.attr("y", yPos - 10)
.attr("class", "tooltip")
.text(d.Classification)
.attr("fill", "thistle")};
})
.on("mouseout",function(d) {d3.select(this).transition().duration(150).style("fill", function (d) {return colorScale (d.Classification)}).style("opacity", 1); // renvoie la couleur initiale à la sortie de la souris
div.transition() // faire disparaitre la div après .5 secondes
.duration(200)
.style("visibility", "hidden")
svg.select(".tooltip").remove();
});
//Transition des barres (rect)
rect.transition()
.delay(1000)
.ease(d3.easePoly)
.duration(500)
.attr("y", function(d) {return yScale(d.Classification)})
.attr("height", function(d) {return svgHeight - yScale(d.Classification)});
//Transition: effet de chargement progressif des nombres
var texte = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(0) //texte à 0 au départ de la transition
.attr("x", function(d, i) {return 8 + i * (svgWidth / data.length)})
.attr("y", svgHeight) // commence à 0, cad à la hauteur du canevas
.attr("fill", "thistle")
.attr("font-family", "futura")
.attr("font-size", "10px")
.transition()
.delay(function(d,i){return i *30}) //ajouter un délai décalé pour chaque élément de data
.ease(d3.easePoly)
.duration(900)
.attr("x", function(d, i) {return 8 + i * (svgWidth / data.length)})
.attr("y", function(d) {return yScale(d.Classification) + (yScale(svgHeight) / 16) })
.tween("text", function(d) {var that = d3.select(this),
i = d3.interpolateNumber(that.text(), d.Classification); //donner une val de départ et de fin
return function(t) {that.text(format(i(t)))}}) //retourne le texte au format défini plus haut
.transition()
.delay(function(d,i){return 500 + i * (-30)}) //delay de 500 + annulation du délai décalé
.style("opacity", 0);
//Axe X, son style et ses transitions
var axeX = svg.append("g")
.attr("transform", "translate(0," + svgHeight + ")")
.call(xAxis)
.attr("class", "axisX") // récupère les indications de style de .axisX à l'interieur de la balise <style>. Utile pour changer la couleur de l'axe
.selectAll("text")
.attr("display", "true")
.attr("font-size", 2)
.attr("dx", "-4.8em")
.attr("dy", "4.15em")
.style("text-anchor", "end") //centre la fin du texte au milieu de chaque barre (rect)
.attr("transform", "rotate(-65)"); //effectue une rotation de -65°
axeX.transition()
.duration(500)
.delay(function(d,i){return i *30})
.attr("font-family", "futura", "Bold")
.attr("font-size", 10); //style de police de caractère
//Axe Y, son style et ses transitions
var axeY = svg.append("g")
.attr("class", "axisY")
.attr("transform", "translate(-5)")
.transition()
.duration(5000)
.call(d3.axisLeft(yScale).tickSize(3))
.selectAll("text")
.attr("transform", "translate(-5)")
.attr("font-family", "futura")
.attr("fill", "thistle"); //couleur de police de caractère
//Rajoute une légende à laxe Y
svg.append("g")
.append("text")
.attr("font-family", "futura")
.attr("font-size", 10)
.attr("transform", "rotate(-90)")
.attr("y", -48)
.attr("x", -68)
.style("text-anchor", "end")
.text("");
//Rajoute des lignes de fonds prolongeant l'axe Y
svg.selectAll("axisY")
.data(yScale)
.enter()
.append("line")
.attr("class", "horizontalGrid")
.attr("x1", 0)
.attr("x2", svgWidth)
.attr("y1", function(d){ return yScale(d);})
.attr("y2", function(d){ return yScale(d);});
//Title
title = svg.append("text")
.attr("x", (svgWidth / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.attr("font-family", "futura")
.style("fill", "#5a5a5a")
.text("Nombre d'observations par régions");
title.on("click", function () {sortBars()});
// Sorting
var sortBars = function() {xScale.domain(data.sort(function(a, b)
{return d3.ascending(a.Region, b.Region)}).map(function(d) {return d.Region}))
// Redraw Rects with new position
svg.selectAll("rect")
.transition()
.duration(1000)
.attr("x", function(d, i) {return xScale(d.Region)})
// Redraw x Axis
axeX.transition()
.duration(1000)
.call(xAxis)
}
});
</script>
<body>
</html>

You need to make sure you are saving the correct thing to axeX. Because you've chained selectAll("text") to it, the axeX variable is referring to the text not the axis.
Try something like this instead:
//Axe X, son style et ses transitions
var axeX = svg.append("g")
.attr("transform", "translate(0," + svgHeight + ")")
.call(xAxis)
.attr("class", "axisX") // récupère les indications de style de .axisX à l'interieur de la balise <style>. Utile pour changer la couleur de l'axe
axeX.selectAll("text")
.attr("display", "true")
.attr("font-size", 2)
.attr("dx", "-10px")
.attr("dy", "10px")
.style("text-anchor", "end") //centre la fin du texte au milieu de chaque barre (rect)
.attr("transform", "rotate(-65)") //effectue une rotation de -65°
.transition()
.duration(500)
.delay(function(d,i){return i *30})
.attr("font-family", "futura", "Bold")
.attr("font-size", 10); //style de police de caractère
I changed the dx and dy attribute to pixels because the ems are making the text jump around on refresh. I'm not quite sure how to get the effect you want using ems. Their value changes with font size.

Related

Making a stacked barchart with 3 columns of data with D3.js

I am new to D3.js. I am trying to use the content of this .csv file to make a stacked bar chart. The x axis represents the movie's names and the y axis represents the box office. each stacked bar will represent the budget and the profit(box office -budget) of each movie.
This is the csv:
MovieTitle,Budget,Box Office
Harry Potter and the Philosopher's Stone,"$125,000,000 ","$1,002,000,000 "
Harry Potter and the Chamber of Secrets,"$100,000,000 ","$880,300,000 "
Harry Potter and the Prisoner of Azkaban,"$130,000,000 ","$796,700,000 "
Harry Potter and the Goblet of Fire,"$150,000,000 ","$896,400,000 "
Harry Potter and the Order of the Phoenix,"$150,000,000 ","$942,000,000 "
Harry Potter and the Half-Blood Prince,"$250,000,000 ","$943,200,000 "
Harry Potter and the Deathly Hallows Part 1,"$200,000,000 ","$976,900,000 "
Harry Potter and the Deathly Hallows Part 2,"$250,000,000 ","$1,342,000,000 "
This is what i have done so far:
<!DOCTYPE html>
<style>
.axis .domain {
display: none;
}
</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: 50, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.05)
.align(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range([ "#d0743c", "#ff8c00"]);
d3.csv("title&price.csv", function(d) {
return {
MovieTitle : d.MovieTitle,
Budget : Number(d['Budget'].trim().slice(1).replaceAll(',', '')),
Box_Office : Number(d['Box Office'].trim().slice(1).replaceAll(',', '')),
Profit : Number(d['Box Office'].trim().slice(1).replaceAll(',', '')) - Number(d['Budget'].trim().slice(1).replaceAll(',', ''))
};
console.log(data);
}, function(error, data) {
if (error) throw error;
var keys = data.columns.slice(1);
console.log(data);
data.sort(function(a, b) { return b.Box_Office - a.Box_Office; });
x.domain(data.map(function(d) { return d.MovieTitle; }));
y.domain([0, d3.max(data, function(d) { return d.Box_Office; })]).nice();
z.domain(keys);
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.data.MovieTitle); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth());
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "em")
.attr("dy", ".90em")
.attr("transform", "rotate(10)");
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Money");
var legend = g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(keys.slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 19)
.attr("width", 19)
.attr("height", 19)
.attr("fill", z);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) { return d; });
});
</script>

How do you change color of lines drawn in d3 with svg?

I'm using this tutorial, Making a cool Bitcoin price chart using D3.js and the CryptoCompare API and I am having trouble styling the lines that are drawn.
For example, I would like to be able to select and style the lines x and y to change their color to white. See this snippet from the tutorial:
var x = d3.scaleTime()
.range([0, width])
I've tried adding .attr("fill", "#fff"), but this only breaks it. How do I change the colors of d3.scaleTime() and d3.scaleLinear()?
I have also tried the following as described in the d3-scale documentation:
var x = d3.scaleTime()
.range([0, width])
x(20); // "#9a3439"
Here's the entire script:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>
</head>
<style>
body {
text-align: center;
margin-top: 5em;
background-color: #74b9ff;
}
h1 {
color: snow;
}
</style>
<body>
<h1>Bitcoin Prices in U.S. Dollars</h1>
<script>
var url = "https://min-api.cryptocompare.com/data/histoday?fsym=BTC&tsym=USD&limit=200&aggregate=3&e=CCCAGG";
d3.json(url).get(function(error, d) {
var data = d.Data;
data.forEach(function(d){ d.time = new Date(d.time * 1000) });
if (error) throw error;
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,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleTime()
.range([0, width])
var y = d3.scaleLinear()
.range([height, 0]);
var line = d3.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.close); });
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.attr("stroke-width", 2)
.attr("fill", "none")
.style("font-size",".8em");
g.append("g")
.call(d3.axisLeft(y))
.attr("stroke-width", 2)
.style("font-size",".8em")
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 20)
.attr("text-anchor", "end")
.attr("font-size", "1.2em")
.text("Price ($)")
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "#ffeaa7")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2)
.attr("d", line);
});
</script>
</body>
</html>
You can style the axes as shown below.
var xAxis = g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
xAxis.select("path") //Axis
.style("stroke","white");
xAxis.selectAll("line") //ticks
.style("stroke","white");
var yAxis = g.append("g")
.call(d3.axisLeft(y));
yAxis.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 20)
.attr("text-anchor", "end")
.attr("font-size", "1.2em")
.text("Price ($)");
yAxis.select("path") //Axis
.style("stroke","white");
yAxis.selectAll("line") //ticks
.style("stroke","white");
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>
</head>
<style>
body {
text-align: center;
margin-top: 5em;
background-color: #74b9ff;
}
h1 {
color: snow;
}
</style>
<body>
<h1>Bitcoin Prices in U.S. Dollars</h1>
<script>
var url = "https://min-api.cryptocompare.com/data/histoday?fsym=BTC&tsym=USD&limit=200&aggregate=3&e=CCCAGG";
d3.json(url).get(function(error, d) {
var data = d.Data;
data.forEach(function(d) {
d.time = new Date(d.time * 1000)
});
if (error) throw error;
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,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleTime()
.range([0, width])
var y = d3.scaleLinear()
.range([height, 0]);
var line = d3.line()
.x(function(d) {
return x(d.time);
})
.y(function(d) {
return y(d.close);
});
x.domain(d3.extent(data, function(d) {
return d.time;
}));
y.domain(d3.extent(data, function(d) {
return d.close;
}));
var xAxis = g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.attr("stroke-width", 2)
.attr("fill", "none")
.style("font-size", ".8em")
xAxis.select("path") //Axis
.style("stroke", "white");
xAxis.selectAll("line") //ticks
.style("stroke", "white");
var yAxis = g.append("g")
.call(d3.axisLeft(y))
.attr("stroke-width", 2)
.style("font-size", ".8em");
yAxis.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 20)
.attr("text-anchor", "end")
.attr("font-size", "1.2em")
.text("Price ($)");
yAxis.select("path") //Axis
.style("stroke", "white");
yAxis.selectAll("line") //ticks
.style("stroke", "white");
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "#ffeaa7")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2)
.attr("d", line);
});
</script>
</body>
</html>
While Gilsha's answer seems like best practice and allows for more direct control over styling, the answer got from that was to use selectAll() to select and style each "path" (axes), "line" (ticks), and "text" (display of price and date).
Including the following in the end of the function d3.json(url).get(function(error, d) styles everything as white:
g.selectAll("path") //Axes
.style("stroke","white");
g.selectAll("line") //Ticks
.style("stroke","white");
g.selectAll("text") //Text displaying date and price
.attr("fill", "white");
Note that this broad-stroke approach includes the path for the plotted data line as well. If you need to distinguish between them, you can define a variable for that and style it independently as Gilsha did for the axes.

Any idea what's wrong with this d3 sort function?

I'm desperately trying to add a simple sort function (alphabetical order) to my bar graph, but strangely all the bars seem to disappear to the left when i click (triggers the sort function).
Can anyone tell me what's wrong with my code ?
Here's the snippet:
//I would like to sort the bars just by clicking on the title
title.on("click", function () {sortBars()});
var sortBars = function() {svg.selectAll("rect")
.sort(function(a, b) {
return d3.ascending(a.Region, b.Region); })
.transition()
.duration(1000)
.attr("x", function(d, i) {return xScale(i);});};
//Title
title = svg.append("text")
.attr("x", (svgWidth / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.attr("font-family", "futura")
.style("fill", "#5a5a5a")
.text("Nombre d'observations par régions");
Here's a screenshot of the graph after the sort function is triggered: all bars to the left
The csv file i'm calling has 2 columns, one called Classification (containing numbers) and the other one Region (containing names of different regions)
Any help would be much appreciated ! Thank you
Edit: here is the full code
<!DOCTYPE html>
<html>
<head>
<meta charset = "utf-8">
<title>D3 Test</title>
</head>
<body>
<style>
div.tooltip {position: absolute;
text-align: center;
width: none;
height: none;
padding: none;
font: 12px futura;
color: thistle;
background: none;
border: 0px;
border-radius: 8px;
pointer-events: none;}
svg {background-color: none;}
.tooltip {font: 10px futura;
color: thistle;}
.axisX line{stroke: white;}
.axisX path{stroke: white;}
.axisX text{fill: black;}
.axisY line{stroke: black;}
.axisY path{stroke: white;}
.horizontalGrid line {stroke: lightgrey;
stroke-opacity: 0;
shape-rendering: crispEdges;}
.horizontalGrid path {stroke-width: 0;}
</style>
<script type ="text/javascript" src = d3/d3.js></script>
<script>
//Variables; dimensions du canevas
var margin = {top: 40, right: 20, bottom: 130, left: 120},
svgWidth = 960 - margin.left - margin.right,
svgHeight = 400 - margin.top - margin.bottom,
barPadding = 2;
//Variables: domaines de l'Axe X et Y
var xScale = d3.scaleBand().range([0, svgWidth]).padding(0.1), //scaleBand pour des proportions
yScale = d3.scaleLinear().range([svgHeight, 0]); //scaleLinear pour des unités (noms, lettres)
//Variable: échelle de couleur divisée en 80 nuances entre deux couleurs
var colorScale = d3.scaleLinear().domain([0,80]).range(["white", "mediumturquoise"]);
//Variable: création du canevas dans <body>
var svg = d3.select("body")
.append("svg")
.attr("width", svgWidth + margin.left + margin.right)
.attr("height", svgHeight + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Variable: petit encadré qui apparait au passage de la souris
var div = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("visibility", "hidden");
var absoluteView = false
//Variable: format des nombre lors de la transition de chargement des nombres -> https://github.com/d3/d3-format
var format = d3.format("d"); //d = notation décimale arrondie aux entiers
//Biding:
d3.csv("/Users/daumardlouis/Desktop/geid3.csv", function(error, data) {if (error) throw error;
data.forEach(function(d) {d.Classification = +d.Classification;});
xScale.domain(data.map(function(d) {return d.Region}));
yScale.domain([0, d3.max(data,function(d) {return d.Classification})]);
//Définition des barres
rect = svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {return xScale(d.Region)})
.attr("width", svgWidth / data.length - barPadding)
.attr("y", svgHeight) // écart de l'axe Y = hauteur du canevas SVG
.attr("height", 0) // hauteur de départ des barres à 0 pour l'effet de transition
.attr("fill", function (d) {return colorScale (d.Classification)})
.style("opacity", 0.9)
.on("mouseover",function(d) {d3.select(this).style("fill", "thistle").style("opacity", 0.5); // rempli de couleur la barre au passage de la souris
div.transition() // affiche le div en 0.2 secondes avec opacité de .5
.duration(200)
.style("visibility", "hidden");
div.html(d.Classification) // affiche une donnée ou un texte dans le div
.style("left", (d3.event.pageX - 50) + "px")
.style("top", (d3.event.pageY - 70) + "px"); // la hauteur du div par rapport à la souris (attention en html la hauteur est inversée)
if(!absoluteView) {
var xPos = parseFloat(d3.select(this).attr("x")) + 6;
var yPos = parseFloat(d3.select(this).attr("y")) + 2;
var height = parseFloat(d3.select(this).attr("height"));
svg.append("text")
.attr("x", xPos)
.attr("y", yPos - 10)
.attr("class", "tooltip")
.text(d.Classification)
.attr("fill", "thistle")};
})
.on("mouseout",function(d) {d3.select(this).transition().duration(150).style("fill", function (d) {return colorScale (d.Classification)}).style("opacity", 1); // renvoie la couleur initiale à la sortie de la souris
div.transition() // faire disparaitre la div après .5 secondes
.duration(200)
.style("visibility", "hidden")
svg.select(".tooltip").remove();
});
//Transition des barres (rect)
rect.transition()
.delay(1000)
.ease(d3.easePoly)
.duration(500)
.attr("y", function(d) {return yScale(d.Classification)})
.attr("height", function(d) {return svgHeight - yScale(d.Classification)});
//Transition: effet de chargement progressif des nombres
var texte = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(0) //texte à 0 au départ de la transition
.attr("x", function(d, i) {return 8 + i * (svgWidth / data.length)})
.attr("y", svgHeight) // commence à 0, cad à la hauteur du canevas
.attr("fill", "thistle")
.attr("font-family", "futura")
.attr("font-size", "10px")
.transition()
.delay(function(d,i){return i *30}) //ajouter un délai décalé pour chaque élément de data
.ease(d3.easePoly)
.duration(900)
.attr("x", function(d, i) {return 8 + i * (svgWidth / data.length)})
.attr("y", function(d) {return yScale(d.Classification) + (yScale(svgHeight) / 16) })
.tween("text", function(d) {var that = d3.select(this),
i = d3.interpolateNumber(that.text(), d.Classification); //donner une val de départ et de fin
return function(t) {that.text(format(i(t)))}}) //retourne le texte au format défini plus haut
.transition()
.delay(function(d,i){return 500 + i * (-30)}) //delay de 500 + annulation du délai décalé
.style("opacity", 0);
//Axe X, son style et ses transitions
var axeX = svg.append("g")
.attr("class", "axisX") // récupère les indications de style de .axisX à l'interieur de la balise <style>. Utile pour changer la couleur de l'axe
.attr("transform", "translate(0," + svgHeight + ")")
.call(d3.axisBottom(xScale).tickSize(0)) //marqueur d'échelle de taille 0
.selectAll("text")
.attr("display", "true")
.attr("font-size", 2)
.attr("dx", "-4.8em")
.attr("dy", "4.15em")
.style("text-anchor", "end") //centre la fin du texte au milieu de chaque barre (rect)
.attr("transform", "rotate(-65)") //effectue une rotation de -65°
.transition()
.duration(500)
.delay(function(d,i){return i *30})
.attr("font-family", "futura", "Bold")
.attr("font-size", 10); //style de police de caractère
//Axe Y, son style et ses transitions
var axeY = svg.append("g")
.attr("class", "axisY")
.attr("transform", "translate(-5)")
.transition()
.duration(5000)
.call(d3.axisLeft(yScale).tickSize(3))
.selectAll("text")
.attr("transform", "translate(-5)")
.attr("font-family", "futura")
.attr("fill", "thistle"); //couleur de police de caractère
//Rajoute une légende à laxe Y
svg.append("g")
.append("text")
.attr("font-family", "futura")
.attr("font-size", 10)
.attr("transform", "rotate(-90)")
.attr("y", -48)
.attr("x", -68)
.style("text-anchor", "end")
.text("");
//Rajoute des lignes de fonds prolongeant l'axe Y
svg.selectAll("axisY")
.data(yScale)
.enter()
.append("line")
.attr("class", "horizontalGrid")
.attr("x1", 0)
.attr("x2", svgWidth)
.attr("y1", function(d){ return yScale(d);})
.attr("y2", function(d){ return yScale(d);});
//I would like to sort the bars just by clicking on the title
title.on("click", function () {sortBars()});
var sortBars = function() {
svg.selectAll("rect")
.sort(function(a, b) {
return d3.ascending(a.Region, b.Region); })
.transition()
.duration(1000)
.attr("x", function(d, i) {return xScale(i);});};
//Title
title = svg.append("text")
.attr("x", (svgWidth / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.attr("font-family", "futura")
.style("fill", "#5a5a5a")
.text("Nombre d'observations par régions");
});
</script>
<body>
</html>
Edit 2: Very strangely, the x Axis (here called axeX) is not redrawn when i .call(xAxis). If i apply another transformation, like changing the color of the x Axis, it works but only the sorting function seem not to operate.
The only solution I found is to select(".axisX")(in the CSS) instead of axeX and then calling (xAxis). However the Regions' names are all messed-up (see screenshot)
Here is what the code looks like
// Redraw x Axis
d3.select(".axisX")
.transition()
.duration(1000)
.call(xAxis)
instead of:
// Redraw x Axis
axeX.transition()
.duration(1000)
.call(xAxis)
Any idea why the calling of the xAxis doesn't work with axeX ? And why the positions of the names on the x Axis are all messed up with my code ?
So the basic idea is that you want to sort the domain of the x scale and redraw with that scale. You can do something like this:
var sortBars = function() {
// Change domain based on sorted data
xScale.domain(data.sort(function(a, b) {
return d3.ascending(a.Region, b.Region);
})
.map(function(d) {return d.Region})
)
// Redraw Rects with new position
svg.selectAll("rect")
.transition()
.duration(1000)
.attr("x", function(d, i) {return xScale(d.Region)})
// Redraw x Axis
axeX.transition()
.duration(1000)
.call(xAxis)
It looks like you are doing some styling on the X axis, so you may need to reapply styles.
EDIT:
Also, I would save a reference to the xAxis so you can call it later:
var xAxis = d3.axisBottom(xScale).tickSize(0)
and replace the .call() with this:
var axeX = svg.append("g")
.attr("class", "axisX") // récupère les indications de style de .axisX à l'interieur de la balise <style>. Utile pour changer la couleur de l'axe
.attr("transform", "translate(0," + svgHeight + ")")
.call(xAxis)
instead of
var axeX = svg.append("g")
.attr("class", "axisX") // récupère les indications de style de .axisX à l'interieur de la balise <style>. Utile pour changer la couleur de l'axe
.attr("transform", "translate(0," + svgHeight + ")")
.call(d3.axisBottom(xScale).tickSize(0))
// ... etc.

How can I add a background colour behind PNG, when image URL is pulled from my D3.js dataset?

I am making a scatterplot, and pulling in an image fill for each circle on the plot. The problem is that the images are PNG's with transparent backgrounds. This means my overlapping circles show through each other:
Seen here - http://i.stack.imgur.com/bphon.png
I have tried setting a background colour with the CSS, but it seems to be completely overwritten by the .style("fill") in the JS. And I am looking to pull in 30ish images, so I don't want to have to save them all to be able to load the images with my CSS.
So, my question is, is there a way to put a white background behind my PNGs, while pulling those PNGs from URL's contained in my dataset?
Thanks for the help
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<style type="text/css">
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis-text {
font-family: sans-serif;
font-size: 11px;
background-color: white;
font-weight: bold;
}
.teamcircle {
background-color: white;
}
</style>
</head>
<body>
<div>
<input type="button" id="playerbtn" value="See Player View">
<input type="button" id="teambtn" value="See Team View">
</div>
<div id="data">
<div id="player-circles">
</div>
</div>
<script type="text/javascript">
//Width and height
var margin = {top: 50, right: 20, bottom: 30, left: 40};
var w = 960 - margin.left - margin.right;
var h = 500 - margin.top - margin.bottom;
//Create scale functions
var xScale = d3.scale.linear()
.range([0, w]);
var yScale = d3.scale.linear()
.range([h, 0]);
// var color = d3.scale.color();
// Define the Axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Load the TEAM data set
var teamdata = d3.tsv("team.tsv", function(error, teamdata) {
if (error) throw error;
teamdata.forEach(function(d) {
d.entriesper60 = +d.entriesper60;
d.carryinpercent = +d.carryinpercent;
});
xScale.domain(d3.extent(teamdata, function(d) { return d.carryinpercent; })).nice();
yScale.domain(d3.extent(teamdata, function(d) { return d.entriesper60; })).nice();
//Create X axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
.append("text")
.attr("class", "axis-text")
.attr("x", w)
.attr("y", -6)
.style("text-anchor", "end")
.text("Carry-in %");
//Create Y axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "axis-text")
.attr("y", -20)
.attr("z", 0)
.style("text-anchor", "middle")
.text("Entries/60")
// DEFS & Pattern for images
svg.append("defs")
.selectAll("pattern")
.data(teamdata)
.enter()
.append("pattern")
.attr('id', function(d, i) {
return d.name;
})
// .attr('patternUnits', 'userSpaceOnUse')
.attr('width', 20)
.attr('height', 20)
.append("image")
.attr("xlink:href", function(d) {
return d.image + d.name;
})
.attr('width', 20)
.attr('height', 20)
.attr("transform", "translate(2, 2)");
// Create TEAM Circles
svg.selectAll("circle")
.data(teamdata)
.enter()
.append("circle")
.attr("class", "teamcircle")
.style("stroke", function(d) { return d.hex; })
.style("stroke-width", 2)
.style("stroke-opacity", .8)
.attr("r", 12)
.attr("cx", function(d) { return xScale(d.carryinpercent); })
.attr("cy", function(d) { return yScale(d.entriesper60); })
.attr("fill", function(d) { return "url(#" + d.name + ")";
});
});
</script>
</body>
</html>
The best way I can think of is to create a group for every circle, and create a circle with a white background first. Something like this:
var teamCircle = svg.selectAll("g.teamcircle")
.data(teamdata)
.enter()
.append("g")
.attr("class", "teamcircle")
.transform(function(d){return "translate(" + xScale(d.carryinpercent) + "," + yScale(d.entriesper60) + ")"});
teamCircle.append("circle")
.attr("fill", "white")
.attr("r", 12)
teamCircle.append("circle")
.style("stroke", function(d) { return d.hex; })
.style("stroke-width", 2)
.style("stroke-opacity", .8)
.attr("r", 12)
.attr("fill", function(d) { return "url(#" + d.name + ")";

D3 change line stroke-width by hovering over legend

I made a two-line chart with a legend. I want to highlight the stroke-width of each of the lines to 4px based on the color of the legend circle when the user hovers over it. So, user hovers over the blue legend circle, the blue line stroke-width changes to 4px. Same for the red one if he hovers over the red circle on the legend. Is this possible to do based on my code? Here is the whole code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Test</title>
<script type="text/javascript" src="../d3/d3.js"></script>
<script type="text/javascript" src="../d3/d3-tip.js"></script>
<style type="text/css">
body{
font: 16px Calibri;
}
.line1{
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
.line1:hover{
stroke-width: 3.5px;
}
.line2{
fill: none;
stroke: red;
stroke-width: 2px;
}
.line2:hover{
stroke-width: 3.5px;
}
.axis path,
.axis line{
fill:none;
stroke: black;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.axis text{
font-family: sans-serif;
font-size: 14px;
stroke: black;
stroke-width: 0.5px;
}
.legend .series {
cursor: pointer;
}
.legend circle {
stroke-width: 2px;
}
.legend .disabled circle {
fill-opacity: 0;
}
</style>
<!--...this code will be used on an external html file and instered-->
<html>
<body>
<div id="dropdown">
<select id = "opts">
<option value = "ds1">Atlas</option>
<option value = "ds2">BioSQL</option>
<option value = "ds3">Coppermine</option>
<option value = "ds4">Ensembl</option>
<option value = "ds5">Mediawiki</option>
<option value = "ds6">Opencart</option>
<option value = "ds7">PhpBB</option>
<option value = "ds8">Typo3</option>
</select>
</div>
<script type="text/javascript">
console.log("worked");
var ds1="../CSV/atlas/results/metrics.csv";
var ds2="../CSV/biosql/results/metrics.csv";
var ds3="../CSV/coppermine/results/metrics.csv";
var ds4="../CSV/ensembl/results/metrics.csv";
var ds5="../CSV/mediawiki/results/metrics.csv";
var ds6="../CSV/opencart/results/metrics.csv";
var ds7="../CSV/phpbb/results/metrics.csv";
var ds8="../CSV/typo3/results/metrics.csv";
</script>
</body>
</html> <!--...............................................................-->
<div id="area1"></div>
<div id="area2"></div>
</head>
<body>
<script type="text/javascript">
var margin = {top: 60, right: 20, bottom: 40, left: 40},
margin2 = {top: 430, right: 20, bottom: 0, left: 50},
width = 800 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
height2 = 870 - margin2.top - margin2.bottom;
var parseDate = d3.time.format("%Y").parse;
var color = d3.scale.category20()
.range(["#1f77b4", "#d62728", "#98df8a"]);
var x = d3.scale.linear()
.range([0, width]);
var x1 = d3.time.scale()
.nice(d3.time.year)
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5);
var x1Axis = d3.svg.axis()
.scale(x1)
.orient("bottom")
.ticks(5);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg1 = d3.select("#area1").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height2 + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var line1 = d3.svg.line()
.x(function(d) { return x(d.trID);})
.y(function (d) {return y(d.newT);})
.interpolate("basis");
var line2 = d3.svg.line()
.x(function(d) { return x1(d.time);})
.y(function (d) {return y(d.newT);})
.interpolate("basis");
var dsv = d3.dsv(";", "text/plain"); //setting the delimiter
var dataset = [] //defining the data array
var datapath="../CSV/atlas/results/metrics.csv";
dsv(datapath, function(data){ //------------select the file to load the csv------------
var label = document.getElementById('opts')[document.getElementById('opts').selectedIndex].innerHTML;//takes the name of the f
console.log(label);
dataset= data.map(function(d){ //parse
return { //insert parsed data in the array
trID: +d["trID"],
newT: +d["#newT"],
time: +d["time"]
};
});
dataset.forEach(function(d){
d.time = new Date(d.time*1000);
});
console.log(dataset);
x.domain(d3.extent(dataset, function(d) { return d.trID; }));
x1.domain(d3.extent(dataset, function(d) { return d.time; }));
// y.domain([0, d3.max(dataset.map( function(d) {return d.newT}))]);
y.domain(d3.extent(dataset, function(d) { return d.newT; }));
//------------------creating the lines---------------------
svg1.append("path")
.datum(dataset)
.attr("class", "line1")
.attr("d", line1);
svg1.append("path")
.datum(dataset)
.attr("class", "line2")
// .style("stroke-dasharray",("5,5"))
.attr("d", line2);
//----------------appending Legend--------------------------
var legend = svg1.selectAll(".legend")
.data((["Duration/Time","Duration/ID"]).slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("circle")
.attr("r", 7)
.attr("cx", 45)
.attr("cy", 10)
.style("fill", color);
legend.append("text")
.attr("x", 54)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "begin")
.style("font-family", "Calibri")
.text(function(d) { return d; });
//-----------------------------------------------------------
svg1.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("trID");
svg1.append("g")
.attr("class", "x1 axis")
.attr("transform", "translate(0," + height2 + ")")
.call(x1Axis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("time");
svg1.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("num of tables");
svg1.append("text")
.attr("class","simpletext")
.attr("x", (width/2))
.attr("y", 0 - (margin.top/2))
.attr("text-anchor", "middle")
.style("font-size", "20px")
.style("text-decoration", "underline")
.text(label);
});
d3.select('#opts')
.on('change', function(){
var dataset=[]
var datapath = eval(d3.select(this).property('value'));
label = document.getElementById('opts')[document.getElementById('opts').selectedIndex].innerHTML;
dsv(datapath, function(data){ //------------select the file to load the csv------------
dataset= data.map(function(d){ //parse
return { //insert parsed data in the array
trID: +d["trID"],
newT: +d["#newT"],
time: +d["time"]
};
});
dataset.forEach(function(d){
d.time = new Date(d.time*1000);
});
x.domain(d3.extent(dataset, function(d) { return d.trID; }));
x1.domain(d3.extent(dataset, function(d) { return d.time; }));
// y.domain([0, d3.max(dataset.map( function(d) {return d.newT}))]);
y.domain(d3.extent(dataset, function(d) { return d.newT; }));
d3.selectAll(".line1")
.transition()
.duration(1000)
.attr("d", line1(dataset));
d3.selectAll(".line2")
.transition()
.duration(1000)
.attr("d", line2(dataset));
//Update Axis
//Update X axis
svg1.select(".x.axis")
.transition()
.duration(1000)
.call(xAxis);
svg1.select(".x1.axis")
.transition()
.duration(1000)
.call(x1Axis);
//Update Y axis
svg1.select(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
svg1.selectAll("path")
.data(dataset)
.exit()
.remove();
console.log(label);
svg1.selectAll(".simpletext")
.transition()
.text(label);
/* .attr("x", (width/2))
.attr("y", 0 - (margin.top/2))
.attr("text-anchor", "middle")
.style("font-size", "20px")
.style("text-decoration", "underline")
.text(label);*/
});
});
</script>
</body>
</html>
I define my color as this:
var color = d3.scale.category20()
.range(["#1f77b4", "#d62728", "#98df8a"]);
And each of my lines as this:
var line1 = d3.svg.line()
.x(function(d) { return x(d.trID);})
.y(function (d) {return y(d.newT);})
.interpolate("basis");
var line2 = d3.svg.line()
.x(function(d) { return x1(d.time);})
.y(function (d) {return y(d.newT);})
.interpolate("basis");
Of course it's possible.
So first it has nothing to do with line1 and line2 - they are about data of the line, not actual visual element. You need to change style of the path elements .line1 and .line2.
You can add event listeners when creating the legend.
//----------------appending Legend--------------------------
var legend = svg1.selectAll(".legend")
.data((["Duration/Time","Duration/ID"]).slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; })
.on("mouseover", function(d) {
if (d === "Duration/ID") {
// line1's legend is hovered
svg1.selectAll(".line1").style("stroke-width", "4px");
}
else if (d === "Duration/Time") {
// line2's legend is hovered
svg1.selectAll(".line2").style("stroke-width", "4px");
}
})
.on("mouseout", function() {
// revert the styles
svg1.selectAll(".line1").style("stroke-width", "2px");
svg1.selectAll(".line2").style("stroke-width", "2px");
});
This is just a demonstration. Of course it would be more consistent to define the styles in css and change class on events, and you may not want to hardcode legend names when you have more/dynamic series.

Resources