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

app.R 13KB

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