User input to Project on JSON map - javascript

I have the following D3.js project that is available here:
http://bl.ocks.org/diggetybo/raw/e75dcb649ae3b26e2312a63434fc970c/
The latitude and longitude inputs are below the map.
It's supposed to take user input numbers of latitude and longitude and "project" svg circles at the given coordinate. The issue is I'm either getting ____ is not a function error or dev tools throws no errors at all, but the circles are never projected.
It's a short file, can someone explain why it's not working the way I thought?

Your update function doesn't make any sense.
It accepts two inputs, but you only ever call it with one.
.selectAll("circle").enter() is not valid d3 syntax.
You need to call projection with both the latitude and longitude, you pass 0 which will result in it returning null since it's outside of the projection.
After you fix all this, you'll still be off because you've moved your paths by your margin and would have been better off putting them in a g moved by the margins.
All that said, a simple rewrite would be:
var lat = d3.select("#latValue").on("input", function() {
update();
}).node();
var long = d3.select("#lonValue").on("input", function() {
update();
}).node();
function update() {
// lat/long to pixel
var coors = projection([long.value, lat.value]);
// if outside projection don't add circle
if (coors === null) return;
// add circle
container
.append("circle")
.attr("cx", coors[0])
.attr("cy", coors[1])
.attr("r", Math.sqrt(5) * 4)
.style("fill", "black")
.style("opacity", 0.85);
}
Running code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
/* On mouse hover, lighten state color */
path:hover {
fill-opacity: .7;
}
</style>
</head>
<body>
<script type="text/javascript">
//Width and height of map
var width = 960;
var height = 500;
var margins = { left: 0, top: 100, right: 0, bottom: 0 };
// D3 Projection
var projection = d3.geo.albersUsa()
.translate([width/2, height/2]) // translate to center of screen
.scale([1000]); // scale things down so see entire US
// Define path generator
var path = d3.geo.path() // path generator that will convert GeoJSON to SVG paths
.projection(projection); // tell path generator to use albersUsa projection
// Define linear scale for output
var color = d3.scale.linear()
.range(["#c3e2ff","#15198e"]);
//Create SVG element and append map to the SVG
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height+margins.top);
svg.append('text')
.text('Coordinate Query')
.attr('font-size','24px')
.attr('transform', 'translate(' + 30 + ',' +70 + ')')
.attr('font-family','Calibri');
svg.append('text')
.text('Data as of 12/2016')
.attr('font-size','12px')
.attr('transform', 'translate(' + 35 + ',' +100 + ')')
.attr('font-family','Calibri');
// Load in my states data!
color.domain([0,100]); // setting the range of the input data
// Load GeoJSON data and merge with states data
d3.json("https://jsonblob.com/api/573228c3-d068-11e6-b16a-b501dc8d2b08", function(json) {
//var coordinates = d3.mouse(this);
// Bind the data to the SVG and create one path per GeoJSON feature
var container = svg.append("g")
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')');
container.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke", "#fff")
.style("stroke-linejoin","round")
.style("stroke-width", "1.5")
.style("fill", 'steelblue');
// Modified Legend Code from Mike Bostock: http://bl.ocks.org/mbostock/3888852
var lat = d3.select("#latValue").on("input", function() {
update();
}).node();
var long = d3.select("#lonValue").on("input", function() {
update();
}).node();
function update() {
// lat/long to pixel
var coors = projection([long.value, lat.value]);
// if outside projection don't add circle
if (coors === null) return;
// add circle
container
.append("circle")
.attr("cx", coors[0])
.attr("cy", coors[1])
.attr("r", Math.sqrt(5) * 4)
.style("fill", "black")
.style("opacity", 0.85);
}
});
</script>
<p>
<label for="latValue"
style="display: inline-block;width:240px;text-align:right;font-size:18px;font-family:Play">
Lattitude:<span id="latValue-value"></span>
</label>
<input type="number"min="-360"max="360"step="1"value="0" id="latValue">
<label for="lonValue"
style="display: inline-block;width:240px;text-align:right;font-size:18px;font-family:Play">
Longitude:<span id="lonValue-value"></span>
</label>
<input type="number"min="-360"max="360"step="1"value="0" id="lonValue">
</p>
</body>
</html>

Related

D3Js v3: Path not showing up on Node Elements

With Javascript Im generating HTML-Code with a SVG in it. I want to display a a donut chart in it then. Im able to draw the chart on a static HTML-Element. However, when I try to display it in my JavaScript-generated node element the path is not showing up, but I can see the text. What am I missing here?
https://jsfiddle.net/fuL5doja/46/
function createNodes(){
var parent = document.getElementById('chart');
var child = document.createElement('div');
child.classList.add('childContainer');
parent.appendChild(child);
var svg = document.createElement('svg');
svg.id = 'donut';
child.appendChild(svg);
}
function donutChart(){
// set the dimensions and margins of the graph
var width = 30
height = 30
margin = 0
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
var radius = 30
// append the svg object to the div called 'my_dataviz'
var svg = d3.select('#donut')
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Create dummy data
var dataDummy = {a: 70, b:30}
// set the color scale
var color = d3.scale.ordinal()
.domain(dataDummy)
.range(["#bebfc2", "#8FB91C"])
// Compute the position of each group on the pie:
var pie = d3.layout.pie()
.value(function(d) {return d.value; })
var data_ready = pie(d3.entries(dataDummy))
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg.selectAll('whatever')
.data(data_ready)
.enter()
.append('path')
.attr('d', d3.svg.arc()
.innerRadius(5) // This is the size of the donut hole
.outerRadius(radius)
)
.attr('fill', function(d){ return(color(d.data.key)) })
.style("opacity", 0.7)
svg.append("text")
.attr("x", -12) // space legend
.attr("y", 2)
.attr("class", "donutText")
.text('30%');
}
function donutChart2(){
// set the dimensions and margins of the graph
var width = 30
height = 30
margin = 0
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
var radius = 30
// append the svg object to the div called 'my_dataviz'
var svg = d3.select('#test')
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Create dummy data
var dataDummy = {a: 70, b:30}
// set the color scale
var color = d3.scale.ordinal()
.domain(dataDummy)
.range(["#bebfc2", "#8FB91C"])
// Compute the position of each group on the pie:
var pie = d3.layout.pie()
.value(function(d) {return d.value; })
var data_ready = pie(d3.entries(dataDummy))
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg.selectAll('whatever')
.data(data_ready)
.enter()
.append('path')
.attr('d', d3.svg.arc()
.innerRadius(5) // This is the size of the donut hole
.outerRadius(radius)
)
.attr('fill', function(d){ return(color(d.data.key)) })
.style("opacity", 0.7)
svg.append("text")
.attr("x", -12) // space legend
.attr("y", 2)
.attr("class", "donutText")
.text('30%');
}
createNodes();
donutChart();
donutChart2();
.childContainer {
width: 200px;
height: 100px;
border: 1px solid black;
}
#mySvg {
}
<div id="chart"></div>
<svg id="test"></svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
You need to create the svg element with svg namespace uri for it to support path when creating directly with JavaScript:
var svg = document.createElementNS('http://www.w3.org/2000/svg','svg');
Instead of just the typical
var svg = document.createElement('svg');
Alternatively, you could use D3 to append the svg, which will make sure it's correctly namespaced!
d3.select(child).append('svg').attr('id', 'donut');

d3 how to tie text to top right corner of view port while zooming and panning

I am creating a mapping application in d3 and want to tie some text to the top right corner of my view port. Additionally, I want the text to remain in the top right corner while I zoom and pan across the application.I think I can solve my problem by figuring out how to get the coordinates of the top right corner of my view. Knowing this information would allow me to then set the coordinates of my text element. I've tried manually setting the dimensions of the containing svg element and then moving the text to that location but interestingly this didn't work. I was hoping to be able to find the coordinates programatically rather than setting coordinates manually. How can I do this in d3/javascript?
EDIT:
My code is a modification of this code by Andy Barefoot: https://codepen.io/nb123456/pen/zLdqvM
My own zooming and panning code has essentially remained the same as the above example:
function zoomed() {
t = d3
.event
.transform
;
countriesGroup
.attr("transform","translate(" + [t.x, t.y] + ")scale(" + t.k + ")")
;
}
I'm trying to append the text at the very bottom of the code:
countriesGroup.append("text")
.attr("transform", "translate(" How do I get top right coordinates? ")")
.style("fill", "#ff0000")
.attr("font-size", "50px")
.text("This is a test");
My idea is to be able to get the top right coordinates of the view port through the code rather than setting it manually and then have the coordinates of the text update as the user zooms or pans.
To keep something in place while zooming and panning you could invert the zoom:
point == invertZoom(applyZoom(point))
This isn't particularly efficient, as we are using two operations to get to the original number. The zoom is applied here:
countriesGroup
.attr("transform","translate(" + [t.x, t.y] + ")scale(" + t.k + ")");
While the inversion would need to look something like:
text.attr("x", d3.zoom.transform.invert(point)[0])
.attr("y", d3.zoom.transform.invert(point)[1])
.attr("font-size", baseFontSize / d3.zoom.transform.k);
Where point and base font size are the original anchor point and font size. This means storing that data somewhere. In the example below I assign it as a datum to the text element:
var width = 500;
var height = 200;
var data = d3.range(100).map(function() {
return {x:Math.random()*width,y:Math.random()*height}
})
var zoom = d3.zoom()
.on("zoom",zoomed);
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height)
.call(zoom);
var g = svg.append("g")
var circles = g.selectAll()
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 5)
.attr("fill","steelblue")
var text = g.append("text")
.datum({x: width-10, y: 20, fontSize: 12})
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.style("text-anchor","end")
.attr("font-size",function(d) { return d.fontSize; })
.text("This is a test");
function zoomed() {
g.attr("transform", d3.event.transform);
var d = text.datum();
var p = d3.event.transform.invert([d.x,d.y]);
var x1 = p[0];
var y1 = p[1];
text.attr("x",x1)
.attr("y",y1)
.attr("font-size", d.fontSize / d3.event.transform.k)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Better Solution
The above is the solution to the approach you seem to be looking for. But the end result is best achieved by a different method. As I mention in my comment, the above approach goes through extra steps that can be avoided. There can also be some size/clarity changes in the text when zooming (quickly) using the above method
As noted above, you are applying the zoom here:
countriesGroup
.attr("transform","translate(" + [t.x, t.y] + ")scale(" + t.k + ")")
The zoom transform is applied only to countriesGroup, if your label happens to be in a different g (and not a child of countriesGroup), it won't be scaled or panned.
We wouldn't need to apply and invert the zoom, and we wouldn't need to update the position or font size of the text at all.
var width = 500;
var height = 200;
var data = d3.range(100).map(function() {
return {x:Math.random()*width,y:Math.random()*height}
})
var zoom = d3.zoom()
.on("zoom",zoomed);
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height)
.call(zoom);
var g = svg.append("g");
var g2 = svg.append("g"); // order does matter in layering
var circles = g.selectAll()
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 5)
.attr("fill","steelblue")
// position once and leave it alone:
var text = g2.append("text")
.attr("x", width - 10)
.attr("y", 20 )
.style("text-anchor","end")
.attr("font-size", 12)
.text("This is a test");
function zoomed() {
// apply the zoom to the g that has zoomable content:
g.attr("transform", d3.event.transform);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

d3: brushing points on map issue

I have succesfully drawn a map and plotted points from a csv. file on it.
But when I try to add a brush (which should color the circles within the brush in the original color, and the ones outside should have a lower opacity - and when releasing the brush all circles should again have the same color), something goes wrong - The map is shown very quickly and then the entire svg just turns into a single color.
I am pretty new to d3 and have just tried to follow this example: http://bl.ocks.org/feyderm/6bdbc74236c27a843db633981ad22c1b . I can't really figure out if it might have something to do with the projection or something totally different..
My attempt is shown below:
<!DOCTYPE html>
...
<style type="text/css">
.brushed {
fill: white;
stroke: black;
stroke-width: 0.5;
opacity: 0.95;
}
.non_brushed {
fill: grey;
opacity: 0.15;
}
</style>
</head>
<body>
<script type="text/javascript">
//Width and height
var w = 500;
var h = 500;
var padding = 60;
//Define path generator, using the mercator projection
var projection = d3.geoMercator()
.scale(90*w)
.translate([58350, 35330]);
var path = d3.geoPath()
.projection(projection);
//define borough colors
var color = ["rgb(0,59,86)","rgb(63,72,77)",
"rgb(243,142,50)", "rgb(246,99,36)", "rgb(21,108,108)"];
//Create SVG element
var svg_map = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Load in GeoJSON data
d3.json("boroughs.json", function(json) {
//Bind data and create one path per GeoJSON feature
svg_map.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke","white")
.style("stroke-width","1px")
.style("fill",function(d,i){
return color[i];
});
//load in csv data
d3.csv("blabla.csv",function(data){
//create circle elements
var circles = svg_map.append("g")
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class","brushed") //original color
.attr("cx", function(d){
return projection([d.Lon,d.Lat])[0];
})
.attr("cy", function(d){
return projection([d.Lon,d.Lat])[1];
})
.attr("r",3);
//create brush
var brush = d3.brush()
.on("brush", highlightBrushedCircles)
.on("end", brushEnd);
svg_map.append("g")
.call(brush);
function highlightBrushedCircles() {
if (d3.event.selection != null) {
// set circles to "non_brushed"
circles.attr("class", "non_brushed");
//coordinates describing the corners of the brush
var brush_coords = d3.brushSelection(this);
// set the circles within the brush to class "brushed" to style them accordingly
circles.filter(function (){
var cx = d3.select(this).attr("cx"),
cy = d3.select(this).attr("cy");
return isBrushed(brush_coords, cx, cy);
})
.attr("class", "brushed");
}
}
function isBrushed(brush_coords, cx, cy) {
//the corners of the brush
var x0 = brush_coords[0][0],
x1 = brush_coords[1][0],
y0 = brush_coords[0][1],
y1 = brush_coords[1][1];
//checks whether the circle is within the brush
return x0 <= cx && cx <= x1 && y0 <= cy && cy <= y1;
}
function brushEnd() {
if (!d3.event.selection) return;
// programmed clearing of brush after mouse-up
d3.select(this).call(brush.move, null);
//set all circles to original color
svg_map.selectAll(".non_brushed").classed("brushed", true);
}
});
});
</script>
</body>
I got it to work - it seemed that the problem was that I in my style css file also had this:
rect {
fill: rgb(21,108,108);
shape-rendering: CrispEdges;
}
Which then just colored the entire svg like that :)

hide circles on Orthographic drag

I've created a globe which has circles and a drag. The problem is that the circles appear on the far side of the globe. I would like those circles to be hidden.
My bl.ock can be found here:
http://bl.ocks.org/anonymous/dc2d4fc810550586d40d4b1ce9088422/40c6e199a5be4e152c0bd94a13ea94eba41f004b
For example, I would like my globe to function like this one: https://bl.ocks.org/larsvers/f8efeabf480244d59001310f70815b4e
I've seen solutions such as this one: How to move points in an orthogonal map? but it doesn't quite work for me. The points simply disappear, as d[0] and d[1] seem to be undefined.
I've also tried using methods such as this: http://blockbuilder.org/tlfrd/df1f1f705c7940a6a7c0dca47041fec8 but that also doesn't seem to work. The problem here seems to be that he is using the json as his data, while my circles data are independent of the json.
Only similar example I've found is the one: https://bl.ocks.org/curran/115407b42ef85b0758595d05c825b346 from Curran but I don't really understand his code. His method is quite different than mine.
Here is my JavaScript code:
(function(){
var h = 600;
var w = 900;
var i = 0;
var map = void 0;
var world = void 0;
var US = void 0;
var margin = {
top: 10,
bottom: 40,
left: 0,
right: 30
};
var circleScale = d3.scaleSqrt()
.domain([0, 4445])
.range([0.5, 10])
var width = w - margin.left - margin.right;
var height = h - margin.top - margin.bottom;
var dragging = function(d){
var c = projection.rotate();
projection.rotate([c[0] + d3.event.dx/6, c[1] - d3.event.dy/6])
map.selectAll('path').attr('d', path);
map.selectAll(".circles").attr("cx", function(d){
var coords = projection([d.Longitude_imp, d.Latitude_imp])
return coords[0];
})
.attr("cy", function(d){
var coords = projection([d.Longitude_imp, d.Latitude_imp])
return coords[1];
})
}
var drag = d3.drag()
.on("drag", dragging)
var projection = d3.geoOrthographic().clipAngle(90);
var path = d3.geoPath().projection(projection);
var svg = d3.select("body")
.append("svg")
.attr("id", "chart")
.attr("width", w)
.attr("height", h)
d3.json("world.json", function(json){
d3.csv("arms_transfer_2012_2016_top - arms_transfer_2012_2016_top.csv", function(error, data){
var countries = topojson.feature(json, json.objects.countries).features
var US = countries[168]
map = svg.append('g').attr('class', 'boundary');
world = map.selectAll('path').data(countries);
US = map.selectAll('.US').data([US]);
Circles = map.selectAll(".circles").data(data)
console.log(countries[168])
world.enter()
.append("path")
.attr("class", "boundary")
.attr("d", path)
US.enter()
.append("path")
.attr("class", "US")
.attr("d", path)
.style("fill", "lightyellow")
.style("stroke", "orange")
Circles.enter()
.append("circle")
.attr("class", "circles")
.attr("r", function(d){
return circleScale(d.Millions)
})
.attr("cx", function(d){
var coords = projection([d.Longitude_imp, d.Latitude_imp])
return coords[0];
})
.attr("cy", function(d){
var coords = projection([d.Longitude_imp, d.Latitude_imp])
return coords[1];
})
.style("fill", "#cd0d0e")
svg.append("rect")
.attr("class", "overlay")
.attr("width", w)
.attr("height", h)
.call(drag)
})
})
})();
There are a few different methods to achieve this, but one of the easier methods would be to calculate the angular distance between the projection centroid (as determined by the rotation) and the circle center on the drag event:
map.selectAll("circle")
.style("display", function(d) {
var circle = [d.Longitude_imp, d.Latitude_imp];
var rotate = projection.rotate(); // antipode of actual rotational center.
var center = [-rotate[0], -rotate[1]]
var distance = d3.geoDistance(circle,center);
return (distance > Math.PI/2 ) ? 'none' : 'inline';
})
Take the center of each point and get the rotational center with projection.rotate() - note that the rotation values are inverse of the centering point. A rotation of [10,-20] centers the map at [-10,20], you move the map under you. With these two points we can use d3.geoDistance() which calculates the distance between two points in radians, hence the use of Math.PI/2 - which gives us points outside of 90 degrees, for these we hide, for the rest we show.
This can be incorporated a little nicer into your code, but I keep it separate here to show what is happening clearer.
Here's an example block - drag to trigger, I haven't applied the logic to the initial load.
An alternative approach, as noted by Gerardo Furtado, would be to use a path to display the circles - using path.pointRadius to set the size of the circle for each point. Instead of appending a circle, you could append path with the following format:
Circles.enter()
.append("path")
.attr("class", "circles")
.attr("d",createGeojsonPoint)
The, on update/drag:
map.selectAll('.circles').attr('d',createGeojsonPoint);
This method uses the clip angle of the orthographic to hide features when they are more than 90 degrees from the center of the projection (as determined by rotation). Your createGeojsonPoint function needs to set the radius and return a valid geojson object:
var createGeojsonPoint = function(d) {
console.log(d);
path.pointRadius(circleScale(d.Millions)); // set point radius
return path({"type":"Point","coordinates":[d.Longitude_imp,d.Latitude_imp]}) // create geojson point, return path data
}
All together, with the necessary modifications, your code might look like this.

grouping data in d3 pie chart

Im creating a small d3 (http://d3js.org/) pie chart where some pieces of the pie are separated
and translated away from the center of the pie on hover.
So far i managed separating the different parts of the pie and translating them away from the center of the circle, but they are translated separatedly. My goal is to group the pieces and translate them away together.
Im very new to d3js.
Basically what I want to achieve is grouping some of the elements and translating them away from the center of origin grouped.
Right now the pieces are translated separately and Im guessing that I can fix the problem by adding a parent and translating that parent away from the center.
So basically my question is :
How can i group pieces of data (fx the ones labeled food) and translate that group away from the center of origin?
The project can be found below and the content of the csv file in the bottom. Copy paste the code into a html-document and copy paste the csv data into a file named 'data.csv' located in the same folder.
Thanks
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.arc path {
stroke: #fff;
}
</style>
<body>
<!--https://github.com/mhemesath/r2d3/-->
<!--[if lte IE 8]><script src="r2d3.min.js"></script><![endif]-->
<!--[if gte IE 9]><!-->
<script src="http://d3js.org/d3.v3.min.js"></script>
<!--<![endif]-->
<script>
// dimension
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
// colors
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
//append svg to body
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.csv("data.csv", function(error, data) {
var g = svg.selectAll(".arcs")
.data(pie(data))
.enter().append("g")
.attr("class", function (d, i) {
return data[i].class; // i is 0-based.
});
// Select all classes named 'pizza' and translate away from center of circle
svg.selectAll('.food')
.attr("transform", function(d) { //set the origin to the center of the arc
//we have to make sure to set these before calling arc.centroid
d.innerRadius = 0;
d.outerRadius = 0;
x = arc.centroid(d)[0];
y = arc.centroid(d)[1];
return "translate(" + x/4 +','+ y/4 + ")"; //this gives us a pair of coordinates like [50, 50]
});
// color fill
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.age); });
// append text
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.data.age; });
});
</script>
//content of csv file :
age,population,class
<5,2704659,food
5-13,4499890,food
14-17,2159981,food
18-24,3853788,icecream
25-44,14106543,icecream
45-64,8819342,pizza
≥65,612463,pizza
I can think of two ways to do this.
1) Create an outer pie chart with a single pie segment for each class, then create addition pie charts inside each outer pie segment. The inner pie charts need to specify a start/end angle for their piechart (which matches the outer pie segment for that class). Here's a example on Plunker: http://plnkr.co/edit/cblP4d?p=preview
2) Instead of translating each datapoint individually, create an array of translations for each class. Each class's translation uses the centroid for that class. When you translate each arc, use the translation array (referenced by class).

Categories

Resources