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
Related
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>.
I have created a stacked bar chart which works fine but I wanted to filter out the bars which are of very small length so that the chart looks good. Here is my code:-
<!DOCTYPE html>
<style>
.axis .domain {
}
</style>
<svg width="3000" height="700"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 200, left: 60},
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.scaleBand()
.rangeRound([0, 700])
.paddingInner(0.55)
.align(0.5);
var y = d3.scaleLinear()
.rangeRound([height,0]);
var z = d3.scaleOrdinal()
.range(["pink","purple"]);
//.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
d3.csv("pivot2.csv", function(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i)
t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}, function(error, data) {
if (error) throw error;
var keys = data.columns.slice(1);
data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { if(d.total>2000){return d.Name;} }));
y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
z.domain(keys);
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function(d) {return z(d.key); })
.selectAll("rect")
.data(function(d) { return d;})
.enter().append("rect")
.attr("x", function(d) { return x(d.data.Name); })
.attr("y", function(d) { return y(d[1]) ; })
.attr("height",0)
.transition()
.duration(50)
.delay(function (d, i) { return i*100; })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width",20)
//.attr("width", x.bandwidth())
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(1))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
//.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.2)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
//.text("Duration");
var legend = g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(keys.slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x",1500)
//.attr("x", width - 20)
.attr("width", 19)
.attr("height",0)
.transition()
.duration(200)
.delay(function (d, i) { return i*50; })
.attr("height", 19)
.attr("fill", z);
legend.append("text")
.attr("x",1490)
.attr("font-size","20px")
//.attr("x", width - 30)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) { return d; });
});
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top + 200) + ")")
.style("text-anchor", "middle")
.style("font-size","28px")
.style("fill","green")
.text("Customer Name");
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y",-5)
.attr("x",-200)
//.attr("y", 0 - margin.left)
//.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.style("fill","green")
.style("font-size","28px")
.text("Machine Duration By Mode");
</script>
As you can see I have applied an "IF" condition
data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { if(d.total>2000){return d.Name;} }));
y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
z.domain(keys);
But the problem is that all the remaining rectangles are being appended in the bottom left corner above the "Locust Valley Central School District" as you can see in the screenshot I have provided. Please help me to remove these extra rectangles.Screenshot for extra rectangles in the stacked bar chart
Map function here:
data.map(function(d) { if(d.total>2000){return d.Name;} })
Doesn't actually change data array, it returns a new array instead, which is used to initialize x.domain only.
Then you use unchanged data to create rectangles:
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function(d) {return z(d.key); })
.selectAll("rect")
.data(function(d) { return d;})
.enter().append("rect")
And it creates redundant rectangles with empty x coordinate. To fix this, you need to filter your data first:
data = data.filter(function(d) {
if (d.total > 2000) {
return true
}
})
And then use it to set x.domain (without any conditions here):
x.domain(data.map(function(d) { return d.Name }))
The rest of the code remains the same.
I have made a scatter plot using d3 and want to connect the dots with lines, I have tried using what people have written online to connect the lines but it does not seem to be working.
function makeGraph(sampleData){
console.log(sampleData);
var vis = d3.select("#svgVisualize");
yMax = d3.max(sampleData, function (point) {return point.y;});
//step 1 : scale the data
xRange = d3.scale.ordinal().domain(sampleData.map(function (d) { return d.x; })).rangePoints([0, 700]);
yRange = d3.scale.linear().range([400, 40]).domain([0, yMax]);
//step 2: scale the axis
xAxis = d3.svg.axis().scale(xRange);
yAxis = d3.svg.axis().scale(yRange).orient("left");
//Step3: append the x and y axis
vis.append('svg:g')
.call(xAxis)
.attr("transform", "translate(90,400)")
.append("text")
.text("Build Model")
.attr("y", 70)
.attr("x", 150);
vis.append('svg:g')
.call(yAxis)
.attr("transform", "translate(90,0)")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -80)
.attr("x", -130)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Number of Users");
var circles = vis.selectAll("circle").data(sampleData);
circles
.enter()
.insert("circle")
.attr("cx", function (d) {return xRange(d.x);})
.attr("cy", function (d) { return yRange(d.y); })
.attr("r", 4)
.attr("transform", "translate(90,0)")
.style("fill", "blue");
var lineFunction = vis.line()
.x(function (d) {
return d.x;
})
.y(function (d) {
return d.y;
})
.interpolate("linear");
vis.append("path")
.attr("d", lineFunction(sampleData))
.style("stroke-width", 0.5)
.style("stroke", "rgb(6,120,155)")
.style("fill", "none")
.on("mouseover", function () {
d3.select(this)
.style("stroke", "orange");
})
.on("mouseout", function () {
d3.select(this)
.style("stroke", "rgb(6,120,155)");
});
}
If anyone could help that would be great, I'm still new to d3
Your example is working fine, I had to do the following
Change the line that creates a path generator to d3.svg.line (just like Gerardo's answer)
Create a group for your circles and the path so that you don't have to add .attr("transform", "translate(90,0)") to both of them
Use a data join for the path (although it's not required)
function makeGraph(sampleData) {
var svg = d3.select('#svgVisualize')
yMax = d3.max(sampleData, function(point) {
return point.y;
});
xRange = d3.scale.ordinal().domain(sampleData.map(function(d) {
return d.x;
})).rangePoints([0, 700]);
yRange = d3.scale.linear().range([400, 40]).domain([0, yMax]);
//step 2: scale the axis
xAxis = d3.svg.axis().scale(xRange);
yAxis = d3.svg.axis().scale(yRange).orient("left");
//Step3: append the x and y axis
svg.append('svg:g')
.call(xAxis)
.attr("transform", "translate(90,400)")
.append("text")
.text("Build Model")
.attr("y", 70)
.attr("x", 150);
svg.append('svg:g')
.call(yAxis)
.attr("transform", "translate(90,0)")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -80)
.attr("x", -130)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Number of Users");
// data
var g = svg.append('g')
.attr('class', 'data')
.attr("transform", "translate(90,0)")
var circles = g.selectAll("circle").data(sampleData);
circles
.enter()
.insert("circle")
.attr("cx", function(d) {
return xRange(d.x);
})
.attr("cy", function(d) {
return yRange(d.y);
})
.attr("r", 4)
.style("fill", "blue");
var lineFunction = d3.svg.line()
.x(function(d) { return xRange(d.x); })
.y(function(d) { return yRange(d.y); })
.interpolate("linear");
var path = g.selectAll('path').data([sampleData])
.enter()
.append('path')
.attr("d", lineFunction)
.style("stroke-width", 0.5)
.style("stroke", "rgb(6,120,155)")
.style("fill", "none")
.on("mouseover", function() {
d3.select(this)
.style("stroke", "orange");
})
.on("mouseout", function() {
d3.select(this)
.style("stroke", "rgb(6,120,155)");
});
}
makeGraph([
{x: 0, y: 100},
{x: 100, y: 100},
{x: 200, y: 200},
{x: 300, y: 100}
])
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id="svgVisualize" width="900" height="500" style="position: relative; left: 2%;"></svg>
The line function has to be like this:
var lineFunction = d3.svg.line()
.x(function(d) { return xRange(d.x); })
.y(function(d) { return yRange(d.y); })
.interpolate("linear");
I've got an update button working that loads new .csv data into a D3.s stacked bar chart. My problem is that on update it seems to be using both data sets instead of just the new one. I think this is related to some confusion I have about update and selectAll, but I haven't been able to figure out how to replace instead of append. Here's my current code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
</style>
<body>
<!-- <label><input type="checkbox"> Sort values</label> -->
<div id="option">
<input name="updateButton"
type="button"
value="Update"
onclick="updateData()" />
</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 200, left: 40},
width = 960 - margin.left - margin.right,
height = 650 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
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("FinalData4.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; }));
data.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
//data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { return d.State; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
// x-axis label
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
// y-axis label
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("USD in Millions");
// adds bars
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
// set up the legend
var legend = svg.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
// ** Update data section (Called from the onclick)
function updateData() {
// Get the data again
d3.csv("FinalData2015.csv", function(error, data2) {
color.domain(d3.keys(data2[0]).filter(function(key) { return key !== "State"; }));
data2.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
x.domain(data2.map(function(d) { return d.State; }));
y.domain([0, d3.max(data2, function(d) { return d.total; })]);
var tran = d3.select("body").transition();
// change x-axis label
tran.select(".x.axis")
.duration(1000)
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
// change the y-axis label
tran.select(".y.axis")
.duration(1000)
.call(yAxis);
// adds bars
var state = svg.selectAll(".State")
.data(data2)
.enter().append("g")
//.transition()
//.duration(1000)
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
var test = state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.transition()
.duration(1000)
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
});
}
</script>
</body>
UPDATE: I changed the functionUpdateData() code to what I have currently because I realized it had some junk in there. New code has working transitions for x and y axis labels, but still the same problem described above with the actual bars.
It is because you are not using update and exiting joins in the update function (read this article carefully http://bost.ocks.org/mike/join/). As far I understand, you want to update the existing csv data with the new one, for that you need to update the current set of data. I'll give you an example with a simpler code:
function updateData() {
//this is the new data that you are binding to some circles on the canvas
var circle = svg.selectAll("circle").data([200, 100, 50,10,5]);
//if there is new data, you get those new circles with the enter() method
var circleEnter = circle.enter().append("circle");
circleEnter.attr("cy", 60);
circleEnter.attr("cx", function(d, i) { return i * 100 + 30; });
circleEnter.attr("r", function(d) { return Math.sqrt(d); });
//------THIS IS THE PART YOU ARE MISSING------
//but the circles that already exist need an update. (check there is no enter() method here)
circle.attr("r", function(d) { return Math.sqrt(d); });
//also you need to remove the circles that don´t have a data binding.
circle.exit().remove();
}
so I think that in your code you have to write something like:
var state = svg.selectAll(".state").data(data);
//the update data
state.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
I'm trying to have a different graph be shown when I click on a different polygon. But the function executes when the page loads, even though I defined it as .onmousedown.
This is the function
function iscrtaj(data, arg) {
alert(arg);
var barwidth = 13;
var w = 700;
var h = (barwidth + 10) * data.length;
var xscale = d3.scale.linear()
.domain([0, d3.max(data, function (d) {
return d.bodovi;
})])
.rangeRound([0, 280]);
var yscale = d3.scale.linear()
.domain([-1, data.length])
.range([0, h]);
var bargraph = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", 870)
.attr("y", function (d, i) {
return yscale(i);
})
.attr("height", barwidth)
.attr("width", width).transition().duration(1000)
.attr("width", function (d) {
return xscale(d.bodovi);
})
.attr("fill", "blue");
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("x", function (d) {
return xscale(d.bodovi) + 910;
})
.attr("y", function (d, i) {
return yscale(i);
})
.attr("dx", barwidth / 2)
.attr("dy", "0.8em")
.attr("text-anchor", "end")
.attr("style", "font-weight: bold; font-size: 11; font-family: Helvetica, sans-serif")
.text(function (d) {
return d.bodovi + " %";
})
.style("fill", "black");
svg.selectAll("text.xaxis")
.data(data)
.enter()
.append("text")
.attr("x", 830)
.attr("y", function (d, i) {
return yscale(i) + barwidth - 2;
})
.attr("dx", -barwidth / 2)
.attr("text-anchor", "end")
.attr("style", "font-size: 12; font-family: Helvetica, sans-serif")
.text(function (d) {
return d.stranka;
})
.attr("transform", "translate(20, 0)")
.attr("class", "xaxis")
.attr("fill", "black");
}
And the rest of the code on JSFiddle. (The pop-up is here just to make sure the function is taking the right arguments.)
http://jsfiddle.net/Y27NG/
.on("mousedown",iscrtaj(data1, 5))
calls the function instantly and the result/return if a function will be bound to the event
.on("mousedown",function(){iscrtaj(data1, 5);})
using a anonymous function like this might fix your problem, the scope and lifetime of data can have impact on this.