Chord Diagram with transitions to see sentiments of each year
forked from willzjc's block: Chord diagram transition - G10 Currency Pairs
license: mit |
Chord Diagram with transitions to see sentiments of each year
forked from willzjc's block: Chord diagram transition - G10 Currency Pairs
/*** Define parameters and tools ***/ | |
var csvfilter = ""; | |
var ccyfilter = ""; | |
var width = 760, | |
height = 820, | |
outerRadius = Math.min(width, height) / 2 - 120,//100, | |
innerRadius = outerRadius - 10; | |
var dataset = "y2017.json"; | |
//string url for the initial data set | |
//would usually be a file path url, here it is the id | |
//selector for the <pre> element storing the data | |
//create number formatting functions | |
var formatPercent = d3.format("%"); | |
var numberWithCommas = d3.format("0,f"); | |
//create the arc path data generator for the groups | |
var arc = d3.svg.arc() | |
.innerRadius(innerRadius) | |
.outerRadius(outerRadius); | |
//create the chord path data generator for the chords | |
var path = d3.svg.chord() | |
.radius(innerRadius - 4);// subtracted 4 to separate the ribbon | |
//define the default chord layout parameters | |
//within a function that returns a new layout object; | |
//that way, you can create multiple chord layouts | |
//that are the same except for the data. | |
function getDefaultLayout() { | |
return d3.layout.chord() | |
.padding(0.03) | |
.sortSubgroups(d3.descending) | |
.sortChords(d3.ascending); | |
} | |
var last_layout; //store layout between updates | |
var regions; //store neighbourhood data outside data-reading function | |
/*** Initialize the visualization ***/ | |
var g = d3.select("#chart_placeholder").append("svg") | |
.attr("width", width) | |
.attr("height", height) | |
.append("g") | |
.attr("id", "circle") | |
.attr("transform", | |
"translate(" + width / 2 + "," + height / 2 + ")"); | |
//the entire graphic will be drawn within this <g> element, | |
//so all coordinates will be relative to the center of the circle | |
g.append("circle") | |
.attr("r", outerRadius); | |
//this circle is set in CSS to be transparent but to respond to mouse events | |
//It will ensure that the <g> responds to all mouse events within | |
//the area, even after chords are faded out. | |
/*** Read in the neighbourhoods data and update with initial data matrix ***/ | |
//normally this would be done with file-reading functions | |
//d3. and d3.json and callbacks, | |
//instead we're using the string-parsing functions | |
//d3.csv.parse and JSON.parse, both of which return the data, | |
//no callbacks required. | |
d3.csv("regionsfish.csv", function(error, regionData) { | |
if (error) {alert("Error reading file: ", error.statusText); return; } | |
regions = regionData; | |
//store in variable accessible by other functions | |
//regions = d3.csv.parse(d3.select("#regions").text()); | |
//instead of d3.csv | |
updateChords(dataset); | |
//call the update method with the default dataset | |
}); //end of d3.csv function | |
/* Create OR update a chord layout from a data matrix */ | |
function updateChords( datasetURL ) { | |
d3.json(datasetURL, function(error, matrix) { | |
if (error) {alert("Error reading file: ", error.statusText); return; } | |
//var matrix = JSON.parse( d3.select(datasetURL).text() ); | |
// instead of d3.json | |
/* Compute chord layout. */ | |
layout = getDefaultLayout(); //create a new layout object | |
layout.matrix(matrix); | |
/* Create/update "group" elements */ | |
var groupG = g.selectAll("g.group") | |
.data(layout.groups(), function (d) { | |
return d.index; | |
//use a key function in case the | |
//groups are sorted differently | |
}); | |
groupG.exit() | |
.transition() | |
.duration(1500) | |
.attr("opacity", 0) | |
.remove(); //remove after transitions are complete | |
var newGroups = groupG.enter().append("g") | |
.attr("class", "group"); | |
//the enter selection is stored in a variable so we can | |
//enter the <path>, <text>, and <title> elements as well | |
//Create the title tooltip for the new groups | |
newGroups.append("title"); | |
//Update the (tooltip) title text based on the data | |
groupG.select("title") | |
.text(function(d, i) { | |
return numberWithCommas(d.value) | |
+ " Total Google Interest for " | |
+ regions[i].name; | |
}); | |
//create the arc paths and set the constant attributes | |
//(those based on the group index, not on the value) | |
newGroups.append("path") | |
.attr("id", function (d) { | |
return "group" + d.index; | |
//using d.index and not i to maintain consistency | |
//even if groups are sorted | |
}) | |
.style("fill", function (d) { | |
return regions[d.index].color; | |
}); | |
//update the paths to match the layout | |
groupG.select("path") | |
.transition() | |
.duration(1500) | |
//.attr("opacity", 0.5) //optional, just to observe the transition//////////// | |
.attrTween("d", arcTween( last_layout )) | |
// .transition().duration(100).attr("opacity", 1) //reset opacity////////////// | |
; | |
//create the group labels | |
newGroups.append("svg:text") | |
.attr("xlink:href", function (d) { | |
return "#group" + d.index; | |
}) | |
.attr("dy", ".35em") | |
.attr("color", "#fff") | |
.text(function (d) { | |
return regions[d.index].name + ' (' + (100* ((d.endAngle - d.startAngle) / 2)/Math.PI).toFixed(1) + '%)'; | |
}); | |
//position group labels to match layout | |
groupG.select("text") | |
.transition() | |
.duration(1500) | |
.attr("transform", function(d) { | |
d.angle = (d.startAngle + d.endAngle) / 2; | |
//store the midpoint angle in the data object | |
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" + | |
" translate(" + (innerRadius + 26) + ")" + | |
(d.angle > Math.PI ? " rotate(180)" : " rotate(0)"); | |
//include the rotate zero so that transforms can be interpolated | |
}) | |
.text(function (d) { | |
return regions[d.index].name + ' (' + (100* ((d.endAngle - d.startAngle) / 2)/Math.PI).toFixed(1) + '%)'; | |
}) | |
.attr("text-anchor", function (d) { | |
return d.angle > Math.PI ? "end" : "begin"; | |
}); | |
/* Create/update the chord paths */ | |
var chordPaths = g.selectAll("path.chord") | |
.data(layout.chords(), chordKey ); | |
//specify a key function to match chords | |
//between updates | |
//create the new chord paths | |
var newChords = chordPaths.enter() | |
.append("path") | |
.attr("class", "chord"); | |
// Add title tooltip for each new chord. | |
newChords.append("title"); | |
// Update all chord title texts | |
chordPaths.select("title") | |
.text(function(d) { | |
if (regions[d.target.index].name !== regions[d.source.index].name) { | |
return [numberWithCommas(d.source.value), | |
" Google Rating for ", | |
regions[d.source.index].name, | |
regions[d.target.index].name, | |
"\n", | |
numberWithCommas(d.target.value), | |
" Google Rating for ", | |
regions[d.target.index].name, | |
regions[d.source.index].name | |
].join(""); | |
//joining an array of many strings is faster than | |
//repeated calls to the '+' operator, | |
//and makes for neater code! | |
} | |
else { //source and target are the same | |
return numberWithCommas(d.source.value) | |
+ " exports ended in " | |
+ regions[d.source.index].name; | |
} | |
}); | |
//handle exiting paths: | |
chordPaths.exit().transition() | |
.duration(1500) | |
.attr("opacity", 0) | |
.remove(); | |
//update the path shape | |
chordPaths.transition() | |
.duration(1500) | |
//.attr("opacity", 0.5) //optional, just to observe the transition | |
.style("fill", function (d) { | |
return regions[d.source.index].color; | |
}) | |
.attrTween("d", chordTween(last_layout)) | |
//.transition().duration(100).attr("opacity", 1) //reset opacity | |
; | |
//add the mouseover/fade out behaviour to the groups | |
//this is reset on every update, so it will use the latest | |
//chordPaths selection | |
groupG.on("mouseover", function(d) { | |
chordPaths.classed("fade", function (p) { | |
//returns true if *neither* the source or target of the chord | |
//matches the group that has been moused-over | |
return ((p.source.index != d.index) && (p.target.index != d.index)); | |
}); | |
}); | |
//the "unfade" is handled with CSS :hover class on g#circle | |
//you could also do it using a mouseout event: | |
/* | |
g.on("mouseout", function() { | |
if (this == g.node() ) | |
//only respond to mouseout of the entire circle | |
//not mouseout events for sub-components | |
chordPaths.classed("fade", false); | |
}); | |
*/ | |
last_layout = layout; //save for next update | |
}); //end of d3.json | |
} | |
function arcTween(oldLayout) { | |
//this function will be called once per update cycle | |
//Create a key:value version of the old layout's groups array | |
//so we can easily find the matching group | |
//even if the group index values don't match the array index | |
//(because of sorting) | |
var oldGroups = {}; | |
if (oldLayout) { | |
oldLayout.groups().forEach( function(groupData) { | |
oldGroups[ groupData.index ] = groupData; | |
}); | |
} | |
return function (d, i) { | |
var tween; | |
var old = oldGroups[d.index]; | |
if (old) { //there's a matching old group | |
tween = d3.interpolate(old, d); | |
} | |
else { | |
//create a zero-width arc object | |
var emptyArc = {startAngle:d.startAngle, | |
endAngle:d.startAngle}; | |
tween = d3.interpolate(emptyArc, d); | |
} | |
return function (t) { | |
return arc( tween(t) ); | |
}; | |
}; | |
} | |
function chordKey(data) { | |
return (data.source.index < data.target.index) ? | |
data.source.index + "-" + data.target.index: | |
data.target.index + "-" + data.source.index; | |
//create a key that will represent the relationship | |
//between these two groups *regardless* | |
//of which group is called 'source' and which 'target' | |
} | |
function chordTween(oldLayout) { | |
//this function will be called once per update cycle | |
//Create a key:value version of the old layout's chords array | |
//so we can easily find the matching chord | |
//(which may not have a matching index) | |
var oldChords = {}; | |
if (oldLayout) { | |
oldLayout.chords().forEach( function(chordData) { | |
oldChords[ chordKey(chordData) ] = chordData; | |
}); | |
} | |
return function (d, i) { | |
//this function will be called for each active chord | |
var tween; | |
var old = oldChords[ chordKey(d) ]; | |
if (old) { | |
//old is not undefined, i.e. | |
//there is a matching old chord value | |
//check whether source and target have been switched: | |
if (d.source.index != old.source.index ){ | |
//swap source and target to match the new data | |
old = { | |
source: old.target, | |
target: old.source | |
}; | |
} | |
tween = d3.interpolate(old, d); | |
} | |
else { | |
//create a zero-width chord object | |
///////////////////////////////////////////////////////////in the copy //////////////// | |
if (oldLayout) { | |
var oldGroups = oldLayout.groups().filter(function(group) { | |
return ( (group.index == d.source.index) || | |
(group.index == d.target.index) ) | |
}); | |
old = {source:oldGroups[0], | |
target:oldGroups[1] || oldGroups[0] }; | |
//the OR in target is in case source and target are equal | |
//in the data, in which case only one group will pass the | |
//filter function | |
if (d.source.index != old.source.index ){ | |
//swap source and target to match the new data | |
old = { | |
source: old.target, | |
target: old.source | |
}; | |
} | |
} | |
else old = d; | |
///////////////////////////////////////////////////////////////// | |
var emptyChord = { | |
source: { startAngle: old.source.startAngle, | |
endAngle: old.source.startAngle}, | |
target: { startAngle: old.target.startAngle, | |
endAngle: old.target.startAngle} | |
}; | |
tween = d3.interpolate( emptyChord, d ); | |
} | |
return function (t) { | |
//this function calculates the intermediary shapes | |
return path(tween(t)); | |
}; | |
}; | |
} | |
/* Activate the buttons and link to data sets */ | |
d3.select("#y2011").on("click", function() { | |
updateChords( "y2011.json" ); | |
csvfilter="2011"; | |
//disableButton(this); | |
}); | |
d3.select("#y2012").on("click", function() { | |
updateChords( "y2012.json" ); | |
csvfilter="2012"; | |
//disableButton(this); | |
}); | |
d3.select("#y2013").on("click", function() { | |
updateChords( "y2013.json" ); | |
csvfilter="2013"; | |
task_delete_tables(); | |
//disableButton(this); | |
}); | |
d3.select("#y2014").on("click", function() { | |
updateChords( "y2014.json" ); | |
csvfilter="2014"; | |
task_delete_tables(); | |
//disableButton(this); | |
}); | |
d3.select("#y2015").on("click", function() { | |
updateChords( "y2015.json" ); | |
csvfilter="2015"; | |
//disableButton(this); | |
task_delete_tables(); | |
}); | |
d3.select("#y2016").on("click", function() { | |
updateChords( "y2016.json" ); | |
csvfilter="2016"; | |
//disableButton(this); | |
task_delete_tables(); | |
}); | |
d3.select("#y2017").on("click", function() { | |
csvfilter="2017"; | |
updateChords( "y2017.json" ); | |
//disableButton(this); | |
task_delete_tables(); | |
}); | |
function injectHTML(inject_content){ | |
//step 1: get the DOM object of the iframe. | |
var iframe = document.getElementById('target_iframe'); | |
alert(iframe); | |
var html_string = '<html><head></head><body><p>' + inject_content + '</p></body></html>'; | |
/* if jQuery is available, you may use the get(0) function to obtain the DOM object like this: | |
var iframe = $('iframe#target_iframe_id').get(0); | |
*/ | |
//step 2: obtain the document associated with the iframe tag | |
//most of the browser supports .document. Some supports (such as the NetScape series) .contentDocumet, while some (e.g. IE5/6) supports .contentWindow.document | |
//we try to read whatever that exists. | |
var iframedoc = iframe.document; | |
if (iframe.contentDocument) | |
iframedoc = iframe.contentDocument; | |
else if (iframe.contentWindow) | |
iframedoc = iframe.contentWindow.document; | |
if (iframedoc){ | |
// Put the content in the iframe | |
iframedoc.open(); | |
iframedoc.writeln(html_string); | |
iframedoc.close(); | |
} else { | |
//just in case of browsers that don't support the above 3 properties. | |
//fortunately we don't come across such case so far. | |
alert('Cannot inject dynamic contents into iframe.'); | |
} | |
} | |
/* | |
function disableButton(buttonNode) { | |
d3.selectAll("button") | |
.attr("disabled", function(d) { | |
return this === buttonNode? "true": null; | |
}); | |
} | |
*/ |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-type" content="text/html; charset=utf-8"> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> | |
<link href="style.css" rel="stylesheet" type="text/css"> | |
</head> | |
<body> | |
<div class="details"> | |
<h1>Regional Currency Sentiments</h1> | |
<div id="yearbuttons"> | |
<!-- <button id="y2013" class="">2013</button> | |
<button id="y2014" class="">2014</button> | |
<button id="y2015" class="">2015</button> | |
<button id="y2016" class="">2016</button> --> | |
<button id="2017" class="current">2017</button> | |
</div> | |
<!--This script is to highlight each year button while on it--> | |
<script type="text/javascript"> | |
$('#yearbuttons button').on('click', function(){ | |
$('button.current').removeClass('current'); | |
$(this).addClass('current'); | |
}); | |
</script> | |
<div id="legend"> | |
<table class="table1"> | |
<tr> | |
<td bgcolor="#FFFFFF" width="10%"> | |
</td> | |
<td width="90%"><h3>Regions</h3> | |
</td> | |
</tr> | |
<tr> | |
<td bgcolor="#FF00FF" width="10%"> | |
</td> | |
<td><a class="two" id="NA" href = "#" onclick="ccyfilter='USD';" >Americas</a> | |
<table class="table2"> | |
<tr> | |
<td><h3>Currencies</h3></td> | |
</tr> | |
<tr> | |
<td>CAD<br/>USD<br/>MXN<br/>COP<br/>BRL<br/>CLP<br/>PEN<br/>ARS</td> | |
</tr> | |
</table> | |
</td> | |
</tr> | |
<tr> | |
<td bgcolor="#0000FF"> | |
</td> | |
<td><a class="two" id="NA" href = "#" >Europe</a> | |
<table class="table2"> | |
<tr> | |
<td><h3>Countries</h3></td> | |
</tr> | |
<tr><td>EUR<br/>GBP<br/>USD<br/>CZK<br/>RUB<br/>PLN<br/>HUF<br/>CHF<br/>TRL</td> | |
</tr> | |
</table> | |
</td> | |
</tr> | |
<tr> | |
<td bgcolor="#6495ED"> | |
</td> | |
<td><a class="two" id="NA" href = "#">Middle East & Africa</a> | |
<table class="table2"> | |
<tr> | |
<td><h3>Countries</h3></td> | |
</tr> | |
<tr> | |
<td> | |
USD<br/>EUR<br/>GBP<br/>TRL<br/>ILS<br/>ZAR<br/>MAD<br/>SOS | |
</td> | |
</tr> | |
</table> | |
</td> | |
</tr> | |
<tr> | |
<td bgcolor="#F4A460"> | |
</td> | |
<td><a class="two" id="NA" href = "#" >Emergent Nations</a> | |
<table class="table2"> | |
<tr> | |
<td><h3>Countries</h3></td> | |
</tr> | |
<tr> | |
<td>USD<br/>MXN<br/>TRY<br/>ILS<br/>CNHZ<br/>SGD<br/>HKD<br/>KRW<br/>BRL<br/>TWD<br/>ZAR<br/>PLN<br/>HUF<br/>CZK<br/>RUB | |
</td> | |
</tr> | |
</table> | |
</td> | |
</tr> | |
</table> | |
</div> | |
</div> | |
<div id="chart_placeholder"></div> | |
<div class="table_control"></div> | |
<div class="table_placeholder"></div> | |
<script type="text/javascript" src="chord.js"></script> | |
</body> | |
</html> |
name | color | |
---|---|---|
ARS | #FF00FF | |
AUD | #0000FF | |
BRL | #6495ED | |
CAD | #F4A460 | |
CHF | #8E8E38 | |
CLP | #C6E2FF | |
CNH | #D8BFD8 | |
CNY | #97FFFF | |
COP | #00EEEE | |
CZK | #FF00FF | |
DKK | #0000FF | |
EUR | #6495ED | |
GBP | #F4A460 | |
HKD | #8E8E38 | |
HUF | #C6E2FF | |
ILS | #D8BFD8 | |
KRW | #97FFFF | |
MAD | #00EEEE | |
MXN | #FF00FF | |
NOK | #0000FF | |
NZD | #6495ED | |
PEN | #F4A460 | |
PLN | #8E8E38 | |
RUB | #C6E2FF | |
SGD | #D8BFD8 | |
SOS | #97FFFF | |
TRL | #00EEEE | |
TRY | #FF00FF | |
TWD | #0000FF | |
USD | #6495ED | |
ZAR | #F4A460 |
body { | |
background: white; | |
width:1150px; | |
} | |
h1 { | |
font-size: 220%; | |
text-align: center; | |
font-family: verdana; | |
color: gray; | |
margin-top:0px; | |
} | |
h2 { | |
font-size: 130%; | |
font-family: verdana; | |
text-align: center; | |
color: black; | |
} | |
h3 { | |
font-size: 130%; | |
font-family: verdana; | |
text-align: center; | |
color: gray; | |
line-height: 100%; | |
} | |
h4 { | |
font-size: 100%; | |
font-family: verdana; | |
text-align: justify; | |
color: black; | |
} | |
h5 { | |
font-size: 100%; | |
font-family: verdana; | |
text-align: center; | |
color: black; | |
} | |
h6 { | |
font-size: 100%; | |
font-family: verdana; | |
text-align: left; | |
color: gray; | |
margin-top:12px; | |
} | |
p { | |
font-family: verdana; | |
font-size: 80%; | |
text-align: center; | |
} | |
#chart_placeholder { | |
text-align: center; | |
float:right; | |
color:#fff; | |
width: 750px; | |
height: 830px; | |
} | |
.details { | |
float:right; | |
width:350px; | |
} | |
/* | |
.dependencyWheel { | |
font: 10px sans-serif; | |
}*/ | |
#circle circle { | |
fill: none; | |
pointer-events: all; | |
} | |
path.chord { | |
stroke: #000; | |
stroke-width: .10px; | |
transition: opacity 0.3s; | |
} | |
#circle path.fade { | |
opacity: 1; | |
} | |
#circle:hover path.fade { | |
opacity: 0; | |
} | |
/*text is regions name only on chord diagram and scroll text*/ | |
text { | |
fill: black; | |
font-family: Arial Narrow,Arial,sans-serif; | |
text-align: center; | |
font-size: 14px; | |
} | |
svg { | |
font-size: 10px; | |
color: green; | |
min-height: 100%; | |
min-width: 100%; | |
} | |
.yearbuttons{/*area*/ | |
float: left; | |
margin-right: 50px; | |
width: 400px; | |
} | |
button{/*this are the year buttons*/ | |
background-color: white; | |
color: white; | |
font-size : 20px; | |
font-family: Arial Narrow,Arial,sans-serif; | |
border-color: white; | |
/* border-radius: 2em; */ | |
border-style: solid; | |
cursor:pointer; | |
} | |
.current{ | |
background-color: white; | |
color: black; | |
font-size : 20px; | |
font-family: Arial Narrow,Arial,sans-serif; | |
border-color: black; | |
/* border-radius: 2em; */ | |
} | |
#legend {/*regions*/ | |
float:left; | |
font-size: 14px; | |
width: 215px; | |
} | |
/*regions as well*/ | |
table.table1{ | |
} | |
/*countries*/ | |
table.table2 { | |
position: absolute; | |
top:180px; | |
left:1050px; | |
white-space: nowrap; | |
border-collapse: collapse; | |
display: none; | |
} | |
table.table2 td { | |
font-family: verdana; | |
font-size: 14px; | |
float: left; | |
} | |
p { | |
font-family: verdana; | |
font-size: 80%; | |
text-align: center; | |
} | |
.link { | |
margin-left: 30px; | |
width:300px; | |
height:50px; | |
float: left; | |
} | |
.scroll { | |
position:absolute; /*on top of vis*/ | |
top:0; | |
left:0px; | |
} | |
.scroll2 { | |
float:left; | |
} | |
/* unvisited link */ | |
a.one:link { | |
color: #3CBCBC; | |
text-decoration: none; | |
} | |
/* visited link */ | |
a.one:visited { | |
color: #54C571; | |
} | |
/* mouse over link */ | |
a.one:hover { | |
color: #2414ff; | |
} | |
/* selected link */ | |
a.one:active { | |
color: #3CBCBC; | |
} | |
/*this are the links to the table not to other pages*/ | |
/* mouse over link */ | |
a.two:hover + table { | |
display: table; | |
color: black; | |
} | |
a.two:click + table { | |
display: table; | |
color: black; | |
} | |
/* unvisited link */ | |
a.two:link { | |
font-family: verdana; | |
color: black; | |
text-decoration: none; | |
} | |
/* visited link */ | |
a.two:visited { | |
font-family: verdana; | |
color: black; | |
} | |
/* mouse over link */ | |
a.two:hover { | |
font-family: verdana; | |
color: gray; | |
} | |
/* selected link */ | |
a.two:active { | |
font-family: verdana; | |
color: black; | |
} | |
/*subframe*/ | |
table.table2a { | |
position: absolute; | |
top:140px; | |
left:1050px; | |
white-space: nowrap; | |
border-collapse: collapse; | |
} | |
table.table2a iframe { | |
border: 0; | |
} | |
table.table2a td { | |
font-family: verdana; | |
font-size: 14px; | |
float: left; | |
} | |
p { | |
font-family: verdana; | |
font-size: 80%; | |
text-align: center; | |
} | |
.link { | |
margin-left: 30px; | |
width:300px; | |
height:50px; | |
float: left; | |
} | |
.scroll { | |
position:absolute; /*on top of vis*/ | |
top:0; | |
left:0px; | |
} | |
.scroll2 { | |
float:left; | |
} | |
/* unvisited link */ | |
a.one:link { | |
color: #3CBCBC; | |
text-decoration: none; | |
} | |
/* visited link */ | |
a.one:visited { | |
color: #54C571; | |
} | |
/* mouse over link */ | |
a.one:hover { | |
color: #2414ff; | |
} | |
/* selected link */ | |
a.one:active { | |
color: #3CBCBC; | |
} | |
/*this are the links to the table not to other pages*/ | |
/* mouse over link */ | |
/* a.two:hover + table { | |
display: table; | |
color: black; | |
} */ | |
a.two:click + table { | |
display: table; | |
color: black; | |
} | |
/* unvisited link */ | |
a.two:link { | |
font-family: verdana; | |
color: black; | |
text-decoration: none; | |
} | |
/* visited link */ | |
a.two:visited { | |
font-family: verdana; | |
color: black; | |
} | |
/* mouse over link */ | |
a.two:hover { | |
font-family: verdana; | |
color: gray; | |
} | |
/* selected link */ | |
a.two:active { | |
font-family: verdana; | |
color: black; | |
} | |
body { | |
} | |
.table_placeholder { | |
position: absolute; | |
top:180px; | |
left:950px; | |
white-space: nowrap; | |
padding-top: 0px; | |
padding-bottom: 0px; | |
min-width: 500px; | |
} | |
.table_placeholder table{ | |
margin=1px; | |
} | |
.well { | |
padding-top: 0px; | |
padding-bottom: 0px; | |
min-width: 500px; | |
} | |
.h3, h3 { | |
text-align: left; | |
} | |
label { | |
margin-bottom: 10px; | |
} | |
.table1 table { | |
border-collapse: separate; | |
} | |
h3, .h3 { | |
text-align: left; | |
} |
'use strict'; | |
// title div with label and button | |
var header = d3.select(".table_placeholder").append("div").attr("class", "well"); | |
header.append("h3").text("Notable events relating to selected CCY " + ccyfilter) | |
.attr("text-align", "left") | |
.attr("class","h3class") | |
; | |
var taskLabel = header.append("label") | |
.attr("id", "taskLabel") | |
.html(" "); | |
header.append("br") | |
var currTask = 0; | |
var taskButton1 = header.append("button") | |
.attr("class", "btn btn-primary") | |
.style("margin-bottom", "20px") | |
.style("width", "200px") | |
.style("text-align", "left") | |
.style("margin-left", "1px") | |
.text("Data Relations") | |
.on("click", function() { | |
this.blur(); | |
// execute the task | |
showmatrix("interests.csv"); | |
// next task | |
// currTask = ++currTask % tasks.length; | |
}) | |
var taskButton2 = header.append("button") | |
.attr("class", "btn btn-primary") | |
.style("margin-bottom", "20px") | |
.style("width", "200px") | |
.style("text-align", "left") | |
.style("margin-left", "1px") | |
.text("Show Matrix") | |
.on("click", function() { | |
this.blur(); | |
// execute the task | |
showmatrix("matrix.csv"); | |
// next task | |
// currTask = ++currTask % tasks.length; | |
}) | |
var button_clear = header.append("button") | |
.attr("class", "btn btn-primary") | |
.style("margin-bottom", "20px") | |
.style("width", "200px") | |
.style("text-align", "left") | |
.style("margin-left", "1px") | |
.text("Clear") | |
.on("click", function() { | |
this.blur(); | |
// execute the task | |
task_delete_tables(); | |
// next task | |
// currTask = ++currTask % tasks.length; | |
}) | |
// container for array of tables | |
var tableDiv = d3.select(".table_placeholder").append("div").attr("id", "tableDiv1"); | |
// initial data | |
var data; | |
var initialData = [ | |
{ table: "Notable Events", rows: [ | |
{ col1: "abc", col2: "Row1", col3: "DataT1R1" }, | |
{ col1: "sdfasdf", col2: "Row1", col3: "DataT1R1" }, | |
{ col1: "asdfasdf", col2: "Row2", col3: "DataT1R2" } | |
] | |
} | |
] | |
// tasks | |
function task_delete_tables() { | |
// clear any existing tables (by providing an empty array) | |
// update([]); | |
// var table = d3.select("wells").select("table").selectAll("tr") | |
var divs = tableDiv.selectAll("div"); | |
ccyfilter=""; | |
divs.remove(); | |
// taskLabel.html("Cleared any existing tables"); | |
} | |
function task_initialize() { | |
// load initial tables | |
data = JSON.parse(JSON.stringify(initialData)); | |
update(data); | |
console.log(data); | |
taskLabel.text("Step 1: Initial tables loaded"); | |
} | |
function task_initialize_csv() { | |
data = JSON.parse(JSON.stringify(initialData)); | |
update(data); | |
update([]); | |
d3.text("interests.csv", function(csvdata) { | |
var divs = tableDiv.selectAll("div") | |
// after .data() is executed below, divs becomes a d3 update selection | |
.data(data, // new data | |
function(d) { return d.table // "key" function to disable default by-index evaluation | |
}) | |
var divsEnter = divs.enter().append("div") | |
.attr("id", function(d) { return d.table + "Div"; }) | |
.attr("class", "well") | |
// add title in new div(s) | |
divsEnter.append("h5").text(function(d) { return d.table; }); | |
// add table in new div(s) | |
var tableEnter = divsEnter.append("table") | |
.attr("id", function(d) { return d.table }) | |
.attr("class", "table table-condensed table-striped table-bordered") | |
// append table head in new table(s) | |
tableEnter.append("thead") | |
.append("tr") | |
.selectAll("th") | |
.data(["Currency Pair", "Time", "Related Information"]) // table column headers (here constant, but could be made dynamic) | |
.enter().append("th") | |
.text(function(d) { return d; }) | |
// append table body in new table(s) | |
tableEnter.append("tbody"); | |
var parsedCSV = d3.csv.parseRows(csvdata); | |
var tr = divs.select("table").select("tbody").selectAll("tr") | |
.data(parsedCSV).enter() | |
.append("tr") | |
.selectAll("td") | |
.data(function(d) { return d; }).enter() | |
.append("td") | |
.text(function(d) { return d; }); | |
}); | |
} | |
function task_update_insert() { | |
// add 4th row to table 2 | |
data[0].rows.push({ table: "Table2", row: "Row4", data: "DataT2R4" }); | |
update(data); | |
taskLabel.text("Step 2: Added 4th row to Table 2"); | |
} | |
function showmatrix(filename){ | |
header.select('h3').text("Notable events relating to selected CCY " + ccyfilter); | |
data = JSON.parse(JSON.stringify(initialData)); | |
update(data); | |
update([]); | |
d3.text(filename, function(csvdata) { | |
var divs = tableDiv.selectAll("div") | |
// after .data() is executed below, divs becomes a d3 update selection | |
.data(data, // new data | |
function(d) { return d.table // "key" function to disable default by-index evaluation | |
}) | |
var divsEnter = divs.enter().append("div") | |
.attr("id", function(d) { return d.table + "Div"; }) | |
.attr("class", "well") | |
// add title in new div(s) | |
divsEnter.append("h5").text(function(d) { return d.table; }); | |
var parsedCSV = d3.csv.parseRows(csvdata); | |
// add table in new div(s) | |
var tableEnter = divsEnter.append("table") | |
.attr("id", function(d) { return d.table }) | |
.attr("class", "table table-condensed table-striped table-bordered") | |
// append table head in new table(s) | |
tableEnter.append("thead") | |
.append("tr") | |
.selectAll("th") | |
.data((parsedCSV[0])) // table column headers (here constant, but could be made dynamic) | |
.enter().append("th") | |
.text(function(d) { return d; }) | |
// append table body in new table(s) | |
tableEnter.append("tbody"); | |
parsedCSV.splice(0,1); | |
// Filter | |
if ( filename.includes('interest')){ | |
for (var i = parsedCSV.length - 1; i >= 0; i--) { | |
// Filter filter | |
if (!parsedCSV[i][1].includes(csvfilter)) { | |
console.log(parsedCSV.splice(i,1)); | |
} | |
// Filter currency | |
if (!parsedCSV[i][0].includes(ccyfilter)) { | |
console.log(parsedCSV.splice(i,1)); | |
} | |
} | |
} | |
var tr = divs.select("table").select("tbody").selectAll("tr") | |
.data(parsedCSV).enter() | |
.append("tr") | |
.selectAll("td") | |
.data(function(d) { return d; }).enter() | |
.append("td") | |
.text(function(d) { return d; }); | |
}); | |
} | |
function task_updated() { | |
// change the content of row 1 of table 1 | |
var item = data[0].rows[0].data; | |
data[0].rows[0].data = item + " - Updated"; | |
update(data); | |
taskLabel.text("About To restart"); | |
taskButton.text("Restart") ; | |
} | |
// task list (array of functions) | |
var tasks = [task_delete_tables,task_initialize,task_update_insert,task_updated] | |
// function in charge of the array of tables | |
function update(data) { | |
// select all divs in the table div, and then apply new data | |
var divs = tableDiv.selectAll("div") | |
// after .data() is executed below, divs becomes a d3 update selection | |
.data(data, // new data | |
function(d) { return d.table // "key" function to disable default by-index evaluation | |
}) | |
// use the exit method of the d3 update selection to remove any deleted table div and contents (which would be absent in the data array just applied) | |
divs.exit().remove(); | |
// use the enter metod of the d3 update selection to add new ("entering") items present in the data array just applied | |
var divsEnter = divs.enter().append("div") | |
.attr("id", function(d) { return d.table + "Div"; }) | |
.attr("class", "well") | |
// add title in new div(s) | |
divsEnter.append("h5").text(function(d) { return d.table; }); | |
// add table in new div(s) | |
var tableEnter = divsEnter.append("table") | |
.attr("id", function(d) { return d.table }) | |
.attr("class", "table table-condensed table-striped table-bordered") | |
// append table head in new table(s) | |
tableEnter.append("thead") | |
.append("tr") | |
.selectAll("th") | |
.data(["Currency Pair", "Time", "Related Information"]) // table column headers (here constant, but could be made dynamic) | |
.enter().append("th") | |
.text(function(d) { return d; }) | |
// append table body in new table(s) | |
tableEnter.append("tbody"); | |
// select all tr elements in the divs update selection | |
var tr = divs.select("table").select("tbody").selectAll("tr") | |
// after the .data() is executed below, tr becomes a d3 update selection | |
.data( | |
function(d) { return d.rows; }, // return inherited data item | |
function(d) { return d.row } // "key" function to disable default by-index evaluation | |
); | |
// use the exit method of the update selection to remove table rows without associated data | |
tr.exit().remove(); | |
// use the enter method to add table rows corresponding to new data | |
tr.enter().append("tr"); | |
// bind data to table cells | |
var td = tr.selectAll("td") | |
// after the .data() is executed below, the td becomes a d3 update selection | |
.data(function(d) { return d3.values(d); }); // return inherited data item | |
// use the enter method to add td elements | |
td.enter().append("td") // add the table cell | |
.text(function(d) { return d; }) // add text to the table cell | |
} | |
[[6, 3, 7, 0, 7, 3, 8, 3, 4, 3, 3, 4, 4, 3, 8, 7, 5, 7, 3, 2, 1, 5, 8, 5, | |
6, 9, 7, 2, 8, 4, 1], | |
[0, 3, 1, 2, 0, 0, 0, 9, 6, 6, 6, 6, 0, 1, 8, 6, 2, 7, 7, 7, 0, 0, 1, 3, | |
5, 9, 1, 2, 6, 7, 9], | |
[3, 2, 8, 5, 9, 2, 0, 4, 2, 5, 2, 1, 7, 9, 8, 6, 2, 9, 5, 4, 0, 6, 0, 3, | |
9, 6, 9, 0, 3, 1, 8], | |
[3, 2, 4, 1, 4, 7, 0, 3, 4, 0, 3, 2, 9, 3, 1, 5, 0, 6, 6, 2, 3, 6, 8, 5, | |
7, 4, 6, 7, 9, 3, 8], | |
[5, 6, 4, 8, 7, 7, 2, 9, 5, 0, 6, 6, 7, 2, 9, 9, 2, 1, 7, 6, 8, 5, 6, 1, | |
2, 2, 5, 0, 8, 4, 5], | |
[8, 5, 8, 9, 7, 7, 1, 6, 6, 3, 1, 5, 8, 2, 2, 6, 9, 4, 2, 1, 4, 1, 6, 9, | |
1, 2, 2, 5, 8, 6, 8], | |
[2, 9, 2, 1, 1, 6, 5, 7, 8, 8, 1, 3, 8, 1, 3, 2, 5, 4, 3, 3, 5, 1, 1, 6, | |
1, 0, 8, 7, 6, 2, 5], | |
[6, 0, 0, 2, 3, 7, 6, 8, 5, 2, 7, 8, 1, 7, 5, 5, 9, 2, 7, 8, 9, 1, 2, 7, | |
8, 4, 3, 5, 7, 4, 0], | |
[1, 4, 5, 1, 8, 6, 0, 5, 0, 8, 8, 2, 7, 0, 6, 5, 7, 6, 3, 4, 1, 2, 8, 9, | |
0, 3, 4, 7, 7, 4, 6], | |
[2, 0, 1, 5, 7, 8, 5, 7, 1, 4, 6, 1, 3, 0, 7, 6, 5, 1, 8, 9, 1, 3, 6, 3, | |
1, 0, 2, 3, 5, 6, 0], | |
[6, 0, 4, 9, 6, 8, 4, 2, 9, 1, 5, 7, 1, 1, 6, 6, 5, 8, 7, 4, 3, 8, 2, 7, | |
8, 3, 0, 9, 5, 0, 2], | |
[3, 4, 2, 4, 2, 8, 1, 7, 2, 0, 9, 4, 7, 9, 9, 0, 1, 2, 2, 4, 0, 5, 0, 1, | |
0, 5, 9, 0, 8, 9, 7], | |
[9, 3, 3, 4, 4, 8, 7, 8, 2, 2, 6, 1, 2, 6, 2, 6, 9, 4, 9, 9, 2, 2, 4, 6, | |
1, 8, 3, 7, 8, 5, 3], | |
[9, 7, 4, 3, 2, 4, 9, 6, 9, 2, 4, 6, 4, 6, 3, 2, 1, 9, 7, 9, 6, 1, 4, 3, | |
7, 8, 5, 7, 9, 8, 7], | |
[3, 5, 3, 3, 0, 0, 6, 7, 9, 9, 3, 5, 1, 9, 2, 4, 9, 8, 3, 6, 8, 8, 9, 1, | |
8, 0, 3, 6, 3, 7, 1], | |
[5, 7, 2, 9, 9, 8, 6, 3, 1, 6, 6, 4, 1, 6, 1, 6, 7, 1, 0, 8, 7, 2, 5, 6, | |
5, 5, 4, 5, 6, 0, 3], | |
[9, 1, 4, 8, 3, 0, 7, 7, 8, 2, 8, 6, 2, 4, 1, 1, 9, 5, 0, 3, 3, 4, 5, 8, | |
3, 7, 3, 9, 9, 0, 7], | |
[0, 5, 7, 9, 6, 9, 2, 2, 6, 6, 2, 7, 4, 6, 9, 1, 3, 7, 6, 1, 9, 3, 3, 8, | |
8, 9, 8, 2, 1, 4, 3], | |
[9, 7, 9, 4, 6, 3, 1, 3, 5, 1, 4, 2, 7, 4, 5, 9, 6, 7, 7, 2, 3, 6, 0, 3, | |
0, 0, 6, 1, 4, 3, 5], | |
[9, 5, 5, 7, 0, 2, 9, 8, 5, 6, 6, 5, 5, 0, 1, 1, 8, 7, 1, 2, 6, 8, 3, 0, | |
8, 7, 5, 0, 1, 3, 7], | |
[2, 0, 6, 9, 3, 8, 5, 4, 5, 1, 4, 0, 5, 6, 6, 2, 6, 0, 4, 7, 7, 7, 1, 8, | |
6, 9, 9, 6, 7, 9, 7], | |
[1, 8, 4, 0, 1, 3, 8, 3, 5, 5, 3, 3, 8, 1, 0, 2, 6, 1, 3, 9, 2, 4, 5, 8, | |
3, 5, 9, 5, 5, 2, 3], | |
[3, 4, 3, 2, 9, 7, 5, 4, 2, 8, 0, 7, 9, 8, 8, 3, 2, 6, 8, 2, 1, 5, 3, 1, | |
4, 5, 1, 0, 0, 8, 5], | |
[7, 1, 4, 7, 3, 5, 0, 8, 2, 9, 4, 1, 9, 7, 0, 3, 9, 7, 8, 5, 3, 9, 3, 6, | |
8, 5, 7, 0, 8, 0, 2], | |
[5, 5, 0, 9, 0, 8, 2, 6, 2, 6, 6, 8, 1, 0, 1, 6, 4, 0, 7, 2, 8, 6, 0, 3, | |
1, 8, 6, 8, 3, 5, 6], | |
[1, 5, 7, 3, 9, 8, 7, 6, 7, 0, 6, 7, 7, 4, 4, 0, 0, 8, 8, 5, 2, 0, 6, 3, | |
1, 7, 1, 7, 7, 8, 4], | |
[3, 1, 5, 9, 1, 5, 1, 3, 0, 3, 3, 4, 9, 5, 1, 7, 1, 4, 6, 9, 7, 8, 3, 2, | |
7, 5, 7, 8, 3, 8, 1], | |
[4, 1, 6, 3, 5, 0, 3, 1, 9, 1, 9, 5, 2, 8, 1, 3, 8, 9, 8, 8, 1, 6, 1, 8, | |
3, 1, 1, 8, 8, 3, 4], | |
[5, 0, 1, 4, 0, 3, 0, 5, 5, 8, 2, 3, 4, 1, 3, 2, 0, 3, 4, 1, 0, 1, 2, 9, | |
7, 4, 2, 5, 8, 8, 4], | |
[1, 8, 4, 1, 6, 1, 5, 1, 6, 3, 2, 6, 7, 5, 7, 7, 6, 4, 5, 8, 3, 6, 9, 9, | |
3, 4, 4, 5, 0, 4, 7], | |
[4, 8, 1, 0, 7, 1, 9, 5, 1, 4, 0, 4, 7, 6, 5, 3, 3, 3, 1, 4, 5, 4, 3, 9, | |
4, 1, 3, 2, 3, 8, 8]] |