File size: 16,820 Bytes
59455e5
 
 
831d877
59455e5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3f20b9b
59455e5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fa53cd1
 
59455e5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fa53cd1
59455e5
 
 
 
 
 
 
 
 
 
 
 
 
 
fa53cd1
 
59455e5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fa53cd1
 
728ae3e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259

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)