How to nest a d3.arc inside another? - d3.js

I'm attempting to nest an arc generated using d3.arc perfectly inside another arc.
I can do this by drawing "on my own":
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
function arc_position(x, y, radius, angle) {
return {
x: x + (radius * Math.cos(angle)),
y: y + (radius * Math.sin(angle))
};
}
function describe_arc(x, y, radius, startAngle, endAngle) {
var s = arc_position(x, y, radius, endAngle);
var e = arc_position(x, y, radius, startAngle);
var sweep = e - s <= 180 ? '0' : '1';
var d = [
'M', s.x, s.y,
'A', radius, radius, 0, sweep, 0, e.x, e.y
].join(' ');
return d;
}
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500)
.append("g");
var s = arc_position(250, 250, 200, Math.PI/2);
var e = arc_position(250, 250, 200, (Math.PI * 3)/2);
svg.append("path")
.style("fill", "none")
.style("stroke", "orange")
.style("stroke-width", "20px")
.attr("d", describe_arc(250,250,180,(Math.PI * 3)/2, Math.PI/2));
svg.append("path")
.style("fill", "none")
.style("stroke", "orange")
.style("stroke-width", "20px")
.attr("d", "M" + (s.x + 30) + "," + s.y + "L" + (e.x + 30) + "," + e.y);
svg.append("path")
.style("fill", "none")
.style("stroke", "steelblue")
.style("stroke-width", "20px")
.attr("d", describe_arc(250,250,200,(Math.PI * 3)/2, Math.PI/2));
svg.append("path")
.style("fill", "none")
.style("stroke", "steelblue")
.style("stroke-width", "20px")
.attr("d", "M" + (s.x + 10) + "," + s.y + "L" + (e.x + 10) + "," + e.y);
</script>
</body>
</html>
But I can't figure out a methodology using d3.arc:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500)
.append("g")
.attr("transform","translate(250,250)");
var arc = d3.arc()
.innerRadius(0)
.outerRadius(200)
.startAngle(0)
.endAngle(Math.PI);
svg.append("path")
.style("fill", "none")
.style("stroke", "steelblue")
.style("stroke-width", "20px")
.attr("d", arc());
arc.outerRadius(200 - 40);
svg.append("path")
.style("fill", "none")
.style("stroke", "orange")
.style("stroke-width", "20px")
.attr("d", arc())
.attr("transform", "translate(20,0)")
</script>
</body>
</html>
Any ideas?

I don't think that there is a good way to do this just using d3.arc because that is meant for drawing sections of circles and you're trying to draw a partial ellipse.
You can get close by generating the angle offset using the stroke width and the radius of the inner arc as offsetAngle = sin(stroke width / inner radius). The arc's startAngle is the offsetAngle and the endAngle is Math.PI - offsetAngle.
Unfortunately, that will generate a path which includes the center point of the circle. You can hack together something that works by just removing the L0,0 from the generated path (innerArc().replace("L0,0","")) and this will give you what you want, albeit in an ugly fashion.
Because it is a fairly simple path, it is probably best to use your own path generator to do this instead.
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500)
.append("g")
.attr("transform","translate(250,250)");
var outerRadius = 200;
var stroke = 20;
var innerRadius = outerRadius - stroke;
var innerAngle = Math.sin(stroke/innerRadius);
var outerArc = d3.arc()
.innerRadius(0)
.outerRadius(outerRadius)
.startAngle(0)
.endAngle(Math.PI);
var innerArc = d3.arc()
.innerRadius(0)
.outerRadius(innerRadius)
.startAngle(innerAngle)
.endAngle(Math.PI - innerAngle);
svg.append("path")
.style("fill", "none")
.style("stroke", "steelblue")
.style("stroke-width", stroke)
.attr("d", outerArc());
svg.append("path")
.style("fill", "none")
.style("stroke", "orange")
.style("stroke-width", stroke)
.attr("d", innerArc().replace("L0,0",""));
</script>
</body>
</html>

(This is not an answer, but just a comment, which I chose to disguise as an answer because I need the S.O. snippet)
I believe that Paul S is right, and he deserves the big prize of the green tick: what you're trying to paint in the inner (orange) path is not an arc of a circumference, but an ellipse instead. And, as the docs say,
The arc generator produces a circular or annular sector
, which will not work for creating an elliptic sector.
You can easily see that in the following demo: using a for loop, we draw arcs with decreasing outer radius. The only way to perfectly fill the internal space of each arc is if the next, smaller arch has the same center of the precedent, bigger one:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500)
.append("g")
.attr("transform","translate(250,250)");
var arc = d3.arc()
.innerRadius(0)
.outerRadius(200)
.startAngle(0)
.endAngle(Math.PI);
var color = d3.scaleOrdinal(d3.schemeCategory10);
for(var i = 0; i<11; i++){
svg.append("path")
.style("stroke", color(i))
.style("fill", "none")
.style("stroke-width", "20px")
.attr("d", arc());
arc.outerRadius(200 - 20*i);
};
</script>
</body>
</html>
There is an even easier way to visualize that. Let's create a single arc, with a huge stroke:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500)
.append("g")
.attr("transform","translate(250,250)");
var arc = d3.arc()
.innerRadius(0)
.outerRadius(150)
.startAngle(0)
.endAngle(Math.PI);
svg.append("path")
.style("fill", "none")
.style("stroke", "steelblue")
.style("stroke-width", "100px")
.attr("d", arc());
</script>
</body>
</html>
Look at the blue arc: it is a semicircle. Now look at the white space inside it: it's clearly not a semicircle. The blue thing is a semicircle, but the space inside it is not.
So, as d3.arc() right now doesn't have the option to set two different focal points, the options are creating your own function (as you did) or hacking the path (as Paul S did).

You draw second arc with outer radius R - 2 * strokewidth and center shifted by strokewidth.
But smaller arc should have the same center as larger one and outer radius R - strokewidth

Related

Make donut chart clickable

Can anybody help me regarding how I can make my following donut chart Clickable? I am just creat a donut chart from some dummy data and want each portion of the donut to be clickable. I am quite new in D3 and finding it hard to incorporate the click function in the donut chart.
I am finding it difficult to make each portion of the donut chart clickable in d3.js. Any help is appreciated. I have added my code snippet here.
function myFunction(width,height,margin,datax,dis,hg) {
var radius = Math.min(width, height) / 2 - margin
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", hg)
.append("g")
.attr("transform", "translate(" + width / dis + "," + height / 2 + ")");
var color = d3.scaleOrdinal()
.domain(Object.keys(data))
.range(d3.schemeDark2);
var pie = d3.pie()
.sort(null)
.value(function(d) {return d.value; })
var data_ready = pie(d3.entries(data))
var arc = d3.arc()
.innerRadius(radius * 0.5)
.outerRadius(radius * 0.8)
svg
.selectAll('allSlices')
.data(data_ready)
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d){ return(color(d.data.key)) })
.attr("stroke", "white")
.style("stroke-width", "2px")
.style("opacity", 0.7)
; }
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
You just need to select all path and bind event click on it
svg.selectAll('path')
.on('click', (d, i, n) => {
console.log(d, i, n)
})

D3 animate an arbitrary svg along a path

I am trying to modify a d3 animation example at http://bl.ocks.org/JMStewart/6455921
!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500);
var line = d3.svg.diagonal();
var lineData = {source:{x:25, y:25},
target:{x:400, y:400}};
var path = svg.append("path")
.attr("d", line(lineData))
.style("stroke", "black")
.style("fill", "none");
svg.append("circle")
.attr("cx", 25) //Starting x
.attr("cy", 25) //Starting y
.attr("r", 25)
.transition()
.delay(250)
.duration(1000)
.ease("linear")
.tween("pathTween", function(){return pathTween(path)})
// .tween("pathTween", pathTween); //Custom tween to set the cx and cy attributes
function pathTween(path){
var length = path.node().getTotalLength(); // Get the length of the path
var r = d3.interpolate(0, length); //Set up interpolation from 0 to the path length
return function(t){
var point = path.node().getPointAtLength(r(t)); // Get the next point along the path
d3.select(this) // Select the circle
.attr("cx", point.x) // Set the cx
.attr("cy", point.y) // Set the cy
}
}
</script>
</body>
</html>
What I would like to do is to replace the circle in the animation with a valid SVG segment and animate that along the path. This is what my code looks like. I am able to append the svg image of a person but I cannot animate the same as the circle. What am I missing?
<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500);
var line = d3.svg.diagonal();
var lineData = {source:{x:25, y:25},
target:{x:400, y:400}};
var person = `
<g transform="scale(.4 .4)">
<g>
<path d="M40.84,0.14 C36.68,0.82 33.28,3.92 32.24,7.99 C30.99,12.84 33.29,17.87 37.77,20.09 C39.42,20.91 40.54,21.15 42.55,21.14 C43.94,21.13 44.28,21.1 45.12,20.87 C48.75,19.9 51.61,17.13 52.64,13.59 C53.39,10.99 53.15,8.39 51.95,5.95 C50.56,3.14 48.08,1.12 45.05,0.34 C43.82,0.03 42.04,-0.06 40.84,0.14 z" fill="#000000"/>
<path d="M37.65,23.45 C36.16,23.69 34.56,24.31 33.22,25.17 C32.48,25.63 11.89,42.32 11.2,43 C10.6,43.59 10.21,44.21 9.96,44.97 C9.74,45.67 5.22,66.05 5.14,66.7 C5.06,67.42 5.29,68.5 5.7,69.3 C6.57,71.02 8.63,72.08 10.53,71.79 C11.44,71.65 12.48,71.11 13.19,70.39 C14.1,69.48 14.41,68.76 14.96,66.3 C15.51,63.8 18.47,50.32 18.52,50.07 C18.54,49.94 25.9,43.87 25.97,43.93 C25.98,43.94 23.43,55.27 20.3,69.11 L14.62,94.27 L7.77,105.77 C4,112.1 0.8,117.54 0.67,117.86 C-0.24,120.04 -0.21,121.98 0.76,123.99 C1.14,124.77 1.36,125.07 2.08,125.8 C3.07,126.79 3.94,127.32 5.12,127.67 C7.62,128.39 10.09,127.8 12.01,126.03 C12.34,125.72 12.81,125.19 13.04,124.86 C13.85,123.72 28.21,99.4 28.8,98.17 L29.39,96.95 L31.17,88.72 C32.14,84.2 32.97,80.51 33.01,80.52 C33.13,80.58 48.3,97.21 48.3,97.29 C48.3,97.45 52.89,121.54 53.06,122.25 C53.64,124.78 55.56,126.91 57.94,127.66 C61.24,128.7 64.9,127.16 66.46,124.08 C67.17,122.68 67.39,120.84 67.06,119.09 C66.98,118.68 66.61,116.76 66.25,114.84 C65.89,112.92 65.1,108.76 64.5,105.6 C63.9,102.44 63.11,98.27 62.75,96.35 C62.1,92.9 61.8,91.73 61.41,91.09 C61.3,90.91 56.99,85.77 51.83,79.67 C46.67,73.58 42.46,68.53 42.47,68.47 C42.55,68.03 47.22,47.54 47.25,47.51 C47.27,47.49 48.21,49.32 49.33,51.57 C50.91,54.72 51.48,55.77 51.84,56.17 C52.41,56.8 67.96,67.56 68.95,68.01 C70.84,68.86 72.94,68.47 74.34,67.01 C76.07,65.19 76.13,62.38 74.47,60.54 C74.16,60.19 71.78,58.5 66.62,54.94 L59.22,49.85 L57.77,46.95 C51.34,34.17 48.46,28.54 48.11,28.07 C45.54,24.58 41.55,22.82 37.65,23.45 z" fill="#000000"/>
</g>
</g>`
var path = svg.append("path")
.attr("d", line(lineData))
.style("stroke", "black")
.style("fill", "none");
svg.append('g').attr('id','person').html(person)
.attr("cx", 25) //Starting x
.attr("cy", 25) //Starting y
.attr("r", 25)
.transition()
.delay(250)
.duration(1000)
.ease("linear")
.tween("pathTween", function(){return pathTween(path)})
// .tween("pathTween", pathTween); //Custom tween to set the cx and cy attributes
function pathTween(path){
var length = path.node().getTotalLength(); // Get the length of the path
var r = d3.interpolate(0, length); //Set up interpolation from 0 to the path length
return function(t){
var point = path.node().getPointAtLength(r(t)); // Get the next point along the path
d3.select(this) // Select the circle
.attr("cx", point.x) // Set the cx
.attr("cy", point.y) // Set the cy
}
}
</script>
</body>
</html>
cx and cy are attributes valid for the circle only. To animate a group, use a transform attribute with the translate(x,y) value. More about transform here: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform
You could define your person with a <svg> element - they can be nested, and you can use x and y attributes for positioning:
var person = `
<g transform="scale(.4 .4)">
<path d="M40.84,0.14 C36.68,0.82 33.28,3.92 32.24,7.99 C30.99,12.84 33.29,17.87 37.77,20.09 C39.42,20.91 40.54,21.15 42.55,21.14 C43.94,21.13 44.28,21.1 45.12,20.87 C48.75,19.9 51.61,17.13 52.64,13.59 C53.39,10.99 53.15,8.39 51.95,5.95 C50.56,3.14 48.08,1.12 45.05,0.34 C43.82,0.03 42.04,-0.06 40.84,0.14 z" fill="#000000"/>
<path d="M37.65,23.45 C36.16,23.69 34.56,24.31 33.22,25.17 C32.48,25.63 11.89,42.32 11.2,43 C10.6,43.59 10.21,44.21 9.96,44.97 C9.74,45.67 5.22,66.05 5.14,66.7 C5.06,67.42 5.29,68.5 5.7,69.3 C6.57,71.02 8.63,72.08 10.53,71.79 C11.44,71.65 12.48,71.11 13.19,70.39 C14.1,69.48 14.41,68.76 14.96,66.3 C15.51,63.8 18.47,50.32 18.52,50.07 C18.54,49.94 25.9,43.87 25.97,43.93 C25.98,43.94 23.43,55.27 20.3,69.11 L14.62,94.27 L7.77,105.77 C4,112.1 0.8,117.54 0.67,117.86 C-0.24,120.04 -0.21,121.98 0.76,123.99 C1.14,124.77 1.36,125.07 2.08,125.8 C3.07,126.79 3.94,127.32 5.12,127.67 C7.62,128.39 10.09,127.8 12.01,126.03 C12.34,125.72 12.81,125.19 13.04,124.86 C13.85,123.72 28.21,99.4 28.8,98.17 L29.39,96.95 L31.17,88.72 C32.14,84.2 32.97,80.51 33.01,80.52 C33.13,80.58 48.3,97.21 48.3,97.29 C48.3,97.45 52.89,121.54 53.06,122.25 C53.64,124.78 55.56,126.91 57.94,127.66 C61.24,128.7 64.9,127.16 66.46,124.08 C67.17,122.68 67.39,120.84 67.06,119.09 C66.98,118.68 66.61,116.76 66.25,114.84 C65.89,112.92 65.1,108.76 64.5,105.6 C63.9,102.44 63.11,98.27 62.75,96.35 C62.1,92.9 61.8,91.73 61.41,91.09 C61.3,90.91 56.99,85.77 51.83,79.67 C46.67,73.58 42.46,68.53 42.47,68.47 C42.55,68.03 47.22,47.54 47.25,47.51 C47.27,47.49 48.21,49.32 49.33,51.57 C50.91,54.72 51.48,55.77 51.84,56.17 C52.41,56.8 67.96,67.56 68.95,68.01 C70.84,68.86 72.94,68.47 74.34,67.01 C76.07,65.19 76.13,62.38 74.47,60.54 C74.16,60.19 71.78,58.5 66.62,54.94 L59.22,49.85 L57.77,46.95 C51.34,34.17 48.46,28.54 48.11,28.07 C45.54,24.58 41.55,22.82 37.65,23.45 z" fill="#000000"/>
</g>`
....
svg.append('svg').attr('id','person').html(person)
.attr("x", 25) //Starting x, left side
.attr("y", 25) //Starting y, top side
.transition()
....

Block in d3 code is deleting some of my data for a histogram

Im following a tutorial on how to create axis' for d3js graphs and I have become hung up. In the following code, when uncommenting out the code, the first two rectangles in my histogram disappear. Somehow when appending a rew rect it causes two of my data points to disappear.
index.html
<html>
<head>
<link href="style.css" rel="stylesheet">
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<script>
var data = [3,6,3,9,11,3,9,12,6] //data.length = 9
var max = d3.max(data);
var heightFactor = 50
var barWidth = 20, barPadding = 1;
var margin = {top:10, right:10, bottom:10, left:50};
var graphWidth = data.length * (barPadding + barWidth) - barPadding;
var totalWidth = margin.left + margin.right + graphWidth; //248
var totalHeight = max*heightFactor + margin.top + margin.bottom;
var newMargin = margin.left -10;
var svg = d3.select("body").append("svg")
.attr("height", totalHeight)
.attr("width", totalWidth)
.append("g");
svg.append("rect")
.attr("width", totalWidth)
.attr("height", totalHeight)
.attr("fill", "white")
.attr("stroke", "blue")
.attr("stroke-width", 1);
var graphGroup = svg
.append('g')
.attr('transform', 'translate(' + margin.right + ',' + margin.top + ")");
/* this is my problem
graphGroup.append('rect').attr({
fill: 'rgba(0,0,0,0.1)',
width: graphWidth,
height: totalHeight - (margin.bottom + margin.top)
});
*/
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("width", barWidth)
.attr("height", function(d){return d*heightFactor;})
.attr("fill", "steelblue")
.attr("x", function(d,i){return i*21})
.attr("y", function(d){return (max*50 - d*50) + margin.top})
</script>
</body>
</html>

Integrating d3 with meteor? Trying to draw pie charts

I'm trying to draw pie charts in Meteor, but I'm very new to both d3 and Meteor and am not really understanding what is going on.
The following d3 code to draw pie charts from a csv file works for me outside of Meteor:
<!DOCTYPE html>
<meta charset="utf-8">
<link href='http://fonts.googleapis.com/css?family=Montserrat:400,700' rel='stylesheet' type='text/css'>
<style>
body {
font: 30px "Montserrat";
text-transform:uppercase;
}
svg {
padding: 10px 0 0 10px;
}
.arc {
stroke: #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var radius = 150,
padding = 10;
var color = d3.scale.ordinal()
.range(["#f65c55","#c8e7ec"]);
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - 40);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Criteria"; }));
data.forEach(function(d) {
d.ages = color.domain().map(function(name) {
return {name: name, population: +d[name]};
});
});
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", radius * 2)
.attr("height", radius * 2)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 50 + ")"; });
legend.append("rect")
.attr("width", 40)
.attr("height", 40)
.style("fill", color);
legend.append("text")
.attr("x", 50)
.attr("y", 20)
.attr("dy", ".35em")
.attr("font-size","20px")
.text(function(d) { return d; });
var svg = d3.select("body").selectAll(".pie")
.data(data)
.enter().append("svg")
.attr("class", "pie")
.attr("width", radius * 2)
.attr("height", radius * 2)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
svg.selectAll(".arc")
.data(function(d) { return pie(d.ages); })
.enter().append("path")
.attr("class", "arc")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.name); });
svg.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.Criteria; });
});
</script>
I also have a Meteor template as follows that I want to draw these charts in:
<div class="tab-pane active" id="playback">
{{> playback}}
</div>
However, when I try and follow web tutorials to integrate the two, the graphs don't get drawn. Can anyone help me understand why? Thanks in advance!
EDIT: forgot to mention, data.csv looks like this:
Criteria,Disapproval, Approval
Too Fast,1,2
Too Slow,5,6
Clarity,2,3
Legibility,202070,343207
The first line is for the legend, and the rest are for 4 separate graphs.
You have to make sure that the template is rendered before you access the DOM elements by code. So put your D3 code inside a template rendered method, like this:
Template.playback.rendered = function() {
// your D3 code
}
or on the body tag e.g.:
UI.body.rendered = function() {
// your D3 code
}
Template.chart.rendered = function(){
Deps.autorun(function () {
//d3 codeing here!!
}
}
It's working for me. If you're coding without Deps.autorun() it's will not render.
Oh!! one morething at html page in you case maybe .
However for my case I using nvd3.org http://nvd3.org/livecode/index.html#codemirrorNav. And this I hope you will clearify.

d3.js: map with concentric circles emanating from dots

Been away from d3.js for a few months... and I've inherited a simple US map with features that someone else started.
The features are represented by simple dots of varying sizes.
I want to add emanating concentric circles to each dot, similar to the classic Onion example by Mike Bostock: http://bl.ocks.org/mbostock/4503672 (maybe not so ominous looking though)
I've got a block set up here: http://bl.ocks.org/mbostock/4503672
(Not sure why the states aren't rendering correctly in the block, but it probably doesn't matter for this.)
In Mike's example there is only one dot, so I'm have trouble understanding how to translate what he did to what I've got (many dots).
Here's my script:
/**
* Page initialization
*/
$(function() {
renderMap('#map-container');
});
function renderMap(container) {
var width = 960,
height = 500,
active;
var projection = d3.geo.albersUsa()
.scale(960)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var radius = d3.scale.sqrt()
.domain([0, 1e7])
.range([0, 10]);
var path2 = d3.geo.path()
.projection(projection);
// Remove svg, if already exist
d3.select(container).select('svg').remove();
var svg = d3.select(container).append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("width", width)
.attr("height", height);
//.on("click", reset);
var g = svg.append("g");
queue()
.defer(d3.json, "/mbostock/raw/4090846/us.json")
.defer(d3.json, "dots.json")
.await(function (error, us, centroid) {
g.append("g")
.attr("id", "states")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter().append("path")
.attr("d", path)
.attr("class", "state");
//.on('click', click);
g.append('path')
.attr("id", "state-borders")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("d", path)
.attr("class", "mesh");
var dots = g.append("g")
.attr("id", "dots")
.selectAll("path")
.data(centroid.data)
.enter().append("path")
.attr("class", "dot")
.attr("d", path2.pointRadius(function(d) { return radius(d.properties.pool); }));
}
);
}
and the key part of Mike's example for making the rings is:
setInterval(function() {
svg.append("circle")
.attr("class", "ring")
.attr("transform", "translate(" + projection([100, -8]) + ")")
.attr("r", 6)
.style("stroke-width", 3)
.style("stroke", "red")
.transition()
.ease("linear")
.duration(6000)
.style("stroke-opacity", 1e-6)
.style("stroke-width", 1)
.style("stroke", "brown")
.attr("r", 160)
.remove();
}, 750);
how do I get the rings positioned on the dots?
Review the differences between the two methods to learn a little bit more about how functional/declarative programming abstracts away the pain of iterative programming.
Approach with D3 idioms:
fiddle: http://jsfiddle.net/blakedietz/E66eT/1/
update: D3 Way
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
body {
background: #192887;
}
.graticule {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
.land {
fill: #007421;
}
.dot {
fill: #c7141a;
}
.ring {
fill: none;
stroke: #c7141a;
}
</style>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
var width = 960,
height = 500;
var projection = d3.geo.mercator()
.center([113, -3])
.scale(1275)
.translate([width / 2, height / 2])
.clipExtent([[0, 0], [width, height]])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule()
.step([5, 5]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
var data = [{x:-8,y:100},{x:-10,y:110},{x: -12,y:120}];
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class","dot")
.attr("transform",translateCircle)
.attr("r",8);
function translateCircle(datum, index)
{
return "translate(" + projection([datum.y, datum.x]) + ")";
};
setInterval(function(){
svg
.selectAll("ring")
.data(data)
.enter()
.append("circle")
.attr("class", "ring")
.attr("transform", translateCircle)
.attr("r", 6)
.style("stroke-width", 3)
.style("stroke", "red")
.transition()
.ease("linear")
.duration(6000)
.style("stroke-opacity", 1e-6)
.style("stroke-width", 1)
.style("stroke", "brown")
.attr("r", 160)
.remove();
}, 750)
d3.select(self.frameElement).style("height", height + "px");
</script>
</body>
</html>
So I didn't create a fully d3 idiomatic approach for this solution, but it will work. If you can get this to work implicitly within a svg.selectAll("circle"/"unique selection"...), etc, that would be even more awesome. I'll work on that in the mean time. Until then there's this more explicitly iterative approach.
With Mike's example you're only appending a single element to the D.O.M. in the setInterval call. In order to expedite the binding process, I've created a projection method which operates on a set of coordinates: the translateCircle will operate on a datum within a collection of coordinates allowing access to the internal attributes of each collection element.
Within each setInterval call the forEach method iterates over the collection of coordinates and then calls the same internals within the setInterval method that was called by Mike originally.
Not So D3
<style>
body {
background: #192887;
}
.graticule {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
.land {
fill: #007421;
}
.dot {
fill: #c7141a;
}
.ring {
fill: none;
stroke: #c7141a;
}
</style>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
var width = 960,
height = 500;
var projection = d3.geo.mercator()
.center([113, -3])
.scale(1275)
.translate([width / 2, height / 2])
.clipExtent([[0, 0], [width, height]])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule()
.step([5, 5]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
var data = [{x:-8,y:100},{x:-10,y:110},{x: -12,y:120}];
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class","dot")
.attr("transform",translateCircle)
.attr("r",8);
function translateCircle(datum, index)
{
return "translate(" + projection([datum.y, datum.x]) + ")";
};
setInterval(function(){
data.forEach(function(datum)
{
svg
.append("circle")
.attr("class", "ring")
.attr("transform", translateCircle(datum))
.attr("r", 6)
.style("stroke-width", 3)
.style("stroke", "red")
.transition()
.ease("linear")
.duration(6000)
.style("stroke-opacity", 1e-6)
.style("stroke-width", 1)
.style("stroke", "brown")
.attr("r", 160)
.remove();
})
}, 750)
d3.select(self.frameElement).style("height", height + "px");
</script>
</body>
</html>

Resources