---
title: "Cell Operations"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Cell Operations}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

```{r setup}
library(vaster)
```

## Overview
 
Cell operations are at the heart of raster grid logic. This vignette covers:

- Converting between **cell indices** and **x,y coordinates**
- Converting between **cell indices** and **row/column positions**
- Working with **extents** from cells
- Finding cells within an **extent**

All functions use the standard vaster convention: `dimension` is `c(ncol, nrow)` and `extent` is `c(xmin, xmax, ymin, ymax)`.

## Cell Indexing

Cells are numbered starting from 1 at the **top-left** corner, proceeding **row-wise** (left-to-right, then top-to-bottom):
 
```
 1  2  3  4
 5  6  7  8
 9 10 11 12
```

```{r}
## define a simple 4x3 grid
dimension <- c(4, 3)   # 4 columns, 3 rows
extent <- c(0, 4, 0, 3) # xmin=0, xmax=4, ymin=0, ymax=3

n_cell(dimension)
n_col(dimension)
n_row(dimension)
```

## Cells ↔ Coordinates

### From cells to coordinates

Use `xy_from_cell()` to get the **centre coordinates** of cells:

```{r}
## single cell
xy_from_cell(dimension, extent, 1)

## multiple cells
xy_from_cell(dimension, extent, c(1, 4, 9, 12))

## all cells
xy_from_cell(dimension, extent, seq_len(n_cell(dimension)))
```

For just x or y coordinates:

```{r}
x_from_cell(dimension, extent, 1:4)
y_from_cell(dimension, extent, c(1, 5, 9))
```

### From coordinates to cells

Use `cell_from_xy()` to find which cell contains given coordinates:

```{r}
## coordinates at cell centres
xy <- cbind(c(0.5, 1.5, 2.5), c(2.5, 1.5, 0.5))
cell_from_xy(dimension, extent, xy)

## coordinates outside the grid return NA
xy_outside <- cbind(c(-1, 5), c(1.5, 1.5))
cell_from_xy(dimension, extent, xy_outside)

## coordinates on cell boundaries go to the cell below/right
## except at the grid edge where they go to the last cell
xy_edge <- cbind(c(1.0, 4.0), c(2.0, 0.0))
cell_from_xy(dimension, extent, xy_edge)
```

## Cells ↔ Row/Column

### From cells to row/column

```{r}
## get row and column for cells
rowcol_from_cell(dimension, extent, 1:12)
```

For just row or column:

```{r}
row_from_cell(dimension, 1:12)
col_from_cell(dimension, 1:12)
```

Note that `row_from_cell()` and `col_from_cell()` only need `dimension` since row/column positions don't depend on extent.

### From row/column to cells

Get cells for specific row/column combinations:
 
```{r}
## single cell at row 2, column 3
cell_from_row_col(dimension, row = 2, col = 3)

## multiple cells (vectorized, with recycling)
cell_from_row_col(dimension, row = 1:3, col = 1:3)  # diagonal cells
```

Get **all cells** in specific rows or columns:

```{r}
## all cells in row 2
cell_from_row(dimension, 2)

## all cells in column 3
cell_from_col(dimension, 3)
```

Get the **cross-product** of rows and columns (all combinations):

```{r}
## all cells in rows 1-2 AND columns 2-3
cell_from_rowcol_combine(dimension, row = 1:2, col = 2:3)
```

## Rows/Columns ↔ Coordinates

Convert directly between row/column indices and coordinates:

```{r}
## x coordinate of each column (centre)
x_from_col(dimension, extent, 1:4)

## y coordinate of each row (centre)
y_from_row(dimension, extent, 1:3)

## which column contains this x coordinate
col_from_x(dimension, extent, c(0.5, 2.5, 3.9))

## which row contains this y coordinate
row_from_y(dimension, extent, c(2.5, 1.5, 0.1))
```

## Cells ↔ Extents

### Extent from cells

Get the bounding box that covers a set of cells:

```{r}
## extent of a single cell
extent_from_cell(dimension, extent, 1)

## extent covering multiple cells
extent_from_cell(dimension, extent, c(1, 4))  # top row
extent_from_cell(dimension, extent, c(1, 12)) # full grid
```

### Cells from extent

Find all cells within a given extent:

```{r}
## cells within a sub-extent
sub_extent <- c(1, 3, 1, 2)  # xmin=1, xmax=3, ymin=1, ymax=2
cell_from_extent(dimension, extent, sub_extent)
```

The extent is automatically aligned to cell boundaries before finding cells.

## Handling Invalid Inputs

Functions return `NA` for invalid inputs:

```{r}
## invalid cell numbers
xy_from_cell(dimension, extent, c(0, 13, -1))

## coordinates outside grid
cell_from_xy(dimension, extent, cbind(c(-1, 10), c(1, 1)))

## invalid row/column
cell_from_row_col(dimension, row = 0, col = 1)
cell_from_row_col(dimension, row = 4, col = 1)  # only 3 rows exist
```

## Practical Example: Sampling Grid Points

Create a stratified sample by selecting one random point from each cell:

```{r}
set.seed(42)
dimension <- c(5, 4)
extent <- c(0, 10, 0, 8)

## get cell centres
centres <- xy_from_cell(dimension, extent, seq_len(n_cell(dimension)))

## add random jitter within each cell
res <- c(x_res(dimension, extent), y_res(dimension, extent))
jitter_x <- runif(n_cell(dimension), -res[1]/2, res[1]/2)
jitter_y <- runif(n_cell(dimension), -res[2]/2, res[2]/2)

sample_points <- cbind(
  x = centres[, 1] + jitter_x,
  y = centres[, 2] + jitter_y
)

head(sample_points)

## verify all points fall in expected cells
all(cell_from_xy(dimension, extent, sample_points) == seq_len(n_cell(dimension)))
```

## See Also

- `vignette("grid-fundamentals")`: Basic grid concepts
- `vignette("extent-alignment")`: Aligning extents to grids
- `vignette("gdal-interop")`: GDAL geotransform operations
