Skip to content

Instantly share code, notes, and snippets.

@iexviz
Last active April 3, 2017 02:04
Show Gist options
  • Save iexviz/dd7f2afab6b9dea7fd8b1d70c2489b3b to your computer and use it in GitHub Desktop.
Save iexviz/dd7f2afab6b9dea7fd8b1d70c2489b3b to your computer and use it in GitHub Desktop.
IEX Market - Orbitting Moons
license: mit
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.experiment {
font-family: Arial, sans-serif;
font-size: 16px;
}
div.container {
display: block;
position: absolute;
//top: 10px;
left:0px;
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
}
svg {
display: block;
position: relative;
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
}
.circleText {
fill: #666;
font-family: SequentialistBB, Arial, sans-serif;
}
.centerText {
fill: #666;
font-family: SequentialistBB, Arial, sans-serif;
font-family: Arial, sans-serif;
font-size: 26px;
}
button {
width: 100px;
height: 34px;
border-radius: 4px;
border: none;
background-color: #ddd;
pointer-events: all;
cursor: pointer;
}
.d3-tip {
font-family: 'Raleway', sans-serif;
font-size: .8em;
line-height: 1;
padding: 7px;
background: black;
color: lightgray;
border-radius: 20px;
}
</style>
<body align=center bgcolor=black>
<div class=dropdown></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.6.3/d3-tip.min.js"> </script>
<script src="karthiviz.js"></script>
<script>
function updateviz(key){
var svg = d3.select("body").append('svg')
.attr('width', 960)
.attr('height', 960);
// animation
var eases = ["sine", "quad", "cubic", "bounce","back"];
var curEase = eases[2];
var curScale = 1;
// dummy data
var centerX = $(window).width() / 2;
var centerY = $(window).height() / 2.2;
var mainTitle = "ripley".toUpperCase().split(' ')
var titleA = "bishop".toUpperCase().split(' ');
var titleB = "vasquez".toUpperCase().split(' ');
var titleC = "gorman".toUpperCase().split(' ');
var titleD = "hicks".toUpperCase().split(' ');
var titles = [titleA,titleB,titleC,titleD];
var bubble = {title: "", frequency: 0, trend: 0, x: 0, y: 100, angle: 0};
var mainBubble = _.clone(bubble);
mainBubble.title = "";
mainBubble.frequency = 1200;
mainBubble.trend = 0;
mainBubble.x = 100;
var bubbleData = [mainBubble];
data.forEach(function(d,index){
var subBubble = _.clone(bubble);
subBubble.title = d.name;
subBubble.value2 = d.value2;
subBubble.frequency = d.value*5;
subBubble.trend = index + 1;
subBubble.x = 300 * (index + 1 );
bubbleData.push(subBubble);
})
////////////////////////////// UTILITIES
var getRadius = function( d,i ){
if(i ==0){
return d.radius = 120
}
return d.radius = 30;
return d.radius = Math.floor( d.frequency / 4.75 );
}
var getColor = function( i ){
if(i ==0){
return "orange"
}
return color(i)
}
var getEase = function(){
var index = Math.floor( Math.random() * eases.length);
return eases[index];
}
var getTextStyle = function (d, textColor) {
var that = this;
var magicNum = 1.7;
var textColor = (undefined !== textColor && null !== textColor) ? textColor : "#000";
//var bubbleRadius = (null !== selRadius ) ? ( selRadius + 20 ) : d.radius;
var longestStrNum = _.max( d.title, function(name){ return name.length; }).length;
// if its 5 or less letters... just use 5 so small words like CAT don't look oddly large
var longestSizeNum = ( longestStrNum <= 5 ) ? 5 : longestStrNum;
var fontSize = Math.round ( d.radius / ( longestSizeNum / magicNum ) );
var styleStr = "font-family: Arial, sans-serif; font-weight:bold; font-size: " + fontSize + "px; fill: " + textColor;
return styleStr;
};
var getFontSize = function( textArray ) {
var textSizeArray = [];
for (var i = 0; i < textArray.length; i++) {
textSizeArray.push( textArray[i].length );
}
return Math.max.apply(Math, textSizeArray);
}
var getDestinationVector = function( originVector, angle, distance) {
var destinationVector = {x: 0, y: 0};
destinationVector.x = Math.round(Math.cos(angle * Math.PI / 180) * distance + originVector.x);
destinationVector.y = Math.round(Math.sin(angle * Math.PI / 180) * distance + originVector.y);
return destinationVector;
}
var getDistance = function(d){
var mainBubbleRadius = bubbleData[0].radius;
var centerPoint = {x: centerX, y: centerY};
var targetRadius = d.radius;
var distance = mainBubbleRadius + targetRadius+20;
return distance;
}
var getSubBubblePoint = function(d,index){
var angles = [-45, -140, -235, -320, -330];
var centerPoint = {x: centerX, y: centerY};
var distance = getDistance(d);
var angle = index*-28+100
d.angle = angle;
var targetPoint = getDestinationVector(centerPoint, angle, distance);
return targetPoint;
}
var getOrbitPoint = function(d, direction){
if (direction === "negative") d.angle++;
else d.angle--;
if (d.angle > 360 || d.angle < -360) d.angle = 0;
var angle = d.angle;
var centerPoint = {x: centerX, y: centerY};
var distance = getDistance(d);
var targetPoint = getDestinationVector(centerPoint, angle, distance);
return targetPoint;
}
var getTranslation = function(d,index){
// move 0 to the center;
var bubbleGroupX;
var bubbleGroupY;
switch( d.trend) {
case 0 :
bubbleGroupX = centerX;
bubbleGroupY = centerY;
break;
default :
var subBubblePoint = getSubBubblePoint(d, index);
bubbleGroupX = subBubblePoint.x;
bubbleGroupY = subBubblePoint.y;
break;
}
d.x = bubbleGroupX;
d.y = bubbleGroupY;
return "translate(" + bubbleGroupX + "," + bubbleGroupY + ") scale(1)";
}
var getOrbitTranslation = function(d, index, direction) {
var vector = getOrbitPoint(d,direction);
return "translate(" + vector.x + "," + vector.y + ") scale(1)";
}
////////////////////////////// DRAW
var bubbles;
var orbitInterval;
var draw = function( bubbles ){
var onBubbleClickHandler = function(d,i){
clearInterval(orbitInterval);
orbit("positive");
}
var gradientRadial = svg.append("defs").selectAll("radialGradient")
.data(bubbles)
.enter().append("radialGradient")
.attr("id", function(d,i){ return "gradient-" + i; })
.attr("cx", "30%")
.attr("cy", "30%")
.attr("r", "65%");
//Append the color stops
gradientRadial.append("stop")
.attr("offset", "0%")
.attr("stop-color", function(d,i) { return d3.rgb(getColor(i)).brighter(1); });
gradientRadial.append("stop")
.attr("offset", "50%")
.attr("stop-color", function(d,i) { return getColor(i); });
gradientRadial.append("stop")
.attr("offset", "100%")
.attr("stop-color", function(d,i) { return d3.rgb(getColor(i)).darker(1.5); });
bubbles = svg.selectAll("g myCircleText")
.data(bubbles)
.enter()
.append("g")
.attr("transform", function(d){return "translate("+d.x+","+d.y+")"});
bubbles
.each(function (d, i) {
if (i === 0 ) {
d3.select(this)
.on("click", onBubbleClickHandler)
.attr("pointer-events", "all")
.attr("cursor", "pointer");
}
});
bubbles
.append("svg:circle")
.attr("class", function(d,i){
if(i==0){return ""}
return "circle"} )
.attr("r", function(d,i){return getRadius(d,i)} )
//.attr("stroke","black")
//.attr("stroke-width","0")
.style("fill", function(d,i) { return "url(#gradient-" + i + ")"; });
bubbles
.each(function (d, i) {
var container = d3.select(this);
var dy0 = -(d.title.length - 1) / 2;
var magicNum = 1.75;
container
.append('svg:text')
.attr('pointer-events', 'none')
.attr("text-anchor", "middle")
.attr("dy", "0em")
.attr("class", function(d,i){
if(i==0){return "centerText"}
return"circleText"
}) .attr("style", function(d) { return getTextStyle(d, null); })
.text(function(d) {
if(i ==0){
return key
}
return d.title; })
container
.append('svg:text')
.attr('pointer-events', 'none')
.attr("text-anchor", "middle")
.attr("dy", "1em")
.attr("class", function(d,i){
if(i==0){return "centerText"}
return"circleText"
})
.attr("style", function(d) { return getTextStyle(d, null); })
.text(function(d) {
if(i ==0){
return total
}
return formatAbbreviation(d.value2); })
;
});
bubbles
.transition()
.duration(2000)
.attr("transform", function(d, index){ return getTranslation(d, index)} );
var orbit = function(direction){
bubbles
.each(function (d, i) {
if (i !== 0 ) {
d3.select(this)
.attr("transform", function(d, index){ return getOrbitTranslation(d, index, direction)} );
//console.log("after: ", d)
}
});
}
var animateOrbit = function(){
orbitInterval = setInterval( function(){
orbit("negative");
}, 50);
}
setTimeout( function(){
animateOrbit();
}, 500);
}
draw(bubbleData);
}
</script>
d3.jsonp = function (url, callback) {
function rand() {
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
c = '', i = -1;
while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
return c;
}
function create(url) {
var e = url.match(/callback=d3.jsonp.(\w+)/),
c = e ? e[1] : rand();
d3.jsonp[c] = function(data) {
callback(data);
delete d3.jsonp[c];
script.remove();
};
return 'd3.jsonp.' + c;
}
var cb = create(url),
script = d3.select('head')
.append('script')
.attr('type', 'text/javascript')
.attr('src', url.replace(/(\{|%7B)callback(\{|%7D)/, cb));
};
d3.jsonp("https://api.iextrading.com/1.0/market?callback=visualise");
function visualise(data){
window.data = data;
var option = [ "marketPercent", "volume", "tapeA", "tapeB", "tapeC"]
var key = option[0]
var select = d3.select('.dropdown')
.append('select')
.style('font-size', '20px')
.attr('class','select')
.on('change',onchange);
var options = select
.selectAll('option')
.data(option).enter()
.append('option')
.text(function (d) { return d; });
function onchange() {
renderviz(d3.select('select').property('value'))
}
data.forEach(function(d,i) {
d.marketPercent = (d.marketPercent*100).toFixed(2);
});
renderviz(key)
};
function renderviz(key) {
var circs = []
data.forEach(function(d){ circs.push(d[key])})
//console.log(d3.sum(circs).toFixed(0))
var total = formatAbbreviation(d3.sum(circs).toFixed(0))
window.total = total
var rscale = d3.scale.sqrt()
.domain([0,(d3.sum(circs)).toFixed(0)])
.range([0,200])
data.forEach(function(d,i) {
d.name = d.venueName;
d.value2 = d[key];
console.log(formatAbbreviation(d.value2))
d.value = rscale(d[key]).toFixed(2);
});
color = d3.scale.category20c();
window.color = color;
d3.select("body").selectAll("svg").remove();
updateviz(key);
setuptip(key);
}
function setuptip(key) {
var format = d3.format(",")
tip = d3.tip()
.attr('class', 'd3-tip')
.html(function(d,i) {
return d.name + ": " + format(d[key]) });
d3.selectAll(".circle").call(tip)
d3.selectAll('.circle')
.data(data)
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
}
formatAbbreviation = function(x) {
var formatNumber = d3.format(".0f"),
formatPercent = d3.format(".2f"),
formatBillion = function(x) { return "" + formatNumber(x / 1e9) + "B"; },
formatMillion = function(x) { return "" + formatNumber(x / 1e6) + "M"; },
formatThousand = function(x) { return "" + formatNumber(x / 1e3) + "k"; };
var v = Math.abs(x);
return (v >= .9995e9 ? formatBillion
: v >= .9995e6 ? formatMillion
: v >= .9995e3 ? formatThousand
: formatPercent)(x);
}
var svg = d3.select("#stage");
var drawCycle = function(){
var createViz = function() {
var dateObj = new Date();
var timeStr = dateObj.toLocaleTimeString();
document.getElementById("drawing").innerHTML = "drawing interval: " + timeStr;
svg.selectAll("g").remove();
var ranFreqA = Math.floor((Math.random()*100)+1);
var ranFreqB = Math.floor((Math.random()*100)+1);
var ranFreqC = Math.floor((Math.random()*100)+1);
var titleA = "toronto walking tours".toUpperCase().split(' ');
var titleB = "san francisco hyperloop".toUpperCase().split(' ');
var titleC = "tokyo and back again".toUpperCase().split(' ');
var responseA = {title: titleA, frequency: ranFreqA, x: 75 };
var responseB = {title: titleB, frequency: ranFreqB, x: 175 };
var responseC = {title: titleC, frequency: ranFreqC, x: 300 };
var responseData = [responseA, responseB, responseC];
// font scaling has to be based on the longest string in the series
var getFontSize = function( textArray ) {
var textSizeArray = [];
for (var i = 0; i < textArray.length; i++) {
textSizeArray.push( textArray[i].length );
}
return Math.max.apply(Math, textSizeArray);
}
var elem = svg.selectAll("g myCircleText")
.data(responseData)
var group = elem.enter()
.append("g")
.attr("transform", function(d){return "translate("+d.x+", 150)"})
var circles = group.append("svg:circle")
.attr("r", function(d){return d.frequency } )
.attr("stroke","black")
.attr("stroke-width","4")
.attr("fill", "white")
group
.each(function (d, i) {
var container = d3.select(this);
var dy0 = -(d.title.length - 1) / 3;
var magicNum = 1.75; // my math isn't the greatest so I got this by trial and error
var longestStrSize = getFontSize(d.title);
for (var i = 0; i < d.title.length; i++)
{
container
.append('svg:text')
.attr('pointer-events', 'none')
.attr("text-anchor", "middle")
.attr("dy", (dy0 + i) + "em")
.attr("class", "circleText")
.style("fill", "black")
.style("font-size", function(d) { return ( d.frequency / ( longestStrSize / magicNum ) ) + "px" })
.text(function(d) { return d.title[i]; })
;
}
});
};
var cycleInterval = setInterval( function(){ createViz(); }, 500 );
//createViz();
}
drawCycle();
.experiment {
font-family: Helvetica, Arial, sans-serif;
font-size: 16px;
}
.circleText {
color: #D7BF00;
font-family: Georgia, serif;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment