Making an existing vertical bar chart horizontal - d3.js

I have an existing bar chart which works fine.
But I want to make it horizontal by flipping the axis.
Full code:
$(document).ready(function() {
render_chart();
});
function render_chart() {
var stack = d3.stack;
var dataset = {
"categories": ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
"series": ["Group1", "Group2", "Group3"],
"colors": ["#3498db", "#e74c3c", "#2ecc71"],
"layers": [
[{
"y": 1,
"y0": 20,
"month": "Monday"
},
{
"y": 2,
"y0": 18,
"month": "Tuesday"
},
{
"y": 5,
"y0": 18,
"month": "Wednesday"
},
{
"y": 10,
"y0": 20,
"month": "Thursday"
},
{
"y": 14,
"y0": 23,
"month": "Friday"
},
{
"y": 18,
"y0": 24,
"month": "Saturday"
},
{
"y": 20,
"y0": 24,
"month": "Sunday"
}
],
[{
"y": 12,
"y0": 24,
"month": "Monday"
},
{
"y": 14,
"y0": 24,
"month": "Tuesday"
},
{
"y": 13,
"y0": 23,
"month": "Wednesday"
},
{
"y": 16,
"y0": 21,
"month": "Thursday"
},
{
"y": 18,
"y0": 23,
"month": "Friday"
},
{
"y": 19,
"y0": 22,
"month": "Saturday"
},
{
"y": 20,
"y0": 22,
"month": "Sunday"
}
],
[{
"y": 8,
"y0": 24,
"month": "Monday"
},
{
"y": 14,
"y0": 21,
"month": "Tuesday"
},
{
"y": 12,
"y0": 19,
"month": "Wednesday"
},
{
"y": 15,
"y0": 23,
"month": "Thursday"
},
{
"y": 18,
"y0": 21,
"month": "Friday"
},
{
"y": 16,
"y0": 23,
"month": "Saturday"
},
{
"y": 17,
"y0": 24,
"month": "Sunday"
}
]
]
};
n = dataset["series"].length;
m = dataset["layers"].length;
yGroupMax = d3.max(dataset["layers"], function(layer) {
return d3.max(layer, function(d) {
return d.y0;
});
});
yGroupMin = d3.min(dataset["layers"], function(layer) {
return d3.min(layer, function(d) {
return d.y;
});
});
var margin = {
top: 50,
right: 50,
bottom: 50,
left: 100
},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var xScale = d3.scaleBand()
.domain(dataset["categories"])
.rangeRound([0, width])
.padding(.08);
var yScale = d3.scaleLinear()
.domain([yGroupMin, yGroupMax])
.range([height, 0]);
var xAxis = d3.axisBottom(xScale)
.tickSize(7)
.tickPadding(6);
var yAxis = d3.axisLeft(yScale).ticks(24);
var svg = d3.select("#groupchart").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 + ")");
var layer = svg.selectAll(".layer")
.data(dataset["layers"])
.enter().append("g")
.attr("class", "layer");
var rect = layer.selectAll("rect")
.data(function(d, i) {
d.map(function(b) {
b.colorIndex = i;
return b;
});
return d;
})
.enter()
.append("rect")
.transition()
.duration(500)
.delay(function(d, i) {
return i * 10;
})
.attr("x", function(d, i, j) {
var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes, j[i].parentNode);
return xScale(d.month) + xScale.bandwidth() / n * k;
})
.attr("width", xScale.bandwidth() / n)
.transition()
.attr("y", function(d) {
return yScale(d.y0);
})
.attr("height", function(d) {
return height - yScale(d.y0 - d.y)
})
.attr("class", "bar")
.style("fill", function(d) {
return dataset["colors"][d.colorIndex];
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.select("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("x", width / 3)
.attr("y", 0)
.attr("dx", ".71em")
.attr("dy", "-.71em")
.text("Grouped Bar Chart Test");
var legend = svg.append("g")
.attr("class", "legend")
legend.selectAll('text')
.data(dataset["colors"])
.enter()
.append("rect")
.attr("x", function(d, i) {
return (i * 120) + (width / 3);
})
.attr("y", (width - margin.right) / 1.6)
.attr("width", 10)
.attr("height", 10)
.style("fill", function(d) {
return d;
})
legend.selectAll('text')
.data(dataset["series"])
.enter()
.append("text")
.attr("x", function(d, i) {
return (i * 120) + (width / 3) + 12;
})
.attr("y", ((width - margin.right) / 1.6) + 9)
.text(function(d) {
return d;
});
var tooltip = d3.select("body")
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'month');
tooltip.append('div')
.attr('class', 'tempRange');
svg.selectAll("rect")
.on('mouseover', function(d) {
if (!d.month) return null;
tooltip.select('.month').html("<b>" + d.month + "</b>");
tooltip.select('.tempRange').html(d.y + ":00 Hours to " + d.y0 + ":00 Hours");
tooltip.style('display', 'block');
tooltip.style('opacity', 2);
})
.on('mousemove', function(d) {
if (!d.month) return null;
tooltip.style('top', (d3.event.layerY + 10) + 'px')
.style('left', (d3.event.layerX - 25) + 'px');
})
.on('mouseout', function() {
tooltip.style('display', 'none');
tooltip.style('opacity', 0);
});
}
.axis path,
.axis line {
fill: none;
font: 10px sans-serif;
stroke: #000;
shape-rendering: crispEdges;
}
.legend {
padding: 5px;
font-size: 15px;
font-family: 'Roboto', sans-serif;
background: yellow;
box-shadow: 2px 2px 1px #888;
}
.tooltip {
background: #eee;
box-shadow: 0 0 5px #999999;
color: #333;
font-size: 12px;
left: 130px;
padding: 10px;
position: absolute;
text-align: center;
top: 95px;
z-index: 10;
display: block;
opacity: 0;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Grouped Bar Graph</title>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<div id="groupchart" class="chart"></div>
</body>
</html>
I tried to do it but I mess it up everytime. When I try to flip the axis by changing the portion below which is just reversing the x and y, it does not appear correctly:
var xAxis = d3.axisBottom(xScale)
.tickSize(7)
.tickPadding(6);
var yAxis = d3.axisLeft(yScale).ticks(24);
I can't figure this out.
Why:
So that I can fit more stuff per group of bars when they are horizontal as they are going to be long and have more space inside.

Here is a reversed (horizontal) version of your chart:
$(document).ready(function() {
render_chart();
});
function render_chart() {
var stack = d3.stack;
var dataset = {
"categories": ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
"series": ["Group1", "Group2", "Group3"],
"colors": ["#3498db", "#e74c3c", "#2ecc71"],
"layers": [
[{
"y": 1,
"y0": 20,
"month": "Monday"
},
{
"y": 2,
"y0": 18,
"month": "Tuesday"
},
{
"y": 5,
"y0": 18,
"month": "Wednesday"
},
{
"y": 10,
"y0": 20,
"month": "Thursday"
},
{
"y": 14,
"y0": 23,
"month": "Friday"
},
{
"y": 18,
"y0": 24,
"month": "Saturday"
},
{
"y": 20,
"y0": 24,
"month": "Sunday"
}
],
[{
"y": 12,
"y0": 24,
"month": "Monday"
},
{
"y": 14,
"y0": 24,
"month": "Tuesday"
},
{
"y": 13,
"y0": 23,
"month": "Wednesday"
},
{
"y": 16,
"y0": 21,
"month": "Thursday"
},
{
"y": 18,
"y0": 23,
"month": "Friday"
},
{
"y": 19,
"y0": 22,
"month": "Saturday"
},
{
"y": 20,
"y0": 22,
"month": "Sunday"
}
],
[{
"y": 8,
"y0": 24,
"month": "Monday"
},
{
"y": 14,
"y0": 21,
"month": "Tuesday"
},
{
"y": 12,
"y0": 19,
"month": "Wednesday"
},
{
"y": 15,
"y0": 23,
"month": "Thursday"
},
{
"y": 18,
"y0": 21,
"month": "Friday"
},
{
"y": 16,
"y0": 23,
"month": "Saturday"
},
{
"y": 17,
"y0": 24,
"month": "Sunday"
}
]
]
};
n = dataset["series"].length;
m = dataset["layers"].length;
xGroupMax = d3.max(dataset["layers"], function(layer) {
return d3.max(layer, function(d) {
return d.y0;
});
});
xGroupMin = d3.min(dataset["layers"], function(layer) {
return d3.min(layer, function(d) {
return d.y;
});
});
var margin = {
top: 50,
right: 50,
bottom: 50,
left: 100
},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var xScale = d3.scaleLinear() //
.domain([xGroupMin, xGroupMax]) //
.range([0, width - margin.left]); //
var yScale = d3.scaleBand() //
.domain(dataset["categories"])
.rangeRound([0, height]) //
.padding(.08);
var xAxis = d3.axisBottom(xScale).ticks(24); //
var yAxis = d3.axisLeft(yScale).tickSize(7).tickPadding(6); //
var svg = d3.select("#groupchart").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 + ")");
var layer = svg.selectAll(".layer")
.data(dataset["layers"])
.enter().append("g")
.attr("class", "layer");
var rect = layer.selectAll("rect")
.data(function(d, i) {
d.map(function(b) {
b.colorIndex = i;
return b;
});
return d;
})
.enter()
.append("rect")
.transition()
.duration(500)
.delay(function(d, i) {
return i * 10;
})
.attr("y", function(d, i, j) { //
var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes, j[i].parentNode);
return yScale(d.month) + yScale.bandwidth() / n * k; //
})
.attr("height", yScale.bandwidth() / n) //
.transition()
.attr("x", function(d) { //
return xScale(d.y) //
})
.attr("width", function(d) { //
return xScale(d.y0 - d.y); //
})
.attr("class", "bar")
.style("fill", function(d) {
return dataset["colors"][d.colorIndex];
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.select("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("x", width / 3)
.attr("y", 0)
.attr("dx", ".71em")
.attr("dy", "-.71em")
.text("Grouped Bar Chart Test");
var legend = svg.append("g")
.attr("class", "legend")
legend.selectAll('text')
.data(dataset["colors"])
.enter()
.append("rect")
.attr("x", function(d, i) {
return (i * 120) + (width / 3);
})
.attr("y", (width - margin.right) / 1.6)
.attr("width", 10)
.attr("height", 10)
.style("fill", function(d) {
return d;
})
legend.selectAll('text')
.data(dataset["series"])
.enter()
.append("text")
.attr("x", function(d, i) {
return (i * 120) + (width / 3) + 12;
})
.attr("y", ((width - margin.right) / 1.6) + 9)
.text(function(d) {
return d;
});
var tooltip = d3.select("body")
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'month');
tooltip.append('div')
.attr('class', 'tempRange');
svg.selectAll("rect")
.on('mouseover', function(d) {
if (!d.month) return null;
tooltip.select('.month').html("<b>" + d.month + "</b>");
tooltip.select('.tempRange').html(d.y + ":00 Hours to " + d.y0 + ":00 Hours");
tooltip.style('display', 'block');
tooltip.style('opacity', 2);
})
.on('mousemove', function(d) {
if (!d.month) return null;
tooltip.style('top', (d3.event.layerY + 10) + 'px')
.style('left', (d3.event.layerX - 25) + 'px');
})
.on('mouseout', function() {
tooltip.style('display', 'none');
tooltip.style('opacity', 0);
});
}
.axis path,
.axis line {
fill: none;
font: 10px sans-serif;
stroke: #000;
shape-rendering: crispEdges;
}
.legend {
padding: 5px;
font-size: 15px;
font-family: 'Roboto', sans-serif;
background: yellow;
box-shadow: 2px 2px 1px #888;
}
.tooltip {
background: #eee;
box-shadow: 0 0 5px #999999;
color: #333;
font-size: 12px;
left: 130px;
padding: 10px;
position: absolute;
text-align: center;
top: 95px;
z-index: 10;
display: block;
opacity: 0;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Grouped Bar Graph</title>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id="groupchart" class="chart"></div>
</body>
</html>
This requires a bit more than just switching x and y axes. You'll also have to modify the w/y/width/height attributes of rectangles, as well as the the position of axes.
I've marked the modified lines with //.
Just for the fun (and if like me, starting from a vertical graph, you're lost doing all these inversions of axes/scales), here is another way of doing using a rotation by 90° of the whole graph!:
$(document).ready(function() {
render_chart();
});
function render_chart() {
var stack = d3.stack;
var dataset = {
"categories": ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
"series": ["Group1", "Group2", "Group3"],
"colors": ["#3498db", "#e74c3c", "#2ecc71"],
"layers": [
[{
"y": 1,
"y0": 20,
"month": "Monday"
},
{
"y": 2,
"y0": 18,
"month": "Tuesday"
},
{
"y": 5,
"y0": 18,
"month": "Wednesday"
},
{
"y": 10,
"y0": 20,
"month": "Thursday"
},
{
"y": 14,
"y0": 23,
"month": "Friday"
},
{
"y": 18,
"y0": 24,
"month": "Saturday"
},
{
"y": 20,
"y0": 24,
"month": "Sunday"
}
],
[{
"y": 12,
"y0": 24,
"month": "Monday"
},
{
"y": 14,
"y0": 24,
"month": "Tuesday"
},
{
"y": 13,
"y0": 23,
"month": "Wednesday"
},
{
"y": 16,
"y0": 21,
"month": "Thursday"
},
{
"y": 18,
"y0": 23,
"month": "Friday"
},
{
"y": 19,
"y0": 22,
"month": "Saturday"
},
{
"y": 20,
"y0": 22,
"month": "Sunday"
}
],
[{
"y": 8,
"y0": 24,
"month": "Monday"
},
{
"y": 14,
"y0": 21,
"month": "Tuesday"
},
{
"y": 12,
"y0": 19,
"month": "Wednesday"
},
{
"y": 15,
"y0": 23,
"month": "Thursday"
},
{
"y": 18,
"y0": 21,
"month": "Friday"
},
{
"y": 16,
"y0": 23,
"month": "Saturday"
},
{
"y": 17,
"y0": 24,
"month": "Sunday"
}
]
]
};
n = dataset["series"].length;
m = dataset["layers"].length;
yGroupMax = d3.max(dataset["layers"], function(layer) {
return d3.max(layer, function(d) {
return d.y0;
});
});
yGroupMin = d3.min(dataset["layers"], function(layer) {
return d3.min(layer, function(d) {
return d.y;
});
});
var margin = {
top: 50,
right: 50,
bottom: 50,
left: 100
},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var xScale = d3.scaleBand()
.domain(dataset["categories"])
.rangeRound([0, width])
.padding(.08);
var yScale = d3.scaleLinear()
.domain([yGroupMin, yGroupMax])
.range([height, 0]);
var xAxis = d3.axisBottom(xScale)
.tickSize(7)
.tickPadding(6);
var yAxis = d3.axisLeft(yScale).ticks(24);
var svg = d3.select("#groupchart").append("svg")
.attr("height", width + margin.left + margin.right) //
.attr("width", height + margin.left + margin.bottom) //
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.left + ") rotate(90) translate(0,-400)"); //
var layer = svg.selectAll(".layer")
.data(dataset["layers"])
.enter().append("g")
.attr("class", "layer");
var rect = layer.selectAll("rect")
.data(function(d, i) {
d.map(function(b) {
b.colorIndex = i;
return b;
});
return d;
})
.enter()
.append("rect")
.transition()
.duration(500)
.delay(function(d, i) {
return i * 10;
})
.attr("x", function(d, i, j) {
var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes, j[i].parentNode);
return xScale(d.month) + xScale.bandwidth() / n * k;
})
.attr("width", xScale.bandwidth() / n)
.transition()
.attr("y", function(d) {
return yScale(d.y0);
})
.attr("height", function(d) {
return height - yScale(d.y0 - d.y)
})
.attr("class", "bar")
.style("fill", function(d) {
return dataset["colors"][d.colorIndex];
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text") //
.attr("transform", "rotate(-90) translate(-10,-18)") //
.style("text-anchor", "end"); //
svg.select("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text") //
.attr("transform", "rotate(-90) translate(13,-18)") //
.style("text-anchor", "end"); //
svg.append("text")
.attr("dx", ".71em")
.attr("dy", "-.71em")
.text("Grouped Bar Chart Test (Rien ne va plus)")
.attr("transform", "rotate(-90) translate(-350,-35)"); //
var legend = svg.append("g")
.attr("class", "legend")
legend.selectAll('text')
.data(dataset["colors"])
.enter()
.append("rect")
.attr("x", function(d, i) {
return (i * 120) + (width / 3);
})
.attr("y", (width - margin.right) / 1.6)
.attr("width", 10)
.attr("height", 10)
.style("fill", function(d) {
return d;
})
.attr("transform", "rotate(-90) translate(-600," + (width/2 - 30) + ")"); //
legend.selectAll('text')
.data(dataset["series"])
.enter()
.append("text")
.attr("x", function(d, i) {
return (i * 120) + (width / 3) + 12;
})
.attr("y", ((width - margin.right) / 1.6) + 9)
.text(function(d) {
return d;
})
.attr("transform", "rotate(-90) translate(-600," + (width/2 - 30) + ")"); //
var tooltip = d3.select("body")
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'month');
tooltip.append('div')
.attr('class', 'tempRange');
svg.selectAll("rect")
.on('mouseover', function(d) {
if (!d.month) return null;
tooltip.select('.month').html("<b>" + d.month + "</b>");
tooltip.select('.tempRange').html(d.y + ":00 Hours to " + d.y0 + ":00 Hours");
tooltip.style('display', 'block');
tooltip.style('opacity', 2);
})
.on('mousemove', function(d) {
if (!d.month) return null;
tooltip.style('top', (d3.event.layerY + 10) + 'px')
.style('left', (d3.event.layerX - 25) + 'px');
})
.on('mouseout', function() {
tooltip.style('display', 'none');
tooltip.style('opacity', 0);
});
}
.axis path,
.axis line {
fill: none;
font: 10px sans-serif;
stroke: #000;
shape-rendering: crispEdges;
}
.legend {
padding: 5px;
font-size: 15px;
font-family: 'Roboto', sans-serif;
background: yellow;
box-shadow: 2px 2px 1px #888;
}
.tooltip {
background: #eee;
box-shadow: 0 0 5px #999999;
color: #333;
font-size: 12px;
left: 130px;
padding: 10px;
position: absolute;
text-align: center;
top: 95px;
z-index: 10;
display: block;
opacity: 0;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Grouped Bar Graph</title>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id="groupchart" class="chart"></div>
</body>
</html>
Note: this is of course not recommended.

Related

Last bin in histogram has smaller width than others

I have a D3.js histogram graph that has an issue where my last x-axis bin is significantly smaller than than the rest of the bins which are equal width. At first I thought this was a axis issue, but the axis is set with the typical width and height range and domains, making me believe it has to do with my bar code, but I'm not sure what would create just one wrong width for a bing, while the other bins are equal width. Provided below is my code. Any help would be appreciated!
Code:
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<style>
#sleep-dist-histo {
stroke-width: 0.5;
stroke: goldenrod;
}
</style>
<script>
var data = [
{ "x": "7.41" },
{ "x": "7.01" },
{ "x": "8.39" },
{ "x": "8.22" },
{ "x": "8.31" },
{ "x": "8.33" },
{ "x": "8.16" },
{ "x": "8.00" },
{ "x": "6.04" },
{ "x": "8.18" },
{ "x": "7.47" },
{ "x": "7.57" },
{ "x": "7.11" },
{ "x": "6.59" },
{ "x": "7.51" },
{ "x": "6.31" },
{ "x": "8.01" },
{ "x": "8.14" },
{ "x": "8.26" },
{ "x": "7.44" },
{ "x": "7.54" },
{ "x": "6.23" },
{ "x": "6.25" },
{ "x": "7.50" },
{ "x": "7.45" },
{ "x": "9.34" },
{ "x": "7.53" },
{ "x": "7.11" },
{ "x": "8.17" },
{ "x": "8.02" },
{ "x": "8.12" },
{ "x": "8.14" },
{ "x": "7.49" },
{ "x": "6.35" },
{ "x": "7.07" },
{ "x": "5.46" },
{ "x": "6.56" },
{ "x": "7.41" },
{ "x": "8.06" },
{ "x": "7.31" },
{ "x": "8.53" },
{ "x": "7.42" },
{ "x": "7.41" },
{ "x": "7.57" },
{ "x": "7.38" },
{ "x": "8.04" },
{ "x": "6.29" },
{ "x": "7.52" },
{ "x": "7.42" },
{ "x": "8.06" },
{ "x": "8.09" },
{ "x": "8.03" },
{ "x": "7.06" },
{ "x": "7.33" },
{ "x": "8.09" },
{ "x": "7.47" },
{ "x": "7.54" },
{ "x": "7.31" },
{ "x": "7.30" },
{ "x": "7.50" },
{ "x": "4.07" },
{ "x": "8.22" },
{ "x": "7.44" }
]
// console.log(data)
var margin = { top: 15, right: 30, bottom: 50, left: 60 }
var width = 800 - margin.left - margin.right;
var height = 800 - margin.top - margin.bottom;
var svg = d3.select("#my_dataviz")
.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 + ")"); // translate(margin left, margin top)
// Append x-scale and x-scale tick labels
var xAxisGroup = svg.append("g")
.attr("class", "y-axis")
.attr("transform", "translate(" + 0 + "," + height + ")");
// Set x-scale range
var x = d3.scaleLinear()
.range([0, width]);
// Append x-scale title
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," + (height + margin.bottom) + ")")
.style("text-anchor", "middle")
.text("Time Asleep in Hours (Bins)");
// Set y-scale range
var y = d3.scaleLinear()
.range([height, margin.top]);
// Append y-scale
var yAxisGroup = svg.append("g")
.attr("class", "y-axis");
// Append y-scale title
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Count of Days Asleep");
d3.interval(function(){
update(data)
}, 1000);
update(data);
function update(data){
// X and Y axis domains
x.domain([0, d3.max(data, function(d) { return +d.x })])
var xAxisCall = d3.axisBottom(x);
var yAxisCall = d3.axisLeft(y);
xAxisGroup.call(xAxisCall);
yAxisGroup.call(yAxisCall);
var histogram = d3.histogram()
.value(function(d) { return d.x })
.domain(x.domain())
.thresholds(x.ticks());
var bins = histogram(data);
y.domain([0, d3.max(bins, function(d){ return d.length; })]);
// Append bars
svg.selectAll("bar")
.data(bins)
.enter()
.append("rect")
.attr("x", 1)
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; })
.attr("width", function(d) { return x(d.x1) - x(d.x0) - 1 ; })
.attr("height", function(d) { return height - y(d.length); })
.style("fill", "steelblue")
.attr("id", "sleep-dist-histo");
svg.selectAll("label")
.data(bins)
.enter()
.append("text")
.text(function(d){ return d.length })
.attr("x", function(d){ return (x(d.x0) + x(d.x1)) / 2; })
.attr("y", function(d){ return y(d.length) - 15; })
.attr("class", "label")
.attr("dy", ".71em")
// .style("text-anchor", "end")
;
}
</script>
It is because the domain of x is [0, 9.34], so it ended up with 9.34 instead of 10 (to keep the same width)
Solution:
add .nice() to x to "Extends the domain so that it starts and ends on nice round values. " (See Here);
remove the last element of the new bin data otherwise there will be a zero from 10 to 11
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<style>
#sleep-dist-histo {
stroke-width: 0.5;
stroke: goldenrod;
}
</style>
<script>
var data = [
{ "x": "7.41" },
{ "x": "7.01" },
{ "x": "8.39" },
{ "x": "8.22" },
{ "x": "8.31" },
{ "x": "8.33" },
{ "x": "8.16" },
{ "x": "8.00" },
{ "x": "6.04" },
{ "x": "8.18" },
{ "x": "7.47" },
{ "x": "7.57" },
{ "x": "7.11" },
{ "x": "6.59" },
{ "x": "7.51" },
{ "x": "6.31" },
{ "x": "8.01" },
{ "x": "8.14" },
{ "x": "8.26" },
{ "x": "7.44" },
{ "x": "7.54" },
{ "x": "6.23" },
{ "x": "6.25" },
{ "x": "7.50" },
{ "x": "7.45" },
{ "x": "9.34" },
{ "x": "7.53" },
{ "x": "7.11" },
{ "x": "8.17" },
{ "x": "8.02" },
{ "x": "8.12" },
{ "x": "8.14" },
{ "x": "7.49" },
{ "x": "6.35" },
{ "x": "7.07" },
{ "x": "5.46" },
{ "x": "6.56" },
{ "x": "7.41" },
{ "x": "8.06" },
{ "x": "7.31" },
{ "x": "8.53" },
{ "x": "7.42" },
{ "x": "7.41" },
{ "x": "7.57" },
{ "x": "7.38" },
{ "x": "8.04" },
{ "x": "6.29" },
{ "x": "7.52" },
{ "x": "7.42" },
{ "x": "8.06" },
{ "x": "8.09" },
{ "x": "8.03" },
{ "x": "7.06" },
{ "x": "7.33" },
{ "x": "8.09" },
{ "x": "7.47" },
{ "x": "7.54" },
{ "x": "7.31" },
{ "x": "7.30" },
{ "x": "7.50" },
{ "x": "4.07" },
{ "x": "8.22" },
{ "x": "7.44" }
]
// console.log(data)
var margin = { top: 15, right: 30, bottom: 50, left: 60 }
var width = 800 - margin.left - margin.right;
var height = 800 - margin.top - margin.bottom;
var svg = d3.select("#my_dataviz")
.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 + ")"); // translate(margin left, margin top)
// Append x-scale and x-scale tick labels
var xAxisGroup = svg.append("g")
.attr("class", "y-axis")
.attr("transform", "translate(" + 0 + "," + height + ")");
// Set x-scale range
var x = d3.scaleLinear()
.range([0, width]);
// Append x-scale title
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," + (height + margin.bottom) + ")")
.style("text-anchor", "middle")
.text("Time Asleep in Hours (Bins)");
// Set y-scale range
var y = d3.scaleLinear()
.range([height, margin.top]);
// Append y-scale
var yAxisGroup = svg.append("g")
.attr("class", "y-axis");
// Append y-scale title
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Count of Days Asleep");
/* d3.interval(function(){
update(data)
}, 1000);
*/
update(data);
function update(data){
// X and Y axis domains
x.domain([0, d3.max(data, function(d) { return (+d.x) })]).nice()
var xAxisCall = d3.axisBottom(x);
var yAxisCall = d3.axisLeft(y);
xAxisGroup.call(xAxisCall);
yAxisGroup.call(yAxisCall);
var histogram = d3.histogram()
.value(function(d) { return +d.x })
.domain(x.domain())
.thresholds(x.ticks());
var bins = histogram(data);
y.domain([0, d3.max(bins, function(d){ return d.length; })]);
bins = bins.slice(0, bins.length-1)
// Append bars
svg.selectAll("bar")
.data(bins)
.enter()
.append("rect")
.attr("x", 1)
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; })
.attr("width", function(d) { return x(d.x1) - x(d.x0) - 1 ; })
.attr("height", function(d) { return height - y(d.length); })
.style("fill", "steelblue")
.attr("id", "sleep-dist-histo");
svg.selectAll("label")
.data(bins)
.enter()
.append("text")
.text(function(d){ return d.length })
.attr("x", function(d){ return (x(d.x0) + x(d.x1)) / 2; })
.attr("y", function(d){ return y(d.length) - 15; })
.attr("class", "label")
.attr("dy", ".71em")
// .style("text-anchor", "end")
;
}
</script>

D3 V4 how to force Label of stacked bars to visible for small bars? please reply I'm really struggling for it

I'm new to D3JS and struggling with stacked bar below is my dummy code. I tried adding 10 to data to make it visible and then subtract 10 from label to make the label look correct but this cause problem with yAxis where value of bar is crossing the ticker with less then bar value, for example label was showing 95 but bar crossed 100 ticker coz bar size is 95 + 10 = 105. Help me with this.[In image you can find out small bars label are not visible.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>d3.js learning</title>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<style type="text/css">
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
</style>
</head>
<body>
<div id="ashu" style="width: 700px; height:400px;"></div>
<script>
var data = [{
"health": "JAN",
"INSOLVENCIES": 1,
"SPV/ASSETBACKED": 67,
"OPERATINGCOMPANIES": 13,
"Bank": 15
}, {
"health": "FEB",
"INSOLVENCIES": 60,
"SPV/ASSETBACKED": 9,
"OPERATINGCOMPANIES": 20,
"Bank": 5
}, {
"health": "MAR",
"INSOLVENCIES": 40,
"SPV/ASSETBACKED": 22,
"OPERATINGCOMPANIES": 21,
"Bank": 99
}, {
"health": "APR",
"INSOLVENCIES": 60,
"SPV/ASSETBACKED": 1,
"OPERATINGCOMPANIES": 99,
"Bank": 90
}, {
"health": "MAY",
"INSOLVENCIES": 2,
"SPV/ASSETBACKED": 27,
"OPERATINGCOMPANIES": 43,
"Bank": 82
}, {
"health": "JUN",
"INSOLVENCIES": 17,
"SPV/ASSETBACKED": 52,
"OPERATINGCOMPANIES": 79,
"Bank": 9
}, {
"health": "JUL",
"INSOLVENCIES": 37,
"SPV/ASSETBACKED": 24,
"OPERATINGCOMPANIES": 35,
"Bank": 51
}, {
"health": "AUG",
"INSOLVENCIES": 16,
"SPV/ASSETBACKED": 17,
"OPERATINGCOMPANIES": 53,
"Bank": 38
}, {
"health": "SEP",
"INSOLVENCIES": 15,
"SPV/ASSETBACKED": 32,
"OPERATINGCOMPANIES": 5,
"Bank": 31
}];
let xData = d3.keys(data[0]);
const yData = xData.shift();
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var margin = {
top: 20,
right: 50,
bottom: 30,
left: 50
},
width = 400,
height = 300,
padding = 100;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var y_axis = d3.svg.axis().scale(y).orient("left").ticks(6).innerTickSize(-width)
.tickPadding(10);
var svg = d3.select("#ashu").append("svg").attr("width",
"100%").attr("height",
"100%").append("g").attr(
"transform",
"translate(" + margin.left + "," + margin.top + ")");
var dataIntermediate = xData.map(function(c) {
return data.map(function(d, yData) {
return {
x: d[Object.keys(d)[0]],
y: d[c]
};
});
});
var dataStackLayout = d3.layout.stack()(dataIntermediate);
x.domain(dataStackLayout[0].map(function(d) {
return d.x;
}));
var total = 0;
var maximumY = d3.max(dataStackLayout[dataStackLayout.length - 1], function(d) {
return (d.y + d.y0);
});
y.domain([-(maximumY * .02), maximumY]).nice();
var layer = svg.selectAll(".stack").data(dataStackLayout).enter()
.append("g").attr("class", "stack").style("fill",
function(d, i) {
return color(i);
});
layer.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y + d.y0);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y + d.y0);
})
.attr("width", x.rangeBand())
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html("Total : " + (d.y + d.y0))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
layer.selectAll("text")
.data(function(d) {
return d;
})
.enter()
.append("text")
.text(function(d) {
let r = ((d.y < 10) ? (d.y - 10) : d.y);
return d.y;
})
.attr("x", function(d, i) {
return x(d.x) + 15;
})
.attr("y", function(d) {
return y(d.y0);
})
.attr("dy", "-0.15em")
.attr("text-anchor", "middle")
.style("fill", "black");
svg.append("g").attr("class", "axis").attr("transform",
"translate(0," + height + ")").call(xAxis);
svg.append("g").attr("class", "y axis").attr("transform", "translate(0,0)").call(y_axis);
var legend = svg.selectAll(".legend")
.data(color.domain().slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + Math.abs((i - 8) * 20) + ")";
});
legend.append("rect")
.attr("x", width + 10)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width + 32)
.attr("y", 10)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
return xData[i];
});
</script>
</body>
</html>
the labels are being hidden due the order in which the g elements are creating in the DOM, in that elements created later will overlap elements created earlier. This means that the INSOLVENCIES group labels will be hidden by the orange rects for the SPV/ASSETBACKED.
A solution would be to create new g element for your labels, so they always appear on top of your rects. See the snippet below.
Now, where you have small values, there will always be a challenge about where to position the labels. The snippet places them in the middle of the bar, but you may need to play around with that
var data = [{
"health": "JAN",
"INSOLVENCIES": 1,
"SPV/ASSETBACKED": 67,
"OPERATINGCOMPANIES": 13,
"Bank": 15
}, {
"health": "FEB",
"INSOLVENCIES": 60,
"SPV/ASSETBACKED": 9,
"OPERATINGCOMPANIES": 20,
"Bank": 5
}, {
"health": "MAR",
"INSOLVENCIES": 40,
"SPV/ASSETBACKED": 22,
"OPERATINGCOMPANIES": 21,
"Bank": 99
}, {
"health": "APR",
"INSOLVENCIES": 60,
"SPV/ASSETBACKED": 1,
"OPERATINGCOMPANIES": 99,
"Bank": 90
}, {
"health": "MAY",
"INSOLVENCIES": 2,
"SPV/ASSETBACKED": 27,
"OPERATINGCOMPANIES": 43,
"Bank": 82
}, {
"health": "JUN",
"INSOLVENCIES": 17,
"SPV/ASSETBACKED": 52,
"OPERATINGCOMPANIES": 79,
"Bank": 9
}, {
"health": "JUL",
"INSOLVENCIES": 37,
"SPV/ASSETBACKED": 24,
"OPERATINGCOMPANIES": 35,
"Bank": 51
}, {
"health": "AUG",
"INSOLVENCIES": 16,
"SPV/ASSETBACKED": 17,
"OPERATINGCOMPANIES": 53,
"Bank": 38
}, {
"health": "SEP",
"INSOLVENCIES": 15,
"SPV/ASSETBACKED": 32,
"OPERATINGCOMPANIES": 5,
"Bank": 31
}];
let xData = d3.keys(data[0]);
const yData = xData.shift();
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var margin = {
top: 20,
right: 50,
bottom: 30,
left: 50
},
width = 400,
height = 300,
padding = 100;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var y_axis = d3.svg.axis().scale(y).orient("left").ticks(6).innerTickSize(-width)
.tickPadding(10);
var svg = d3.select("#ashu").append("svg").attr("width",
"100%").attr("height",
"100%").append("g").attr(
"transform",
"translate(" + margin.left + "," + margin.top + ")");
var dataIntermediate = xData.map(function(c) {
return data.map(function(d, yData) {
return {
x: d[Object.keys(d)[0]],
y: d[c]
};
});
});
var dataStackLayout = d3.layout.stack()(dataIntermediate);
x.domain(dataStackLayout[0].map(function(d) {
return d.x;
}));
var total = 0;
var maximumY = d3.max(dataStackLayout[dataStackLayout.length - 1], function(d) {
return (d.y + d.y0);
});
y.domain([0, maximumY]).nice();
var layer1 = svg.selectAll(".stack1").data(dataStackLayout).enter()
.append("g").attr("class", "stack").style("fill",
function(d, i) {
return color(i);
});
var layer2 = svg.selectAll(".stack2").data(dataStackLayout).enter()
.append("g")
.attr("class", "stack")
.style("fill", "black");
layer1.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y + d.y0);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y + d.y0);
})
.attr("width", x.rangeBand())
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html("Total : " + (d.y + d.y0))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
layer2.selectAll("text")
.data(function(d) {
return d;
})
.enter()
.append("text")
.text(function(d) {
return d.y;
})
.attr("x", function(d, i) {
return x(d.x) + (x.rangeBand())/2;
})
.attr("y", function(d) {
return y(d.y0 + (d.y/2));
})
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.style("fill", "black");
svg.append("g").attr("class", "axis").attr("transform",
"translate(0," + height + ")").call(xAxis);
svg.append("g").attr("class", "y axis").attr("transform", "translate(0,0)").call(y_axis);
var legend = svg.selectAll(".legend")
.data(color.domain().slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + Math.abs((i - 8) * 20) + ")";
});
legend.append("rect")
.attr("x", width + 10)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width + 32)
.attr("y", 10)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
return xData[i];
});
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="ashu" style="width: 700px; height:400px;"></div>

d3 connect visibility attribute to slider position

hey guys i am currently trying to change the visibility of 4 lines depending on whether the slider position is at the same "year-tick" as i have in my data.
for example: i would want line 3 which has the value "1994" stored to only be visible as long as the slider is on position "1994"
i havent workder with sliders yet, which is why i am having this much trouble getting this to work.
here is a fiddle of my code: https://fiddle.jshell.net/42jdw2Lt/3/
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js">
</script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>
<script>
var width = 1500;
var height = 1500;
var margin = {top: 50, left: 50, right: 50, bottom:50},
height = 650-margin.top-margin.bottom,
width = 1200-margin.left-margin.right;
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+")")
var strwi = d3.scaleLinear()
.domain([100, 400])
.range([7,35])
var group = svg.append("g")
var series = [
[{"x": 360, "y": 250, "num": 100}, {"x": 520, "y": 400, "num": 100}, {"x":
630, "y": 300, "num": 100, "year": 1991}],
[{"x": 71, "y": 45, "num": 200}, {"x": 32, "y": 39, "num": 200}, {"x": 43,
"y": 70, "num": 200, "year": 1992}],
[{"x": 100, "y": 300, "num": 300}, {"x": 200, "y": 200, "num": 300}, {"x":
300, "y": 200, "num": 300, "year": 1994}],
[{"x": 101, "y": 202, "num": 400}, {"x": 102, "y": 204, "num": 400}, {"x":
103, "y": 215, "num": 400, "year": 1995}]
];
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
group.selectAll(".line")
.data(series)
.enter().append("path")
.attr("class", "line")
// .attr("visibility", "hidden")
.attr("stroke-width", function(d) {return strwi(d); })
.attr("stroke", "black")
.attr("fill", "none")
.attr("d", line);
var data = [1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
2000, 2001, 2002]
var x = d3.scaleLinear()
.domain(d3.extent(data))
.range([0, width])
.clamp(true);
var slider = svg.append("g")
.attr("class", "slider")
.attr("transform", "translate(" + 0 + "," + 300 + ")");
slider.append("line")
.attr("class", "track")
.attr("x1", x.range()[0])
.attr("x2", x.range()[1])
.attr("stroke", "black")
.attr("stroke-width", "4")
.select(function() { return
this.parentNode.appendChild(this.cloneNode(true)); })
.attr("class", "track-inset")
.select(function() { return
this.parentNode.appendChild(this.cloneNode(true)); })
.attr("class", "track-overlay")
.call(d3.drag()
.on("start.interrupt", function() { slider.interrupt(); })
.on("start drag", function() { hue(x.invert(d3.event.x));
}));
slider.insert("g", ".track-overlay")
.attr("class", "ticks")
.selectAll("ticks")
.data(x.ticks(data.length))
.enter().append("text")
.attr("x", x)
.attr("text-anchor", "middle")
.attr("transform", "translate(0," + 30 + ")")
.text(function(d) { return d; })
.exit()
.data(x.ticks(data.length * 2))
.enter().append("circle")
.attr("cx", x)
.attr("r", 3)
.attr("fill", "#c1c7cd");
slider.insert("g", ".track-overlay")
.attr("class", "ticks--cirlces")
.selectAll("ticks--ticks");
var handle = slider.insert("circle", ".track-overlay")
.attr("class", "handle")
.attr("r", 9);
slider.transition() // Gratuitous intro!
.duration(750)
.tween("hue", function() {
var i = d3.interpolate(0, 70);
return function(t) { hue(i(t)); };
});
function hue(h) {
handle.attr("cx", x(h));
d3.select(".text")
.text( (Math.round(h*2)/2).toFixed(1) );
}
</script>
I added the prop year to all your elements in the data selecting all the line paths in a variable called paths so that I can change the opacity when you move your slider like this
var paths = group.selectAll(".line")
.data(series)
.enter().append("path")
.attr("class", "line")
// .attr("visibility", "hidden")
.attr("stroke-width", function(d) {
return strwi(d);
})
.attr("stroke", "black")
.attr("fill", "none")
.attr("d", line);
and then in your hue function, I added this code to change the opacity based on your slider
paths.attr('opacity', function(d) {
if (((Math.round(h * 2) / 2).toFixed(1)) > d[2]['year']) {
return 0;
} else {
return 1;
}
})
Here's a fiddle for you:
var width = 1500;
var height = 1500;
var margin = {
top: 50,
left: 50,
right: 50,
bottom: 50
},
height = 650 - margin.top - margin.bottom,
width = 1200 - margin.left - margin.right;
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 + ")")
var strwi = d3.scaleLinear()
.domain([100, 400])
.range([7, 35])
var group = svg.append("g")
var series = [
[{
"x": 360,
"y": 250,
"num": 100,
"startYear": 1991,
"endYear": 1995
}, {
"x": 520,
"y": 400,
"num": 100,
"startYear": 1991,
"endYear": 1995
}, {
"x": 630,
"y": 300,
"num": 100,
"startYear": 1991,
"endYear": 1995
}],
[{
"x": 71,
"y": 45,
"num": 200,
"startYear": 1992,
"endYear": 1993
}, {
"x": 32,
"y": 39,
"num": 200,
"startYear": 1992,
"endYear": 1993
}, {
"x": 43,
"y": 70,
"num": 200,
"startYear": 1992,
"endYear": 1993
}],
[{
"x": 100,
"y": 300,
"num": 300,
"startYear": 1994,
"endYear": 1996
}, {
"x": 200,
"y": 200,
"num": 300,
"startYear": 1994,
"endYear": 1996
}, {
"x": 300,
"y": 200,
"num": 300,
"startYear": 1994,
"endYear": 1996
}],
[{
"x": 101,
"y": 202,
"num": 400,
"startYear": 1995,
"endYear": 1997
}, {
"x": 102,
"y": 204,
"num": 400,
"startYear": 1995,
"endYear": 1997
}, {
"x": 103,
"y": 215,
"num": 400,
"startYear": 1995,
"endYear": 1997
}]
];
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
});
var paths = group.selectAll(".line")
.data(series)
.enter().append("path")
.attr("class", "line")
.attr('v1',function(d){
return d[2]['startYear'];
})
.attr('v2',function(d) {
return d[2]['endYear'];
})
.attr('opacity',0)
//.attr("visibility", "hidden")
.attr("stroke-width", function(d) {
return strwi(d);
})
.attr("stroke", "black")
.attr("fill", "none")
.attr("d", line);
var data = [1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002]
var x = d3.scaleLinear()
.domain(d3.extent(data))
.range([0, width])
.clamp(true);
var slider = svg.append("g")
.attr("class", "slider")
.attr("transform", "translate(" + 0 + "," + 300 + ")");
slider.insert("g", ".track-overlay")
.attr("class", "ticks")
.selectAll("ticks")
.data(x.ticks(data.length))
.enter().append("text")
.attr("x", x)
.attr("text-anchor", "middle")
.attr("transform", "translate(0," + 30 + ")")
.text(function(d) {
return d;
})
.exit()
.data(x.ticks(data.length * 2))
.enter().append("circle")
.attr("cx", x)
.attr("r", 3)
.attr("fill", "#c1c7cd");
slider.append("line")
.attr("class", "track")
.attr("x1", x.range()[0])
.attr("x2", x.range()[1])
.attr("stroke", "black")
.attr("stroke-width", "4")
.select(function() {
return this.parentNode.appendChild(this.cloneNode(true));
})
.attr("class", "track-inset")
.select(function() {
return this.parentNode.appendChild(this.cloneNode(true));
})
.attr("class", "track-overlay")
.call(d3.drag()
.on("start.interrupt", function() {
slider.interrupt();
})
.on("start drag", function() {
//console.log(d3.event.x)
hue(x.invert(d3.event.x));
}));
slider.insert("g", ".track-overlay")
.attr("class", "ticks--cirlces")
.selectAll("ticks--ticks");
var handle = slider.insert("circle", ".track-overlay")
.attr("class", "handle")
.attr("r", 9);
slider.transition() // Gratuitous intro!
.duration(750)
.tween("hue", function() {
var i = d3.interpolate(0, 70);
return function(t) {
//nsole.log(t)
hue(i(t));
};
});
function hue(h) {
handle.attr("cx", x(h));
d3.select(".text")
.text((Math.round(h * 2) / 2).toFixed(1));
paths
.transition().duration(300)
.attr('opacity', function(d) {
if (((Math.round(h * 2) / 2).toFixed(1)) >= d[2]['startYear'] && ((Math.round(h * 2) / 2).toFixed(1)) <= d[2]['endYear']) {
return 1;
} else {
return 0;
}
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>
Let me know if this is what you wanted or there's something else.

d3.js static force layout

I am using d3.js and would like the force layout to be NOT random every time I load up the page.
I have already read over a few questions and I set the node position but the layout still is random.
Just set static x and y positions in your node objects.
var graph = {
"nodes": [{
"name": "1",
"rating": 90,
"id": 2951,
x: 5, //Any random value
y: 10 //Any random value
}, {
"name": "2",
"rating": 80,
"id": 654654,
x: 15,
y: 20
------------------
-------------------
}],
"links": [{
"source": 5,
"target": 2,
"value": 6,
"label": "publishedOn"
}, {
"source": 1,
"target": 5,
"value": 6,
"label": "publishedOn"
},
------------------
-------------------
}
Here is the working code snippet.
var graph = {
"nodes": [{
"name": "1",
"rating": 90,
"id": 2951,
x: 5,
y: 10
}, {
"name": "2",
"rating": 80,
"id": 654654,
x: 15,
y: 20
}, {
"name": "3",
"rating": 80,
"id": 6546544,
x: 5,
y: 60
}, {
"name": "4",
"rating": 1,
"id": 68987978,
x: 55,
y: 17
}, {
"name": "5",
"rating": 1,
"id": 9878933,
x: 24,
y: 70
}, {
"name": "6",
"rating": 1,
"id": 6161,
x: 35,
y: 10
}],
"links": [{
"source": 5,
"target": 2,
"value": 6,
"label": "publishedOn"
}, {
"source": 1,
"target": 5,
"value": 6,
"label": "publishedOn"
}, {
"source": 4,
"target": 5,
"value": 4,
"label": "containsKeyword"
}, {
"source": 2,
"target": 3,
"value": 3,
"label": "containsKeyword"
}, {
"source": 3,
"target": 2,
"value": 4,
"label": "publishedBy"
}]
}
var margin = {
top: -5,
right: -5,
bottom: -5,
left: -5
};
var width = 500 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-200)
.linkDistance(50)
.size([width + margin.left + margin.right, height + margin.top + margin.bottom]);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var drag = d3.behavior.drag()
.origin(function(d) {
return d;
})
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
var svg = d3.select("#map").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.right + ")")
.call(zoom);
var rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all");
var container = svg.append("g");
//d3.json('http://blt909.free.fr/wd/map2.json', function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = container.append("g")
.attr("class", "links")
.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.call(drag);
node.append("circle")
.attr("r", function(d) {
return d.weight * 2 + 12;
})
.style("fill", function(d) {
return color(1 / d.rating);
});
force.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
var linkedByIndex = {};
graph.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index];
}
node.on("mouseover", function(d) {
node.classed("node-active", function(o) {
thisOpacity = isConnected(d, o) ? true : false;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
link.classed("link-active", function(o) {
return o.source === d || o.target === d ? true : false;
});
d3.select(this).classed("node-active", true);
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", (d.weight * 2 + 12) * 1.5);
})
.on("mouseout", function(d) {
node.classed("node-active", false);
link.classed("link-active", false);
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", d.weight * 2 + 12);
});
function dottype(d) {
d.x = +d.x;
d.y = +d.y;
return d;
}
function zoomed() {
container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
force.start();
}
function dragged(d) {
d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this).classed("dragging", false);
}
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.node-active{
stroke: #555;
stroke-width: 1.5px;
}
.link {
stroke: #555;
stroke-opacity: .3;
}
.link-active {
stroke-opacity: 1;
}
.overlay {
fill: none;
pointer-events: all;
}
#map{
border: 2px #555 dashed;
width:500px;
height:400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body>
<div id="map"></div>
</body>

How do I change nodes to be rectangles instead of circles in a d3 force layout?

How can I change the nodes to be rectangles instead of circles in the following d3 forced directed graph?
You have to append a rect SVG element instead of a circle.
So, in the script, where it shows this:
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) { return color(d.group); })
.call(force.drag);
You should change it to maybe this:
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("rect")
.attr("class", "node")
.attr("width", 40)
.attr("height", 20)
.style("fill", function(d) { return color(d.group); })
.call(force.drag);
And, where it shows:
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
Change it to:
node.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
Change circle to rect. Remove r attribute. Add width and height attibutes. Change cx and cy in fdg to x and y:
var width = 960, height = 500;
var rectWidth = 20, rectHeight = 10;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("https://raw.githubusercontent.com/d3/d3-plugins/master/graph/data/miserables.json", function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("rect")
.attr("class", "node")
.attr("width", rectWidth)
.attr("height", rectHeight)
.style("fill", function(d) { return color(d.group); })
.call(force.drag);
node.append("title")
.text(function(d) { return d.name; });
force.on("tick", function() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.attr("x", function(d) { return d.x - rectWidth / 2; })
.attr("y", function(d) { return d.y - rectHeight / 2; });
});
});
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
<script src="http://d3js.org/d3.v3.min.js"></script>
Here is a simple example of using Rectangles with a json file :
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.link {
stroke: #000;
stroke-width: 1.5px;
}
.node {
cursor: move;
fill: #ccc;
stroke: #000;
stroke-width: 1.5px;
}
.node.fixed {
fill: #f00;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 800;
var force = d3.layout.force()
.size([width, height])
.charge(-400)
.linkDistance(100)
.on("tick", tick);
var drag = force.drag();
//.on("dragstart", dragstart);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var link = svg.selectAll(".link"),
node = svg.selectAll(".node");
d3.json("graph.json", function(error, graph) {
if (error) throw error;
force
.nodes(graph.nodes)
.links(graph.links)
.start();
link = link.data(graph.links)
.enter().append("line")
.attr("class", "link");
node = node.data(graph.nodes)
.enter().append("rect")
.attr("class", "node")
.attr("width", 60)
.attr("height", 60)
.on("dblclick", dblclick)
.call(drag);
});
function tick() {
link.attr("x1", function(d) { return d.source.x+25; })
.attr("y1", function(d) { return d.source.y+25; })
.attr("x2", function(d) { return d.target.x+25; })
.attr("y2", function(d) { return d.target.y+25; });
node.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
function dblclick(d) {
d3.select(this).classed("fixed", d.fixed = false);
}
function dragstart(d) {
d3.select(this).classed("fixed", d.fixed = true);
}
</script>
The Json file is bellow. Give the exact path of your json file in the code:
{
"nodes": [
{"x": 469, "y": 410},
{"x": 493, "y": 364},
{"x": 442, "y": 365},
{"x": 467, "y": 314},
{"x": 477, "y": 248},
{"x": 425, "y": 207},
{"x": 402, "y": 155},
{"x": 369, "y": 196},
{"x": 350, "y": 148},
{"x": 539, "y": 222},
{"x": 594, "y": 235},
{"x": 582, "y": 185},
{"x": 633, "y": 200}
],
"links": [
{"source": 0, "target": 1},
{"source": 0, "target": 2},
{"source": 0, "target": 3},
{"source": 1, "target": 4},
{"source": 1, "target": 5},
{"source": 2, "target": 6},
{"source": 2, "target": 7},
{"source": 3, "target": 8},
{"source": 3, "target": 9},
{"source": 4, "target": 10},
{"source": 5, "target": 11},
{"source": 6, "target": 12}
]
}

Resources