I read previously answered questions but had no luck.
I am plotting a bar graph of elements on array using d3.js but the bars are not shown as they should be.
This is the script I am trying:
var data = [1, 2, 3, 4, 5];
var svg = d3.select("svg");
var margin = 100,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin;
var Xscale = d3.scaleBand()
.domain([0, data.length])
.range([0, width])
.padding(0.2);
var Yscale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(Xscale).tickFormat(function(d) {
return d;
}).ticks(10));
// .append("text")
// .attr("x", 6)
// .attr("text-anchor", "end")
// .text("index");
g.append("g")
.call(d3.axisLeft(Yscale).tickFormat(function(d) {
return d;
}).ticks(10))
.append("text")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("value");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d, i) {
return 0;
})
.attr("y", function(d, i) {
return 0;
})
.attr("width", Xscale.bandwidth())
.attr("height", function(d, i) {
return 0;
});
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Bar chart with D3.js</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="styles/da.css">
<script src="https://d3js.org/d3.v5.min.js"></script>
<!-- <script src="js/da.js"></script> -->
</head>
<body>
<div id='layout'>
<h2>Bar chart example</h2>
<div class='container'>
<svg class="chart" height="500" width="1000" />
</div>
</div>
<p>Why this is not working?? </p>
</body>
</html>
I do feel that the problem is in last few lines, fetching x, y, width and height. I can't understand what values to return, I tried various, but didn't get the graph. So, I have just put return 0 there.
What should be the values? And how to decide it?
Thank you for help. :)
Your scaleBand().domain() has to be an array for the Xscale. In my solution I choose to have the indexes of the values as the array. You could map your data (usually an array of objects) to other values of the objects in an array.
Additionally there were several other issues with the scaling in terms of height and width of the actual bars and their positioning. Keep in mind that the SVG origin is the top left corner and everything is with respect to that.
I have updated the code below which makes necessary changes to produce a bar graph. Please go through it and let me know if there is anything that you do not understand.
var data = [1, 2, 3, 4, 5];
var svg = d3.select("svg");
var margin = 100,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin;
var Xscale = d3.scaleBand()
.domain(data.map((e,i) => i)) //returns array [0,1,2,3,4] for the index of the values
.range([0, width])
.padding(0.2);
var dmax = d3.max(data) //calculates the max value of the data
var Yscale = d3.scaleLinear()
.domain([0, dmax])
.range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 50 + "," + 50 + ")");
var x = g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(Xscale).tickFormat(function(d) {
return d;
}).ticks(10))
.append("text")
.attr("x", 6)
.attr("text-anchor", "end")
.text("index");
var y = g.append("g")
.call(d3.axisLeft(Yscale).tickFormat(function(d) {
return d;
}).ticks(10))
.append("text")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("value");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d, i){ return Xscale(i)}) //move the bar to the x position where it should appear
.attr("y", function(d, i) { return Yscale(d); }) //move the bar from the top down to the level of the value.
.attr("width", Xscale.bandwidth() ) //the width of the bar is the width between the points on the x-axis
.attr("height", function(d, i) {
return Yscale(dmax-d);
}); // the height of the points is calculated based on the scale and the difference between this point and the max value of the data.
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Bar chart with D3.js</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id='layout'>
<h2>Bar chart example</h2>
<div class='container'>
<svg class="chart" height="500" width="1000" />
</div>
</div>
<p>Why this is not working?? </p>
</body>
</html>
I recommand you to read this article. It explain very well how to use bar charts.
https://blog.risingstack.com/d3-js-tutorial-bar-charts-with-javascript/
I used your code and did a simple example. https://codepen.io/mp-9007/pen/jJpEWY
The main issues are because of the return values of x, y and height;
You have to provide the x and y position in the graph area. Drawing the bar chart is like drawing on a Cartesian plane, you must provide the coordinate of where to start the bar, the width of the bar and the height of it. The origin of the plan is at the top left of the image.
.attr("x", function(d, i) { //d = input data, i = index of it
return Xscale(d); //The scaling function returns the coordinate for a given domain value.
})
.attr("y", function(d, i) { //d = input data, i = index of it
return Yscale(d); //The scaling function returns the coordinate for a given domain value.
})
.attr("width", Xscale.bandwidth())
.attr("height", function(d, i) { //d = input data, i = index of it
return height - Yscale(d); //The computed y coordinate has to be subtracted from the height of the chart to get the correct representation of the value as a column.
});
Also, the domain for the x-axis can be think as categories. In your code, you were providing only two category: 0 and data.length . Provinding the array solved this issue.
var Xscale = d3.scaleBand().domain(data)
Related
I am using D3 to make a stacked bar chart (for more artistic purposes than scientific). I want to design my stacked bar chart to be centered around one group, with half above and half below an invisible line, and have the other two groups be on either side of the line.
Currently, my graph looks like this
But I want it to look more like this
My code is here:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Stacked Bar</title>
</head>
<body>
<div class="container">
<div id="chart"></div>
</div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
const width = 860,
height = 400,
margin = { top: 40, right: 30, bottom: 20, left: 20 };
const svg = d3
.select("#chart")
.append("svg")
.attr("viewBox", [0, 0, width, height]);
d3.csv("test.csv").then((data) => {
let x = d3
.scaleBand(
data.map((d) => d.Time),
[margin.left, width - margin.right]
)
.padding([0.2]);
let y = d3.scaleLinear([0, 500], [height - margin.bottom, margin.top]);
svg
.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x));
svg.append("g").attr("transform", `translate(${margin.left},0)`);
// .call(d3.axisLeft(y).tickSize(-width + margin.left + margin.right));
//protein,carbs,fiber
const subgroups = data.columns.slice(1);
const color = d3.scaleOrdinal(subgroups, [
"#e41a1c",
"#377eb8",
"#4daf4a",
]);
const stackedData = d3.stack().keys(subgroups)(data);
console.log(stackedData);
svg
.append("g")
.selectAll("g")
.data(stackedData)
.join("g")
.attr("fill", (d) => color(d.key))
.selectAll("rect")
.data((d) => d)
.join("rect")
.attr("x", (d) => x(d.data.Time))
.attr("y", (d) => y(d[1]))
.attr("height", (d) => y(d[0]) - y(d[1]))
.attr("width", x.bandwidth());
let legendGroup = svg
.selectAll(".legend-group")
.data(subgroups)
.join("g")
.attr("class", "legend-group");
legendGroup
.append("circle")
.attr("cx", (d, i) => 10 + i * 75)
.attr("cy", 10)
.attr("r", 3)
.attr("fill", (d, i) => color(i));
legendGroup
.append("text")
.attr("x", (d, i) => 20 + i * 75)
.attr("y", 15)
.text((d, i) => subgroups[i]);
});
</script>
</body>
</html>
and csv:
Time,team1,team2,middle
0,5,2,70
1,10,13,89
2,4,15,110
3,6,16,145
4,12,2,167
5,42,3,111
6,6,4,108
7,7,5,92
8,8,34,140
9,12,89,190
10,22,90,398
11,42,91,459
12,60,23,256
13,69,13,253
14,43,11,188
15,42,7,167
16,21,9,124
17,16,12,156
18,7,14,167
19,12,13,188
Does anyone know how I could vertically center each line around the middle group? Is this something to do in the data pre-processing or in the graph making itself?
You have to use the correct offset, in this case d3.offsetWiggle:
const stackedData = d3.stack().offset(d3.stackOffsetWiggle)
In this solution I'm flattening the stacked data and getting the extent, which I'll pass to the y scale:
const flatData = stackedData.flat(2);
y.domain(d3.extent(flatData));
Finally, I'm just moving the x axis to the middle of the y range. Also, I'm hardcoding the stack keys, but making the sequence programatically is trivial, as well as some other details you'll have to adjust.
Here's the result:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Stacked Bar</title>
</head>
<body>
<div class="container">
<div id="chart"></div>
</div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
const width = 860,
height = 400,
margin = {
top: 40,
right: 30,
bottom: 20,
left: 20
};
const svg = d3
.select("#chart")
.append("svg")
.attr("viewBox", [0, 0, width, height]);
const csv = `Time,team1,team2,middle
0,5,2,70
1,10,13,89
2,4,15,110
3,6,16,145
4,12,2,167
5,42,3,111
6,6,4,108
7,7,5,92
8,8,34,140
9,12,89,190
10,22,90,398
11,42,91,459
12,60,23,256
13,69,13,253
14,43,11,188
15,42,7,167
16,21,9,124
17,16,12,156
18,7,14,167
19,12,13,188`;
const data = d3.csvParse(csv);
let x = d3
.scaleBand(
data.map((d) => d.Time), [margin.left, width - margin.right]
)
.padding([0.2]);
let y = d3.scaleLinear().range([height - margin.bottom, margin.top]);
svg.append("g").attr("transform", `translate(${margin.left},0)`);
// .call(d3.axisLeft(y).tickSize(-width + margin.left + margin.right));
//protein,carbs,fiber
const subgroups = ["team1", "middle", "team2"];
const color = d3.scaleOrdinal(subgroups, [
"#377eb8",
"#4daf4a",
"#e41a1c"
]);
const stackedData = d3.stack().offset(d3.stackOffsetWiggle).order(d3.stackOrderNone).keys(subgroups)(data);
const flatData = stackedData.flat(2);
y.domain(d3.extent(flatData));
svg
.append("g")
.selectAll("g")
.data(stackedData)
.join("g")
.attr("fill", (d) => color(d.key))
.selectAll("rect")
.data((d) => d)
.join("rect")
.attr("x", (d) => x(d.data.Time))
.attr("y", (d) => y(d[1]))
.attr("height", (d) => y(d[0]) - y(d[1]))
.attr("width", x.bandwidth());
svg
.append("g")
.attr("transform", `translate(0,${margin.top + (height - margin.bottom)/2})`)
.call(d3.axisBottom(x));
let legendGroup = svg
.selectAll(".legend-group")
.data(subgroups)
.join("g")
.attr("class", "legend-group");
legendGroup
.append("circle")
.attr("cx", (d, i) => 10 + i * 75)
.attr("cy", 10)
.attr("r", 3)
.attr("fill", (d, i) => color(i));
legendGroup
.append("text")
.attr("x", (d, i) => 20 + i * 75)
.attr("y", 15)
.text((d, i) => subgroups[i]);
</script>
</body>
</html>
I changed the attributes of the X axis to plot its values over the bars of the chart. But anywhere I put the code, the values are always plotted before ("behind") the bars and therefore we cannot see it.
//This part of the code is OUTSIDE of the update function (line 44 of the fiddle)
//append group to plot X axis
const xAxisGroup = g.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${HEIGHT})`)
//This part of the code is INSIDE the update function (line 92)
const xAxisCall = d3.axisBottom(x)
xAxisGroup.call(xAxisCall)
.selectAll("text")
.attr("x", "-5") // <<<--- I change this to 50
.attr("y", "10")
.attr("text-anchor", "end")
.attr("transform", "rotate(-45)") // <<<--- I changed this to -90
How would be possible to plot this values over the bars instead?
This is the fiddle of the original chart and this is the modified one. The month values may be behind the bars... :-/
In an SVG, whatever is painted later stays on top. So, just append your x axis <g> element after painting the rectangles. Alternatively, raise it:
xAxisGroup.raise()
Here's your code with that change:
//set general margin, width and height values
const MARGIN = {
LEFT: 128,
RIGHT: 8,
TOP: 32,
BOTTOM: 128
}
const WIDTH = 400 - MARGIN.LEFT - MARGIN.RIGHT
const HEIGHT = 300 - MARGIN.TOP - MARGIN.BOTTOM
//append svg plot area into div chart area
const svg = d3.select("#chart-area").append("svg")
.attr("width", WIDTH + MARGIN.LEFT + MARGIN.RIGHT)
.attr("height", HEIGHT + MARGIN.TOP + MARGIN.BOTTOM)
//append group into svg
const g = svg.append("g")
.attr("transform", `translate(${MARGIN.LEFT}, ${MARGIN.TOP})`)
//X label
g.append("text")
.attr("class", "x axis-label")
.attr("x", WIDTH / 2)
.attr("y", HEIGHT + 60)
.attr("font-size", "20px")
.attr("text-anchor", "middle")
.text("Month")
//Y label
g.append("text")
.attr("class", "y axis-label")
.attr("x", -(HEIGHT / 2))
.attr("y", -60)
.attr("font-size", "20px")
.attr("text-anchor", "middle")
.attr("transform", "rotate(-90)")
.text("Value")
//set scale for X axis
const x = d3.scaleBand()
.range([0, WIDTH])
.paddingInner(0.3)
.paddingOuter(0.2)
//set scale for Y axis
const y = d3.scaleLinear()
.range([HEIGHT, 0])
//append group to plot X axis
const xAxisGroup = g.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${HEIGHT})`)
//append group to plot Y axis
const yAxisGroup = g.append("g")
.attr("class", "y axis")
//import data
d3.csv("https://raw.githubusercontent.com/dbahiense/sotabook/main/revenues.csv").then(data => {
//parse values
data.forEach(d => {
d.revenue = Number(d.revenue)
d.profit = Number(d.profit)
})
//listen drop-down lists and trigger update function on change
//state
d3.select("#state")
.on("change", function(event, d) {
update(data)
})
//round
d3.select("#round")
.on("change", function(event, d) {
update(data)
})
//plot chart on page first load
update(data)
})
// update chart function
function update(data) {
//drop-down list listened values
let state = d3.select("#state").property("value")
let round = d3.select("#round").property("value")
//filter data by drop-down list values
let filteredData = data.filter(function(d) {
return d.state == state & d.round == round
})
//set domains for X and Y axes
x.domain(filteredData.map(d => d.month))
y.domain([0, d3.max(filteredData, d => d.revenue)])
const xAxisCall = d3.axisBottom(x)
const yAxisCall = d3.axisLeft(y)
//.tickFormat(d => d + "m")
yAxisGroup.call(yAxisCall)
// JOIN new data with old elements.
const rects = g.selectAll("rect")
.data(filteredData)
// EXIT old elements not present in new data.
rects.exit().remove()
// UPDATE old elements present in new data.
rects
.attr("y", d => y(d.revenue))
.attr("x", (d) => x(d.month))
.attr("width", x.bandwidth)
.attr("height", d => HEIGHT - y(d.revenue))
// ENTER new elements present in new data.
rects.enter().append("rect")
.attr("y", d => y(d.revenue))
.attr("x", (d) => x(d.month))
.attr("width", x.bandwidth)
.attr("height", d => HEIGHT - y(d.revenue))
.attr("fill", "steelblue")
xAxisGroup.raise()
.call(xAxisCall)
.selectAll("text")
.attr("x", "50")
.attr("y", "10")
.attr("text-anchor", "end")
.attr("transform", "rotate(-90)")
}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="">
<title>5.4</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<!-- Custom styling -->
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<!-- Bootstrap grid setup -->
<div class="container">
<div class="row">
<select id="state">
<option value="US">US</option>
<option value="EU">EU</option>
<option value="AS">AS</option>
</select>
<select id="round">
<option value="1">1</option>
<option value="2">2</option>
</select>
</div>
<div class="row">
<div id="chart-area"></div>
</div>
</div>
<!-- External JS libraries -->
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<!-- Custom JS below-->
</body>
</html>
I am trying to add state abbreviations within a scatterplot like so:
Here's a snippet of the CSV file I am working with:
id abbr healthcare poverty
1 AL 13.9 19.3
2 AK 15 11.2
3 AZ 14.4 18.2
4 AR 16.3 18.9
5 CA 14.8 16.4
6 CO 12.8 12
7 CT 8.7 10.8
8 DE 8.7 12.5
Here's my JavaScript code:
// #TODO: YOUR CODE HERE!
var svgWidth = 750;
var svgHeight = 500;
var margin = {
top: 20,
right: 40,
bottom: 60,
left: 100
};
var width = svgWidth - margin.left - margin.right;
var height = svgHeight - margin.top - margin.bottom;
// Create an SVG wrapper, append an SVG group that will hold our chart and shift the latter by left and top margins
var svg = d3.select("#scatter")
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight);
var chartGroup = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// Import Data
d3.csv("data.csv").then(function(censusData) {
// Parse Data & Cast as numbers
censusData.forEach(function(data) {
data.healthcare = +data.healthcare;
data.poverty = +data.poverty;
});
// Create scale functions
var xLinearScale = d3.scaleLinear()
.domain(d3.extent(censusData, d => d.poverty))
.range([0, width]);
var yLinearScale = d3.scaleLinear()
.domain([0, d3.max(censusData, d => d.healthcare)])
.range([height, 0]);
// Create axis functions
var bottomAxis = d3.axisBottom(xLinearScale);
var leftAxis = d3.axisLeft(yLinearScale);
// Append axes to the chart
chartGroup.append("g")
.attr("transform", `translate(0, ${height})`)
.call(bottomAxis);
chartGroup.append("g")
.call(leftAxis);
// Create circles
var circlesGroup = chartGroup.selectAll("Circle")
.data(censusData)
.enter()
.append("circle")
.attr("cx", d => xLinearScale(d.poverty))
.attr("cy", d => yLinearScale(d.healthcare))
.attr("r", "15")
.attr("fill", "rgb(117, 145, 197)")
.attr("opacity", "0.5");
// Add state labels to the points
var circleLabels = circlesGroup.selectAll("text").data(censusData).enter().append("text");
circleLabels
.attr("x", function(d) { return d.poverty; })
.attr("y", function(d) { return d.healthcare; })
.text(function(d) { return d.abbr; })
.attr("font-family", "sans-serif")
.attr("font-size", "5px")
.attr("fill", "white");
// Create axes labels
chartGroup.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left + 40)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.attr("class", "axisText")
.style("text-anchor", "middle")
.text("Lacks Healthcare (%)");
chartGroup.append("text")
.attr("transform", `translate(${width / 2}, ${height + margin.top + 30})`)
.attr("class", "axisText")
.style("text-anchor", "middle")
.text("In Poverty (%)");
// Initialize tooltip
var toolTip = d3.tip()
.attr("class", "tooltip")
.offset([80, -60])
.html(function(d) {
return `${d.state}<br>Poverty: ${d.poverty}<br>Healthcare: ${d.healthcare}<br>`;
});
// Create tooltip in the chart
chartGroup.call(toolTip);
// Create event listeners to display and hide the tooltip
circlesGroup.on("mouseover", function(data) {
toolTip.show(data, this);
})
// onmouseout event
.on("mouseout", function(data, index) {
toolTip.hide(data);
});
});
I attempted to add them in the circleLabels part of the code but to no avail.
Can anyone tell me what I am doing wrong in this part:
// Add state labels to the points
var circleLabels = circlesGroup.selectAll("text").data(censusData).enter().append("text");
circleLabels
.attr("x", function(d) { return d.poverty; })
.attr("y", function(d) { return d.healthcare; })
.text(function(d) { return d.abbr; })
.attr("font-family", "sans-serif")
.attr("font-size", "5px")
.attr("fill", "white");
Any suggestions or changes are welcome.
You have three problems:
circlesGroup is a circles' selection. You cannot append <text> elements to <circle> elements. Therefore, change it to chartGroup:
var circleLabels = chartGroup.selectAll("text")//etc...
and that brings us to the second problem:
There are text elements in that selection. So, to avoid binding data to existing elements (which reduces the size of the enter selection), use selectAll(null:
var circleLabels = chartGroup.selectAll(null)//etc...
to read more about selectAll(null), read my Q/A pair here: Selecting null: what is the reason behind 'selectAll(null)' in D3.js?
You are not using the scales for positioning the texts.
Finally, use text-anchor: middle for entering the texts.
Here is your code with those changes:
var csv = `id,abbr,healthcare,poverty
1,AL,13.9,19.3
2,AK,15,11.2,
3,AZ,14.4,18.2
4,AR,16.3,18.9
5,CA,14.8,16.4
6,CO,12.8,12
7,CT,8.7,10.8
8,DE,8.7,12.5`;
const censusData = d3.csvParse(csv)
var svgWidth = 960;
var svgHeight = 500;
var margin = {
top: 20,
right: 40,
bottom: 60,
left: 100
};
var width = svgWidth - margin.left - margin.right;
var height = svgHeight - margin.top - margin.bottom;
// Create an SVG wrapper, append an SVG group that will hold our chart and shift the latter by left and top margins
var svg = d3.select("#scatter")
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight);
var chartGroup = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// Parse Data & Cast as numbers
censusData.forEach(function(data) {
data.healthcare = +data.healthcare;
data.poverty = +data.poverty;
});
// Create scale functions
var xLinearScale = d3.scaleLinear()
.domain(d3.extent(censusData, d => d.poverty))
.range([0, width]);
var yLinearScale = d3.scaleLinear()
.domain([0, d3.max(censusData, d => d.healthcare)])
.range([height, 0]);
// Create axis functions
var bottomAxis = d3.axisBottom(xLinearScale);
var leftAxis = d3.axisLeft(yLinearScale);
// Append axes to the chart
chartGroup.append("g")
.attr("transform", `translate(0, ${height})`)
.call(bottomAxis);
chartGroup.append("g")
.call(leftAxis);
// Create circles
var circlesGroup = chartGroup.selectAll("Circle")
.data(censusData)
.enter()
.append("circle")
.attr("cx", d => xLinearScale(d.poverty))
.attr("cy", d => yLinearScale(d.healthcare))
.attr("r", "15")
.attr("fill", "blue")
.attr("opacity", "0.5");
var circleLabels = chartGroup.selectAll(null).data(censusData).enter().append("text");
circleLabels
.attr("x", function(d) {
return xLinearScale(d.poverty);
})
.attr("y", function(d) {
return yLinearScale(d.healthcare);
})
.text(function(d) {
return d.abbr;
})
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.attr("text-anchor", "middle")
.attr("fill", "white");
// Create axes labels
chartGroup.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left + 40)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.attr("class", "axisText")
.text("Lacks Healthcare (%)");
chartGroup.append("text")
.attr("transform", `translate(${width / 2}, ${height + margin.top + 30})`)
.attr("class", "axisText")
.text("In Poverty (%)");
<head>
<meta charset="UTF-8">
<title>D3Times</title>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="assets/css/style.css">
<link rel="stylesheet" href="assets/css/d3Style.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-12 col-md-12">
<h1>D3Times</h1>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-9">
<div id="scatter">
<!-- We append our chart here. -->
</div>
</div>
</div>
</div>
<!-- Footer-->
<div id="footer">
<p>The Coding Boot CampĀ©2016</p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.5.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.9.1/d3-tip.js"></script>
<script type="text/javascript" src="assets/js/app.js"></script>
</body>
Hope someone can help, I have a slight problem in that the horizontal axis label 100 gets cut off the end of the stacked horizontal barchart. I can't seem to figure out what is wrong in the code. Thanks in advance for your help. Please see code below.
<!DOCTYPE html>
-->
<html>
<head>
<title>Horizontal stacked bar</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<script type="text/javascript" src="d3/d3.js"> </script>
<style>
.axis{
font-size: 14px;
}
#h{
}
</style>
</head>
<body>
<script>
var margin = {
top: 12,
left: 15,
right: 15,
bottom: 14
};
var w = 500 - margin.left - margin.right;
var h = 300 - margin.top - margin.bottom;
var dataset = [
[
{x:0,y:20}
],
[
{x:0,y:30}
],
[
{x:0,y:50}
]
];
//Set up stack method
var stack = d3.layout.stack();
//Data, stacked
stack(dataset);
//Set up scales
var xScale = d3.scale.linear()
.domain([0,d3.max(dataset, function(d) {return d3.max(d, function(d)
{return d.y0 + d.y;}); }) ])
// note use of margin + right to get axis to scale width
.range([0, w + margin.right]);
var yScale = d3.scale.ordinal()
.domain(d3.range(dataset[0].length))
.rangeRoundBands([0,w ], 0.05);
//Easy colors accessible via a 10-step ordinal scale
var colors = d3.scale.category10();
//or make your own colour palet
var color = d3.scale.ordinal()
.range(["#1459D9", "#148DD9", "#87ceeb", "#daa520"]);
// good site for colour codes http://www.colorpicker.com/113EF2
//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 + ")")
;
// Add a group for each row of data
var groups = svg.selectAll("g")
.data(dataset)
.enter()
.append("g")
.style("fill", function(d,i){return color(i);})
;
// Add a rect for each data value
var rects = groups.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d) {return xScale(d.y0) ;}) //+99 will move axis right
.attr("y", 180)
.attr("height", 90)
.attr("width", yScale.rangeBand());
//Add an axis
var xAxis = d3.svg.axis()
.scale(xScale);
svg.append("g")
.call(xAxis)
;
</script>
</body>
</html>
You are really better off using the xScale for both dimensions, x and y. After all, your y is really a width. Here is what I mean:
...
//Set up scales
var xScale = d3.scale.linear()
.domain([0, d3.max(dataset, function (d) {
return d3.max(d, function (d) {
return d.y0 + d.y;
});
})])
.range([0, w]); // no need to tamper with margins since w already accounts for that
...
// Add a rect for each data value
var rects = groups.selectAll("rect")
.data(function (d) {return d;})
.enter()
.append("rect")
.attr("x", function (d) {
return xScale(d.y0); // use x scale
})
.attr("y", 50)
.attr("height", 50)
.attr("width", function (d) {
return xScale(d.y); // use x scale
})
...
And here is the updated FIDDLE. You can go ahead and make changes to the right margin value and any of your data y values (I placed comments in the code to that effect) and you can see that this solution scales well.
i sucessfully rendered d3js area chart in a html page here is the code which is rendering sucessfully in chrome or mozilla.The name of the file is say temp.html.Here is the code
<!DOCTYPE html>
<html lang="en" class="no-js">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>js graphs and charts libraries</title>
<script
src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="http://d3js.org/d3.v3.js"></script>
<body>
<div id="dbar">
</div>
<script type="text/javascript">
var margin = {top:10, right: 20, bottom: 30,left: 40},
width = 960 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var svg = d3.select ("#dbar").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 parseDate = d3.time.format("%m-%Y").parse;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
var area = d3.svg.area().x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.count); });
d3.json("data/json.json", function(error, data) {
data.StoreVisitGraphCount.list.forEach(function(d) {
d.date = parseDate(d.date);
d.count = +d.count;
});
//console.log(data.StoreVisitGraphCount.list);
x.domain(d3.extent(data.StoreVisitGraphCount.list, function(d) {
return d.date; }));
y.domain([0, d3.max(data.StoreVisitGraphCount.list, function(d) {
`return d.count; })]);`
console.log(data.StoreVisitGraphCount.list);
svg.append("path")
.datum(data.StoreVisitGraphCount.list)
.attr("class", "area")
.attr("d", area);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
});
</script>
</body>
</html>
But when i am copy pasting this code to say "temp.jsp" page its not rendering and chart is not coming.
need suggestions
I see in your title you mention flexigrid :) You should update it.
To answer your question, there are 2 possible issues:
1. Make sure that "data/json.json" is served OK (not a 404 error)
2. Having the file as .jsp might mean you need extra code for it.