-
Notifications
You must be signed in to change notification settings - Fork 0
/
climate-change.js
253 lines (207 loc) · 8.02 KB
/
climate-change.js
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
function ClimateChange() {
// Name for the visualisation to appear in the menu bar.
this.name = 'Climate Change';
// Each visualisation must have a unique ID with no special
// characters.
this.id = 'climate-change';
// Names for each axis.
this.xAxisLabel = 'year';
this.yAxisLabel = '℃';
// Layout object to store all common plot layout parameters and methods.
var marginSize = 35;
this.layout = {
marginSize: marginSize,
// Locations of margin positions. Left and bottom have double margin
// size due to axis and tick labels.
leftMargin: marginSize * 2,
rightMargin: width - marginSize,
topMargin: marginSize,
bottomMargin: height - marginSize * 2,
pad: 5,
plotWidth: function () {
return this.rightMargin - this.leftMargin;
},
plotHeight: function () {
return this.bottomMargin - this.topMargin;
},
// Boolean to enable/disable background grid.
grid: false,
// Number of axis tick labels to draw so that they are not drawn on
// top of one another.
numXTickLabels: 8,
numYTickLabels: 8,
};
// Property to represent whether data has been loaded.
this.loaded = false;
// Preload the data. This function is called automatically by the
// gallery when a visualisation is added.
this.preload = function () {
var self = this;
this.data = loadTable(
'./data/tech-diversity/surface-temperature.csv', 'csv', 'header',
// Callback function to set the value
// this.loaded to true.
function (table) {
self.loaded = true;
});
};
this.setup = function () {
// Font defaults.
textSize(16);
textAlign('center', 'center');
// Set min and max years: assumes data is sorted by year.
this.minYear = this.data.getNum(0, 'year');
this.maxYear = this.data.getNum(this.data.getRowCount() - 1, 'year');
// Find min and max temperature for mapping to canvas height.
this.minTemperature = min(this.data.getColumn('temperature'));
this.maxTemperature = max(this.data.getColumn('temperature'));
// Find mean temperature to plot average marker.
this.meanTemperature = mean(this.data.getColumn('temperature'));
// Count the number of frames drawn since the visualisation
// started so that we can animate the plot.
this.frameCount = 0;
// Create sliders to control start and end years. Default to
// visualise full range.
this.startSlider = createSlider(this.minYear,
this.maxYear - 1,
this.minYear,
1);
this.startSlider.position(400, 10);
this.endSlider = createSlider(this.minYear + 1,
this.maxYear,
this.maxYear,
1);
this.endSlider.position(600, 10);
};
this.destroy = function () {
this.startSlider.remove();
this.endSlider.remove();
};
this.draw = function () {
if (!this.loaded) {
console.log('Data not yet loaded');
return;
}
// Prevent slider ranges overlapping.
if (this.startSlider.value() >= this.endSlider.value()) {
this.startSlider.value(this.endSlider.value() - 1);
}
this.startYear = this.startSlider.value();
this.endYear = this.endSlider.value();
// Draw all y-axis tick labels.
drawYAxisTickLabels(this.minTemperature,
this.maxTemperature,
this.layout,
this.mapTemperatureToHeight.bind(this),
1);
// Draw x and y axis.
drawAxis(this.layout);
// Draw x and y axis labels.
drawAxisLabels(this.xAxisLabel,
this.yAxisLabel,
this.layout);
// Plot average line.
stroke(200);
strokeWeight(1);
line(this.layout.leftMargin,
this.mapTemperatureToHeight(this.meanTemperature),
this.layout.rightMargin,
this.mapTemperatureToHeight(this.meanTemperature));
// Plot all temperatures between startYear and endYear using the
// width of the canvas minus margins.
var previous;
var numYears = this.endYear - this.startYear;
var segmentWidth = this.layout.plotWidth() / numYears;
// Count the number of years plotted each frame to create
// animation effect.
var yearCount = 0;
// Loop over all rows but only plot those in range.
for (var i = 0; i < this.data.getRowCount(); i++) {
// Create an object to store data for the current year.
var current = {
// Convert strings to numbers.
'year': this.data.getNum(i, 'year'),
'temperature': this.data.getNum(i, 'temperature')
};
if (previous != null
&& current.year > this.startYear
&& current.year <= this.endYear) {
// Draw background gradient to represent colour temperature of
// the current year.
noStroke();
fill(this.mapTemperatureToColour(current.temperature));
rect(this.mapYearToWidth(previous.year),
this.layout.topMargin,
segmentWidth,
this.layout.plotHeight());
// Draw line segment connecting previous year to current
// year temperature.
stroke(0);
line(this.mapYearToWidth(previous.year),
this.mapTemperatureToHeight(previous.temperature),
this.mapYearToWidth(current.year),
this.mapTemperatureToHeight(current.temperature));
// The number of x-axis labels to skip so that only
// numXTickLabels are drawn.
var xLabelSkip = ceil(numYears / this.layout.numXTickLabels);
// Draw the tick label marking the start of the previous year.
if (yearCount % xLabelSkip == 0) {
drawXAxisTickLabel(previous.year, this.layout,
this.mapYearToWidth.bind(this));
}
// When six or fewer years are displayed also draw the final
// year x tick label.
if ((numYears <= 6
&& yearCount == numYears - 1)) {
drawXAxisTickLabel(current.year, this.layout,
this.mapYearToWidth.bind(this));
}
yearCount++;
}
// Stop drawing this frame when the number of years drawn is
// equal to the frame count. This creates the animated effect
// over successive frames.
if (yearCount >= this.frameCount) {
break;
}
// Assign current year to previous year so that it is available
// during the next iteration of this loop to give us the start
// position of the next line segment.
previous = current;
}
// Count the number of frames since this visualisation
// started. This is used in creating the animation effect and to
// stop the main p5 draw loop when all years have been drawn.
this.frameCount++;
// Stop animation when all years have been drawn.
if (this.frameCount >= numYears) {
//noLoop();
}
};
this.mapYearToWidth = function (value) {
return map(value,
this.startYear,
this.endYear,
// Draw left-to-right from margin.
this.layout.leftMargin,
this.layout.rightMargin);
};
this.mapTemperatureToHeight = function (value) {
return map(value,
this.minTemperature,
this.maxTemperature,
// Lower temperature at bottom.
this.layout.bottomMargin,
// Higher temperature at top.
this.layout.topMargin);
};
this.mapTemperatureToColour = function (value) {
var red = map(value,
this.minTemperature,
this.maxTemperature,
0,
255);
var blue = 255 - red;
return color(200, 120, blue, 100);
};
}