D3 Data Visualisation aligning tick labels and rect - javascript

I am new to D3 library for Data Visualisation.
I am trying to create a vertical legend.
And below is my implementation.
We can see there is huge gap between the column of rects(are on extreme right) and vertical ticks.
I guess, I am missing something in g.call because of my limited knowledge.
Can someone please, what mistake I am doing ?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
.counties {
fill: none;
}
.states {
fill: none;
stroke: #fff;
stroke-linejoin: round;
}
</style>
<svg width="1260" height="600"></svg>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
</head>
<body>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var poverty = d3.map();
var path = d3.geoPath();
var x = d3.scaleLinear()
//.domain([1, 10])
.domain([1, 10])
.rangeRound([600, 860]);
//console.log("x:==>", x);
var y = d3.scaleLinear()
//.domain([1, 10])
.domain([1, 10])
.rangeRound([15, 160]);
var color = d3.scaleThreshold()
//.domain(d3.range(2, 10))
.domain(d3.range(2, 10))
.range(d3.schemeBlues[9]);
var g = svg.append("g")
.attr("class", "key")
//.attr("transform", "translate(0,40)");
.attr("transform", "translate(350,40)");
g.selectAll("rect")
.data(color.range().map(function(d) {
d = color.invertExtent(d);
if (d[0] == null) d[0] = x.domain()[0];
if (d[1] == null) d[1] = x.domain()[1];
return d;
}))
.enter().append("rect")
/*.attr("height", 8)
.attr("x", function(d) { return x(d[0]); })
.attr("width", function(d) { return x(d[1]) - x(d[0]); })
.attr("fill", function(d) { return color(d[0]); })*/
.attr("height", 15)
.attr("x", 600)
.attr("y", function(d) { return y(d[0]); })
.attr("width", function(d) { return x(d[1]) - x(d[0]); })
.attr("fill", function(d) { return color(d[0]); }) ;
g.append("text")
.attr("class", "caption")
/*.attr("x", x.range()[0])
.attr("y", -6)*/
.attr("x",x.range()[0])
.attr("y", -6)
.attr("fill", "#000")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("Poverty rate");
g.call(d3.axisRight(y)
//.tickSize(13)
.tickFormat(function(x, i) { return i ? 2*x : 2*x + "%"; })
.tickValues(color.domain()))
.select(".domain")
.remove();
var promises = [
d3.json("https://snippetnuggets.com/TnD/us.json"),
d3.csv("https://snippetnuggets.com/TnD/county_poverty.csv", function(d) { poverty.set(d.id, +d.rate); console.log(d); })
]
Promise.all(promises).then(ready)
function ready([us]) {
svg.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter().append("path")
.attr("fill", function(d) { return color(d.rate = poverty.get(d.id)); })
.attr("d", path)
.append("title")
.text(function(d) { return d.rate + "%"; });
svg.append("path")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("class", "states")
.attr("d", path);
}
</script>
</body>
</html>

Your large gap between your axis ticks/labels and legend rects is there because you set x to 600 on your rects (.attr("x", 600)), which means you position the rects 600 pixels to the right, relative to the rects' parent container.
What happens is that first you append a g element, which you translate 350 pixels horizontally to the right (and 40 vertically downwards). When you later append rects to this g element, the rects are positioned relative to the g elements position. Therefore, setting the x attribute on the rects to 600 means in effect that you position the rects 950 pixels (350 + 600) to the right of the left side of the SVG.
To fix this, you should lower your x attribute on the rects. Negative values are valid too.
Check the reference for SVG rect elements here: SVG

Related

D3 line chart data not aligned proper on x axis [duplicate]

My data points and the values in the scaleBand y axis are not aligned. I am not able to align them properly, when I read the documentation, saw that by default the alignment is 0.5 and that's why my data points are plotted between the two points in the axis. But I tried to override the alignment my giving the alignment as 0, but there seems to be no change.
The following is my code.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>D3 v4 - linechart</title>
<style>
#graph {
width: 900px;
height: 500px;
}
.tick line {
stroke-dasharray: 2 2 ;
stroke: #ccc;
}
.y path{
fill: none;
stroke: none;
}
</style>
</head>
<body>
<div id="graph"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.1.1/d3.min.js"></script>
<script>
!(function(){
"use strict"
var width,height
var chartWidth, chartHeight
var margin
var svg = d3.select("#graph").append("svg")
var axisLayer = svg.append("g").classed("axisLayer", true)
var chartLayer = svg.append("g").classed("chartLayer", true)
var xScale = d3.scaleLinear()
var yScale = d3.scaleBand()
var align = 0
//d3.tsv("data1.tsv", cast, main)
d3.json("http://localhost/d32.json",cast)
//データの方変換
function cast(data) {
console.log("got it");
data.forEach(function(data) {
console.log(data.Letter);
data.Letter = data.Letter;
data.Freq = +data.Freq;
});
main(data);
}
function main(data) {
console.log("in main");
setSize(data)
drawAxis()
drawChart(data)
}
function setSize(data) {
width = document.querySelector("#graph").clientWidth
height = document.querySelector("#graph").clientHeight
margin = {top:40, left:100, bottom:40, right:0 }
chartWidth = width - (margin.left+margin.right+8)
chartHeight = height - (margin.top+margin.bottom)
svg.attr("width", width).attr("height", height)
axisLayer.attr("width", width).attr("height", height)
chartLayer
.attr("width", chartWidth)
.attr("height", chartHeight)
.attr("transform", "translate("+[margin.left, margin.top]+")")
xScale.domain([0, d3.max(data, function(d) { return d.Freq; })]).range([0,chartWidth])
yScale.domain(data.map(function(d) { return d.Letter; })).range([chartHeight, 0]).align(1)
}
function drawChart(data) {
console.log("in drawChart");
var t = d3.transition()
.duration(8000)
.ease(d3.easeLinear)
.on("start", function(d){ console.log("transiton start") })
.on("end", function(d){ console.log("transiton end") })
var lineGen = d3.line()
.x(function(d) { return xScale(d.Freq) })
.y(function(d) { return yScale(d.Letter) })
.curve(d3.curveStepAfter)
var line = chartLayer.selectAll(".line")
.data([data])
line.enter().append("path").classed("line", true)
.merge(line)
.attr("d", lineGen)
.attr("fill", "none")
.attr("stroke", "blue")
.attr("stroke-width","2px")
.attr("stroke-dasharray", function(d){ return this.getTotalLength() })
.attr("stroke-dashoffset", function(d){ return this.getTotalLength() })
chartLayer.selectAll(".line").transition(t)
.attr("stroke-dashoffset", 0)
chartLayer.selectAll("circle").classed("circle",true)
.data(data)
.enter().append("circle")
.attr("class", "circle")
.attr("fill","none")
.attr("stroke","black")
.attr("cx", function(d) { return xScale(d.Freq); })
.attr("cy", function(d) { return yScale(d.Letter); })
.attr("r", 4);
chartLayer.selectAll(".logo").transition(t)
.attr("stroke-dashoffset", 0)
}
function drawAxis(){
var yAxis = d3.axisLeft(yScale)
.tickSizeInner(-chartWidth)
axisLayer.append("g")
.attr("transform", "translate("+[margin.left, margin.top]+")")
.attr("class", "axis y")
.call(yAxis);
var xAxis = d3.axisBottom(xScale)
axisLayer.append("g")
.attr("class", "axis x")
.attr("transform", "translate("+[margin.left, chartHeight+margin.top]+")")
.call(xAxis);
}
}());
</script>
</body>
</html>
The output is shown here:
The band scale is the wrong tool in this situation. The main reason is that a band scale has an associated bandwidth.
You can tweak the paddingInner and paddingOuter values of the band scale to give you the expected result. However, the easiest solution is using a point scale instead. Point scales:
...are a variant of band scales with the bandwidth fixed to zero. (emphasis mine)
So, it should be:
var yScale = d3.scalePoint()

How to put different D3.js scripts on the same page without overlapping?

I am using D3.js to showcase my data. However, I am unable to get two different objects to not overlap. For example, the code below shows a line graph and a bar graph. I am using code from https://github.com/d3/d3/wiki/Gallery for this example to show my issue. The line graph code is from https://bl.ocks.org/mbostock/3883245. The bar graph code is from https://bl.ocks.org/mbostock/3885304. I tried using as shown in this example http://www.d3noob.org/2013/07/arranging-more-than-one-d3js-graph-on.html, but it did not work. I also made sure they both used the same version of d3.js. The data is from two tsv files that are on the links above for the line and bar graph. Any help would be appreciated!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis--x path {
display: none;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="lineg"></div>
<div id="barg"></div>
<lineg width="960" height="500"></lineg>
<barg width="960" height="500"></barg>
<script>
var svga = d3.select("#lineg"),
margina = {top: 20, right: 20, bottom: 30, left: 40},
widtha = +svga.attr("width") - margina.left - margina.right,
heighta = +svga.attr("height") - margina.top - margina.bottom;
var xa = d3.scaleBand().rangeRound([0, widtha]).padding(0.1),
ya = d3.scaleLinear().rangeRound([heighta, 0]);
var ga = svga.append("g")
.attr("transform", "translate(" + margina.left + "," + margina.top + ")");
d3.tsv("bardata.tsv", function(d) {
d.frequency = +d.frequency;
return d;
}, function(error, data) {
if (error) throw error;
xa.domain(data.map(function(d) { return d.letter; }));
ya.domain([0, d3.max(data, function(d) { return d.frequency; })]);
ga.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + heighta + ")")
.call(d3.axisBottom(xa));
ga.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(ya).ticks(10, "%"))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Frequency");
ga.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xa(d.letter); })
.attr("y", function(d) { return ya(d.frequency); })
.attr("width", xa.bandwidth())
.attr("height", function(d) { return heighta - ya(d.frequency); });
});
</script>
<script>
var svgb = d3.select("barg"),
marginb = {top: 20, right: 20, bottom: 30, left: 50},
widthb = +svgb.attr("width") - marginb.left - marginb.right,
heightb = +svgb.attr("height") - marginb.top - marginb.bottom,
gb = svgb.append("g").attr("transform", "translate(" + marginb.left + "," + marginb.top + ")");
var parseTime = d3.timeParse("%d-%b-%y");
var xb = d3.scaleTime()
.rangeRound([0, widthb]);
var yb = d3.scaleLinear()
.rangeRound([heightb, 0]);
var line = d3.line()
.x(function(d) { return xb(d.date); })
.y(function(d) { return yb(d.close); });
d3.tsv("linedata.tsv", function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
return d;
}, function(error, data) {
if (error) throw error;
xb.domain(d3.extent(data, function(d) { return d.date; }));
yb.domain(d3.extent(data, function(d) { return d.close; }));
gb.append("g")
.attr("transform", "translate(0," + heightb + ")")
.call(d3.axisBottom(xb))
.select(".domain")
.remove();
gb.append("g")
.call(d3.axisLeft(yb))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
gb.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
});
</script>
In your code barg and lineg are just divs:
<div id="lineg"></div>
<div id="barg"></div>
Instead of that, they should be SVG elements, like this:
<svg id="barg" width="960" height="500"></svg>
<svg id="lineg" width="960" height="500"></svg>
Or, alternatively, append an SVG in your selection:
var svga = d3.select("#lineg").append("svg");
var svgb = d3.select("#barg").append("svg");
However, in that case, you cannot use the getters to get the width and height of the SVGs.
Finally, there is no HTML tag named <lineg> or <barg>.

D3.js Combining Candlestick Chart with Line Graph

I am trying to plot a moving average on top of a candlestick chart but the "path" is not appearing completely on the svg canvas that I created.
I have tried looking at several post on how to put a line on top of a bar graph (because I figured it would be similar) but it has not worked.
A couple of the examples and post I have looked at are below:
https://bl.ocks.org/nanu146/f48ffc5ec10270f55c9e1fb3da8b38f0
d3.js How to add lines to a bar chart
D3.js combining bar and line chart
I have all the data in a array.
I am using the same x "scale" for both the candle stick graph and the moving average (line). I have tried using the same y "scale" for both the line and the candlestick but it did not work. Therefore i tried creating 2 scales for y, one for the moving average and one for the candlestick chart. That is what Im doing in my code below.
<script src="https://d3js.org/d3.v4.min.js"></script>
<script type="text/javascript">
var twoHundredDayCandleStickChart = [];
//pulling from 2 properties so must do this way
#for (int i = 0; i != 100; ++i)
{
#:twoHundredDayCandleStickChart.push({date: '#Model.DailyTimeSeriesData.Data.ElementAt(i).Key', high: '#Model.DailyTimeSeriesData.Data.ElementAt(i).Value.high', low: '#Model.DailyTimeSeriesData.Data.ElementAt(i).Value.low', open: '#Model.DailyTimeSeriesData.Data.ElementAt(i).Value.open', close: '#Model.DailyTimeSeriesData.Data.ElementAt(i).Value.close', sma: '#Model.TwoHundredDaySma.Data.ElementAt(i).Value.Sma'})
}
console.log(twoHundredDayCandleStickChart);
var width = 900;
var height = 500;
var margin = 50;
function min(a, b) { return a < b ? a : b; }
function max(a, b) { return a > b ? a : b; }
//y for the candlestick
var y = d3.scaleLinear().range([height - margin, margin]);
var x = d3.scaleTime().range([margin, width - margin]);
//y for the line
var y1 = d3.scaleLinear().range([height - margin, margin]);
//line for the sma
var line1 = d3.line()
.x(function (d) { return x(d["date"]); })
.y(function (d) { return y(d["sma"]); });
function buildChart(data) {
data.forEach(function (d) {
d.date = new Date(d.date);
d.high = +d.high;
d.low = +d.low;
d.open = +d.open;
d.close = +d.close;
d.sma = +d.sma;
});
var chart = d3.select("#twoHundredDaySmaWithCandleStickChart")
.append("svg")
.attr("class", "chart")
.attr("width", width)
.attr("height", height);
//map is going to create an array with all the lows and then d3.min will take the min out of all of them
y.domain([d3.min(data.map(function (x) { return x["low"]; })), d3.max(data.map(function (x) { return x["high"]; }))])
x.domain(d3.extent(data, function (d) { return d["date"]; }))
y1.domain(d3.extent(68, d3.max(data, function (d) { return d["sma"]; })))
//grid for the chart; x and y axis
chart.selectAll("line.x")
.data(x.ticks(10))
.enter().append("line")
.attr("class", "x")
//.text(String)
.attr("x1", x)
.attr("x2", x)
.attr("y1", margin)
.attr("y2", height - margin)
.attr("stroke", "#ccc");
chart.selectAll("line.y")
.data(y.ticks(10))
.enter().append("line")
.attr("class", "y")
.attr("x1", margin)
.attr("x2", width - margin)
.attr("y1", y)
.attr("y2", y)
.attr("stroke", "#ccc");
//x axis
chart.append("g")
.attr("transform", "translate(0," + 450 + ")") //need to change this 450 to a variable- it is how far down the axis will go
.attr("class", "xrule") // give it a class so it can be used to select only xaxis labels or change color
//the x axis
.call(d3.axisBottom(x))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function (d) {
return "rotate(-65)"
});
//the y axis
chart.selectAll("text.yrule")
.data(y.ticks(10))
.enter()
.append("text")
.attr("class", "yrule")
.attr("x", 0)
.attr("y", y)
.attr("dy", 0)
.attr("dx", 20)
.attr("text-anchor", "middle")
.text(String);
//add rectangles- if open higher then close then red
chart.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function (d) { return x(d["date"]); })
.attr("y", function (d) { return y(max(d["open"], d["close"])); })
.attr("height", function (d) { return y(min(d["open"], d["close"])) - y(max(d["open"], d["close"])); })
.attr("width", function (d) { return 0.5 * (width - 2 * margin) / data.length; })
.attr("fill", function (d) { return d["open"] > d["close"] ? "red" : "green"; });
//add a stem to the rectangle
chart.selectAll("line.stem")
.data(data)
.enter().append("line")
.attr("class", "stem")
.attr("x1", function (d) { return x(d["date"]) + 0.25 * (width - 2 * margin) / data.length; })
.attr("x2", function (d) { return x(d["date"]) + 0.25 * (width - 2 * margin) / data.length; })
.attr("y1", function (d) { return y(d["high"]); })
.attr("y2", function (d) { return y(d["low"]); })
.attr("stroke", function (d) { return d.open > d.close ? "red" : "green"; });
chart.append("path")
.data([data])
.attr("d", line1)
.attr("class", "line")
.style("stroke", "white")
.attr("fill", "none")
.attr("stroke-width", 2);
}
buildChart(twoHundredDayCandleStickChart);
</script>
The above code is giving me the image below:
The problem in the chart above was my scales! I was taking the domain for the candle stick data but the line data was a lot lower of a min. Therefore the whole graph was not showing up on the scale because the min of the domain had to be adjusted. MANY hours wasted but hopefully this can save someone else time!
d3.select("#twoHundredDaySmaWithCandleStickChart")
Try to change the above code like below
d3.select("svg") or give the div Id

d3js does not enter into line function

I would like to draw a line by d3 with below codes.(http://jsfiddle.net/totaljj/L9n7qnv1)
It draws x,y-axis, but does not enter into the line function when appending d attribute.
You can debug on line number 104 to see that the code does not enter into the line function.
Any help would be appreciated.
<!DOCTYPE html>
<html>
<style>
.axis--x path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
<body>
<!-- Page Content -->
<div>
<svg width="430" height="250"></svg>
</div>
<section>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var data=
'{"recordsFiltered":5,"raCounts":[{"name":"comp_name","values":[{"date_":"2016","actual":170.0,"DT_RowId":"row_null"},{"date_":"2015","actual":198.0,"DT_RowId":"row_null"},{"date_":"2015","actual":149.0,"DT_RowId":"row_null"},{"date_":"2014","actual":197.0,"DT_RowId":"row_null"},{"date_":"2014","actual":146.0,"DT_RowId":"row_null"}],"DT_RowId":"row_null"}],"draw":null,"recordsTotal":5}';
var d = JSON.parse(data);
draw(d.raCounts);
function draw(data){
//svg
var svg = d3.select("svg"),
margin = {top: 100, right: 80, 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 + ")");
//time
var parseTime = d3.timeParse("%Y%m%d");
//domain
// var x = d3.scaleTime().range([0, width]),
var x = d3.scaleLinear().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
z = d3.scaleOrdinal(d3.schemeCategory10);
//line
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) {
return x(d.date_); })
.y(function(d) {
return y(d.actual); });
//domain
x.domain(d3.extent(data[0].values, function(d) {
return d.date_; }));
y.domain([
d3.min(data, function(c) {
return d3.min(c.values, function(d) {
return d.actual; }); }),
d3.max(data, function(c) {
return d3.max(c.values, function(d) {
return d.actual; }); })
]);
z.domain(data.map(function(c) {
return c.DT_RowId; }));
//axis
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("fill", "#000")
.text("count");
var ra = g.selectAll(".ra")
.data(data)
.enter().append("g")
.attr("class", "ra");
//ra line
ra.append("path")
.attr("class", "line")
.attr("d",
function(d) { return
line(d.values); })
.style("stroke-dasharray", ("1","1"));
}
</script>
</section>
</body>
</html>
JavaScript doesn't always require semicolons at the end of a line. It will automatically insert them in certain situations, and the place where you call your line function is one of them:
.attr("d",
function(d) { return
line(d.values); })
The fix is therefore to remove the newline after return:
.attr("d",
function(d) { return line(d.values); })
I think both Gerardo Furtado's answer as well as Luke Woodward's answer have good points, but both circumvent the fact, that OP's solution is somewhat off the beaten track. To make full use of the data binding the typical approach would be something like the following:
//ra line
ra.selectAll("path.line")
.data(function(d) { return [d.values]; })
.enter().append("path")
.attr("class", "line")
.attr("d", line)
Passing just the line generator function get rids of the automatical semicolon insertion after the return statement. On the other hand, doing the data binding for the path.line element still allows for multiple lines drawn by the same statement.
Have a look at the following snippet for a working example:
var data =
'{"recordsFiltered":5,"raCounts":[{"name":"comp_name","values":[{"date_":"2016","actual":170.0,"DT_RowId":"row_null"},{"date_":"2015","actual":198.0,"DT_RowId":"row_null"},{"date_":"2015","actual":149.0,"DT_RowId":"row_null"},{"date_":"2014","actual":197.0,"DT_RowId":"row_null"},{"date_":"2014","actual":146.0,"DT_RowId":"row_null"}],"DT_RowId":"row_null"}],"draw":null,"recordsTotal":5}';
data = JSON.parse(data).raCounts;
//svg
var svg = d3.select("svg"),
margin = {
top: 100,
right: 80,
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 + ")");
//time
var parseTime = d3.timeParse("%Y%m%d");
//domain
// var x = d3.scaleTime().range([0, width]),
var x = d3.scaleLinear().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
z = d3.scaleOrdinal(d3.schemeCategory10);
//line
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) {
return x(d.date_);
})
.y(function(d) {
return y(d.actual);
});
//domain
x.domain(d3.extent(data[0].values, function(d) {
return d.date_;
}));
y.domain([
d3.min(data, function(c) {
return d3.min(c.values, function(d) {
return d.actual;
});
}),
d3.max(data, function(c) {
return d3.max(c.values, function(d) {
return d.actual;
});
})
]);
z.domain(data.map(function(c) {
return c.DT_RowId;
}));
//axis
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("fill", "#000")
.text("count");
var ra = g.selectAll(".ra")
.data(data)
.enter().append("g")
.attr("class", "ra");
//ra line
ra.selectAll("path.line")
.data(function(d) { return [d.values]; })
.enter().append("path")
.attr("class", "line")
.attr("d", line)
.style("stroke-dasharray", ("1", "1"));
<style> .axis--x path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
<script src="https://d3js.org/d3.v4.js"></script>
<svg width="430" height="250">
</svg>
You're not passing the correct data to your line generator. It should be:
ra.append("path")
.attr("class", "line")
.attr("d", line(data[0].values))
.style("stroke-dasharray", ("1", "1"));
Here is your updated fiddle: http://jsfiddle.net/67zs8ph7/
PS: this will plot just one line (see comment below)

How to update text in heatmap using D3?

I've created a D3 heatmap based on this example, and have written an update function for it. Here's the relevant code for the base heatmap:
<!DOCTYPE html>
<html lange = "en">
<head>
<meta charset="UTF-8">
<title>Heatmap</title>
<script type ="text/javascript" src="d3/d3.v3.js"></script>
<script type ="text/javascript" src="updateHeatmap.js"> </script>
<style type ="text/css">
.btn {
display: inline;
}
rect.bordered {
stroke: #E6E6E6;
stroke-width:2px;
}
text.mono {
font-size: 9pt;
font-family: Consolas, courier;
fill: #aaa;
}
text.axis-workweek {
fill: #000;
}
text.axis-worktime {
fill: #000;
}</style>
</head>
<body>
<div id="n1" class="btn">
<input name="updateButton"
type="button"
value="1"
/>
</div>
<div id="n2" class="btn">
<input name="updateButton"
type="button"
value="2"
/>
</div>
<div id="chart"></div>
<script type="text/javascript">
var margin = {top:50, right:0, bottom:100, left:30},
width=960-margin.left-margin.right,
height=430-margin.top-margin.bottom,
gridSize=Math.floor(width/24),
legendElementWidth=gridSize*2.665,
buckets = 10,
colors = ["#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4","#4eb3d3","#2b8cbe","#0868ac","#084081"],
days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
times = ["12am", "1am", "2am", "3am", "4am", "5am", "6am", "7am", "8am", "9am", "10am", "11am", "12am", "1pm", "2pm", "3pm", "4pm", "5pm", "6pm", "7pm", "8pm", "9pm", "10pm", "11pm"];
var heatmap;
var legend;
var svg = d3.select("#chart").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("1.csv", function(d){
return {
day:+d.day2,
hour:+d.hour,
value:+d.per_day_per_hour
};
},
function(error, data){
console.log(data);
var colorScale = d3.scale.quantile()
.domain([0, (d3.max(data, function(d){return d.value;})/2), d3.max(data, function(d){return d.value;})])
.range(colors);
var dayLabels = svg.selectAll(".dayLabel")
.data(days)
.enter().append("text")
.text(function (d) {return d; })
.attr("y", function (d, i){ return i*gridSize;})
.style("text-anchor", "end")
.attr("transform", "translate(-6," + gridSize/1.5+")")
.attr("class", function(d, i) { return ((i>=0 && i<=4) ? "dayLabel mono axis axis-workweek": "dayLabel mono axis"); });
var timeLabels = svg.selectAll(".timeLabel")
.data(times)
.enter().append("text")
.text(function(d){return d;})
.attr("x", function(d,i) {return i * gridSize;})
.attr("y",0)
.style("text-anchor", "middle")
.attr("transform", "translate(" + gridSize/2+", -6)")
.attr("class", function(d, i) { return ((i>=9 && i<= 17) ? "timeLabel mono axis axis-worktime": "timeLabel mono axis"); });
var heatMap = svg.selectAll(".hour")
.data(data)
.enter().append("rect")
.attr("x", function(d) {return (d.hour) * gridSize;})
.attr("y", function(d) {return (d.day) * gridSize;})
.attr("rx", 4)
.attr("ry", 4)
.attr("class", "hour bordered")
.attr("width", gridSize)
.attr("height", gridSize)
.style("fill", colors[0]);
heatMap.transition().duration(1000)
.style("fill", function(d){ return colorScale(d.value);});
heatMap.append("title").text(function(d) {return d.value;});
var legend = svg.selectAll(".legend")
.data([0].concat(colorScale.quantiles()), function(d) {return d;})
.enter().append("g")
.attr("class", "legend");
legend.append("rect")
.attr("x", function(d, i){ return legendElementWidth * i;})
.attr("y", height)
.attr("width", legendElementWidth)
.attr("height", gridSize/2)
.style("fill", function(d, i) {return colors[i]; });
legend.append("text")
.attr("class", "mono")
.text(function(d) {return "≥ "+d.toString().substr(0,4);})
.attr("x", function(d, i){ return legendElementWidth *i;})
.attr("y", height+ gridSize);
d3.select("#n1")
.on("click", function() {
updateHeatmap("1_1.csv");
});
d3.select("#n2")
.on("click", function() {
updateHeatmap("1_2.csv");
});
;
}
);
</script>
<script>
</script>
</body>
</html>
The code above is essentially the same as that in the fiddle I've linked to up top, except that it includes 2 buttons, and has the legend, svg, and heatmap variables declared globally.
Here's the meat of my question, which has to do with the update function I created to load in two new CSVs:
function updateHeatmap(x){
svg.selectAll(".legend").attr("opacity", 0);
d3.csv(x, function(d){
return {
day:+d.day2,
hour:+d.hour,
value:+d.per_day_per_hour
};
},
function(error, data){
colorScale = d3.scale.quantile()
.domain([0, (d3.max(data, function(d){return d.value;})/2), d3.max(data, function(d){return d.value;})])
.range(colors);
var heatMap = svg.selectAll(".hour")
.data(data)
.transition().duration(1000)
.style("fill", function(d){ return colorScale(d.value);});
heatMap.selectAll("title").text(function(d) {return d.value;});
var legend = svg.selectAll(".legend")
.data([0].concat(colorScale.quantiles()), function(d) {return d;})
.enter().append("g")
.attr("class", "legend");
legend.append("rect")
.attr("x", function(d, i){ return legendElementWidth * i;})
.attr("y", height)
.attr("width", legendElementWidth)
.attr("height", gridSize/2)
.style("fill", function(d, i) {return colors[i]; });
legend.append("text")
.attr("class", "mono")
.text(function(d) {return "≥ "+d.toString().substr(0,4);})
.attr("x", function(d, i){ return legendElementWidth *i;})
.attr("y", height+ gridSize);
}
)
}
I've managed to update the color of the heatmap squares (huzzah), but can't get the legend that sits below the heatmap to cooperate, and can't get the values underlying each square to display on hover like I did on initial loading. I get a feeling that it's because my JS is pretty flaky (as is, let's face it, my D3), but can't be sure — I think it may have to do with my screwing up the appropriate syntax for selecting the text element (i.e., I'm unsure of how to do it the right way).
To sum up: legend in this heatmap (here's the gist block) isn't updating properly, and the on-hover values for each of the squares don't appear on update. Yikes. Any suggestions?
I've updated my example to allow switching between datasets - this should help.
http://bl.ocks.org/tjdecke/5558084

Categories