# Data Import and Wrangling Tools
library(tidyverse) # All things tidy
library(here) # File locations and paths
# Final plot tools
library(scales) # Nice Scales for ggplot2
library(fontawesome) # Icons display in ggplot2
library(ggtext) # Markdown text support for ggplot2
library(showtext) # Display fonts in ggplot2
library(colorspace) # Lighten and Darken colours
library(patchwork) # Combining plots
library(gt) # Beautiful Tables in R
# Mapping tools
library(sf) # All spatial objects in R
library(osmdata) # Getting Open Street Maps dataVisualizing Highways and Healthcare Facilities
Analyzing the integration of healthcare facilities and transportation networks - using power of {osmdata}, {sf} and {ggspatial}
Introduction
This project visualizes the distribution of health facilities in Haryana, India, alongside the national highways, using data from OpenStreetMap. The primary aim is to map health facilities categorized as small, medium, or large and assess their proximity to highways (within 200 meters or farther).
Process:
Data Collection: Using the
osmdatapackage, bounding boxes for Haryana, Delhi and later India were defined. Data on highways and health facilities were downloaded, pre-processed, and saved for subsequent analysis.Spatial Processing: The
sfpackage (Pebesma and Bivand 2023) facilitated spatial operations, such as clipping datasets to Delhi’s, Haryana’s and India’s boundaries, transforming geometry types (e.g., polygons to points on the map usingst_centroid()), and, Most importantly, calculating distances between health facilities and highways usingst_is_within_distance()with techniques from the book Geocomputation with R (Chapter 4).Visualization: A detailed map was created with
ggplot2, incorporating highways, health facilities, and annotations for scale and direction usingggspatial. Facilities were color-coded (near or far from highways) and differentiated by size and shape.Inset Charts: Additional insights were provided through inset charts showing the number of health facilities by district in Haryana, and the percentage of facilities near highways, categorized by size, across India.
Key Insights:
Proximity Analysis: The project quantifies the percentage of health facilities near highways, highlighting disparities in accessibility.
Facility Distribution: The data shows varying distributions of small, medium, and large facilities across districts, providing insights into healthcare accessibility.
Comprehensive Visualization: The use of
patchworkto combine plots andgtfor tabular data enhances the project’s visual and analytical depth.
The final map, saved as an image using ggsave, effectively communicates the spatial relationship between health infrastructure and transportation networks. This visualization can inform infrastructure planning and policy decisions.
Loading Libraries
Part 1: Getting Raw Data
This code demonstrates how to query and store OpenStreetMap (OSM) data for health amenities and highways in Haryana and Delhi using the osmdata package ( et al. 2017). Initially, bounding boxes for Haryana (hy_bbox) and Delhi (del_bbox) are obtained using osmdata::getbb(). For each region, two categories of health facilities are retrieved: smaller establishments like clinics, dentists, and pharmacies (health_small) and larger institutions like hospitals and nursing homes (health_large). These datasets are fetched using the opq() function to create an Overpass API query, combined with add_osm_feature() to specify the type of amenities, and are then converted into simple feature (sf) objects via osmdata_sf().
Similarly, highways are categorized into major roads (motorways and trunks) and secondary roads (primary and secondary), and their data is retrieved and converted into sf objects. To ensure efficient future use, all datasets are temporarily saved as RDS files using saveRDS(). This structured approach ensures the data is readily accessible for visualization or analysis. For more on querying OSM data, refer to the osmdata package documentation.
Code
hy_bbox <- osmdata::getbb("Haryana")
del_bbox <- osmdata::getbb("Delhi")
######## Haryana Health
health_small <- opq(bbox = hy_bbox) |>
add_osm_feature(
key = "amenity",
value = c("clinic", "dentist", "doctors", "pharmacy")
) |>
osmdata_sf()
object.size(health_small) |> print(units = "Mb")
health_large <- opq(bbox = hy_bbox) |>
add_osm_feature(
key = "amenity",
value = c("hospital", "nursing_home", "social_facility")
) |>
osmdata_sf()
object.size(health_large) |> print(units = "Mb")
# Temporary Saving the Data for easy use in travel
saveRDS(
object = health_large,
file = "haryana_health_large.rds"
)
saveRDS(
object = health_small,
file = "haryana_health_small.rds"
)
# Haryana Highways ---------------------
roads_1 <- opq(bbox = hy_bbox) |>
add_osm_feature(
key = "highway",
value = c("motorway", "trunk", "motorway_link", "trunk_link")
) |>
osmdata_sf()
object.size(roads_1) |> print(units = "Mb")
roads_2 <- opq(bbox = hy_bbox) |>
add_osm_feature(
key = "highway",
value = c("primary", "secondary")
) |>
osmdata_sf()
object.size(roads_2) |> print(units = "Mb")
# Temporary Saving the Data for easy use in travel
saveRDS(
object = roads_1,
file = "haryana_roads_1.rds"
)
saveRDS(
object = roads_2,
file = "haryana_roads_2.rds"
)
############ Delhi Health
health_small <- opq(bbox = del_bbox) |>
add_osm_feature(
key = "amenity",
value = c("clinic", "dentist", "doctors", "pharmacy")
) |>
osmdata_sf()
object.size(health_small) |> print(units = "Mb")
health_large <- opq(bbox = del_bbox) |>
add_osm_feature(
key = "amenity",
value = c("hospital", "nursing_home", "social_facility")
) |>
osmdata_sf()
object.size(health_large) |> print(units = "Mb")
# Temporary Saving the Data for easy use in travel
saveRDS(
object = health_large,
file = "delhi_health_large.rds"
)
saveRDS(
object = health_small,
file = "delhi_health_small.rds"
)
# Delhi Highways ---------------------
roads_1 <- opq(bbox = del_bbox) |>
add_osm_feature(
key = "highway",
value = c("motorway", "trunk", "motorway_link", "trunk_link")
) |>
osmdata_sf()
object.size(roads_1) |> print(units = "Mb")
roads_2 <- opq(bbox = del_bbox) |>
add_osm_feature(
key = "highway",
value = c("primary", "secondary")
) |>
osmdata_sf()
object.size(roads_2) |> print(units = "Mb")
# Temporary Saving the Data for easy use in travel
saveRDS(
object = roads_1,
file = "delhi_roads_1.rds"
)
saveRDS(
object = roads_2,
file = "delhi_roads_2.rds"
)Part 2: Get massive data for India
This code focuses on querying and saving OpenStreetMap (OSM) data for health amenities and highways across entire India. To ensure efficient handling of such a large geographical scope, the data retrieval process is divided into smaller datasets. Health facilities are then split into two categories: smaller facilities like clinics, dentists, and pharmacies (health_small) and larger institutions such as hospitals, nursing homes, and social facilities (health_large). For highways, the script retrieves major road types, including motorways and trunk roads (roads_1) only. While a placeholder for secondary roads exists (roads_2), it is commented out to optimize download times or avoid potential interruptions. To facilitate future use, all retrieved datasets are saved as RDS files using saveRDS().
Code
###########################################################
############### India #################
###########################################################
in_bbox <- osmdata::getbb("India")
######## India Health -----------------------------------------
# Note: I have split downloading health facilties into larger and
# Smaller ones to save on downloading times, and prevent
# breakage during downloading such large datasets
health_small <- opq(bbox = in_bbox) |>
add_osm_feature(
key = "amenity",
value = c("clinic", "dentist", "doctors", "pharmacy")
) |>
osmdata_sf()
object.size(health_small) |> print(units = "Mb")
# Temporarily Saving the Data for easy use in travel / reload
saveRDS(
object = health_small,
file = "india_health_small.rds"
)
health_large <- opq(bbox = in_bbox) |>
add_osm_feature(
key = "amenity",
value = c("hospital", "nursing_home")
) |>
osmdata_sf()
object.size(health_large) |> print(units = "Mb")
# Temporarily Saving the Data for easy use in travel / reload
saveRDS(
object = health_large,
file = "india_health_large.rds"
)
########### India Highways -----------------------------------
# Troubleshoot: Large data, hence need to increase time-out
# Credits: https://github.com/ropensci/osmdata/issues/200
roads_1 <- opq(bbox = in_bbox, timeout = 500) |>
add_osm_feature(
key = "highway",
value = c("motorway")
) |>
osmdata_sf()
object.size(roads_1) |> print(units = "Mb")
saveRDS(
object = roads_1,
file = "india_roads_1.rds"
)
roads_1_5 <- opq(bbox = in_bbox, timeout = 100) |>
add_osm_feature(
key = "highway",
value = c("trunk")
) |>
osmdata_sf()
object.size(roads_1) |> print(units = "Mb")
saveRDS(
object = roads_1_5,
file = "india_roads_1_5.rds"
)
#
# roads_2 <- opq(bbox = in_bbox) |>
# add_osm_feature(
# key = "highway",
# value = c("primary", "secondary")
# ) |>
# osmdata_sf()
#
# object.size(roads_2) |> print(units = "Mb")
#
# # Temporary Saving the Data for easy use in travel
#
# saveRDS(
# object = roads_2,
# file = "india_roads_2.rds"
# )Part 3: Trial Code with Delhi’s smaller dataset
The process begins by loading the saved datasets for health facilities (health_small and health_large) and highways (roads_1 and roads_2) using readRDS(). Each dataset is further refined to include only the relevant geographic data (osm_lines for highways and points or centroids for health facilities).
A shapefile of Delhi’s boundary is imported and cleaned, ensuring that the spatial coordinate reference systems match for all datasets. Highways and health facilities are filtered to include only those within Delhi’s boundary using st_intersection().
For health facilities, geometries (e.g., polygons) are converted to centroids, ensuring consistency in spatial representation. Facilities are classified based on size (e.g., “small”, “medium”, or “large”) and evaluated for their proximity to highways using st_is_within_distance(). A distance threshold of 200 meters is applied to categorize facilities as "near_highway" or "far_from_highway".
The results are summarized in tabular format for both small and large health facilities, showing the distribution of facilities based on proximity to highways. Finally, the data is visualized in Figure 1 using ggplot2, with:
Highways (
roads_1androads_2) displayed as lines of varying weights and colors.Health facilities plotted as points, styled by their proximity to highways.
Code
# Get pre-saved data (Trial Phase)
health_small <- readRDS(file = "delhi_health_small.rds")
health_large <- readRDS(file = "delhi_health_large.rds")
roads_1 <- readRDS(file = "delhi_roads_1.rds")
roads_2 <- readRDS(file = "delhi_roads_2.rds")
# Get only the relevant sf obejct out of each
roads_1 <- roads_1$osm_lines
roads_2 <- roads_2$osm_lines
# Get a Map of the State / City and keep only the highways and facilties within that State / City
region_map <- sf::read_sf(
here::here("data", "india_map", "India_State_Boundary.shp")
) |>
janitor::clean_names() |>
filter(state_name == "Delhi") |>
st_transform(crs = st_crs(roads_1))
# ggplot() +
# geom_sf(data = region_map) +
# geom_sf(data = roads_1)
#
# ggplot() +
# geom_sf(data = region_map) +
# geom_sf(data = roads_1 |> st_intersection(region_map))
# Select Roads and Facilties that are within the selected region
roads_1 <- roads_1 |> st_intersection(region_map)
roads_2 <- roads_2 |> st_intersection(region_map)
# Health facilities are as points, polygons and multi-polygons.
# Lets convert them all into points, since on a large map
# like a city or state, all will anyways appear as a point
# Exploration of data available in the health facilities
# health_large$osm_points |>
# st_drop_geometry() |>
# select(name, beds, building, `building:levels`,highway, short_name) |>
# visdat::vis_miss()
# names()
# Do for health_large -------------------------------------------
temp1 <- health_large$osm_points |>
select(name, geometry) |>
filter(!is.na(name)) |>
mutate(facility_type = "medium")
temp2 <- health_large$osm_polygons |>
st_centroid() |>
select(name, geometry) |>
filter(!is.na(name)) |>
mutate(facility_type = "large")
temp3 <- health_large$osm_multipolygons |>
st_centroid() |>
select(name, geometry) |>
filter(!is.na(name)) |>
mutate(facility_type = "large")
health_large <- bind_rows(
temp1,
temp2,
temp3
)
rm(temp1, temp2, temp3)
health_large <- health_large |>
st_intersection(region_map)
health_large <- health_large |>
mutate(
near_highway = health_large |>
# Checking which points are within a specific distance of highway
st_is_within_distance(
y = roads_1,
dist = 200,
sparse = F
) |>
as_tibble() |>
mutate(
any_true = rowSums(across(everything(), as.logical)) > 0
) |>
pull(any_true)
) |>
mutate(
near_highway = if_else(
near_highway,
"near_highway",
"far_from_highway"
)
)
# A Table of near and away from highway facilties for
# Large and Medium Health Facilties
table_large <- health_large |>
st_drop_geometry() |>
count(facility_type, near_highway) |>
pivot_wider(
id_cols = facility_type,
names_from = near_highway,
values_from = n
) |>
mutate(
perc_on_highway = near_highway / (near_highway + far_from_highway)
)
# Do for health_small --------------------------------------------
temp1 <- health_small$osm_points |>
select(name, geometry) |>
filter(!is.na(name)) |>
mutate(facility_type = "small")
temp2 <- health_small$osm_polygons |>
st_centroid() |>
select(name, geometry) |>
filter(!is.na(name)) |>
mutate(facility_type = "medium")
# temp3 <- health_small$osm_multipolygons |>
# st_centroid() |>
# select(name, geometry) |>
# mutate(facility_type = "medium")
health_small <- bind_rows(
temp1,
temp2
)
rm(temp1, temp2)
health_small <- health_small |>
st_intersection(region_map)
health_small <- health_small |>
mutate(
near_highway = health_small |>
# Checking which points are within a specific distance of highway
st_is_within_distance(
y = roads_1,
dist = 200,
sparse = F
) |>
as_tibble() |>
mutate(
any_true = rowSums(across(everything(), as.logical)) > 0
) |>
pull(any_true)
) |>
mutate(
near_highway = if_else(
near_highway,
"near_highway",
"far_from_highway"
)
)
# A Table of near and away from highway facilties for
# Large and Medium Health Facilties
table_small <- health_small |>
st_drop_geometry() |>
count(facility_type, near_highway) |>
pivot_wider(
id_cols = facility_type,
names_from = near_highway,
values_from = n
) |>
mutate(
perc_on_highway = near_highway / (near_highway + far_from_highway)
)
# The final table product -------------------------------------------
show_table <- bind_rows(table_large, table_small) |>
group_by(facility_type) |>
summarise(
far_from_highway = sum(far_from_highway),
near_highway = sum(near_highway)
) |>
mutate(
perc_on_highway = near_highway / (near_highway + far_from_highway)
)
# The plot
g <- ggplot() +
geom_sf(
data = region_map,
linewidth = 0.5,
colour = "grey20",
fill = "transparent"
) +
geom_sf(
data = roads_1,
linewidth = 0.5,
alpha = 0.8,
colour = "darkgrey"
) +
geom_sf(
data = roads_2,
linewidth = 0.3,
alpha = 0.4,
colour = "lightgrey"
) +
geom_sf(
data = health_large,
mapping = aes(
colour = near_highway,
size = near_highway
),
alpha = 0.8
) +
scale_size_manual(
values = c(0.5, 1.5)
) +
labs(
title = "Delhi: Highways and Health Facilties"
) +
theme_minimal() +
theme(
legend.position = "bottom"
)
ggsave(
plot = g,
filename = here("projects", "images", "highways_heath_1.png"),
height = 1500,
width = 1500,
units = "px"
)
Part 4: Similar analysis for Haryana state
This R code analyzes the proximity of health facilities to highways in Haryana. It loads pre-saved datasets for small and large health facilities and roads, filters them by Haryana’s boundary, and converts all facilities into point geometries for simplicity. The proximity of facilities to highways is assessed using st_is_within_distance from the {sf} package. The code then generates summary tables categorizing facilities by type and proximity to highways. A map is created using {ggplot2} and {ggspatial}, showing highways and health facilities with annotations for scale and orientation. The final map is shown in Figure 2.
Code
# Get pre-saved data (Trial Phase - use pre-saved data to
# save laoding times)
# Note: I have split downloading health facilties into larger and
# Smaller ones to save on downloading times, and prevent
# breakage during downloading such large datasets
health_small <- readRDS(file = "haryana_health_small.rds")
health_large <- readRDS(file = "haryana_health_large.rds")
roads_1 <- readRDS(file = "haryana_roads_1.rds")
roads_2 <- readRDS(file = "haryana_roads_2.rds")
# Get only the relevant sf object out of each downlaoded data-set
# For roads, only "LINES" are relevant sf feature.
roads_1 <- roads_1$osm_lines
roads_2 <- roads_2$osm_lines
# Get a Map of the State / City and keep only the highways and
# facilties within that State / City for plotting (instead of entire)
# bounding box
region_map <- sf::read_sf(
here::here("data", "india_map", "India_State_Boundary.shp")
) |>
janitor::clean_names() |>
filter(state_name == "Haryana") |>
st_transform(crs = st_crs(roads_1))
# Checking the results
# ggplot() +
# geom_sf(data = region_map) +
# geom_sf(data = roads_1)
#
# ggplot() +
# geom_sf(data = region_map) +
# geom_sf(data = roads_1 |> st_intersection(region_map))
# Select Roads and Facilties that are within the selected region
roads_1 <- roads_1 |> st_intersection(region_map)
roads_2 <- roads_2 |> st_intersection(region_map)
# Health facilities are as points, polygons and multi-polygons.
# Lets convert them all into points, since on a large map
# like a city or state, all will anyways appear as a point
# -------------------------------------------------------------------
# Extracting the Larger Health Facilities in a nicely formatted clean
# sf object, to make plotting easier subsequently--------------------
temp1 <- health_large$osm_points |>
select(name, geometry) |>
filter(!is.na(name)) |>
mutate(facility_type = "medium")
temp2 <- health_large$osm_polygons |>
st_centroid() |>
select(name, geometry) |>
filter(!is.na(name)) |>
mutate(facility_type = "large")
temp3 <- health_large$osm_multipolygons |>
st_centroid() |>
select(name, geometry) |>
filter(!is.na(name)) |>
mutate(facility_type = "large")
health_large <- bind_rows(
temp1,
temp2,
temp3
)
# Clean the workspace
rm(temp1, temp2, temp3)
# Retain only the health facilities that are within our region
health_large <- health_large |>
st_intersection(region_map)
# Now the MAGIC of {sf}: mark the facilities that are near highways
health_large <- health_large |>
mutate(
near_highway = health_large |>
# Checking which points are within a specific distance of highway
# The awesome power & speed of sf::st_is_within_distance()
st_is_within_distance(
y = roads_1,
dist = 200,
sparse = F
) |>
as_tibble() |>
# Keep value TRUE if facility is near ANY highway
mutate(
any_true = rowSums(across(everything(), as.logical)) > 0
) |>
pull(any_true)
) |>
mutate(
near_highway = if_else(
near_highway,
"near_highway",
"far_from_highway"
)
)
# An Output Table of near and away from highway facilties for
# Large and Medium Health Facilties
table_large <- health_large |>
st_drop_geometry() |>
count(facility_type, near_highway) |>
pivot_wider(
id_cols = facility_type,
names_from = near_highway,
values_from = n
) |>
mutate(
perc_on_highway = near_highway / (near_highway + far_from_highway)
)
# Do the same process for snmaller health facilities ----------------
temp1 <- health_small$osm_points |>
select(name, geometry) |>
filter(!is.na(name)) |>
mutate(facility_type = "small")
temp2 <- health_small$osm_polygons |>
st_centroid() |>
select(name, geometry) |>
filter(!is.na(name)) |>
mutate(facility_type = "medium")
health_small <- bind_rows(
temp1,
temp2
)
rm(temp1, temp2)
health_small <- health_small |>
st_intersection(region_map)
health_small <- health_small |>
mutate(
near_highway = health_small |>
# Checking which points are within a specific distance of highway
st_is_within_distance(
y = roads_1,
dist = 200,
sparse = F
) |>
as_tibble() |>
mutate(
any_true = rowSums(across(everything(), as.logical)) > 0
) |>
pull(any_true)
) |>
mutate(
near_highway = if_else(
near_highway,
"near_highway",
"far_from_highway"
)
)
# An output Table of near and away from highway facilties for
# Large and Medium Health Facilties
table_small <- health_small |>
st_drop_geometry() |>
count(facility_type, near_highway) |>
pivot_wider(
id_cols = facility_type,
names_from = near_highway,
values_from = n
) |>
mutate(
perc_on_highway = near_highway / (near_highway + far_from_highway)
)
# The final table product -------------------------------------------
show_table <- bind_rows(table_large, table_small) |>
group_by(facility_type) |>
summarise(
far_from_highway = sum(far_from_highway),
near_highway = sum(near_highway)
) |>
mutate(
perc_on_highway = near_highway / (near_highway + far_from_highway)
) |>
mutate(
facility_type = case_when(
facility_type == "large" ~ "Large establishments: Hospitals, Nursing Homes, Institutions",
facility_type == "medium" ~ "Medium: Clinics, Doctors's Practices",
facility_type == "small" ~ "Small facilties: Dentists, Pharmacies",
.default = NA
)
)
# Combining health_large and health_small to map shape to facility size
health_combined <- bind_rows(
health_large,
health_small
) |>
mutate(
facility_type = fct(
facility_type,
levels = c("small", "medium", "large")
)
)
rm(health_large, health_small)
# The plot
g <- ggplot() +
geom_sf(
data = region_map,
linewidth = 0.5,
colour = "grey20",
fill = "transparent"
) +
geom_sf(
data = roads_1,
linewidth = 0.5,
alpha = 0.8,
colour = "darkgrey"
) +
geom_sf(
data = roads_2,
linewidth = 0.3,
alpha = 0.4,
colour = "lightgrey"
) +
geom_sf(
data = health_combined,
mapping = aes(
colour = near_highway,
size = facility_type,
shape = facility_type
),
alpha = 0.7
) +
scale_size_manual(
values = c(0.2, 1, 2)
) +
scale_shape_manual(
values = c(20, 2, 0)
) +
scale_colour_manual(
values = c("lightblue", "red")
) +
# Add North-Arrow and Scale
ggspatial::annotation_north_arrow(
style = ggspatial::north_arrow_orienteering(
line_col = "grey20",
fill = c("grey90", "grey10")
),
location = "tl"
) +
ggspatial::annotation_scale(
bar_cols = c("grey10", "white"),
location = "bl"
) +
labs(
title = "Haryana: Highways and Health Facilties",
shape = NULL,
size = NULL,
colour = NULL
) +
theme_minimal() +
theme(
legend.position = "bottom",
legend.direction = "vertical"
)
ggsave(
plot = g,
filename = here("projects", "images", "highways_heath_2.png"),
height = 2500,
width = 2000,
units = "px"
)
Part 5: Try code for a {gt} table and an inset plot
This R code generates a table and a plot to analyze the proximity of health facilities to highways in Haryana. First, a summary table is created using {gt} with bar plots showing the percentage of health facilities located near highways. The table is formatted using gtExtras::gt_theme_538() for styling. Then, a plot is created with {ggplot2}, visualizing the percentage of health facilities located near highways by facility type (small, medium, large) and state. The data is transformed using {tidyverse} functions such as pivot_wider and count, and the plot is faceted by facility type for clarity.
Code
insert_gt_table <- show_table |>
mutate(facility_type = str_to_title(facility_type)) |>
rename(percentage_on_highway = perc_on_highway) |>
gt() |>
cols_label_with(fn = snakecase::to_title_case) |>
# cols_width(
# facility_type ~ px(100),
# far_from_highway ~ px(20),
# near_highway ~ px(20),
# percentage_on_highway ~ px(50)
# ) |>
gtExtras::gt_plt_bar(
percentage_on_highway,
labels = TRUE,
color = "darkgreen",
scale_type = "percent"
) |>
gtExtras::gt_theme_538()
plot_data_df <- health_combined |>
st_drop_geometry() |>
as_tibble() |>
count(state_name, facility_type, near_highway) |>
pivot_wider(
id_cols = c(state_name, facility_type),
names_from = near_highway,
values_from = n
) |>
mutate(
total = far_from_highway + near_highway,
perc_on_hwy = near_highway / total,
.keep = "unused"
)
strip_labels <- c(
"Small facilties: Dentists, Pharmacies",
"Medium: Clinics, Doctors's Practices",
"Large: Hospitals, Nursing Homes, Institutions"
) |>
str_wrap(30)
names(strip_labels) <- c("small", "medium", "large")
inset_plot <- plot_data_df |>
ggplot(
mapping = aes(
y = state_name,
x = perc_on_hwy
)
) +
geom_col(
alpha = 0.5
) +
geom_text(
mapping = aes(
label = paste0("(", number(total,
scale = 1,
accuracy = 1,
big.mark = ","),
")"),
x = 0
),
nudge_x = 0.01,
hjust = 0
) +
geom_text(
mapping = aes(
label = paste0(round(perc_on_hwy * 100, 2), "%")
),
nudge_x = 0.005,
hjust = 0
) +
labs(
y = NULL,
x = "Health facilities located on the Highway"
) +
facet_wrap(
~ facility_type,
labeller = labeller(
facility_type = strip_labels
)
) +
scale_x_continuous(
expand = expansion(c(0, 0.25)),
labels = scales::label_percent()
) +
theme_minimal(
base_size = 10
) +
theme()Part 6: A final product map for Haryana
The code creates a detailed Figure 3 for Haryana, showcasing highways and healthcare facilities. The map displays highways in different colours and health facilities marked by size and shape based on their type (small, medium, large) and proximity to highways (within 200 meters). The final map includes district boundaries, highways, and health facilities, with color indicating proximity to highways. A secondary plot summarizes healthcare facilities by district and type.
Code
# Basics of Final Visualization: Fonts
font_add_google("Rouge Script", "title_font")
font_add_google("El Messiri", "body_font")
font_add_google("Saira Extra Condensed", "caption_font")
showtext_auto()
text_col <- "grey10"
text_hil <- "grey20"
bts <- 150
bg_col <- "white"
# Caption stuff for the plot
sysfonts::font_add(
family = "Font Awesome 6 Brands",
regular = here::here("docs", "Font Awesome 6 Brands-Regular-400.otf")
)
github <- ""
github_username <- "aditya-dahiya"
xtwitter <- ""
xtwitter_username <- "@adityadahiyaias"
social_caption_1 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{github};</span> <span style='color:{text_hil}'>{github_username} </span>")
social_caption_2 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{xtwitter};</span> <span style='color:{text_hil}'>{xtwitter_username}</span>")
plot_title <- "Haryana: Highways and Health-Care"
plot_subtitle <- "The purple lines depict highways in Haryana. Each dot represents a Health-Care Facility, with shape & size depicting facility's level, and colour depicting proximity to a National Highway (within 200 metres)"
plot_caption <- paste0(
"**Data:** Open Street Maps; Survey of India",
" | **Code:** ",
social_caption_1,
" | **Graphics:** ",
social_caption_2
)
rm(github, github_username, xtwitter,
xtwitter_username, social_caption_1,
social_caption_2)
# Get pre-saved data (Trial Phase - use pre-saved data to
# save laoding times)
# Note: I have split downloading health facilties into larger and
# Smaller ones to save on downloading times, and prevent
# breakage during downloading such large datasets
health_small <- readRDS(file = "haryana_health_small.rds")
health_large <- readRDS(file = "haryana_health_large.rds")
roads_1 <- readRDS(file = "haryana_roads_1.rds")
roads_2 <- readRDS(file = "haryana_roads_2.rds")
# Get only the relevant sf object out of each downlaoded data-set
# For roads, only "LINES" are relevant sf feature.
roads_1 <- roads_1$osm_lines
roads_2 <- roads_2$osm_lines
# Get a Map of the State / City and keep only the highways and
# facilties within that State / City for plotting (instead of entire)
# bounding box
region_map <- sf::read_sf(
here::here("data", "india_map", "India_State_Boundary.shp")
) |>
janitor::clean_names() |>
filter(state_name == "Haryana") |>
st_transform(crs = st_crs(roads_1))
haryana_districts_map <- sf::read_sf(
here::here("data", "haryana_map", "HARYANA_DISTRICT_BDY.shp")
) |>
janitor::clean_names() |>
st_transform(crs = st_crs(roads_1))
# Checking the results
# ggplot() +
# geom_sf(data = region_map) +
# geom_sf(data = roads_1)
#
# ggplot() +
# geom_sf(data = region_map) +
# geom_sf(data = roads_1 |> st_intersection(region_map))
# Select Roads and Facilties that are within the selected region
roads_1 <- roads_1 |> st_intersection(region_map)
roads_2 <- roads_2 |> st_intersection(region_map)
# Health facilities are as points, polygons and multi-polygons.
# Lets convert them all into points, since on a large map
# like a city or state, all will anyways appear as a point
# -------------------------------------------------------------------
# Extracting the Larger Health Facilities in a nicely formatted clean
# sf object, to make plotting easier subsequently--------------------
temp1 <- health_large$osm_points |>
select(name, geometry, "addr:district") |>
# filter(!is.na(name)) |>
mutate(facility_type = "medium")
temp2 <- health_large$osm_polygons |>
st_centroid() |>
select(name, geometry, "addr:district") |>
# filter(!is.na(name)) |>
mutate(facility_type = "large")
temp3 <- health_large$osm_multipolygons |>
st_centroid() |>
select(name, geometry, "addr:district") |>
# filter(!is.na(name)) |>
mutate(facility_type = "large")
health_large <- bind_rows(
temp1,
temp2,
temp3
)
# Clean the workspace
rm(temp1, temp2, temp3)
# Retain only the health facilities that are within our region
health_large <- health_large |>
st_intersection(region_map)
# Now the MAGIC of {sf}: mark the facilities that are near highways
health_large <- health_large |>
mutate(
near_highway = health_large |>
# Checking which points are within a specific distance of highway
# The awesome power & speed of sf::st_is_within_distance()
st_is_within_distance(
y = roads_1,
dist = 200,
sparse = F
) |>
as_tibble() |>
# Keep value TRUE if facility is near ANY highway
mutate(
any_true = rowSums(across(everything(), as.logical)) > 0
) |>
pull(any_true)
) |>
mutate(
near_highway = if_else(
near_highway,
"near_highway",
"far_from_highway"
)
)
# Do the same process for snmaller health facilities ----------------
temp1 <- health_small$osm_points |>
select(name, geometry, "addr:district") |>
# filter(!is.na(name)) |>
mutate(facility_type = "small")
temp2 <- health_small$osm_polygons |>
st_centroid() |>
select(name, geometry, "addr:district") |>
# filter(!is.na(name)) |>
mutate(facility_type = "medium")
health_small <- bind_rows(
temp1,
temp2
)
rm(temp1, temp2)
health_small <- health_small |>
st_intersection(region_map)
health_small <- health_small |>
mutate(
near_highway = health_small |>
# Checking which points are within a specific distance of highway
st_is_within_distance(
y = roads_1,
dist = 200,
sparse = F
) |>
as_tibble() |>
mutate(
any_true = rowSums(across(everything(), as.logical)) > 0
) |>
pull(any_true)
) |>
mutate(
near_highway = if_else(
near_highway,
"near_highway",
"far_from_highway"
)
)
# Combining health_large and health_small to map shape to facility size
health_combined <- bind_rows(
health_large,
health_small
) |>
mutate(
facility_type = fct(
facility_type,
levels = c("small", "medium", "large")
)
) |>
rename(district = addr.district) |>
filter(!is.na(district)) |>
mutate(
facility_type = str_to_title(facility_type),
near_highway = snakecase::to_title_case(near_highway)
)
rm(health_large, health_small)
# The plot
g <- ggplot() +
# Background map of districts
geom_sf(
data = haryana_districts_map,
linewidth = 2,
colour = "grey90",
alpha = 0.1,
fill = "transparent"
) +
# Background overall map of Haryana
geom_sf(
data = region_map,
linewidth = 2,
colour = "grey10",
fill = "transparent"
) +
# Plotting the Highways and other major roads
geom_sf(
data = roads_1,
linewidth = 0.9,
alpha = 1,
colour = "#702F8AFF"
) +
geom_sf(
data = roads_2,
linewidth = 0.5,
alpha = 0.8,
colour = "#9063CDFF"
) +
# Plotting the Health Facilties
geom_sf(
data = health_combined,
mapping = aes(
colour = near_highway,
size = facility_type,
shape = facility_type
),
alpha = 0.5
) +
scale_size_manual(
values = c(14, 8, 4)
) +
scale_shape_manual(
values = c(0, 2, 1)
) +
scale_colour_manual(
values = c("#FFA400FF", "#862633FF")
) +
# Add North-Arrow and Scale
ggspatial::annotation_north_arrow(
style = ggspatial::north_arrow_orienteering(
line_col = "grey20",
fill = c("grey90", "grey20"),
text_size = bts,
text_family = "body_font"
),
location = "tr",
height = unit(5, "cm"),
width = unit(5, "cm")
) +
ggspatial::annotation_scale(
bar_cols = c("grey30", bg_col),
location = "bl",
height = unit(0.75, "cm"),
text_cex = 10,
text_family = "body_font"
) +
labs(
title = plot_title,
subtitle = str_wrap(plot_subtitle, 70),
caption = plot_caption,
shape = NULL,
size = NULL,
colour = NULL
) +
guides(
colour = guide_legend(
override.aes = list(
size = 10
)
)
) +
ggthemes::theme_map(
base_size = bts,
base_family = "body_font"
) +
theme(
# Overall
plot.margin = margin(10,10,10,10, "mm"),
plot.title.position = "plot",
text = element_text(
colour = text_col,
lineheight = 0.3,
margin = margin(0,0,0,0, "mm"),
hjust = 0
),
# Legend
legend.position = "inside",
legend.position.inside = c(0, 0.8),
legend.direction = "vertical",
legend.text = element_text(
margin = margin(5,5,5,5, "mm")
),
legend.margin = margin(0,0,0,0, "mm"),
legend.box = "horizontal",
legend.box.just = "top",
legend.key.spacing = unit(10, "mm"),
# Labels
plot.title = element_text(
size = 3 * bts,
hjust = 0.5,
colour = text_hil,
margin = margin(30,0,10,0, "mm"),
family = "title_font"
),
plot.subtitle = element_text(
size = 0.8 * bts,
hjust = 0,
colour = text_hil,
margin = margin(10,0,-50,0, "mm"),
family = "body_font"
),
plot.caption = element_textbox(
family = "caption_font",
hjust = 0.5,
margin = margin(10,0,10,0, "mm"),
colour = text_hil,
size = 0.5 * bts
)
)
###### Computing Data for Table
plot_data_df <- health_combined |>
st_drop_geometry() |>
as_tibble() |>
filter(!(district %in% c("Chandigarh", "Gautam Buddha Nagar",
"Haryana", "", " ",
"Sahibzada Ajit Singh Nagar"))) |>
mutate(
district = case_when(
district == "Gurgaon" ~ "Gurugram",
district == "Nuh" ~ "Mewat",
.default = district
)
) |>
count(district, facility_type, near_highway) |>
group_by(district, facility_type) |>
mutate(total = sum(n)) |>
ungroup()
# pivot_wider(
# id_cols = c(district, facility_type),
# names_from = near_highway,
# values_from = n,
# values_fill = 0
# ) |>
# mutate(
# total = `Far from Highway` + `Near Highway`,
# perc_on_hwy = `Near Highway` / total,
# district = as.character(district),
# .keep = "unused"
# )
strip_labels <- c(
"Small: Dentists, Pharmacies",
"Medium: Hospitals & Clinics",
"Large Institutions"
) |>
str_wrap(30)
names(strip_labels) <- c("Small", "Medium", "Large")
# Deciding order of district (preference is for descending order)
order_districts <- plot_data_df |>
group_by(district) |>
summarise(n = sum(n)) |>
arrange(n) |>
pull(district)
inset_plot <- plot_data_df |>
mutate(district = fct(district, levels = order_districts)) |>
ggplot(
mapping = aes(
y = district,
x = n
)
) +
geom_col(
mapping = aes(
fill = near_highway
),
alpha = 0.7,
position = position_stack()
) +
geom_text(
mapping = aes(
label = n,
colour = near_highway
),
hjust = 1.4,
family = "caption_font",
size = bts / 20,
position = position_stack()
) +
geom_text(
mapping = aes(
label = total,
x = total
),
hjust = -0.2,
family = "body_font",
size = bts / 12,
colour = text_col,
fontface = "bold"
) +
scale_fill_manual(
values = c("#FFA400FF", "#862633FF")
) +
scale_colour_manual(
values = darken(c("#FFA400FF", "#862633FF"), 0.5)
) +
scale_y_discrete(
expand = expansion(0)
) +
scale_x_continuous(
expand = expansion(c(0, 0.25))
) +
labs(
y = NULL,
x = "Number of Health-Care facilties"
) +
facet_wrap(
~ facility_type,
labeller = labeller(
facility_type = strip_labels
),
scales = "free_x"
) +
coord_cartesian(clip = "off") +
theme_minimal(
base_size = bts / 3,
base_family = "body_font"
) +
theme(
legend.position = "none",
plot.margin = margin(0,0,0,0, "mm"),
plot.background = element_rect(
fill = "transparent",
colour = "transparent"
),
panel.background = element_rect(
fill = "transparent",
colour = "transparent"
),
panel.grid = element_blank(),
panel.grid.major.x = element_line(
colour = alpha(text_col, 0.2),
linetype = 1,
linewidth = 0.3
),
axis.line.x = element_line(
colour = text_hil,
linewidth = 0.3
),
axis.text.x = element_text(
margin = margin(5,0,0,0, "mm")
),
axis.text.y = element_text(
margin = margin(0,5,0,0, "mm")
),
axis.ticks = element_blank(),
axis.ticks.length = unit(0, "mm")
)
# Temporary Code to check the output of inset plot
# ggsave(
# plot = inset_plot,
# filename = here("projects", "images",
# "highways_health_temp1.png"),
# height = 8,
# width = 8,
# units = "in",
# bg = bg_col
# )
# Compose the final plot
g2 <- g +
inset_element(
p = inset_plot,
left = 0, right = 0.4,
bottom = 0.1 , top = 0.3,
align_to = "full",
on_top = TRUE,
clip = FALSE
)
ggsave(
plot = g2,
filename = here("projects", "images",
"highways_health_haryana.png"),
height = 36,
width = 24,
units = "in",
bg = bg_col
)
Part 7: Final Product for India (in-progress)
Code
# Basics of Final Visualization: Fonts
font_add_google("Rouge Script", "title_font")
font_add_google("El Messiri", "body_font")
font_add_google("Saira Extra Condensed", "caption_font")
showtext_auto()
text_col <- "grey10"
text_hil <- "grey20"
bts <- 150
bg_col <- "white"
# Caption stuff for the plot
sysfonts::font_add(
family = "Font Awesome 6 Brands",
regular = here::here("docs", "Font Awesome 6 Brands-Regular-400.otf")
)
github <- ""
github_username <- "aditya-dahiya"
xtwitter <- ""
xtwitter_username <- "@adityadahiyaias"
social_caption_1 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{github};</span> <span style='color:{text_hil}'>{github_username} </span>")
social_caption_2 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{xtwitter};</span> <span style='color:{text_hil}'>{xtwitter_username}</span>")
plot_title <- "India: Health-Care Institutions"
plot_caption <- paste0(
"**Data:** Open Street Maps; Survey of India",
" | **Code:** ",
social_caption_1,
" | **Graphics:** ",
social_caption_2
)
rm(github, github_username, xtwitter,
xtwitter_username, social_caption_1,
social_caption_2)
# Get pre-saved data (Trial Phase - use pre-saved data to
# save laoding times)
# Note: I have split downloading health facilties into larger and
# Smaller ones to save on downloading times, and prevent
# breakage during downloading such large datasets
health_small <- readRDS(file = "india_health_small.rds")
health_large <- readRDS(file = "india_health_large.rds")
# roads_1 <- readRDS(file = "india_roads_1.rds")
# roads_2 <- readRDS(file = "haryana_roads_2.rds")
#
# # Get only the relevant sf object out of each downlaoded data-set
# # For roads, only "LINES" are relevant sf feature.
# roads_1 <- roads_1$osm_lines
# roads_2 <- roads_2$osm_lines
# Get a Map of the State / City and keep only the highways and
# facilties within that State / City for plotting (instead of entire)
# bounding box
region_map <- sf::read_sf(
here::here("data", "india_map", "India_Country_Boundary.shp")
) |>
janitor::clean_names() |>
st_transform(crs = st_crs(health_large$osm_points))
# # Select Roads and Facilties that are within the selected region
# roads_1 <- roads_1 |> st_intersection(region_map)
# roads_2 <- roads_2 |> st_intersection(region_map)
# Health facilities are as points, polygons and multi-polygons.
# Lets convert them all into points, since on a large map
# like a city or state, all will anyways appear as a point
# ----------------------------------------------------------
# Extracting the Larger Health Facilities in a nicely formatted clean
# sf object, to make plotting easier subsequently--------------------
temp1 <- health_large$osm_points |>
select(name, geometry, "addr:state") |>
filter(!is.na(name)) |>
mutate(facility_type = "medium")
temp2 <- health_large$osm_polygons |>
st_centroid() |>
select(name, geometry, "addr:state") |>
filter(!is.na(name)) |>
mutate(facility_type = "large")
sf_use_s2(FALSE)
temp3 <- health_large$osm_multipolygons |>
st_centroid(of_largest_polygon = TRUE) |>
select(name, geometry, "addr:state") |>
filter(!is.na(name)) |>
mutate(facility_type = "large")
health_large <- bind_rows(
temp1,
temp2,
temp3
)
# Clean the workspace
rm(temp1, temp2, temp3)
sf_use_s2(TRUE)
# Retain only the health facilities that are within our region
health_large <- health_large |>
st_intersection(region_map)
# Now the MAGIC of {sf}: mark the facilities that are near highways
# health_large <- health_large |>
# mutate(
# near_highway = health_large |>
#
# # Checking which points are within a specific distance of highway
# # The awesome power & speed of sf::st_is_within_distance()
# st_is_within_distance(
# y = roads_1,
# dist = 200,
# sparse = F
# ) |>
# as_tibble() |>
# # Keep value TRUE if facility is near ANY highway
# mutate(
# any_true = rowSums(across(everything(), as.logical)) > 0
# ) |>
# pull(any_true)
# ) |>
# mutate(
# near_highway = if_else(
# near_highway,
# "near_highway",
# "far_from_highway"
# )
# )
# Do the same process for snmaller health facilities ----------------
temp1 <- health_small$osm_points |>
select(name, geometry, "addr:state") |>
filter(!is.na(name)) |>
mutate(facility_type = "small")
temp2 <- health_small$osm_polygons |>
st_centroid() |>
select(name, geometry, "addr:state") |>
filter(!is.na(name)) |>
mutate(facility_type = "medium")
health_small <- bind_rows(
temp1,
temp2
)
rm(temp1, temp2)
health_small <- health_small |>
st_intersection(region_map)
# health_small <- health_small |>
# mutate(
# near_highway = health_small |>
#
# # Checking which points are within a specific distance of highway
# st_is_within_distance(
# y = roads_1,
# dist = 200,
# sparse = F
# ) |>
# as_tibble() |>
# mutate(
# any_true = rowSums(across(everything(), as.logical)) > 0
# ) |>
# pull(any_true)
# ) |>
# mutate(
# near_highway = if_else(
# near_highway,
# "near_highway",
# "far_from_highway"
# )
# )
# Combining health_large and health_small to map shape to facility size
health_combined <- bind_rows(
health_large,
health_small
) |>
mutate(
facility_type = fct(
facility_type,
levels = c("small", "medium", "large")
)
) |>
rename(district = addr.district) |>
filter(!is.na(district)) |>
mutate(
facility_type = str_to_title(facility_type),
near_highway = snakecase::to_title_case(near_highway)
)
rm(health_large, health_small)
# The plot
g <- ggplot() +
# Background map of districts
geom_sf(
data = haryana_districts_map,
linewidth = 2,
colour = "grey90",
alpha = 0.1,
fill = "transparent"
) +
# Background overall map of Haryana
geom_sf(
data = region_map,
linewidth = 2,
colour = "grey10",
fill = "transparent"
) +
# Plotting the Highways and other major roads
geom_sf(
data = roads_1,
linewidth = 0.9,
alpha = 0.8,
colour = "#702F8AFF"
) +
geom_sf(
data = roads_2,
linewidth = 0.5,
alpha = 0.5,
colour = "#9063CDFF"
) +
# Plotting the Health Facilties
geom_sf(
data = health_combined,
mapping = aes(
colour = near_highway,
size = facility_type,
shape = facility_type
),
alpha = 0.5
) +
scale_size_manual(
values = c(10, 7, 3)
) +
scale_shape_manual(
values = c(0, 2, 20)
) +
scale_colour_manual(
values = c("#FFA400FF", "#862633FF")
) +
# Add North-Arrow and Scale
ggspatial::annotation_north_arrow(
style = ggspatial::north_arrow_orienteering(
line_col = "grey20",
fill = c("grey90", "grey20"),
text_size = bts,
text_family = "body_font"
),
location = "tr",
height = unit(5, "cm"),
width = unit(5, "cm")
) +
ggspatial::annotation_scale(
bar_cols = c("grey30", bg_col),
location = "bl",
height = unit(0.75, "cm"),
text_cex = 10,
text_family = "body_font"
) +
labs(
title = plot_title,
caption = plot_caption,
shape = NULL,
size = NULL,
colour = NULL
) +
guides(
colour = guide_legend(
override.aes = list(
size = 10
)
)
) +
ggthemes::theme_map(
base_size = bts,
base_family = "body_font"
) +
theme(
# Overall
plot.margin = margin(10,10,10,10, "mm"),
plot.title.position = "plot",
text = element_text(
colour = text_col,
lineheight = 0.3,
margin = margin(0,0,0,0, "mm"),
hjust = 0
),
# Legend
legend.position = "inside",
legend.position.inside = c(0, 0.8),
legend.direction = "vertical",
legend.text = element_text(
margin = margin(5,5,5,5, "mm")
),
legend.margin = margin(0,0,0,0, "mm"),
legend.box = "horizontal",
legend.box.just = "top",
legend.key.spacing = unit(10, "mm"),
# Labels
plot.title = element_text(
size = 3 * bts,
hjust = 0.5,
colour = text_hil,
margin = margin(30,0,30,0, "mm"),
family = "title_font"
),
plot.caption = element_textbox(
family = "caption_font",
hjust = 0.5,
margin = margin(10,0,10,0, "mm"),
colour = text_hil,
size = 0.75 * bts
)
)
###### Computing Data for Table
plot_data_df <- health_combined |>
st_drop_geometry() |>
as_tibble() |>
count(district, facility_type, near_highway) |>
pivot_wider(
id_cols = c(district, facility_type),
names_from = near_highway,
values_from = n,
values_fill = 0
) |>
mutate(
total = `Far from Highway` + `Near Highway`,
perc_on_hwy = `Near Highway` / total,
district = as.character(district),
.keep = "unused"
) |>
filter(!(district %in% c("Chandigarh", "Gautam Buddha Nagar",
"Haryana", "", " ",
"Sahibzada Ajit Singh Nagar"))) |>
mutate(
district = case_when(
district == "Gurgaon" ~ "Gurugram",
district == "Nuh" ~ "Mewat",
.default = district
)
)
strip_labels <- c(
"Small facilties: Dentists, Pharmacies",
"Medium: Clinics, Doctors's Practices",
"Large: Hospitals, Nursing Homes, Institutions"
) |>
str_wrap(30)
names(strip_labels) <- c("Small", "Medium", "Large")
inset_plot <- plot_data_df |>
ggplot(
mapping = aes(
y = district,
x = total
)
) +
geom_col(
alpha = 0.5
) +
geom_text(
mapping = aes(
label = paste0("(", round(perc_on_hwy * 100, 2),
"% )"),
x = 0
),
nudge_x = 0.01,
hjust = 0
) +
geom_text(
mapping = aes(
label = paste0(total)
),
nudge_x = 0.005,
hjust = 0
) +
labs(
y = NULL,
x = "Health facilities located on the Highway"
) +
facet_wrap(
~ facility_type,
labeller = labeller(
facility_type = strip_labels
),
scales = "free_x"
) +
scale_x_continuous(
expand = expansion(c(0, 0.25))
) +
theme_minimal(
base_size = 10
) +
theme()
inset_plot
ggsave(
plot = g,
filename = here("projects", "images",
"highways_health_haryana.png"),
height = 36,
width = 24,
units = "in",
bg = bg_col
)Part 8 : Thumbnail for webpage, and list of packages used
Code
# Saving a thumbnail
library(magick)
# Saving a thumbnail for the webpage
image_read(here::here("projects", "images",
"highways_health_haryana.png")) |>
image_resize(geometry = "400") |>
image_write(
here::here(
"projects",
"images",
"highways_health_haryana_thumbnail.png"
)
)Code
# Data Import and Wrangling Tools
library(tidyverse) # All things tidy
library(here) # File locations and paths
# Final plot tools
library(scales) # Nice Scales for ggplot2
library(fontawesome) # Icons display in ggplot2
library(ggtext) # Markdown text support for ggplot2
library(showtext) # Display fonts in ggplot2
library(colorspace) # Lighten and Darken colours
library(patchwork) # Combining plots
library(gt) # Beautiful Tables in R
# Mapping tools
library(sf) # All spatial objects in R
library(osmdata) # Getting Open Street Maps data
sessioninfo::session_info()$packages |>
as_tibble() |>
select(package,
version = loadedversion,
date, source) |>
arrange(package) |>
janitor::clean_names(
case = "title"
) |>
gt::gt() |>
gt::opt_interactive(
use_search = TRUE
)