cjerzak commited on
Commit
7952109
·
verified ·
1 Parent(s): 5199cd8

Update app.R

Browse files
Files changed (1) hide show
  1. app.R +65 -78
app.R CHANGED
@@ -9,8 +9,7 @@ library(sf)
9
  library(rnaturalearth)
10
  library(rnaturalearthdata)
11
  library(countrycode)
12
- library(ggplot2)
13
- library(ggiraph) # For interactive hover tooltips and selections
14
 
15
  # =============================
16
  # UI
@@ -43,9 +42,7 @@ ui <- dashboardPage(
43
  )
44
  ),
45
 
46
- # ---- Here is the minimal "Share" button HTML + JS inlined in Shiny ----
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
- girafeOutput("cartogramPlot", width = "100%", height = "100%")
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 = "ESRI:54009") # Mollweide projection
308
  })
309
 
310
- # 4. Create the joined sf object (currently just a regular map)
311
  cartogram_sf <- reactive({
312
  merged_sf <- world_sf() %>%
313
  left_join(rankings_data(), by = "iso_a3")
314
- merged_sf <- merged_sf[!is.na(merged_sf$Overall),]
315
- merged_sf
316
  })
317
 
318
- # 5. Render the interactive map with ggiraph
319
- output$cartogramPlot <- renderGirafe({
320
- req(input$indexChoice)
 
 
 
 
 
 
 
 
 
321
 
322
  plot_data <- cartogram_sf()
323
  index_col <- input$indexChoice
324
 
325
- # Build a tooltip string (same as data to display on click)
326
- plot_data$tooltip_text <- paste0(
327
- "<b>Country:</b> ", plot_data$Country, "<br/>",
328
- "<b>Overall:</b> ", ifelse(!is.na(plot_data$Overall), plot_data$Overall, "N/A"), "<br/>",
329
- "<b>Representation Gap:</b> ", ifelse(!is.na(plot_data$RepresentationGap), plot_data$RepresentationGap, "N/A"), "<br/>",
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
- p <- ggplot(plot_data) +
337
- geom_sf_interactive(
338
- aes(
339
- fill = get(index_col),
340
- tooltip = tooltip_text,
341
- data_id = iso_a3 # Enable selection with unique identifier
342
- ),
343
- color = "grey20",
344
- size = 0.1
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
- girafe(
365
- ggobj = p,
366
- width_svg = 10,
367
- height_svg = 6,
368
- options = list(
369
- opts_tooltip(
370
- css = "background-color: white;
371
- font-family: 'OCR A Extended', monospace;
372
- color: black;"
373
- ),
374
- opts_selection(type = "single"), # Single selection: one country at a time
375
- opts_zoom(min = 1, max = 10), # <-- Enables zooming
376
- opts_toolbar(position = "topright") # <-- Adds a small toolbar
 
377
  )
378
- )
379
- })
 
 
380
 
381
- # Reactive to track the selected country's ISO code
382
- selected_iso <- reactive({
383
- input$cartogramPlot_selected # Returns the data_id of the selected country or NULL
 
 
384
  })
385
 
386
  # Reactive to fetch the selected country's data
387
  selected_data <- reactive({
388
- if (is.null(selected_iso())) {
389
- return(NULL)
390
- } else {
391
- rankings_data() %>%
392
- filter(iso_a3 == selected_iso())
393
- }
394
  })
395
 
396
- # Render the selected country data below the map
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)