I am new to javascript and d3.js. I am trying to create pie chart.
I want to create something like this.
I am using d3.js. I want to thicken one of the arcs. I cannot find out to do this. I just want to thicken only "Engaged" item. It is not related to percentiles.
Here is my js code:
var engaged = 10;
var notEngaged = 50;
var maybeEngaged = 50;
var engagedYuzdelik = (100*engaged)/(engaged+notEngaged+maybeEngaged);
var notEngagedYuzdelik = (100*notEngaged)/(engaged+notEngaged+maybeEngaged);
var maybeEngagedYuzdelik=(100*maybeEngaged)/(engaged+notEngaged+maybeEngaged);
var dataset = [
{ name: 'Engaged', percent: [engagedYuzdelik.toFixed(2)] },
{ name: 'Not Engaged', percent: [notEngagedYuzdelik.toFixed(2)] },
{ name: 'Maybe Not Engaged', percent: [maybeEngagedYuzdelik.toFixed(2)] },
];
var pie=d3.layout.pie()
.value(function(d){return d.percent})
.sort(null);
var w=300,h=300;
var radius = Math.min(w, h) / 2;
var outerRadius=w/2;
var innerRadius=100;
var color = d3.scale.ordinal()
.range(["#65A6BF", "#9AC4D7", "#CCE2EA"]);
var arc=d3.svg.arc()
.outerRadius(radius - 50)
.innerRadius(radius - 60);
var svg=d3.select("#chart")
.append("svg")
.attr({
width:w,
height:h,
class:'shadow'
}).append('g')
.attr({
transform:'translate('+w/2+','+h/2+')'
});
var path=svg.selectAll('path')
.data(pie(dataset))
.enter()
.append('path')
.attr({
d:arc,
fill:function(d,i){
return color(d.data.name);
}
});
path.transition()
.duration(1000)
.attrTween('d', function(d) {
var interpolate = d3.interpolate({startAngle: 0, endAngle: 0}, d);
return function(t) {
return arc(interpolate(t));
};
});
};
Make the arc drawing conditional:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.17" data-semver="3.5.17" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
var engaged = 10;
var notEngaged = 50;
var maybeEngaged = 50;
var engagedYuzdelik = (100*engaged)/(engaged+notEngaged+maybeEngaged);
var notEngagedYuzdelik = (100*notEngaged)/(engaged+notEngaged+maybeEngaged);
var maybeEngagedYuzdelik=(100*maybeEngaged)/(engaged+notEngaged+maybeEngaged);
var dataset = [
{ name: 'Engaged', percent: [engagedYuzdelik.toFixed(2)] },
{ name: 'Not Engaged', percent: [notEngagedYuzdelik.toFixed(2)] },
{ name: 'Maybe Not Engaged', percent: [maybeEngagedYuzdelik.toFixed(2)] },
];
var pie=d3.layout.pie()
.value(function(d){return d.percent})
.sort(null);
var w=300,h=300;
var radius = Math.min(w, h) / 2;
var outerRadius=w/2;
var innerRadius=100;
var color = d3.scale.ordinal()
.range(["#65A6BF", "#9AC4D7", "#CCE2EA"]);
var thinArc = d3.svg.arc()
.outerRadius(radius - 50)
.innerRadius(radius - 60),
thickArc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 100);
var svg=d3.select("#chart")
.append("svg")
.attr({
width:w,
height:h,
class:'shadow'
}).append('g')
.attr({
transform:'translate('+w/2+','+h/2+')'
});
var path=svg.selectAll('path')
.data(pie(dataset))
.enter()
.append('path')
.attr({
fill:function(d,i){
return color(d.data.name);
}
});
path.transition()
.duration(1000)
.attrTween('d', function(d) {
var interpolate = d3.interpolate({startAngle: 0, endAngle: 0}, d);
return function(t) {
if (d.data.name === "Maybe Not Engaged"){
return thickArc(interpolate(t));
} else {
return thinArc(interpolate(t));
}
};
});
</script>
</body>
</html>
Related
Here is the code I'm working with. I'm generating data in php and sending that to d3 via json:
php file:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
stroke: #fff;
}
</style>
<body>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"> </script>
<script type="text/javascript" src="flare.js"></script>
<?php
// Move php data to JSON to be used in d3 apps
$flare_child_1 = array("name"=> "subchild1", "size"=> 90);
$flare_child_2 = array("name"=> "subchild2", "size"=> 10);
$flare_child_3 = array("name"=> "subchild3", "size"=> 55);
$flare_child_4 = array("name"=> "subchild4", "size"=> 72);
$flare_child_5 = array("name"=> "subchild5", "size"=> 60);
$flare_children_1[] = $flare_child_1;
$flare_children_1[] = $flare_child_2;
$flare_children_1[] = $flare_child_3;
$flare_children_1[] = $flare_child_4;
$flare_children_1[] = $flare_child_5;
$flare_children[] = array('name'=> "first", 'children'=>$flare_children_1);
$flare = array('name'=> "flare", 'children'=>$flare_children);
echo "<script> var root = "; echo json_encode($flare); echo ";";
echo "input_data(root);</script>";
?>
</body>
js file
var width = 960,
height = 700,
radius = (Math.min(width, height) / 2) - 10;
var formatNumber = d3.format(",d");
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.sqrt()
.range([0, radius]);
var color = d3.scale.category20c();
var partition = d3.layout.partition()
.value(function(d) { return d.size; });
var arc = d3.svg.arc()
.startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI,x(d.x))); })
.endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI,x(d.x + d.dx))); })
.innerRadius(function(d) { return Math.max(0, y(d.y)); })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
function getRootmostAncestorByRecursion(node) {
return node.depth > 1 ? getRootmostAncestorByRecursion(node.parent) : node;
}
function input_data(root) {
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2) + ")");
svg.selectAll("path")
.data(partition.nodes(root))
.enter().append("path")
.attr("d", arc)
.style("fill", function(d) {
return color(getRootmostAncestorByRecursion(d).name);
})
.on("click", click)
.append("title")
.text(function(d) {
return d.name + "\n" + formatNumber(d.value);
});
}
function click(d) {
svg.transition()
.duration(750)
.tween("scale", function() {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); };
})
.selectAll("path")
.attrTween("d", function(d) { return function() { return arc(d);}; });
}
d3.select(self.frameElement).style("height", height + "px");
I'm expecting something similar to this https://bl.ocks.org/mbostock/raw/4348373/
but all I'm seeing a blue spot. The children don't feature. I'm not sure what I'm doing wrong.
Thanks for looking at this.
Couple things wrong here.
First, while you are correct path creation is wrapped in a function and will execute after the php code but your svg creation will execute before the body tag, so you'll never get an svg tag.
Second, your JSON is malformed. I executed the php and it produces:
<script>
var root = {
"name": "flare",
"children": {
"name": "first",
"children": [{
"name": "subchild1",
"size": 90
}, {
"name": "subchild2",
"size": 10
}, {
"name": "subchild3",
"size": 55
}, {
"name": "subchild4",
"size": 72
}, {
"name": "subchild5",
"size": 60
}]
}
};
input_data(root);
Notice, that the first children is an object and not an array of objects.
Putting these two things together here.
Following Pluralsight D3.js Data Visualization Fundamentals Course.
Get error:
Data is from this Github endpoint
Plunker is here
Code:
var realHeight = document.getElementById('d3Container').offsetHeight;
var realWidth = document.getElementById('d3Container').offsetWidth;
var scale = d3.scaleLinear().domain([130, 350]).range([10, 100]);
var githubUrl = 'https://api.github.com/repos/bsullins/d3js-resources/contents/monthlySalesbyCategoryMultiple.json';
var buildLine = function(ds, dl, idx) {
var minSales = d3.min(ds.monthlySales, function(d) {
return d.month
});
var maxSales = d3.max(ds.monthlySales, function(d) {
return d.month
});
// create scales
var xScale = d3.scaleLinear()
.domain([minSales, maxSales])
.range(0, realWidth);
var yScale = d3.scaleLinear()
.domain([0, maxSales])
.range([realHeight, 0]);
// add scales below in .x and .y
var lineFun = d3.line()
.x(function(d) {
console.log(d.month); // outputs as type numbers in console
// return ((d.month -20130001)/3.25); // hardcoded works
return xScale( d.month );
})
.y(function(d, i) {
// var svgHeight = document.getElementById('d3Container').offsetHeight;
// return ( (svgHeight / ( dl * (1+idx) ) - d.sales) ); // hardcoded works
return yScale( d.sales );
})
.curve(d3.curveLinear);
var svg3 = d3.select('body #d3Container')
.append('svg')
.attrs({
'h': '100%',
'w': '100%',
'fill': 'black'
});
var viz = svg3.append('path')
.attrs({
/** access nested JSON here **/
'd': lineFun(ds.monthlySales),
'stroke': function() {
return (idx + 1 === dl) ? 'royalblue' : 'lime';
},
'stroke-width': 4,
'fill': 'white;'
})
};
var showHeader = function(ds) {
d3.select('body #d3Container')
.append('h2')
.text(ds.category + 'Sales 2013');
};
d3.json(githubUrl, function(error, data) {
if (error) {
return;
} else {
// decode data
var decodedData = JSON.parse(window.atob(data.content));
// pass in functions for each
decodedData.contents.forEach(function(ds, idx) {
showHeader(ds);
buildLine(ds, decodedData.contents.length, idx);
})
}
});
Easiest to work with the plunk.
Can uncomment out the 'hardcoded works' comments to see what it should look like.
In D3 (any version), range() accepts an array.
Your xScale right now has this range:
.range(0, hackWidth);
However, it has to be:
.range([0, hackWidth]);
Here is the updated plunker: https://plnkr.co/edit/DB0VVRsGQO4teIC6ltyQ?p=preview
Hello i need to make chart like this
stroke on sides
lighter arc
no stroke between lighter and darker arc
Here is what i've got:
Codepen.io
HTML:
<div id="chart" style="width:400px; height: 400px; position:relative;"></div>
JavaScript:
var w = 400;
var h = 400;
var r = h/2;
var color = ["#27c794", "#fd7b74", "#fd9f9a"]
var color2 = ["#fd9f9a", "#27c794", "#fd7b74"]
var image_width = 32;
var image_height = 32;
var data = [
{"label": "Category A", "value":10, "icon": "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"},
{"label": "Category B", "value":45, "icon": "http://files.gamebanana.com/img/ico/sprays/4f69c5f09c7bf.png"},
{"label": "Category C", "value":45, "icon": "http://files.gamebanana.com/img/ico/sprays/4ecb328ca104a.png"}
];
var vis = d3.select('#chart').append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")");
var pie = d3.pie().value(function(d){return d.value;});
// declare an arc generator function
var arc = d3.arc().outerRadius(r -20).innerRadius(r - 100);
var arc2 = d3.arc().outerRadius(r -10).innerRadius(r - 20);
// select paths, use arc generator to draw
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice");
arcs.append("svg:path")
.attr("fill", function(d, i){
return color[i];
}).attr("stroke", "#fff").style("stroke-width", "3")
.attr("d", function (d) {
return arc(d);
})
arcs.append("svg:path").attr("d", function (d) {
return arc2(d);
}).attr("fill", function(d, i){
return color[i];
}).style("opacity", "0.4");
var x = d3.select("#chart").append("div");
x.append("p").text("1212");
x.append("p").text("11");
x.selectAll("p").attr('style', function(d, i) {
return "color:" + color[i];
})
// add the image
arcs.append("svg:image").attr("transform", function(d, i){
var x = arc.centroid(d)[0] - image_width/2;
var y = arc.centroid(d)[1] - image_height/2;
return "translate(" + x + "," + y + ")";
})
.attr("xlink:href",function(d) { if(d.data.value >= 15) {return d.data.icon;}})
.attr("width", image_width)
.attr("height", image_height);
My goal is to create an animated donut chart that shows 75% - 90% accuracy rate. For this I've started with the code below, but I'd like to make a few tweaks:
I would like to customize the colors of each node output by the
chart (I've added the variable section_path_fill_colors). Currently the code
just chooses random colors I believe.
I would like to add a static text label in the middle of the donut
75% - 90% (I've added the variable static_label).
Currently the labels are attached to each node.
Can someone help me accomplish this?
UPDATE:
I was able to solve the coloring of nodes with:
var color = d3.scale.ordinal()
.domain(["one", "two", "three"])
.range(["#ffffff" , "#d1d2d4" , "#17afd1"]);
Now just need help setting the static label in the middle
JS:
var static_label = '75% - 90%';
var employees = [
{dept: '', count : 75},
{dept: '', count : 15},
{dept: '', count : 10}
];
var color = d3.scale.ordinal()
.domain(["one", "two", "three"])
.range(["#ffffff" , "#d1d2d4" , "#17afd1"]);
var maxWidth = 200;
var maxHeight = 200;
var outerRadius = 100;
var ringWidth = 20;
function checkEndAll(transition, callback) {
var n = 0;
transition
.each(function() { ++n; })
.each("end", function() {
if (!--n) callback.apply(this, arguments);
});
}
function drawAnimatedRingChart(config) {
var pie = d3.layout.pie().value(function (d) {
return d.count;
});
//var color = d3.scale.category10();
var arc = d3.svg.arc();
function tweenPie(finish) {
var start = {
startAngle: 0,
endAngle: 0
};
var i = d3.interpolate(start, finish);
return function(d) { return arc(i(d)); };
}
arc.outerRadius(config.outerRadius || outerRadius)
.innerRadius(config.innerRadius || innerRadius);
// Remove the previous ring
d3.select(config.el).selectAll('g').remove();
var svg = d3.select(config.el)
.attr({
width : maxWidth,
height: maxHeight
});
// Add the groups that will hold the arcs
var groups = svg.selectAll('g.arc')
.data(pie(config.data))
.enter()
.append('g')
.attr({
'class': 'arc',
'transform': 'translate(' + outerRadius + ', ' + outerRadius + ')'
});
// Create the actual slices of the pie
groups.append('path')
.attr({
'fill': function (d, i) {
return color(i);
}
})
.transition()
.duration(config.duration || 1000)
.attrTween('d', tweenPie)
.call(checkEndAll, function () {
// Finally append the title of the text to the node
groups.append('text')
.attr({
'text-anchor': 'middle',
'transform': function (d) {
return 'translate(' + arc.centroid(d) + ')';
}
})
.text(function (d) {
// Notice the usage of d.data to access the raw data item
return d.data.dept;
});
});
}
// Render the initial ring
drawAnimatedRingChart({
el: '.animated-ring svg',
outerRadius: outerRadius,
innerRadius: outerRadius - ringWidth,
data: employees
});
Just do this:
svg.append('text')
.attr({
x: outerRadius,
y: outerRadius,
'text-anchor': 'middle
})
.text(static_label);
I`m trying to make the rest of the chart transparent or set it to a specific color after I click on a specific slice of the doughnut. So far so good in console the filter is working if I hard-code the type it works( I set it to null at the beginning). I don't know why i can not get the slice that I click and make the rest of the chart set to that specific color. My though is that I have to update the chart somehow but with drawdata() function doesn't work ...
Here is my code:
var filter = {
device: null,
os_version: null,
app_version: null
};
// Creating the object Doughnut
var Doughnut = function(type) {
// Properties
var width = 160;
var height = 160
var radius = Math.min(width, height) / 2;
var donutWidth = 35;
var legendRectSize = 18;
var legendSpacing = 4;
var type = type;
// Array of Colors for the graph
var color = d3.scale.category20c();
var colorFunc = function(key) {
var normalColor = color(key);
if (filter[type] == null || key == filter[type]) {
console.log("normal color")
return normalColor;
}
console.log("trans color")
return "#d5eff2";
};
// Graph Elements
var chart = null;
var svg = null;
var path = null;
var legend = null;
// Our current dataSet
var dataSet = null;
// d3 functions
var arc = d3.svg.arc()
.innerRadius(radius - donutWidth)
.outerRadius(radius);
var pie = d3.layout.pie()
.value(function(d) {
return d.value;
});
// This is the initialize method - we create the basic graph, no data
var initialize = function(chartElement){
chart = chartElement;
svg = d3.select(chart)
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2) +
',' + (height / 2) + ')');
};
var update = function() {
d3.json("./api/distribution/", function(data){
dataSet = data;
data.value = +data.value;
drawData();
});
}
var drawData = function() {
path = svg.selectAll('path')
.data(pie(dataSet[type]))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d) {
return colorFunc(d.data.key);
})
.on('click', function(d) {
if (filter[type] == d.data.key) {
filter[type] = null;
} else {
filter[type] = d.data.key;
}
console.log(filter)
// $(chart).empty()
drawData();
});
createLegends();
};
var createLegends = function() {
legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length /2;
var horz = -2 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) {
return d;
});
};
return{
init: initialize,
update: update
}
};
// Here we create instance of doughnuts
var doughnutGraphs = (function() {
var init = function() {
// Create four doughnuts
var doughnut1 = new Doughnut("device");
var doughnut2 = new Doughnut("os_version");
var doughnut3 = new Doughnut("app_version");
// Initialize with an element
doughnut1.init("#chart_1");
doughnut2.init("#chart_2");
doughnut3.init("#chart_3");
// Update each of them with data
doughnut1.update();
doughnut2.update();
doughnut3.update();
};
return {
init: init
}
})();
I found the answer :
Create a method to clean then call it in the drawdata()
var clean = function() {
svg.selectAll('path').remove();
and call it .on('click')
.on('click', function(d) {
if (filter[type] == d.data.key) {
filter[type] = null;
} else {
filter[type] = d.data.key;
}
console.log(filter)
// $(chart).empty()
clean();
drawData();
});