block by sxywu 9c11f2880587196e0fded1f977ff46ff

openvis tweets #5

Full Screen

Spread of tweets during the two days of Openvis Conf.

Built with blockbuilder.org

forked from sxywu‘s block: openvis tweets #1

forked from sxywu‘s block: openvis tweets #2

forked from sxywu‘s block: openvis tweets #3

forked from sxywu‘s block: openvis tweets #4

forked from sxywu‘s block: openvis tweets #4.5: data crunch

index.html

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <script src="https://fb.me/react-0.14.3.js"></script>
  <script src="https://fb.me/react-dom-0.14.3.js"></script>
  <script src="https://npmcdn.com/babel-core@5.8.34/browser.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.js'></script>
  <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
  <link href='https://fonts.googleapis.com/css?family=Lora' rel='stylesheet' type='text/css'>
  <style>
    body {
      font-family: 'Lora', serif;
      margin:0;
      color: #49438C;
    }
    text {
      font-size: .8em;
    }
    
    .axis path,
    .axis line {
      fill: none;
      stroke: #000;
      shape-rendering: crispEdges;
    }
    
    a {
      color: #fe2b75;
    }
  </style>
</head>

<body>
  <div id='main'></div>

  <script type="text/babel">
var playing = false;
var padding = {top: 25, left: 50};
var colors = {purple: '#49438C', pink: '#fe2b75'};
var startDate = new Date('2016-04-25T00:00:00-04:00');
var endDate = new Date('2016-04-27T00:00:00-04:00');
var dateFormat = d3.time.format('%x %I:%M%p');

var Bubbles = React.createClass({
	getInitialState() {
  	return {
    	wordsByTweet: {},
      correlations: {},
      nodes: {},
      links: {},
      width: 900,
      height: 350,
    }
  },
  
  componentWillMount() {
  	d3.json('wordsByTweet.json', (wordsByTweet) => {
    	d3.json('correlations.json', (correlations) => {
      	d3.json('words.json', (words) => {
        	var maxTweets = d3.max(words, (word) => word.tweets.length);
          var maxUsers = d3.max(words, (word) => word.users.length);
        	this.tweetsScale = d3.scale.log()
          	.domain([1, maxTweets * 1.1])
            .range([padding.left, this.state.width - padding.left]);
          this.usersScale = d3.scale.log()
          	.domain([1, maxUsers * 1.1])
            .range([this.state.height - padding.top, padding.top]);
        });
        this.setState({wordsByTweet, correlations});
      });
    });
  },
  
  componentWillReceiveProps(nextProps) {
    var nodes = {};
    _.some(nextProps.tweetsByTime, (obj) => {
    	var {date, tweets} = obj;
      if (date <= nextProps.time) {
      	// go through each of the tweets and aggregate the words
      	_.each(tweets, (tweet) => {
        	// so first find the tweet, and then get the words in those tweets
          var tweetId = tweet.id;
          var username = tweet.actor.preferredUsername;
          var words = this.state.wordsByTweet[tweetId];
          _.each(words, (word) => {
          	if (!nodes[word]) {
            	nodes[word] = {
              	text: word,
                favorites: 0,
                tweets: {},
                users: {},
              }
            }
            if (!nodes[word].tweets[tweetId]) {
              nodes[word].favorites += tweet.favoritesCount || 1;
              nodes[word].tweets[tweetId] = tweetId;
              nodes[word].users[username] = username;
            }
          });
        });
      	return false;
      }
      return true;
    });
    
    _.each(nodes, node => {
    	node.x = this.tweetsScale(_.size(node.tweets));
      node.y = this.usersScale(_.size(node.users));
    });
    
    console.log(nodes)
    this.setState({nodes});
  },
  
  render() {
  	var nodeStyle = {
    	fill: colors.purple,
      fillOpacity: 0.5,
    };
  	var nodes = _.map(this.state.nodes, (node) => {
    	return (<circle key={node.text} cx={node.x} cy={node.y}
      	r={node.favorites} style={nodeStyle} />);
    });
  	return (
    	<svg width={this.state.width} height={this.state.height}>
      	{nodes}
      </svg>
    );
  },
});

var App = React.createClass({
  getInitialState() {
    return {
      tweets: {},
      tweetsByTime: [],
      time: startDate,
    };
  },
  
  componentWillMount() {
    d3.json('tweets.json', tweets => {      
      // group by every 5min
      var fifteen = 1000 * 60 * 15;
      var tweetsByTime = _.chain(tweets)
        .groupBy(tweet => {
        	tweet.id = tweet.link;
        	tweet.date = new Date(tweet.postedTime);
          tweet.dateRounded = Math.floor(tweet.date.getTime() / fifteen) * fifteen;
          return tweet.dateRounded;
        }).map((tweets, date) => {
          return {
            date: new Date(parseInt(date)),
            tweets: tweets,
          };
        }).sortBy((obj) => obj.date).value();

      this.setState({tweets, tweetsByTime});
    });
  },
  
  onClickPlay() {
    playing = true;

    var fifteen = 1000 * 60 * 15;
    var tick = 250;
    var lastTick = 0;
    var time = this.state.time >= endDate ? startDate : this.state.time;
    d3.timer(elapsed => {
      if (elapsed > (lastTick + tick)) {
        lastTick = elapsed;
        time = new Date(time.getTime() + fifteen);

        this.setState({time});
      }

      if (time >= endDate || !playing) {
        playing = false;
        this.forceUpdate();
        return true;
      }
    });
  },

  onClickPause() {
    playing = false;
    this.forceUpdate();
  },
  
  render() {
    var headerStyle = {
      padding: padding.top + 'px ' + padding.left + 'px',
      cursor: 'pointer',
    }
    var play = (<span className='glyphicon glyphicon-play' onClick={this.onClickPlay} />);
    if (playing) {
      play = (<span className='glyphicon glyphicon-pause' onClick={this.onClickPause} />);
    }

    return (
      <div>
        <h2 style={headerStyle}>
          {play} {dateFormat(this.state.time)}
        </h2>
        <Bubbles {...this.state} />
      </div>
    );
  },
});

ReactDOM.render(
  <App />,
  document.getElementById('main') 
);
  </script>
</body>