Skip to contents

The classes provided in post are designed so that the majority of functions and methods available for cubble or stars objects are still applicable to post_* objects, while keeping the post class and attributes in the process.

The main compatibility functions are presented in this vignette.

cubble compatibility

Objects of class post_table are compatible with most of cubbles methods as presented below.

Spatial and temporal faces

The main functionalities of cubble are the ability to do analyses on the spatial or on the temporal faces of the vector data cube. For this, the functions cubble::face_spatial() and cubble::face_temporal() are provided. The spatial face is the default state of a cubble object.

tab = as_post_table(polygons)
face_spatial(tab)
#> ℹ The cubble is already in the nested form
#> # cubble:   key: gid [5], index: datetime, nested form, [sf]
#> # spatial:  [0, 0.18, 0.75, 0.9], WGS 84
#> # temporal: datetime [date], geometry [POLYGON [°]]
#>   gid       long   lat                 geom_sum ts          
#> * <chr>    <dbl> <dbl>              <POINT [°]> <list>      
#> 1 a      0.648   0.902     (0.647816 0.9018588) <sf [5 × 2]>
#> 2 b     -0.00254 0.840 (-0.002543201 0.8403816) <sf [5 × 2]>
#> 3 c      0.390   0.541    (0.3897167 0.5412363) <sf [5 × 2]>
#> 4 d      0.753   0.354     (0.752585 0.3538271) <sf [5 × 2]>
#> 5 e      0.469   0.178      (0.4690683 0.17772) <sf [5 × 2]>
face_temporal(tab)
#> # cubble:   key: gid [5], index: datetime, long form
#> # temporal: 2020-10-01 -- 2020-10-05 [1D], no gaps
#> # spatial:  long [dbl], lat [dbl], geom_sum [POINT [°]]
#>    gid   datetime                                                       geometry
#>  * <chr> <date>                                                    <POLYGON [°]>
#>  1 a     2020-10-01 ((0.5474949 0.8088912, 0.4874872 0.8838477, 0.5520055 1.032…
#>  2 a     2020-10-02 ((0.4961102 0.8728385, 0.4361026 0.947795, 0.5006209 1.0966…
#>  3 a     2020-10-03 ((0.5578801 0.8616378, 0.5623908 1.085448, 0.8520717 0.8981…
#>  4 a     2020-10-04 ((0.5652241 0.872057, 0.5685463 1.036898, 0.7819022 0.89897…
#>  5 a     2020-10-05 ((0.6063791 0.8304178, 0.5463715 0.9053744, 0.6108898 1.054…
#>  6 b     2020-10-01 ((0.2791708 0.8337388, -0.2847997 0.6540191, -0.2825181 0.6…
#>  7 b     2020-10-02 ((0.3298312 0.7612067, -0.2341393 0.5814869, -0.2318577 0.5…
#>  8 b     2020-10-03 ((0.3796448 0.767853, -0.1843257 0.5881332, 0.09777458 1.05…
#>  9 b     2020-10-04 ((0.3642467 0.779727, -0.1182199 0.6259799, 0.1231118 1.026…
#> 10 b     2020-10-05 ((0.2665368 0.8649996, -0.2974337 0.6852798, -0.2951521 0.6…
#> # ℹ 15 more rows

Attribute extraction

Extracting cubble attributes is also possible. cubble follows the attributes from tsibble objects, where the key is equivalent to the group identifier and the index is equivalent to the temporal column in post_table objects.

cubble::coords(tab)
#> [1] "long" "lat"
cubble::index(tab)
#> datetime
tsibble::key(tab)
#> [[1]]
#> gid
cubble::spatial(tab)
#> Simple feature collection with 5 features and 3 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: -0.002543201 ymin: 0.17772 xmax: 0.752585 ymax: 0.9018588
#> Geodetic CRS:  WGS 84
#> # A tibble: 5 × 4
#>   gid       long   lat                 geom_sum
#>   <chr>    <dbl> <dbl>              <POINT [°]>
#> 1 a      0.648   0.902     (0.647816 0.9018588)
#> 2 b     -0.00254 0.840 (-0.002543201 0.8403816)
#> 3 c      0.390   0.541    (0.3897167 0.5412363)
#> 4 d      0.753   0.354     (0.752585 0.3538271)
#> 5 e      0.469   0.178      (0.4690683 0.17772)

Gap filling with tsibble

cubble also supports compatibility with some tsibble functions, namely gap filling, tsibble::fill_gaps(). If we sample the original polygon to create random gaps, we can use this function to fill them with NA values and empty geometries.

set.seed(324)
tab_gaps = polygons[sample(nrow(polygons), 13), ] |> 
  as_post_table() 
tab_gaps |> face_temporal()
#> # cubble:   key: gid [5], index: datetime, long form
#> # temporal: 2020-10-01 -- 2020-10-05 [1D], has gaps!
#> # spatial:  long [dbl], lat [dbl], geom_sum [POINT [°]]
#>    gid   datetime                                                       geometry
#>  * <chr> <date>                                                    <POLYGON [°]>
#>  1 a     2020-10-01 ((0.5474949 0.8088912, 0.4874872 0.8838477, 0.5520055 1.032…
#>  2 a     2020-10-05 ((0.6063791 0.8304178, 0.5463715 0.9053744, 0.6108898 1.054…
#>  3 b     2020-10-01 ((0.2791708 0.8337388, -0.2847997 0.6540191, -0.2825181 0.6…
#>  4 b     2020-10-02 ((0.3298312 0.7612067, -0.2341393 0.5814869, -0.2318577 0.5…
#>  5 c     2020-10-03 ((0.2897647 0.6168588, 0.3415208 0.710566, 0.5357965 0.4922…
#>  6 c     2020-10-04 ((0.2930046 0.6546896, 0.3210764 0.7055149, 0.4264484 0.587…
#>  7 d     2020-10-01 ((0.7650701 0.4744459, 0.8787185 0.375316, 0.8266421 0.2890…
#>  8 d     2020-10-04 ((0.6687306 0.4190113, 0.7593946 0.3399296, 0.6862765 0.233…
#>  9 d     2020-10-05 ((0.8594322 0.4155757, 0.9730806 0.3164458, 0.9210042 0.230…
#> 10 e     2020-10-01 ((0.3825692 0.3537803, 0.5519791 0.3669884, 0.6063218 0.096…
#> 11 e     2020-10-02 ((0.3101455 0.3168974, 0.4795555 0.3301055, 0.5338981 0.059…
#> 12 e     2020-10-03 ((0.2813385 0.2544898, 0.4507484 0.2676979, 0.505091 -0.002…
#> 13 e     2020-10-04 ((0.3130673 0.2176433, 0.4498424 0.228307, 0.4939709 0.0085…
tab_gaps |> face_temporal() |> fill_gaps()
#> # cubble:   key: gid [5], index: datetime, long form
#> # temporal: 2020-10-01 -- 2020-10-05 [1D], no gaps
#> # spatial:  long [dbl], lat [dbl], geom_sum [POINT [°]]
#>    gid   datetime                                                       geometry
#>  * <chr> <date>                                                    <POLYGON [°]>
#>  1 a     2020-10-01 ((0.5474949 0.8088912, 0.4874872 0.8838477, 0.5520055 1.032…
#>  2 a     2020-10-02                                                        EMPTY
#>  3 a     2020-10-03                                                        EMPTY
#>  4 a     2020-10-04                                                        EMPTY
#>  5 a     2020-10-05 ((0.6063791 0.8304178, 0.5463715 0.9053744, 0.6108898 1.054…
#>  6 b     2020-10-01 ((0.2791708 0.8337388, -0.2847997 0.6540191, -0.2825181 0.6…
#>  7 b     2020-10-02 ((0.3298312 0.7612067, -0.2341393 0.5814869, -0.2318577 0.5…
#>  8 c     2020-10-03 ((0.2897647 0.6168588, 0.3415208 0.710566, 0.5357965 0.4922…
#>  9 c     2020-10-04 ((0.2930046 0.6546896, 0.3210764 0.7055149, 0.4264484 0.587…
#> 10 d     2020-10-01 ((0.7650701 0.4744459, 0.8787185 0.375316, 0.8266421 0.2890…
#> 11 d     2020-10-02                                                        EMPTY
#> 12 d     2020-10-03                                                        EMPTY
#> 13 d     2020-10-04 ((0.6687306 0.4190113, 0.7593946 0.3399296, 0.6862765 0.233…
#> 14 d     2020-10-05 ((0.8594322 0.4155757, 0.9730806 0.3164458, 0.9210042 0.230…
#> 15 e     2020-10-01 ((0.3825692 0.3537803, 0.5519791 0.3669884, 0.6063218 0.096…
#> 16 e     2020-10-02 ((0.3101455 0.3168974, 0.4795555 0.3301055, 0.5338981 0.059…
#> 17 e     2020-10-03 ((0.2813385 0.2544898, 0.4507484 0.2676979, 0.505091 -0.002…
#> 18 e     2020-10-04 ((0.3130673 0.2176433, 0.4498424 0.228307, 0.4939709 0.0085…

The function tsibble::scan_gaps() on the other hand does not return a post_table object since all the geometries would be EMPTY and the inherit structure of a post object is lost.

tab_gaps |> face_temporal() |> scan_gaps()
#> # cubble:   key: gid [5], index: datetime, long form, [tsibble]
#> # temporal: 2020-10-02 -- 2020-10-04 [1D], no gaps
#> # spatial:  long [dbl], lat [dbl], geom_sum [POINT [°]]
#>   gid   datetime  
#>   <chr> <date>    
#> 1 a     2020-10-02
#> 2 a     2020-10-03
#> 3 a     2020-10-04
#> 4 d     2020-10-02
#> 5 d     2020-10-03

Glyph maps

For visualisation, cubble provides the cubble::geom_glyph() function to be used with ggplot2 plots. This geom is useful to have a quick glance at the temporal variation of specific variables while also being aware of the spatial distribution of the features.

To use this one with post_table objects we have to unfold the long and lat functions into the temporal face of the vector data cube.

Then, to pass it onto the ggplot function, the post_table class should be dropped to avoid conflicts. The utility function remove_post_table() is provided for this purpose.

tab_union = as_post_table(polygons, geometry_summary = summarise_geometry_union)
#> Warning: st_centroid assumes attributes are constant over geometries
tab_unf = polygons |> 
  mutate(area = as.numeric(st_area(geometry))) |> 
  as_post_table() |> 
  face_temporal() |> 
  unfold(long, lat)

ggplot(remove_post_table(tab_unf)) +
  aes(x_major = long, y_major = lat, x_minor = datetime, y_minor = area) +
  geom_sf(data = tab_union, fill = "grey80", color = NA, inherit.aes = FALSE) +
  geom_glyph_box(width = 0.18, height = 0.1) +
  geom_glyph(aes(color = area), width = 0.18, height = 0.1, linewidth = 0.7) +
  scale_color_viridis_c() +
  theme_bw()

stars compatibility

Objects of class post_array inherit also the stars class, so most of the methods for stars objects are applicable to post objects as shown below.

Subsetting

stars and hence post_array objects are in esence arrays, therefore we can use the [ operator for subsetting (see more on its usage on the corresponding stars vignette.

arr = as_post_array(polygons, geometry_summary = summarise_geometry_union)
# subset first two features
arr[,1:2,]
#> stars object with 2 dimensions and 1 attribute
#> attribute(s):
#>          geometry  
#>  POLYGON      :10  
#>  epsg:4326    : 0  
#>  +proj=long...: 0  
#> dimension(s):
#>          from to     offset  delta refsys point
#> geom_sum    1  2         NA     NA WGS 84 FALSE
#> datetime    1  5 2020-10-01 1 days   Date FALSE
#>                                                                  values
#> geom_sum POLYGON ((0.4971 0.8718, 0...., POLYGON ((-0.1774 0.5996, -...
#> datetime                                                           NULL
# subset last three timestamps
arr[,,3:5]
#> stars object with 2 dimensions and 1 attribute
#> attribute(s):
#>          geometry  
#>  POLYGON      :15  
#>  epsg:4326    : 0  
#>  +proj=long...: 0  
#> dimension(s):
#>          from to     offset  delta refsys point
#> geom_sum    1  5         NA     NA WGS 84 FALSE
#> datetime    3  5 2020-10-01 1 days   Date FALSE
#>                                                                 values
#> geom_sum POLYGON ((0.4971355 0.871...,...,POLYGON ((0.2973931 0.255...
#> datetime                                                          NULL

Note how the number of geometries in the attributes varies according to the subsetting operation.

When extracting the attributes themselves, the [[ can be used. Note that the geometry attribute in the arr object we created is an sfc object but it has dimensions.

arr[[1]]
#> Geometry set for 25 features  [dim: 5 x 5]
#> Geometry type: POLYGON
#> Dimension:     XY
#> Bounding box:  xmin: -0.2974337 ymin: -0.00297557 xmax: 0.9730806 ymax: 1.153558
#> Geodetic CRS:  WGS 84
#> First 5 geometries:
#> POLYGON ((0.5474949 0.8088912, 0.4874872 0.8838...
#> POLYGON ((0.2791708 0.8337388, -0.2847997 0.654...
#> POLYGON ((0.2807462 0.6277986, 0.3325024 0.7215...
#> POLYGON ((0.7650701 0.4744459, 0.8787185 0.3753...
#> POLYGON ((0.3825692 0.3537803, 0.5519791 0.3669...
class(arr[[1]])
#> [1] "sfc_POLYGON" "sfc"
dim(arr[[1]])
#> geom_sum datetime 
#>        5        5

We can also create new attributes, for example:

arr$area = st_area(arr[[1]])
arr
#> stars object with 2 dimensions and 2 attributes
#> attribute(s):
#>          geometry       area           
#>  POLYGON      :25   Min.   :5.366e+07  
#>  epsg:4326    : 0   1st Qu.:3.928e+08  
#>  +proj=long...: 0   Median :4.060e+08  
#>                     Mean   :6.216e+08  
#>                     3rd Qu.:6.933e+08  
#>                     Max.   :1.537e+09  
#> dimension(s):
#>          from to     offset  delta refsys point
#> geom_sum    1  5         NA     NA WGS 84 FALSE
#> datetime    1  5 2020-10-01 1 days   Date FALSE
#>                                                                 values
#> geom_sum POLYGON ((0.4971355 0.871...,...,POLYGON ((0.2973931 0.255...
#> datetime                                                          NULL

And the functionality to use [ as a “crop” operator is also supported. However, in this case a spatial filtering rather than a cropping operation takes place.

circle = st_point(c(0.55,0.5)) |>
  st_sfc(crs = 4326) |>
  st_buffer(10000)
arr[circle]
#> stars object with 2 dimensions and 2 attributes
#> attribute(s):
#>          geometry       area           
#>  POLYGON      :10   Min.   : 53657361  
#>  epsg:4326    : 0   1st Qu.:266964303  
#>  +proj=long...: 0   Median :392817773  
#>                     Mean   :321257030  
#>                     3rd Qu.:394413594  
#>                     Max.   :394950797  
#> dimension(s):
#>          from to     offset  delta refsys point
#> geom_sum    3  4         NA     NA WGS 84 FALSE
#> datetime    1  5 2020-10-01 1 days   Date FALSE
#>                                                                  values
#> geom_sum POLYGON ((0.2866 0.6085, 0...., POLYGON ((0.5737 0.4614, 0....
#> datetime                                                           NULL

Note that the spatial dimension geom_sum is the one that gets filtered and not the changing geometries 1. Therefore, the filtered post_array also keeps geometries that do not intersect circle by themselves but their summary geometry does.

par(mar = c(0,0,0,0))
plot(arr$geometry, col = "grey80", border = "white")
plot(circle, col = "deepskyblue3", border = NA, add = TRUE)
plot(arr[circle]$geometry, border = "firebrick1", add = TRUE)

Apply functions to array dimensions

sf compatibility

Both post classes, i.e. post_array and post_table support most sf methods since they inherit implicitly or explicitly the sf or sfc class.

Methods that are not supported for post objects include:

  • Geometric operations on pairs of simple features (see ?sf::geos_binary_ops) are not supported for post objects since they change the topology of the object in ways that can’t be reconstructed without further knowledge on how summary and changing polygons should be adapted.

Converting to sf

When converting a post_* object to sf both the summary and changing geometries are preserved, and the changing geometry is set as the active geometry. Converting from post_table also includes the coordinate columns created by cubble.

# post_array
arr = as_post_array(polygons,
                    geometry_summary = summarise_geometry_bbox, 
                    rotated = TRUE)
#> Warning in st_minimum_rotated_rectangle.sfc(x_unioned):
#> st_minimum_rotated_rectangle does not work correctly for longitude/latitude
#> data
st_as_sf(arr)
#> Simple feature collection with 25 features and 2 fields
#> Active geometry column: geometry
#> Geometry type: POLYGON
#> Dimension:     XY
#> Bounding box:  xmin: -0.2974337 ymin: -0.00297557 xmax: 0.9730806 ymax: 1.153558
#> Geodetic CRS:  WGS 84
#> # A tibble: 25 × 4
#>    gid                             geom_sum datetime                    geometry
#>    <chr>                      <POLYGON [°]> <date>                 <POLYGON [°]>
#>  1 a     ((0.5137787 1.117004, 0.3759687 0… 2020-10-01 ((0.5474949 0.8088912, 0…
#>  2 a     ((0.5137787 1.117004, 0.3759687 0… 2020-10-02 ((0.4961102 0.8728385, 0…
#>  3 a     ((0.5137787 1.117004, 0.3759687 0… 2020-10-03 ((0.5578801 0.8616378, 0…
#>  4 a     ((0.5137787 1.117004, 0.3759687 0… 2020-10-04 ((0.5652241 0.872057, 0.…
#>  5 a     ((0.5137787 1.117004, 0.3759687 0… 2020-10-05 ((0.6063791 0.8304178, 0…
#>  6 b     ((0.3796448 0.767853, 0.2316513 1… 2020-10-01 ((0.2791708 0.8337388, -…
#>  7 b     ((0.3796448 0.767853, 0.2316513 1… 2020-10-02 ((0.3298312 0.7612067, -…
#>  8 b     ((0.3796448 0.767853, 0.2316513 1… 2020-10-03 ((0.3796448 0.767853, -0…
#>  9 b     ((0.3796448 0.767853, 0.2316513 1… 2020-10-04 ((0.3642467 0.779727, -0…
#> 10 b     ((0.3796448 0.767853, 0.2316513 1… 2020-10-05 ((0.2665368 0.8649996, -…
#> # ℹ 15 more rows
# post_table
tab = as_post_table(polygons,
                    geometry_summary = summarise_geometry_bbox, 
                    rotated = TRUE)
#> Warning in st_minimum_rotated_rectangle.sfc(x_unioned):
#> st_minimum_rotated_rectangle does not work correctly for longitude/latitude
#> data
#> Warning: st_centroid assumes attributes are constant over geometries
st_as_sf(tab)
#> Simple feature collection with 25 features and 4 fields
#> Active geometry column: geometry
#> Geometry type: POLYGON
#> Dimension:     XY
#> Bounding box:  xmin: -0.2974337 ymin: -0.00297557 xmax: 0.9730806 ymax: 1.153558
#> Geodetic CRS:  WGS 84
#> # A tibble: 25 × 6
#>    gid      long   lat                                       geom_sum datetime  
#>    <chr>   <dbl> <dbl>                                  <POLYGON [°]> <date>    
#>  1 a      0.638  0.885 ((0.5137787 1.117004, 0.3759687 0.9038164, 0.… 2020-10-01
#>  2 a      0.638  0.885 ((0.5137787 1.117004, 0.3759687 0.9038164, 0.… 2020-10-02
#>  3 a      0.638  0.885 ((0.5137787 1.117004, 0.3759687 0.9038164, 0.… 2020-10-03
#>  4 a      0.638  0.885 ((0.5137787 1.117004, 0.3759687 0.9038164, 0.… 2020-10-04
#>  5 a      0.638  0.885 ((0.5137787 1.117004, 0.3759687 0.9038164, 0.… 2020-10-05
#>  6 b     -0.0136 0.898 ((0.3796448 0.767853, 0.2316513 1.232265, -0.… 2020-10-01
#>  7 b     -0.0136 0.898 ((0.3796448 0.767853, 0.2316513 1.232265, -0.… 2020-10-02
#>  8 b     -0.0136 0.898 ((0.3796448 0.767853, 0.2316513 1.232265, -0.… 2020-10-03
#>  9 b     -0.0136 0.898 ((0.3796448 0.767853, 0.2316513 1.232265, -0.… 2020-10-04
#> 10 b     -0.0136 0.898 ((0.3796448 0.767853, 0.2316513 1.232265, -0.… 2020-10-05
#> # ℹ 15 more rows
#> # ℹ 1 more variable: geometry <POLYGON [°]>

CRS transformation

When transforming the CRS, both the changing and the summary geometry are updated within the post_* object.

# post_array
st_transform(arr, 3035)
#> stars object with 2 dimensions and 1 attribute
#> attribute(s):
#>          geometry  
#>  POLYGON      :25  
#>  epsg:3035    : 0  
#>  +proj=laea...: 0  
#> dimension(s):
#>          from to     offset  delta                       refsys point
#> geom_sum    1  5         NA     NA ETRS89-extended / LAEA Eu... FALSE
#> datetime    1  5 2020-10-01 1 days                         Date FALSE
#>                                                                 values
#> geom_sum POLYGON ((3155515 -218766...,...,POLYGON ((3169752 -230713...
#> datetime                                                          NULL
# post_table
st_transform(tab, 3035)
#> # cubble:   key: gid [5], index: datetime, nested form, [sf]
#> # spatial:  [3042412.03, -2307131.39, 3209654.8, -2172399.69], ETRS89-extended
#> #   / LAEA Europe
#> # temporal: datetime [date], geometry [POLYGON [m]]
#>   gid      long   lat                                             geom_sum ts   
#> * <chr>   <dbl> <dbl>                                        <POLYGON [m]> <lis>
#> 1 a      0.638  0.885 ((3155515 -2187663, 3137528 -2207166, 3183656 -2237… <sf> 
#> 2 b     -0.0136 0.898 ((3137266 -2220797, 3121618 -2172400, 3042412 -2183… <sf> 
#> 3 c      0.402  0.507 ((3131201 -2224770, 3110859 -2245607, 3146055 -2269… <sf> 
#> 4 d      0.744  0.341 ((3171061 -2243942, 3149988 -2262115, 3188598 -2292… <sf> 
#> 5 e      0.509  0.188 ((3169752 -2307131, 3185342 -2273850, 3130687 -2253… <sf>
st_transform(tab, 3035) |> face_temporal()
#> # cubble:   key: gid [5], index: datetime, long form
#> # temporal: 2020-10-01 -- 2020-10-05 [1D], no gaps
#> # spatial:  long [dbl], lat [dbl], geom_sum [POLYGON [m]]
#>    gid   datetime                                                       geometry
#>  * <chr> <date>                                                    <POLYGON [m]>
#>  1 a     2020-10-01 ((3158048 -2218898, 3151083 -2210627, 3159756 -2196590, 319…
#>  2 a     2020-10-02 ((3152082 -2211840, 3145122 -2203563, 3153799 -2189527, 318…
#>  3 a     2020-10-03 ((3159592 -2213763, 3161300 -2191451, 3195825 -2213869, 315…
#>  4 a     2020-10-04 ((3160545 -2212817, 3161802 -2196385, 3187231 -2212904, 316…
#>  5 a     2020-10-05 ((3165375 -2217510, 3158407 -2209244, 3167076 -2195200, 320…
#>  6 b     2020-10-01 ((3125303 -2212881, 3055244 -2223078, 3055592 -2221881, 305…
#>  7 b     2020-10-02 ((3131127 -2220801, 3061050 -2231032, 3061397 -2229835, 306…
#>  8 b     2020-10-03 ((3137266 -2220797, 3067191 -2231067, 3104291 -2188180, 313…
#>  9 b     2020-10-04 ((3135442 -2219407, 3075499 -2228212, 3107229 -2191506, 313…
#> 10 b     2020-10-05 ((3123921 -2209589, 3053870 -2219778, 3054218 -2218581, 305…
#> # ℹ 15 more rows

Geometric binary predicates

Geometric binary predicates (?sf::geos_binary_pred) are supported by post_* objects in different ways.

For post_array objects, the predicates cannot be applied to the summary geometry dimension, but can be applied to the changing geometries.

st_disjoint(arr, circle)
#> Error in UseMethod("as_s2_geography"): no applicable method for 'as_s2_geography' applied to an object of class "c('post_array', 'stars')"
st_disjoint(arr$geometry, circle)
#> Sparse geometry binary predicate list of length 25, where the predicate
#> was `disjoint'
#> first 10 elements:
#>  1: 1
#>  2: 1
#>  3: (empty)
#>  4: 1
#>  5: 1
#>  6: 1
#>  7: 1
#>  8: (empty)
#>  9: (empty)
#>  10: 1

For post_table objects, the predicates can be applied to either the spatial or the temporal face of the vector data cube.

st_disjoint(tab, circle)
#> Sparse geometry binary predicate list of length 5, where the predicate
#> was `disjoint'
#>  1: 1
#>  2: 1
#>  3: (empty)
#>  4: (empty)
#>  5: 1
st_disjoint(face_temporal(tab), circle)
#> Sparse geometry binary predicate list of length 25, where the predicate
#> was `disjoint'
#> first 10 elements:
#>  1: 1
#>  2: 1
#>  3: 1
#>  4: 1
#>  5: 1
#>  6: 1
#>  7: 1
#>  8: 1
#>  9: 1
#>  10: 1

Applying the predicate to the changing geometry of post_array objects renders the same result as applying it to the post_table objects in the long format or on its temporal face.

sum(st_disjoint(arr$geometry, circle, sparse = FALSE))
#> [1] 19
sum(st_disjoint(face_temporal(tab), circle, sparse = FALSE))
#> [1] 19

Spatial filtering

The predicates shown above are particularly relevant to use inside spatial filtering operations.

sf::st_filter() does not work on post_array objects.

st_filter(arr)
#> Error in UseMethod("st_filter"): no applicable method for 'st_filter' applied to an object of class "c('post_array', 'stars')"
st_filter(arr$geometry)
#> Error in UseMethod("st_filter"): no applicable method for 'st_filter' applied to an object of class "c('sfc_POLYGON', 'sfc')"

But it does work on post_table objects, both for the spatial and temporal face.

st_filter(tab, circle)
#> # cubble:   key: gid [2], index: datetime, nested form, [sf]
#> # spatial:  [0.18, 0.11, 0.99, 0.72], WGS 84
#> # temporal: datetime [date], geometry [POLYGON [°]]
#>   gid    long   lat                                               geom_sum ts   
#> * <chr> <dbl> <dbl>                                          <POLYGON [°]> <lis>
#> 1 c     0.402 0.507 ((0.3321186 0.7217665, 0.1760477 0.4920647, 0.4712469… <sf> 
#> 2 d     0.744 0.341 ((0.6633874 0.573032, 0.500051 0.3696662, 0.8247803 0… <sf>
st_filter(face_temporal(tab), circle)
#> # cubble:   key: gid [2], index: datetime, long form
#> # temporal: 2020-10-01 -- 2020-10-05 [1D], has gaps!
#> # spatial:  long [dbl], lat [dbl], geom_sum [POLYGON [°]]
#>   gid   datetime                                                        geometry
#> * <chr> <date>                                                     <POLYGON [°]>
#> 1 c     2020-10-01 ((0.2807462 0.6277986, 0.3325024 0.7215057, 0.5164992 0.5964…
#> 2 c     2020-10-02 ((0.328914 0.5687438, 0.3806701 0.6624509, 0.5646669 0.53743…
#> 3 c     2020-10-03 ((0.2897647 0.6168588, 0.3415208 0.710566, 0.5357965 0.49222…
#> 4 c     2020-10-05 ((0.2145846 0.5285578, 0.2663408 0.6222649, 0.4503375 0.4972…
#> 5 d     2020-10-02 ((0.7217233 0.5261787, 0.8353717 0.4270488, 0.7832953 0.3407…
#> 6 d     2020-10-03 ((0.6752472 0.4565396, 0.7888956 0.3574097, 0.6915808 0.2158…

When a spatial filtering needs to be applied to the changing geometry of a post_array object, we can first convert to post_table, perform the filtering on the temporal face and then convert back to a post_array object.

arr |> 
  as_post_table() |> 
  face_temporal() |> 
  st_filter(circle) |> 
  as_post_array()
#> Warning: st_centroid assumes attributes are constant over geometries
#> stars object with 2 dimensions and 1 attribute
#> attribute(s):
#>          geometry 
#>  POLYGON      :8  
#>  epsg:4326    :0  
#>  +proj=long...:0  
#> dimension(s):
#>          from to refsys point
#> geom_sum    1  2 WGS 84 FALSE
#> datetime    1  4   Date FALSE
#>                                                                  values
#> geom_sum POLYGON ((0.3321 0.7218, 0...., POLYGON ((0.6634 0.573, 0.5...
#> datetime                                      2020-10-01,...,2020-10-05

Geometric measurements and unary operations

Geometric measurements (?sf::geos_measures) can be applied to the summary and changing geometries as:

# post_array
st_area(st_geometry(arr))
#> Units: [m^2]
#> [1] 1445426429 4038521331 1225403966 1343229500 2148196534
st_area(arr$geometry)
#> Units: [m^2]
#>  [1]  693272313 1537008395  394944271  392819049  585061160  693260956
#>  [7] 1537040160  394948297  392816498  585062691  405985931 1319132956
#> [13]  182397989  385893114  585064729  220231724  965402936   53657361
#> [19]  227321366  384440186  693268587 1536993945  394950797  392821562
#> [25]  585064518
#post_table
st_area(tab)
#> Units: [m^2]
#> [1] 1445426429 4038521331 1225403966 1343229500 2148196534
st_area(face_temporal(tab))
#> Units: [m^2]
#>  [1]  693272313  693260956  405985931  220231724  693268587 1537008395
#>  [7] 1537040160 1319132956  965402936 1536993945  394944271  394948297
#> [13]  182397989   53657361  394950797  392819049  392816498  385893114
#> [19]  227321366  392821562  585061160  585062691  585064729  384440186
#> [25]  585064518

Unary operations (?sf::geos_unary) can be applied to post objects but different behaviors are to be expected. For post_array the operation is performed on the changing geometries by extracting the geometries.

st_boundary(arr$geometry)
#> Geometry set for 25 features 
#> Geometry type: LINESTRING
#> Dimension:     XY
#> Bounding box:  xmin: -0.2974337 ymin: -0.00297557 xmax: 0.9730806 ymax: 1.153558
#> Geodetic CRS:  WGS 84
#> First 5 geometries:
#> LINESTRING (0.5474949 0.8088912, 0.4874872 0.88...
#> LINESTRING (0.2791708 0.8337388, -0.2847997 0.6...
#> LINESTRING (0.2807462 0.6277986, 0.3325024 0.72...
#> LINESTRING (0.7650701 0.4744459, 0.8787185 0.37...
#> LINESTRING (0.3825692 0.3537803, 0.5519791 0.36...

If the operation is applied to a post_table the geometry column gets updated to the new geometry type.

st_centroid(tab)
#> Warning: st_centroid assumes attributes are constant over geometries
#> # cubble:   key: gid [5], index: datetime, nested form, [sf]
#> # spatial:  [-0.01, 0.19, 0.74, 0.9], WGS 84
#> # temporal: datetime [date], geometry [POLYGON [°]]
#>   gid      long   lat                geom_sum ts          
#>   <chr>   <dbl> <dbl>             <POINT [°]> <list>      
#> 1 a      0.638  0.885   (0.6382744 0.8853982) <sf [5 × 2]>
#> 2 b     -0.0136 0.898 (-0.01362879 0.8983264) <sf [5 × 2]>
#> 3 c      0.402  0.507   (0.4016836 0.5066306) <sf [5 × 2]>
#> 4 d      0.744  0.341   (0.7440853 0.3409447) <sf [5 × 2]>
#> 5 e      0.509  0.188   (0.5091203 0.1882993) <sf [5 × 2]>
plot(tab$geom_sum)
plot(st_centroid(tab)$geom_sum)
#> Warning: st_centroid assumes attributes are constant over geometries

Original summary geometry: minimum rotated rectangle

Updated summary geometry: centroid

dplyr compatibility

Both post classes support dplyr methods as long as they don’t alter the vector data cube structure. See the available methods in ?dplyr-post-array and ?dplyr-post-table. Some specific behaviors of these functions are explained below.

Modifying columns

The dplyr::mutate() function modifies the existing object by creating or modifying existing columns. It is useful in combination with geometric measurement functions from sf. For instance, we can compute the area of the changing geometry as:

arr |> 
  mutate(area = st_area(geometry))
#> stars object with 2 dimensions and 2 attributes
#> attribute(s):
#>          geometry     area [m^2]       
#>  POLYGON      :25   Min.   :5.366e+07  
#>  epsg:4326    : 0   1st Qu.:3.928e+08  
#>  +proj=long...: 0   Median :4.060e+08  
#>                     Mean   :6.216e+08  
#>                     3rd Qu.:6.933e+08  
#>                     Max.   :1.537e+09  
#> dimension(s):
#>          from to     offset  delta refsys point
#> geom_sum    1  5         NA     NA WGS 84 FALSE
#> datetime    1  5 2020-10-01 1 days   Date FALSE
#>                                                                 values
#> geom_sum POLYGON ((0.5137787 1.117...,...,POLYGON ((0.678186 -0.059...
#> datetime                                                          NULL
tab |> 
  face_temporal() |> 
  mutate(area = st_area(geometry))
#> # cubble:   key: gid [5], index: datetime, long form
#> # temporal: 2020-10-01 -- 2020-10-05 [1D], no gaps
#> # spatial:  long [dbl], lat [dbl], geom_sum [POLYGON [°]]
#>    gid   datetime                                                geometry   area
#>  * <chr> <date>                                             <POLYGON [°]>  [m^2]
#>  1 a     2020-10-01 ((0.5474949 0.8088912, 0.4874872 0.8838477, 0.552005… 6.93e8
#>  2 a     2020-10-02 ((0.4961102 0.8728385, 0.4361026 0.947795, 0.5006209… 6.93e8
#>  3 a     2020-10-03 ((0.5578801 0.8616378, 0.5623908 1.085448, 0.8520717… 4.06e8
#>  4 a     2020-10-04 ((0.5652241 0.872057, 0.5685463 1.036898, 0.7819022 … 2.20e8
#>  5 a     2020-10-05 ((0.6063791 0.8304178, 0.5463715 0.9053744, 0.610889… 6.93e8
#>  6 b     2020-10-01 ((0.2791708 0.8337388, -0.2847997 0.6540191, -0.2825… 1.54e9
#>  7 b     2020-10-02 ((0.3298312 0.7612067, -0.2341393 0.5814869, -0.2318… 1.54e9
#>  8 b     2020-10-03 ((0.3796448 0.767853, -0.1843257 0.5881332, 0.097774… 1.32e9
#>  9 b     2020-10-04 ((0.3642467 0.779727, -0.1182199 0.6259799, 0.123111… 9.65e8
#> 10 b     2020-10-05 ((0.2665368 0.8649996, -0.2974337 0.6852798, -0.2951… 1.54e9
#> # ℹ 15 more rows

For post_table objects it is also possible to use them in the summary geoemtry.

tab |> mutate(area = st_area(geom_sum))
#> # cubble:   key: gid [5], index: datetime, nested form, [sf]
#> # spatial:  [-0.41, -0.06, 0.99, 1.23], WGS 84
#> # temporal: datetime [date], geometry [POLYGON [°]]
#>   gid      long   lat                                      geom_sum ts      area
#> * <chr>   <dbl> <dbl>                                 <POLYGON [°]> <lis>  [m^2]
#> 1 a      0.638  0.885 ((0.5137787 1.117004, 0.3759687 0.9038164, 0… <sf>  1.45e9
#> 2 b     -0.0136 0.898 ((0.3796448 0.767853, 0.2316513 1.232265, -0… <sf>  4.04e9
#> 3 c      0.402  0.507 ((0.3321186 0.7217665, 0.1760477 0.4920647, … <sf>  1.23e9
#> 4 d      0.744  0.341 ((0.6633874 0.573032, 0.500051 0.3696662, 0.… <sf>  1.34e9
#> 5 e      0.509  0.188 ((0.678186 -0.05907163, 0.7911437 0.2894808,… <sf>  2.15e9

Sticky geometry

Following the sf design of an sticky geometry, both post_array and post_table methods, as spatial or temporal cubes have sticky geometries when applying dplyr methods.

(arr2 = arr |> 
  mutate(
    area = st_area(geometry),
    perimeter = st_perimeter(geometry)
))
#> stars object with 2 dimensions and 3 attributes
#> attribute(s):
#>          geometry     area [m^2]        perimeter [m]   
#>  POLYGON      :25   Min.   :5.366e+07   Min.   : 40715  
#>  epsg:4326    : 0   1st Qu.:3.928e+08   1st Qu.: 80659  
#>  +proj=long...: 0   Median :4.060e+08   Median : 96203  
#>                     Mean   :6.216e+08   Mean   :102089  
#>                     3rd Qu.:6.933e+08   3rd Qu.:107075  
#>                     Max.   :1.537e+09   Max.   :172705  
#> dimension(s):
#>          from to     offset  delta refsys point
#> geom_sum    1  5         NA     NA WGS 84 FALSE
#> datetime    1  5 2020-10-01 1 days   Date FALSE
#>                                                                 values
#> geom_sum POLYGON ((0.5137787 1.117...,...,POLYGON ((0.678186 -0.059...
#> datetime                                                          NULL
arr2 |> select(perimeter)
#> stars object with 2 dimensions and 2 attributes
#> attribute(s):
#>  perimeter [m]            geometry  
#>  Min.   : 40715   POLYGON      :25  
#>  1st Qu.: 80659   epsg:4326    : 0  
#>  Median : 96203   +proj=long...: 0  
#>  Mean   :102089                     
#>  3rd Qu.:107075                     
#>  Max.   :172705                     
#> dimension(s):
#>          from to     offset  delta refsys point
#> geom_sum    1  5         NA     NA WGS 84 FALSE
#> datetime    1  5 2020-10-01 1 days   Date FALSE
#>                                                                 values
#> geom_sum POLYGON ((0.5137787 1.117...,...,POLYGON ((0.678186 -0.059...
#> datetime                                                          NULL

For post_table objects, cubble also applies a sticky concept for the key (group_id), index (time_column) and coords, and prints a message to inform you about it.

(tab2 = tab |> 
   face_temporal() |> 
    mutate(
      area = st_area(geometry),
      perimeter = st_perimeter(geometry)
))
#> # cubble:   key: gid [5], index: datetime, long form
#> # temporal: 2020-10-01 -- 2020-10-05 [1D], no gaps
#> # spatial:  long [dbl], lat [dbl], geom_sum [POLYGON [°]]
#>    gid   datetime                                      geometry   area perimeter
#>  * <chr> <date>                                   <POLYGON [°]>  [m^2]       [m]
#>  1 a     2020-10-01 ((0.5474949 0.8088912, 0.4874872 0.8838477… 6.93e8   107076.
#>  2 a     2020-10-02 ((0.4961102 0.8728385, 0.4361026 0.947795,… 6.93e8   107075.
#>  3 a     2020-10-03 ((0.5578801 0.8616378, 0.5623908 1.085448,… 4.06e8    96203.
#>  4 a     2020-10-04 ((0.5652241 0.872057, 0.5685463 1.036898, … 2.20e8    70856.
#>  5 a     2020-10-05 ((0.6063791 0.8304178, 0.5463715 0.9053744… 6.93e8   107075.
#>  6 b     2020-10-01 ((0.2791708 0.8337388, -0.2847997 0.654019… 1.54e9   172703.
#>  7 b     2020-10-02 ((0.3298312 0.7612067, -0.2341393 0.581486… 1.54e9   172705.
#>  8 b     2020-10-03 ((0.3796448 0.767853, -0.1843257 0.5881332… 1.32e9   171452.
#>  9 b     2020-10-04 ((0.3642467 0.779727, -0.1182199 0.6259799… 9.65e8   146674.
#> 10 b     2020-10-05 ((0.2665368 0.8649996, -0.2974337 0.685279… 1.54e9   172702.
#> # ℹ 15 more rows
tab2 |> select(perimeter)
#> ℹ Missing attribute `gid` and `datetime`, add it back.
#> # cubble:   key: gid [5], index: datetime, long form
#> # temporal: 2020-10-01 -- 2020-10-05 [1D], no gaps
#> # spatial:  long [dbl], lat [dbl], geom_sum [POLYGON [°]]
#>    gid   datetime   perimeter                                           geometry
#>  * <chr> <date>           [m]                                      <POLYGON [°]>
#>  1 a     2020-10-01   107076. ((0.5474949 0.8088912, 0.4874872 0.8838477, 0.552…
#>  2 a     2020-10-02   107075. ((0.4961102 0.8728385, 0.4361026 0.947795, 0.5006…
#>  3 a     2020-10-03    96203. ((0.5578801 0.8616378, 0.5623908 1.085448, 0.8520…
#>  4 a     2020-10-04    70856. ((0.5652241 0.872057, 0.5685463 1.036898, 0.78190…
#>  5 a     2020-10-05   107075. ((0.6063791 0.8304178, 0.5463715 0.9053744, 0.610…
#>  6 b     2020-10-01   172703. ((0.2791708 0.8337388, -0.2847997 0.6540191, -0.2…
#>  7 b     2020-10-02   172705. ((0.3298312 0.7612067, -0.2341393 0.5814869, -0.2…
#>  8 b     2020-10-03   171452. ((0.3796448 0.767853, -0.1843257 0.5881332, 0.097…
#>  9 b     2020-10-04   146674. ((0.3642467 0.779727, -0.1182199 0.6259799, 0.123…
#> 10 b     2020-10-05   172702. ((0.2665368 0.8649996, -0.2974337 0.6852798, -0.2…
#> # ℹ 15 more rows

Wrangle and conversion

The usefulness of having two classes in post lies on the ability to apply certain functions in the tabular format and then other functions on the array format with conversion.

arr2 |> 
  as_post_table() |> 
  face_temporal()
#> Warning: st_centroid assumes attributes are constant over geometries
#> # cubble:   key: gid [5], index: datetime, long form
#> # temporal: 2020-10-01 -- 2020-10-05 [1D], no gaps
#> # spatial:  long [dbl], lat [dbl], geom_sum [POLYGON [°]]
#>    gid   datetime          area perimeter                               geometry
#>  * <chr> <date>           [m^2]       [m]                          <POLYGON [°]>
#>  1 a     2020-10-01  693272313.   107076. ((0.5474949 0.8088912, 0.4874872 0.88…
#>  2 a     2020-10-02  693260956.   107075. ((0.4961102 0.8728385, 0.4361026 0.94…
#>  3 a     2020-10-03  405985931.    96203. ((0.5578801 0.8616378, 0.5623908 1.08…
#>  4 a     2020-10-04  220231724.    70856. ((0.5652241 0.872057, 0.5685463 1.036…
#>  5 a     2020-10-05  693268587.   107075. ((0.6063791 0.8304178, 0.5463715 0.90…
#>  6 b     2020-10-01 1537008395.   172703. ((0.2791708 0.8337388, -0.2847997 0.6…
#>  7 b     2020-10-02 1537040160.   172705. ((0.3298312 0.7612067, -0.2341393 0.5…
#>  8 b     2020-10-03 1319132956.   171452. ((0.3796448 0.767853, -0.1843257 0.58…
#>  9 b     2020-10-04  965402936.   146674. ((0.3642467 0.779727, -0.1182199 0.62…
#> 10 b     2020-10-05 1536993945.   172702. ((0.2665368 0.8649996, -0.2974337 0.6…
#> # ℹ 15 more rows

There are, however, certain issues to be taken into account when doing this.

1. post_table cannot be created from an empty post_array.

If a filtering or subsetting operation is applied to a post_array, which can handle emptiness, when converting to a post_table we get an error.

as_post_array(polygons)[circle]
#> stars object with 2 dimensions and 1 attribute
#> attribute(s):
#>          geometry 
#>  epsg:4326    :0  
#>  +proj=long...:0  
#> dimension(s):
#>          from to     offset  delta refsys point values
#> geom_sum    1  5         NA     NA WGS 84  TRUE       
#> datetime    1  5 2020-10-01 1 days   Date FALSE   NULL
as_post_array(polygons)[circle] |> as_post_table()
#> Error in if (prod(dim(x))%%length(value) != 0) {: missing value where TRUE/FALSE needed

We can however have empty post_table objects by doing the filtering after conversion.

as_post_array(polygons) |> 
  as_post_table() |> 
  st_filter(circle)
#> # cubble: key: gid [0], index: datetime, nested form, [sf]
#> # ℹ 5 variables: gid <chr>, long <dbl>, lat <dbl>, geom_sum <GEOMETRY [°]>,
#> #   ts <list>

2. post_table requires at least two time values per group for creation

When converting and sf or post_array object to post_table, a single time value per group is not supported.

arr |> 
  filter(datetime == "2020-10-01") |> 
  as_post_table()
#> Error in `as_post_table()`:
#> ! `x` has only one time value per group
#> <post_table> creation requires at least two time values

You can have, however, a post_table with a single time value per group after its creation.

arr |> 
  as_post_table() |> 
  face_temporal() |> 
  filter(datetime == "2020-10-01") |> 
  face_spatial()
#> Warning: st_centroid assumes attributes are constant over geometries
#> # cubble:   key: gid [5], index: datetime, nested form, [sf]
#> # spatial:  [-0.41, -0.06, 0.99, 1.23], WGS 84
#> # temporal: datetime [date], geometry [POLYGON [°]]
#>   gid      long   lat                                             geom_sum ts   
#> * <chr>   <dbl> <dbl>                                        <POLYGON [°]> <lis>
#> 1 a      0.638  0.885 ((0.5137787 1.117004, 0.3759687 0.9038164, 0.762760… <sf> 
#> 2 b     -0.0136 0.898 ((0.3796448 0.767853, 0.2316513 1.232265, -0.406896… <sf> 
#> 3 c      0.402  0.507 ((0.3321186 0.7217665, 0.1760477 0.4920647, 0.47124… <sf> 
#> 4 d      0.744  0.341 ((0.6633874 0.573032, 0.500051 0.3696662, 0.8247803… <sf> 
#> 5 e      0.509  0.188 ((0.678186 -0.05907163, 0.7911437 0.2894808, 0.3400… <sf>

3. Do not drop dimensions when slicing post_array objects

The default behavior of dplyr::slice() for stars and post_table objects is to drop dimensions with a single value.

arr |> 
  slice("datetime", 1)
#> stars object with 1 dimensions and 1 attribute
#> attribute(s):
#>          geometry 
#>  POLYGON      :5  
#>  epsg:4326    :0  
#>  +proj=long...:0  
#> dimension(s):
#>          from to refsys point
#> geom_sum    1  5 WGS 84 FALSE
#>                                                                 values
#> geom_sum POLYGON ((0.5137787 1.117...,...,POLYGON ((0.678186 -0.059...

As we saw in 2, this brings problems when converting to a post_table but in general it also generates other types of issues. Make a habit of using drop = FALSE instead.

arr |> 
  slice("geom_sum", 1, drop = FALSE) |> 
  as_post_table()
#> Warning: st_centroid assumes attributes are constant over geometries
#> # cubble:   key: gid [1], index: datetime, nested form, [sf]
#> # spatial:  [0.38, 0.65, 0.9, 1.12], WGS 84
#> # temporal: datetime [date], geometry [POLYGON [°]]
#>   gid    long   lat                                               geom_sum ts   
#> * <chr> <dbl> <dbl>                                          <POLYGON [°]> <lis>
#> 1 a     0.638 0.885 ((0.5137787 1.117004, 0.3759687 0.9038164, 0.7627607 … <sf>