Spaces:
Sleeping
Sleeping
| required_packages <- c("leaflet","shiny","dplyr","RColorBrewer","scales","lattice","plotly","bslib","shinydashboard","remotes") | |
| remotes::install_github("briatte/ggnet") | |
| #remotes::install_github("rspatial/terra") | |
| options(repos = c(CRAN = "https://cloud.r-project.org")) | |
| for (pkg in required_packages) { | |
| if (!requireNamespace(pkg, quietly = TRUE)) { install.packages(pkg) } | |
| library(pkg, character.only = TRUE) | |
| } | |
| lapply(c("leaflet","shiny","dplyr","RColorBrewer","scales","lattice", | |
| "plotly","ggnet","bslib","shinydashboard"), require, character.only = TRUE) | |
| # install.packages("leafpop") # this is for the popups in leaflet | |
| # for testing, set working directory to the app directory | |
| #setwd("/Users/luciagomezteijeiro/Library/CloudStorage/OneDrive-UniversitédeGenève/BPL/Nisa_Lucia_Vini/philea/app") | |
| # Load data | |
| geo <- as.data.frame(readRDS("data/geo_QC_annotated_connectivity_rescaled.rds")) | |
| geo <- geo[!is.na(geo$lat | !is.na(geo$lon)),] # remove foundations without addresses | |
| p_members_data <- readRDS("data/p_members_data.rds") | |
| p_purposes_data <- readRDS("data/p_purposes_data.rds") | |
| p_geo_data <- readRDS("data/p_geo_data.rds") | |
| # PREPARE POPUP FOR MAP AS FORMATED TEXT | |
| geo$popup_content <- paste0( | |
| "<b><span style='font-size:16px;'>", ifelse(is.na(geo$name_clean), "No name available", geo$name_clean), "</span></b><br>","<br>", | |
| "<b>GPS Coordinates:</b> ", ifelse(is.na(geo$lat), "No latitude available", round(geo$lat,2)), ", ", ifelse(is.na(geo$lon), "No longitude available", round(geo$lon,2)), "<br>", # Example variable 1 | |
| "<b>ID:</b> ", ifelse(is.na(geo$ID), "Not available", geo$ID), "<br>","<br>", | |
| "<b><span style='font-size:14px;'> Overall Connectivity Score:</b> ", ifelse(is.na(geo$overall_connectivity), "Not available", round(geo$overall_connectivity,2)), "<br>", | |
| "<b>Board Connectivity Score: </b> ", ifelse(is.na(geo$strength_memb_scaled), "Not available", round(geo$strength_memb_scaled,2)), "<br>", | |
| "<b>Purpose Connectivity Score: </b> ", ifelse(is.na(geo$strength_purp_scaled), "Not available", round(geo$strength_purp_scaled,2)), "<br>", | |
| "<b>Geography Connectivity Score: </b> ", ifelse(is.na(geo$strength_geo_scaled), "Not available", round(geo$strength_geo_scaled,2)), "<br>","<br>", | |
| "<b><span style='font-size:14px;'> Most connected foundations:</b> ","<br>", | |
| "<b>Closer by board: </b> ", ifelse(is.na(geo$top_memb), "Not available", geo$top_memb), "<br>", | |
| "<b>Closer by purpose: </b> ", ifelse(is.na(geo$top_purp), "Not available", geo$top_purp), "<br>", | |
| "<b>Closer by geography: </b> ", ifelse(is.na(geo$top_geo), "Not available", geo$top_geo), "<br>" | |
| # "<b>Funding:</b> $", format(funding, big.mark = ","), "<br>", # If you want to add financials | |
| # "<b>Website:</b> <a href='", geo$website, "' target='_blank'>Visit</a>" # If you want a clickable URL | |
| ) | |
| ui <- navbarPage("Spanish Philanthropic Landscape",id="nav", | |
| # THEME | |
| # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
| #theme = bs_theme(version = 4), # uncomment this to have a theme selector in the navbar, do the same in server side with bs_themer() | |
| theme = bs_theme(version = 4, bootswatch = "lumen"), | |
| tags$style(HTML( | |
| # custom css to modify theme | |
| " | |
| .navbar-brand { font-size: 25px !important;} | |
| body { font-size: 16px; } | |
| h1 { font-size: 40px; } | |
| h2, h3, h4, h5, h6 { font-size: 20px; } | |
| .navbar, .navbar-nav, .navbar-default, .navbar-header { font-size: 20px !important; } | |
| .container-fluid { font-size: 16px; } | |
| " | |
| )), | |
| # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
| # FIRST PAGE - INTERACTIVE MAP | |
| # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
| tabPanel("Interactive Map", | |
| fluidRow(column(12, | |
| absolutePanel(top = 10, left = 20, right=20, width = "95%", | |
| div(class="container-fluid", | |
| selectizeInput("name_clean", | |
| label=tagList( | |
| tags$strong("Highlight one or several Foundations by Name") | |
| ), | |
| choices = NULL, multiple = TRUE, | |
| options = list(placeholder = "Search a foundation..."),width="100%") ) | |
| ), | |
| div(style = "margin-left: 20px; margin-right:20px; margin-top: 80px; font-size: 12px; color: #555; text-align: left;", | |
| "Use the search bar to find and highlight foundations on the map. | |
| All foundations are displayed by default in blue, while the selected ones will be highlighted in red.") | |
| )), | |
| fluidRow(column(12, | |
| div(style = "padding-top: 25px;margin-left: 20px; margin-right:20px;", leafletOutput("map",height = "600px")) | |
| )), | |
| fluidRow(column(12, | |
| div(style= "margin-left: 20px; margin-right:20px; margin-top:20px; font-size: 12px; color: #555; text-align: center;", | |
| "Data compiled for ", | |
| tags$em("PIO Winter Meeting 2025"), | |
| " by Lucia Gomez Teijeiro (1), Nisa Thomas (2) and Jack O'Neill (3) (University of Geneva (1,2) / BFH (1) and Philea (3) respectively, 2025)", | |
| tags$br(), | |
| "Methodology derived from the Mapping Philanthropy research project. Open Source and available at", | |
| tags$a(href="https://github.com/gomez-L/mapping_philanthropy","https://github.com/gomez-L/mapping_philanthropy",target="https://github.com/gomez-L/mapping_philanthropy") | |
| ))) | |
| ), | |
| # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
| # SECOND PAGE - NETWORK EXPLORER | |
| # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
| tabPanel("Network Explorer", | |
| fluidRow(column(12, | |
| absolutePanel(top = 10, left = 20, right=20, width = "95%", | |
| div(class="container-fluid", | |
| selectizeInput("label_plotly", | |
| label=tagList( | |
| tags$strong("Highlight one or several Foundations by Name") | |
| ), | |
| choices = NULL, multiple = TRUE, | |
| options = list(placeholder = "Search a foundation..."),width="100%") ) | |
| ), | |
| div(style = "margin-left: 20px; margin-right:20px; margin-top: 80px; margin-bottom: 20px; font-size: 12px; color: #555; text-align: left;", | |
| HTML( | |
| "Use the search bar to find and highlight foundations in all networks at the same time. | |
| All foundations are displayed by default in blue, while the selected ones will be highlighted in red.<br> | |
| Here you will discover that foundation similarity is heterogeneous, as it depends on the dimension you explore 😊 !")) | |
| )), | |
| fluidRow( | |
| column(4, # First Column - 1/3 is 4, as total width is 12 elements) | |
| box( | |
| status = "primary",width=12,style = "background-color: rgba(128, 128, 128, 0.1); border: 0.5px solid #000000;", | |
| plotlyOutput("p_members", height = "60vh"), | |
| p(HTML(paste("Network arranging foundations by the number of board members in common.<br> | |
| Dot size indicates the number of board members each foundation has.<br> | |
| Foundations in the periphery have less board members in common, those overlapping have connected boards")), | |
| style="margin-left:10px; margin-right:10px; margin-top:10px; margin-bottom:10px; font-size:12px; text-align:center;") | |
| )), | |
| column(4, # Second column | |
| box( | |
| status = "primary",width=12,style = "background-color: rgba(128, 128, 128, 0.1); border: 0.5px solid #000000;", | |
| plotlyOutput("p_purposes", height = "60vh"), | |
| p(HTML(paste("Network arranging foundations by the similarity in their mission statements.<br> | |
| Transformer-based instructed retrieval ensures that purpose similarity is captured. | |
| Foundations in the periphery have more specific purposes, those in the center broader purposes.")), | |
| style="margin-left:10px; margin-right:10px; margin-top:10px; margin-bottom:10px; font-size:12px; text-align:center;") | |
| )), | |
| column(4, # Third Column | |
| box( | |
| status = "primary",width=12,style = "background-color: rgba(128, 128, 128, 0.1); border: 0.5px solid #000000;", | |
| plotlyOutput("p_geo", height = "60vh"), | |
| p(HTML(paste("Network arranging foundations by their geographical proximity, in kilometers.<br>It resembles a map.<br><br><br>")), | |
| style="margin-left:10px; margin-right:10px; margin-top:10px; margin-bottom:10px; font-size:12px; text-align:center;") | |
| )), | |
| ), | |
| fluidRow(column(12, | |
| div(style= "margin-left: 20px; margin-right:20px; margin-top:20px; margin-bottom:50px; font-size: 12px; color: #555; text-align: center;", | |
| "Data compiled for ", | |
| tags$em("PIO Winter Meeting 2025"), | |
| " by Lucia Gomez Teijeiro (1), Nisa Thomas (2) and Jack O'Neill (3) (University of Geneva (1,2) / BFH (1) and Philea (3) respectively, 2025)", | |
| tags$br(), | |
| "Methodology derived from the Mapping Philanthropy research project. Open Source and available at", | |
| tags$a(href="https://github.com/gomez-L/mapping_philanthropy","https://github.com/gomez-L/mapping_philanthropy",target="https://github.com/gomez-L/mapping_philanthropy") | |
| ))) | |
| ), | |
| ) | |
| # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
| server <- function(input, output, session) { | |
| #bs_themer() # uncomment this to have a theme selector in the navbar, do the same in ui side with theme = bs_theme(version = 4), | |
| # INTERACTIVE MAP | |
| # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
| # DEFINE SELECTIZEINPUT FOR MAP | |
| # Load foundation names dynamically | |
| observe({ updateSelectizeInput(session, "name_clean", choices = unique(geo$name_clean), server = TRUE) }) | |
| # OBSERVE MAP | |
| # Render initial Leaflet Map with all points in blue | |
| output$map <- renderLeaflet({ leaflet(geo) %>% addTiles() %>% | |
| addCircleMarkers(lng =~lon,lat =~lat,label=~name_clean,color ="blue",layerId =~name_clean,radius=3,stroke=FALSE,fillOpacity=1, | |
| popup =~popup_content) | |
| }) | |
| # Observe selection and update marker colors | |
| observeEvent(input$name_clean, { | |
| leafletProxy("map") %>% | |
| clearMarkers() %>% | |
| addCircleMarkers(data = geo, lng = ~lon, lat = ~lat, | |
| label = ~name_clean,color =~ifelse(name_clean %in% input$name_clean,"red","blue"),layerId = ~name_clean, # color changes with selection | |
| radius=~ifelse(name_clean %in% input$name_clean,10,3), # size changes with selection | |
| stroke=FALSE,fillOpacity=1,popup =~popup_content ) | |
| }) | |
| # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
| # NETWORK EXPLORER | |
| # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
| # DEFINE SELECTIZEINPUT FOR NETWORK EXPLORER | |
| observe({ updateSelectizeInput(session, "label_plotly", choices = unique(c(geo$name_clean,p_members_data$label,p_purposes_data$label)), server = TRUE) }) | |
| output$p_members <- renderPlotly({ | |
| plot_ly(p_members_data, x = ~x, y = ~y) %>% | |
| add_markers(color = I("blue"),alpha = 0.8,marker = list(size = ~n_members),text=~label,hoverinfo="text") %>% | |
| filter(label %in% input$label_plotly) %>% | |
| add_markers(color = I("red"),alpha = 0.8,marker = list(size=20),text=~label,hoverinfo="text") %>% | |
| toWebGL() %>% layout(showlegend = FALSE,title=list(text="Shared Board Members",font=list(size=20),y=0.9,yanchor="top"), | |
| xaxis = list(visible = FALSE,scaleanchor = "y"),yaxis = list(visible = FALSE), | |
| margin = list(t = 100, r = 0, b = 0, l = 0),hoverlabel = list(bgcolor = "white") ) | |
| }) | |
| output$p_purposes <- renderPlotly({ | |
| plot_ly(p_purposes_data, x = ~x, y = ~y) %>% | |
| add_markers(color = I("blue"), alpha = 0.5,text=~label,hoverinfo="text") %>% | |
| filter(label %in% input$label_plotly) %>% | |
| # WE COULD HAVE DETERMINED THE SIZE!! IT COULD HAVE BEEN THE PURPOSE HETEROGENEITY | |
| add_markers(color = I("red"),alpha = 0.8,marker=list(size=20),text=~label,hoverinfo="text") %>% | |
| toWebGL() %>% layout(showlegend = FALSE,title=list(text="Purpose Similarity",font=list(size=20),y=0.9,yanchor="top"), | |
| xaxis = list(visible = FALSE,scaleanchor = "y"),yaxis = list(visible = FALSE), | |
| margin = list(t = 100, r = 0, b = 0, l = 0),hoverlabel = list(bgcolor = "white") ) | |
| }) | |
| output$p_geo <- renderPlotly({ | |
| plot_ly(p_geo_data, x = ~(-y), y = ~x) %>% | |
| add_markers(color = I("blue"), alpha = 0.5,text=~label,hoverinfo="text") %>% | |
| filter(label %in% input$label_plotly) %>% | |
| # WE COULD HAVE DETERMINED THE SIZE!! IT COULD HAVE BEEN THE CLOSENESS TO A CITY CENTER | |
| add_markers(color = I("red"),alpha = 0.8,marker=list(size=20),text=~label,hoverinfo="text") %>% | |
| toWebGL() %>% layout(showlegend = FALSE,title=list(text="Geographic Proximity",font=list(size=20),y=0.9,yanchor="top"), | |
| xaxis = list(visible = FALSE,scaleanchor = "y"),yaxis = list(visible = FALSE), | |
| margin = list(t = 100, r = 0, b = 0, l = 0),hoverlabel = list(bgcolor = "white") ) | |
| }) | |
| # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
| } | |
| shinyApp(ui, server) | |