Update app.R
Browse files
app.R
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
|
2 |
options(error = NULL)
|
3 |
library(shiny)
|
@@ -9,26 +10,19 @@ library(rnaturalearth)
|
|
9 |
library(rnaturalearthdata)
|
10 |
library(countrycode)
|
11 |
library(ggplot2)
|
12 |
-
library(ggiraph)
|
13 |
-
#library(extrafont)
|
14 |
-
#font_import() # run
|
15 |
-
#loadfonts() # load the fonts into R session
|
16 |
|
17 |
# =============================
|
18 |
# UI
|
19 |
# =============================
|
20 |
ui <- dashboardPage(
|
21 |
-
|
22 |
-
# Use black skin for the dashboard
|
23 |
skin = "black",
|
24 |
-
|
25 |
dashboardHeader(
|
26 |
title = span(
|
27 |
style = "font-weight: 600; font-size: 18px;",
|
28 |
"Country Representation"
|
29 |
)
|
30 |
),
|
31 |
-
|
32 |
dashboardSidebar(
|
33 |
sidebarMenu(
|
34 |
menuItem("Map Type", tabName = "cartogramTab", icon = icon("globe"))
|
@@ -44,27 +38,21 @@ ui <- dashboardPage(
|
|
44 |
)
|
45 |
)
|
46 |
),
|
47 |
-
|
48 |
dashboardBody(
|
49 |
-
# Bring in the OCR A Extended font from Google Fonts
|
50 |
tags$head(
|
51 |
tags$link(
|
52 |
href = "https://fonts.googleapis.com/css2?family=OCR+A+Extended&display=swap",
|
53 |
rel = "stylesheet"
|
54 |
),
|
55 |
tags$style(HTML("
|
56 |
-
/* Force OCR A Extended font across the entire UI and all HTML elements */
|
57 |
html, body, h1, h2, h3, h4, h5, h6, p, div, span, label, input, button, select,
|
58 |
.box, .content-wrapper, .main-sidebar, .main-header .navbar, .main-header .logo,
|
59 |
.sidebar-menu, .sidebar-menu li a, .sidebar-menu .fa {
|
60 |
font-family: 'OCR A Extended', monospace !important;
|
61 |
}
|
62 |
-
|
63 |
-
/* Header gradient background */
|
64 |
.main-header .navbar {
|
65 |
background: linear-gradient(to right, #3b6978, #204051) !important;
|
66 |
}
|
67 |
-
/* Logo area (left corner of the header) */
|
68 |
.main-header .logo {
|
69 |
background: #1b2a2f !important;
|
70 |
color: #ffffff !important;
|
@@ -72,31 +60,23 @@ ui <- dashboardPage(
|
|
72 |
font-size: 18px;
|
73 |
font-weight: 600;
|
74 |
}
|
75 |
-
/* Sidebar background */
|
76 |
.main-sidebar {
|
77 |
background-color: #1b2a2f !important;
|
78 |
}
|
79 |
-
/* Active or hovered tab in the sidebar */
|
80 |
.sidebar-menu > li.active > a,
|
81 |
.sidebar-menu > li:hover > a {
|
82 |
background-color: #344e5c !important;
|
83 |
border-left-color: #78cdd7 !important;
|
84 |
color: #ffffff !important;
|
85 |
}
|
86 |
-
|
87 |
-
/* Sidebar menu item icons */
|
88 |
.sidebar-menu .fa {
|
89 |
color: #78cdd7 !important;
|
90 |
}
|
91 |
-
|
92 |
-
/* Sidebar menu item text */
|
93 |
.sidebar-menu > li > a {
|
94 |
color: #b8c7ce !important;
|
95 |
font-size: 15px;
|
96 |
font-weight: 500;
|
97 |
}
|
98 |
-
|
99 |
-
/* Customize the boxes */
|
100 |
.box {
|
101 |
border-top: none !important;
|
102 |
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
@@ -107,17 +87,14 @@ ui <- dashboardPage(
|
|
107 |
color: #fff;
|
108 |
border-radius: 6px 6px 0 0;
|
109 |
}
|
110 |
-
/* Plot box spacing */
|
111 |
.box .box-body {
|
112 |
padding: 0 !important;
|
113 |
}
|
114 |
-
/* Footer text styling (plot captions, etc.) */
|
115 |
.small, small {
|
116 |
font-size: 75%;
|
117 |
}
|
118 |
"))
|
119 |
),
|
120 |
-
|
121 |
tabItems(
|
122 |
tabItem(
|
123 |
tabName = "cartogramTab",
|
@@ -126,11 +103,15 @@ ui <- dashboardPage(
|
|
126 |
width = 12,
|
127 |
title = strong("Global Leadership Project (GLP)"),
|
128 |
solidHeader = TRUE,
|
129 |
-
# Use girafeOutput instead of plotOutput
|
130 |
div(
|
131 |
style = "height: 80vh; padding: 10px;",
|
132 |
girafeOutput("cartogramPlot", width = "100%", height = "100%")
|
133 |
)
|
|
|
|
|
|
|
|
|
|
|
134 |
)
|
135 |
)
|
136 |
)
|
@@ -143,11 +124,7 @@ ui <- dashboardPage(
|
|
143 |
# =============================
|
144 |
server <- function(input, output, session) {
|
145 |
|
146 |
-
|
147 |
-
custom_iso_matches <- c("Kosovo" = "XKX",
|
148 |
-
"Somaliland" = "SOM") # or any valid code you prefer
|
149 |
-
|
150 |
-
# 2. Read CSV data and create ISO3 codes with custom matches
|
151 |
rankings_data <- reactive({
|
152 |
read_csv("CountryRepresentationRankings.csv", show_col_types = FALSE) %>%
|
153 |
mutate(iso_a3 = countrycode(
|
@@ -158,30 +135,24 @@ server <- function(input, output, session) {
|
|
158 |
))
|
159 |
})
|
160 |
|
161 |
-
# 3. Read/prepare world map shapefile
|
162 |
world_sf <- reactive({
|
163 |
ne_countries(scale = "medium", returnclass = "sf") %>%
|
164 |
dplyr::select(name, iso_a3, pop_est, geometry) %>%
|
165 |
-
st_transform(crs = "ESRI:54009")
|
166 |
})
|
167 |
|
168 |
-
# 4. Create the joined sf object (currently just a regular map)
|
169 |
cartogram_sf <- reactive({
|
170 |
merged_sf <- world_sf() %>%
|
171 |
left_join(rankings_data(), by = "iso_a3")
|
172 |
-
|
173 |
-
merged_sf <- merged_sf[!is.na(merged_sf$Overall),]
|
174 |
-
merged_sf
|
175 |
})
|
176 |
|
177 |
-
# 5. Render the interactive map with ggiraph
|
178 |
output$cartogramPlot <- renderGirafe({
|
179 |
req(input$indexChoice)
|
180 |
|
181 |
plot_data <- cartogram_sf()
|
182 |
index_col <- input$indexChoice
|
183 |
|
184 |
-
# Build a tooltip string: customize to your preference
|
185 |
plot_data$tooltip_text <- paste0(
|
186 |
"<b>Country:</b> ", plot_data$Country, "<br/>",
|
187 |
"<b>Overall:</b> ", ifelse(!is.na(plot_data$Overall), plot_data$Overall, "N/A"), "<br/>",
|
@@ -192,7 +163,6 @@ server <- function(input, output, session) {
|
|
192 |
"<b>Language:</b> ", ifelse(!is.na(plot_data$Language), plot_data$Language, "N/A")
|
193 |
)
|
194 |
|
195 |
-
# Use geom_sf_interactive with aes() + get() instead of aes_string()
|
196 |
p <- ggplot(plot_data) +
|
197 |
geom_sf_interactive(
|
198 |
aes(
|
@@ -205,8 +175,6 @@ server <- function(input, output, session) {
|
|
205 |
) +
|
206 |
scale_fill_viridis_c(option = "D", na.value = "white") +
|
207 |
coord_sf(expand = FALSE) +
|
208 |
-
# Use a generic base_family to avoid font errors
|
209 |
-
#theme_void(base_size = 14, base_family = "Courier New") +
|
210 |
theme_void(base_size = 14, base_family = "sans") +
|
211 |
labs(
|
212 |
fill = paste(index_col, "Index"),
|
@@ -223,7 +191,6 @@ server <- function(input, output, session) {
|
|
223 |
legend.key.width = unit(2, "cm")
|
224 |
)
|
225 |
|
226 |
-
# Wrap the ggplot in a girafe object, including tooltip styling
|
227 |
girafe(
|
228 |
ggobj = p,
|
229 |
width_svg = 10,
|
@@ -233,10 +200,43 @@ server <- function(input, output, session) {
|
|
233 |
css = "background-color: white;
|
234 |
font-family: 'OCR A Extended', monospace;
|
235 |
color: black;"
|
|
|
|
|
|
|
|
|
236 |
)
|
237 |
)
|
238 |
)
|
239 |
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
240 |
}
|
241 |
|
242 |
# =============================
|
|
|
1 |
+
# setwd("~/Downloads")
|
2 |
|
3 |
options(error = NULL)
|
4 |
library(shiny)
|
|
|
10 |
library(rnaturalearthdata)
|
11 |
library(countrycode)
|
12 |
library(ggplot2)
|
13 |
+
library(ggiraph)
|
|
|
|
|
|
|
14 |
|
15 |
# =============================
|
16 |
# UI
|
17 |
# =============================
|
18 |
ui <- dashboardPage(
|
|
|
|
|
19 |
skin = "black",
|
|
|
20 |
dashboardHeader(
|
21 |
title = span(
|
22 |
style = "font-weight: 600; font-size: 18px;",
|
23 |
"Country Representation"
|
24 |
)
|
25 |
),
|
|
|
26 |
dashboardSidebar(
|
27 |
sidebarMenu(
|
28 |
menuItem("Map Type", tabName = "cartogramTab", icon = icon("globe"))
|
|
|
38 |
)
|
39 |
)
|
40 |
),
|
|
|
41 |
dashboardBody(
|
|
|
42 |
tags$head(
|
43 |
tags$link(
|
44 |
href = "https://fonts.googleapis.com/css2?family=OCR+A+Extended&display=swap",
|
45 |
rel = "stylesheet"
|
46 |
),
|
47 |
tags$style(HTML("
|
|
|
48 |
html, body, h1, h2, h3, h4, h5, h6, p, div, span, label, input, button, select,
|
49 |
.box, .content-wrapper, .main-sidebar, .main-header .navbar, .main-header .logo,
|
50 |
.sidebar-menu, .sidebar-menu li a, .sidebar-menu .fa {
|
51 |
font-family: 'OCR A Extended', monospace !important;
|
52 |
}
|
|
|
|
|
53 |
.main-header .navbar {
|
54 |
background: linear-gradient(to right, #3b6978, #204051) !important;
|
55 |
}
|
|
|
56 |
.main-header .logo {
|
57 |
background: #1b2a2f !important;
|
58 |
color: #ffffff !important;
|
|
|
60 |
font-size: 18px;
|
61 |
font-weight: 600;
|
62 |
}
|
|
|
63 |
.main-sidebar {
|
64 |
background-color: #1b2a2f !important;
|
65 |
}
|
|
|
66 |
.sidebar-menu > li.active > a,
|
67 |
.sidebar-menu > li:hover > a {
|
68 |
background-color: #344e5c !important;
|
69 |
border-left-color: #78cdd7 !important;
|
70 |
color: #ffffff !important;
|
71 |
}
|
|
|
|
|
72 |
.sidebar-menu .fa {
|
73 |
color: #78cdd7 !important;
|
74 |
}
|
|
|
|
|
75 |
.sidebar-menu > li > a {
|
76 |
color: #b8c7ce !important;
|
77 |
font-size: 15px;
|
78 |
font-weight: 500;
|
79 |
}
|
|
|
|
|
80 |
.box {
|
81 |
border-top: none !important;
|
82 |
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
|
87 |
color: #fff;
|
88 |
border-radius: 6px 6px 0 0;
|
89 |
}
|
|
|
90 |
.box .box-body {
|
91 |
padding: 0 !important;
|
92 |
}
|
|
|
93 |
.small, small {
|
94 |
font-size: 75%;
|
95 |
}
|
96 |
"))
|
97 |
),
|
|
|
98 |
tabItems(
|
99 |
tabItem(
|
100 |
tabName = "cartogramTab",
|
|
|
103 |
width = 12,
|
104 |
title = strong("Global Leadership Project (GLP)"),
|
105 |
solidHeader = TRUE,
|
|
|
106 |
div(
|
107 |
style = "height: 80vh; padding: 10px;",
|
108 |
girafeOutput("cartogramPlot", width = "100%", height = "100%")
|
109 |
)
|
110 |
+
),
|
111 |
+
box(
|
112 |
+
width = 12,
|
113 |
+
title = "Country Details",
|
114 |
+
verbatimTextOutput("countryDetails")
|
115 |
)
|
116 |
)
|
117 |
)
|
|
|
124 |
# =============================
|
125 |
server <- function(input, output, session) {
|
126 |
|
127 |
+
custom_iso_matches <- c("Kosovo" = "XKX", "Somaliland" = "SOM")
|
|
|
|
|
|
|
|
|
128 |
rankings_data <- reactive({
|
129 |
read_csv("CountryRepresentationRankings.csv", show_col_types = FALSE) %>%
|
130 |
mutate(iso_a3 = countrycode(
|
|
|
135 |
))
|
136 |
})
|
137 |
|
|
|
138 |
world_sf <- reactive({
|
139 |
ne_countries(scale = "medium", returnclass = "sf") %>%
|
140 |
dplyr::select(name, iso_a3, pop_est, geometry) %>%
|
141 |
+
st_transform(crs = "ESRI:54009")
|
142 |
})
|
143 |
|
|
|
144 |
cartogram_sf <- reactive({
|
145 |
merged_sf <- world_sf() %>%
|
146 |
left_join(rankings_data(), by = "iso_a3")
|
147 |
+
merged_sf[!is.na(merged_sf$Overall),]
|
|
|
|
|
148 |
})
|
149 |
|
|
|
150 |
output$cartogramPlot <- renderGirafe({
|
151 |
req(input$indexChoice)
|
152 |
|
153 |
plot_data <- cartogram_sf()
|
154 |
index_col <- input$indexChoice
|
155 |
|
|
|
156 |
plot_data$tooltip_text <- paste0(
|
157 |
"<b>Country:</b> ", plot_data$Country, "<br/>",
|
158 |
"<b>Overall:</b> ", ifelse(!is.na(plot_data$Overall), plot_data$Overall, "N/A"), "<br/>",
|
|
|
163 |
"<b>Language:</b> ", ifelse(!is.na(plot_data$Language), plot_data$Language, "N/A")
|
164 |
)
|
165 |
|
|
|
166 |
p <- ggplot(plot_data) +
|
167 |
geom_sf_interactive(
|
168 |
aes(
|
|
|
175 |
) +
|
176 |
scale_fill_viridis_c(option = "D", na.value = "white") +
|
177 |
coord_sf(expand = FALSE) +
|
|
|
|
|
178 |
theme_void(base_size = 14, base_family = "sans") +
|
179 |
labs(
|
180 |
fill = paste(index_col, "Index"),
|
|
|
191 |
legend.key.width = unit(2, "cm")
|
192 |
)
|
193 |
|
|
|
194 |
girafe(
|
195 |
ggobj = p,
|
196 |
width_svg = 10,
|
|
|
200 |
css = "background-color: white;
|
201 |
font-family: 'OCR A Extended', monospace;
|
202 |
color: black;"
|
203 |
+
),
|
204 |
+
opts_selection(
|
205 |
+
type = "single",
|
206 |
+
css = "stroke:yellow;stroke-width:2px;"
|
207 |
)
|
208 |
)
|
209 |
)
|
210 |
})
|
211 |
+
|
212 |
+
selected_country_data <- reactive({
|
213 |
+
selected_iso <- input$cartogramPlot_selected
|
214 |
+
if (length(selected_iso) == 0) {
|
215 |
+
return(NULL)
|
216 |
+
}
|
217 |
+
plot_data <- cartogram_sf()
|
218 |
+
country_data <- plot_data %>% filter(iso_a3 == selected_iso)
|
219 |
+
if (nrow(country_data) == 0) {
|
220 |
+
return(NULL)
|
221 |
+
}
|
222 |
+
country_data
|
223 |
+
})
|
224 |
+
|
225 |
+
output$countryDetails <- renderText({
|
226 |
+
country_data <- selected_country_data()
|
227 |
+
if (is.null(country_data)) {
|
228 |
+
return("Click on a country to see details.")
|
229 |
+
}
|
230 |
+
paste0(
|
231 |
+
"Country: ", country_data$Country, "\n",
|
232 |
+
"Overall: ", ifelse(is.na(country_data$Overall), "N/A", country_data$Overall), "\n",
|
233 |
+
"Representation Gap: ", ifelse(is.na(country_data$RepresentationGap), "N/A", country_data$RepresentationGap), "\n",
|
234 |
+
"Ethnicity: ", ifelse(is.na(country_data$Ethnicity), "N/A", country_data$Ethnicity), "\n",
|
235 |
+
"Gender: ", ifelse(is.na(country_data$Gender), "N/A", country_data$Gender), "\n",
|
236 |
+
"Religion: ", ifelse(is.na(country_data$Religion), "N/A", country_data$Religion), "\n",
|
237 |
+
"Language: ", ifelse(is.na(country_data$Language), "N/A", country_data$Language)
|
238 |
+
)
|
239 |
+
})
|
240 |
}
|
241 |
|
242 |
# =============================
|