I am trying to delete the D3 pie chart. I have created the pie chart but what I want to do is on some button click I want to delete the pie chart and want to re-draw the new pie chart with some new data. I didn't find any help from the documentation of D3's website. I am sharing the code for drawing the pie chart below.
var w =300;
var h =300;
var r =150;
data = [{"label": "joy", "value": 20},
{"label": "fear", "value": 20},
{"label": "anger", "value": 20},
{"label": "disgust", "value": 20},
{"label": "sadness", "value": 20}]
var colorArray = ['#f6cf31', '#7e53a3', '#f8522a', '#3cbf55', '#00a7de']
var vis = d3.select(".chart-holder")
.append("svg:svg")
.data([data])
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + r + "," + r + ")")
var arc = d3.svg.arc()
.outerRadius(r);
var pie = d3.layout.pie()
.value(function(d) {
return d.value;
});
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 colorArray[i];
})
If you set the id of the pie chart, you can then use it later to remove the element:
var arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice")
.attr("id", "mypiechart");
...
d3.select("#mypiechart").remove();
I have resolved it my self, well anyway thanks #ilivewithian.
The soultion is
var vis = angular.element('svg');
// var vis = d3.select(".chart-holder").selectAll("svg");
vis.remove();
Remove is actually javascript default function to remove any element.
So i get the svg element from the div i bind for pie chart.And removed the svg element.
And then i redraw it,using the same techinique i used for drawing the pie chart first time.
As long as the exit method for d3 data is working the same way like update, per documentation
http://bl.ocks.org/mbostock/3808218
you have first to assign the data to the path then remove it for updating
var path = svg.selectAll('path')
.data(pie(data));
path.enter()
.append('path')
.attr('d', arc)
.attr('class', '.path')
.attr('id', function(d) {
return d.data.key;
})
.attr('fill', function(d, i) {
return d.data.color;
})
// exit
path.exit().remove();
I hope it helps.
You should be able to simply set a new value for your data variable and the chart can update from this.
function setData(){
data = [{"label": "new", "value": 50},
{"label": "old", "value": 50}]
myChart.update();
};
Pulled from the GitHub Repo:
https://github.com/novus/nvd3/wiki/Sample-chart-%28your-first-nvd3-chart!%29
Related
I've encountered an error while coding my chart on my "card" bootstrap.
The thing is I only want the label to show on my pie chart but the problem is console on google chrome says "Uncaught TypeError: d3.arc(...).outerRadius(...).innerRadius(...).text is not a function"
In which i don't get why.
Below is my code for the pie-chart:
var data = [
{"platform": "Android", "percentage": 40.11},
{"platform": "Windows", "percentage": 36.69},
{"platform": "iOS", "percentage": 13.06}
];
var svgCirWidth = 600, svgCirHeight = 300, radius = Math.min(svgCirWidth, svgCirHeight) / 2;
const pieContainer = d3.select("#pieChart")
.append("svg")
.attr("width", svgCirWidth)
.attr("height", svgCirHeight);
//create group element to hold pie chart
var g = pieContainer.append("g")
.attr("transform", "translate(" + 170 + "," + radius + ")");
var color = d3.scaleOrdinal(d3.schemeCategory10);
var pie = d3.pie().value(function(d){
return d.percentage;
});
var path = d3.arc()
.outerRadius(radius)
.innerRadius(0);
var arc = g.selectAll("arc")
.data(pie(data))
.enter() //means keeps looping in the data
.append("g");
arc.append("path")
.attr("d", path)
.attr("fill", function(d){
return color(d.data.percentage);
})
var label = d3.arc()
.outerRadius(radius)
.innerRadius(0)
.text(function(d){
return d;
});
d3.arc returns a path's d attribute. This cannot contain text.
In SVG, text must be enclosed inside <text>.
Since your variable arc is a selection of a g element, you could append <text> to it:
arc.append('text')
.text(/* ... */)
Edit after receiving a comment that "it still won't work"
Please be specific whenever reporting problems, in order to receive proper help: is there any error message? What is the observed behaviour, and what was the expected one?
Using arc.append('text') seems to work just fine.
Debugging tip
It is often helpful to use console.log as shown below to see what is going on:
arc.append('text')
.text(function(d){
console.log('text', d)
return d;
})
The code above will show that d is an object, this is why the labels are shown in the page as [object object].
I assume that the desired behaviour is rather to show the platform and percentage values.
Then instead of return d, we know thanks to the console.log output that we should rather use d.data.platform and d.data.percentage:
return d.data.platform + ': ' + d.data.percentage;
Labels placement
Another issue observed is that all labels are overlapping. This is normal, no position has been defined for them.
A quick use of search engine shows us this example, where arc.centroid is used together with text-anchor: middle to position each label in a suitable location.
Solution is illustrated in the snipped below.
var data = [
{"platform": "Android", "percentage": 40.11},
{"platform": "Windows", "percentage": 36.69},
{"platform": "iOS", "percentage": 13.06}
];
var svgCirWidth = 600, svgCirHeight = 300, radius = Math.min(svgCirWidth, svgCirHeight) / 2;
const pieContainer = d3.select("#pieChart")
.append("svg")
.attr("width", svgCirWidth)
.attr("height", svgCirHeight);
//create group element to hold pie chart
var g = pieContainer.append("g")
.attr("transform", "translate(" + 170 + "," + radius + ")");
var color = d3.scaleOrdinal(d3.schemeCategory10);
var pie = d3.pie().value(function(d){
return d.percentage;
});
var path = d3.arc()
.outerRadius(radius)
.innerRadius(0);
var arc = g.selectAll("arc")
.data(pie(data))
.enter() //means keeps looping in the data
.append("g")
arc.append("path")
.attr("d", path)
.attr("fill", function(d){
return color(d.data.percentage);
})
arc.append('text')
.text(function(d){
return d.data.platform + ': ' + d.data.percentage;
})
.attr('transform', function(d){
return 'translate(' + path.centroid(d) + ')'
})
.attr('text-anchor', 'middle')
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.15.0/d3.min.js"></script>
<div id="pieChart"></div>
I want to repeat a group of shapes specifically
text rect text circles
Where in circles is again a repeat of circle
My data is
Jsondata =[
{ "name":"A", "WidthOfRect":50, "justAnotherText":"250", "numberOfCircles" :3 },
{ "name":"B", "WidthOfRect":150, "justAnotherText":"350","numberOfCircles" :2 },
{ "name":"C", "WidthOfRect":250, "justAnotherText":"450","numberOfCircles" :1 }]
Basically Out of this data i am trying to construct a customized bar chart.
The width of the rect is based upon the data widthofrect from the json, as well as number of circles is based upon numberofcircles property.
I looked out for a number of options to repeat group of shapes but couldn't find one.
First of all, you're right in your comment: do not use loops to append elements in a D3 code. Also, your supposition about the length of the data is correct.
Back to the question:
The text and rect part is pretty basic, D3 101, so let's skip that. The circles is the interesting part here.
My proposed solution involves using d3.range to create an array whose number of elements (or length) is specified by numberOfCircles. That involves two selections.
First, we create the groups (here, scale is, obviously, a scale):
var circlesGroups = svg.selectAll(null)
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(20," + scale(d.name) + ")"
});
And then we create the circles. Pay attention to the d3.range:
var circles = circlesGroups.selectAll(null)
.data(function(d) {
return d3.range(d.numberOfCircles)
})
.enter()
.append("circle")
//etc...
Here is a demo, I'm changing the numberOfCircles in your data to paint more circles:
var width = 500,
height = 200;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var data = [{
"name": "A",
"WidthOfRect": 50,
"justAnotherText": "250",
"numberOfCircles": 13
},
{
"name": "B",
"WidthOfRect": 150,
"justAnotherText": "350",
"numberOfCircles": 22
},
{
"name": "C",
"WidthOfRect": 250,
"justAnotherText": "450",
"numberOfCircles": 17
}
];
var scale = d3.scalePoint()
.domain(data.map(function(d) {
return d.name
}))
.range([20, height - 20])
.padding(0.5);
var colorScale = d3.scaleOrdinal(d3.schemeCategory10);
var circlesGroups = svg.selectAll(null)
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(20," + scale(d.name) + ")"
})
.style("fill", function(d) {
return colorScale(d.name)
})
var circles = circlesGroups.selectAll(null)
.data(function(d) {
return d3.range(d.numberOfCircles)
})
.enter()
.append("circle")
.attr("r", 5)
.attr("cx", function(d) {
return 10 + 12 * d
});
var axis = d3.axisLeft(scale)(svg.append("g").attr("transform", "translate(20,0)"));
<script src="https://d3js.org/d3.v5.min.js"></script>
PS: I'm using D3 v5.
I've got a pie chart and it will only draw once. I got it from Mike Bostock's pie chart example. I'm new to D3 and I can't figure out why it won't redraw. I saw this post about redrawing a bar chart, but for some reason that technique doesn't work on my pie chart. I'm sure I'm doing something wrong.
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.percent; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
function drawChart(error, data) {
console.log("here");
data.forEach(function(d) {
d.percent = +d.percent;
});
var g = svg.selectAll(".arc")
.data(pie(data))
.enter()
.append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) {
console.log("inside path");
return d.data.color;
});
g.append("text")
.attr("transform", function(d) { console.log("inside transform", d);return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.data.color; });
}
drawChart(undefined, [{"color": "green", "percent": 50}, {"color": "red", "percent": 50}]);
setTimeout(function () {
drawChart(undefined, [{"color": "green", "percent": 75}, {"color": "red", "percent": 25}]);
}, 1000)
here's the jsbin.
Problem 1:
You are adding the d attribute to the DOM g which is wrong.
<g class="arc" d="M-240,2.939152317953648e-14A240,240 0 0,1 -4.408728476930471e-14,-240L0,0Z">
<path d="M-9.188564877424678e-14,240A240,240 0 1,1 1.6907553595872533e-13,-240L0,0Z" style="fill: red;">
</path>
<text transform="translate(-120,-9.188564877424678e-14)" dy=".35em" style="text-anchor: middle;">red</text>
</g>
d attribute is only for path not for g.
So this line is incorrect
g.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
Problem 2:
Your update function is incorrect(same reason problem 1)
function update (data) {
console.log("here", data);
var value = this.value;
g = g.data(pie(data)); // compute the new angles
g.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
};
In my opinion you should call your drawChart function again for update.
with an exception that you remove old g group like this.
svg.selectAll(".arc").remove();
The advantage is that we are using the same code for create and update (DRY).
So your timeout function becomes like thsi
setTimeout(function () {
drawChart(undefined, [{"color": "green", "percent": 75}, {"color": "red", "percent": 25}]);
}, 2000);
Full working code here
Hope this helps!
I am trying to create a map and plot some points to it using d3, I found a few good examples to build off of but I believe i am stuck. My guess is I am not handling the plot points correctly according to how I have the data structured. I could use a bit of help - this is my first attempt. Here's what I have so far :
var m_width = document.getElementById("map").offsetWidth,
width = 938,
height = 500;
var projection = d3.geo.mercator()
.scale(150)
.translate([width / 2, height / 1.5]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("#map").append("svg")
.attr("preserveAspectRatio", "xMidYMid")
.attr("viewBox", "0 0 " + width + " " + height)
.attr("width", m_width)
.attr("height", m_width * height / width);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
var g = svg.append("g");
d3.json("scripts/world-110m2.json", function(error, us) {
g.append("g")
.attr("id", "countries")
.selectAll("path")
.data(topojson.feature(us, us.objects.countries).features)
.enter()
.append("path")
.attr("id", function(d) { return d.id; })
.attr("d", path)
});
svg.selectAll(".pin")
.data(places)
.enter().append("circle", ".pin")
.attr("r", 5)
.attr("transform", function(d) {
return "translate(" + projection([
d.earthquakes.lon,
d.earthquakes.lat
]) + ")"
});
window.addEventListener('resize', function(event){
var w = document.getElementById("map").offsetWidth;
svg.attr("width", w);
svg.attr("height", w * height / width);
});
And the "places" data is structured like so
var places = {"count":"392","earthquakes":[{"src":"us","eqid":"2010sdbk","timedate":"2010-01-31 15:18:44","lat":"-18.7507","lon":"169.3940","magnitude":"5.1","depth":"231.50","region":"Vanuatu"}
Where all the place are inside an object array "earthquakes" inside places. (lon and lat specifically inside of that).
The world map shows up fine, I am just having trouble getting these plot points to work. Would appreciate any help greatly. Thanks for reading!!
You almost had it, but couple problems here:
1.) The data you pass to .data should be an array (of where to add your circles).
2.) In your places object, you lat/lon are strings and need to be converted to numbers.
Try:
var places = {
"count": "392",
"earthquakes": [{
"src": "us",
"eqid": "2010sdbk",
"timedate": "2010-01-31 15:18:44",
"lat": "-18.7507",
"lon": "169.3940",
"magnitude": "5.1",
"depth": "231.50",
"region": "Vanuatu"
}]
};
svg.selectAll(".pin")
.data(places.earthquakes) //<-- pass array
.enter()
.append("circle")
.attr("class","pin")
.attr("r", 5)
.attr("transform", function(d) {
return "translate(" + projection([
+d.lon, //<-- coerce to number
+d.lat
]) + ")";
});
Example here.
I am very new to coding and am trying to learn D3. I have map of France which I am able to make appear in the browser. However, I am trying to display circles on the map based on a csv file. I am not sure what I am doing wrong with the function... Any help/direction would be great.
Here is a Cloud9 of the code and files... https://ide.c9.io/santiallende/d3-map-bubbles-france
I won't sugarcoat, your code's a mess.
You define and append 4 different svg elements to the body and you create 3 different projections. All of it is unnecessary. Just go through and remove all the redundancies.
//Width and height
var w = 800;
var h = 350;
var canvas = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h)
d3.json("france.json", function(data) {
var group = canvas.selectAll("g")
.data(data.features)
.enter()
.append("g")
//Define map projection
var projection = d3.geo.mercator()
.translate([400, 1200])
.scale([1100]);
//Define path generator
var path = d3.geo.path()
.projection(projection);
var areas = group.append("path")
.attr("d", path)
.attr("class", "area")
.attr("fill", "steelblue");
//Load in cities data
d3.csv("wineregions.csv", function(data) {
canvas.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("cy", function(d) {
return projection([d.lon, d.lat])[1];
})
.attr("r", 5)
.style("fill", "yellow")
.style("opacity", 0.75);
});
});
Fixed code here.