I have been working off of this basic pie example to learn about writing pie charts in D3, but while I got the example to work when mimicking their data structure, I wanted to try it out with a JSON data structure that would be more native to how I would structure data for the visualizations. When switching the same data to this new structure I noticed that the black stroke appears and the annotations, but the slices aren't present and the annotation labels are referencing an index and object value.
I believe this is due to the .entries() method that converts it to a key-value data structure, but I'm curious if that is a necessary method to use in order to visualize the data points or if there is a simpler method to utilize the structure I have in place.
Working data structure:
var data = {
deep: 22.37484390963787,
light: 62.65183335225337,
rem: 14.973322738108752
}
JSON data structure:
var data = [
{ "label": "deep", "value": 22.37484390963787 },
{ "label": "light", "value": 62.65183335225337 },
{ "label": "rem", "value": 14.973322738108752 }
]
var data = [
{ "label": "deep", "value": 22.37484390963787 },
{ "label": "light", "value": 62.65183335225337 },
{ "label": "rem", "value": 14.973322738108752 }
]
// var data = {
// deep: 22.37484390963787,
// light: 62.65183335225337,
// rem: 14.973322738108752
// }
console.log(data)
var width = 480;
var height = 480;
var margin = 40;
var radius = Math.min(width, height) / 2 - margin;
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var color = d3.scaleOrdinal()
.domain(data)
.range(["#98abc5", "#8a89a6", "#7b6888"]);
var pie = d3.pie()
.value(function(d) { return d.value; });
var data_ready = pie(d3.entries(data));
console.log(data_ready);
var arcGenerator = d3.arc()
.innerRadius(0)
.outerRadius(radius);
svg.selectAll('viz')
.data(data_ready)
.enter()
.append('path')
.attr('d', arcGenerator)
.attr('fill', function(d){ return color(d.data.key)})
.attr("stroke", "black")
.style("stroke-width", "2px")
.style("opacity", 0.7);
svg.selectAll('viz')
.data(data_ready)
.enter()
.append('text')
.text(function(d){ return d.data.key + ', ' + d.data.value})
.attr("transform", function(d) { return "translate(" + arcGenerator.centroid(d) + ")"; })
.style("text-anchor", "middle")
.style("font-size", 17);
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
The other way to achieve this is to not use d3.entries and pass your data directly. A couple of other tweaks are required where you get the color and label text (ie use d.data.label in place of d.data.key).
var data = [
{ "label": "deep", "value": 22.37484390963787 },
{ "label": "light", "value": 62.65183335225337 },
{ "label": "rem", "value": 14.973322738108752 }
]
// var data = {
// deep: 22.37484390963787,
// light: 62.65183335225337,
// rem: 14.973322738108752
// }
console.log(data)
var width = 480;
var height = 480;
var margin = 40;
var radius = Math.min(width, height) / 2 - margin;
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var color = d3.scaleOrdinal()
.domain(data)
.range(["#98abc5", "#8a89a6", "#7b6888"]);
var pie = d3.pie()
.value(function(d) { return d.value; });
var data_ready = pie(data);
console.log(data_ready);
var arcGenerator = d3.arc()
.innerRadius(0)
.outerRadius(radius);
svg.selectAll('viz')
.data(data_ready)
.enter()
.append('path')
.attr('d', arcGenerator)
.attr('fill', function(d){ return color(d.data.label)})
.attr("stroke", "black")
.style("stroke-width", "2px")
.style("opacity", 0.7);
svg.selectAll('viz')
.data(data_ready)
.enter()
.append('text')
.text(function(d){ return d.data.label + ', ' + d.data.value})
.attr("transform", function(d) { return "translate(" + arcGenerator.centroid(d) + ")"; })
.style("text-anchor", "middle")
.style("font-size", 17);
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
You can't just change the format of the data without changing something else too. The simplest solution is to reformat your structure into the structure that d3 expected in the first place:
var formatted_data = data.reduce((acc,i) => {acc[i.label] = i.value; return acc;},{});
And then pass that to entries:
var data = [
{ "label": "deep", "value": 22.37484390963787 },
{ "label": "light", "value": 62.65183335225337 },
{ "label": "rem", "value": 14.973322738108752 }
]
// var data = {
// deep: 22.37484390963787,
// light: 62.65183335225337,
// rem: 14.973322738108752
// }
var formatted_data = data.reduce((acc,i) => {acc[i.label] = i.value; return acc;},{});
console.log(formatted_data)
var width = 480;
var height = 480;
var margin = 40;
var radius = Math.min(width, height) / 2 - margin;
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var color = d3.scaleOrdinal()
.domain(data)
.range(["#98abc5", "#8a89a6", "#7b6888"]);
var pie = d3.pie()
.value(function(d) { return d.value; });
var data_ready = pie(d3.entries(formatted_data));
console.log(data_ready);
var arcGenerator = d3.arc()
.innerRadius(0)
.outerRadius(radius);
svg.selectAll('viz')
.data(data_ready)
.enter()
.append('path')
.attr('d', arcGenerator)
.attr('fill', function(d){ return color(d.data.key)})
.attr("stroke", "black")
.style("stroke-width", "2px")
.style("opacity", 0.7);
svg.selectAll('viz')
.data(data_ready)
.enter()
.append('text')
.text(function(d){ return d.data.key + ', ' + d.data.value})
.attr("transform", function(d) { return "translate(" + arcGenerator.centroid(d) + ")"; })
.style("text-anchor", "middle")
.style("font-size", 17);
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
Related
I have pieces of a pie, I am tracking pieces less than 10% - I draw an additional path with them and I need to draw a line up from the last path, like this:
Here's an example:
const height = 300,
width = 300,
margin = 30,
data = [{
browser: "Google Chrome",
rate: 42.52
},
{
browser: "Firefox",
rate: 16.23
},
{
browser: "Opera",
rate: 12.6
},
{
browser: "Internet Explorer",
rate: 8.97
},
{
browser: "Yandex Browser",
rate: 9.12
},
{
browser: "Other",
rate: 10.56
}
];
const color = d3.scale.category10();
const radius = Math.min(width - 2 * margin, height - 2 * margin) / 2;
const arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(0);
const arc2 = d3.svg.arc()
.outerRadius(radius + 5)
.innerRadius(radius + 3);
const pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.rate;
});
const svg = d3.select("body").append("svg")
.attr("class", "axis")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform",
"translate(" + (width / 2) + "," + (height / 2) + ")");
const g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) {
return color(d.data.browser);
});
g.append("path")
.attr("d", arc2)
.style("display", d => d.data.rate < 10 ? 'block' : 'none');
g.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.style("text-anchor", "middle")
.text(function(d) {
return d.data.browser;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
The outer circle is drawn from a variable arc2.
Ultimately pieces less than 10% must be carried up:
Inspired by the answer here
get the d attribute from the last element
ust path.split("L")[1].split("A")[0] to get the position of the endpoint of the arc.
g.filter(d=>d.data.rate < 10) // using filter to only draw what we need
.append("path")
.attr("class", "arc2")
.attr("d", arc2)
let last_arc2 = d3.selectAll(".arc2")[0][d3.selectAll(".arc2")[0].length-1]
let last_arc2_end = d3.select(last_arc2).attr("d").split("L")[1].split("A")[0]
let last_arc2_end_x = last_arc2_end.split(",")[0],
last_arc2_end_y = last_arc2_end.split(",")[1]
g.append("line")
.attr("x1", last_arc2_end_x)
.attr("x2", last_arc2_end_x)
.attr("y1", last_arc2_end_y)
.attr("y2", last_arc2_end_y-20)
.style("stroke", "black")
working example
I create a bubble map with D3, and I want the user to be able to click on a button and the circles on the map will transition into bars of a bar chart. I am using the enter, update, exit pattern, but right now what I have isn't working as the bar chart is drawn on top and all the bars and circles are translated, instead of the circles transitioning into bars and the bars being translated into place. Below is the relevant part of my code, and here is the link to the demo: https://jhjanicki.github.io/circles-to-bars/
var projection = d3.geo.mercator()
.scale(150)
.center([20, 40])
.translate([width / 2, height / 2]);
var path= d3.geo.path()
.projection(projection);
var features = countries2.features;
d3.csv("data/sum_by_country.csv", function(error, data) {
data.sort(function(a,b){
return a.value - b.value;
});
var myfeatures= joinData(features, data, ['value']);
var worldMap = svg.append('g');
var world = worldMap.selectAll(".worldcountries")
.data(myfeatures)
.enter()
.append("path")
.attr("class", function(d){
return "World " + d.properties.name+" worldcountries";
})
.attr("d", path)
.style("fill", "#ddd")
.style("stroke", "white")
.style("stroke-width", "1");
var radius = d3.scale.sqrt()
.domain([0,1097805])
.range([3, 20]);
var newFeatures = [];
myfeatures.forEach(function(d){
if(d.properties.hasOwnProperty("value")){
console.log(d.properties.name);
newFeatures.push(d);
}
});
newFeatures.sort(function(a,b){
return b.properties.value - a.properties.value;
});
var bubbles = svg.append("g").classed("bubbleG","true");
bubbles.selectAll("circle")
.data(newFeatures)
.enter().append("circle")
.attr("class", "bubble")
.attr("transform", function(d) {
return "translate(" + path.centroid(d) + ")";
})
.attr("r", function(d){
return radius(d.properties.value);
})
.attr("fill","#2166ac")
.attr("stroke","white")
.attr("id", function(d){
return "circle "+d.properties.name;
});
$('#bubblebar').click(function(){
mapBarTransition(newFeatures,bubbles)
});
});
// button onclick
function mapBarTransition(data,bubbles){
var margin = {top:20, right:20, bottom:120, left:80},
chartW = width - margin.left - margin.right,
chartH = height - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(data.map(function(d) { return d.properties.name; }))
.rangeRoundBands([0, chartW], .4);
var y = d3.scale.linear()
.domain([0,1097805])
.nice()
.range([chartH,0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.ticks(8)
.orient("left");
var barW = width / data.length;
bubbles.append("g").classed("bubblebar-chart-group", true);
bubbles.append("g").classed("bubblebar-x-axis-group axis", true);
bubbles.append("g").classed("bubblebar-y-axis-group axis", true);
bubbles.transition().duration(1000).attr({transform: "translate(" + margin.left + "," + margin.top + ")"});
bubbles.select(".bubblebar-x-axis-group.axis")
.attr({transform: "translate(0," + (chartH) + ")"})
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
bubbles.select(".bubblebar-y-axis-group.axis")
.transition()
.call(yAxis);
barW = x.rangeBand();
var bars = bubbles.select(".bubblebar-chart-group")
.selectAll(".bubble")
.data(data);
bars.enter().append("rect")
.classed("bubble", true)
.attr("x", function(d) { return x(d.properties.name); })
.attr("y", function(d) { return y(d.properties.value); })
.attr("width", barW)
.attr("height", function(d) { return chartH - y(d.properties.value); })
.attr("fill","#2166ac");
bars.transition()
.attr("x", function(d) { return x(d.properties.name); })
.attr("y", function(d) { return y(d.properties.value); })
.attr("width", barW)
.attr("height", function(d) { return chartH - y(d.properties.value); });
bars.exit().transition().style({opacity: 0}).remove();
}
And here is the repo for your reference: https://github.com/jhjanicki/circles-to-bars
First, you have two very different selections with your circles and bars. They are in separate g containers and when you draw the bar chart, you aren't even selecting the circles.
Second, I'm not sure that d3 has any built-in way to know how to transition from a circle element to a rect element. There's some discussion here and here
That said, here's a quick hack with your code to demonstrate one way you could do this:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" rel='stylesheet'>
<link href="//rawgit.com/jhjanicki/circles-to-bars/master/css/style.css" rel='stylesheet'>
<!-- Roboto & Asar CSS -->
<link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
<link href="https://fonts.googleapis.com/css?family=Asar" rel="stylesheet">
</head>
<body>
<button type="button" class="btn btn-primary" id="bubblebar">Bar Chart</button>
<div id="chart"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<!-- D3.geo -->
<script src="https://d3js.org/d3.geo.projection.v0.min.js"></script>
<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//rawgit.com/jhjanicki/circles-to-bars/master/data/countries2.js"></script>
<script>
window.onload = function() {
// set up svg and scrolling
var width = window.innerWidth / 2;
var height = window.innerHeight;
var svg = d3.select("#chart").append("svg")
.attr('width', width)
.attr('height', height);
var bubbleMapState = 'map'; // map or bar
var projection = d3.geo.mercator()
.scale(150)
.center([20, 40])
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var features = countries2.features;
d3.csv("//rawgit.com/jhjanicki/circles-to-bars/master/data/sum_by_country.csv", function(error, data) {
data.sort(function(a, b) {
return a.value - b.value;
});
var myfeatures = joinData(features, data, ['value']);
var worldMap = svg.append('g');
var world = worldMap.selectAll(".worldcountries")
.data(myfeatures)
.enter()
.append("path")
.attr("class", function(d) {
return "World " + d.properties.name + " worldcountries";
})
.attr("d", path)
.style("fill", "#ddd")
.style("stroke", "white")
.style("stroke-width", "1");
var radius = d3.scale.sqrt()
.domain([0, 1097805])
.range([3, 20]);
var newFeatures = [];
myfeatures.forEach(function(d) {
if (d.properties.hasOwnProperty("value")) {
console.log(d.properties.name);
newFeatures.push(d);
}
});
newFeatures.sort(function(a, b) {
return b.properties.value - a.properties.value;
});
var bubbles = svg.append("g").classed("bubbleG", "true");
bubbles.selectAll("rect")
.data(newFeatures)
.enter().append("rect")
.attr("class", "bubble")
.attr("transform", function(d) {
return "translate(" + path.centroid(d) + ")";
})
.attr("width", function(d) {
return radius(d.properties.value) * 2;
})
.attr("height", function(d){
return radius(d.properties.value) * 2;
})
.attr("rx", 20)
.attr("fill", "#2166ac")
.attr("stroke", "white")
.attr("id", function(d) {
return "circle " + d.properties.name;
});
$('#bubblebar').click(function() {
mapBarTransition(newFeatures, bubbles)
});
});
// button onclick
function mapBarTransition(data, bubbles) {
if (bubbleMapState == 'map') {
bubbleMapState == 'bar';
} else {
bubbleMapState == 'map';
}
var margin = {
top: 20,
right: 20,
bottom: 120,
left: 80
},
chartW = width - margin.left - margin.right,
chartH = height - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(data.map(function(d) {
return d.properties.name;
}))
.rangeRoundBands([0, chartW], .4);
var y = d3.scale.linear()
.domain([0, 1097805])
.nice()
.range([chartH, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.ticks(8)
.orient("left");
var barW = width / data.length;
bubbles.append("g").classed("bubblebar-chart-group", true);
bubbles.append("g").classed("bubblebar-x-axis-group axis", true);
bubbles.append("g").classed("bubblebar-y-axis-group axis", true);
bubbles.transition().duration(1000).attr({
transform: "translate(" + margin.left + "," + margin.top + ")"
});
bubbles.select(".bubblebar-x-axis-group.axis")
.attr({
transform: "translate(0," + (chartH) + ")"
})
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
bubbles.select(".bubblebar-y-axis-group.axis")
.transition()
.call(yAxis);
barW = x.rangeBand();
var bars = d3.select(".bubbleG")
.selectAll(".bubble")
.data(data);
bars.enter().append("rect")
.classed("bubble", true)
.attr("x", function(d) {
return x(d.properties.name);
})
.attr("y", function(d) {
return y(d.properties.value);
})
.attr("width", barW)
.attr("height", function(d) {
return chartH - y(d.properties.value);
})
.attr("fill", "#2166ac");
bars.transition()
.duration(1000)
.attr("transform", function(d){
return "translate(" + x(d.properties.name) + "," + y(d.properties.value) + ")";
})
.attr("rx", 0)
.attr("width", barW)
.attr("height", function(d) {
return chartH - y(d.properties.value);
});
bars.exit().transition().style({
opacity: 0
}).remove();
}
function joinData(thisFeatures, thisData, DataArray) {
//loop through csv to assign each set of csv attribute values to geojson counties
for (var i = 0; i < thisData.length; i++) {
var csvCountry = thisData[i]; //the current county
var csvKey = csvCountry.Country; //the CSV primary key
//loop through geojson regions to find correct counties
for (var a = 0; a < thisFeatures.length; a++) {
var geojsonProps = thisFeatures[a].properties; //the current region geojson properties
var geojsonKey = geojsonProps.name; //the geojson primary key
//where primary keys match, transfer csv data to geojson properties object
if (geojsonKey == csvKey) {
//assign all attributes and values
DataArray.forEach(function(attr) {
var val = parseFloat(csvCountry[attr]); //get csv attribute value
geojsonProps[attr] = val; //assign attribute and value to geojson properties
});
};
};
};
return thisFeatures;
};
}
</script>
</body>
I am interested in creating this liquid bar chart of sorts. With the pointers/dotted markers pulling off to the side like shown.
22 April - latest code to work with dynamic data http://jsfiddle.net/NYEaX/1855/
latest bar chart code
http://jsfiddle.net/NYEaX/1827/
latest cleaned up water wave code
http://jsfiddle.net/Qh9X5/10331/
//I want to strip down this waterwave code
http://jsfiddle.net/Qh9X5/10091/
//progress chart
http://jsfiddle.net/NYEaX/1740/
latest base code for this bar chart.
http://jsfiddle.net/NYEaX/1822/
var $this = $("#checmicalbars");
var data = [{
"label": "Rendering",
"value": 90,
"startcolor": "#c3da54",
"endcolor": "#c1e500"
},
{
"label": "Character Design",
"value": 95,
"startcolor": "#e94adc",
"endcolor": "#aae3dd"
},
{
"label": "Sketching",
"value": 80,
"startcolor": "#c3da54",
"endcolor": "#fa5283"
},
{
"label": "Story Boarding",
"value": 90,
"startcolor": "#e94adc",
"endcolor": "#f83b03"
},
{
"label": "Drawing",
"value": 82,
"startcolor": "#c3da54",
"endcolor": "#f88504"
},
{
"label": "Painting",
"value": 90,
"startcolor": "#e94adc",
"endcolor": "#f7d200"
}];
var h = 150;
var w = 300;
var options = {
minlimit: 0,
maxlimit: 100
}
// setup scales
var x = d3.scale.ordinal()
.rangeRoundBands([0, w], .1);
var y = d3.scale.linear()
.range([h, 0]);
var xAxis = d3.svg.axis()
.scale(this.x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(this.y)
.orient("left");
// setup scales
// chart container
var progresschart = d3.select($this[0]).append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(0,5)");
var barrectsholder = progresschart.append("g")
.attr("class", "chart")
.attr("transform", "translate(15,0)");
// chart container
y.domain([options["minlimit"], options["maxlimit"]]);
//__ bars
var bar = barrectsholder.selectAll("rect")
.data(data);
//__ enter
bar.enter()
.append("rect")
.attr("class", "bar")
.attr("y", h);
//__ update
bar
.attr("y", h)
.attr("height", 0)
.style("fill", function(d){
return d.startcolor;
})
.transition()
.duration(2500)
.style("fill", function(d){
return d.endcolor;
})
.attr("width", 20)
.attr("x", function(d, i) {
return 30 * i;
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return h - y(d.value);
})
//__ exit
bar.exit()
.transition()
.duration(250)
.attr("y", 0)
.attr("height", 0)
.remove();
//__ bars
I've merged the two charts together - but the watercode is translated correctly if its a separate svg - be good to get this code cleaned up/reviewed. Also ensuring the pointers/labels adjust/adapt with more/less data sets.
latest jsfiddle
http://jsfiddle.net/NYEaX/1843/
var $this = $("#checmicalbars");
var data = [{
"label": "Rendering",
"value": 90,
"startcolor": "#c3da54",
"endcolor": "#c1e500"
},
{
"label": "Character Design",
"value": 95,
"startcolor": "#e94adc",
"endcolor": "#aae3dd"
},
{
"label": "Sketching",
"value": 80,
"startcolor": "#c3da54",
"endcolor": "#fa5283"
},
{
"label": "Story Boarding",
"value": 90,
"startcolor": "#e94adc",
"endcolor": "#f83b03"
},
{
"label": "Drawing",
"value": 82,
"startcolor": "#c3da54",
"endcolor": "#f88504"
},
{
"label": "Painting",
"value": 90,
"startcolor": "#e94adc",
"endcolor": "#f7d200"
}];
var h = 450;
var w = 400;
var barHeight = 150;
var barWidth = 180;
var options = {
minlimit: 0,
maxlimit: 100
}
// setup scales
var x = d3.scale.ordinal()
.rangeRoundBands([0, barWidth], .1);
var y = d3.scale.linear()
.range([barHeight, 0]);
var xAxis = d3.svg.axis()
.scale(this.x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(this.y)
.orient("left");
// setup scales
// chart container
var progresschart = d3.select($this[0]).append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(0,5)");
var barrectsholder = progresschart.append("g")
.attr("class", "barrectsholder")
.attr("transform", "translate(15,0)");
var labelsholder = progresschart.append("g")
.attr("class", "labelsholder")
.attr("transform", "translate(10,"+(barHeight+ 20)+")");
var lineholder = progresschart.append("g")
.attr("class", "lineholder")
.attr("transform", "translate(25,"+(barHeight+ 15)+")");
// chart container
y.domain([options["minlimit"], options["maxlimit"]]);
/*
var gauge = barrectsholder
.append("g")
.attr("width", config.w)
.attr("height", config.h)
.append("g");
liquidBar(gauge, config);
*/
var bar = barrectsholder.selectAll("svg")
.data(data);
bar.enter()
.append("svg")
.attr("class", function(d, i) {
return "bar"+i;
})
.attr("width", 20)
.attr("x", function(d, i) {
return 30 * i;
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return barHeight - y(d.value);
})
$.each(data, function( index, value ) {
//alert( index + ": " + value );
var config = {
w: 20,
h: barHeight,
value: value.value,
amplitude: 0.02, // The wave height as a percentage of the radius of the wave circle.
countPerWidth: 1, // The number of full waves per width of the wave circle.
riseTime: 1000, // The amount of time in milliseconds for the wave to rise from 0 to it's final height.
animateTime: 1000, // The amount of time in milliseconds for a full wave to enter the wave circle.
rise: true, // Control if the wave should rise from 0 to it's full height, or start at it's full height.
colorTransition: 1000,
colorBefore: value.startcolor, // The color before of the fill wave.
colorAfter: value.endcolor, // The color after of the fill wave.
offset: 0 // The amount to initially offset the wave. 0 = no offset. 1 = offset of one full wave.
};
var gauge = barrectsholder.selectAll("svg.bar"+index)
liquidBar(gauge, config);
});
/*
//__ bars
var bar = barrectsholder.selectAll("rect")
.data(data);
//__ enter
bar.enter()
.append("rect")
.attr("class", "bar")
.attr("y", barHeight);
//__ update
bar
.attr("y", barHeight)
.attr("height", 0)
.style("fill", function(d){
return d.startcolor;
})
.transition()
.duration(2500)
.style("fill", function(d){
return d.endcolor;
})
.attr("width", 20)
.attr("x", function(d, i) {
return 30 * i;
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return barHeight - y(d.value);
})
//__ exit
bar.exit()
.transition()
.duration(250)
.attr("y", 0)
.attr("height", 0)
.remove();
//__ bars
*/
//__ labels
var labels = labelsholder.selectAll("text")
.data(data);
labels.enter()
.append("text")
.attr("class", "barlabels")
.attr("x", 200)
.attr("y", function(d, i) {
return 20 * i;
})
.text(function(d) {
return d.label;
})
var lines = lineholder.selectAll("text")
.data(data);
lines.enter()
.append("line")// attach a line
.style("stroke-dasharray", ("3, 3"))
.style("stroke", "black")// colour the line
.attr("x1", function(d, i) {
return barWidth-(30 * (i+1));
})//x pos of the 1st end of the line
.attr("y1", function(d, i) {
return 20 * i;
})//y pos of the 1st end of the line
.attr("x2", function(d, i) {
return barWidth;
})//x pos of the 2nd end of the line
.attr("y2", function(d, i) {
return 20 * i;
});//y pos of the 2nd end of the line
var lineHeights = 100;
lines.enter()
.append("line")// attach a line
.style("stroke-dasharray", ("3, 3"))
.style("stroke", "black")// colour the line
.attr("x1", function(d, i) {
return 30 * i;
})//x pos of the 1st end of the line
.attr("y1", function(d, i) {
return lineHeights - (20 * i);
})//y pos of the 1st end of the line
.attr("x2", function(d, i) {
return 30 * i;
})//x pos of the 2nd end of the line
.attr("y2", function(d, i) {
return -15;
});//y pos of the 2nd end of the line
function liquidBar(gauge, config) {
var fillPercent = Math.max(0, Math.min(100, config.value)) / 100;
var waveHeightScale = d3.scale.linear()
.range([0, config.amplitude, 0])
.domain([0, 50, 100]);
var waveHeight = (config.h / 2) * waveHeightScale(fillPercent * 100);
var waveLength = config.w / config.countPerWidth;
var waveClipCount = 1 + config.countPerWidth;
var waveClipWidth = waveLength * waveClipCount;
// Data for building the clip wave area.
var data = [];
for (var i = 0; i <= 40 * waveClipCount; i++) {
data.push({
x: i / (40 * waveClipCount),
y: (i / (40))
});
}
// Scales for controlling the size of the clipping path.
var waveScaleX = d3.scale.linear().range([0, waveClipWidth]).domain([0, 1]);
var waveScaleY = d3.scale.linear().range([0, waveHeight]).domain([0, 1]);
// Scales for controlling the position of the clipping path.
var waveRiseScale = d3.scale.linear()
// The clipping area size is the height of the fill circle + the wave height, so we position the clip wave
// such that the it will overlap the fill circle at all when at 0%, and will totally cover the fill
// circle at 100%.
.range([(config.h + waveHeight), (waveHeight)])
.domain([0, 1]);
var waveAnimateScale = d3.scale.linear()
.range([0, waveClipWidth - config.w]) // Push the clip area one full wave then snap back.
.domain([0, 1]);
// Center the gauge within the parent SVG.
var gaugeGroup = gauge.append("g")
.attr("class", "gaugeGroup")
.attr("transform", "translate(0,0)");
var randomId = Math.floor(Math.random() * 26) + Date.now();
// The clipping wave area.
var clipArea = d3.svg.area()
.x(function(d) {
return waveScaleX(d.x);
})
.y0(function(d) {
return waveScaleY(Math.sin(Math.PI * 2 * config.offset * -1 + Math.PI * 2 * (1 - config.countPerWidth) + d.y * 2 * Math.PI));
})
.y1(function(d) {
return (config.h + waveHeight);
});
var waveGroup = gaugeGroup.append("defs")
.append("clipPath")
.attr("id", "clipWave" + randomId);
var wave = waveGroup.append("path")
.datum(data)
.attr("d", clipArea)
.attr("T", 0);
// The inner circle with the clipping wave attached.
var fillGroup = gaugeGroup.append("g")
.attr("clip-path", "url(#clipWave" + randomId + ")");
fillGroup.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", config.w)
.attr("height", config.h)
.style("fill", config.colorBefore)
.transition()
.duration(config.colorTransition)
.style("fill", config.colorAfter);
// Make the wave rise. wave and waveGroup are separate so that horizontal and vertical movement can be controlled independently.
var waveGroupXPosition = config.w - waveClipWidth;
if (config.rise) {
waveGroup.attr('transform', 'translate(' + waveGroupXPosition + ',' + waveRiseScale(0) + ')')
.transition()
.duration(config.riseTime)
.attr('transform', 'translate(' + waveGroupXPosition + ',' + waveRiseScale(fillPercent) + ')')
.each("start", function() {
wave.attr('transform', 'translate(1,0)');
}); // This transform is necessary to get the clip wave positioned correctly when waveRise=true and waveAnimate=false. The wave will not position correctly without this, but it's not clear why this is actually necessary.
} else {
waveGroup.attr('transform', 'translate(' + waveGroupXPosition + ',' + waveRiseScale(fillPercent) + ')');
}
animateWave();
function animateWave() {
wave.attr('transform', 'translate(' + waveAnimateScale(wave.attr('T')) + ',0)');
wave.transition()
.duration(config.animateTime * (1 - wave.attr('T')))
.ease('linear')
.attr('transform', 'translate(' + waveAnimateScale(1) + ',0)')
.attr('T', 1)
.each('end', function() {
wave.attr('T', 0);
animateWave(config.animateTime);
});
}
}
I've looked through similar questions but still can't implement code correctly.
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var percentageFormat = d3.format("%");
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.values;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.json("staff.json", function(error, json_data) {
var data = d3.nest()
.key(function(d) {
return d.Position;
})
.rollup(function(d) {
return d.length;
}).entries(json_data);
data.forEach(function(d) {
d.percentage = d.values / json_data.length;
});
console.log("data variable", data);
console.log("pie(data)", pie(data));
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc")
.on('mouseover', function() {
var current = this;
var others = svg.selectAll(".arc").filter(function(el) {
return this != current
});
others.selectAll("path").style('opacity', 0.8);
})
.on('mouseout', function() {
var current = this;
d3.select(this)
.style('opacity', 1);
var others = svg.selectAll(".arc").filter(function(el) {
return this != current
});
others.selectAll("path").style('opacity', 1);
});
g.append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return color(i);
});
g.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) {
console.log("d is", d);
return percentageFormat(d.data.percentage);
});
Here's a JSON-file:
[{
"Position" : "Programmer",
"Name" : "Giacomo Gulizzoni",
"Age" : 37,
"Sex" : "Male",
"Project" : "SmartFactory"
}, {
"Position" : "Tester",
"Name" : "Marko Botton",
"Age" : 34,
"Sex" : "Male",
"Project" : "SmartFactory"
}, {
"Position" : "Tester",
"Name" : "Mariah Maclachian",
"Age" : 37,
"Sex" : "Female",
"Project" : "SmartFactory"
}, {
"Position" : "Tester",
"Name" : "Valerie Liberty",
"Age" : 25,
"Sex" : "Female",
"Project" : "SmartProject"
}, {
"Position" : "Programmer",
"Name " : "Guido Jack Gulizzoni",
"Age" : 22,
"Sex" : "Male",
"Project" : "SmartProject"
}]
Actually, legends are just rectangles that are bound to data if I'm not mistaken. But in my case items in JSON file are not numerical values. Have look at my plunk for the results of my experiments.
In your code:
You are setting the data in the legend selection wrong
var legend = ...
.data(pie(data))//this is wrong
.enter().append("g")
You need to do like this
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.selectAll("g")
.data(data)//setting the data as we know there are only two set of data[programmar/tester] as per the nest function you have written
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return color(d.key);
});
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d.key; });
Working code here
Hope this helps!
Actually it was in your code,
var legend = svg.selectAll('.legend') // NEW
.data(pie(data)) // NEW
.enter() // NEW
.append('g') // NEW
.attr('class', 'legend') // NEW
.attr('transform', function(d, i) { // NEW
var height = legendRectSize + legendSpacing; // NEW
var offset = height * pie(data).length / 2; // NEW
var horz = -2 * legendRectSize; // NEW
var vert = i * height - offset; // NEW
return 'translate(' + horz + ',' + vert + ')'; // NEW
});
legend.append('rect') // NEW
.attr('width', legendRectSize) // NEW
.attr('height', legendRectSize) // NEW
.style('fill', function(d,i){return color(i);}) // NEW
.style('stroke', function(d,i){return color(i);}); // NEW
legend.append('text') // NEW
.attr('x', legendRectSize + legendSpacing) // NEW
.attr('y', legendRectSize - legendSpacing) // NEW
.text(function(d) { console.log("text: ");console.log(d);return d.data.key; });
I've un commented and did little changes.
You can observe the code.
I am learning d3 and trying to make use d3 library in my application for showing some visualization chart .i am getting data through web api in the form of json here.
My script:
var data= "rows":[
{
"STATUS" : "Active",
"count" : "246"
},
{
"STATUS" : "Not Proceeded With",
"count" : "40"
}
]
var width = 800,
height = 250,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) {
return d.count;
});
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data.rows))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function (d) {
return color(d.data.rows.STATUS);
});
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.rows.STATUS;
});
Now i want to use that data set for creating the donut chart.i have tried but not getting success.can any one plz suggest me any idea how i can do
Couple problems:
1.) Your JSON isn't valid:
var data = { //<- missing bracket
"rows": [
{
"STATUS": "Active",
"count": "246"
}, {
"STATUS": "Not Proceeded With",
"count": "40"
}
]
} //<- missing bracket
2.) You are not accessing the STATUS attribute correctly for your fill and text label:
return color(d.data.STATUS); //<-- don't need "rows", you only passed in data.rows
Here is is working nicely:
var data= { "rows":[
{
"STATUS" : "Active",
"count" : "246"
}
,
{
"STATUS" : "Not Proceeded With",
"count" : "40"
}
]};
var width = 800,
height = 250,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) {
return d.count;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data.rows))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function (d) {
return color(d.data.STATUS);
});
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.STATUS;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>