The Twitter Universe



 

The Twitter Universe project was a struggle to make come together. I delved into learning about a very complicated and hard to parse API, Twitter, without maybe having the technical ability to do so. It was a learning experience, but mostly it was frustrating. I went back and forth from Processing libraries Twitter4J, to Temboo, to trying to use Python to get the data, to trying to use a discontinued API Topsy, to inquiring with official partners of Twitter about getting the data I needed, back to Temboo. It turns out it was going to be extremely expensive to get the amount of tweets about a given trend, so I had to make do with what I had available.

 

What I did was use Temboo to get the top 10 trends for a selected city (Toronto, New York, Los Angeles, Sydney, London, and Vancouver are all options). Then, Temboo will find the most recent 200 tweets for each of those trends. Since that would make a boring data visualization, I added up all the words from the Tweets, and then crosschecked that with the original trends. It counted the top words used in the tweets that were in the trends, and I used those numbers to map the diameter of the planets.

 

I ordered the size of the planets as our Solar System currently exists (in order of greatest size: Jupiter, Saturn, Uranus, Neptune, Earth, Venus, Mars, Mercury). However, depending on the trends and tweets, the size of the planets really very. If something is blowing up on Twitter (i.e. Ferguson), Jupiter will be way bigger than the other planets. More likely, since it’s weighing the equal amount of tweets from the equal amount of trends, the planets are more balanced than in real life.

 

To contrast the amount of tweets about a given subject, I made the speed of rotation around the sun reflect the amount of followers. Based on the users who tweeted the given words reflected by the planets, the more followers the sum of the users have, the more quickly the planet will spin around the Sun. This demonstrates that depending on who tweets something, the word could spread more than more popular trends or tweets.

 

After a planet is selected, the sketch begins from a bird’s eye view of our solar system, and the user can highlight each planet and see what word that reflects in the ‘Twitter Universe’ and how many times it’s been mentioned in the selected tweets. But if you want to cruise through the Universe (mostly because it’s really neat), you can press ENTER and use the mouse to control going through the Solar System.

 

The most interesting element comes from comparing the trends in the different English speaking cities, and seeing how the planets respond to those different topics. Who’s talking about Ferguson? Who is talking about local events instead of worldwide issues? Unfortunately the biggest fault is the time it takes to load one of these ‘Twitter Universes’, by the time the API is used to get the top trends, all of those tweets, and then word counting and sorting them, it takes a long time. But the information is interesting.

 

It took so many hours to be able to get any Twitter data that I felt reflected what I was trying to show in my sketch, that I am proud of the outcome. I don’t think I’ll dedicate so much time trying to parse data through a very difficult API anytime soon, but it was a good experience that took me out of my ‘logic game’ comfort zone in ICM.

 

YouTube Preview Image

 

 

 

Here’s my source code:

//Despite my best intentions, Rubin and his code helped me while I was stuck in figuring out how to make
//this solar system work. Especially the math for the camera.
//So thank you very much to him.

//Music and sound effect
import ddf.minim.*;
Minim minim;
AudioPlayer player;
AudioPlayer player2;

//Drop down menu to select city
import controlP5.*;
ControlP5 cp5;
DropdownList d1;

//Arrays for the planets and for the stars
Planets [] aPlanet = new Planets [9];
PShape [] Planet = new PShape [9];
PImage [] PlanetTexture = new PImage [9];
int [] starX = new int [200];
int [] starY = new int [200];
int [] starZ = new int [200];

//Camera variables
float cameraZ=-19390;
float cameraX = mouseX;
float zoom;

//Twitter variables
String [] twitterInfo;
String [] twitterTrends;
String [] orderedTrends;
String [] twitterNumbers;
int [] tweetNums;
//Turn twitter numbers to good numbers to use for the sketch
float [] scaledNums = new float [8];
float [] scaledSpeed = new float [8];
boolean citySelected = false;
boolean cityHasChanged = false;
boolean searchTwitter = false;
boolean threeDimension = false;
int selectionCode=1;
String cityCode = “4118″;

void setup () {

size(int (displayWidth * 0.9), int (displayHeight * 0.9), P3D);
background (0);
noStroke ();
sphereDetail(40);

//Sun info
Planet[8] = createShape (SPHERE, 40);
PlanetTexture[8] = loadImage (“sun.png”);
Planet[8].setTexture (PlanetTexture[8]);
Planet[8].rotateX (1*HALF_PI);
aPlanet [8] = new Planets (0, 0.004, 0);

 

//Stars randomly in the 3D space
for (int i=0; i<starX.length; i++) {
starX[i] = int (random (-1500, 1500));
starY[i] = int (random (-1500, 1500));
starZ[i] = int (random (-1500, 1500));
}

minim = new Minim(this);
player = minim.loadFile(“space.mp3″);
player2 = minim.loadFile(“quickSFXedit.mp3″);
player.play();
player.loop();
}

 

void draw () {

if (!player2.isPlaying()) {
player2.rewind();
}

//If going through space, use the camera function
if (threeDimension) {
camera (1.3*width+cameraX, 1.17*height+zoom, cameraZ-0.7*zoom, 0.5*width, 0.5*height, 0, 0.2, -0.3, 1);
cameraZ += (-390+2*(mouseY – 0.5*height)-cameraZ)*0.1;
cameraX += (-390+2*(mouseX – 0.5*width)-cameraX)*0.1;
}

background(0);
lights ();
pointLight(255, 255, 255, width*0.5, height*0.5, 0);

//Show display if not going through space
if (!threeDimension) {
cp5 = new ControlP5(this);
// create a DropdownList
d1 = cp5.addDropdownList(“myList-d1″)
.setPosition(10, 24)
;
customize(d1); // customize the first list
}

stroke (255);
//Draw the stars
for (int i=0; i<starX.length; i++) {
point (starX[i], starY[i], starZ[i]);
}
noStroke ();

//Change city if selected
if (searchTwitter) {
cityChange ();
searchTwitter = false;
}

//Run the twitter functions to find trends and tweets
if (citySelected && cityHasChanged) {
// Run the Place Choreo function
runPlaceChoreo(cityCode);
// Run the Tweets Choreo function
runTweetsChoreo();
//Run Word Counting
runWordCounting ();
//Compare trends with words in tweets
findTrendsCount ();
for (int i=0; i<displayTrends.length; i++) {
if (trendCounts [i] == 0) {
trendCounts [i] = 4; //Make a small planet if there aren’t enough top words.
}
if (displayFollowers [i] == 0) {
displayFollowers [i] = sortedFollowers [0]/10;
}
scaledNums [i] = map (trendCounts [i], 0, trendCounts [0], 0, 28);
scaledSpeed [i] = map (displayFollowers [i], 0, sortedFollowers [0], 0.0001, 0.025);
}
planetSetup ();
cityHasChanged = false;
}

//Draw Saturns rings
if (citySelected) {
//Saturns rings
for (int i=1; i<50; i++) {
stroke(205, 205, 170, 170);
strokeWeight(1);
if (i>14 && i<24) {
stroke(170, 150, 110, 125);
} else if (i>38 && i<44) {
stroke(140, 140, 100, 155);
}
ellipse(aPlanet[1].ellipseX, aPlanet[1].ellipseY, scaledNums[1]+i+20, scaledNums[1]+i+20);
}

//Draw planets and move planets
for (int i = 0; i<aPlanet.length; i++) {
aPlanet[i].drawPlanet();
Planet[i].rotate(-1*aPlanet[i].planetRotate*HALF_PI, 0, 0, 1);
shape (Planet[i], aPlanet[i].ellipseX, aPlanet[i].ellipseY);
aPlanet[i].update ();
aPlanet[i].isMouseOverCircle ();

//See if the mouse is over planets in overhead view mode
if (!threeDimension) {
camera ();
textSize (14);
text (“Press enter to travel through the Twitter Universe”, 200, 20);
if (i<8) {
if (aPlanet[i].mouseOverCircle (aPlanet[i].ellipseX, aPlanet[i].ellipseY)) {
player2.play();
//camera();
textSize (18);
text (displayTrends[i] + “: ” + trendCounts[i], 10, height-20);
}
}
}
}
}
}

//Customizing button
void customize(DropdownList ddl) {
// a convenience function to customize a DropdownList
ddl.setBackgroundColor(color(190));
ddl.setItemHeight(20);
ddl.setBarHeight(15);
ddl.captionLabel().set(“Pick a city”);
ddl.captionLabel().style().marginTop = 3;
ddl.captionLabel().style().marginLeft = 3;
ddl.valueLabel().style().marginTop = 3;
ddl.addItem(“Toronto”, 1);
ddl.addItem(“New York”, 2);
ddl.addItem(“Los Angeles”, 3);
ddl.addItem(“London”, 4);
ddl.addItem(“Sydney”, 5);
ddl.addItem(“Vancouver”, 6);
ddl.setColorBackground(color(60));
ddl.setColorActive(color(255, 128));
}

//If something is selected in the button
void controlEvent(ControlEvent theEvent) {
// DropdownList is of type ControlGroup.

if (theEvent.isGroup()) {
// check if the Event was triggered from a ControlGroup
println(“event from group : “+theEvent.getGroup().getValue()+” from “+theEvent.getGroup());
selectionCode = int(theEvent.getGroup().getValue());
citySelected = true;
searchTwitter = true;
fill (255);
textSize (24);
text (“LOADING”, width-120, 20);
}
}

void keyPressed () {
if (key == ENTER) {
threeDimension = !threeDimension;
}
}

 

class Planets {
float ellipseX = 0;
float ellipseY = 0;
float planetMove=0;
float planetRotate;
float originalRotate;
float planetSpeed;
float originalSpeed;
int planetOrbit;

Planets (int tempOrbit, float tempRotate, float tempSpeed) {
planetOrbit = tempOrbit;
planetRotate = tempRotate;
planetSpeed = tempSpeed;
originalRotate = tempRotate;
originalSpeed = tempSpeed;
}

void drawPlanet () {
ellipseX = planetOrbit*sin(planetMove)+width*0.5;
ellipseY = planetOrbit*cos(planetMove)+height*0.5;
}

void update () {
planetMove=planetMove+planetSpeed;
}

void isMouseOverCircle () {
if (mouseOverCircle (ellipseX, ellipseY)) {
planetSpeed = 0;
planetRotate =0;
} else {
planetSpeed = originalSpeed;
planetRotate = originalRotate;
}
}

boolean mouseOverCircle(float x, float y) {
return (dist(mouseX, mouseY, x, y) < 50);
}
}

 

 

import com.temboo.Library.Twitter.Search.TweetsResultSet;
import com.temboo.Library.Twitter.Search.Tweets;
import com.temboo.core.*;
import com.temboo.Library.Twitter.Trends.*;
// Create a session using your Temboo account application details
TembooSession session = new TembooSession(“saxani”, “myFirstApp”, “ecc87ed5c3ed4a41a5e84c094e690129″);
JSONObject getTrendsJSON;
JSONObject [] getTweetsJSON = new JSONObject [10];
String rawTrends;
//String trendNames;
StringList trendList;
StringList tweetsList;
String [] tweetInfo = new String [10];
String allWords;
String [] allWordsArray;
IntDict concordance = new IntDict ();
String [] displayTrends = new String [8];
int [] trendCounts = new int [8];
int [] followerCount = new int [10];
int [] displayFollowers = new int [8];
int [] sortedFollowers = new int [8];

void runPlaceChoreo(String cityCode) {
// Create the Choreo object using your Temboo session
Place placeChoreo = new Place(session);

// Set inputs
placeChoreo.setAccessToken(“131780656-v989GjcmovCwlBwEPyWO15xS4aEtlcU9jWpnW8vq”);
placeChoreo.setID(cityCode);
placeChoreo.setAccessTokenSecret(“moSUoVttPn7EGkoAsRdLRNn8XmyaSb7XbVBdUxlqL7zPz”);
placeChoreo.setConsumerSecret(“acYNb8WbwAkZnvhIOXHoqUDm7hM9gz6Zn4c1GCWRiGuPnXihp0″);
placeChoreo.setConsumerKey(“tMbwxfG746da6ud1MAZCasEZR”);

// Run the Choreo and store the results
PlaceResultSet placeResults = placeChoreo.run();

// Print results
//println(placeResults.getResponse());
rawTrends = placeResults.getResponse();
//Chop off punctuation stopping it from going to JSON
int trendsBegin = rawTrends.indexOf (“[");
int trendsEnd = rawTrends.indexOf ("}]}]”);
String editedTrends = rawTrends.substring (trendsBegin+1, trendsEnd+3);
//Turn to JSON
getTrendsJSON = parseJSONObject(editedTrends);
trendList = new StringList();
for (int i = 0; i < 10; i++) {
String name = getTrendsJSON.getJSONArray(“trends”).getJSONObject(i).getString(“name”);
trendList.append(name.toLowerCase());
}
//println (trendList);
}

 

void runTweetsChoreo() {
// Create the Choreo object using your Temboo session
Tweets tweetsChoreo = new Tweets(session);

// Set inputs
for (int i = 0; i < 10; i ++) {
String tempString = trendList.get(i);
tweetsChoreo.setCount(“200″);
tweetsChoreo.setAccessToken(“131780656-v989GjcmovCwlBwEPyWO15xS4aEtlcU9jWpnW8vq”);
tweetsChoreo.setQuery(tempString);
tweetsChoreo.setAccessTokenSecret(“moSUoVttPn7EGkoAsRdLRNn8XmyaSb7XbVBdUxlqL7zPz”);
tweetsChoreo.setConsumerSecret(“acYNb8WbwAkZnvhIOXHoqUDm7hM9gz6Zn4c1GCWRiGuPnXihp0″);
tweetsChoreo.setConsumerKey(“tMbwxfG746da6ud1MAZCasEZR”);

// Run the Choreo and store the results
TweetsResultSet tweetsResults = tweetsChoreo.run();
tweetInfo [i] = tweetsResults.getResponse();
getTweetsJSON [i] = parseJSONObject(tweetInfo[i]);
}

int tweetsSize = getTweetsJSON[0].getJSONArray(“statuses”).size();
tweetsList = new StringList();

for (int i = 0; i < 10; i++) {
for (int j = 0; j<tweetsSize; j++) {
String tweet = getTweetsJSON[i].getJSONArray(“statuses”).getJSONObject(j).getString(“text”);
int followers = getTweetsJSON[i].getJSONArray(“statuses”).getJSONObject(j).getJSONObject(“user”).getInt(“followers_count”);
tweetsList.append(tweet);
allWords = allWords + ” ” + tweet;
followerCount [i] = followerCount [i] + followers;
}
}
allWordsArray = splitTokens (allWords, ” ,.:;!”);
}

void runWordCounting () {
for (int i=0; i<allWordsArray.length; i++) {
concordance.increment (allWordsArray[i].toLowerCase());
}
concordance.sortValuesReverse ();
//println (concordance);
}

void findTrendsCount () {
String [] keys = concordance.keyArray();
int topTrendsCount = 0;

for (int i = 0; i < keys.length; i++) {
int count = concordance.get (keys[i]);

//println (keys [i] + ” ” + count);

for (int j=0; j < 10; j++) {
String [] m1 = match (keys [i], trendList.get(j));
if (m1 !=null && topTrendsCount < 8) {
displayTrends [topTrendsCount] = keys [i];
trendCounts [topTrendsCount] = count;
displayFollowers [topTrendsCount] = followerCount [j];
println (displayTrends [topTrendsCount] + ” ” + trendCounts [topTrendsCount] + ” ” + displayFollowers [topTrendsCount]);
topTrendsCount ++;
}
}
}
sortedFollowers = displayFollowers;
sortedFollowers = sort (sortedFollowers);
sortedFollowers = reverse (sortedFollowers);
println (sortedFollowers);
}

void cityChange () {
if (selectionCode == 1) {
cityCode = “4118″; //Toronto
} else if (selectionCode == 2) {
cityCode = “2459115″; //New York
} else if (selectionCode == 3) {
cityCode = “2442047″; //LA
} else if (selectionCode == 4) {
cityCode = “44418″; //London
} else if (selectionCode == 5) {
cityCode = “1105779″; //Sydney
} else if (selectionCode == 6) {
cityCode = “9807″; //Vancouver
}
cityHasChanged = true;
}

 

 

void planetSetup () {

Planet[7] = createShape (SPHERE, scaledNums [7]);
PlanetTexture[7] = loadImage (“mercury.jpg”);
Planet[7].setTexture (PlanetTexture[7]);
Planet[7].rotateX (1*HALF_PI);
aPlanet [7] = new Planets (75, 0.015, scaledSpeed [7]);

Planet[5] = createShape (SPHERE, scaledNums [5]);
PlanetTexture[5] = loadImage (“venus.jpg”);
Planet[5].setTexture (PlanetTexture[5]);
Planet[5].rotateX (1*HALF_PI);
aPlanet [5] = new Planets (110, 0.015, scaledSpeed [5]);

Planet[4] = createShape (SPHERE, scaledNums [4]);
PlanetTexture[4] = loadImage (“earth.jpg”);
Planet[4].setTexture (PlanetTexture[4]);
Planet[4].rotateX (1*HALF_PI);
aPlanet [4] = new Planets (150, 0.015, scaledSpeed [4]);

Planet[6] = createShape (SPHERE, scaledNums [6]);
PlanetTexture[6] = loadImage (“mars.jpg”);
Planet[6].setTexture (PlanetTexture[6]);
Planet[6].rotateX (1*HALF_PI);
aPlanet [6] = new Planets (200, 0.015, scaledSpeed [6]);

Planet[0] = createShape (SPHERE, scaledNums [0]);
PlanetTexture[0] = loadImage (“jupiter.jpg”);
Planet[0].setTexture (PlanetTexture[0]);
Planet[0].rotateX (1*HALF_PI);
aPlanet [0] = new Planets (260, 0.015, scaledSpeed [0]);

Planet[1] = createShape (SPHERE, scaledNums [1]);
PlanetTexture[1] = loadImage (“saturn.jpg”);
Planet[1].setTexture (PlanetTexture[1]);
Planet[1].rotateX (1*HALF_PI);
aPlanet [1] = new Planets (340, 0.015, scaledSpeed [1]);

Planet[2] = createShape (SPHERE, scaledNums [2]);
PlanetTexture[2] = loadImage (“uranus.jpg”);
Planet[2].setTexture (PlanetTexture[2]);
Planet[2].rotateX (1*HALF_PI);
aPlanet [2] = new Planets (420, 0.015, scaledSpeed [2]);

Planet[3] = createShape (SPHERE, scaledNums [3]);
PlanetTexture[3] = loadImage (“neptune.jpg”);
Planet[3].setTexture (PlanetTexture[3]);
Planet[3].rotateX (1*HALF_PI);
aPlanet [3] = new Planets (500, 0.015, scaledSpeed [3]);
}

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>