## ----include = FALSE----------------------------------------------------------
has_sf <- requireNamespace("sf", quietly = TRUE)
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = has_sf,
  fig.width = 7,
  fig.height = 2.4,
  dev.args = list(bg = "white")
)

## ----setup, message = FALSE---------------------------------------------------
library(a5R)
library(sf)

## ----helpers, include = FALSE-------------------------------------------------
# Plotting helpers for the figures; the a5R calls are in the visible chunks.
pal <- list(
  a = "#2a78d6", b = "#eb6834", result = "#1baf7a",
  base = "#e4e3df", ink = "#333333", muted = "#8a8984"
)
ramp <- c("#cde2fb", "#9ec5f4", "#6da7ec", "#3987e5", "#256abf", "#184f95", "#0d366b")

cells_of <- function(x) a5_cell_to_boundary(if (is_a5_covering(x)) a5_uncompact(x) else x)

stored_cells <- function(x) a5_covering_stored(x)

panels <- function(n, top = 1.6) {
  par(mfrow = c(1, n), mar = c(0.2, 0.2, top, 0.2), xpd = NA)
}

frame <- function(..., main = "", pad = 0.04) {
  bbs <- lapply(list(...), function(g) unclass(wk::wk_bbox(g)))
  xr <- range(unlist(lapply(bbs, `[`, c("xmin", "xmax"))))
  yr <- range(unlist(lapply(bbs, `[`, c("ymin", "ymax"))))
  xr <- xr + c(-1, 1) * pad * diff(xr)
  yr <- yr + c(-1, 1) * pad * diff(yr)
  plot.new()
  plot.window(xr, yr, asp = 1 / cos(mean(yr) * pi / 180))
  title(main, cex.main = 0.85, font.main = 1, col.main = pal$ink, line = 0.4)
}

fill <- function(g, col, border = "white", lwd = 0.4) {
  if (length(g) > 0) plot(g, col = col, border = border, lwd = lwd, add = TRUE)
}

outline <- function(g, col = pal$ink, lwd = 1.2, lty = 1) {
  plot(g, col = NA, border = col, lwd = lwd, lty = lty, add = TRUE)
}

circle <- function(cell, radius) {
  ll <- a5_cell_to_lonlat(cell, as_dataframe = TRUE)
  t <- seq(0, 2 * pi, length.out = 200)
  d <- radius / 6371008.8 * 180 / pi
  wk::wkt(sprintf("LINESTRING (%s)", paste(
    ll$lon + d * cos(t) / cos(ll$lat * pi / 180), ll$lat + d * sin(t),
    collapse = ", "
  )))
}

# Stored cells shaded by how much coarser they are than the covering.
stored_cols <- ramp[c(6, 4, 2, 1)]
stored_fill <- function(x) {
  s <- stored_cells(x)
  if (length(s) == 0) return(invisible())
  gap <- pmin(a5_covering_resolution(x) - a5_get_resolution(s), 3)
  fill(a5_cell_to_boundary(s), paste0(stored_cols[gap + 1], "c0"), border = pal$ink, lwd = 0.5)
}
stored_legend <- function(res, where = "left") {
  plot.new()
  legend(where, bty = "n", cex = 0.75, fill = paste0(stored_cols, "c0"), border = pal$ink,
         text.col = pal$ink,
         legend = c(paste("res", res - 0:2), paste0("res \u2264 ", res - 3)),
         title = "Stored at", title.adj = 0)
}

# Two rows (cells, then stored cells) of n panels, plus a legend column.
two_rows <- function(n, legend_column = TRUE) {
  m <- matrix(seq_len(2 * n), 2, byrow = TRUE)
  if (legend_column) m <- cbind(m, 2 * n + 1)
  layout(m, widths = c(rep(1, n), if (legend_column) 0.45))
  par(mar = c(0.2, 0.2, 1.6, 0.2), oma = c(0, 1.2, 0, 0), xpd = NA, cex = 1)
}
row_labels <- function() {
  mtext("cells", side = 2, outer = TRUE, at = 0.75, cex = 0.75, col = pal$muted)
  mtext("stored", side = 2, outer = TRUE, at = 0.25, cex = 0.75, col = pal$muted)
}

## ----what---------------------------------------------------------------------
raleigh <- a5_lonlat_to_cell(-78.64, 35.78, resolution = 11)
cap <- a5_spherical_cap(raleigh, radius = 12000)
cap

## ----what-plot, echo = FALSE, fig.height = 2.8--------------------------------
stored <- stored_cells(cap)
layout(matrix(1:3, 1), widths = c(1, 1, 0.4))
par(mar = c(0.2, 0.2, 1.6, 0.2), xpd = NA, cex = 1)
frame(cells_of(cap), main = sprintf("Stands for: %d cells at resolution 11", a5_count(cap)))
fill(cells_of(cap), pal$a)
frame(cells_of(cap), main = sprintf("Stores: %d cells at mixed resolutions", length(stored)))
stored_fill(cap)
stored_legend(11)

## ----polygons-----------------------------------------------------------------
nc <- st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)
nc <- st_transform(nc, 4326) # A5 works in WGS 84 longitude and latitude
alleghany <- nc[nc$NAME == "Alleghany", ]

centre <- a5_polygon_to_cells(alleghany, resolution = 11)
overlapping <- a5_polygon_to_cells(alleghany, resolution = 11, containment = "overlapping")
c(centre, overlapping)

## ----polygons-plot, echo = FALSE, fig.height = 4.6----------------------------
county <- wk::as_wkb(st_geometry(alleghany))
extent <- list(cells_of(overlapping), a5_cell_to_boundary(stored_cells(overlapping)))
two_rows(2)
for (x in list(centre, overlapping)) {
  do.call(frame, c(extent, main = sprintf('containment = "%s"',
    if (identical(x, centre)) "centre" else "overlapping")))
  fill(cells_of(x), pal$a)
  outline(county)
}
for (x in list(centre, overlapping)) {
  do.call(frame, c(extent, main = sprintf("%d stored", length(stored_cells(x)))))
  stored_fill(x)
  outline(county)
}
stored_legend(11)
row_labels()

## ----around-------------------------------------------------------------------
disk <- a5_grid_disk(raleigh, k = 3)
cap <- a5_spherical_cap(raleigh, radius = 8000)
coarse <- a5_cell_to_supercell(raleigh, resolution = 9)
subcells <- a5_cell_to_subcells(coarse, resolution = 11)

## ----around-plot, echo = FALSE, fig.height = 4.4------------------------------
ring <- circle(raleigh, 8000)
ring_xy <- wk::wk_coords(ring)[, c("x", "y")]
ext <- function(x, ...) list(cells_of(x), a5_cell_to_boundary(stored_cells(x)), ...)
two_rows(3)
do.call(frame, c(ext(disk), main = "a5_grid_disk(k = 3)"))
fill(cells_of(disk), pal$a)
fill(cells_of(raleigh), pal$ink)
do.call(frame, c(ext(cap, ring), main = "a5_spherical_cap(8 km)"))
fill(cells_of(cap), pal$a)
lines(ring_xy, col = pal$ink, lwd = 1.2)
fill(cells_of(raleigh), pal$ink)
do.call(frame, c(ext(subcells, cells_of(coarse)), main = "a5_cell_to_subcells(res 9 to 11)"))
fill(cells_of(subcells), pal$a)
outline(cells_of(coarse), lwd = 1.6)
do.call(frame, c(ext(disk), main = sprintf("%d stored", length(stored_cells(disk)))))
stored_fill(disk)
do.call(frame, c(ext(cap, ring), main = sprintf("%d stored", length(stored_cells(cap)))))
stored_fill(cap)
lines(ring_xy, col = pal$ink, lwd = 1.2)
do.call(frame, c(ext(subcells, cells_of(coarse)), main = sprintf("%d stored", length(stored_cells(subcells)))))
stored_fill(subcells)
outline(cells_of(coarse), lwd = 1.6)
stored_legend(11)
row_labels()

## ----measures-----------------------------------------------------------------
nc$cells <- a5_polygon_to_cells(nc, resolution = 11)
nc$cell_area <- a5_area(nc$cells, units = "km^2")
head(nc[c("NAME", "cells", "cell_area")], 3)

## ----measures-plot, echo = FALSE, fig.width = 3.6, fig.height = 3.4, fig.align = "center"----
true_area <- as.numeric(units::set_units(st_area(nc), "km^2"))
par(mar = c(3.2, 3.6, 0.6, 0.6), mgp = c(2.1, 0.5, 0), tcl = -0.25,
    col.axis = pal$muted, col.lab = pal$ink, cex.axis = 0.75, cex.lab = 0.8)
lim <- range(0, true_area, nc$cell_area)
plot(NULL, xlim = lim, ylim = lim, asp = 1, bty = "n", las = 1,
     xlab = "County area (km²)", ylab = "Covering area (km²)")
abline(h = pretty(lim), v = pretty(lim), col = "#f0efec", lwd = 0.8)
abline(0, 1, col = pal$muted, lwd = 1, lty = 2)
points(true_area, nc$cell_area, pch = 21, bg = pal$a, col = "white", cex = 1.1)

## ----combine------------------------------------------------------------------
a <- a5_spherical_cap(a5_lonlat_to_cell(-78.72, 35.78, 11), radius = 10000)
b <- a5_spherical_cap(a5_lonlat_to_cell(-78.58, 35.78, 11), radius = 10000)
a5_union(a, b)
a5_intersect(a, b)
a5_difference(a, b)

## ----combine-plot, echo = FALSE, fig.height = 3.8-----------------------------
both <- cells_of(a5_union(a, b))
results <- list(a5_union(a, b), a5_intersect(a, b), a5_difference(a, b))
extent <- c(list(both), lapply(results, function(x) a5_cell_to_boundary(stored_cells(x))))
two_rows(4, legend_column = FALSE)
do.call(frame, c(extent, main = "a and b"))
fill(cells_of(a), paste0(pal$a, "b0"))
fill(cells_of(b), paste0(pal$b, "b0"))
centres <- a5_cell_to_lonlat(a5_lonlat_to_cell(c(-78.79, -78.51), 35.78, 11), as_dataframe = TRUE)
text(centres$lon, centres$lat, c("a", "b"), col = "white", font = 2, cex = 1.2)
labels <- c("a5_union(a, b)", "a5_intersect(a, b)", "a5_difference(a, b)")
for (i in 1:3) {
  do.call(frame, c(extent, main = labels[i]))
  fill(both, pal$base)
  fill(cells_of(results[[i]]), pal$result)
}
stored_legend(11, where = "center")
for (i in 1:3) {
  do.call(frame, c(extent, main = sprintf("%d stored", length(stored_cells(results[[i]])))))
  fill(both, pal$base)
  stored_fill(results[[i]])
}
row_labels()

## ----recycle------------------------------------------------------------------
near_raleigh <- a5_spherical_cap(raleigh, radius = 60000)
nc$share_near <- a5_count(a5_intersect(nc$cells, near_raleigh)) / a5_count(nc$cells)

state <- a5_union(nc$cells)
state

## ----recycle-plot, echo = FALSE, fig.height = 2.9-----------------------------
share <- nc$share_near
steps <- ramp[c(2, 3, 5, 7)]
cols <- ifelse(share == 0, pal$base, steps[pmax(1, ceiling(share * 4))])
par(mar = c(0.2, 0.2, 0.2, 0.2))
plot(st_geometry(nc), col = cols, border = "white", lwd = 0.6)
lines(wk::wk_coords(circle(raleigh, 60000))[, c("x", "y")], col = pal$ink, lwd = 1.2)
legend("bottomleft", bty = "n", cex = 0.7, border = NA, text.col = pal$ink,
       fill = c(pal$base, steps),
       legend = c("none", "up to 25%", "25 to 50%", "50 to 75%", "75 to 100%"),
       title = "Share of county within 60 km of Raleigh", title.adj = 0)

## ----mismatch, error = TRUE---------------------------------------------------
try({
cap10 <- a5_spherical_cap(a5_cell_to_supercell(raleigh), radius = 60000)
a5_union(state, cap10)
a5_union(a5_covering_to_supercells(state, 10), cap10)
})

## ----nest-plot, echo = FALSE, fig.width = 3.6, fig.height = 3, fig.align = "center"----
coarse_cells <- a5_uncompact(a5_grid_disk(a5_cell_to_parent(raleigh), k = 1))
fine_cells <- a5_cell_to_children(coarse_cells)
par(mar = c(0.2, 0.2, 1.6, 0.2))
frame(cells_of(coarse_cells), main = "Resolution 11 cells over resolution 10 outlines")
fill(cells_of(fine_cells), pal$base, border = "white", lwd = 0.8)
outline(cells_of(coarse_cells), lwd = 1.4)

## ----contains-----------------------------------------------------------------
set.seed(1)
lon <- stats::runif(300, -81.4, -80.9)
lat <- stats::runif(300, 36.35, 36.6)
points <- a5_lonlat_to_cell(lon, lat, resolution = 11)
inside <- a5_contains(centre, points)
table(inside)

## ----contains-plot, echo = FALSE, fig.height = 2.8----------------------------
par(mar = c(0.2, 0.2, 1.6, 0.2))
frame(county, wk::xy(lon, lat), main = "a5_contains(centre, points)")
fill(cells_of(centre), pal$base)
outline(county)
points(lon[!inside], lat[!inside], pch = 21, bg = "white", col = pal$muted, cex = 0.7)
points(lon[inside], lat[inside], pch = 21, bg = pal$a, col = "white", cex = 0.9)
legend("bottomright", bty = "n", cex = 0.7, pch = 21, pt.cex = c(0.9, 0.7),
       pt.bg = c(pal$a, "white"), col = c("white", pal$muted), text.col = pal$ink,
       legend = c("TRUE", "FALSE"))

## ----overlaps-----------------------------------------------------------------
pair <- nc[nc$NAME %in% c("Alleghany", "Ashe"), ]
by_centre <- a5_polygon_to_cells(pair, resolution = 11)
gap_free <- a5_polygon_to_cells(pair, resolution = 11, containment = "overlapping")
a5_overlaps(by_centre[1], by_centre[2])
a5_overlaps(gap_free[1], gap_free[2])

## ----overlaps-plot, echo = FALSE, fig.height = 2.4----------------------------
borders <- wk::as_wkb(st_geometry(pair))
panels(2)
for (step in list(list(by_centre, "centre: no shared cells"),
                  list(gap_free, "overlapping: shared border cells"))) {
  x <- step[[1]]
  frame(cells_of(gap_free), main = step[[2]])
  fill(cells_of(x[1]), paste0(pal$a, "b0"))
  fill(cells_of(x[2]), paste0(pal$b, "b0"))
  fill(cells_of(a5_intersect(x[1], x[2])), pal$result)
  outline(borders, lwd = 0.8)
}

## ----uncompact----------------------------------------------------------------
per_county <- a5_uncompact(nc$cells[1:3], simplify = FALSE)
lengths(per_county)

