In the following example I mapped histograms on letter vs frequency. Now, I want a line chart also for the same data without making much change. This means just a red line joining top of histograms. Can someone help me out?
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
//Our basic data
var data = [
{frequency:0.08, letter:"A"},
{frequency:0.11,letter:"B"},
{frequency:0.13,letter:"C"}
];
var svg = d3.select("svg");
var margin = {top: 40, bottom: 40, left: 40, right: 40};
var width = svg.attr("width") - margin.left - margin.right;
var height = svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.6);
var y = d3.scaleLinear().rangeRound([height, 0]);
//defining our main g in svg
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Looping for data bars
data.forEach(function(){
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
g
.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g
.append("g")
.call(d3.axisLeft(y).ticks(10, "%"));
g
.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return x(d.letter); })
.attr("y", function(d) { return y(d.frequency); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.frequency); });
});
</script>
</body>
</html>
HISTOGRAM:
First, get rid of that data.forEach: why do you want to paint everything 3 times?
After that, define your line generator:
var line = d3.line()
.x(function(d){ return x(d.letter) + x.bandwidth()/2})
.y(function(d){ return y(d.frequency)})
.curve(d3.curveCardinal);;
Here, x.bandwidth()/2 will put the line in the middle of the top of each bar. I'm using d3.curveCardinal, but you have other options for the curve.
Then, append the line:
g.append("path")
.datum(data)
.attr("d", line);
Here is the demo:
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
//Our basic data
var data = [
{frequency:0.08, letter:"A"},
{frequency:0.11,letter:"B"},
{frequency:0.13,letter:"C"}
];
var svg = d3.select("svg");
var margin = {top: 40, bottom: 40, left: 40, right: 40};
var width = svg.attr("width") - margin.left - margin.right;
var height = svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.6);
var y = d3.scaleLinear().rangeRound([height, 0]);
//defining our main g in svg
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Looping for data bars
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
var line = d3.line()
.x(function(d){ return x(d.letter) + x.bandwidth()/2})
.y(function(d){ return y(d.frequency)})
.curve(d3.curveCardinal);
g
.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g
.append("g")
.call(d3.axisLeft(y).ticks(10, "%"));
g
.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return x(d.letter); })
.attr("y", function(d) { return y(d.frequency); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.frequency); });
g.append("path")
.datum(data)
.attr("d", line)
.attr("stroke", "red")
.attr("fill", "none");
</script>
Related
This is my expected result
Please Click to see Image
This is the data I am using: -
year,co2,ghg
2000,15.34,34.86
2001,15.54,34.86
2002,16.53,34.06
2003,17.03,29.74
2004,17.48,31.97
2005,16.98,29.66
2006,17.62,31.52
2007,19.82,30.91
2008,17.24,29.93
2009,17.66,29.14
2010,17.31,27.13
2011,17.43,28.61
2012,17.91,28.08
2013,16.67,23.88
2014,16.8,24
2015,16.6,23.67
2016,16.04,23.8
2017,15.78,25.34
2018,15.2,24.87
When I use it in HTML like this It works completely fine
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="main.css">
<title>Stacked Bar Graph</title>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.js"></script>
</head>
<body>
<script>
var margin = {top: 10, right: 30, bottom: 20, left: 50},
width = 1000 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
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 + ")");
d3.csv("data.csv", function(data) {
var subgroups = data.columns.slice(1);
var groups = d3.map(data, function(d){return(d.year)}).keys()
var x = d3.scaleBand()
.domain(groups)
.range([0, width])
.padding([0.2])
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSize(5));
var y = d3.scaleLinear()
.domain([0, 40])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y).ticks(8));
var xSubgroup = d3.scaleBand()
.domain(subgroups)
.range([0, x.bandwidth()])
.padding([0.05])
var color = d3.scaleOrdinal()
.domain(subgroups)
.range(['#e41a1c','#377eb8','#4daf4a'])
svg.append("g")
.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) { return "translate(" + x(d.year) + ",0)"; })
.selectAll("rect")
.data(function(d) { return subgroups.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect")
.attr("x", function(d) { return xSubgroup(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", xSubgroup.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return color(d.key); });
})
</script>
</body>
</html>
But when I use it in a separate Js file like this
function GroupBar() {
// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 20, left: 50},
width = 1000 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
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 + ")");
// Parse the Data
d3.csv("src/data/co2_vs_ghg.csv", function(data) {
// List of subgroups = header of the csv files = soil condition here
var subgroups = data.columns.slice(1);
// List of groups = species here = value of the first column called group -> I show them on the X axis
var groups = d3.map(data, function(d){return(d.year)}).keys()
// Add X axis
var x = d3.scaleBand()
.domain(groups)
.range([0, width])
.padding([0.2])
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSize(5));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 40])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y).ticks(8));
// Another scale for subgroup position?
var xSubgroup = d3.scaleBand()
.domain(subgroups)
.range([0, x.bandwidth()])
.padding([0.05])
// color palette = one color per subgroup
var color = d3.scaleOrdinal()
.domain(subgroups)
.range(['#e41a1c','#377eb8','#4daf4a'])
// Show the bars
svg.append("g")
.selectAll("g")
// Enter in data = loop group per group
.data(data)
.enter()
.append("g")
.attr("transform", function(d) { return "translate(" + x(d.year) + ",0)"; })
.selectAll("rect")
.data(function(d) { return subgroups.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect")
.attr("x", function(d) { return xSubgroup(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", xSubgroup.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return color(d.key); });
});
}
window.addEventListener("load", GroupBar);
It gives me an error
shown here
I have tried some alternate methods online but none of them work I think I am missing a minute detail but cannot figure out what if anyone can double check this it would be great
I am a beginner, I am learning d3.js but I am unable to figure out on how to draw a graph or code according to the data is in json format from api.
Here is one of the things I have tried but I am unable to change it according to the new data in another api. Can anybody help me?
where do I make the changes?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>
</head>
<style>
body {
text-align: center;
margin-top: 5em;
background-color: #74b9ff;
}
h1 {
color: snow;
}
</style>
<body>
<h1>Bitcoin Prices in U.S. Dollars</h1>
<script>
var url = "https://min-api.cryptocompare.com/data/histoday?fsym=BTC&tsym=USD&limit=200&aggregate=3&e=CCCAGG";
d3.json(url).get(function(error, d) {
var data = d.Data;
data.forEach(function(d){ d.time = new Date(d.time * 1000) });
if (error) throw error;
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleTime()
.range([0, width])
var y = d3.scaleLinear()
.range([height, 0]);
var line = d3.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.close); });
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.attr("stroke-width", 2)
.attr("fill", "none")
.style("font-size",".8em");
g.append("g")
.call(d3.axisLeft(y))
.attr("stroke-width", 2)
.style("font-size",".8em")
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 20)
.attr("text-anchor", "end")
.attr("font-size", "1.2em")
.text("Price ($)")
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "#ffeaa7")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2)
.attr("d", line);
});
</script>
</body>
</html>
I get the correct output for the above mentioned code but I want to change the api to https://blockchain.info/ticker
where could I make the changes to make it work?
Because it is the convertion rate of bitcoin compared to other coins it does not make sense to plot them all in one graph. The JPY would crush all the other bars.
Why buy/sell bitcoin using different coins if they just are conversions given the current rate.
If you draw a single coin values you get the following graph
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis--x path {
display: none;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("https://blockchain.info/ticker", function(error, data) {
if (error) throw error;
data = data.EUR;
var keys = ["15m", "last", "buy", "sell"];
x.domain(keys);
y.domain([0, d3.max(keys, function(k) { return data[k]; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("price");
g.selectAll(".bar")
.data(keys)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(k) { return x(k); })
.attr("y", function(k) { return y(data[k]); })
.attr("width", x.bandwidth())
.attr("height", function(k) { return height - y(data[k]); });
});
</script>
Also not useful. Because all the variation is in the top few pixels.
What you need is a way to record the values from the JSON file over time for a particular coin and graph that like your original dataset.
Can be done by fetching the data every x minutes and then modify the graph with the enter/exit/remove data() call or just redraw the graph like this.
Place comments before the DEBUG sections for the real data. And uncomment the following line
//setInterval(getData, 5 * 60 * 1000);
Here I generate dummy data every 5 seconds for the demo.
To prevent out of memory data length is limited to 1000 samples.
Edit
It now shows the date of the sample on the x-axis.
<!DOCTYPE html>
<style>
.p15m { stroke: steelblue;}
.pbuy { stroke: red;}
.plast { stroke: green;}
.psell { stroke: orange;}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var data = [];
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var line = d3.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
function getData() {
//DEBUG
data.push( {"15m": Math.random()*100 + 100, "last": Math.random()*100 + 100, "buy": Math.random()*100 + 100, "sell": Math.random()*100 + 100, "date": new Date() } );
updateGraph();
return;
// DEBUG END
d3.json("https://blockchain.info/ticker", function(error, dataNew) {
if (error) throw error;
var d = dataNew.EUR;
d.date = new Date();
data.push();
if (data.length > 1000) data = data.shift();
updateGraph();
});
}
getData();
setTimeout(getData, 5000);
//DEBUG
setInterval(getData, 5 * 1000);
//DEBUG END
//setInterval(getData, 5 * 60 * 1000);
function updateGraph() {
if (data.length < 2) return;
svg.select("g").remove(); // clean the graph
var keys = ["15m", "last", "buy", "sell"];
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(data, d => d.date));
var flat = [];
data.map( d => keys.map(k => d[k]) ).forEach(e => { flat = flat.concat(e); });
y.domain(d3.extent(flat , function(d) { return d; }));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price (EUR)");
g.selectAll("g.key")
.data(keys)
.enter()
.append("g")
.attr("class", d => "key p" + d )
.append("path")
.datum(k => data.map( (d, i) => [d.date, d[k]]))
.attr("fill", "none")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
}
</script>
In Bellow code I am trying to use separate charts (i.e. Bar and line chart) and getting Error: attribute d: Expected number, "MNaN,116LNaN,117L…"
Tried with using different SVG elements for both charts. Give some suggestions if I am missing something.
If I commenting Bar chart code then Line chart works properly and same works with the Bar chart but both at a same time doesn't work.
<div>
<div>Simple Bar Chart</div>
<div class="simpleBar">
<svg width="960" height="500"></svg>
</div>
</div>
<div>
<div>Line Chart</div>
<div id="lineGraph"></div>
</div>
<script>
// --------------- Bar chart code starts -----------------------
var svg = d3.select(".simpleBar > svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var colours = d3.scaleOrdinal()
.range(["#6F257F", "#CA0D59"]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("dataSample.json", function(error, data) {
if (error) throw error;
x.domain(data.map(function(d) { return d.Timestamp; }));
y.domain([0, d3.max(data, function(d) { return d.DayTotal; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(5).tickFormat(function(d) { return parseInt(d) + "Rs"; }).tickSizeInner([-width]))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 1)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.attr("fill", "#5D6971")
.text("Average Expence(Rs)");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("x", function(d) { return x(d.Timestamp); })
.attr("y", function(d) { return y(d.DayTotal); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.DayTotal); })
.attr("fill", function(d) { return colours(d.Timestamp); })
.on("mousemove", function(d){
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html((d.Timestamp) + "<br>" + "Rs" + (d.DayTotal));
})
.on("mouseout", function(d){ tooltip.style("display", "none");});
});
// --------------- Line chart code starts -----------------------
// parse the Timestamp / time
var parseTime = d3.timeParse("%d-%b-%y");
// set the ranges
var xLine = d3.scaleTime().range([0, width]);
var yLine = d3.scaleLinear().range([height, 0]);
// define the line
var valueline = d3.line()
.x(function(d) { return x(d.Timestamp); })
.y(function(d) { return y(d.DayTotal); });
var svgLine = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svgLine = d3.select("#lineGraph").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 + ")");
// Get the data
d3.json("dataSample.json", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.Timestamp = parseTime(d.Timestamp);
d.DayTotal = +d.DayTotal;
});
// Scale the range of the data
xLine.domain(d3.extent(data, function(d) { return d.Timestamp; }));
yLine.domain([0, d3.max(data, function(d) { return d.DayTotal; })]);
// Add the valueline path.
svgLine.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// Add the X Axis
svgLine.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xLine).ticks(5));
// Add the Y Axis
svgLine.append("g")
.call(d3.axisLeft(yLine));
});
</script>
and bellow is the Json data format:
[
{
"DayTotal": "418",
"Timestamp": "15-Mar-18"
},{...}
]
You are supposed to use xLine, yLine in your d3 line function whereas you used the ones(x, y) that you meant for bar charts.
var valueline = d3.line()
.x(function(d) { return xLine(d.Timestamp); })
.y(function(d) { return yLine(d.DayTotal); });
Solved JSFiddle
The following snippet is basically this example https://bl.ocks.org/mbostock/4248145 but with custom data points. No matter how I scale or modify my points array, the hexagons are always at the upper left corner, though it seems that the distribution is displayed correctly.
How can I fix this?
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var points = [[1,1]]
var color = d3.scaleSequential(d3.interpolateLab("white", "#5B85AA"))
.domain([0, 3]);
var hexbin = d3.hexbin()
.radius(20)
.size([0, 3]);
var x = d3.scaleLinear()
.domain([1, 4])
.range([0, width]);
var y = d3.scaleLinear()
.domain([1, 4])
.range([height, 0]);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
g.append("g")
.attr("class", "hexagon")
.attr("clip-path", "url(#clip)")
.selectAll("path")
.data(hexbin(points))
.enter().append("path")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + 0 + "," + height + ")"; })
.transition()
.duration(1000)
.delay(function (d, i) {
return i * 10;
})
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("fill", function(d) { return color(d.length); });
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-hexbin.v0.2.min.js"></script>
<svg width="960" height="500"></svg>
That's the expected result, since your data is just:
[1, 1]
Which is a single data point next to the origin. For instance, using the same code but creating 1000 random data points from 0 to the width...
var points = d3.range(1000).map(d=>([Math.random()*width, Math.random()*width]));
... will have a different result:
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var points = d3.range(1000).map(d=>([Math.random()*width, Math.random()*width]));
var color = d3.scaleSequential(d3.interpolateLab("white", "#5B85AA"))
.domain([0, 3]);
var hexbin = d3.hexbin()
.radius(20)
.size([0, 3]);
var x = d3.scaleLinear()
.domain([1, 4])
.range([0, width]);
var y = d3.scaleLinear()
.domain([1, 4])
.range([height, 0]);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
g.append("g")
.attr("class", "hexagon")
.attr("clip-path", "url(#clip)")
.selectAll("path")
.data(hexbin(points))
.enter().append("path")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + 0 + "," + height + ")"; })
.transition()
.duration(1000)
.delay(function (d, i) {
return i * 10;
})
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("fill", function(d) { return color(d.length); });
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-hexbin.v0.2.min.js"></script>
<svg width="960" height="500"></svg>
Besides that, what you said:
No matter how I scale or modify my points array, the hexagons are always at the upper left corner.
Is not accurate. For instance, this is the same code, but using [[100,100]]. You can see the hexagon further down and to the right:
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var points = [[100,100]];
var color = d3.scaleSequential(d3.interpolateLab("white", "#5B85AA"))
.domain([0, 3]);
var hexbin = d3.hexbin()
.radius(20)
.size([0, 3]);
var x = d3.scaleLinear()
.domain([1, 4])
.range([0, width]);
var y = d3.scaleLinear()
.domain([1, 4])
.range([height, 0]);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
g.append("g")
.attr("class", "hexagon")
.attr("clip-path", "url(#clip)")
.selectAll("path")
.data(hexbin(points))
.enter().append("path")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + 0 + "," + height + ")"; })
.transition()
.duration(1000)
.delay(function (d, i) {
return i * 10;
})
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("fill", function(d) { return color(d.length); });
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-hexbin.v0.2.min.js"></script>
<svg width="960" height="500"></svg>
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>