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

extras.R 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. extracss <- "
  2. html, body {
  3. width:100%;
  4. height:100%
  5. }
  6. #map {
  7. height: 100% !important;
  8. position: absolute !important;
  9. top: 0;
  10. left: 0;
  11. }
  12. #loading {
  13. cursor: progress !important;
  14. z-index: 1002;
  15. }
  16. #loading p {
  17. border-radius: 5px;
  18. background-color: rgba(255, 255, 255, 0.8);
  19. padding: 6px 8px;
  20. box-shadow: 0 0 15px rgba(0,0,0,0.2);
  21. font-size: 1.5em;
  22. font-weight: bold;
  23. margin: 0;
  24. }
  25. #mapcontrol, #infobuttoncontainer, #infopanel {
  26. background-color: rgba(255, 255, 255, 0.8);
  27. border-radius: 5px;
  28. box-shadow: 0 0 15px rgba(0,0,0,0.2);
  29. padding: 6px 8px;
  30. font: 14px/16px Arial, Helvetica, sans-serif;
  31. }
  32. #mapcontrol, #control2 {
  33. z-index: 1000;
  34. }
  35. #mapcontrol {
  36. width: 22em;
  37. max-height: 90%;
  38. overflow: auto;
  39. -ms-overflow-style: none; /* IE and Edge */
  40. scrollbar-width: none; /* Firefox */
  41. }
  42. #mapcontrol::-webkit-scrollbar {
  43. display: none;
  44. }
  45. #infopanel {
  46. background-color: rgba(255, 255, 255, 0.9);
  47. padding: 10px;
  48. z-index: 1001;
  49. }
  50. #lochtml ul {
  51. padding-left: 15px;
  52. }
  53. .radio label span p {
  54. margin-top: 3px;
  55. margin-bottom: 0px;
  56. }
  57. .leaflet-container {
  58. background-color: #84e1e1;
  59. }
  60. #infobuttoncontainer {
  61. margin-bottom: 16px;
  62. z-index: 1002;
  63. }
  64. #infobuttoncontainer .shiny-input-container,
  65. #infobuttoncontainer div div {
  66. margin-right: 0px !important;
  67. margin-bottom: 0px !important;
  68. }
  69. h3 {
  70. margin-top: 5px;
  71. margin-bottom: 5px;
  72. }
  73. .overflowable {
  74. max-height: 100%;
  75. overflow: auto;
  76. -ms-overflow-style: none; /* IE and Edge */
  77. scrollbar-width: none; /* Firefox */
  78. }
  79. .overflowable::-webkit-scrollbar {
  80. display: none;
  81. }
  82. blockquote {
  83. font-size: 1em;
  84. font-style: italic;
  85. border: none;
  86. margin: 0;
  87. }
  88. .scrollbuffer {
  89. height: 50px;
  90. }
  91. .overflowable:before {
  92. content:'';
  93. width:100%;
  94. height:50px;
  95. position:absolute;
  96. left:0;
  97. bottom:5px;
  98. right:0;
  99. background:linear-gradient(rgba(255, 255, 255, 0) 0px, rgba(255, 255, 255, 0.9));
  100. }
  101. #infobuttoncontainer label {
  102. font-weight: bold;
  103. }
  104. .locinfo {
  105. max-width: 100%;
  106. }
  107. .loading p {
  108. margin: 0;
  109. }
  110. hr {
  111. margin-top: 10px;
  112. margin-bottom: 10px;
  113. border-top: 1px solid #000;
  114. }
  115. .leaflet-control-search {
  116. box-shadow: none;
  117. }
  118. .shortcut {
  119. text-decoration: underline 1px dotted;
  120. }
  121. #shortcutlist {
  122. columns: 20em 2;
  123. }
  124. kbd {
  125. color: black;
  126. background-color: #eee;
  127. border-radius: 4px;
  128. padding: 1px 6px;
  129. border-color: #444;
  130. border-width: 1px;
  131. -webkit-box-shadow: 1px 1px #444;
  132. box-shadow: 1px 1px #444;
  133. font-size: 120%;
  134. font-family: arial, Helvetica, sans-serif;
  135. cursor: pointer;
  136. }
  137. kbd:hover {
  138. background-color: #ddd;
  139. box-shadow: 0.5px 0.5px;
  140. -webkit-box-shadow: 0.5px 0.5px;
  141. }
  142. #infoint li {
  143. padding-bottom: 10px;
  144. }
  145. "
  146. attribhtml <- '
  147. <a href="http://leafletjs.com"
  148. title="A JS library for interactive maps">Leaflet</a> | <a
  149. href="http://datafinder.stats.govt.nz/data/category/census/2018/commuter-view/"
  150. title="Source data">
  151. StatsNZ</a> | <a href="http://petras.space/page/cv/" title="Hire me!">
  152. Petra Lamborn</a> | Numbers subject to <a
  153. href="http://archive.stats.govt.nz/about_us/legisln-policies-protocols/
  154. confidentiality-of-info-supplied-to-snz/safeguarding-confidentiality.aspx"
  155. title="A method of preserving confidentiality and anonymity">
  156. random rounding</a>
  157. '
  158. infotext <- div(class="overflowable", id="infoint", tabindex="2",
  159. h3("How did Kiwis commute in 2018?"),
  160. p("This tool maps the 2018 census",
  161. a(href=
  162. "https://datafinder.stats.govt.nz/data/category/census/2018/commuter-view/",
  163. "commuter data"),
  164. "to help visualise transport ",
  165. "connections. It is ", a(href="http://petras.space", "Petra Lamborn's"),
  166. " entry for the ", em("There and Back Again"),
  167. a(href=
  168. "https://www.stats.govt.nz/2018-census/there-and-back-
  169. again-data-visualisation-competition", "data visualisation competition",
  170. .noWS = "after"),
  171. HTML(". The employment data counts employed persons 15 years or ",
  172. "older who gave an employment location in the 2018 census, while the ",
  173. "education data counts people in education 15 years or older who gave an ",
  174. "education location in the 2018 census—including older highschoolers and ",
  175. "university students, but not e.g. primary school students.")),
  176. h4("The data"),
  177. p("The 2018 New Zealand Census of Population and Dwellings",
  178. a(href=paste0("https://cdm20045.contentdm.oclc.org/digital/",
  179. "collection/p20045coll2/id/713/rec/3"), "asked"),
  180. "individuals for primary locations of employment and education ",
  181. "and their usual method of transportation. Stats NZ has released",
  182. a(href=
  183. "https://datafinder.stats.govt.nz/data/category/census/2018/commuter-view/",
  184. "these data"),
  185. "aggregated at the level of",
  186. a(href=paste0(
  187. "http://archive.stats.govt.nz/methods/classifications-and-standards/",
  188. "classification-related-stats-standards/geographic-areas/pg4.aspx#gsc.tab=0"),
  189. "Statistical Area 2", .noWS = "after"), ". ",
  190. "SA2 boundaries typically enclose areas with a population of a few ",
  191. "thousand, corresponding approximately to urban suburbs and rural towns. ",
  192. "The shapes of these areas have been heavily simplified in this map ",
  193. "to reduce bandwidth and memory usage.",
  194. a(href=
  195. "https://datafinder.stats.govt.nz/data/category/census/2018/commuter-view/",
  196. "The original boundaries can be viewed and downloaded from the Stats NZ ",
  197. "datafinder website.")),
  198. h4("Options"),
  199. p("The top right panel (toggleable via the blue switch) allows ",
  200. "you to choose between the employment and education datasets, ",
  201. "visualising the people who commute from ",
  202. "and to each area to both employment and education, and between ",
  203. "showing numbers of people or ",
  204. "their primary mode of transportation."),
  205. p("Select an area by clicking on it; deselect by clicking it again ",
  206. "or clicking in the water. When an area is selected the map is ",
  207. "coloured according to the people who commute to or from that area ",
  208. "(including the people who commute within the area). When no area is ",
  209. "selected, colouring is according to commutes to or from all ",
  210. "localities. Hover over areas for a summary (on a tablet, hold press)."),
  211. h4("Keyboard shortcuts"),
  212. shiny::tags$ul(id="shortcutlist",
  213. shiny::tags$li(shiny::tags$kbd("I"), ": toggle this info page"),
  214. shiny::tags$li(shiny::tags$kbd("M"), ": focus map"),
  215. shiny::tags$ul(
  216. shiny::tags$li(shiny::tags$kbd("+"), shiny::tags$kbd("-"),
  217. ": zoom map"),
  218. shiny::tags$li(shiny::tags$kbd(HTML("&larr;")),
  219. shiny::tags$kbd(HTML("&uarr;")),
  220. shiny::tags$kbd(HTML("&rarr;")),
  221. shiny::tags$kbd(HTML("&darr;")),
  222. ": move map")
  223. ),
  224. shiny::tags$li(shiny::tags$kbd("S"), ": search map"),
  225. shiny::tags$li(shiny::tags$kbd("P"), ": show employment data"),
  226. shiny::tags$li(shiny::tags$kbd("D"), ": show education data"),
  227. shiny::tags$li(shiny::tags$kbd("F"), ": show people commuting from localities"),
  228. shiny::tags$li(shiny::tags$kbd("T"), ": show people commuting to localities"),
  229. shiny::tags$li(shiny::tags$kbd("O"), ": colour by most common commute type"),
  230. shiny::tags$li(shiny::tags$kbd("U"), ": colour by number of commuters")
  231. ),
  232. h4("FAQ"),
  233. shiny::tags$blockquote("Why are so many areas marked as 'works at ",
  234. "home'?"),
  235. p("Working from home includes working from your kitchen table, ",
  236. "but also farming. Anyone who is employed but does not commute ",
  237. "is included in this category."),
  238. shiny::tags$blockquote("Why is every number divisible by three?"),
  239. p("To ensure anonymity and the confidentiality of your census data, ",
  240. "Statistics New Zealand employs a technique called ",
  241. em(a(href=paste0(
  242. "http://archive.stats.govt.nz/about_us/legisln-policies-protocols/",
  243. "confidentiality-of-info-supplied-to-snz/safeguarding-confidentiality.aspx"),
  244. "random rounding", .noWS = "after"), .noWS = "after"), ". ",
  245. "This method rounds all values to a multiple of three, but one-third of ",
  246. "the time rounds to the second nearest multiple, rather than the closest. ",
  247. "Additionally, rounded values less than six are censored."),
  248. p("Note that this also means totals do not always add up."),
  249. shiny::tags$blockquote("In some views there are areas that are greyed ",
  250. "out when looking at transport type, but not ",
  251. "when looking at numbers of people. How does ",
  252. "that work?"),
  253. p("As mentioned above some low values are censored to avoid providing ",
  254. "potentially identifiable information. However it is possible for the ",
  255. "total number of people to be large enough to escape censoring while ",
  256. "all transport", em("type"), "numbers are too small and so no most ",
  257. "common type can be determined."),
  258. shiny::tags$blockquote("Does anyone commute to (or from?) the Chatham Islands?"),
  259. p("No, but some people commute within it. Of 249 people commuting to ",
  260. "employment, 105 travel by private car, 24 by company car, 18 walk, 12 ",
  261. "are passengers in cars driven by other people, and 93 work at home. ",
  262. "Of 84 people commuting to education, 39 take a schoolbus, 9 walk, 9 are ",
  263. "passengers in a car, and 15 are educated at home."),
  264. div(class="scrollbuffer")
  265. )
  266. keyboardjs <- tags$head(tags$script(HTML("
  267. $(function(){
  268. $(document).keyup(function(e) {
  269. var active = document.activeElement
  270. if (active.id == 'searchtext9') return;
  271. switch(e.key) {
  272. case 'p':
  273. case 'P':
  274. document.getElementsByName('radioeduemp')[0].checked = true;
  275. Shiny.onInputChange('radioeduemp', 'Employment')
  276. break;
  277. case 'd':
  278. case 'D':
  279. document.getElementsByName('radioeduemp')[1].checked = true;
  280. Shiny.onInputChange('radioeduemp', 'Education')
  281. break;
  282. case 'f':
  283. case 'F':
  284. document.getElementsByName('radioinout')[0].checked = true;
  285. Shiny.onInputChange('radioinout', 'res')
  286. break;
  287. case 't':
  288. case 'T':
  289. document.getElementsByName('radioinout')[1].checked = true;
  290. Shiny.onInputChange('radioinout', 'work')
  291. break;
  292. case 'o':
  293. case 'O':
  294. document.getElementsByName('radiocolour')[0].checked = true;
  295. Shiny.onInputChange('radiocolour', 'type')
  296. break;
  297. case 'u':
  298. case 'U':
  299. document.getElementsByName('radiocolour')[1].checked = true;
  300. Shiny.onInputChange('radiocolour', 'number')
  301. break;
  302. case 'i':
  303. case 'I':
  304. shinyjs.click('mapinfobutton')
  305. break;
  306. case 'm':
  307. case 'M':
  308. document.getElementById('map').focus()
  309. break;
  310. case 's':
  311. case 'S':
  312. var evObj = document.createEvent('Events');
  313. evObj.initEvent('click', true, false);
  314. document.getElementsByClassName('search-button')[0].dispatchEvent(evObj);
  315. break;
  316. default:
  317. break;
  318. }
  319. });
  320. })
  321. ")))