Using the journal R Markdown theme 😎

R code:

# Load necessary libraries
install.packages(c("jsonlite", "data.table"))
## Installing packages into 'C:/Users/T490/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
## package 'jsonlite' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'jsonlite'
## Warning in file.copy(savedcopy, lib, recursive = TRUE): problem copying
## C:\Users\T490\AppData\Local\R\win-library\4.4\00LOCK\jsonlite\libs\x64\jsonlite.dll
## to
## C:\Users\T490\AppData\Local\R\win-library\4.4\jsonlite\libs\x64\jsonlite.dll:
## Permission denied
## Warning: restored 'jsonlite'
## package 'data.table' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'data.table'
## Warning in file.copy(savedcopy, lib, recursive = TRUE): problem copying
## C:\Users\T490\AppData\Local\R\win-library\4.4\00LOCK\data.table\libs\x64\data_table.dll
## to
## C:\Users\T490\AppData\Local\R\win-library\4.4\data.table\libs\x64\data_table.dll:
## Permission denied
## Warning: restored 'data.table'
## 
## The downloaded binary packages are in
##  C:\Users\T490\AppData\Local\Temp\Rtmp63Z1BM\downloaded_packages
library(jsonlite)
library(data.table)

# Define URLs
regions_url <- "https://keeper.sparecores.net/table/region"
zones_url <- "https://keeper.sparecores.net/table/zone"

# Load data from URLs
regions_json <- fromJSON(regions_url)
zones_json <- fromJSON(zones_url)

# Convert to data.table for easier manipulation
regions <- as.data.table(regions_json)
zones <- as.data.table(zones_json)

The loaded data look like:

str(regions)
## Classes 'data.table' and 'data.frame':   148 obs. of  17 variables:
##  $ name         : chr  "us-central1" "europe-west1" "us-west1" "asia-east1" ...
##  $ zip_code     : logi  NA NA NA NA NA NA ...
##  $ region_id    : chr  "1000" "1100" "1210" "1220" ...
##  $ api_reference: chr  "us-central1" "europe-west1" "us-west1" "asia-east1" ...
##  $ lon          : num  -95.8 3.87 -121.2 120.43 -80.04 ...
##  $ vendor_id    : chr  "gcp" "gcp" "gcp" "gcp" ...
##  $ display_name : chr  "Council Bluffs (US)" "St. Ghislain (BE)" "The Dalles (US)" "Changhua County (TW)" ...
##  $ lat          : num  41.2 50.5 45.6 24.1 33.1 ...
##  $ aliases      :List of 148
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr "nbg1"
##   ..$ : chr "hel1"
##   ..$ : chr "fsn1"
##   ..$ : chr "ash"
##   ..$ : chr "hil"
##   ..$ : chr "sin"
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr "EU (Frankfurt)"
##   ..$ : chr 
##   ..$ : chr "EU (Stockholm)"
##   ..$ : chr "EU (Milan)"
##   ..$ : chr 
##   ..$ : chr "EU (Ireland)"
##   ..$ : chr "EU (London)"
##   ..$ : chr "EU (Paris)"
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   ..$ : chr 
##   .. [list output truncated]
##  $ state        : chr  "Iowa" NA "Oregon" "Changhua County" ...
##  $ founding_year: int  2009 2015 2016 2013 2015 2016 2017 2017 2017 2017 ...
##  $ country_id   : chr  "US" "BE" "US" "TW" ...
##  $ green_energy : logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
##  $ status       : chr  "active" "active" "active" "active" ...
##  $ city         : chr  "Council Bluffs" "St. Ghislain" "The Dalles" NA ...
##  $ observed_at  : chr  "2025-01-31T08:25:22.661169" "2025-01-31T08:25:22.660883" "2025-01-31T08:25:22.661252" "2025-01-31T08:25:22.660620" ...
##  $ address_line : logi  NA NA NA NA NA NA ...
##  - attr(*, ".internal.selfref")=<externalptr>
str(zones)
## Classes 'data.table' and 'data.frame':   353 obs. of  8 variables:
##  $ region_id    : chr  "australiacentral" "australiacentral2" "australiasoutheast" "brazilsoutheast" ...
##  $ zone_id      : chr  "0" "0" "0" "0" ...
##  $ name         : chr  "0" "0" "0" "0" ...
##  $ display_name : chr  "australiacentral-0" "australiacentral2-0" "australiasoutheast-0" "brazilsoutheast-0" ...
##  $ observed_at  : chr  "2025-01-31T08:27:29.508997" "2025-01-31T08:27:29.509045" "2025-01-31T08:27:29.509112" "2025-01-31T08:27:29.509165" ...
##  $ vendor_id    : chr  "azure" "azure" "azure" "azure" ...
##  $ api_reference: chr  "0" "0" "0" "0" ...
##  $ status       : chr  "active" "active" "active" "active" ...
##  - attr(*, ".internal.selfref")=<externalptr>

Let’s count the number of regions per country, shown in desceding order:

region_count <- regions[, .N, by = country_id][order(-N)]
region_count
##     country_id     N
##         <char> <int>
##  1:         US    32
##  2:         AU     9
##  3:         IN     9
##  4:         DE     8
##  5:         JP     6
##  6:         CA     6
##  7:         SG     5
##  8:         GB     5
##  9:         ZA     5
## 10:         BR     4
## 11:         FI     4
## 12:         CH     4
## 13:         KR     4
## 14:         IT     4
## 15:         FR     4
## 16:         ES     4
## 17:         NL     3
## 18:         HK     3
## 19:         PL     3
## 20:         IL     3
## 21:         SE     3
## 22:         AE     3
## 23:         ID     2
## 24:         QA     2
## 25:         CN     2
## 26:         IE     2
## 27:         NO     2
## 28:         BE     1
## 29:         TW     1
## 30:         CL     1
## 31:         SA     1
## 32:         MX     1
## 33:         BH     1
## 34:         NZ     1
##     country_id     N

A nicer table:

install.packages("pander")
## Installing package into 'C:/Users/T490/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
## package 'pander' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\T490\AppData\Local\Temp\Rtmp63Z1BM\downloaded_packages
# Load necessary libraries
library(data.table)
library(pander)

# 
# Count the number of regions per country and sort in descending order
region_count <- regions[, .N, by = country_id][order(-N)]

# Render the table as HTML using pander
pander(region_count)
country_id N
US 32
AU 9
IN 9
DE 8
JP 6
CA 6
SG 5
GB 5
ZA 5
BR 4
FI 4
CH 4
KR 4
IT 4
FR 4
ES 4
NL 3
HK 3
PL 3
IL 3
SE 3
AE 3
ID 2
QA 2
CN 2
IE 2
NO 2
BE 1
TW 1
CL 1
SA 1
MX 1
BH 1
NZ 1

Let’s show the distribution of the founding year of the regions:

# Install ggplot2 if not already installed
install.packages("ggplot2")
## Installing package into 'C:/Users/T490/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
## package 'ggplot2' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\T490\AppData\Local\Temp\Rtmp63Z1BM\downloaded_packages
# Load the ggplot2 package

install.packages("dplyr")
## Installing package into 'C:/Users/T490/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
## package 'dplyr' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\T490\AppData\Local\Temp\Rtmp63Z1BM\downloaded_packages
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:data.table':
## 
##     between, first, last
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)
ggplot(regions %>% filter(!is.na(founding_year)), aes(x = factor(founding_year))) +
  geom_bar() +
  labs(
    title = "Number of datacentres by founding year",
    subtitle = "Note that regions with unknown founding year were excluded from the plot"
  )+
theme_minimal() +
  theme(
    panel.grid.major.x = element_blank(),
    panel.border = element_rect(fill = NA),
    axis.title.x = element_blank(),  # Remove x-axis title
    axis.title.y = element_blank()   # Remove y-axis title# Add plot border
  )

Also showing the average founding year on the same plot:

# Calculate the mean founding year (excluding NA values)
avg_year <- mean(regions$founding_year, na.rm = TRUE)

ggplot(regions %>% filter(!is.na(founding_year)), aes(x = founding_year)) +
  geom_bar() +
   geom_vline(aes(xintercept = avg_year), linetype = "solid", color = "red", size =2)+ 
  labs(
    title = "Number of datacentres by founding year",
    subtitle = "Note that regions with unknown founding year were excluded from the plot"
  )+
theme_minimal() +
  theme(
    panel.grid.major.x = element_blank(),
    panel.border = element_rect(fill = NA),
    axis.title.x = element_blank(),  # Remove x-axis title
    axis.title.y = element_blank()   # Remove y-axis title# Add plot border
  )
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

# Install the 'countrycode' package if not already installed
install.packages("countrycode")
## Installing package into 'C:/Users/T490/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
## package 'countrycode' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\T490\AppData\Local\Temp\Rtmp63Z1BM\downloaded_packages
# Load the package
library(countrycode)

# Add a 'continent' column based on country codes
regions$continent <- countrycode(regions$country_id, "iso2c", "continent")

# Filter for European regions
regions_europe <- regions %>% filter(continent == "Europe")


# Count zones per region and merge the zone count
zone_count_per_region <- zones %>% 
  filter(region_id %in% regions_europe$region_id) %>% 
  group_by(region_id) %>% 
  summarise(zone_count = n())


europe_regions_with_zone_count <- left_join(regions_europe, zone_count_per_region, by = "region_id")



# Plot the results
ggplot(europe_regions_with_zone_count, aes(x = vendor_id, fill = factor(zone_count))) +
  geom_bar(position = "stack") +
  labs(
    y = "Number of regions",
    fill = "Number of availability zones in the region"
  ) +
  scale_fill_manual(values = c("1" = "#fc9272", "3" = "#7fcdbb")) +  # Custom colors
  theme_minimal()+
    theme(
    panel.grid.major.x = element_blank(),
    panel.border = element_rect(fill = NA),
    axis.title.x = element_blank(),  # Remove x-axis title
  ) +
  theme(legend.position = "top")

Now let’s load a GeoJSON file on the boundaries of the European countries. You can use leakyMirror/map-of-europe’s europe.geojson.

Plotting the downloaded shapes:

install.packages("sf")
## Installing package into 'C:/Users/T490/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
## package 'sf' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\T490\AppData\Local\Temp\Rtmp63Z1BM\downloaded_packages
install.packages("ggthemes")
## Installing package into 'C:/Users/T490/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
## package 'ggthemes' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\T490\AppData\Local\Temp\Rtmp63Z1BM\downloaded_packages
library(sf)
## Linking to GEOS 3.12.2, GDAL 3.9.3, PROJ 9.4.1; sf_use_s2() is TRUE
library(ggthemes)  # Optional for better themes

europe_boundaries <- st_read("https://raw.githubusercontent.com/leakyMirror/map-of-europe/refs/heads/master/GeoJSON/europe.geojson")
## Reading layer `europe' from data source 
##   `https://raw.githubusercontent.com/leakyMirror/map-of-europe/refs/heads/master/GeoJSON/europe.geojson' 
##   using driver `GeoJSON'
## Simple feature collection with 51 features and 12 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -24.54222 ymin: 29.48671 xmax: 50.37499 ymax: 71.15471
## Geodetic CRS:  WGS 84
ggplot() +
  geom_sf(data = europe_boundaries) +
  theme_void()

Now let’s get a background tile for this area!

install.packages("maptiles") 
## Installing package into 'C:/Users/T490/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
## package 'maptiles' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\T490\AppData\Local\Temp\Rtmp63Z1BM\downloaded_packages
install.packages("tidyterra")
## Installing package into 'C:/Users/T490/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
## package 'tidyterra' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\T490\AppData\Local\Temp\Rtmp63Z1BM\downloaded_packages
install.packages("terra")  # Install terra package
## Installing package into 'C:/Users/T490/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
## package 'terra' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'terra'
## Warning in file.copy(savedcopy, lib, recursive = TRUE): problem copying
## C:\Users\T490\AppData\Local\R\win-library\4.4\00LOCK\terra\libs\x64\terra.dll
## to C:\Users\T490\AppData\Local\R\win-library\4.4\terra\libs\x64\terra.dll:
## Permission denied
## Warning: restored 'terra'
## 
## The downloaded binary packages are in
##  C:\Users\T490\AppData\Local\Temp\Rtmp63Z1BM\downloaded_packages
library(terra)             # Load terra package
## terra 1.8.15
## 
## Attaching package: 'terra'
## The following object is masked from 'package:pander':
## 
##     wrap
## The following object is masked from 'package:data.table':
## 
##     shift
library(maptiles)
library(tidyterra)  # For raster visualization
## 
## Attaching package: 'tidyterra'
## The following object is masked from 'package:stats':
## 
##     filter
# Define bounding box for Europe
bbox <- st_bbox(europe_boundaries)

# Get the background tiles
background_tile <- get_tiles(bbox, provider = "Stadia.StamenTerrain", zoom = 4, apikey = "c8880324-95c7-4b2c-be2f-c7faf72b49db")

masked_background <- mask(background_tile, europe_boundaries)

ggplot() +
  geom_spatraster_rgb(data = background_tile) +
  theme_void() # Clean theme without axis labels or ticks
## <SpatRaster> resampled to 501280 cells.

Now let’s put together all the loaded layers (background raster map, polygon on country borders, and location of regions weighted by the number of zones, using color to represent if the region is powered by green energy, and use the shape to also visualize the vendor)!

ggplot() +
  geom_spatraster_rgb(data = background_tile) +  # Background tile layer
  geom_point(data = europe_regions_with_zone_count, 
             aes(x = lon, y = lat, color = green_energy, size = as.factor(zone_count), shape = vendor_id)) + 
  scale_color_manual(values = c("TRUE" = "green", "FALSE" = "red")) +  # Correct color mapping for TRUE/FALSE
  scale_size_manual(values = c("1" = 3, "3" = 6)) +  # Adjust point sizes for zone_count.y of 1 and 3
  theme_void() + 
  theme(legend.position = "right")+  # Adjust legend position
  labs(color = "green_energy",        # Rename the color legend
       size = "zones",                # Rename the size legend
       shape = "vendor_id")+
  geom_sf(data = europe_boundaries, fill = NA, color = "black", size = 1)  # Add Europe boundary with black lines
## <SpatRaster> resampled to 501280 cells.