US nighttime lights data, 1994 (DMSP, composite from 29 orbits)
US nighttime lights data, 1995 (DMSP, composite from 236 orbits)
Nearly all production and consumption after sundown requires (and emits) light
Remote sensing of nighttime lights allows us to observe human activity from space
Emitting light
Economic activity, income, growth
Luminosity and GDP/capita
Urbanization
South Asia, 1994
South Asia, 2010
Emergency management, recovery
Before landfall
After landfall
Armed conflict
October 2021
October 2022
Technological change
Alles ist erleuchtet
Space-based sensors for night-lights (partial list)
Sensor | Spatial resolution | Temporal resolution | Availability | Free? | On-board calibration |
---|---|---|---|---|---|
DMSP/OLS | 3km | Monthly | 1992-2013 | \(\checkmark\) | |
Landsat 8 | 30m | Irregular | 2013- | \(\checkmark\) | |
VIIRS/DNB | 740m | Daily | 2012- | \(\checkmark\) | \(\checkmark\) |
EROS-B | <1m | Daily | 2013- | \(\checkmark\) | |
Jilin-1 | <1m | Daily | 2017- | \(\checkmark\) |
Defense Meteorological Satellite Program (DMSP)
DMSP-5D2
Visible Infrared Imaging Radiometer Suite (VIIRS)
Suomi NPP
Not all orbits make good data
Requirements for nighttime light observation:
Swath path
Illustration of cloud cover contamination
Daily VIIRS data for Ukraine, 2021-2022
Overview of lab exercise
Vignette 1 / Korea
Vignette 2 / Hispaniola
Vignette 3 / Syria
We can obtain VIIRS nighttime luminosity (vnl) data from eogdata.mines.edu/products/vnl/
Scroll down to the “Annual VNL V2” section
Click on the “Go to Download V2.2” button for the most recent year’s data
Download the file ending with .average_masked.dat.tif.gz
Let’s also grab data for the “oldest” year available.
Navigate to the parent directory and find the annual data in the v21/
folder
There are two files ending in .average_masked.dat.tif.gz
here.
Download the first one (April through December)
Let’s get some country and administrative boundaries from geoboundaries.org
Navigate to the “Individual Country Files” section
Download country-level (ADM0
) data for South and North Korea (KOR
, PRK
)
When you unzip, the only file you need to extract is one ending in ADM0.geojson
Repeat this process for country-level (ADM0
) data for Haiti (HTI
)…
and country-level (ADM0
) data for Dominican Republic (DOM
)…
and district-level (ADM2
) data for Syria (SYR
)
We will be using the same event data on violence as in the last lab:
UCDP GED version 23.1, in csv
format
Here is the full list of data sources and links:
Category | Type | Format | Data source |
---|---|---|---|
Nighttime luminosity | Raster | .tif |
VIIRS |
Administrative units | Vector (polygons) | .geojson |
geoBoundaries |
Political violence | Table (non-geo) | .csv |
UCDP GED |
These are all in the Lab10WT02.zip
file posted on Canvas.
Always save your progress!
Go to Project
\(\to\) Save As...
Vignette 1. Load the 2023 VNL data (Layer
\(\to\) Add Layer
\(\to\) Add Raster Layer
). VNL_npp_2023_....tif
file in Data/VIIRS
folder
The default color scheme is too dark. Let’s see if we can add some contrast
In the layer’s Properties
, change Render type
to Singleband pseudocolor
and set Mode
to Quantile
. Click Classify
and OK
This is probably too much contrast, but at least we can see the distribution
Load country boundaries for the two Koreas (Layer
\(\to\) Add Layer
\(\to\) Add Vector Layer
). 2 files: geoBoundaries-PRK-ADM0.geojson
and geoBoundaries-KOR-ADM0.geojson
from Data/geoBoundaries
folder.
Let’s merge the two Koreas into a single layer (Vector
menu \(\to\) Data Management Tools
\(\to\) Merge Vector Layers...
)
Input layers
\(=\) geoBoundaries-KOR-ADM0
and geoBoundaries-PRK-ADM0
Save the merged file as koreas.geojson
Change the symbology of the new layer, to make all but the borders transparent
Let’s extract the part of the global VNL raster that overlaps with the Koreas.
Go to Raster
menu \(\to\) Extraction
\(\to\) Clip Raster by Mask Layer...
Set parameters
Input layer
\(=\) VNL_npp_2023...
Mask layer
\(=\) koreas
Match the extent of the clipped raster to the extent of the mask layer
koreas_nl2023_mask.tif
The clipped raster should look something like this
You can zoom in to see Seoul in greater detail
Change color scheme to Singleband pseudocolor
, Quantile
again for contrast
General tip: set Interpolation
\(=\) Discrete
, Mode
\(=\) Equal interval
and manually edit the cutpoints like this
This way, you can customize the appearance of the map for your needs
This map is ready to be exported (you know how to do this)
Now for Vignette 2, let’s repeat this process for the island of Hispaniola.
Load country boundaries for Haiti and the Dominican Republic (2 files: geoBoundaries-HTI-ADM0.geojson
and geoBoundaries-DOM-ADM0.geojson
from Data/geoBoundaries
).
Once loaded, let’s merge the two countries into a single layer again (Vector
menu \(\to\) Data Management Tools
\(\to\) Merge Vector Layers...
)
Input layers
\(=\) geoBoundaries-DOM-ADM0
and geoBoundaries-HTI-ADM0
Save the merged file as hispaniola.geojson
Let’s extract the part of the global VNL raster that overlaps with the island.
Go to Raster
menu \(\to\) Extraction
\(\to\) Clip Raster by Mask Layer...
Set Input layer
\(=\) VNL_npp_2023...
, Mask layer
\(=\) hispaniola
. Save file as hispaniola_nl2023_mask.tif
The clipped raster should look something like this
Customize the color ramp and export the map (just like last time)
Vignette 3! Load administrative boundaries for Syria. File is geoBoundaries-SYR-ADM2.geojson
from Data/geoBoundaries
folder.
You may see a “Select Items to Add” screen after clicking Add
. Click Add Layers
To compare current luminosity to a period earlier in the Syrian Civil War, load the 2012 VNL data (VNL_v21_npp_201204....tif
file in Data/VIIRS
folder)
Let’s extract the parts of both the 2023 and 2012 global VNL rasters that overlap with Syria. Raster
menu \(\to\) Extraction
\(\to\) Clip Raster by Mask Layer...
Set Input layer
\(=\) VNL_npp_2023...
, Mask layer
\(=\) geoBoundaries-SYR-ADM2
. Save file as syria_nl2023_mask.tif
Repeat with Input layer
\(=\) VNL_v21_npp_2012...
, Mask layer
\(=\) geoBoundaries-SYR-ADM2
. Save file as syria_nl2012_mask.tif
Let’s now calculate the difference between 2023 and 2012. Go to Raster
menu \(\to\) Raster Calculator...
Set the expression to "syria_nl2023_mask@1" - "syria_nl2012_mask@1"
, save output layer as syria_nldiff.tif
Explore the distribution of this new raster by modifying the color scheme
(here, purple areas lost luminosity, orange areas gained luminosity)
We can now calculate average differences in luminosity per district, using Zonal statistics
(in the Processing Toolbox
)
Set the Input layer
\(=\) geoBoundaries-SYR-ADM2
, Raster layer
\(=\) syria_nldiff
, Output column prefix
\(=\) nldiff_
. Save the output as syria_nl_1.geojson
Adjust the symbology in the new syria_nl_1
layer to visualize the nldiff_mean
variable with graduated colors, Equal Interval
mode and Symmetric Classification
around 0.00
The district-level luminosity differences should look something like this.
Now let’s see if places hardest-hit by violence saw the biggest declines…
Add the Syrian Civil War violence data to the project, using Add Delimited Text Layer...
. Load the GEDEvent_v23.csv
file in Data/GED
folder. Set X field
\(=\) longitude
and Y field
\(=\) latitude
. Check box \(\checkmark\) Use spatial index
Highlight GED layer and go to Edit
\(\to\) Select
\(\to\) Select by Expression...
Expression
: year>=2012 AND country='Syria' AND where_prec<4
Click Select Features
This procedure should have selected about 72 thousand events within Syria.
Now let’s calculate the number of violent events per district
Open the Count Points in Polygon
tool
Select Polygons
\(=\) syria_nl_1
, Points
\(=\) GEDEvent_v23_1
. Make sure the box is checked next to Selected Features Only
for the points. Name the count field all_violence
, and save the output file as syria_nl_2.geojson
. Click Run
The new layer syria_nl_2
should appear in your project window.
Now, let’s see if there is a relationship between violence and change in luminosity
Let’s run a simple regression model in R to see how violence impacted luminosity.
This code chunk imports the syria_nl_2.geojson
file we created into an object called syr
, and then lists the variable names:
syr = sf::read_sf("Output/syria_nl_2.geojson")
names(syr)
## [1] "shapeName" "shapeISO" "shapeID" "shapeGroup" "shapeType"
## [6] "nldiff_mean" "all_violence" "geometry"
This code chunk estimates an Ordinary Least Squares model that regresses dependent variable nldiff_mean
on explanatory variable all_violence
mod = lm(nldiff_mean~all_violence,data=syr)
summary(mod)
##
## Call:
## lm(formula = nldiff_mean ~ all_violence, data = syr)
##
## Residuals:
## Min 1Q Median 3Q Max
## -19.3243 0.0181 0.4239 0.8397 2.6816
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.4000160 0.4428367 -0.903 0.3700
## all_violence -0.0004699 0.0002258 -2.081 0.0418 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.754 on 59 degrees of freedom
## Multiple R-squared: 0.06839, Adjusted R-squared: 0.0526
## F-statistic: 4.331 on 1 and 59 DF, p-value: 0.04176
Let’s rescale the variable all_violence
to make the coefficient more interpretable.
mod = lm(nldiff_mean~I(all_violence/100),data=syr)
summary(mod)
##
## Call:
## lm(formula = nldiff_mean ~ I(all_violence/100), data = syr)
##
## Residuals:
## Min 1Q Median 3Q Max
## -19.3243 0.0181 0.4239 0.8397 2.6816
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.40002 0.44284 -0.903 0.3700
## I(all_violence/100) -0.04699 0.02258 -2.081 0.0418 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.754 on 59 degrees of freedom
## Multiple R-squared: 0.06839, Adjusted R-squared: 0.0526
## F-statistic: 4.331 on 1 and 59 DF, p-value: 0.04176
For every 100 violent events, the change in luminosity falls by 0.047.
You can perform all these steps in R
(see replication code wt02_demo.R
in Lab10WT02.zip
)
Vignette 1
Vignette 2
Vignette 3