Built with blockbuilder.org
xxxxxxxxxx
<meta charset="utf-8">
<head>
<style>
svg {
float: left;
}
#title {
width: 700px;
text-align: center;
font-family: 'Corben', Georgia, Times, serif;
}
#commentDiv {
float: left;
margin-left: 20px;
/*height: 100%;
overflow: auto;*/
}
.links line {
/*stroke: #999;*/
/*stroke-opacity: 0.6;*/
}
.nodes circle {
/*stroke: #fff;*/
stroke-width: 5px;
}
.green {
color: #32CD32;
}
.node text {
pointer-events: none;
font: 16px sans-serif;
text-anchor: middle;
}
/*This borrowed is stolen from: https://www.w3schools.com/css/tryit.asp?filename=trycss_table_fancy*/
#table {
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 400px;
table-layout: fixed;
}
#intro {
width: 362px;
border: 1px solid #ddd;
padding: 18px;
margin-left: 20px;
float: left;
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
}
#table td, #table th {
border: 1px solid #ddd;
padding: 18px;
}
#table tr:nth-child(even){background-color: #f2f2f2;}
#table tr:hover {background-color: #ddd;}
#table th {
padding-top: 12px;
padding-bottom: 12px;
text-align: center;
background-color: #4CAF50;
color: white;
}
div.tooltip {
position: absolute;
text-align: center;
width: 100px;
height: 40px;
padding: 2px;
font: 12px sans-serif;
background-color: #D0F0F0;
border: 1px;
border-radius: 8px;
pointer-events: none;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<h2 id="title">Common Words in UFO Sighting Descriptions</h2>
<svg width="700" height="600"></svg>
<div id="commentDiv"></div>
<div id="intro">
<p>
Each circle represents a common word in descriptions of alien cities. The size of the circle corresponds to the frequency of the word. Each link between 2 words represents the frequency with which those two words were in the same comment. <h3>Click a word to see some of the descriptions it belongs to!</h3>
</p>
</div>
<script>
var comments = [];
var chosenComments = [];
var chosenWord = "No Word Selected"
var chosenWordCount = 0
var linkSet = {}
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var minRadius = 15;
var maxRadius = 100;
var minLinkWidth = 1
var maxLinkWidth = 30
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }).distance(200))
.force("charge", d3.forceManyBody().strength(-250))
.force("center", d3.forceCenter(width / 2, height / 2));
function createlinkSet(links) {
result = new Set();
links.forEach(link => {
source = link['source'];
target = link['target'];
sourceTarget = source + ' ' + target;
targetSource = target + ' ' + source;
result.add(sourceTarget);
result.add(targetSource);
})
return result;
}
d3.json("force_250.json", function(error, graph) {
if (error) throw error;
linkSet = createlinkSet(graph.links);
var minGroup = Math.min.apply(Math, graph.nodes.map(function(d){return d["group"];}));
var maxGroup = Math.max.apply(Math, graph.nodes.map(function(d){return d["group"];}));
var minValue = Math.min.apply(Math, graph.links.map(function(d){return d["value"];}));
var maxValue = Math.max.apply(Math, graph.links.map(function(d){return d["value"];}));
var rScale = d3.scaleSqrt()
.domain([minGroup, maxGroup])
.range([minRadius, maxRadius]);
var linkScale = d3.scaleLinear()
.domain([minValue, maxValue])
.range([minLinkWidth, maxLinkWidth]);
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke", "#999")
.attr("stroke-width", function(d) { return linkScale(d.value); })
.attr("stroke-opacity", 0.6)
;
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", function(d) {
circleClicked(d)
})
;;
node.append("circle")
.attr("r", function(d) {return rScale(d.group)})
.attr("fill", function(d) { return color(d.group); })
;
node.append("title")
.text(function(d) { return d.id; });
node.append("text")
.attr("clip-path", function(d) { return "url(#clip-" + d.id + ")"; })
.attr("dx", 0)
.attr("dy", ".35em")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
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 = Math.max(
rScale(d.group) + minRadius/2,
Math.min(width - rScale(d.group), d.x) - minRadius/2
);
})
.attr("cy", function(d) {
return d.y = Math.max(
rScale(d.group) + minRadius/2,
Math.min(height - rScale(d.group), d.y) - minRadius/2
);
})
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
function linkExists(word1, word2){
word12 = word1 + ' ' + word2;
return linkSet.has(word12);
}
function getColorForWord(chosenCircleID, d) {
if (d.id == chosenCircleID) {
return "black";
} else if(linkExists(chosenCircleID, d.id)) {
return "black";
} else {
return color(d.group);
}
}
function getStrokeOpacity(word, d) {
if (word == d.source.id || word == d.target.id) {
return 1.0;
} else {
return 0.6;
}
}
function getStroke(word, d) {
if (word == d.source.id || word == d.target.id) {
return "black";
} else {
return "#999";
}
}
function updateCircles(chosenCircleID) {
d3.selectAll("circle")
.attr("stroke", function(d) { return getColorForWord(chosenCircleID, d); })
.attr("stroke-width", function(d) { return d.id == chosenCircleID ? "5px" : "2.5px"; })
;
d3.selectAll("line")
.attr("stroke", function(d) { return getStroke(chosenCircleID, d); })
.attr("stroke-opacity", d => getStrokeOpacity(chosenCircleID, d))
;
}
function circleClicked(d) {
updateCircles(d.id);
if (comments) {
chosenWord = d["id"];
chosenWordCount = Math.round(d["group"] * 100000);
chosenComments = comments.filter(function(comment) {
if (comment) {
return comment.includes(" " + chosenWord);
} else {
return false;
}
});
updateChosenWord(chosenWord, chosenWordCount);
updateComments(chosenComments);
}
}
d3.json("comments_common_500.json", function(error, data) {
comments = data
console.log("Loaded comments: " + comments.length)
})
var commentTable = d3.select('#commentDiv')
.append('table')
.attr('id', 'table')
;
var commentTableHead = commentTable.append('thead')
.append('tr')
.append('th')
.attr('id', 'commentHeadText')
.text(chosenWord)
;
var commentTableBody = commentTable.append('tbody');
function updateChosenWord(chosenWord, numChosenComments) {
d3.select("#commentHeadText")
.text(chosenWord + " (Count: " + numChosenComments + ")")
;
}
function createCommentHTML(comment, word) {
var regex = new RegExp(" " + word, 'ig');
var replacement = "<span class=green> " + word + "</span>";
return comment.replace(regex, replacement);
}
function updateComments(chosenComments) {
var rows = commentTableBody.selectAll('tr')
.data(shuffle(chosenComments))
;
rows.exit()
.remove();
rows
.enter()
.append('tr')
.merge(rows)
.html(d => "<td>" + createCommentHTML(d, chosenWord) + "</td>");
}
updateComments(chosenComments);
updateChosenWord(chosenWord, chosenComments.length);
// Borrowed from https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
</script>
</body>
https://d3js.org/d3.v4.min.js