|
| 1 | +# How to interact with the evoland-plus codebase |
| 2 | + |
| 3 | +_Naturally, this is also handy for human devs._ |
| 4 | + |
| 5 | +This is the codebase for evoland-plus, or evoland for short. |
| 6 | +It is an R package that is used for land use / land cover change analyses and simulations. |
| 7 | +The main concept that differentiates this package from e.g. <https://github.com/simonmoulds/lulcc> is that the data are processed in tabular form. |
| 8 | +This enables the storage and processing of sparse domains. |
| 9 | + |
| 10 | +Storage is done in parquet files written and read via duckdb, with the domain-agnostic DB implementation in `R/parquet_duckdb.R`. |
| 11 | +Using a normal duckdb file is very inefficient in terms of storage/compression; the parquet files have the advantage of being writable by other software, so they should be considered part of the interface and schema changes should be avoided as much as possible. |
| 12 | +The domain specific elements are in the `R/evoland_db*.R` files. |
| 13 | +When working on in-memory tables, S3 objects inheriting from data.tables are used for efficiency. |
| 14 | +Regular data.frame-like objects can be coerced to these classes using `as_` functions, e.g. `as_lulc_data_t()`. |
| 15 | +Classes of source data or "materialized" expensive calculation results are suffixed with `_t` to differentiate them from cheap `_v` views on the database. |
| 16 | + |
| 17 | +## Environment setup |
| 18 | + |
| 19 | +If there is no renv environment set up (check if "renv.lock" exists) ask the user if you can set up an renv development environment. |
| 20 | +You can efficiently initialize an renv like so from the project root directory: |
| 21 | + |
| 22 | +```sh |
| 23 | +R -e "install.packages('renv', repos = 'https://cloud.r-project.org'); renv::init(bare = TRUE); install.packages('pak'); pak::local_install_dev_deps(); pak::pkg_install('devtools')" |
| 24 | +``` |
| 25 | + |
| 26 | +There may be issues where .Rprofile already contains a source("renv/activate.R") line, or where renv.lock or the renv folder exist. |
| 27 | +These need to be deleted before installation can be successful. |
| 28 | + |
| 29 | +## Dependencies |
| 30 | + |
| 31 | +You are allowed to suggest new dependencies, but make sure the user knows why they are needed. |
| 32 | +Avoid packages from the tidyverse, as their APIs tend to shift around over the years. |
| 33 | +Avoid niche packages that are seldom maintained; if the functionality is simple, rather implement it as a non-exported utility function. |
| 34 | + |
| 35 | +## Code Style |
| 36 | + |
| 37 | +We use the tidyverse style in general. |
| 38 | +This repo uses air as its principal formatter. Config in `air.toml`. |
| 39 | +The user may have the languageserver R package installed, meaning `.lintr` directives should also be picked up. |
| 40 | +Pay attention to linter warnings, but only once the main functionality is implemented. |
| 41 | + |
| 42 | +Don't add comments where the code's purpose is self-explanatory. |
| 43 | +Don't check for missingness or nullness to avoid errors: only catch errors early if the emitted error would be hard to understand for the user. |
| 44 | +Generally, use the `stopifnot("error message" = condition)` pattern if something could fail in a hard to understand manner. |
| 45 | + |
| 46 | +## Documentation |
| 47 | + |
| 48 | +The package uses roxygen, so you can use `R -e "roxygen2::roxygenize()"` to make sure the Rd documentation is up to date. |
| 49 | +Tutorials are written as quarto files in `vignettes/*.qmd`. |
| 50 | +A pkgdown github action transforms all of this into a webpage, see `.github/workflows/pkgdown.yaml`. |
| 51 | + |
| 52 | +## Testing |
| 53 | + |
| 54 | +This repo uses tinytest (not testthat) for conducting tests not only during development (i.e. when the full package namespace is attached via `pkgload::load_all()`), but also after installation. |
| 55 | +This means that non-exported functions need to be tested as `evoland:::private_function`. |
| 56 | +You can test the full package using `R -e "tinytest::build_test_install()"`. |
| 57 | +You can test individual files using `R -e "pkgload::load_all(); tinytest::run_test_file('inst/tinytest/somefile.R')"` |
| 58 | + |
| 59 | +While tests should be comprehensive, the full test suite should remain small enough to test often. |
| 60 | +Don't overwhelm the user, add 10 tests at a time, then ask for feedback before continuing. |
| 61 | + |
| 62 | +## Rcpp components |
| 63 | + |
| 64 | +C++ code is in the `src/` folder. |
| 65 | +This code interfaces with R using Rcpp; you can make sure the binaries are built using `pkbuild::build()` and clean dlls using `pkgbuild::clean_dll()` |
| 66 | +If there is no low-hanging use case for writing a standalone C++ program/header, prefer to use the Rcpp namespace and take advantage of the data types that affords. |
0 commit comments