d3 js - adding points to an area without path data parsing - d3.js

I got an area that generated by the following functions
area = d3.svg.area.radial().interpolate('cardinal-closed').angle(function (d) {
return angle(d.index);
}).innerRadius(function (d) {
return radius(d.y0);
}).outerRadius(function (d) {
return radius(d.y0 + d.y);
});
and I want to put a point(circle) on my data points, this is what I did:
makePoints = function(){
points = svg.selectAll(".point").data(layers[0].values);
points.enter().append("circle")
.attr("stroke", z(0))
.attr("fill", function(d, i) { return z(0) })
.attr("r", function(d, i) { return 3 });
points.exit().remove();
};
updatePoints = function() {
points.data(layers[0].values);
points.transition().attr("transform",
function (d, i) {
var vals = area([d]).split(',');
return "translate(" + vals[0].split('M')[1] + "," + vals[1].split('M')[0] + ")";
}
);
};
It works, but I think my code is crappy, how can I get the translate data without parsing the area result?

Related

How can I highlight a slice of multiple donut chart d3

I am beginner in d3 v3. I created multiple donut charts. When I move my mouse over a slice, I get the tooltip that appears. But I would also like the slice to grow a little bit.
I have already tried several codes but I can't get there. I wonder if the problem may be related to d3 tip
this is my js file :
//Source : http://bl.ocks.org/mbostock/1305337
var m = 15,
r = 80,
z = d3.scale.ordinal()
.range(["#50FFC5", "#54E868", "#54CCE8", "#6395FF"]);
var pie = d3.layout.pie()
.value(function (d) {
return +d.count;
})
.sort(function (a, b) {
return b.count - a.count;
});
var arc = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r);
var radius = d3.scale.linear()
.range([10, r])
var tip = d3.tip()
.attr('class', 'd3-tip')
.html(function (d) {
return d.data.genre + ": " + d.data.count;
})
.direction('s');
d3.csv("../CSV/genreHameau.csv", function (error, hameau) {
if (error) throw error;
var datas = d3.nest()
.key(function (d) {
return d.origine;
})
.entries(hameau);
datas.forEach(function (d) {
totalOrigin = d3.sum(d.values, function (d) {
return +d.count;
})
d.values.forEach(function (dd) {
dd.totalOrigin = totalOrigin
})
})
datas.sort(function (a, b) {
return d3.descending(a.values[0].totalOrigin, b.values[0].totalOrigin)
})
// définir le radius / rayon des arcs (rendre fonction de 'count')
var max = d3.max(datas, function (d) {
return d.values[0].totalOrigin
})
var min = d3.min(datas, function (d) {
return d.values[0].totalOrigin
})
radius.domain([min, max])
arc
.innerRadius(function (d) {
return radius(d.data.totalOrigin) / 2
})
.outerRadius(function (d) {
return radius(d.data.totalOrigin)
})
function size(d) {
return radius(d.values[0].totalOrigin) + m
}
var div = d3.select("body").selectAll("div")
.data(datas)
.enter().append("div") // http://code.google.com/p/chromium/issues/detail?id=98951
.style("display", "inline-block")
.style("width", function (d) {
return 2 * size(d) + "px"
})
.style("height", function (d) {
return 2 * size(d) + "px"
})
.style("min-width", "80px")
.style("min-height", "80px")
div.append("span")
.attr("class", "nomhameau")
.text(function (d) {
return d.key;
})
.append("span")
.attr("class", "nombrehameau")
.text(function (d) {
return " (" + d.values[0].totalOrigin + ")"
});
var svg = div.append("svg")
//.attr("width", (r + m) * 2)
//.attr("height", (r + m) * 2)
.attr("width", function (d) {
return 2 * size(d)
})
.attr("height", function (d) {
return 2 * size(d)
})
.append("g")
.attr("transform", function (d) {
return "translate (" + size(d) + "," + size(d) + ")"
});
svg.call(tip);
var g = svg.selectAll("g")
.data(function (d) {
return pie(d.values);
})
.enter().append("g")
.on("mouseover", tip.show)
.on("mouseout", tip.hide)
g.append("path")
.attr("d", arc)
.style("fill", function (d) {
return z(d.data.genre);
})
.append("title")
});
Thank you for your help
I would do it this way:
Just like you created the arc, create a new one with a greater radius, either inner, outer or both, something like:
var arcHighlight = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r*1.1);
Then you add mouseover and mouseout events to the slice and modify the slice doing something like:
// Add a colored arc path, with a mouseover title showing the count.
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return z(d.data.carrier); })
// new code
.on('mouseover', function(d) {
d3.select(this)
.transition()
.attr('d', arcHighlight(d));
})
.on('mouseout', function(d) {
d3.select(this)
.transition()
.attr('d', arc(d));
})
.append("title")
.text(function(d) { return d.data.carrier + ": " + d.data.count; });
The transition is optional, but it looks fancier ;)

D3 Zoomable Circle Packing throws NaN erros

I've constructed a D3 visualization that was working on my local machine. However, now i've exported to my server, the code breaks and throws several errors:
Error: invalid value for <circle> attribute transform="translate"(NaN,NaN)"
Error: invalid value for <text> attribute transform="translate"(NaN,NaN)"
Error: invalid value for <circle> attribute r="NaN"
I've had these errors before with similar code and was able to solve them. However, i cannot grasp what is going wrong. Any suggestions? Thanx!
function drawBubbles() {
var margin = 20,
diameter = 740;
var color = d3.scale.linear()
.domain([-1, 10])
.range(["hsl(152,80%,80%)", "hsl(228,30%,40%)"])
.interpolate(d3.interpolateHcl);
var pack = d3.layout.pack()
.size([diameter - margin, diameter - margin])
.value(function (d) { return d.size; })
var svg = d3.select("form").append("svg")
.attr("width", 1280)
.attr("height", 800)
.append("g")
.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
d3.json("../Resources/output.json", function (error, root) {
if (error) throw error;
var focus = root,
nodes = pack.nodes(root),
view;
var circle = svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("class", function (d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
.style("fill", function (d) { return d.children ? color(d.depth) : null; })
.on("click", function (d) { if (focus !== d) zoom(d), d3.event.stopPropagation(); });
var text = svg.selectAll("text")
.data(nodes)
.enter().append("text")
.attr("class", "label")
.style("fill-opacity", function (d) { return d.parent === root ? 1 : 0; })
.style("display", function (d) { return d.parent === root ? "inline" : "none"; })
.text(function (d) { return d.name; });
var node = svg.selectAll("circle,text");
d3.select("form")
.on("click", function () { zoom(root); });
zoomTo([root.x, root.y, root.r * 2 + margin]);
function zoom(d) {
var focus0 = focus; focus = d;
var transition = d3.transition()
.duration(d3.event.altKey ? 7500 : 750)
.tween("zoom", function (d) {
var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
return function (t) { zoomTo(i(t),d); };
});
transition.selectAll("text")
.filter(function (d) { return d.parent === focus || this.style.display === "inline"; })
.style("fill-opacity", function (d) { return d.parent === focus ? 1 : 0; })
.each("start", function (d) { if (d.parent === focus) this.style.display = "inline"; })
.each("end", function (d) { if (d.parent !== focus) this.style.display = "none"; });
}
function zoomTo(v) {
var k = diameter / v[2]; view = v;
console.log(d.x)
console.log(d.y)
console.log(d.r)
console.log(k)
node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
circle.attr("r", function(d) { return d.r * k; });
}
});
d3.select(self.frameElement).style("height", diameter + "px"); }
I had exactly this issue - although most browsers seemed to treat NaN as zero and so not complain, but Chrome wasn't happy at all.
For me I found the packed nodes collection returned from the packer had NaNs for zero value items passed in (which my data could contain). I just looped all the nodes after the pack call and set them to zero. Something like this should do it (it should probably be a recursive call to avoid duplication, but you get the idea!)...
// Replace any zero value items with zero x/y/r values
nodes.forEach( function (currNode) {
if (!currNode.count) {
currNode.x = 0;
currNode.y = 0;
currNode.r = 0;
currNode.children.forEach( function (currChild) {
if (!currChild.count) {
currChild.x = 0;
currChild.y = 0;
currChild.r = 0;
}
});
}
});
Hope this helps you

Simple Colission Example With g groups?

Simple collision example, using g groups?
Hi all, i test this examples:
1- http://bl.ocks.org/mbostock/3231298 - this example use a invisible circle for generate the force.
2- http://bl.ocks.org/mbostock/1747543 - this example use a central circle for force layout
This examples always use a circles.
My idea is use pie charts with collision force but i can't find a simple example of this.
I use this construction:
chart_vars_object.svg = d3.select(".canvas_svg").append("svg")
.attr({
"width": general_vars_object.width,
"height": general_vars_object.height
})
var nodes = chart_vars_object.svg.selectAll(".pie")
.data(chart_vars_object.json_init_data)
.enter()
.append("g")
.attr("class", function(d) {
return "pie pie_id_" + d.hash_chart_id
})
.attr("width", chart_vars_object.general_radius * 2)
.attr("height", chart_vars_object.general_radius * 2)
.attr("hash_link", function(d) {
return d.hash_link
})
.call(chart_vars_object.force.drag);
chart_vars_object.force.on("tick", function(e) {
chart_vars_object.svg.selectAll(".pie")
.attr("transform", function(d, i) {
return "translate(" + Math.round(d.x) + "," + Math.round(d.y) + ")";
});
});
//CREATE ARC GROUP's
var g = nodes.selectAll(".arc")
.data(function(d) {
return chart_vars_object.pie(d.hash_taggeds);
})
.enter().append("g")
.attr("class", "arc_group")
.attr("hash_type", function(d, i) {
if (i == 0 ) {
return "article";
}
if (i == 1 ) {
return "image";
}
if (i == 2 ) {
return "video";
}
})
.on("click", function() {
d3.select(this).select('.arc')
alert('Abro el Has Tag');
})
.on("mouseover", function() {
d3.select(this).select('.arc')
.classed("active", true )
})
.on("mouseout", function() {
d3.select(this).select('.arc')
.classed("active", false)
});
//CREATE PATHS WITH ARC
chart_vars_object.path = g.append("path")
.attr("class","arc")
.style({'cursor': 'pointer', 'opacity': '0.9'})
chart_vars_object.path.transition()
.duration(20)
.attr("fill", function(d) {
return chart_vars_object.color(d.data.hash_taggeds_name);
})
.attr("d", chart_vars_object.arc)
.each(function(d) {
this._current = d;
});
Any suggest?

D3 update pie data with zero values

I am updating a pie chart with a dynamic JSON feed. My update function is below
function updateChart(data) {
arcs.data(pie(data)); // recompute angles, rebind data
arcs.transition()
.ease("elastic")
.duration(1250)
.attrTween("d", arcTween)
sliceLabel.data(pie(data));
sliceLabel.transition()
.ease("elastic").duration(1250)
.attr("transform", function (d) {
return "translate(" + arc2.centroid(d) + ")";
})
.style("fill-opacity", function (d) {
return d.value == 0 ? 1e-6 : 1;
});
}
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function (t) {
return arc(i(t));
};
When the JSON has 0 values for all objects, the arcs and labels disappear. Exactly what I want to happen.
The problem is when I pass a new JSON after one that was full of zeros, the labels come back and tween etc but the arcs never redraw.
Any suggestions on correcting my update function so that the arcs redraw correctly after they their d values have been pushed to zero?
-- edit --
Lars suggested below that I use the .enter() exactly in the same way as I did when I created the graph. I tried doing this but the results did not change. See new update function below.
this.updatePie = function updateChart(data) {
arcs.data(pie(data))
.enter()
.append("svg:path")
.attr("stroke", "white")
.attr("stroke-width", 0.5)
.attr("fill", function (d, i) {
return color(i);
})
.attr("d", arc)
.each(function (d) {
this._current = d
})
arcs.transition()
.ease("elastic")
.duration(1250)
.attrTween("d", arcTween)
sliceLabel.data(pie(data));
sliceLabel.transition()
.ease("elastic").duration(1250)
.attr("transform", function (d) {
return "translate(" + arc2.centroid(d) + ")";
})
.style("fill-opacity", function (d) {
return d.value == 0 ? 1e-6 : 1;
});
}
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function (t) {
return arc(i(t));
};
}
You've actually hit a bug in D3 there -- if everything is zero, the pie layout returns angles of NaN, which cause errors when drawing the paths. As a workaround, you can check whether everything is zero and handle this case separately. I've modified your change function as follows.
if(data.filter(function(d) { return d.totalCrimes > 0; }).length > 0) {
path = svg.selectAll("path").data(pie(data));
path.enter().append("path")
.attr("fill", function(d, i) { return color(d.data.crimeType); })
.attr("d", arc)
.each(function(d) { this._current = d; });
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
} else {
path.remove();
}
Complete jsbin here.

d3 fisheye(d) returns undefined object

So I am working with the d3 fisheye plugin, and I am having some pretty basic problems.
I implemented this very basic code, pretty much a direct copy from here https://github.com/d3/d3-plugins/tree/master/fisheye
fisheye = d3.fisheye.circular()
.radius(200)
.distortion(2);
//initialize fisheye
chart.on("mousemove", function() {
fisheye.focus(d3.mouse(this));
dataPoint.each(function(d) { d.fisheye = fisheye(d); })
.attr('y', function(d){ return d.fisheye.y; })
.attr('x', function(d){ return d.fisheye.x; });
});
But d.fisheye.x and d.fisheye.y are undefined. In fact, looking at fisheye(d), it returns:
{x: undefined, y: undefined, z: 1}
On the other hand, d3.mouse(this) is properly returning an array.
Does any one have suggestion on why this might be occurring?
More code: by the way, the code is like this because it is inside a ext-js panel, so each function (drawWords is a property of this object). It is kind of complicated which is why I hesitated to post it all, and this is still not all the code, but the relevant part I think. I didn't include the initialization of any of the other global variables, or helper functions.
//imagine some sort of onload function
onLoad: function () {
this.drawWords();
this.animateVis();
}
,drawWords: function () {
toolObject = this;
var h = this.body.getHeight(),
w = this.body.getWidth();
//initialize word text
this.dataPoint = this.chart.selectAll('text')
.data(toolObject.termometerData, function (d) {return d.word;})
.enter().append('text')
.attr('class', 'points')
.attr('id', function(d) {return d.word + '-point';})
.attr('x', function() {
return toolObject.xScale(toolObject.shiftCount);
})
.attr('y', function (d) {
return toolObject.fanVertical(d, toolObject.shiftCount);
})
.attr('transform', function (d) {
var thisXPosition = toolObject.xScale(toolObject.shiftCount),
thisYPosition = toolObject.fanVertical(d, toolObject.shiftCount);
return 'translate(0, 0) rotate(-20 ' + thisXPosition + ' ' + thisYPosition + ')';
})
.attr('text-anchor', 'middle')
.attr('fill-opacity', function (d) {return toolObject.opacityScale(0);})
.text(function(d) {return d.word;});
this.applyFisheye();
}
,fisheye: d3.fisheye.circular()
.radius(200)
.distortion(2)
,applyFisheye: function () {
var toolObject = this;
//initialize fisheye
this.chart.on("mousemove", function() {
fisheye.focus(d3.mouse(this));
toolObject.dataPoint.each(function(d) { d.fisheye = toolObject.fisheye(d); })
.attr('y', function(d){ return d.fisheye.y; })
.attr('x', function(d){ return d.fisheye.x; })
.attr('transform', function(d){
return 'translate(0, 0) rotate(-20 ' + d.fisheye.x + ' '+ d.fisheye.y + ')';
});
});
}
,animateVis: function () {
var toolObject = this;
var h = this.body.getHeight(),
w = this.body.getWidth();
var tick;
if(this.animationIdArray.length < 1){
tick = setInterval(function(){
animate();
}, this.duration);
this.animationIdArray.push(tick);
}
function animate() {
if(toolObject.shiftCount < toolObject.numDataPoints){
toolObject.shiftCount++;
//animate words
toolObject.dataPoint.transition()
.duration(toolObject.duration)
.ease('linear')
.attr('x', function(d){ return toolObject.xScale(toolObject.shiftCount - 1); })
.attr('y', function(d){ return toolObject.fanVertical(d, toolObject.shiftCount - 1); })
.attr('transform', function(d){
var thisXPosition = toolObject.xScale(toolObject.shiftCount - 1),
thisYPosition = toolObject.fanVertical(d, toolObject.shiftCount - 1);
return 'translate(0, 0) rotate(-20 ' + thisXPosition + ' '+ thisYPosition + ')';
})
.attr('fill-opacity', function(d){
return toolObject.opacityScale(d.series[toolObject.shiftCount - 1].freq);
});
toolObject.applyFisheye();
}else{
clearInterval(tick);
toolObject.animationIdArray.shift();
}
}
}
fisheye assumes that the x and y coordinates of your objects are defined by keys named "x" and "y". It's probably enough (but possibly overkill, depending how often this code is called) to use
this.dataPoint
.each(function(d) {
d.x = toolObject.xScale(toolObject.shiftCount);
d.y = toolObject.fanVertical(d, toolObject.shiftCount)
.attr('x', function(d) { return d.x; })
.attr('y', function(d) { return d.y; });
when you //initialize word text

Resources