-
Notifications
You must be signed in to change notification settings - Fork 0
/
lib.js
416 lines (332 loc) · 14 KB
/
lib.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
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
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
// controls the throbber
let throbber = function(on) {
if (on === 0) {
document.getElementById("overlay").style.display = "none";
} else {
document.getElementById("overlay").style.display = "block";
}
};
// we need to keep track of the file loading process
let queue_length = 0;
let loading_queue = function(num = 1) {
//if this function is called, change the queue_length
queue_length = queue_length + num;
throbber(1);
// turn off the throbber once there is no more queue
if (num < 0 && queue_length === 0) {
throbber(0);
}
};
// this will setup select menus
let setup_select = function(select_menu_id, options_array) {
let menu = document.getElementById(select_menu_id);
// if the input options_array is not actually an array but an
// object, we will set it up as an optgroup list
if (Object.prototype.toString.call(options_array) === "[object Object]") {
// the object keys will be our optgroup name so we need to get them
let keys = Object.keys(options_array);
let key_count = keys.length;
let optgroup_array = [];
//extract the array in each object
for (let i = 0; i < key_count; i++) {
optgroup_array[parseInt(i,10)] = document.createElement("optgroup");
optgroup_array[parseInt(i,10)].id = keys[parseInt(i,10)];
optgroup_array[parseInt(i,10)].label = keys[parseInt(i,10)];
let optgroup_options = options_array[keys[parseInt(i,10)]];
//active each of the option in the group as options
let options = [];
for (let j = 0; j < optgroup_options.length; j++) {
options[parseInt(j,10)] = document.createElement("option");
options[parseInt(j,10)].id = optgroup_options[parseInt(j,10)];
options[parseInt(j,10)].text = optgroup_options[parseInt(j,10)];
options[parseInt(j,10)].value = optgroup_options[parseInt(j,10)];
optgroup_array[parseInt(i,10)].appendChild(options[parseInt(j,10)]);
}
// add it to the menu
menu.appendChild(optgroup_array[parseInt(i,10)]);
}
// relaod the menu so changes will show up
let elems = document.querySelectorAll("select");
let instances = M.FormSelect.init(elems, materialize_options);
return;
}
//here we deal with the simple array situation
let option_len = options_array.length;
try {
//check to see if there are options that are not supposed to be here
let current_options = menu.options;
for (let i = current_options.length - 1; i >= 0; i--) {
if (current_options[parseInt(i,10)].value !== "None"){ menu.remove(i);}
}
} catch (error) {
// no action expected
}
let options = [];
for (let i = 0; i < option_len; i++) {
//add each member to the option list
options[parseInt(i,10)] = document.createElement("option");
options[parseInt(i,10)].text = options_array[parseInt(i,10)];
options[parseInt(i,10)].value = options_array[parseInt(i,10)];
menu.add(options[parseInt(i,10)]);
}
elems = document.querySelectorAll("select");
instances = M.FormSelect.init(elems, materialize_options);
};
// this function is to fuse small segments of data together
let fuse_data = function(obj_list, str) {
for (let j = 1; j <= obj_list[str.toString()]; j++) {
// try case is used since the data might not be required yet
// since we will be calling this function many times,
// it will fuse everything eventually
try {
// this try case is for array type of data
window[str.toString()] = window[str.toString()].concat(window[str.toString() + "_" + String(j)]);
window[str.toString() + "_" + String(j)] = [];
} catch (err) {
// the try case for object data type
try {
window[str.toString()]=Object.assign(window[str.toString()],window[str.toString()+"_"+String(j)]);
window[str.toString()+"_"+String(j)]={};
} catch (err) {
// no action expected
}
}
}
};
// we need a function to get all the data segments
let get_file = function(obj_list, str, path_to_data) {
if (typeof obj_list[str.toString()] !== "undefined" && obj_list[str.toString()] !== null) {
// get the small segments of the data
require([path_to_data + "\\" + str]);
for (let i = 1; i <= obj_list[str.toString()]; i++) {
loading_queue(1);
require([path_to_data + "\\" + str + "_" + String(i)], function() {
loading_queue(-1);
// call the fuse_data function to piece segments together
fuse_data(obj_list, str);
});
}
}
};
// this is for us to get the selected options in a menu easier
// give an id, it will return selected options in an array.
let get_selected_options = function(menu_id) {
let selected = document.getElementById(menu_id).selectedOptions;
let value_array = [];
for (let i = 0; i < selected.length; i++) {
value_array[parseInt(i,10)] = selected[parseInt(i,10)].value;
}
return value_array;
};
// load selected files and the geo file
let user_load = function() {
throbber(1);
let available_data = get_selected_options("load data list");
setup_select("data 1", available_data);
setup_select("data 2", available_data);
for (let year_count = 0; year_count < available_data.length; year_count++) {
get_file(window[input_data_catalog.toString()], input_data_file_pefix + available_data[parseInt(year_count,10)], input_data_folder);
}
if (typeof(window[input_geo_file_prefix.toString()]) === "undefined") {
// the geo location of the accounts
get_file(window[input_geo_catalog.toString()], input_geo_file_prefix, input_geo_folder);
}
};
// This will select data points. If it does not meet the criteria
// this function will return 0. If it fits, it returns 1.
// idk if there is a more elegant way to code this
let data_selector = function(data_point, geo_data, selected_tag) {
// essentially this will return if any criteria is not met
// hopefully will increase the efficiency than simple if else
switch (true) {
case parseInt(data_point[parameter1.toString()],10) < parseInt(parameter1_lower_bound.value,10):
return 0;
case parseInt(data_point[parameter1.toString()],10) > parseInt(parameter1_upper_bound.value,10):
return 0;
case parseInt(data_point[parameter2.toString()],10) < parseInt(parameter2_lower_bound.value,10):
return 0;
case parseInt(data_point[parameter2.toString()],10) > parseInt(parameter2_upper_bound.value,10):
return 0;
case true:
for (tag_count = 0; tag_count < selected_tag.length; tag_count++) {
if (selected_tag[tag_count.toString()]==="All") return 1;
if (typeof(data_point[tag1.toString()]) !== "undefined"){
try {current_tag = data_point[tag1.toString()];}
catch (err) {}
}else{
current_tag = geo_data[tag1.toString()];
}
if (current_tag === selected_tag[tag_count.toString()]) {
return 1;
}
}
}
return 0;
};
// get points to plot base on some requirements
// not really useful right now, but it will be used to
// filter the data points when the UI is implemented
let getPoints = function(str1, str2, loc) {
let result_loc = [];
let arr_length = window[input_id_catalog.toString()].length;
let tag1_selected=get_selected_options("tag1");
for (let i = 0; i < arr_length; i++) {
if (i > arr_length) {break;}
try {
let acc_num = window[input_id_catalog.toString()][parseInt(i,10)];
let prop_info_obj = loc[parseInt(acc_num,10)];
if (str2 === "None") {
// first case where the second data set is not selected
let data1 = window[input_data_file_pefix.toString() + str1.toString()];
if (data_selector(data1[parseInt(acc_num,10)], loc[parseInt(acc_num,10)], tag1_selected)) {
result_loc[parseInt(i,10)] = [data1[parseInt(acc_num,10)][parameter1.toString()], prop_info_obj.Latitude, prop_info_obj.Longitude, acc_num];
//here is where you can change the output data formula
}
} else {
// when the second data set is selected
let data1 = window[input_data_file_pefix.toString() + str1.toString()];
let data2 = window[input_data_file_pefix.toString() + str2.toString()];
if (data_selector(data1[parseInt(acc_num,10)], loc[parseInt(acc_num,10)], tag1_selected)) {
result_loc[parseInt(i,10)] = [data2[parseInt(acc_num,10)][parameter1.toString()] / data1[parseInt(acc_num,10)][parameter1.toString()] - 1, prop_info_obj.Latitude, prop_info_obj.Longitude, acc_num,input_data_file_pefix.toString() +" "+ str1.toString(),data1[parseInt(acc_num,10)][parameter1.toString()],input_data_file_pefix.toString() +" "+ str2.toString(), data2[parseInt(acc_num,10)][parameter1.toString()]];
//here is where you can change the output data formula
}
}
} catch (err) {}
}
return result_loc;
};
// get the RGB color to plot on the border of the circle markers
let get_color = function(val, min_val, max_val) {
//re adjust the val to a scale of 100 between min_val and max_val
val = (val - min_val) / (max_val - min_val) * 100;
switch (true) {
//if val is absolutely inside of the bounds
case (val > 0 && val < 100):
let color_index = parseInt(Math.round(val / 100 * (RGB_steps-1)), 10);
let color_x = val - parseInt(Math.round(val / (100 / RGB_steps)), 10) * 100 / RGB_steps;
//the RGB_gradient_function is essentially a linear interpolation between two rgb settings
//RGB_gradient_function is nested arrays in the form of [[R=[slope, intercept],G=....],[R=....]...]
r = RGB_gradient_function[parseInt(color_index,10)][0][0] * (val) / RGB_steps / 100 + RGB_gradient_function[parseInt(color_index,10)][0][1];
g = RGB_gradient_function[parseInt(color_index,10)][1][0] * (val) / RGB_steps / 100 + RGB_gradient_function[parseInt(color_index,10)][1][1];
b = RGB_gradient_function[parseInt(color_index,10)][2][0] * (val) / RGB_steps / 100 + RGB_gradient_function[parseInt(color_index,10)][2][1];
break;
//if val is absolutely outside of the bounds
case (val < 0 || val > 100):
return "rgb(175, 175, 175)";
//then we have the two boundary conditions
case (val === 0):
[r, g, b] = RGB_gradient[0];
break;
case (val === 100):
[r, g, b] = RGB_gradient[parseInt(RGB_steps,10)];
break;
}
try {
return "rgb(" + Math.round(r) + "," + Math.round(g) + "," + Math.round(b) + ")";
} catch (err) {
console.log(val);
}
};
// this will generate the legend on the map
const make_legend = function(min, max) {
min = parseInt(min, 10);
max = parseInt(max, 10);
legend.onAdd = function(map) {
const max_min_range = max - min;
let div = L.DomUtil.create("div", "info legend");
//make the segments round to 1k if values large enough
if (max > 100000){ round_stop = 1000;}
else {var round_stop = 1;}
const legend_step = [];
//generate the vaules to generate legends
for (let i = 0; i < RGB_steps; i++) {
legend_step.push(Math.round((max_min_range / (RGB_steps + 1) * (i + 1) + min) / round_stop) * round_stop);
}
grades = legend_step;
//add the min and max at beginning and the end
grades.unshift(min);
grades.push(max);
let labels = [];
if (min > 0) {grades.unshift(0);}
else {grades.unshift(min * 2);}
// loop through our density intervals and generate a label with a colored square for each interval
for (let i = 0; i < grades.length; i++) {
let prefix='';
let suffix='<br>';
if (typeof(grades[i-1]) === "undefined") {prefix='< ';}
else if (typeof(grades[i+1]) === "undefined") {prefix=' >';}
else {suffix=' – ' + grades[parseInt(i + 1,10)]+ '<br>';}
div.innerHTML +=
'<i style="background:' + get_color(grades[parseInt(i,10)] + 1, min, max) + '"></i> ' +
prefix+grades[parseInt(i,10)] + suffix;
}
return div;
};
legend.addTo(mymap);
};
// plot points as circle markers on the leaflet map
function plot_points(arr, parameter2_str) {
let arr_length = arr.length;
let marker_plotted = 0;
try {
colour_array = parameter1_colour_slider.noUiSlider.get();
} catch (err) {
colour_array = parameter1_change_colour_slider.noUiSlider.get();
//since the values are in %, we need to get rid of that symbol
colour_array[0] = colour_array[0].slice(0, -1);
colour_array[1] = colour_array[1].slice(0, -1);
}
for (let i = 0; i < arr_length; i++) {
if (typeof arr[parseInt(i,10)] !== "undefined" && arr[parseInt(i,10)] !== null) {
//make the pop up texts
if (parameter2_str === "None") {
text = "<p> "+parameter1+": " + arr[parseInt(i,10)][0].toString() + "CAD</p> <p>"+id_key+": " + arr[parseInt(i,10)][3].toString() + "</p>";
} else {
arr[parseInt(i,10)][0] = arr[parseInt(i,10)][0] * 100;
text = "<p> "+parameter1+": " + (arr[parseInt(i,10)][0]).toPrecision(3).toString() + "%</p> <p>"+id_key+": " + arr[parseInt(i,10)][3].toString() +
"</p><p>"+arr[parseInt(i,10)][4].toString()+ " ="+Math.round(parseInt(arr[parseInt(i,10)][5],10)).toString()+"CAD</p><p>"+
arr[parseInt(i,10)][6].toString()+" ="+Math.round(parseInt(arr[parseInt(i,10)][7],10)).toString()+"CAD</p>";
}
//make the marker
Marker = new L.circle([arr[parseInt(i,10)][1], arr[parseInt(i,10)][2]], {
radius: c_radius,
color: (get_color(arr[parseInt(i,10)][0], colour_array[0], colour_array[1]))
})
.bindPopup(text) // add the pop up text
.addTo(data_points);
marker_plotted++;
}
}
make_legend(colour_array[0], colour_array[1]);
//console.log(marker_plotted.toString() + " markers plotted");
return loading_queue(-1);
}
//prepare the action needed and send it to plot
let execute_plot = function() {
data_points.clearLayers();
let marker_loc = getPoints(get_selected_options("data 1")[0], get_selected_options("data 2")[0], window[input_geo_file_prefix.toString()]);
return plot_points(marker_loc, get_selected_options("data 2")[0]);
};
//when the plot! botton pressed, this function will be called
let user_plot = function() {
throbber(1);
loading_queue(1);
// we need tp give throbber some time to react, otherwise
// the processes will jam the computer and the throbber will not show up
setTimeout(function() {
return execute_plot();
}, 50);
};
let clear_plot = function() {
data_points.clearLayers();
return loading_queue(-1);
};
let user_clear_plot = function() {
throbber(1);
loading_queue(1);
// we need tp give throbber some time to react, otherwise
// the processes will jam the computer and the throbber will not show up
setTimeout(function() {
return clear_plot();
}, 50);
};