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

app.R 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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. cols.labs <- c(transport.t[1:10], "Total")
  19. codelist <- shpf@data %>%
  20. mutate(sa2_code = as.numeric(as.character(SA22018_V1))) %>%
  21. select(sa2_code)
  22. startcols.res <- codelist %>% left_join(work_from, by = c("sa2_code" = "res_code"))
  23. startcols.res <- tencols[startcols.res$MAX]
  24. startcols.res <- ifelse(is.na(startcols.res), "#808080", startcols.res)
  25. startcols.work <- codelist %>% left_join(work_to, by = c("sa2_code" = "work_code"))
  26. startcols.work <- tencols[startcols.work$MAX]
  27. startcols.work <- ifelse(is.na(startcols.work), "#808080", startcols.work)
  28. hrstr <- "<hr style='border-top: 1px solid #000;'/>"
  29. # Define UI
  30. ui <- fluidPage(
  31. useShinyjs(),
  32. leafletjs,
  33. tags$style(type = "text/css", extracss),
  34. leafletOutput("map"),
  35. absolutePanel(top = 10, right = 10, id="mapcontrol",
  36. radioButtons("radioinout", label="Show commuters who",
  37. choiceNames = list(
  38. HTML("<p>Commute <b>from</b> selected area</p>"),
  39. HTML("<p>Commute <b>to</b> selected area</p>")),
  40. choiceValues = list(
  41. "res",
  42. "work"
  43. ),
  44. inline = FALSE),
  45. radioButtons("radiocolour",
  46. label = "Colour by",
  47. choiceNames = list(
  48. HTML("<p>Most common commute method</p>"),
  49. HTML("<p>Number of commuters</p>")
  50. ),
  51. choiceValues = list(
  52. "type",
  53. "number"
  54. ),
  55. inline = FALSE),
  56. div(id="locinfo",
  57. htmlOutput("lochtml"))),
  58. absolutePanel(top = 25, right = 10, id="control2",
  59. materialSwitch("controlswitch", value=TRUE, right=TRUE,
  60. inline=TRUE, status="info")),
  61. absolutePanel(bottom = 30, left = 10, id="loading",
  62. p("Loading...")),
  63. absolutePanel(bottom=26, right=10, left=10, top=10, id="infopanel",
  64. p("Test")),
  65. absolutePanel(bottom=10, right=10, id="infobuttoncontainer",
  66. prettyToggle("mapinfobutton", label_on = "Info",
  67. label_off = "Info", icon_on=icon("times"),
  68. icon_off = icon("info"),
  69. animation = "pulse",
  70. inline = TRUE,
  71. status_on = "danger",
  72. status_off = "info")
  73. )
  74. )
  75. # Define server logic
  76. server <- function(input, output) {
  77. sel.SA2.code <- reactiveVal(0)
  78. output$map <- renderLeaflet({
  79. leaf <- leaflet(shpf, options = leafletOptions(minZoom = 3, maxZoom = 13)) %>%
  80. addPolygons(color="#000", opacity = 1, weight=1,
  81. fillColor = startcols.res,
  82. layerId = ~SA22018_V1,
  83. label = shpf@data$SA22018__1,
  84. fillOpacity = 1) %>%
  85. setView(174, -41, 6) %>%
  86. addResetMapButton() %>%
  87. addLegend(position = "topleft",
  88. colors = c(tencols, "#808080"),
  89. labels = transport.t, opacity = 1,
  90. title = "Commute method")
  91. shinyjs::hideElement(selector="#loading p", asis = TRUE,
  92. anim=TRUE, animType = "slide", time=10)
  93. leaf
  94. })
  95. updateMap <- function() {
  96. shinyjs::showElement(selector="#loading p", asis = TRUE,
  97. anim=TRUE, animType = "slide")
  98. selcode <- sel.SA2.code()
  99. selcode <- ifelse(is.na(selcode), 0, selcode)
  100. psel <- selcode %in% shpf@data$SA22018_V1
  101. if (input$radiocolour == "type") {
  102. if (input$radioinout == "work") {
  103. fcols <- startcols.work
  104. if (psel) {
  105. codvs <- work_simp %>% filter(work_code == selcode)
  106. codvs <- codelist %>% left_join(codvs, by=c("sa2_code" = "res_code"))
  107. codvs <- tencols[codvs$MAX]
  108. fcols <- ifelse(is.na(codvs), "#808080", codvs)
  109. }
  110. } else {
  111. fcols <- startcols.res
  112. if (psel) {
  113. codvs <- work_simp %>% filter(res_code == selcode)
  114. codvs <- codelist %>% left_join(codvs, by=c("sa2_code" = "work_code"))
  115. codvs <- tencols[codvs$MAX]
  116. fcols <- ifelse(is.na(codvs), "#808080", codvs)
  117. }
  118. }
  119. lp <- leafletProxy("map", data = shpf) %>%
  120. setShapeStyle(layerId = ~SA22018_V1, fillColor = fcols) %>%
  121. clearControls() %>%
  122. addLegend(position = "topleft",
  123. colors = c(tencols, "#808080"),
  124. labels = transport.t, opacity = 1,
  125. title = "Commute method"
  126. ) %>%
  127. clearGroup("hpoly")
  128. } else {
  129. if (input$radioinout == "work") {
  130. if (psel) {
  131. codvs <- work_simp %>% filter(work_code == selcode)
  132. codvs <- codelist %>% left_join(codvs, by=c("sa2_code" = "res_code"))
  133. cvals <- ifelse(codvs$total == 0, NA, codvs$total)
  134. } else {
  135. codvs <- codelist %>%
  136. left_join(work_to, by = c("sa2_code" = "work_code"))
  137. cvals <- ifelse(codvs$total == 0, NA, codvs$total)
  138. }
  139. } else {
  140. if (psel) {
  141. codvs <- work_simp %>% filter(res_code == selcode)
  142. codvs <- codelist %>% left_join(codvs, by=c("sa2_code" = "work_code"))
  143. cvals <- ifelse(codvs$total == 0, NA, codvs$total)
  144. } else {
  145. codvs <- codelist %>%
  146. left_join(work_from, by = c("sa2_code" = "res_code"))
  147. cvals <- ifelse(codvs$total == 0, NA, codvs$total)
  148. }
  149. }
  150. cvr <- range(cvals, na.rm = TRUE)
  151. binner <- colorBin(c("white", "red"), cvr, bins = 7, pretty = TRUE)
  152. lp <- leafletProxy("map", data = shpf) %>%
  153. setShapeStyle(layerId = ~SA22018_V1, fillColor = binner(cvals)) %>%
  154. clearControls() %>%
  155. addLegend(position = "topleft",
  156. pal = binner,
  157. values = cvals, opacity = 1,
  158. na.label = "None",
  159. title = "Number of commuters") %>%
  160. clearGroup("hpoly")
  161. }
  162. if (psel) {
  163. lp %>% addPolygons(group = "hpoly",
  164. weight = 4,
  165. data = shpf[which(shpf@data$SA22018_V1 == selcode),],
  166. color = "#000000",
  167. fill = FALSE, opacity = 1)
  168. }
  169. shinyjs::hideElement(selector="#loading p", asis=TRUE,
  170. anim=TRUE, animType = "slide",
  171. time = 1)
  172. }
  173. observeEvent(input$map_click, ignoreInit = TRUE, {
  174. p <- input$map_click
  175. pdat <- data.frame(Longitude = p$lng,
  176. Latitude =p$lat)
  177. coordinates(pdat) <- ~ Longitude + Latitude
  178. proj4string(pdat) <- proj4string(shpf)
  179. ppoly <- over(pdat, shpf)
  180. codetmp <- as.numeric(as.character(ppoly[1,"SA22018_V1"]))
  181. codetmp <- ifelse(is.na(codetmp), 0, codetmp)
  182. sel.SA2.code(ifelse(sel.SA2.code() == codetmp, 0, codetmp))
  183. updateMap()
  184. })
  185. observeEvent(input$map_shape_mouseover, once=TRUE,{
  186. shinyjs::html(selector=".leaflet-control-attribution.leaflet-control",
  187. html = '
  188. <a href="http://leafletjs.com"
  189. title="A JS library for interactive maps">Leaflet</a> | <a
  190. href="https://datafinder.stats.govt.nz/data/category/census/2018/commuter-view/"
  191. title="Source data">
  192. StatsNZ</a> | <a href="https://petras.space/page/cv/" title="Hire me!">
  193. Petra Lamborn</a> | Numbers subject to <a
  194. href="http://archive.stats.govt.nz/about_us/legisln-policies-protocols/
  195. confidentiality-of-info-supplied-to-snz/safeguarding-confidentiality.aspx"
  196. title="A method of preserving confidentiality and anonymity">
  197. random rounding</a>
  198. '
  199. )
  200. })
  201. observeEvent(input$radioinout, ignoreInit = TRUE, {
  202. updateMap()
  203. })
  204. observeEvent(input$infobutton, {
  205. print(input$infobutton)
  206. })
  207. observeEvent(input$radiocolour, ignoreInit = TRUE, {
  208. updateMap()
  209. })
  210. observeEvent(input$controlswitch, ignoreInit = TRUE, {
  211. shinyjs::toggleElement("mapcontrol", anim=TRUE,
  212. time = 0.5)
  213. })
  214. observeEvent(input$mapinfobutton, ignoreInit = TRUE, {
  215. shinyjs::toggleElement("infopanel", anim=TRUE,
  216. time = 1)
  217. })
  218. output$lochtml <- renderUI({
  219. seled <- sel.SA2.code()
  220. seled <- ifelse(is.na(seled), 0, seled)
  221. if (!(seled %in% shpf@data$SA22018_V1)) {
  222. HTML(paste0(hrstr,
  223. "<p><em>No area selected</em></p>"))
  224. } else {
  225. namesel <- shpf@data$SA22018__1[shpf@data$SA22018_V1 == seled]
  226. if (input$radiocolour == "type") {
  227. str <- sprintf("<b>%s</b>", namesel)
  228. if (input$radioinout == "work") {
  229. str <- sprintf("<p>Commuting method of people who <b>work</b> in</p>
  230. <p><b><u>%s</u></b></p>", str)
  231. vals <- as.numeric(work_to[work_to$work_code == seled, 5:15])
  232. vals <- ifelse(is.na(vals), 0, vals)
  233. vals <- ifelse(vals < 0, "~0", as.character(vals))
  234. listi <- paste0(sprintf("<li>%s: %s</li>", cols.labs,
  235. vals),
  236. collapse="")
  237. str <- paste0(hrstr, str, "<ul>", listi, "</ul>")
  238. } else {
  239. str <- sprintf("<p>Commuting method of people who <b>live</b> in</p>
  240. <p><u>%s</u></p>", str)
  241. vals <- as.numeric(work_from[work_from$res_code == seled, 5:15])
  242. vals <- ifelse(is.na(vals), 0, vals)
  243. vals <- ifelse(vals < 0, "~0", as.character(vals))
  244. listi <- paste0(sprintf("<li>%s: %s</li>", cols.labs,
  245. vals),
  246. collapse="")
  247. str <- paste0(hrstr, str, "<ul>", listi, "</ul>")
  248. }
  249. HTML(str)
  250. } else {
  251. str <- hrstr
  252. if (input$radioinout == "work") {
  253. val <- as.numeric(work_to[work_to$work_code == seled, 15])
  254. val <- ifelse(is.na(val), 0, ifelse(val < 0, 0, val))
  255. str <- sprintf("%s<p>%d people commute <b>to</b></p>
  256. <p><b><u>%s</u></b></p>", str, val, namesel)
  257. if (val > 0) {
  258. subs <- work_simp %>% filter(work_code == seled) %>%
  259. arrange(desc(total)) %>% head(10)
  260. listi <- paste0(sprintf("<li>%s: %s</li>", subs$res_name,
  261. subs$total),
  262. collapse="")
  263. str <- sprintf("%s<p>Top areas to commute from<p>
  264. <ul>%s</ul>", str, listi)
  265. }
  266. } else {
  267. val <- as.numeric(work_from[work_from$res_code == seled, 15])
  268. val <- ifelse(is.na(val), 0, ifelse(val < 0, 0, val))
  269. str <- sprintf("%s<p>%d people commute <b>from</b></p>
  270. <p><b><u>%s</u></b></p>", str, val, namesel)
  271. if (val > 0) {
  272. subs <- work_simp %>% filter(res_code == seled) %>%
  273. arrange(desc(total)) %>% head(10)
  274. listi <- paste0(sprintf("<li>%s: %s</li>", subs$work_name,
  275. subs$total),
  276. collapse="")
  277. str <- sprintf("%s<p>Top areas to commute to<p>
  278. <ul>%s</ul>", str, listi)
  279. }
  280. }
  281. HTML(str)
  282. }
  283. }
  284. })
  285. }
  286. # Run the application
  287. shinyApp(ui = ui, server = server)