-
Notifications
You must be signed in to change notification settings - Fork 6
/
analysis_utils
513 lines (400 loc) · 16.7 KB
/
analysis_utils
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
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
// released under Open Source GPL License Copyright © 2023 Ka Hei Chow
// ################################# //
// ###### intra-urban ###### //
// ###### analysis functions ###### //
// ################################# //
// TO DO: https://gis.stackexchange.com/questions/368569/google-earth-engine-name-parameters-in-custom-function
// import module
var palettes = require('users/gena/packages:palettes');
var helper = require('users/pinkychow1010/WB_IntraUrban:helper');
/**
* This is an analysis function on choropleth map, where polygons in the vector layer
* will be colored according to the attribute values and defined color scale.
*/
/**
* Choropleth Map: statistical thematic map that uses pseudocolor of a attributes within spatial units.
*
* This function is used to construct and visualize a choropleth map based on values of a vector layer.
*
* @author Ka Hei Chow.
*
* @see helper.add_colormap
* @link 'users/pinkychow1010/GEE:WorldBank/helper'
* @global
*
* @fires add colored map layer
* @listens select layer to be evaluated
*
* @param {ee.FeatureCollection} vector A vector layer which consists of multiple polygons.
* @param {string} attribute The attribute to be categorized for the color mapping.
* @param {variable} [palette=palettes.cmocean.Balance[7]] A list of color with length of class_n or palette variable from 'users/gena/packages:palettes'.
* @param {float} [opacity=1] Opacity of the map layer. Default 1.
* @param {bool} [log=0] Boolean option for log color scale. With 1 the values will be colored in log scale. With 0 the values will be mapped linearly.
* @param {integer} [class_n=7] Number of class for color mapping. Default 7.
* @param {string} [title=attribute] Title for the map layer. Default same as attribute.
* @param {int} [decimal=2] Decimal rounded for legend. Default 2.
* @param {string} [size="large"] Legend size. One of the following "small"/"medium"/"large". Default "large".
* @param {string} [legend_text="Legend"] Legend text. Default "Legend".
*
* @return {null} null.
*
*/
var choropleth_map = function choropleth_map(
vector,
attribute,
palette,
opacity,
log,
class_n,
title,
map_object,
decimal,
size,
legend_text
) {
if (log === undefined || log === null){var log = 0};
if (class_n === undefined || class_n === null){var class_n = 7};
if (title === undefined || title === null){var title = attribute};
if (opacity === undefined || opacity === null){var opacity = 1};
if (palette === undefined || palette === null){var palette = palettes.cmocean.Balance[7]};
if (legend_text === undefined || legend_text === null){var legend_text = "Legend"};
var colors = ee.List(palette);
var zone = ee.FeatureCollection(vector);
// error catching for non-existing layer property
if (vector.filter(ee.Filter.notNull([attribute])).size()===0) {
throw new Error("Defined property not found in vector layer!");
}
// filter null values to prevent errors from missing attribute values
// then get zonal max and min for reclassification
var min_val = ee.Number(vector.filter(ee.Filter.neq(attribute, null)).aggregate_min(attribute).getInfo());
var max_val = ee.Number(vector.filter(ee.Filter.neq(attribute, null)).aggregate_max(attribute).getInfo());
// check if min value is negative, if so turn off log (regardless of user input) to avoid errors
var positive = min_val.abs().eq(min_val);
log = ee.Number(log).multiply(positive);
// center object using map object
if (map_object === undefined || map_object === null){
Map.centerObject(zone, 5);
} else {
map_object.centerObject(zone, 5);
}
function get_digit(number) {
return number.toString().length;
}
var get_index = function(value) {
/*
This is the function to reclassify original value spectrum into new class to map different colors
This is tested on both negative and positive value spectrums.
User can define a log argument to transform the values before classification.
It, nonetheless, does not work for negetive values.
The rational of the function is as follow:
First, check user log argument. Calculate the values with (*_log) and without log (*_linear).
Perserve the one based on user definition.
Get max and min to calculate the range. Use the range to calculate the step.
Assign index class based on pixel values. Fit all values into predefined classes using clamp.
The index will be used for coloring the polygons.
*/
// log option invert (binary)
var log_inv = ee.Number(log).eq(ee.Number(0)); // 1 if log = 0 and 0 if log = 1
// non-log version for pixel value
var scaled_linear = value.multiply(log_inv);
// log version for pixel value
var scaled_log = value.multiply(log).add(log_inv).log();
// final pixel value
var scaled_val = scaled_linear.add(scaled_log);
// non-log version for min value
var min_linear = min_val.multiply(log_inv);
// log version for min value
var min_log = min_val.multiply(log).add(log_inv).log();
// final min value
var min = min_log.add(min_linear);
// non-log version for max value
var max_linear = max_val.multiply(log_inv);
// log version for max value
var max_log = max_val.multiply(log).add(log_inv).log();
// final max value
var max = max_log.add(max_linear);
// bins for classification
var step = (max.subtract(min)).divide(class_n);
// get index class
var index = (scaled_val.subtract(min)).divide(step).round();
// fit into range
var index_clamp = index.clamp(0, ee.Number(class_n).subtract(1));
return index_clamp;
};
// generate choropleth map using function
zone = zone.map(function(f) {
// use index to get specific color
var value = f.getNumber(attribute);
var index = get_index(value);
var color = colors.get(index);
// use color to set style
return f.set('style', {fillColor: color});
});
// style zonal layer using customized color map
var VisCustom = zone.style({styleProperty: 'style'});
// add choropleth map to map object
if (map_object === undefined || map_object === null){
Map.addLayer(VisCustom, {'opacity': opacity}, title);
} else {
map_object.addLayer(VisCustom, {'opacity': opacity}, title);
}
// use layer statistics to define legend color bar
var vis = {
min: vector.aggregate_min(attribute).getInfo(),
max: vector.aggregate_max(attribute).getInfo(),
palette: palette
};
var log_hint = ee.Dictionary({
'1': "(log)",
'0': ""
});
function toString(number) {
return ee.Number(number).format('%d');
}
// set up legend with color bar
var legend = helper.add_colorbar(
vis,
legend_text + " " + log_hint.get(toString(log)).getInfo(),
decimal,
size
);
// display legend layer
if (map_object === undefined || map_object === null){
Map.add(legend);
} else {
map_object.add(legend);
}
return legend;
};
// var choropleth_map = function choropleth_map(
// vector,
// attribute,
// palette,
// opacity,
// log,
// class_n,
// title,
// map_object
// ) {
// if (log === undefined || log === null){var log = 0};
// if (class_n === undefined || class_n === null){var class_n = 7};
// if (title === undefined || title === null){var title = attribute};
// if (opacity === undefined || opacity === null){var opacity = 1};
// if (palette === undefined || palette === null){var palette = palettes.cmocean.Balance[7]};
// var colors = ee.List(palette);
// var zone = ee.FeatureCollection(vector);
// // filter null values to prevent errors from missing attribute values
// var min_val = ee.Number(vector.filter(ee.Filter.neq(attribute, null)).aggregate_min(attribute).getInfo());
// var max_val = ee.Number(vector.filter(ee.Filter.neq(attribute, null)).aggregate_max(attribute).getInfo());
// // turn off log if minimal value is negetive
// var positive = min_val.abs().eq(min_val);
// log = ee.Number(log).multiply(positive);
// if (map_object === undefined || map_object === null){
// Map.centerObject(zone, 10);
// // Map.setOptions('HYBRID');
// } else {
// map_object.centerObject(zone, 10);
// }
// function get_digit(number) {
// return number.toString().length;
// }
// var get_index = function(value) {
// var log_inv = ee.Number(log).eq(ee.Number(0)); // 1 if log = 0 and 0 if log = 1
// var scaled_linear = value.multiply(log_inv);
// // cope with negetive values
// var scaled_log = value.multiply(log).log();
// // try {
// // var scaled_log = value.log().multiply(log);
// // } catch (exception) {
// // var scaled_log = ee.Number(0);
// // log = 0;
// // }
// var scaled_val = scaled_linear.add(scaled_log);
// var min_linear = min_val.multiply(log_inv);
// //
// var min_log = min_val.multiply(log).log();
// // try {
// // var min_log = min_val.log().multiply(log);
// // } catch (exception) {
// // var min_log = ee.Number(0);
// // }
// var min = min_log.add(min_linear);
// var max_linear = max_val.multiply(log_inv);
// //
// var max_log = max_val.multiply(log).log();
// // try {
// // var max_log = max_val.log().multiply(log);
// // } catch (exception) {
// // var max_log = ee.Number(0);
// // }
// var max = max_log.add(max_linear);
// var step = (max.subtract(min)).divide(class_n);
// var index = (scaled_val.subtract(min)).divide(step).round();
// var index_clamp = index.clamp(0, ee.Number(class_n).subtract(1));
// return index_clamp;
// };
// zone = zone.map(function(f) {
// var value = f.getNumber(attribute);
// var index = get_index(value);
// var color = colors.get(index);
// return f.set('style', {fillColor: color});
// });
// var VisCustom = zone.style({
// styleProperty: 'style'
// });
// if (map_object === undefined || map_object === null){
// Map.addLayer(VisCustom, {'opacity': opacity}, title);
// } else {
// map_object.addLayer(VisCustom, {'opacity': opacity}, title);
// }
// var vis = {
// min: vector.aggregate_min(attribute).getInfo(),
// max: vector.aggregate_max(attribute).getInfo(),
// palette: palette
// };
// var log_hint = ee.Dictionary({
// '1': "(log)",
// '0': ""
// });
// function toString(number) {
// return ee.Number(number).format('%d')
// };
// var legend = helper.add_colorbar(vis, "Legend " + log_hint.get(toString(log)).getInfo());
// if (map_object === undefined || map_object === null){
// Map.add(legend);
// } else {
// map_object.add(legend);
// }
// return legend;
// };
// var choropleth_map = function choropleth_map(
// vector,
// attribute,
// palette,
// opacity,
// log,
// class_n,
// title,
// ) {
// if (log === undefined || log === null){var log = 0};
// if (class_n === undefined || class_n === null){var class_n = 7};
// if (title === undefined || title === null){var title = attribute};
// if (opacity === undefined || opacity === null){var opacity = 1};
// if (palette === undefined || palette === null){var palette = palettes.cmocean.Balance[7]};
// var colors = ee.List(palette);
// var zone = ee.FeatureCollection(vector);
// // filter null values to prevent errors from missing attribute values
// var min_val = ee.Number(vector.filter(ee.Filter.neq(attribute, null)).aggregate_min(attribute).getInfo());
// var max_val = ee.Number(vector.filter(ee.Filter.neq(attribute, null)).aggregate_max(attribute).getInfo());
// // var min_val = ee.Number(vector.aggregate_min(attribute).getInfo());
// // var max_val = ee.Number(vector.aggregate_max(attribute).getInfo());
// Map.centerObject(zone, 10);
// Map.setOptions('HYBRID');
// function get_digit(number) {
// return number.toString().length;
// }
// var get_index = function(value) {
// var log_inv = ee.Number(log).eq(ee.Number(0)); // 1 if log = 0 and 0 if log = 1
// var scaled_linear = value.multiply(log_inv);
// var scaled_log = value.log().multiply(log);
// var scaled_val = scaled_linear.add(scaled_log);
// var min_linear = min_val.multiply(log_inv);
// var min_log = min_val.log().multiply(log);
// var min = min_log.add(min_linear);
// var max_linear = max_val.multiply(log_inv);
// var max_log = max_val.log().multiply(log);
// var max = max_log.add(max_linear);
// var step = (max.subtract(min)).divide(class_n);
// var index = (scaled_val.subtract(min)).divide(step).round();
// var index_clamp = index.clamp(0, ee.Number(class_n).subtract(1));
// return index_clamp;
// };
// zone = zone.map(function(f) {
// var value = f.getNumber(attribute);
// var index = get_index(value);
// var color = colors.get(index);
// return f.set('style', {fillColor: color});
// });
// var VisCustom = zone.style({
// styleProperty: 'style'
// });
// Map.addLayer(VisCustom, {'opacity': opacity}, title);
// var vis = {
// min: vector.aggregate_min(attribute).getInfo(),
// max: vector.aggregate_max(attribute).getInfo(),
// palette: palette
// };
// var log_hint = ee.Dictionary({
// '1': "(log)",
// '0': ""
// });
// function toString(number) {
// return ee.Number(number).format('%d')
// }
// var legend = helper.add_colorbar(vis, "Legend " + log_hint.get(toString(log)).getInfo());
// Map.add(legend);
// return legend;
// };
/**
* This is a wrapper function on choropleth map, population sum inside the feature collection
* will be colored for data visualization. Population data are extracted from GPWv411: Population Count (Gridded Population of the World Version 4.11),
* which is also used in the GHSL - Global Human Settlement Layer.
*/
/**
* Total Population Zonal Statistics.
*
* This function is used to construct and visualize a choropleth map based on population sum of a vector layer.
*
* Example usage:
* var geometry = ee.FeatureCollection("projects/ee-pinkychow1010/assets/WB_GEE/karachi_union_council_subset");
* var analysis = require('users/pinkychow1010/GEE:WorldBank/analysis_utils');
* analysis.zonal_pop_sum(geometry);
*
* @author Ka Hei Chow.
*
* @see helper.add_colormap, choropleth_map
* @link 'users/pinkychow1010/GEE:WorldBank/helper'
* @global
*
* @fires add colored map layer
* @listens select layer to be evaluated
*
* @param {ee.FeatureCollection} geometry A vector layer which consists of multiple polygons.
* @param {variable} [palette=palettes.cmocean.Balance[7]] A list of color with length of class_n or palette variable from 'users/gena/packages:palettes'.
* @param {float} [opacity=1] Opacity of the map layer. Default 1.
* @param {bool} [log=0] Boolean option for log color scale. With 1 the values will be colored in log scale. With 0 the values will be mapped linearly.
* @param {integer} [class_n=7] Number of class for color mapping. Default 7.
* @param {string} [title=attribute] Title for the map layer. Default same as attribute.
*
* @return {null} null.
*
*/
exports.zonal_pop_sum = function zonal_pop_sum(
geometry,
palette,
log,
class_n,
title,
opacity
) {
var dataset = ee.ImageCollection("CIESIN/GPWv411/GPW_Population_Count").first();
var pop = dataset.select('population_count').clip(geometry);
var total_pop = pop.reduceRegions({
collection: geometry,
reducer: ee.Reducer.sum()
});
var vector = total_pop;
var attribute = 'sum';
if (title === undefined || title === null){var title = "Total Population"};
if (log === undefined || log === null){var log = 1};
return choropleth_map(
vector=vector,
attribute=attribute,
palette=palette,
opacity=opacity,
log=log,
class_n=class_n,
title=title
);
};
exports.choropleth_map = choropleth_map;