--- title: "Csquares objects" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Csquares objects} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r setup} library(csquares) ``` ## The S3 class `csquares` The csquares package has defined a `csquares` class. Objects that are of this class contain a `character` vector or is a `character` vector itself, where the characters represent valid [csquares](https://en.wikipedia.org/wiki/C-squares) codes. The advantage to have a distinct class for these objects is that you automatically inherit all methods that are available for parent classes. The `csquares` object can essentially inherit from the following classes (arranged from simplest to most complex): * `character` vectors; * `data.frame`; * spatial classes: * sf (`sf::st_sf()`); * stars (`stars::st_as_stars()`). It is important to realise that `csquare` objects can have varying parents, which is visualised in the diagram below. What you can do with `csquares` objects in part depends on its parent. Let's start with showing how `csqaures` objects are created and how they will inherit properties from the different parents listed above. ```{r csquares-schematic, echo=FALSE, warning=FALSE, result='asis'} library(DiagrammeR) library(DiagrammeRsvg) library(xml2) grViz( " digraph 'Csquares schematic' { layout = 'dot'; splines = spline; node [fontname = Helvetica shape = box style = filled fillcolor = white class = 'csquare_nodes']; csquares [label = 'csquares' fillcolor = palegreen]; strucstrs [label = '{stars::stars|csquares}' URL='#using-spatiotemporal-arrays-stars' shape = record]; subgraph cluster_dfc { style = filled; fontname = Helvetica; fillcolor = lightcyan; label = 'data.frame csquare'; strucdf [label = '{data.frame|csquares}' URL='#using-data-frames' shape = record]; strucsf [label = '{sf::sf|csquares}' URL='#using-simple-features-sf' shape = record]; } subgraph cluster_char { style = filled; fontname = Helvetica; fillcolor = lightcyan; label = 'character csquare'; strucchar [label = '{character|csquares}' URL='#using-characters' shape = record]; vctr [label = 'vctrs::vctrs_vctr']; } vctr -> strucchar:character strucchar:charcsq -> csquares; strucdf:dfcsq -> csquares; strucsf:sfcsq -> csquares; strucstrs:starscsq -> csquares; strucdf:df -> strucsf:sf; strucchar:charcsq -> strucdf:dfcsq; strucchar:charcsq -> strucsf:sfcsq; } ") |> export_svg() |> read_xml() |> as.character() |> ## By rendering it as html instead of a Graphviz object ## we save a huge chunk of javascript that we don't need (~600 kB) ## the result is still the same htmltools::HTML() ``` ## Creating `csquares` objects ### Using characters The simplest way of creating a `csquares` object is by coercing other objects with `as_csquares()`. The example below shows how to create a `csquares` object from a vector of `characters`: ```{r create-csquares} csquares_char <- as_csquares(c("1000", "3000", "5000", "7000")) ``` ### Using data.frames Perhaps a more useful format is a `data.frame`, as you can add data associated with the spatial squares. You can cast the previous object to a `data.frame` and it will automatically inherit the `csquares` class: ```{r csquares-data.frame} csquares_df <- as.data.frame(csquares_char) class(csquares_df) ``` This means that you can apply all operations that you could apply to a normal `data.frame`. This includes [tidyverse](https://www.tidyverse.org) operations as explained in more detail in `vignette("tidy")`. For example, you can add columns with data: ```{r csquares-obj-mut, message=FALSE} library(dplyr) csquares_df <- csquares_df |> mutate(dummy = 1:4) ``` You can also cast a plain `data.frame` to a csquares object. In that case, your `data.frame` should already contain a column with csquares codes. All you have to do is specify which column this is: ```{r plain-df} orca_csq <- as_csquares(orca, csquares = "csquares") ``` ### Using Simple Features (sf) Remember that csquares encode geographic rectangles, so it makes sense to include this spatial information in the `data.frame`. This is achieved by coercing a `csquares` `data.frame` to a simple features (`sf`) object. It too will automatically inherit the `csquares` class: ```{r csquares-sf, message=FALSE} library(sf) csquares_sf <- st_as_sf(csquares_df) ``` The object `csquares_sf` now has a column holding the csquares codes and a column containing the corresponding geometric features. Both should represent the same spatial object. But be careful! Not all methods are aware of this, so modifying the geometric column might break this association. ### Using Spatiotemporal Arrays (stars) `csquares` objects inheriting from the `stars` are a little trickier. This is because there are more constraints: You cannot include just any csquares at any location with varying resolutions. At the moment `csquares` only support regular linear grids where each grid cell size is the same as that of the associated csquares. To create a `csquares` `stars` object, you have to provide a bounding box and a resolution in degrees: ```{r csquares-stars, message=FALSE} library(stars) csquares_stars <- new_csquares(csquares_sf, resolution = 10L) ``` Even though we use the `csquares_sf` object to create the grid, it doesn't include any of the columns from the `sf` object in the resulting `stars` object. That is because only the bounding box of `csquares_sf` is used to create `csquares_stars`. You can add information to the grid by matching the csquares codes: ```{r csquares-stars-fill} ## create an empty column: csquares_stars[["dummy"]] <- NA csquares_stars[["dummy"]] <- csquares_df[["dummy"]] [ match(csquares_stars[["csquares"]], csquares_df[["csquares"]]) ] ``` Or simply use `left_join()`: ```{r csquares-stars-join} csquares_stars <- left_join(csquares_stars, data.frame(csquares = "1000", foo = "bar"), by = "csquares") ``` ## Validating `csquares` objects When creating a csquares object with `as_csquares`, it is not allowed to pass illegal codes, or codes with wildcards (see `vignette("wildcards")`). It will throw an error. You could try to work around this and create a fake `csquares` object by assigning the class manually to an illegal code. However, if you test its validity you will see that it is not going to fly: ```{r validate} ## Let's create a fake csquares object with a code that is merely impossible: fake_csquares <- "1099" class(fake_csquares) <- "csquares" ## Of course this isn't valid validate_csquares(fake_csquares) ## This one is: validate_csquares(csquares_char) ```