Create a semi circle pie char with variable attribute heights - javascript

I am new to JS and looking at the charts on http://d3js.org/ and I am having trouble understanding how to manipulate them. Basically I want to use the animated donut chart but I only want to display a half circle as well as different heights for each attribute(section in the pie). I have taken a look at some of the other tutorials and I just can't seem to wrap my head around it, any help would be greatly appreciated.
here is the code I am using:
var resume_dataset =
[
{job:"", start:2007, end: 2009, color: "#eb9ca1"},
{job:"", start:2008, end: 2010, color: "#f9a482"},
{job:"", start:2010, end: 2011, color: "#d0ebe9"},
{job:"", start:2013, end: 2014, color: "#cccc99"},
{job:"", start:2012, end: 2013.25, color: "#ffcc99"},
{job:"", start:2011, end: 2014, color:"#9999cc"}
];
function year_to_angle(year) {
return ((year - 2007)*Math.PI)/7-(Math.PI/2.0);
}
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
var degree = Math.PI/180;
var arc = d3.svg.arc()
.innerRadius(radius - 180)
.outerRadius(function(datum, i) { if(i != undefined){datum._i = i}; return radius - 30 + datum._i *10} );
var job_arc = d3.svg.arc()
.innerRadius(radius - 180)
.outerRadius(function(d){return radius - 30 + 20*(d.start-2007)})
.startAngle(function(d){return year_to_angle(d.start)})
.endAngle(function(d){return year_to_angle(d.end)})
//.onMouseOver(function(d){$(d.job).attr("class", "highlight")})
var svg = d3.select("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height - 70) + ")");
var path = svg.selectAll("path")
.data(resume_dataset)
.enter().append("path")
.attr("fill", function(d){return d.color})
.attr("d", job_arc).attr("opacity", 0.7)
</script>
Got it figured out! thanks for everyone's contributions!

concerning the half circle, you can set the .startAngle and .endAngle in the pie-variable.
var degree = Math.PI/180; // just to convert the radian-numbers
var pie = d3.layout.pie().sort(null).startAngle(-90*degree).endAngle(90*degree);

Related

Equivalent of chord.groups in D3.js v7

I am trying to recreate Nadia Bremer's chord diagram for storytelling with data from https://gist.github.com/nbremer/94db779237655907b907
She accesses the chord.groups element when creating the g element:
var g = svg.selectAll("g.group")
.data(chord.groups)
.enter().append("svg:g")
.attr("class", function(d) {return "group " + NameProvider[d.index];});
This works fine in v3 of D3. Here's a jsfiddle that works
However, when I try the same thing in D3 v7, chord.groups becomes undefined. Here is a jsfiddle with the v7 variant which gives an error when accessing chord.groups
The d3.chord() reference states that "The chords array also defines a secondary array of length n, chords.groups, where each group represents the combined outflow for node i..."
I've also found several examples on Observable which access chord.groups and they run fine. What am I doing wrong?
The error is very subtle, and here I'd blame the docs for lack of clarity, not you.
The problem is that you passed the data to the chord generator like this:
var chord = d3.chord(matrix)
But d3.chord() doesn't accept arguments. Instead, it returns a function which accepts the arguments. Thus, it should be:
var chord = d3.chord()(matrix)
Alternatively, defining the generator and then passing the data:
const chord = d3.chord();
const chordLayout = chord(matrix);
As you can see, it's a bit different. The confusion is maybe worsened by the fact that some D3 methods, like scales, accept data arguments like that, e. g. d3.scaleLinear(domain, range).
Most of D3 generators are like this. For instance, using a common line generator...
const lineGenerator = d3.line();
You get the path d attribute using:
lineGenerator(data);
Which is the same of d3.line()(data), but it's not the same of d3.line(data).
Here's your working v7 version:
var NameProvider = ["Apple", "HTC", "Huawei", "LG", "Nokia", "Samsung", "Sony", "Other"];
var matrix = [
[9.6899, 0.8859, 0.0554, 0.443, 2.5471, 2.4363, 0.5537, 2.5471], /*Apple 19.1584*/
[0.1107, 1.8272, 0, 0.4983, 1.1074, 1.052, 0.2215, 0.4983], /*HTC 5.3154*/
[0.0554, 0.2769, 0.2215, 0.2215, 0.3876, 0.8306, 0.0554, 0.3322], /*Huawei 2.3811*/
[0.0554, 0.1107, 0.0554, 1.2182, 1.1628, 0.6645, 0.4983, 1.052], /*LG 4.8173*/
[0.2215, 0.443, 0, 0.2769, 10.4097, 1.2182, 0.4983, 2.8239], /*Nokia 15.8915*/
[1.1628, 2.6024, 0, 1.3843, 8.7486, 16.8328, 1.7165, 5.5925], /*Samsung 38.0399*/
[0.0554, 0.4983, 0, 0.3322, 0.443, 0.8859, 1.7719, 0.443], /*Sony 4.4297*/
[0.2215, 0.7198, 0, 0.3322, 1.6611, 1.495, 0.1107, 5.4264] /*Other 9.9667*/
];
/*Sums up to exactly 100*/
var colors = ["#C4C4C4", "#69B40F", "#EC1D25", "#C8125C", "#008FC8", "#10218B", "#134B24", "#737373"];
/*Initiate the color scale*/
var fill = d3.scaleOrdinal()
.domain(d3.range(NameProvider.length))
.range(colors);
/*//////////////////////////////////////////////////////////
/////////////// Initiate Chord Diagram /////////////////////
//////////////////////////////////////////////////////////*/
var margin = {
top: 30,
right: 25,
bottom: 20,
left: 25
},
width = 650 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom,
innerRadius = Math.min(width, height) * .39,
outerRadius = innerRadius * 1.04;
/*Initiate the SVG*/
var svg = d3.select("#chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("svg:g")
.attr("transform", "translate(" + (margin.left + width / 2) + "," + (margin.top + height / 2) + ")");
var chord = d3.chord()
.sortSubgroups(d3.descending) /*sort the chords inside an arc from high to low*/
.sortChords(d3.descending)(matrix);
/*//////////////////////////////////////////////////////////
////////////////// Draw outer Arcs /////////////////////////
//////////////////////////////////////////////////////////*/
var arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var g = svg.selectAll("g.group")
.data(chord.groups)
.enter().append("svg:g")
.attr("class", function(d) {
return "group " + NameProvider[d.index];
});
g.append("svg:path")
.attr("class", "arc")
.style("stroke", function(d) {
return fill(d.index);
})
.style("fill", function(d) {
return fill(d.index);
})
.attr("d", arc)
.style("opacity", 0)
.transition().duration(1000)
.style("opacity", 0.4);
#chart rect {
fill: steelblue;
}
#chart text {
fill: white;
font: 10px Helvetica;
text-anchor: end;
}
<script src="https://d3js.org/d3.v7.min.js"></script>
<svg id="chart"></svg>

Native d3.js v5 - constructing a radial scale with desired interval

I am trying to create a circle and then have a radial linear scale to run along the circumference of that circle. It would be ideal to have the domain of this custom scale to be easily configurable. I think the snippet below gets me pretty close, but the rotate logic is hard to map out. The result should be ticks that represent each item in the (scale/dummy) data array. In this case, 0 to 50:
var margins = {top:100, bottom:300, left:100, right:100};
var height = 600;
var width = 900;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
graphGroup.append('circle').attr('r',30).attr('cx',0).attr('cy',0).style('fill',"#003366");
var axisG = graphGroup.selectAll(null)
.data(d3.range(0, 60, 10))
.enter()
.append("g")
.attr("transform", function(d,i) {
if (i<3) {
return "rotate(" + (-90 + (d * 180)) + ")";
}
if (i>3) {
return "rotate(" + (-90 + (d * 180)) + ")";
}
});
axisG.append("line")
.attr("x1", 40 - 4)
.attr("x2", 40 + 4)
.style("stroke", "black");
<script src="https://d3js.org/d3.v5.min.js"></script>
As we can see, the ticks don't get spaced correctly, and wind up all in the same place. Not sure why I can't append the tick lines in a way that follows the granularity of the data in the data() portion of axisG.
In other words, imagine the circle as going from 0 to 50 about its circumference (0-50 is the spread of my data). 0 and 50 would both occur at 12 o'clock (if it were a clock). Since we are moving in increments of 10, 10 would be 72 degrees from 12 o'clock, 20 would be 144 and so forth.
Question
What rotate logic would be needed to ensure that ticks are appended as desired? (appending lines at various angles/orientations depending on which tick value / which degree in the overall circle).
You're pretty close, but you need to translate numbers to degrees in some way. For instance, you could make a variable called amountPerRevolution and set it to 50 if you want it to have 50 for each revolution, then change your transform function to:
var amountPerRevolution = 50;
//...
.attr("transform", function(d) {
return "rotate(" + (-90 + (d * 360) / amountPerRevolution) + ")";
});
Alternatively, a more d3 idiomatic way of doing this is to use a linear scale:
var degreeScale = d3.scaleLinear([0, 50], [-90, 270]);
//...
.attr("transform", function(d) {
return "rotate(" + degreeScale(d) + ")";
});
Snippet:
var margins = {top:100, bottom:300, left:100, right:100};
var height = 600;
var width = 900;
var amountPerRevolution = 50;
var degreeScale = d3.scaleLinear([0, 50], [-90, 270]);
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
graphGroup.append('circle').attr('r',30).attr('cx',0).attr('cy',0).style('fill',"#003366");
var axisG = graphGroup.selectAll(null)
.data(d3.range(0, 60, 10))
.enter()
.append("g")
.attr("transform", function(d) {
// return "rotate(" + degreeScale(d) + ")";
return "rotate(" + (-90 + (d * 360) / amountPerRevolution) + ")";
});
axisG.append("line")
.attr("x1", 40 - 4)
.attr("x2", 40 + 4)
.style("stroke", "black");
<script src="https://d3js.org/d3.v5.min.js"></script>

Not able to center D3 pie chart

I'm trying to draw a circle with different data values as angles but for some reason, it's only the last data point that gets the color and display. I've tried to translate the svg but it seems not to budge.
I'm fairly new to D3 so I'm sure I've done something less intelligent without realizing it. As far I could tell, the angles in the g and path elements are as supposed to.
var height = 400, width = 600, radius = Math.min(height, width) / 2;
var colors = ["#red", "pink", "green", "yellow", "blue","magent","brown","olive","orange"];
var data = [1,2,1,2,1,2,1,3,1];
var chart = d3.select("#chart").append("svg")
.attr("width", width).attr("height", height);
chart.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var pie = d3.layout.pie().sort(null).value(function (d) { return d; });
var arc = d3.svg.arc().startAngle(0).innerRadius(0).outerRadius(radius);
var grx = chart.selectAll(".sector").data(pie(data))
.enter().append("g").attr("class", "sector");
grx.append("path")
.attr("d", arc)
.style("fill", function (d, i) {
console.log(d);
return colors[i];
});
The problem is that you're appending all the sectors of the pie to the svg node when they should be appended to the translated g node, you have two options to solve this problem
make chart equal to the translated g node
select g before all the .sectors and store that in grx
The first solution is simpler e.g.
var chart = d3.select("#chart").append("svg")
.attr("width", width).attr("height", height);
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
demo

D3pie.js labels.inner distance from the center

I use http://d3pie.org/#docs-settings
But there is no such parameter as the distance from the center to the internal labels.
Can someone tried to do it?
I want to move the internal labels closer to the outer edge of the circle.
Thank you so much.
now so:
need:
You can position the labels by defining a new arc as suggested in https://stackoverflow.com/a/8270668/2314737 and then applying the centroid function.
I defined a new arc newarc with an inner radius equal to 2/3 of the outer radius.
var newarc = d3.svg.arc()
.innerRadius(2 * radius / 3)
.outerRadius(radius);
Here's the JS code:
var width = 300;
var height = 300;
var svg = d3.select("body").append("svg");
svg.attr("width", width)
.attr("height", height);
var dataset = [11, 13, 18, 25, 31];
var radius = width / 2;
var innerRadius = 0;
var arc = d3.svg.arc()
.innerRadius(0)
.outerRadius(radius);
var pie = d3.layout.pie();
var arcs = svg.selectAll("g.arc")
.data(pie(dataset))
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(" + radius + ", " + radius + ")");
//Draw arc paths
var color = d3.scale.category10();
arcs.append("path")
.attr("fill", function (d, i) {
console.log(d);
return color(i);
})
.attr("stroke", "white")
.attr("d", arc);
var newarc = d3.svg.arc()
.innerRadius(2 * radius / 3)
.outerRadius(radius);
// Place labels
arcs.append("text")
.attr("transform", function (d) {
return "translate(" + newarc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.attr("fill", "white")
.text(function (d) {
return d.value + "%";
});
Here is a working demo: http://jsfiddle.net/user2314737/kvz8uev8/2/
I decided to enroll in another way.
I added my property in the object and function of positioning inner labels in D3pie file d3pie.js
This function is located on the line - 996 d3pie.js
positionLabelGroups: function(pie, section) {
d3.selectAll("." + pie.cssPrefix + "labelGroup-" + section)
.style("opacity", 0)
.attr("transform", function(d, i) {
var x, y;
if (section === "outer") {
x = pie.outerLabelGroupData[i].x;
y = pie.outerLabelGroupData[i].y;
} else {
var pieCenterCopy = extend(true, {}, pie.pieCenter);
// now recompute the "center" based on the current _innerRadius
if (pie.innerRadius > 0) {
var angle = segments.getSegmentAngle(i, pie.options.data.content, pie.totalSize, { midpoint: true });
var newCoords = math.translate(pie.pieCenter.x, pie.pieCenter.y, pie.innerRadius, angle);
pieCenterCopy.x = newCoords.x;
pieCenterCopy.y = newCoords.y;
//console.log('i ='+i , 'angle='+angle, 'pieCenterCopy.x='+pieCenterCopy.x, 'pieCenterCopy.y='+pieCenterCopy.y);
}
var dims = helpers.getDimensions(pie.cssPrefix + "labelGroup" + i + "-inner");
var xOffset = dims.w / 2;
var yOffset = dims.h / 4; // confusing! Why 4? should be 2, but it doesn't look right
// ADD VARAIBLE HERE !!! =)
var divisor = pie.options.labels.inner.pieDistanceOfEnd;
x = pieCenterCopy.x + (pie.lineCoordGroups[i][0].x - pieCenterCopy.x) / divisor;
y = pieCenterCopy.y + (pie.lineCoordGroups[i][0].y - pieCenterCopy.y) / divisor;
x = x - xOffset;
y = y + yOffset;
}
return "translate(" + x + "," + y + ")";
});
},
I add var divisor = pie.options.labels.inner.pieDistanceOfEnd;
Then I spotted this property devoltnyh the configuration file bhp and passed for plotting parameters.
inner: {
format: "percentage",
hideWhenLessThanPercentage: null,
pieDistanceOfEnd : 1.8
},
Meaning pieDistanceOfEnd: 1 hang tag on the outer radius of the chart
value pieDistanceOfEnd: 1.25 turn them slightly inward ....
You can play these parameters and to achieve the desired option.
In d3pie.js look for the function positionLabelGroups. In this function both labels (outer and inner) are positioned.
To modify the distance from the center you can play with the x,y here:
x = pieCenterCopy.x + (pie.lineCoordGroups[i][0].x - pieCenterCopy.x) / 1.8;
y = pieCenterCopy.y + (pie.lineCoordGroups[i][0].y - pieCenterCopy.y) / 1.8;
What I did was decreasing the 1.8 to 1.2 and obtained what youre looking for. Dont know what the other vars do, but you can study the code to figure it out

making an arc in d3.js

I am using the javascript library d3.js (http://d3js.org/) to create canvas data visualizations. I'm trying to make an arc, but it's not accepting the data parameters from my array. Does anyone know what I'm doing wrong? This is my code:
var chartConfig = { "canvasSize" : 800 }
var radius = chartConfig.canvasSize / 2;
var pi = Math.PI;
var vis = d3.select("#chart").append("svg")
.attr("width", radius * 2)
.attr("height", radius * 2)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
var arcData = [
{aS: 0, aE: 45,rI:radius/2,rO:radius}
];
var arc = vis.selectAll("arc").data(arcData).enter().append("arc");
arc.attr("innerRadius", function(d){d.rI}).attr("outerRadius",function(d){d.rO}).attr("class","arc");
function degToRad(degrees){
return degrees * (pi/180);
}
There is no arc element in SVG, you need to define the appropriate path element. Luckily there is a d3 helper function to do this.
var arc = d3.svg.arc()
.innerRadius(50)
.outerRadius(70)
.startAngle(45 * (Math.PI/180)) //converting from degs to radians
.endAngle(3) //just radians
vis.append("path")
.attr("d", arc)
.attr("transform", "translate(200,200)")
Working example at http://jsfiddle.net/g0r9n090/;

Categories

Resources