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 cubble
s 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.
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.
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
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>