mirror of
https://github.com/dylanbenzi/hurricane_normalization_app.git
synced 2026-07-30 05:08:57 +00:00
resolve merge conflict
This commit is contained in:
+137
-66
@@ -98,19 +98,15 @@ Col {data-width=500}
|
|||||||
|
|
||||||
HTML(
|
HTML(
|
||||||
'
|
'
|
||||||
<h4>Welcome to the Hurricane Cost Normalization Web App!</h4>
|
<h4>Welcome to the Hurricane Cost Normalization Web App</h4>
|
||||||
|
|
||||||
<p>Our platform provides access to normalized hurricane damage data spanning from 1900 to 2024, allowing researchers, policymakers, insurance professional, and the public to better understand how hurricane costs have changed over time.</p>
|
<p>Our platform provides access to normalized hurricane damage data spanning from 1900 to 2024, allowing researchers, policymakers, insurance professionals, and the public to better understand how hurricane costs have changed over time.</p>
|
||||||
|
|
||||||
<h6>Our Data</h6>
|
<h6>Our Data</h6>
|
||||||
|
|
||||||
<p>The core datasets used in this app are based on research by Muller et al. (2025) published in <i>Bulletin of the American Meteorlogical Society</i>. This study updates and refines hurricane damage normalization methodologies to provide a more accurate picture of how historical hurricanes would impact today\'s society.</p>
|
<p>The core datasets used in this app are based on research by Muller et al. (2025) published in <i>Bulletin of the American Meteorlogical Society</i>. This study updates and refines hurricane damage normalization methodologies to provide a more accurate picture of how historical hurricanes would impact today\'s society.</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>PL22:</b> The Pielke-Landsea (2022) normalization that adjusts for inflation, wealth per capita, and population changes. This data has been updated to 2022.</li>
|
|
||||||
|
|
||||||
<li><b>CL22:</b> The Collins-Lowe (2022) normalization that adjusts for inflation, wealth per housing unit, and housing unit changes. This data has been updated to 2022.</li>
|
|
||||||
|
|
||||||
<li><b>MMP24:</b> The Muller-Mooney Population (2024) normalization with RMW weighting on affected population.</li>
|
<li><b>MMP24:</b> The Muller-Mooney Population (2024) normalization with RMW weighting on affected population.</li>
|
||||||
|
|
||||||
<li><b>MMH24:</b> The Muller-Mooney Housing (2024) normalization with RMW weighting on affected housing units.</li>
|
<li><b>MMH24:</b> The Muller-Mooney Housing (2024) normalization with RMW weighting on affected housing units.</li>
|
||||||
@@ -118,20 +114,19 @@ HTML(
|
|||||||
|
|
||||||
<h6>Methodology Innovations</h6>
|
<h6>Methodology Innovations</h6>
|
||||||
|
|
||||||
<p>Our platform incoroprates several methodological innovations over previously used cost normalization formulas:
|
<p>Our platform incorporates several methodological innovations over previously used cost normalization formulas:
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>Radius of Maximum Wind (RMW) Data:</b> Using landfalling RMWs to identify impacted coastal counties.</li>
|
<li><b>Radius of Maximum Wind (RMW) Data:</b> Using landfalling RMWs to identify impacted coastal counties.</li>
|
||||||
|
|
||||||
<li><b>RMW Affected Area Weighting:</b> Determining affected population and housing unit figures based on RMW.</li>
|
<li><b>RMW Affected Area Weighting:</b> Determining affected population and housing unit figures based on RMW.</li>
|
||||||
|
|
||||||
<li><b>Expanded Storm Coverage:</b> Inlcuding over 200 storms analyzed with interactive data.</li>
|
<li><b>Expanded Storm Coverage:</b> Including over 200 storms analyzed with interactive data.</li>
|
||||||
|
|
||||||
<li><b>Up-to-date Data:</b> Using the latest population, housing unit, and economic data through 2024.</li>
|
<li><b>Up-to-date Data:</b> Using the latest population, housing unit, and economic data through 2024.</li>
|
||||||
</ul>
|
</ul>
|
||||||
'
|
'
|
||||||
)
|
)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Col {data-width=500}
|
Col {data-width=500}
|
||||||
@@ -155,6 +150,10 @@ fluidRow(
|
|||||||
<h5>Select a Storm</h5>
|
<h5>Select a Storm</h5>
|
||||||
|
|
||||||
We are currently tracking 201 CONUS storms with over $3.6T in losses spanning from 1900 to 2024
|
We are currently tracking 201 CONUS storms with over $3.6T in losses spanning from 1900 to 2024
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
Use the storm selector to the left or the table below to select a storm to analyze
|
||||||
')
|
')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -413,14 +412,14 @@ observe({
|
|||||||
|
|
||||||
test_storm <- reactiveValues(
|
test_storm <- reactiveValues(
|
||||||
storm_basin = "AL",
|
storm_basin = "AL",
|
||||||
storm_year = 2005,
|
storm_year = 1926,
|
||||||
storm_name = "KATRINA",
|
storm_name = "GREAT MIAMI",
|
||||||
)
|
)
|
||||||
|
|
||||||
katrina_counties <- reactive({
|
test_counties <- reactive({
|
||||||
req(storm_selection$is_selected)
|
req(storm_selection$is_selected)
|
||||||
|
|
||||||
counties <- get_normalized_metric_growth(test_storm, "LF2")
|
counties <- get_normalized_metric_growth(test_storm, "LF1")
|
||||||
|
|
||||||
result <- counties %>%
|
result <- counties %>%
|
||||||
filter(year == 2024) %>%
|
filter(year == 2024) %>%
|
||||||
@@ -435,20 +434,18 @@ katrina_counties <- reactive({
|
|||||||
|
|
||||||
output$pop_growth_map <- renderLeaflet({
|
output$pop_growth_map <- renderLeaflet({
|
||||||
leaflet() %>%
|
leaflet() %>%
|
||||||
addProviderTiles("CartoDB.Positron", option = providerTileOptions(minZoom = 2, maxZoom = 18))
|
addProviderTiles("CartoDB.Positron", option = providerTileOptions(minZoom = 2, maxZoom = 18)) %>% setView(lng = -81.3, lat = 25.6, zoom = 7)
|
||||||
# %>% setView(lng = -89.8, lat = 29.6, zoom = 8)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
output$housing_growth_map <- renderLeaflet({
|
output$housing_growth_map <- renderLeaflet({
|
||||||
leaflet() %>%
|
leaflet() %>%
|
||||||
addProviderTiles("CartoDB.Positron", option = providerTileOptions(minZoom = 2, maxZoom = 18))
|
addProviderTiles("CartoDB.Positron", option = providerTileOptions(minZoom = 2, maxZoom = 18)) %>% setView(lng = -81.3, lat = 25.6, zoom = 7)
|
||||||
# %>% setView(lng = -89.8, lat = 29.6, zoom = 8)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
observe({
|
observe({
|
||||||
req(katrina_counties)
|
req(test_counties)
|
||||||
|
|
||||||
leafletProxy("pop_growth_map", data = katrina_counties()) %>%
|
leafletProxy("pop_growth_map", data = test_counties()) %>%
|
||||||
clearShapes() %>%
|
clearShapes() %>%
|
||||||
addPolygons(
|
addPolygons(
|
||||||
fillColor = "red",
|
fillColor = "red",
|
||||||
@@ -457,7 +454,7 @@ observe({
|
|||||||
weight = 2
|
weight = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
leafletProxy("housing_growth_map", data = katrina_counties()) %>%
|
leafletProxy("housing_growth_map", data = test_counties()) %>%
|
||||||
clearShapes() %>%
|
clearShapes() %>%
|
||||||
addPolygons(
|
addPolygons(
|
||||||
fillColor = "blue",
|
fillColor = "blue",
|
||||||
@@ -478,7 +475,7 @@ fillCol(
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-itmes: center;
|
align-itmes: center;
|
||||||
",
|
",
|
||||||
sliderInput("growth_trend_map_slider", label = NULL, min = 1900, max = 2024, step = 1, animate = T, value = 1700, sep = "", width = "100%", ticks = F)
|
sliderInput("growth_trend_map_slider", label = NULL, min = 1926, max = 2024, step = 1, animate = T, value = 1926, sep = "", width = "100%", ticks = F)
|
||||||
),
|
),
|
||||||
leafletOutput("pop_growth_map", height = "100%"),
|
leafletOutput("pop_growth_map", height = "100%"),
|
||||||
leafletOutput("housing_growth_map", height = "100%")
|
leafletOutput("housing_growth_map", height = "100%")
|
||||||
@@ -508,13 +505,72 @@ fillCol(
|
|||||||
|
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
dygraphOutput("test_dy")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
output$test_dy <- renderDygraph({
|
||||||
|
test_ts <- test_query()
|
||||||
|
|
||||||
|
dygraph(test_ts, main = "Normalized Aggregate Growth") %>%
|
||||||
|
dySeries("population_index", label = "Population") %>%
|
||||||
|
dySeries("housing_index", label = "Housing Units") %>%
|
||||||
|
dyRangeSelector()
|
||||||
|
})
|
||||||
|
|
||||||
|
#output$popHu <- renderDygraph({
|
||||||
|
# dygraph(aggregate_normalized_growth_metrics_lf_ts(), main = "Normalized Aggregate Growth") %>%
|
||||||
|
# dySeries("normalized_population", label = "Population") %>%
|
||||||
|
# dySeries("normalized_housing_units", label = "Housing Units") %>%
|
||||||
|
# dyRangeSelector()
|
||||||
|
#3})
|
||||||
```
|
```
|
||||||
|
|
||||||
### County Data {data-height=450 .no-padding}
|
### County Data {data-height=450 .no-padding}
|
||||||
```{r}
|
```{r}
|
||||||
# Growth - County Table
|
# Growth - County Table
|
||||||
|
great_miami_data <- data.frame(
|
||||||
|
county_name = c("Broward County", "Collier County", "Miami-Dade County", "Monroe County"),
|
||||||
|
housing_1926 = c(3.7, 0, 25, 3.5),
|
||||||
|
housing_2024 = c(869, 250, 1100, 55),
|
||||||
|
population_1926 = c(14, 0, 103, 16),
|
||||||
|
population_2024 = c(2100, 428, 2990, 81)
|
||||||
|
)
|
||||||
|
|
||||||
|
output$great_miami_dt <- renderDT({
|
||||||
|
datatable(
|
||||||
|
great_miami_data,
|
||||||
|
rownames = F,
|
||||||
|
options = list(
|
||||||
|
order = list(0, 'asc'),
|
||||||
|
paging = F,
|
||||||
|
searching = F,
|
||||||
|
info = F,
|
||||||
|
lengthChange = F,
|
||||||
|
server = T
|
||||||
|
),
|
||||||
|
colnames = c(
|
||||||
|
"County", "1926 HU", "2024 HU", "1926 POP", "2024 POP"
|
||||||
|
),
|
||||||
|
) %>%
|
||||||
|
# Format housing columns with blue background
|
||||||
|
formatStyle(
|
||||||
|
columns = c("housing_1926", "housing_2024"),
|
||||||
|
backgroundColor = "rgba(0, 0, 255, 0.2)"
|
||||||
|
) %>%
|
||||||
|
# Format population columns with red background
|
||||||
|
formatStyle(
|
||||||
|
columns = c("population_1926", "population_2024"),
|
||||||
|
backgroundColor = "rgba(255, 0, 0, 0.2)"
|
||||||
|
) %>%
|
||||||
|
formatCurrency(
|
||||||
|
columns = c("housing_1926", "housing_2024", "population_1926", "population_2024"),
|
||||||
|
currency = "k",
|
||||||
|
digits = 0,
|
||||||
|
before = F
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
DTOutput("great_miami_dt")
|
||||||
```
|
```
|
||||||
|
|
||||||
Storm Fatalities {data-navmenu="Storm Details"}
|
Storm Fatalities {data-navmenu="Storm Details"}
|
||||||
@@ -537,8 +593,25 @@ Column {data-width=700 .tabset}
|
|||||||
|
|
||||||
output$impact_analysis_map <- renderLeaflet({
|
output$impact_analysis_map <- renderLeaflet({
|
||||||
leaflet() %>%
|
leaflet() %>%
|
||||||
addProviderTiles("CartoDB.Positron", option = providerTileOptions(minZoom = 2, maxZoom = 18))
|
addProviderTiles("CartoDB.Positron", option = providerTileOptions(minZoom = 2, maxZoom = 18)) %>%
|
||||||
# %>% setView(lng = -89.8, lat = 29.6, zoom = 8)
|
setView(lng = -80.3, lat = 25.6, zoom = 10) %>%
|
||||||
|
addCircles(
|
||||||
|
lng = -80.3,
|
||||||
|
lat = 25.6,
|
||||||
|
radius = 37040,
|
||||||
|
color = "blue",
|
||||||
|
weight = 2,
|
||||||
|
opacity = 0.3
|
||||||
|
) %>%
|
||||||
|
addCircleMarkers(
|
||||||
|
lng = -80.3,
|
||||||
|
lat = 25.6,
|
||||||
|
radius = 5,
|
||||||
|
weight = 0,
|
||||||
|
color = "blue",
|
||||||
|
fillColor = "blue",
|
||||||
|
fillOpacity = 0.6
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
leafletOutput("impact_analysis_map", height = "100%")
|
leafletOutput("impact_analysis_map", height = "100%")
|
||||||
@@ -557,61 +630,59 @@ Column {data-width=300}
|
|||||||
# Calculator - Input
|
# Calculator - Input
|
||||||
# TODO: add data population and compute
|
# TODO: add data population and compute
|
||||||
|
|
||||||
fillCol(
|
div(
|
||||||
flex = c(.5, .5),
|
h6("Storm Selector"),
|
||||||
div(
|
selectInput("impact_storm_basin", label = NULL, choices = "AL", width = "100%"),
|
||||||
p("Storm Selector"),
|
selectInput("impact_storm_year", label = NULL, choices = 1926, width = "100%"),
|
||||||
selectInput("impact_storm_basin", label = NULL, choices = "AL", width = "100%"),
|
selectInput("impact_storm_name", label = NULL, choices = "GREAT MIAMI", width = "100%"),
|
||||||
selectInput("impact_storm_year", label = NULL, choices = 2005, width = "100%"),
|
|
||||||
selectInput("impact_storm_name", label = NULL, choices = "KATRINA", width = "100%"),
|
|
||||||
|
|
||||||
fluidRow(
|
fluidRow(
|
||||||
column(6,
|
column(6,
|
||||||
selectInput("impact_storm_lf_type", label = NULL, choices = "LF", width = "100%")
|
selectInput("impact_storm_lf_type", label = NULL, choices = "LF", width = "100%")
|
||||||
),
|
|
||||||
column(6,
|
|
||||||
selectInput("impact_storm_lf_id", label = NULL, choices = "1", width = "100%")
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
|
column(6,
|
||||||
actionButton("impact_populate_storm", label = "Populate", width = "100%", class = "btn-primary rounded")
|
selectInput("impact_storm_lf_id", label = NULL, choices = "1", width = "100%")
|
||||||
|
)
|
||||||
),
|
),
|
||||||
div(
|
|
||||||
p("Impact Area"),
|
|
||||||
|
|
||||||
fluidRow(
|
actionButton("impact_populate_storm", label = "Populate", width = "100%", class = "btn-primary rounded")
|
||||||
column(6,
|
)
|
||||||
textInput("impact_lat", placeholder = "Lat", label = NULL, width = "100%")
|
|
||||||
),
|
hr()
|
||||||
column(6,
|
|
||||||
textInput("impact_lon", placeholder = "Lon", label = NULL, width = "100%")
|
div(
|
||||||
)
|
fluidRow(
|
||||||
|
column(6,
|
||||||
|
textInput("impact_lat", placeholder = "Lat", value = "25.6", label = "Latitude", width = "100%")
|
||||||
),
|
),
|
||||||
|
column(6,
|
||||||
|
textInput("impact_lon", placeholder = "Lon", value = "-80.3", label = "Longitude", width = "100%")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
fluidRow(
|
fluidRow(
|
||||||
column(4,
|
column(4,
|
||||||
textInput("impact_rmw", placeholder = "RMW", label = NULL, width = "100%")
|
textInput("impact_rmw", placeholder = "RMW", value = "20", label = "RMW (NM)", width = "100%")
|
||||||
),
|
|
||||||
column(8,
|
|
||||||
sliderInput("impact_rmw_slider", label = NULL, min = 1, max = 20, value = 5, ticks = F, width = "100%")
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
|
column(8,
|
||||||
|
sliderInput("impact_rmw_slider", label = NULL, min = 1, max = 150, value = 20, ticks = F, width = "100%")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
radioGroupButtons("impact_rmw_multiplier", label = NULL, choices = c("1x", "2x", "3x"), status = "outline-primary rounded-0", justified = T),
|
radioGroupButtons("impact_rmw_multiplier", label = "RMW Multiplier", choices = c("1x", "2x", "3x"), status = "outline-primary rounded-0", justified = T),
|
||||||
|
|
||||||
fluidRow(
|
fluidRow(
|
||||||
column(6,
|
column(6,
|
||||||
textInput("impact_base_year", placeholder = "Impact Year", label = NULL, width = "100%")
|
textInput("impact_base_year", placeholder = "Impact Year", value = "1926", label = "Base Year", width = "100%")
|
||||||
),
|
|
||||||
column(6,
|
|
||||||
textInput("impact_ref_year", placeholder = "Reference Year", label = NULL, width = "100%")
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
|
column(6,
|
||||||
|
textInput("impact_ref_year", placeholder = "Reference Year", value = "2024", label = "Ref Year", width = "100%")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
textInput("impact_base_damage", placeholder = "Storm Base Damage", label = NULL, width = "100%"),
|
textInput("impact_base_damage", placeholder = "Storm Base Damage", label = "Base Damage", value = "76,000,000", width = "100%"),
|
||||||
|
|
||||||
actionButton("impact_calculate", label = "Calculate", width = "100%", class = "btn-primary rounded")
|
actionButton("impact_calculate", label = "Calculate", width = "100%", class = "btn-primary rounded")
|
||||||
)
|
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -155,6 +155,50 @@ get_normalized_cost_index <- function(storm, full_lf_id) {
|
|||||||
return(result)
|
return(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_query <- function() {
|
||||||
|
result <- dbGetQuery(con, "WITH yearly_totals AS (
|
||||||
|
SELECT
|
||||||
|
year,
|
||||||
|
SUM(population) as total_population,
|
||||||
|
SUM(housing_units) as total_housing_units
|
||||||
|
FROM metrics.pop_and_housing
|
||||||
|
WHERE state_fips = '12'
|
||||||
|
AND county_fips IN ('011', '021', '086', '087')
|
||||||
|
AND year BETWEEN 1926 AND 2024
|
||||||
|
GROUP BY year
|
||||||
|
),
|
||||||
|
base_year AS (
|
||||||
|
SELECT
|
||||||
|
total_population as base_population,
|
||||||
|
total_housing_units as base_housing
|
||||||
|
FROM yearly_totals
|
||||||
|
WHERE year = (SELECT MIN(year) FROM yearly_totals) -- Uses first year in dataset as base
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
year,
|
||||||
|
total_population,
|
||||||
|
total_housing_units,
|
||||||
|
ROUND(total_population / base_population, 4) as population_index,
|
||||||
|
ROUND(total_housing_units / base_housing, 4) as housing_index
|
||||||
|
FROM yearly_totals
|
||||||
|
CROSS JOIN base_year
|
||||||
|
ORDER BY year;")
|
||||||
|
|
||||||
|
result <- result %>%
|
||||||
|
mutate(
|
||||||
|
year = as.Date(paste0(year, "-01-01"))
|
||||||
|
) %>%
|
||||||
|
select(
|
||||||
|
year, population_index, housing_index
|
||||||
|
)
|
||||||
|
|
||||||
|
result_ts <- result %>%
|
||||||
|
select(-year) %>%
|
||||||
|
xts(order.by = result$year)
|
||||||
|
|
||||||
|
return(result_ts)
|
||||||
|
}
|
||||||
|
|
||||||
# returns landfalls and data at landfall from HURDAT
|
# returns landfalls and data at landfall from HURDAT
|
||||||
get_hurdat_landfalls <- function(storm) {
|
get_hurdat_landfalls <- function(storm) {
|
||||||
query <- view.hurdat_track %>%
|
query <- view.hurdat_track %>%
|
||||||
|
|||||||
Reference in New Issue
Block a user