luciagomez commited on
Commit
59455e5
·
verified ·
1 Parent(s): cde2a3c

update app.R

Browse files
Files changed (1) hide show
  1. app.R +250 -50
app.R CHANGED
@@ -1,58 +1,258 @@
1
- library(shiny)
2
- library(bslib)
3
- library(dplyr)
4
- library(ggplot2)
5
-
6
- df <- readr::read_csv("penguins.csv")
7
- # Find subset of columns that are suitable for scatter plot
8
- df_num <- df |> select(where(is.numeric), -Year)
9
-
10
- ui <- page_sidebar(
11
- theme = bs_theme(bootswatch = "minty"),
12
- title = "Penguins explorer",
13
- sidebar = sidebar(
14
- varSelectInput("xvar", "X variable", df_num, selected = "Bill Length (mm)"),
15
- varSelectInput("yvar", "Y variable", df_num, selected = "Bill Depth (mm)"),
16
- checkboxGroupInput("species", "Filter by species",
17
- choices = unique(df$Species), selected = unique(df$Species)
18
- ),
19
- hr(), # Add a horizontal rule
20
- checkboxInput("by_species", "Show species", TRUE),
21
- checkboxInput("show_margins", "Show marginal plots", TRUE),
22
- checkboxInput("smooth", "Add smoother"),
23
- ),
24
- plotOutput("scatter")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  )
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  server <- function(input, output, session) {
28
- subsetted <- reactive({
29
- req(input$species)
30
- df |> filter(Species %in% input$species)
 
 
 
 
 
 
 
 
 
 
 
31
  })
32
 
33
- output$scatter <- renderPlot(
34
- {
35
- p <- ggplot(subsetted(), aes(!!input$xvar, !!input$yvar)) +
36
- theme_light() +
37
- list(
38
- theme(legend.position = "bottom"),
39
- if (input$by_species) aes(color = Species),
40
- geom_point(),
41
- if (input$smooth) geom_smooth()
42
- )
43
-
44
- if (input$show_margins) {
45
- margin_type <- if (input$by_species) "density" else "histogram"
46
- p <- p |> ggExtra::ggMarginal(
47
- type = margin_type, margins = "both",
48
- size = 8, groupColour = input$by_species, groupFill = input$by_species
49
- )
50
- }
51
-
52
- p
53
- },
54
- res = 100
55
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  }
57
 
58
  shinyApp(ui, server)
 
1
+
2
+ required_packages <- c("leaflet","shiny","dplyr","RColorBrewer","scales","lattice","plotly","bslib","shinydashboard","remotes")
3
+ remotes::install_github("briatte/ggnet")
4
+ remotes::install_github("rspatial/terra")
5
+ options(repos = c(CRAN = "https://cloud.r-project.org"))
6
+ for (pkg in required_packages) {
7
+ if (!requireNamespace(pkg, quietly = TRUE)) { install.packages(pkg) }
8
+ library(pkg, character.only = TRUE)
9
+ }
10
+
11
+
12
+ lapply(c("leaflet","shiny","dplyr","RColorBrewer","scales","lattice",
13
+ "plotly","ggnet","bslib","shinydashboard"), require, character.only = TRUE)
14
+
15
+ # install.packages("leafpop") # this is for the popups in leaflet
16
+
17
+ # for testing, set working directory to the app directory
18
+ #setwd("/Users/luciagomezteijeiro/Library/CloudStorage/OneDrive-UniversitédeGenève/BPL/Nisa_Lucia_Vini/philea/app")
19
+
20
+ # Load data
21
+ geo <- as.data.frame(readRDS("data/geo_QC_annotated_connectivity.rds"))
22
+ geo <- geo[!is.na(geo$lat | !is.na(geo$lon)),] # remove foundations without addresses
23
+ p_members_data <- readRDS("data/p_members_data.rds")
24
+ p_purposes_data <- readRDS("data/p_purposes_data.rds")
25
+ p_geo_data <- readRDS("data/p_geo_data.rds")
26
+
27
+ # PREPARE POPUP FOR MAP AS FORMATED TEXT
28
+ geo$popup_content <- paste0(
29
+ "<b><span style='font-size:16px;'>", ifelse(is.na(geo$name_clean), "No name available", geo$name_clean), "</span></b><br>","<br>",
30
+ "<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
31
+ "<b>ID:</b> ", ifelse(is.na(geo$ID), "Not available", geo$ID), "<br>","<br>",
32
+ "<b><span style='font-size:14px;'> Overall Connectivity Score:</b> ", ifelse(is.na(geo$overall_connectivity), "Not available", round(geo$overall_connectivity,2)), "<br>",
33
+ "<b>Board Connectivity Score: </b> ", ifelse(is.na(geo$strength_memb_scaled), "Not available", round(geo$strength_memb_scaled,2)), "<br>",
34
+ "<b>Purpose Connectivity Score: </b> ", ifelse(is.na(geo$strength_purp_scaled), "Not available", round(geo$strength_purp_scaled,2)), "<br>",
35
+ "<b>Geography Connectivity Score: </b> ", ifelse(is.na(geo$strength_geo_scaled), "Not available", round(geo$strength_geo_scaled,2)), "<br>","<br>",
36
+ "<b><span style='font-size:14px;'> Most connected foundations:</b> ","<br>",
37
+ "<b>Closer by board: </b> ", ifelse(is.na(geo$top_memb), "Not available", geo$top_memb), "<br>",
38
+ "<b>Closer by purpose: </b> ", ifelse(is.na(geo$top_purp), "Not available", geo$top_purp), "<br>",
39
+ "<b>Closer by geography: </b> ", ifelse(is.na(geo$top_geo), "Not available", geo$top_geo), "<br>"
40
+ # "<b>Funding:</b> $", format(funding, big.mark = ","), "<br>", # If you want to add financials
41
+ # "<b>Website:</b> <a href='", geo$website, "' target='_blank'>Visit</a>" # If you want a clickable URL
42
  )
43
 
44
+ ui <- navbarPage("Spanish Philanthropic Landscape",id="nav",
45
+
46
+ # THEME
47
+ # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- #
48
+
49
+ #theme = bs_theme(version = 4), # uncomment this to have a theme selector in the navbar, do the same in server side with bs_themer()
50
+ theme = bs_theme(version = 4, bootswatch = "lumen"),
51
+
52
+ tags$style(HTML(
53
+ # custom css to modify theme
54
+ "
55
+ .navbar-brand { font-size: 25px !important;}
56
+ body { font-size: 16px; }
57
+ h1 { font-size: 40px; }
58
+ h2, h3, h4, h5, h6 { font-size: 20px; }
59
+ .navbar, .navbar-nav, .navbar-default, .navbar-header { font-size: 20px !important; }
60
+ .container-fluid { font-size: 16px; }
61
+ "
62
+ )),
63
+
64
+ # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- #
65
+
66
+
67
+ # FIRST PAGE - INTERACTIVE MAP
68
+ # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- #
69
+
70
+ tabPanel("Interactive Map",
71
+
72
+ fluidRow(column(12,
73
+
74
+ absolutePanel(top = 10, left = 20, right=20, width = "95%",
75
+ div(class="container-fluid",
76
+ selectizeInput("name_clean",
77
+ label=tagList(
78
+ tags$strong("Highlight one or several Foundations by Name")
79
+ ),
80
+ choices = NULL, multiple = TRUE,
81
+ options = list(placeholder = "Search a foundation..."),width="100%") )
82
+ ),
83
+ div(style = "margin-left: 20px; margin-right:20px; margin-top: 80px; font-size: 12px; color: #555; text-align: left;",
84
+ "Use the search bar to find and highlight foundations on the map.
85
+ All foundations are displayed by default in blue, while the selected ones will be highlighted in red.")
86
+ )),
87
+
88
+ fluidRow(column(12,
89
+
90
+ div(style = "padding-top: 25px;margin-left: 20px; margin-right:20px;", leafletOutput("map",height = "600px"))
91
+
92
+ )),
93
+
94
+ fluidRow(column(12,
95
+
96
+ div(style= "margin-left: 20px; margin-right:20px; margin-top:20px; font-size: 12px; color: #555; text-align: center;",
97
+ "Data compiled for ",
98
+ tags$em("PIO Winter Meeting 2025"),
99
+ " 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)",
100
+ tags$br(),
101
+ "Methodology derived from the Mapping Philanthropy research project. Open Source and available at",
102
+ 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")
103
+ )))
104
+
105
+ ),
106
+
107
+ # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- #
108
+
109
+
110
+
111
+ # SECOND PAGE - NETWORK EXPLORER
112
+ # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- #
113
+
114
+ tabPanel("Network Explorer",
115
+
116
+ fluidRow(column(12,
117
+ absolutePanel(top = 10, left = 20, right=20, width = "95%",
118
+ div(class="container-fluid",
119
+ selectizeInput("label_plotly",
120
+ label=tagList(
121
+ tags$strong("Highlight one or several Foundations by Name")
122
+ ),
123
+ choices = NULL, multiple = TRUE,
124
+ options = list(placeholder = "Search a foundation..."),width="100%") )
125
+
126
+ ),
127
+ div(style = "margin-left: 20px; margin-right:20px; margin-top: 80px; margin-bottom: 20px; font-size: 12px; color: #555; text-align: left;",
128
+ HTML(
129
+ "Use the search bar to find and highlight foundations in all networks at the same time.
130
+ All foundations are displayed by default in blue, while the selected ones will be highlighted in red.<br>
131
+ Here you will discover that foundation similarity is heterogeneous, as it depends on the dimension you explore 😊 !"))
132
+ )),
133
+
134
+ fluidRow(
135
+ column(4, # First Column - 1/3 is 4, as total width is 12 elements)
136
+ box(
137
+ status = "primary",width=12,style = "background-color: rgba(128, 128, 128, 0.1); border: 0.5px solid #000000;",
138
+ plotlyOutput("p_members", height = "60vh"),
139
+ p(HTML(paste("Network arranging foundations by the number of board members in common.<br>
140
+ Dot size indicates the number of board members each foundation has.<br>
141
+ Foundations in the periphery have less board members in common, those overlapping have connected boards")),
142
+ style="margin-left:10px; margin-right:10px; margin-top:10px; margin-bottom:10px; font-size:12px; text-align:center;")
143
+ )),
144
+ column(4, # Second column
145
+ box(
146
+ status = "primary",width=12,style = "background-color: rgba(128, 128, 128, 0.1); border: 0.5px solid #000000;",
147
+ plotlyOutput("p_purposes", height = "60vh"),
148
+ p(HTML(paste("Network arranging foundations by the similarity in their mission statements.<br>
149
+ Transformer-based instructed retrieval ensures that purpose similarity is captured.
150
+ Foundations in the periphery have more specific purposes, those in the center broader purposes.")),
151
+ style="margin-left:10px; margin-right:10px; margin-top:10px; margin-bottom:10px; font-size:12px; text-align:center;")
152
+ )),
153
+ column(4, # Third Column
154
+ box(
155
+ status = "primary",width=12,style = "background-color: rgba(128, 128, 128, 0.1); border: 0.5px solid #000000;",
156
+ plotlyOutput("p_geo", height = "60vh"),
157
+ p(HTML(paste("Network arranging foundations by their geographical proximity, in kilometers.<br>It resembles a map.<br><br><br>")),
158
+ style="margin-left:10px; margin-right:10px; margin-top:10px; margin-bottom:10px; font-size:12px; text-align:center;")
159
+ )),
160
+ ),
161
+
162
+
163
+ fluidRow(column(12,
164
+
165
+ div(style= "margin-left: 20px; margin-right:20px; margin-top:20px; margin-bottom:50px; font-size: 12px; color: #555; text-align: center;",
166
+ "Data compiled for ",
167
+ tags$em("PIO Winter Meeting 2025"),
168
+ " 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)",
169
+ tags$br(),
170
+ "Methodology derived from the Mapping Philanthropy research project. Open Source and available at",
171
+ 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")
172
+ )))
173
+
174
+ ),
175
+
176
+ )
177
+
178
+ # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- #
179
+
180
+
181
+
182
+
183
  server <- function(input, output, session) {
184
+ #bs_themer() # uncomment this to have a theme selector in the navbar, do the same in ui side with theme = bs_theme(version = 4),
185
+
186
+
187
+ # INTERACTIVE MAP
188
+ # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- #
189
+
190
+ # DEFINE SELECTIZEINPUT FOR MAP
191
+ # Load foundation names dynamically
192
+ observe({ updateSelectizeInput(session, "name_clean", choices = unique(geo$name_clean), server = TRUE) })
193
+ # OBSERVE MAP
194
+ # Render initial Leaflet Map with all points in blue
195
+ output$map <- renderLeaflet({ leaflet(geo) %>% addTiles() %>%
196
+ addCircleMarkers(lng =~lon,lat =~lat,label=~name_clean,color ="blue",layerId =~name_clean,radius=3,stroke=FALSE,fillOpacity=1,
197
+ popup =~popup_content)
198
  })
199
 
200
+ # Observe selection and update marker colors
201
+ observeEvent(input$name_clean, {
202
+ leafletProxy("map") %>%
203
+ clearMarkers() %>%
204
+ addCircleMarkers(data = geo, lng = ~lon, lat = ~lat,
205
+ label = ~name_clean,color =~ifelse(name_clean %in% input$name_clean,"red","blue"),layerId = ~name_clean, # color changes with selection
206
+ radius=~ifelse(name_clean %in% input$name_clean,10,3), # size changes with selection
207
+ stroke=FALSE,fillOpacity=1,popup =~popup_content )
208
+ })
209
+
210
+ # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- #
211
+
212
+
213
+
214
+
215
+ # NETWORK EXPLORER
216
+ # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- #
217
+
218
+ # DEFINE SELECTIZEINPUT FOR NETWORK EXPLORER
219
+ observe({ updateSelectizeInput(session, "label_plotly", choices = unique(c(geo$name_clean,p_members_data$label,p_purposes_data$label)), server = TRUE) })
220
+
221
+ output$p_members <- renderPlotly({
222
+ plot_ly(p_members_data, x = ~x, y = ~y) %>%
223
+ add_markers(color = I("blue"),alpha = 0.8,marker = list(size = ~n_members),text=~label,hoverinfo="text") %>%
224
+ filter(label %in% input$label_plotly) %>%
225
+ add_markers(color = I("red"),alpha = 0.8,marker = list(size=20),text=~label,hoverinfo="text") %>%
226
+ toWebGL() %>% layout(showlegend = FALSE,title=list(text="Shared Board Members",font=list(size=20),y=0.9,yanchor="top"),
227
+ xaxis = list(visible = FALSE,scaleanchor = "y"),yaxis = list(visible = FALSE),
228
+ margin = list(t = 100, r = 0, b = 0, l = 0),hoverlabel = list(bgcolor = "white") )
229
+ })
230
+
231
+ output$p_purposes <- renderPlotly({
232
+ plot_ly(p_purposes_data, x = ~x, y = ~y) %>%
233
+ add_markers(color = I("blue"), alpha = 0.5,text=~label,hoverinfo="text") %>%
234
+ filter(label %in% input$label_plotly) %>%
235
+ # WE COULD HAVE DETERMINED THE SIZE!! IT COULD HAVE BEEN THE PURPOSE HETEROGENEITY
236
+ add_markers(color = I("red"),alpha = 0.8,marker=list(size=20),text=~label,hoverinfo="text") %>%
237
+ toWebGL() %>% layout(showlegend = FALSE,title=list(text="Purpose Similarity",font=list(size=20),y=0.9,yanchor="top"),
238
+ xaxis = list(visible = FALSE,scaleanchor = "y"),yaxis = list(visible = FALSE),
239
+ margin = list(t = 100, r = 0, b = 0, l = 0),hoverlabel = list(bgcolor = "white") )
240
+ })
241
+
242
+ output$p_geo <- renderPlotly({
243
+ plot_ly(p_geo_data, x = ~(-y), y = ~x) %>%
244
+ add_markers(color = I("blue"), alpha = 0.5,text=~label,hoverinfo="text") %>%
245
+ filter(label %in% input$label_plotly) %>%
246
+ # WE COULD HAVE DETERMINED THE SIZE!! IT COULD HAVE BEEN THE CLOSENESS TO A CITY CENTER
247
+ add_markers(color = I("red"),alpha = 0.8,marker=list(size=20),text=~label,hoverinfo="text") %>%
248
+ toWebGL() %>% layout(showlegend = FALSE,title=list(text="Geographic Proximity",font=list(size=20),y=0.9,yanchor="top"),
249
+ xaxis = list(visible = FALSE,scaleanchor = "y"),yaxis = list(visible = FALSE),
250
+ margin = list(t = 100, r = 0, b = 0, l = 0),hoverlabel = list(bgcolor = "white") )
251
+ })
252
+
253
+ # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- #
254
+
255
+
256
  }
257
 
258
  shinyApp(ui, server)