Response D3 Histogram Chart on Screen Size - javascript

I have made a D3 histogram look responsive but I want it to be 100% responsive on any screen (div) resize. I think the idea is to calculate the diagonal of the parent div and use that to change the axis length accordingly. In this case below, The said idea calculates the horizontal size but I can not get it to recognize a vertical length and that messes up the horizontal resize.
var color = "steelblue";
// Generate a 1000 data points using normal distribution with mean=20, deviation=5
var values = d3.range(1000).map(d3.random.normal(20, 5));
//var values = {{data}}
// A formatter for counts.
var formatCount = d3.format(",.0f");
var margin = {
top: 30,
right: 5,
bottom: 20,
left: 5
},
width = parseInt(d3.select("#histog").style("width")) - margin.left - margin.right
height = 275 - margin.top - margin.bottom;
var max = d3.max(values);
var min = d3.min(values);
var x = d3.scale.linear()
.domain([min, max])
.range([0, width]);
// Generate a histogram using twenty uniformly-spaced bins.
var data = d3.layout.histogram()
.bins(x.ticks(20))
(values);
var yMax = d3.max(data, function(d) {
return d.length
});
var yMin = d3.min(data, function(d) {
return d.length
});
var colorScale = d3.scale.linear()
.domain([yMin, yMax])
.range([d3.rgb(color).brighter(), d3.rgb(color).darker()]);
var y = d3.scale.linear()
.domain([0, yMax])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bOkottom");
var svg = d3.select("#histog").append("svg")
.attr("width", "100%")
.attr("height", "26vh")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var bar = svg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) {
return "translate(" + x(d.x) + "," + y(d.y) + ")";
});
bar.append("rect")
.attr("x", 1)
.attr("width", (x(data[0].dx) - x(0)) - 1)
.attr("height", function(d) {
return height - y(d.y);
})
.attr("fill", function(d) {
return colorScale(d.y)
});
bar.append("text")
.attr("dy", ".75em")
.attr("y", -12)
.attr("x", (x(data[0].dx) - x(0)) / 2)
.attr("text-anchor", "middle")
.text(function(d) {
return formatCount(d.y);
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
/*
* Adding refresh method to reload new data
*/
function refresh(values) {
// var values = d3.range(1000).map(d3.random.normal(20, 5));
var data = d3.layout.histogram()
.bins(x.ticks(20))
(values);
// Reset y domain using new data
var yMax = d3.max(data, function(d) {
return d.length
});
var yMin = d3.min(data, function(d) {
return d.length
});
y.domain([0, yMax]);
var colorScale = d3.scale.linear()
.domain([yMin, yMax])
.range([d3.rgb(color).brighter(), d3.rgb(color).darker()]);
var bar = svg.selectAll(".bar").data(data);
// Remove object with data
bar.exit().remove();
bar.transition()
.duration(1000)
.attr("transform", function(d) {
return "translate(" + x(d.x) + "," + y(d.y) + ")";
});
bar.select("rect")
.transition()
.duration(1000)
.attr("height", function(d) {
return height - y(d.y);
})
.attr("fill", function(d) {
return colorScale(d.y)
});
bar.select("text")
.transition()
.duration(1000)
.text(function(d) {
return formatCount(d.y);
});
}
.histocontainer {
float: right;
width: 55%;
margin-right: auto;
margin-left: auto;
text-align: top;
}
#media (min-width: 576px) {
.histocontainer {
max-width: 540px;
}
}
#media (min-width: 768px) {
.histocontainer {
max-width: 720px;
}
}
#media (min-width: 992px) {
.histocontainer {
max-width: 960px;
}
}
#media (min-width: 1200px) {
.histocontainer {
max-width: 1140px;
}
}
#scalesvg {
width: 20%;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<svg id="scalesvg">
<div id=histog></div>
</svg>

I've simplified the example a bit and used the structure of your refresh function to create a resize function. I use window.addEventListener("resize") to detect resizes and trigger the function.
All the stuff that can be done without knowing the width and height, I do outside the function, and inside I just redraw the bins and the axis.
It's all your code, I just moved it around a little.
var color = "steelblue";
// Generate a 1000 data points using normal distribution with mean=20, deviation=5
var values = d3.range(1000).map(d3.random.normal(20, 5));
// A formatter for counts.
var formatCount = d3.format(",.0f");
var margin = {
top: 30,
right: 5,
bottom: 20,
left: 5
};
var max = d3.max(values);
var min = d3.min(values);
var x = d3.scale.linear()
.domain([min, max]);
var data = d3.layout.histogram()
.bins(x.ticks(20))
(values);
var yMax = d3.max(data, function(d) {
return d.length
});
var yMin = d3.min(data, function(d) {
return d.length
});
var colorScale = d3.scale.linear()
.domain([yMin, yMax])
.range([d3.rgb(color).brighter(), d3.rgb(color).darker()]);
var y = d3.scale.linear()
.domain([0, yMax]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var svg = d3.select("svg")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis");
function resize() {
var rect = d3.select('svg').node().getBoundingClientRect();
var width = rect.width - margin.left - margin.right;
var height = Math.min(rect.height, window.innerHeight) - margin.top - margin.bottom;
x.range([0, width]);
y.range([height, 0]);
// Reposition elements
svg.select('x.axis')
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
var bar = svg.selectAll(".bar").data(data);
// Remove object with data
bar.exit().remove();
var newBars = bar.enter()
.append("g")
.attr("class", "bar");
newBars.append("rect")
.attr("x", 1);
newBars.append("text")
.attr("dy", ".75em")
.attr("y", -12)
.attr("text-anchor", "middle")
bar.transition()
.duration(1000)
.attr("transform", function(d) {
return "translate(" + x(d.x) + "," + y(d.y) + ")";
});
bar.select("rect")
.transition()
.duration(1000)
.attr("width", (x(data[0].dx) - x(0)) - 1)
.attr("height", function(d) {
return height - y(d.y);
})
.attr("fill", function(d) {
return colorScale(d.y);
});
bar.select("text")
.transition()
.duration(1000)
.attr("x", (x(data[0].dx) - x(0)) / 2)
.text(function(d) {
return formatCount(d.y);
});
}
window.addEventListener("resize", function() {
console.log('resize!');
resize();
});
resize();
svg {
float: left;
width: 100%;
height: 300px;
margin-right: auto;
margin-left: auto;
text-align: top;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<svg></svg>

Related

Fix x-axis position in scrollable Heatmap d3

I created a scrollable heatmap and I want to make the x-axis fixed when scrolling the y-axis. I checked some posts here and tried to make the solutions work for example with position fixed, but it did not work out.
Edit: I tried to solve it with this example Fix x-axis position in scrollable Heatmap d3
but now my x-axis description disappeared and the x-axis is placed at the bottom.
var width = 500,
height = 600,
margintop = 50,
marginbottom = 50,
marginright = 10,
marginleft = 50
d3.csv("https://raw.githubusercontent.com/Lea1216/d3/main/heatmap.csv", function(data) {
var svg = d3.select("#my_dataviz")
.append("div")
.classed("chart",true)
.append("svg")
.attr("width",width +marginleft + marginright)
.attr("height",height+margintop + marginbottom)
.append("g")
.attr("transform",
"translate(" + marginleft + "," + margintop + ")");
var axis = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + marginleft +marginright)
.attr("height",40)
.append("g")
.attr("transform", "translate(" + marginleft + ", 0)");
var x_axis = d3.scaleBand()
.range([0, width])
.domain(data.map(function(d) {
return d.group;
}))
.padding(0.01);
axis.call(d3.axisTop(x_axis))
.selectAll("text")
.style("text-anchor", "end")
.style("position","fixed")
.attr("dx",15)
.attr("dy",5)
.attr("transform", "rotate(-65)");
var y_axis = d3.scaleBand()
.range([height, 0])
.domain(data.map(function(d) {
return d.activity;
}))
.padding(0.01);
svg.append("g")
.call(d3.axisLeft(y_axis))
.attr("class", "y_axis")
.selectAll("text")
.on("click", function(d) {
window.open(d.url, "_blank")
});
var myColor = d3.scaleLinear()
.range(["white", "#C37B89"])
.domain([1, 100])
svg.selectAll()
.data(data, function(d) {
return d.group + ':' + d.activity;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x_axis(d.group)
})
.attr("y", function(d) {
return y_axis(d.activity)
})
.attr("width", x_axis.bandwidth())
.attr("height", y_axis.bandwidth())
.style("fill", function(d) {
return myColor(d.value)
})
.style("stroke-width", 1)
.style("stroke", "none")
})
.rect {
opacity: 0.8;
}
#my_dataviz {
width: 600px;
height: 500px;
overflow-y: scroll;
padding: 50px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="my_dataviz"></div>
There are several possible solutions for this. The first one is to draw two SVG elements on the same level and use position: fixed on the X-axis element. You can see that the blocks still show up behind the axis, but you could fix that by drawing a rect the total size of the axis SVG element and giving it fill: white:
var width = 500,
height = 600,
margintop = 50,
marginbottom = 50,
marginright = 10,
marginleft = 50
d3.csv("https://raw.githubusercontent.com/Lea1216/d3/main/heatmap.csv", function(data) {
// Add the axis *before* adding the SVG, because the order matters in HTML
var axis = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + marginleft + marginright)
// Add 2 so you have a little bit of room left for the black bar
// i.e. margin top has to be less than total height!
.attr("height", margintop + 2)
.style("position", "fixed") // this makes the axis fixed
.append("g")
.attr("transform", "translate(" + marginleft + ", " + margintop + ")");
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + marginleft + marginright)
.attr("height", height + margintop + marginbottom)
.append("g")
.attr("transform", "translate(" + marginleft + ", " + margintop + ")");
var x_axis = d3.scaleBand()
.range([0, width])
.domain(data.map(function(d) {
return d.group;
}))
.padding(0.01);
axis.call(d3.axisTop(x_axis))
.selectAll("text")
.style("text-anchor", "end")
.style("position", "fixed")
.attr("dx", 15)
.attr("dy", 5)
.attr("transform", "rotate(-65)");
var y_axis = d3.scaleBand()
.range([height, 0])
.domain(data.map(function(d) {
return d.activity;
}))
.padding(0.01);
svg.append("g")
.call(d3.axisLeft(y_axis))
.attr("class", "y_axis")
.selectAll("text")
.on("click", function(d) {
window.open(d.url, "_blank")
});
var myColor = d3.scaleLinear()
.range(["white", "#C37B89"])
.domain([1, 100])
svg.selectAll()
.data(data, function(d) {
return d.group + ':' + d.activity;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x_axis(d.group)
})
.attr("y", function(d) {
return y_axis(d.activity)
})
.attr("width", x_axis.bandwidth())
.attr("height", y_axis.bandwidth())
.style("fill", function(d) {
return myColor(d.value)
})
.style("stroke-width", 1)
.style("stroke", "none")
})
.rect {
opacity: 0.8;
}
#my_dataviz {
border: solid 1px red;
width: 600px;
height: 300px;
overflow-y: scroll;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="my_dataviz"></div>
The second option is to put the other SVG in a div and scroll the div instead of the SVG. Then, you do need to do more with CSS and you need to move some CSS rules from the #my_dataviz element to the .chart element:
var width = 500,
height = 600,
margintop = 50,
marginbottom = 50,
marginright = 10,
marginleft = 50
d3.csv("https://raw.githubusercontent.com/Lea1216/d3/main/heatmap.csv", function(data) {
// Add the axis *before* adding the SVG, because the order matters in HTML
var axis = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + marginleft + marginright)
// Add 2 so you have a little bit of room left for the black bar
// i.e. margin top has to be less than total height!
.attr("height", margintop + 1)
.append("g")
.attr("transform", "translate(" + marginleft + ", " + margintop + ")");
var svg = d3.select("#my_dataviz")
.append("div")
// Note the CSS rules for .chart
.attr("class", "chart")
.append("svg")
.attr("width", width + marginleft + marginright)
// No margin-top required here, because the other element already took care of it
.attr("height", height + marginbottom)
.append("g")
// Same, no margin-top
.attr("transform", "translate(" + marginleft + ", 0)");
var x_axis = d3.scaleBand()
.range([0, width])
.domain(data.map(function(d) {
return d.group;
}))
.padding(0.01);
axis.call(d3.axisTop(x_axis))
.selectAll("text")
.style("text-anchor", "end")
.style("position", "fixed")
.attr("dx", 15)
.attr("dy", 5)
.attr("transform", "rotate(-65)");
var y_axis = d3.scaleBand()
.range([height, 0])
.domain(data.map(function(d) {
return d.activity;
}))
.padding(0.01);
svg.append("g")
.call(d3.axisLeft(y_axis))
.attr("class", "y_axis")
.selectAll("text")
.on("click", function(d) {
window.open(d.url, "_blank")
});
var myColor = d3.scaleLinear()
.range(["white", "#C37B89"])
.domain([1, 100])
svg.selectAll()
.data(data, function(d) {
return d.group + ':' + d.activity;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x_axis(d.group)
})
.attr("y", function(d) {
return y_axis(d.activity)
})
.attr("width", x_axis.bandwidth())
.attr("height", y_axis.bandwidth())
.style("fill", function(d) {
return myColor(d.value)
})
.style("stroke-width", 1)
.style("stroke", "none")
})
.rect {
opacity: 0.8;
}
#my_dataviz {
width: 600px;
border: solid 1px red;
}
/* To make sure there is no space between the DIV and the SVG */
#my_dataviz > * {
display: block;
}
.chart {
overflow-y: scroll;
max-height: 300px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="my_dataviz"></div>

Why the graph lines are off axis Y and X?

I am having trouble creating a chart with a simple line.
I'll put here my code and an image of how the line is getting off axis Y and X. I really have no idea why this is happening.
Chart:
HTML:
<div id="myChart"></div>
JavaScript:
Function to adjust my json only with the data I need:
var reduceVendedores = vendedores.reduce(function (allSales, sales) {
if (allSales.some(function (e) {
return e.vendnm === sales.vendnm;
})) {
allSales.filter(function (e) {
return e.vendnm === sales.vendnm
})[0].Vendas_Ano += sales.Vendas_Ano;
allSales.filter(function (e) {
return e.vendnm === sales.vendnm
})[0].Vendas_Ant += sales.Vendas_Ant
} else {
allSales.push({
vendnm: sales.vendnm,
Vendas_Ano: sales.Vendas_Ano,
Vendas_Ant: sales.Vendas_Ant
})
}
return allSales;
}, []);
Defining X and Y Axes of the Chart:
var margin = { top: 30, right: 30, bottom: 70, left: 60 },
width = 600 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
function getMax() {
return reduceVendedores.map(d => d.Vendas_Ano)
}
var svg = d3.select("#myChart")
.append("svg")
.attr("width", width)
.attr("height", height);
var xscale = d3.scaleBand()
.range([0, width - 100])
.domain(reduceVendedores.map(function (d) { return d.vendnm; }))
var yscale = d3.scaleLinear()
.domain([0, d3.max(getMax()) + 30000])
.range([height / 2, 0]);
var x_axis = d3.axisBottom().scale(xscale);
var y_axis = d3.axisLeft().scale(yscale);
svg.append("g")
.attr("transform", "translate(50, 10)")
.call(y_axis);
var xAxisTranslate = height / 2 + 10;
svg.append("g")
.attr("transform", "translate(50, " + xAxisTranslate + ")")
.call(d3.axisBottom(xscale))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end");
Defining the chart line:
svg.append("path")
.datum(reduceVendedores)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(function (d) { return xscale(d.vendnm); })
.y(function (d) { return yscale(d.Vendas_Ano) })
)
You're not adjusting for the margins you gave to your axis with:
.attr("transform", "translate(50, 10)")
Try:
svg.append('g')
.attr('transform','translate(50,0)')
.append("path")
.datum(reduceVendedores)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(function (d) { return xscale(d.vendnm); })
.y(function (d) { return yscale(d.Vendas_Ano) })
)
Typically you'd set your margin in svg, like:
var svg = d3.select("#myChart")
.append("svg")
.attr("width", width)
.attr("height", height)
.append('g')
.attr('transform','translate(' + margin.left + ',' + margin.top + ')')
But doing so know would ruin the alignment of your axis.

X axis values for histogram

How should one define values range for X axis?
I've took example which used decimal values in range 0 to 1, and this clearly doesn't work for greater numbers.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar rect {
fill: steelblue;
}
.bar text {
fill: #fff;
font: 10px sans-serif;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// var data = d3.range(1000).map(d3.randomBates(10));
var data = [1321017167, 1421017167, 1421017167, 1421017167, 1521017167, 1521017167];
var formatCount = d3.format(",.0f");
var svg = d3.select("svg"),
margin = {top: 10, right: 30, bottom: 30, left: 30},
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.scaleLinear()
.rangeRound([0, width]);
var bins = d3.histogram()
.domain(x.domain())
.thresholds(x.ticks(20))
(data);
var y = d3.scaleLinear()
.domain([0, d3.max(bins, function(d) { return d.length; })])
.range([height, 0]);
var bar = g.selectAll(".bar")
.data(bins)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; });
bar.append("rect")
.attr("x", 1)
.attr("width", x(bins[0].x1) - x(bins[0].x0) - 1)
.attr("height", function(d) { return height - y(d.length); });
bar.append("text")
.attr("dy", ".75em")
.attr("y", 6)
.attr("x", (x(bins[0].x1) - x(bins[0].x0)) / 2)
.attr("text-anchor", "middle")
.text(function(d) { return formatCount(d.length); });
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
</script>
By default, the domain of a linear scale is [0, 1]. You just copied that code from Mike Bostock without changing the domain. In his original code the domain is, coincidentally, the default domain, but in your code you have to define it:
var x = d3.scaleLinear()
.rangeRound([0, width])
.domain(d3.extent(data))//domain here
Here I'm using d3.extent, but you can use any other array you want.
Here is your code with that change only:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar rect {
fill: steelblue;
}
.bar text {
fill: #fff;
font: 10px sans-serif;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// var data = d3.range(1000).map(d3.randomBates(10));
var data = [1321017167, 1421017167, 1421017167, 1421017167, 1521017167, 1521017167];
var formatCount = d3.format(",.0f");
var svg = d3.select("svg"),
margin = {top: 10, right: 30, bottom: 30, left: 30},
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.scaleLinear()
.rangeRound([0, width])
.domain(d3.extent(data))
var bins = d3.histogram()
.domain(x.domain())
.thresholds(x.ticks(20))
(data);
var y = d3.scaleLinear()
.domain([0, d3.max(bins, function(d) { return d.length; })])
.range([height, 0]);
var bar = g.selectAll(".bar")
.data(bins)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; });
bar.append("rect")
.attr("x", 1)
.attr("width", x(bins[0].x1) - x(bins[0].x0) - 1)
.attr("height", function(d) { return height - y(d.length); });
bar.append("text")
.attr("dy", ".75em")
.attr("y", 6)
.attr("x", (x(bins[0].x1) - x(bins[0].x0)) / 2)
.attr("text-anchor", "middle")
.text(function(d) { return formatCount(d.length); });
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
</script>
PS: You'll have to adjust the horizontal position of the rectangles.

data not bind from json for d3 chart

Hi I am using D3 chart.
i'm using This d3 chart :http://bl.ocks.org/diethardsteiner/3287802
In this chart all the data are read from variable. I want to read the data from json file.
I have complete the half of the work. I did same to complete the another half of the work but it is not working.
Pie chart is read data from json. but the bar chart is not to read data from json.
Here I have Created Plunker check this and give some solution.
https://plnkr.co/edit/TaXMsUWuIXe5kv3yzazk?p=preview
here what i tried to read data is not read from json.
I want to run this chart as same as this example:http://bl.ocks.org/diethardsteiner/3287802
i tried like this
d3.json("data1.json", function(datasetBarChart){
debugger;
// set initial group value
var group = "All";
function datasetBarChosen(group) {
var ds = [];
for (x in datasetBarChart) {
if(datasetBarChart[x].group==group){
ds.push(datasetBarChart[x]);
}
}
return ds;
}
function dsBarChartBasics() {
debugger;
var margin = {top: 30, right: 5, bottom: 20, left: 50},
width = 500 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom,
colorBar = d3.scale.category20(),
barPadding = 1
;
return {
margin : margin,
width : width,
height : height,
colorBar : colorBar,
barPadding : barPadding
}
;
}
function dsBarChart() {
debugger;
var firstDatasetBarChart = datasetBarChosen(group);
var basics = dsBarChartBasics();
var margin = basics.margin,
width = basics.width,
height = basics.height,
colorBar = basics.colorBar,
barPadding = basics.barPadding
;
var xScale = d3.scale.linear()
.domain([0, firstDatasetBarChart.length])
.range([0, width])
;
var yScale = d3.scale.linear()
.domain([0, d3.max(firstDatasetBarChart, function(d) { return d.measure; })])
.range([height, 0])
;
//Create SVG element
var svg = d3.select("#barChart")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("id","barChartPlot")
;
var plot = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
;
plot.selectAll("rect")
.data(firstDatasetBarChart)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("width", width / firstDatasetBarChart.length - barPadding)
.attr("y", function(d) {
return yScale(d.measure);
})
.attr("height", function(d) {
return height-yScale(d.measure);
})
.attr("fill", "lightgrey")
;
// Add y labels to plot
plot.selectAll("text")
.data(firstDatasetBarChart)
.enter()
.append("text")
.text(function(d) {
return formatAsInteger(d3.round(d.measure));
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return (i * (width / firstDatasetBarChart.length)) + ((width / firstDatasetBarChart.length - barPadding) / 2);
})
.attr("y", function(d) {
return yScale(d.measure) + 14;
})
.attr("class", "yAxis")
var xLabels = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + (margin.top + height) + ")")
;
debugger;
xLabels.selectAll("text.xAxis")
.data(firstDatasetBarChart)
.enter()
.append("text")
.text(function(d) { return d.category;})
.attr("text-anchor", "middle")
// Set x position to the left edge of each bar plus half the bar width
.attr("x", function(d, i) {
return (i * (width / firstDatasetBarChart.length)) + ((width / firstDatasetBarChart.length - barPadding) / 2);
})
.attr("y", 15)
.attr("class", "xAxis")
//.attr("style", "font-size: 12; font-family: Helvetica, sans-serif")
;
// Title
svg.append("text")
.attr("x", (width + margin.left + margin.right)/2)
.attr("y", 15)
.attr("class","title")
.attr("text-anchor", "middle")
.text("Elevator Trips by Material Stream and Destination")
;
}
dsBarChart();
/* ** UPDATE CHART ** */
/* updates bar chart on request */
function updateBarChart(group, colorChosen) {
debugger;
var currentDatasetBarChart = datasetBarChosen(group);
var basics = dsBarChartBasics();
var margin = basics.margin,
width = basics.width,
height = basics.height,
colorBar = basics.colorBar,
barPadding = basics.barPadding
;
var xScale = d3.scale.linear()
.domain([0, currentDatasetBarChart.length])
.range([0, width])
;
var yScale = d3.scale.linear()
.domain([0, d3.max(currentDatasetBarChart, function(d) { return d.measure; })])
.range([height,0])
;
var svg = d3.select("#barChart svg");
var plot = d3.select("#barChartPlot")
.datum(currentDatasetBarChart)
;
/* Note that here we only have to select the elements - no more appending! */
plot.selectAll("rect")
.data(currentDatasetBarChart)
.transition()
.duration(750)
.attr("x", function(d, i) {
return xScale(i);
})
.attr("width", width / currentDatasetBarChart.length - barPadding)
.attr("y", function(d) {
return yScale(d.measure);
})
.attr("height", function(d) {
return height-yScale(d.measure);
})
.attr("fill", colorChosen)
;
plot.selectAll("text.yAxis") // target the text element(s) which has a yAxis class defined
.data(currentDatasetBarChart)
.transition()
.duration(750)
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return (i * (width / currentDatasetBarChart.length)) + ((width / currentDatasetBarChart.length - barPadding) / 2);
})
.attr("y", function(d) {
return yScale(d.measure) + 14;
})
.text(function(d) {
return formatAsInteger(d3.round(d.measure));
})
.attr("class", "yAxis")
;
svg.selectAll("text.title") // target the text element(s) which has a title class defined
.attr("x", (width + margin.left + margin.right)/2)
.attr("y", 15)
.attr("class","title")
.attr("text-anchor", "middle")
.text(group + "'s Elevator Trips by Material Stream and Destination")
;
}
});
Thanks,
You are using wrong d3.json callback signature. The first argument of the callback is an error that occurred, or null if no error occurred, the second argument is the returned data. So, you should use following:
d3.json("data1.json", function(error, data){
if(error) {
// handle error
} else {
// work with the data array
}
}
The following code snippet demonstrates the loading of dataset from an external JSON storage. When data has been loaded it is passed to the dsPieChart function that draws the above mentioned pie chart.
d3.select(window).on('load', function() {
// Loading data from external JSON source
d3.json('https://api.myjson.com/bins/1hewit', function(error, json) {
if (error) {
throw new Error('An error occurs');
} else {
dsPieChart(json);
}
});
});
// Format options
var formatAsPercentage = d3.format("%"),
formatAsPercentage1Dec = d3.format(".1%"),
formatAsInteger = d3.format(","),
fsec = d3.timeFormat("%S s"),
fmin = d3.timeFormat("%M m"),
fhou = d3.timeFormat("%H h"),
fwee = d3.timeFormat("%a"),
fdat = d3.timeFormat("%d d"),
fmon = d3.timeFormat("%b");
// PIE CHART drawing
function dsPieChart(dataset) {
var width = 400,
height = 400,
outerRadius = Math.min(width, height) / 2,
innerRadius = outerRadius * .999,
innerRadiusFinal = outerRadius * .5,
innerRadiusFinal3 = outerRadius * .45,
color = d3.scaleOrdinal(d3.schemeCategory20);
var vis = d3.select("#pieChart")
.append("svg:svg")
.data([dataset])
.attr("width", width)
.attr("height", height)
.append("svg:g")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")");
var arc = d3.arc()
.outerRadius(outerRadius).innerRadius(innerRadius);
var arcFinal = d3.arc().innerRadius(innerRadiusFinal).outerRadius(outerRadius);
var arcFinal3 = d3.arc().innerRadius(innerRadiusFinal3).outerRadius(outerRadius);
var pie = d3.pie()
.value(function(d) {
return d.measure;
});
var arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", up);
arcs.append("svg:path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc)
.append("svg:title")
.text(function(d) {
return d.data.category + ": " + formatAsPercentage(d.data.measure);
});
d3.selectAll("g.slice").selectAll("path").transition()
.duration(750)
.delay(10)
.attr("d", arcFinal);
arcs.filter(function(d) {
return d.endAngle - d.startAngle > .2;
})
.append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "translate(" + arcFinal.centroid(d) + ")rotate(" + angle(d) + ")";
})
.text(function(d) {
return d.data.category;
});
function angle(d) {
var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
return a > 90 ? a - 180 : a;
}
vis.append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text("Revenue Share 2012")
.attr("class", "title");
function mouseover() {
d3.select(this).select("path").transition()
.duration(750)
.attr("d", arcFinal3);
}
function mouseout() {
d3.select(this).select("path").transition()
.duration(750)
.attr("d", arcFinal);
}
function up(d, i) {
updateBarChart(d.data.category, color(i));
updateLineChart(d.data.category, color(i));
}
}
#pieChart {
position: absolute;
top: 10px;
left: 10px;
width: 400px;
height: 400px;
}
.slice {
font-size: 12pt;
font-family: Verdana;
fill: white;
font-weight: bold;
}
.title {
font-family: Verdana;
font-size: 15px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="pieChart"></div>
See also Bar chart from external JSON file example.

d3 v4 Why can't I get my x axis and bars to line up?

I've got as far as making my bar chart with an x axis but can't work out why I can't get the bars and the tick spacing on the x axis to line up exactly. At the moment the ticks are slightly to the right of center of the bar.
csv file example:
crop,records
CASSAVA,350
MAIZE,226
TOMATOES,137
code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Abbie's attempt at D3</title>
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
<style>
svg {
background-color: rgba(227, 227, 227, 0.97);
}
.bar {
margin: 20px;
}
</style>
</head>
<body>
<script type="text/javascript">
var margin = {top: 20, right: 30, bottom: 100, left: 40};
var w = 500 - margin.left - margin.right;
var h = 500 - margin.top - margin.bottom;
var barPadding = 5;
var cropData;
// load the csv file
d3.csv("crops.csv", function(error, data) {
data.forEach(function(d) {
d.records = +d.records;
});
cropData = data;
var arrayLength = cropData.length;
var yMax = d3.max(cropData, function(d) {
return d.records;
});
var yScale = d3.scaleLinear()
.domain([0, yMax])
.range([h, 0]);
var xScale = d3.scaleBand()
.domain(cropData.map(function(d) {
return d.crop;
}))
.rangeRound([0, w]);
// create the svg
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 + ")");
// bars
svg.selectAll("rect")
.data(cropData)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * xScale.bandwidth() + 5;
})
.attr("y", function(d) {
return yScale(d.records);
})
.attr("width", xScale.bandwidth() - 5)
.attr("height", function(d) {
return h - yScale(d.records);
})
.attr("fill", "teal")
.attr("class", "bar");
// x Axis
var xAxis = d3.axisBottom(xScale);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-60)"
});
// y Axis
var yAxis = d3.axisLeft(yScale);
svg.append("g")
.call(yAxis);
</script>
</body>
Am I doing it right with i * xScale.bandwidth() + 5 and xScale.bandwidth() - 5 in the x and width attributes of rect or is this not the correct way to do it? How do I change the spacing of the ticks on the x axis if I change the bars? They both use xScale so I feel it must be something to do with that.
I'd make the following changes.
First, introduce a padding to your xScale:
var xScale = d3.scaleBand()
.domain(cropData.map(function(d) {
return d.crop;
}))
.rangeRound([0, w])
.padding(0.1);
This will space the bars by a percent of the bandWidth.
Second, place your bars using your xScale:
svg.selectAll("rect")
.data(cropData)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(d.crop); //<-- place by xScale
})
.attr("y", function(d) {
return yScale(d.records);
})
.attr("width", xScale.bandwidth()) //<-- no -5 padding takes care of breaks
Runnable code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Abbie's attempt at D3</title>
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
<style>
svg {
background-color: rgba(227, 227, 227, 0.97);
}
.bar {
margin: 20px;
}
</style>
</head>
<body>
<script type="text/javascript">
var margin = {
top: 20,
right: 30,
bottom: 100,
left: 40
};
var w = 500 - margin.left - margin.right;
var h = 500 - margin.top - margin.bottom;
var barPadding = 5;
var cropData;
// load the csv file
//d3.csv("crops.csv", function(error, data) {
var data = [{
"crop": "CASSAVA",
"records": "350"
}, {
"crop": "MAIZE",
"records": "226"
}, {
"crop": "TOMATOES",
"records": "137"
}];
data.forEach(function(d) {
d.records = +d.records;
});
cropData = data;
var arrayLength = cropData.length;
var yMax = d3.max(cropData, function(d) {
return d.records;
});
var yScale = d3.scaleLinear()
.domain([0, yMax])
.range([h, 0]);
var xScale = d3.scaleBand()
.domain(cropData.map(function(d) {
return d.crop;
}))
.rangeRound([0, w])
.padding(0.1);
// create the svg
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 + ")");
// bars
svg.selectAll("rect")
.data(cropData)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(d.crop);
})
.attr("y", function(d) {
return yScale(d.records);
})
.attr("width", xScale.bandwidth())
.attr("height", function(d) {
return h - yScale(d.records);
})
.attr("fill", "teal")
.attr("class", "bar");
// x Axis
var xAxis = d3.axisBottom(xScale);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-60)"
});
// y Axis
var yAxis = d3.axisLeft(yScale);
svg.append("g")
.call(yAxis);
// });
</script>
</body>

Categories