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

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

```{r setup }
library(geometries)
library(Rcpp)
```


### Header

```
#include "geometries/geometries.hpp"
```

### Functions

```
SEXP make_geometries()
```

## data as geometries

When one thinks of geometries in `R`, one of the most common data structures is the matrix. For example, in the `sf` world, an POINT is a single-row matrix (i.e, a vector)


```
sf::st_point( 1:2 )
POINT (1 2)
```

A LINESTRING is a matrix

```
sf::st_linestring( matrix( c(1,1,1,2,2,2,2,1,1,1), ncol = 2, byrow = T) )
LINESTRING (1 1, 1 2, 2 2, 2 1, 1 1)
```

and a POLYGON is a list of matrices


```
sf::st_polygon( list( matrix( c(1,1,1,2,2,2,2,1,1,1), ncol = 2, byrow = T) ) )
POLYGON ((1 1, 1 2, 2 2, 2 1, 1 1))
```

And to group these into a **collection** you would put each geometry inside a list

```

sf::st_sfc(
  list(
    sf::st_linestring( matrix( c(1,1,1,2,2,2,2,1,1,1), ncol = 2, byrow = T) )
    , sf::st_polygon( list( matrix( c(1,1,1,2,2,2,2,1,1,1), ncol = 2, byrow = T) ) )
  )
)

Geometry set for 2 features 
geometry type:  GEOMETRY
dimension:      XY
bbox:           xmin: 1 ymin: 1 xmax: 2 ymax: 2
CRS:            NA
LINESTRING (1 1, 1 2, 2 2, 2 1, 1 1)
POLYGON ((1 1, 1 2, 2 2, 2 1, 1 1))
```


---


From my limited research (i.e., practically none), I estimate most users will have a `data.frame` and will want to convert it into a collection of geometries. 

For example, take a `data.frame` of `x` and `y` coordinates, and two `id` columns. 


```{r}

df <- data.frame(
  id1 = c( rep(1,12), rep(2, 12) )
  , id2 = c( rep(1:4, each = 3), rep(1:4, each = 3) )
  , x = 1:24
  , y = 24:1
)

df

```

You can think of the ID columns in this example as

- `id1` defines a polygon
- `id2` is each ring inside the polygon

Calling `geometries::make_geometries()` will split this data.frame by these ID columns and put the resulting matrices inside list elements.

```r
cppFunction(
  
  depends = "geometries"
  , includes = '#include "geometries/geometries.hpp"'
  , code = '
    SEXP my_shape( SEXP df, SEXP id_cols, SEXP geometry_cols ) {
      return geometries::make_geometries( df, id_cols, geometry_cols );
    }
  '
  , plugins = "cpp11"
)

my_shape( df, c(0L,1L), c(2L,3L) )

# [[1]]
# [[1]][[1]]
#      [,1] [,2]
# [1,]    1   24
# [2,]    2   23
# [3,]    3   22
# 
# [[1]][[2]]
#      [,1] [,2]
# [1,]    4   21
# [2,]    5   20
# [3,]    6   19
# 
# [[1]][[3]]
#      [,1] [,2]
# [1,]    7   18
# [2,]    8   17
# [3,]    9   16
# 
# [[1]][[4]]
#      [,1] [,2]
# [1,]   10   15
# [2,]   11   14
# [3,]   12   13
# 
# 
# [[2]]
# [[2]][[1]]
#      [,1] [,2]
# [1,]   13   12
# [2,]   14   11
# [3,]   15   10
# 
# [[2]][[2]]
#      [,1] [,2]
# [1,]   16    9
# [2,]   17    8
# [3,]   18    7
# 
# [[2]][[3]]
#      [,1] [,2]
# [1,]   19    6
# [2,]   20    5
# [3,]   21    4
# 
# [[2]][[4]]
#      [,1] [,2]
# [1,]   22    3
# [2,]   23    2
# [3,]   24    1
```



Notice here there are no class attributes on the shapes. In `{geometries}` I only want to provide the tools to build these structures, then each user can define what they mean.

For example, if you want to define a class for each geometry you can supply a list containing a "class" vector as the 4th argument

```r

cppFunction(
  depends = "geometries"
  , includes = '#include "geometries/geometries.hpp"'
  , code = '
    SEXP my_shape( Rcpp::DataFrame df, Rcpp::IntegerVector id_cols, Rcpp::IntegerVector geometry_cols, Rcpp::List class_attributes ) {
      return geometries::make_geometries( df, id_cols, geometry_cols, class_attributes );
    }
  '
  , plugins = "cpp11"
)

my_shape( df, c(0,1), c(2,3), list(class = "my_polygon") )

# [[1]]
# [[1]]
#      [,1] [,2]
# [1,]    1   24
# [2,]    2   23
# [3,]    3   22
# 
# [[2]]
#      [,1] [,2]
# [1,]    4   21
# [2,]    5   20
# [3,]    6   19
# 
# [[3]]
#      [,1] [,2]
# [1,]    7   18
# [2,]    8   17
# [3,]    9   16
# 
# [[4]]
#      [,1] [,2]
# [1,]   10   15
# [2,]   11   14
# [3,]   12   13
# 
# attr(,"class")
# [1] "my_polygon"
# 
# [[2]]
# [[1]]
#      [,1] [,2]
# [1,]   13   12
# [2,]   14   11
# [3,]   15   10
# 
# [[2]]
#      [,1] [,2]
# [1,]   16    9
# [2,]   17    8
# [3,]   18    7
# 
# [[3]]
#      [,1] [,2]
# [1,]   19    6
# [2,]   20    5
# [3,]   21    4
# 
# [[4]]
#      [,1] [,2]
# [1,]   22    3
# [2,]   23    2
# [3,]   24    1
# 
# attr(,"class")
# [1] "my_polygon"

```

Notice here that each list element now has a `"my_polygon"` class.

And if you have `library(sf)` loaded, setting the class as `sfg` `POLYGON`, you should see each element printed in the usual `sf` way

```
library(sf)
my_shape( df, c(0,1), c(2,3), list( class = c("XY", "POLYGON","sfg") ) )

# [[1]]
# POLYGON ((1 24, 2 23, 3 22), (4 21, 5 20, 6 19), (7 18, 8 17, 9 16), (10 15, 11 14, 12 13))
# 
# [[2]]
# POLYGON ((13 12, 14 11, 15 10), (16 9, 17 8, 18 7), (19 6, 20 5, 21 4), (22 3, 23 2, 24 1))

```

You can use this function to define any shape you want. The number of `id` columns you supply will determine how deeply nested the matrices are. If I add two more `id` columns, this will nest each matrix 2-levels deeper


```r

df$id0 <- 1
df$id00 <- 1
head( df ) 

my_shape( df, c(0,1,4,5), c(2,3), list(class = "my_new_shape") )

# [[1]]
# [[1]]
# [[1]][[1]]
# [[1]][[1]][[1]]
#      [,1] [,2]
# [1,]    1   24
# [2,]    2   23
# [3,]    3   22
# 
# 
# 
# [[2]]
# [[2]][[1]]
# [[2]][[1]][[1]]
#      [,1] [,2]
# [1,]    4   21
# [2,]    5   20
# [3,]    6   19
# 
# 
# 
# [[3]]
# [[3]][[1]]
# [[3]][[1]][[1]]
#      [,1] [,2]
# [1,]    7   18
# [2,]    8   17
# [3,]    9   16
# 
# 
# 
# [[4]]
# [[4]][[1]]
# [[4]][[1]][[1]]
#      [,1] [,2]
# [1,]   10   15
# [2,]   11   14
# [3,]   12   13
# 
# 
# 
# attr(,"class")
# [1] "my_new_shape"
# 
# [[2]]
# [[1]]
# [[1]][[1]]
# [[1]][[1]][[1]]
#      [,1] [,2]
# [1,]   13   12
# [2,]   14   11
# [3,]   15   10
# 
# 
# 
# [[2]]
# [[2]][[1]]
# [[2]][[1]][[1]]
#      [,1] [,2]
# [1,]   16    9
# [2,]   17    8
# [3,]   18    7
# 
# 
# 
# [[3]]
# [[3]][[1]]
# [[3]][[1]][[1]]
#      [,1] [,2]
# [1,]   19    6
# [2,]   20    5
# [3,]   21    4
# 
# 
# 
# [[4]]
# [[4]][[1]]
# [[4]][[1]][[1]]
#      [,1] [,2]
# [1,]   22    3
# [2,]   23    2
# [3,]   24    1
# 
# 
# 
# attr(,"class")
# [1] "my_new_shape"

```

