As already mentioned in the comments, drawing this kind of axes in ggplot2 could be achieved via rects or segments. I went for geom_rects as they allow for both a fill and a color. While doing such a legend is "trivial" in that it simply means to draw some rects in my opinion it isn't as it demands some (or a lot of?) data wrangling to set up the data for drawing the rectangles, ...
Basically I make
- use of
geom_rect to draw the segments of the axis,
- use of
geom_rect to draw the corners,
- remove the default expansion of the scale,
- added a separate fill scale for the rects via
ggnewscale
- Tweaked the axis ticks to have the same size as the rect border.
I tried my best to make my approach as general as possible but there is still a lot of space for improvement, e.g. in my approach you have to manually set the scales of the axes, i.e. longitudes and latitudes.
library(ggplot2)
library(ggnewscale)
library(dplyr)
nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)
axis_black_white <- function(xl, xr, yb, yt, width = .05, size = .1,
x.breaks = ggplot2::waiver(), y.breaks = ggplot2::waiver()) {
if (xl > xr) { h <- xr; xr <- xl; xl <- h }
if (yb > yt) { h <- yb; yb <- yt; yt <- h }
# Top and bottom
xmin <- seq(xl, xr - 1, 1)
xmax <- seq(xl + 1, xr, 1)
df1 <- data.frame(where = "b", xmin = xmin, xmax = xmax, ymin = yb, ymax = yb - width)
df2 <- data.frame(where = "t", xmin = xmin, xmax = xmax, ymin = yt, ymax = yt + width)
dfx <- dplyr::bind_rows(df1, df2) %>%
dplyr::mutate(fill = xmin %% 2 == 0)
# Left and right
ymin <- seq(yb, yt - 1, 1)
ymax <- seq(yb + 1, yt, 1)
df1 <- data.frame(where = "l", xmin = xl, xmax = xl - width, ymin = ymin, ymax = ymax)
df2 <- data.frame(where = "r", xmin = xr, xmax = xr + width, ymin = ymin, ymax = ymax)
dfy <- dplyr::bind_rows(df1, df2) %>%
dplyr::mutate(fill = ymin %% 2 == 0)
dfxy <- bind_rows(dfx, dfy)
# Corners
dfc <- data.frame(
where = c("tl", "tr", "bl", "br"),
xmin = c(xl - width, xr, xl - width, xr),
xmax = c(xl, xr + width, xl, xr + width),
ymin = c(yb - width, yb - width, yt + width, yt + width),
ymax = c(yb, yb, yt, yt),
fill = c(TRUE, TRUE, FALSE, FALSE)
)
list(
scale_x_continuous(breaks = x.breaks, expand = c(0, 0)),
scale_y_continuous(breaks = y.breaks, expand = c(0, 0)),
ggnewscale::new_scale_fill(),
geom_rect(
data = dfxy, aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, fill = fill),
color = "black", size = size
),
geom_rect(
data = dfc, aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, fill = fill),
color = "black", size = size
),
scale_fill_manual(values = c("white", "black"), guide = "none"),
theme(axis.ticks = element_line(size = size))
)
}
ggplot(nc) +
geom_sf(aes(fill = AREA)) +
scale_fill_viridis_c(option = "plasma") +
axis_black_white(-86, -74, 33, 37, width = .1, y.breaks = seq(33, 37, 1))
