Skip to content

Commit

Permalink
1.0.0 working version.
Browse files Browse the repository at this point in the history
  • Loading branch information
IMBurbank committed Jul 18, 2017
1 parent 16a8e4f commit fa8132f
Show file tree
Hide file tree
Showing 10 changed files with 1,003 additions and 67 deletions.
47 changes: 39 additions & 8 deletions app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,60 @@
<html>

<head>
<title>TrumpWorldGraph</title>
<title>Trump World Connections Graph</title>

<!-- Font not currently used -->
<link href="https://fonts.googleapis.com/css?family=VT323" rel="stylesheet">
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Diplomata+SC" rel="stylesheet">

<!--build:css styles/css/main.min.css-->
<link rel="stylesheet" href="styles/css/main.css">
<!--endbuild-->
</head>

<body>
<header><h1>Trump World Connections</h1></header>

<div id="root">
<div class="intro-content">
<h3>
From the Buzzfeed article
<a href="https://www.buzzfeed.com/johntemplon/help-us-map-trumpworld" target="_blank">
Help Us Map TrumpWorld
</a>
</h3>

<p class="quote">
No American president has taken office with a giant network of businesses, investments,
and corporate connections like that amassed by Donald J. Trump. His family and advisers
have touched a staggering number of ventures, from a hotel in Azerbaijan to a poker company
in Las Vegas.
</p>

<p class="page-summary">
The force-directed graph below represents the relationship between all known entities
with three or more links to other entities within Trump's world. I made use
of <a href="https://data.world/sya/trumpworld">this great dataset</a> and
d3.js to create this graph. Click the nodes for more information.
</p>
</div>

<div id="graph" class="graph-container">
<!-- This div content will be managed by D3. -->
</div>

<footer>
<span class='author'>By Isaac Burbank 2017</span>
<span class='repo-link'>
<a href="https://github.com/IMBurbank/trump-world-graph">Click to Github Repo</a>
</span>
</footer>

<!-- ***Switch to D3 min for production*** -->
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script> -->
<!-- <script src="https://d3js.org/d3-selection-multi.v1.min.js"></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>

<!-- ***Switch for Dev*** -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.js"></script> -->
<!-- <script src="https://d3js.org/d3-selection-multi.v1.min.js"></script> -->

<!--build:js scripts/js/main.min.js-->
<script type="text/javascript" src="scripts/js/main.js" ></script>
Expand Down
240 changes: 226 additions & 14 deletions app/scripts/es6/main.es6
Original file line number Diff line number Diff line change
@@ -1,13 +1,133 @@
const dataPath = 'assets/datasets/trumpworld.csv';
/**
* Set Global Graph/Similation Variables
*/

const width = 900,
height = width / 1.6,
fillColors = { Person: 'rgba(44, 143, 204, 0.95)', Organization: 'rgba(244, 89, 58, 0.95)' },
dataPath = 'assets/datasets/trumpworld.csv';

const simulation = d3.forceSimulation()
.force('link',
d3.forceLink()
.id( d => d.id )
.distance([50])
)
.force('charge',
d3.forceManyBody()
.strength( d => [-4 * d.links**(1/2) - 20] )
.distanceMin([0.001])
.distanceMax([width / 2])
)
.force('center',
d3.forceCenter(width / 2, height / 2)
);


/**
* Create/Append HTML Graph Components
*/

const svg = d3.select('#graph')
.append('svg')
.attrs({ height, width, class: 'chart-palette' });

const detailDiv = d3.select("#graph")
.append("div")
.attrs({ id: "details", class: "details" })
.style("opacity", 0);

const tooltipDiv = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);


/**
* Define Graph & Simulation Functions
*/

const updateDetailDiv = function updateDetailDiv(d) {
detailDiv
.transition()
.duration(200)
.styles({opacity: 0.9, 'z-index': 5 });

detailDiv
.styles({
'min-width': width / 3 + 'px',
'height': height / 3 + 'px'
})
.html(
`<strong>${d.id}</strong><br/>
<br/>
Type: ${d.group}<br/>
Links: ${d.links}<br/>
<div class="table-container">
<table class="link-table">
<caption><h3>Trump World Connections</h3></caption>
<col/><col/><col/><col/>
<thead>
<tr>
<th class="link-table-index">#</th>
<th class="link-table-name">Name</th>
<th class="link-table-type">Type</th>
<th class="link-table-details">Details</th>
<th class="link-table-scroll"></th>
</tr>
</thead>
<tbody>
${d.connections.reduce( (a, b, i) => {
return `${a}<tr>
<td class="link-table-index">${i + 1}</td>
<td class="link-table-name">${b.name}</td>
<td class="link-table-type">${b.type}</td>
<td class="link-table-details">
<a href=${b.source} target="_blank">${b.connection}</a>
</td>
</tr>`;
}, "")}
</tbody>
</table>
</div>`
);
}


const dragStarted = function dragSimulationStarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();

d.fx = d.x;
d.fy = d.y
}


const dragged = function draggedSimulation(d) {
d.fx = Math.max(d.radius, Math.min(width - d.radius, d3.event.x));
d.fy = Math.max(d.radius, Math.min(height - d.radius, d3.event.y));
}


const dragEnded = function dragSimulationEnded(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;

updateDetailDiv(d);
}


const treatData = function treatGraphData(data) {
const entityLetters = ['A', 'B'];
const entityLetters = ['A', 'B'],
linksCutoff = 3;

let nodeData = {},
nodes = [],
links = [],
entity = '';
rowObj = {},
nodes = [],
links = [],
endNodes = [],
finalEntities = new Set(),
entity = '';

data.forEach( row => {
entityLetters.forEach( (key, i) => {
Expand All @@ -16,10 +136,10 @@ const treatData = function treatGraphData(data) {
if (nodeData[entity]) {
nodeData[entity].links++;
nodeData[entity].connections.push({
Name: row[`Entity ${entityLetters[1 - i]}`],
Type: row[`Entity ${entityLetters[1 - i]} Type`],
Connection: row['Connection'],
'Source(s)': row['Source(s)']
name: row[`Entity ${entityLetters[1 - i]}`],
type: row[`Entity ${entityLetters[1 - i]} Type`],
connection: row['Connection'],
source: row['Source(s)']
});
}
else {
Expand All @@ -28,10 +148,10 @@ const treatData = function treatGraphData(data) {
links: 1,
group: row[`Entity ${key} Type`],
connections: [{
Name: row[`Entity ${entityLetters[1 - i]}`],
Type: row[`Entity ${entityLetters[1 - i]} Type`],
Connection: row['Connection'],
'Source(s)': row['Source(s)']
name: row[`Entity ${entityLetters[1 - i]}`],
type: row[`Entity ${entityLetters[1 - i]} Type`],
connection: row['Connection'],
source: row['Source(s)']
}],
};
}
Expand All @@ -40,17 +160,109 @@ const treatData = function treatGraphData(data) {
links.push({source: row['Entity A'], target: row['Entity B']});
});

for (let key in nodeData) nodes.push(nodeData[key]);
for (let key in nodeData) {
rowObj = nodeData[key];

if (rowObj.links <= linksCutoff) endNodes.push(key);
else rowObj['radius'] = rowObj.links**(9/16) + 3, nodes.push(rowObj);
}

links = links.filter( el => !endNodes.includes(el.source) && !endNodes.includes(el.target) );

links.forEach( el => {finalEntities.add(el.source), finalEntities.add(el.target)} );

nodes = nodes
.filter( el => finalEntities.has(el.id))
.sort( (a, b) => b.links - a.links );

nodes.forEach( el => el.connections.sort( (a, b) => a.name <= b.name ? -1 : 1) );

return {nodes, links};
}


const forceDirectedGraph = function createForceDirectedGraph(nodes, links) {
const link = svg.append('g')
.attr('class', 'link')
.selectAll('line')
.data(links)
.enter()
.append('line')

const node = svg.append('g')
.attr('class', 'node')
.selectAll('circle')
.data(nodes)
.enter()
.append('circle')
.attrs({
r: d => d.radius,
fill: d => fillColors[d.group]
})
.on('mouseover', d => {
tooltipDiv.transition()
.duration(200)
.style('opacity', 0.9);
tooltipDiv
.html(
`${d.id}<br/>
Type: ${d.group}<br/>
Links: ${d.links}`
)
.styles({ left: (d3.event.pageX) + 'px', top: (d3.event.pageY + 12) + "px"});
})
.on('mouseout', d => {
tooltipDiv.transition()
.duration(500)
.style("opacity", 0);
})
.call(d3.drag()
.on('start', dragStarted)
.on('drag', dragged)
.on('end', dragEnded)
)

const ticked = function linkTicked() {
link.attrs({
'x1': d => d.source.x,
'y1': d => d.source.y,
'x2': d => d.target.x,
'y2': d => d.target.y
});

node.attrs({
cx: d => d.x = Math.max(d.radius, Math.min(width - d.radius, d.x)),
cy: d => d.y = Math.max(d.radius, Math.min(height - d.radius, d.y))
});
}


d3.select('body')
.on('click', e => {
if (d3.event.target.localName !== 'circle' &&
d3.event.path.every( el => el.id !== 'details')) {

detailDiv
.transition()
.duration(333)
.styles({ opacity: 0, 'z-index': -1 });
}
});

simulation
.nodes(nodes)
.on('tick', ticked);

simulation
.force('link')
.links(links);
}


/**
* Run Force Directed Graph Simulation
*/

d3.csv(dataPath, (error, data) => {
if (error) throw error;

Expand Down
Loading

0 comments on commit fa8132f

Please sign in to comment.