Disaster Estimate

Last edit: 29th of September 2025

Demonstration report: every figure on this page is simulated This is a public demonstration of a disaster damage and response estimation pipeline originally built for the Government of Vanuatu. All values shown here, including baselines, damage estimates, resource requirements and financial figures, are synthetic data, generated from scratch by simulate_data.R in this repository. They do not describe Vanuatu and must not be cited or reused as if they did.

What is real is everything around the numbers: the geography, the indicator and attribute structure, the sector coverage, and every line of calculation, aggregation and presentation logic. The point of the demonstration is the pipeline, not the results.

1 Intro

This report runs based on a config file which provides the cyclone intensity applied to each council in Vanuatu. It has 5 columns:

  • National
  • Province
  • Area Council
  • Hazard
  • Intensity (number from 2 to 4)

Let’s load this file to use it later in our analysis:

Show code
config <- read.csv(here::here("data", "hazard_scenario.csv"), check.names = FALSE)

Another file that will be useful: a 2 columns dataframe that provides the list of councils with their Province

Show code
council_province_lookup <- read.csv(here::here("data", "council_province_lookup.csv"))

And load the required libs

Show code
# Load required packages
library(dplyr)
library(tidyr)
library(reactable)
library(htmltools)
library(readxl)
library(here)
library(sf)
library(leaflet)

Order of councils and provinces in the final tables:

Show code
# Define the custom order for regions
region_order <- c(
  "National", # Will be displayed as Vanuatu
  "Torba", "Torres", "Ureparapara", "Motalava", "West Vanualava", "East Vanualava", 
  "Mota", "East Gaua", "West Gaua", "Merelava",
  "Sanma", "Luganville", "North West Santo", "Big Bay Coast", "Big Bay Inland", 
  "West Santo", "South Santo 1", "South Santo 2", "East Santo", "South East Santo", 
  "Canal Fanafo", "West Malo", "East Malo",
  "Penama", "West Ambae", "North Ambae", "East Ambae", "South Ambae", "North Maewo", 
  "South Maewo", "North Pentecost", "Central Pentecost 1", "Central Pentecost 2", 
  "South Pentecost",
  "Malampa", "North West Malekula", "North East Malekula", "Central Malekula", 
  "South West Malekula", "South East Malekula", "South Malekula", "North Ambrym", 
  "West Ambrym", "South East Ambrym", "Paama",
  "Shefa", "Port Vila", "Vermali", "Vermaul", "Varisu", "South Epi", "North Tongoa", 
  "Tongariki", "Makimae", "Nguna", "Emau", "Malorua", "North Efate", "Mele", 
  "Tanvasoko", "Ifira", "Pango", "Erakor", "Eratap", "Eton",
  "Tafea", "North Erromango", "South Erromango", "Aniwa", "North Tanna", "West Tanna", 
  "Middle Bush Tanna", "South West Tanna", "Whitesands", "South Tanna", "Futuna", "Aneityum"
)

# Provincial levels (to be bolded)
provinces <- c("National", "Torba", "Sanma", "Penama", "Malampa", "Shefa", "Tafea")

A function to aggregate a table with one row per council. It adds value per province and for the whole country

Show code
# council_data must be a data frame with a column called "Region"
compute_council_aggregates <- function(council_data) {
  
  province_data <- council_data %>%
    left_join(council_province_lookup, by = c("Region" = "Council")) %>%
    group_by(Province) %>%
    summarise(across(where(is.numeric), sum, na.rm = TRUE), .groups = "drop") %>%
    rename(Region = Province)  
  
  national_data <- province_data %>%
    summarise(across(where(is.numeric), sum, na.rm = TRUE)) %>%
    mutate(Region = "National")
  
  result <- bind_rows(
    national_data,
    province_data,
    council_data
  )
  
  # Add default order and formatting columns
  result <- result %>%
    mutate(default_order = match(Region, region_order)) %>%
    arrange(default_order) %>%
    select(-default_order)

  return(result)
}

format_table <- function(data){
  result <- data %>%
    select(Region, everything()) %>% 
    mutate(
      Region = ifelse(
        Region %in% provinces, 
        paste0("<b>", Region, "</b>"), 
        Region
      )
    )
  return(result)
}

Load and clean ‘full data’

Show code
full_data <- read.csv(here::here("data", "baseline_indicators.csv"), check.names = FALSE)

# Standardize Area Council names
full_data <- full_data %>%
  mutate(`Area Council` = case_when(
    `Area Council` == "Central malekula" ~ "Central Malekula",
    `Area Council` == "Central pentecost 2" ~ "Central Pentecost 2",
    `Area Council` == "East santo" ~ "East Santo",
    `Area Council` == "luganville" ~ "Luganville",
    `Area Council` == "North West malekula" ~ "North West Malekula",
    `Area Council` == "pentecost" ~ "Pentecost",
    `Area Council` == "West gaua" ~ "West Gaua",
    TRUE ~ `Area Council`
  ))

# Filter to only valid Area Councils to avoid double-counting
valid_councils <- region_order[!region_order %in% c("National", "Torba", "Sanma", "Penama", "Malampa", "Shefa", "Tafea")]

full_data <- full_data %>%
  filter(`Area Council` %in% valid_councils)

2 Data Quality Checks

Show code
# === 1. INPUT DATA VALIDATION ===

# Check for unrecognized Area Council names
data_councils <- unique(full_data$`Area Council`)
unrecognized <- setdiff(data_councils, region_order)
if (length(unrecognized) > 0) {
  warning("⚠️ Unrecognized Area Councils found: ", paste(unrecognized, collapse = ", "))
} else {
  message("✓ All Area Councils recognized")
}
✓ All Area Councils recognized
Show code
# Check for case inconsistencies (potential duplicates)
lowercase_counts <- table(tolower(data_councils))
potential_dupes <- names(lowercase_counts[lowercase_counts > 1])
if (length(potential_dupes) > 0) {
  warning("⚠️ Possible duplicate councils with different capitalization: ", paste(potential_dupes, collapse = ", "))
} else {
  message("✓ No capitalization inconsistencies detected")
}
✓ No capitalization inconsistencies detected
Show code
# Check for missing councils (councils in config but not in baseline)
config_councils <- unique(config$`Area Council`)
missing_baseline <- setdiff(config_councils, data_councils)
if (length(missing_baseline) > 0) {
  warning("⚠️ Councils in hazard config but missing from baseline: ", paste(missing_baseline, collapse = ", "))
} else {
  message("✓ All configured councils have baseline data")
}
✓ All configured councils have baseline data
Show code
# Check for councils in baseline but not in config (won't receive damage estimates)
missing_config <- setdiff(data_councils, config_councils)
if (length(missing_config) > 0) {
  message("ℹ️ Councils with baseline data but no hazard config (will show 0 damage): ", paste(missing_config, collapse = ", "))
}
ℹ️ Councils with baseline data but no hazard config (will show 0 damage): Torres, Ureparapara, Motalava, East Vanualava, West Vanualava, Mota, East Gaua, West Gaua, Merelava, Luganville, North West Santo, Big Bay Coast, Big Bay Inland, West Santo, South Santo 1, South Santo 2, East Santo, South East Santo, Canal Fanafo, East Malo, West Malo, West Ambae, North Ambae, East Ambae, South Ambae, North Maewo, South Maewo, North Pentecost, Central Pentecost 1, Central Pentecost 2, South Pentecost
Show code
# === 2. DATA COMPLETENESS CHECK ===

# Check which baselines are available per council
baseline_coverage <- full_data %>%
  group_by(`Area Council`, Baseline) %>%
  summarise(n_records = n(), .groups = "drop") %>%
  pivot_wider(names_from = Baseline, values_from = n_records, values_fill = 0)

# Expected baselines that may legitimately be missing for some councils
# (rural/remote areas without businesses, telecom towers, or health facilities)
optional_baselines <- c("Business", "Emergency Telecommunications", "Health")
required_baselines <- c("Education", "Energy", "Food Security", "Gender & Protection", 
                        "Logistics", "Shelter", "WASH")

# Check for missing REQUIRED baselines (these are real issues)
missing_required <- baseline_coverage %>%
  rowwise() %>%
  filter(any(c_across(any_of(required_baselines)) == 0)) %>%
  ungroup()

# Check for missing OPTIONAL baselines (informational only)
missing_optional <- baseline_coverage %>%
  rowwise() %>%
  filter(any(c_across(any_of(optional_baselines)) == 0)) %>%
  ungroup()

if (nrow(missing_required) > 0) {
  warning("⚠️ Some councils are missing required baseline data. See 'QC_baseline_coverage.csv'")
  write.csv(baseline_coverage, here::here("output", "QC_baseline_coverage.csv"), row.names = FALSE)
  baseline_completeness_pass <- FALSE
} else {
  message("✓ All councils have required baseline data")
  baseline_completeness_pass <- TRUE
}
✓ All councils have required baseline data
Show code
if (nrow(missing_optional) > 0) {
  n_missing_business <- sum(baseline_coverage$Business == 0, na.rm = TRUE)
  n_missing_telecom <- sum(baseline_coverage$`Emergency Telecommunications` == 0, na.rm = TRUE)
  n_missing_health <- sum(baseline_coverage$Health == 0, na.rm = TRUE)
  message("ℹ️ Optional baseline gaps (expected for rural councils): ",
          n_missing_business, " without Business, ",
          n_missing_telecom, " without Telecom, ",
          n_missing_health, " without Health")
  write.csv(baseline_coverage, here::here("output", "QC_baseline_coverage.csv"), row.names = FALSE)
}
ℹ️ Optional baseline gaps (expected for rural councils): 14 without Business, 8 without Telecom, 3 without Health
Show code
# === 3. RANDOM SAMPLE FOR MANUAL REVIEW ===

set.seed(as.numeric(Sys.Date())) # Reproducible daily sample
sample_councils <- sample(data_councils, min(5, length(data_councils)))

message("📋 Manual review sample for ", Sys.Date(), ": ", paste(sample_councils, collapse = ", "))
📋 Manual review sample for 2026-08-26: South Santo 1, South East Malekula, South East Ambrym, East Ambae, South East Santo
Show code
# Store for use after calculations complete
qc_sample_councils <- sample_councils

3 Education

3.1 Baseline: Number of Schools, Students, and Teachers

Show code
# === DATA WRANGLING ===
# Filter for Education in Baseline column and Area Council level data
education_data <- full_data %>%
  filter(Baseline == "Education") %>%  # Changed to filter Baseline column
  filter(Year == max(Year, na.rm = TRUE)) %>%  # Filter for the latest year
  filter(!is.na(`Area Council`)) %>%
  mutate(Region = `Area Council`)

# Reshape the data to get Schools, Students, Teachers as separate columns
education_wide <- education_data %>%
  mutate(
    education_level = tolower(Attribute),
    measure_type = case_when(
      grepl("Number Schools", Indicator) ~ "schools",
      grepl("Students", Indicator) ~ "students",
      grepl("Teachers", Indicator) ~ "teachers",
      TRUE ~ "other"
    )
  ) %>%
  filter(measure_type %in% c("schools", "students", "teachers")) %>%
  select(Region, education_level, measure_type, Value) %>%
  pivot_wider(
    names_from = c(education_level, measure_type),
    values_from = Value,
    names_sep = "_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Add total columns
education_wide <- education_wide %>%
  mutate(
    total_schools = ecce_schools + primary_schools + secondary_schools,
    total_students = ecce_students + primary_students + secondary_students,
    total_teachers = ecce_teachers + primary_teachers + secondary_teachers
  ) %>%
  select(Region, total_schools, total_students, total_teachers, everything())

# Compute aggregates (province and national levels)
education_aggregated <- compute_council_aggregates(education_wide)

# === EXPORT TO CSV ===
write.csv(
  education_aggregated %>% select(Region, everything()),
  here::here("output", "Education_01_baseline_education.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
# Format for display
formatted <- format_table(education_aggregated)

# Create the reactable with proper case labels
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sortable = TRUE,
      defaultSortOrder = "asc",
      html = TRUE,
      sticky = "left"
    ),
    
    # Add to columns list
    total_schools = colDef(name = "Schools", format = colFormat(digits = 0)),
    total_students = colDef(name = "Students", format = colFormat(digits = 0)),
    total_teachers = colDef(name = "Teachers", format = colFormat(digits = 0)),

    # ECCE columns
    ecce_schools = colDef(name = "Schools", format = colFormat(digits = 0)),
    ecce_students = colDef(name = "Students", format = colFormat(digits = 0)),
    ecce_teachers = colDef(name = "Teachers", format = colFormat(digits = 0)),
    
    # Primary columns  
    primary_schools = colDef(name = "Schools", format = colFormat(digits = 0)),
    primary_students = colDef(name = "Students", format = colFormat(digits = 0)),
    primary_teachers = colDef(name = "Teachers", format = colFormat(digits = 0)),
    
    # Secondary columns
    secondary_schools = colDef(name = "Schools", format = colFormat(digits = 0)),
    secondary_students = colDef(name = "Students", format = colFormat(digits = 0)),
    secondary_teachers = colDef(name = "Teachers", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Totals", columns = c("total_schools", "total_students", "total_teachers")),
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "ECCE", columns = c("ecce_schools", "ecce_students", "ecce_teachers")),
    colGroup(name = "Primary", columns = c("primary_schools", "primary_students", "primary_teachers")),
    colGroup(name = "Secondary", columns = c("secondary_schools", "secondary_students", "secondary_teachers"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

3.2 Estimating damage: Number of damaged schools and students affected

Show code
# === DATA WRANGLING ===
# Add cyclone strength to baseline values
education_with_config <- education_wide %>%
  left_join(config, by = c("Region" = "Area Council"))

# Load the damage multipliers: proportion of each asset lost per cyclone category
# Read CSV while preserving column names with spaces
baseline_factors <- read.csv(
  here::here("data", "damage_multipliers.csv"),
  check.names = FALSE
)

# Helper function for damage multipliers (now considers council-specific values)
get_damage_multiplier <- function(cyclone_category, education_level, measure_type, area_council) {
  intensity_col <- paste0("Intensity ", cyclone_category)
  
  multiplier <- baseline_factors %>%
    filter(
      Cluster == "Education",
      Attribute == education_level,
      grepl(measure_type, Indicator, ignore.case = TRUE),
      `Area Council` == area_council  # Add council-specific filter
    )
  
  # Check if we got any results
  if(nrow(multiplier) == 0) {
    return(0)
  }
  
  # Extract the value from the intensity column
  result <- multiplier[[intensity_col]][1]
  
  return(ifelse(is.na(result) || length(result) == 0, 0, result))
}

# Calculate damage estimates
damage_estimates <- education_with_config %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # ECCE - now passing Region to get council-specific multipliers
    ecce_schools_damaged = round(ecce_schools * get_damage_multiplier(Intensity, "ecce", "schools", Region)),
    ecce_students_affected = round(ecce_students * get_damage_multiplier(Intensity, "ecce", "students", Region)),
    
    # Primary
    primary_schools_damaged = round(primary_schools * get_damage_multiplier(Intensity, "primary", "schools", Region)),
    primary_students_affected = round(primary_students * get_damage_multiplier(Intensity, "primary", "students", Region)),
    
    # Secondary
    secondary_schools_damaged = round(secondary_schools * get_damage_multiplier(Intensity, "secondary", "schools", Region)),
    secondary_students_affected = round(secondary_students * get_damage_multiplier(Intensity, "secondary", "students", Region))
  ) %>%
  ungroup() %>%
  select(Region, contains("_damaged"), contains("_affected"))

# Add total columns
damage_estimates <- damage_estimates %>%
  mutate(
    total_schools_damaged = ecce_schools_damaged + primary_schools_damaged + secondary_schools_damaged,
    total_students_affected = ecce_students_affected + primary_students_affected + secondary_students_affected
  ) %>%
  select(Region, total_schools_damaged, total_students_affected, everything())

# Aggregate to province and national levels
damage_estimates_full <- compute_council_aggregates(damage_estimates)

# === EXPORT TO CSV ===
write.csv(
  damage_estimates_full %>% select(Region, everything()),
  here::here("output", "Education_02_damage_estimates.csv"),
  row.names = FALSE
)
# === PRESENTATION ===
# Format for display
formatted <- format_table(damage_estimates_full)

# Create the damage estimation reactable
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    total_schools_damaged = colDef(name = "Schools", format = colFormat(digits = 0)),
    total_students_affected = colDef(name = "Students", format = colFormat(digits = 0)),

    # ECCE columns
    ecce_schools_damaged = colDef(name = "Schools Damaged", format = colFormat(digits = 0)),
    ecce_students_affected = colDef(name = "Students Affected", format = colFormat(digits = 0)),
    
    # Primary columns  
    primary_schools_damaged = colDef(name = "Schools Damaged", format = colFormat(digits = 0)),
    primary_students_affected = colDef(name = "Students Affected", format = colFormat(digits = 0)),
    
    # Secondary columns
    secondary_schools_damaged = colDef(name = "Schools Damaged", format = colFormat(digits = 0)),
    secondary_students_affected = colDef(name = "Students Affected", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Totals", columns = c("total_schools_damaged", "total_students_affected")),
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "ECCE", columns = c("ecce_schools_damaged", "ecce_students_affected")),
    colGroup(name = "Primary", columns = c("primary_schools_damaged", "primary_students_affected")),
    colGroup(name = "Secondary", columns = c("secondary_schools_damaged", "secondary_students_affected"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

3.3 Resources needed to be sent to those affected

Show code
# === DATA WRANGLING ===
# Load the response configuration: relief items issued per affected unit

resource_config <- read.csv(here::here("data", "response_resources.csv"), check.names = FALSE)

# Helper function to get resource multipliers from config
get_resource_multiplier <- function(resource_type) {
  multiplier <- resource_config %>%
    filter(Cluster == "Education", Indicator == resource_type) %>%
    pull(Value)
  
  return(ifelse(length(multiplier) > 0, multiplier, 0))
}

# Configuration parameters
days_of_support <- 14  # Number of days to provide resources

# Add cyclone categories from config file to education baseline data
education_with_config_resources <- education_wide %>%
  left_join(config, by = c("Region" = "Area Council"))

# Calculate resources needed using council-specific cyclone categories
resources_needed <- education_with_config_resources %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Get damage multiplier for this council's specific cyclone category
    damage_multiplier = get_damage_multiplier(Intensity, "ecce", "schools", Region),
    
    # ECCE Resources
    ecce_tents = round(ecce_schools * damage_multiplier * get_resource_multiplier("Tent"), 0),
    ecce_solar_lamps = round(ecce_schools * damage_multiplier * get_resource_multiplier("Solar Lamp"), 0),
    ecce_water = ceiling(ecce_students * damage_multiplier * get_resource_multiplier("Water") * days_of_support),
    ecce_tin_fish = round(ecce_students * damage_multiplier * get_resource_multiplier("Tin Fish") * days_of_support, 0),  # Changed to "Tin Fish"
    
    # Primary Resources
    primary_tents = round(primary_schools * damage_multiplier * get_resource_multiplier("Tent"), 0),
    primary_solar_lamps = round(primary_schools * damage_multiplier * get_resource_multiplier("Solar Lamp"), 0),
    primary_water = ceiling(primary_students * damage_multiplier * get_resource_multiplier("Water") * days_of_support),
    primary_tin_fish = round(primary_students * damage_multiplier * get_resource_multiplier("Tin Fish") * days_of_support, 0),  # Changed to "Tin Fish"
    
    # Secondary Resources
    secondary_tents = round(secondary_schools * damage_multiplier * get_resource_multiplier("Tent"), 0),
    secondary_solar_lamps = round(secondary_schools * damage_multiplier * get_resource_multiplier("Solar Lamp"), 0),
    secondary_water = ceiling(secondary_students * damage_multiplier * get_resource_multiplier("Water") * days_of_support),
    secondary_tin_fish = round(secondary_students * damage_multiplier * get_resource_multiplier("Tin Fish") * days_of_support, 0),  # Changed to "Tin Fish"
    
    # Total Resources (sum across all education levels)
    total_tents = ecce_tents + primary_tents + secondary_tents,
    total_solar_lamps = ecce_solar_lamps + primary_solar_lamps + secondary_solar_lamps,
    total_water = ecce_water + primary_water + secondary_water,
    total_tin_fish = ecce_tin_fish + primary_tin_fish + secondary_tin_fish
  ) %>%
  ungroup() %>%
  select(Region, contains("tents"), contains("solar_lamps"), contains("water"), contains("tin_fish"))

# Aggregate to province and national levels
resources_needed_full <- compute_council_aggregates(resources_needed)

# === EXPORT TO CSV ===
write.csv(
  resources_needed_full %>% select(Region, everything()),
  here::here("output", "Education_03_resources_needed.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
# Format for display
formatted <- format_table(resources_needed_full)

# Create the resources needed reactable
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    # ECCE columns
    ecce_tents = colDef(name = "Tents", format = colFormat(digits = 0)),
    ecce_solar_lamps = colDef(name = "Solar Lamps", format = colFormat(digits = 0)),
    ecce_water = colDef(name = "Water (1L bottles)", format = colFormat(digits = 0)),
    ecce_tin_fish = colDef(name = "Tinned Fish", format = colFormat(digits = 0)),
    
    # Primary columns  
    primary_tents = colDef(name = "Tents", format = colFormat(digits = 0)),
    primary_solar_lamps = colDef(name = "Solar Lamps", format = colFormat(digits = 0)),
    primary_water = colDef(name = "Water (1L bottles)", format = colFormat(digits = 0)),
    primary_tin_fish = colDef(name = "Tinned Fish", format = colFormat(digits = 0)),
    
    # Secondary columns
    secondary_tents = colDef(name = "Tents", format = colFormat(digits = 0)),
    secondary_solar_lamps = colDef(name = "Solar Lamps", format = colFormat(digits = 0)),
    secondary_water = colDef(name = "Water (1L bottles)", format = colFormat(digits = 0)),
    secondary_tin_fish = colDef(name = "Tinned Fish", format = colFormat(digits = 0)),
    
    # Total columns
    total_tents = colDef(name = "Tents", format = colFormat(digits = 0)),
    total_solar_lamps = colDef(name = "Solar Lamps", format = colFormat(digits = 0)),
    total_water = colDef(name = "Water (1L bottles)", format = colFormat(digits = 0)),
    total_tin_fish = colDef(name = "Tinned Fish", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "ECCE", columns = c("ecce_tents", "ecce_solar_lamps", "ecce_water", "ecce_tin_fish")),
    colGroup(name = "Primary", columns = c("primary_tents", "primary_solar_lamps", "primary_water", "primary_tin_fish")),
    colGroup(name = "Secondary", columns = c("secondary_tents", "secondary_solar_lamps", "secondary_water", "secondary_tin_fish")),
    colGroup(name = "Totals", columns = c("total_tents", "total_solar_lamps", "total_water", "total_tin_fish"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

3.4 Estimate financial damage from cyclone

Show code
# === DATA WRANGLING ===
# Load the unit replacement costs, in vatu
# Read CSV while preserving column names with spaces
financial_config <- read.csv(
  here::here("data", "unit_costs.csv"),
  check.names = FALSE
)

# Remove empty column if exists
if("" %in% names(financial_config)) {
  financial_config <- financial_config %>% select(-1)
}

# Clean column names to remove any extra spaces
names(financial_config) <- trimws(names(financial_config))

# Helper function to get unit costs from config (now council-specific)
get_unit_cost <- function(school_type, area_council) {
  unit_cost <- financial_config %>%
    filter(
      trimws(Cluster) == "Education", 
      trimws(tolower(Attribute)) == tolower(school_type),
      grepl("Number Schools", trimws(Indicator), ignore.case = TRUE),
      `Area Council` == area_council  # Add council-specific filter
    ) %>%
    pull(Value)
  
  return(ifelse(length(unit_cost) > 0, unit_cost, 0))
}

# Add cyclone categories from config file to education baseline data
education_with_config_financial <- education_wide %>%
  left_join(config, by = c("Region" = "Area Council"))

# Calculate financial damage using council-specific cyclone categories
financial_damage <- education_with_config_financial %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Get council-specific damage multipliers for each education level
    ecce_damage_multiplier = get_damage_multiplier(Intensity, "ecce", "schools", Region),
    primary_damage_multiplier = get_damage_multiplier(Intensity, "primary", "schools", Region),
    secondary_damage_multiplier = get_damage_multiplier(Intensity, "secondary", "schools", Region),
    
    # Calculate financial damage per education level (in VT) using council-specific values
    ecce_financial_damage = ecce_schools * get_unit_cost("ecce", Region) * ecce_damage_multiplier,
    primary_financial_damage = primary_schools * get_unit_cost("primary", Region) * primary_damage_multiplier,
    secondary_financial_damage = secondary_schools * get_unit_cost("secondary", Region) * secondary_damage_multiplier,
    
    # Calculate total financial damage
    total_financial_damage = ecce_financial_damage + primary_financial_damage + secondary_financial_damage
  ) %>%
  ungroup() %>%
  select(Region, contains("financial_damage"))

# Aggregate to province and national levels
financial_damage_full <- compute_council_aggregates(financial_damage)

# === EXPORT TO CSV ===
write.csv(
  financial_damage_full %>% select(Region, everything()),
  here::here("output", "Education_04_financial_damage.csv"),
  row.names = FALSE
)


# === PRESENTATION ===
# Format for display
formatted <- format_table(financial_damage_full)

# Create the financial damage reactable
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    # Financial damage columns
    ecce_financial_damage = colDef(
      name = "ECCE", 
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    primary_financial_damage = colDef(
      name = "Primary", 
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    secondary_financial_damage = colDef(
      name = "Secondary", 
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    total_financial_damage = colDef(
      name = "Total", 
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Financial Damage (VT)", columns = c("ecce_financial_damage", "primary_financial_damage", "secondary_financial_damage", "total_financial_damage"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

3.4.1 Map: Education Financial Damage by Area Council

Show map code
library(sf)
library(leaflet)

# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare education financial data for mapping
education_map_data <- financial_damage_full %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with financial damage data
education_map <- councils_sf %>%
  left_join(education_map_data, by = c("acname" = "Region"))

# Create color palette
pal_edu <- colorNumeric(
  palette = "YlOrRd",
  domain = education_map$total_financial_damage,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(education_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_edu(total_financial_damage),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Financial Damage:</strong> ", format(round(total_financial_damage, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>By Education Level</strong><br>",
      "ECCE: ", format(round(ecce_financial_damage, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Primary: ", format(round(primary_financial_damage, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Secondary: ", format(round(secondary_financial_damage, 0), big.mark = ",", scientific = FALSE), " VT"
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "300px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_edu,
    values = ~total_financial_damage,
    title = "Financial<br>Damage (VT)",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 0)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays estimated financial damage to education infrastructure by Area Council. Hover over each council to view the breakdown by education level (ECCE, Primary, Secondary). Councils shown in grey have no damage data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

4 Emergency Telecommunications

4.1 Baseline: Number of Telecommunication Towers

Show code
# === DATA WRANGLING ===
# Filter for Emergency Telecommunications in Baseline column and Area Council level data
telecom_data <- full_data %>%
  filter(Baseline == "Emergency Telecommunications") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  mutate(Region = `Area Council`)

# Reshape the data to get Digicel and Vodafone as separate columns
telecom_wide <- telecom_data %>%
  mutate(
    provider = tolower(Attribute)  # digicel or vodafone
  ) %>%
  filter(provider %in% c("digicel", "vodafone")) %>%
  select(Region, provider, Value) %>%
  # Group and sum to handle duplicates (4 councils have duplicate entries)
  group_by(Region, provider) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = provider,
    values_from = Value,
    names_prefix = "towers_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Add total column
telecom_wide <- telecom_wide %>%
  mutate(
    total_towers = towers_digicel + towers_vodafone
  ) %>%
  select(Region, total_towers, everything())

# Compute aggregates (province and national levels)
telecom_aggregated <- compute_council_aggregates(telecom_wide)

# === EXPORT TO CSV ===
write.csv(
  telecom_aggregated %>% select(Region, everything()),
  here::here("output", "Telecom_01_baseline_towers.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
# Format for display
formatted <- format_table(telecom_aggregated)

# Create the reactable
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sortable = TRUE,
      defaultSortOrder = "asc",
      html = TRUE,
      sticky = "left"
    ),
    total_towers = colDef(
      name = "Total", 
      format = colFormat(digits = 0)),
    towers_digicel = colDef(
      name = "Digicel", 
      format = colFormat(digits = 0)
    ),
    towers_vodafone = colDef(
      name = "Vodafone", 
      format = colFormat(digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Number of Towers", columns = c("total_towers","towers_digicel", "towers_vodafone"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

4.2 Estimating Damage: Number of Damaged Towers

Show code
# === DATA WRANGLING ===
# Add cyclone strength to baseline values
telecom_with_config <- telecom_wide %>%
  left_join(config, by = c("Region" = "Area Council"))

# Helper function for damage multipliers (council and provider-specific)
get_damage_multiplier <- function(cyclone_category, provider, area_council) {
  intensity_col <- paste0("Intensity ", cyclone_category)
  
  multiplier <- baseline_factors %>%
    filter(
      Cluster == "Emergency Telecommunications",
      Attribute == provider,
      grepl("Number Towers", Indicator, ignore.case = TRUE),
      `Area Council` == area_council
    )
  
  # Check if we got any results
  if(nrow(multiplier) == 0) {
    return(0)
  }
  
  # Extract the value from the intensity column
  result <- multiplier[[intensity_col]][1]
  
  return(ifelse(is.na(result) || length(result) == 0, 0, result))
}

# Calculate damage estimates
damage_estimates <- telecom_with_config %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Digicel towers damaged
    towers_digicel_damaged = towers_digicel * get_damage_multiplier(Intensity, "digicel", Region),
    
    # Vodafone towers damaged
    towers_vodafone_damaged = towers_vodafone * get_damage_multiplier(Intensity, "vodafone", Region),
    
    # Total towers damaged
    total_towers_damaged = towers_digicel_damaged + towers_vodafone_damaged
  ) %>%
  ungroup() %>%
  select(Region, towers_digicel_damaged, towers_vodafone_damaged, total_towers_damaged)

# Aggregate to province and national levels
damage_estimates_full <- compute_council_aggregates(damage_estimates)

# === EXPORT TO CSV ===
write.csv(
  damage_estimates_full %>% select(Region, everything()),
  here::here("output", "Telecom_02_damage_estimates.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
# Format for display
formatted <- format_table(damage_estimates_full)

# Create the damage estimation reactable
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    towers_digicel_damaged = colDef(
      name = "Digicel", 
      format = colFormat(digits = 1)
    ),
    towers_vodafone_damaged = colDef(
      name = "Vodafone", 
      format = colFormat(digits = 1)
    ),
    total_towers_damaged = colDef(
      name = "Total", 
      format = colFormat(digits = 1)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Damaged Towers", columns = c("towers_digicel_damaged", "towers_vodafone_damaged", "total_towers_damaged"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

4.2.1 Map: Damaged Telecommunication Towers by Area Council

Show map code
library(sf)
library(leaflet)

# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare telecom damage data for mapping
telecom_map_data <- damage_estimates_full %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with damage data
telecom_map <- councils_sf %>%
  left_join(telecom_map_data, by = c("acname" = "Region"))

# Create color palette
pal_telecom <- colorNumeric(
  palette = "YlOrRd",
  domain = telecom_map$total_towers_damaged,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(telecom_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_telecom(total_towers_damaged),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Towers Damaged:</strong> ", format(round(total_towers_damaged, 1), big.mark = ",", scientific = FALSE), "<br>",
      "<br>",
      "<strong>By Provider</strong><br>",
      "Digicel: ", format(round(towers_digicel_damaged, 1), big.mark = ",", scientific = FALSE), "<br>",
      "Vodafone: ", format(round(towers_vodafone_damaged, 1), big.mark = ",", scientific = FALSE)
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "300px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
    addLegend(
    position = "bottomright",
    pal = pal_telecom,
    values = ~total_towers_damaged,
    title = "Towers<br>Damaged",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 1)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays estimated damage to telecommunication towers by Area Council. Hover over each council to view the breakdown by provider (Digicel, Vodafone). Councils shown in grey have no damage data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

5 Energy

5.1 Baseline: Household Energy Access and Usage

Show code
# === DATA WRANGLING ===

# Get total households from Shelter baseline
total_households <- full_data %>%
  filter(Baseline == "Shelter") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Household Type", Attribute == "number households") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Value) %>%
  rename(total_households = Value)

# Get household electricity data
electricity_data <- full_data %>%
  filter(Baseline == "Energy") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Household Electricity") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value)

# Reshape electricity data
electricity_wide <- electricity_data %>%
  mutate(attribute_clean = tolower(trimws(Attribute))) %>%
  group_by(Region, attribute_clean) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = attribute_clean,
    values_from = Value,
    names_prefix = "elec_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Get household cooking fuel data
cooking_data <- full_data %>%
  filter(Baseline == "Energy") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Household Cooking Fuel") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value)

# Reshape cooking data
cooking_wide <- cooking_data %>%
  mutate(attribute_clean = tolower(trimws(Attribute))) %>%
  group_by(Region, attribute_clean) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = attribute_clean,
    values_from = Value,
    names_prefix = "cook_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Combine all energy data
energy_wide <- total_households %>%
  left_join(electricity_wide, by = "Region") %>%
  left_join(cooking_wide, by = "Region") %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Compute aggregates (province and national levels)
energy_aggregated <- compute_council_aggregates(energy_wide)

# === EXPORT TO CSV ===
# Reorder columns to match client's specification before export
energy_aggregated_ordered <- energy_aggregated %>%
  select(
    Region,
    total_households,
    # Household Electricity in client's order
    `elec_battery lamp`,
    elec_generator,
    `elec_main grid`,
    `elec_no access`,
    `elec_solar system`,
    # Household Cooking in client's order
    `cook_bottle gas`,
    `cook_open fire`,
    `cook_solar power`,
    cook_electricity,
    `cook_wood stove`
  )

write.csv(
  energy_aggregated_ordered,
  here::here("output", "Energy_01_baseline.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
# Format for display
formatted <- format_table(energy_aggregated)

# Reorder columns to match client's specification
formatted <- formatted %>%
  select(
    Region,
    total_households,
    # Household Electricity in client's order
    `elec_battery lamp`,
    elec_generator,
    `elec_main grid`,
    `elec_no access`,
    `elec_solar system`,
    # Household Cooking in client's order
    `cook_bottle gas`,
    `cook_open fire`,
    `cook_solar power`,
    cook_electricity,
    `cook_wood stove`
  )

# Create the reactable with client's specified column order
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sortable = TRUE,
      defaultSortOrder = "asc",
      html = TRUE,
      sticky = "left"
    ),
    # Total Households
    total_households = colDef(name = "Total", format = colFormat(digits = 0)),
    
    # Household Electricity columns (in client's order)
    `elec_battery lamp` = colDef(name = "Battery Lamp", format = colFormat(digits = 0)),
    elec_generator = colDef(name = "Generator", format = colFormat(digits = 0)),
    `elec_main grid` = colDef(name = "Main Grid", format = colFormat(digits = 0)),
    `elec_no access` = colDef(name = "No Access", format = colFormat(digits = 0)),
    `elec_solar system` = colDef(name = "Solar System", format = colFormat(digits = 0)),
    
    # Household Cooking columns (in client's order)
    `cook_bottle gas` = colDef(name = "Bottle Gas", format = colFormat(digits = 0)),
    `cook_open fire` = colDef(name = "Open Fire", format = colFormat(digits = 0)),
    `cook_solar power` = colDef(name = "Solar Power", format = colFormat(digits = 0)),
    cook_electricity = colDef(name = "Electricity", format = colFormat(digits = 0)),
    `cook_wood stove` = colDef(name = "Wood Stove", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Total Households", columns = c("total_households")),
    colGroup(name = "Household Electricity", columns = c("elec_battery lamp", "elec_generator", "elec_main grid", "elec_no access", "elec_solar system")),
    colGroup(name = "Household Cooking", columns = c("cook_bottle gas", "cook_open fire", "cook_solar power", "cook_electricity", "cook_wood stove"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

5.2 Estimating damage: Households affected by electricity disruption

Show code
# === DATA WRANGLING ===
# Add cyclone strength to baseline values
energy_with_config <- energy_wide %>%
  left_join(config, by = c("Region" = "Area Council"))

# Helper function for damage multipliers (council-specific, for both electricity and cooking)
get_energy_damage_multiplier <- function(cyclone_category, indicator_type, attribute_type, area_council) {
  intensity_col <- paste0("Intensity ", cyclone_category)
  
  multiplier <- baseline_factors %>%
    filter(
      Cluster == "Energy",
      Indicator == indicator_type,
      tolower(trimws(Attribute)) == tolower(trimws(attribute_type)),
      `Area Council` == area_council
    )
  
  # Check if we got any results
  if(nrow(multiplier) == 0) {
    return(0)
  }
  
  # Extract the value from the intensity column
  result <- multiplier[[intensity_col]][1]
  
  return(ifelse(is.na(result) || length(result) == 0, 0, result))
}

# Calculate damage estimates
damage_estimates <- energy_with_config %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Total households affected (same as baseline - no damage applied)
    total_households_affected = total_households,
    
    # Damaged households for each electricity type
    `elec_battery lamp_damaged` = `elec_battery lamp` * get_energy_damage_multiplier(Intensity, "Household Electricity", "battery lamp", Region),
    elec_generator_damaged = elec_generator * get_energy_damage_multiplier(Intensity, "Household Electricity", "generator", Region),
    `elec_main grid_damaged` = `elec_main grid` * get_energy_damage_multiplier(Intensity, "Household Electricity", "main grid", Region),
    `elec_no access_damaged` = `elec_no access` * get_energy_damage_multiplier(Intensity, "Household Electricity", "no access", Region),
    `elec_solar system_damaged` = `elec_solar system` * get_energy_damage_multiplier(Intensity, "Household Electricity", "solar system", Region),
    
    # Damaged households for each cooking fuel type
    `cook_bottle gas_damaged` = `cook_bottle gas` * get_energy_damage_multiplier(Intensity, "Household Cooking Fuel", "bottle gas", Region),
    `cook_open fire_damaged` = `cook_open fire` * get_energy_damage_multiplier(Intensity, "Household Cooking Fuel", "open fire", Region),
    `cook_solar power_damaged` = `cook_solar power` * get_energy_damage_multiplier(Intensity, "Household Cooking Fuel", "solar power", Region),
    cook_electricity_damaged = cook_electricity * get_energy_damage_multiplier(Intensity, "Household Cooking Fuel", "electricity", Region),
    `cook_wood stove_damaged` = `cook_wood stove` * get_energy_damage_multiplier(Intensity, "Household Cooking Fuel", "wood stove", Region)
  ) %>%
  ungroup() %>%
  select(Region, total_households_affected, contains("_damaged"))

# Aggregate to province and national levels
damage_estimates_full <- compute_council_aggregates(damage_estimates)

# === EXPORT TO CSV ===
# Reorder columns to match client's specification before export
damage_estimates_ordered <- damage_estimates_full %>%
  select(
    Region,
    total_households_affected,
    # Household Electricity in client's order
    `elec_battery lamp_damaged`,
    elec_generator_damaged,
    `elec_main grid_damaged`,
    `elec_no access_damaged`,
    `elec_solar system_damaged`,
    # Household Cooking in client's order
    `cook_bottle gas_damaged`,
    `cook_open fire_damaged`,
    `cook_solar power_damaged`,
    cook_electricity_damaged,
    `cook_wood stove_damaged`
  )

write.csv(
  damage_estimates_ordered,
  here::here("output", "Energy_02_damage_estimates.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
# Format for display
formatted <- format_table(damage_estimates_ordered)

# Create the damage estimation reactable
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    # Total Households
    total_households_affected = colDef(name = "Total", format = colFormat(digits = 0)),
    
    # Household Electricity columns (in client's order)
    `elec_battery lamp_damaged` = colDef(name = "Battery Lamp", format = colFormat(digits = 0)),
    elec_generator_damaged = colDef(name = "Generator", format = colFormat(digits = 0)),
    `elec_main grid_damaged` = colDef(name = "Main Grid", format = colFormat(digits = 0)),
    `elec_no access_damaged` = colDef(name = "No Access", format = colFormat(digits = 0)),
    `elec_solar system_damaged` = colDef(name = "Solar System", format = colFormat(digits = 0)),
    
    # Household Cooking columns (in client's order)
    `cook_bottle gas_damaged` = colDef(name = "Bottle Gas", format = colFormat(digits = 0)),
    `cook_open fire_damaged` = colDef(name = "Open Fire", format = colFormat(digits = 0)),
    `cook_solar power_damaged` = colDef(name = "Solar Power", format = colFormat(digits = 0)),
    cook_electricity_damaged = colDef(name = "Electricity", format = colFormat(digits = 0)),
    `cook_wood stove_damaged` = colDef(name = "Wood Stove", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Total Households", columns = c("total_households_affected")),
    colGroup(name = "Household Electricity Affected", columns = c("elec_battery lamp_damaged", "elec_generator_damaged", "elec_main grid_damaged", "elec_no access_damaged", "elec_solar system_damaged")),
    colGroup(name = "Household Cooking Affected", columns = c("cook_bottle gas_damaged", "cook_open fire_damaged", "cook_solar power_damaged", "cook_electricity_damaged", "cook_wood stove_damaged"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

5.3 Immediate Response Resources

Show code
# === DATA WRANGLING ===

# First, we need to get infrastructure baseline data and calculate damage
energy_infra_data <- full_data %>%
  filter(Baseline == "Energy") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Energy Infrastructure") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value)

# Reshape infrastructure data
energy_infra_wide <- energy_infra_data %>%
  mutate(attribute_clean = tolower(trimws(Attribute))) %>%
  group_by(Region, attribute_clean) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = attribute_clean,
    values_from = Value,
    names_prefix = "infra_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Add cyclone config to infrastructure data
energy_infra_with_config <- energy_infra_wide %>%
  left_join(config, by = c("Region" = "Area Council"))

# Helper function for infrastructure damage multipliers
get_infra_damage_multiplier <- function(cyclone_category, attribute_type, area_council) {
  intensity_col <- paste0("Intensity ", cyclone_category)
  
  multiplier <- baseline_factors %>%
    filter(
      Cluster == "Energy",
      Indicator == "Energy Infrastructure",
      tolower(trimws(Attribute)) == tolower(trimws(attribute_type)),
      `Area Council` == area_council
    )
  
  if(nrow(multiplier) == 0) {
    return(0)
  }
  
  result <- multiplier[[intensity_col]][1]
  return(ifelse(is.na(result) || length(result) == 0, 0, result))
}

# Calculate damaged infrastructure
infra_damage <- energy_infra_with_config %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Calculate damaged poles
    `infra_low voltage poles_damaged` = `infra_low voltage poles` * get_infra_damage_multiplier(Intensity, "low voltage poles", Region),
    `infra_high voltage poles_damaged` = `infra_high voltage poles` * get_infra_damage_multiplier(Intensity, "high voltage poles", Region),
    
    # Total damaged poles for power line cable calculation
    total_poles_damaged = `infra_low voltage poles_damaged` + `infra_high voltage poles_damaged`
  ) %>%
  ungroup() %>%
  select(Region, contains("_damaged"), total_poles_damaged)

# Now calculate resources needed
# Join household damage data with infrastructure damage data
resources_base <- damage_estimates %>%
  left_join(infra_damage, by = "Region") %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Calculate total electricity households affected
resources_needed <- resources_base %>%
  mutate(
    # Total households with electricity affected (for solar lamps)
    total_elec_affected = `elec_battery lamp_damaged` + elec_generator_damaged + 
                          `elec_main grid_damaged` + `elec_no access_damaged` + 
                          `elec_solar system_damaged`,
    
    # Resource calculations based on specifications
    solar_lamp = round(1 * total_elec_affected, 0),
    low_voltage_poles_support = round(0.2 * `infra_low voltage poles_damaged`, 0),
    high_voltage_pole_support = round(0.2 * `infra_high voltage poles_damaged`, 0),
    power_line_cable = round(100 * total_poles_damaged, 0)
  ) %>%
  select(Region, solar_lamp, low_voltage_poles_support, high_voltage_pole_support, power_line_cable)

# Aggregate to province and national levels
resources_needed_full <- compute_council_aggregates(resources_needed)

# === EXPORT TO CSV ===
write.csv(
  resources_needed_full,
  here::here("output", "Energy_03_resources_needed.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
# Format for display
formatted <- format_table(resources_needed_full)

# Create the resources needed reactable
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    solar_lamp = colDef(
      name = "Solar Lamp", 
      format = colFormat(digits = 0)
    ),
    low_voltage_poles_support = colDef(
      name = "Low Voltage Poles Support", 
      format = colFormat(digits = 0)
    ),
    high_voltage_pole_support = colDef(
      name = "High Voltage Pole Support", 
      format = colFormat(digits = 0)
    ),
    power_line_cable = colDef(
      name = "Power Line Cable (metres)", 
      format = colFormat(digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Household Electricity", columns = c("solar_lamp")),
    colGroup(name = "Non-renewable", columns = c("low_voltage_poles_support", "high_voltage_pole_support", "power_line_cable"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

5.4 Estimated Financial Damage

5.4.1 Table 1: Household Electricity Financial Damage

Show code
# Helper function to get unit costs for household electricity
get_household_elec_unit_cost <- function(electricity_type, area_council) {
  unit_cost <- financial_config %>%
    filter(
      Cluster == "Energy",
      Indicator == "Household Electricity",
      tolower(trimws(Attribute)) == tolower(trimws(electricity_type)),
      `Area Council` == area_council
    ) %>%
    pull(Value)
  
  return(ifelse(length(unit_cost) > 0, unit_cost, 0))
}

# Add cyclone categories from config file
energy_damage_with_config <- damage_estimates %>%
  left_join(config, by = c("Region" = "Area Council"))

# Calculate household electricity financial damage
household_elec_financial <- energy_damage_with_config %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Financial damage for each electricity type (in VT)
    battery_lamp_damage = `elec_battery lamp_damaged` * get_household_elec_unit_cost("battery lamp", Region),
    generator_damage = elec_generator_damaged * get_household_elec_unit_cost("generator", Region),
    main_grid_damage = `elec_main grid_damaged` * get_household_elec_unit_cost("electricity poles", Region),  # Using "electricity poles" as proxy for main grid
    
    # Total household electricity financial damage
    total_household_elec_damage = battery_lamp_damage + generator_damage + main_grid_damage
  ) %>%
  ungroup() %>%
  select(Region, battery_lamp_damage, generator_damage, main_grid_damage, total_household_elec_damage)

# Aggregate to province and national levels
household_elec_financial_full <- compute_council_aggregates(household_elec_financial)

# === EXPORT TABLE 1 TO CSV ===
write.csv(
  household_elec_financial_full,
  here::here("output", "Energy_04a_financial_damage_household_electricity.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 1 ===
formatted1 <- format_table(household_elec_financial_full)

reactable(
  formatted1,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    battery_lamp_damage = colDef(
      name = "Battery Lamp",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    generator_damage = colDef(
      name = "Generator",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    main_grid_damage = colDef(
      name = "Main Grid",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    total_household_elec_damage = colDef(
      name = "Total Value",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Household Electricity", columns = c("battery_lamp_damage", "generator_damage", "main_grid_damage", "total_household_elec_damage"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

5.4.2 Map: Household Electricity Financial Damage by Area Council

Show map code
# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare household electricity financial data for mapping
household_elec_map_data <- household_elec_financial_full %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with financial damage data
household_elec_map <- councils_sf %>%
  left_join(household_elec_map_data, by = c("acname" = "Region"))

# Create color palette
pal_household_elec <- colorNumeric(
  palette = "YlOrRd",
  domain = household_elec_map$total_household_elec_damage,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(household_elec_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_household_elec(total_household_elec_damage),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Financial Damage:</strong> ", format(round(total_household_elec_damage, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>By Electricity Type</strong><br>",
      "Battery Lamp: ", format(round(battery_lamp_damage, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Generator: ", format(round(generator_damage, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Main Grid: ", format(round(main_grid_damage, 0), big.mark = ",", scientific = FALSE), " VT"
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "300px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_household_elec,
    values = ~total_household_elec_damage,
    title = "Financial<br>Damage (VT)",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 0)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays estimated financial damage to household electricity infrastructure by Area Council. Hover over each council to view the breakdown by electricity type (Battery Lamp, Generator, Main Grid). Councils shown in grey have no damage data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

5.4.3 Table 2: Energy Infrastructure Financial Damage

Show code
# Helper function to get unit costs for infrastructure
get_infrastructure_unit_cost <- function(infrastructure_type, area_council) {
  unit_cost <- financial_config %>%
    filter(
      Cluster == "Energy",
      Indicator == "Energy Infrastructure",
      tolower(trimws(Attribute)) == tolower(trimws(infrastructure_type)),
      `Area Council` == area_council
    ) %>%
    pull(Value)
  
  return(ifelse(length(unit_cost) > 0, unit_cost, 0))
}

# Join infrastructure baseline with config
energy_infra_with_config <- energy_infra_wide %>%
  left_join(config, by = c("Region" = "Area Council"))

# Calculate infrastructure financial damage
infrastructure_financial <- energy_infra_with_config %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Calculate damaged infrastructure units and their financial cost
    hydro_damaged = if("infra_hydro" %in% names(.)) infra_hydro * get_infra_damage_multiplier(Intensity, "hydro", Region) else 0,
    hydro_financial = hydro_damaged * get_infrastructure_unit_cost("hydro", Region),
    
    solar_damaged = if("infra_solar" %in% names(.)) infra_solar * get_infra_damage_multiplier(Intensity, "solar", Region) else 0,
    solar_financial = solar_damaged * get_infrastructure_unit_cost("solar", Region),
    
    pole_support_damaged = if("infra_electrcity pole support" %in% names(.)) `infra_electrcity pole support` * get_infra_damage_multiplier(Intensity, "electrcity pole support", Region) else 0,
    pole_support_financial = pole_support_damaged * get_infrastructure_unit_cost("electrcity pole support", Region),
    
    terminals_damaged = if("infra_electrical terminals" %in% names(.)) `infra_electrical terminals` * get_infra_damage_multiplier(Intensity, "electrical terminals", Region) else 0,
    terminals_financial = terminals_damaged * get_infrastructure_unit_cost("electrical terminals", Region),
    
    hv_poles_damaged = if("infra_high voltage poles" %in% names(.)) `infra_high voltage poles` * get_infra_damage_multiplier(Intensity, "high voltage poles", Region) else 0,
    hv_poles_financial = hv_poles_damaged * get_infrastructure_unit_cost("high voltage poles", Region),
    
    hv_support_damaged = if("infra_high voltage support poles" %in% names(.)) `infra_high voltage support poles` * get_infra_damage_multiplier(Intensity, "high voltage support poles", Region) else 0,
    hv_support_financial = hv_support_damaged * get_infrastructure_unit_cost("high voltage support poles", Region),
    
    hv_transformer_damaged = if("infra_high voltage transformer substation" %in% names(.)) `infra_high voltage transformer substation` * get_infra_damage_multiplier(Intensity, "high voltage transformer substation", Region) else 0,
    hv_transformer_financial = hv_transformer_damaged * get_infrastructure_unit_cost("high voltage transformer substation", Region),
    
    lv_poles_damaged = if("infra_low voltage poles" %in% names(.)) `infra_low voltage poles` * get_infra_damage_multiplier(Intensity, "low voltage poles", Region) else 0,
    lv_poles_financial = lv_poles_damaged * get_infrastructure_unit_cost("low voltage poles", Region),
    
    lv_distribution_damaged = if("infra_low voltage street distrbution boxes" %in% names(.)) `infra_low voltage street distrbution boxes` * get_infra_damage_multiplier(Intensity, "low voltage street distrbution boxes", Region) else 0,
    lv_distribution_financial = lv_distribution_damaged * get_infrastructure_unit_cost("low voltage street distrbution boxes", Region),
    
    # Total infrastructure financial damage
    total_infrastructure_damage = hydro_financial + solar_financial + pole_support_financial + 
                                  terminals_financial + hv_poles_financial + hv_support_financial +
                                  hv_transformer_financial + lv_poles_financial + lv_distribution_financial
  ) %>%
  ungroup() %>%
  select(Region, hydro_financial, solar_financial, pole_support_financial, terminals_financial,
         hv_poles_financial, hv_support_financial, hv_transformer_financial, 
         lv_poles_financial, lv_distribution_financial, total_infrastructure_damage)

# Aggregate to province and national levels
infrastructure_financial_full <- compute_council_aggregates(infrastructure_financial)

# === EXPORT TABLE 2 TO CSV ===
write.csv(
  infrastructure_financial_full,
  here::here("output", "Energy_04b_financial_damage_infrastructure.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 2 ===
formatted2 <- format_table(infrastructure_financial_full)

reactable(
  formatted2,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    hydro_financial = colDef(name = "Hydro", format = colFormat(suffix = " VT", separators = TRUE, digits = 0)),
    solar_financial = colDef(name = "Solar", format = colFormat(suffix = " VT", separators = TRUE, digits = 0)),
    pole_support_financial = colDef(name = "Electricity Pole Support", format = colFormat(suffix = " VT", separators = TRUE, digits = 0)),
    terminals_financial = colDef(name = "Electrical Terminals", format = colFormat(suffix = " VT", separators = TRUE, digits = 0)),
    hv_poles_financial = colDef(name = "High Voltage Poles", format = colFormat(suffix = " VT", separators = TRUE, digits = 0)),
    hv_support_financial = colDef(name = "High Voltage Support Poles", format = colFormat(suffix = " VT", separators = TRUE, digits = 0)),
    hv_transformer_financial = colDef(name = "HV Transformer Substation", format = colFormat(suffix = " VT", separators = TRUE, digits = 0)),
    lv_poles_financial = colDef(name = "Low Voltage Poles", format = colFormat(suffix = " VT", separators = TRUE, digits = 0)),
    lv_distribution_financial = colDef(name = "LV Street Distribution Boxes", format = colFormat(suffix = " VT", separators = TRUE, digits = 0)),
    total_infrastructure_damage = colDef(name = "Total Value", format = colFormat(suffix = " VT", separators = TRUE, digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Renewable", columns = c("hydro_financial", "solar_financial")),
    colGroup(name = "Non-renewable", columns = c("pole_support_financial", "terminals_financial", 
                                                   "hv_poles_financial", "hv_support_financial",
                                                   "hv_transformer_financial", "lv_poles_financial",
                                                   "lv_distribution_financial")),
    colGroup(name = "Total", columns = c("total_infrastructure_damage"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

5.4.4 Map: Energy Infrastructure Financial Damage by Area Council

Show map code
# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare infrastructure financial data for mapping
infra_financial_map_data <- infrastructure_financial_full %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with financial damage data
infra_financial_map <- councils_sf %>%
  left_join(infra_financial_map_data, by = c("acname" = "Region"))

# Create color palette
pal_infra_financial <- colorNumeric(
  palette = "YlOrRd",
  domain = infra_financial_map$total_infrastructure_damage,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(infra_financial_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_infra_financial(total_infrastructure_damage),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Financial Damage:</strong> ", format(round(total_infrastructure_damage, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>Renewable</strong><br>",
      "Hydro: ", format(round(hydro_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Solar: ", format(round(solar_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>Non-renewable</strong><br>",
      "Electricity Pole Support: ", format(round(pole_support_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Electrical Terminals: ", format(round(terminals_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "High Voltage Poles: ", format(round(hv_poles_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "HV Support Poles: ", format(round(hv_support_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "HV Transformer Substation: ", format(round(hv_transformer_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Low Voltage Poles: ", format(round(lv_poles_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "LV Street Distribution Boxes: ", format(round(lv_distribution_financial, 0), big.mark = ",", scientific = FALSE), " VT"
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "350px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_infra_financial,
    values = ~total_infrastructure_damage,
    title = "Financial<br>Damage (VT)",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 0)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays estimated financial damage to energy infrastructure by Area Council. Hover over each council to view the breakdown by infrastructure type (Renewable and Non-renewable). Councils shown in grey have no damage data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

6 Food security

6.1 Baseline: Food Security

6.1.1 Table 1: Staple crops

Show code
# === DATA WRANGLING ===

# Get total households from Shelter baseline (for both tables)
total_households <- full_data %>%
  filter(Baseline == "Shelter") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Household Type", Attribute == "number households") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Value) %>%
  rename(total_households = Value)

# === TABLE 1: STAPLE CROPS ===

# Get staple crop data
staple_crop_data <- full_data %>%
  filter(Baseline == "Food Security") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator %in% c("Stable Crop Households", "Staple Crop Production")) %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Indicator, Attribute, Value)

# Reshape staple crop data
staple_crop_wide <- staple_crop_data %>%
  mutate(
    crop = tolower(trimws(Attribute)),
    metric = case_when(
      grepl("Households", Indicator) ~ "households",
      grepl("Production", Indicator) ~ "production",
      TRUE ~ "other"
    )
  ) %>%
  group_by(Region, crop, metric) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = c(crop, metric),
    values_from = Value,
    names_sep = "_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Combine with total households
staple_crop_full <- total_households %>%
  left_join(staple_crop_wide, by = "Region") %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Compute aggregates (province and national levels)
staple_crop_aggregated <- compute_council_aggregates(staple_crop_full)

# Reorder columns according to client's specification
staple_crop_ordered <- staple_crop_aggregated %>%
  select(
    Region,
    total_households,
    # Island Cabbage
    `island cabbage_households`,
    `island cabbage_production`,
    # Banana
    banana_households,
    banana_production,
    # Taro
    taro_households,
    taro_production,
    # Kumala
    kumala_households,
    kumala_production,
    # Manioc
    manioc_households,
    manioc_production,
    # Yam
    yam_households,
    yam_production
  )

# === EXPORT TABLE 1 TO CSV ===
write.csv(
  staple_crop_ordered,
  here::here("output", "FoodSecurity_01a_baseline_staple_crops.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 1 ===
formatted1 <- format_table(staple_crop_ordered)

reactable(
  formatted1,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_households = colDef(name = "Total", format = colFormat(digits = 0)),
    
    # Island Cabbage
    `island cabbage_households` = colDef(name = "Households", format = colFormat(digits = 0)),
    `island cabbage_production` = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Banana
    banana_households = colDef(name = "Households", format = colFormat(digits = 0)),
    banana_production = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Taro
    taro_households = colDef(name = "Households", format = colFormat(digits = 0)),
    taro_production = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Kumala
    kumala_households = colDef(name = "Households", format = colFormat(digits = 0)),
    kumala_production = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Manioc
    manioc_households = colDef(name = "Households", format = colFormat(digits = 0)),
    manioc_production = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Yam
    yam_households = colDef(name = "Households", format = colFormat(digits = 0)),
    yam_production = colDef(name = "Production", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Total Households", columns = c("total_households")),
    colGroup(name = "Island Cabbage", columns = c("island cabbage_households", "island cabbage_production")),
    colGroup(name = "Banana", columns = c("banana_households", "banana_production")),
    colGroup(name = "Taro", columns = c("taro_households", "taro_production")),
    colGroup(name = "Kumala", columns = c("kumala_households", "kumala_production")),
    colGroup(name = "Manioc", columns = c("manioc_households", "manioc_production")),
    colGroup(name = "Yam", columns = c("yam_households", "yam_production"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

6.1.2 Table 2: Cash Crops

Show code
# Get cash crop data
cash_crop_data <- full_data %>%
  filter(Baseline == "Food Security") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator %in% c("Cash Crop Households", "Cash Crop Production")) %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Indicator, Attribute, Value)

# Reshape cash crop data
cash_crop_wide <- cash_crop_data %>%
  mutate(
    crop = tolower(trimws(Attribute)),
    metric = case_when(
      grepl("Households", Indicator) ~ "households",
      grepl("Production", Indicator) ~ "production",
      TRUE ~ "other"
    )
  ) %>%
  group_by(Region, crop, metric) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = c(crop, metric),
    values_from = Value,
    names_sep = "_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Combine with total households
cash_crop_full <- total_households %>%
  left_join(cash_crop_wide, by = "Region") %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Compute aggregates (province and national levels)
cash_crop_aggregated <- compute_council_aggregates(cash_crop_full)

# Reorder columns according to client's specification
cash_crop_ordered <- cash_crop_aggregated %>%
  select(
    Region,
    total_households,
    # Kava
    kava_households,
    kava_production,
    # Coconut
    coconut_households,
    coconut_production,
    # Cocoa
    cocoa_households,
    cocoa_production,
    # Coffee
    coffee_households,
    coffee_production,
    # Vanilla
    vanilla_households,
    vanilla_production,
    # Tahitian Lime
    `tahitian lime_households`,
    `tahitian lime_production`,
    # Pepper
    pepper_households,
    pepper_production,
    # Noni
    noni_households,
    noni_production
  )

# === EXPORT TABLE 2 TO CSV ===
write.csv(
  cash_crop_ordered,
  here::here("output", "FoodSecurity_01b_baseline_cash_crops.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 2 ===
formatted2 <- format_table(cash_crop_ordered)

reactable(
  formatted2,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_households = colDef(name = "Total", format = colFormat(digits = 0)),
    
    # Kava
    kava_households = colDef(name = "Households", format = colFormat(digits = 0)),
    kava_production = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Coconut
    coconut_households = colDef(name = "Households", format = colFormat(digits = 0)),
    coconut_production = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Cocoa
    cocoa_households = colDef(name = "Households", format = colFormat(digits = 0)),
    cocoa_production = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Coffee
    coffee_households = colDef(name = "Households", format = colFormat(digits = 0)),
    coffee_production = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Vanilla
    vanilla_households = colDef(name = "Households", format = colFormat(digits = 0)),
    vanilla_production = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Tahitian Lime
    `tahitian lime_households` = colDef(name = "Households", format = colFormat(digits = 0)),
    `tahitian lime_production` = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Pepper
    pepper_households = colDef(name = "Households", format = colFormat(digits = 0)),
    pepper_production = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Noni
    noni_households = colDef(name = "Households", format = colFormat(digits = 0)),
    noni_production = colDef(name = "Production", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Total Households", columns = c("total_households")),
    colGroup(name = "Kava", columns = c("kava_households", "kava_production")),
    colGroup(name = "Coconut", columns = c("coconut_households", "coconut_production")),
    colGroup(name = "Cocoa", columns = c("cocoa_households", "cocoa_production")),
    colGroup(name = "Coffee", columns = c("coffee_households", "coffee_production")),
    colGroup(name = "Vanilla", columns = c("vanilla_households", "vanilla_production")),
    colGroup(name = "Tahitian Lime", columns = c("tahitian lime_households", "tahitian lime_production")),
    colGroup(name = "Pepper", columns = c("pepper_households", "pepper_production")),
    colGroup(name = "Noni", columns = c("noni_households", "noni_production"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

6.2 Estimated Hazard Damage

6.2.1 Table 1: Staple Crops Damage

Show code
# Helper function for food security damage multipliers
get_food_security_damage_multiplier <- function(cyclone_category, indicator_type, crop_type, area_council) {
  intensity_col <- paste0("Intensity ", cyclone_category)
  
  multiplier <- baseline_factors %>%
    filter(
      Cluster == "Food Security",
      Indicator == indicator_type,
      tolower(trimws(Attribute)) == tolower(trimws(crop_type)),
      `Area Council` == area_council
    )
  
  if(nrow(multiplier) == 0) {
    return(0)
  }
  
  result <- multiplier[[intensity_col]][1]
  return(ifelse(is.na(result) || length(result) == 0, 0, result))
}

# Add cyclone categories from config file
staple_crop_with_config <- staple_crop_full %>%
  left_join(config, by = c("Region" = "Area Council"))

# Calculate staple crop damage estimates
staple_damage <- staple_crop_with_config %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Total households affected (same as baseline)
    total_households_affected = total_households,
    
    # Island Cabbage
    `island cabbage_households_damaged` = `island cabbage_households` * get_food_security_damage_multiplier(Intensity, "Stable Crop Households", "island cabbage", Region),
    `island cabbage_production_damaged` = `island cabbage_production` * get_food_security_damage_multiplier(Intensity, "Stable Crop Production", "island cabbage", Region),
    
    # Banana
    banana_households_damaged = banana_households * get_food_security_damage_multiplier(Intensity, "Stable Crop Households", "banana", Region),
    banana_production_damaged = banana_production * get_food_security_damage_multiplier(Intensity, "Stable Crop Production", "banana", Region),
    
    # Taro
    taro_households_damaged = taro_households * get_food_security_damage_multiplier(Intensity, "Stable Crop Households", "taro", Region),
    taro_production_damaged = taro_production * get_food_security_damage_multiplier(Intensity, "Stable Crop Production", "taro", Region),
    
    # Kumala
    kumala_households_damaged = kumala_households * get_food_security_damage_multiplier(Intensity, "Stable Crop Households", "kumala", Region),
    kumala_production_damaged = kumala_production * get_food_security_damage_multiplier(Intensity, "Stable Crop Production", "kumala", Region),
    
    # Manioc
    manioc_households_damaged = manioc_households * get_food_security_damage_multiplier(Intensity, "Stable Crop Households", "manioc", Region),
    manioc_production_damaged = manioc_production * get_food_security_damage_multiplier(Intensity, "Stable Crop Production", "manioc", Region),
    
    # Yam
    yam_households_damaged = yam_households * get_food_security_damage_multiplier(Intensity, "Stable Crop Households", "yam", Region),
    yam_production_damaged = yam_production * get_food_security_damage_multiplier(Intensity, "Stable Crop Production", "yam", Region)
  ) %>%
  ungroup() %>%
  select(Region, total_households_affected, contains("_damaged"))

# Aggregate to province and national levels
staple_damage_full <- compute_council_aggregates(staple_damage)

# Reorder columns according to client's specification
staple_damage_ordered <- staple_damage_full %>%
  select(
    Region,
    total_households_affected,
    # Island Cabbage
    `island cabbage_households_damaged`,
    `island cabbage_production_damaged`,
    # Banana
    banana_households_damaged,
    banana_production_damaged,
    # Taro
    taro_households_damaged,
    taro_production_damaged,
    # Kumala
    kumala_households_damaged,
    kumala_production_damaged,
    # Manioc
    manioc_households_damaged,
    manioc_production_damaged,
    # Yam
    yam_households_damaged,
    yam_production_damaged
  )

# === EXPORT TABLE 1 TO CSV ===
write.csv(
  staple_damage_ordered,
  here::here("output", "FoodSecurity_02a_damage_staple_crops.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 1 ===
formatted1 <- format_table(staple_damage_ordered)

reactable(
  formatted1,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_households_affected = colDef(name = "Total", format = colFormat(digits = 0)),
    
    # Island Cabbage
    `island cabbage_households_damaged` = colDef(name = "Households", format = colFormat(digits = 0)),
    `island cabbage_production_damaged` = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Banana
    banana_households_damaged = colDef(name = "Households", format = colFormat(digits = 0)),
    banana_production_damaged = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Taro
    taro_households_damaged = colDef(name = "Households", format = colFormat(digits = 0)),
    taro_production_damaged = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Kumala
    kumala_households_damaged = colDef(name = "Households", format = colFormat(digits = 0)),
    kumala_production_damaged = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Manioc
    manioc_households_damaged = colDef(name = "Households", format = colFormat(digits = 0)),
    manioc_production_damaged = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Yam
    yam_households_damaged = colDef(name = "Households", format = colFormat(digits = 0)),
    yam_production_damaged = colDef(name = "Production", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Total Households", columns = c("total_households_affected")),
    colGroup(name = "Island Cabbage", columns = c("island cabbage_households_damaged", "island cabbage_production_damaged")),
    colGroup(name = "Banana", columns = c("banana_households_damaged", "banana_production_damaged")),
    colGroup(name = "Taro", columns = c("taro_households_damaged", "taro_production_damaged")),
    colGroup(name = "Kumala", columns = c("kumala_households_damaged", "kumala_production_damaged")),
    colGroup(name = "Manioc", columns = c("manioc_households_damaged", "manioc_production_damaged")),
    colGroup(name = "Yam", columns = c("yam_households_damaged", "yam_production_damaged"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

6.2.2 Table 2: Cash Crops Damage

Show code
# Add cyclone categories from config file
cash_crop_with_config <- cash_crop_full %>%
  left_join(config, by = c("Region" = "Area Council"))

# Calculate cash crop damage estimates
cash_damage <- cash_crop_with_config %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Total households affected (same as baseline)
    total_households_affected = total_households,
    
    # Kava
    kava_households_damaged = kava_households * get_food_security_damage_multiplier(Intensity, "Cash Crop Households", "kava", Region),
    kava_production_damaged = kava_production * get_food_security_damage_multiplier(Intensity, "Cash Crop Production", "kava", Region),
    
    # Coconut
    coconut_households_damaged = coconut_households * get_food_security_damage_multiplier(Intensity, "Cash Crop Households", "coconut", Region),
    coconut_production_damaged = coconut_production * get_food_security_damage_multiplier(Intensity, "Cash Crop Production", "coconut", Region),
    
    # Cocoa
    cocoa_households_damaged = cocoa_households * get_food_security_damage_multiplier(Intensity, "Cash Crop Households", "cocoa", Region),
    cocoa_production_damaged = cocoa_production * get_food_security_damage_multiplier(Intensity, "Cash Crop Production", "cocoa", Region),
    
    # Coffee
    coffee_households_damaged = coffee_households * get_food_security_damage_multiplier(Intensity, "Cash Crop Households", "coffee", Region),
    coffee_production_damaged = coffee_production * get_food_security_damage_multiplier(Intensity, "Cash Crop Production", "coffee", Region),
    
    # Vanilla
    vanilla_households_damaged = vanilla_households * get_food_security_damage_multiplier(Intensity, "Cash Crop Households", "vanilla", Region),
    vanilla_production_damaged = vanilla_production * get_food_security_damage_multiplier(Intensity, "Cash Crop Production", "vanilla", Region),
    
    # Tahitian Lime
    `tahitian lime_households_damaged` = `tahitian lime_households` * get_food_security_damage_multiplier(Intensity, "Cash Crop Households", "tahitian lime", Region),
    `tahitian lime_production_damaged` = `tahitian lime_production` * get_food_security_damage_multiplier(Intensity, "Cash Crop Production", "tahitian lime", Region),
    
    # Pepper
    pepper_households_damaged = pepper_households * get_food_security_damage_multiplier(Intensity, "Cash Crop Households", "pepper", Region),
    pepper_production_damaged = pepper_production * get_food_security_damage_multiplier(Intensity, "Cash Crop Production", "pepper", Region),
    
    # Noni
    noni_households_damaged = noni_households * get_food_security_damage_multiplier(Intensity, "Cash Crop Households", "noni", Region),
    noni_production_damaged = noni_production * get_food_security_damage_multiplier(Intensity, "Cash Crop Production", "noni", Region)
  ) %>%
  ungroup() %>%
  select(Region, total_households_affected, contains("_damaged"))

# Aggregate to province and national levels
cash_damage_full <- compute_council_aggregates(cash_damage)

# Reorder columns according to client's specification
cash_damage_ordered <- cash_damage_full %>%
  select(
    Region,
    total_households_affected,
    # Kava
    kava_households_damaged,
    kava_production_damaged,
    # Coconut
    coconut_households_damaged,
    coconut_production_damaged,
    # Cocoa
    cocoa_households_damaged,
    cocoa_production_damaged,
    # Coffee
    coffee_households_damaged,
    coffee_production_damaged,
    # Vanilla
    vanilla_households_damaged,
    vanilla_production_damaged,
    # Tahitian Lime
    `tahitian lime_households_damaged`,
    `tahitian lime_production_damaged`,
    # Pepper
    pepper_households_damaged,
    pepper_production_damaged,
    # Noni
    noni_households_damaged,
    noni_production_damaged
  )

# === EXPORT TABLE 2 TO CSV ===
write.csv(
  cash_damage_ordered,
  here::here("output", "FoodSecurity_02b_damage_cash_crops.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 2 ===
formatted2 <- format_table(cash_damage_ordered)

reactable(
  formatted2,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_households_affected = colDef(name = "Total", format = colFormat(digits = 0)),
    
    # Kava
    kava_households_damaged = colDef(name = "Households", format = colFormat(digits = 0)),
    kava_production_damaged = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Coconut
    coconut_households_damaged = colDef(name = "Households", format = colFormat(digits = 0)),
    coconut_production_damaged = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Cocoa
    cocoa_households_damaged = colDef(name = "Households", format = colFormat(digits = 0)),
    cocoa_production_damaged = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Coffee
    coffee_households_damaged = colDef(name = "Households", format = colFormat(digits = 0)),
    coffee_production_damaged = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Vanilla
    vanilla_households_damaged = colDef(name = "Households", format = colFormat(digits = 0)),
    vanilla_production_damaged = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Tahitian Lime
    `tahitian lime_households_damaged` = colDef(name = "Households", format = colFormat(digits = 0)),
    `tahitian lime_production_damaged` = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Pepper
    pepper_households_damaged = colDef(name = "Households", format = colFormat(digits = 0)),
    pepper_production_damaged = colDef(name = "Production", format = colFormat(digits = 0)),
    
    # Noni
    noni_households_damaged = colDef(name = "Households", format = colFormat(digits = 0)),
    noni_production_damaged = colDef(name = "Production", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Total Households", columns = c("total_households_affected")),
    colGroup(name = "Kava", columns = c("kava_households_damaged", "kava_production_damaged")),
    colGroup(name = "Coconut", columns = c("coconut_households_damaged", "coconut_production_damaged")),
    colGroup(name = "Cocoa", columns = c("cocoa_households_damaged", "cocoa_production_damaged")),
    colGroup(name = "Coffee", columns = c("coffee_households_damaged", "coffee_production_damaged")),
    colGroup(name = "Vanilla", columns = c("vanilla_households_damaged", "vanilla_production_damaged")),
    colGroup(name = "Tahitian Lime", columns = c("tahitian lime_households_damaged", "tahitian lime_production_damaged")),
    colGroup(name = "Pepper", columns = c("pepper_households_damaged", "pepper_production_damaged")),
    colGroup(name = "Noni", columns = c("noni_households_damaged", "noni_production_damaged"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

6.3 Immediate Response Resources

Show code
## Resources needed for Food Security restoration

# === Staple Crops Resources ===

# Helper function to get resource multipliers
get_food_security_resource_multiplier <- function(resource_type, area_council) {
  multiplier <- resource_config %>%
    filter(
      Cluster == "Food Security",
      Indicator == resource_type,
      `Area Council` == area_council
    ) %>%
    pull(Value)
  
  return(ifelse(length(multiplier) > 0, multiplier, 0))
}

# Calculate resources needed based on total affected households
resources_needed <- staple_damage %>%
  rowwise() %>%
  mutate(
    # Number of affected households (for the first column)
    num_households_affected = total_households_affected,
    
    # Resources = total affected households * resource multiplier
    island_cabbage_cuttings = round(total_households_affected * get_food_security_resource_multiplier("Island Cabbage Cuttings", Region), 0),
    taro_seedlings = round(total_households_affected * get_food_security_resource_multiplier("Taro Seedlings", Region), 0),
    kumala_cuttings = round(total_households_affected * get_food_security_resource_multiplier("Kumala Cuttings", Region), 0),
    manioc_cuttings = round(total_households_affected * get_food_security_resource_multiplier("Manioc Cuttings", Region), 0),
    yam_cuttings = round(total_households_affected * get_food_security_resource_multiplier("Yam Cuttings", Region), 0)
  ) %>%
  ungroup() %>%
  select(Region, num_households_affected, island_cabbage_cuttings, taro_seedlings, kumala_cuttings, manioc_cuttings, yam_cuttings)

# Aggregate to province and national levels
resources_needed_full <- compute_council_aggregates(resources_needed)

# === EXPORT TO CSV ===
write.csv(
  resources_needed_full,
  here::here("output", "FoodSecurity_03_resources_needed.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
formatted <- format_table(resources_needed_full)

reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    num_households_affected = colDef(
      name = "Number of Households", 
      format = colFormat(digits = 0)
    ),
    island_cabbage_cuttings = colDef(
      name = "Island Cabbage", 
      format = colFormat(digits = 0)
    ),
    taro_seedlings = colDef(
      name = "Taro", 
      format = colFormat(digits = 0)
    ),
    kumala_cuttings = colDef(
      name = "Kumala", 
      format = colFormat(digits = 0)
    ),
    manioc_cuttings = colDef(
      name = "Manioc", 
      format = colFormat(digits = 0)
    ),
    yam_cuttings = colDef(
      name = "Yam", 
      format = colFormat(digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Affected Households", columns = c("num_households_affected")),
    colGroup(name = "Staple Crops Resources (Cuttings/Seedlings)", columns = c("island_cabbage_cuttings", "taro_seedlings", "kumala_cuttings", "manioc_cuttings", "yam_cuttings"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

6.4 Estimated Financial Damage

6.4.1 Table 1: Staple Crops Financial Damage

Show code
# Helper function to get unit costs for food security
get_food_security_unit_cost <- function(indicator_type, crop_type, area_council) {
  unit_cost <- financial_config %>%
    filter(
      Cluster == "Food Security",
      Indicator == indicator_type,
      tolower(trimws(Attribute)) == tolower(trimws(crop_type)),
      `Area Council` == area_council
    ) %>%
    pull(Value)
  
  return(ifelse(length(unit_cost) > 0, unit_cost, 0))
}

# Calculate staple crops financial damage
staple_financial <- staple_damage %>%
  rowwise() %>%
  mutate(
    # Financial damage for each crop (damaged production * unit cost)
    island_cabbage_financial = `island cabbage_production_damaged` * get_food_security_unit_cost("Staple Crop Production", "island cabbage", Region),
    banana_financial = banana_production_damaged * get_food_security_unit_cost("Staple Crop Production", "banana", Region),
    taro_financial = taro_production_damaged * get_food_security_unit_cost("Staple Crop Production", "taro", Region),
    kumala_financial = kumala_production_damaged * get_food_security_unit_cost("Staple Crop Production", "kumala", Region),
    manioc_financial = manioc_production_damaged * get_food_security_unit_cost("Staple Crop Production", "manioc", Region),
    yam_financial = yam_production_damaged * get_food_security_unit_cost("Staple Crop Production", "yam", Region),
    
    # Total staple crops financial damage
    total_staple_financial = island_cabbage_financial + banana_financial + taro_financial + 
                             kumala_financial + manioc_financial + yam_financial
  ) %>%
  ungroup() %>%
  select(Region, total_staple_financial, island_cabbage_financial, banana_financial, 
         taro_financial, kumala_financial, manioc_financial, yam_financial)

# Aggregate to province and national levels
staple_financial_full <- compute_council_aggregates(staple_financial)

# Reorder columns
staple_financial_ordered <- staple_financial_full %>%
  select(Region, total_staple_financial, island_cabbage_financial, banana_financial, 
         taro_financial, kumala_financial, manioc_financial, yam_financial)

# === EXPORT TABLE 1 TO CSV ===
write.csv(
  staple_financial_ordered,
  here::here("output", "FoodSecurity_04a_financial_damage_staple_crops.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 1 ===
formatted1 <- format_table(staple_financial_ordered)

reactable(
  formatted1,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_staple_financial = colDef(
      name = "Total Value",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    island_cabbage_financial = colDef(
      name = "Island Cabbage",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    banana_financial = colDef(
      name = "Banana",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    taro_financial = colDef(
      name = "Taro",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    kumala_financial = colDef(
      name = "Kumala",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    manioc_financial = colDef(
      name = "Manioc",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    yam_financial = colDef(
      name = "Yam",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Staple Crops Financial Damage (VT)", 
             columns = c("total_staple_financial", "island_cabbage_financial", "banana_financial", 
                        "taro_financial", "kumala_financial", "manioc_financial", "yam_financial"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

6.4.2 Map: Staple Crops Financial Damage by Area Council

Show map code
# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare staple crops financial data for mapping
staple_financial_map_data <- staple_financial_full %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with financial damage data
staple_financial_map <- councils_sf %>%
  left_join(staple_financial_map_data, by = c("acname" = "Region"))

# Create color palette
pal_staple_financial <- colorNumeric(
  palette = "YlOrRd",
  domain = staple_financial_map$total_staple_financial,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(staple_financial_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_staple_financial(total_staple_financial),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Financial Damage:</strong> ", format(round(total_staple_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>By Staple Crop</strong><br>",
      "Island Cabbage: ", format(round(island_cabbage_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Banana: ", format(round(banana_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Taro: ", format(round(taro_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Kumala: ", format(round(kumala_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Manioc: ", format(round(manioc_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Yam: ", format(round(yam_financial, 0), big.mark = ",", scientific = FALSE), " VT"
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "300px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_staple_financial,
    values = ~total_staple_financial,
    title = "Financial<br>Damage (VT)",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 0)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays estimated financial damage to staple crops by Area Council. Hover over each council to view the breakdown by crop type (Island Cabbage, Banana, Taro, Kumala, Manioc, Yam). Councils shown in grey have no damage data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

6.4.3 Table 2: Cash Crops Financial Damage

Show code
# Calculate cash crops financial damage
cash_financial <- cash_damage %>%
  rowwise() %>%
  mutate(
    # Financial damage for each crop (damaged production * unit cost)
    kava_financial = kava_production_damaged * get_food_security_unit_cost("Cash Crop Production", "kava", Region),
    coconut_financial = coconut_production_damaged * get_food_security_unit_cost("Cash Crop Production", "coconut", Region),
    cocoa_financial = cocoa_production_damaged * get_food_security_unit_cost("Cash Crop Production", "cocoa", Region),
    coffee_financial = coffee_production_damaged * get_food_security_unit_cost("Cash Crop Production", "coffee", Region),
    vanilla_financial = vanilla_production_damaged * get_food_security_unit_cost("Cash Crop Production", "vanilla", Region),
    tahitian_lime_financial = `tahitian lime_production_damaged` * get_food_security_unit_cost("Cash Crop Production", "tahitian lime", Region),
    pepper_financial = pepper_production_damaged * get_food_security_unit_cost("Cash Crop Production", "pepper", Region),
    noni_financial = noni_production_damaged * get_food_security_unit_cost("Cash Crop Production", "noni", Region),
    
    # Total cash crops financial damage
    total_cash_financial = kava_financial + coconut_financial + cocoa_financial + 
                           coffee_financial + vanilla_financial + tahitian_lime_financial + 
                           pepper_financial + noni_financial
  ) %>%
  ungroup() %>%
  select(Region, total_cash_financial, kava_financial, coconut_financial, cocoa_financial,
         coffee_financial, vanilla_financial, tahitian_lime_financial, pepper_financial, noni_financial)

# Aggregate to province and national levels
cash_financial_full <- compute_council_aggregates(cash_financial)

# Reorder columns
cash_financial_ordered <- cash_financial_full %>%
  select(Region, total_cash_financial, kava_financial, coconut_financial, cocoa_financial,
         coffee_financial, vanilla_financial, tahitian_lime_financial, pepper_financial, noni_financial)

# === EXPORT TABLE 2 TO CSV ===
write.csv(
  cash_financial_ordered,
  here::here("output", "FoodSecurity_04b_financial_damage_cash_crops.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 2 ===
formatted2 <- format_table(cash_financial_ordered)

reactable(
  formatted2,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_cash_financial = colDef(
      name = "Total Value",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    kava_financial = colDef(
      name = "Kava",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    coconut_financial = colDef(
      name = "Coconut",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    cocoa_financial = colDef(
      name = "Cocoa",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    coffee_financial = colDef(
      name = "Coffee",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    vanilla_financial = colDef(
      name = "Vanilla",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    tahitian_lime_financial = colDef(
      name = "Tahitian Lime",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    pepper_financial = colDef(
      name = "Pepper",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    noni_financial = colDef(
      name = "Noni",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Cash Crops Financial Damage (VT)", 
             columns = c("total_cash_financial", "kava_financial", "coconut_financial", "cocoa_financial",
                        "coffee_financial", "vanilla_financial", "tahitian_lime_financial", 
                        "pepper_financial", "noni_financial"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

6.4.4 Map: Cash Crops Financial Damage by Area Council

Show map code
library(sf)
library(leaflet)

# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare cash crops financial data for mapping
cash_financial_map_data <- cash_financial_full %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with financial damage data
cash_financial_map <- councils_sf %>%
  left_join(cash_financial_map_data, by = c("acname" = "Region"))

# Create color palette
pal_cash_financial <- colorNumeric(
  palette = "YlOrRd",
  domain = cash_financial_map$total_cash_financial,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(cash_financial_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_cash_financial(total_cash_financial),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Financial Damage:</strong> ", format(round(total_cash_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>By Cash Crop</strong><br>",
      "Kava: ", format(round(kava_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Coconut: ", format(round(coconut_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Cocoa: ", format(round(cocoa_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Coffee: ", format(round(coffee_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Vanilla: ", format(round(vanilla_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Tahitian Lime: ", format(round(tahitian_lime_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Pepper: ", format(round(pepper_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Noni: ", format(round(noni_financial, 0), big.mark = ",", scientific = FALSE), " VT"
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "300px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_cash_financial,
    values = ~total_cash_financial,
    title = "Financial<br>Damage (VT)",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 0)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays estimated financial damage to cash crops by Area Council. Hover over each council to view the breakdown by crop type (Kava, Coconut, Cocoa, Coffee, Vanilla, Tahitian Lime, Pepper, Noni). Councils shown in grey have no damage data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

7 Gender & Protection

7.1 Baseline : Gender & Protection

7.1.1 Table 1: Population by Sex and Age

Show code
# === DATA WRANGLING ===

# Get total population from the data
total_population <- full_data %>%
  filter(Baseline == "Gender & Protection") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Population Sex") %>%
  mutate(Region = `Area Council`) %>%
  group_by(Region) %>%
  summarise(total_population = sum(Value, na.rm = TRUE), .groups = "drop")

# Get population by sex
population_sex <- full_data %>%
  filter(Baseline == "Gender & Protection") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Population Sex") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value)

# Reshape sex data
sex_wide <- population_sex %>%
  mutate(sex = tolower(trimws(Attribute))) %>%
  group_by(Region, sex) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = sex,
    values_from = Value,
    names_prefix = "sex_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Get population by age
population_age <- full_data %>%
  filter(Baseline == "Gender & Protection") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Population Age") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value)

# Reshape age data
age_wide <- population_age %>%
  mutate(age_group = tolower(trimws(Attribute))) %>%
  group_by(Region, age_group) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = age_group,
    values_from = Value,
    names_prefix = "age_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Combine all population data
population_full <- total_population %>%
  left_join(sex_wide, by = "Region") %>%
  left_join(age_wide, by = "Region") %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Compute aggregates (province and national levels)
population_aggregated <- compute_council_aggregates(population_full)

# Reorder columns
population_ordered <- population_aggregated %>%
  select(
    Region,
    total_population,
    sex_male,
    sex_female,
    `age_0-4`,
    `age_5-11`,
    `age_12-18`,
    `age_19-35`,
    `age_36-54`,
    `age_55+`
  )

# === EXPORT TABLE 1 TO CSV ===
write.csv(
  population_ordered,
  here::here("output", "GenderProtection_01a_baseline_population.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 1 ===
formatted1 <- format_table(population_ordered)

reactable(
  formatted1,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_population = colDef(name = "Total", format = colFormat(digits = 0)),
    sex_male = colDef(name = "Male", format = colFormat(digits = 0)),
    sex_female = colDef(name = "Female", format = colFormat(digits = 0)),
    `age_0-4` = colDef(name = "0-4", format = colFormat(digits = 0)),
    `age_5-11` = colDef(name = "5-11", format = colFormat(digits = 0)),
    `age_12-18` = colDef(name = "12-18", format = colFormat(digits = 0)),
    `age_19-35` = colDef(name = "19-35", format = colFormat(digits = 0)),
    `age_36-54` = colDef(name = "36-54", format = colFormat(digits = 0)),
    `age_55+` = colDef(name = "55+", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Total Population", columns = c("total_population")),
    colGroup(name = "Sex", columns = c("sex_male", "sex_female")),
    colGroup(name = "Age Group", columns = c("age_0-4", "age_5-11", "age_12-18", "age_19-35", "age_36-54", "age_55+"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

7.1.2 Table 2: Marital Status

Show code
# Get marital status data
marital_data <- full_data %>%
  filter(Baseline == "Gender & Protection") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Marital Status") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value)

# Reshape marital status data
marital_wide <- marital_data %>%
  mutate(status = tolower(trimws(Attribute))) %>%
  group_by(Region, status) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = status,
    values_from = Value,
    names_prefix = "marital_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Combine with total population
marital_full <- total_population %>%
  left_join(marital_wide, by = "Region") %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Compute aggregates (province and national levels)
marital_aggregated <- compute_council_aggregates(marital_full)

# Reorder columns
marital_ordered <- marital_aggregated %>%
  select(
    Region,
    total_population,
    marital_defacto,
    marital_married,
    `marital_never married`,
    marital_separated,
    marital_widowed
  )

# === EXPORT TABLE 2 TO CSV ===
write.csv(
  marital_ordered,
  here::here("output", "GenderProtection_01b_baseline_marital_status.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 2 ===
formatted2 <- format_table(marital_ordered)

reactable(
  formatted2,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_population = colDef(name = "Total", format = colFormat(digits = 0)),
    marital_defacto = colDef(name = "Defacto", format = colFormat(digits = 0)),
    marital_married = colDef(name = "Married", format = colFormat(digits = 0)),
    `marital_never married` = colDef(name = "Never Married", format = colFormat(digits = 0)),
    marital_separated = colDef(name = "Separated", format = colFormat(digits = 0)),
    marital_widowed = colDef(name = "Widow", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Marital Status", columns = c("total_population", "marital_defacto", "marital_married", "marital_never married", "marital_separated", "marital_widowed"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

7.1.3 Table 3: Employment Status

Show code
# Get employment status data
employment_data <- full_data %>%
  filter(Baseline == "Gender & Protection") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Employment Status") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value)

# Reshape employment status data
employment_wide <- employment_data %>%
  mutate(status = tolower(trimws(Attribute))) %>%
  group_by(Region, status) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = status,
    values_from = Value,
    names_prefix = "employ_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Combine with total population
employment_full <- total_population %>%
  left_join(employment_wide, by = "Region") %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Compute aggregates (province and national levels)
employment_aggregated <- compute_council_aggregates(employment_full)

# Reorder columns
employment_ordered <- employment_aggregated %>%
  select(
    Region,
    total_population,
    employ_government,
    employ_private,
    employ_employer,
    `employ_self-employed`,
    employ_voluntary,
    employ_unpaid,
    `employ_own consumption`
  )

# === EXPORT TABLE 3 TO CSV ===
write.csv(
  employment_ordered,
  here::here("output", "GenderProtection_01c_baseline_employment_status.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 3 ===
formatted3 <- format_table(employment_ordered)

reactable(
  formatted3,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_population = colDef(name = "Total", format = colFormat(digits = 0)),
    employ_government = colDef(name = "Government", format = colFormat(digits = 0)),
    employ_private = colDef(name = "Private", format = colFormat(digits = 0)),
    employ_employer = colDef(name = "Employer", format = colFormat(digits = 0)),
    `employ_self-employed` = colDef(name = "Self Employed", format = colFormat(digits = 0)),
    employ_voluntary = colDef(name = "Voluntary", format = colFormat(digits = 0)),
    employ_unpaid = colDef(name = "Unpaid", format = colFormat(digits = 0)),
    `employ_own consumption` = colDef(name = "Own Consumption", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Employment Status", columns = c("total_population", "employ_government", "employ_private", "employ_employer", "employ_self-employed", "employ_voluntary", "employ_unpaid", "employ_own consumption"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

7.2 Immediate Response Resources

Show code
## Resources needed for Gender & Protection

# === DATA WRANGLING ===

# Configuration parameters
days_of_support <- 14  # Number of days to provide resources (adjust as needed)

# Helper function to get resource multipliers
get_gender_protection_resource_multiplier <- function(resource_type, area_council) {
  multiplier <- resource_config %>%
    filter(
      Cluster == "Gender & Protection",
      Indicator == resource_type,
      `Area Council` == area_council
    ) %>%
    pull(Value)
  
  return(ifelse(length(multiplier) > 0, multiplier, 0))
}

# Add cyclone categories to population data
population_with_config <- population_full %>%
  left_join(config, by = c("Region" = "Area Council"))

# Calculate resources needed based on total population in affected areas
resources_needed <- population_with_config %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Total population affected
    population_affected = total_population,
    
    # Resources = population * resource multiplier * days of support
    water = ceiling(total_population * get_gender_protection_resource_multiplier("Water", Region) * days_of_support),
    tin_fish = round(total_population * get_gender_protection_resource_multiplier("Tin Fish", Region) * days_of_support, 0),
    rice = round(total_population * get_gender_protection_resource_multiplier("Rice", Region) * days_of_support, 0)
  ) %>%
  ungroup() %>%
  select(Region, population_affected, water, tin_fish, rice)

# Aggregate to province and national levels
resources_needed_full <- compute_council_aggregates(resources_needed)

# === EXPORT TO CSV ===
write.csv(
  resources_needed_full,
  here::here("output", "GenderProtection_02_resources_needed.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
formatted <- format_table(resources_needed_full)

reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    population_affected = colDef(
      name = "Total Population", 
      format = colFormat(digits = 0)
    ),
    water = colDef(
      name = "Water (litres)", 
      format = colFormat(digits = 0)
    ),
    tin_fish = colDef(
      name = "Tin Fish (kg)", 
      format = colFormat(digits = 0)
    ),
    rice = colDef(
      name = "Rice (kg)", 
      format = colFormat(digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Resources Needed", columns = c("population_affected", "water", "tin_fish", "rice"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

7.2.1 Map: Immediate Response Resources by Area Council

Show map code
# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare gender & protection resources data for mapping
gp_resources_map_data <- resources_needed_full %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with resources data
gp_resources_map <- councils_sf %>%
  left_join(gp_resources_map_data, by = c("acname" = "Region"))

# Create color palette based on population affected
pal_gp_resources <- colorNumeric(
  palette = "YlOrRd",
  domain = gp_resources_map$population_affected,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(gp_resources_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_gp_resources(population_affected),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Population:</strong> ", format(round(population_affected, 0), big.mark = ",", scientific = FALSE), "<br>",
      "<br>",
      "<strong>Resources Needed</strong><br>",
      "Water: ", format(round(water, 0), big.mark = ",", scientific = FALSE), " litres<br>",
      "Tin Fish: ", format(round(tin_fish, 0), big.mark = ",", scientific = FALSE), " kg<br>",
      "Rice: ", format(round(rice, 0), big.mark = ",", scientific = FALSE), " kg"
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "300px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_gp_resources,
    values = ~population_affected,
    title = "Population<br>Affected",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 0)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays immediate response resource needs for Gender & Protection by Area Council. Hover over each council to view the population affected and resource requirements (Water, Tin Fish, Rice). Councils shown in grey have no data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

8 Health

8.1 Baseline: Number of Health Facilities and Staff

Show code
# === DATA WRANGLING ===

# Get health facility data
health_facility_data <- full_data %>%
  filter(Baseline == "Health") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Health Facility") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value)

# Reshape health facility data
facility_wide <- health_facility_data %>%
  mutate(facility_type = tolower(trimws(Attribute))) %>%
  group_by(Region, facility_type) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = facility_type,
    values_from = Value,
    names_prefix = "facility_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Get health professionals data
health_professionals_data <- full_data %>%
  filter(Baseline == "Health") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Health Professionals") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value)

# Reshape health professionals data
professionals_wide <- health_professionals_data %>%
  mutate(professional_type = tolower(trimws(Attribute))) %>%
  group_by(Region, professional_type) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = professional_type,
    values_from = Value,
    names_prefix = "prof_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Get hospital staff data
hospital_staff_data <- full_data %>%
  filter(Baseline == "Health") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Hospital Staff") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value)

# Reshape hospital staff data
hospital_staff_wide <- hospital_staff_data %>%
  mutate(staff_type = tolower(trimws(Attribute))) %>%
  group_by(Region, staff_type) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = staff_type,
    values_from = Value,
    names_prefix = "staff_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Combine all health data
health_full <- facility_wide %>%
  full_join(professionals_wide, by = "Region") %>%
  full_join(hospital_staff_wide, by = "Region") %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Compute aggregates (province and national levels)
health_aggregated <- compute_council_aggregates(health_full)

# Reorder columns according to client's specification
# Add total columns
health_ordered <- health_aggregated %>%
  mutate(
    total_facilities = facility_hospital + `facility_health centre` + facility_dispensary + facility_aidpost,
    total_professionals = prof_doctor + `prof_nurse practitioner` + `prof_registered nurse` + prof_midwife + `prof_nurse aid`,
    total_staff = `staff_clinical medical services` + `staff_nonclinical medical services` + `staff_clinical nursing services` + `staff_nonclinical nursing services` + `staff_pharmacy services`
  ) %>%
  select(
    Region,
    # Health Facility
    total_facilities,
    facility_hospital,
    `facility_health centre`,
    facility_dispensary,
    facility_aidpost,
    # Health Professional
    total_professionals,
    prof_doctor,
    `prof_nurse practitioner`,
    `prof_registered nurse`,
    prof_midwife,
    `prof_nurse aid`,
    # Hospital Staff
    total_staff,
    `staff_clinical medical services`,
    `staff_nonclinical medical services`,
    `staff_clinical nursing services`,
    `staff_nonclinical nursing services`,
    `staff_pharmacy services`
  )

# === EXPORT TO CSV ===
write.csv(
  health_ordered,
  here::here("output", "Health_01_baseline.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
formatted <- format_table(health_ordered)

reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    # Health Facility
    total_facilities = colDef(name = "Total", format = colFormat(digits = 0)),
    facility_hospital = colDef(name = "Hospital", format = colFormat(digits = 0)),
    `facility_health centre` = colDef(name = "Health Centre", format = colFormat(digits = 0)),
    facility_dispensary = colDef(name = "Dispensary", format = colFormat(digits = 0)),
    facility_aidpost = colDef(name = "Aidpost", format = colFormat(digits = 0)),
    
    # Health Professional
    total_professionals = colDef(name = "Total", format = colFormat(digits = 0)),
    prof_doctor = colDef(name = "Doctor", format = colFormat(digits = 0)),
    `prof_nurse practitioner` = colDef(name = "Nurse Practitioner", format = colFormat(digits = 0)),
    `prof_registered nurse` = colDef(name = "Registered Nurse", format = colFormat(digits = 0)),
    prof_midwife = colDef(name = "Midwife", format = colFormat(digits = 0)),
    `prof_nurse aid` = colDef(name = "Nurse Aid", format = colFormat(digits = 0)),
    
    # Hospital Staff
    total_staff = colDef(name = "Total", format = colFormat(digits = 0)),
    `staff_clinical medical services` = colDef(name = "Clinical Medical", format = colFormat(digits = 0)),
    `staff_nonclinical medical services` = colDef(name = "Nonclinical Medical", format = colFormat(digits = 0)),
    `staff_clinical nursing services` = colDef(name = "Clinical Nursing", format = colFormat(digits = 0)),
    `staff_nonclinical nursing services` = colDef(name = "Nonclinical Nursing", format = colFormat(digits = 0)),
    `staff_pharmacy services` = colDef(name = "Pharmacy", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Health Facility", columns = c("total_facilities", "facility_hospital", "facility_health centre", "facility_dispensary", "facility_aidpost")),
    colGroup(name = "Health Professional", columns = c("total_professionals", "prof_doctor", "prof_nurse practitioner", "prof_registered nurse", "prof_midwife", "prof_nurse aid")),
    colGroup(name = "Hospital Staff", columns = c("total_staff", "staff_clinical medical services", "staff_nonclinical medical services", "staff_clinical nursing services", "staff_nonclinical nursing services", "staff_pharmacy services"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

8.2 Estimated Hazard Damage

Show code
# === DATA WRANGLING ===

# Add cyclone strength to baseline values
health_with_config <- health_full %>%
  left_join(config, by = c("Region" = "Area Council"))

# Helper function for health damage multipliers (council-specific)
get_health_damage_multiplier <- function(cyclone_category, indicator_type, attribute_type, area_council) {
  intensity_col <- paste0("Intensity ", cyclone_category)
  
  multiplier <- baseline_factors %>%
    filter(
      Cluster == "Health",
      Indicator == indicator_type,
      tolower(trimws(Attribute)) == tolower(trimws(attribute_type)),
      `Area Council` == area_council
    )
  
  # Check if we got any results
  if(nrow(multiplier) == 0) {
    return(0)
  }
  
  # Extract the value from the intensity column
  result <- multiplier[[intensity_col]][1]
  
  return(ifelse(is.na(result) || length(result) == 0, 0, result))
}

# Calculate damage estimates for Health Facilities only
# Keep UNROUNDED values for aggregation first
damage_estimates_raw <- health_with_config %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Health Facility Damage - keep as decimals for now
    facility_hospital_damaged = facility_hospital * get_health_damage_multiplier(Intensity, "Health Facility", "hospital", Region),
    `facility_health centre_damaged` = `facility_health centre` * get_health_damage_multiplier(Intensity, "Health Facility", "health centre", Region),
    facility_dispensary_damaged = facility_dispensary * get_health_damage_multiplier(Intensity, "Health Facility", "dispensary", Region),
    facility_aidpost_damaged = facility_aidpost * get_health_damage_multiplier(Intensity, "Health Facility", "aidpost", Region)
  ) %>%
  ungroup() %>%
  select(Region, facility_hospital_damaged, `facility_health centre_damaged`, 
         facility_dispensary_damaged, facility_aidpost_damaged)

# Aggregate to province and national levels FIRST
damage_estimates_aggregated <- compute_council_aggregates(damage_estimates_raw)

# NOW round all values (council, province, and national)
# Add total column after aggregation and rounding
damage_estimates_full <- damage_estimates_aggregated %>%
  mutate(
    facility_hospital_damaged = round(facility_hospital_damaged, 0),
    `facility_health centre_damaged` = round(`facility_health centre_damaged`, 0),
    facility_dispensary_damaged = round(facility_dispensary_damaged, 0),
    facility_aidpost_damaged = round(facility_aidpost_damaged, 0),
    total_facilities_damaged = facility_hospital_damaged + `facility_health centre_damaged` + facility_dispensary_damaged + facility_aidpost_damaged
  ) %>%
  select(Region, total_facilities_damaged, facility_hospital_damaged, `facility_health centre_damaged`,
         facility_dispensary_damaged, facility_aidpost_damaged)

# === EXPORT TO CSV ===
write.csv(
  damage_estimates_full,
  here::here("output", "Health_02_damage_estimates.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
# Format for display
formatted <- format_table(damage_estimates_full)

# Create the damage estimation reactable
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_facilities_damaged = colDef(name = "Total", format = colFormat(digits = 0)),
    facility_hospital_damaged = colDef(name = "Hospital", format = colFormat(digits = 0)),
    `facility_health centre_damaged` = colDef(name = "Health Centre", format = colFormat(digits = 0)),
    facility_dispensary_damaged = colDef(name = "Dispensary", format = colFormat(digits = 0)),
    facility_aidpost_damaged = colDef(name = "Aidpost", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Health Facility Damaged", columns = c("total_facilities_damaged", "facility_hospital_damaged", "facility_health centre_damaged", "facility_dispensary_damaged", "facility_aidpost_damaged"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

8.3 Immediate Response Resources

Show code
# === DATA WRANGLING ===

# Helper function to get resource multipliers from config
get_health_resource_multiplier <- function(resource_type, area_council) {
  multiplier <- resource_config %>%
    filter(
      Cluster == "Health",
      Indicator == resource_type,
      `Area Council` == area_council
    ) %>%
    pull(Value)
    
  return(ifelse(length(multiplier) > 0, multiplier, 0))
}

# Calculate total damaged facilities (this is what we need for resources)
health_facilities_damaged <- damage_estimates_raw %>%
  left_join(config, by = c("Region" = "Area Council")) %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Calculate total damaged facilities
    total_facilities_damaged = facility_hospital_damaged + `facility_health centre_damaged` +
                                 facility_dispensary_damaged + facility_aidpost_damaged
  ) %>%
  ungroup() %>%
  select(Region, total_facilities_damaged)

# Calculate resources needed based on FACILITIES with multipliers from config
resources_needed <- health_facilities_damaged %>%
  rowwise() %>%
  mutate(
    # ALL resources based on damaged facilities with multipliers from config file
    medical_tent = round(get_health_resource_multiplier("Medical Tent", Region) * total_facilities_damaged, 0),
    emergency_health_kits = round(get_health_resource_multiplier("Emergency Health Kits", Region) * total_facilities_damaged, 0),
    trauma_surgical_kits = round(get_health_resource_multiplier("Trauma and Surgical Kits", Region) * total_facilities_damaged, 0),
    essential_ncd_medicines = round(get_health_resource_multiplier("Essential NCD Medicines", Region) * total_facilities_damaged, 0),
    mosquito_nets = round(get_health_resource_multiplier("Mosquito Nets", Region) * total_facilities_damaged, 0)
  ) %>%
  ungroup() %>%
  select(Region, medical_tent, emergency_health_kits, trauma_surgical_kits,
         essential_ncd_medicines, mosquito_nets)

# Aggregate to province and national levels
resources_needed_full <- compute_council_aggregates(resources_needed)

# === EXPORT TO CSV ===
write.csv(
  resources_needed_full,
  here::here("output", "Health_03_resources_needed.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
# Format for display
formatted <- format_table(resources_needed_full)

# Create the resources needed reactable
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    medical_tent = colDef(
      name = "Medical Tent",
      format = colFormat(digits = 0)
    ),
    emergency_health_kits = colDef(
      name = "Emergency Health Kits",
      format = colFormat(digits = 0)
    ),
    trauma_surgical_kits = colDef(
      name = "Trauma and Surgical Kits",
      format = colFormat(digits = 0)
    ),
    essential_ncd_medicines = colDef(
      name = "Essential NCD Medicines",
      format = colFormat(digits = 0)
    ),
    mosquito_nets = colDef(
      name = "Mosquito Nets",
      format = colFormat(digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Resources Needed", columns = c("medical_tent", "emergency_health_kits", "trauma_surgical_kits", "essential_ncd_medicines", "mosquito_nets"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

8.4 Estimated Financial Damage

Show code
# === DATA WRANGLING ===

# Helper function to get unit costs for health facilities
get_health_unit_cost <- function(facility_type, area_council) {
  unit_cost <- financial_config %>%
    filter(
      Cluster == "Health",
      Indicator == "Health Facility",
      tolower(trimws(Attribute)) == tolower(trimws(facility_type)),
      `Area Council` == area_council
    ) %>%
    pull(Value)
    
  return(ifelse(length(unit_cost) > 0, unit_cost, 0))
}

# Use the raw damage estimates (before rounding) for financial calculations
health_financial <- damage_estimates_raw %>%
  rowwise() %>%
  mutate(
    # Financial damage for each facility type (in VT)
    hospital_financial = facility_hospital_damaged * get_health_unit_cost("hospital", Region),
    health_centre_financial = `facility_health centre_damaged` * get_health_unit_cost("health centre", Region),
    dispensary_financial = facility_dispensary_damaged * get_health_unit_cost("dispensary", Region),
    aidpost_financial = facility_aidpost_damaged * get_health_unit_cost("aidpost", Region),
    
    # Total financial damage
    total_financial_damage = hospital_financial + health_centre_financial +
                              dispensary_financial + aidpost_financial
  ) %>%
  ungroup() %>%
  select(Region, total_financial_damage, hospital_financial, health_centre_financial,
         dispensary_financial, aidpost_financial)

# Aggregate to province and national levels
health_financial_full <- compute_council_aggregates(health_financial)

# Reorder columns to match specification
health_financial_ordered <- health_financial_full %>%
  select(Region, total_financial_damage, hospital_financial, health_centre_financial,
         dispensary_financial, aidpost_financial)

# === EXPORT TO CSV ===
write.csv(
  health_financial_ordered,
  here::here("output", "Health_04_financial_damage.csv"),
  row.names = FALSE
)

# === PRESENTATION ===

# Format for display
formatted <- format_table(health_financial_ordered)

# Create the financial damage reactable
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    total_financial_damage = colDef(
      name = "Total Value",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    hospital_financial = colDef(
      name = "Hospital",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    health_centre_financial = colDef(
      name = "Health Centre",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    dispensary_financial = colDef(
      name = "Dispensary",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    aidpost_financial = colDef(
      name = "Aidpost",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Health Facility Financial Damage (VT)", 
             columns = c("total_financial_damage", "hospital_financial",
                        "health_centre_financial", "dispensary_financial",
                        "aidpost_financial"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

8.4.1 Map: Health Financial Damage by Area Council

Show map code
library(sf)
library(leaflet)

# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare health financial data for mapping
health_financial_map_data <- health_financial_full %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with financial damage data
health_financial_map <- councils_sf %>%
  left_join(health_financial_map_data, by = c("acname" = "Region"))

# Create color palette
pal_health_financial <- colorNumeric(
  palette = "YlOrRd",
  domain = health_financial_map$total_financial_damage,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(health_financial_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_health_financial(total_financial_damage),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Financial Damage:</strong> ", format(round(total_financial_damage, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>By Facility Type</strong><br>",
      "Hospital: ", format(round(hospital_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Health Centre: ", format(round(health_centre_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Dispensary: ", format(round(dispensary_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Aidpost: ", format(round(aidpost_financial, 0), big.mark = ",", scientific = FALSE), " VT"
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "300px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_health_financial,
    values = ~total_financial_damage,
    title = "Financial<br>Damage (VT)",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 0)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays estimated financial damage to health facilities by Area Council. Hover over each council to view the breakdown by facility type (Hospital, Health Centre, Dispensary, Aidpost). Councils shown in grey have no damage data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

9 Logistics

9.1 Baseline: Logistics Infrastructure

9.1.1 Table 1: Infrastructure

Show code
# === DATA WRANGLING ===

# Get logistics infrastructure data
logistics_infrastructure_data <- full_data %>%
  filter(Baseline == "Logistics") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Infrastructure") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value)

# Reshape infrastructure data
infrastructure_wide <- logistics_infrastructure_data %>%
  mutate(infrastructure_type = tolower(trimws(Attribute))) %>%
  group_by(Region, infrastructure_type) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = infrastructure_type,
    values_from = Value,
    names_prefix = "infra_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Compute aggregates (province and national levels)
infrastructure_aggregated <- compute_council_aggregates(infrastructure_wide)

# Reorder columns according to specification
infrastructure_ordered <- infrastructure_aggregated %>%
  mutate(
    total_infrastructure = infra_airport + infra_wharf + `infra_permanent bridge` + `infra_temporary bridge` + `infra_police stations` + `infra_fire hydrants` + `infra_main water valves`
  ) %>%
  select(
    Region,
    total_infrastructure,
    infra_airport,
    infra_wharf,
    `infra_permanent bridge`,
    `infra_temporary bridge`,
    `infra_police stations`,
    `infra_fire hydrants`,
    `infra_main water valves`
  )

# === EXPORT TABLE 1 TO CSV ===
write.csv(
  infrastructure_ordered,
  here::here("output", "Logistics_01a_baseline_infrastructure.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 1 ===
formatted1 <- format_table(infrastructure_ordered)

reactable(
  formatted1,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_infrastructure = colDef(name = "Total", format = colFormat(digits = 0)),
    infra_airport = colDef(name = "Airport", format = colFormat(digits = 0)),
    infra_wharf = colDef(name = "Wharf", format = colFormat(digits = 0)),
    `infra_permanent bridge` = colDef(name = "Permanent Bridge", format = colFormat(digits = 0)),
    `infra_temporary bridge` = colDef(name = "Temporary Bridge", format = colFormat(digits = 0)),
    `infra_police stations` = colDef(name = "Police Stations", format = colFormat(digits = 0)),
    `infra_fire hydrants` = colDef(name = "Fire Hydrants", format = colFormat(digits = 0)),
    `infra_main water valves` = colDef(name = "Main Water Valves", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Infrastructure", columns = c("total_infrastructure", "infra_airport", "infra_wharf", "infra_permanent bridge",
                                                   "infra_temporary bridge", "infra_police stations",
                                                   "infra_fire hydrants", "infra_main water valves"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

9.1.2 Table 2: Road Surface

Show code
# Get logistics road surface data
logistics_road_data <- full_data %>%
  filter(Baseline == "Logistics") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  filter(Indicator == "Road Surface") %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value)

# Reshape road surface data
road_wide <- logistics_road_data %>%
  mutate(road_type = tolower(trimws(Attribute))) %>%
  group_by(Region, road_type) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = road_type,
    values_from = Value,
    names_prefix = "road_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Compute aggregates (province and national levels)
road_aggregated <- compute_council_aggregates(road_wide)

# Reorder columns according to specification
road_ordered <- road_aggregated %>%
  mutate(
    total_road_km = road_asphalt + `road_chips seal` + road_concrete + road_earth + road_gravel
  ) %>%
  select(
    Region,
    total_road_km,
    road_asphalt,
    `road_chips seal`,
    road_concrete,
    road_earth,
    road_gravel
  )

# === EXPORT TABLE 2 TO CSV ===
write.csv(
  road_ordered,
  here::here("output", "Logistics_01b_baseline_road_surface.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 2 ===
formatted2 <- format_table(road_ordered)

reactable(
  formatted2,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_road_km = colDef(name = "Total (km)", format = colFormat(digits = 0)),
    road_asphalt = colDef(name = "Asphalt", format = colFormat(digits = 0)),
    `road_chips seal` = colDef(name = "Chips Seal", format = colFormat(digits = 0)),
    road_concrete = colDef(name = "Concrete", format = colFormat(digits = 0)),
    road_earth = colDef(name = "Earth", format = colFormat(digits = 0)),
    road_gravel = colDef(name = "Gravel", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Road Surface (km)", columns = c("total_road_km", "road_asphalt", "road_chips seal", "road_concrete",
                                                 "road_earth", "road_gravel"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

9.2 Estimated Hazard Damage

9.2.1 Table 1: Infrastructure Damage

Show code
# === DATA WRANGLING ===

# Add cyclone strength to baseline values
infrastructure_with_config <- infrastructure_wide %>%
  left_join(config, by = c("Region" = "Area Council"))

# Helper function for infrastructure damage multipliers (council-specific)
get_logistics_damage_multiplier <- function(cyclone_category, indicator_type, attribute_type, area_council) {
  intensity_col <- paste0("Intensity ", cyclone_category)
  
  multiplier <- baseline_factors %>%
    filter(
      Cluster == "Logistics",
      Indicator == indicator_type,
      tolower(trimws(Attribute)) == tolower(trimws(attribute_type)),
      `Area Council` == area_council
    )
  
  # Check if we got any results
  if(nrow(multiplier) == 0) {
    return(0)
  }
  
  # Extract the value from the intensity column
  result <- multiplier[[intensity_col]][1]
  
  return(ifelse(is.na(result) || length(result) == 0, 0, result))
}

# Calculate damage estimates for Infrastructure
# Keep UNROUNDED values for aggregation first
infrastructure_damage_raw <- infrastructure_with_config %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Infrastructure Damage - keep as decimals for now
    infra_airport_damaged = infra_airport * get_logistics_damage_multiplier(Intensity, "Infrastructure", "airport", Region),
    infra_wharf_damaged = infra_wharf * get_logistics_damage_multiplier(Intensity, "Infrastructure", "wharf", Region),
    `infra_permanent bridge_damaged` = `infra_permanent bridge` * get_logistics_damage_multiplier(Intensity, "Infrastructure", "permanent bridge", Region),
    `infra_temporary bridge_damaged` = `infra_temporary bridge` * get_logistics_damage_multiplier(Intensity, "Infrastructure", "temporary bridge", Region),
    `infra_police stations_damaged` = `infra_police stations` * get_logistics_damage_multiplier(Intensity, "Infrastructure", "police stations", Region),
    `infra_fire hydrants_damaged` = `infra_fire hydrants` * get_logistics_damage_multiplier(Intensity, "Infrastructure", "fire hydrants", Region),
    `infra_main water valves_damaged` = `infra_main water valves` * get_logistics_damage_multiplier(Intensity, "Infrastructure", "main water valves", Region)
  ) %>%
  ungroup() %>%
  select(Region, infra_airport_damaged, infra_wharf_damaged, `infra_permanent bridge_damaged`,
         `infra_temporary bridge_damaged`, `infra_police stations_damaged`, 
         `infra_fire hydrants_damaged`, `infra_main water valves_damaged`)

# Aggregate to province and national levels FIRST
infrastructure_damage_aggregated <- compute_council_aggregates(infrastructure_damage_raw)

# NOW round all values (council, province, and national)
# Add total column after rounding
infrastructure_damage_full <- infrastructure_damage_aggregated %>%
  mutate(
    infra_airport_damaged = round(infra_airport_damaged, 0),
    infra_wharf_damaged = round(infra_wharf_damaged, 0),
    `infra_permanent bridge_damaged` = round(`infra_permanent bridge_damaged`, 0),
    `infra_temporary bridge_damaged` = round(`infra_temporary bridge_damaged`, 0),
    `infra_police stations_damaged` = round(`infra_police stations_damaged`, 0),
    `infra_fire hydrants_damaged` = round(`infra_fire hydrants_damaged`, 0),
    `infra_main water valves_damaged` = round(`infra_main water valves_damaged`, 0),
    total_infrastructure_damaged = infra_airport_damaged + infra_wharf_damaged + `infra_permanent bridge_damaged` + `infra_temporary bridge_damaged` + `infra_police stations_damaged` + `infra_fire hydrants_damaged` + `infra_main water valves_damaged`
  ) %>%
  select(Region, total_infrastructure_damaged, infra_airport_damaged, infra_wharf_damaged, `infra_permanent bridge_damaged`,
         `infra_temporary bridge_damaged`, `infra_police stations_damaged`,
         `infra_fire hydrants_damaged`, `infra_main water valves_damaged`)

# === EXPORT TABLE 1 TO CSV ===
write.csv(
  infrastructure_damage_full,
  here::here("output", "Logistics_02a_damage_infrastructure.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 1 ===
# Format for display
formatted1 <- format_table(infrastructure_damage_full)

# Create the damage estimation reactable
reactable(
  formatted1,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_infrastructure_damaged = colDef(name = "Total", format = colFormat(digits = 0)),
    infra_airport_damaged = colDef(name = "Airport", format = colFormat(digits = 0)),
    infra_wharf_damaged = colDef(name = "Wharf", format = colFormat(digits = 0)),
    `infra_permanent bridge_damaged` = colDef(name = "Permanent Bridge", format = colFormat(digits = 0)),
    `infra_temporary bridge_damaged` = colDef(name = "Temporary Bridge", format = colFormat(digits = 0)),
    `infra_police stations_damaged` = colDef(name = "Police Stations", format = colFormat(digits = 0)),
    `infra_fire hydrants_damaged` = colDef(name = "Fire Hydrants", format = colFormat(digits = 0)),
    `infra_main water valves_damaged` = colDef(name = "Main Water Valves", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Infrastructure Damaged", columns = c("total_infrastructure_damaged", "infra_airport_damaged", "infra_wharf_damaged",
                                                   "infra_permanent bridge_damaged", "infra_temporary bridge_damaged",
                                                  "infra_police stations_damaged", "infra_fire hydrants_damaged",
                                                  "infra_main water valves_damaged"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

9.2.2 Table 2: Road Surface Damage

Show code
# Add cyclone strength to road baseline values
road_with_config <- road_wide %>%
  left_join(config, by = c("Region" = "Area Council"))

# Calculate damage estimates for Road Surface
# Keep UNROUNDED values for aggregation first
road_damage_raw <- road_with_config %>%
  filter(!is.na(Intensity)) %>%
  rowwise() %>%
  mutate(
    # Road Surface Damage - keep as decimals for now
    road_asphalt_damaged = road_asphalt * get_logistics_damage_multiplier(Intensity, "Road Surface", "asphalt", Region),
    `road_chips seal_damaged` = `road_chips seal` * get_logistics_damage_multiplier(Intensity, "Road Surface", "chips seal", Region),
    road_concrete_damaged = road_concrete * get_logistics_damage_multiplier(Intensity, "Road Surface", "concrete", Region),
    road_earth_damaged = road_earth * get_logistics_damage_multiplier(Intensity, "Road Surface", "earth", Region),
    road_gravel_damaged = road_gravel * get_logistics_damage_multiplier(Intensity, "Road Surface", "gravel", Region)
  ) %>%
  ungroup() %>%
  select(Region, road_asphalt_damaged, `road_chips seal_damaged`, road_concrete_damaged,
         road_earth_damaged, road_gravel_damaged)

# Aggregate to province and national levels FIRST
road_damage_aggregated <- compute_council_aggregates(road_damage_raw)

# NOW round all values (council, province, and national)
road_damage_full <- road_damage_aggregated %>%
  mutate(
    road_asphalt_damaged = round(road_asphalt_damaged, 0),
    `road_chips seal_damaged` = round(`road_chips seal_damaged`, 0),
    road_concrete_damaged = round(road_concrete_damaged, 0),
    road_earth_damaged = round(road_earth_damaged, 0),
    road_gravel_damaged = round(road_gravel_damaged, 0),
    total_road_damaged = road_asphalt_damaged + `road_chips seal_damaged` + road_concrete_damaged + road_earth_damaged + road_gravel_damaged
  ) %>%
  select(Region, total_road_damaged, road_asphalt_damaged, `road_chips seal_damaged`, road_concrete_damaged,
         road_earth_damaged, road_gravel_damaged)

# === EXPORT TABLE 2 TO CSV ===
write.csv(
  road_damage_full,
  here::here("output", "Logistics_02b_damage_road_surface.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 2 ===
# Format for display
formatted2 <- format_table(road_damage_full)

# Create the damage estimation reactable
reactable(
  formatted2,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    total_road_damaged = colDef(name = "Total (km)", format = colFormat(digits = 0)),
    road_asphalt_damaged = colDef(name = "Asphalt", format = colFormat(digits = 0)),
    `road_chips seal_damaged` = colDef(name = "Chips Seal", format = colFormat(digits = 0)),
    road_concrete_damaged = colDef(name = "Concrete", format = colFormat(digits = 0)),
    road_earth_damaged = colDef(name = "Earth", format = colFormat(digits = 0)),
    road_gravel_damaged = colDef(name = "Gravel", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Road Surface Damaged (km)", columns = c("total_road_damaged", "road_asphalt_damaged", "road_chips seal_damaged",
                                                 "road_concrete_damaged", "road_earth_damaged",
                                                 "road_gravel_damaged"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

9.3 Immediate Response Resources

Show code
# === DATA WRANGLING ===

# Helper function to get resource multipliers
get_logistics_resource_multiplier <- function(resource_type, area_council) {
  multiplier <- resource_config %>%
    filter(
      Cluster == "Logistics",
      Indicator == resource_type,
      `Area Council` == area_council
    ) %>%
    pull(Value)
  
  return(ifelse(length(multiplier) > 0, multiplier, 0))
}

# Determine which councils are affected (have cyclone intensity assigned)
affected_councils <- config %>%
  filter(!is.na(Intensity)) %>%
  select(`Area Council`, Province) %>%
  distinct()

# Count number of affected councils
num_affected_councils <- nrow(affected_councils)

# Count number of affected provinces
num_affected_provinces <- affected_councils %>%
  select(Province) %>%
  distinct() %>%
  nrow()

# Create council-level resources (for councils that are affected)
resources_needed_councils <- affected_councils %>%
  rename(Region = `Area Council`) %>%
  rowwise() %>%
  mutate(
    # Resources = multiplier * 1 (per affected council)
    truck = round(1 * get_logistics_resource_multiplier("Truck", Region), 0),
    fibreglass_boat = round(10 * get_logistics_resource_multiplier("Fibreglass Boat", Region), 0),
    fuel = round(100 * get_logistics_resource_multiplier("Fuel", Region), 0),
    chainsaw = round(2 * get_logistics_resource_multiplier("Chainsaw", Region), 0),
    ship = 0  # Ships only at province/national level
  ) %>%
  ungroup() %>%
  select(Region, truck, fibreglass_boat, ship, fuel, chainsaw)

# Aggregate to province level
province_resources <- resources_needed_councils %>%
  left_join(council_province_lookup, by = c("Region" = "Council")) %>%
  group_by(Province) %>%
  summarise(
    truck = sum(truck, na.rm = TRUE),
    fibreglass_boat = sum(fibreglass_boat, na.rm = TRUE),
    ship = 1,  # 1 ship per affected province
    fuel = sum(fuel, na.rm = TRUE),
    chainsaw = sum(chainsaw, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  rename(Region = Province)

# National level - sum of provinces
national_resources <- province_resources %>%
  summarise(
    truck = sum(truck, na.rm = TRUE),
    fibreglass_boat = sum(fibreglass_boat, na.rm = TRUE),
    ship = sum(ship, na.rm = TRUE),
    fuel = sum(fuel, na.rm = TRUE),
    chainsaw = sum(chainsaw, na.rm = TRUE)
  ) %>%
  mutate(Region = "National")

# Combine all levels
resources_needed_full <- bind_rows(
  national_resources,
  province_resources,
  resources_needed_councils
) %>%
  mutate(default_order = match(Region, region_order)) %>%
  arrange(default_order) %>%
  select(-default_order)

# === EXPORT TO CSV ===
write.csv(
  resources_needed_full,
  here::here("output", "Logistics_03_resources_needed.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
# Format for display
formatted <- format_table(resources_needed_full)

# Create the resources needed reactable
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    truck = colDef(
      name = "Truck",
      format = colFormat(digits = 0)
    ),
    fibreglass_boat = colDef(
      name = "Fibreglass Boat",
      format = colFormat(digits = 0)
    ),
    ship = colDef(
      name = "Ship",
      format = colFormat(digits = 0)
    ),
    fuel = colDef(
      name = "Fuel (litres)",
      format = colFormat(digits = 0)
    ),
    chainsaw = colDef(
      name = "Chainsaw",
      format = colFormat(digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Resources Needed", columns = c("truck", "fibreglass_boat", "ship", "fuel", "chainsaw"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

9.4 Estimated Financial Damage

9.4.1 Table 1: Infrastructure Financial Damage

Show code
# === DATA WRANGLING ===

# Helper function to get unit costs for infrastructure
get_infrastructure_unit_cost <- function(infrastructure_type, area_council) {
  unit_cost <- financial_config %>%
    filter(
      Cluster == "Logistics",
      Indicator == "Infrastructure",
      tolower(trimws(Attribute)) == tolower(trimws(infrastructure_type)),
      `Area Council` == area_council
    ) %>%
    pull(Value)
  
  return(ifelse(length(unit_cost) > 0, unit_cost, 0))
}

# Use the raw damage estimates (before rounding) for financial calculations
infrastructure_financial <- infrastructure_damage_raw %>%
  rowwise() %>%
  mutate(
    # Financial damage for each infrastructure type (in VT)
    airport_financial = infra_airport_damaged * get_infrastructure_unit_cost("airport", Region),
    wharf_financial = infra_wharf_damaged * get_infrastructure_unit_cost("wharf", Region),
    permanent_bridge_financial = `infra_permanent bridge_damaged` * get_infrastructure_unit_cost("permanent bridge", Region),
    temporary_bridge_financial = `infra_temporary bridge_damaged` * get_infrastructure_unit_cost("temporary bridge", Region),
    police_stations_financial = `infra_police stations_damaged` * get_infrastructure_unit_cost("police stations", Region),
    fire_hydrants_financial = `infra_fire hydrants_damaged` * get_infrastructure_unit_cost("fire hydrants", Region),
    main_water_valves_financial = `infra_main water valves_damaged` * get_infrastructure_unit_cost("main water valves", Region),
    
    # Total infrastructure financial damage
    total_infrastructure_financial = airport_financial + wharf_financial + permanent_bridge_financial +
                                      temporary_bridge_financial + police_stations_financial + 
                                      fire_hydrants_financial + main_water_valves_financial
  ) %>%
  ungroup() %>%
  select(Region, total_infrastructure_financial, airport_financial, wharf_financial, 
         permanent_bridge_financial, temporary_bridge_financial, police_stations_financial,
         fire_hydrants_financial, main_water_valves_financial)

# Aggregate to province and national levels
infrastructure_financial_full <- compute_council_aggregates(infrastructure_financial)

# Reorder columns
infrastructure_financial_ordered <- infrastructure_financial_full %>%
  select(Region, total_infrastructure_financial, airport_financial, wharf_financial,
         permanent_bridge_financial, temporary_bridge_financial, police_stations_financial,
         fire_hydrants_financial, main_water_valves_financial)

# === EXPORT TABLE 1 TO CSV ===
write.csv(
  infrastructure_financial_ordered,
  here::here("output", "Logistics_04a_financial_damage_infrastructure.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 1 ===
formatted1 <- format_table(infrastructure_financial_ordered)

reactable(
  formatted1,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    total_infrastructure_financial = colDef(
      name = "Total Value",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    airport_financial = colDef(
      name = "Airport",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    wharf_financial = colDef(
      name = "Wharf",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    permanent_bridge_financial = colDef(
      name = "Permanent Bridge",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    temporary_bridge_financial = colDef(
      name = "Temporary Bridge",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    police_stations_financial = colDef(
      name = "Police Stations",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    fire_hydrants_financial = colDef(
      name = "Fire Hydrants",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    main_water_valves_financial = colDef(
      name = "Main Water Valves",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Infrastructure", columns = c("total_infrastructure_financial", "airport_financial", 
                                                    "wharf_financial", "permanent_bridge_financial",
                                                    "temporary_bridge_financial", "police_stations_financial",
                                                    "fire_hydrants_financial", "main_water_valves_financial"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

9.4.2 Map: Logistics Infrastructure Financial Damage by Area Council

Show map code
# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare logistics infrastructure financial data for mapping
logistics_infra_financial_map_data <- infrastructure_financial_full %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with financial damage data
logistics_infra_financial_map <- councils_sf %>%
  left_join(logistics_infra_financial_map_data, by = c("acname" = "Region"))

# Create color palette
pal_logistics_infra_financial <- colorNumeric(
  palette = "YlOrRd",
  domain = logistics_infra_financial_map$total_infrastructure_financial,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(logistics_infra_financial_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_logistics_infra_financial(total_infrastructure_financial),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Financial Damage:</strong> ", format(round(total_infrastructure_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>By Infrastructure Type</strong><br>",
      "Airport: ", format(round(airport_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Wharf: ", format(round(wharf_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Permanent Bridge: ", format(round(permanent_bridge_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Temporary Bridge: ", format(round(temporary_bridge_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Police Stations: ", format(round(police_stations_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Fire Hydrants: ", format(round(fire_hydrants_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Main Water Valves: ", format(round(main_water_valves_financial, 0), big.mark = ",", scientific = FALSE), " VT"
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "320px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_logistics_infra_financial,
    values = ~total_infrastructure_financial,
    title = "Financial<br>Damage (VT)",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 0)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays estimated financial damage to logistics infrastructure by Area Council. Hover over each council to view the breakdown by infrastructure type (Airport, Wharf, Bridges, Police Stations, Fire Hydrants, Main Water Valves). Councils shown in grey have no damage data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

9.4.3 Table 2: Road Surface Financial Damage

Show code
# Helper function to get unit costs for road surface
get_road_unit_cost <- function(road_type, area_council) {
  unit_cost <- financial_config %>%
    filter(
      Cluster == "Logistics",
      Indicator == "Road Surface",
      tolower(trimws(Attribute)) == tolower(trimws(road_type)),
      `Area Council` == area_council
    ) %>%
    pull(Value)
  
  return(ifelse(length(unit_cost) > 0, unit_cost, 0))
}

# Use the raw damage estimates (before rounding) for financial calculations
road_financial <- road_damage_raw %>%
  rowwise() %>%
  mutate(
    # Financial damage for each road surface type (in VT)
    asphalt_financial = road_asphalt_damaged * get_road_unit_cost("asphalt", Region),
    chips_seal_financial = `road_chips seal_damaged` * get_road_unit_cost("chips seal", Region),
    concrete_financial = road_concrete_damaged * get_road_unit_cost("concrete", Region),
    earth_financial = road_earth_damaged * get_road_unit_cost("earth", Region),
    gravel_financial = road_gravel_damaged * get_road_unit_cost("gravel", Region),
    
    # Total road surface financial damage
    total_road_financial = asphalt_financial + chips_seal_financial + concrete_financial +
                            earth_financial + gravel_financial
  ) %>%
  ungroup() %>%
  select(Region, total_road_financial, asphalt_financial, chips_seal_financial,
         concrete_financial, earth_financial, gravel_financial)

# Aggregate to province and national levels
road_financial_full <- compute_council_aggregates(road_financial)

# Reorder columns
road_financial_ordered <- road_financial_full %>%
  select(Region, total_road_financial, asphalt_financial, chips_seal_financial,
         concrete_financial, earth_financial, gravel_financial)

# === EXPORT TABLE 2 TO CSV ===
write.csv(
  road_financial_ordered,
  here::here("output", "Logistics_04b_financial_damage_road_surface.csv"),
  row.names = FALSE
)

# === PRESENTATION TABLE 2 ===
formatted2 <- format_table(road_financial_ordered)

reactable(
  formatted2,
  columns = list(
    Region = colDef(
      name = "Region", 
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    total_road_financial = colDef(
      name = "Total Value",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    asphalt_financial = colDef(
      name = "Asphalt",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    chips_seal_financial = colDef(
      name = "Chips Seal",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    concrete_financial = colDef(
      name = "Concrete",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    earth_financial = colDef(
      name = "Earth",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    gravel_financial = colDef(
      name = "Gravel",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Road Surface", columns = c("total_road_financial", "asphalt_financial", 
                                                  "chips_seal_financial", "concrete_financial",
                                                  "earth_financial", "gravel_financial"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

9.4.4 Map: Road Surface Financial Damage by Area Council

Show map code
# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare road surface financial data for mapping
road_financial_map_data <- road_financial_full %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with financial damage data
road_financial_map <- councils_sf %>%
  left_join(road_financial_map_data, by = c("acname" = "Region"))

# Create color palette
pal_road_financial <- colorNumeric(
  palette = "YlOrRd",
  domain = road_financial_map$total_road_financial,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(road_financial_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_road_financial(total_road_financial),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Financial Damage:</strong> ", format(round(total_road_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>By Road Surface Type</strong><br>",
      "Asphalt: ", format(round(asphalt_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Chips Seal: ", format(round(chips_seal_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Concrete: ", format(round(concrete_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Earth: ", format(round(earth_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Gravel: ", format(round(gravel_financial, 0), big.mark = ",", scientific = FALSE), " VT"
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "300px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_road_financial,
    values = ~total_road_financial,
    title = "Financial<br>Damage (VT)",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 0)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays estimated financial damage to road surfaces by Area Council. Hover over each council to view the breakdown by road surface type (Asphalt, Chips Seal, Concrete, Earth, Gravel). Councils shown in grey have no damage data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

10 Shelter

10.1 Baseline: Number of Private Households by Roof and Wall Materials

Show code
# Filter for Shelter baseline at Area Council level
shelter_data <- full_data %>%
  filter(Baseline == "Shelter") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  mutate(Region = `Area Council`)

# Get total households
total_households <- shelter_data %>%
  filter(Indicator == "Household Type", Attribute == "number households") %>%
  select(Region, Value) %>%
  rename(total_households = Value)

# Get roof materials
roof_materials <- shelter_data %>%
  filter(Indicator == "Household Roof Material") %>%
  mutate(material = paste0("roof_", tolower(trimws(Attribute)))) %>%
  select(Region, material, Value) %>%
  pivot_wider(
    names_from = material,
    values_from = Value
  )

# Get wall materials
wall_materials <- shelter_data %>%
  filter(Indicator == "Household Wall Material") %>%
  mutate(material = paste0("wall_", tolower(trimws(Attribute)))) %>%
  select(Region, material, Value) %>%
  pivot_wider(
    names_from = material,
    values_from = Value
  )

# Combine all data
shelter_wide <- total_households %>%
  left_join(roof_materials, by = "Region") %>%
  left_join(wall_materials, by = "Region") %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Compute aggregates (province and national levels)
shelter_aggregated <- compute_council_aggregates(shelter_wide)

# === EXPORT TO CSV ===
write.csv(
  shelter_aggregated %>% select(Region, everything()),
  here::here("output", "Shelter_01_baseline.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
# Format for display
formatted <- format_table(shelter_aggregated)

# Create the reactable
reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region",   
      minWidth = 150,
      sortable = TRUE,
      defaultSortOrder = "asc",
      html = TRUE,
      sticky = "left"
    ),
    
    total_households = colDef(name = "Total Households", format = colFormat(digits = 0)),
    
    roof_concrete = colDef(name = "Concrete", format = colFormat(digits = 0)),
    roof_metal = colDef(name = "Metal", format = colFormat(digits = 0)),
    roof_wood = colDef(name = "Wood", format = colFormat(digits = 0)),
    
    wall_concrete = colDef(name = "Concrete", format = colFormat(digits = 0)),
    wall_metal = colDef(name = "Metal", format = colFormat(digits = 0)),
    wall_wood = colDef(name = "Wood", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Total Households", columns = c("total_households")),
    colGroup(name = "Main Roof Materials", columns = c("roof_concrete", "roof_metal", "roof_wood")),
    colGroup(name = "Main Wall Materials", columns = c("wall_concrete", "wall_metal", "wall_wood"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

10.2 Estimated Hazard Damage

Show code
# === DATA WRANGLING ===

# Load damage multiplier configuration
baseline_factors <- read.csv(
  here::here("data", "damage_multipliers.csv"),
  check.names = FALSE
)

# Filter for Shelter damage functions
shelter_damage_functions <- baseline_factors %>%
  filter(Cluster == "Shelter")

# Function to get damage rate for a given indicator, attribute, area council, and intensity
get_shelter_damage_rate <- function(indicator, attribute, area_council, intensity) {
  if (intensity == 0) return(0)
  
  intensity_col <- paste0("Intensity ", intensity)
  
  rate <- shelter_damage_functions %>%
    filter(
      Indicator == indicator,
      tolower(trimws(Attribute)) == tolower(trimws(attribute)),
      `Area Council` == area_council
    ) %>%
    pull(!!intensity_col)
  
  return(ifelse(length(rate) > 0, rate, 0))
}

# Get baseline shelter data at council level
shelter_baseline <- full_data %>%
  filter(Baseline == "Shelter") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  mutate(Region = `Area Council`)

# Get private households
private_households <- shelter_baseline %>%
  filter(Indicator == "Household Type", Attribute == "private households") %>%
  select(Region, Value) %>%
  rename(private_households = Value)

# Get roof materials
roof_materials <- shelter_baseline %>%
  filter(Indicator == "Household Roof Material") %>%
  mutate(material = paste0("roof_", tolower(trimws(Attribute)))) %>%
  select(Region, material, Value) %>%
  pivot_wider(
    names_from = material,
    values_from = Value
  )

# Get wall materials
wall_materials <- shelter_baseline %>%
  filter(Indicator == "Household Wall Material") %>%
  mutate(material = paste0("wall_", tolower(trimws(Attribute)))) %>%
  select(Region, material, Value) %>%
  pivot_wider(
    names_from = material,
    values_from = Value
  )

# Combine baseline data
shelter_baseline_wide <- private_households %>%
  left_join(roof_materials, by = "Region") %>%
  left_join(wall_materials, by = "Region") %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Join with hazard intensity from config
shelter_with_intensity <- shelter_baseline_wide %>%
  left_join(
    config %>% select(`Area Council`, Intensity),
    by = c("Region" = "Area Council")
  ) %>%
  mutate(Intensity = replace_na(Intensity, 0))

# Calculate damage for each household type/material
shelter_damage_raw <- shelter_with_intensity %>%
  rowwise() %>%
  mutate(
    # Private households damaged
    private_households_damaged = private_households * get_shelter_damage_rate("Household Type", "private households", Region, Intensity),
    
    # Roof materials damaged
    roof_concrete_damaged = roof_concrete * get_shelter_damage_rate("Household Roof Material", "concrete", Region, Intensity),
    roof_metal_damaged = roof_metal * get_shelter_damage_rate("Household Roof Material", "metal", Region, Intensity),
    roof_wood_damaged = roof_wood * get_shelter_damage_rate("Household Roof Material", "wood", Region, Intensity),
    
    # Wall materials damaged
    wall_concrete_damaged = wall_concrete * get_shelter_damage_rate("Household Wall Material", "concrete", Region, Intensity),
    wall_metal_damaged = wall_metal * get_shelter_damage_rate("Household Wall Material", "metal", Region, Intensity),
    wall_wood_damaged = wall_wood * get_shelter_damage_rate("Household Wall Material", "wood", Region, Intensity)
  ) %>%
  ungroup()

# Round for presentation
shelter_damage_rounded <- shelter_damage_raw %>%
  filter(Intensity > 0) %>%  
  select(Region, ends_with("_damaged")) %>%
  mutate(across(where(is.numeric), ~round(.x, 0)))

# Aggregate to province and national levels
shelter_damage_aggregated <- compute_council_aggregates(shelter_damage_rounded)

# Reorder columns
shelter_damage_ordered <- shelter_damage_aggregated %>%
  mutate(
    total_roof_damaged = roof_concrete_damaged + roof_metal_damaged + roof_wood_damaged,
    total_wall_damaged = wall_concrete_damaged + wall_metal_damaged + wall_wood_damaged
  ) %>%
  select(Region, private_households_damaged,
         total_roof_damaged, roof_concrete_damaged, roof_metal_damaged, roof_wood_damaged,
         total_wall_damaged, wall_concrete_damaged, wall_metal_damaged, wall_wood_damaged)

# === EXPORT TO CSV ===
write.csv(
  shelter_damage_ordered,
  here::here("output", "Shelter_02_estimated_damage.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
formatted <- format_table(shelter_damage_ordered)

reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region",       
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    private_households_damaged = colDef(name = "Total Private Households", format = colFormat(digits = 0)),
    
    total_roof_damaged = colDef(name = "Total", format = colFormat(digits = 0)),
    roof_concrete_damaged = colDef(name = "Concrete", format = colFormat(digits = 0)),
    roof_metal_damaged = colDef(name = "Metal", format = colFormat(digits = 0)),
    roof_wood_damaged = colDef(name = "Wood", format = colFormat(digits = 0)),
    
    total_wall_damaged = colDef(name = "Total", format = colFormat(digits = 0)),
    wall_concrete_damaged = colDef(name = "Concrete", format = colFormat(digits = 0)),
    wall_metal_damaged = colDef(name = "Metal", format = colFormat(digits = 0)),
    wall_wood_damaged = colDef(name = "Wood", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Number of Households", columns = c("private_households_damaged")),
    colGroup(name = "Main Roof Materials Damaged", columns = c("total_roof_damaged", "roof_concrete_damaged", "roof_metal_damaged", "roof_wood_damaged")),
    colGroup(name = "Main Wall Materials Damaged", columns = c("total_wall_damaged", "wall_concrete_damaged", "wall_metal_damaged", "wall_wood_damaged"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

10.3 Immediate Response Resources

Show code
# === DATA WRANGLING ===

# Load resources configuration
resource_config <- read.csv(
  here::here("data", "response_resources.csv"),
  check.names = FALSE
)

# Filter for Shelter resources
shelter_resources <- resource_config %>%
  filter(Cluster == "Shelter")

# Function to get resource quantity per household
get_shelter_resource_quantity <- function(resource_type, area_council) {
  quantity <- shelter_resources %>%
    filter(
      tolower(trimws(Indicator)) == tolower(trimws(resource_type)),
      `Area Council` == area_council
    ) %>%
    pull(Value)
  
  return(ifelse(length(quantity) > 0, quantity, 0))
}

# Use the damaged households from previous section
shelter_resources_calc <- shelter_damage_raw %>%
  filter(Intensity > 0) %>%  # <-- ADD THIS LINE
  select(Region, private_households_damaged) %>%
  rowwise() %>%
  mutate(
    # Calculate resource needs based on damaged households
    tent = private_households_damaged * get_shelter_resource_quantity("tent", Region),
    solar_lamp = private_households_damaged * get_shelter_resource_quantity("solar lamp", Region),
    kitchen_set = private_households_damaged * get_shelter_resource_quantity("kitchen set", Region),
    jerrycan_10l = private_households_damaged * get_shelter_resource_quantity("jerrycan 10l", Region),
    sleeping_mat = private_households_damaged * get_shelter_resource_quantity("sleeping mat", Region),
    blanket = private_households_damaged * get_shelter_resource_quantity("blanket", Region)
  ) %>%
  ungroup()

# Round for presentation
shelter_resources_rounded <- shelter_resources_calc %>%
  mutate(across(where(is.numeric), ~round(.x, 0)))

# Aggregate to province and national levels
shelter_resources_aggregated <- compute_council_aggregates(shelter_resources_rounded)

# Reorder columns
shelter_resources_ordered <- shelter_resources_aggregated %>%
  select(Region, private_households_damaged, tent, solar_lamp, kitchen_set, 
         jerrycan_10l, sleeping_mat, blanket)

# === EXPORT TO CSV ===
write.csv(
  shelter_resources_ordered,
  here::here("output", "Shelter_03_resources.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
formatted <- format_table(shelter_resources_ordered)

reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region",   
      minWidth = 150,
      sortable = TRUE,
      defaultSortOrder = "asc",
      html = TRUE,
      sticky = "left"
    ),
    
    private_households_damaged = colDef(name = "Number of Households", format = colFormat(digits = 0)),
    tent = colDef(name = "Tent", format = colFormat(digits = 0)),
    solar_lamp = colDef(name = "Solar Lamp", format = colFormat(digits = 0)),
    kitchen_set = colDef(name = "Kitchen Set", format = colFormat(digits = 0)),
    jerrycan_10l = colDef(name = "Jerrycan 10L", format = colFormat(digits = 0)),
    sleeping_mat = colDef(name = "Sleeping Mat", format = colFormat(digits = 0)),
    blanket = colDef(name = "Blanket", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Number of Households", columns = c("private_households_damaged")),
    colGroup(name = "Resources", columns = c("tent", "solar_lamp", "kitchen_set", 
                                               "jerrycan_10l", "sleeping_mat", "blanket"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

10.4 Estimated Financial Damage

Show code
# === DATA WRANGLING ===

# Load financial configuration
financial_config <- read.csv(
  here::here("data", "unit_costs.csv"),
  check.names = FALSE
)

# Filter for Shelter financial costs
shelter_financial <- financial_config %>%
  filter(Cluster == "Shelter")

# Function to get unit cost for shelter items
get_shelter_unit_cost <- function(indicator, attribute, area_council) {
  unit_cost <- shelter_financial %>%
    filter(
      Indicator == indicator,
      tolower(trimws(Attribute)) == tolower(trimws(attribute)),
      `Area Council` == area_council
    ) %>%
    pull(Value)
  
  return(ifelse(length(unit_cost) > 0, unit_cost, 0))
}

# Get baseline appliance data at council level
appliance_baseline <- full_data %>%
  filter(Baseline == "Shelter", Indicator == "Household Appliances") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Attribute, Value) %>%
  pivot_wider(
    names_from = Attribute,
    values_from = Value,
    names_prefix = "appliance_"
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Get baseline private households (needed for damage rate calculation)
baseline_private_households <- full_data %>%
  filter(Baseline == "Shelter", Indicator == "Household Type", Attribute == "private households") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Value) %>%
  rename(private_households_baseline = Value)

# Use the raw damage estimates (before rounding) for financial calculations
shelter_financial_calc <- shelter_damage_raw %>%
  filter(Intensity > 0) %>%  # <-- ADD THIS LIN
  left_join(appliance_baseline, by = "Region") %>%
  left_join(baseline_private_households, by = "Region") %>%
  rowwise() %>%
  mutate(
    # Financial damage for roof materials
    roof_concrete_financial = roof_concrete_damaged * get_shelter_unit_cost("Household Roof Material", "concrete", Region),
    roof_metal_financial = roof_metal_damaged * get_shelter_unit_cost("Household Roof Material", "metal", Region),
    roof_wood_financial = roof_wood_damaged * get_shelter_unit_cost("Household Roof Material", "wood", Region),
    
    # Financial damage for wall materials
    wall_concrete_financial = wall_concrete_damaged * get_shelter_unit_cost("Household Wall Material", "concrete", Region),
    wall_metal_financial = wall_metal_damaged * get_shelter_unit_cost("Household Wall Material", "metal", Region),
    wall_wood_financial = wall_wood_damaged * get_shelter_unit_cost("Household Wall Material", "wood", Region),
    
    # Calculate damaged appliances based on household damage rate
    damage_rate = ifelse(private_households_baseline > 0, private_households_damaged / private_households_baseline, 0),
    freezer_damaged = ifelse(!is.na(appliance_freezer), appliance_freezer * damage_rate, 0),
    refrigerator_damaged = ifelse(!is.na(appliance_refrigerator), appliance_refrigerator * damage_rate, 0),
    tv_damaged = ifelse(!is.na(appliance_tv), appliance_tv * damage_rate, 0),
    
    # Calculate financial damage for appliances
    freezer_financial = freezer_damaged * get_shelter_unit_cost("Household Appliances", "freezer", Region),
    refrigerator_financial = refrigerator_damaged * get_shelter_unit_cost("Household Appliances", "refridgerator", Region),
    tv_financial = tv_damaged * get_shelter_unit_cost("Household Appliances", "tv", Region),
    
    # Calculate total household financial damage (sum of all components)
    private_households_financial = roof_concrete_financial + roof_metal_financial + roof_wood_financial +
                                   wall_concrete_financial + wall_metal_financial + wall_wood_financial +
                                   freezer_financial + refrigerator_financial + tv_financial,
    
    # Total financial damage (same as private households since that's the sum of all components)
    total_financial = private_households_financial
  ) %>%
  ungroup() %>%
  select(Region, total_financial, private_households_financial,
         roof_concrete_financial, roof_metal_financial, roof_wood_financial,
         wall_concrete_financial, wall_metal_financial, wall_wood_financial,
         freezer_financial, refrigerator_financial, tv_financial)

# Aggregate to province and national levels
shelter_financial_aggregated <- compute_council_aggregates(shelter_financial_calc)

# Reorder columns
shelter_financial_ordered <- shelter_financial_aggregated %>%
  select(Region, total_financial, private_households_financial,
         roof_concrete_financial, roof_metal_financial, roof_wood_financial,
         wall_concrete_financial, wall_metal_financial, wall_wood_financial,
         freezer_financial, refrigerator_financial, tv_financial)

# === EXPORT TO CSV ===
write.csv(
  shelter_financial_ordered,
  here::here("output", "Shelter_04_financial_damage.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
formatted <- format_table(shelter_financial_ordered)

reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region",   
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    total_financial = colDef(
      name = "Total Value",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    private_households_financial = colDef(
      name = "Total Private Households",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    
    roof_concrete_financial = colDef(
      name = "Concrete",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    roof_metal_financial = colDef(
      name = "Metal",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    roof_wood_financial = colDef(
      name = "Wood",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    
    wall_concrete_financial = colDef(
      name = "Concrete",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    wall_metal_financial = colDef(
      name = "Metal",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    wall_wood_financial = colDef(
      name = "Wood",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    
    freezer_financial = colDef(
      name = "Freezer",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    refrigerator_financial = colDef(
      name = "Refrigerator",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    tv_financial = colDef(
      name = "TV",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Total Value", columns = c("total_financial")),
    colGroup(name = "Total Private Households", columns = c("private_households_financial")),
    colGroup(name = "Main Roof Materials", columns = c("roof_concrete_financial", "roof_metal_financial", "roof_wood_financial")),
    colGroup(name = "Main Wall Materials", columns = c("wall_concrete_financial", "wall_metal_financial", "wall_wood_financial")),
    colGroup(name = "Household Appliances", columns = c("freezer_financial", "refrigerator_financial", "tv_financial"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

10.4.1 Map: Shelter Financial Damage by Area Council

Show map code
# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare shelter financial data for mapping
shelter_financial_map_data <- shelter_financial_ordered %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with financial damage data
shelter_financial_map <- councils_sf %>%
  left_join(shelter_financial_map_data, by = c("acname" = "Region"))

# Create color palette
pal_shelter_financial <- colorNumeric(
  palette = "YlOrRd",
  domain = shelter_financial_map$total_financial,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(shelter_financial_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_shelter_financial(total_financial),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Financial Damage:</strong> ", format(round(total_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>Main Roof Materials</strong><br>",
      "Concrete: ", format(round(roof_concrete_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Metal: ", format(round(roof_metal_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Wood: ", format(round(roof_wood_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>Main Wall Materials</strong><br>",
      "Concrete: ", format(round(wall_concrete_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Metal: ", format(round(wall_metal_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Wood: ", format(round(wall_wood_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>Household Appliances</strong><br>",
      "Freezer: ", format(round(freezer_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Refrigerator: ", format(round(refrigerator_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "TV: ", format(round(tv_financial, 0), big.mark = ",", scientific = FALSE), " VT"
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "320px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_shelter_financial,
    values = ~total_financial,
    title = "Financial<br>Damage (VT)",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 0)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays estimated financial damage to shelter by Area Council. Hover over each council to view the breakdown by roof materials, wall materials, and household appliances. Councils shown in grey have no damage data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

11 WASH

11.1 Baseline: Household Drinking Water Sources and Toilet Types

Show code
# === DATA WRANGLING ===

# Filter for WASH baseline at Area Council level
wash_data <- full_data %>%
  filter(Baseline == "WASH") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  mutate(Region = `Area Council`)

# Get total households from Shelter baseline (since WASH doesn't have it)
total_households <- full_data %>%
  filter(Baseline == "Shelter", Indicator == "Household Type", Attribute == "number households") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  mutate(Region = `Area Council`) %>%
  select(Region, Value) %>%
  rename(total_households = Value)

# Get drinking water sources
drinking_water <- wash_data %>%
  filter(Indicator == "Household Drinking Water") %>%
  mutate(water_type = paste0("water_", tolower(trimws(Attribute)))) %>%
  select(Region, water_type, Value) %>%
  pivot_wider(
    names_from = water_type,
    values_from = Value
  )

# Get toilet types
toilet_type <- wash_data %>%
  filter(Indicator == "Household Toilet") %>%
  mutate(toilet_type = paste0("toilet_", tolower(trimws(Attribute)))) %>%
  select(Region, toilet_type, Value) %>%
  pivot_wider(
    names_from = toilet_type,
    values_from = Value
  )

# Combine all data
wash_wide <- total_households %>%
  left_join(drinking_water, by = "Region") %>%
  left_join(toilet_type, by = "Region") %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Compute aggregates (province and national levels)
wash_aggregated <- compute_council_aggregates(wash_wide)

# Add total columns
wash_aggregated <- wash_aggregated %>%
  mutate(
    total_water = water_piped + water_well + water_tank,
    total_toilet = `toilet_pit latrine` + toilet_vip + toilet_flush + `toilet_water sealed`
  ) %>%
  select(Region, total_households,
         total_water, water_piped, water_well, water_tank,
         total_toilet, `toilet_pit latrine`, toilet_vip, toilet_flush, `toilet_water sealed`)

# === EXPORT TO CSV ===
write.csv(
  wash_aggregated %>% select(Region, everything()),
  here::here("output", "WASH_01_baseline.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
formatted <- format_table(wash_aggregated)

reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region",       
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    total_households = colDef(name = "", format = colFormat(digits = 0)),
    
    total_water = colDef(name = "Total", format = colFormat(digits = 0)),
    water_piped = colDef(name = "Piped", format = colFormat(digits = 0)),
    water_well = colDef(name = "Well", format = colFormat(digits = 0)),
    water_tank = colDef(name = "Tank", format = colFormat(digits = 0)),
    
    total_toilet = colDef(name = "Total", format = colFormat(digits = 0)),
    `toilet_pit latrine` = colDef(name = "Pit Latrine", format = colFormat(digits = 0)),
    toilet_vip = colDef(name = "VIP", format = colFormat(digits = 0)),
    toilet_flush = colDef(name = "Flush", format = colFormat(digits = 0)),
    `toilet_water sealed` = colDef(name = "Water Sealed", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Total Households", columns = c("total_households")),
    colGroup(name = "Drinking Water", columns = c("total_water", "water_piped", "water_well", "water_tank")),
    colGroup(name = "Toilet Type", columns = c("total_toilet", "toilet_pit latrine", "toilet_vip", "toilet_flush", "toilet_water sealed"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

11.2 Estimated Hazard Damage

Show code
# === DATA WRANGLING ===

# Filter for WASH damage functions
wash_damage_functions <- baseline_factors %>%
  filter(Cluster == "WASH")

# Function to get damage rate for WASH items
get_wash_damage_rate <- function(indicator, attribute, area_council, intensity) {
  if (intensity == 0) return(0)
  
  intensity_col <- paste0("Intensity ", intensity)
  
  rate <- wash_damage_functions %>%
    filter(
      Indicator == indicator,
      tolower(trimws(Attribute)) == tolower(trimws(attribute)),
      `Area Council` == area_council
    ) %>%
    pull(!!intensity_col)
  
  return(ifelse(length(rate) > 0, rate, 0))
}

# Get baseline WASH data at council level
wash_baseline <- wash_wide

# Join with hazard intensity from config
wash_with_intensity <- wash_baseline %>%
  left_join(
    config %>% select(`Area Council`, Intensity),
    by = c("Region" = "Area Council")
  ) %>%
  mutate(Intensity = replace_na(Intensity, 0))

# Calculate damage for each WASH type
wash_damage_raw <- wash_with_intensity %>%
  rowwise() %>%
  mutate(
    # Drinking water damaged
    water_piped_damaged = water_piped * get_wash_damage_rate("Household Drinking Water", "piped", Region, Intensity),
    water_well_damaged = water_well * get_wash_damage_rate("Household Drinking Water", "well", Region, Intensity),
    water_tank_damaged = water_tank * get_wash_damage_rate("Household Drinking Water", "tank", Region, Intensity),
    
    # Toilet types damaged
    toilet_pit_latrine_damaged = `toilet_pit latrine` * get_wash_damage_rate("Household Toilet", "pit latrine", Region, Intensity),
    toilet_vip_damaged = toilet_vip * get_wash_damage_rate("Household Toilet", "vip", Region, Intensity),
    toilet_flush_damaged = toilet_flush * get_wash_damage_rate("Household Toilet", "flush", Region, Intensity),
    toilet_water_sealed_damaged = `toilet_water sealed` * get_wash_damage_rate("Household Toilet", "water sealed", Region, Intensity)
  ) %>%
  ungroup()

# Round for presentation
wash_damage_rounded <- wash_damage_raw %>%
  filter(Intensity > 0) %>%  # <-- ADD THIS LINE
  select(Region, ends_with("_damaged")) %>%
  mutate(across(where(is.numeric), ~round(.x, 0)))

# Aggregate to province and national levels
wash_damage_aggregated <- compute_council_aggregates(wash_damage_rounded)

# Reorder columns
wash_damage_ordered <- wash_damage_aggregated %>%
  mutate(
    total_water_damaged = water_piped_damaged + water_well_damaged + water_tank_damaged,
    total_toilet_damaged = toilet_pit_latrine_damaged + toilet_vip_damaged + toilet_flush_damaged + toilet_water_sealed_damaged
  ) %>%
  select(Region,
         total_water_damaged, water_piped_damaged, water_well_damaged, water_tank_damaged,
         total_toilet_damaged, toilet_pit_latrine_damaged, toilet_vip_damaged, toilet_flush_damaged, toilet_water_sealed_damaged)

# === EXPORT TO CSV ===
write.csv(
  wash_damage_ordered,
  here::here("output", "WASH_02_estimated_damage.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
formatted <- format_table(wash_damage_ordered)

reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region",       
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    total_water_damaged = colDef(name = "Total", format = colFormat(digits = 0)),
    water_piped_damaged = colDef(name = "Piped", format = colFormat(digits = 0)),
    water_well_damaged = colDef(name = "Well", format = colFormat(digits = 0)),
    water_tank_damaged = colDef(name = "Tank", format = colFormat(digits = 0)),
    
    total_toilet_damaged = colDef(name = "Total", format = colFormat(digits = 0)),
    toilet_pit_latrine_damaged = colDef(name = "Pit Latrine", format = colFormat(digits = 0)),
    toilet_vip_damaged = colDef(name = "VIP", format = colFormat(digits = 0)),
    toilet_flush_damaged = colDef(name = "Flush", format = colFormat(digits = 0)),
    toilet_water_sealed_damaged = colDef(name = "Water Sealed", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Drinking Water Damaged", columns = c("total_water_damaged", "water_piped_damaged", "water_well_damaged", "water_tank_damaged")),
    colGroup(name = "Toilet Type Damaged", columns = c("total_toilet_damaged", "toilet_pit_latrine_damaged", "toilet_vip_damaged", "toilet_flush_damaged", "toilet_water_sealed_damaged"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

11.3 Immediate Response Resources

Show code
# === DATA WRANGLING ===

# Filter for WASH resources
wash_resources <- resource_config %>%
  filter(Cluster == "WASH")

# Function to get resource quantity
get_wash_resource_quantity <- function(resource_type, area_council) {
  quantity <- wash_resources %>%
    filter(
      tolower(trimws(Indicator)) == tolower(trimws(resource_type)),
      `Area Council` == area_council
    ) %>%
    pull(Value)
  
  return(ifelse(length(quantity) > 0, quantity, 0))
}

# Calculate resource needs based on damaged facilities
wash_resources_calc <- wash_damage_raw %>%
  filter(Intensity > 0) %>%  # <-- ADD THIS LINE
  select(Region, water_piped_damaged, water_well_damaged, water_tank_damaged,
         toilet_pit_latrine_damaged) %>%
  rowwise() %>%
  mutate(
    # Keep damaged facility counts (these become the columns)
    piped = water_piped_damaged,
    well = water_well_damaged,
    tank = water_tank_damaged,
    pit_latrine = toilet_pit_latrine_damaged
  ) %>%
  ungroup() %>%
  select(Region, piped, well, tank, pit_latrine)

# Round for presentation
wash_resources_rounded <- wash_resources_calc %>%
  mutate(across(where(is.numeric), ~round(.x, 0)))

# Aggregate to province and national levels
wash_resources_aggregated <- compute_council_aggregates(wash_resources_rounded)

# === EXPORT TO CSV ===
write.csv(
  wash_resources_aggregated,
  here::here("output", "WASH_03_immediate_response_resources.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
formatted <- format_table(wash_resources_aggregated)

reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region",   
      minWidth = 150,
      sortable = TRUE,
      defaultSortOrder = "asc",
      html = TRUE,
      sticky = "left"
    ),
    
    piped = colDef(name = "Piped", format = colFormat(digits = 0)),
    well = colDef(name = "Well", format = colFormat(digits = 0)),
    tank = colDef(name = "Tank", format = colFormat(digits = 0)),
    pit_latrine = colDef(name = "Pit Latrine", format = colFormat(digits = 0))
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Water Tank", columns = c("piped")),
    colGroup(name = "Water Purifier Tablets", columns = c("well")),
    colGroup(name = "Hygiene Kit", columns = c("tank")),
    colGroup(name = "Pit Latrine", columns = c("pit_latrine"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

11.4 Estimated Financial Damage

Show code
# === DATA WRANGLING ===

# Filter for WASH financial costs
wash_financial <- financial_config %>%
  filter(Cluster == "WASH")

# Function to get unit cost for WASH items
get_wash_unit_cost <- function(indicator, attribute, area_council) {
  unit_cost <- wash_financial %>%
    filter(
      Indicator == indicator,
      tolower(trimws(Attribute)) == tolower(trimws(attribute)),
      `Area Council` == area_council
    ) %>%
    pull(Value)
  
  return(ifelse(length(unit_cost) > 0, unit_cost, 0))
}

# Use the raw damage estimates (before rounding) for financial calculations
wash_financial_calc <- wash_damage_raw %>%
  filter(Intensity > 0) %>%  # <-- ADD THIS LINE
  rowwise() %>%
  mutate(
    # Financial damage for drinking water (in VT)
    water_piped_financial = water_piped_damaged * get_wash_unit_cost("Household Drinking Water", "piped", Region),
    water_well_financial = water_well_damaged * get_wash_unit_cost("Household Drinking Water", "well", Region),
    water_tank_financial = water_tank_damaged * get_wash_unit_cost("Household Drinking Water", "tank", Region),
    
    # Financial damage for toilet types
    toilet_pit_latrine_financial = toilet_pit_latrine_damaged * get_wash_unit_cost("Household Toilet", "pit latrine", Region),
    toilet_vip_financial = toilet_vip_damaged * get_wash_unit_cost("Household Toilet", "vip", Region),
    toilet_flush_financial = toilet_flush_damaged * get_wash_unit_cost("Household Toilet", "flush", Region),
    toilet_water_sealed_financial = toilet_water_sealed_damaged * get_wash_unit_cost("Household Toilet", "water sealed", Region),
    
    # Total financial damage
    total_financial = water_piped_financial + water_well_financial + water_tank_financial +
                     toilet_pit_latrine_financial + toilet_vip_financial + toilet_flush_financial + toilet_water_sealed_financial
  ) %>%
  ungroup() %>%
  select(Region, total_financial, water_piped_financial, water_well_financial, water_tank_financial,
         toilet_pit_latrine_financial, toilet_vip_financial, toilet_flush_financial, toilet_water_sealed_financial)

# Aggregate to province and national levels
wash_financial_aggregated <- compute_council_aggregates(wash_financial_calc)

# Reorder columns
wash_financial_ordered <- wash_financial_aggregated %>%
  select(Region, total_financial, water_piped_financial, water_well_financial, water_tank_financial,
         toilet_pit_latrine_financial, toilet_vip_financial, toilet_flush_financial, toilet_water_sealed_financial)

# === EXPORT TO CSV ===
write.csv(
  wash_financial_ordered,
  here::here("output", "WASH_04_financial_damage.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
formatted <- format_table(wash_financial_ordered)

reactable(
  formatted,
  columns = list(
    Region = colDef(
      name = "Region",   
      minWidth = 150,
      sticky = "left",
      html = TRUE
    ),
    
    total_financial = colDef(
      name = "Total Value",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    
    water_piped_financial = colDef(
      name = "Piped",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    water_well_financial = colDef(
      name = "Well",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    water_tank_financial = colDef(
      name = "Tank",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    
    toilet_pit_latrine_financial = colDef(
      name = "Pit Latrine",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    toilet_vip_financial = colDef(
      name = "VIP",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    toilet_flush_financial = colDef(
      name = "Flush",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    ),
    toilet_water_sealed_financial = colDef(
      name = "Water Sealed",
      format = colFormat(suffix = " VT", separators = TRUE, digits = 0)
    )
  ),
  columnGroups = list(
    colGroup(name = "Region", columns = c("Region")),
    colGroup(name = "Total Value", columns = c("total_financial")),
    colGroup(name = "Drinking Water", columns = c("water_piped_financial", "water_well_financial", "water_tank_financial")),
    colGroup(name = "Toilet Type", columns = c("toilet_pit_latrine_financial", "toilet_vip_financial", "toilet_flush_financial", "toilet_water_sealed_financial"))
  ),
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

11.4.1 Map: WASH Financial Damage by Area Council

Show map code
# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare WASH financial data for mapping
wash_financial_map_data <- wash_financial_ordered %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with financial damage data
wash_financial_map <- councils_sf %>%
  left_join(wash_financial_map_data, by = c("acname" = "Region"))

# Create color palette
pal_wash_financial <- colorNumeric(
  palette = "YlOrRd",
  domain = wash_financial_map$total_financial,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(wash_financial_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_wash_financial(total_financial),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Financial Damage:</strong> ", format(round(total_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>Drinking Water</strong><br>",
      "Piped: ", format(round(water_piped_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Well: ", format(round(water_well_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Tank: ", format(round(water_tank_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "<br>",
      "<strong>Toilet Type</strong><br>",
      "Pit Latrine: ", format(round(toilet_pit_latrine_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "VIP: ", format(round(toilet_vip_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Flush: ", format(round(toilet_flush_financial, 0), big.mark = ",", scientific = FALSE), " VT<br>",
      "Water Sealed: ", format(round(toilet_water_sealed_financial, 0), big.mark = ",", scientific = FALSE), " VT"
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "300px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_wash_financial,
    values = ~total_financial,
    title = "Financial<br>Damage (VT)",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 0)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays estimated financial damage to WASH infrastructure by Area Council. Hover over each council to view the breakdown by drinking water source (Piped, Well, Tank) and toilet type (Pit Latrine, VIP, Flush, Water Sealed). Councils shown in grey have no damage data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

12 Business

12.1 Baseline

Show code
# === DATA WRANGLING ===

# Filter for Business baseline at Area Council level
business_data <- full_data %>%
  filter(Baseline == "Business") %>%
  filter(Year == max(Year, na.rm = TRUE)) %>%
  filter(!is.na(`Area Council`)) %>%
  mutate(Region = `Area Council`)

# Get businesses by industry type and sum them by Region and Attribute
industry_types <- business_data %>%
  filter(Indicator == "Industry Type") %>%
  group_by(Region, Attribute) %>%
  summarise(Value = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  mutate(industry = paste0("industry_", tolower(trimws(Attribute)))) %>%
  select(Region, industry, Value) %>%
  pivot_wider(
    names_from = industry,
    values_from = Value
  ) %>%
  mutate(across(where(is.numeric), ~replace_na(.x, 0)))

# Calculate total businesses
business_wide <- industry_types %>%
  mutate(total_businesses = rowSums(select(., starts_with("industry_")), na.rm = TRUE)) %>%
  select(Region, total_businesses, everything())

# Compute aggregates (province and national levels)
business_aggregated <- compute_council_aggregates(business_wide)

# === EXPORT TO CSV ===
write.csv(
  business_aggregated %>% select(Region, everything()),
  here::here("output", "Business_01_baseline.csv"),
  row.names = FALSE
)

# === PRESENTATION ===
formatted <- format_table(business_aggregated)

# Get all industry columns (excluding Region and total_businesses)
industry_cols <- setdiff(names(formatted), c("Region", "total_businesses"))

# Create column definitions dynamically
column_defs <- list(
  Region = colDef(
    name = "Region",   
    minWidth = 150,
    sortable = TRUE,
    defaultSortOrder = "asc",
    html = TRUE,
    sticky = "left"
  ),
  total_businesses = colDef(name = "", format = colFormat(digits = 0))
)

# Add definitions for all industry columns
for (col in industry_cols) {
  # Create readable name from column name
  readable_name <- gsub("industry_", "", col)
  readable_name <- tools::toTitleCase(readable_name)
  
  column_defs[[col]] <- colDef(
    name = readable_name,
    format = colFormat(digits = 0)
  )
}

# Create column groups - single "Industry Type" group spanning all industry columns
all_groups <- list(
  colGroup(name = "Region", columns = c("Region")),
  colGroup(name = "Total Businesses", columns = c("total_businesses")),
  colGroup(name = "Industry Type", columns = industry_cols)
)

reactable(
  formatted,
  columns = column_defs,
  columnGroups = all_groups,
  striped = TRUE,
  highlight = TRUE,
  bordered = TRUE,
  theme = reactableTheme(
    headerStyle = list(
      "&:hover" = list(background = "#eee")
    ),
    borderColor = "#ddd",
    stripedColor = "#f6f8fa"
  ),
  defaultPageSize = 15,
  showPageSizeOptions = TRUE,
  pageSizeOptions = c(10, 15, 25, 50, 75, 100),
  showSortable = TRUE
)

12.1.1 Map: Business by Area Council

Show map code
# Load Area Council boundaries (if not already loaded)
if (!exists("councils_sf")) {
  councils_sf <- st_read(here::here("data", "GIS_layers", "area_councils.geojson"), quiet = TRUE)
}

# Prepare business data for mapping
business_map_data <- business_aggregated %>%
  filter(!Region %in% c("National", provinces))

# Join spatial data with business data
business_map <- councils_sf %>%
  left_join(business_map_data, by = c("acname" = "Region"))

# Get industry column names (all columns starting with "industry_")
industry_cols <- names(business_map)[grepl("^industry_", names(business_map))]

# Build dynamic label for each polygon
business_map <- business_map %>%
  rowwise() %>%
  mutate(
    industry_details = paste(
      unlist(lapply(industry_cols, function(col) {
        col_name <- gsub("industry_", "", col)
        col_name <- tools::toTitleCase(col_name)
        value <- get(col)
        if (!is.na(value) && value > 0) {
          paste0(col_name, ": ", format(round(value, 0), big.mark = ",", scientific = FALSE))
        } else {
          NULL
        }
      })),
      collapse = "<br>"
    )
  ) %>%
  ungroup()

# Create color palette
pal_business <- colorNumeric(
  palette = "YlOrRd",
  domain = business_map$total_businesses,
  na.color = "#cccccc"
)

# Create interactive map
leaflet(business_map) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~pal_business(total_businesses),
    fillOpacity = 0.7,
    color = "#333333",
    weight = 1,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#000000",
      fillOpacity = 0.9,
      bringToFront = TRUE
    ),
    label = ~paste0(
      "<strong>", acname, "</strong><br>",
      "<hr style='margin: 5px 0;'>",
      "<strong>Total Businesses:</strong> ", format(round(total_businesses, 0), big.mark = ",", scientific = FALSE), "<br>",
      "<br>",
      "<strong>By Industry Type</strong><br>",
      industry_details
    ) %>% lapply(htmltools::HTML),
    labelOptions = labelOptions(
      style = list(
        "font-weight" = "normal", 
        padding = "8px 12px",
        "max-width" = "300px"
      ),
      textsize = "12px",
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_business,
    values = ~total_businesses,
    title = "Total<br>Businesses",
    opacity = 0.7,
    labFormat = labelFormat(big.mark = ",", digits = 0)
  ) %>%
  setView(lng = 167.5, lat = -16, zoom = 6)

This interactive map displays the number of businesses by Area Council. Hover over each council to view the breakdown by industry type. Councils shown in grey have no business data available.

Note: Tanvasoko council (Shefa Province) is not displayed on this map due to a naming mismatch between the 2016 council boundary data and current council names. Data for this council is included in the tables above.

13 Output Quality Checks

Show code
# === VALIDATE EXPORTED CSV FILES ===

validate_csv <- function(filepath) {
  if (!file.exists(filepath)) return(data.frame(File = basename(filepath), Issue = "File not found", Pass = FALSE))
  
  df <- read.csv(filepath, check.names = FALSE)
  issues <- character()
  
  numeric_cols <- df %>% select(where(is.numeric))
  
  if (any(numeric_cols < 0, na.rm = TRUE)) {
    issues <- c(issues, "Negative values")
  }
  if (any(is.na(numeric_cols))) {
    issues <- c(issues, paste0("NA values (", sum(is.na(numeric_cols)), ")"))
  }
  
  if (length(issues) == 0) {
    return(data.frame(File = basename(filepath), Issue = "✓ Pass", Pass = TRUE))
  } else {
    return(data.frame(File = basename(filepath), Issue = paste(issues, collapse = "; "), Pass = FALSE))
  }
}

# Get all output CSVs, excluding QC files (they may have NAs by design)
output_files <- list.files(here::here("output"), pattern = "\\.csv$", full.names = TRUE)
output_files <- output_files[!grepl("^QC_", basename(output_files))]

# Validate each
validation_results <- lapply(output_files, validate_csv) %>% bind_rows()

# Determine overall pass/fail
output_validation_pass <- all(validation_results$Pass)

if (output_validation_pass) {
  message("✓ All output files passed validation")
} else {
  warning("⚠️ Some output files have issues")
}

# Display detailed results
reactable(
  validation_results %>% select(File, Issue),
  columns = list(
    File = colDef(name = "Output File", minWidth = 300),
    Issue = colDef(name = "Status", minWidth = 150)
  ),
  striped = TRUE,
  bordered = TRUE,
  filterable = TRUE
)
Show code
# === MANUAL REVIEW SAMPLE EXPORT ===

# Compile key metrics for sampled councils
manual_review <- bind_rows(
  education_aggregated %>% 
    filter(Region %in% qc_sample_councils) %>% 
    select(Region, starts_with("ecce_"), starts_with("primary_")) %>%
    mutate(Sector = "Education", .before = 1),
  
  shelter_damage_ordered %>% 
    filter(Region %in% qc_sample_councils) %>%
    mutate(Sector = "Shelter Damage", .before = 1),
  
  health_financial_ordered %>% 
    filter(Region %in% qc_sample_councils) %>%
    mutate(Sector = "Health Financial", .before = 1)
)

write.csv(manual_review, here::here("output", "QC_manual_review_sample.csv"), row.names = FALSE)
message("📁 Manual review sample exported to output/QC_manual_review_sample.csv")

# === QC SUMMARY TABLE ===

qc_summary <- data.frame(
  Check = c("Area Councils recognized", 
            "No capitalization issues",
            "Config-baseline alignment",
            "Required baseline completeness",
            "Output file validation"),
  Status = c(
    ifelse(length(unrecognized) == 0, "✓ Pass", "⚠️ Issues"),
    ifelse(length(potential_dupes) == 0, "✓ Pass", "⚠️ Issues"),
    ifelse(length(missing_baseline) == 0, "✓ Pass", "⚠️ Issues"),
    ifelse(baseline_completeness_pass, "✓ Pass", "⚠️ Issues"),
    ifelse(output_validation_pass, "✓ Pass", "⚠️ Issues")
  )
)

reactable(
  qc_summary,
  columns = list(
    Check = colDef(name = "Quality Check", minWidth = 250),
    Status = colDef(name = "Status", minWidth = 100)
  ),
  striped = TRUE,
  bordered = TRUE
)

A work by Government of Vanuatu

Yan.holtz.data@gmail.com

Contact: info@vanuato.com