September 14, 2021


I made Wot3LdnEmojis: a London-only clone of What3Emojis using London-related emojis and very little R code.

Hover over a grid cell to get the emoji-triplet reference. Zoom and pan, change to dark mode, toggle the grid.

U wot m8

By now you’ve heard about various β€˜alternative’ location systems that split the world into a grid and assign each with a value that’s more human-interpretable than latitude and longitude. For example, Google Plus Codes and What3Words.

The latter has been in the news a lot.1 In the meantime, their product has spawned a raft of spoofs, like the NSFW Four Kings Map, What3Emojis and What2Numbers, lol.2

I like What3Emojis because it’s tongue-in-cheek3, yes, but they also understand the 21st Century mantra that:

No system is perfect, except for emoji.

The What3Emojis code is openly available, but of course I wondered how easy it would be to make something like this in R.

I’ve limited it to London because us Londoners aren’t aware of anything outside of the M25. Read on for the how-to of this obnoxiously-named, cockney-baiting Wot3LdnEmojis system.

Adam β€˜n’ Eve it

First we need to attach the {sf} package for geospatial operations; {leaflet} for interactive mapping; and {tidyverse} for data wrangling. We’ll also set a seed here for reproducible results.4


We can grab the official Greater London boundary from a GeoJSON of geographic units in the UK5, which is served by the Open Geography Portal from the Office for National Statistics.

Or we would, if the site wasn’t down when I went to run this. Instead, we can use the handy JSONs hosted by Martin Chorley on GitHub. Hero.

nuts_path <- paste0(

ldn_sf <- st_read(nuts_path, quiet = TRUE) %>% 
  filter(NUTS112NM == "London")

Simple feature collection with 1 feature and 2 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -0.5102962 ymin: 51.28676 xmax: 0.3339957 ymax: 51.69187
Geodetic CRS:  WGS 84
  NUTS112CD NUTS112NM                       geometry
1       UKI    London MULTIPOLYGON (((-0.3210316 ...

So, it’s an sf-class object that behaves like a dataframe, but has some extra geospatial information stored in it.

We’ve got the boundary, how do we do a grid?

What3Emojis say they used β€˜gazillions of 4mΓ—4m triangles’ in their grid, but I don’t have the computing power for that and I can’t count that high.

Instead, I bring you low-fidelity, massive hexagons. But hexagons are the patron shape of R users, so I think that’s okay.

{sf} has st_make_grid() makes gridding easy. We can pass arguments for size and shape, then st_intersection() limits the grid to the area inside the London boundary only.

grid_sf <- ldn_sf %>% 
  st_make_grid(cellsize = 0.01, square = FALSE, flat_topped = TRUE) %>% 

[1] 2554

I’ve checked the length of the object so we know how many grid cells we need to label uniquely. The fewest number of emoji we’ll need is therefore 14, since 14^3 is 2744.

Of course, I have chosen emojis that at least vaguely represent London. Below I’ve added names and commented with my interpretation. Let me know if you have better ideas.

ldn_emo <- c(
  metro           = "πŸš‡",  # the tube
  guard           = "πŸ’‚",  # Queen's Guard
  queen           = "πŸ‘Έ",  # HMQE2
  castle          = "🏰",  # Tower of London
  ferris_wheel    = "🎑",  # London Eye
  bell            = "πŸ””",  # Big Ben (not a clock!)
  whale           = "πŸ‹",  # Natural History Museum
  cityscape       = "πŸ™οΈ",  # Canary Wharf
  cucumber        = "πŸ₯’",  # The Gherkin
  performing_arts = "🎭",  # Theatre District
  stadium         = "🏟️",  # Wembley Stadium
  dragon          = "πŸ‰",  # City of London emblem
  bird            = "🐦",  # pigeon
  deciduous_tree  = "🌳"   # London plane tree

Depending on your operating system, there’s a chance you might note be able to see some of these emoji. Oh no! Ah well.

You may also have noticed that it’s utterly ridiculous to use London-related emojis to label locations in London. β€˜Where are you?’ β€˜London Eye, London Eye, London Eye’. β€˜You’re at the London Eye?’ β€˜No.’ Oh no! Ah well.

Anyway, we can get all three-way combinations of these with expand.grid(), then shuffle them randomly.

ldn_emo_combo <- expand.grid(
  emo_a = ldn_emo, emo_b = ldn_emo, emo_c = ldn_emo
) %>% 
  sample_n(length(grid_sf)) %>%
  transmute(emo_triplet = paste(emo_a, emo_b, emo_c))

[1] "πŸ‹ 🏟️ 🌳"  "πŸš‡ 🏰 🎑" "🏟️ 🎭 πŸ₯’" 

Then it’s a case of adding the emoji information into the grid_sf object, which can be done via st_df().

grid_sf_emo <- grid_sf %>% 
  st_sf(ldn_emo_combo) %>%
  rename(., geometry = .)

Simple feature collection with 6 features and 1 field
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: -0.1289476 ymin: 51.28676 xmax: 0.07882465 ymax: 51.29676
Geodetic CRS:  WGS 84
  emo_triplet                       geometry
1     πŸ‹ 🏟️ 🌳 POLYGON ((-0.123496 51.2868...
2    πŸš‡ 🏰 🎑 POLYGON ((-0.114679 51.2917...
3     🏟️ 🎭 πŸ₯’ POLYGON ((0.06512026 51.290...
4    🐦 🎑 πŸ‹ POLYGON ((0.07882465 51.291...
5     🏟️ 🌳 🎭 POLYGON ((-0.1149681 51.291...
6       🏟️ 🏟️ 🏟️ POLYGON ((-0.1003241 51.296...

You can see the triplets have been added as an extra column so there’s one triplet per grid cell.

Time to create the interactive map with {leaflet}, which is built up in layers.

I’ve added a light and a dark underlying map that you can toggle between6 I’ve also made the hexagons transparent with thin borders to it’s easier to see the map, but you can toggle the grid on and off to help pinpoint a location.

leaflet() %>% 
  addProviderTiles("CartoDB.Voyager", group = "Light") %>%
  addProviderTiles("CartoDB.DarkMatter", group = "Dark") %>%
    data = grid_sf_emo, group = "Grid", 
    color = "grey", weight = 1, opacity = 0.5,
    fill = TRUE, fillOpacity = 0,
    label = paste(grid_sf_emo$emo_triplet),
    labelOptions = labelOptions(
      direction = "top", style = list("font-size" = "35px")
    highlightOptions = highlightOptions(color = "blue", weight = 3,)
  ) %>% 
    baseGroups = c("Light", "Dark"),
    overlayGroups = "Grid",
    position = "topright",
    options = layersControlOptions(collapsed = FALSE)

I found:

  • Buckingham Palace at πŸ””πŸ¦πŸ₯’ (Big Ben, pigeon, gherkin)
  • Leicester Square at πŸŒ³πŸŒ³πŸ™οΈ (plane tree, plane tree, Canary Wharf)
  • The Shard at πŸ¦πŸ‹πŸ’‚ (pigeon, whale, guard)
  • Wimbledon at πŸ’‚πŸŒ³πŸŸ (guard, plane tree, Wembley)
  • The Millennium Dome at πŸ‘ΈπŸš‡πŸ‰ (Queen, tube, dragon)

Literally minutes of fun. Of course, you shouldn’t use this map for anything whatsoever, possibly not even for your own amusement. I, on the other hand, can do whatever I like.


Session info
Last rendered: 2023-07-21 19:29:22 BST
R version 4.3.1 (2023-06-16)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS Ventura 13.2.1

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0

[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: Europe/London
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] leaflet_2.1.2   lubridate_1.9.2 forcats_1.0.0   stringr_1.5.0  
 [5] dplyr_1.1.2     purrr_1.0.1     readr_2.1.4     tidyr_1.3.0    
 [9] tibble_3.2.1    ggplot2_3.4.2   tidyverse_2.0.0 sf_1.0-14      

loaded via a namespace (and not attached):
 [1] s2_1.1.4                utf8_1.2.3              generics_0.1.3         
 [4] class_7.3-22            KernSmooth_2.23-21      stringi_1.7.12         
 [7] hms_1.1.3               digest_0.6.33           magrittr_2.0.3         
[10] timechange_0.2.0        evaluate_0.21           grid_4.3.1             
[13] fastmap_1.1.1           jsonlite_1.8.7          e1071_1.7-13           
[16] DBI_1.1.3               fansi_1.0.4             crosstalk_1.2.0        
[19] scales_1.2.1            cli_3.6.1               rlang_1.1.1            
[22] units_0.8-2             ellipsis_0.3.2          munsell_0.5.0          
[25] withr_2.5.0             yaml_2.3.7              tools_4.3.1            
[28] tzdb_0.4.0              colorspace_2.1-0        vctrs_0.6.3            
[31] R6_2.5.1                proxy_0.4-27            lifecycle_1.0.3        
[34] classInt_0.4-9          leaflet.providers_1.9.0 htmlwidgets_1.6.2      
[37] pkgconfig_2.0.3         pillar_1.9.0            gtable_0.3.3           
[40] glue_1.6.2              Rcpp_1.0.11             xfun_0.39              
[43] tidyselect_1.2.0        rstudioapi_0.15.0       knitr_1.43.1           
[46] htmltools_0.5.5         rmarkdown_2.23          wk_0.7.3               
[49] compiler_4.3.1         


  1. This post is not about that company or its practices. See instead, for example, the excellent YouTube videos by Andrew Steele and Mia Mulder, or Terence Eden’s blog post. Even the BBC covered it.β†©οΈŽ

  2. I was genuinely tricked by this website’s joke, fair play.β†©οΈŽ

  3. This blog is obviously a fan of emojis, see the one about creating SVG versions of the original emoji set or the one about a package for generating β€˜emojiscapes’.β†©οΈŽ

  4. The seed value has a meaning related to London; guess what it is.β†©οΈŽ

  5. The file contains NUTS level 1, which is a European standard for administrative geographies. Now the UK has left the EU, it has technically switched to something called β€˜International Territorial Units’ (ITU), which I think are the same boundaries as NUTS for the time being.β†©οΈŽ

  6. Darkmode required to prevent damage to poor Millennial eyes when doomscrolling at 0300.β†©οΈŽ