Permalink
Browse files

added visuals for circle mode of follower network

  • Loading branch information...
1 parent 3cbdd08 commit 90244d7d2cb6a48ed2045dabc5c6f56f6d029e54 @JuliAne committed Feb 6, 2012
Showing with 388 additions and 69 deletions.
  1. +7 −2 lib/githubviz.rb
  2. +62 −63 lib/public/d3.layout.js
  3. +29 −0 lib/public/flare-imports.json
  4. +242 −2 lib/views/circle.erb
  5. +6 −2 lib/views/follower.erb
  6. +42 −0 lib/views/index.erb
View
9 lib/githubviz.rb
@@ -66,7 +66,7 @@ def process_data
if @user
- @api = GitHubV3API.new('')
+ @api = GitHubV3API.new('c1616feca6aa3e63655dd92766a475c2227ed6a0')
@data[@user] = @api.get("/users/#{@user}")
@data[@user]['level'] = 0
@@ -80,7 +80,7 @@ def process_data
get '/repo_viz' do
@user = params[:user]
- @api = GitHubV3API.new('')
+ @api = GitHubV3API.new('c1616feca6aa3e63655dd92766a475c2227ed6a0')
user_data = @api.users.get(@user)
my_repos = @api.repos.list
page = 1
@@ -175,7 +175,12 @@ def process_data
end
get '/circle_viz' do
+ @api = GitHubV3API.new('c1616feca6aa3e63655dd92766a475c2227ed6a0')
@user = params[:user]
+ user_data = @api.users.get(@user)
+ #@data[@user] = @api.get("/users/#{@user}")
+ #@data[@user]['follower_count'] = @data[@user]['followers']
+ #@data[@user]['followers'] = @api.get("/users/#{@user}/followers")
erb :circle
end
View
125 lib/public/d3.layout.js
@@ -69,48 +69,44 @@ d3.layout.chord = function() {
function relayout() {
var subgroups = {},
- groupSums = [],
groupIndex = d3.range(n),
subgroupIndex = [],
+ sum_cell = [],
k,
x,
x0,
i,
j;
-
+ a = 0;
+ b = 0;
+ c = 0;
chords = [];
groups = [];
// Compute the sum.
k = 0, i = -1; while (++i < n) {
x = 0, j = -1; while (++j < n) {
- x += matrix[i][j];
+ x += matrix[i][j];// Summe der Zellinhalte
}
- groupSums.push(x);
subgroupIndex.push(d3.range(n));
- k += x;
- }
-
- // Sort groups…
- if (sortGroups) {
- groupIndex.sort(function(a, b) {
- return sortGroups(groupSums[a], groupSums[b]);
- });
+ sum_cell.push({index: i, value: x});//Zum Zählen der "Null-Nodes"
+ k += x; //Addition und Zuweisung der Summe ausgehender Verlinkungen
}
-
- // Sort subgroups…
- if (sortSubgroups) {
- subgroupIndex.forEach(function(d, i) {
- d.sort(function(a, b) {
- return sortSubgroups(matrix[i][a], matrix[i][b]);
- });
- });
+
+ a = 0; while (++a < sum_cell.length){
+ if (sum_cell[a].value == 0){
+ b += 1;
+ }else{
+ c += 1;
+ }
}
-
+ //k += b;
+ //alert("Länge: " + sum_cell.length + ", b:" + b + ", k: " + k);
// Convert the sum to scaling factor for [0, 2pi].
// TODO Allow start and end angle to be specified.
// TODO Allow padding to be specified as percentage?
- k = (2 * Math.PI - padding * n) / k;
+ k = (2 * Math.PI - padding * n - 0.025 * b) / k;
+
// Compute the start and end angle for each group and subgroup.
// Note: Opera has a bug reordering object literal properties!
@@ -119,48 +115,71 @@ d3.layout.chord = function() {
var di = groupIndex[i],
dj = subgroupIndex[di][j],
v = matrix[di][dj],
- a0 = x,
- a1 = x += v * k;
+
+ a0 = x += (v * k)/3,
+ a1 = x += (v * k)/3;//Neuzuweisung x (Startposition)
+ //if (v==0){a0 = a1 = x += 0.025/2}
subgroups[di + "-" + dj] = {
- index: di,
+ index: di, //jeder Index 23Mal vorhanden --> min = 0, max = 22
subindex: dj,
startAngle: a0,
endAngle: a1,
- value: v
+ value: v //binäre Werte für ausgehende Verbindungen aus der quadratischen Beziehungs-Matrix --> je Index 23 Werte --> gesamt 23²
};
+ x += (v * k)/3 //Padding für Graphen des Nodes
+ //alert("subgroups: " + subgroups[di + "-" + dj].index + ", start: " + subgroups[di + "-" + dj].startAngle + ", end: " + subgroups[di + "-" + dj].endAngle + ", value: " + subgroups[di + "-" + dj].value);
+ }
+ if (x0 == x){
+ x += 0.025 //zeige auch die Nodes, die keine ausgehenden Verbindungen haben
}
- groups.push({
- index: di,
+ groups.push({ // Groups bildet RückgabeArray an View
+ index: di, //Node
startAngle: x0,
endAngle: x,
- value: (x - x0) / k
+ value: (x - x0) / k //Anzahl ausgehender Verbindungen je Node
});
x += padding;
+ //alert("groups: " + groups[di].index + ", value: " + groups[di].value + ", start: " + groups[di].startAngle + ", end: " + groups[di].endAngle);
+
}
// Generate chords for each (non-empty) subgroup-subgroup link.
i = -1; while (++i < n) {
j = i - 1; while (++j < n) {
var source = subgroups[i + "-" + j],
target = subgroups[j + "-" + i];
- if (source.value || target.value) {
- chords.push(source.value < target.value
- ? {source: target, target: source}
- : {source: source, target: target});
+ if (source.value || target.value){
+ if (source.value < target.value) {
+ var group = groups[source.index];
+ if (group.startAngle == source.startAngle){
+ source.startAngle += (group.endAngle - group.startAngle)/2;
+ source.endAngle += (group.endAngle - group.startAngle)/2;}
+ else if (group.endAngle == source.endAngle){
+ source.startAngle -= (group.endAngle - group.startAngle)/2;
+ source.endAngle -= (group.endAngle - group.startAngle)/2;}
+ chords.push({source: target, target: source});}
+ //groups.push({source: target, target: source});}
+
+ else {
+ var group = groups[target.index];
+ if (group.startAngle == target.startAngle){
+ target.startAngle += (group.endAngle - group.startAngle)/2;
+ target.endAngle += (group.endAngle - group.startAngle)/2;}
+ else if (group.endAngle == target.endAngle){
+ target.startAngle -= (group.endAngle - group.startAngle)/2;
+ target.endAngle -= (group.endAngle - group.startAngle)/2;
+ }
+ chords.push({source: source, target: target});}
+ //groups.push({source: target, target: source});}
+
+
+ //alert("Index: " + chords[i].target.index + "TargetSTART: " + chords[i].target.startAngle + "TargetEND: " + chords[i].target.endAngle + "Value: " + chords[i].target.value);
+
}
}
}
-
- if (sortChords) resort();
- }
-
- function resort() {
- chords.sort(function(a, b) {
- return sortChords(
- (a.source.value + a.target.value) / 2,
- (b.source.value + b.target.value) / 2);
- });
}
+
chord.matrix = function(x) {
if (!arguments.length) return matrix;
@@ -176,26 +195,6 @@ d3.layout.chord = function() {
return chord;
};
- chord.sortGroups = function(x) {
- if (!arguments.length) return sortGroups;
- sortGroups = x;
- chords = groups = null;
- return chord;
- };
-
- chord.sortSubgroups = function(x) {
- if (!arguments.length) return sortSubgroups;
- sortSubgroups = x;
- chords = null;
- return chord;
- };
-
- chord.sortChords = function(x) {
- if (!arguments.length) return sortChords;
- sortChords = x;
- if (chords) resort();
- return chord;
- };
chord.chords = function() {
if (!chords) relayout();
View
29 lib/public/flare-imports.json
@@ -0,0 +1,29 @@
+[
+{"name":"flare.analytics.cluster","size":3938,"imports":["flare.animate"]},
+{"name":"flare.analytics.graph","size":3534,"imports":["flare.animate","flare.vis.data","flare.util","flare.vis.operator"]},
+{"name":"flare.animate","size":17010,"imports":[]},
+{"name":"flare.data.converters","size":721,"imports":[]},
+{"name":"flare.data.DataField","size":1759,"imports":["flare.data.DataUtil"]},
+{"name":"flare.data.DataSchema","size":2165,"imports":["flare.data.DataField","flare.util"]},
+{"name":"flare.data.DataSet","size":586,"imports":["flare.data.DataTable"]},
+{"name":"flare.data.DataSource","size":3331,"imports":["flare.data.converters","flare.data.DataSchema"]},
+{"name":"flare.data.DataTable","size":772,"imports":["flare.data.DataSchema"]},
+{"name":"flare.data.DataUtil","size":3322,"imports":["flare.data.DataField","flare.data.DataSchema"]},
+{"name":"flare.display","size":8833,"imports":[]},
+{"name":"flare.flex","size":4116,"imports":["flare.display","flare.data.DataSet"]},
+{"name":"flare.physics","size":1082,"imports":[]},
+{"name":"flare.query","size":1616,"imports":[]},
+{"name":"flare.scale","size":2105,"imports":[]},
+{"name":"flare.util","size":8258,"imports":[]},
+{"name":"flare.vis.axis","size":1302,"imports":["flare.animate","flare.vis.Visualization"]},
+{"name":"flare.vis.controls","size":2138,"imports":["flare.vis.Visualization","flare.vis.operator"]},
+{"name":"flare.vis.data","size":20544,"imports":["flare.util","flare.vis.events","flare.data.DataSet","flare.data.DataSchema","flare.data.DataField"]},
+{"name":"flare.vis.events","size":2313,"imports":["flare.vis.data"]},
+{"name":"flare.vis.legend","size":4614,"imports":["flare.util","flare.display"]},
+{"name":"flare.vis.operator","size":4461,"imports":[]},
+{"name":"flare.vis.pix","size":4461,"imports":[]},
+{"name":"flare.vis.pic","size":4461,"imports":[]},
+{"name":"flare.vis.piz","size":4461,"imports":[]},
+{"name":"flare.vis.pit","size":4461,"imports":[]},
+{"name":"flare.vis.Visualization","size":16540,"imports":["flare.animate","flare.vis.operator","flare.vis.events","flare.vis.data","flare.vis.axis","flare.util","flare.vis.controls"]}
+]
View
244 lib/views/circle.erb
@@ -1,5 +1,245 @@
-<h1>Circle Mode</h1>
-<div class="gallery"><%= @user %></div>
+<h1><%= @user %>'s CIRCLE Mode</h1>
+<div class="gallery" id ="chord"></div>
+<script type="text/javascript" src="d3.js?2.7.2"></script>
+<script src='d3.layout.js?2.7.2' type='text/javascript'></script>
+<script src='d3.geom.js?2.7.2' type='text/javascript'> </script>
+<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
+<script type="text/javascript">
+$(function (){
+var fader = false;
+var index = [];
+var index2 = [];
+var a = 0;
+var b = 0;
+var r1 = 700 / 2,
+ r0 = r1 - 140;
+
+var fill = d3.scale.category20();
+
+var chord = d3.layout.chord()
+ .padding(.02)
+
+var arc = d3.svg.arc()
+ .innerRadius(r0)
+ .outerRadius(r0 + 20);
+
+var clickarea = d3.select("#chord").append("div")
+ .attr("class", "clickarea")
+ .style("opacity", 0);
+
+var svg = d3.select("#chord").append("svg")
+ .attr("width", r1 * 2)
+ .attr("height", r1 * 2)
+ //.attr("transform", "translate(" + r1 + "," + r1 + ")")
+ //.attr("height", 10)
+ .append("g")
+ .attr("transform", "translate(" + r1 + "," + r1 + ")");
+
+d3.select("div.clickarea").on("click", fade2(1));
+
+
+d3.json("flare-imports.json", function(imports) {
+ var indexByName = {},
+ nameByIndex = {},
+ matrix = [],
+ n = 0;
+
+ // Returns the Flare package name for the given class name.
+ function name(name) {
+ return name;
+ }
+
+ // Compute a unique index for each package name.
+ imports.forEach(function(d) {
+ d = name(d.name);
+ if (!(d in indexByName)) {
+ nameByIndex[n] = d;
+ indexByName[d] = n++;
+ }
+ });
+
+ // Construct a square matrix counting package imports.
+ imports.forEach(function(d) {
+ var source = indexByName[name(d.name)],
+ row = matrix[source];
+ if (!row) {
+ row = matrix[source] = [];
+ for (var i = -1; ++i < n;) row[i] = 0;
+ }
+ d.imports.forEach(function(d) { row[indexByName[name(d)]]++; });
+ });
+
+ chord.matrix(matrix);
+
+ var node = svg.selectAll("g.group")
+ .data(chord.groups)
+ .enter().append("g")
+
+ node.append("path")
+ .style("fill", "#666666")
+ .style("stroke","#666666")
+ .attr("d", arc)
+ .on("mouseover", fade_mover(0))
+ .on("mouseout", fade_mout(1));
+
+ node.append("text")
+ .each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
+ .attr("dy", ".35em")
+ .attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
+ .attr("transform", function(d) {
+ return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ + "translate(" + (r0 + 26) + ")"
+ + (d.angle > Math.PI ? "rotate(180)" : "");
+ })
+ .text(function(d) { return nameByIndex[d.index]; });
+
+ svg.selectAll("path.chord")
+ .data(chord.chords)
+ .enter().append("path")
+ .attr("class", "chord")
+ .style("stroke", "#999999")
+ .style("fill", "#999999" )
+ .attr("d", d3.svg.chord().radius(r0))
+ .style("opacity", 0);
+
+});
+//show or hide chords of selected node by mouseover (depends on switching opacity on/ off in function fade2)
+function fade_mover(opacity) {
+ return function(g, i) {
+ a = 0;
+ b = 0;
+ index = [];
+ index2 = [];
+ svg.selectAll("path.chord")
+ .filter(function(d) {
+ if (d.source.index == i){
+ index[a] = [d.target.index];
+ a += 1;}
+ if (d.target.index == i){
+ index2[b] = [d.source.index];
+ b += 1;}
+ if (fader == false){
+ opacity = 1;
+ return d.source.index == i || d.target.index == i;
+ }
+ else if (fader == true) {
+ opacity = 0;
+ return d.source.index != i && d.target.index != i;}
+ })
+ .transition()
+ .style("opacity", opacity)
+ .duration(400)
+
+ index.forEach(function(ind){
+ svg.selectAll("path")
+ .filter(function(d) {
+ return d.index == ind;
+ index = [];
+ a = 0;
+ })
+ .transition()
+ .style("stroke", "#0d50f6")
+ .style("fill", "#0d50f6");
+ });
+ index2.forEach(function(ind2){
+ svg.selectAll("path")
+ .filter(function(d) {
+ return d.index == ind2;
+ index2 = [];
+ b = 0;
+ })
+ .transition()
+ .style("stroke", "#0cb0f6")
+ .style("fill", "#0cb0f6");
+ });
+ svg.selectAll("path")
+ .filter(function(d) {
+ return d.index == i;
+ })
+ .transition()
+ .style("stroke", "#ff0442")
+ .style("fill", "#ff0442");
+ };
+};
+//show or hide chords of selected node by mouseout (depends on switching opacity on/ off in function fade2)
+function fade_mout(opacity) {
+ return function(g, i) {
+ a = 0;
+ b = 0;
+ index = [];
+ index2 = [];
+ svg.selectAll("path.chord")
+ .filter(function(d) {
+ if (d.source.index == i){
+ index[a] = [d.target.index];
+ a += 1;}
+ if (d.target.index == i){
+ index2[b] = [d.source.index];
+ b += 1;}
+ if (fader == false){
+ opacity = 0;
+ return d.source.index == i || d.target.index == i;}
+ else if (fader == true) {
+ opacity = 1;
+ return d.source.index != i && d.target.index != i;}
+ })
+ .transition()
+ .style("opacity", opacity)
+ .duration(400)
+
+ index.forEach(function(ind){
+ svg.selectAll("path")
+ .filter(function(d) {
+ return d.index == ind;
+ index = [];
+ a = 0;
+ })
+ .transition()
+ .style("stroke", "#666666")
+ .style("fill", "#666666");
+ });
+
+ index2.forEach(function(ind2){
+ svg.selectAll("path")
+ .filter(function(d) {
+ return d.index == ind2;
+ index2 = [];
+ b = 0;
+ })
+ .transition()
+ .style("stroke", "#666666")
+ .style("fill", "#666666");
+ });
+ svg.selectAll("path")
+ .filter(function(d) {
+ return d.index == i;
+ })
+ .transition()
+ .style("stroke", "#666666")
+ .style("fill", "#666666");
+ };
+};
+//show or hide all chords by clicking the center of the circle
+function fade2(opacity) {
+ return function(g, i) {
+ if (fader == true){
+ opacity = 0;
+ fader = false;}
+ else{
+ opacity = 1;
+ fader= true;}
+ svg.selectAll("path.chord")
+ .filter(function(d) {
+ return d.source.index != i || d.target.index != i;
+ })
+ .transition()
+ .style("opacity", opacity)
+ .duration(400);
+ };
+};
+});
+</script>
+
View
8 lib/views/follower.erb
@@ -23,6 +23,9 @@
$('#chart').html(data);
}
});
+ $('#desc_text').empty();
+ $('#desc_text').append($("<p>network of followers</p>"));
+ $('#description').show();
});
$('#network_mode').click(function(ev){
ev.preventDefault();
@@ -40,6 +43,7 @@
});
</script>
<div id="viz">
+ <!--<%= @result%>-->
<div class="description">
<div class="level_buttons">
<form class="level_form" action="/follower_viz" method="post">
@@ -57,7 +61,7 @@
<a href="#" id ="network_mode" class="mode">Network Mode</a>
</div>
</div>
- <div style="float:right;padding-right:20%">
+ <div id ="desc_text" style="float:right;padding-right:18%">
<h3><%= @user%>'s network of followers</h3>
<p>This visualization shows the user's network of followers in 1 level or 2 levels.<br/>
In the 1st level all gitusers which are directly following <%=@user%> are displayed. <br/>
@@ -75,7 +79,7 @@
$(function (){
var w = $(window).width(),
- h = $(window).height() - $("#header").height() -20,
+ h = $(window).height() - $("#header").height() -80,
fill = d3.scale.category20();
var vis = d3.select("#chart").append("svg")
View
42 lib/views/index.erb
@@ -112,13 +112,22 @@
font-size: 10pt;
}
+ .gallery{
+ text-align: center;
+ }
+
+ #desc_text{
+ width:450px;
+ }
+
#header{
padding:10px;
}
.description {
text-align:center;
clear:both;
+ height: 85px;
}
.formlabel {
@@ -152,6 +161,39 @@
font-size: 10pt;
padding: 5px 5px 0 0;
}
+
+ #chord {
+ font-size: 8pt;
+ font-family: sans-serif;
+ height: 700px;
+ width: 700px;
+ padding-left: 0px;
+ }
+
+ path.chord {
+ fill-opacity: .85;
+ stroke-opacity: 1.0;
+ }
+
+ path{
+ fill-opacity: 1.0;
+ stroke-opacity: 1.0;
+ }
+
+ .clickarea{
+ width: 280px;
+ height: 280px;
+ margin: 210px 0 0 16%;
+ z-index: 1;
+ position: absolute;
+ cursor: pointer;
+ }
+
+ svg {
+ clear: both;
+ position: absolute;
+ z-index: 0;
+ }
</style>
</head>

0 comments on commit 90244d7

Please sign in to comment.