This is a demo page that is both an entry in the 2023 30-Day Map Challenge (Day 14) and an example of the shiny new WebR 0.2.2! Hopefully everything loaded as you took the time to read this intro paragraph (WebR is fast, but we still do need to download a WASM binary and a bunch of R packages).
The Europe map is loaded from a GeoJSON file into R’s {sf} package. We use {dplyr} to join some metadata (where the Tour de France has ridden through), then {ggplot2} to plot it.
While it does support light/dark mode switching (we use dynamic color variables in the {ggplot2} calls), don’t do that too much. There are some unfortunate 2D context errors that I haven’t figured out the cause for (so I can submit a bug report).
Use your browser’s “view source” capability to see the code. It’s all self-contained in this page. All vanilla JavaScript.
---code-annotations: hover---# Day 14: Europe {.unnumbered}<style>td, h2 {border: none;border-bottom: none!important;}figure {padding-left: 20px;padding-right: 20px;}figure h2 {padding-bottom: 0px;line-height: 1em;}figure h3 {margin-top: 0px;}.jan6-ramp {display: block;background: transparent!important;height: auto;height: intrinsic;max-width: 100%;overflow: visible;}.jan6-ramp text {white-space: pre;}g[aria-label="tip"] {fill: #2b2b2b; }</style>## WebR 0.2.2 + {sf}This is a demo page that is both an entry in the 2023 30-Day Map Challenge (Day 14) and an example of the shiny new WebR 0.2.2! Hopefully everything loaded as you took the time to read this intro paragraph (WebR is fast, but we still do need to download a WASM binary and a bunch of R packages).The Europe map is loaded from a GeoJSON file into R's {sf} package. We use {dplyr} to join some metadata (where the Tour de France has ridden through), then {ggplot2} to plot it.While it _does_ support light/dark mode switching (we use dynamic color variables in the {ggplot2} calls), don't do that too much. There are some unfortunate 2D context errors that I haven't figured out the cause for (so I can submit a bug report).Use your browser's "view source" capability to see the code. It's all self-contained in this page. All vanilla JavaScript.WebR is <span id="webr-msg">loading…</span>.::: {.column-screen}<div id="ggplot" style="text-align:center"></div>:::<script type="module">import { WebR } from'https://webr.r-wasm.org/latest/webr.mjs'globalThis.webR=newWebR();await globalThis.webR.init();globalThis.R=asyncfunctionR(strings,...values) {const [ options ] = values;let result ="";for (let i =0; i < strings.length; i++) { result += strings[ i ]; }let res, tmp;if (options ===undefined) { tmp =await webR.evalR(result)if (typeof tmp =="function") return tmp } else { tmp =await webR.evalR(result, options) } res =simplifyRJs(await tmp.toJs())let ret = resreturnPromise.resolve(ret)}exportfunctionsimplifyRJs(obj) {// if the result is a single char/dbl/bool/int then return a plain value// if it's an unnamed vector then return a typed array// if function, return w/o running toJs()let ret = obj;if ([ 'character','double','logical','integer' ].includes(obj.type)) {if (obj.names===null) {if (obj.values.length<=1) { ret = obj.values[ 0 ] } else { ret = obj.values } } }return ret}globalThis.isDarkMode=window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches;let webrMessage =document.querySelector("#webr-status");const europeRes =awaitfetch('static/data/europe.geojson')const europeTxt =await europeRes.text()await webR.objs.globalEnv.bind('europe', europeTxt)const ggplotDiv =document.querySelector("#ggplot")awaitR` webr::install(c("ggplot2", "sf", "dplyr")) suppressPackageStartupMessages({ library(ggplot2) library(dplyr) library(sf) })`;document.querySelector("#webr-msg").innerText="loaded!"awaitR`eur <- st_read(europe, crs = 4326, quiet = TRUE) eur |> left_join( data.frame( NAME = c( "Andorra", "Belgium", "Denmark", "France", "Germany", "Ireland", "Italy", "Luxembourg", "Monaco", "Netherlands", "Spain", "San Marino", "Switzerland", "United Kingdom" ), tdf = "TDF Visited" ), by = join_by(NAME) ) -> eur_sf`;asyncfunctiondrawMap() {// re-visit when i have more timeconst foregroundColor = globalThis.isDarkMode?"white":"white";await webR.evalRVoid(` webr::canvas(1040, 800) ggplot() + geom_sf( data = eur_sf, aes( fill = tdf, color = I(ifelse(is.na(tdf), "black", "#e6491d")), linewidth = I(ifelse(is.na(tdf), 0.125, 0.25)) ), ) + coord_sf( crs = 3035 ) + scale_fill_manual( name = NULL, values = c( "TDF Visited" = "#edbc31" ), na.value = "#f3e3bd", drop = TRUE ) + guides( fill = guide_legend( override.aes = list( colour = "#e6491d" ) ) ) + labs( title = "Countries Visited By The 🚴🏼♀️ Tour de France 🇫🇷", caption = r"(A 2023 30-Day Map Challenge Entry For Day 14 ("Europe") by @hrbrmstr to showcase WebR 0.2.2)" ) + theme_void() + theme( plot.title = element_text( colour = "${foregroundColor}", size = 24, hjust = 0 ), plot.caption = element_text( colour = "${foregroundColor}" ), legend.text = element_text( colour = "${foregroundColor}", size = 12 ), legend.position = c(0.025, 0.7) ) -> gg print(gg) dev.off() `)let canvas =null;for (;;) {const output =await webR.read();switch (output.type) {case'canvas':if (output.data.event==='canvasImage') { canvas.getContext('2d').drawImage(output.data.image,0,0); } elseif (output.data.event==='canvasNewPage') { canvas =document.createElement('canvas'); canvas.setAttribute('width','2080'); canvas.setAttribute('height','1600'); canvas.style.width="1040px"; canvas.style.height="800px"; canvas.style.display="block"; canvas.style.margin="auto"; ggplotDiv.appendChild(canvas); }break; } }}drawMap()// does not work consistently// window.matchMedia("(prefers-color-scheme: dark)")// .addEventListener("change", event => {// if (event.matches) {// globalThis.isDarkMode = true// ggplotDiv.replaceChildren()// drawMap()// } else {// globalThis.isDarkMode = false// ggplotDiv.replaceChildren()// drawMap()// }// });</script>