I've created a d3 map with US states, following this example:
http://bl.ocks.org/mbostock/4699541
and added markers following this SO question:
Put markers to a map generated with topoJSON and d3.js
The problem is that on zoom, the map markers stay in place. I believe I need to translate them into a new position, but not sure how to make that happen.
var width = 900,
height = 500,
active = d3.select(null);
var projection = d3.geo.albersUsa()
.scale(1000)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select(".rebates").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", reset);
var g = svg.append("g")
.style("stroke-width", "1.5px");
d3.json("/files/d3-geo/us.json", function(error, us) {
if (error) { throw error; }
g.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter().append("path")
.attr("d", path)
.attr("class", function(item) {
return window.US_STATES[item.id].water_authorities > 0 ? 'avail' : 'unavail';
})
.on("click", clicked);
g.append("path")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("class", "mesh")
.attr("d", path);
});
d3.json('/files/coordinates.json', function(error, coords) {
if (error) { throw error; }
svg.selectAll(".mark")
.data(coords)
.enter()
.append("image")
.attr('class','mark')
.attr('width', 20)
.attr('height', 20)
.attr("xlink:href",'assets/gmap_red.png')
.attr("transform", function(d) {
return "translate(" + projection([d[1],d[0]]) + ")";
});
});
function clicked(d) {
if (active.node() === this) { return reset(); }
if (window.US_STATES[d.id].water_authorities === 0) { return; }
active.classed("active", false);
active = d3.select(this).classed("active", true);
var bounds = path.bounds(d),
dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2,
scale = .9 / Math.max(dx / width, dy / height),
translate = [width / 2 - scale * x, height / 2 - scale * y];
g.transition()
.duration(750)
.style("stroke-width", 1.5 / scale + "px")
.attr("transform", "translate(" + translate + ")scale(" + scale + ")");
}
function reset() {
active.classed("active", false);
active = d3.select(null);
rebatesTable.clear().draw();
g.transition()
.duration(750)
.style("stroke-width", "1.5px")
.attr("transform", "");
}
Step 1
Add all the points in the group and not in the svg.
This will ensure that the marker points translate with the main group.
g.selectAll(".mark")//adding mark in the group
.data(marks)
.enter()
.append("image")
.attr('class', 'mark')
.attr('width', 20)
.attr('height', 20)
.attr("xlink:href", 'https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/24x24/DrawingPin1_Blue.png')
.attr("transform", function(d) {
return "translate(" + projection([d.long, d.lat]) + ")";
});
Step2
Negate the scaling effect of the main group. else the markers will come zoomed up.
g.selectAll(".mark")
.transition()
.duration(750)
.attr("transform", function(d) {
var t = d3.transform(d3.select(this).attr("transform")).translate;//maintain aold marker translate
return "translate(" + t[0] +","+ t[1] + ")scale("+1/scale+")";//inverse the scale of parent
});
Step3
On zoom out make the marker scale back to 1.
g.selectAll(".mark")
.attr("transform", function(d) {
var t = d3.transform(d3.select(this).attr("transform")).translate;
console.log(t)
return "translate(" + t[0] +","+ t[1] + ")scale("+1+")";
});
Working code here
Hope this helps!
Related
I am trying to get Zoom to work, I am new to D3 and I find it very abstract and not intuitive. I recently finished a beginners course in JavaScript but D3 feels like a completely new language.
I found this topic which might help a bit.
D3 Zooming in graph
I also found the following code that created the graph on the web, the simplest I could find and I don't understand all of it. Now I wanna zoom in and used code that I also found on the web but which has to be adapted. I understood that much that the zoom variable at the top is calling a function called NeuerChart which has the actual zooming behaviour in it. It needs to zoom the graph and the axes when I spin the mousewheel.
In the end I need to implement this into a real problem, thanks. Using D3.v5.
<script>
let zoom = d3.zoom()
.scaleExtent([0.5, 10])
.extent([[0, 0], [width, height]])
.on('zoom', NeuerChart);
// Step 1
let min = 0;
let max = 100;
let x_arr = [];
let y_arr = [];
let s_arr = [];
let z_arr = [];
for (let i = 0; i < 360; i++) {
var r = Math.round(Math.random() * (max - min)) + min;
x_arr[i]= i;
y_arr[i]= r;
z_arr.push([x_arr[i],y_arr[i]]);
}
s_arr = y_arr.sort(function(a, b){return a - b});
let neu_arr = [];
let zz_arr = [];
for (let i = 0; i < 360; i++) {
neu_arr[i]= i;
zz_arr.push([neu_arr[i], s_arr[i]]);
}
console.log(z_arr);
console.log(zz_arr);
var dataset1 = zz_arr;
// Step 3
var svg = d3.select("svg"),
margin = 200,
width = svg.attr("width") - margin, //1700
height = svg.attr("height") - margin //700
// Step 4
var xScale = d3.scaleLinear().domain([0 , 365]).range([0, width]),
yScale = d3.scaleLinear().domain([0, 105]).range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
// Step 5
// Title
svg.append('text')
.attr('x', width/2 + 100)
.attr('y', 100)
.attr('text-anchor', 'middle')
.style('font-family', 'Helvetica')
.style('font-size', 20)
.text('Line Chart');
// X label
svg.append('text')
.attr('x', width/2 + 100)
.attr('y', height - 15 + 150)
.attr('text-anchor', 'middle')
.style('font-family', 'Helvetica')
.style('font-size', 12)
.text('Zeitachse');
// Y label
svg.append('text')
.attr('text-anchor', 'middle')
.attr('transform', 'translate(60,' + 500 + ')rotate(-90)')
.style('font-family', 'Helvetica')
.style('font-size', 12)
.text('Wert');
// Step 6
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale).ticks(7).tickValues([0, 60, 120, 180, 240, 300, 360]));
g.append("g")
.call(d3.axisLeft(yScale));
// Step 7
svg.append('g')
.selectAll("dot")
.data(dataset1)
.enter()
.append("circle")
.attr("cx", function (d) { return xScale(d[0]); } )
.attr("cy", function (d) { return yScale(d[1]); } )
.attr("r", 3)
.attr("transform", "translate(" + 100 + "," + 100 + ")")
.style("fill", "#CC0000");
// Step 8
var line = d3.line()
.x(function(d) { return xScale(d[0]); })
.y(function(d) { return yScale(d[1]); })
.curve(d3.curveMonotoneX)
svg.append("path")
.datum(dataset1)
.attr("class", "line")
.attr("transform", "translate(" + 100 + "," + 100 + ")")
.attr("d", line)
.style("fill", "none")
.style("stroke", "#CC0000")
.style("stroke-width", "2")
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.attr("transform", "translate(" + 100 + "," + 100 + ")")
.call(zoom);
function NeuerChart () {
// recover the new scale
var newX = d3.event.transform.rescaleX(xScale);
var newY = d3.event.transform.rescaleY(yScale);
// update axes with these new boundaries
xAxis.call(d3.axisBottom(newX))
yAxis.call(d3.axisLeft(newY))
}
</script>
I added the code here in Codepen:
https://codepen.io/Dvdscot/pen/zYjpzVP
This is how it should work:
https://codepen.io/Dvdscot/pen/BaxJdKN
Problem is solved, see the code at Codepen:
`
Reset zoom
`https://codepen.io/Dvdscot/pen/zYjpzVP
I am working on a multi-line scatterplot with zoom using d3 v6. I am new to d3 and based on different examples, I could get the zoom function working for the images/points. The problem is that the lines aren't zooming. I looked at many similar questions, but none of those solutions are working for me.
The code I am using:
var margin = {
top: 50,
right: 30,
bottom: 30,
left: 210,
};
var svg = d3.select("svg"),
width = 1410 - margin.left - margin.right,
height = 620 - margin.top - margin.bottom;
svg
.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
d3.csv("CSV_files/NSW_pathway.csv").then(function (data1) {
var groupData = d3.group(data1, (d) => d.pathway_name);
var xScale = d3.scaleLinear().domain([0, 1]).range([0, width]);
var yScale = d3.scaleLinear().domain([0, 1]).range([height, 0]);
var xAxis = d3.axisBottom(xScale).ticks(0).tickSize(-height);
var yAxis = d3.axisLeft(yScale).ticks(0).tickSize(-width);
var gX = svg
.append("g")
.attr(
"transform",
"translate(" + margin.left + "," + (margin.top + height) + ")"
)
.call(xAxis);
var gY = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(yAxis);
var focus = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "line")
.attr("clip-path", "url(#clip)");
const color = d3
.scaleOrdinal()
.range(["#e41a1c", "#377eb8", "#4daf4a", "#984ea3"]);
var points_g = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("clip-path", "url(#clip)")
.classed("points_g", true);
var label = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "label")
.attr("clip-path", "url(#clip)");
var div = d3
.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
const mouseover = function (event, d) {
div.style("opacity", 1);
};
const mousemove = function (event, d) {
div
.html(function (d1) {
if (d.type != "learner")
return `The resource name is ${d.resource_name}`;
else return `This is ${d.name}`;
})
.style("position", "absolute")
.style("left", event.pageX + 15 + "px")
.style("top", event.pageY + 15 + "px");
};
const mouseleave = function (event, d) {
div.transition().duration(200).style("opacity", 0);
};
var points = points_g.selectAll("point").data(data1);
points = points
.enter()
.append("image")
.attr("xlink:href", function (d) {
if (d.type == "video") return "Images/3.jpg";
else if (d.type == "pdf") return "Images/4.png";
else if (d.type == "none") return "Images/5.png";
})
.attr("x", function (d) {
return xScale(+d.x) - 10;
})
.attr("y", function (d) {
return yScale(+d.y) - 10;
})
.attr("width", 20)
.attr("height", 20)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseleave", mouseleave);
label
.selectAll(".text")
.data(data1)
.enter()
.append("text")
.text(function (d) {
return d.topic;
})
.attr("x", function (d) {
return xScale(+d.x) + 10;
})
.attr("y", function (d) {
return yScale(+d.y) + 10;
});
focus
.selectAll("line")
.data(groupData)
.enter()
.append("path")
.attr("fill", "none")
.attr("stroke", function (d) {
return color(d[0]);
})
.attr("stroke-width", 1)
.attr("d", function (d) {
return d3
.line()
.curve(d3.curveMonotoneX)
.x(function (d) {
return xScale(+d.x);
})
.y(function (d) {
return yScale(+d.y);
})(d[1]);
});
var zoom = d3
.zoom()
.scaleExtent([0.5, 20])
.extent([
[0, 0],
[width, height],
])
.on("zoom", zoomed);
svg
.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.lower();
svg.call(zoom).call(zoom.transform, d3.zoomIdentity);
function zoomed({ transform }) {
var new_xScale = transform.rescaleX(xScale);
var new_yScale = transform.rescaleY(yScale);
gX.call(xAxis.scale(new_xScale));
gY.call(yAxis.scale(new_yScale));
points
.data(data1)
.attr("x", function (d) {
return new_xScale(d.x) - 10;
})
.attr("y", function (d) {
return new_yScale(d.y) - 10;
});
label
.selectAll("text")
.data(data1)
.attr("x", function (d) {
return new_xScale(d.x) + 15;
})
.attr("y", function (d) {
return new_yScale(d.y) + 15;
});
focus.selectAll("line").attr("d", function (d) {
return d3
.line()
.curve(d3.curveMonotoneX)
.x(function (d) {
return xScale(+d.x);
})
.y(function (d) {
return yScale(+d.y);
})(d[1]);
});
}
});
A sample of the csv file:
x,y,name,type,topic,resource_name,pathway_name
0,0,start,none,Sponsored Search Markets,Networks Crowd and Markets_NCMch15.pdf,pathwayOne
0,0,start,none,Sponsored Search Markets,Networks Crowd and Markets_NCMch15.pdf,pathwayTwo
0.086511627906977,0.16,horse,pdf,Graphs,Networks Crowd and Markets_NCMch2.pdf,pathwayOne
0.12,0.283768436578171,choice,pdf,Network Centrality,Notes_CGT BASED network CENTRALITY - L2.pdf,pathwayTwo
0.32,0.27217943628424,plex,video,Network Models,Network Analysis_LNch13.pdf,pathwayOne
0.775398773006135,0.33,social,pdf,Clustering,Network Analysis_LNch8.pdf,pathwayTwo
1,1,end,none,Allocation in Networks,Notes_Allocation in networks with DON-L3.pdf,pathwayOne
1,1,end,none,Allocation in Networks,Notes_Allocation in networks with DON-L3.pdf,pathwayTwo
Thank you for your help.
It's not zooming the whole page, it's zooming the whole svg, your large margins extend beyond the charting area. One solution is to add the g element not on your svg but only on your chart area.
But using your code, there are 2 things preventing your lines from zooming.
1: your selection is empty - line is a d3 abstraction that returns a path
function zoomed() {
...
// empty selection
console.log(focus.selectAll('line'))
// try instead
console.log(focus.selectAll('path'))
}
2: Simple mistake - you're using the old scale not the new one
function zoomed() {
...
focus.selectAll('path').attr('d', d => {
return d3.line()
// using old scale
.x(di => xScale(+di.x))
// change to
.x(di => new_xScale(+di.x))
})
}
I don't have a sample of your csv file so this isn't tested, but if you want to zoom the whole chart just add a parent g after your svg and transform that..
...
svg
.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
// NEW - add g
.append('g')
// NEW - adjust scaleExtent to your needs
const zoom = d3.zoom()
.scaleExtent([1, 8])
.on('zoom', updateChart)
svg.call(zoom)
function updateChart(event) {
svg.attr('transform', event.transform)
}
Note that this also adds pan, but if you only want zoom you can use:
let scale = 1
...
function updateChart(event) {
if(event.transform.k === scale) { return }
svg.attr('transform', event.transform)
scale = event.transform.k
}
Hopefully this is the right place for this. This is my first time working in d3 and I'm tracking some paths across latitudes (.bars) on a mercator projection map of the world.
While I'm nowhere near where I want to be with this map (trying to animate change over time, and then include tooltips to the .bars): I'm stuck with the zoom feature. I've gotten it to change the scale of the projection when zooming, but it has no effect on my rectangle shapes (.bars). Could someone take a look and let me know what's going on? Why are the rectangles not scaling on zoom? Am I not correctly accessing them in my zoom function?
var width = 960,
height = 550,
scale0 = (width - 1) / 2 / Math.PI;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
// .append("g");
var g = svg.append("g");
var bars = svg.append("rect");
var zoom = d3.behavior.zoom()
.translate([width / 2, height / 2])
.scale(scale0)
.scaleExtent([scale0, 8 * scale0])
.on("zoom", zoomed);
var color = d3.scale.quantize() // Takes data value inputs and will return colors
.range(['rgb(215,48,39)','rgb(244,109,67)','rgb(253,174,97)','rgb(254,224,144)','rgb(255,255,191)','rgb(224,243,248)','rgb(171,217,233)','rgb(116,173,209)','rgb(69,117,180)']);
var projection = d3.geo.mercator()
.scale((width + 1) / 2 / Math.PI)
.translate([width / 2, height / 2])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
svg
.call(zoom)
.call(zoom.event);
d3.json("world-110m.json", function(error, world) {
if (error) throw error;
g.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
g.insert("path", ".graticule")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
d3.csv("data/apr_dove.csv", function(data) {
// Returns value based on min/max of data set to Colorbrewer colors
color.domain([
d3.max(data, function(d) { return d.average_revisit; }),
d3.min(data, function(d) { return d.average_revisit; })
]);
// Defining the rectangle's attributes by monthly data of satellites (latitude and average revisit_rate)
g.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("width", width)
.attr("class", "bars")
.attr("height", function(d) {
// console.log(projection.translate([0, 0.5])[1])
return projection([0, d.latitude - 0.5])[1] - projection([0, d.latitude])[1];
})
.attr("opacity", .6)
.style("fill", function(d) {
//Get data value
var value = d.average_revisit;
if (value) {
//If value exists…
return color(value);
} else {
//If value is undefined…
return "#ccc";
}
})
//Define position of each rectangle by it's latitude from the data
.attr("transform", function(d) {
return "translate(" + projection([-180, d.latitude]) + ")"
})
.attr("d", path);
});
});
function zoomed() {
projection
.translate(zoom.translate())
.scale(zoom.scale());
svg.selectAll("*")
.attr("d", path);
}
d3.select(self.frameElement).style("height", height + "px");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="https://gist.github.com/sadbumblebee/64581d15b89b361a17a85e8f37172673.js"></script>
<script src="https://gist.github.com/sadbumblebee/359fc826e4e55c8c3dcb3d279783e910.js"></script>
One idea to get the appropriate width would be to basis it on the size of your land path:
var land;
d3.json("world-110m.json", function(error, world) {
if (error) throw error;
land = g.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
....
function getLandWidth() {
return land ? land.node().getBBox().width : 0;
}
...
function zoomed() {
projection
.translate(zoom.translate())
.scale(zoom.scale());
svg.selectAll("path")
.attr("d", path);
g.selectAll(".bars")
.attr("height", function(d) {
return projection([0, d.latitude - 0.5])[1] - projection([0, d.latitude])[1];
})
.attr("transform", function(d) {
return "translate(" + projection([-180, d.latitude]) + ")"
})
.attr("width", getLandWidth());
}
Full code here.
I am trying to add labels in a d3 pie as displayed at http://bl.ocks.org/dbuezas/9306799 but it is always displays the labels inside the slices.
var dataset = ${pieList};
var width = 700,
height = 700,
outerRadius = Math.min(width, height) / 2,
innerRadius = outerRadius * .999,
innerRadiusFinal = outerRadius * .5,
innerRadiusFinal3 = outerRadius * .45,
color = d3.scale.category20()
;
var vis = d3.select("#pieChart")
.append("svg:svg")
.data([dataset])
.attr("width", width)
.attr("height", height)
.append("svg:g")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")")
;
var arc = d3.svg.arc()
.outerRadius(outerRadius).innerRadius(innerRadius);
var arcFinal = d3.svg.arc().innerRadius(innerRadiusFinal).outerRadius(outerRadius);
var arcFinal3 = d3.svg.arc().innerRadius(innerRadiusFinal3).outerRadius(outerRadius);
var pie = d3.layout.pie()
.value(function (d) {
return d.measure;
});
var arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", up)
;
arcs.append("svg:path")
.attr("fill", function (d, i) {
return color(i);
})
.attr("d", arc)
.append("svg:title")
.text(function (d) {
return d.data.category + ": " + formatAsPercentage(d.data.measure);
});
d3.selectAll("g.slice").selectAll("path").transition()
.duration(750)
.delay(10)
.attr("d", arcFinal)
;
arcs.filter(function (d) {
return d.endAngle - d.startAngle > .2;
})
.append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("transform", function (d) {
return "translate(" + arcFinal.centroid(d) + ")rotate(" + angle(d) + ")";
})
.text(function (d) {
return d.data.category;
})
;
.attr("transform", function (d) {
return "translate(" + arcFinal.centroid(d)
+ ")rotate(" + angle(d)
+ ")translate(" + 700 + ",0)";
})
function angle(d) {
var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
return a > 90 ? a - 180 : a;
}
// Pie chart title
vis.append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text("Attendance report 2015")
.attr("class", "title")
;
function mouseover() {
d3.select(this).select("path").transition()
.duration(750)
//.attr("stroke","red")
//.attr("stroke-width", 1.5)
.attr("d", arcFinal3)
;
}
function mouseout() {
d3.select(this).select("path").transition()
.duration(750)
//.attr("stroke","blue")
//.attr("stroke-width", 1.5)
.attr("d", arcFinal)
;
}
function up(d, i) {
/* update bar chart when user selects piece of the pie chart */
//updateBarChart(dataset[i].category);
updateBarChart(d.data.category, color(i));
updateLineChart(d.data.category, color(i));
}
I want to display the labels outside of the pie slices but it always display inside the slices.
One option would be to translate your text outward by the radius of the pie, which might be easiest to do after the rotate. So, something like:
.attr("transform", function (d) {
return "translate(" + arcFinal.centroid(d)
+ ")rotate(" + angle(d)
+ ")translate(" + radius + ",0)";
})
Where radius is the radius of your pie chart. Sometimes you might want to translate a few pixels further, so that you have a margin between the chart and the label.
This is certainly not the only answer, here are some more issues to watch out for though:
Rotating your text can end up with it being upside down on one side of the graph, which is not very readable.
After fixing the rotation issue, you might find that your label's textanchor causes long labels to go over the graph anyway.
If your pie slices are thin, you can end up writing labels over one another.
Im new to d3.js im able to create donut chart and im able to rotate chart but i dont want labels on chart to rotate. Can anyone help how to proceed. how shall i make sure that labels on chart should not rotate
var dataset = [
{
count: 10
},
{
count: 20
},
{
label: 'Cantaloupe',
count: 30
}
];
var width = 360;
var height = 360;
var radius = 100;
var color = d3.scale.ordinal()
.range(["red", "blue", "green"]);
var svg = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2) +
',' + (height / 2) + ')');
var arc = d3.svg.arc()
.innerRadius(radius - 60)
.outerRadius(radius);
var pie = d3.layout.pie()
.value(function (d) {
return d.count;
});
var arcs = svg.selectAll('.arc')
.data(pie(dataset))
.enter()
.append('g')
.attr("class", "arc");
arcs.append("path")
.attr("d", arc)
.attr("fill", function (d) {
return color(d.data.count);
})
.on("click", function (d) {
var curAngle = 180;
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ") rotate(" + curAngle + ")");
});
arcs.append("text")
.attr("transform", function (d) {
return "translate(" + arc.centroid(d) + ")";
}).attr("text-anchor", "middle")
.attr("font-size", "1.5em")
.text(function (d) {
return d.data.count
});