I had two broad ideas for my final piece. A gesture based video controller using the Kinect and a data visualizer that built a narrative around the data.

I finally decided to go with the data project after finding a similar product had already been developed by Canesta to control a television using gestures.

The broad idea was to represent data in a way that tells a story. Many of the current data visualizations use geometric shapes, color distinctions, linear patterns and repeated icons to represent important data. While this is useful for representing large data sets, the visuals are becoming increasingly removed from the story that the data tells. With this piece I wanted to move towards a representation of data that is intimately connected to the narrative behind the numbers.

Important numbers and datasets are becoming increasingly ignored due to a lack of context to the presentation. In the sphere of politics and development work, which often demands action on the part of the audience, data needs to be visualized in a more powerful way that impacts the viewer on both an emotional and analytical level.

I looked around for images that I could work with, specifically something that had linear movement that I could control using processing. I found this timelapse film that seemed perfect for visualizing military spending data:

I started off with a basic sketch that took the background to this film as the canvas and added a series of troops that moved from the right of the screen to the left. The moving troops were built into a Troops class, with an arraylist in the main sketch that was added to by clicking the mouse:

import ddf.minim.*;
AudioPlayer player;
Minim minim;

ArrayList troopArray;

PImage background;
PImage background_overlay;

float w = 150;
float h = 75;

void setup() {
  size(1280, 719);
  smooth();
  frameRate(30);
  background = loadImage("background.jpg");
  background_overlay = loadImage("background_overlay.png");
  imageMode (CENTER);

  troopArray = new ArrayList();
  troopArray.add(new Troops(width, height/1.45, w, h));

  minim = new Minim(this);
  player = minim.loadFile("sample.mp3", 2048);
  player.play();
}

void draw() {

image (background, width/2, height/2);

for (int i = troopArray.size()-1; i >= 0; i--) {
  Troops troops = (Troops) troopArray.get(i);

     troops.move();
     troops.display();
}
image (background_overlay, width/2, height/2);

//   float militarySpending = 1;
//
//  float r = random(100);
//  if (r < militarySpending) {
//      troopArray.add(new Troops(width, height/1.45, w, h));
//  }
}

   void mousePressed() {
   troopArray.add(new Troops(width, height/1.5, 150,75));
 }
class Troops {
  float x, y;
  float speed;
  float angle;
  float rotation;
  float frame = 0;

  PImage tankImage, towersImage, trucksImage;
  PImage[] soldiers = new PImage[5];

  Troops(float tempX, float tempY, float tempW, float tempH) {

    x = tempX;
    y = tempY;
    w = tempW;
    h = tempH;
    speed = -2;
    angle = .1;
    rotation = 0;

  tankImage = loadImage("tanks.png");
  towersImage = loadImage("towers.png");
  trucksImage = loadImage("trucks.png");
   for (int i = 0; i soldiers.length-1) {
     frame = 0;
   }
  }

  void spin() {

    rotation = rotation + 0.1;
  }
}

This gave me the structure of the piece. I was advised to use a structure that added objects into one Array List for countries, and then to build the main sketch using another Array List of the country objects. I also had to find a way to add to the array list using my data instead of any user action. The data was contained in a CSV file that was parsed by splitting up the string of text and delineating using line break and commas to provide two sets of values, the countries and the budgets per country. These budgets were in the billions of dollars, so I took the values and divided by 10 and generated an integer in order to tell processing to add a line of tanks for every 10 billion dollars. I used the initial billion dollar figure to display the numerical value at the top of the screen.

This is the final code:

import ddf.minim.*;
import fullscreen.*;
AudioPlayer player;
Minim minim;
FullScreen fs; 

String[] countries;
int[] amounts;
int[] originalAmounts;
PImage[] tanks = new PImage[1];
PImage[] soldiers = new PImage[5];
PImage[] mario = new PImage[8];
PImage[] banner = new PImage[12];
PImage plane;
PFont font;
//boolean frameRateOn = false;
//boolean restart = false;
int frameRateVal = 8;

int totalTanks;
int totalCountries = 0;
//int indexGlobal =0;
//float lastTroopX;
ArrayList countryArray;
//boolean still = false;

PImage background;
PImage background_overlay;

void setup() {
  size(1080, 600);
  smooth();
  //frameRate(frameRateVal);
  font = loadFont("CarnivaleeFreakshow-50.vlw");
  textFont(font);
  background = loadImage("background.jpg");
  background_overlay = loadImage("background_overlay.png");
  minim = new Minim(this);
  player = minim.loadFile("sample.mp3", 2048);
 // player.play();
  imageMode (CENTER);

  tanks[0] = loadImage("tanks.png");
  plane = loadImage("plane.png");

  for (int i = 0; i= 0; i--) { 

    Country countries = (Country) countryArray.get(i);
    countries.CountryCreate();
    textSize(40);
    fill(0);
 //   Country lastCountry = (Country) countryArray.get(countryArray.size()-1);
 //   lastCountry.lastTroop.displayName(countryName, budget, 300);

    //   countries.xLocation(countries);
    //  if (i == 0)  setup();
  }

  image (background_overlay, width/2, height/2);

  // if (restart = true)
  // setup();
}

void keyPressed() {
     if (key == 'a') {
     player.close();
     setup();
  }

      if (key == CODED){
      if (keyCode == LEFT) {
   frameRateVal = frameRateVal + 1;
      }
        if (keyCode == RIGHT  && frameRateVal > 5) {
   frameRateVal = frameRateVal - 1;

    }
      }
}

class Country {

  int troopsPerCountry;
  ArrayList troopArray;
  PImage flag;

  String countryName;
  int budget;
  int originalAmounts;
  float w, h;
  float frame = 0;
  float nameX;

  Country(float x, String name, int _troopsPerCountry, int _originalAmounts) {

    flag  = loadImage(name+".png");
    troopsPerCountry = _troopsPerCountry;
    countryName = name;
    originalAmounts = _originalAmounts;
    budget = originalAmounts;

    troopArray = new ArrayList();

    // Building arraylist of troop objects for each country
    for (int i = 0; i < troopsPerCountry; i++) {       if (i == 0) troopArray.add(new Troops(soldiers, x+width+i*100, height/1.32, 150, 75));            if (i == 1) troopArray.add(new Troops(tanks, x+width+i*100, height/1.32-5, 160, 80));       if (i == 2) troopArray.add(new Troops(tanks, x+width+i*100, height/1.32-5, 160, 80));        if (i == 3) troopArray.add(new Troops(tanks, x+width+i*100, height/1.32-5, 160, 80));       if (i == 67) troopArray.add(new Troops(mario, x+width+i*100+300, height/1.38, 120, 100));       else if (i > 3 ) troopArray.add(new Troops(tanks, x+width+i*100, height/1.32-5, 160, 80));
    }
  } 

  void CountryCreate() {

    Troops firstTroop = (Troops) troopArray.get(0);
    firstTroop.displayFlag(flag);
    firstTroop.planeFly();
    firstTroop.bannerFly();
    //   firstTroop.displayName(countryName, budget, still);

    Troops lastTroop = (Troops) troopArray.get(troopsPerCountry-1);   

    firstTroop.displayName(countryName, budget, nameX);   

    if (lastTroop.x > width) {
    lastTroop.displayName(countryName, budget, 300);
    }

    for (int i = troopArray.size()-1; i >= 0; i--) {
      Troops troops = (Troops) troopArray.get(i);

      troops.display();
      troops.move();
    }
  }
}

class Troops {
  float x, y;
  float xPlane, speedPlane;
  float speed;
  float w, h;
  float frame = 0;

  PImage[] images;
  int counter = 0;
  int ticker = 0;

  Troops(PImage[] tempImages, float tempX, float tempY, float tempW, float tempH) {
    images = tempImages;
    x = tempX;
    y = tempY;
    w = tempW;
    h = tempH;
    speed = -17;
  }

  void display() {
    image(images[counter], x, y + 10, w, h);
    counter = (counter + 1) % images.length;
  }

  void displayFlag(PImage flag) {
    image(flag, x - 47, y - 35, 45, 100);
    //    frame = (frame +1) % flag.length;
  }

  void planeFly() {
    image(plane, x - 175, y - 385, 153, 50);
  }

  void displayName(String countryName, int budget, float nameX) {
    //   if (still = true) {
    nameX = x;
    text(countryName + " : $" + budget +" billion", nameX -75, 85);
    //   }
    //  else {
    //  nameX = 300;
  }

  void bannerFly() {
    image(banner[counter], x + 100, y - 380, 830, 55);
    ticker = (ticker + 1) % banner.length;
  }

  void move() {
    x = x + speed;
  }
}

Additions that I hope to implement are:

  1. Developing interactivity into the piece — So that perhaps the user can select from a number of different data-sets or comparisons
  2. Building some physicality into the objects so that there is something more dynamic in the movements. Perhaps the troops pile up on top of each other and are swept away. Perhaps countries face off against each other and the dominant powers persist.
  3. In the long term, I believe this sort of project could become a platform that allows any user to import a data-set, create their own canvas and objects and build a time-lapse style animation from their data.
 

Leave a Reply

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

*

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