PROJ 3: STUDENT DEFINED VISUALIZATION

glegrady
Posts: 203
Joined: Wed Sep 22, 2010 12:26 pm

PROJ 3: STUDENT DEFINED VISUALIZATION

Post by glegrady » Sat Dec 26, 2015 6:22 pm

PROJ 3: STUDENT DEFINED VISUALIZATION

Each student selects their data and the topic of what the final project will address. Software to be used is Processing. Please provide as with any relevant information pertaining to your project such as brief concept description, sketch, sequel query, data, screen shots, analysis and code.
George Legrady
legrady@mat.ucsb.edu

thomasahervey
Posts: 4
Joined: Wed Jan 06, 2016 12:50 pm

Visualizing SPL Book Adoptions (progress report)

Post by thomasahervey » Wed Mar 02, 2016 1:00 pm

Since I will not be able to present in class, this is a progress report of my final project. Once i am finished with the project, I will post a reply to this post with the completed documentation and code.

After tweaking my 3D visualization based on feedback, I have decided to focus on several main areas for improvement: user controls, intractability, alternate time representations, heavier statistical analysis and visualization. In conjunction with statistical work from another one of my classes, I have opted to include more variables (publication day of the year, first checkout day of the year, dewey classes & numbers).

The first two figures show preliminary regression exploration in an attempt to see which variables may be predictors of adoption (library and/or user), and checkout frequency. Figure 3 simply shows publication order plotted against adoption, showing clear stridation. Figure 2 shows publication day of the year plotted against first checkout day of the year (colored by total checkout). There Doesn't seem to be a any pattern beyond the clear 1-to-1 correlation of time (suggesting many items are checked out shortly after publication. Both of these need further exploration.

Figure 1 shows preliminary results of plotting publication data annually rather than by order. Once the previous statistical explorations are finished and integrated, this visualization should be able to clearly demonstrate the links between publications, adoptions and checkouts throughout the months of the year. Ultimately, these two main visualizations (with the linear one in the previous project portion) will help to demonstrate to the Seattle Public Library and patrons how the trends in book adoption and popularity have changed, allowing for future exploration into marketing and customer relationship trends.
Attachments
Screen Shot 2016-03-02 at 12.59.40 PM.png
Screen Shot 2016-03-02 at 12.45.16 PM.png
Screen Shot 2016-03-02 at 12.45.31 PM.png

qiu0717
Posts: 9
Joined: Wed Jan 06, 2016 1:44 pm

Re: PROJ 3: STUDENT DEFINED VISUALIZATION

Post by qiu0717 » Sun Mar 13, 2016 10:27 pm

Exposure & Light
In 24 hours, photographs shot around the world.


Weihao Qiu

When we are at the most precious, unforgettable, sweetest moments in our lives, what we mostly want to do is to freeze them, and keep them forever. Photography is a common way to do that.

For that reason, social networks like Flickr, Facebook and Instagram are full of photos people uploaded, which makes up extremely big dataset where we can explore countless interesting patterns.

Question
Personally, in these massive datasets, I am most curious about the patterns of two aspects.

1. In general, when, where and what people takes a photo?
This question actually reveals a general pattern of people's activities of photography.

2. Is there any way to see beyond the image's visible content?
I am curious about when, where a photo is taken, but I am more interested in how it is taken. Because EXIF records the shooting information including camera settings in details, exploring EXIF could be a way to dig deeper to a image, thereby inferring the invisible background information from visible content and data.

Data
Considering Flickr is currently one of the most popular social network in photography field, I utilized its API to get shot time, geo information, EXIF of each photo in a photoset, and even download its thumbnail and compute its average color to understand its actual content. In addition, the photoset consists of all photos shot, which is up to 80k images, in 24 hours of some day(2015-07-17).


Tips: What is EXIF?
EXIF is built in any JPG, RAW image file. It records the camera settings when a photo is shot. With that information, Exposure Value can be calculated.

What is Exposure Value (EV) ? How can I compute EV?

With automatic photometric system, people can take accurately exposed (similar to what you look) pictures without any knowledge of Aperture(f-number), Shutter speed or ISO, because that system built in the camera computes those settings for us automatically. However, before this automatic system was invented, certain rules, like using longer exposure time when in dark environment, must be followed to get the photo accurately exposed. Otherwise, overexposure or underexposure will make the photo looks much brighter or darker than what you see from your eyes.

"In photography, exposure value (EV) is a number that represents a combination of a camera's shutter speed and f-number, such that all combinations that yield the same exposure have the same EV value (for any fixed scene luminance)." -- Wikipedia
"The EV concept was developed in an attempt to simplify choosing among combinations of equivalent camera settings" -- Wikipediahttps://en.wikipedia.org/wiki/Exposure_value

Lighting Condition Chart.png
Lighting Condition Chart
Traditionally, the first step of taking a accurately exposed photo is to measure the luminance of shooting environment by exposure meter, or estimate by the chart above when the meter is not available. Notice, the chart is measured based on ISO 100.
EV chart.png
Exposure Value Chart
The next step is to find the row, whose EV is equal to the measured EV, from the chart above and choose a combination of camera settings at that row.
Finally, shoot with that setting.


In short, the brighter the shooting environment is, a higher measured EV the exposure meter may give, less amount of light should come into lens for accurate exposure, which means using shorter Exposure Time or larger F-number.

Given that most photos are automatically accurately exposed, except for people intentionally using exposure compensation, the subjective camera settings of a photo can reveals the objective lighting condition when it was shot. Studying those hidden information enables us to infer the intensity of sunlight, the time in the day and even the weather when the photo is shot.

EV original equation.png
Original equation for computing exposure value
EV original equation.png (2.73 KiB) Viewed 18171 times
Exposure Value Equation.png
Equation of computation of Exposure Value (EV)
The first equation is the one used to calculate that EV chart. Because it doesn't take ISO into consideration, I modify it into the second equation. Based on the assumption of most photos using settings that reach the exact measured EV of the shooting scene, I calculate each photo's EV with second equation by its ISO, Exposure time and F-number, which I collected from Flickr.


To make convenience for visualization, I didn't store the JSON file locally, but organized those information into a table. Reference: Flickr APIhttps://www.flickr.com/services/api/

Step 1. Due to some limitation of Flickr, I store images information by half hour of shot time.

Code: Select all

StringList photoID;
Table table;
String api_key;
void setup() {
    size(100,100,P2D);
  api_key="ed88994bbcb534c7c994f858de1f7d75";

  photoID = new StringList();
  table = new Table();
  table.addColumn("id");
  table.addColumn("Time");
  table.addColumn("ExposureTime");
  table.addColumn("Aperture");
  table.addColumn("ISO");
  table.addColumn("FocalLength");
  table.addColumn("Lon");
  table.addColumn("Lat");
  float[] bbox = new float[4];
  bbox[0] = -180;
  bbox[1] = -90;
  bbox[2] = 180;
  bbox[3] = 90;

  String date = "2015-07-17";

  for (int t = 0; t<48; t++) {
    table = new Table();
    table.addColumn("id");
    table.addColumn("Time");
    table.addColumn("ExposureTime");
    table.addColumn("Aperture");
    table.addColumn("ISO");
    table.addColumn("FocalLength");
    table.addColumn("Lon");
    table.addColumn("Lat");
    table.addColumn("Red");
    table.addColumn("Green");
    table.addColumn("Blue");

    int page = 1;
    int pages = 1;

    String JSON_url = getJSONUrl(date, t, bbox, page);
    //println(JSON_url);


    JSONObject jsobj = loadJSONObject(JSON_url);
    if (jsobj.getString("stat").equals("ok")) {
      pages = jsobj.getJSONObject("photos").getInt("pages");
    }
    println(pages);
    for (int i =1; i<=pages; i++) {
      JSON_url = getJSONUrl(date, t, bbox, i);
      //println(JSON_url);

      jsobj = loadJSONObject(JSON_url);
      if (jsobj.getString("stat").equals("ok")) {
        JSONArray photoArray = jsobj.getJSONObject("photos").getJSONArray("photo");
        for (int j = 0; j < photoArray.size(); j ++) {
          photoID.append(photoArray.getJSONObject(j).getString("id"));
          String onePhotoID = photoArray.getJSONObject(j).getString("id");


          String exposure="";
          String fNumber="";
          String iso="";
          String focalLength="";
          String shootDate = "";
          int shootTime = 0;
          float longitude = 0;
          float latitude = 0;
          float red = 0 ;
          float green = 0;
          float blue = 0;

          String url_exif = " https://api.flickr.com/services/rest/?method=flickr.photos.getExif"
            + "&api_key=" + api_key
            + "&photo_id=" + onePhotoID
            + "&format=json&nojsoncallback=1";
          JSONObject jsobj2 = loadJSONObject(url_exif);
          //saveJSONObject(jsobj2, "data/"+ onePhotoID+".json");
          if (!jsobj2.isNull("photo")) { 
            JSONArray tempArray = jsobj2.getJSONObject("photo").getJSONArray("exif");
            //println(tempJSON2.getJSONObject("photo").getString("id"));



            for (int k = 0; k<tempArray.size(); k++) {
              JSONObject oneExifLine = tempArray.getJSONObject(k);
              String tag = oneExifLine.getString("tag");
              //println(label);
              if (tag.equals("ExposureTime")) {
                exposure = oneExifLine.getJSONObject("raw").getString("_content");
                //hm.put(photoId, "ExposureTime"+exposure);
              }
              if (tag.equals("FNumber")) {
                fNumber = oneExifLine.getJSONObject("raw").getString("_content");
                //hm.put(photoId, "FNumber"+exposure);
              }
              if (tag.equals("ISO")) {
                iso= oneExifLine.getJSONObject("raw").getString("_content");
                //hm.put(photoId, "ISO"+exposure);
              }
              if (tag.equals("FocalLength")) {
                focalLength = oneExifLine.getJSONObject("raw").getString("_content");
                //hm.put(photoId, "FocalLength"+exposure);
              }
              if (tag.equals("DateTimeOriginal")) {
                shootDate = oneExifLine.getJSONObject("raw").getString("_content");
                int hour=0;
                int minute=0;
                int second=0;
                if (shootDate.length() > 1) {
                  hour = parseInt(shootDate.substring(11, 13));
                  minute = parseInt(shootDate.substring(14, 16));


                  shootTime = 3600*hour+60*minute+second;
                  //println(" hour: "+str(hour)
                  //+" minute: "+str(minute)
                  //+" second: "+str(second)
                  //+" time: "+str(dataMatrix[i][0]));
                }
                //hm.put(photoId, "FocalLength"+exposure);
              }
            }
          }


          String url_geo ="https://api.flickr.com/services/rest/?method=flickr.photos.geo.getLocation"
            + "&api_key="+ api_key
            + "&photo_id=" + onePhotoID
            + "&format=json&nojsoncallback=1";
          JSONObject jsobj3 = loadJSONObject(url_geo);
          //saveJSONObject(jsobj3, "data/geo_"+ onePhotoID+".json");
          if (jsobj3.getString("stat").equals("ok")) {
            //println(onePhotoID);
            longitude = jsobj3.getJSONObject("photo").getJSONObject("location").getFloat("longitude");
            latitude = jsobj3.getJSONObject("photo").getJSONObject("location").getFloat("latitude");
          }

          String url_size = "https://api.flickr.com/services/rest/?method=flickr.photos.getSizes"
            + "&api_key=" + api_key
            + "&photo_id=" + onePhotoID
            + "&format=json&nojsoncallback=1";
          JSONObject jsobj4 = loadJSONObject(url_size);
          if (jsobj4.getString("stat").equals("ok")) {
            JSONArray jsa = jsobj4.getJSONObject("sizes").getJSONArray("size");
            for (int ss=0; ss < jsa.size(); ss++) {
              JSONObject ssJSO =  jsa.getJSONObject(ss);
              if (ssJSO.getString("label").equals("Thumbnail")) {
                String url_img_raw = ssJSO.getString("source");
                String[] url_img_parts = splitTokens(url_img_raw, "\\");
                String url_img = join(url_img_parts, "");
                //println(url_img);
                float [] rgb = averageColor(url_img, "data/img/"+date+"/"+str(t)+"/"+onePhotoID+".jpg");
                red = rgb[0];
                green = rgb[1];
                blue = rgb[2];
                //println(rgb);
              }
            }
          }

          TableRow row = table.addRow();
          row.setString("id", onePhotoID);
          row.setInt("Time", shootTime);
          row.setString("ExposureTime", exposure);
          row.setString("Aperture", fNumber);
          row.setString("ISO", iso);
          row.setString("FocalLength", focalLength);
          row.setFloat("Lon", longitude);
          row.setFloat("Lat", latitude);
          row.setFloat("Red", red);
          row.setFloat("Green", green);
          row.setFloat("Blue", blue);
        }
      }
    }

    saveTable(table, "data/"+date+"_"+str(t)+"_withColor.csv");

    println(photoID.size());
    //saveJSONObject(jsobj,"data/"+startDate+"_"+str(page)+".json");
  }
}


String getJSONUrl(String date, int t, float[] bbox, int page) {
  String JSON_url = "https://api.flickr.com/services/rest/?method=flickr.photos.search"
    + "&api_key=" + api_key
    + "&min_taken_date=" + date + "+" + nf(t/2, 2) + "%3A" + nf(t%2*30, 2) + "%3A00"
    + "&max_taken_date=" + date + "+" + nf((t+1)/2, 2) + "%3A" + nf((t+1)%2*30, 2) + "%3A00"
    + "&sort=date-taken-asc"
    + "&bbox=" + str(bbox[0]) + "%2C+" + str(bbox[1])  + "%2C+"+ str(bbox[2]) + "%2C+" + str(bbox[3])   
    + "&accuracy=11&has_geo=1"
    + "&page=" + str(page)
    + "&format=json&nojsoncallback=1";

  println(JSON_url);
  return JSON_url;
}

float[] averageColor(String img_url, String output_url) {
  PImage img = loadImage(img_url);
  int imgWidth = img.width;
  int imgHeight = img.height;
  //PGraphics pg = createGraphics(imgWidth, imgHeight, P2D, output_url);
  //pg.size(imgWidth,imgHeight);
  saveStream(output_url,img_url);
  //pg.beginDraw();
  //pg.image(img, 0, 0);
  //pg.endDraw();
  //pg.save("11.png");
  float redSum = 0 ;
  float greenSum = 0;
  float blueSum = 0;
  for (int i = 0; i<imgWidth; i++) {
    for (int j = 0; j<imgHeight; j++) {
      color c = img.get(i, j);
      redSum += red(c);
      greenSum += green(c);
      blueSum += blue(c);
    }
  }
  float[] rgb = new float[3];
  rgb[0] = redSum/(imgWidth*imgHeight);
  rgb[1] = greenSum/(imgWidth*imgHeight);
  rgb[2] = blueSum/(imgWidth*imgHeight);
  return rgb;
}
data1.png
Image information by half hour
data2.png
Image information by half hour - details
data3.png
Thumbnails by half hour named with ID

Step 2. Connect those pieces of data of half hours into data of a whole day

Code: Select all

Table[] dataTable;
Table outputTable;
Table inputTable;
void setup() {

  outputTable = new Table();
  outputTable.addColumn("id");
  outputTable.addColumn("Time");
  outputTable.addColumn("ExposureTime");
  outputTable.addColumn("Aperture");
  outputTable.addColumn("ISO");
  outputTable.addColumn("FocalLength");
  outputTable.addColumn("Lon");
  outputTable.addColumn("Lat");
  outputTable.addColumn("Red");
  outputTable.addColumn("Green");
  outputTable.addColumn("Blue");
  outputTable.addColumn("ImgDic");


  for (int i = 24; i<48; i++) {
    inputTable = loadTable("2015-07-16_"+str(i)+"_withColor.csv", "header");
    for (int j = 0; j<inputTable.getRowCount(); j++) {
      if (inputTable.getRow(j).getFloat(1)!=0) {
        TableRow temprow = outputTable.addRow(inputTable.getRow(j));
        temprow.setInt("ImgDic", 16*100+i);
      }
    }
  }
  for (int i = 0; i<48; i++) {
    inputTable = loadTable("2015-07-17_"+str(i)+"_withColor.csv", "header");
    for (int j = 0; j<inputTable.getRowCount(); j++) {
      if (inputTable.getRow(j).getFloat(1)!=0) {
        TableRow temprow = outputTable.addRow(inputTable.getRow(j));
        temprow.setInt("ImgDic", 17*100+i);
      }
    }
  }
  for (int i = 0; i<24; i++) {
    inputTable = loadTable("2015-07-18_"+str(i)+"_withColor.csv", "header");
    for (int j = 0; j<inputTable.getRowCount(); j++) {
      if (inputTable.getRow(j).getFloat(1)!=0) {
        TableRow temprow = outputTable.addRow(inputTable.getRow(j));
        temprow.setInt("ImgDic", 18*100+i);
      }
    }
  }
  saveTable(outputTable, "tableNew.csv");
}
This code connects data of each half hour and clear out those meaningless photos.
data4.png
Image information after connecting and filtering

Sketches

[The extension tiff has been deactivated and can no longer be displayed.]

I was inspired by this kind of effect. I am thinking to make each photo a low-transparency color shape, which could be ellipse, rectangle or polygon, and make photos overlap together but not completely covered by others.


20160314-3.pic_hd.jpg
Sketch 1
This sketch mainly demonstrates how to decide each cells color and shape. The idea is to map the Exposure Value(EV) into colors, ranging from blue to red and then to yellow as EV increases. Moreover, whether the shape is landscape-direction or portrait-direction is decided by focal length.


20160314-1.pic_hd.jpg
Sketch 2
This sketch manly shows the layout of the foot area, which is the user interface. I arrange the controllers mainly at the left area and the title texts right area.


Result
https://vimeo.com/159016833
1.png
Overview
The project is changing over time. As it runs, the universal time changes from 00:00 to 24:00. At any moment, the photographs with shot time within a range around that moment will show up as a cell. The size is becoming larger as the "program time" gets closer to the shoot time. The size increases to reach a peak, and then becomes smaller until the cell disappears.

Color:
There are three view modes.
Mode 1:
Color represents the exposure value calculated from camera settings. It ranges from blue to red, and then to yellow as EV increases.
Mode 2:
Color represents the exposure value too. It ranges from the color before sunrise to the color after sunrise, which can be seen from the thin time bar at the top of the user interface. This color reveals its shoot time in a day.
Mode 3:
Color is determined by the average color of the actual image. This color reveals the actual content of the image.
Switching from different modes, user can compare EV of each photo with its shoot time and its actual content to explore the potential connections.
6.png
Mapping the EV to time of a day
7.png
Set the color to be the actual average color of the image
Shape:
2 shapes of those cells are provided.
1. Blurred rectangle:
As shown in the sketch 2, the width-height ration of each rectangle cell is decided by focal length. Images with focal length less than 50 mm is presented as 1:1 ration, i.e. square; images with less than 50 mm will be presented as landscape-direction rectangles; images with more than 50 mm will be presented as portrait-direction rectangles.
2. Ellipse:
Compared to rectangle, ellipse puts viewer further from what they are looking at, photographs. However, ellipse make each shooting seem like a happening event. By zooming the ellipse, each photograph attracts viewer's eyesight.
2.png
Ellipse shape
Setting Control
Alpha:
Control each cell's color transparency.
Hour Range:
Within how much time around the local time,
Show Period:
The duration of showing a image, from appearance to disappearance .
Speed:
How fast the time goes.
Zoom Ratio:
Zoom all cells together.
Background:
Switch on or off the background of map.

Mouse Interaction
When point at a cell, the actual thumbnail of cell's image will show up around the mouse pointer.
4.png
Mouse Interaction: Point to show image
Some interesting screenshot
31.png
Background Off 1
32.png
Background off 2
5.png
Different Zoom Ratio
Last edited by qiu0717 on Tue Mar 15, 2016 1:49 am, edited 20 times in total.

theuniqueeye
Posts: 4
Joined: Wed Jan 06, 2016 12:51 pm

Re: PROJ 3: STUDENT DEFINED VISUALIZATION

Post by theuniqueeye » Mon Mar 14, 2016 1:59 pm

/*
I like the poetic nature of the sentence “Life is Elsewhere,” first created by Rimbaud then passed down to Milan Kundera. Besides, I was thinking about myself, the situation of moving away from the homeland and living in somewhere else.
*/


Life is Elsewhere :: Jing Yan

Question
I would like to explore something, some influence, relationship, or maybe interaction between peoples’ temporal changes and their social contact.

Concept
In this project, the “elsewhere” represents the remote areas, the geolocation changes. It represents a specific point of time, that “frozen moment” you keep as photos, some retainable matters in the digital world. It also represents a reflection of yourself in someone else’s eyes, the trace of your existence in the social world.

Data
Due to the concept, the Instagram API is a good fit for this project.

The data mainly contains two parts:
users’ temporal information – location and date
users’ social contact – comment and like


Actually, I use the comment information of users’ media post instead of the like information, because the comment data has more detailed information, such as the comment created time and the comment text. Also, since the “like” function is overly used, the comment data might be more accurate about social contacts.

Common name >> User ID >> Recent Media’s Location >> Filter the User ID >> Recent Media Data

Step1. I searched 30 common names and save them as csv file. Step2. With those names, I get about 30*50=1500 users’ ID. Step3. With all the user IDs, I get the location data from their recent media, and filter them by the number of their valid locations. That means if the user has more than 5 null locations, I just skip him/her. And as I would like to limit my geo-space to the united states, I set a threshold with longitude and latitude to filter all the users. Step4. With those “valid” users’ ID, I get the recent medias from each of them. And in each recent post media, I get the media’s location, date, comment from who (ID and username), comment time, comment text, and calculate the text length, then store them into a csv table.

Here is part of the csv table.
csv.png
processing code for data collector

Code: Select all

/* 2016-3-5 
 Data Visualization Final (Processing 3)
 
 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
 ::::::: L I F E  I S  E L S E W H E R E ::::::::::::::::::::::::::
 ::::::::::::::::::::::::::::::::::::::::::: code: Jing Yan :::::::
 ::::::::::::::::::: theuniqueeye@gmail.com :::::::::::::::::::::::
 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
 ::::::: [Part1: Data Collector] ::::::::::::::::::::::::::::::::*/

// 2.0 version update: 
// collect more information about a single media, add the comment time and comment text, comment out the social contact's profile picture
// build a parse date function
// filter the user by their location data, only collect the user with full location record >> collect the user whose first location within US 

import java.util.Date; // library for computing json date 

// url elements
String user_url = "https://api.instagram.com/v1/users/";
String media_recent = "/media/recent/";
String access_token = "access_token=1472326062.1677ed0.462dba68da62463486e191047523c6ce";
String json_url;

Table commonNames;
String commonName;
String user_ID;
int numUsers=0;
JSONArray data_id;
JSONObject userInfo;

String mediaTime, mediaFilter, userName, mediaID;
String[] media_date = new String[3];
String[] comment_date = new String[3];
JSONArray data_media, mediaComment, mediaLike;
JSONObject recentMedia, mediaLocation;
String commentFrom_userName, commentFrom_id, commentFrom_pic, comment_time, comment_text;
String likeFrom_userName, likeFrom_id, likeFrom_pic;
float[] IdLatLong = new float[3];
int null_location, count_comment, count_like;
Table dataCollect_comment, dataCollect_like, dataCollect_count;
TableRow newRowc, newRowl, newRow;
int countUser, countMedia, countComment,text_length;


// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

void setup() {
  countUser=0;
  countMedia=0;
  countComment=0;

  // &&&&&&&&&&&&&&&&&&   build tables to collect data   &&&&&&&&&&&&&&&&&&&&
  // comment table
  dataCollect_comment = new Table();

  dataCollect_comment.addColumn("userID");
  dataCollect_comment.addColumn("userName");
  dataCollect_comment.addColumn("mediaID");
  dataCollect_comment.addColumn("mediaDate");
  dataCollect_comment.addColumn("mediaLocationLat");
  dataCollect_comment.addColumn("mediaLocationLong");
  dataCollect_comment.addColumn("mediaCommentFrom_id");
  dataCollect_comment.addColumn("mediaCommentFrom_userName");
  dataCollect_comment.addColumn("mediaComment_time");
  dataCollect_comment.addColumn("text_length");
  dataCollect_comment.addColumn("mediaComment_text");
 

  // like table
  dataCollect_like = new Table();

  dataCollect_like.addColumn("userID");
  dataCollect_like.addColumn("userName");
  dataCollect_like.addColumn("mediaID");
  dataCollect_like.addColumn("mediaDate");
  //dataCollect_like.addColumn("mediaLocationID");
  dataCollect_like.addColumn("mediaLocationLat");
  dataCollect_like.addColumn("mediaLocationLong");
  dataCollect_like.addColumn("mediaLikeFrom_id");
  dataCollect_like.addColumn("mediaLikeFrom_userName");

  // count table
  dataCollect_count = new Table();
  dataCollect_count.addColumn("numUsers");
  dataCollect_count.addColumn("numMedias");
  dataCollect_count.addColumn("numComments");

  // &&&&&&&&&&&&&&&&&&   load common name from csv file   &&&&&&&&&&&&&&&&&&&&&&&&&&&&
  commonNames = loadTable("testName.csv"); // [test]
  //commonNames = loadTable("commonName_199.csv"); // [199 names]
  int numNames = commonNames.getRowCount();
  println("[total number of names: " + commonNames.getRowCount()+"]" ); 

  // &&&&&&&&&&&&&&&&&&   use common name to search for user_ID   &&&&&&&&&&&&&&&&&&&&&
  for (int i=0; i<numNames; i++) {
    commonName = commonNames.getString(i, 0);
    json_url = user_url+"search?q="+commonName+"&"+access_token;

    try { // try :: if the name is related to a real user_ID (user exists)

      userInfo = loadJSONObject(json_url); /// ********Prob1: NullPointerException *********
      saveJSONObject(userInfo, "data/" + "userInfo_" + commonName + ".json"); ////

      data_id = userInfo.getJSONArray("data");
      println("data_id.length() = "+data_id.size());
      //println("data_id: " + data_id.size());
    }
    catch(Exception io) {
      println("user not exists");
    }

    // &&&&&&&&&&&&&&&&&&   use user_ID to search for recent media   &&&&&&&&&&&&&&&&&&

    for (int j=0; j<data_id.size(); j++) {

      user_ID = data_id.getJSONObject(j).getString("id");  
      userName = data_id.getJSONObject(j).getString("username"); 

      json_url = user_url+user_ID+media_recent+"?"+access_token;
      //println("User num = "+j);
      // try :: if the recent media link is allowed by user (not private account)
      try {
        countUser = data_id.size();
        recentMedia = loadJSONObject(json_url);
        //numUsers++;
        //saveJSONObject(recentMedia, "data/" + "recentMedia_" + user_ID + ".json"); ////
        data_media = recentMedia.getJSONArray("data");

        ///////////////////////////////////
        // count location: filter the user

        null_location = 0; 
        for (int k=0; k<data_media.size(); k++) { 
          if (data_media.getJSONObject(k).isNull("location") == true) {
            //println("No location");
            null_location++;
          }
        }
        //println("count_nulllocation: " + null_location);

        if (null_location<=5) {
          numUsers++;
          println("found, user = "+userName);

          // &&&&&&&&&&&&&&&&&&   loop each recent post from all recent medias   &&&&&&&&&&&&&&&&&& 
          // &&&&&&&&&&&&&&&&&&  [get data of time,location,filter,comment,like] &&&&&&&&&&&&&&&&&& 
          countMedia = data_media.size();
          for (int k=0; k<data_media.size(); k++) { 

            // get media's [location]: location_ID, lattitude, longtitude
            if (data_media.getJSONObject(k).isNull("location") == true) {
              println("No location");
              IdLatLong[0] = 0;
              IdLatLong[1] = 0;
              IdLatLong[2] = 0;
            } else {
              mediaLocation = data_media.getJSONObject(k).getJSONObject("location");
              //IdLatLong[0] = mediaLocation.getFloat("id");
              IdLatLong[1] = mediaLocation.getFloat("latitude");
              IdLatLong[2] = mediaLocation.getFloat("longitude");
              //println("location= " +IdLatLong[0]+ " " +IdLatLong[1]+ " " +IdLatLong[2]);
            }

            // determine if the user locates within the us
            boolean inUS = (IdLatLong[1]>25.18&&IdLatLong[1]<49)&&(IdLatLong[2]>-124.69&&IdLatLong[2]<-66.97);
            println("inUS = "+inUS);
            if (inUS) {

              // get media's [id]
              mediaID = data_media.getJSONObject(k).getString("id");

              // get media's [created_time]
              mediaTime = data_media.getJSONObject(k).getString("created_time");
              media_date = dateParse(mediaTime);

              // get media's [filter]
              //mediaFilter = data_media.getJSONObject(k).getString("filter");

              // get media's [comment info]: userName, id, profile_pic (who made comment),comment created time, comment text
              mediaComment = data_media.getJSONObject(k).getJSONObject("comments").getJSONArray("data");
              countComment = mediaComment.size();
              for (int l=0; l<mediaComment.size(); l++) { // loop each comment of a post
                newRowc = dataCollect_comment.addRow();
                comment_time = mediaComment.getJSONObject(l).getString("created_time");
                comment_date = dateParse(comment_time);
                comment_text = mediaComment.getJSONObject(l).getString("text");
                text_length = comment_text.length();
                commentFrom_userName = mediaComment.getJSONObject(l).getJSONObject("from").getString("username"); 
                commentFrom_id = mediaComment.getJSONObject(l).getJSONObject("from").getString("id"); 
                //
                newRow = dataCollect_count.addRow();
                newRow.setInt("numComments", countMedia);
                newRow.setInt("numMedias", countUser);
                newRow.setInt("numUsers", numUsers);
                
                //
                newRowc.setString("mediaComment_time", comment_date[0]+"-"+comment_date[1]+"-"+comment_date[2]); // [add to table]
                newRowc.setInt("text_length", text_length); // [add to table]
                newRowc.setString("mediaComment_text", comment_text); // [add to table]
                
                newRowc.setString("mediaCommentFrom_userName", commentFrom_userName); // [add to table]
                newRowc.setString("mediaCommentFrom_id", commentFrom_id); // [add to table]
                //
                newRowc.setString("mediaID", mediaID);
                //println("MID = "+mediaID);
                newRowc.setString("mediaDate", media_date[0]+"-"+media_date[1]+"-"+media_date[2]); // [add to table]
                newRowc.setFloat("mediaLocationLat", IdLatLong[1]); // [add to table]
                newRowc.setFloat("mediaLocationLong", IdLatLong[2]); // [add to table]
                newRowc.setString("userID", user_ID); // [add to table]
                newRowc.setString("userName", userName); // [add to table]
              }

              // get media's like info: userName, id, profile_pic (who like)
              mediaLike = data_media.getJSONObject(k).getJSONObject("likes").getJSONArray("data");

              for (int l=0; l<mediaLike.size(); l++) { // loop each like of a post
                //println("user_ID: " + user_ID+"  info = "+mediaLike.getJSONObject(l).toString());

                likeFrom_userName = mediaLike.getJSONObject(l).getString("username"); 
                likeFrom_id = mediaLike.getJSONObject(l).getString("id"); 
                likeFrom_pic = mediaLike.getJSONObject(l).getString("profile_picture");
                count_like ++;
                newRowl = dataCollect_like.addRow();
                newRowl.setString("mediaLikeFrom_userName", likeFrom_userName); // [add to table]
                newRowl.setString("mediaLikeFrom_id", likeFrom_id); // [add to table]
                //
                newRowl.setString("mediaID", mediaID); // [add to table]
                newRowl.setString("mediaDate", media_date[0]+"-"+media_date[1]+"-"+media_date[2]); // [add to table]
                //newRowl.setFloat("mediaLocationID", IdLatLong[0]); // [add to table]
                newRowl.setFloat("mediaLocationLat", IdLatLong[1]); // [add to table]
                newRowl.setFloat("mediaLocationLong", IdLatLong[2]); // [add to table]
                newRowl.setString("userID", user_ID); // [add to table]
                newRowl.setString("userName", userName); // [add to table]
              }
            }
          }
        }
      }

      catch(Exception io) {
        //println("private user");
      }
    }
  }
  saveTable(dataCollect_count, "dataCollect_count.csv");
  saveTable(dataCollect_comment, "dataCollect_comment.csv");
  saveTable(dataCollect_like, "dataCollect_like.csv");
  println("numUsers = "+numUsers); // count users
}
Model
space-time-path.png
I am using the 3D space time cube to present data of temporal changes. The horizontal ground represents the geo-location, and the vertical axis represent the time line.

Visualization
SKETCH.JPG
FirstViewWithTitle.png
generalView.png
path.png
path_.png
The general view shows the trajectory of users in the space-time cube. The bottom plane located as the area of US. And the vertical axis represents the timeline from 2015.12.01 to 2016.3.14. (The time is limited due to the limitation of access tocken from the Instagram API. Only 20 recent media post can be got from each user. This might be improved if you get a access tocken with higher permission.)
Each box represents one image which was posted at that certain location and time, and I use this data to represent the movement of that user.
pathWithData_.png
pathWithData.png
Upon the space-time cube model, I combine it with a metaphor of galaxy to present the social contact of each user in a specific space-time location.
Each small cubes rotating around the large cube represents one comment of that photo the user has posted. The number of comments is mapped to the number of boxed. (If possible, the idea of boxes could be improved as pieces of pixels from photos in order to be more consistent to the image based API.) The radius of the orbit represents the time difference between the comment time and photo post time, which means the small comment box will be far away from the large box if the comment is made g ago from the photo post time. The speed of that rotating comment box is mapped to the text length of that comment.
In this way, I trying to show the social contact of that particular user in a specific time and place.

Level of Perspective
In order to provide the whole dataset with a better organization, I design three levels of perspective: the user group, the medias of each user, and the comment of each media.

There is a selection mode, which switches on and off automatically depends on the distance of camera. (Though you can still manually turn on/off that mode with key.)

User group: When you are far away, the selection mode turns off. All the lines are equally draw which provides you with a feeling of the general shape and motions. You can also click the box “rotate” to get a thorough view.
Medias: When you get closer, the selection mode will turn on. All the lines and boxes of other users will fade out; except the user you pick up with your mouse will be illuminated with more bunch of lines. This allows you to get a clearer view when examine the detail of data.
Comments: You can select one user to show the path. Also you can select one media to show the social contact. When a media is selected, the motion of it’s related comments will be exaggerated which allows you to examine the difference between each comment.

Label/Map
mapAndLabelOn.png
generalViewWithLable.png
A transparent dots of map is added to the visualization as label so that the user will be able to examine specific data better. And in order to enhance that function, I link it with a red line and project a red shadow on the ground each time you choose one media box. However, since data visualization is somehow emerging out of the organization data, I offer an opportunity for the user to switch off the map and the link as switch off labels.

Color Mode
colorModeWithData.png
colorMode.png
The color represents the general heat of social contact which combine all the elements from the comment data.

Style
I meant to create a lively world even with some randomness. However, it turned out to be full of mechanical sensitivity. It reminds me of the inner structure of a clock, the hidden part of an elevator, or those fully organized sections of artifacts.
Yet, in contrast to the ordered data, the tiny motion of social contact adds some aliveness and even absurdity to the whole scene. When gazing at the data visualization, it weirdly provides me with an omniscient point of view: the busy and commonplace lives in this well structured world.

Processing code for visualization

Code: Select all

/* 2016-3-9 
 Data Visualization Final (Processing 3)
 
 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
 ::::::: L I F E  I S  E L S E W H E R E ::::::::::::::::::::::::::
 ::::::::::::::::::::::::::::::::::::::::::: code: Jing Yan :::::::
 ::::::::::::::::::: theuniqueeye@gmail.com :::::::::::::::::::::::
 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
 ::::::: [Part3: Visualization] ::::::::::::::::::::::::::::::::::*/


import peasy.*;
PeasyCam cam;
import peasy.org.apache.commons.math.*;
import peasy.org.apache.commons.math.geometry.*;

import controlP5.*;
ControlP5 cp5;
CheckBox checkbox;

String startDate = "2015/12/01";
String endDate = "2016/03/14";
int totalDays;

// Data
Table socialData;
int numSocials, count_user, count_media;
ArrayList<Social> socials = new ArrayList<Social>();
String user_id, user_name, media_id, date, social_id, social_name;
float location_long, location_lat, heat;
int count_comment, count_like, date_index, comment_date_index;
int comment_length, maximum_length=565;
String comment_date, comment_text;
IntList media_comments = new IntList(); 
IntList numUsers = new IntList(); 
IntList user_medias= new IntList();

// Draw
PImage map;
PShape test, title;
float cubeWidth, cubeHeight, cubeDepth;
FloatList location_x = new FloatList(); 
FloatList location_y = new FloatList(); 
FloatList location_z = new FloatList(); 
FloatList radius = new FloatList();
FloatList velocity = new FloatList();
IntList heatColor = new IntList();
float positionX, positionY;

// Interaction
float mouseObjectDistance;
String mouseDate, mousePlace, mouseUser_id="", mouseUser_name;
int lineColor;
boolean selectMode=true, showMap=false, rotateY=false, showTitle=true, colorMode=false;

// Font
PFont titleFont, titleFont2, labelFont, labelFont2, footnoteFont;

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

void setup() {
  //fullScreen(P3D, SPAN);
  size(1080, 720, P3D);
  cam = new PeasyCam(this, 780); // 780 // 1130

  /* try to rotate the camera a little bit
   float cameraPositionX = -cubeDepth;
   CameraState state;
   Vector3D axisY = new Vector3D(0, 1, 0);
   Vector3D axisX = new Vector3D(0, 0, 1);
   Vector3D npos = new Vector3D(0, 0, 0);
   Rotation rotn = new Rotation(axisY, radians(-5));
   rotn = rotn.applyTo(new Rotation(axisX, radians(5)));
   state = new CameraState(rotn, npos, 0);
   cam.setState(state, 8000);*/


  socialData = loadTable("dataCollect_comment.csv", "header"); // text cannot be load ###
  numSocials = socialData.getRowCount();
  println("[number of rows: " + numSocials +"]" );

  //println("socialData.getInt(81, 10) "+socialData.getInt(81, 10));

  // add variables into the socials object
  for (int i=0; i<numSocials; i++) {
    user_id = socialData.getString(i, 0);
    user_name = socialData.getString(i, 1);
    media_id = socialData.getString(i, 2);
    date = socialData.getString(i, 3);
    location_long = socialData.getFloat(i, 5);
    location_lat = socialData.getFloat(i, 4);
    social_id = socialData.getString(i, 6);
    social_name = socialData.getString(i, 7);
    comment_date = socialData.getString(i, 8);
    comment_text = socialData.getString(i, 10);
    comment_length = socialData.getInt(i, 9);
    //if(i<=100)
    //println("socialData.getInt(i, 10) "+(i+2)+" "+socialData.getInt(i, 10));
    count_comment = 0;

    socials.add(new Social(user_id, user_name, media_id, date, location_long, location_lat, 
      social_id, social_name, count_comment, comment_date, comment_text, comment_length));
  }
  println("[number of socials: " + socials.size() +"]");

  // calculate total days
  totalDays = totalDays(startDate, endDate); 

  // load map as ground
  map = loadImage("map.png");
  println("[map.width: " + map.width +"]" );
  println("[map.height: " + map.height +"]" );
  test = loadShape("test.svg"); // the same image size, svg for transparent
  //title = loadShape("title.svg"); 

  // set cube size
  cubeWidth = map.width;
  cubeDepth = map.height;
  cubeHeight = 1000;

  // get the location of each point in the space-time cube, store them in the floatlist
  for (int i=0; i<numSocials; i++) {
    location_long = socials.get(i).location_long;
    location_lat = socials.get(i).location_lat;
    date_index = dateParser(socials.get(i).date, startDate); 
    comment_date_index = dateParser(socials.get(i).comment_date, startDate);
    comment_length = socials.get(i).comment_length; 
    heat = comment_date_index * comment_length;


    velocity.append(map(log(comment_length), log(1), log(maximum_length), 20.f, 300.f));
    radius.append(map(log(comment_date_index-date_index), log(1), log(totalDays), 5, 20.f));
    heatColor.append(int(map(log(heat), log(1), log(totalDays*maximum_length), 1.f, 255.f)));

    location_x.append(map(location_long, -124.7, -66.94, -map.width/2.f, map.width/2.f));
    location_y.append(map(date_index, 1, totalDays, 0, -cubeHeight));
    location_z.append(map(location_lat, 49, 25.1, - map.height/2.f, map.height/2.f));
  }
  //println("location_x: "+location_x);
  //println("location_y: "+location_y);
  //println("location_z: "+location_z);

  count_comment =0;
  user_id = socials.get(0).user_id;
  media_id = socials.get(0).media_id;

  for (int i=0; i<numSocials; i++) {
    String temp = socials.get(i).media_id;
    String[] tempSlit = temp.split("_");

    // count user
    temp = socials.get(i).user_id;
    if (!user_id.equals(temp)) {
      //println("count_user = "+ count_user);
      user_medias.append(count_media);
      count_media=0;
      user_id = socials.get(i).user_id;
      count_user++;
    }

    // count comment
    temp = socials.get(i).media_id;
    if (media_id.equals(temp)) {
      count_comment++;
    } else {
      media_comments.append(count_comment);
      count_comment=1;

      // count media
      media_id = socials.get(i).media_id;
      count_media++;
      //println(count_media);
    }
  }

  // load font
  titleFont = loadFont("AnonymousPro-Bold-50.vlw");
  titleFont2 = loadFont("AndaleMono-30.vlw");
  labelFont = loadFont("KohinoorDevanagari-Light-48.vlw");
  labelFont2 = loadFont("KohinoorDevanagari-Medium-10.vlw");
  footnoteFont = createFont("stan0758.ttf", 15);
  //println(user_medias);


  // &&&&&&&&&&&&&&&&&&   ControlP5   &&&&&&&&&&&&&&&&&&&&

  //buttonClickCount = 0;
  cp5 = new ControlP5(this);

  // check box
  checkbox = cp5.addCheckBox("checkBox")
    .setPosition(10, 10) //350
    .setColorForeground(color(150))
    .setColorBackground(color(230))
    .setColorActive(color(0))
    .setColorLabel(color(0))
    .setSize(10, 10)
    .setItemsPerRow(1)
    .setSpacingColumn(20)
    .setSpacingRow(15)
    .addItem("Rotate", 0)
    .addItem("Label", 50)
    .addItem("Color", 100)
    ;

  /*
   // button
   cp5.addButton("Change View")
   .setPosition(10, 85)
   .setSize(55, 10)
   .setColorForeground(color(180))
   .setColorBackground(color(80))
   .setColorActive(color(255))
   ;
   */

  // display with PeasyCam
  cp5.setAutoDraw(false);  // prevent auto draw
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

void draw() {
  background(255);

  gui(); // cP5 with Peasy

  //&&&&&&&&&&&&&&&&&&   [space-time cube]   &&&&&&&&&&&&&&&&&&&&
  strokeWeight(0.5);
  cube(cubeWidth, cubeHeight, cubeDepth);

  //&&&&&&&&&&&&&&&&&&   [map-plane as ground]   &&&&&&&&&&&&&&&&&&&&
  if (showMap==true) {
    pushMatrix();
    translate(0, cubeHeight/2, 0);
    rotateX(PI/2);
    translate(-map.width/2, -map.height/2, 0);
    shape(test, 0, 0);  // test to make the map transparent
    //image(map, 0, 0); // try to set initial position at the center of 3D space
    //tint(255, 10);
    popMatrix();
    
    /*
    // date label
    smooth();
    textAlign(BOTTOM, RIGHT);
    textFont(labelFont, 14);
    fill(0, 0, 0);
    pushMatrix();
    translate(0, cubeHeight/2, 0);
    translate(-map.width/2, 0, 0);
    text(startDate, cubeWidth+50, 0, cubeDepth/2);
    popMatrix();
    pushMatrix();
    translate(0, cubeHeight/2, 0);
    translate(-map.width/2, 0, 0);
    text(endDate, cubeWidth+50, -cubeHeight+10, cubeDepth/2);
    popMatrix();
    */
  }


  //&&&&&&&&&&&&&&&&&&   [text and title]   &&&&&&&&&&&&&&&&&&&&
  if (showTitle==true)showTitle();
  //showFootnote();

  pushMatrix();
  translate(0, cubeHeight/2, 0); // translate the whole things to start growing from the ground

  //&&&&&&&&&&&&&&&&&&   [USER PATH]   &&&&&&&&&&&&&&&&&&&&
  // line up media space time path [USER PATH]
  lineColor = 0;
  noFill();
  strokeWeight(0.8);
  smooth();
  user_id = socials.get(0).user_id;

  beginShape();
  for (int i=1; i<numSocials; i++) { 
    if (selectMode==false ||mouseUser_id.equals(socials.get(i).user_id))
      stroke(20, 150);
    else {
      strokeWeight(1);
      stroke(230, 150);
    }
    if (user_id.equals(socials.get(i).user_id)) {
      vertex(location_x.get(i-1), location_y.get(i-1), location_z.get(i-1));
      vertex(location_x.get(i), location_y.get(i), location_z.get(i));

      // make the line more distinguishable when selected
      if (selectMode==true&&mouseUser_id.equals(socials.get(i).user_id)) {
        vertex(location_x.get(i), location_y.get(i)+2, location_z.get(i)+2);
        vertex(location_x.get(i-1), location_y.get(i-1)+2, location_z.get(i-1)+2);

        vertex(location_x.get(i-1), location_y.get(i-1)-2, location_z.get(i-1)-2);
        vertex(location_x.get(i), location_y.get(i)-2, location_z.get(i)-2);

        //vertex(location_x.get(i), location_y.get(i)+2, location_z.get(i)-2);
        //vertex(location_x.get(i-1), location_y.get(i-1)+2, location_z.get(i-1)-2);

        //vertex(location_x.get(i-1), location_y.get(i-1)-2, location_z.get(i-1)+2);
        //vertex(location_x.get(i), location_y.get(i)-2, location_z.get(i)+2);
      }
    } else {
      endShape();
      //stroke((255/2)-((i*10)%255)/2, 255-(i*10)%255, (i*10)%255); // test colorful mode
      user_id = socials.get(i).user_id;
      beginShape();
    }
  }
  endShape();

  String tempMediaID = socials.get(0).media_id;
  int drawCounter = 0;

  //&&&&&&&&&&&&&&&&&&   [MEDIA BOX]   &&&&&&&&&&&&&&&&&&&&
  // draw [MEDIA BOX] at every joint point
  //println("colorMode: "+colorMode+"  "+"selectMode: "+selectMode+"  "+"pick: "+mouseUser_id.equals(socials.get(0).user_id));
  for (int i=0; i<numSocials; i++) {  
    date_index = dateParser(socials.get(i).date, startDate);

    strokeWeight(1);
    stroke(230);
    if (selectMode==false)
      stroke(color(0, 0, 0), 80);
    else {
      if ( mouseUser_id.equals(socials.get(i).user_id)) {

        if (colorMode==true)
          stroke(color((2*heatColor.get(i)-50)%255, 0, 0), 100);
        else
          stroke(color(0, 0, 0), 80);
      }
    }

    if (user_id.equals(socials.get(i).user_id)) {
      user_id = socials.get(i).user_id;
    } else {
      pushMatrix();
      translate(location_x.get(i), location_y.get(i), location_z.get(i));
      box(10, 10, 10);


      // &&&&&&&&&&&&&&&&&&   [mouse pick]   &&&&&&&&&&&&&&&&&&&&
      // picking object data by mouse
      // map the 3D positions onto 2D screen 

      mouseObjectDistance = sq(mouseX-screenX(0, 0, 0))+sq(mouseY-screenY(0, 0, 0)); 
      if (mouseObjectDistance < 50) { 
        //println("mouseObjectDistance = "+mouseObjectDistance);

        mouseDate = socials.get(i).date;
        mousePlace = "Long "+socials.get(i).location_long +" / "+"Lat "+ socials.get(i).location_lat ;
        mouseUser_id = socials.get(i).user_id;
        mouseUser_name = socials.get(i).user_name;

        if (selectMode==true) {
          textAlign(LEFT, TOP);
          textFont(labelFont, 15);
          pushMatrix();
          //translate(0, 0, -location_z.get(i));
          float textDistanceY = 15;
          fill(0);
          text("User ID / " + mouseUser_id, 10, 10);
          text("Username / " + mouseUser_name, 10, 10+textDistanceY);
          text("Date / " + mouseDate, 10, 10+textDistanceY*2);
          text(mousePlace, 10, 10+textDistanceY*3); //"Location = " +
          noFill();
          popMatrix();
        }

        // show shadow on the gound
        if (showMap==true) {
          noStroke();
          pushMatrix();
          rotateX(-PI/2);
          translate(0, 0, -location_y.get(i)-10);
          fill(color(255, 0, 0), 30);
          //rect(0, 0, 20, 20);
          ellipse(0, 0, 20, 20);

          // draw a timeline to connect the space-time point to specific world coordinate(map + timeline)
          stroke(color(255, 0, 0), 30);
          line(0, 0, 0, 0, 0, location_y.get(i)+10); // long horizontal line

          // short vertical lines as scale

          for (int j=0; j<date_index; j++) {
            strokeWeight(0.4);
            float scaleSizeY;
            scaleSizeY = cubeHeight/(totalDays-1);
            float scalePositionY = scaleSizeY * j;
            line(0, 0, -scalePositionY, 3, 0, -scalePositionY);
          }
          popMatrix();
          noFill();
          stroke(0);
        }
      }


      //&&&&&&&&&&&&&&&&&&   [COMMENT BOX]   &&&&&&&&&&&&&&&&&&&&
      // draw [comment box] around each media box 
      // box numbers = comment numbers
      // rotate around the media with radius (= map to time difference between comment time and media time)
      if (tempMediaID.equals(socials.get(i).media_id)) {
        pushMatrix();

        positionX = radius.get(i)*sin(((frameCount+i*10)/(velocity.get(i))*2)*PI);
        positionY = radius.get(i)*cos(((frameCount+i*10)/(velocity.get(i))*2)*PI);

        if (mouseObjectDistance < 100) { 
          // 
          positionX = 8*radius.get(i)*sin(((frameCount+i*10)/(velocity.get(i))*5)*PI);
          positionY = 8*radius.get(i)*cos(((frameCount+i*10)/(velocity.get(i))*5)*PI);
          rotateX(PI/2);
          line(0, 0, positionX, positionY);
          rotateX(-PI/2);
          if (colorMode==true)stroke(color((2*heatColor.get(i)-50)%255, 0, 0), 100);///
        }
        translate(positionX, 0, positionY);      
        box(3, 3, 3);
        stroke(0);
        drawCounter++;
        popMatrix();
      }
      tempMediaID = socials.get(i).media_id;

      //if(i<=30)
      //println("i = "+i+"  drawCounter = "+drawCounter);
      //drawCounter=0;
      popMatrix();
    }
  }
  popMatrix();

  //&&&&&&&&&&&&&&&&&&   [Interaction/Motion]   &&&&&&&&&&&&&&&&&&&&
  // automatically change the selection mode
  if (cam.getDistance()>1100) {
    selectMode=false;
    showTitle=false;
  } else selectMode=true;

  if (rotateY==true)
    cam.rotateY(radians(1)*0.1);
}
Attachments
workInProgress_3.png
workInProgress_2.png
workInProgress.png

junxiangyao
Posts: 10
Joined: Wed Jan 06, 2016 1:38 pm

Re: PROJ 3: STUDENT DEFINED VISUALIZATION

Post by junxiangyao » Tue Mar 15, 2016 1:51 am

Art&Design Distribution
concept description
In this project, I want to find the relationship between art&design, economy and technology. During my undergraduate study, since my major was industrial design, I knew there was a website called Behance.net, which is like a instagram for designers to post their new projects on. Many designers use this website as their online portfolios. Since now I am major in media arts and technology, I am interested in getting an idea of the current situation of art and design. I want to visualize the relationship among culture, economy, technology and design using their API.

Before I working on the data from Behance, I found some lists from Wikipedia. And eventually, I decided to use the List of cities by GDP made by Brookings Institution from this link:https://en.wikipedia.org/wiki/List_of_cities_by_GDP
I wanted to set the data of the cities on the actual location of those cities, so I used this website:http://www.latlong.net
And copy the value of latitude and longitude into the list.
In addition, I want to add another column which containing the data of the population of the city respectively. My solution is searching those cities on Wikipedia, finding the pages of those cities and copying the value of population into the new column.

My plan was searching in the “creative field” provided by JSON from Bechance. “Creative field” is a JSON object containing words describing the field in which the user is working, like “Graphic Design”, “Interaction Design” and “Digital Art”. I wanted to find which city is more interested in new media by counting the times that words like “Digital Art”,”User Interface Design” have shown up. But after I getting a better understanding of their API, I realized that it is not possible to regard this API as a MySQL database like SPL which I used in my former two projects. Because the API of Behance.net is not well developed, if I want to get the data, I have to download thousands of the JSON files and calculate the data manually. Bechance.net has limitations about user accessing their data, I can only download 150 JSON files in one hour using one API key. Therefore, I applied 10 API keys and used them to download JSON files.
Here is one of the processing code I used to download JSON files:

Code: Select all

void setup() {
  size(1200, 800);
  JSONObject[] Behance_user = new JSONObject[10000];

  for (int i = 2701; i < 3000; i+=2) {
      int userID = i+12500000;        
      try{
        Behance_user[i]= loadJSONObject("https://api.behance.net/v2/users/"+userID
         +"?client_id=4qjo7tQtTzQtrIEDhAZKlDodWldXyCgR");
        saveJSONObject(Behance_user[i], "data/Behance_user"+userID+".json");
      } catch (NullPointerException e){
        println("Null");
      } 
    }
  }

Here is one of the JSON files:{

Code: Select all

  "http_code": 200,
  "user": {
    "country": "United States",
    "images": {
      "100": "https://mir-s3-cdn-cf.behance.net/user/100/d9b2ae1802903.559b1bcdb8e42.jpg",
      "276": "https://mir-s3-cdn-cf.behance.net/user/276/d9b2ae1802903.559b1bcdb8e42.jpg",
      "115": "https://mir-s3-cdn-cf.behance.net/user/115/d9b2ae1802903.559b1bcdb8e42.jpg",
      "138": "https://mir-s3-cdn-cf.behance.net/user/138/d9b2ae1802903.559b1bcdb8e42.jpg",
      "50": "https://mir-s3-cdn-cf.behance.net/user/50/d9b2ae1802903.559b1bcdb8e42.jpg",
      "230": "https://mir-s3-cdn-cf.behance.net/user/230/d9b2ae1802903.559b1bcdb8e42.jpg"
    },
    "website": "www.jaredbrainerd.com",
    "occupation": "Visual Designer",
    "city": "Los Angeles",
    "social_links": [
      {
        "social_id": 1,
        "service_name": "Twitter",
        "value": "jaredbrainerd",
        "url": ""
      },
      {
        "social_id": 4,
        "service_name": "LinkedIn",
        "value": "in/jaredbrainerd",
        "url": ""
      },
    ],
    "last_name": "Brainerd",
    "display_name": "Jared Brainerd",
    "url": " ",
    "sections": {"About": "I've focused my efforts on clean design, inspired by the things around me. An adventurer at heart, I use my resources and environment to create a clean, natural aesthetic. "},
    "twitter": "@jaredbrainerd",
    "created_on": 1352252224,
    "has_default_image": 0,
    "stats": {
      "followers": 49,
      "comments": 8,
      "appreciations": 134,
      "following": 37,
      "team_members": 0,
      "views": 2526
    },
    "location": "Los Angeles, CA, USA",
    "company": "",
    "links": [],
    "id": 1802903,
    "state": "California",
    "fields": [
      "Branding",
      "Graphic Design",
      "Product Design"
    ],
    "first_name": "Jared",
    "username": "jaredbrainerd"
  }
}

There are also other parameters I want to use, like the number of following, followers and appreciates, to support my main idea and enrich the detail of the project. So after I downloaded about 20 thousand files, I used 10 processing file to calculate those ten portions of data, and save the data as ten csv files.
Here is one of the processing code:

Code: Select all

String json_url = "https://api.behance.net/v2/users/XXXXX?client_id=4qjo7tQtTzQtrIEDhAZKlDodWldXyCgR";

Table table;
Table tablec;
void setup() {
  size(1200, 800);
  JSONObject[] Behance_user = new JSONObject[2500];
  table = new Table();
  tablec= loadTable("GDP_city.csv", "header");

  int num = 0;
  int following = 0;
  int follower = 0;
  int app = 0;
  int views = 0;
  float new_media = 0;
  table.addColumn("City");
  table.addColumn("num");
  table.addColumn("following");
  table.addColumn("follower");
  table.addColumn("app");
  table.addColumn("views");
  table.addColumn("nA");
  table.addColumn("tA");

  int k = 0;
  int [] f = {50750, 101350,102850,301350, 401350, 502700, 702700, 902700, 1202700, 1502700, 1802700, 12102700, 12502700};
  for (int j = 0; j<f.length; j++) {
    for (int i = 0; i < 1000; i+=1) {
      int userID = i+f[j];        
      try {
        Behance_user[k]= loadJSONObject("Behance_user"+userID+".json");
        k += 1;
        println(k);
      } 
      catch (NullPointerException e) {
        println("Null");
      }
    }
  }

  println(Behance_user[0].getJSONObject("user").getString("city"));





  for (int j = 0; j < tablec.getRowCount(); j++) {
    num = 0;
    following = 0;
    follower = 0;
    app = 0;
    views = 0;
    int tA = 0;
    int nA = 0;
    for (int i = 0; i < 1761; i++) {
      String CityName = Behance_user[i].getJSONObject("user").getString("city");
      //FOR NEW YORK
      //print(i+",");
      if (j==1) {
        if (CityName.equals("Manhattan")||CityName.equals("The Bronx")||CityName.equals("Brooklyn")||CityName.equals("Queens")||CityName.equals("Staten Island")) {
          CityName = tablec.getString(j, 0);
          print(CityName);
        }        
      }
      if (j==11) {
        if (CityName.equals("Bochum")||CityName.equals("Bottrop")||CityName.equals("Dortmund,")||CityName.equals("Duisburg")||
          CityName.equals("Gelsenkirchen")||CityName.equals("Hagen")||CityName.equals("Hamm")||
          CityName.equals("Mulheim an der Ruhr")||CityName.equals("Oberhausen")||CityName.equals("die Kreise Recklinghausen")||
          CityName.equals("Unna")||CityName.equals("Wesel")||CityName.equals("Ennepe-Ruhr-Kreis")) {
          CityName = tablec.getString(j, 0);
          print(CityName);
        }
      }
      if (CityName.equals(tablec.getString(j, 0))) {
        print(CityName);
        num+=1;
        following += Behance_user[i].getJSONObject("user").getJSONObject("stats").getInt("following");
        follower += Behance_user[i].getJSONObject("user").getJSONObject("stats").getInt("followers");
        app += Behance_user[i].getJSONObject("user").getJSONObject("stats").getInt("appreciations");
        views += Behance_user[i].getJSONObject("user").getJSONObject("stats").getInt("views");
        for (int l = 0; l<Behance_user[i].getJSONObject("user").getJSONArray("fields").size(); l++) {
          if (Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Automotive Design")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Computer Animation")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Digital Art")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Digital Imaging")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Digital Photography")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("DJing")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Game Design")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Information Architecture")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Installation Design")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Interaction Design")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Multimedia")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Motion Graphics")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Programming")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Software Architecture")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Television")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("UI/UX")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Video Arts")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Video Blogging")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Video Game Design")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Video Jockey")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Virtual World Design")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Web Design")||
            Behance_user[i].getJSONObject("user").getJSONArray("fields").getString(l).equals("Web Development")
            ) {
            nA++;
          } else {
            tA++;
          }
        }
      }
    }  
    TableRow newRow = table.addRow();
    newRow.setString("City", tablec.getString(j, 0));
    newRow.setInt("num", num);
    newRow.setInt("following", following);
    newRow.setInt("follower", follower);
    newRow.setInt("app", app);
    newRow.setInt("views", views);
    newRow.setFloat("nA", nA);
    newRow.setFloat("tA", tA);
  }
  saveTable(table, "data/city9.csv");
  print("\nDONE!");
}
Next, I added all ten cvs files together and wrote the data into one cvs file using processing.
Here is the code:

Code: Select all

Table[] table;
Table tableAll;
Table tablec;


void setup() {
  size(1200, 800);
  tableAll = new Table();
  tablec = loadTable("GDP_city.csv", "header");
  table = new Table[10];
  for (int i = 0; i < 10; i++) {
    table[i]= loadTable("city"+i+".csv", "header");
  }

  int num = 0;
  int following = 0;
  int follower = 0;
  int app = 0;
  int views = 0;
  float new_media = 0;
  int tA = 0;
  int nA = 0;

  tableAll.addColumn("City");
  tableAll.addColumn("num");
  tableAll.addColumn("following");
  tableAll.addColumn("follower");
  tableAll.addColumn("app");
  tableAll.addColumn("views");
  tableAll.addColumn("nA");
  tableAll.addColumn("tA");


  for (int j = 0; j < tablec.getRowCount(); j++) {
    num = 0;
    following = 0;
    follower = 0;
    app = 0;
    views = 0;
    tA = 0;
    nA = 0;
    for (int i = 0; i < 10; i++) {
      num += table[i].getInt(j, 1);
      following += table[i].getInt(j, 2);
      follower += table[i].getInt(j, 3);
      app += table[i].getInt(j, 4);
      views += table[i].getInt(j, 5);
      nA += table[i].getInt(j, 6);
      tA += table[i].getInt(j, 7);
    }
    TableRow newRow = tableAll.addRow();
    newRow.setString("City", tablec.getString(j, 0));
    newRow.setInt("num", num);
    newRow.setInt("following", following);
    newRow.setInt("follower", follower);
    newRow.setInt("app", app);
    newRow.setInt("views", views);
    newRow.setFloat("nA", nA);
    newRow.setFloat("tA", tA);
  }
  saveTable(tableAll, "data/city_user.csv");
  print("\nDONE!");
}
The data I downloaded from Behance.net contains many empty items which I cannot use in my visualization. And I couldn't figure out a better solution to get those data since I need to learn data mining, data analysis and skills about Java which I cannot finish in such a short time. As a result, I decided to make a good use of the data I've already got. Also, I think it would be fun to add a function which can search the most recent projects from the cities in the lists and show the cover picture and the fields.
Here is the code I used to search the most recent project posted on Bechance.net by cities:

Code: Select all

Table table;
int rows;

void setup() {
  size(120, 80);
  JSONObject[] Behance_user = new JSONObject[200];
  table = loadTable("GDP_city.csv", "header");
  rows = table.getRowCount(); 

  for (int i = 0; i < 150; i++) {
    String cityName = table.getString(i,0);
    if(i==1){
     cityName = "Manhattan";
    }
    if(1==11){
     cityName = "Essen";
    }
     try{
       Behance_user[i] = loadJSONObject("https://api.behance.net/v2/projects?city="+cityName+"&client_id=bMWqVFB27LovOd4TXfDvWe3qq0jCyZXn");
       saveJSONObject(Behance_user[i], "data/"+i+".json");
     } catch (NullPointerException e){
       println("Null");
     } 
   }
    for (int i = 151; i < 154; i++) {
    String cityName = table.getString(i,0);
    try{
       Behance_user[i] = loadJSONObject("https://api.behance.net/v2/projects?city="+cityName+"&client_id=zfrSSyfv2QIfJ8veuBmzUAK5oX4LVwlB");
      saveJSONObject(Behance_user[i], "data/"+cityName+".json");
    } catch (NullPointerException e){
      println("Null");
    } 
    }
  }
And here is the code of my visualization:

Code: Select all

/************************************************************
 * MAT259 PROJ 3 : Student Defined Visualization            *
 *                                                          *     
 * Junxiang Yao                                             *   
 *                                                          *
 *                                                          *
 * Usage: Press M to show / hide the map                    *
 *                                                          *
 *        Press T to show / hide the user information towers*
 *                                                          *
 *        Press B to show / hide the user data boxes        *
 *                                                          *
 *        Press S to show all the most recent projects      *
 *                                                          *
 *        Press H to hide all the most recent projects      *
 *                                                          * 
 *        Press L to show / hide the location line          *
 *                                                          *    
 *        Press P to change transparency                    * 
 *                                                          *
 *                                                          * 
 *                                                          *
 * Tags:  Interaction - Keyboard Control+mouse control      *
 *                                                          *
 *        pyramid     - drawPyramid()                       *
 *                                                          *
 *        structure   - drawStructure                       *
 *                                                          *  
 *        gui         - draw verbal information.            *
 *                                                          *
 *                                                          *
 ************************************************************/

import controlP5.*;
ControlP5 cp5;


import peasy.*;
import peasy.org.apache.commons.math.*;
import peasy.org.apache.commons.math.geometry.*;
import peasy.test.*;

PeasyCam cam;



int horMargin = 100;
int verMargin = 100;

ArrayList<PImage> covers = new ArrayList();
PImage map;
PVector[] screenPos;
PImage colorBar;

Table table;
Table user;
int rows, cols;
float[][] dataMatrix;
String[] cityName;
JSONObject[] picture;
IntList Indexes = new IntList();
IntList App = new IntList();
IntList V = new IntList();
StringList C = new StringList();
ArrayList<StringList> Fields = new ArrayList<StringList>();
boolean[] fieldLine;
boolean loc = true;
boolean tower = false;
boolean box = true;
boolean imgs = true;
boolean mapShow = true;
boolean grid = false;
boolean transparency = false;


//-----------------------------
//fields
//-----------------------------
int rad = 1800;
String[] FieldName = {"Other", "Painting", "Graphic Design", "Spatial Design", "Crafts", "Photography", "Audio", "Performance", "Industrial Design", 
  "New Media", "Interaction Design"};  
PVector[] FieldPos = new PVector[11];
String[] Painting = {"Visual Arts", " Visual Effects", "Street Art", "Graffiti", "Fine Arts", "Drawing", "Painting", "Caricature", "Cartooning"};
String[] GraphicDesign = {"Icon Design", "Branding", "Design", "Graphic Design", "Illustration", "Packaging", "Pattern Design", 
  "Typography", "Advertising", "Calligraphy", "Print Design", "Publishing", "Editing", "Editorial Design", "Creative Direction", "Art Direction"};
String[] SpatialDesign = {"Exhibition Design", "Architecture", "Interior Design", "Landscape Design", "Urbanism"};
String[] Crafts= {"Textile Design", "Toy Design", "Wood Working", "Origami", "Metal Working", "Masonry", "MakeUp Arts (MUA)", 
  "Leather Working", "Jewelry Design", "Glass Blowing", "Culinary Alchemy", "Culinary Arts", "Crafts", "Ceramics", 
  "Carpentry", "Enology (Wines)", "Sculpting", "Ice Sculpting", "Confectionary Arts"};
String[] Photography ={"Retouching", "Photo Illustration", "Photo Manipulation", "Photography", "Photojournalism"};
String[] Audio={"Singing", "Sound Design", "Songwriting", "Music", "Music Composition", "Music Production", "DJing", "Podcasting"};
String[] Performance={"Theater", "Screenwriting", "Performing Arts", "Dance", "Character Design", "Choreography", "Cinematography", 
  "Claymation", "Comedy", "Lighting Design", "Magic", "Film", "Directing", "Costume Design", "Set Design", "Puppetry", "Playwriting"};
String[] IndustrialDesign={"Product Design", "Production", "Industrial Design", "Engineering", "Furniture Design"};
String[] NewMedia={"Video Jockey", "Video Game Design", "Video Blogging", "Video Arts", "Multimedia", "Digital Art", "Digital Imaging", 
  "Digital Photography", "Installation Design", "Television", "Motion Graphics"};
String[] InteractionDesign={"Virtual World Design", "Web Design", "Web Development", "Software Architecture", "Programming", "Animation", 
  "Automotive Design", "Computer Animation", "Game Design", "Information Architecture", "Interaction Design", "UI/UX", "Machinima", 
  "Storyboarding", "Storytelling"};
String[] Other={"Academia", "Blogging", "Consulting", "Copywriting", "Documentary", "Entrepreneurship", "Fashion", "Fashion Styling", 
  "Hair Styling", "Journalism", "Marketing", "Millinery", "Perfumery", "Philanthropy", "Philosophy", "Poetry", "Writing", };

color[] fieldColor ={color(207, 0, 114), color(237, 28, 36), color(241, 90, 36), color(247, 147, 30), color(251, 176, 59), color(252, 238, 33), 
  color(140, 198, 63), color(0, 146, 69), color(0, 113, 188), color(46, 49, 146), color(108, 17, 157)};

PFont font;
void setup() {
  size(1250, 720, P3D);
  font = createFont("Arial", 80);
  cam = new PeasyCam(this, 2500);
  cam.setMinimumDistance(100);
  cam.setMaximumDistance(4500);

  textFont(font);
  map = loadImage("mapX.png");
  JSONObject[] picture = new JSONObject[154];
  //covers = new PImage[154];

  //________________________________________________  
  //Deal with the JSON;
  //________________________________________________
  for (int i = 0; i < 154; i++) {
    try {
      picture[i] = loadJSONObject(i+".json");
    }
    catch (NullPointerException e) {
      println("Null");
    }
  } 

  println(picture[0]);

  //int index = 0;
  for (int i = 0; i < 154; i++) {

    JSONArray jsa = picture[i].getJSONArray("projects");
    println("debug_01: " + i);
    if (jsa.size()>0) {
      String imgURL = jsa.getJSONObject(0).getJSONObject("covers").getString("202");
      println(imgURL);
      covers.add(loadImage(imgURL));
      Indexes.append(i);
      App.append(jsa.getJSONObject(0).getJSONObject("stats").getInt("appreciations"));
      V.append(jsa.getJSONObject(0).getJSONObject("stats").getInt("views"));
      C.append(jsa.getJSONObject(0).getJSONArray("owners").getJSONObject(0).getString("city"));
      StringList temp1 = new StringList();
      if (jsa.getJSONObject(0).getJSONArray("owners").getJSONObject(0).getJSONArray("fields").size()==3) { 
        temp1.append(jsa.getJSONObject(0).getJSONArray("owners").getJSONObject(0).getJSONArray("fields").getString(0));
        temp1.append(jsa.getJSONObject(0).getJSONArray("owners").getJSONObject(0).getJSONArray("fields").getString(1)); 
        temp1.append(jsa.getJSONObject(0).getJSONArray("owners").getJSONObject(0).getJSONArray("fields").getString(2));
        println(temp1);
      }
      if (jsa.getJSONObject(0).getJSONArray("owners").getJSONObject(0).getJSONArray("fields").size()==2) { 
        temp1.append(jsa.getJSONObject(0).getJSONArray("owners").getJSONObject(0).getJSONArray("fields").getString(0)); 
        temp1.append(jsa.getJSONObject(0).getJSONArray("owners").getJSONObject(0).getJSONArray("fields").getString(1));
      }
      if (jsa.getJSONObject(0).getJSONArray("owners").getJSONObject(0).getJSONArray("fields").size()==1) { 
        temp1.append(jsa.getJSONObject(0).getJSONArray("owners").getJSONObject(0).getJSONArray("fields").getString(0));
      }
      Fields.add(temp1);
    }
  }
  fieldLine = new boolean[covers.size()];
  for (int i = 0; i < fieldLine.length; i++) {
    fieldLine[i] = false;
  }

  println("Cover Size: " + covers.size());
  println("Cover Size: " + Fields);





  //-----------------------------
  //backgroundData
  //-----------------------------
  table = loadTable("GDP_city.csv", "header");
  user = loadTable("city_user.csv", "header");
  rows = table.getRowCount(); 
  cols = table.getColumnCount();
  println(rows, cols);
  dataMatrix = new float[cols-2][rows];
  cityName = new String[rows];
  screenPos = new PVector[rows];

  for (int j=0; j<rows; j++) {
    for (int i=0; i<cols-2; i++) {
      dataMatrix[i][j] = table.getFloat(j, i+2);
    }
  }

  for (int j=0; j<rows; j++) {
    dataMatrix[1][j] = map(dataMatrix[1][j], -90, 90, height/2, -height/2);
    dataMatrix[2][j] = map(dataMatrix[2][j], -180, 180, -width/2, width/2);
  }

  for (int i=0; i<rows; i++) {
    cityName[i] = table.getString(i, 0);
  }
  for (int i = 0; i<11; i++) {
    float theta = i*TWO_PI/11+PI/2;
    FieldPos[i] = new PVector((rad/2)*cos(theta+PI/2), (rad/2)*sin(theta+PI/2), 0);
  }
  colorBar = loadImage("colorBar.jpg");
}

void draw() {
  smooth();
  rotateX(+.2);
  //rotateY(-.5);
  background(48, 42, 56);
  if (mapShow) {
    imageMode(CORNER);
    tint(255, 255);
    image(map, -width/2, -height/2, width, height);
  }


  //________________________________________________  
  //Deal with the JSON;
  //________________________________________________
  for (int i = 0; i<covers.size(); i++) {
    //covers.size()
    if (fieldLine[i]==true) {
      pushMatrix();
      imageMode(CENTER);
      try {
        translate(dataMatrix[2][Indexes.get(i)], dataMatrix[1][Indexes.get(i)], App.get(i)/16);
        if (transparency) {
          tint(255, 40);
        } else {
          tint(255, 255);
        }
        image(covers.get(i), 0, 0, covers.get(i).width*V.get(i)/40000, covers.get(i).height*V.get(i)/40000);
        if (loc) {
          stroke(100, 100);
          line(0, 0, 0, 0, 0, -App.get(i)/16);
        }
        for (int j = 0; j < Fields.get(i).size(); j++) {
          for (int k = 0; k < Painting.length; k++) {
            if (Fields.get(i).get(j).equals(Painting[k])) {
              pushMatrix();
              translate(-dataMatrix[2][Indexes.get(i)], -dataMatrix[1][Indexes.get(i)], -App.get(i)/16);
              stroke(237, 28, 36, 100);
              line(dataMatrix[2][Indexes.get(i)], dataMatrix[1][Indexes.get(i)], App.get(i)/16, FieldPos[1].x, FieldPos[1].y, FieldPos[1].z);
              popMatrix();
            }
          }
          for (int k = 0; k < GraphicDesign.length; k++) {
            if (Fields.get(i).get(j).equals(GraphicDesign[k])) {
              pushMatrix();
              translate(-dataMatrix[2][Indexes.get(i)], -dataMatrix[1][Indexes.get(i)], -App.get(i)/16);
              stroke(241, 90, 36, 100);
              line(dataMatrix[2][Indexes.get(i)], dataMatrix[1][Indexes.get(i)], App.get(i)/16, FieldPos[2].x, FieldPos[2].y, FieldPos[2].z);
              popMatrix();
            }
          }
          for (int k = 0; k < SpatialDesign.length; k++) {
            if (Fields.get(i).get(j).equals(SpatialDesign[k])) {
              pushMatrix();
              translate(-dataMatrix[2][Indexes.get(i)], -dataMatrix[1][Indexes.get(i)], -App.get(i)/16);
              stroke(247, 147, 30, 100);
              line(dataMatrix[2][Indexes.get(i)], dataMatrix[1][Indexes.get(i)], App.get(i)/16, FieldPos[3].x, FieldPos[3].y, FieldPos[3].z);
              popMatrix();
            }
          }
          for (int k = 0; k < Crafts.length; k++) {
            if (Fields.get(i).get(j).equals(Crafts[k])) {
              pushMatrix();
              translate(-dataMatrix[2][Indexes.get(i)], -dataMatrix[1][Indexes.get(i)], -App.get(i)/16);
              stroke(251, 176, 59, 100);
              line(dataMatrix[2][Indexes.get(i)], dataMatrix[1][Indexes.get(i)], App.get(i)/16, FieldPos[4].x, FieldPos[4].y, FieldPos[4].z);
              popMatrix();
            }
          }
          for (int k = 0; k < Photography.length; k++) {
            if (Fields.get(i).get(j).equals(Photography[k])) {
              pushMatrix();
              translate(-dataMatrix[2][Indexes.get(i)], -dataMatrix[1][Indexes.get(i)], -App.get(i)/16);
              stroke(252, 238, 33, 100);
              line(dataMatrix[2][Indexes.get(i)], dataMatrix[1][Indexes.get(i)], App.get(i)/16, FieldPos[5].x, FieldPos[5].y, FieldPos[5].z);
              popMatrix();
            }
          }
          for (int k = 0; k < Audio.length; k++) {
            if (Fields.get(i).get(j).equals(Audio[k])) {
              pushMatrix();
              translate(-dataMatrix[2][Indexes.get(i)], -dataMatrix[1][Indexes.get(i)], -App.get(i)/16);
              stroke(140, 198, 63, 100);
              line(dataMatrix[2][Indexes.get(i)], dataMatrix[1][Indexes.get(i)], App.get(i)/16, FieldPos[6].x, FieldPos[6].y, FieldPos[6].z);
              popMatrix();
            }
          }
          for (int k = 0; k < Performance.length; k++) {
            if (Fields.get(i).get(j).equals(Performance[k])) {
              pushMatrix();
              translate(-dataMatrix[2][Indexes.get(i)], -dataMatrix[1][Indexes.get(i)], -App.get(i)/16);
              stroke(0, 146, 69, 100);
              line(dataMatrix[2][Indexes.get(i)], dataMatrix[1][Indexes.get(i)], App.get(i)/16, FieldPos[7].x, FieldPos[7].y, FieldPos[7].z);
              popMatrix();
            }
          }
          for (int k = 0; k < IndustrialDesign.length; k++) {
            if (Fields.get(i).get(j).equals(IndustrialDesign[k])) {
              pushMatrix();
              translate(-dataMatrix[2][Indexes.get(i)], -dataMatrix[1][Indexes.get(i)], -App.get(i)/16);
              stroke(0, 113, 188, 100);
              line(dataMatrix[2][Indexes.get(i)], dataMatrix[1][Indexes.get(i)], App.get(i)/16, FieldPos[8].x, FieldPos[8].y, FieldPos[8].z);
              popMatrix();
            }
          }
          for (int k = 0; k < NewMedia.length; k++) {
            if (Fields.get(i).get(j).equals(NewMedia[k])) {
              pushMatrix();
              translate(-dataMatrix[2][Indexes.get(i)], -dataMatrix[1][Indexes.get(i)], -App.get(i)/16);
              stroke(46, 49, 146, 100);
              line(dataMatrix[2][Indexes.get(i)], dataMatrix[1][Indexes.get(i)], App.get(i)/16, FieldPos[9].x, FieldPos[9].y, FieldPos[9].z);
              popMatrix();
            }
          }
          for (int k = 0; k < InteractionDesign.length; k++) {
            if (Fields.get(i).get(j).equals(InteractionDesign[k])) {
              pushMatrix();
              translate(-dataMatrix[2][Indexes.get(i)], -dataMatrix[1][Indexes.get(i)], -App.get(i)/16);
              stroke(108, 17, 157, 100);
              line(dataMatrix[2][Indexes.get(i)], dataMatrix[1][Indexes.get(i)], App.get(i)/16, FieldPos[10].x, FieldPos[10].y, FieldPos[10].z);
              popMatrix();
            }
          }
          for (int k = 0; k < Other.length; k++) {
            if (Fields.get(i).get(j).equals(Other[k])) {
              pushMatrix();
              translate(-dataMatrix[2][Indexes.get(i)], -dataMatrix[1][Indexes.get(i)], -App.get(i)/16);
              stroke(207, 0, 114, 100);
              line(dataMatrix[2][Indexes.get(i)], dataMatrix[1][Indexes.get(i)], App.get(i)/16, FieldPos[0].x, FieldPos[0].y, FieldPos[0].z);
              popMatrix();
            }
          }
        }
      } 
      catch (NullPointerException e) {
        //println("Image Invalid");
      }
      popMatrix();
      //println(covers.get(i));
    }
  }




  //-----------------------------
  //backgroundData
  //-----------------------------
  
  //Pyramid
  for (int j=0; j<rows; j++) {
    float h = (dataMatrix[3][j]/250000);
    if (h<1) {
      h = 1;
    }
    float e = (dataMatrix[0][j]/35);
    noStroke();
    fill(color(#6d6cca), 100);
    pushMatrix();
    translate(dataMatrix[2][j], dataMatrix[1][j]);
    //sphere(h);
    drawPyramid(h, e);
    //translate(0,0,0.01);
    //ellipse(dataMatrix[2][j], dataMatrix[1][j],r,r);
    translate(-dataMatrix[2][j], -dataMatrix[1][j]);
    screenPos[j]= new PVector(screenX(dataMatrix[2][j], dataMatrix[1][j], 0.01), screenY(dataMatrix[2][j], dataMatrix[1][j], 0.01));
    popMatrix();
  }




  //cityName

  for (int j=0; j<rows; j++) {
    if (dist(mouseX, mouseY, screenPos[j].x, screenPos[j].y)<4) {
      pushMatrix();
      float trans = dataMatrix[3][j]/1000000;
      if (trans<1) {
        trans = 1;
      }
      translate(dataMatrix[2][j], dataMatrix[1][j], 0.01);
      fill(239, 108, 49);
      textSize(20);
      textAlign(CENTER);
      text(cityName[j], 0, 0, 0);
      rotateZ(PI);
      translate(0, 0, -0.02);
      rotateX(PI);     
      text(cityName[j], 0, 0, 0);
      popMatrix();
    }
  }
  //Box 
  for (int j=rows-1; j>=0; j--) {
    stroke(color(#6db9bd));
    fill(color(#6db9bd), 50);
    //stroke(color(#6d6cca));
    //fill(color(#6d6cca), 50);

    if (box) {
      pushMatrix();
      translate(dataMatrix[2][j], dataMatrix[1][j], user.getInt(j, 1)/12+1.01 );
      box(user.getInt(j, 1)/6);
      popMatrix();
    }
    if (tower) {
      pushMatrix();
      translate(dataMatrix[2][j], dataMatrix[1][j], 0 );
      drawStructure(user.getInt(j, 1)/8, user.getInt(j, 2)/100, user.getInt(j, 3)/100, user.getInt(j, 4)/1000, user.getInt(j, 5)/10000, user.getInt(j, 6), user.getInt(j, 7));
      popMatrix();
    }
  }





  pushMatrix();
  noFill();
  rotateZ(PI/2);
  stroke(180);
  strokeWeight(1);
  ellipse(0, 0, rad, rad);
  for (int i = 0; i < 11; i++) {
    noFill();
    pushMatrix();
    float theta = i*TWO_PI/11;
    translate((rad/2)*cos(theta+PI/2), (rad/2)*sin(theta+PI/2), 0.01);
    rotate(theta+PI/2+PI);
    textAlign(LEFT, CENTER);
    textSize(30);
    rectMode(CENTER);
    fill(fieldColor[i]);
    noStroke();
    rect(20, 0, 40, 40);
    fill(180);
    text(FieldName[i], 40, 0);
    popMatrix();
  }
  popMatrix();

  gui();
}

void gui() {
  hint(DISABLE_DEPTH_TEST);
  cam.beginHUD();
  //cp5.draw();
  //-----------------------------
  //title
  //-----------------------------
  fill(230);
  textAlign(LEFT);
  textFont(font);
  textSize(50);
  text("Art&Design Distribution", horMargin, verMargin);
  textSize(16);
  text("Using Data from Behance.net and Wikipedia to Find the Relationship among Art&Design, Technology and Economy.", 
    horMargin, verMargin+24);


  //-----------------------------
  //name
  //-----------------------------
  fill(200);
  textAlign(LEFT, CENTER);
  textSize(14);
  text("Junxiang Yao", horMargin, verMargin+48);
  text("MAT259 PROJ 3", horMargin, verMargin+62);
  text("Student Defined Visualization", horMargin, verMargin+74);



  //-----------------------------
  //grid system
  //----------------------------- 
  if (grid) {
    stroke(#6d6cca);
    strokeWeight(1);
    line(0, verMargin, width, verMargin);
    line(0, verMargin+8+16, width, verMargin+8+16);
    line(0, verMargin+8+16+20, width, verMargin+8+16+20);
    line(0, verMargin+8+16+60, width, verMargin+8+16+60);
    //line(0, verMargin+8+16+206, width, verMargin+8+16+206);
    //line(0, verMargin+8+16+216, width, verMargin+8+16+216);
    line(0, verMargin+8+16+304, width, verMargin+8+16+304);
    line(0, verMargin+412, width, verMargin+412);
    line(0, height-verMargin, width, height-verMargin);

    line(horMargin, 0, horMargin, height);
    line(horMargin + 80, 0, horMargin + 80, height);
    line(horMargin + 160, 0, horMargin + 160, height);
    line(width - horMargin - 200, 0, width - horMargin - 200, height); 
    line(width-horMargin, 0, width-horMargin, height);
  }


  //-----------------------------
  //keyboard  control discription
  //-----------------------------
  fill(200);
  textSize(20);
  text("Keyboard Control", 
    horMargin, verMargin+396);


  textSize(12);
  textAlign(LEFT, CENTER);

  text("Press M to show / hide the map", 
    horMargin, verMargin+416);
  text("Press T to show / hide the user information towers", 
    horMargin, verMargin+14+416);
  text("Press B to show / hide the user data boxes", 
    horMargin, verMargin+28+416);  
  text("Press S to show all the most recent projects", 
    horMargin, verMargin+42+416);
  text("Press H to hide all the most recent projects", 
    horMargin, verMargin+56+416);
  text("Press L to show / hide the location lines", 
    horMargin, verMargin+70+416);
  text("Press P to change transparency", 
    horMargin, verMargin+84+416);

  cam.endHUD();
  hint(ENABLE_DEPTH_TEST);
}

void keyPressed() {
  if (key == 's'||key == 'S') {
    for (int i = 0; i < fieldLine.length; i++) {
      fieldLine[i] = true;
    }
  }
  if (key == 'h'||key == 'H') {
    for (int i = 0; i < fieldLine.length; i++) {
      fieldLine[i] = false;
    }
  }

  for (int i = 0; i < fieldLine.length; i++) {
    if (fieldLine[i] == true||key == 'l'||key == 'L') {
      loc = !loc;
    }
  }
  if (key == 't'||key == 'T') {
    tower=!tower;
  }
  if (key == 'b'||key == 'B') {
    box=!box;
  }
  if (key == 'm'||key == 'M') {
    mapShow=!mapShow;
  }
  if (key == 'g'||key == 'G') {
    grid=!grid;
  }
  for (int i = 0; i < fieldLine.length; i++) {
    if (fieldLine[i] == true||key == 'i'||key == 'I') {
      imgs=!imgs;
    }
  }
  if (key == 'p'||key == 'P') {
    transparency=!transparency;
  }
}
void mousePressed() {
  for (int m = 0; m < C.size(); m++) { 
    if (dist(mouseX, mouseY, screenPos[Indexes.get(m)].x, screenPos[Indexes.get(m)].y)<4) {
      if (mousePressed||C.get(m)==cityName[Indexes.get(m)]) {
        fieldLine[m]=!fieldLine[m];
      }
    }
  }
}
void drawPyramid(float r,float t) {
 smooth();
 stroke(100);
 color c = color(200);
 // this pyramid has 4 sides, each drawn as a separate triangle
 // each side has 3 vertices, making up a triangle shape
 // the parameter " t " determines the size of the pyramid
beginShape(TRIANGLES);

noFill();
vertex(-t/2, t/2, 0);
vertex(t/2, t/2, 0);
vertex( 0, 0, -r);

noFill();
vertex(t/2, t/2, 0);
vertex(t/2, -t/2, 0);
vertex( 0, 0, -r);

noFill();
vertex(t/2, -t/2, 0);
vertex(-t/2, -t/2, 0);
vertex( 0, 0, -r);

noFill();
vertex(-t/2, -t/2, 0);
vertex(-t/2, t/2, 0);
vertex( 0, 0, -r);

endShape();
}

void drawStructure(float t, float a, float b, float c, float d, float nA, float tA) {
  smooth();

  float rate;
  if (tA>0) {
    rate = nA/tA;
  } else {
    rate = 0;
  }
  if (rate>0.4) {
    rate = 0.4;
  }
  int transp;
  if (transparency) {
    transp = 150;
  } else {
    transp = 255;
  }

  //println(rate);
  color cro = colorBar.get((int)map(rate, 0, 0.4, 0, colorBar.width), colorBar.height/2);
  stroke(cro);

  beginShape();
  if (t<1) {
    t = 1;
  }
  fill(cro, transp);
  vertex(-t/2, t/2, 0);
  vertex(t/2, t/2, 0);
  vertex(t/2, t/2, a);
  vertex(-t/2, t/2, b);
  endShape();
  beginShape();
  fill(cro, transp);
  vertex(t/2, t/2, 0);
  vertex(t/2, -t/2, 0);
  vertex(t/2, -t/2, c);
  vertex(t/2, t/2, a);
  endShape();
  beginShape();
  fill(cro, transp);
  vertex(t/2, -t/2, 0);
  vertex(-t/2, -t/2, 0);
  vertex(-t/2, -t/2, d);
  vertex(t/2, -t/2, c);
  endShape();
  beginShape();
  fill(cro, transp);
  vertex(-t/2, -t/2, 0);
  vertex(-t/2, t/2, 0);
  vertex(-t/2, t/2, b);
  vertex(-t/2, -t/2, d);

  endShape();

  beginShape();
  fill(cro, transp);
  vertex(t/2, t/2, a);
  vertex(-t/2, t/2, b);
  vertex(t/2, -t/2, c);
  endShape();

  beginShape();
  fill(cro, transp);
  vertex(-t/2, t/2, b);
  vertex(t/2, -t/2, c);
  vertex(-t/2, -t/2, d);
  endShape();
}
Final Result:
屏幕快照 2016-03-15 上午8.48.47.png
屏幕快照 2016-03-15 上午8.49.07.png
屏幕快照 2016-03-15 上午8.49.33.png
屏幕快照 2016-03-15 上午8.49.55.png
屏幕快照 2016-03-15 上午8.50.56.png
屏幕快照 2016-03-15 上午8.53.03.png
屏幕快照 2016-03-15 上午8.54.32.png
屏幕快照 2016-03-15 上午8.55.10.png
屏幕快照 2016-03-15 上午8.55.28.png
屏幕快照 2016-03-15 上午8.56.17.png
屏幕快照 2016-03-15 上午8.56.35.png
屏幕快照 2016-03-15 上午8.57.02.png
Attachments
junxiangyaoProj3.zip
(4.74 MiB) Downloaded 724 times
Last edited by junxiangyao on Thu Mar 17, 2016 12:36 am, edited 4 times in total.

d.aleman.24.da
Posts: 4
Joined: Wed Jan 06, 2016 1:40 pm

Re: PROJ 3: STUDENT DEFINED VISUALIZATION

Post by d.aleman.24.da » Tue Mar 15, 2016 7:34 am

The Melting World

by David Aleman

The Melting World is a data visualization of the Arctic Sea Ice levels between the years 1980 - 2015.
The Polar Science Center website contained text files with the sea ice volume. Using the simple import tool with Microsoft Excel I was able to convert the text files into CSV files to upload into my sketch. Metadata includes:
1. Year
2. Month
3. Volume(10^3km^3)
4. Yearly Average

I did not use an API or JQuery. Here is the link that contains the text files I mentioned:
http://psc.apl.uw.edu/research/project ... maly/data/

Here is some sample code form my processing sketch:

Code: Select all

/*
The Melting World (Beta Version)

by David Aleman

Data borrowed from Polar Science Center

Schweiger, A., R. Lindsay, J. Zhang, M. Steele, H. Stern, Uncertainty in modeled 
arctic sea ice volume, J. Geophys. Res., 
doi:10.1029/2011JC007084, 2011

Interactions: 

Press Key 1 = Display data from 1980
      Key 2 = Display data from 1985
      Key 3 = Display data from 1990
      Key 4 = Display data from 1995
      Key 5 = Display data from 2000
      Key 6 = Display data from 2005
      Key 7 = Display data from 2010
      Key 8 = Display data from 2015
      Key 9 = Animate data from 1980 - 2015

Notes to Consider:
Although it is ready to present I would have liked the program to be different. Ideally
I would have liked the individual boxes to fall down the y-axis in an animation to 
reflect ice thinning in the Arctic. This will have to wait for future edits.


Special Thanks to Rodger Luo and George Legrady
*/

import controlP5.*;
import peasy.*;

ControlP5 cp5;
PeasyCam cam;

PImage img;
PFont myFont;

Table table, table80, table85,
      table90, table95, table00,
      table05, table10, table15;

int rowNums, rowNums80, rowNums85,
    rowNums90, rowNums95, rowNums00,
    rowNums05, rowNums10, rowNums15;

int hMargin = 30;
int vMargin = 50;

int yearAxis, yearAxis80, yearAxis85,
    yearAxis90, yearAxis95, yearAxis00,
    yearAxis05, yearAxis10, yearAxis15;
    
int monthAxis, monthAxis80, monthAxis85,
    monthAxis90, monthAxis95, monthAxis00,
    monthAxis05, monthAxis10, monthAxis15;

float volumeAxis, volumeAxis80, volumeAxis85,
      volumeAxis90, volumeAxis95, volumeAxis00,
      volumeAxis05, volumeAxis10, volumeAxis15;

float avgVolumeAxis, avgVolumeAxis80, avgVolumeAxis85,
      avgVolumeAxis90, avgVolumeAxis95, avgVolumeAxis00,
      avgVolumeAxis05, avgVolumeAxis10, avgVolumeAxis15;
      
float startPosition = 0;
float endPosition = 720;
float []box; 
double change = 255;
double end = 376.846;
int startVolume = 24600;
int endVolume = 15400;
int startYear = 1980;
int endYear = 2015;

color c;

boolean [] structure;
boolean [] center;
boolean resetOnDoubleClick = false;
boolean label1 = false;
boolean label2 = false;
boolean label3 = false;
boolean label4 = false;
boolean label5 = false;
boolean label6 = false;
boolean label7 = false;
boolean label8 = false;
boolean label9 = false;
boolean label10 = false;

ArrayList<Ice> ice = new ArrayList<Ice>();
ArrayList<Ice2> ice2 = new ArrayList<Ice2>();
ArrayList<Ice3> ice3 = new ArrayList<Ice3>();
ArrayList<Ice4> ice4 = new ArrayList<Ice4>();
ArrayList<Ice5> ice5 = new ArrayList<Ice5>();
ArrayList<Ice6> ice6 = new ArrayList<Ice6>();
ArrayList<Ice7> ice7 = new ArrayList<Ice7>();
ArrayList<Ice8> ice8 = new ArrayList<Ice8>();
ArrayList<Ice9> ice9 = new ArrayList<Ice9>();


void setup() {
  size(1280, 720, P3D);
  cam = new PeasyCam(this, 440); //Changed by Rodger, the original is 1620
  cp5 = new ControlP5(this);
  table = loadTable("seaIceLevel.csv", "header");
  table80 = loadTable("seaIceLevel80.csv", "header");
  table85 = loadTable("seaIceLevel85.csv", "header");
  table90 = loadTable("seaIceLevel90.csv", "header");
  table95 = loadTable("seaIceLevel95.csv", "header");
  table00 = loadTable("seaIceLevel00.csv", "header");
  table05 = loadTable("seaIceLevel05.csv", "header");
  table10 = loadTable("seaIceLevel10.csv", "header");
  table15 = loadTable("seaIceLevel15.csv", "header");
  
  rowNums = table.getRowCount();
  rowNums80 = table80.getRowCount();
  rowNums85 = table85.getRowCount();
  rowNums90 = table90.getRowCount();
  rowNums95 = table95.getRowCount();
  rowNums00 = table00.getRowCount();
  rowNums05 = table05.getRowCount();
  rowNums10 = table10.getRowCount();
  rowNums15 = table15.getRowCount();
  
  
  println();
  println("Data Size: " + rowNums + " rows");

  cp5.setAutoDraw(false);

  cam.setMinimumDistance(200);
  cam.setMaximumDistance(1800);

  img = loadImage("berg.jpg");
  img.loadPixels();
  
  getData();

  println("Year: " + ice.get(0).year);
  println("Month: " + ice.get(0).month);
  println("Volume: " + ice.get(0).volume);
  println("Volume Yearly Average: " + ice.get(0).avgVolume);
  
  

  myFont = createFont("sanFranFont.ttf", 100);
  noStroke();
}

void draw() {
  smooth();
  background(225);
 
  translate(-width/5, -height/5, 0); //Added by Rodger
  
  lights();
  scale(5);
   
  mapData();
  
  //the initial visual when sketch is running. it is under "interaction" tab
  //initial();
  //labelStart();
  label1 = true;
  
  //"interaction" tab has information of what is being drawn during keyPresses
  keyPressed();
  gui();
  //println("Volume 80s: " + ice2.get(0).volume80);
  
    //println(startPosition);
  
}
Screenshots:
finalProject-000123.jpg
finalProject-000237.jpg
finalProject-000408.jpg
finalProject-000976.jpg
Attachments
meltingWorldFinalProject.zip
(2.2 MiB) Downloaded 708 times
finalProject-001173.jpg
finalProject-000904.jpg
finalProject-000843.jpg

joo
Posts: 4
Joined: Thu Jan 07, 2016 10:19 am

Re: PROJ 3: STUDENT DEFINED VISUALIZATION

Post by joo » Tue Mar 15, 2016 5:49 pm

600 Hottest Albums in 2015 in the US

Subject
I am personally enjoying managing my music playlist with iTunes library and some other online digital music services based on the released year. From this idea, I was curious about what music albums were released on the same date. Since most music API providers does not offer an appropriate info that I was looking for, instead I decided to focus on the same release year in a particular music style.

API query
https://api.discogs.com/database/search ... y="YOURKEY"

API data for this project come from Discog. Below query is an example of 100 rock albums in 2015 in the US market, in order of popularity (Discog limits 100 data per page). In addition to this, for the whole data set, I have also looked into below lists.

1. 600 hottest rock albums in 2015
2. 600 hottest jazz albums in 2015
3. 600 hottest electronic albums in 2015

The result includes artists, titles, release year, genre, label, and album covers.

Code: Select all

    {
      "country": "US",
      "resource_url": "https://api.discogs.com/masters/878145",
      "thumb": "https://api-img.discogs.com/0NTU574_norgaP1qowXiEJL8BzE=/fit-in/150x150/filters:strip_icc():format(jpeg):mode_rgb()/discogs-images/R-7410812-1440942485-6604.jpeg.jpg",
      "year": "2015",
      "format": [
        "CD",
        "Album"
      ],
      "label": [
        "Barsuk Records",
        "Control Freak Kitten Records"
      ],
      "community": {
        "want": 5,
        "have": 15
      },
      "title": "Small Feet - From Far Enough Away Everything Sounds Like The Ocean ",
      "type": "master",
      "uri": "/Small-Feet-From-Far-Enough-Away-Everything-Sounds-Like-The-Ocean-/master/878145",
      "catno": "BARK153",
      "genre": ["Rock"],
      "style": [
        "Folk Rock",
        "Indie Rock"
      ],
      "id": 878145,
      "barcode": [" 6 55173 11531 9"]
    }
Design
I adopted 2D matrix type mapping by using 600(30X20) pixel units, inspired by iTunes’s screensaver mode. For a visualization process, I retrieved 3 elements which are album cover, released year, and genre(1D linear type). In terms of the popularity order, album covers are arranged from top to bottom and left to right.

1. Default Screen
v1.png
As a default, all pixels are blinking with random colors. This means just pretend it is waiting data. I mimicked TV screen with no signal. By using the menu on the right part of the screen, you can get 600 albums which were released in 2015, ordered by popularity.

2. Menu
vmenu.png
Menu on the right side of screen functions to search data according to music style, and button X allows you to go back to the first screen and refresh the data.

3. An example of 600 Rock albums with a blurred color arrangement
v2rock.png
I tried to express this 2D matrix more abstract, so I extracted only 6X6 pixels from the center of the album covers instead of displaying the whole images. But you can see the original album cover and get the info by moving the mouse pointer over each pixel and clicking it.

4. An example of 600 Rock albums with mouse over
v3.png
All original covers will be upsizing with an animation effect.

5. An example of 600 Rock albums without an image distortion
v4.png
6. An example of 600 Jazz albums with mouse over
v6.png
Processing Code

Code: Select all

//final 
//Woohun Joo

//JSON
JSONObject json;
JSONObject json1;
JSONObject json2;

Button button;
Button button1;
Button button2;
Button button3;

PImage[] covers;
PImage[] covers1;
PImage[] covers2;

String[] textYear;
String[] textYear1;
String[] textYear2;
String[] textTitle;
String[] textTitle1;
String[] textTitle2;
String[] textCountry;
String[] textCountry1;
String[] textCountry2;

int[] want;
int[] have;

PFont head;
PFont title;
PFont year;
PFont label;

ArrayList<Album> albums = new ArrayList<Album>();
ArrayList<Album> albums1 = new ArrayList<Album>();
ArrayList<Album> albums2 = new ArrayList<Album>();

//the number of units
int col = 30;
int row = 20;

//unit and color array
boolean[][] shouldDraw = new boolean[col][row];
color[][] rect_color = new color[col][row];

//grid size
int grid_width = 42;
int grid_height = 42;

//pixel capture
int getPixel = 6;

//unit size
int rect_width_percentage = 100;
int rect_height_percentage = 100;
int rect_width = grid_width * rect_width_percentage / 100;
int rect_height = grid_height * rect_height_percentage / 100;

int grid_start_x = 110;
int grid_start_y = 100;

int text_start_x = 1420;
int text_start_y = 100;

int grid_end_x = grid_start_x + col * grid_width;
int grid_end_y = grid_start_y + row * grid_height;

boolean is_in_grid(int mx, int my) {
  return grid_start_x < mx && mx < grid_end_x &&
    grid_start_y < my && my < grid_end_y;
}

float rect_scale = 1.0;

boolean rotate;
float alpha;

int index;
int index1;

boolean getCover = false;
boolean getCover1 = false;
boolean getCover2 = false;

void setup() {
  size(1920, 1080, P3D);

  //font
  head = createFont("Brown-Bold", 28);
  title = createFont("Brown-Bold", 16);
  year = createFont("Brown-Bold", 16);
  label = createFont("Brown-Bold", 16);

  //button
  button = new Button(text_start_x, text_start_y+280, "rock");
  button1 = new Button(text_start_x, text_start_y+320, "jazz");
  button2 = new Button(text_start_x, text_start_y+360, "electronic");
  button3 = new Button(text_start_x, text_start_y+400, "x");

  //json
  json = loadJSONObject("data/2015rock.json");
  json1 = loadJSONObject("data/2015jazz.json");
  json2 = loadJSONObject("data/2015elec.json");

  //perpage & item
  int perPage = json.getJSONObject("pagination").getInt("per_page");
  int totalItem = json.getJSONObject("pagination").getInt("items");
  println(perPage);
  println(totalItem);

  covers = new PImage[900];
  covers1 = new PImage[900];
  covers2 = new PImage[900];

  textYear = new String[900];
  textTitle = new String[900];
  textCountry = new String[900];

  textYear1 = new String[900];
  textTitle1 = new String[900];
  textCountry1 = new String[900];
  
  textYear2 = new String[900];
  textTitle2 = new String[900];
  textCountry2 = new String[900];

  want = new int[900];
  have = new int[900];

  JSONArray items = json.getJSONArray("results");
  JSONArray items1 = json1.getJSONArray("results");
  JSONArray items2 = json2.getJSONArray("results");

  //total items with cover images
  println(items.size());
  println(items1.size());
  println(items2.size());

  //int coverIndex - ROCK
  int indexR = 0;
  for (int i=0; i < items.size(); i++) {
    if (items.getJSONObject(i).getString("thumb").length()>0) {
      covers[indexR] = loadImage(items.getJSONObject(i).getString("thumb"));
      textYear[indexR] = items.getJSONObject(i).getString("year");
      textTitle[indexR] = items.getJSONObject(i).getString("title");
      textCountry[indexR] = items.getJSONObject(i).getString("country");
      want[indexR] = items.getJSONObject(i).getJSONObject("community").getInt("want");
      have[indexR] = items.getJSONObject(i).getJSONObject("community").getInt("have");

      albums.add(new Album(items.getJSONObject(i).getString("thumb")));
      println(items.getJSONObject(i).getString("thumb"));
      println(items.getJSONObject(i).getString("year"));
      println(items.getJSONObject(i).getJSONObject("community").getInt("want"));
      println(items.getJSONObject(i).getJSONObject("community").getInt("have"));
      println(items.getJSONObject(i).getString("country"));
      println(items.getJSONObject(i).getString("title"));

      indexR += 1;
    }
  }
  //int coverIndex - JAZZ
  int indexR1 = 0;
  for (int i=0; i < items1.size(); i++) {
    if (items1.getJSONObject(i).getString("thumb").length()>0) {
      covers1[indexR1] = loadImage(items1.getJSONObject(i).getString("thumb"));
      textYear1[indexR1] = items1.getJSONObject(i).getString("year");
      textTitle1[indexR1] = items1.getJSONObject(i).getString("title");
      textCountry1[indexR1] = items1.getJSONObject(i).getString("country");
      albums1.add(new Album(items1.getJSONObject(i).getString("thumb")));
      println(items1.getJSONObject(i).getString("thumb"));
      indexR1 += 1;
    }
  }
  //int coverIndex - ELECTRONIC
  int indexR2 = 0;
  for (int i=0; i < items2.size(); i++) {
    if (items2.getJSONObject(i).getString("thumb").length()>0) {
      covers2[indexR2] = loadImage(items2.getJSONObject(i).getString("thumb"));
      textYear2[indexR2] = items2.getJSONObject(i).getString("year");
      textTitle2[indexR2] = items2.getJSONObject(i).getString("title");
      textCountry2[indexR2] = items2.getJSONObject(i).getString("country");
      albums2.add(new Album(items2.getJSONObject(i).getString("thumb")));
      println(items2.getJSONObject(i).getString("thumb"));
      indexR2 += 1;
    }
  }

  //the number of the covers with each file 
  println(albums.size());
  println(albums1.size());
  println(albums2.size());

  colorMode(HSB, 360, 100, 100, 100);
  rectMode(CENTER);

  for (int i = 0; i < col; i++) {
    for (int j = 0; j < row; j++) {
      shouldDraw[i][j] = true;
      rect_color[i][j] = color(random(1.0), 1.0, 1.0);
    }
  }
  //register_dead_cell();
}

void bar(int n) {
  if (n == 0) {
    getCover = true;
    getCover1 = false;
    getCover2 = false;
  }
  if (n == 1) {
    getCover1 = true;
    getCover = false;
    getCover2 = false;
  }
  if (n == 2) {
    getCover2 = true;
    getCover = false;
    getCover1 = false;
  }
  if (n == 3) {
    getCover = false;
    getCover1 = false;
    getCover2 = false;
  }
}


void draw() {

  background(48, 2, 95);

  int mouse_on_x = -1;
  int mouse_on_y = -1;

  //cp5.draw();

  //head
  fill(245, 100, 100);
  textAlign(LEFT, TOP);
  textFont(head);
  text("600 Hottest Albums", text_start_x, text_start_y);
  text("in 2015 in the US", text_start_x, text_start_y+35);
  //stroke(180, 0, 100);
  stroke(245, 100, 100);
  strokeWeight(3);
  line(text_start_x, text_start_y+65, text_start_x+275, text_start_y+65);
  
  //cover box
  noFill();
  stroke(245, 100, 100, 80);
  strokeWeight(4);
  rect(text_start_x+110, text_start_y+600, 220, 220);
 
  //menu
  button.draw();
  button1.draw();
  button2.draw();
  button3.draw();

  if (is_in_grid(mouseX, mouseY)) {
    mouse_on_x = (mouseX - grid_start_x) / grid_width;
    mouse_on_y = (mouseY - grid_start_y) / grid_height;
  }

  noStroke();

  //original
  index = 0;

  for (int i = 0; i < col; i++) {
    for (int j = 0; j <row; j++) {

      int x = grid_width / 2 + i * grid_width + grid_start_x;
      int y = grid_height / 2 + j * grid_height + grid_start_y;
      imageMode(CENTER);

      index += 1;

      if (index < albums.size() && getCover == true && getCover1 == false && getCover2 == false) {
        image(covers[index].get(rect_width/2, rect_height/2, getPixel, getPixel), x, y, rect_width, rect_height);
      } else if (index < albums1.size() && getCover1 == true && getCover == false && getCover2 == false) {
        image(covers1[index].get(rect_width/2, rect_height/2, getPixel, getPixel), x, y, rect_width, rect_height);
      } else if (index < albums2.size() && getCover2 == true && getCover == false && getCover1 == false) {
        image(covers2[index].get(rect_width/2, rect_height/2, getPixel, getPixel), x, y, rect_width, rect_height);
      } else {
        fill(random(360), 100, 100);
        rect(x, y, rect_width, rect_height);
      }
    }
  }

  //mouse over
  index1 = 0;
  for (int i = 0; i < col; i++) {
    for (int j = 0; j < row; j++) {

      int x1 = grid_width / 2 + i * grid_width + grid_start_x;
      int y1 = grid_height / 2 + j * grid_height + grid_start_y;

      imageMode(CENTER);
      index1 += 1;  

      if (mouse_on_x == i && mouse_on_y == j && index1 < albums.size() && getCover == true) {
        //fill(245, 100, 100, 80);
        noFill();
        stroke(245, 100, 100, 80);
        strokeWeight(4);
        rect(x1, y1, rect_scale * rect_width * 1.2, rect_scale * rect_height* 1.2);
        image(covers[index1], x1, y1, rect_scale * rect_width, rect_scale * rect_height);

        if (mousePressed == true) {
          fill(180, 100, 100);
          
          image(covers[index1], text_start_x+110, text_start_y+600, 200, 200);

          //fill(180, 0, 100);
          fill(245, 100, 100);
          textAlign(LEFT);
          textFont(title);
          text(textTitle[index1], text_start_x, grid_end_y - rect_height - 55);
          textFont(year);
          text(textYear[index1], text_start_x, grid_end_y - rect_height - 25);
          textFont(label);
          text(textCountry[index1], text_start_x, grid_end_y - rect_height + 5);
        }
      } else if (mouse_on_x == i && mouse_on_y == j && index1 < albums1.size() && getCover1 == true) {
        noFill();
        stroke(245, 100, 100, 80);
        strokeWeight(4);
        rect(x1, y1, rect_scale * rect_width * 1.2, rect_scale * rect_height* 1.2);
        image(covers1[index1], x1, y1, rect_scale * rect_width, rect_scale * rect_height);
        
        if (mousePressed == true) {
          fill(180, 100, 100);
          image(covers1[index1], text_start_x+110, text_start_y+600, 200, 200);

          //fill(180, 0, 100);
          fill(245, 100, 100);
          textAlign(LEFT);
          textFont(title);
          text(textTitle1[index1], text_start_x, grid_end_y - rect_height - 55);
          textFont(year);
          text(textYear1[index1], text_start_x, grid_end_y - rect_height - 25);
          textFont(label);
          text(textCountry1[index1], text_start_x, grid_end_y - rect_height + 5);
        }
      } else if (mouse_on_x == i && mouse_on_y == j && index1 < albums2.size() && getCover2 == true) {
        noFill();
        stroke(245, 100, 100, 80);
        strokeWeight(4);
        rect(x1, y1, rect_scale * rect_width * 1.2, rect_scale * rect_height* 1.2);
        image(covers2[index1], x1, y1, rect_scale * rect_width, rect_scale * rect_height);
        
        if (mousePressed == true) {
          fill(180, 100, 100);
          image(covers2[index1], text_start_x+110, text_start_y+600, 200, 200);

          //fill(180, 0, 100);
          fill(245, 100, 100);
          textAlign(LEFT);
          textFont(title);
          text(textTitle2[index1], text_start_x, grid_end_y - rect_height - 55);
          textFont(year);
          text(textYear2[index1], text_start_x, grid_end_y - rect_height - 25);
          textFont(label);
          text(textCountry2[index1], text_start_x, grid_end_y - rect_height + 5);
        }
      }
    }
  }

  //animation
  rect_scale = 4.00 + 0.15 * sin(millis() / 150);
}

void keyReleased() {
  if (key == 'a') {
    rect_scale += 0.1;
  } else if (key == 's') {
    rect_scale -= 0.1;
  }
}

void checkBox(float[] a) {
  println(a);
}

void mousePressed() {
  if (button.over()) {
    getCover = true;
    getCover1 = false;
    getCover2 = false;
    println("Whoo clicky!");
  }
  if (button1.over()) {
    getCover1 = true;
    getCover = false;
    getCover2 = false;
    println("Whoo clicky!");
  }
  if (button2.over()) {
    getCover2 = true;
    getCover = false;
    getCover1 = false;
    println("Whoo clicky!");
  }
  if (button3.over()) {
    getCover = false;
    getCover1 = false;
    getCover2 = false;
    println("Whoo clicky!");
  }
}

class Button {

  int x, y;
  String label;
  Button(int x, int y, String label) {
    this.x = x;
    this.y = y;
    this.label = label;
  }
  void draw() {
    //fill(200);
    noFill();

    if (over()) {
      fill(60, 100, 100);
    }
    noStroke();
    rect(x+textWidth(label)/2, y+25/2, textWidth(label), 25);

    fill(245, 100, 100);
    textAlign(LEFT, TOP);
    textFont(head);
    text(label, x, y);
  }
  boolean over() {
    if (mouseX >= x && mouseY >= y && mouseX <= x + textWidth(label) && mouseY <= y + 22) {
      return true;
    }
    return false;
  }
}

class Album{
  String url;
  
  Album(String url){
    this.url = url;
  }
}
To get all 1800 album covers, it might take more than 5 minutes.

xindi
Posts: 8
Joined: Wed Jan 06, 2016 1:39 pm

Project 3: Visualizing the Aurora Borealis ------ Xindi Kang

Post by xindi » Tue Mar 15, 2016 9:15 pm

Visualizing the Aurora Borealis

Introduction:

An aurora, sometimes referred to as a polar light, is a natural light display in the sky, predominantly seen in the high latitude (Arctic and Antarctic) regions. (wikiPedia)
auroraBorealis.jpg
A Photo of Aurora Borealis
Personally I am fascinated by the idea of going to the north pole and see the northern light, and I'm sure most people have seen or at least heard of the beauty of the aurora. Let alone the fact that it drives photographers and travelers to travel for thousands of miles to see it, inspired by it's dancing and wavering nature, artists all around the world have created thousands of hundreds of songs, myths, movies and literature works giving the aurora spiritual values.

Therefore, I decided to use this as my subject to create an intuitive and artistic representation of the social power of the aurora; in other words, I want to create a visualization that shows how the aurora drives people to learn and imagine.

Data:

To be more specific, data wise, the goal of this project is to show the relationship between the intensity of the aurora and people's intension to learn about it.

In order to show people's intension to learn about the aurora, data are mined from the database of Seattle Public Library.
Below is the query.

Code: Select all

SELECT 
    FLOOR(deweyClass) AS Dewey,
    DATE_FORMAT(checkOut, '%Y-%m-%d') AS checkOutDates,
    COUNT(checkout) AS CheckoutCount,
    TITLE,
    bibNumber
FROM
    spl3._rawXmlDataCheckOuts
WHERE
    (title LIKE '%aurora%'
        OR title LIKE '%northern light%'
        OR title LIKE '%polar light%')
        AND title NOT LIKE '%aurora county%'
        AND title NOT LIKE '%teagarden%'
        AND title NOT LIKE '%perfect party%'
        AND title NOT LIKE '%quilt%'
        AND title NOT LIKE '%Aurora Brigida y Carlos%'
        AND title NOT LIKE '%means dawn%'
        AND title NOT LIKE '%Andrew Reale%'
        AND title NOT LIKE '%Pliny%'
        AND title NOT LIKE '%Aurora West%'
GROUP BY checkOutDates
ORDER BY SUM(CASE
    WHEN
        (YEAR(checkOut) >= 2006
            OR YEAR(checkOut) <= 2016)
    THEN
        1
    ELSE 0
END) DESC
This query extract check outs times for books related to "aurora" or "the northern light" by day over the period of the past nine years (2006 - 2014).
Each year's data was saved as a separate CSV file for later use.

For the data that shows the intensity of the aurora borealis, I used data from NASA.
One of the columns in the data is the real time solar wind speed by hour, which directly relates to the intensity of the aurora. I downloaded them as .txt files for later use.

Visualization:

In order to create a visual effect that is similar to the aurora, I used a spherical coordinate system.
In this system, there are three main variables that determine where and how lone the radiating lines should be:

1. rho --- the length of line a, representing the check out times.
2. phi --- the angle from Y axis, representing dewey class of the book.
3. theta --- the angle from Z axis, representing the date of check out.

coordinates.png
spherical coordinates system

Since this project is an extension of my previous project, it is necessary to show the initial visualization.
In this visualization, the lines radiating from the sphere represent the check out times of the books related to "aurora" or "the northern light". each year's data is visualized separately and the user can see them by clicking on keys 1 through 9. No animation was added in the initial version. At that point, my next step was to add a color scheme to represent the intensity of the aurora in real life.
YearSelection.png
initial 3D visialization
Color Scheme, Animation and User Interface:

After a color scheme was implemented on the data, the color of the radiating lines tells the intensity of the aurora borealis. The redder the color, the stronger the intensity of the aurora. In this "dateMode" shown below, with the "date points" plane (the z plane of the sphere) facing forward, the viewer should be able to see the variation of check outs and the aurora intensity through time.
Screen Shot 2016-03-15 at 9.24.54 PM.png
Aurora Visualization date mode initial position
And in In this "deweyMode" shown below, with the "dewey points" plane (the z plane of the sphere) facing forward, the viewer should be able to see the correlation between check outs of books from different dewey classes and the aurora intensity.
Screen Shot 2016-03-15 at 10.01.30 PM.png
Aurora Visualization dewey mode initial position
To show the changes between years I added an animation feature that makes the changes between years less sudden. When user slide the "years" slider from one year to another, the current visualization will make gradual animated movements towards the year that the user wants to see.
Screen Shot 2016-03-15 at 9.31.20 PM.png
2013Visualization

jordanhughes
Posts: 4
Joined: Wed Jan 06, 2016 12:48 pm

Re: PROJ 3: STUDENT DEFINED VISUALIZATION

Post by jordanhughes » Wed Mar 16, 2016 12:48 pm

The S&P 500
Jordan Hughes

Introduction

For my final project for the course I wanted to implement some kind of real time visualization of the stock market. Starting from that, I decided to work with the S&P 500. The S&P500 is what is known as a stock index. It is a list of 500 (505 to be exact) companies that make up a very large percentage (70-80%) of the total market capitalization of American exchanges. I decided I wanted to used some kind of a space or planetary metaphor for the actual visualization.

Data:

The first step in gathering the data was to get a list of the S&P500. I found one an appropriate list here. After that, I needed to find an API that would work for getting real time quotes for those securities. I went with the TradeKing API which has many endpoints and can be queried for quotes at a rate of 60 per second. The full API is located here. Below is a sample of the data that I can query on a per second basis.

Code: Select all

//A Sample Data Query
{ "response":{ "@id":"385eca86-7b6b-4c30-b9b2-02472bab7850", "elapsedtime": "0", "quotes":{"quote":{"adp_100":"21.9758","adp_200":"21.6211","adp_50":"21.3420","adv_21":"4150403","adv_30":"4328619","adv_90":"4592093","ask":"21.110","ask_time":"13:09","asksz":"23","basis":"na","beta":"1.5578","bid":"21.100","bid_time":"13:09","bidsz":"21","bidtick":"d","chg":"0.2350","chg_sign":"d","chg_t":"na","cl":"21.340","contract_size":"na","cusip":"na","date":"2016-03-15","datetime":"2016-03-15T13:09:00-04:00","days_to_expiration":"na","div":"0.1500","divexdate":"20160226","divfreq":"Q","divpaydt":"20160315","dollar_value":"30807571.625","eps":"1.0900","exch":"NYSE","exch_desc":"New York Stock Exchange","hi":"21.290","iad":"0.6000","idelta":"na","igamma":"na","imp_volatility":"na","incr_vl":"100","irho":"na","issue_desc":"na","itheta":"na","ivega":"na","last":"21.105","lo":"21.100","name":"INTERPUBLIC GROUP OF COMPANIES INC.","op_delivery":"na","op_flag":"1","op_style":"na","op_subclass":"na","openinterest":"na","opn":"21.210","opt_val":"na","pchg":"1.10","pchg_sign":"na","pcls":"21.340","pe":"19.5780","phi":"21.420","plo":"21.190","popn":"21.220","pr_adp_100":"21.9854","pr_adp_200":"21.6056","pr_adp_50":"21.3420","pr_date":"2016-03-14","pr_openinterest":"na","prbook":"4.360","prchg":"0.120","prem_mult":"na","put_call":"na","pvol":"2828648","qcond":"0","rootsymbol":"na","secclass":"0","sesn":"na","sho":"406348000","strikeprice":"na","symbol":"IPG","tcond":"54","timestamp":"1458061756","tr_num":"8261","tradetick":"e","trend":"na","under_cusip":"na","undersymbol":"na","vl":"1452523","volatility12":"0.2297","vwap":"21.209","wk52hi":"23.8000","wk52hidate":"20151230","wk52lo":"18.1600","wk52lodate":"20150901","xdate":"na","xday":"na","xmonth":"na","xyear":"na","yield":"2.8116"}}, "error": "Success" } }
Bulding the Visualization:
I ended up pulling the Processing libraries into a separate IDE called intellij for this project so there were essentially two aspects of building the code. The first was creating a means to query the data and store the relevant data for the visualization. I created a background thread to loop through and query the data at a constant rate, which means the visualization is built in realtime with corresponding data. The visualization aspect involved creating a new Processing sketch within the IDE.

I decided to structure the sketch by using the sectors that each stock was in as a basis for its orbit. So I created a sphere for each of the 10 sectors. From there, I rotated the stock spheres around their corresponding sector sphere. The rotational speed is based on the amount of current volume relative to the volume of the stock for the last three weeks. Faster stock spheres mean there is more relative volume. The scale of each sphere is related to its market capitalization-- a measure of how much total money is held in each stock. Finally, the position vertically indicates whether the sector is up or down on the day, and the depth position is a a measure of average market capitalization of the stock in that sector.


Final Results and Code:

Here are some final images of the visualization itself.
Screen Shot 2016-03-15 at 6.46.56 AM.png
Colors dictate the sector of that particular stock.
Screen Shot 2016-03-15 at 6.47.42 AM.png
I added bezier curves to give a greater sense of depth and position relative to each sector for the spheres.
Screen Shot 2016-03-15 at 9.24.44 AM.png
I also added a small amount of interactivity in which the user can press the 'a' key to loop through the Top 3 relative volume stocks on the day.
Screen Shot 2016-03-16 at 1.24.11 PM.png
Motion of the spheres
StockGif.gif
Final code. Note that this code will not run unless there is a TradeKing API Oauth key and Secret provided.
Attachments
MATfinal.zip
(10.78 MiB) Downloaded 704 times

hkung
Posts: 4
Joined: Wed Jan 06, 2016 12:47 pm

Re: PROJ 3: STUDENT DEFINED VISUALIZATION

Post by hkung » Wed Mar 16, 2016 11:41 pm

Concept
I visualize the occurrence of plant bugs across Canada, the United States, and Mexico. I first implement a program that converts data into 3D terrain-like maps and then collect insect occurrence data as an input to my program.

Query
I contacted Dr. Katja Seltmann and she provided my with plant bug datasets that can be read in Excel.

Process
I first wrote a program to draw triangle strips. Then I implemented the midpoint displacement algorithm to generate cloud-like fractal. Finally I integrated both programs and entered the information on where the bugs were collect into my program, creating the 3D terrain that visualize insect occurrence. I also colored the terrain to show when the plant bugs were collected. The time frame ranges from 1800 to 2010.
Attachments
source.zip
(131.93 KiB) Downloaded 709 times
midptdisp.jpg
polys1.jpg
occurrence.jpg
final_0.png
final_1.png
final_2.png

Post Reply