-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.js
More file actions
349 lines (298 loc) · 9.48 KB
/
Copy pathmain.js
File metadata and controls
349 lines (298 loc) · 9.48 KB
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
/*
Name: Sea Level Rise Data Words Visualization
Purpose: To visualize data of the rise of sea levels from 1993 to 2024 using words.
Author: Shreeya Prasanna
Created: 11-April-2025
Updated: 17-April-2025
Data obtained from: US Gouvernment https://earth.gov/sealevel/sea-level-explorer/
*/
let table;
let currentYear = 1993;
let countryData = {};
let countryWords = {};
let shapes = {};
let showStart = true;
let prevButton, nextButton;
let loadedCountries = 0;
let lines, markov, data1, data2;
let generatedText = " ";
let countryKeywords = {
'Canada': 'maple',
'Solomon Islands': 'lagoon',
'Brazil': 'jungle',
'Egypt': 'tomb',
'UAE': 'camel',
'India': 'guru',
'France': 'café',
'Australia': 'reef',
'Barbados': 'beach'
};
/**
* Preloads the required the sea level CSV and two text files for RiTa Markov generation before the sketch starts.
*
* @returns {void}
*/
function preload() {
table = loadTable('annual_sea_level_rise.csv', 'csv', 'header');
data1 = loadStrings('data1.txt');
data2 = loadStrings('data2.txt');
}
/**
* Initializes the sketch environment and loads the RiTa Markov model.
*
* @returns {void}
*/
function setup() {
createCanvas(windowWidth, windowHeight);
fetchAllCountryWords();
//creates 'year' navigation buttons
prevButton = createButton('Previous Year');
nextButton = createButton('Next Year');
prevButton.position((width / 2 - 200), 20);
nextButton.position((width / 2 + 100), 20);
prevButton.hide();
nextButton.hide();
//initializes RiTa Markov chain generator
lines = ["click to (re)generate"];
//setting order as n=4
markov = RiTa.markov(4);
// Loads the text from both files
markov.addText(data1.join(' '));
markov.addText(data2.join(' '));
generateText();
}
/**
* Adjusts the canvas when window size changes.
*
* @returns {void}
*/
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
/**
* Handles rendering of visulization, scene-switching and animated word shapes.
*
* @returns {void}
*/
function draw() {
background(255);
textAlign(CENTER, TOP);
//controls 'loading' text display in case loading/setup phases takes some time
if (!shapesReady()) {
textSize(24);
text('Loading...', width / 2, height / 2);
return;
}
//creates the first scene
if (showStart) {
textSize(32);
//provides context about the data visualization
text('Sea Level Rise from 1993-2024', width / 2, 40);
textSize(20);
text('Click anywhere to start', width / 2, 80);
return;
}
//displays current year and environmental message
textSize(32);
let yearTextHeight = 23;
text('Year: ' + currentYear, width / 2, 20);
//ensures the message appears below the 'year' text with appropriate spacing
let generatedTextY = 20 + yearTextHeight + 20;
let maxWidth = 350;
let maxHeight = height - generatedTextY - 20;
textSize(14);
//calls the generateText function to make the message appear
text(generatedText, 600, generatedTextY, maxWidth, maxHeight);
//draws country-symbolic shapes in a 3-column grid layout
let countries = Object.keys(countryKeywords);
let cols = 3;
let rows = ceil(countries.length / cols);
let spacingX = width / (cols + 1);
let spacingY = height / (rows + 2);
for (let i = 0; i < countries.length; i++) {
let country = countries[i];
let info = countryData[currentYear][country];
if (!info) continue;
let col = i % cols;
let rowIndex = floor(i / cols);
let x = spacingX * (col + 1);
let y = spacingY * (rowIndex + 1);
//adjusts the spacing above the first row to avoid overlap with 'year' text and buttons
if (rowIndex === 0) {
y += 100;
}
//manually adjusts the y-position for animated word shapes to avoid overlap
if (country === 'Canada') {
y -= 100; //moves it up
}
if (country === 'Egypt') {
y += 100; //moves it down
}
if (country === 'France') {
y -= 20;
}
if (country === 'Solomon Islands') {
y += 50;
}
if (country === 'UAE') {
y += 110;
}
if (country === 'Australia') {
y += 100;
}
if (country === 'Brazil') {
y -= 55;
}
if (country === 'India') {
y += 80;
}
if (country === 'Barbados') {
y += 120;
}
push();
translate(x, y);
//controls the size and pulsing effect of the animated word shapes
scale(min(info.sizeFactor, 2));
info.shape.update();
info.shape.display(info.words, 0.5);
pop();
}
}
/**
* Manages mouse interaction on start screen to trigger main visualization.
*
* @returns {void}
*/
function mousePressed() {
if (showStart && shapesReady()) {
//executes the following only when scenes is switched from 'start' scene
showStart = false;
//adjusts positions of the 'year' navigation buttons
prevButton.position((width / 2 - 200), 20);
nextButton.position((width / 2 + 100), 20);
//shows the buttons
prevButton.show();
nextButton.show();
//controls button functionality
prevButton.mousePressed(() => changeYear(-1));
nextButton.mousePressed(() => changeYear(1));
//ensures display of RiTa Markov text generation
generateText();
}
}
/**
*Generates a Markov-based sentence to accompany the visualization.
*
* @returns {void}
*/
function generateText() {
//generates text to show urgency of the sea level rise issue based on the current year
lines = markov.generate(1.5);
generatedText = lines.join(' ');
y = 80;
}
/**
*changes the current year by a given step and regenerates text.
*
* @param {number} step - number of years to shift (+1 or -1)
*
* @returns {void}
*/
function changeYear(step) {
let newYear = currentYear + step;
if (countryData[newYear]) {
currentYear = newYear;
generateText(); //regenerates the RiTa Markov text only when year changes
}
}
/**
* Checks if all country word lists and shapes have been loaded.
*
* @returns {boolean} - whether all animated word shapes and country-associated words are ready for display.
*/
function shapesReady() {
return Object.keys(countryKeywords).every(c => countryWords[c] && shapes[c]);
}
/**
* Processes the CSV data into a structured object and calculates visual size factor from the difference of annual sea level values.
*
* @returns {void}
*/
function processData() {
let previousSeaLevel = {};
//processes the CSV data
for (let row of table.rows) {
let year = int(row.get('Year'));
let country = row.get('Country');
let seaLevelRise = float(row.get('Average Satellite Altimetry')); //extracts sea level value for a particular year
if (!countryData[year]) countryData[year] = {};
if (countryKeywords[country] && countryWords[country]) {
let words = countryWords[country]; //pre-fetched related words
let shape = getShapeForCountry(country); //retrieves shape object for the country
//calculates visual scaling factor based on change from previous year
let sizeFactor = 1;
if (previousSeaLevel[country] !== undefined) {
let delta = seaLevelRise - previousSeaLevel[country];
sizeFactor = map(delta, -7, 7, 0.8, 1.1);
sizeFactor = constrain(sizeFactor, 0.8, 1.2); //clamps the scale factor within a visual range
}
//store current sea level as previous for next iteration
previousSeaLevel[country] = seaLevelRise;
//stores the processed data for visualization use
countryData[year][country] = {
seaLevelRise,
words,
shape,
sizeFactor
};
}
}
}
/**
* Fetches related words for each country's word shape based on their respective intialized keyword from the Wordnik API.
*
* @returns {void}
*/
function fetchAllCountryWords() {
for (let country in countryKeywords) {
let keyword = countryKeywords[country]; //gets keyword representing country
let url = `https://api.wordnik.com/v4/word.json/${keyword}/relatedWords?useCanonical=false&limitPerRelationshipType=10&api_key=vr64p5wcw7uog9d9lywbmqlg19v7t30x4glsmekg7n184ydwa`; //initializes WordNik API query
//fetch related words asynchronously from Wordnik
loadJSON(url, (data) => {
let words = [];
for (let rel of data) {
if (rel.words) words = words.concat(rel.words); //merge words from all relationship types
}
countryWords[country] = words.slice(0, 10); //keep to max 10 words
shapes[country] = getShapeForCountry(country);
loadedCountries++; //tracks how many countries have been loaded
//only process data once all countries' words and shapes are ready
if (loadedCountries === Object.keys(countryKeywords).length) {
processData();
}
});
}
}
/**
* Returns the shape object associated with a specific country.
*
* @param {string} country - the name of the country
*
* @returns {Object} - a wordshape object
*/
function getShapeForCountry(country) {
let words = countryWords[country] || []; //fallback to empty array if words missing
//return a new object instance for each countries' specific shapes
switch (country) {
case 'Canada': return new CNTower(words);
case 'Solomon Islands': return new PalmTree(words);
case 'Brazil': return new ChristStatue(words);
case 'Egypt': return new Pyramid(words);
case 'UAE': return new GoldenFalcon(words);
case 'India': return new AshokaChakra(words);
case 'France': return new Croissant(words);
case 'Australia': return new Fish(words);
case 'Barbados': return new Sun(words);
default: return new WordShape(countryWords[country]); //generic shape if no specific match
}
}