Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save rfprod/c78d21f223e0453527a1 to your computer and use it in GitHub Desktop.
Save rfprod/c78d21f223e0453527a1 to your computer and use it in GitHub Desktop.
Data Visualization with Force Directed Chart

Data Visualization with Force Directed Chart

User can see a Force-directed Graph that shows which campers are posting links on Camper News to which domains. User can see each camper's icon on their node. User can see the relationship between the campers and the domains they're posting. User can tell approximately many times campers have linked to a specific domain from it's node size. User can tell approximately how many times a specific camper has posted a link from their node's size.

A Pen by V on CodePen.

License.

<div class="container-fluid nopadding">
<nav class="navbar navbar-inverse navbar-fixed-top topnav" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#toggle-nav" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand font-effect-neon" target=_blank href="http://codepen.io/rfprod"><span class="glyphicon glyphicon-wrench"></span> RFProd</a>
</div>
<div class="collapse navbar-collapse" id="toggle-nav">
<div class="container-fluid">
<ul class="nav navbar-nav navbar-right font-effect-emboss">
<li class="nav-tabs"><a href="#approot"><span class="glyphicon glyphicon-stats"></span> Data Visualization with Force Directed Chart</a></li>
<li class="nav-tabs"><a href="https://gist.github.com/rfprod/c78d21f223e0453527a1" target=_blank><span class="glyphicon glyphicon-download-alt" ></span> GIST</a></li>
</ul>
</div>
</div>
</nav>
<a name="approot"></a>
<div class="home sect">
<div class="container-fluid">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<h2 class="hidden-md hidden-lg hidden-xl"><span class="glyphicon glyphicon-stats"></span> Data Visualization with Force Directed Chart</h2>
<div id="output">
Output
</div>
<span class="credits">info <a href="https://d3js.org/" target=_blank>D3.js</a> | <a href="https://www.freecodecamp.com/news/hot" target=_blank>data</a> <br/>licence <a href="http://www.gnu.org/licenses/gpl-3.0.en.html" target=_blank>GPL 3.0</a></span>
</div>
</div>
</div>
</div>
$(document).on('ready',function(){
(function (){
var UserInstructionsAndGDPChart = React.createClass({
render: function(){
return (
<span>
<p id="user-instructions">Camper News Network visualization using D3.js.</p>
<div id="map"></div>
</span>
);
}
});
ReactDOM.render(<UserInstructionsAndGDPChart />,document.getElementById('output'));
var chartContainerObj = d3.select('#map');
/*
* DEPRECATED endpoint
*/
// $.getJSON('https://www.freecodecamp.com/news/hot', function(json){
const mockDeprecatedAPI = (dummyUrl, callback) => callback([
{
id: '1',
link: 'https://www.domain1.dummy/link1',
author: {
username: 'username 1',
picture: 'https://image.flaticon.com/icons/svg/145/145867.svg',
userId: '1'
}
},
{
id: '2',
link: 'https://www.domain2.dummy/link2',
author: {
username: 'username 2',
picture: 'https://image.flaticon.com/icons/svg/145/145852.svg',
userId: '2'
}
},
{
id: '3',
link: 'https://www.domain3.dummy/link3',
author: {
username: 'username 3',
picture: 'https://image.flaticon.com/icons/svg/145/145859.svg',
userId: '3'
}
},
{
id: '4',
link: 'https://www.domain4.dummy/link2',
author: {
username: 'username 4',
picture: 'https://image.flaticon.com/icons/svg/145/145862.svg',
userId: '4'
}
},
{
id: '5',
link: 'https://www.domain3.dummy/link3',
author: {
username: 'username 2',
picture: 'https://image.flaticon.com/icons/svg/145/145852.svg',
userId: '2'
}
}
]);
mockDeprecatedAPI('https://www.freecodecamp.com/news/hot', (json) => {
//console.log(json[0].id);
//console.log(json[0].link);
//console.log(json[0].author.picture);
//console.log(json[0].author.userId);
var domains = [], uniqueDomCounter = 0, domainArrCheckDupl = [];
var users = [], uniqueUsrsCounter = 0, usersArrCheckDupl = [];
var links = [];
for (var i=0;i<json.length;i++){
var domain = '', domainValue = 1;
var domArr = json[i].link.split('/');
if (json[i].link.indexOf('://') != -1) domain = domArr[2];
else domain = domArr[0];
if (domain.indexOf('www.') != -1) domain = domain.substr(4,domain.length);
var domId = domainArrCheckDupl.indexOf(domain);
var tempDomIndex = 0;
if (domId != -1) {
domains[domId].value++;
tempDomIndex = domId;
}
else{
domainArrCheckDupl.push(domain);
domains.push({'index':uniqueDomCounter,'domain':domain,'value':domainValue});
tempDomIndex = uniqueDomCounter;
uniqueDomCounter++;
}
var usrId = usersArrCheckDupl.indexOf(json[i].author.username);
if (usrId != -1) {
users[usrId].linksCount++;
links.push({'source':users[usrId].index,'target':tempDomIndex});
}else{
usersArrCheckDupl.push(json[i].author.username); users.push({'index':uniqueUsrsCounter,'username':json[i].author.username,'pic':json[i].author.picture,'linksCount':1});
links.push({'source':uniqueUsrsCounter,'target':tempDomIndex});
uniqueUsrsCounter++;
}
}
//console.log(JSON.stringify(domains));
//console.log(JSON.stringify(users));
//console.log(JSON.stringify(links));
var nodes = [];
for (var i=0;i<domains.length;i++) {
nodes.push(domains[i]);
}
for (var i=0;i<users.length;i++) {
for (var l=0;l<links.length;l++){
if (links[l].source == users[i].index) links[l].source = domains.length+i;
}
users[i].index = domains.length+i;
nodes.push(users[i]);
}
console.log(JSON.stringify(nodes));
console.log(JSON.stringify(links));
var k = 0, windowWidth = $(window).width();
if (windowWidth < 1000) k = windowWidth/500;
else k = windowWidth/500;
var width = $('#map').width()-75*k, height = 400;
var restHeight = $(window).height() - $('nav').height()*2 - $('.home').find('h2').height() - $('#user-instructions').height() - $('.credits').height()*2;
//console.log('restHeight: '+restHeight);
height = restHeight;
chartContainerObj.append('svg').attr('class','chart');
var chartObj = d3.select('.chart').attr("width", width).attr("height", height);
chartObj.append('defs').selectAll('pattern').data(users).enter()
.append('pattern')
.attr('id', function(val,i){return 'img-'+val.index})
.attr('x',0)
.attr('y',0)
.attr('height',function(val){
if (typeof val.value == 'undefined') return 30+val.linksCount*2;
})
.attr('width',function(val){
if (typeof val.value == 'undefined') return 30+val.linksCount*2;
})
.append('image')
.attr('x',0)
.attr('y',0)
.attr('height',function(val){
if (typeof val.value == 'undefined') return 30+val.linksCount*2;
})
.attr('width',function(val){
if (typeof val.value == 'undefined') return 30+val.linksCount*2;
})
.attr('xlink:href', function(val){return val.pic});
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-130)
.friction(0.95)
.linkDistance(90)
.theta(0.9)
.size([width,height]);
var div = d3.select("body").append("div").attr("class", "tooltip").style("opacity", 0);
var tooltip = d3.select('.tooltip');
var xCursorPosition = 0, yCursorPosition = 0;
force.nodes(nodes).links(links).start();
var link = chartObj.selectAll('.link').data(links)
.enter().append('line')
.attr('class','link')
.style('stroke-width', 5);
var node = chartObj.selectAll('.node').data(nodes)
.enter().append('circle')
.attr('class','node')
.attr('r', function(val,i){
if (typeof val.value != 'undefined') return val.value*3;
else return 15+val.linksCount*1.001;
})
.style('stroke-width', function(val){
if (typeof val.value == 'undefined') return 1.5;
else return 2+val.linksCount*1.01;
})
.style('fill', function(val){
if (typeof val.value != 'undefined') return '#ff0000';
else return 'url(#img-'+val.index+')';
})
.call(force.drag);
node.append("title")
.text(function(val) {
if (typeof val.domain != 'undefined') return val.domain;
else return val.username;
});
force.on("tick", function() {
link.attr("x1", function(d) {return d.source.x;})
.attr("y1", function(d) {return d.source.y;})
.attr("x2", function(d) {return d.target.x;})
.attr("y2", function(d) {return d.target.y;});
node.attr("cx", function(d) {return d.x;})
.attr("cy", function(d) {return d.y;});
});
});
})();
});
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
$black: #000000
$white: #ffffff
$grey: #bfbfbf
$blue: #3366cc
$green: #33cc33
$darkblue1: #000066
$darkblue2: #0000ff
$lightyellow: #ffff99
$yellow: #ffff00
$orange: #ff6600
$red: #ff3300
body
color: $black
font-family: 'Play', sans-serif
font-size: 2.2em
overflow-x:hidden
.tooltip
position: absolute
text-align: center
width: auto !important
height: auto !important
padding: 2px
font: 12px sans-serif
background: lightsteelblue
border: 0px
border-radius: 8px
pointer-events: none
.nopadding
padding: 0
.navbar-brand
font-size: 1em
.home
min-height: 92vh
height: auto !important
.sect
padding-top: 8vh
.githublogo
height: 1em
h2
text-align: center
font-weight: bold
#output
text-align: center
width: 100%
height: auto !important
#user-instructions
text-align: center
font-size: 0.75em
margin-top: 1em
#map
display: block
background-color: $grey
padding-top: 1em
padding-left: 1em
.chart
width: 97%
.node
stroke: #fff
stroke-width: 1.5px
.link
stroke: #999
stroke-opacity: 0.6
.hidden
display: none
a
text-decoration: none
.credits
display: block
text-align: center
font-size: 0.75em
a:hover
text-decoration: none
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" />
<link href="//cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.3/animate.min.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Play&effect=neon" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment