d3 animating a bubble chart within a given radius - javascript

//bubble chart base.
http://jsfiddle.net/NYEaX/1450/
I am trying to animate the bubbles - via changing their scale -- and if possible fade them in and out. At some stage I need to cluster them with some kind of gravity to occupy more of a containing circumference.
(function() {
var diameter = 250;
var svg = d3.select('#graph').append('svg')
.attr('width', diameter)
.attr('height', diameter);
var bubble = d3.layout.pack()
.size([diameter, diameter])
.value(function(d) {
return d.size;
})
.padding(3);
var color = d3.scale.ordinal()
.domain(["Lorem ipsum", "dolor sit", "amet", "consectetur", "adipisicing"])
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56"]);
function randomData() {
var data1 = {
"children": [
{
name: "AA",
className: "aa",
size: 170
},
{
name: "BB",
className: "bb",
size: 393
},
{
name: "CC",
className: "cc",
size: 293
},
{
name: "DD",
className: "dd",
size: 89
}
]
};
var data2 = {
"children": [
{
name: "AA",
className: "aa",
size: 120
},
{
name: "BB",
className: "bb",
size: 123
},
{
name: "CC",
className: "cc",
size: 193
},
{
name: "DD",
className: "dd",
size: 289
}
]
};
var j = Math.floor((Math.random() * 2) + 1);
console.log("j", j);
if (j == 1) {
return data1;
} else {
return data2;
}
}
change(randomData());
d3.select(".randomize")
.on("click", function() {
change(randomData());
});
function change(data) {
console.log("data", data);
// generate data with calculated layout values
var nodes = bubble.nodes(data)
.filter(function(d) {
return !d.children;
}); // filter out the outer bubble
var vis = svg.selectAll('circle')
.data(nodes);
vis.enter()
.insert("circle")
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')';
})
.attr('r', function(d) {
return d.r;
})
.style("fill", function(d) {
return color(d.name);
})
.attr('class', function(d) {
return d.className;
});
vis
.transition().duration(1000)
vis.exit()
.remove();
};
})();

These animated bubbles will be part of this overall chart - I need support in fine-tuning the animated arcs and bubbles accordingly. So the pie chart arcs should tween smoothly -- and the bubbles should fade in/out/grow in radius/reduce in radius
Latest fiddle.
http://jsfiddle.net/NYEaX/1505/
( http://jsfiddle.net/NYEaX/1506/ )- refactored
1. -- how to animate the arcs
2. -- how to animate the bubbles
3. -- adding back the randomise button to test with 2 dummy data sets.
this is the old pie animations and worked very well
/* ------- ANIMATE PIE SLICES -------*/
var slice = doughpie.select(".slices").selectAll("path.slice")
.data(pie(data), key);
slice.enter()
.insert("path")
.style("fill", function(d) {
return color(d.data.label);
})
.style("transform", function(d, i){
//return "translate(0, 0)";
})
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
/* ------- ANIMATE PIE SLICES -------*/
//this is the current pie arcs - but when I try and animate the pie in the same manner - it fails.
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) {
return color(d.data.label);
});
arc
.outerRadius(radius - 10)
.innerRadius(0);

Try and animate the pie chart first, then review the bubble animation

To animate the bubbles (grow in) use:
vis.enter()
.insert("circle")
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')';
})
.attr('r',0)
.transition()
.duration(1000)
.attr('r', function(d) {
return d.r;
})
.style("fill", function(d) {
return color(d.name);
})
.attr('class', function(d) {
return d.className;
});
I tried adding this code to the jsfiddle you posted above : http://jsfiddle.net/NYEaX/1450/

Related

d3 filtering bar chart with legend toggling

I am trying to filter/update a bar chart with legend toggling. I am unsure how to set active states on the bars during initialization - then trying to deactivate - exclude the required datasets on toggle, but restore them when the active states come back.
http://jsfiddle.net/5ruhac83/5/
//legend toggling
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colores_google(i);
})
.on("click", function(name) {
var active = false;
newState = active ? "active" : "inactive";
// Hide or show the elements
d3.select(this).attr("class", newState);
//set active state
console.log("name", name)
toggleBar(name)
});
//animating the bars - with a pruned data set
function toggleBar(name) {
var hiddenClassName = 'hidden',
bar = chartHolder.selectAll('.bars'),
currentBars = bar.selectAll('[value="' + name + '"]')
currentBars.classed(hiddenClassName, !currentBars.classed(hiddenClassName))
var barData = data.map(item => {
item.valores = item.valores.map(valor => {
return Object.assign({}, valor, {
value: bar.selectAll('[value="' + valor.name + '"]').classed(hiddenClassName) ?
0 : item[valor.name]
})
})
return item;
})
var barData = [{
label: "a",
"Current Period": 20
}, {
label: "b",
"Current Period": 15
}, {
label: "c",
"Current Period": 25
}, {
label: "d",
"Current Period": 5
}];
var options = getOptions(barData);
barData = refactorData(barData, options);
console.log("barData", barData)
bar
.data(barData)
var rect = bar.selectAll("rect")
.data(function(d) {
return d.valores;
})
rect
.transition()
.duration(1000)
.delay(100)
.attr("width", x0.rangeBand() / 2)
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
rect.exit().remove();
/*
var bar = bar.selectAll("rect")
bar.transition()
//.attr("id", function(d){ return 'tag'+d.state.replace(/\s|\(|\)|\'|\,+/g, '');})
.attr("x", function(d) { return x1(d.name); })
.attr("width", x0.rangeBand())
.attr("y", function(d) {
return 0;
//return y(d.value);
})
.attr("height", function(d) {
return 0;
//return height - y(d.value);
});
//bar.exit().remove();
*/
}
Here's a chart which resets the domain with new options based on the toggled legend:
JS Fiddle DEMO
function toggleBar(name, state) {
data.forEach(function(d) {
_.findWhere(d.valores, {name: name}).hidden = state;
});
var filteredOptions;
if(state) {
filteredOptions = options.filter(function(d) { return d !== name; });
} else {
filteredOptions = options;
}
x1.domain(filteredOptions).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(d.valores.filter(function(k) { return !k.hidden;}), function(d) {
return d.value;
});
})]);
Changes:
You don't need to reset the data on every toggle. I just added a hidden attribute to the "valores" and the while resetting the domain in the toggleBar function, filtered the data based on non-hidden options and set the domain accordingly.
I'd recommend to get used to d3's "enter, update and exit" methods. I hope the code helps you understand that as well.
drawBars() is a function that does that.
Changed the way the tooltip is rendered as well. Instead of using querySelector for hovered elements (that's definitely one way), you can just use the parent node's data using the datum() function.
Legends: I've added a stroke for every legend and to indicate whether the corresponding option is hidden or not, the fill-opacity is toggled on every click.
Used a separate color scale with an ordinal domain of options and range to be same as previous colors so that the colors are based on the names and not indices (as before)
Added simple transitions.
Used underscore.js in toggleBars() function. You could switch back to pure JS as well.
And to answer your question on active states, please check for toggling of the "clicked" classnames.
Please go through the code and let me know if you any part of it is unclear. I'll add some comments too.
:)
here is a solution for grouped bar chart legend toggling with animation.
//jsfiddle - http://jsfiddle.net/0ht35rpb/259/
var $this = this.$('.barChart');
var w = $this.data("width");
var h = $this.data("height");
//var configurations = $this.data("configurations");
var data = [{
"State": "a",
"AA": 100,
"BB": 200
}, {
"State": "b",
"AA": 454,
"BB": 344
},{
"State": "c",
"AA": 140,
"BB": 500
}, {
"State": "d",
"AA": 154,
"BB": 654
}];
var yLabel = "Count";
var svg = d3.select($this[0]).append("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom,
g = svg
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// The scale spacing the groups:
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1);
// The scale for spacing each group's bar:
var x1 = d3.scaleBand()
.padding(0.05);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f", "#8bf41b"]);
var keys = d3.keys(data[0]).slice(1);
x0.domain(data.map(function(d) {
return d.State;
}));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(keys, function(key) {
return d[key];
});
})]).nice();
g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) {
return "translate(" + x0(d.State) + ",0)";
})
.selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return {
key: key,
value: d[key]
};
});
})
.enter().append("rect")
.attr("x", function(d) {
return x1(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("width", x1.bandwidth())
.attr("height", function(d) {
return height - y(d.value);
})
.attr("fill", function(d, i) {
return z(d.key);
});
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x0));
g.append("g")
.attr("class", "yaxis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text(yLabel);
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", width - 17)
.attr("width", 15)
.attr("height", 15)
.attr("fill", z)
.attr("stroke", z)
.attr("stroke-width", 2)
.on("click", function(d) {
update(d)
});
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) {
return d;
});
var filtered = [];
////
//// Update and transition on click:
////
function update(d) {
//
// Update the array to filter the chart by:
//
// add the clicked key if not included:
if (filtered.indexOf(d) == -1) {
filtered.push(d);
// if all bars are un-checked, reset:
if (filtered.length == keys.length) filtered = [];
}
// otherwise remove it:
else {
filtered.splice(filtered.indexOf(d), 1);
}
//
// Update the scales for each group(/states)'s items:
//
var newKeys = [];
keys.forEach(function(d) {
if (filtered.indexOf(d) == -1) {
newKeys.push(d);
}
})
x1.domain(newKeys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(keys, function(key) {
if (filtered.indexOf(key) == -1) return d[key];
});
})]).nice();
//g.select(".yaxis")
//.call(d3.axisLeft(y).ticks(null, "s"));
var t0 = svg.transition().duration(250);
var t1 = t0.transition();
t1.selectAll(".yaxis").call(d3.axisLeft(y).ticks(null, "s"));
//
// Filter out the bands that need to be hidden:
//
var bars = svg.selectAll(".bar").selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return {
key: key,
value: d[key]
};
});
})
bars.filter(function(d) {
return filtered.indexOf(d.key) > -1;
})
.transition()
.attr("x", function(d) {
return (+d3.select(this).attr("x")) + (+d3.select(this).attr("width")) / 2;
})
.attr("height", 0)
.attr("width", 0)
.attr("y", function(d) {
return height;
})
.duration(500);
//
// Adjust the remaining bars:
//
bars.filter(function(d) {
return filtered.indexOf(d.key) == -1;
})
.transition()
.attr("x", function(d) {
return x1(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
})
.attr("width", x1.bandwidth())
.attr("fill", function(d, i) {
return z(d.key);
})
.duration(500);
// update legend:
legend.selectAll("rect")
.transition()
.attr("fill", function(d, i) {
if (filtered.length) {
if (filtered.indexOf(d) == -1) {
return z(d);
} else {
return "white";
}
} else {
return z(d);
}
})
.duration(100);
}

Ensure transition completes

I have the following pie chart with a very nice transition:
http://plnkr.co/edit/b4uLimUSZzxiPzAkNpBt?p=preview
The code for the pie chart is the following:
function addPieChart(meas, dataFile) {
var width = 400,
height = 400,
radius = Math.min(width, height) / 2.1,
color = d3.scale.ordinal()
.range(["#016da9", "#4c96d5"])
.domain([0, 1]);
//check if the svg already exists
var plot = d3.select("#svgPIEChart")
if (plot.empty()) {
var vis = d3.select("#pieChart")
.append("svg") //create the SVG element
.attr({
id: "svgPIEChart"
})
} else {
var vis = d3.select("#svgPIEChart")
vis.selectAll("*").remove();
};
//svg element
vis.attr({
//set the width and height of our visualization (these will be attributes of the <svg> tag
width: width,
height: height
});
//group of the svg element
var svg = vis
.append("g")
.attr({
'transform': "translate(" + width / 2 + "," + height * .52 + ")"
});
var arc = d3.svg.arc()
.startAngle(function(d) {
return d.x;
})
.endAngle(function(d) {
return d.x + d.dx;
})
.outerRadius(function(d) {
return (d.y + d.dy) / (radius);
})
.innerRadius(function(d) {
return d.y / (radius);
});
d3.text(dataFile, function(text) {
var csv = d3.csv.parseRows(text);
var json = buildHierarchy(csv);
// it seems d3.layout.partition() can be either squares or arcs
var partition = d3.layout.partition()
.sort(null)
.size([2 * Math.PI, radius * radius])
.value(function(d) {
return d.Revenue;
});
var path = svg.data([json]).selectAll(".theArc")
.data(partition.nodes)
.enter()
.append("path")
.attr("class", "theArc") //<<<<<<<<<<new jq
.attr("id", function(d, i) {
return "theArc_" + i;
}) //Give each slice a unique ID //<<<<<<<<<<new jq
.attr("display", function(d) {
return d.depth ? null : "none";
})
.attr("d", arc)
.style("stroke", "#fff")
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.attr("fill-rule", "evenodd")
.style("opacity", 1)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", up)
.each(stash);
// path.each(stash).on("click", up)
//this is a start>>>>>
path
.append("title") //mouseover title showing the figures
.text(function(d) {
return d.name + ": " + formatAsShortInteger(d.Revenue);
});
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
svg.data([json]).selectAll(".theTxts")
.data(partition.nodes)
.enter()
.append("text")
.attr("class", "theTxts")
.attr("dx", 10) //Move the text from the start angle of the arc
.attr("dy", 15) //Move the text down
.append("textPath")
.attr("class", "foo")
.attr("xlink:href", function(d, i) {
return "#theArc_" + i;
})
.text(function(d) {
if ((d.name != 'root') && ((d.name != 'B T') || (currentMeasure != 'W'))) {
return d.name;
}
})
.on("click", up) //<<<new jq;
svg.append("text")
.attr({
x: "0",
y: "0",
'class': "title",
"id": "titleX",
'text-anchor': "middle"
})
.text(currentMeasure + " split")
//>>
.append("tspan")
.attr({
dy: "1.1em",
x: 0,
'text-anchor': "middle"
})
.text("for " + latestMth)
.attr("class", "title");
d3.selectAll("input").on("change", function change() {
value = createValueFunc(this.value);
currentMeasure = this.value;
var path2 = svg.data([json]).selectAll(".theArc");
path2
.data(partition.value(value).nodes)
.transition()
.duration(1500)
.attrTween("d", arcTween)
//to update the tooltips
svg.selectAll("title")
.text(function(d) {
return d.name + ": " + formatAsShortInteger(value(d));
});
svg.selectAll("textPath")
.text(function(d) {
if ((d.name != 'root') && ((d.name != 'B T') || (currentMeasure != 'W'))) {
return d.name;
}
});
// the following deletes what was originally created and then recreates the text
svg.selectAll("#titleX").remove();
svg.append("text")
.attr({
x: "0",
y: "0",
'class': "title",
"id": "titleX",
'text-anchor': "middle"
})
.text(currentMeasure + " split")
.append("tspan")
.attr({
dy: "1.1em",
x: 0,
'text-anchor': "middle"
})
.text("for " + latestMth)
.attr("class", "title");
addProdTitl();
updateTOPbarChart(currentGrp, currentMeasure, currentColr);
updateBOTbarChart(currentGrp, currentMeasure, currentColr);
});
function mouseover() {
d3.select(this)
.transition()
.duration(200)
.style("opacity", 0.9);
};
function mouseout() {
d3.select(this)
.transition()
.duration(200)
.style("opacity", 1);
};
// update bar chart when user selects piece of the pie chart
function up(d, i) {
currentGrp = d.name;
// (d.children ? d : d.parent).name
currentColr = color((d.children ? d : d.parent).name); // color(d.name);
addProdTitl();
updateTOPbarChart(d.name, currentMeasure, currentColr); //color(d.name));
updateBOTbarChart(d.name, currentMeasure, currentColr); //color(d.name));
};
// Stash the old values for transition.
function stash(d) {
d.x0 = d.x;
d.dx0 = d.dx;
};
// Interpolate the arcs in data space.
function arcTween(a) {
var i = d3.interpolate({
x: a.x0,
dx: a.dx0
}, a);
return function(t) {
var b = i(t);
a.x0 = b.x;
a.dx0 = b.dx;
return arc(b);
};
};
});
};
If you change the selection of the radio button and then click on a section of the pie chart that section does not complete its transition as I believe it decides it needs to move on to its "on" event.
How do I ensure that the transition completes? (and the pie chart does not do a pac-man impersonation)
Clear your listeners when the tranistion starts.
Attach it when the transition is ended
Like this:
path2
.data(partition.value(value).nodes)
.transition()
.duration(1500)
.attrTween("d", arcTween)
.each("start", function(){
d3.select(this)
.on("mouseover", null) //CLEARING the listeners
.on("mouseout", null) //CLEARING the listeners
.on("click", null) //CLEARING the listeners
})
.each("end", function(){
d3.select(this)
.on("mouseover", mouseover) //attaching the listeners
.on("mouseout", mouseout) //attaching the listeners
.on("click", up) ////attaching the listeners
});
working code here

D3 Pie Chart new path values not being calculated after update

I have a very straightforward data structure where I want to have a D3 Donut Chart update based on a different key/value pair in the same object. Here is the data:
var data = [
{
id: 1,
name: 'Bernie Sanders',
name2: 'Jesus Christ',
color: '#0362ad',
state: 3.55,
national: 4.88
},
{
id: 2,
name: 'Donald Trump',
name2: 'Miley Cyrus',
color: '#f0a611',
state: 1.36,
national: 2.65
}
]
I'm setting up my pie chart in the following way:
var pie = d3.layout.pie()
.sort(null)
.value( function(d) { return d.state });
var arc = d3.svg.arc()
.outerRadius(radius - 30)
.innerRadius(radius - 80);
var paths = chart.selectAll('g')
.data(pie(data), function(d) { return d.data.id })
.enter()
.append('g')
.attr('class', 'arc')
.attr('transform', 'translate(' + (radius - margin.left) + ',' + (radius - margin.top) + ')');
var arcs = paths.append('path')
.attr('d', arc)
.style('fill', function(d) { return d.data.color; });
I have an update() function in which I'm trying to change the Donut Chart values to show the national value rather than the state value.
// "which" is equal to 'state' or 'national'
function update(which) {
// change location of values
pie.value( function(d) { return d[which] );
// compute new angles
paths.data(pie(data));
// update arcs
arcs.attr('d', arc);
};
My arcs are not properly updating, and when I log them to the console, the __data__ property still shows the value and angles for the initialized property (d.state).
How can properly compute the new paths for each arc?
You need to handle transition scenario for the paths like so:
(function() {
var data = [{
id: 1,
name: 'Bernie Sanders',
name2: 'Jesus Christ',
color: '#0362ad',
state: 3.55,
national: 4.88
}, {
id: 2,
name: 'Donald Trump',
name2: 'Miley Cyrus',
color: '#f0a611',
state: 1.36,
national: 2.65
}]
var svgElement = $("#piechart");
var width = svgElement.width();
var height = svgElement.height();
var radius = Math.min(width, height) / 2;
var chart = d3.select("#piechart");
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.state
});
var arc = d3.svg.arc()
.outerRadius(radius - 30)
.innerRadius(radius - 80);
chart.append('g')
.attr('class', 'arc')
.attr('transform', 'translate(' + (radius) + ',' + (radius) + ')');
var paths = chart.select('g').selectAll('path')
.data(pie(data), function(d) {
return d.data.id
});
paths.enter()
.append('path')
.attr('d', arc)
.style('fill', function(d) {
return d.data.color;
})
// which is equal to 'state' or 'national'
function update(which) {
pie.value(function(d) {
return d[which]
});
paths.data(pie(data), function(d) {
return d.data.id
});
paths.transition()
.duration(1000)
.attr('d', arc)
.style('fill', function(d) {
return d.data.color;
});
};
$("#stateButton").click(function() {
update("state");
});
$("#nationalButton").click(function() {
update("national");
});
})()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<button id="stateButton">State</button>
<button id="nationalButton">National</button>
<svg id="piechart"></svg>

D3 Y axis for scatterplot not scaling/changing

I made a scatterplot that changes dynamically based on the variable for the Y axis that is selected in a dropdown box (the x axis is fixed). However the Y axis isn’t scaling/changing based on which variable is selected in the dropdown box.
// select svg canvas
var m = [20, 20, 30, 20], // margins
w = 1100-m[1]-m[3], // width
h = 650-m[0]-m[2], // height
xcol = 0, // active x column
ycol = 1; // active y column
// create svg element, adjusted by margins
var svg = d3.select('#chart')
.append("g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
// add the tooltip area to the webpage
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// load data from csv file
d3.csv('nbaWins.csv', function(data) {
// get columns of csv, mark excluded columns
var columns = d3.keys( data[0] ),
excluded = ['Team'];
// get quantitative dimensions
var dimensions = _(columns)
.difference(excluded);
// extents for each dimension
var extents = _(dimensions)
.map(function(col) {
return [0, d3.max(data, function(d) { return parseFloat(d[col]) })]
});
// x & y variables
var x = d3.scale.linear().domain(extents[xcol]).range([0, w - 50]),
y = d3.scale.linear().domain(extents[ycol]).range([h, 0]),
xAxis = d3.svg.axis().scale(x).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left");
// color scale (change colors later)
var color = {
"ATL": '#ff9896',
"BOS": '#ff9896',
"BKN": '#ff9896',
"CHA": '#ff9896',
"CHI": '#ff9896',
"CLE": '#ff9896',
"DAL": '#ff9896',
"DEN": '#ff9896',
"DET": '#ff9896',
"GSW": '#ff9896',
"HOU": '#ff9896',
"IND": '#ff9896',
"LAC": '#ff9896',
"LAL": '#ff9896',
"MEM": '#ff9896',
"MIA": "#ff9896",
"MIL": '#ff9896',
"MIN": '#ff9896',
"NO" : '#ff9896',
"NYK": '#ff9896',
"OKC": '#ff9896',
"ORL": '#ff9896',
"PHI": '#ff9896',
"PHO": '#ff9896',
"POR": '#ff9896',
"SAC": '#ff9896',
"SAS": '#ff9896',
"TOR": '#ff9896',
"UTA": '#ff9896',
"WAS": '#ff9896'
};
// create x axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", w - 40)
.attr("y", -6)
.style("text-anchor", "end")
.text("Wins");
// create y axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// bind data to chart
svg.selectAll('circle')
.data(data)
.enter().append('circle')
.attr("fill", function(d) { return color[d.Team]; })
.attr("cx", function(d) { return x(d[dimensions[xcol]]); })
.attr("cy", function(d) { return y(d[dimensions[ycol]]); })
.attr("r", 5) //backup
.on("mouseover", function(d) {
tooltip.transition().duration(200).style("opacity", .9);
tooltip.html(d["Team"] + "<br/> (" + d[dimensions[xcol]]
+ ", " + d[dimensions[ycol]] + ")")
.style("left", d + "px")
.style("top", d + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
// change x axis data (may not need: x axis is static)
function xaxis(i) {
xcol = i;
x.domain(extents[i]);
svg.selectAll('circle')
.transition()
.ease('linear')
.duration(1000)
.attr("cx", function(d) { return x(d[dimensions[0]]); })
.attr("cy", function(d) { return y(d[dimensions[ycol]]); });
};
// change y axis data
function yaxis(i) {
ycol = i;
y.domain(extents[i]);
svg.selectAll('circle')
.transition()
.ease('linear')
.duration(1000)
.attr("cx", function(d) { return x(d[dimensions[0]]); })
.attr("cy", function(d) { return y(d[dimensions[i]]); });
};
// create dropdowns to change y axes
d3.select("#yaxis")
.selectAll("option")
.data(dimensions)
.enter().append("option")
.attr("value", function(d,i) { return i; })
.text(function(d) { return d; })
.each(function(d,i) {
if (i == ycol) d3.select(this).attr("selected", "yes");
});
d3.select("#yaxis")
.on("change", function() { yaxis(this.selectedIndex) });
window.data = data;
});
Here is the console error I get when I try to change the Y variable:
NotFoundError: DOM Exception 8: An attempt was made to reference a Node in a context where it does not exist.
Any ideas on how to fix this? Any help would be much appreciated.
You've set the domain of the y but haven't redrawn the axis with it:
// change y axis data
function yaxis(i) {
ycol = i;
y.domain(extents[i]);
// update y axis
svg.selectAll("g.y.axis")
.call(yAxis);
svg.selectAll('circle')
.transition()
.ease('linear')
.duration(1000)
.attr("cx", function(d) { return x(d[dimensions[0]]); })
.attr("cy", function(d) { return y(d[dimensions[i]]); });
};
Example here.

Create wordcloud d3.js from elasticsearch

I am using the following example as base and want to make it dynamic word cloud https://github.com/jasondavies/d3-cloud
define(['scripts/d3.v3', 'scripts/elasticsearch'], function (d3, elasticsearch) {
"use strict";
var client = new elasticsearch.Client();
client.search({
index: 'nfl',
size: 5,
body: {
// Begin query.
query: {
// Boolean query for matching and excluding items.
bool: {
must: { match: { "description": "TOUCHDOWN" }},
must_not: { match: { "qtr": 5 }}
}
},
// Aggregate on the results
aggs: {
touchdowns: {
terms: {
field: "qtr",
order: { "_term" : "asc" }
}
}
}
// End query.
}
}).then(function (resp) {
console.log(resp);
// D3 code goes here.
var touchdowns = resp.aggregations.touchdowns.buckets;
// d3 donut chart
var width = 600,
height = 300,
radius = Math.min(width, height) / 2;
var color = ['#ff7f0e', '#d62728', '#2ca02c', '#1f77b4'];
var arc = d3.svg.arc()
.outerRadius(radius - 60)
.innerRadius(120);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) { return d.doc_count; });
var svg = d3.select("#donut-chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/1.4 + "," + height/2 + ")");
var g = svg.selectAll(".arc")
.data(pie(touchdowns))
.enter()
.append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function (d, i) {
return color[i];
});
g.append("text")
.attr("transform", function (d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.style("fill", "white")
.text(function (d) { return d.data.key; });
});
this is example code from elasticsearch website how to use with d3
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="../lib/d3/d3.js"></script>
<script src="../d3.layout.cloud.js"></script>
<script>
var fill = d3.scale.category20();
d3.layout.cloud().size([300, 300])
.words([
"Hello", "world", "normally", "you", "want", "more", "words",
"than", "this"].map(function(d) {
return {text: d, size: 10 + Math.random() * 90};
}))
.padding(5)
.rotate(function() { return ~~(Math.random() * 2) * 90; })
.font("Impact")
.fontSize(function(d) { return d.size; })
.on("end", draw)
.start();
function draw(words) {
d3.select("body").append("svg")
.attr("width", 300)
.attr("height", 300)
.append("g")
.attr("transform", "translate(150,150)")
.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "Impact")
.style("fill", function(d, i) { return fill(i); })
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function(d) { return d.text; });
}
</script>
This is code from d3 jason davies about wordcloud
How to make d3 wordcloud to listen data from elasticsearch ?
You'll need to get the elasticsearch response data into a format that's easy to pass into the wordcloud sample code, something like this:
var data = [{
"text": "How",
"size": 20
}, {
"text": "to",
"size": 30
}, ... ]
See: http://jsfiddle.net/henbox/RUTpJ/659/
The response you get back from your Elasticsearch aggregation will look something like this:
You'll also see this in a console when the ES response is logged:
}).then(function (resp) {
console.log(resp);
...
So to manipulate that data, add:
var data = resp.aggregations.myaggregation.buckets.map(function(d) {
return {
text: d.key,
size: d.doc_count
};
});
Note that myaggregation is a name you will define. In your NFL example code above it's actually called touchdowns
Pushing this data straight into the wordcloud will cause problems, however. In the wordcloud example, the font-size is determined directly from the size, but there's a good chance your doc_counts are much too high, and will need to be scaled down.
For that, try D3 linear scale. In this case, it will scale the range of input values down to a value between 15 and 100, which can be used for font size:
var fontsize = d3.scale.linear()
.domain(d3.extent(data, function (d) {return d.size}))
.range([15, 100]);
Then, instead of
.style("font-size", function (d) {
return d.size + "px";
})
use:
.style("font-size", function (d) {
return fontsize(d.size) + "px";
})

Categories

Resources