There and Back Again competition entry https://shiny.petras.space/commute/
rstats
rshiny
census
competition
leaflet
javascript
stats-nz

app.R 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. library(shiny)
  2. library(shinyjs)
  3. library(leaflet)
  4. library(rgdal)
  5. library(dplyr)
  6. library(leaflet.extras)
  7. library(shinyWidgets)
  8. source("leafletfunctions.R")
  9. source("extras.R")
  10. # work_travel <- read_csv("../travel-work.csv")
  11. load(file="datasets.RData")
  12. shpf <- readOGR(dsn="sa20025WGSfilcth")
  13. sa.in.dest <- shpf@data$SA22018_V1 %in% work_to$work_code
  14. sa.in.home <- shpf@data$SA22018_V1 %in% work_from$res_code
  15. transport.t <- c("Work at home", "Private car", "Company car",
  16. "Carpool", "Bus", "Train", "Bicycle", "Walk",
  17. "Ferry", "Other", "None")
  18. edu.t <- c("Drive self", "Passenger in car", "Walk", "Bicycle",
  19. "School bus", "Public bus", "Train", "Ferry", "Study at home",
  20. "Other", "None")
  21. cols.labs <- c(transport.t[1:10], "Total")
  22. cols.edu.labs <- c(edu.t[1:10], "Total")
  23. codelist <- shpf@data %>%
  24. mutate(sa2_code = as.numeric(as.character(SA22018_V1))) %>%
  25. select(sa2_code)
  26. startcols.res <- codelist %>% left_join(work_from, by = c("sa2_code" = "res_code"))
  27. startcols.res <- tencols[startcols.res$MAX]
  28. startcols.res <- ifelse(is.na(startcols.res), "#808080", startcols.res)
  29. startcols.work <- codelist %>% left_join(work_to, by = c("sa2_code" = "work_code"))
  30. startcols.work <- tencols[startcols.work$MAX]
  31. startcols.work <- ifelse(is.na(startcols.work), "#808080", startcols.work)
  32. startcols.edures <- codelist %>% left_join(edu_from,
  33. by = c("sa2_code" = "res_code"))
  34. startcols.edures <- tencols[startcols.edures$MAX]
  35. startcols.edures <- ifelse(is.na(startcols.edures), "#808080", startcols.edures)
  36. startcols.edu <- codelist %>% left_join(edu_to, by = c("sa2_code" = "edu_code"))
  37. startcols.edu <- tencols[startcols.edu$MAX]
  38. startcols.edu <- ifelse(is.na(startcols.edu), "#808080", startcols.edu)
  39. hrstr <- "<hr style='border-top: 1px solid #000;'/>"
  40. # Define UI
  41. ui <- fluidPage(
  42. useShinyjs(),
  43. leafletjs,
  44. tags$style(type = "text/css", extracss),
  45. leafletOutput("map"),
  46. absolutePanel(top = 10, right = 10, id="mapcontrol",
  47. div(
  48. radioButtons("radioeduemp",
  49. label = "Commuters (age 15+) travelling to",
  50. choices = c("Employment", "Education"),
  51. inline = TRUE),
  52. radioButtons("radioinout", label="Show commuters who",
  53. choiceNames = list(
  54. HTML("<p>Commute <b>from</b> selected area</p>"),
  55. HTML("<p>Commute <b>to</b> selected area</p>")),
  56. choiceValues = list(
  57. "res",
  58. "work"
  59. ),
  60. inline = FALSE),
  61. radioButtons("radiocolour",
  62. label = "Colour by",
  63. choiceNames = list(
  64. HTML("<p>Most common commute method</p>"),
  65. HTML("<p>Number of commuters</p>")
  66. ),
  67. choiceValues = list(
  68. "type",
  69. "number"
  70. ),
  71. inline = FALSE),
  72. div(id="locinfo",
  73. htmlOutput("lochtml")))),
  74. absolutePanel(top = 25, right = 10, id="control2",
  75. materialSwitch("controlswitch", value=TRUE, right=TRUE,
  76. inline=TRUE, status="info")),
  77. absolutePanel(bottom = 26, right = 10, id="loading",
  78. p("Loading...")),
  79. absolutePanel(bottom=26, right=10, left=10, top=10, id="infopanel",
  80. infotext),
  81. absolutePanel(bottom=10, left=10, id="infobuttoncontainer",
  82. prettyToggle("mapinfobutton", label_on = "Info",
  83. label_off = "Info", icon_on=icon("times"),
  84. icon_off = icon("info"),
  85. animation = "pulse",
  86. inline = TRUE,
  87. status_on = "danger",
  88. status_off = "info")
  89. )
  90. )
  91. # Define server logic
  92. server <- function(input, output) {
  93. sel.SA2.code <- reactiveVal(0)
  94. attribupdate <- FALSE
  95. output$map <- renderLeaflet({
  96. leaf <- leaflet(shpf, options = leafletOptions(minZoom = 3, maxZoom = 13)) %>%
  97. addPolygons(color="#000", opacity = 1, weight=1,
  98. fillColor = startcols.res,
  99. layerId = ~SA22018_V1,
  100. label = shpf@data$SA22018__1,
  101. fillOpacity = 1) %>%
  102. setView(174, -41, 6) %>%
  103. addResetMapButton() %>%
  104. addLegend(position = "topleft",
  105. colors = c(tencols, "#808080"),
  106. labels = transport.t, opacity = 1,
  107. title = "Commute method")
  108. shinyjs::hideElement(selector="#loading p", asis = TRUE,
  109. anim=TRUE, animType = "slide", time=10)
  110. leaf
  111. })
  112. updateMap <- function() {
  113. shinyjs::showElement(selector="#loading p", asis = TRUE,
  114. anim=TRUE, animType = "slide")
  115. selcode <- sel.SA2.code()
  116. selcode <- ifelse(is.na(selcode), 0, selcode)
  117. psel <- selcode %in% shpf@data$SA22018_V1
  118. if (input$radioeduemp == "Employment") {
  119. if (input$radiocolour == "type") {
  120. if (input$radioinout == "work") {
  121. fcols <- startcols.work
  122. if (psel) {
  123. codvs <- work_simp %>% filter(work_code == selcode)
  124. codvs <- codelist %>% left_join(codvs, by=c("sa2_code" = "res_code"))
  125. codvs <- tencols[codvs$MAX]
  126. fcols <- ifelse(is.na(codvs), "#808080", codvs)
  127. }
  128. } else {
  129. fcols <- startcols.res
  130. if (psel) {
  131. codvs <- work_simp %>% filter(res_code == selcode)
  132. codvs <- codelist %>% left_join(codvs, by=c("sa2_code" = "work_code"))
  133. codvs <- tencols[codvs$MAX]
  134. fcols <- ifelse(is.na(codvs), "#808080", codvs)
  135. }
  136. }
  137. lp <- leafletProxy("map", data = shpf) %>%
  138. setShapeStyle(layerId = ~SA22018_V1, fillColor = fcols) %>%
  139. clearControls() %>%
  140. addLegend(position = "topleft",
  141. colors = c(tencols, "#808080"),
  142. labels = transport.t, opacity = 1,
  143. title = "Commute method"
  144. ) %>%
  145. clearGroup("hpoly")
  146. } else {
  147. if (input$radioinout == "work") {
  148. if (psel) {
  149. codvs <- work_simp %>% filter(work_code == selcode)
  150. codvs <- codelist %>% left_join(codvs, by=c("sa2_code" = "res_code"))
  151. cvals <- ifelse(codvs$total == 0, NA, codvs$total)
  152. } else {
  153. codvs <- codelist %>%
  154. left_join(work_to, by = c("sa2_code" = "work_code"))
  155. cvals <- ifelse(codvs$total == 0, NA, codvs$total)
  156. }
  157. } else {
  158. if (psel) {
  159. codvs <- work_simp %>% filter(res_code == selcode)
  160. codvs <- codelist %>% left_join(codvs, by=c("sa2_code" = "work_code"))
  161. cvals <- ifelse(codvs$total == 0, NA, codvs$total)
  162. } else {
  163. codvs <- codelist %>%
  164. left_join(work_from, by = c("sa2_code" = "res_code"))
  165. cvals <- ifelse(codvs$total == 0, NA, codvs$total)
  166. }
  167. }
  168. cvr <- range(cvals, na.rm = TRUE)
  169. binner <- colorBin(c("white", "red"), cvr, bins = 7, pretty = TRUE)
  170. lp <- leafletProxy("map", data = shpf) %>%
  171. setShapeStyle(layerId = ~SA22018_V1, fillColor = binner(cvals)) %>%
  172. clearControls() %>%
  173. addLegend(position = "topleft",
  174. pal = binner,
  175. values = cvals, opacity = 1,
  176. na.label = "None",
  177. title = "Number of commuters") %>%
  178. clearGroup("hpoly")
  179. }
  180. } else {
  181. # Education code
  182. if (input$radiocolour == "type") {
  183. if (input$radioinout == "work") {
  184. fcols <- startcols.edu
  185. if (psel) {
  186. codvs <- edu_simp %>% filter(edu_code == selcode)
  187. codvs <- codelist %>% left_join(codvs, by=c("sa2_code" = "res_code"))
  188. codvs <- tencols[codvs$MAX]
  189. fcols <- ifelse(is.na(codvs), "#808080", codvs)
  190. }
  191. } else {
  192. fcols <- startcols.edures
  193. if (psel) {
  194. codvs <- edu_simp %>% filter(res_code == selcode)
  195. codvs <- codelist %>% left_join(codvs, by=c("sa2_code" = "edu_code"))
  196. codvs <- tencols[codvs$MAX]
  197. fcols <- ifelse(is.na(codvs), "#808080", codvs)
  198. }
  199. }
  200. lp <- leafletProxy("map", data = shpf) %>%
  201. setShapeStyle(layerId = ~SA22018_V1, fillColor = fcols) %>%
  202. clearControls() %>%
  203. addLegend(position = "topleft",
  204. colors = c(tencols, "#808080"),
  205. labels = edu.t, opacity = 1,
  206. title = "Commute method"
  207. ) %>%
  208. clearGroup("hpoly")
  209. } else {
  210. if (input$radioinout == "work") {
  211. if (psel) {
  212. codvs <- edu_simp %>% filter(edu_code == selcode)
  213. codvs <- codelist %>% left_join(codvs, by=c("sa2_code" = "res_code"))
  214. cvals <- ifelse(codvs$total == 0, NA, codvs$total)
  215. } else {
  216. codvs <- codelist %>%
  217. left_join(edu_to, by = c("sa2_code" = "edu_code"))
  218. cvals <- ifelse(codvs$total == 0, NA, codvs$total)
  219. }
  220. } else {
  221. if (psel) {
  222. codvs <- edu_simp %>% filter(res_code == selcode)
  223. codvs <- codelist %>% left_join(codvs, by=c("sa2_code" = "edu_code"))
  224. cvals <- ifelse(codvs$total == 0, NA, codvs$total)
  225. } else {
  226. codvs <- codelist %>%
  227. left_join(edu_from, by = c("sa2_code" = "res_code"))
  228. cvals <- ifelse(codvs$total == 0, NA, codvs$total)
  229. }
  230. }
  231. cvr <- range(cvals, na.rm = TRUE)
  232. binner <- colorBin(c("white", "red"), cvr, bins = 7, pretty = TRUE)
  233. lp <- leafletProxy("map", data = shpf) %>%
  234. setShapeStyle(layerId = ~SA22018_V1, fillColor = binner(cvals)) %>%
  235. clearControls() %>%
  236. addLegend(position = "topleft",
  237. pal = binner,
  238. values = cvals, opacity = 1,
  239. na.label = "None",
  240. title = "Number of commuters") %>%
  241. clearGroup("hpoly")
  242. }
  243. }
  244. if (psel) {
  245. lp %>% addPolygons(group = "hpoly",
  246. weight = 4,
  247. data = shpf[which(shpf@data$SA22018_V1 == selcode),],
  248. color = "#000000",
  249. fill = FALSE, opacity = 1)
  250. }
  251. shinyjs::hideElement(selector="#loading p", asis=TRUE,
  252. anim=TRUE, animType = "slide",
  253. time = 1)
  254. }
  255. observeEvent(input$map_click, ignoreInit = TRUE, {
  256. cursel <- sel.SA2.code()
  257. p <- input$map_click
  258. pdat <- data.frame(Longitude = p$lng,
  259. Latitude =p$lat)
  260. coordinates(pdat) <- ~ Longitude + Latitude
  261. proj4string(pdat) <- proj4string(shpf)
  262. ppoly <- over(pdat, shpf)
  263. codetmp <- as.numeric(as.character(ppoly[1,"SA22018_V1"]))
  264. codetmp <- ifelse(is.na(codetmp), 0, codetmp)
  265. newsl <- ifelse(sel.SA2.code() == codetmp, 0, codetmp)
  266. if (newsl != cursel) {
  267. sel.SA2.code(newsl)
  268. updateMap()
  269. }
  270. })
  271. observeEvent(input$map_zoom, once=TRUE, {
  272. if (!attribupdate) {
  273. shinyjs::html(selector=".leaflet-control-attribution.leaflet-control",
  274. html = attribhtml)
  275. attribupdate <<- TRUE
  276. }
  277. })
  278. observeEvent(input$map_shape_mouseover, once=TRUE,{
  279. # Backup
  280. if (!attribupdate) {
  281. shinyjs::html(selector=".leaflet-control-attribution.leaflet-control",
  282. html = attribhtml)
  283. attribupdate <<- TRUE
  284. }
  285. })
  286. observeEvent(input$radioeduemp, ignoreInit = TRUE, {
  287. updateMap()
  288. })
  289. observeEvent(input$radioinout, ignoreInit = TRUE, {
  290. updateMap()
  291. })
  292. observeEvent(input$infobutton, {
  293. print(input$infobutton)
  294. })
  295. observeEvent(input$radiocolour, ignoreInit = TRUE, {
  296. updateMap()
  297. })
  298. observeEvent(input$controlswitch, ignoreInit = TRUE, {
  299. shinyjs::toggleElement("mapcontrol", anim=TRUE,
  300. time = 0.5)
  301. })
  302. observeEvent(input$mapinfobutton, ignoreInit = TRUE, {
  303. shinyjs::toggleElement("infopanel", anim=TRUE,
  304. time = 0.5)
  305. })
  306. output$lochtml <- renderUI({
  307. seled <- sel.SA2.code()
  308. seled <- ifelse(is.na(seled), 0, seled)
  309. if (!(seled %in% shpf@data$SA22018_V1)) {
  310. div(class="locinfo",
  311. HTML(paste0(hrstr,
  312. "<p><em>No area selected</em></p>"))
  313. )
  314. } else {
  315. namesel <- shpf@data$SA22018__1[shpf@data$SA22018_V1 == seled]
  316. if (input$radioeduemp == "Employment") {
  317. if (input$radiocolour == "type") {
  318. str <- sprintf("<b>%s</b>", namesel)
  319. if (input$radioinout == "work") {
  320. str <- sprintf("<p>Commuting method of people who <b>work</b> in</p>
  321. <p><b><u>%s</u></b></p>", str)
  322. vals <- as.numeric(work_to[work_to$work_code == seled, 5:15])
  323. vals <- ifelse(is.na(vals), 0, vals)
  324. vals <- ifelse(vals < 0, "~0", as.character(vals))
  325. listi <- paste0(sprintf("<li>%s: %s</li>", cols.labs,
  326. vals),
  327. collapse="")
  328. str <- paste0(hrstr, str, "<ul>", listi, "</ul>")
  329. } else {
  330. str <- sprintf("<p>Commuting method of people who <b>live</b> in</p>
  331. <p><u>%s</u></p>", str)
  332. vals <- as.numeric(work_from[work_from$res_code == seled, 5:15])
  333. vals <- ifelse(is.na(vals), 0, vals)
  334. vals <- ifelse(vals < 0, "~0", as.character(vals))
  335. listi <- paste0(sprintf("<li>%s: %s</li>", cols.labs,
  336. vals),
  337. collapse="")
  338. str <- paste0(hrstr, str, "<ul>", listi, "</ul>")
  339. }
  340. div(class="locinfo",
  341. HTML(str)
  342. )
  343. } else {
  344. str <- hrstr
  345. if (input$radioinout == "work") {
  346. val <- as.numeric(work_to[work_to$work_code == seled, 15])
  347. val <- ifelse(is.na(val), 0, ifelse(val < 0, 0, val))
  348. str <- sprintf("%s<p>%d people commute <b>to</b> employment in</p>
  349. <p><b><u>%s</u></b></p>", str, val, namesel)
  350. if (val > 0) {
  351. subs <- work_simp %>% filter(work_code == seled) %>%
  352. arrange(desc(total)) %>% head(10)
  353. listi <- paste0(sprintf("<li>%s: %s</li>", subs$res_name,
  354. subs$total),
  355. collapse="")
  356. str <- sprintf("%s<p>Top areas to commute from:<p>
  357. <ul>%s</ul>", str, listi)
  358. }
  359. } else {
  360. val <- as.numeric(work_from[work_from$res_code == seled, 15])
  361. val <- ifelse(is.na(val), 0, ifelse(val < 0, 0, val))
  362. str <- sprintf("%s<p>%d people commute to employment <b>from</b></p>
  363. <p><b><u>%s</u></b></p>", str, val, namesel)
  364. if (val > 0) {
  365. subs <- work_simp %>% filter(res_code == seled) %>%
  366. arrange(desc(total)) %>% head(10)
  367. listi <- paste0(sprintf("<li>%s: %s</li>", subs$work_name,
  368. subs$total),
  369. collapse="")
  370. str <- sprintf("%s<p>Top areas to commute to:<p>
  371. <ul>%s</ul>", str, listi)
  372. }
  373. }
  374. div(class="locinfo",
  375. HTML(str)
  376. )
  377. }
  378. } else {
  379. if (input$radiocolour == "type") {
  380. str <- sprintf("<b>%s</b>", namesel)
  381. if (input$radioinout == "work") {
  382. str <- sprintf("<p>Commuting method of people who<br/>go to
  383. <b>education</b> in</p>
  384. <p><b><u>%s</u></b></p>", str)
  385. vals <- as.numeric(edu_to[edu_to$edu_code == seled, 5:15])
  386. vals <- ifelse(is.na(vals), 0, vals)
  387. vals <- ifelse(vals < 0, "~0", as.character(vals))
  388. listi <- paste0(sprintf("<li>%s: %s</li>", cols.edu.labs,
  389. vals),
  390. collapse="")
  391. str <- paste0(hrstr, str, "<ul>", listi, "</ul>")
  392. } else {
  393. str <- sprintf("<p>Commuting method to education<br/>
  394. of people who <b>live</b> in</p>
  395. <p><u>%s</u></p>", str)
  396. vals <- as.numeric(edu_from[edu_from$res_code == seled, 5:15])
  397. vals <- ifelse(is.na(vals), 0, vals)
  398. vals <- ifelse(vals < 0, "~0", as.character(vals))
  399. listi <- paste0(sprintf("<li>%s: %s</li>", cols.edu.labs,
  400. vals),
  401. collapse="")
  402. str <- paste0(hrstr, str, "<ul>", listi, "</ul>")
  403. }
  404. } else {
  405. str <- hrstr
  406. if (input$radioinout == "work") {
  407. val <- as.numeric(edu_to[edu_to$edu_code == seled, 15])
  408. val <- ifelse(is.na(val), 0, ifelse(val < 0, 0, val))
  409. str <- sprintf("%s<p>%d people commute <b>to</b> education in</p>
  410. <p><b><u>%s</u></b></p>", str, val, namesel)
  411. if (val > 0) {
  412. subs <- edu_simp %>% filter(edu_code == seled) %>%
  413. arrange(desc(total)) %>% head(10)
  414. listi <- paste0(sprintf("<li>%s: %s</li>", subs$res_name,
  415. subs$total),
  416. collapse="")
  417. str <- sprintf("%s<p>Top areas to commute from:<p>
  418. <ul>%s</ul>", str, listi)
  419. }
  420. } else {
  421. val <- as.numeric(edu_from[edu_from$res_code == seled, 15])
  422. val <- ifelse(is.na(val), 0, ifelse(val < 0, 0, val))
  423. str <- sprintf("%s<p>%d people commute to education <b>from</b></p>
  424. <p><b><u>%s</u></b></p>", str, val, namesel)
  425. if (val > 0) {
  426. subs <- edu_simp %>% filter(res_code == seled) %>%
  427. arrange(desc(total)) %>% head(10)
  428. listi <- paste0(sprintf("<li>%s: %s</li>", subs$edu_name,
  429. subs$total),
  430. collapse="")
  431. str <- sprintf("%s<p>Top areas to commute to:<p>
  432. <ul>%s</ul>", str, listi)
  433. }
  434. }
  435. }
  436. div(class="locinfo",
  437. HTML(str)
  438. )
  439. }
  440. }
  441. })
  442. }
  443. # Run the application
  444. shinyApp(ui = ui, server = server)