33 Developing new functions in SITS
33.1 General principles
New functions that build on the sits API should follow the general principles below.
The target audience for
sitsis the community of remote sensing experts with Earth Sciences background who want to use state-of-the-art data analysis methods with minimal investment in programming skills. The design of thesitsAPI considers the typical workflow for land classification using satellite image time series and thus provides a clear and direct set of functions, which are easy to learn and master.For this reason, we welcome contributors that provide useful additions to the existing API, such as new ML/DL classification algorithms. In case of a new API function, before making a pull request please raise an issue stating your rationale for a new function.
Most functions in
sitsuse the S3 programming model with a strong emphasis on generic methods wich are specialized depending on the input data type. See for example the implementation of thesits_bands()function.Please do not include contributed code using the S4 programming model. Doing so would break the structure and the logic of existing code. Convert your code from S4 to S3.
Use generic functions as much as possible, as they improve modularity and maintenance. If your code has decision points using
if-elseclauses, such asif A, do X; else do Yconsider using generic functions.Functions that use the
torchpackage use the R6 model to be compatible with that package. To convertpyTorchcode to R and include it is straightforward. See for example, the code insits_tempcnn.Randapi_torch.R.The sits code relies on the packages of the
tidyverseto work with tables and list. We usedplyrandtidyrfor data selection and wrangling,purrrandsliderfor loops on lists and table,lubridateto handle dates and times.
33.2 Adherence to the sits data types
The sits package in built on top of three data types: time series tibble, data cubes and models. Most sits functions have one or more of these types as inputs and one of them as return values. The time series tibble contains data and metadata. The first six columns contain the metadata: spatial and temporal information, the label assigned to the sample, and the data cube from where the data has been extracted. The time_series column contains the time series data for each spatiotemporal location. All time series tibbles are objects of class sits.
The cube data type is designed to store metadata about image files. In principle, images which are part of a data cube share the same geographical region, have the same bands, and have been regularized to fit into a pre-defined temporal interval. Data cubes in sits are organized by tiles. A tile is an element of a satellite’s mission reference system, for example MGRS for Sentinel-2 and WRS2 for Landsat. A cube is a tibble where each row contains information about data covering one tile. Each row of the cube tibble contains a column named file_info; this column contains a list that stores a tibble
The cube data type is specialised in raster_cube (ARD images), vector_cube (ARD cube with segmentation vectors). probs_cube (probabilities produced by classification algorithms on raster data), probs_vector_cube(probabilites generated by vector classification of segments), uncertainty_cube (cubes with uncertainty information), and class_cube (labelled maps). See the code in sits_plot.R as an example of specialisation of plot to handle different classes of raster data.
All ML/DL models in sits which are the result of sits_train belong to the ml_model class. In addition, models are assigned a second class, which is unique to ML models (e.g, rfor_model, svm_model) and generic for all DL torch based models (torch_model). The class information is used for plotting models and for establishing if a model can run on GPUs.
33.3 Literal values, error messages, and testing
The internal sits code has no literal values, which are all stored in the YAML configuration files ./inst/extdata/config.yml and ./inst/extdata/config_internals.yml. The first file contains configuration parameters that are relevant to users, related to visualisation and plotting; the second contains parameters that are relevant only for developers. These values are accessible using the .conf function. For example, the value of the default size for ploting COG files is accessed using the command .conf["plot", "max_size"].
Error messages are also stored outside of the code in the YAML configuration file ./inst/extdata/config_messages.yml. These values are accessible using the .conf function. For example, the error associated to an invalid NA value for an input parameter is accessible using th function .conf("messages", ".check_na_parameter").
We strive for high code coverage (> 90%). Every parameter of all sits function (including internal ones) is checked for consistency. Please see api_check.R.
33.4 Using AI agents to develop new functions
AI coding agents, such as Claude Code, OpenAI Codex, GitHub Copilot, and Posit Assistant, can help developers write new sits functions. These agents work directly on a local copy of the source code: they read files, search the code base, propose and edit code, and run tests in an R session. Used well, they reduce the effort needed to learn the internal structure of sits, which has more than 180 R source files and many internal APIs. Used without guidance, they tend to write generic R code that ignores the package conventions. For example, an agent might call terra functions directly, use lapply() loops, hard-code error messages, or access cube columns with $. Code written this way may work, but it is harder to maintain and will not be accepted as a contribution.
To avoid these problems, the sits repository includes a file called AGENTS.md in its root directory. AGENTS.md is an open convention for giving instructions to AI coding agents. It plays the same role for agents that contributing.md plays for human developers. Most agents read AGENTS.md automatically at the start of a session, so its rules guide every change the agent makes. Some agents look for their own file name instead. For this reason, the repository also contains a CLAUDE.md file that points Claude Code to AGENTS.md, so that all agents follow the same set of instructions. Both files are listed in .Rbuildignore and are not part of the package distributed on CRAN.
33.4.1 What AGENTS.md contains
AGENTS.md is a short summary of the rules described in this chapter, written so that an agent can apply them. Its main sections are:
Repository map: the layout of the package (
R/,src/,tests/,inst/extdata/,man/), with notes on which files are generated automatically and must never be edited by hand, such asNAMESPACE,man/*.Rd, andRcppExports.R.File organisation and naming: exported functions go in
sits_<topic>.Rfiles and are namedsits_<name>(). Internal code goes inapi_<topic>.Rfiles, and function names start with a dot and a domain prefix (.raster_*,.cube_*,.tile_*,.bbox_*). When adding a feature, the agent puts the user-facing entry point in asits_file and the supporting code in the matchingapi_file.Internal API usage: agents must use the existing internal APIs instead of handling objects directly. All raster input and output goes through
api_raster.R, never through direct calls toterra::. Cube and tile fields are read with accessors such as.cube_bands(),.cube_timeline(),.tile_bbox(), and.tile_path(). Loops over tiles use.cube_foreach_tile()orslider. Before writing new helper code, the agent should check whether an existing internal function already does the job.Programming model: new functions must be S3 generics, starting with
.check_set_caller()followed byUseMethod(), with methods for each input class (sits,raster_cube,tbl_df,default). S4 is not allowed. R6 is used only fortorchmodels, followingsits_tempcnn.Randapi_torch.R. This section also describes the core data types: time series tibbles, data cubes and their subclasses, andml_modelobjects.Code style: four-space indentation, lines of about 80 characters, the
tidyverse/sf/terrastack, andpurrrandsliderinstead of theapplyfamily.No literal values: configuration values come from
config.ymlandconfig_internals.yml, error messages fromconfig_messages.yml, and colours fromconfig_colors.yml. All of them are read with.conf().Parameter checking, documentation, and testing: inputs are checked with the
.check_*functions inapi_check.R. Exported functions have fullroxygen2documentation, with examples wrapped inif (sits_run_examples()), while internal functions use@noRd. Tests are written withtestthat, one file per topic, focusing on the public functions.Build and verification: the agent should run
devtools::document(), the relevant tests, anddevtools::check()before it reports that the work is done. Because the full test suite can take hours, the agent should run only the tests for the function it changed, for example withdevtools::test(filter = "bands").Commits: the agent must never commit on the user’s behalf. The developer reviews all changes and commits them.
33.4.2 A suggested workflow
The following workflow works well when using an AI agent to develop a new sits function.
Clone the
sitsrepository and start the agent in its root directory, so thatAGENTS.mdis read automatically. If you use an agent that does not readAGENTS.md, tell it explicitly to read the file before starting.Describe the new function precisely. Give its name and purpose, the input data types it should accept, its parameters and their defaults, and the expected output. Point to an existing function with a similar structure. For example:
Write a new generic function
sits_band_stats()that computes, for each band, the median and the interquartile range of the values of a time series tibble. Follow the pattern ofsits_bands.R, with methods forsitsanddefault. Put the helper code in anapi_file, add the error messages toconfig_messages.yml, write roxygen documentation with an example, and add a test file. Run only the new tests.Ask the agent to explore the code first and present a plan before it edits any files. Check that the plan reuses existing internal functions (
.cube_*,.tile_*,.raster_*,.check_*) rather than creating new ones that duplicate them.Review the proposed code against the rules in
AGENTS.mdand in this chapter. Look for direct calls toterra::, use of$on cubes or tiles,if-elsedecisions on object class instead of S3 methods, literal values or messages in the code, and missing parameter checks.Run the new tests and
devtools::check(), and make sure the package stays free of CRAN check notes, warnings, and errors.Review the full set of changes yourself before committing them and opening a pull request. As for any other contribution, open an issue explaining why a new API function is needed before submitting it.
AI agents speed up development, but they do not replace the developer’s judgement. Agents can produce code that looks plausible but is wrong, especially for scientific algorithms, where results must be checked against known reference values. The author of a pull request is responsible for all the code it contains, whether it was written by hand or with the help of an agent. Contributors who find that the agent repeatedly makes the same kind of mistake are encouraged to suggest an improvement to AGENTS.md, so that future contributors benefit from the clarified rule.