Design of Data - Self Initiated Learning

Here are the three components of my homework assignment for May 19, 2020.

Part 0 : Self Reflection


reflection map 1.JPG



Part I : What Did I Teach Myself?

For my Design of Data homework, I decided to explore the process of embedding Processing scripts on my personal website. I’ve been using Processing for awhile and it seems like a great match for this course as it is very visual. I have a fair amount of experience with Java and Processing is a great package for bringing my data to life. I started exploring using it for my last project to analyze, explore, manipulate and visualize some data, and I am interested in continuing to explore this tool for this class and moving forward.

One of the limitation of a java applet, as I have historically used Processing, is the inability to easily share the visualizations. It’s one thing to display some data and then export an image of what was created, but that really leaves out the ability to interact with or dynamically display data. So I began wondering if it was possible to embed Processing code easily on my website so that I could utilize my experience with Processing to produce content available online.

It ended up being simpler than I thought to get something running on my Squarespace website. After digging around a bit, I found a guide on Reddit that was able to walk me through the basics. I am now continuing to understand how features are implemented in this medium. I can take input from the page viewer and I am working on figuring out how to upload images as well.  

Below is a reasonably simple example I built to prove to myself that I could generate graphics quickly and get them on my website. Now that I understand the workflow, I can create these custom data visualizations and display them to the outside world incredibly quickly.

[Click in the rectangle to produce a randomly colored circle of a size that corresponds to the length of the click]


Part II : Corum

This is where I’ll put my Corum Graphic. Still trying to work out some bugs in my code… :/


I am still chasing down bugs but it should look something like this below:

raceday.JPG

Here is the code that I am working with as it is not rendering…yet... I started working with this data from my phones motion tracking during the last assignment and now I am trying to revamp it utilizing Corum’s point about providing the context around the data points to better tell the story rather than just presenting the visualization.

<script src="/s/processing.js"></script>
<script type="text/processing" data-processing-target="pjs">

 /* @pjs preload="applehealthdata.xml"; */

///////////////////////////////////////////////////////////////////////////////////////
//
// Personal Geography - Revamp
//
///////////////////////////////////////////////////////////////////////////////////////

import java.text.SimpleDateFormat;
import java.util.Date;

XML healthXML;
String xmlLocation = "applehealthdata.xml";
ArrayList<String> records = new ArrayList<String>();
ArrayList<String> recordList = new ArrayList<String>();
HealthRecords healthRecords = new HealthRecords();
Record lastRecord;
int BORDER =  100;
XML ytXML;
String ytDataLocation = "watch-history.json";
JSONObject ytRecords;
int xAdjust = 0;
int xAdjustInterval = 100;

HealthRecords targetHealthRecords = new HealthRecords();


///////////////////////////////////////////////////////////////////////////////////////
//
// INPUTS
//
String TARGETSTARTTIME = "2017-03-20 01:01:01 -0700";
String TARGETENDTIME = "2017-03-30 10:01:01 -0700";
//
//
///////////////////////////////////////////////////////////////////////////////////////

void setup(){
  
  ///////////////////////////////////////////////////////////////////////////////////////
  // Setup Display
  size(1800, 675);
  background(50);
  stroke(144, 217,234);
  fill(144, 217,234);
  //ellipse(width/2, height/2, 20, 20);
  strokeWeight(3);
  line(BORDER, height-BORDER, width-BORDER, height-BORDER); 
  ///////////////////////////////////////////////////////////////////////////////////////
  
  ///////////////////////////////////////////////////////////////////////////////////////
  // Execute Data
  loadXMLData();
  createHealthRecord();
  getSubRecord(TARGETSTARTTIME, TARGETENDTIME);
  plotSubRecord();
  //searchHealthRecord();
  //loadYTData();
  //loadStravaData();
  ///////////////////////////////////////////////////////////////////////////////////////
}


String[] listFileNames(String dir) {
  File file = new File(dir);
  if (file.isDirectory()) {
    String names[] = file.list();
    return names;
  } else {
    // If it's not a directory
    return null;
  }
}

void loadYTData(){
  println("Loading Youtube data...");
  ytRecords = loadJSONObject(ytDataLocation);
  
}

void keyPressed(){
  if(key == CODED){
    if(keyCode == LEFT){
      xAdjust -= xAdjustInterval;  
      println(xAdjust);
      fill(144, 217,234);
      plotSubRecord(); 
    }
    if(keyCode == RIGHT){
      xAdjust += xAdjustInterval;  
      println(xAdjust);
      fill(144, 217,234);
      plotSubRecord();
    }
  }
}

void displayBackground(){
  background(50);
  stroke(144, 217,234);
  fill(144, 217,234);
  //ellipse(width/2, height/2, 20, 20);
  strokeWeight(3);
  line(BORDER, height-BORDER, width-BORDER, height-BORDER);   
}


void plotSubRecord(){
  displayBackground();
  int modWidth = width - 2*BORDER;
  int modHeight = height - 2*BORDER;
 
  long startMillis = targetHealthRecords.recordList.get(0).startTimeMillis;
  long endMillis = targetHealthRecords.recordList.get(targetHealthRecords.recordList.size()-1).startTimeMillis;
  println("Size of Targeted Health Records: " + str(targetHealthRecords.recordList.size()));
  
  println("Starting at " + targetHealthRecords.recordList.get(0).startDate + " to " + targetHealthRecords.recordList.get(targetHealthRecords.recordList.size()-1).endDate);

  for(Record tempRecord : targetHealthRecords.recordList){
    float xPos = map(tempRecord.startTimeMillis, startMillis, endMillis, 0, modWidth) + BORDER;
    float yPos = height - BORDER - map(tempRecord.value, targetHealthRecords.minValue, targetHealthRecords.maxValue, 0, modHeight);
    //float yPos = height - BORDER - map(tempRecord.value/1000, getMin(targetHealthRecords.recordList)/1000, getMax(targetHealthRecords.recordList)/1000, 0, modHeight);
    float yValue = modHeight - yPos + 2*BORDER;
    float modYValue = yValue/30;
    //noStroke();
    //fill(144, 217,234, map(yPos, 0, modHeight, 255,0));
    ellipse(xPos + xAdjust, yPos, 1, 1);
  }
  println("Finished displaying data...");
  
  
}

long getMax(ArrayList<Record> recordList){
  long maxValue = 0;
  for(Record temp : recordList){
    if(temp.elapsedTime > maxValue){
      maxValue = temp.elapsedTime;  
    }
  } 
  return maxValue;
}

long getMin(ArrayList<Record> recordList){
  long minValue = 10;
  for(Record temp : recordList){
    if(temp.elapsedTime < minValue){
      minValue = temp.elapsedTime;  
    }
  } 
  return minValue;
}

void getSubRecord(String startTime, String endTime){
  println("Searching for subrecords...");
  targetHealthRecords = healthRecords.getSubRecord(startTime, endTime);
  print("Total Records Collected: ");
  println(targetHealthRecords.recordList.size());
  //for(Record temp : targetHealthRecords.recordList){
  //  println(temp.value);
  //}
  targetHealthRecords.printMin();
  targetHealthRecords.printMax();
}


void searchHealthRecord(){
  Record tempRec = healthRecords.findStartDate("2018-12-04 18:22:32 -0700");
  long tempTime = tempRec.creationTimeMillis;
  Record nextRec = tempRec.nextRecord;
  long nextTime = nextRec.creationTimeMillis;
  print("Next Record Value: ");
  println(nextRec.value);
  Record prevRec = tempRec.prevRecord;
  long prevTime = prevRec.creationTimeMillis;
  print("Prev Record Value: ");
  println(prevRec.value);
  print("Time Difference [min]: ");
  println((nextTime - prevTime)/1000/60);
  
}

void loadXMLData(){
  println("Loading XML...");
  healthXML = loadXML(xmlLocation); 
}

void createHealthRecord(){
  println("Creating Health Records...");
  int count = 0;
  XML[] xmlChildren = healthXML.getChildren();
  for(int i = 0; i < xmlChildren.length; i++){
    addName(xmlChildren[i].getName());
    if(xmlChildren[i].getName() == "Record"){
     records.add(xmlChildren[i].getName());
     String tempType = xmlChildren[i].getString("type");
     String tempSourceName = xmlChildren[i].getString("sourceName");
     String tempUnit = xmlChildren[i].getString("unit");
     String tempCreationDate = xmlChildren[i].getString("creationDate");
     String tempStartDate = xmlChildren[i].getString("startDate");
     String tempEndDate = xmlChildren[i].getString("endDate");
     float tempValue = float(xmlChildren[i].getString("value"));
      
     healthRecords.addRecord(new Record(tempType, tempSourceName, tempUnit, tempCreationDate, tempStartDate, tempEndDate, tempValue)); 
     count++;
    }
  }
  
  healthRecords.printMax();
  healthRecords.printMin();
  healthRecords.linkRecords();
}


void addName(String incoming){
  for(String item: recordList){
    if(item.equals(incoming)){
      return;  
    }
  }
  recordList.add(incoming);
  return;
}


void draw(){
  fill(144, 217,234);
  //plotSubRecord();
  
}
class HealthRecords{
 
  ArrayList<Record> recordList;
  float maxValue = 0;
  float minValue = 100;
  
  ArrayList<Record> dailyRecords;
  
  HealthRecords(){
    recordList = new ArrayList<Record>();  
  }
  
  void addRecord(Record newRecord){
    recordList.add(newRecord);  
    checkMin(newRecord);
    checkMax(newRecord);
  }
  
  Record findStartDate(String targetStartDate){
    for(Record record : recordList){
      if(record.startDate.equals(targetStartDate)){
        return record;  
      }
    }
    return null;  
  }
  
  void checkMin(Record newRecord){
    if(newRecord.value < this.minValue){
      this.minValue = newRecord.value;
    }
  }
  
  void checkMax(Record newRecord){
    if(newRecord.value > this.maxValue){
      this.maxValue = newRecord.value;
    }
  }
  
  void printMax(){
     print("Max Value: ");
     println(this.maxValue);
  }
  void printMin(){
     print("Min Value: ");
     println(this.minValue);
  }
  
  void linkRecords(){
    for(int i = 0; i < recordList.size(); i++){
      if(i > 0 ){
        recordList.get(i).setPrevRecord(recordList.get(i-1));
      }
      if(i < recordList.size()-1){
        recordList.get(i).setNextRecord(recordList.get(i+1));
      }
    }
    println("Records Linked...");
  }
  
  HealthRecords getSubRecord(String sDateStr, String eDateStr){
    HealthRecords subRecord = new HealthRecords();
    Date sDate, eDate;
    long sDateMillis, eDateMillis;
    
    try{
      sDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(sDateStr);
      eDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(eDateStr);
      println("Searching for dates between " + sDateStr + " & " + eDateStr);
      
      sDateMillis = sDate.getTime();
      eDateMillis = eDate.getTime();
      
      for(Record tempRec: recordList){
        if(sDateMillis < tempRec.startTimeMillis && eDateMillis > tempRec.endTimeMillis){
          subRecord.addRecord(tempRec);
          //println(tempRec.startDate);
        }
      }
    }
    catch(Exception e){
      println("Error Parsing Date");
    }   
    return subRecord;
  }
  
  
  void generateDailyRecords(){
     Record tempRecord = recordList.get(0);
     while(tempRecord.nextRecord != null){
       
     }
    
    
  }
  
  
}

class Record{
  
  String type;
  String sourceName;
  String unit;
  String creationDate;
  String startDate;
  String endDate;
  float value;
  Date startTimeDate;
  Date endTimeDate;
  Date creationTimeDate;
  long elapsedTime;

  long startTimeMillis, endTimeMillis, creationTimeMillis;
  
  Record prevRecord, nextRecord;

  Record(){
    
  }
  
  Record(String type, String sourceName, String unit, String creationDate, String startDate, String endDate, float value){
    this.type = type;
    this.sourceName = sourceName;
    this.unit = unit;
    this.creationDate = creationDate;
    this.startDate = startDate;
    this.endDate = endDate;
    this.value = value;
    
    try{
      this.startTimeDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(this.startDate);
      this.endTimeDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(this.endDate);
      this.creationTimeDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(this.creationDate);
    
      this.startTimeMillis = this.startTimeDate.getTime();
      this.endTimeMillis = this.endTimeDate.getTime();
      this.creationTimeMillis = this.creationTimeDate.getTime();
      
      this.elapsedTime = this.endTimeMillis - this.startTimeMillis;
  
    }
    catch(Exception e){
      println("Error Parsing Date");
    }
    
  }
  
  void setPrevRecord(Record lastRecord){
   this.prevRecord = lastRecord; 
  }
  
  void setNextRecord(Record nextRecord){
    this.nextRecord = nextRecord;  
  }
  void printValue(){
    println(this.value);  
  }
  
}

</script>
<canvas id="pjs"></canvas>