D3.js - Get a random point within an arc - javascript

How would one attempt to get a random coordinate within an existing arc?
I am currently rendering a piechart from user data and would like to render a number of points on each arc at random locations - they may be outside of their arc, however
At the moment, I am using a random deviation (within a certain range) from the centroid of each arc. This approach is problematic as arcs might be too small and points end up outside of their arc.
I can't really provide any example code at the moment as I am really just rendering a pie chart with five slices so far.

I've used this example as a starting point. What I did was generate 10 times 2 numbers for every arc: a distance from the centre and an angle (in radians). Then I plotted the circles using those values.
To show that it works, I made the radius constant, so you see a circle of black dots. If you want, you can use the commented out line to make that random as well.
Note how the circles are the same colour as the arcs.
I also had to subtract Math.PI / 2, because of the different zero points between degrees and radians:
0 degrees is a vertical line to the top;
0 radians is a horizontal line to the right. -Math.PI / 2 radians is a vertical line to the top.
const data = [{
"region": "North",
"count": "53245"
},
{
"region": "South",
"count": "28479"
},
{
"region": "East",
"count": "19697"
},
{
"region": "West",
"count": "24037"
},
{
"region": "Central",
"count": "40245"
}
];
const width = 360;
const height = 360;
const radius = Math.min(width, height) / 2;
const svg = d3.select("#chart-area")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", `translate(${width / 2}, ${height / 2})`);
const color = d3.scaleOrdinal(["#66c2a5", "#fc8d62", "#8da0cb",
"#e78ac3", "#a6d854", "#ffd92f"
]);
const pie = d3.pie()
.value(d => d.count)
.sort(null);
const arc = d3.arc()
.innerRadius(0)
.outerRadius(radius);
// Join new data
const path = svg.selectAll("path")
.data(pie(data));
// Enter new arcs
path.enter().append("path")
.attr("fill", (d, i) => color(i))
.attr("d", arc)
.attr("stroke", "white")
.attr("stroke-width", "6px")
.each(drawPoints);
function drawPoints(d, i) {
// Generate random numbers (x, y) where x between startAngle
// and endAngle
// and y between 0 and radius
const points = new Array(10).fill(undefined).map(() => ({
angle: d.startAngle + Math.random() * (d.endAngle - d.startAngle) - Math.PI / 2,
//radius: Math.random() * radius,
radius: radius / 2,
}));
svg.selectAll(`.point-${i}`)
.data(points)
.enter()
.append('circle')
.attr('class', `point point-${i}`)
.attr("fill", (d) => color(i))
.attr('stroke', 'black')
.attr('stroke-width', '2px')
.attr('cx', (d) => d.radius * Math.cos(d.angle))
.attr('cy', (d) => d.radius * Math.sin(d.angle))
.attr('r', 3)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.js"></script>
<div id="chart-area"></div>

Related

How can I create a doughnut chart with rounded edges only on one end of each segment?

I'm trying to build a doughnut chart with rounded edges only on one side. My problem is that I have both sided rounded and not just on the one side. Also can't figure out how to do more foreground arcs not just one.
const tau = 2 * Math.PI; // http://tauday.com/tau-manifesto
const arc = d3.arc()
.innerRadius(80)
.outerRadius(100)
.startAngle(0)
.cornerRadius(15);
const svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
Background arc, but I'm not sure if this is even needed?
const background = g.append("path")
.datum({endAngle: tau})
.style("fill", "#ddd")
.attr("d", arc);
const data = [ .51];
const c = d3.scaleThreshold()
.domain([.200,.205,.300,.310, .501, 1])
.range(["green","#ddd", "orange","#ddd", "red"]);
Const pie = d3.pie()
.sort(null)
.value(function(d) {
return d;
});
Only have one foreground, but need to be able to have multiple:
const foreground = g.selectAll('.arc')
.data(pie(data))
.enter()
.append("path")
.attr("class", "arc")
.datum({endAngle: 3.8})
.style("fill", function(d) {
return c(d.value);
})
.attr("d", arc)
What am I doing wrong?
var tau = 2 * Math.PI; // http://tauday.com/tau-manifesto
// An arc function with all values bound except the endAngle. So, to compute an
// SVG path string for a given angle, we pass an object with an endAngle
// property to the `arc` function, and it will return the corresponding string.
var arc = d3.arc()
.innerRadius(80)
.outerRadius(100)
.startAngle(0)
.cornerRadius(15);
// Get the SVG container, and apply a transform such that the origin is the
// center of the canvas. This way, we don’t need to position arcs individually.
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Add the background arc, from 0 to 100% (tau).
var background = g.append("path")
.datum({endAngle: tau})
.style("fill", "#ddd")
.attr("d", arc);
var data = [ .51];
var c = d3.scaleThreshold()
.domain([.200,.205,.300,.310, .501, 1])
.range(["green","#ddd", "orange","#ddd", "red"]);
var pie = d3.pie()
.sort(null)
.value(function(d) {
return d;
});
// Add the foreground arc in orange, currently showing 12.7%.
var foreground = g.selectAll('.arc')
.data(pie(data))
.enter()
.append("path")
.attr("class", "arc")
.datum({endAngle: 3.8})
.style("fill", function(d) {
return c(d.value);
})
.attr("d", arc)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="960" height="500"></svg>
The documentation states, that the corner radius is applied to both ends of the arc. Additionally, you want the arcs to overlap, which is also not the case.
You can add the one-sided rounded corners the following way:
Use arcs arc with no corner radius for the data.
Add additional path objects corner just for the rounded corner. These need to be shifted to the end of each arc.
Since corner has rounded corners on both sides, add a clipPath that clips half of this arc. The clipPath contains a path for every corner. This is essential for arcs smaller than two times the length of the rounded corners.
raise all elements of corner to the front and then sort them descending by index, so that they overlap the right way.
const arc = d3.arc()
.innerRadius(50)
.outerRadius(70);
const arc_corner = d3.arc()
.innerRadius(50)
.outerRadius(70)
.cornerRadius(10);
const svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
const clipPath = g.append("clipPath")
.attr("id", "clip_corners");
const c = d3.scaleQuantile()
.range(["#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4","#4eb3d3","#2b8cbe","#08589e"]);
const pie = d3.pie().value(d => d);
function render(values) {
c.domain(values);
const arcs = pie(values);
const corners = pie(values).map(d => {
d.startAngle = d.endAngle - 0.2;
d.endAngle = d.endAngle + 0.2;
return d;
});
const clip = pie(values).map(d => {
d.startAngle = d.endAngle - 0.01;
d.endAngle = d.endAngle + 0.2;
return d;
});
g.selectAll(".arc")
.data(arcs)
.join("path")
.attr("class", "arc")
.style("fill", d => c(d.value))
.attr("d", arc);
clipPath.selectAll("path")
.data(clip)
.join("path")
.attr("d", arc);
g.selectAll(".corner")
.data(corners)
.join("path")
.raise()
.attr("class", "corner")
.attr("clip-path", "url(#clip_corner)")
.style("fill", d => c(d.value))
.attr("d", arc_corner)
.sort((a, b) => b.index - a.index);
}
function randomData() {
const num = Math.ceil(8 * Math.random()) + 2;
const values = Array(num).fill(0).map(d => Math.random());
render(values);
}
d3.select("#random_data")
.on("click", randomData);
randomData();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
<button id="random_data">
Random data
</button>
<svg width="150" height="150"></svg>
I changed the dependency to the current version of d3.

D3 draw lines around a pie with a circular rotation

Given a pie chart with an arc, I'd like to append lines at each arcs center points to follow the path. I'm able to append the lines on the path properly currently but I'm not able to angle them appropriately. I have a set of circles going around my pie chart and need to place these lines slightly below the circles at the mid point between 1 circle and the next. Here is a pretty gross image of what I'm trying to do, the lines should be at the center of the arc but just below the circles and following the path of the arc.
And here is what I am currently getting with my code.I'm trying to get the dark black lines to follow a similar rotation to the light gray ones that you see at the center.
const startAngle = (-45 * Math.PI) / 180;
const endAngle = (-45 * Math.PI) / 180 + 2 * Math.PI;
const width = 500;
const height = Math.min(width, 500);
const viewBox = [-width / 2, -height / 2, width, height];
const svg = d3
.select("#canvas")
.append("svg")
.attr("viewBox", viewBox);
function drawGridDonut(svg, innerFactor, outerFactor) {
const radius = Math.min(width, height) / 2;
const graph = new Array(6); // 6 equal sections
const gridPie = d3
.pie()
.startAngle(startAngle)
.endAngle(endAngle)
.sort(null)
.value(1);
const arc = d3
.arc()
.innerRadius(radius * innerFactor)
.outerRadius(radius * outerFactor);
// base path + arc
svg
.append("g")
.attr("class", "grid")
.selectAll("path")
.data(gridPie(graph))
.join("path")
.attr("stroke-width", 1)
.attr("fill", "none")
.attr("d", arc);
// border lines
svg
.selectAll(".grid")
// border lines, 1 = dark border, 0 = light border
.data(gridPie([1, 0, 1, 0, 1, 0]))
.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr(
"y2",
d =>
Math.sin(d.startAngle - Math.PI / 2) *
(radius - (d.data === 1 ? 0 : 75.5))
)
.attr(
"x2",
d =>
Math.cos(d.startAngle - Math.PI / 2) *
(radius - (d.data === 1 ? 0 : 75.5))
)
.attr("stroke", d => (d.data === 0 ? "#C8C8C8" : "#000"))
.attr("stroke-width", "0.1");
// dots for overuse + benefits
svg
.selectAll(".dot")
.data(gridPie([...Array(12)]))
.enter()
.append("circle")
.attr('class', '.dot')
.attr("cx", d => arc.centroid(d)[0])
.attr("cy", d => arc.centroid(d)[1])
.attr("r", 1)
.style("fill", "#C8C8C8");
// this is where the lines are being made
svg
.selectAll('.hash')
.data(gridPie([...Array(12)]))
.enter()
.append('line')
.attr('class', '.hash')
.attr('d', arc)
.attr("x1", (d) => arc.centroid(d)[0] - d.startAngle)
.attr("y1", (d) => arc.centroid(d)[1] - d.startAngle)
.attr('x2', d => arc.centroid(d)[0] + d.endAngle )
.attr('y2', d => arc.centroid(d)[1] + d.endAngle)
.attr('width', 10)
.attr('height', 4)
.attr("stroke", "black")
.attr('transform', (d) => 'rotate(180)')
}
for (let i = 0; i < 6; i++) {
drawGridDonut(svg, 0.9 - 0.04, 0.9 + 0.002)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="canvas"></div>
I fixed this by drawing a line from the center of my pie to the top then adding dasharray (with fine tuning of the inner and outer arcs to mark the safe spaces.
function drawHyphen(svg, innerFactor, outerFactor, dashPercent) {
const radius = Math.min(width, height) / 2;
const gridPie = d3
.pie()
.startAngle(startAngle)
.endAngle(endAngle)
.sort(null)
.value(1);
svg
.selectAll('.line')
.data(gridPie([...Array(12)]))
.enter()
.append("line")
.attr('class', (d, i) => `line${i}`)
.attr(
"y1",
d =>
Math.sin((d.startAngle) - Math.PI / 4) * radius * innerFactor
)
.attr(
"x1",
d =>
Math.cos((d.startAngle) - Math.PI / 4) * radius * innerFactor
)
.attr(
"y2",
d =>
Math.sin((d.startAngle) - Math.PI / 4) * radius / outerFactor
)
.attr(
"x2",
d =>
Math.cos((d.startAngle) - Math.PI / 4) * radius / outerFactor
)
.attr("stroke", "#EEEEEE")
.attr("stroke-dasharray", `0.5, ${dashPercent}`)
.attr("stroke-width", 4)
}
then I call the function as many times as I need to and set the outerFactor to be the threshold where the safe space will be.
let outerArc = 1 - 0.051
drawHyphen(svg, outerArc - 0.835, outerArc - 2.3, "1.9%")

Wrap labels in semi donut pie chart using d3 js

Not able to wrap labels of semi donut pie chart which is developed using d3 js.
I tried using CSS by giving word-wrap, max-width etc., but it's not working.
How can I warp the text labels if the labels are having more than three to fours words?
Here is my fiddle: https://jsfiddle.net/SampathPerOxide/hcvuqjt2/22/
var width = 400;
var height = 300; //this is the double because are showing just the half of the pie
var radius = Math.min(width, height) / 2;
var labelr = radius + 30; // radius for label anchor
//array of colors for the pie (in the same order as the dataset)
var color = d3.scale.ordinal()
.range(['#2b5eac', '#0dadd3', '#ffea61', '#ff917e', '#ff3e41']);
data = [{
label: 'CDU',
value: 10
},
{
label: 'SPD',
value: 15
},
{
label: 'Die Grünen',
value: 8
},
{
label: 'Die Mitte',
value: 1
},
{
label: 'Frei Wähler',
value: 3
}
];
var vis = d3.select("#chart")
.append("svg") //create the SVG element inside the <body>
.data([data]) //associate our data with the document
.attr("width", width) //set the width and height of our visualization (these will be attributes of the <svg> tag
.attr("height", height)
.append("svg:g") //make a group to hold our pie chart
.attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')'); //move the center of the pie chart from 0, 0 to radius, radius
var arc = d3.svg.arc() //this will create <path> elements for us using arc data
.innerRadius(79)
// .outerRadius(radius);
.outerRadius(radius - 10) // full height semi pie
//.innerRadius(0);
var pie = d3.layout.pie() //this will create arc data for us given a list of values
.startAngle(-90 * (Math.PI / 180))
.endAngle(90 * (Math.PI / 180))
.padAngle(.02) // some space between slices
.sort(null) //No! we don't want to order it by size
.value(function(d) {
return d.value;
}); //we must tell it out to access the value of each element in our data array
var arcs = vis.selectAll("g.slice") //this selects all <g> elements with class slice (there aren't any yet)
.data(pie) //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties)
.enter() //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
.append("svg:g") //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
.attr("class", "slice"); //allow us to style things in the slices (like text)
arcs.append("svg:path")
.attr("fill", function(d, i) {
return color(i);
}) //set the color for each slice to be chosen from the color function defined above
.attr("d", arc); //this creates the actual SVG path using the associated data (pie) with the arc drawing function
const textEl = arcs.append("svg:text")
.attr("class", "labels") //add a label to each slice
.attr("fill", "grey")
.attr("transform", function(d) {
var c = arc.centroid(d),
xp = c[0],
yp = c[1],
// pythagorean theorem for hypotenuse
hp = Math.sqrt(xp * xp + yp * yp);
return "translate(" + (xp / hp * labelr) + ',' +
(yp / hp * labelr) + ")";
})
.attr("text-anchor", "middle"); //center the text on it's origin
textEl.append('tspan')
.text(function(d, i) {
return data[i].label;
});
textEl.append('tspan')
.text(function(d, i) {
return data[i].value;
})
.attr('x', '0')
.attr('dy', '1.2em');
arcs.append("svg:text")
.attr("class", "labels")//add a label to each slice
.attr("fill", "grey")
.attr("transform", function(d) {
var c = arc.centroid(d),
xp = c[0],
yp = c[1],
// pythagorean theorem for hypotenuse
hp = Math.sqrt(xp*xp + yp*yp);
return "translate(" + (xp/hp * labelr) + ',' +
(yp/hp * labelr) + ")";
})
.attr("text-anchor", "middle") //center the text on it's origin
.text(function(d, i) { return d.data.value; })
.text(function(d, i) { return d.data.label; });
//tooltip
arcs.on("mouseover", function(d) {
d3.select("#tooltip")
.style("left", `${d3.event.clientX}px`)
.style("top", `${d3.event.clientY}px`)
.classed("hidden", false);
d3.select("#tooltip-data")
.html(`Label: ${d.data.label}<br>Value: ${d.data.value}`);
});
arcs.on("mouseout", function(d) {
d3.select("#tooltip")
.classed("hidden", true);
});

How can I draw an arc with array of points in d3 v4

I wanted to draw an arc from an array of points like this:
var points = [
[
51.93326250000001,
21.4375
],
[
36.72733749999999,
40.603550000000002
],
[
21.527537500000008,
21.4144
]
];
I tried with d3.line(), d3.curveBasis() and d3.curveBundle.beta(1).
var arcPath = d3.line()
.x(function (d) {
return d[0];
})
.y(function (d) {
return d[1];
})
.curve(d3.curveBasis);
var arc = node.append('path').attr("d", arcPath(points));
But it is drawing a curved line:
which is not what I am looking for. I would like an arc instead:
I don't understand how to use this:
var arc = d3.arc()
.innerRadius(180)
.outerRadius(240)
.startAngle(0);
with my points.
In order to draw an arc, you need to know the center coordinates of its associated circle and its radius.
In this case, as your arc (part of circle) is defined by the coordinates of 3 points, you need to compute the center of the circle defined by these 3 points:
var points = [
[
51.93326250000001,
21.4375
],
[
36.72733749999999,
40.603550000000002
],
[
21.527537500000008,
21.4144
]
];
function calculateCircleCenter(A, B, C) {
var yDelta_a = B[1] - A[1];
var xDelta_a = B[0] - A[0];
var yDelta_b = C[1] - B[1];
var xDelta_b = C[0] - B[0];
var center = [];
var aSlope = yDelta_a / xDelta_a;
var bSlope = yDelta_b / xDelta_b;
center[0] = (aSlope*bSlope*(A[1] - C[1]) + bSlope*(A[0] + B[0]) - aSlope*(B[0]+C[0]) )/(2* (bSlope-aSlope) );
center[1] = -1*(center[0] - (A[0]+B[0])/2)/aSlope + (A[1]+B[1])/2;
return center;
}
function distance(A, B) {
var a = A[0] - B[0];
var b = A[1] - B[1];
return Math.sqrt(a*a + b*b);
}
var center = calculateCircleCenter(points[0], points[1], points[2]);
var radius = distance(points[0], center);
var svg = d3.select("svg").attr("width", 200).attr("height", 200);
// The circle
svg.append("circle")
.attr("cx", center[0])
.attr("cy", center[1])
.attr("r", radius)
.attr("fill", "white")
.attr("stroke", "black");
var startAngle = Math.atan2(points[0][1] - center[1], points[0][0] - center[0]) + 0.5 * Math.PI;
var endAngle = Math.atan2(center[1] - points[2][1], center[0] - points[2][0]) + 1.5 * Math.PI;
var arc = d3.arc().innerRadius(radius).outerRadius(radius);
var sector = svg.append("path")
.attr("fill", "none")
.attr("stroke-width", 2)
.attr("stroke", "blue")
.attr("d", arc({ "startAngle": startAngle, "endAngle": endAngle }))
.attr("transform", "translate(" + center[0] + "," + center[1] + ")");
// The 3 points:
svg.selectAll("small_circle")
.data(points)
.enter().append("circle")
.attr("cx", function (d) { return d[0]; })
.attr("cy", function (d) { return d[1]; })
.attr("r", 2)
.attr("fill", "red");
<svg></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
Concerning the maths:
You can use whatever method to compute the center of a circle defined by 3 points. Here is used this one.
You can then compute the radius of this circle by computing the distance between this center and one of the three points.
And you will also need to know the start and end angles of the arc, based on the angle between the first point and the circle's center and the angle between the last point and the circle's center. This can be achieved using this formula.
Concerning the drawing:
Here is how you can draw an arc with d3.js:
var arc = d3.arc().innerRadius(radius).outerRadius(radius);
var sector = svg.append("path")
.attr("fill", "none")
.attr("stroke-width", 2)
.attr("stroke", "blue")
.attr("d", arc({ startAngle: 0.5 * Math.PI, endAngle: 1.5 * Math.PI }))
.attr("transform", "translate(" + center[0] + "," + center[1] + ")");
An arc is defined by its radius. More specifically its innerRadius and outerRadius. In our case it's the same thing.
We then specify the center of the arc by translating the arc:
.attr("transform", "translate(" + center[0] + "," + center[1] + ")");
And we specify the start and end angles of the arc this way:
.attr("d", arc({ "startAngle": startAngle, "endAngle": endAngle }))
where startAngle and endAngle are computed based on first/last points and the center:
var startAngle = Math.atan2(points[0][1] - center[1], points[0][0] - center[0]) + 0.5 * Math.PI;
var endAngle = Math.atan2(center[1] - points[2][1], center[0] - points[2][0]) + 1.5 * Math.PI;

Replacing fixed json variable with a json value returned from a php file

I have a JavaScript code where the dataset is hard-coded in a variable, like this -
var dataset = [
{category: "Dept 1", measure: 0.30},
{category: "Dept 2", measure: 0.25},
{category: "Dept 4", measure: 0.15},
{category: "Dept 3", measure: 0.05},
{category: "Dept 5", measure: 0.18},
{category: "Dept 6", measure: 0.04},
{category: "Dept 7", measure: 0.03}
]
;
Now I want to use the json data which is getting returned from a php file (fetching through mysql query).
Whats the effective way to do this. does getJSON works well in this case?
Note- I am working on a pie chart in d3.js and this dataset requirement is for that chart.
EDIT -
This is how the code looks after suggested changes-
function dsPieChart(){
var width = 400,
height = 400,
outerRadius = Math.min(width, height) / 2,
innerRadius = outerRadius * .999,
// for animation
innerRadiusFinal = outerRadius * .5,
innerRadiusFinal3 = outerRadius* .45,
color = d3.scale.category20() //builtin range of colors
;
d3.json("data/mixchart.php", function(error, dataset) {
if (error) return console.warn(error);
else
{
var vis = d3.select("#pieChart")
.append("svg:svg") //create the SVG element inside the <body>
.data([dataset]) //associate our data with the document
.attr("width", width) //set the width and height of our visualization (these will be attributes of the <svg> tag
.attr("height", height)
.append("svg:g") //make a group to hold our pie chart
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")") //move the center of the pie chart from 0, 0 to radius, radius
;
var arc = d3.svg.arc() //this will create <path> elements for us using arc data
.outerRadius(outerRadius).innerRadius(innerRadius);
// for animation
var arcFinal = d3.svg.arc().innerRadius(innerRadiusFinal).outerRadius(outerRadius);
var arcFinal3 = d3.svg.arc().innerRadius(innerRadiusFinal3).outerRadius(outerRadius);
var pie = d3.layout.pie() //this will create arc data for us given a list of values
.value(function(d) { return d.measure; }); //we must tell it out to access the value of each element in our data array
var arcs = vis.selectAll("g.slice") //this selects all <g> elements with class slice (there aren't any yet)
.data(pie) //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties)
.enter() //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
.append("svg:g") //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
.attr("class", "slice") //allow us to style things in the slices (like text)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", up)
;
arcs.append("svg:path")
.attr("fill", function(d, i) { return color(i); } ) //set the color for each slice to be chosen from the color function defined above
.attr("d", arc) //this creates the actual SVG path using the associated data (pie) with the arc drawing function
.append("svg:title") //mouseover title showing the figures
.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 )
;
// Add a label to the larger arcs, translated to the arc centroid and rotated.
// source: http://bl.ocks.org/1305337#index.html
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 formatAsPercentage(d.value); })
.text(function(d) { return d.data.category; })
;
// Computes the label angle of an arc, converting from radians to degrees.
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("Revenue Share 2012")
.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));
}
}
dsPieChart();
Edit 2 -
<script type="text/javascript">
/*
################ FORMATS ##################
-------------------------------------------
*/
var formatAsPercentage = d3.format("%"),
formatAsPercentage1Dec = d3.format(".1%"),
formatAsInteger = d3.format(","),
fsec = d3.time.format("%S s"),
fmin = d3.time.format("%M m"),
fhou = d3.time.format("%H h"),
fwee = d3.time.format("%a"),
fdat = d3.time.format("%d d"),
fmon = d3.time.format("%b")
;
var width = 400,
height = 400,
outerRadius = Math.min(width, height) / 2,
innerRadius = outerRadius * .999,
// for animation
innerRadiusFinal = outerRadius * .5,
innerRadiusFinal3 = outerRadius* .45,
color = d3.scale.category20() //builtin range of colors
;
d3.json("data/mixchart.php", function(error,data) {
data.forEach(function(d) {
d.category =d.category;
d.measure = d.measure;
});
//if (err) return console.warn(err);
var vis = d3.select("#pieChart")
.append("svg:svg") //create the SVG element inside the <body>
.data(data) //associate our data with the document
.attr("width", width) //set the width and height of our visualization (these will be attributes of the <svg> tag
.attr("height", height)
.append("svg:g") //make a group to hold our pie chart
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")") //move the center of the pie chart from 0, 0 to radius, radius
;
var arc = d3.svg.arc() //this will create <path> elements for us using arc data
.outerRadius(outerRadius).innerRadius(innerRadius);
// for animation
var arcFinal = d3.svg.arc().innerRadius(innerRadiusFinal).outerRadius(outerRadius);
var arcFinal3 = d3.svg.arc().innerRadius(innerRadiusFinal3).outerRadius(outerRadius);
var pie = d3.layout.pie() //this will create arc data for us given a list of values
.value(function(d) { return d.measure; }); //we must tell it out to access the value of each element in our data array
var arcs = vis.selectAll("g.slice") //this selects all <g> elements with class slice (there aren't any yet)
.data(pie) //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties)
.enter() //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
.append("svg:g") //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
.attr("class", "slice") //allow us to style things in the slices (like text)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", up)
;
arcs.append("svg:path")
.attr("fill", function(d, i) { return color(i); } ) //set the color for each slice to be chosen from the color function defined above
.attr("d", arc) //this creates the actual SVG path using the associated data (pie) with the arc drawing function
.append("svg:title") //mouseover title showing the figures
.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 )
;
// Add a label to the larger arcs, translated to the arc centroid and rotated.
// source: http://bl.ocks.org/1305337#index.html
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 formatAsPercentage(d.value); })
.text(function(d) { return d.data.category; })
;
// Computes the label angle of an arc, converting from radians to degrees.
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("Revenue Share 2012")
.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));
}
</script>
var dataset = [];
$.getJSON("your_php_file", function(result){
dataset = result;
});
this will work but keep in mind that your php file is returning only json... rest you can play with the options.
There are numerous ways of fetching JSON, but as you're already working with d3, d3.json would be a good way to go.
E.g.
d3.json('your/json.json', function(error, json) {
if (error) return console.warn(error);
doSomethingWithJson(json)
});
Also see the d3 API

Categories

Resources