Using d3, I created a bar graph that displays the text value of each bar on it. I am toggling between two different data sets through a click event on a button. The data sets change successfully on mousedown, i.e. the bar graphs change in size as they should, but I am unable to change the text labels on the bars. My redrawText function does not do anything, and calling my drawText function again just redraws the data on top of the previous label (as one would expect). I am looking for a way to remove the old label and redraw the label reflecting the new data inside my removeText function.
Here is my drawText function, which is called initially to create the label. 'datachoose' is the name of the variable that is selected to graph the proper data set.
function drawText(dataChoose) {
new_svg.selectAll("text.dataChoose")
.data(dataChoose)
.enter().append("text")
.text(function(d) {
return d;
})
/* removed some transform code */
.attr("fill", "white")
.attr("style", "font-size: 12; font-family: Garamond, sans-serif");
}
Here are the relevant parts of my mousedown event handler, which is used to update the data set and redraw the graph:
.on("mousedown", function() {
if (dataChoose == data) {
dataChoose = data2;
}
else {
dataChoose = data;
}
redraw(dataChoose);
redrawText(dataChoose);
});
and here is my redrawText() function
function redrawText(dataChoose) {
var new_text = new_svg.selectAll("text.dataChoose")
.data(dataChoose);
new_text.transition()
.duration(1000)
.text(function(d) {
return d;
})
/* removed transform code */
.attr("fill", "white")
.attr("style", "font-size: 16; font-family: Garamond, sans-serif");
}
Without a full example it's hard to see exactly what you're doing but it looks like if the text label is a property of the data you might not be getting the label field correctly.
Here's a simple example of what I think you're describing as your desired behavior: (LINK): http://tributary.io/inlet/9064381
var svg = d3.select('svg');
var data = [{"tag":"abc","val":123}]
data2 = [{"tag":"ijk","val":321}]
var dataChoose = data;
var myBarGraph = svg.selectAll('rect')
.data(dataChoose)
.enter()
.append('rect')
.attr({
x: 160,
y: 135,
height: 20,
width: function(d) { return d.val; },
fill: 'black'
});
var updateBarGraph = function() {
myBarGraph
.data(dataChoose)
.transition()
.duration(1000)
.attr('width', function(d) { return d.val; })
}
var myText = svg.append('text')
.data(dataChoose)
.attr('x', 129)
.attr('y', 150)
.attr('fill', '#000')
.classed('dataChoose', true)
.text(function(d) { return d.tag })
svg.on("click", function() {
if (dataChoose == data) {
dataChoose = data2;
} else {
dataChoose = data;
}
redrawText();
updateBarGraph();
});
function redrawText() {
myText
.data(dataChoose)
.transition()
.duration(1000)
.style("opacity", 0)
.transition().duration(500)
.style("opacity", 1)
.text(function(d) { return d.tag })
}
EDIT: The other possibility is that your label transition wasn't working because you need to tell d3 how to do the transition for text (see the updated redrawText).
Related
I created this chart using D3 V5. Also, I have attached the sample data on the fiddle you can view by clicking here.
I've included the tick function code block which appends new domains for x and y scales and line/data on the path to slide left:
When the tick function executes, the line sort of rebuilds which makes it look like it bounces.
How can it be smooth, without a bounce at all when it rebuilds the line?
var tr = d3
.transition()
.duration(obj.tick.duration)
.ease(d3.easeLinear);
function tick() {
return setInterval(function() {
var newData = [];
var tickFunction = obj.tick.fnTickData;
if (tickFunction !== undefined && typeof tickFunction === "function") {
newData = tickFunction();
for (var i = 0; i < newData.length; i++) {
obj.data.push(newData[i]);
}
}
if (newData.length > 0) {
var newMaxDate, newMinDate, newDomainX;
if (isKeyXDate) {
newMaxDate = new Date(
Math.max.apply(
null,
obj.data.map(function(e) {
return new Date(e[obj.dataKeys.keyX]);
})
)
);
newMinDate = new Date(
Math.min.apply(
null,
obj.data.map(function(e) {
return new Date(e[obj.dataKeys.keyX]);
})
)
);
newDomainX = [newMinDate, newMaxDate];
} else {
newDomainX = [
d3.min(obj.data, function(d) {
return d[obj.dataKeys.keyX];
}),
d3.max(obj.data, function(d) {
return d[obj.dataKeys.keyX];
})
];
}
// update the domains
//x.domain([newMinDate, newMaxDate]);
if (obj.tick.updateXDomain) {
newDomainX = obj.tick.updateXDomain;
}
x.domain(newDomainX);
if (obj.tick.updateYDomain) {
y.domain(obj.tick.updateYDomain);
}
path.attr("transform", null);
// slide the line left
if (obj.area.allowArea) {
areaPath.attr("transform", null);
areaPath
.transition()
.transition(tr)
.attr("d", area);
}
path
.transition()
.transition(tr)
.attr("d", line);
svg
.selectAll(".x")
.transition()
.transition(tr)
.call(x.axis);
svg
.selectAll(".y")
.transition()
.transition(tr)
.call(y.axis);
// pop the old data point off the front
obj.data.shift();
}
}, obj.tick.tickDelay);
}
this.interval = tick();
That bounce is actually the expected result when you transition the d attribute, which is just a string.
There are several solutions here. Without refactoring your code too much, a simple one is using the pathTween function written by Mike Bostock in this bl.ocks: https://bl.ocks.org/mbostock/3916621. Here, I'm changing it a little bit so you can pass the datum, like this:
path.transition()
.transition(tr)
.attrTween("d", function(d) {
var self = this;
var thisd = line(d);
return pathTween(thisd, 1, self)()
})
Here is the forked plunker: https://plnkr.co/edit/aAqpdSb9JozwHsErpqa9?p=preview
As Gerardo notes, transitioning the d attribute of the path won't work very well unless you modfiy the approach. Here's a simple example of the sort of wiggle/bouncing that will arise if simply updating the d attribute of the path:
Pᴏɪɴᴛs ᴛʀᴀɴsɪᴛɪᴏɴɪɴɢ ᴀᴄʀᴏss sᴄʀᴇᴇɴ, ᴡɪᴛʜ ᴘᴀᴛʜ ᴛʀᴀɴsɪᴛɪᴏɴɪɴɢ ғʀᴏᴍ ᴏɴᴇ ᴅᴀᴛᴀ sᴇᴛ ᴛᴏ ᴛʜᴇ ɴᴇxᴛ.
The above behavior is noted by Mike Bostock in a short piece here, and here's a snippet reproducing the above animation:
var n = 10;
var data = d3.range(n).map(function(d) {
return {x: d, y:Math.random() }
})
var x = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return d.x; }))
.range([10,490])
var y = d3.scaleLinear()
.range([290,10]);
var line = d3.line()
.x(function(d) { return x(d.x); })
.y(function(d) { return y(d.y); })
var svg = d3.select("body")
.append("svg")
.attr("width",500)
.attr("height", 400)
.append("g");
var path = svg.append("path")
.datum(data)
.attr("d", line);
var points = svg.selectAll("circle")
.data(data, function(d) { return d.x; })
.enter()
.append("circle")
.attr("cx", function(d) { return x(d.x); })
.attr("cy", function(d) { return y(d.y); })
.attr("r", 5);
function tick() {
var transition = d3.transition()
.duration(1000);
var newPoint = {x:n++, y: Math.random() };
data.shift()
data.push(newPoint);
x.domain(d3.extent(data,function(d) { return d.x; }))
points = svg.selectAll("circle").data(data, function(d) { return d.x; })
points.exit()
.transition(transition)
.attr("cx", function(d) { return x(d.x); })
.attr("cy", function(d) { return y(d.y); })
.remove();
points.enter().append("circle")
.attr("cx", function(d) { return x(d.x)+30; })
.attr("cy", function(d) { return y(d.y); })
.merge(points)
.transition(transition)
.attr("cx", function(d) { return x(d.x); })
.attr("r", 5);
path.datum(data)
.transition(transition)
.attr("d", line)
.on("end", tick);
}
tick();
path {
fill: none;
stroke: black;
stroke-width: 2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
One solution to this wiggle/bounce is:
add an additional point(s) to the data,
redraw the line with the recently added to data array
find out the next extent of the data
transition the line to the left
update the scale and transition the axis
remove the first data point(s)
This is also proposed in Mike's article that I've linked to. Here would be a basic implementation with your code:
I've avoided a setInterval function by recursively calling the function at the end of the last transition:
function slide() {
// Stop any ongoing transitions:
d3.selectAll().interrupt();
// A transition:
var transition = d3.transition()
.duration(2000)
.ease(d3.easeLinear)
// 1. add an additional point(s) to the data
var newData = obj.tick.fnTickData();
obj.data.push(...newData);
// 2. redraw the line with the recently added to data array
path.datum(obj.data)
areaPath.datum(obj.data)
// Redraw the graph, without the translate, with less data:
path.attr("transform","translate(0,0)")
.attr("d", line)
areaPath.attr("transform","translate(0,0)")
.attr("d", area)
// 3. find out the next extent of the data
// Assuming data is in chronological order:
var min = obj.data[newData.length][obj.dataKeys.keyX];
var max = obj.data[obj.data.length-1][obj.dataKeys.keyX];
// 4. transition the line to the left
path.datum(obj.data)
.transition(transition)
.attr("transform", "translate("+(-x(new Date(min)))+",0)");
areaPath.datum(obj.data)
.transition(transition)
.attr("transform", "translate("+(-x(new Date(min)))+",0)");
// 5. update the scale and transition the axis
x.domain([new Date(min),new Date(max)])
// Update the xAxis:
svg.selectAll('.x')
.transition(transition)
.call(x.axis)
.on("end",slide); // Trigger a new transition at the end.
// 6. remove the first data point(s)
obj.data.splice(0,newData.length)
}
slide();
Here's an updated plunkr.
i am new to D3.js. I have been trying to add tooltip to the existing d3 chloropleth Map by Michelle Chandra. However i am unable to make any progress, the tooltip doesnt seem to appear. Where am i doing wrong? Any Help will be appreciated. http://bl.ocks.org/michellechandra/0b2ce4923dc9b5809922.
Thanks
<style type="text/css">
/* On mouse hover, lighten state color */
path:hover {
fill-opacity: .7;
}
/* Style for Custom Tooltip */
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: white;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
/* Legend Font Style */
body {
font: 11px sans-serif;
}
/* Legend Position Style */
.legend {
position:absolute;
left:800px;
top:350px;
}
</style>
</head>
<body>
<script type="text/javascript">
/* This visualization was made possible by modifying code provided by:
Scott Murray, Choropleth example from "Interactive Data Visualization for the Web"
https://github.com/alignedleft/d3-book/blob/master/chapter_12/05_choropleth.html
Malcolm Maclean, tooltips example tutorial
http://www.d3noob.org/2013/01/adding-tooltips-to-d3js-graph.html
Mike Bostock, Pie Chart Legend
http://bl.ocks.org/mbostock/3888852 */
//Width and height of map
var w = 900;
var h = 600;
// D3 Projection
var projection = d3.geo.albersUsa()
.translate([w/2, h/2]) // translate to center of screen
.scale([1000]); // scale things down so see entire US
// Define path generator
var path = d3.geo.path() // path generator that will convert GeoJSON to SVG paths
.projection(projection); // tell path generator to use albersUsa projection
// Define linear scale for output
var color = d3.scale.linear()
.range(["rgb(24,143,95)","rgb(51,188,196)","rgb(155,226,183)","rgb(217,91,67)"]);
var legendText = ["Cities Lived", "States Lived", "States Visited", "States Not Visited Yet"];
//Create SVG element and append map to the SVG
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
// Append Div for tooltip to SVG
var div = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Load in my states data!
d3.csv("stateslived.csv", function(data) {
color.domain([0,1,2,3]); // setting the range of the input data
// Load GeoJSON data and merge with states data
d3.json("us-states.json", function(json) {
// Loop through each state data value in the .csv file
for (var i = 0; i < data.length; i++) {
// Grab State Name
var dataState = data[i].state;
// Grab data value
var dataValue = data[i].visited;
// Find the corresponding state inside the GeoJSON
for (var j = 0; j < json.features.length; j++) {
var jsonState = json.features[j].properties.name;
if (dataState == jsonState) {
// Copy the data value into the JSON
json.features[j].properties.visited = dataValue;
// Stop looking through the JSON
break;
}
}
}
// Bind the data to the SVG and create one path per GeoJSON feature
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke", "#fff")
.style("stroke-width", "1")
.style("fill", function(d) {
// Get data value
var value = d.properties.visited;
if (value) {
//If value exists…
return color(value);
} else {
//If value is undefined…
return "rgb(213,222,217)";
}
});
// Map the cities I have lived in!
d3.csv("cities-lived.csv", function(data) {
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("cy", function(d) {
return projection([d.lon, d.lat])[1];
})
.attr("r", function(d) {
return Math.sqrt(d.years) * 4;
})
.style("fill", "rgb(217,91,67)")
.style("opacity", 0.85)
// add browser tooltip of city name
//.append("title")
//.text(function(d) {
// return d.place;
//});
// Modification of custom tooltip code provided by Malcolm Maclean, "D3 Tips and Tricks"
// http://www.d3noob.org/2013/01/adding-tooltips-to-d3js-graph.html
.on("mouseover", function(d) {
d3.select(this).transition().duration(300).style("opacity", 1);
div.transition().duration(200)
.style("opacity", .9);
div.text(d.properties.visited)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
// fade out tooltip on mouse out
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
});
// Modified Legend Code from Mike Bostock: http://bl.ocks.org/mbostock/3888852
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", 140)
.attr("height", 200)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter()
.append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.data(legendText)
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d; });
});
});
/* This code generate paths without mapping to other data
// Load JSON file and generate path for each state
d3.json("us-states.json", function(json) { // file path, callback function called when data loaded
svg.selectAll("path") // creates empty references to all the paths
.data(json.features) // loop through our data (the states in the array) and bind to paths
.enter() // create placeholder to reference the new elements
.append("path") // add to the DOM!
.attr("d", path) // generate paths for each state
.style("fill", "steelblue"); // make the states blue!
}); */
</script>
I'm not sure what this statement is supposed to do:
d3.select(this).transition().duration(300).style("opacity", 1);
It appears to select the window object.
One would need to look at your json file to see if you're getting the right data into the div.
Shameless plug. Take a look at foxToolTip.js. I think its easier and more flexible than d3tip.
https://github.com/MichaelRFox/foxToolTip.js
Once div is a div, you have to use html, not text:
div.transition().duration(200)
.style("opacity", .9);
div.html(d.properties.visited)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
I'm trying to implement bar chart and pie chart similar to the example provided in d3js http://bl.ocks.org/NPashaP/96447623ef4d342ee09b with dropdown menu for user selection.
Goal is to override the existing chart with another chart with legends and labels changed. I tried the below Stack Overflow Question did not work.
Calling alternate chart drawing function based on user selection with d3js
I tried making changes accordingly, but the charts are showing one after another.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body{
width:1060px;
margin:50px auto;
}
path { stroke: #fff; }
path:hover { opacity:0.9; }
rect:hover { fill:blue; }
.axis { font: 10px sans-serif; }
.legend tr{ border-bottom:1px solid grey; }
.legend tr:first-child{ border-top:1px solid grey; }
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path { display: none; }
.legend{
margin-bottom:76px;
display:inline-block;
border-collapse: collapse;
border-spacing: 0px;
}
.legend td{
padding:4px 5px;
vertical-align:bottom;
}
.legendFreq, .legendPerc{
align:right;
width:50px;
}
</style>
<body>
<select id="drop-down">
<option value="line">line</option>
<option value="line1">line1</option>
</select>
<div id='dashboard'>
</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
function dashboard(id, fData){
var barColor = 'steelblue';
function segColor(c){ return {low:"#807dba", Medium:"#e08214",High:"#41ab5d"}[c]; }
// compute total for each state.
fData.forEach(function(d){d.total=d.freq.low+d.freq.Medium+d.freq.High;});
// function to handle histogram.
function histoGram(fD){
var hG={}, hGDim = {t: 60, r: 0, b: 30, l: 0};
hGDim.w = 500 - hGDim.l - hGDim.r,
hGDim.h = 300 - hGDim.t - hGDim.b;
//create svg for histogram.
var hGsvg = d3.select(id).append("svg")
.attr("width", hGDim.w + hGDim.l + hGDim.r)
.attr("height", hGDim.h + hGDim.t + hGDim.b).append("g")
.attr("transform", "translate(" + hGDim.l + "," + hGDim.t + ")");
// create function for x-axis mapping.
var x = d3.scale.ordinal().rangeRoundBands([0, hGDim.w], 0.1)
.domain(fD.map(function(d) { return d[0]; }));
// Add x-axis to the histogram svg.
hGsvg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + hGDim.h + ")")
.call(d3.svg.axis().scale(x).orient("bottom"));
// Create function for y-axis map.
var y = d3.scale.linear().range([hGDim.h, 0])
.domain([0, d3.max(fD, function(d) { return d[1]; })]);
// Create bars for histogram to contain rectangles and freq labels.
var bars = hGsvg.selectAll(".bar").data(fD).enter()
// bars.exit().remove()
.append("g").attr("class", "bar");
//a
//create the rectangles.
bars.append("rect")
.attr("x", function(d) { return x(d[0]); })
.attr("y", function(d) { return y(d[1]); })
.attr("width", x.rangeBand())
.attr("height", function(d) { return hGDim.h - y(d[1]); })
.attr('fill',barColor)
.on("mouseover",mouseover)// mouseover is defined below.
.on("mouseout",mouseout);// mouseout is defined below.
//Create the frequency labels above the rectangles.
bars.append("text").text(function(d){ return d3.format(",")(d[1])})
.attr("x", function(d) { return x(d[0])+x.rangeBand()/2; })
.attr("y", function(d) { return y(d[1])-5; })
.attr("text-anchor", "Mediumdle");
function mouseover(d){ // utility function to be called on mouseover.
// filter for selected state.
var st = fData.filter(function(s){ return s.State == d[0];})[0],
nD = d3.keys(st.freq).map(function(s){ return {type:s, freq:st.freq[s]};});
// call update functions of pie-chart and legend.
pC.update(nD);
leg.update(nD);
}
function mouseout(d){ // utility function to be called on mouseout.
// reset the pie-chart and legend.
pC.update(tF);
leg.update(tF);
}
// create function to update the bars. This will be used by pie-chart.
hG.update = function(nD, color){
// update the domain of the y-axis map to reflect change in frequencies.
y.domain([0, d3.max(nD, function(d) { return d[1]; })]);
// Attach the new data to the bars.
var bars = hGsvg.selectAll(".bar").data(nD);
// transition the height and color of rectangles.
bars.select("rect").transition().duration(500)
.attr("y", function(d) {return y(d[1]); })
.attr("height", function(d) { return hGDim.h - y(d[1]); })
.attr("fill", color);
// transition the frequency labels location and change value.
bars.select("text").transition().duration(500)
.text(function(d){ return d3.format(",")(d[1])})
.attr("y", function(d) {return y(d[1])-5; });
}
return hG;
}
// function to handle pieChart.
function pieChart(pD){
var pC ={}, pieDim ={w:250, h: 250};
pieDim.r = Math.min(pieDim.w, pieDim.h) / 2;
// create svg for pie chart.
var piesvg = d3.select(id).append("svg")
.attr("width", pieDim.w).attr("height", pieDim.h).append("g")
.attr("transform", "translate("+pieDim.w/2+","+pieDim.h/2+")");
// create function to draw the arcs of the pie slices.
var arc = d3.svg.arc().outerRadius(pieDim.r - 10).innerRadius(0);
// create a function to compute the pie slice angles.
var pie = d3.layout.pie().sort(null).value(function(d) { return d.freq; });
// Draw the pie slices.
piesvg.selectAll("path").data(pie(pD)).enter()
//piesvg.exit().remove()
.append("path").attr("d", arc)
.each(function(d) { this._current = d; })
.style("fill", function(d) { return segColor(d.data.type); })
.on("mouseover",mouseover).on("mouseout",mouseout);
// create function to update pie-chart. This will be used by histogram.
pC.update = function(nD){
piesvg.selectAll("path").data(pie(nD)).transition().duration(500)
.attrTween("d", arcTween);
}
// Utility function to be called on mouseover a pie slice.
function mouseover(d){
// call the update function of histogram with new data.
hG.update(fData.map(function(v){
return [v.State,v.freq[d.data.type]];}),segColor(d.data.type));
}
//Utility function to be called on mouseout a pie slice.
function mouseout(d){
// call the update function of histogram with all data.
hG.update(fData.map(function(v){
return [v.State,v.total];}), barColor);
}
// Animating the pie-slice requiring a custom function which specifies
// how the intermediate paths should be drawn.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) { return arc(i(t)); };
}
return pC;
}
// function to handle legend.
function legend(lD){
var leg = {};
// create table for legend.
var legend = d3.select(id).append("table").attr('class','legend');
// create one row per segment.
var tr = legend.append("tbody").selectAll("tr").data(lD).enter().append("tr");
// tr.exit().remove()
// create the first column for each segment.
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect")
.attr("width", '16').attr("height", '16')
.attr("fill",function(d){ return segColor(d.type); });
// create the second column for each segment.
tr.append("td").text(function(d){ return d.type;});
// create the third column for each segment.
tr.append("td").attr("class",'legendFreq')
.text(function(d){ return d3.format(",")(d.freq);});
// create the fourth column for each segment.
tr.append("td").attr("class",'legendPerc')
.text(function(d){ return getLegend(d,lD);});
// Utility function to be used to update the legend.
leg.update = function(nD){
// update the data attached to the row elements.
var l = legend.select("tbody").selectAll("tr").data(nD);
// update the frequencies.
l.select(".legendFreq").text(function(d){ return d3.format(",")(d.freq);});
// update the percentage column.
l.select(".legendPerc").text(function(d){ return getLegend(d,nD);});
}
function getLegend(d,aD){ // Utility function to compute percentage.
return d3.format("%")(d.freq/d3.sum(aD.map(function(v){ return v.freq; })));
}
return leg;
}
// calculate total frequency by segment for all state.
var tF = ['low','Medium','High'].map(function(d){
return {type:d, freq: d3.sum(fData.map(function(t){ return t.freq[d];}))};
});
// calculate total frequency by state for all segment.
var sF = fData.map(function(d){return [d.State,d.total];});
var hG = histoGram(sF), // create the histogram.
pC = pieChart(tF), // create the pie-chart.
leg= legend(tF); // create the legend.
}
</script>
<script>
var freqData=[
{State:'WK-1',freq:{low:10, Medium:20, High:10}}
,{State:'WK-2',freq:{low:30, Medium:10, High:15}}
,{State:'WK-3',freq:{low:40, Medium:45, High:35}}
,{State:'WK-4',freq:{low:15, Medium:4, High:20}}
,{State:'WK-5',freq:{low:50, Medium:8, High:19}}
];
var freqData1=[
{State:'WK-1',freq:{low:20, Medium:20, High:10}}
,{State:'WK-2',freq:{low:10, Medium:20, High:25}}
,{State:'WK-3',freq:{low:20, Medium:25, High:35}}
,{State:'WK-4',freq:{low:65, Medium:4, High:20}}
,{State:'WK-5',freq:{low:50, Medium:8, High:19}}
];
d3.select("#drop-down").on("change", function () {
d3.selectAll(".alt-view").remove();
//d3.exit().remove();
selected = this.value;
if(selected == "line"){dashboard('#dashboard',freqData);}
else if(selected == "line1"){dashboard('#dashboard',freqData1);}
});
</script>
Reviewed your plunker. The issue is that you are trying to remove the previous charts using below call.
d3.selectAll(".alt-view").remove();
However, while building chart elements, you have not assigned alt-view class to any of the elements. All the elements you want to remove, make sure they are contained in a parent element that has alt-view class and you should be good to go.
Alternatively, you can do following to quickly test this.
Replace
d3.selectAll(".alt-view").remove();
with
d3.selectAll("#dashboard").remove();
d3.select('body').append('div').attr('id', 'dashboard');
I'm following the given tutorial on D3
bar chart -2
I've setup my code in two functions one is init and one is update
var xScale = null;
var chart = null;
function init(w, c) {
xScale = d3.scale.linear()
.range([0, w]);
chart = d3.select(c)
.append('svg')
.attr('width', w);
function update(data) {
xScale.domain([0, d3.max(data, function(d) { return +d.value; })]);
chart.attr('height', 20 * data.length);
var bars = chart.selectAll('g')
.data(data);
bars.exit().remove();
bars.enter().append('g')
.attr('transform', function(d, i) { return 'translate(0,' + i * 20 + ')'; });
bars.append('rect')
.attr('width', function(d) { return xScale(+d.value); })
.attr('height', 18);
bars.append('text')
.attr('x', function(d) { return xScale(+d.value); })
.attr('y', 10)
.attr('dy', '.45em')
.text(function (d) { return d.name; });
}
When I call update first time, the bar chart is created correctly, on subsequenet update calls, it creates rect and text elements under tags instead of updating
My data is a dict {'name': a, 'value': 12, .....} The number of elements per update can be different. There might be same keys(names) with different values in each update
bars = chart.selectAll('g')
You are selecting all of the g elements (both new and existing).
bars.append('rect');
bars.append('text');
As a result, when you call append on bars, you are appending rect and text elements to both the new and existing g elements.
/* Enter */
enter = bars.enter().append('g');
enter.append('rect');
enter.append('text');
/* Update */
bars.attr('transform', function(d, i) {
return 'translate(0,' + i * 20 + ')';
});
bars.select('rect')
.attr('width', function(d) { return xScale(+d.value); })
.attr('height', 18);
bars.select('text')
.attr('x', function(d) { return xScale(+d.value); })
.attr('y', 10)
.attr('dy', '.45em')
.text(function (d) { return d.name; });
This allows you to append rect and text elements only to the enter selection, yet still allows you to update all the elements using your new data.
Note:
The enter selection merges into the update selection when you append or insert. Rather than applying the same operators to the enter and update selections separately, you can now apply them only once to the update selection after entering the nodes.
See: https://github.com/mbostock/d3/wiki/Selections#data
I'm trying to plot circles from data in my csv file, but the circles are not appearing on the svg canvas. I believe the problem stems from how I load in the data (it gets loaded as an array of objects), but I'm not quite sure how to figure out what to do next.
Based off this tutorial: https://www.dashingd3js.com/svg-text-element
D3.js code:
var circleData = d3.csv("files/data.csv", function (error, data) {
data.forEach(function (d) {
d['KCComment'] = +d['KCComment'];
d['pscoreResult'] = +d['pscoreResult'];
d['r'] = +d['r'];
});
console.log(data);
});
var svg = d3.select("body").append("svg")
.attr("width", 480)
.attr("height", 480);
var circles = svg.selectAll("circle")
.data(circleData)
.enter()
.append("circle");
var circleAttributes = circles
.attr("cx", function (d) { return d.KCComment; })
.attr("cy", function (d) { return d.pscoreResult; })
.attr("r", function (d) { return d.r; })
.style("fill", "green");
var text = svg.selectAll("text")
.data(circleData)
.enter()
.append("text");
var textLabels = text
.attr("x", function(d) { return d.KCComment; })
.attr("y", function(d) { return d.pscoreResult; })
.text(function (d) { return "( " + d.KCComment + ", " + d.pscoreResult + " )"; })
.attr("font-family", "sans-serif")
.attr("font-size", "20px")
.attr("fill", "red");
What the CSV looks like:
fmname, fmtype, KCComment, pscoreResult, r
test1, type1, 7.1, 8, 39
test2, type2, 1.2, 3, 12
You should have the circle-drawing code within the d3.csv function's callback, so it's only processed when the data is available.
d3.csv("data.csv", function (error, circleData) {
circleData.forEach(function (d) {
d['KCComment'] = +d['KCComment'];
d['pscoreResult'] = +d['pscoreResult'];
d['r'] = +d['r'];
});
console.log(circleData);
// Do the SVG drawing stuff
...
// Finished
});
Also note that instead of setting var circleData = d3.csv(... you should just define it in the callback function.
Here's a plunker with the working code: http://embed.plnkr.co/fzBX0o/preview
You'll be able to see a number of further issues now: both circles are overlapping and only one quarter is visible. That's because your KCComment and pscoreResult values used to define the circles' cx and cy are too small. Try multiplying them up so that the circles move right and down and are a bit more visible! Same is true of the text locations, but I'll leave those problems for you to solve