Smooth transition of data in a polar chart - javascript

I have js code that plots a simple polar chart in the HTML page. I am trying to have it update the data on the plot every 1 second.
I was able to update the data and plot the new points, however, there are two things I have not figured out yet.
Right now every second I am cleaning the plot area and plotting everything new, instead I would like to update the data by creating a smooth sliding transition from the previous position to the new one.
The second issue is that the labeling of the points is not being deleted with the old point position, which creates multiple labeling on top of each other.
What should I add in order to create this smooth transition for the plotted points and their labels?
Here is my code snippet:
<html>
<head>
<meta charset='ISO-8859-1'>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body style='background-color:lightgray'>
<div id="chart" style='width: 400px; height: 400px; padding-left: 5px; padding-bottom: 5px;'></div>
<script>
var color = d3.scale.category20();
var deg2rad = Math.PI / 180;
var width = 400,
height = 350,
radius = Math.min(width, height) / 2 - 30;
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {
return r(d[1]);
})
.angle(function(d) {
return -d[0] + Math.PI / 2;
});
var gr = null;
createSkyplot();
updateSkyPlot();
function createSkyplot() {
//////////////////////
gr = svg.append("g")
.attr("class", "r axis")
.selectAll("g")
.data(r.ticks(5))
.enter().append("g");
gr.append("circle").attr("r", r).style('fill', 'white');
gr.append("text")
.attr("y", function(d) {
return -r(d) - 4;
})
.attr("transform", "rotate(20)")
.style("text-anchor", "middle")
.style('fill', 'blue')
.text(function(d) {
return d;
});
/////////////////////
/////////////////////
var ga = svg.append("g")
.attr("class", "a axis")
.selectAll("g")
.data(d3.range(0, 360, 45))
.enter().append("g")
.attr("transform", function(d) {
return "rotate(" + (d - 90) + ")";
});
ga.append("line").attr("x2", radius).style('stroke', 'black').style('stroke-dasharray', '1,8');
ga.append("text")
.attr("x", radius + 6)
.attr("dy", ".35em")
.style("text-anchor", function(d) {
return d < 360 && d > 90 ? "end" : null;
})
.attr("transform", function(d) {
return d < 360 && d > 90 ? "rotate(180 " + (radius + 3) + ",0)" : null;
})
.text(function(d) {
return d + "°";
});
/////////////////////
}
function updateSkyPlot() {
var pos = [{
"position": [1, Math.random() * 20, Math.random() * 20],
"label": 1
}, {
"position": [3, Math.random() * 20, Math.random() * 20],
"label": 5
}];
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {
return r(d[1]);
})
.angle(function(d) {
return -d[0] + Math.PI / 2;
});
svg.selectAll('circle').remove();
gr.append("circle").attr("r", r).style('fill', 'white');
var points = svg.selectAll("point")
.data(pos)
.enter()
.append("a") // The container
.attr("transform", function(d) {
var coors = line([d.position]).slice(1).slice(0, -1);
return "translate(" + coors + ")"
});
points.append("circle")
.attr("class", "point")
.attr("r", 10)
.attr("fill", function(d, i) {
return color(i);
});
points.append("text")
.attr("class", "label")
.text(function(d) {
return d.label
})
.style("font-size", "10")
.attr("transform", "translate(-4,5)");
setTimeout(updateSkyPlot, 1000);
}
</script>
</body>
</html>
Code can also be seen here.
I have tried adding
svg.selectAll("label").remove(); or svg.selectAll('point.label').remove();
but it does not work and when i add svg.selectAll("text").remove(); it removes all the text in the plot which is obviously something i don't want.
Any help on fixing those issues would be appreciated. Thank you.

Related

d3js scroll visibility - series animation for pie chart

I am working on a d3 applicaton - with a pie chart -- I would like to get animation onload and on a call to action. Like when the chart becomes visible during a scroll.
Where the pie segments grow around the central pivot. So tween or snap to the other segment like a relay race
http://jsfiddle.net/pg886/192/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<div class="piechart" data-role="piechart" data-width=400 data-height=400 data-radius=30 data-innerradius=20
data-data=x>
</div>
<style>
.piechart{
/*border: 1px solid black;*/
/*text-align: center;
font-size: 12px;*/
}
</style>
<script>
$( document ).ready(function() {
console.log("test")
var $this = $('.piechart');
var data = [{
"label": "Apples",
"value": 100
},
{
"label": "Pears",
"value": 120
},
{
"label": "Bananas",
"value": 20
}];
var w = $this.data("width");
var h = $this.data("height");
var ir = $this.data("innerradius");
var r = $this.data("radius");
function colores_google(n) {
var colores_g = ["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f"];
//var colores_g = ["#47abd5", "#005a70", "#f5a0a3", "#ff7276", "#a9a19c", "#d0743c", "#ff8c00"];
return colores_g[n % colores_g.length];
}
var radius = Math.min(w, h) / 4;
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var labelArc = d3.svg.arc()
.outerRadius(radius - r)
.innerRadius(radius - ir);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.value; });
var chart = d3.select('.piechart').append("svg")
.attr("class", "chart")
.attr("width", w)
.attr("height", h)
.attr("transform", "translate(0,0)");
var piechart = chart
.append("g")
.attr("class", "piechart")
.attr("width", (radius*2))
.attr("transform", "translate(0,"+h/4+")");
var path_group = piechart.append("g")
.attr("class", "path_group")
.attr("transform", "translate(90," + ((h / 4) - 20) + ")");
var padding = 45;
var legendPaddingTop = 30;
var legend = chart.append("g")
.attr("class", "legend")
.attr("width", w/2)
.attr("height", h)
.attr("transform", "translate(" + (w - 50) + "," + (h / 4) + ")");
var label_group = legend.append("svg:g")
.attr("class", "label_group")
.attr("transform", "translate(" + (-(w / 3) + 20) + "," + 0 + ")");
var legend_group = legend.append("svg:g")
.attr("class", "legend_group")
.attr("transform", "translate(" + (-(w / 3) - 100) + "," + 0 + ")");
var g = path_group.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return colores_google(i);
});
var legendHeight = legendPaddingTop;
var ySpace = 18;
//draw labels
var labels = label_group.selectAll("text.labels")
.data(data);
labels.enter().append("svg:text")
.attr("class", "labels")
.attr("dy", function(d, i) {
legendHeight+=ySpace;
return (ySpace * i) + 4;
})
.attr("text-anchor", function(d) {
return "start";
})
.text(function(d) {
return d.label;
});
labels.exit().remove();
//draw labels
//draw legend
var legend = legend_group.selectAll("circle").data(data);
legend.enter().append("svg:circle")
.attr("cx", 100)
.attr("cy", function(d, i) {
return ySpace * i;
})
.attr("r", 7)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colores_google(i);
});
legend.exit().remove();
//draw legend
//reset legend height
//console.log("optimum height for legend", legendHeight);
$this.find('.legend').attr("height", legendHeight);
function type(d) {
d.value = +d.value;
return d;
}
});
</script>
So you can achieve this pretty easily, and there are a couple of blocks that will help you.
Arc Tween Firstly this block gives you an example of how to tween an arc. Basically you can't get that automatically so you have to write your own attrTween function. Fortunately this is pretty simple and Mike Bostock gives a really good example in there.
Here's a code sample - but the link gives a really good verbose description of what's going on.
.attrTween("d", function(d) {
var interpolate = d3.interpolate(d.endAngle, newAngle);
return function(t) {
d.endAngle = interpolate(t);
return arc(d);
};
}
Next you want something like this Donut with transitions. This is actually the end-result for where you're trying to get to. This effect is really easy to achieve, all you need to do is set your angles correctly and the timings.
Angles: So here you want both the endAngle and the startAngle to be the same at the start (which should be the endAngle value of the previous segment or 0 for the first segment).
Timing: You want to allow 1 animation to complete before you start the next, simply by delaying them. You can see how that's done with this snippet:
.transition()
.delay(function(d,i) { return i * 500; })
.duration(500)
.attrTween(...)
const dataset = [
{ age: "<5", population: 2704659 },
{ age: "5-13", population: 4499890 },
{ age: "14-17", population: 2159981 },
{ age: "18-24", population: 3853788 },
{ age: "25-44", population: 14106543 },
{ age: "45-64", population: 8819342 },
{ age: "≥65", population: 612463 },
];
const TIME = 2000 / dataset.length;
const color = d3.scaleOrdinal(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
const pie = d3.pie()
.sort(null)
.value(function(d) { return d.population; });
const path = d3.arc()
.innerRadius(0)
.outerRadius(350);
d3.select("#container")
.selectAll(".arc")
.data(pie(dataset))
.enter()
.append("g")
.attr("class", "arc")
.append("path")
.attr("fill", function(d) { return color(d.data.age); })
.transition()
.duration(TIME)
.ease(d3.easeLinear)
.delay(function(d, i) { return i * TIME; })
.attrTween("d", function(d) {
// Note the 0.1 to prevent errors generating the path
const angleInterpolation = d3.interpolate(d.startAngle + 0.1, d.endAngle);
return function(t) {
d.endAngle = angleInterpolation(t);
return path(d);
}
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="800" height="800">
<g id="container" transform="translate(400, 400)"></g>
</svg>

adding text values into a circle in html page using JS

I have an HTML page on which I am trying to display a sky plot. For the display, I am using the D3 library in JS.
Here is my code:
var deg2rad = Math.PI/180;
var width = 400, height = 350, radius = Math.min(width, height) / 2 - 30;
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {return r(d[1]);})
.angle(function(d) {return -d[0] + Math.PI / 2;});
var gr = null;
createSkyplot();
function createSkyplot(){
gr = svg.append("g")
.attr("class", "r axis")
.selectAll("g")
.data(r.ticks(5))
.enter().append("g");
gr.append("circle").attr("r", r).style('fill', 'white');
gr.append("text")
.attr("y", function(d) { return -r(d) - 4; })
.attr("transform", "rotate(20)")
.style("text-anchor", "middle")
.style('fill', 'blue')
.text(function(d) { return d;});
var ga = svg.append("g")
.attr("class", "a axis")
.selectAll("g")
.data(d3.range(0, 360, 45))
.enter().append("g")
.attr("transform", function(d) {return "rotate(" + (d - 90) + ")";});
ga.append("line").attr("x2", radius).style('stroke', 'black').style('stroke-dasharray', '1,8');
ga.append("text")
.attr("x", radius + 6)
.attr("dy", ".35em")
.style("text-anchor", function(d) { return d < 360 && d > 90 ? "end" : null; })
.attr("transform", function(d) { return d < 360 && d > 90 ? "rotate(180 " + (radius + 3) + ",0)" : null; })
.text(function(d) { return d + "°"; });
}
function updateSkyPlot(d){
var pos = [];
var inview = d.inView;
for (var elem in inview){
if (inview.hasOwnProperty(elem)) {
console.log(inview[elem].data[0]);
// the azimuth should be in radians and substracted from (Math.PI/2)
var d = [(Math.PI/2) - inview[elem].data[2]*deg2rad, inview[elem].data[1]];
pos.push(d);
}
}
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {return r(d[1]);})
.angle(function(d) {return -d[0] + Math.PI / 2;});
var color = d3.scale.category20();
svg.selectAll('circle').remove();
gr.append("circle").attr("r", r).style('fill', 'white');
svg.selectAll("point").data(pos).enter().append("circle").attr("class", "point")
.attr("transform", function(d) {
var coors = line([d]).slice(1).slice(0, -1);
return "translate(" + coors + ")"})
.attr("r", 8)
.attr("fill",function(d,i){return color(i);});
}
I use the updateSkyPlot(d) method to update the plot. The input for this method is a JSON object of the next type:
{"type" : "data", "inView" : {[prn, elv, azi], [prn, elv, azi] ..}}.
When I run this code in the HTML page I am able to produce the next plot:
My question is, what would be the best way to add the prn number inside the corresponding circles?
What should I add to the code to make it work?
Any help would be appreciated, thank you.
Here is Full HTML snippet:
<!DOCTYPE html>
<html>
<head>
<meta charset='ISO-8859-1'>
<script src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body style='background-color:lightgray'>
<div id="chart" style='width: 400px; height: 400px; padding-left: 5px; padding-bottom: 5px;'></div>
<script>
var deg2rad = Math.PI/180;
var width = 400, height = 350, radius = Math.min(width, height) / 2 - 30;
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {return r(d[1]);})
.angle(function(d) {return -d[0] + Math.PI / 2;});
var gr = null;
createSkyplot();
var json = {"type" : "GSV",
"inView" : [{"data" : [1, 45, 90]},
{"data" : [3, 70, 225]}]
};
updateSkyPlot(json);
function createSkyplot(){
//////////////////////
gr = svg.append("g")
.attr("class", "r axis")
.selectAll("g")
.data(r.ticks(5))
.enter().append("g");
gr.append("circle").attr("r", r).style('fill', 'white');
gr.append("text")
.attr("y", function(d) { return -r(d) - 4; })
.attr("transform", "rotate(20)")
.style("text-anchor", "middle")
.style('fill', 'blue')
.text(function(d) { return d;});
/////////////////////
/////////////////////
var ga = svg.append("g")
.attr("class", "a axis")
.selectAll("g")
.data(d3.range(0, 360, 45))
.enter().append("g")
.attr("transform", function(d) {return "rotate(" + (d - 90) + ")";});
ga.append("line").attr("x2", radius).style('stroke', 'black').style('stroke-dasharray', '1,8');
ga.append("text")
.attr("x", radius + 6)
.attr("dy", ".35em")
.style("text-anchor", function(d) { return d < 360 && d > 90 ? "end" : null; })
.attr("transform", function(d) { return d < 360 && d > 90 ? "rotate(180 " + (radius + 3) + ",0)" : null; })
.text(function(d) { return d + "°"; });
/////////////////////
}
function updateSkyPlot(d){
var pos = [];
var inview = d.inView;
for (var elem in inview){
if (inview.hasOwnProperty(elem)) {
// the azimuth should be in radians and substracted from (Math.PI/2)
var d = [(Math.PI/2) - inview[elem].data[2]*deg2rad, inview[elem].data[1]];
pos.push(d);
}
}
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {return r(d[1]);})
.angle(function(d) {return -d[0] + Math.PI / 2;});
var color = d3.scale.category20();
svg.selectAll('circle').remove();
gr.append("circle").attr("r", r).style('fill', 'white');
svg.selectAll("point").data(pos).enter().append("circle").attr("class", "point")
.attr("transform", function(d) {
var coors = line([d]).slice(1).slice(0, -1);
return "translate(" + coors + ")"})
.attr("r", 8)
.attr("fill",function(d,i){return color(i);});
}
</script>
</body>
</html>
Here is a possiblity:
<!DOCTYPE html>
<html>
<head>
<meta charset='ISO-8859-1'>
<script src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body style='background-color:lightgray'>
<div id="chart" style='width: 400px; height: 400px; padding-left: 5px; padding-bottom: 5px;'></div>
<script>
var deg2rad = Math.PI/180;
var width = 400, height = 350, radius = Math.min(width, height) / 2 - 30;
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {return r(d[1]);})
.angle(function(d) {return -d[0] + Math.PI / 2;});
var gr = null;
createSkyplot();
var json = {"type" : "GSV",
"inView" : [{"data" : [1, 45, 90]},
{"data" : [3, 70, 225]}]
};
updateSkyPlot(json);
function createSkyplot(){
//////////////////////
gr = svg.append("g")
.attr("class", "r axis")
.selectAll("g")
.data(r.ticks(5))
.enter().append("g");
gr.append("circle").attr("r", r).style('fill', 'white');
gr.append("text")
.attr("y", function(d) { return -r(d) - 4; })
.attr("transform", "rotate(20)")
.style("text-anchor", "middle")
.style('fill', 'blue')
.text(function(d) { return d;});
/////////////////////
/////////////////////
var ga = svg.append("g")
.attr("class", "a axis")
.selectAll("g")
.data(d3.range(0, 360, 45))
.enter().append("g")
.attr("transform", function(d) {return "rotate(" + (d - 90) + ")";});
ga.append("line").attr("x2", radius).style('stroke', 'black').style('stroke-dasharray', '1,8');
ga.append("text")
.attr("x", radius + 6)
.attr("dy", ".35em")
.style("text-anchor", function(d) { return d < 360 && d > 90 ? "end" : null; })
.attr("transform", function(d) { return d < 360 && d > 90 ? "rotate(180 " + (radius + 3) + ",0)" : null; })
.text(function(d) { return d + "°"; });
/////////////////////
}
function updateSkyPlot(d){
var pos = [];
var inview = d.inView;
for (var elem in inview){
if (inview.hasOwnProperty(elem)) {
// the azimuth should be in radians and substracted from (Math.PI/2)
var d = [(Math.PI/2) - inview[elem].data[2]*deg2rad, inview[elem].data[1]];
pos.push({ "angle": d, "label": inview[elem].data[0] });
}
}
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {return r(d[1]);})
.angle(function(d) {return -d[0] + Math.PI / 2;});
var color = d3.scale.category20();
svg.selectAll('circle').remove();
gr.append("circle").attr("r", r).style('fill', 'white');
var points = svg.selectAll("point")
.data(pos)
.enter()
.append("a")
.attr("transform", function(d) {
var coors = line([d.angle]).slice(1).slice(0, -1);
return "translate(" + coors + ")"
});
points.append("circle")
.attr("class", "point")
.attr("r", 8)
.attr("fill",function(d,i){return color(i);});
points.append("text")
.text( function(d) { return d.label })
.attr("transform", "translate(-4,5)")
}
</script>
</body>
</html>
I have kept the label in data (when you create the pos array with circle positions):
var pos = [];
for (var elem in inview) {
if (inview.hasOwnProperty(elem)) {
var d = [(Math.PI/2) - inview[elem].data[2]*deg2rad, inview[elem].data[1]];
// This is the modified line:
pos.push({ "position": d, "label": inview[elem].data[0] });
// pos.push(d);
}
}
Then instead of directly creating the circles, I create an intermediate container which will contain for each data point both the circle and the text label; and which is translated to its position:
var points = svg.selectAll("point")
.data(pos)
.enter()
.append("a") // The container
.attr("transform", function(d) {
var coors = line([d.position]).slice(1).slice(0, -1);
return "translate(" + coors + ")"
});
This way we can include for each data point both the circle:
points.append("circle")
.attr("class", "point")
.attr("r", 8)
.attr("fill",function(d,i){return color(i);});
and the label:
points.append("text")
.text( function(d) { return d.label })
.attr("transform", "translate(-4,5)")

some trouble in d3 v4 when I want to draw a chord

I have some problem in d3 v4 when I draw a chord. The question is 'Uncaught TypeError: g_outer.selectAll(...).data(...).enter is not a function' at the 53th in my code. But when I see the source code refering to https://bost.ocks.org/mike/uberdata/ and https://github.com/d3/d3/blob/master/CHANGES.md#chords-d3-chord. I have modified some errors, but it still can't work.
Here is my code:
<html>
<head>
<meta charset="UTF-8">
<title>Chord char</title>
</head>
 
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
    <script>
var city_name = [ "English" , "America" , "German" , "Japan" , "Austrilia" ];
var population = [
[ 1000, 3045, 4567, 1234, 3714 ],
[ 3214, 2000, 2060, 124 , 3234 ],
[ 8761, 6545, 3000, 8045, 647 ],
[ 3211, 1067, 3214, 4000, 1006 ],
[ 2146, 1034, 6745, 4764, 5000 ],
];
var chord_layout = d3.chord(population)
.padAngle(0.03)
.sortSubgroups(d3.descending);
var groups = chord_layout.sortGroups();
var chords = chord_layout.sortChords();
var width = 600;
var height = 600;
var innerRadius = width/2 * 0.7;
var outerRadius = innerRadius * 1.1;
var color20 = d3.scaleOrdinal(d3.schemeCategory20);
//add element
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");
//draw nodes
var outer_arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var g_outer = svg.append("g");
//add color
g_outer.selectAll("path")
.data(groups)
.enter().append("path")
.style("fill", function(d) { return color20(d.index); })
.style("stroke", function(d) { return color20(d.index); })
.attr("d", outer_arc );
//add text
g_outer.selectAll("text")
.data(groups)
.enter()
.append("text")
.each( function(d,i) {
d.angle = (d.startAngle + d.endAngle) / 2; //calculate the average of the start angle and the end angle
d.name = city_name[i]; //assignment for the city
})
.attr("dy", ".35em") //width
.attr("transform", function(d){ //angle
return "rotate(" + (d.angle * 180 / Math.PI ) + ")" +
"translate(0,"+ -1.0*(outerRadius+10) +")" +
( ( d.angle > Math.PI*3/4 && d.angle < Math.PI*5/4 ) ? "rotate(180)" : "");
}) //to spin when the angle between 135 to 225 degrees
.text(function(d){
return d.name;
})
//add chord
var inner_chord = d3.ribbon()
.radius(innerRadius);
svg.append("g")
.attr("class", "chord")
.selectAll("path") //use the path as element
.attr("d", inner_chord) //
.style("fill", function(d) { return color20(d.source.index); })
.style("opacity" ,1)
.on("mouseover", function(d,i){
d3.select(this)
.style("fill","yellow");
})
.on("mouseout", function(d,i){
d3.select(this)
.transition()
.duration(1000)
.style("fill",color20(d.source.index));
});
    </script>    
</body>
</html>
I want to draw a chord like this:
But in google chrome, the chord can not appear. I am new to d3, can you help me? I will appreciate it if you give me a good idea! Thankyou!
You have a slew of mistakes in your code. The biggest this that you aren't understanding how d3.chord function works. The general flow in d3 is to set-up your layout function before giving it any data. In your case like this:
var chord_layout = d3.chord()
.padAngle(0.03)
.sortSubgroups(d3.descending);
var groups = chord_layout(population);
After fixing that, I blended this example with your code to produce:
<html>
<head>
<meta charset="UTF-8">
<title>Chord char</title>
</head>
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script>
var city_name = ["English", "America", "German", "Japan", "Austrilia"];
var population = [
[1000, 3045, 4567, 1234, 3714],
[3214, 2000, 2060, 124, 3234],
[8761, 6545, 3000, 8045, 647],
[3211, 1067, 3214, 4000, 1006],
[2146, 1034, 6745, 4764, 5000],
];
var chord_layout = d3.chord()
.padAngle(0.03)
.sortSubgroups(d3.descending);
var width = 600;
var height = 600;
var innerRadius = width / 2 * 0.7;
var outerRadius = innerRadius * 1.1;
var color20 = d3.scaleOrdinal(d3.schemeCategory20);
//add element
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
//draw nodes
var outer_arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var groups = chord_layout(population);
var g_outer = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.datum(groups);
var group = g_outer.append("g")
.attr("class", "groups")
.selectAll("g")
.data(function(chords) { return chords.groups; })
.enter().append("g");
//add color
group.append("path")
.style("fill", function(d) {
return color20(d.index);
})
.style("stroke", function(d) {
return color20(d.index);
})
.attr("d", outer_arc);
//add text
group.append("text")
.attr("dy", ".35em") //width
.attr("transform", function(d,i) { //angle
d.angle = (d.startAngle + d.endAngle) / 2; //calculate the average of the start angle and the end angle
d.name = city_name[i]; //assignment for the city
return "rotate(" + (d.angle * 180 / Math.PI) + ")" +
"translate(0," + -1.0 * (outerRadius + 10) + ")" +
((d.angle > Math.PI * 3 / 4 && d.angle < Math.PI * 5 / 4) ? "rotate(180)" : "");
}) //to spin when the angle between 135 to 225 degrees
.text(function(d) {
return d.name;
});
//add chord
var inner_chord = d3.ribbon()
.radius(innerRadius);
g_outer.append("g")
.attr("class", "ribbons")
.selectAll("path")
.data(function(chords) { return chords; })
.enter().append("path")
.attr("d", inner_chord)
.style("fill", function(d) {
return color20(d.source.index);
})
.style("stroke", "black")
.style("opacity", 0.6)
.on("mouseover", function(d, i) {
d3.select(this)
.style("fill", "yellow");
})
.on("mouseout", function(d, i) {
d3.select(this)
.transition()
.duration(1000)
.style("fill", color20(d.source.index));
});
</script>
</body>
</html>

Alternating or preventing overlapping paths in D3

I am creating an arc diagram where I'd like to, hopefully, find a way to prevent the overlap of arcs. There's an example of the working bl.ock here.
The darker lines in this case are overlapping lines where multiple nodes share the same edge. I'd like to prevent that, perhaps by doing two passes: the first would alternate the arc to go above the nodes rather than below, giving a sort of helix appearance; the second would draw a slightly larger arc if an arc already exists above/below to help differentiate the links.
var width = 1000,
height = 500,
margin = 20,
pad = margin / 2,
radius = 6,
yfixed = pad + radius;
var color = d3.scale.category10();
// Main
//-----------------------------------------------------
function arcDiagram(graph) {
var radius = d3.scale.sqrt()
.domain([0, 20])
.range([0, 15]);
var svg = d3.select("#chart").append("svg")
.attr("id", "arc")
.attr("width", width)
.attr("height", height);
// create plot within svg
var plot = svg.append("g")
.attr("id", "plot")
.attr("transform", "translate(" + pad + ", " + pad + ")");
// fix graph links to map to objects
graph.links.forEach(function(d,i) {
d.source = isNaN(d.source) ? d.source : graph.nodes[d.source];
d.target = isNaN(d.target) ? d.target : graph.nodes[d.target];
});
linearLayout(graph.nodes);
drawLinks(graph.links);
drawNodes(graph.nodes);
}
// layout nodes linearly
function linearLayout(nodes) {
nodes.sort(function(a,b) {
return a.uniq - b.uniq;
})
var xscale = d3.scale.linear()
.domain([0, nodes.length - 1])
.range([radius, width - margin - radius]);
nodes.forEach(function(d, i) {
d.x = xscale(i);
d.y = yfixed;
});
}
function drawNodes(nodes) {
var gnodes = d3.select("#plot").selectAll("g.node")
.data(nodes)
.enter().append('g');
var nodes = gnodes.append("circle")
.attr("class", "node")
.attr("id", function(d, i) { return d.name; })
.attr("cx", function(d, i) { return d.x; })
.attr("cy", function(d, i) { return d.y; })
.attr("r", 5)
.style("stroke", function(d, i) { return color(d.gender); });
nodes.append("text")
.attr("dx", function(d) { return 20; })
.attr("cy", ".35em")
.text(function(d) { return d.name; })
}
function drawLinks(links) {
var radians = d3.scale.linear()
.range([Math.PI / 2, 3 * Math.PI / 2]);
var arc = d3.svg.line.radial()
.interpolate("basis")
.tension(0)
.angle(function(d) { return radians(d); });
d3.select("#plot").selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("transform", function(d,i) {
var xshift = d.source.x + (d.target.x - d.source.x) / 2;
var yshift = yfixed;
return "translate(" + xshift + ", " + yshift + ")";
})
.attr("d", function(d,i) {
var xdist = Math.abs(d.source.x - d.target.x);
arc.radius(xdist / 2);
var points = d3.range(0, Math.ceil(xdist / 3));
radians.domain([0, points.length - 1]);
return arc(points);
});
}
Any pointers on how I might start approaching the problem?
Here is a bl.ock for reference. It shows your original paths in gray, and the proposed paths in red.
First store the counts for how many times a given path occurs:
graph.links.forEach(function(d,i) {
var pathCount = 0;
for (var j = 0; j < i; j++) {
var otherPath = graph.links[j];
if (otherPath.source === d.source && otherPath.target === d.target) {
pathCount++;
}
}
d.pathCount = pathCount;
});
Then once you have that data, I would use an ellipse instead of a radial line since it appears the radial line can only draw a curve for a circle:
d3.select("#plot").selectAll(".ellipse-link")
.data(links)
.enter().append("ellipse")
.attr("fill", "transparent")
.attr("stroke", "gray")
.attr("stroke-width", 1)
.attr("cx", function(d) {
return (d.target.x - d.source.x) / 2 + radius;
})
.attr("cy", pad)
.attr("rx", function(d) {
return Math.abs(d.target.x - d.source.x) / 2;
})
.attr("ry", function(d) {
return 150 + d.pathCount * 20;
})
.attr("transform", function(d,i) {
var xshift = d.source.x - radius;
var yshift = yfixed;
return "translate(" + xshift + ", " + yshift + ")";
});
Note that changing the value for ry above will change the heights of different curves.
Finally you'll have to use a clippath to restrict the area of each ellipse that's actually shown, so that they only display below the nodes. (This is not done in the bl.ock)

Display Pie chart, Grouped and Stacked bar chart in one page

I want to display two charts in one page.The Pie chart gets displayed but the Grouped and Stacked bar chart is not displaying . I tried to change the Id name in but no luck :( . Will appreciate if anyone helps me with the code correction .
/* Display Matrix Chart ,Pie chart, Grouped and Stacked bar chart in one page */
<apex:page showHeader="false">
<apex:includeScript value="{!URLFOR($Resource.jquery1)}"/>
<apex:includeScript value="{!URLFOR($Resource.D3)}"/>
<apex:includeScript value="{!URLFOR($Resource.nvD3)}"/>
<div id="body" height="50%" width="100px" ></div> // Id for Pie chart
<div id="body1" height="30%" width="90px"></div> // Id for Stacked and Grouped bar chart
<script>
// Matrix Chart starts here
var drawChart = function(divId,matrixReportId) {
$.ajax('/services/data/v29.0/analytics/reports/'+matrixReportId,
{
beforeSend: function(xhr) {
xhr.setRequestHeader('Authorization', 'Bearer {!$Api.Session_ID}');
},
success: function(response) {
console.log(response);
var chart = nv.models.multiBarChart();
var chartData = [];
document.getElementById(divId).innerHTML = '';
$.each(response.groupingsDown.groupings, function(di, de) {
var values = [];
chartData.push({"key":de.label, "values": values});
$.each(response.groupingsAcross.groupings, function(ai, ae) {
values.push({"x": ae.label, "y": response.factMap[de.key+"!"+ae.key].aggregates[0].value});
});
});
d3.select('#'+divId).datum(chartData).transition().duration(100).call(chart);
window.setTimeout(function(){
drawChart(divId,matrixReportId);
}, 5000);
}
}
);
};
$(document).ready(function(){
drawChart('chart','00O90000005SSHv');
});
// Pie Chart Starts here
var width =250 ,
height = 450,
radius = Math.min(width, height) / 2;
var data = [{"age":"<5","population":2704659},{"age":"14-17","population":2159981},
{"age":"14-17","population":2159981},{"age":"18-24","population":3853788},
{"age":"25-44","population":14106543},{"age":"45-64","population":8819342},
{"age":">65","population":612463}];
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc().outerRadius(radius - 10).innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
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))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.age); });
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.age; });
// Grouped and Stacked Bar Chart starts here
var n = 2, // number of layers
m = 10, // number of samples per layer
stack = d3.layout.stack(),
layers = stack(d3.range(n).map(function() { return bumpLayer(m, .1); })),
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
var margin = {top: 40, right: 10, bottom: 20, left: 10},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([0, width], .08);
var y = d3.scale.linear()
.domain([0, yStackMax])
.range([height, 0]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(0)
.tickPadding(6)
.orient("bottom");
var svg = d3.select("#body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
var rect = layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", height)
.attr("width", x.rangeBand())
.attr("height", 0);
rect.transition()
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
d3.selectAll("input").on("change", change);
var timeout = setTimeout(function() {
d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(timeout);
if (this.value === "grouped") transitionGrouped();
else transitionStacked();
}
function transitionGrouped() {
y.domain([0, yGroupMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("x", function(d, i, j) { return x(d.x) + x.rangeBand() / n * j; })
.attr("width", x.rangeBand() / n)
.transition()
.attr("y", function(d) { return y(d.y); })
.attr("height", function(d) { return height - y(d.y); });
}
function transitionStacked() {
y.domain([0, yStackMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
.transition()
.attr("x", function(d) { return x(d.x); })
.attr("width", x.rangeBand());
}
// Inspired by Lee Byron's test data generator.
function bumpLayer(n, o) {
function bump(a) {
var x = 1/(.1 + Math.random()),
y = 2*Math.random()-.5,
z = 10/(.1 + Math.random());
for (var i = 0; i < n; i++) {
var w = (i/n- y) * z;
a[i] += x * Math.exp(-w * w);
}
}
var a=[],i;
for (i = 0; i < n; ++i) a[i] = o + o * Math.random();
for (i = 0; i < 5; ++i) bump(a);
return a.map(function(d, i) { return {x: i, y: Math.max(0, d)}; });
}
</script>
<svg id="chart" height="50%" width="500px" ></svg> // Id for Matrix chart
</apex:page>
Thanks in advance
first, as we discussed here (d3 Donut chart does not render), you should position your
<div id="body1" height="30%" width="90px"></div>
above the script.
second, you are missing the # in your second svg-declaration to correctly select the div by its id, it has to be
var svg = d3.select("#body1").append("svg")
you could also think about naming the second svg differently (eg svg2) so you don't override your first svg-variable (in case you want to do something with it later).

Categories

Resources