library(tidyverse)
library(patchwork)
If ICES rectangles is not a variable in your dataset there are some functions floating around that allows one to assign a geographical position to a rectangle without converting things first to an sf-object. One sweep of such functions is located in the geo-package. The package has been thrown off cran, mostly because improper bookkeeping of c++-functions given the latest standard. So to install it we need:
library(devtools)
install_github("hafro/geo")
library(geo)
Importing data used:
<-
minke read_csv("ftp://ftp.hafro.is/pub/data/csv/minke.csv")
<-
minke %>%
minke select(lon, lat) %>%
mutate(ir = d2ir(lat, lon),
ir_lon = ir2d(ir)$lon,
ir_lat = ir2d(ir)$lat)
%>% glimpse() minke
## Rows: 190
## Columns: 5
## $ lon <dbl> -21.42350, -21.39183, -19.81333, -21.57500, -15.61167, -18.6700…
## $ lat <dbl> 65.66183, 65.65350, 66.51167, 65.67333, 66.29000, 66.17833, 65.…
## $ ir <chr> "60C8", "60C8", "62D0", "60C8", "61D4", "61D1", "60C8", "61C7",…
## $ ir_lon <dbl> -21.5, -21.5, -19.5, -21.5, -15.5, -18.5, -21.5, -22.5, -17.5, …
## $ ir_lat <dbl> 65.75, 65.75, 66.75, 65.75, 66.25, 66.25, 65.75, 66.25, 66.75, …
Now we can do some summary statistics by ICES rectangles (actually the mid-points of lon and lat) and visualize:
<-
minke.sum %>%
minke group_by(ir_lon, ir_lat) %>%
summarise(n = n()) %>%
ungroup()
%>%
minke.sum ggplot() +
geom_tile(aes(x = ir_lon, y = ir_lat, fill = n)) +
geom_text(aes(x = ir_lon, y = ir_lat, label = n), angle = 45, colour = "red") +
coord_quickmap() +
labs(x = NULL, y = NULL)
It is easiest to use functions in the raster-package for setting spatial data on a grid. There are however other means, here we demonstrate using a home-made function (so use with caution).
#' grade
#'
#' @param x A numerical vector to set on a grid
#' @param dx The resolution (NOTE: not tested for values greater than 1)
#'
#' @return A vector of grid midpoint values
<- function(x, dx) {
grade
if(dx > 1) warning("Not tested for grids larger than one")
<- seq(floor(min(x)), ceiling(max(x)),dx)
brks <- findInterval(x, brks, all.inside = TRUE)
ints <- (brks[ints] + brks[ints + 1]) / 2
x return(x)
}
This function in action:
%>%
minke select(lon, lat) %>%
mutate(glon = grade(lon, 0.50),
glat = grade(lat, 0.25)) %>%
glimpse()
## Rows: 190
## Columns: 4
## $ lon <dbl> -21.42350, -21.39183, -19.81333, -21.57500, -15.61167, -18.67000,…
## $ lat <dbl> 65.66183, 65.65350, 66.51167, 65.67333, 66.29000, 66.17833, 65.66…
## $ glon <dbl> -21.25, -21.25, -19.75, -21.75, -15.75, -18.75, -21.25, -22.75, -…
## $ glat <dbl> 65.625, 65.625, 66.625, 65.625, 66.375, 66.125, 65.625, 66.125, 6…
Effectively we have binned the:
Lets repeat the tile-plot we did in the first ggplot but using double as high resolution as the ICES rectangle:
<-
smb2019 read_csv("ftp://ftp.hafro.is/pub/data/csv/is_smb.csv") %>%
filter(year == 2019)
<-
smb2019.g %>%
smb2019 mutate(glon = grade(lon1, 0.50),
glat = grade(lat1, 0.25)) %>%
group_by(glon, glat) %>%
summarise(Cod = mean(cod_n)) %>%
ungroup()
<-
p %>%
smb2019.g ggplot() +
geom_tile(aes(x = glon, y = glat, fill = Cod)) +
coord_quickmap() +
labs(x = NULL, y = NULL)
+
p scale_fill_viridis_c(option = "B", direction = -1)
While we are at it, just as we can control the values on the x- and y-axis we can set the label breaks on colours:
+
p scale_fill_viridis_c(option = "B", direction = -1,
breaks = seq(0, 1500, by = 250))
Some more gritty details on poor-mans gridding can be found here
In ggplot there are a bunch of inbuilt transformation (“asn”, “atanh”, “boxcox”, “date”, “exp”, “hms”, “identity”, “log”, “log10”, “log1p”, “log2”, “logit”, “modulus”, “probability”, “probit”, “pseudo_log”, “reciprocal”, “reverse”, “sqrt” and “time”) that may be useful when plotting spatial data mapped to colour. Take e.g. these two examples:
<-
p1 +
p scale_fill_viridis_c(option = "B", direction = -1,
trans = "log")
<-
p2 +
p scale_fill_viridis_c(option = "B", direction = -1,
trans = "sqrt")
+ p2 p1
One may often want to generate a plot that shows a range of some continuous values as discrete interval. For that one could e.g. use the cut-function. Here is an example were based on intervals that such that the number in each interval are roughly the same:
<-
p %>%
smb2019.g mutate(Cod = cut(Cod,
breaks = c(0, 23, 62, 145, 1600),
include.lowest = TRUE)) %>%
ggplot() +
geom_tile(aes(x = glon, y = glat, fill = Cod)) +
coord_quickmap() +
labs(x = NULL, y = NULL)
+
p scale_fill_viridis_d(option = "B", direction = -1)
# Try a softer colour palette, check also tcrenv
If you only have pure tbl-object, i.e. your object is not sf and/or you are not using the geom_sf-function) you can format the axis using these home-made functions (if there is a bug send me an e-mail):
# https://stackoverflow.com/questions/33302424/format-latitude-and-longitude-axis-labels-in-ggplot
<- function(min = -180, max = 180, step = 1, ...) {
scale_longitude <- seq(min, max, step)
breaks <-
labels ifelse(breaks < 0,
paste0(breaks, "\u00B0", "W"),
paste0(breaks, "\u00B0", "E"))
return(scale_x_continuous(name = NULL, breaks = breaks, labels = labels, ...))
}<- function(min = -90, max = 90, step = 0.5, ...) {
scale_latitude <- seq(min, max, step)
breaks <-
labels ifelse(breaks < 0,
paste0(breaks, "\u00B0", "S"),
paste0(breaks, "\u00B0", "N"))
return(scale_y_continuous(name = NULL, breaks = breaks, labels = labels, ...))
}
<-
p1 ggplot(geo::island, aes(lon, lat)) +
geom_path() +
coord_quickmap()
<-
p2 +
p1 scale_longitude(step = 2) +
scale_latitude(step = 1)
+ p2 p1
Here is an example if you want the ICES rectangles as a label on the x- and the y-axis:
<- function(min = -44, max = 68.5, step = 1, ...) {
scale_longitude_ices <- seq(min + 0.5, max - 0.5, step)
breaks <- geo::d2ir(60, breaks) %>% str_sub(3)
labels return(scale_x_continuous(name = NULL, breaks = breaks, labels = labels, ...))
}<- function(min = 36, max = 84.5, step = 0.5, ...) {
scale_latitude_ices <- seq(min + 0.25, max - 0.25, step)
breaks <- geo::d2ir(breaks, 0) %>% str_sub(1, 2)
labels return(scale_y_continuous(name = NULL, breaks = breaks, labels = labels, ...))
}
ggplot(geo::island, aes(lon, lat)) +
geom_path() +
coord_quickmap() +
geom_path() +
scale_longitude_ices() +
scale_latitude_ices()
A detailed formatting example:
<-
ir ::read_sf("ftp://ftp.hafro.is/pub/data/shapes/ices_rectangles.gpkg") %>%
sffilter(between(west, -25, -14),
between(south, 63, 66))
ggplot() +
#geom_sf(data = ir) +
geom_path(data = geo::island, aes(lon, lat),
colour = "black") +
geom_text(data = ir,
aes(x = west + 0.5, y = south + 0.25,
label = icesname),
angle = 45,
colour = "red",
size = 3) +
scale_longitude_ices() +
scale_latitude_ices() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_line(size = 1),
axis.ticks = element_blank()) +
coord_quickmap()
If you dig deep into ggplot you can set up subplot within your spatial plot, here an example that uses pure tibble-approach. What is shown is the abundance trend by year (1985-2019) of the cod in the Icelandic spring survey:
<-
smb read.csv("ftp://ftp.hafro.is/pub/data/csv/is_smb.csv",
stringsAsFactors = FALSE) %>%
as_tibble()
library(GGally)
<-
by.rect %>%
smb #filter(ir_lon <= -22, ir_lat >= 66) %>%
rename(n = cod_n) %>%
mutate(n = ifelse(n > quantile(n, 0.99), quantile(n, 0.99), n)) %>%
group_by(year, ir_lon, ir_lat) %>%
summarise(n = mean(n))
<-
n.glyph %>%
by.rect #mutate(n = ifelse(n > 0 & n > quantile(n, 0.40), quantile(n, 0.40), n)) %>%
glyphs(x_major = "ir_lon",
y_major = "ir_lat",
x_minor = "year",
y_minor = "n",
width = 1,
height = 0.5)
<-
tows %>%
smb filter(year == 2019)
%>%
n.glyph mutate(pos = ifelse(n != 0, TRUE, FALSE),
base = ir_lat - 0.25,
gy = ifelse(n == 0, gy + 0.005, gy)) %>%
ggplot() +
theme_bw() +
geom_linerange(aes(x = gx, ymin = base, ymax = gy),
colour = "black") +
geom_segment(data = tows,
aes(x = lon1, y = lat1, xend = lon2, yend = lat2),
colour = "red") +
geom_path(data = geo::island, aes(lon, lat)) +
coord_quickmap() +
scale_longitude_ices() +
scale_latitude_ices() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_line(size = 1),
axis.ticks = element_blank())