Update app.R
Browse files
app.R
CHANGED
@@ -9,8 +9,7 @@ library(sf)
|
|
9 |
library(rnaturalearth)
|
10 |
library(rnaturalearthdata)
|
11 |
library(countrycode)
|
12 |
-
library(
|
13 |
-
library(ggiraph) # For interactive hover tooltips and selections
|
14 |
|
15 |
# =============================
|
16 |
# UI
|
@@ -43,9 +42,7 @@ ui <- dashboardPage(
|
|
43 |
)
|
44 |
),
|
45 |
|
46 |
-
# ----
|
47 |
-
# We wrap it in tags$div(...) and tags$script(HTML(...)) so it is recognized
|
48 |
-
# by Shiny. You can adjust the styling or placement as needed.
|
49 |
tags$div(
|
50 |
style = "text-align: left; margin: 1em 0 1em 2em;",
|
51 |
HTML('
|
@@ -76,7 +73,6 @@ ui <- dashboardPage(
|
|
76 |
<strong>Share</strong>
|
77 |
</button>
|
78 |
'),
|
79 |
-
# Insert the JS as well
|
80 |
tags$script(
|
81 |
HTML("
|
82 |
(function() {
|
@@ -231,7 +227,7 @@ ui <- dashboardPage(
|
|
231 |
solidHeader = TRUE,
|
232 |
div(
|
233 |
class = "map-container",
|
234 |
-
|
235 |
)
|
236 |
)
|
237 |
),
|
@@ -248,7 +244,6 @@ ui <- dashboardPage(
|
|
248 |
)
|
249 |
),
|
250 |
|
251 |
-
# -- 2nd row: citation spanning the full width below --
|
252 |
fluidRow(
|
253 |
column(
|
254 |
width = 9,
|
@@ -300,100 +295,92 @@ server <- function(input, output, session) {
|
|
300 |
))
|
301 |
})
|
302 |
|
303 |
-
# 3. Read/prepare world map shapefile
|
|
|
304 |
world_sf <- reactive({
|
305 |
ne_countries(scale = "medium", returnclass = "sf") %>%
|
306 |
dplyr::select(name, iso_a3, pop_est, geometry) %>%
|
307 |
-
st_transform(crs =
|
308 |
})
|
309 |
|
310 |
-
# 4. Create the joined sf object
|
311 |
cartogram_sf <- reactive({
|
312 |
merged_sf <- world_sf() %>%
|
313 |
left_join(rankings_data(), by = "iso_a3")
|
314 |
-
|
315 |
-
merged_sf
|
316 |
})
|
317 |
|
318 |
-
# 5.
|
319 |
-
|
320 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
321 |
|
322 |
plot_data <- cartogram_sf()
|
323 |
index_col <- input$indexChoice
|
324 |
|
325 |
-
# Build a
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
"<b>Ethnicity:</b> ", ifelse(!is.na(plot_data$Ethnicity), plot_data$Ethnicity, "N/A"), "<br/>",
|
331 |
-
"<b>Gender:</b> ", ifelse(!is.na(plot_data$Gender), plot_data$Gender, "N/A"), "<br/>",
|
332 |
-
"<b>Religion:</b> ", ifelse(!is.na(plot_data$Religion), plot_data$Religion, "N/A"), "<br/>",
|
333 |
-
"<b>Language:</b> ", ifelse(!is.na(plot_data$Language), plot_data$Language, "N/A")
|
334 |
)
|
335 |
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
scale_fill_viridis_c(option = "D", na.value = "white") +
|
347 |
-
coord_sf(expand = FALSE) +
|
348 |
-
theme_void(base_size = 14, base_family = "sans") +
|
349 |
-
labs(
|
350 |
-
fill = paste(index_col, "Index"),
|
351 |
-
title = "Country-level Representation",
|
352 |
-
subtitle = "Map Colored by Selected Representation Index",
|
353 |
-
caption = "Source: Global Leadership Project (GLP) & Natural Earth"
|
354 |
-
) +
|
355 |
-
theme(
|
356 |
-
plot.title = element_text(face = "bold", hjust = 0.5, size = 20),
|
357 |
-
plot.subtitle = element_text(hjust = 0.5, size = 14),
|
358 |
-
plot.caption = element_text(hjust = 1, size = 10),
|
359 |
-
legend.position = "bottom",
|
360 |
-
legend.direction = "horizontal",
|
361 |
-
legend.key.width = unit(2, "cm")
|
362 |
-
)
|
363 |
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
|
|
377 |
)
|
378 |
-
|
379 |
-
|
|
|
|
|
380 |
|
381 |
-
|
382 |
-
|
383 |
-
|
|
|
|
|
384 |
})
|
385 |
|
386 |
# Reactive to fetch the selected country's data
|
387 |
selected_data <- reactive({
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
filter(iso_a3 == selected_iso())
|
393 |
-
}
|
394 |
})
|
395 |
|
396 |
-
# Render the selected country data
|
397 |
output$selectedCountryData <- renderUI({
|
398 |
if (is.null(selected_data())) {
|
399 |
HTML("<p>Select a country by clicking on the map.</p>")
|
@@ -416,4 +403,4 @@ server <- function(input, output, session) {
|
|
416 |
# =============================
|
417 |
# Launch the Shiny App
|
418 |
# =============================
|
419 |
-
shinyApp(ui = ui, server = server)
|
|
|
9 |
library(rnaturalearth)
|
10 |
library(rnaturalearthdata)
|
11 |
library(countrycode)
|
12 |
+
library(leaflet) # <-- New: for OpenStreetMap-based map
|
|
|
13 |
|
14 |
# =============================
|
15 |
# UI
|
|
|
42 |
)
|
43 |
),
|
44 |
|
45 |
+
# ---- Minimal "Share" button HTML + JS inlined ----
|
|
|
|
|
46 |
tags$div(
|
47 |
style = "text-align: left; margin: 1em 0 1em 2em;",
|
48 |
HTML('
|
|
|
73 |
<strong>Share</strong>
|
74 |
</button>
|
75 |
'),
|
|
|
76 |
tags$script(
|
77 |
HTML("
|
78 |
(function() {
|
|
|
227 |
solidHeader = TRUE,
|
228 |
div(
|
229 |
class = "map-container",
|
230 |
+
leafletOutput("cartogramPlot", width = "100%", height = "100%")
|
231 |
)
|
232 |
)
|
233 |
),
|
|
|
244 |
)
|
245 |
),
|
246 |
|
|
|
247 |
fluidRow(
|
248 |
column(
|
249 |
width = 9,
|
|
|
295 |
))
|
296 |
})
|
297 |
|
298 |
+
# 3. Read/prepare world map shapefile (still from Natural Earth),
|
299 |
+
# but transform to lat/lon (EPSG:4326) to align with Leaflet.
|
300 |
world_sf <- reactive({
|
301 |
ne_countries(scale = "medium", returnclass = "sf") %>%
|
302 |
dplyr::select(name, iso_a3, pop_est, geometry) %>%
|
303 |
+
st_transform(crs = 4326) # Leaflet requires lat/lon
|
304 |
})
|
305 |
|
306 |
+
# 4. Create the joined sf object
|
307 |
cartogram_sf <- reactive({
|
308 |
merged_sf <- world_sf() %>%
|
309 |
left_join(rankings_data(), by = "iso_a3")
|
310 |
+
# Filter out countries with no data in "Overall"
|
311 |
+
merged_sf[!is.na(merged_sf$Overall), ]
|
312 |
})
|
313 |
|
314 |
+
# 5. Create the Leaflet map with OSM tiles
|
315 |
+
# and dynamically add polygons based on index choice.
|
316 |
+
|
317 |
+
# Initialize the leaflet map (empty) so it renders once:
|
318 |
+
output$cartogramPlot <- renderLeaflet({
|
319 |
+
leaflet() %>%
|
320 |
+
addProviderTiles("OpenStreetMap.Mapnik") %>%
|
321 |
+
setView(lng = 0, lat = 20, zoom = 2) # A broad global view
|
322 |
+
})
|
323 |
+
|
324 |
+
# Observe changes in the chosen index and update polygons
|
325 |
+
observeEvent(input$indexChoice, {
|
326 |
|
327 |
plot_data <- cartogram_sf()
|
328 |
index_col <- input$indexChoice
|
329 |
|
330 |
+
# Build a color palette based on the chosen index
|
331 |
+
pal <- colorNumeric(
|
332 |
+
palette = "viridis",
|
333 |
+
domain = plot_data[[index_col]],
|
334 |
+
na.color = "white"
|
|
|
|
|
|
|
|
|
335 |
)
|
336 |
|
337 |
+
# Construct a label/popup-like text
|
338 |
+
# (You could use labelOptions, popup, or separate tooltips)
|
339 |
+
# We'll just use label here for quick hover info:
|
340 |
+
labels <- sprintf(
|
341 |
+
"<strong>Country:</strong> %s<br/>
|
342 |
+
<strong>%s Index:</strong> %s",
|
343 |
+
plot_data$Country,
|
344 |
+
index_col,
|
345 |
+
ifelse(is.na(plot_data[[index_col]]), "N/A", plot_data[[index_col]])
|
346 |
+
) %>% lapply(htmltools::HTML)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
347 |
|
348 |
+
leafletProxy("cartogramPlot", data = plot_data) %>%
|
349 |
+
clearShapes() %>%
|
350 |
+
addPolygons(
|
351 |
+
fillColor = ~pal(get(index_col)),
|
352 |
+
fillOpacity = 0.7,
|
353 |
+
color = "grey20",
|
354 |
+
weight = 0.4,
|
355 |
+
layerId = ~iso_a3,
|
356 |
+
label = labels,
|
357 |
+
highlightOptions = highlightOptions(
|
358 |
+
color = "white",
|
359 |
+
weight = 2,
|
360 |
+
bringToFront = TRUE
|
361 |
+
)
|
362 |
)
|
363 |
+
}, ignoreNULL = FALSE) # Trigger once on startup too
|
364 |
+
|
365 |
+
# Track which country was clicked
|
366 |
+
selected_iso <- reactiveVal(NULL)
|
367 |
|
368 |
+
observeEvent(input$cartogramPlot_shape_click, {
|
369 |
+
click <- input$cartogramPlot_shape_click
|
370 |
+
if (!is.null(click$id)) {
|
371 |
+
selected_iso(click$id)
|
372 |
+
}
|
373 |
})
|
374 |
|
375 |
# Reactive to fetch the selected country's data
|
376 |
selected_data <- reactive({
|
377 |
+
iso <- selected_iso()
|
378 |
+
if (is.null(iso)) return(NULL)
|
379 |
+
rankings_data() %>%
|
380 |
+
filter(iso_a3 == iso)
|
|
|
|
|
381 |
})
|
382 |
|
383 |
+
# Render the selected country data in the box
|
384 |
output$selectedCountryData <- renderUI({
|
385 |
if (is.null(selected_data())) {
|
386 |
HTML("<p>Select a country by clicking on the map.</p>")
|
|
|
403 |
# =============================
|
404 |
# Launch the Shiny App
|
405 |
# =============================
|
406 |
+
shinyApp(ui = ui, server = server)
|