Making maps in R using ggplot and sf
Setup
# Activating packages
suppressWarnings(suppressMessages({
library(tidyverse) # Modern data science workflow
library(stringr) # Functions designed to work with strings
library(sf) # Encodes spatial vector data
library(RColorBrewer) # ColorBrewer Palettes
library(leaflet) # Interactive web maps
library(ggplot2) # Modern visualization tools
library(ggthemes) # For extra themes, scales and geoms for 'ggplot2'
library(patchwork) # make it simple to combine separate ggplots
library(plotly) # Create interactive web graphics from 'ggplot2'
library(ggrepel) ## For displaying labels on ggplot2 object
}))
# Adjustments
knitr::opts_chunk$set(fig.align = 'center')
options(warn = -1)
options(scipen = 10000)
options(repr.plot.width = 6, repr.plot.height = 4)
Import data
df <- read.csv("https://raw.githubusercontent.com/haginta/regional-convergence-indonesia-satellite-gdp/main/java118_.csv")
glimpse(df)
Rows: 118
Columns: 7
$ districtID <int> 3101, 3171, 3172, 3173, 3174, 3175, 3201, 3202, 3203, 3204,…
$ district <chr> "Kabupaten Kepulauan Seribu", "Kota Jakarta Selatan", "Kota…
$ province <chr> "Jakarta", "Jakarta", "Jakarta", "Jakarta", "Jakarta", "Jak…
$ sat2019 <dbl> 71.00861, 21369.91255, 23277.88858, 11117.19703, 19429.9449…
$ sat2020 <dbl> 74.22578, 18978.59423, 21811.98936, 11131.61250, 17563.8948…
$ gdp2019 <dbl> 3900.480, 421291.000, 313550.656, 452820.375, 318891.500, 3…
$ gdp2020 <dbl> 3710.920, 419329.719, 299704.562, 449615.344, 316204.344, 3…
mymap <- read_sf("https://github.com/haginta/regional-convergence-indonesia-satellite-gdp/raw/main/java_118.gpkg")
glimpse(mymap)
Rows: 118
Columns: 7
$ districtID <chr> "3204", "3217", "3526", "3304", "3402", "3302", "3510", "33…
$ Region <chr> "Java -Bali", "Java -Bali", "Java -Bali", "Java -Bali", "Ja…
$ x <dbl> 107.6087, 107.4150, 112.9295, 109.6571, 110.3549, 109.1758,…
$ y <dbl> -7.097588, -6.890438, -7.044336, -7.351327, -7.901333, -7.4…
$ district <chr> "Kabupaten Bandung", "Kabupaten Bandung Barat", "Kabupaten …
$ province <chr> "West Java", "West Java", "East Java", "Central Java", "Yog…
$ geom <MULTIPOLYGON [°]> MULTIPOLYGON (((107.9147 -6..., MULTIPOLYGON (…
st_crs(mymap)
Transform data
df[,4:7] <- log(df[4:7])
head(df)
df <- df %>%
mutate(sat_g = sat2020 - sat2019,
gdp_g = gdp2020 - gdp2019)
head(df)
Merge datasets
df_map <- merge(mymap, df, by=c("districtID", "district", "province"))
df_map <- st_make_valid(df_map)
glimpse(df_map)
Rows: 118
Columns: 13
$ districtID <chr> "3101", "3171", "3172", "3173", "3174", "3175", "3201", "32…
$ district <chr> "Kabupaten Kepulauan Seribu", "Kota Jakarta Selatan", "Kota…
$ province <chr> "Jakarta", "Jakarta", "Jakarta", "Jakarta", "Jakarta", "Jak…
$ Region <chr> "Java -Bali", "Java -Bali", "Java -Bali", "Java -Bali", "Ja…
$ x <dbl> 106.5787, 106.8100, 106.9006, 106.8348, 106.7482, 106.8655,…
$ y <dbl> -5.695733, -6.272524, -6.254792, -6.181257, -6.165224, -6.1…
$ sat2019 <dbl> 4.262801, 9.969739, 10.055259, 9.316248, 9.874571, 10.05763…
$ sat2020 <dbl> 4.307111, 9.851067, 9.990215, 9.317544, 9.773601, 9.991138,…
$ gdp2019 <dbl> 8.268855, 12.951079, 12.655716, 13.023251, 12.672606, 12.71…
$ gdp2020 <dbl> 8.219035, 12.946413, 12.610552, 13.016148, 12.664144, 12.65…
$ sat_g <dbl> 0.044310299, -0.118672253, -0.065044133, 0.001295842, -0.10…
$ gdp_g <dbl> -0.0498198173, -0.0046662775, -0.0451637324, -0.0071030980,…
$ geometry <MULTIPOLYGON [°]> MULTIPOLYGON (((106.484 -5...., MULTIPOLYGON (…
Maps
Simple boundaries
df_map %>%
ggplot() +
geom_sf()
Labeled boundaries
#Create dataframe of selected cities
city <- c("Kota Jakarta Pusat", "Kota Yogyakarta", "Kota Surabaya")
h_city <- df_map %>% filter(district %in% city)
#Add coordinates
coordinates <- as.data.frame(st_coordinates(st_centroid(h_city)))
h_city$X <- coordinates$X
h_city$Y <- coordinates$Y
glimpse(h_city)
Rows: 3
Columns: 15
$ districtID <chr> "3173", "3471", "3578"
$ district <chr> "Kota Jakarta Pusat", "Kota Yogyakarta", "Kota Surabaya"
$ province <chr> "Jakarta", "Yogyakarta", "East Java"
$ Region <chr> "Java -Bali", "Java -Bali", "Java -Bali"
$ x <dbl> 106.8348, 110.3744, 112.7238
$ y <dbl> -6.181257, -7.803298, -7.274398
$ sat2019 <dbl> 9.316248, 8.141781, 10.585382
$ sat2020 <dbl> 9.317544, 8.040169, 10.599126
$ gdp2019 <dbl> 13.02325, 10.22866, 12.92605
$ gdp2020 <dbl> 13.01615, 10.20417, 12.87630
$ sat_g <dbl> 0.001295842, -0.101612475, 0.013744240
$ gdp_g <dbl> -0.007103098, -0.024490707, -0.049754625
$ geometry <MULTIPOLYGON [°]> MULTIPOLYGON (((106.7968 -6..., MULTIPOLYGON (((110.3908 -7…
$ X <dbl> 106.8347, 110.3746, 112.7242
$ Y <dbl> -6.181886, -7.803409, -7.274890
ggplot() +
geom_sf(data = h_city, aes(fill = district), lwd = 0) +
geom_sf(data = df_map, aes(fill = NA, alpha=0), show.legend = F) +
geom_sf(data = h_city, aes(fill = district, alpha=0), show.legend = F, color="red") +
geom_label_repel(data = h_city, aes(x = X, y = Y, label = district), size=2, color="blue", point.padding = NA) +
theme(legend.position="None") +
labs(x = "Longitude", y="Latitude")
Choropleth map
ggplot(df_map) +
geom_sf(aes(fill=sat2019), color = "white", lwd = 0.05) +
scale_fill_viridis_c(option = "magma", name = "Radiance (log)") +
theme_map() +
theme(legend.direction="horizontal") +
labs(title = "Districts of Java seen from space at night",
subtitle = "Nighttime light (NTL) intensity across 118 districts in Java, Indonesia, 2019",
caption = "Geometries: GADM \n Data: Earth Observation Group")
ggplot() +
geom_sf(data = h_city, aes(fill = sat2019), color = "white", lwd = 0.05) +
geom_sf(data = df_map, aes(fill = sat2019), color = "white", lwd = 0.05, show.legend = T) +
geom_sf(data = h_city, aes(fill = sat2019), show.legend = F, color="red") +
scale_fill_viridis_c(option = "magma", name = "Radiance (log)") +
theme_map() +
geom_label_repel(data = h_city, aes(x = X, y = Y, label = district), size=2, color="blue", point.padding = NA) +
theme(legend.direction="horizontal", legend.position=c(0.05, -0.12))+
labs(x = "Longitude", y="Latitude")
Comparative maps
p1 <- ggplot(df_map) +
geom_sf(aes(fill=sat2020), color = "white", lwd = 0.05) +
scale_fill_viridis_c(option = "magma", name = "Radiance (log)") +
theme_map() +
theme(legend.direction="horizontal", legend.position=c(-0.0, -0.15))+
labs(subtitle = "(a) Spatial distribution of NTL per capita")
p2 <- ggplot(data = df_map) +
geom_sf(aes(fill=gdp2020), color = "white", lwd = 0.05) +
scale_fill_viridis_c(option = "magma", name = "GDP (log)") +
theme_map() +
theme(legend.direction="horizontal", legend.position=c(-0.0, -0.15))+
labs(subtitle = "(b) Spatial distribution of GDP per capita")
p1 / p2
Interactive
p <- ggplot(df_map) + geom_sf(aes(fill=gdp2019, label=district), lwd = 0.05) +
scale_fill_viridis_c(option = "magma", name = "GDP (log)") + theme_bw() +
labs(x = "Longitude", y="Latitude", title = "GDP across 118 districts in Java, Indonesia, 2019 (constant price 2010)")
p_inter <- ggplotly(p)
p_inter