-
Notifications
You must be signed in to change notification settings - Fork 6
/
8_temperature.qmd
622 lines (431 loc) · 21.7 KB
/
8_temperature.qmd
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
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
# Temperature
## Resources
::: callout-tip
## This week
- Wilson, B., 2020. [Urban Heat Management and the Legacy of Redlining. Journal of the American Planning Association 86, 443–457](https://doi.org/10.1080/01944363.2020.1759127)
- Klinenberg, E., 1999. [Denaturalizing Disaster: A Social Autopsy of the 1995 Chicago Heat Wave. Theory and Society 28, 239–295.](https://www.jstor.org/stable/3108472#metadata_info_tab_contents)
- Li, D., Newman, G.D., Wilson, B., Zhang, Y., Brown, R.D., 2022. [Modeling the relationships between historical redlining, urban heat, and heat-related emergency department visits: An examination of 11 Texas cities. Environment and Planning B: Urban Analytics and City Science 49, 933–952.](https://doi.org/10.1177/23998083211039854)
- Nowak, D.J., Ellis, A., Greenfield, E.J., 2022. [The disparity in tree cover and ecosystem service values among redlining classes in the United States. Landscape and Urban Planning 221, 104370.](https://doi.org/10.1016/j.landurbplan.2022.104370)
- [Cloud-Based Remote Sensing with Google Earth Engine](https://www.eefabook.org/go-to-the-book.html) (accessed 1.5.23).
- Chapter A1.5 Heat Islands
- MacLachlan, A., Biggs, E., Roberts, G., Boruff, B., 2021. [Sustainable City Planning: A Data-Driven Approach for Mitigating Urban Heat. Frontiers in Built Environment 6.](https://www.frontiersin.org/articles/10.3389/fbuil.2020.519599/full)
:::
Within this practical we are going to explore temperature across urban areas using two different data products: Landsat and MODIS. We'll then look at which admin areas are hottest over time.
The first stage of this practical is to load up a level 2 admin area to extract temperature for....
## Vector data
```{r, eval=FALSE}
//--------------------------Vector data---------------------------
var dataset = ee.FeatureCollection("FAO/GAUL/2015/level1");
var dataset_style = dataset.style({
color: '1e90ff',
width: 2,
fillColor: '00000000', // with alpha set for partial transparency
// lineType: 'dotted',
// pointSize: 10,
// pointShape: 'circle'
});
Map.addLayer(dataset, {}, 'Second Level Administrative Units_1');
var Beijing = dataset.filter('ADM1_CODE == 899');
Map.addLayer(Beijing, {}, 'Beijing');
```
## Landsat
Next, the Landsat data...**note** below that i have set a month range as i want to consider the summer and not the whole year...
```{r, eval=FALSE}
//--------------------------Landsat data---------------------------
function applyScaleFactors(image) {
var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
return image.addBands(opticalBands, null, true)
.addBands(thermalBands, null, true);
}
var landsat = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
.filterDate('2022-01-01', '2022-10-10')
.filter(ee.Filter.calendarRange(5, 9,'month'))
.filterBounds(Beijing) // Intersecting ROI
.filter(ee.Filter.lt("CLOUD_COVER", 1))
.map(applyScaleFactors);
print(landsat)
```
The temperature band in Landsat is B10, however it comes in Kelvin and not Celsius, to cover we just subtract 273.1. **But** i want to do this per image...at the moment `.subtract` won't work over an image collection so we can subtract the value from each individual image in a collection...we also want to mask any pixels that have a below 0 value (e.g. probably no data ones). I've done this within this function as well..
```{r, eval=FALSE}
var subtracted = landsat.select('ST_B10').map(function (image) {
var subtract = image.subtract(273.1); // subtract
var mask = subtract.gt(0); //set mask up
var mask_0 = subtract.updateMask(mask); //Apply this in a mask
return mask_0
})
```
If we were to plot this now, we would still show all of the images we have in the collection, but, we can take the mean ("reduce") and clip to our study area...
```{r, eval=FALSE}
var subtracted_mean = subtracted.reduce(ee.Reducer.mean())
.clip(Beijing)
```
Then finally plot the data...
```{r, eval=FALSE}
// set up some of the visualisation paramters
// the palette is taken from the MODIS example (which we will see later on)
var vis_params = {
min: 20,
max: 55,
palette: [
'040274', '040281', '0502a3', '0502b8', '0502ce', '0502e6',
'0602ff', '235cb1', '307ef3', '269db1', '30c8e2', '32d3ef',
'3be285', '3ff38f', '86e26f', '3ae237', 'b5e22e', 'd6e21f',
'fff705', 'ffd611', 'ffb613', 'ff8b13', 'ff6e08', 'ff500d',
'ff0000', 'de0101', 'c21301', 'a71001', '911003'
]
};
Map.addLayer(subtracted_mean, vis_params, 'Landsat Temp');
```
```{r echo=FALSE, out.width = "900px", fig.align='center', cache=FALSE}
knitr::include_graphics('prac_8/GEE_temp_out.png')
```
## MODIS
Now, we can also do the same process with the Moderate Resolution Imaging Spectroradiometer (MODIS), MODIS is an instrument aboard both the Terra and Aqua satellites. Terra crosses the equator from N to S in the morning and Aqua S to N in the afternoon meaning the entire earth is sampled every 1-2 days, with most places getting 2 images a day.
### Aqua
```{r, eval=FALSE}
var MODIS_Aqua_day = ee.ImageCollection('MODIS/061/MYD11A1')
.filterDate('2022-01-01', '2022-10-10')
.filter(ee.Filter.calendarRange(5, 9,'month'))
.filterBounds(Beijing) // Intersecting ROI;
.select('LST_Day_1km')
print(MODIS_Aqua_day, "MODIS_AQUA")
```
### Terra
```{r, eval=FALSE}
var MODIS_Terra_day = ee.ImageCollection('MODIS/061/MOD11A1')
.filterDate('2022-01-01', '2022-10-10')
.filter(ee.Filter.calendarRange(5, 9,'month'))
.filterBounds(Beijing) // Intersecting ROI;
.select('LST_Day_1km')
print(MODIS_Terra_day, "MODIS_Terra")
```
### Scaling
For the Landsat data in this period we had 5 images, compared to 108 for Aqua and 107 for Terra!But! MODIS data is at 1km resolution...**and** if you look at the values (through adding one of the collections to the map) we haven't addressed the scaling....
Like Landsat data MODIS data needs to have the scale factor applied, looking at the [GEE documentation](https://developers.google.com/earth-engine/datasets/catalog/MODIS_061_MYD11A1#bands) or the MODIS documentation we can see that the value is 0.02 and the values are in Kelvin, when we want Celcius....
```{r, eval=FALSE}
function MODISscale(image) {
var temp = image.select('LST_.*').multiply(0.02).subtract(273.1);
return image.addBands(temp, null, true)
}
var MODIS_Aqua_day = ee.ImageCollection('MODIS/061/MYD11A1')
.filterDate('2022-01-01', '2022-10-10')
.filter(ee.Filter.calendarRange(5, 9,'month'))
.select('LST_Day_1km')
.map(MODISscale)
.filterBounds(Beijing); // Intersecting ROI;
print(MODIS_Aqua_day, "MODIS_AQUA")
var MODIS_Terra_day = ee.ImageCollection('MODIS/061/MOD11A1')
.filterDate('2022-01-01', '2022-10-10')
.filter(ee.Filter.calendarRange(5, 9,'month'))
.filterBounds(Beijing) // Intersecting ROI;
.select('LST_Day_1km')
.map(MODISscale);
```
### Collection merge
Merge the collections and plot a mean summer temperature image...
```{r, eval=FALSE}
var mean_aqua_terra = MODIS_Aqua_day.merge(MODIS_Terra_day)
.reduce(ee.Reducer.mean())
.clip(Beijing)
Map.addLayer(mean_aqua_terra, landSurfaceTemperatureVis,
'MODIS Land Surface Temperature');
```
### Display the results
```{r, eval=FALSE}
var landSurfaceTemperatureVis = {
min: 15,
max: 45,
palette: [
'040274', '040281', '0502a3', '0502b8', '0502ce', '0502e6',
'0602ff', '235cb1', '307ef3', '269db1', '30c8e2', '32d3ef',
'3be285', '3ff38f', '86e26f', '3ae237', 'b5e22e', 'd6e21f',
'fff705', 'ffd611', 'ffb613', 'ff8b13', 'ff6e08', 'ff500d',
'ff0000', 'de0101', 'c21301', 'a71001', '911003'
],
};
Map.addLayer(mean_aqua_terra, landSurfaceTemperatureVis,
'MODIS Land Surface Temperature');
```
What do you see ?
```{r echo=FALSE, out.width = "900px", fig.align='center', cache=FALSE}
knitr::include_graphics('prac_8/GEE_temp_out_MODIS.png')
```
## Timeseries
So the real benefit of MODIS data is that we can plot the time series...**however** in doing so we lose the spatial element...
```{r, eval=FALSE}
var aqua_terra = MODIS_Aqua_day.merge(MODIS_Terra_day) //merge the two collections
var timeseries = ui.Chart.image.series({
imageCollection: aqua_terra,
region: Beijing,
reducer: ee.Reducer.mean(),
scale: 1000,
xProperty: 'system:time_start'})
.setOptions({
title: 'Temperature time series',
vAxis: {title: 'LST Celsius'}});
print(timeseries);
```
```{r echo=FALSE, out.width = "900px", fig.align='center', cache=FALSE}
knitr::include_graphics('prac_8/ee-chart.png')
```
Times series modelling....https://developers.google.com/earth-engine/tutorials/community/time-series-modeling
## Statistics per spatial unit
### Landsat
To start with let's just explore the average temperature per GAUL level 2 area within the Beijing level 1 area....we can do this using the `reduceRegions` function....
```{r, eval=FALSE}
//--------------------------Statistics per level 2---------------------------.
var GAUL_2 = ee.FeatureCollection("FAO/GAUL/2015/level2");
var Beijing_level2 = GAUL_2.filter('ADM1_CODE == 899');
Map.addLayer(Beijing_level2, {}, 'Second Level Administrative Units_2');
print(subtracted_mean)
var mean_Landsat_level2 = subtracted_mean.reduceRegions({
collection: Beijing_level2,
reducer: ee.Reducer.mean(),
scale: 30,
});
Map.addLayer(mean_Landsat_level2, {}, 'mean_Landsat_level2');
```
When we inspect the new vector file we can see that a mean column has been added to the attribute table....
```{r echo=FALSE, out.width = "400px", fig.align='center', cache=FALSE}
knitr::include_graphics('prac_8/GEE_mean_Lsat.png')
```
**Notes** of the reduce regions:
* There is a reduce percentile function...`reducer: ee.Reducer.percentile([10, 20, 30, 40, 50, 60, 70, 80, 90])` that might also be useful. This takes all the pixels in each polygon and lists the value of the percentiles specified in the attribute table.
#### Export
To export a feature collection like this...
```{r, eval=FALSE}
// Export the FeatureCollection to a SHP file.
Export.table.toDrive({
collection: mean_Landsat_level2,
description:'mean_Landsat_level2',
fileFormat: 'SHP'
});
```
We can then open it in QGIS to see our average tempearture per spatial untit.
### MODIS
To get the time series data per spatial unit in MODIS we can use the similar code as before, but this time we:
* use the function `ui.Chart.image.seriesByRegion`
* set our `regions` argument to the lower level spatial data....
```{r, eval=FALSE}
var chart = ui.Chart.image.seriesByRegion({
imageCollection: MODIS_Aqua_day,
regions: Beijing_level2, //this is the difference
reducer: ee.Reducer.mean()
})
print(chart)
```
We could make the plot a bit fancier in GEE...here, i have adapted the code [from the documentation](https://developers.google.com/earth-engine/apidocs/ui-chart-image-seriesbyregion)...
```{r, eval=FALSE}
var timeseries_per_unit = ui.Chart.image.seriesByRegion({
imageCollection: aqua_terra,
//band: 'NDVI',
regions: Beijing_level2,
reducer: ee.Reducer.mean(),
scale: 1000,
//seriesProperty: 'label',
xProperty: 'system:time_start'
})
.setOptions({
title: 'Average temp per spatial unit',
hAxis: {title: 'Date', titleTextStyle: {italic: false, bold: true}},
vAxis: {
title: 'LST Celsius',
titleTextStyle: {italic: false, bold: true}
},
lineWidth: 1
// colors: ['0f8755', '808080'],
});
```
```{r echo=FALSE, out.width = "900px", fig.align='center', cache=FALSE}
knitr::include_graphics('prac_8/ee-chart-two-units.png')
```
#### Export
We could then export the data to run some time series analysis for the spatial units (e.g. in R), this might be interesting if we know characteristics of land cover or development change over time.....
```{r, eval=FALSE}
// Collect block, image, value triplets.
var triplets = aqua_terra.map(function(image) {
return image.select('LST_Day_1km').reduceRegions({
collection: Beijing_level2.select(['ADM2_CODE']),
reducer: ee.Reducer.mean(),
scale: 30
}).filter(ee.Filter.neq('mean', null))
.map(function(f) {
return f.set('imageId', image.id());
});
}).flatten();
print(triplets.first(), "trip")
Export.table.toDrive({
collection: triplets,
description: triplets,
fileNamePrefix: triplets,
fileFormat: 'CSV'
});
```
You can also change how the data is exported, although I would now just do some wrangling in R. See the:
* [GEE presentation](https://docs.google.com/presentation/d/1D7rezUHPElCfYWHMRNBChHjbEv6nXDD8xnh7_YgyK6A/edit#slide=id.g22bb639feb_1_27)
* [Example code for exporting time series per spatial unit](https://code.earthengine.google.com/9c091e27ca86c3d1e6adbefea8769cc3)
## Trend analysis
To extended our analysis we could explore statistics that will establish if we have a significant increasing or decreasing trend within our data. For example...the [Mann-Kendall trend test](https://developers.google.com/earth-engine/tutorials/community/nonparametric-trends). Once we have identified these pixels or areas we could then investigate them further with Landsat surface temperature data and landcover data.
## Heat index
In the next part of the practical we are going to replicate a heat index produced by [the BBC ](https://www.bbc.co.uk/news/uk-62243280). Here it says...
> A statistical method published by academics was used to standardise land surface temperatures for each postcode area, which involved combining satellite images for different dates over the past three years.
Further...
> Using satellite data from 4 Earth Intelligence, the BBC mapped how vulnerable postcode areas were to extreme heat in England, Wales and Scotland during periods of hot weather over the past three summers - shown with a heat hazard score.
The temperature was from....
> The potential heat hazard score for each postcode area was calculated by 4EI, who measured the average land surface temperature over a sample of days in the past three summers across Britain.
The score was calculated ....
Index 1 = 40th percentile and lower, if you lined up all the postcodes by heat hazard score, your postcode is in the coolest 40%.
Index 2 = 40th - 70th percentile, your postcode is in the mid-range. 40% of postcodes have a lower heat hazard than yours but 30% have a higher one.
Index 3 is the 70-90th, 4 is the 90-99th and 5 is the 99th.
However, feel free to change these assumptions, for example, as noted earlier is it more appropriate to use the percentile within each spatial unit than compare then across the entire study area?
To calculate the percentile rank of the mean temperature per spatial unit, in my first attempt at this I downloaded my data from GEE and ran it in R....I then consulted Ollie Ballinger who helped me re-create the `case_when()` that you see below from R...First the R attempt...
#### R
First make a quick map of our mean temperature per spatial unit, in my case wards...
```{r, message=FALSE, warning=FALSE}
library(tidyverse)
library(sf)
library(tmap)
temp <- st_read(here::here("prac_8",
"mean_Landsat_ward.shp"))
tmap_mode("plot")
# plot each map
tm1 <- tm_shape(temp) +
tm_polygons("mean",
# breaks=breaks,
palette="Reds")+
tm_legend(show=TRUE)+
tm_layout(frame=FALSE)
tm1
```
Then for percentile rank...the percentile code is [from stackexchange](https://stats.stackexchange.com/questions/11924/computing-percentile-rank-in-r/11928#11928)
Note, that percentile rank is "percentage of scores within a norm group that is lower than the score you have"....if you are in the 25% then 25% of values are below that [value and it is the 25 rank...](https://www.statisticshowto.com/probability-and-statistics/percentiles-rank-range/)
```{r, message=FALSE, warning=FALSE}
perc.rank <- function(x) trunc(rank(x))/length(x)
percentile <- temp %>%
mutate(percentile1 = perc.rank(mean))
```
Now assign the classes...
```{r, message=FALSE, warning=FALSE}
perc.rank <- function(x) trunc(rank(x))/length(x)
percentile <- temp %>%
mutate(percentile1 = (perc.rank(mean))*100)%>%
mutate(Level = case_when(between(percentile1, 0, 39.99)~ "1",
between(percentile1, 40, 69.99)~ "2",
between(percentile1, 70, 89.99)~ "3",
between(percentile1, 90, 98.99)~ "4",
between(percentile1, 99, 100)~ "5"))
```
To understand this a bit further check out the percentiles of the breaks we set...a percentile is.."
> a statistical measure that indicates the value below which a percentage of data falls, [Rbloggers](https://www.r-bloggers.com/2022/06/r-percentile/)
Run the code below then open your `percentile` data above, you should see that the ranks correspond to the percentiles set...
```{r, message=FALSE, warning=FALSE}
test<-quantile(temp$mean, c(0.39999, 0.69999, 0.8999, 0.98999, 1.0))
test
```
In my case a temperature of 39.87138 is set to level 2, whilst a temperature of 39.87861 is set to level 3...
Map the heat index....
```{r, message=FALSE, warning=FALSE}
# plot each map
tm1 <- tm_shape(percentile) +
tm_polygons("Level",
# breaks=breaks,
palette="Reds")+
tm_legend(show=TRUE)+
tm_layout(frame=FALSE)
tm1
```
Interactive...
```{r, message=FALSE, warning=FALSE}
library(terra)
temp_rast <- rast("prac_8/subtracted_mean_HI.tif")
tmap_mode("view")
interactive <- tm_shape(temp_rast) +
tm_raster("subtracted_mean_HI.tif",
palette = "Reds") +
tm_shape(percentile) +
tm_polygons("Level",
# breaks=breaks,
palette="Reds",
fillOpacity = 0.7,
)+
tm_legend(show=TRUE)+
tm_layout(frame=FALSE)
interactive
```
#### GEE
This is very similar (but slightly different) in GEE....
First of all we need to get the percentiles of the mean column...
```{r, eval=FALSE}
var percentiles = mean_Landsat_ward.reduceColumns({
reducer: ee.Reducer.percentile([10, 20, 30, 40, 50, 60, 70, 80, 90, 99, 100]),
selectors: ['mean']
});
print(percentiles, "percentile") ///gives the overall percentile then need to map
```
Next, pull out these values are numbers....
```{r, eval=FALSE}
var p40 = ee.Number(percentiles.get('p40'))
var p70 = ee.Number(percentiles.get('p70'))
var p90 = ee.Number(percentiles.get('p90'))
var p99 = ee.Number(percentiles.get('p99'))
var p100 = ee.Number(percentiles.get('p100'))
print(p40, "p40")// check one
```
Now, do the `case_when()` equivalent ....it's a lot of code!
```{r, eval=FALSE}
var resub= mean_Landsat_ward.map(function(feat){
return ee.Algorithms.If(ee.Number(feat.get('mean')).lt(p40).and(ee.Number(feat.get('mean')).gt(0)),
feat.set({'percentile_cat': 1}),
ee.Algorithms.If(ee.Number(feat.get('mean')).gt(p40).and(ee.Number(feat.get('mean')).lt(p70)),
feat.set({'percentile_cat': 2}),
ee.Algorithms.If(ee.Number(feat.get('mean')).gt(p70).and(ee.Number(feat.get('mean')).lt(p90)),
feat.set({'percentile_cat': 3}),
ee.Algorithms.If(ee.Number(feat.get('mean')).gt(p90).and(ee.Number(feat.get('mean')).lt(p99)),
feat.set({'percentile_cat': 4}),
ee.Algorithms.If(ee.Number(feat.get('mean')).gt(p99).and(ee.Number(feat.get('mean')).lte(p100)),
feat.set({'percentile_cat': 5}),
feat.set({'percentile_cat': 0}))
))))})
```
Next up is setting some visualization parameters, i got the CSS colour codes (HEX codes) from [colorBrewer](https://colorbrewer2.org/#type=sequential&scheme=BuGn&n=3)
```{r, eval=FALSE}
var visParams_vec = {
'palette': ['#fee5d9', '#fcae91', '#fb6a4a', '#de2d26', '#a50f15'],
'min': 0.0,
'max': 5,
'opacity': 0.8,
}
```
Now, we make an image from out our column in the vector data....
```{r, eval=FALSE}
var image = ee.Image().float().paint(resub, 'percentile_cat')
```
Finally, we map it....
```{r, eval=FALSE}
Map.addLayer(image, visParams_vec, 'Percentile_cat')
```
```{r echo=FALSE, out.width = "900px", fig.align='center', cache=FALSE}
knitr::include_graphics('prac_8/GEE_temp_index.png')
```
As as side note here, it we want to add columns in GEE like we would in R using `mutate()` or a `field calculator` in GIS software we use something like this...
```{r, eval=FALSE}
//example of field calculator or mutate in R...
var collectionWithCount = mean_Landsat_ward.map(function (feature) {
///make a new column
return feature.set('percentage',
// take the mean column and * 100
feature.getNumber('mean').multiply(100));
});
```
Now we have the temperature per ward (or the index), we could explore factors of the built environment that might contribute to that temperature....such as...buildings, lack of vegetation, lack of water areas and so on. This could include some more analysis in GEE such as NDVI, [building area](https://developers.google.com/earth-engine/datasets/catalog/GOOGLE_Research_open-buildings_v1_polygons) per spatial unit or other data from local authorities...
## Considerations
* [Google Earth Engine: Analyzing Land Surface Temperature Data](https://docs.google.com/document/d/1WDxMR5UpxqiTB4GsJcdP9AB0hP-Z8oaX-O9akBg2PZ0/edit)
* Following the lecture, could red lined areas experience higher temperatures?
* Could a similar analysis be done for other variables? Such as [pollution](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S5P_OFFL_L3_NO2)
* Could we make a pollution index similar to the heat index
* Could we make a [flood index](https://developers.google.com/earth-engine/datasets/catalog/GLOBAL_FLOOD_DB_MODIS_EVENTS_V1) or explore flooding over time in cities / determine areas that need remediation
## Learning diary
Consult the assignment requirements document and complete your learning diary entry in your Quarto learning diary.
## Feedback
Was anything that we explained unclear this week or was something really clear...let us know using the [feedback form](https://forms.gle/ArGHKA2sSmN29pVLA). It’s anonymous and we’ll use the responses to clear any issues up in the future / adapt the material.