Saturday, July 13, 2019

Hurricane Data Visualization

So basically my final project was to create a slightly interactive data visualization chart, right?
But what, I could do that 3 days after first learning Processing. I wanted a challenge that actually felt like a final project to me. 36 or so hours later, I ended up with four charts, three of which are interactive and immersive, and a menu-based interaction system.

Basically, paste these code snippets into various tabs of Processing and execute them. Alternatively download the zip file of the project from GitHub. Bearing in mind there's close to a thousand lines of code (over 900 at least), I'm going to post my YouTube video explaining it first.


 2017HurricaneData.txt file:

Storm Name,Minimum Pressure,Max Wind Speed,Damage,Damage String,Wiki
Bret,1007,50,2960000,$2.960.000,https://en.wikipedia.org/wiki/Tropical_Storm_Bret_(2017)
Cindy,991,60,25000000,$25.000.000,https://en.wikipedia.org/wiki/Tropical_Storm_Cindy_(2017)
Emily,1001,60,10000000,$10.000.000,https://en.wikipedia.org/wiki/Tropical_Storm_Emily_(2017)
Franklin,981,85,15000000,$15.000.000,https://en.wikipedia.org/wiki/Hurricane_Franklin
Harvey,937,130,125000000000,$125.000.000.000,https://en.wikipedia.org/wiki/Hurricane_Harvey
Irma,914,180,77160000000,$77.160.000.000,https://en.wikipedia.org/wiki/Hurricane_Irma
Jose,938,155,2840000,$2.840.000,https://en.wikipedia.org/wiki/Hurricane_Jose_(2017)
Katia,972,105,3250000,$3.250.000,https://en.wikipedia.org/wiki/Hurricane_Katia_(2017)
Maria,908,175,91610000000,$91.610.000.000,https://en.wikipedia.org/wiki/Hurricane_Maria
Nate,981,90,787000000,$787.000.000,https://en.wikipedia.org/wiki/Hurricane_Nate
Ophelia,959,115,65300000,$65.300.000,https://en.wikipedia.org/wiki/Hurricane_Ophelia_(2017)

ACEdata.txt file


Storm Name,ACE,Wiki
Arlene,0.81,http://www.nhc.noaa.gov/data/tcr/AL012017_Arlene.pdf
Bret,0.5225,https://www.nhc.noaa.gov/data/tcr/AL022017_Bret.pdf
Cindy,0.81,https://www.nhc.noaa.gov/data/tcr/AL032017_Cindy.pdf
Don,0.5225,http://www.nhc.noaa.gov/data/tcr/AL052017_Don.pdf
Emily,1.8425,http://www.nhc.noaa.gov/data/tcr/AL062017_Emily.pdf
Franklin,0.7675,http://www.nhc.noaa.gov/data/tcr/AL072017_Franklin.pdf
Gert,7.805,http://www.nhc.noaa.gov/data/tcr/AL082017_Gert.pdf
Harvey,11.4425,http://www.nhc.noaa.gov/data/tcr/AL092017_Harvey.pdf
Irma,64.8925,http://www.nhc.noaa.gov/data/tcr/AL112017_Irma.pdf
Jose,43.28,http://www.nhc.noaa.gov/data/tcr/AL122017_Jose.pdf
Katia,6.055,http://www.nhc.noaa.gov/data/tcr/AL132017_Katia.pdf
Lee,17.925,http://www.nhc.noaa.gov/data/tcr/AL142017_Lee.pdf
Maria,44.805,http://www.nhc.noaa.gov/data/tcr/AL152017_Maria.pdf
Nate,4.13,http://www.nhc.noaa.gov/data/tcr/AL162017_Nate.pdf
Ophelia,14.5225,http://www.nhc.noaa.gov/data/tcr/AL172017_Ophelia.pdf
Phillipe,0.1225,https://www.nhc.noaa.gov/data/tcr/AL182017_Philippe.pdf
Rina,1.6625,http://www.nhc.noaa.gov/data/tcr/AL192017_Rina.pdf

hurricaneYearData.txt file:

Season,Tropical Depressions,Named Storms,Hurricanes,Category ≥2,Major hurricanes (Category≥3),Category≥4,Category 5,Strongest Storm,Wiki
1967,29,8,6,2,1,1,1,Beulah,https://en.wikipedia.org/wiki/Hurricane_Beulah
1968,14,8,5,0,0,0,0,Gladys,https://en.wikipedia.org/wiki/Hurricane_Gladys_(1968)
1969,20,18,12,7,5,1,1,Camille,https://en.wikipedia.org/wiki/Hurricane_Camille
1970,19,10,5,3,2,0,0,Celia,https://en.wikipedia.org/wiki/Hurricane_Celia
1971,22,13,6,2,1,1,1,Edith,https://en.wikipedia.org/wiki/Hurricane_Edith_(1971)
1972,19,7,3,1,0,0,0,Betty,https://en.wikipedia.org/wiki/1972_Atlantic_hurricane_season#Hurricane_Betty
1973,24,8,4,1,1,0,0,Ellen,https://en.wikipedia.org/wiki/1973_Atlantic_hurricane_season#Hurricane_Ellen
1974,21,11,4,3,2,1,0,Carmen,https://en.wikipedia.org/wiki/Hurricane_Carmen
1975,23,9,6,5,3,1,0,Gladys,https://en.wikipedia.org/wiki/Hurricane_Carmen
1976,23,10,6,4,2,0,0,Belle,https://en.wikipedia.org/wiki/Hurricane_Belle_(1976)
1977,16,6,5,1,1,1,1,Anita,https://en.wikipedia.org/wiki/Hurricane_Anita
1978,24,12,5,3,2,2,0,Greta,https://en.wikipedia.org/wiki/Hurricane_Greta-Olivia
1979,26,9,5,3,2,2,1,David,https://en.wikipedia.org/wiki/Hurricane_David
1980,15,11,5,5,2,1,1,Allen,https://en.wikipedia.org/wiki/Hurricane_Allen
1981,22,12,7,4,3,1,0,Harvey,https://en.wikipedia.org/wiki/1981_Atlantic_hurricane_season#Hurricane_Harvey
1982,9,6,2,1,1,1,0,Debby,https://en.wikipedia.org/wiki/Hurricane_Debby_(1982)
1983,7,4,3,1,1,0,0,Alicia,https://en.wikipedia.org/wiki/Hurricane_Alicia
1984,20,13,5,2,1,1,0,Diana,https://en.wikipedia.org/wiki/Hurricane_Diana_(1984)
1985,14,11,7,3,3,1,0,Gloria,https://en.wikipedia.org/wiki/Hurricane_Gloria
1986,10,6,4,1,0,0,0,Earl,https://en.wikipedia.org/wiki/1986_Atlantic_hurricane_season#Hurricane_Earl
1987,14,7,3,1,1,0,0,Emily,https://en.wikipedia.org/wiki/Hurricane_Emily_(1987)
1988,19,12,5,3,3,3,1,Gilbert,https://en.wikipedia.org/wiki/Hurricane_Gilbert
1989,15,11,7,4,2,2,1,Hugo,https://en.wikipedia.org/wiki/Hurricane_Hugo
1990,16,14,8,2,1,0,0,Gustav,https://en.wikipedia.org/wiki/Hurricane_Gustav_(1990)
1991,12,8,4,3,2,1,0,Claudette,https://en.wikipedia.org/wiki/1991_Atlantic_hurricane_season#Hurricane_Claudette
1992,10,7,4,3,1,1,1,Andrew,https://en.wikipedia.org/wiki/Hurricane_Andrew
1993,10,8,4,2,1,0,0,Emily,https://en.wikipedia.org/wiki/Hurricane_Emily_(1993)
1994,12,7,3,1,0,0,0,Florence,https://en.wikipedia.org/wiki/Hurricane_Florence_(1994)
1995,21,19,11,8,5,3,0,Opal,https://en.wikipedia.org/wiki/Hurricane_Opal
1996,13,13,9,6,6,2,0,Edouard,https://en.wikipedia.org/wiki/Hurricane_Edouard_(1996)
1997,9,8,3,1,1,0,0,Erika,https://en.wikipedia.org/wiki/Hurricane_Erika_(1997)
1998,14,14,10,7,3,2,1,Mitch,https://en.wikipedia.org/wiki/Hurricane_Mitch
1999,16,12,8,8,5,5,0,Floyd,https://en.wikipedia.org/wiki/Hurricane_Floyd
2000,19,15,8,4,3,2,0,Keith,https://en.wikipedia.org/wiki/Hurricane_Keith
2001,17,15,9,5,4,2,0,Michelle,https://en.wikipedia.org/wiki/Hurricane_Michelle
2002,14,12,4,3,2,1,0,Isidore,https://en.wikipedia.org/wiki/Hurricane_Isidore
2003,21,16,7,4,3,2,1,Isabel,https://en.wikipedia.org/wiki/Hurricane_Isabel
2004,16,15,9,7,6,4,1,Ivan,https://en.wikipedia.org/wiki/Hurricane_Ivan
2005,31,28,15,8,7,5,4,Wilma,https://en.wikipedia.org/wiki/Hurricane_Wilma
2006,10,10,5,2,2,0,0,Gordon,https://en.wikipedia.org/wiki/Hurricane_Gordon_(2006)
2007,17,15,6,2,2,2,2,Dean,https://en.wikipedia.org/wiki/Hurricane_Dean
2008,17,16,8,6,5,4,0,Ike,https://en.wikipedia.org/wiki/Hurricane_Ike
2009,11,9,3,3,2,1,0,Bill,https://en.wikipedia.org/wiki/Hurricane_Bill_(2009)
2010,21,19,12,9,5,4,0,Igor,https://en.wikipedia.org/wiki/Hurricane_Igor
2011,20,19,7,4,4,2,0,Ophelia,https://en.wikipedia.org/wiki/Hurricane_Ophelia_(2011)
2012,19,19,10,5,2,0,0,Sandy,https://en.wikipedia.org/wiki/Hurricane_Sandy
2013,15,14,2,0,0,0,0,Humberto,https://en.wikipedia.org/wiki/2013_Atlantic_hurricane_season#Hurricane_Humberto
2014,9,8,6,3,2,1,0,Gonzalo,https://en.wikipedia.org/wiki/Hurricane_Gonzalo
2015,12,11,4,2,2,1,0,Joaquin,https://en.wikipedia.org/wiki/Hurricane_Joaquin
2016,16,15,7,4,4,2,1,Matthew,https://en.wikipedia.org/wiki/Hurricane_Matthew
2017,18,17,10,8,6,4,2,Maria,https://en.wikipedia.org/wiki/Hurricane_Maria
2018,16,15,8,5,2,2,1,Michael,https://en.wikipedia.org/wiki/Hurricane_Michael

Processing Code New Tab:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//Initializes the menu.
boolean globalMenu = true;
boolean globalLine = false;
boolean globalScatter = false;
boolean globalPie = false;
boolean globalBar = false;
boolean globalBack = false;
//End of menu initialization

//Initializes the line chart.
Table hurricaneYearData;
ArrayList<HurricaneYearPoints> hurricaneSeason;
//End of line chart initialization.

//Initializes the scatter plot.
Table hurricane2017;
ArrayList<Hur2017Plot> hur2017Points;
//End of scatter plot initialization.

//Initializes the bar graph.
Table aceData;
ArrayList<ACEBar> aceArray;
//End of bar graph initialization.



void setup() {
  size(800, 600);

  //Initializes the ArrayList and table for the line chart.
  hurricaneYearData = loadTable("hurricaneYearData.csv", "header");
  hurricaneSeason = new ArrayList<HurricaneYearPoints>();
  plotHYD(); //This is necessary to pre-load the data graphically.
  //End of initialization of the line chart.

  //Initializes the ArrayList and table for the scatter plot.
  hurricane2017 = loadTable("2017HurricaneData.csv", "header");
  hur2017Points = new ArrayList<Hur2017Plot>();
  plotHurricane2017(); // This is necessary to pre-load the data graphically.
  //End of initialization of the scatter plot.

  //Initializes the ArrayList and table for the bar graph.
  aceData = loadTable("ACEdata.csv", "header");
  aceArray = new ArrayList<ACEBar>();
  plotACEData(); // This is necessary to pre-load the data graphically.
  //End of initialization of the bar graph.
}
void draw() {
  // These act as buttons. If it is on a certain window, then execute that window's code.
  // All code is structured as simple function calls so that the draw function looks tidy.
  if (globalMenu == true) { //The menu

    animationDraw();
    buttonLine();
    buttonScatter();
    buttonPie();
    buttonBar(); 
  }

  if (globalLine == true) { //The line chart
    drawLineGraphSpecs();
    drawHYD();
    backButton(); // A universal back button that acts as the "Menu" button. It is in all charts.
  }

  if (globalScatter == true) { //The scatter plot
    drawScatterPlotSpecs();
    drawHurricane2017();
    drawScatterLabels2();
    backButton();
  }

  if (globalPie == true) { // The unsatisfactory pie chart. It was a pain.
    pieChart(300, majHurData);
    backButton();
  }

  if (globalBar == true) { // The bar graph.
    drawACEDetails();
    plotACEData();
    backButton();
    drawACELabels();
  }
}

void mouseClicked() { //Since Processing only has one mouseClick function, I have to use if/then statements.

  if (globalMenu == true) { // If we are on the menu
    
    if (clickButtonLine() == true) { // If the line graph button is clicked
      globalMenu = false; // Set the menu to false and line graph to true.
      globalLine = true;
      globalScatter = false;
      globalPie = false;
      globalBar = false;
    }
    if (clickButtonScatter() == true) { // If the scatter plot button is clicked
      globalMenu = false;
      globalLine = false;
      globalScatter = true;
      globalPie = false;
      globalBar = false;
    }
    if (clickButtonPie() == true) { // If the pie chart button is clicked
      globalMenu = false;
      globalLine = false;
      globalScatter = false;
      globalPie = true;
      globalBar = false;
    }
    if (clickButtonBar() == true) { // If the bar graph button is clicked
      globalMenu = false;
      globalLine = false;
      globalScatter = false;
      globalPie = false;
      globalBar = true;
    }
  }

  if (globalLine == true) { // Handles the mouse events for the line graph.

    for (int i = 0; i <=51; i++) {
      HurricaneYearPoints hurricanes = hurricaneSeason.get(i);
      float mouseDist = dist(hurricanes.thisYear, hurricanes.thisYearQuant, mouseX, mouseY);
      if (mouseDist <= 5) {
        link(hurricanes.hurWiki);
      }
    }
  }

  if (globalScatter == true) { // Handles the mouse events for the scatter plot.

    for (int i = 0; i <=10; i++) {
      Hur2017Plot hurricane2017 = hur2017Points.get(i);

      float hur2017Dist = dist(hurricane2017.logDamage, hurricane2017.wind, mouseX, mouseY);

      if ((hur2017Dist <= hurricane2017.pressure)) {
        link(hurricane2017.hurricaneURL);
      }
    }
  }

  if (globalBar == true) { // Handles the mouse events for the bar graph
    for (int i = 0; i <=16; i++) {
      ACEBar ace2017 = aceArray.get(i);
      float mxLower = ace2017.posX;
      float myLower = height - 75;
      float mxHigher = ace2017.posX + 30;
      float myHigher = -ace2017.mapACE + height - 75;

      if (mouseX >= mxLower && mouseX <= mxHigher && mouseY <= myLower && mouseY >= myHigher) {
        link(ace2017.aceWiki);
      }
    }
  }

  // The back button. If it's clicked and any one of the graphs are drawn, then it'll access the menu.
  if (clickBackButton() == true && (globalLine == true || globalScatter == true || globalPie == true || globalBar == true)) {
    globalMenu = true;
    globalLine = false;
    globalScatter = false;
    globalPie = false;
    globalBar = false;
  }
}



New tab



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
void buttonLine () { //Draws the aesthetic of the line graph button.
  noStroke();
  fill(230);
  shapeMode(CENTER);
  rect(50, height/2-90, 300, 50, 20);
  textAlign(CENTER, CENTER);
  textSize(17);
  fill(0);
  text("Hurricane Frequency 1967-2018", 200, height/2-65);
}

boolean clickButtonLine() { // The functional line graph button.
  int mx = mouseX ;
  int my = mouseY ;
  int ylower = height/2-40 ;
  int yhigher = height/2-90 ;
  println ( "ylower=" + ylower + ",yhigher=" + yhigher ) ;
  println ( "mx = " + mx + ", my = " + my ) ;
  if (mx <= 350 && mx >= 50 && my >= yhigher && my <= ylower) {
    return true;
  } else {
    return false;
  }
}

void buttonScatter () { // Draws the aesthetic of the scatter plot button.
  noStroke();
  fill(230);
  shapeMode(CENTER);
  rect(50, height/2-30, 300, 50, 20);
  textAlign(CENTER, CENTER);
  textSize(17);
  fill(0);
  text("2017 Costly Hurricanes", 200, height/2-5);
}

boolean clickButtonScatter() { // The functional scatter plot button.
  int mx = mouseX ;
  int my = mouseY ;
  int ylower = height/2+20 ;
  int yhigher = height/2-30 ;
  println ( "y lower=" + ylower + ",y higher=" + yhigher ) ;
  println ( "m x = " + mx + ", m y = " + my ) ;
  if (mx <= 350 && mx >= 50 && my >= yhigher && my <= ylower) {
    println("true");
    return true;
  } else {
    return false;
  }
}

void buttonPie () { // Draws the aesthetic of the pie chart button.
  noStroke();
  fill(230);
  shapeMode(CENTER);
  rect(50, height/2+30, 300, 50, 20);
  textAlign(CENTER, CENTER);
  textSize(17);
  fill(0);
  text("2016-2018 Major Hurricane", 200, height/2+45);
  text("Relative Damages", 200, height/2+60);
}

boolean clickButtonPie() { // The functional pie chart button.
  int mx = mouseX ;
  int my = mouseY ;
  int ylower = height/2+80 ;
  int yhigher = height/2+30 ;
  println ( "y lower=" + ylower + ",y higher=" + yhigher ) ;
  println ( "m x = " + mx + ", m y = " + my ) ;
  if (mx <= 350 && mx >= 50 && my >= yhigher && my <= ylower) {
    println("true");
    return true;
  } else {
    return false;
  }
}


void buttonBar () { // Draws the aesthetic of the bar graph button.
  noStroke();
  fill(230);
  shapeMode(CENTER);
  rect(50, height/2+90, 300, 50, 20);
  textAlign(CENTER, CENTER);
  textSize(17);
  fill(0);
  text("2017 Accumulated Cyclone Energy", 200, height/2+115);
}

boolean clickButtonBar() { // The functional bar graph button.
  int mx = mouseX ;
  int my = mouseY ;
  int ylower = height/2+140 ;
  int yhigher = height/2+90 ;
  println ( "y lower=" + ylower + ",y higher=" + yhigher ) ;
  println ( "m x = " + mx + ", m y = " + my ) ;
  if (mx <= 350 && mx >= 50 && my >= yhigher && my <= ylower) {
    println("true");
    return true;
  } else {
    return false;
  }
}



void backButton() { // Draws the aesthetic of the back button
  fill(0);
  stroke(100);
  rect(10, 10, 30, 30);
  fill(255);
  textAlign(CENTER, CENTER);
  textSize(20);
  text("<", 25, 20);
}

boolean clickBackButton() { // The functional back button
  int mx = mouseX ;
  int my = mouseY ;
  int ylower = 40 ;
  int yhigher = 10 ;
  println ( "y lower=" + ylower + ",y higher=" + yhigher ) ;
  println ( "m x = " + mx + ", m y = " + my ) ;
  if (mx <= 40 && mx >= 10  && my >= yhigher && my <= ylower) {
    println("true");
    return true;
  } else {
    return false;
  }
}

void animationDraw() { // This whole section just draws the menu aesthetics. 
  background(200);
  noStroke();
  fill(255, 255, 0);

  float cloudCX = map(mouseX, 0, width, -100, 900);
  float cloudCY = map(mouseY, 0, height, 100, 500);

  fill(100);
  ellipse(cloudCX, cloudCY, 75, 75);
  ellipse(cloudCX+30, cloudCY, 100, 100);
  ellipse(cloudCX+60, cloudCY, 75, 75);

  ellipse((cloudCX/2), cloudCY-50, 75, 75);
  ellipse((cloudCX/2)+30, cloudCY-50, 100, 100);
  ellipse((cloudCX/2)+60, cloudCY-50, 75, 75);

  ellipse((cloudCX/2*3), cloudCY+200, 75, 75);
  ellipse((cloudCX/2*3)+30, cloudCY+200, 100, 100);
  ellipse((cloudCX/2*3)+60, cloudCY+200, 75, 75);

  ellipse((cloudCX-300), cloudCY+300, 75, 75);
  ellipse((cloudCX-300)+30, cloudCY+300, 100, 100);
  ellipse((cloudCX-300)+60, cloudCY+300, 75, 75);


  //

  float cloudAX = map(mouseX, 0, width, 300, 500);
  float cloudAY = map(mouseY, 0, height, 200, 230);

  fill(140);

  ellipse(cloudAX, cloudAY, 75, 75);
  ellipse(cloudAX+30, cloudAY, 100, 100);
  ellipse(cloudAX+60, cloudAY, 75, 75);

  ellipse((cloudAX/2), cloudAY-50, 75, 75);
  ellipse((cloudAX/2)+30, cloudAY-50, 100, 100);
  ellipse((cloudAX/2)+60, cloudAY-50, 75, 75);

  ellipse((cloudAX/2*3), cloudAY+200, 75, 75);
  ellipse((cloudAX/2*3)+30, cloudAY+200, 100, 100);
  ellipse((cloudAX/2*3)+60, cloudAY+200, 75, 75);

  ellipse((cloudAX-300), cloudAY+300, 75, 75);
  ellipse((cloudAX-300)+30, cloudAY+300, 100, 100);
  ellipse((cloudAX-300)+60, cloudAY+300, 75, 75);

  //

  fill(180);

  float cloudBX = map(mouseX, 0, width, -200, 1000);
  float cloudBY = map(mouseY, 0, height, 300, 350);


  ellipse((cloudBX-300), cloudBY+300, 200, 200);
  ellipse((cloudBX-300)+100, cloudBY+300, 250, 250);
  ellipse((cloudBX-300)+200, cloudBY+300, 200, 200);

  ellipse((cloudBX/2-400), cloudBY-300, 200, 200);
  ellipse((cloudBX/2-400)+100, cloudBY-300, 250, 250);
  ellipse((cloudBX/2-400)+200, cloudBY-300, 200, 200);


  stroke(100, 150, 255);
  float x1 = random (0, width);
  float y1 = random (0, height);

  float x2 = random (0, width);
  float y2 = random (0, height);

  float x3 = random (0, width);
  float y3 = random (0, height);

  float x4 = random (0, width);
  float y4 = random (0, height);

  line(x1, y1, x1-50, y1-10);
  line(x2, y2, x2-50, y2-10);
  line(x3, y3, x3-50, y3-10);
  line(x4, y4, x4-50, y4-10);

  noStroke();

  fill(0);
  textSize(60);
  textAlign(CENTER);
  text("Hurricane Data", width-225, height-300);
  text("Visualization", width-225, height-200);
}



New tab



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
class ACEBar { // Defines the class for the bar graph.

  float aceValue;
  float mapACE;
  String hurr2017Name;
  String aceWiki;
  float posX;
  // Passes in the constructor arguments.
  ACEBar(float tempACEValue, float tempMapACE, String tempHurr2017Name, String tempACEWiki, float tempPosX) {
    aceValue = tempACEValue;
    mapACE = tempMapACE;
    hurr2017Name = tempHurr2017Name;
    aceWiki = tempACEWiki;    
    posX = tempPosX;
  }

  void makeBar() { // Draws the actual data-containing portion of the graph.
    shapeMode(BOTTOM);
    fill(100, 100, 255);
    strokeWeight(3);
    stroke(0, 0, 255);
    rect(posX, height-75, 30, -mapACE );
  }

  void makeBarLabel() { // Handles the mouseover event.
    if (mouseX >= posX && mouseX <= posX+30 && mouseY <= height-75 && mouseY >= -mapACE+height-75) {
      fill(255, 0, 0);
      textSize(25);
      textAlign(CENTER, BOTTOM);
      text(hurr2017Name + " ACE: " + aceValue, posX+15, -mapACE+(height-75));
    }
  }
}

void drawACEDetails() { //Draws the window and the grid.
  background(100, 150, 255);
  fill(0);
  rect(10, 50, 775, 540);
  fill(255);
  stroke(100);
  rect(75, 100, 650, 450);
  int initi = 0;
  float inity = 525;
  int labelY = 0;
  while (initi <= 8) {
    stroke(150);
    strokeWeight(1);
    line(75, inity, 725, inity);
    textSize(15);
    text(labelY, 65, inity);
    inity -= 50;
    initi++;
    labelY += 8;
  }
}

void drawACELabels() { // Handles the labels 

  textAlign(CENTER, CENTER);
  textSize(37);
  fill(255);
  text("2017 ACE", width/2, 75);

  textSize(17);
  text("Accumulated Cyclone Energy (ACE) is a measure of overall hurricane intensity.", width/2, 10);
  text("Click column for detailed report for the cyclone.", width/2, 30);

  textSize(30);
  text("2017 Atlantic Cyclones", width/2, 565);

  textSize(35);
  rotate(-PI/2);

  text("Accumulated Cyclone Energy", -325, 30);
}
void plotACEData() { // The part that actually processes the data.
  float posX = 100.0;
  for (TableRow row : aceData.rows()) { 

    String hurr2017Name = row.getString("Storm Name");
    float hurr2017ACE = row.getFloat("ACE");
    String hurr2017Wiki = row.getString("Wiki");

    float mapACE = map(hurr2017ACE, 0, 65, 0, 400);
    aceArray.add(new ACEBar(hurr2017ACE, mapACE, hurr2017Name, hurr2017Wiki, posX));
    posX += 35.0;
  }
  for (int i=0; i<= 16; i++) {
    ACEBar ace2017 = aceArray.get(i);
    ace2017.makeBar();
  }
  for (int i=0; i<= 16; i++) {
    ACEBar ace2017 = aceArray.get(i);
    ace2017.makeBarLabel();
  }
}



New tab



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
class HurricaneYearPoints { // Defines the class for the line graph.

  float thisYear;
  float thisYearQuant;
  float previousYear;
  float previousYearQuant;
  String hurricaneName;
  String hurWiki;
  // Passes in the constructor arguments.
  HurricaneYearPoints(String tempHurWiki, float tempThisYear, float tempThisYearQuant, float tempPreviousYear, float tempPreviousYearQuant, String tempHurricaneName) {
    thisYear = tempThisYear;
    thisYearQuant = tempThisYearQuant;
    previousYear = tempPreviousYear;
    previousYearQuant = tempPreviousYearQuant;
    hurricaneName = tempHurricaneName;
    hurWiki = tempHurWiki;
  }

  void makeLine() { // Draws the actual line and a circle to make the points clearer.

    stroke(0, 100, 255);
    strokeWeight(2);
    line(previousYear, previousYearQuant, thisYear, thisYearQuant);
    fill(0, 255, 0);
    ellipse(thisYear, thisYearQuant, 10, 10);
  }

  void makeLabel() { // Handles the mouseover event for the points.
    fill(255, 0, 0);
    float mouseDist = dist(thisYear, thisYearQuant, mouseX, mouseY);
    if ( mouseDist <= 5) {
      textSize(20);
      textAlign(RIGHT);
      text(hurricaneName, thisYear, thisYearQuant-30); 
      textAlign(LEFT);
      text(int(map(thisYear, 50, width-50, 1967, 2018)), thisYear+5, thisYearQuant-30);
    }
  }
}

void plotHYD() { //The part that actually handles the data for the line graph. It doesn't draw it to the screen.
  float previousYearSeason = 1967;
  float previousNamedStorms = 8;
  for (TableRow row : hurricaneYearData.rows()) {

    float hurricaneYearSeason = row.getFloat("Season");
    float namedStorms = row.getFloat("Named Storms");
    String strongestStorm = row.getString("Strongest Storm");
    String yearWiki = row.getString("Wiki");

    float newhurricaneYearSeason = map(hurricaneYearSeason, 1967, 2018, 50, width-50);
    float newnamedStorms = map(namedStorms, 0, 28, height-50, 200);
    float newpreviousYearSeason = map(previousYearSeason, 1967, 2018, 50, width-50);
    float newpreviousNamedStorms = map(previousNamedStorms, 0, 28, height-50, 200);

    hurricaneSeason.add(new HurricaneYearPoints(yearWiki, newhurricaneYearSeason, newnamedStorms, newpreviousYearSeason, newpreviousNamedStorms, strongestStorm));
    previousYearSeason = hurricaneYearSeason;
    previousNamedStorms = namedStorms;
  }
  //End of Hurricane Year Data Line Graph draw portion.

  for (int i=0; i<= 51; i++) {
    HurricaneYearPoints hurricanes = hurricaneSeason.get(i);
    hurricanes.makeLabel();
    hurricanes.makeLine();
  }
}

void drawHYD() { // Actually draws it to the screen.
  plotHYD();
  for (int i=0; i<= 51; i++) {
    HurricaneYearPoints hurricanes = hurricaneSeason.get(i);
    hurricanes.makeLabel();
    hurricanes.makeLine();
  }
}

void drawLineGraphSpecs() { // Draws the labeling of the code.
  background(100, 150, 255);
  stroke(0, 255, 255);
  fill(0);
  rect(10, 75, width-20, height-85);
  fill(255);
  rect(50, 150, width-100, height-200);

  int initX = 50;
  int initY = height-50;
  int i = 0;
  int j = 0;
  int xMark = 0;
  int yMark = 1967;

  strokeWeight(1);


  while (i <= 12) {
    while (j <=8) {
      stroke(150);
      line(50, initY, width-50, initY);
      initY = initY - 50;
      j++;

      textAlign(CENTER);
      textSize(20);
      fill(255);
      text(xMark, width-30, initY+50);
      xMark = xMark + 4;
    }
    stroke(200);
    line(initX, 150, initX, height-50);
    initX = initX+(width-100)/13+2;
    i++;

    textAlign(CENTER, CENTER);
    textSize(15);
    fill(255);
    text(yMark, initX-50, height-42);
    yMark = yMark + 4;
  }

  text(2018, width-50, height-42);
  textSize(20);
  text("Year", width/2, height-25);
  pushMatrix();
  rotate(-PI/2);
  translate(-375, -275);
  text("Named Storms Per Season", 20, height/2);
  popMatrix();

  textSize(35);
  textAlign(CENTER, CENTER);
  text("Hurricane Frequency In The Satellite Era", width/2, 110);
  textSize(20);
  text("Mouse over point for more details.", width/2, 25);
  text("Click on point to learn more.", width/2, 50);
}



New tab



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
String [][] majHurData = { 
  // Data is simple enough to not need a csv and works better as an array over a table
  {"Harvey", "125", "https://en.wikipedia.org/wiki/Hurricane_Harvey"}, 
  {"Matthew", "16.47", "https://en.wikipedia.org/wiki/Hurricane_Matthew"}, 
  {"Irma", "77.16", "https://en.wikipedia.org/wiki/Hurricane_Irma"}, 
  {"Maria", "91.61", "https://en.wikipedia.org/wiki/Hurricane_Maria"}, 
  {"Michael", "25.1", "https://en.wikipedia.org/wiki/Hurricane_Michael"}, 
};


void pieChart(float diameter, String [][] data) { // This section is a modified version of the 
//code in the documentation on pie charts.
  background(100, 150, 255);
  fill(0);
  rect(width/2-250, height/2-250, 500, 500);
  textAlign(CENTER, CENTER);
  textSize(25);
  fill(255);
  text("Relative Damages of Devastating ", width/2, height/2-235);
  text("Hurricanes (2016-2018)", width/2, height/2-205);
  text("This is a static graphic.", width/2, 30);

  float lastAngle = 0;
  float totalDam = 0;

  for (int i = 0; i < data.length; i++) {
    totalDam = totalDam + float(majHurData[i][1]);
  }

  for (int i = 0; i < data.length; i++) {
    float dataGreen = map(i, 0, data.length, 150, 255);
    float dataRed = map(i, 0, data.length, 50, 255);
    float dataBlue = map(i, 0, data.length, 200, 255);
    float damAngle = (float(data[i][1])/totalDam)*360;
    fill(dataRed, dataGreen, dataBlue);
    arc(width/2, height/2, diameter, diameter, lastAngle, lastAngle+radians(damAngle));
    lastAngle += radians(damAngle);

    pushMatrix();
    translate(width/2, height/2);
    float xPos =(200 * cos(lastAngle));
    float yPos =(200 * sin(lastAngle));

    float textGreen = map(i, 0, data.length, 150, 255);
    float textRed = map(i, 0, data.length, 50, 255);
    float textBlue = map(i, 0, data.length, 200, 255);



    fill(textRed, textGreen, textBlue);
    textSize(20);
    textAlign(CENTER, CENTER);
    text(data[i][0], xPos, yPos);
    popMatrix();
  }
}



New tab



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
class Hur2017Plot { // Defines the class for the scatter graph.

  float wind;
  float logDamage;
  float pressure;
  float unmapWind;
  String hurricaneURL;
  String hurricaneName;
  String damageString;
  // Passing in the constructor arguments.
  Hur2017Plot(String tempDamageString, float tempUnmapWind, float tempWind, float tempLogDamage, float tempPressure, String tempURL, String tempName) {
    unmapWind = tempUnmapWind;
    wind = tempWind;
    logDamage = tempLogDamage;
    pressure = tempPressure;
    hurricaneURL = tempURL;
    hurricaneName = tempName;
    damageString = tempDamageString;
  }

  void plotHurCircle() { // Plots the actual circle and defines its color.

    if (unmapWind >= 157.0) {
      stroke(180, 0, 0);
      fill(255, 20, 0);
    } else if (unmapWind >= 130.0) {
      stroke(255, 80, 0);
      fill(255, 100, 0);
    } else if (unmapWind >= 111.0) {
      stroke(255, 120, 0);
      fill(255, 150, 0);
    } else if (unmapWind >= 96.0) {
      stroke(255, 180, 70);
      fill(255, 200, 100);
    } else if (unmapWind >= 74.0) {
      stroke(255, 200, 180);
      fill(255, 255, 200);
    } else if (unmapWind < 74.0) {
      stroke(200, 255, 255);
      fill(0, 255, 255);
    }

    shapeMode(CENTER);
    textSize(12);
    textAlign(CENTER, CENTER);
    ellipse(logDamage, wind, pressure, pressure);
    fill(0);
    text(hurricaneName, logDamage, wind-pressure/2-5);
  }

  void hurInfoBox() { // Handles the mouseover event for each data point.
    float hur2017Dist = dist(logDamage, wind, mouseX, mouseY);
    int unmapPressure = int(map(pressure, 60, 10, 908, 1007));

    if (hur2017Dist <= pressure/2) {
      fill(255);
      rect(width-485, 160, 230, 230);

      textAlign(LEFT, BOTTOM);
      textSize(13);
      fill(0);
      text("Name: " + hurricaneName, width-475, 190);
      text("Min Pressure: " + unmapPressure + "mbar", width-475, 220);
      text("Damage Cost: " + damageString, width-475, 250);

      if (unmapWind >= 157.0) {
        text("Wind Speed: " + int(unmapWind) + "mph (Category 5)", width-475, 280);
      } else if (unmapWind >= 130.0) {
        text("Wind Speed: " + int(unmapWind) + "mph (Category 4)", width-475, 280);
      } else if (unmapWind >= 111.0) {
        text("Wind Speed: " + int(unmapWind) + "mph (Category 3)", width-475, 280);
      } else if (unmapWind >= 96.0) {
        text("Wind Speed: " + int(unmapWind) + "mph (Category 2)", width-475, 280);
      } else if (unmapWind >= 74.0) {
        text("Wind Speed: " + int(unmapWind) + "mph (Category 1)", width-475, 280);
      } else if (unmapWind < 74.0) {
        text("Wind Speed: " + int(unmapWind) + "mph (Tropical Storm)", width-475, 280);
      }

      textSize(11);
      text("Click for more info. Wind speed is", width-475, 320);
      text("represented as max sustained gust", width-475, 340);
      text("(over one minute long). Larger circles", width-475, 360);
      text("represent lower barometric pressure.", width-475, 380);
    }
  }
}

void plotHurricane2017() { // Handles the actual data processing.

  int damMapMin=150;
  int damMapMax=width-100;
  int windMapMin=height-100;
  int windMapMax=200;


  for (TableRow hur2017Row : hurricane2017.rows()) {
    String hurName = hur2017Row.getString("Storm Name");
    String hurURL = hur2017Row.getString("Wiki");
    String damString = hur2017Row.getString("Damage String");
    float hurPressure = hur2017Row.getFloat("Minimum Pressure");
    float maxWind = hur2017Row.getFloat("Max Wind Speed");
    float hurDamage = hur2017Row.getFloat("Damage");
    float logHurDamage = log(hurDamage);

    float mapLogHurDamage = map(logHurDamage, 14.85, 27.55, damMapMin, damMapMax);
    float mapHurPressure = map(hurPressure, 908, 1007, 60, 10); 
    float mapMaxWind = map(maxWind, 50, 180, windMapMin, windMapMax);

    hur2017Points.add(new Hur2017Plot(damString, maxWind, mapMaxWind, mapLogHurDamage, mapHurPressure, hurURL, hurName));
  }
}

void drawHurricane2017() { // The part that actually plots it to the screen.
  plotHurricane2017();
  for (int i = 0; i <=10; i++) {
    Hur2017Plot hurricane2017 = hur2017Points.get(i);
    hurricane2017.plotHurCircle();
    hurricane2017.hurInfoBox();
  }
}

void drawScatterPlotSpecs() { // A two-part thing that draws the labels and box and stuff.
  background(100, 150, 255);
  stroke(100);
  fill(0);
  rect(25, 100, width-50, height-110);  

  stroke(0);
  fill(255);
  rect(100, 150, width-250, height-225);

  int windMapMin=height-100;
  int windMapMax=200;


  float tropStorm = map(50, 50, 180, windMapMin, windMapMax);
  float cat1 = map(74, 50, 180, windMapMin, windMapMax);
  float cat2 = map(96, 50, 180, windMapMin, windMapMax);
  float cat3 = map(111, 50, 180, windMapMin, windMapMax);
  float cat4 = map(130, 50, 180, windMapMin, windMapMax);
  float cat5 = map(157, 50, 180, windMapMin, windMapMax);

  textAlign(RIGHT, CENTER);
  textSize(15);
  stroke(200);
  line(100, cat1, width-150, cat1);
  text("74", 95, cat1);
  line(100, cat2, width-150, cat2);
  text("96", 95, cat2);
  line(100, cat3, width-150, cat3);
  text("111", 95, cat3);
  line(100, cat4, width-150, cat4);
  text("130", 95, cat4); 
  line(100, cat5, width-150, cat5);
  text("157", 95, cat5);

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

  stroke(200, 255, 255);
  fill(0, 255, 255);
  rect(width-140, 250, 20, 20);
  text("Trop Storm", width-115, 260);

  stroke(255, 200, 180);
  fill(255, 255, 200);
  rect(width-140, 280, 20, 20);
  text("Category 1", width-115, 290);

  stroke(255, 180, 70);
  fill(255, 200, 100);
  rect(width-140, 310, 20, 20);
  text("Category 2", width-115, 320);

  stroke(255, 120, 0);
  fill(255, 150, 0);
  rect(width-140, 340, 20, 20);
  text("Category 3", width-115, 350);

  stroke(255, 80, 0);
  fill(255, 100, 0);
  rect(width-140, 370, 20, 20);
  text("Category 4", width-115, 380);

  stroke(180, 0, 0);
  fill(255, 20, 0);
  rect(width-140, 400, 20, 20);
  text("Category 5", width-115, 410);

  stroke(200);
  int counti = 0;
  int offsetY = 100; 
  while (counti <= 5) {
    line(offsetY, 150, offsetY, height-75);
    counti++;
    offsetY = offsetY + (width-250)/6;
  }
}

void drawScatterLabels2() { // The other part because things were getting cluttered in the other function.

  int offsetY = (width-250)/6;

  textAlign(CENTER, TOP);
  fill(255);
  textSize(20);
  text("$1M", 100, height-75);
  text("$10M", 100+offsetY, height-75);
  text("$100M", 100+2*offsetY, height-75);
  text("$1B", 100+3*offsetY, height-75);
  text("$10B", 100+4*offsetY, height-75);
  text("$100B", 100+5*offsetY, height-75);
  text("$1T", 100+6*offsetY, height-75);

  textSize(30);
  text("Logarithmic Cost of Damage", 100+3*offsetY, height-55);

  pushMatrix();
  rotate(-PI/2);
  translate(-350, 30);
  text("Wind Speed (mph)", 0, 0);
  popMatrix();

  textSize(40);
  text("2017 Costly Atlantic Cyclones", width/2, 100);

  textSize(20);
  text("Mouse over circle for more details.", width/2, 25);
  text("Click circle to learn more.", width/2, 50);
  text("Bear in mind the x-axis is on a logarithmic scale.", width/2, 75);
}