options(error = NULL) library(shiny) library(shinydashboard) library(dplyr) library(readr) library(sf) #library(cartogram) # optional if you decide to do cartograms later library(ggplot2) library(rnaturalearth) library(rnaturalearthdata) library(countrycode) # ============================= # UI # ============================= ui <- dashboardPage( # Use black skin for the dashboard skin = "black", dashboardHeader( title = span( style = "font-weight: 600; font-size: 18px;", "Country Representation" ) ), dashboardSidebar( sidebarMenu( menuItem("Map Type", tabName = "cartogramTab", icon = icon("globe")) ), div( style = "margin: 15px;", selectInput( inputId = "indexChoice", label = "Select Representation Index:", choices = c("Overall", "RepresentationGap", "Ethnicity", "Gender", "Religion", "Language"), selected = "Overall" ) ) ), dashboardBody( # Bring in the OCR A Extended font from Google Fonts tags$head( tags$link( href = "https://fonts.googleapis.com/css2?family=OCR+A+Extended&display=swap", rel = "stylesheet" ), tags$style(HTML(" /* Force OCR A Extended font across the entire UI and all HTML elements */ html, body, h1, h2, h3, h4, h5, h6, p, div, span, label, input, button, select, .box, .content-wrapper, .main-sidebar, .main-header .navbar, .main-header .logo, .sidebar-menu, .sidebar-menu li a, .sidebar-menu .fa { font-family: 'OCR A Extended', monospace !important; } /* Header gradient background */ .main-header .navbar { background: linear-gradient(to right, #3b6978, #204051) !important; } /* Logo area (left corner of the header) */ .main-header .logo { background: #1b2a2f !important; color: #ffffff !important; border-bottom: none; font-size: 18px; font-weight: 600; } /* Sidebar background */ .main-sidebar { background-color: #1b2a2f !important; } /* Active or hovered tab in the sidebar */ .sidebar-menu > li.active > a, .sidebar-menu > li:hover > a { background-color: #344e5c !important; border-left-color: #78cdd7 !important; color: #ffffff !important; } /* Sidebar menu item icons */ .sidebar-menu .fa { color: #78cdd7 !important; } /* Sidebar menu item text */ .sidebar-menu > li > a { color: #b8c7ce !important; font-size: 15px; font-weight: 500; } /* Customize the boxes */ .box { border-top: none !important; box-shadow: 0 4px 8px rgba(0,0,0,0.1); border-radius: 6px; } .box.box-solid > .box-header { background-color: #204051; color: #fff; border-radius: 6px 6px 0 0; } /* Plot box spacing */ .box .box-body { padding: 0 !important; } /* Footer text styling (plot captions, etc.) */ .small, small { font-size: 75%; } ")) ), tabItems( tabItem( tabName = "cartogramTab", fluidRow( box( width = 12, title = strong("Global Leadership Project (GLP)"), solidHeader = TRUE, div(style = "height: 80vh; padding: 10px;", plotOutput("cartogramPlot", height = "100%") ) ) ) ) ) ) ) # ============================= # SERVER # ============================= server <- function(input, output, session) { # ---- Read CSV data and create ISO3 codes ---- rankings_data <- reactive({ read_csv("CountryRepresentationRankings.csv") %>% mutate(iso_a3 = countrycode(Country, origin = "country.name", destination = "iso3c")) }) # ---- Read/prepare world map shapefile ---- world_sf <- reactive({ ne_countries(scale = "medium", returnclass = "sf") %>% dplyr::select(name, iso_a3, pop_est, geometry) %>% st_transform(crs = "ESRI:54009") # Mollweide projection }) # ---- Create cartogram (currently a regular map) ---- cartogram_sf <- reactive({ merged_sf <- world_sf() %>% left_join(rankings_data(), by = "iso_a3") # Filter out NA values in 'Overall' to avoid missing countries merged_sf <- merged_sf[!is.na(merged_sf$Overall),] return(merged_sf) }) # ---- Plot output ---- output$cartogramPlot <- renderPlot({ req(input$indexChoice) index_col <- input$indexChoice plot_data <- cartogram_sf() ggplot(plot_data) + geom_sf(aes_string(fill = index_col), color = "grey20", size = 0.1) + scale_fill_viridis_c(option = "D", na.value = "white") + coord_sf(expand = FALSE) + # Force OCR A Extended font in the plot #theme_void(base_size = 14, base_family = "OCR A Extended") + theme_void(base_size = 14, base_family = "Courier New") + labs( fill = paste(index_col, "Index"), title = "Country Representation Rankings", subtitle = "Map Colored by Selected Representation Index", caption = "Source: Global Leadership Project (GLP) & Natural Earth" ) + theme( plot.title = element_text(face = "bold", hjust = 0.5, size = 20), plot.subtitle = element_text(hjust = 0.5, size = 14), plot.caption = element_text(hjust = 1, size = 10), legend.position = "bottom", legend.direction = "horizontal", legend.key.width = unit(2, "cm") ) }) } # ============================= # Launch the Shiny App # ============================= shinyApp(ui = ui, server = server)