# A regular cube, by date and by place

A year of one tile made regular by sits: a stack of one image per period, the series of one cell in every band, the properties of a regular cube, and the dates where cells have no value.

[From step 5 of Building data cubes from image collections](/regular-cubes.md).

In the sits book: [Earth observation data cubes](https://e-sensing.github.io/sitsbook/datacubes.html) and [Building regular data cubes](https://e-sensing.github.io/sitsbook/dc_regularize.html).

Choose a date on the strip, a cell on the open sheet and one band or all, and read the image of that date and the series of that cell.

## The figure

The year of one tile made regular by sits, as a stack of one image per period; each sheet shows a small window of cells around the chosen cell, and the chart its series in every band.

The figure (recorded: 176 images made regular by sits, 23 dates) draws the cube as a stack of one sheet per period, time going up, each sheet a window of 12 × 12 of the 256 × 200 cells around the chosen cell; the open sheet carries the picture in natural colour (B04, B03, B02). The chosen cell is outlined on every sheet, dashed where it has no value. Beside it, the properties of a regular cube; under it, the series of the cell in every band, open where there is no value, and a strip with one bar per date for the share of the region with a value. The reader chooses a date on the strip, a cell on the open sheet, and all the bands or one.

| Property of a regular cube (in the sits book's words)                                                                                                                                                                                                                          | This cube                                         |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------- |
| A regular data cube is a set of three-dimensional arrays with two spatial dimensions and one temporal dimension. Each array contains cells which have the same spatial resolution, the same temporal duration, and the same set of attributes (spectral bands and/or indices). | 256 × 200 cells of 40 m × 23 intervals of 16 days |
| The number of dimensions is fixed (2D + time), while the number of attributes is not limited.                                                                                                                                                                                  | 5 bands: B02, B03, B04, B8A, B11                  |
| The spatial dimensions are associated with a coordinate projection system, such as the MGRS grid (Military Grid Reference System). A tile of the grid corresponds to a unique zone of the coordinate system. A data cube may span various tiles and projection zones.          | EPSG:32720 (UTM zone 20S), tile 20LLP             |
| The temporal dimension is a set of continuous and equally spaced intervals.                                                                                                                                                                                                    | 23 intervals of 16 days from 6 Jan 2018           |
| For each position in space, the cube should provide a multi-attribute time series. For each time interval, the cube should provide a multi-attribute 2D image.                                                                                                                 | 51,200 series of 23 dates; 23 images              |

Of the 23 dates, 7 are complete and 16 have cells with no value. The longest run of complete dates is 7, from 15 Jun 2018 to 19 Sep 2018.

| Date        | Share of the region with no value |
| ----------- | --------------------------------- |
| 6 Jan 2018  | 39%                               |
| 22 Jan 2018 | 100%                              |
| 7 Feb 2018  | 54%                               |
| 23 Feb 2018 | 1%                                |
| 11 Mar 2018 | 17%                               |
| 27 Mar 2018 | 5%                                |
| 12 Apr 2018 | 10%                               |
| 28 Apr 2018 | 8%                                |
| 14 May 2018 | 19%                               |
| 30 May 2018 | 60%                               |
| 15 Jun 2018 | none                              |
| 1 Jul 2018  | none                              |
| 17 Jul 2018 | none                              |
| 2 Aug 2018  | none                              |
| 18 Aug 2018 | none                              |
| 3 Sep 2018  | none                              |
| 19 Sep 2018 | none                              |
| 5 Oct 2018  | 56%                               |
| 21 Oct 2018 | 40%                               |
| 6 Nov 2018  | 18%                               |
| 22 Nov 2018 | 37%                               |
| 8 Dec 2018  | 21%                               |
| 24 Dec 2018 | 76%                               |

The figure opens on cell col 126 · row 136 and on 15 Jun 2018. Its series, in surface reflectance:

| Date        | B02      | B03      | B04      | B8A      | B11      |
| ----------- | -------- | -------- | -------- | -------- | -------- |
| 6 Jan 2018  | no value | no value | no value | no value | no value |
| 22 Jan 2018 | no value | no value | no value | no value | no value |
| 7 Feb 2018  | no value | no value | no value | no value | no value |
| 23 Feb 2018 | 0.02     | 0.04     | 0.02     | 0.29     | 0.14     |
| 11 Mar 2018 | 0.03     | 0.04     | 0.02     | 0.35     | 0.17     |
| 27 Mar 2018 | 0.03     | 0.05     | 0.03     | 0.35     | 0.17     |
| 12 Apr 2018 | 0.04     | 0.06     | 0.04     | 0.33     | 0.16     |
| 28 Apr 2018 | 0.05     | 0.07     | 0.04     | 0.36     | 0.17     |
| 14 May 2018 | 0.02     | 0.03     | 0.02     | 0.28     | 0.13     |
| 30 May 2018 | 0.21     | 0.23     | 0.21     | 0.49     | 0.35     |
| 15 Jun 2018 | 0.02     | 0.03     | 0.02     | 0.28     | 0.13     |
| 1 Jul 2018  | 0.02     | 0.03     | 0.02     | 0.26     | 0.13     |
| 17 Jul 2018 | 0.02     | 0.04     | 0.02     | 0.27     | 0.14     |
| 2 Aug 2018  | 0.03     | 0.04     | 0.03     | 0.29     | 0.15     |
| 18 Aug 2018 | 0.02     | 0.04     | 0.02     | 0.29     | 0.15     |
| 3 Sep 2018  | 0.03     | 0.04     | 0.03     | 0.30     | 0.14     |
| 19 Sep 2018 | 0.03     | 0.05     | 0.03     | 0.33     | 0.17     |
| 5 Oct 2018  | no value | no value | no value | no value | no value |
| 21 Oct 2018 | 0.06     | 0.06     | 0.04     | 0.17     | 0.07     |
| 6 Nov 2018  | 0.03     | 0.05     | 0.03     | 0.40     | 0.17     |
| 22 Nov 2018 | 0.03     | 0.04     | 0.02     | 0.18     | 0.07     |
| 8 Dec 2018  | 0.09     | 0.09     | 0.08     | 0.23     | 0.14     |
| 24 Dec 2018 | no value | no value | no value | no value | no value |

### How it was made

1. Ask the catalogue: sits asks the provider's catalogue which images cover the book's tiles, bands and dates, and a whole year of one tile. The answers are kept, so every later run uses the same list.
2. Find the tiles: The pipeline finds the tiles of the satellite's grid around the region, from the grid that comes with sits. It reads a small picture of each tile of the cube from its least cloudy image.
3. Build the regular cube: sits makes a regular cube of the region from those images: one image per period, all on the same grid of pixels. For each pixel of a period it takes the first valid value, least cloudy image first.
4. Read the images as they come: The pipeline reads the images the catalogue listed, as they come. It marks each pixel as having no data, a clear value, or a value sits masks, such as cloud or shadow.
5. Try other periods and pixel sizes: sits makes the regular cube again at other periods and pixel sizes. It also makes it once without the images' cloud cover, so sits takes the images in date order, not least cloudy first.
6. Read each image alone: Each image is read alone, on the grid sits builds for it. Stacked in the order sits takes them, the images show which one gave each pixel of the regular cube.
7. Check against sits: The pipeline compares its own reads with what sits made, and stops the run on the checks that must hold. The results are gathered only once those checks pass.
8. Prepare the pictures: The pipeline picks the brightness drawn black and white, the same for every date, so the dates can be compared. Values are packed into one byte each.
9. Write the files: The pipeline writes the files the website reads, each with a record of its sources and of how closely a rerun repeats it.

### Reproducibility details

- A rerun on aarch64-apple-darwin23 gave the same files, byte for byte.

Every step of the pipeline:

1. Lists the bands read: the book's and those of the natural-colour pictures, in the order sits has them. `cube_read_bands("AWS", "SENTINEL-2-L2A", c("B02", "B8A", "B11"), c("B03", "B04"))`
2. Lists every period with every resolution of the runs. `regularize_choices(c("P8D", "P16D", "P1M"), c(20L, 40L, 80L))`
3. The arguments of the book's call to sits_cube(). `cube_call(list(source = "AWS", collection = "SENTINEL-2-L2A", tiles = c("20LKP", "20LLP"), bands = c("B02", "B8A", "B11" ), picture_bands = c("B03", "B04"), cloud_band = "CLOUD", start_date = "2018-06-30", end_date = "2018-08-31", period = "P16D", res = 40L, region = list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), year = list( tile = "20LLP", start_date = "2018-01-01", end_date = "2018-12-31", window = 12L), multicores = 4L, rgb = list(red = "B04", green = "B03", blue = "B02"), stretch = c(0.02, 0.98), levels = 32L, quantiles = c(0.001, 0.999), map = list(around_km = 100L, overview = 3L)), cube_bands)`
4. The same call for one tile over a whole year. `cube_call(list(source = "AWS", collection = "SENTINEL-2-L2A", tiles = c("20LKP", "20LLP"), bands = c("B02", "B8A", "B11" ), picture_bands = c("B03", "B04"), cloud_band = "CLOUD", start_date = "2018-06-30", end_date = "2018-08-31", period = "P16D", res = 40L, region = list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), year = list( tile = "20LLP", start_date = "2018-01-01", end_date = "2018-12-31", window = 12L), multicores = 4L, rgb = list(red = "B04", green = "B03", blue = "B02"), stretch = c(0.02, 0.98), levels = 32L, quantiles = c(0.001, 0.999), map = list(around_km = 100L, overview = 3L)), cube_bands, "20LLP", "2018-01-01", "2018-12-31")`
5. Lists the bands of the runs: the natural-colour ones, in the order sits has them. `regularize_run_bands(cube_bands, list(red = "B04", green = "B03", blue = "B02"))`
6. Lists the images of the book's call with sits_cube(): a copy of the live AWS catalogue, kept in cache/cubes/ and read from there. `cube_kept_listing(cube_book_call, "cache/cubes", "cube_book")`
7. Lists the images of the year's call with sits_cube(): a copy of the live AWS catalogue, kept in cache/cubes/ and read from there. `cube_kept_listing(cube_year_call, "cache/cubes", "cube_year")`
8. Makes the regular cube of the book's call over the region with sits_regularize(), keeping the files already made. `cube_regularize(cube_listing, list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), "P16D", 40L, "cache/cubes/reg_book_B02-B03-B04-B8A-B11", 4L)`
9. Finds the Sentinel-2 tiles around the region. `cube_images_grid(list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), cube_listing, 100L)`
10. Reads a small picture of each tile from the overview of its clearest image, keeping the reads in cache/cubes/. `cube_images_read(cube_listing, list(red = "B04", green = "B03", blue = "B02"), 3L, "cache/cubes")`
11. Makes the regular cube of the year with sits_regularize(), keeping the files already made. `cube_regularize(cube_year_listing, list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), "P16D", 40L, "cache/cubes/reg_year_B02-B03-B04-B8A-B11", 4L)`
12. Runs sits_regularize() on the book's images at one period and resolution, keeping the files in cache/regularize/. `regularize_run_cube(cube_listing, regularize_choice, regularize_bands, "CLOUD", list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), "cache/regularize", 4L, 1L)`
13. Reads every image of each tile over the tile's part of the region, as it comes, keeping the reads in cache/cubes/. `cube_tile_images(cube_listing, cube_book, cube_book_call, 40L, "EPSG:32720", "cache/cubes")`
14. Gathers the images, ranked in the order sits takes them, the regular values, the year and the map. `cube_export_gather(cube_listing, cube_book, cube_images, cube_year_listing, cube_year, cube_map_images, cube_map_grid, cube_book_call, list(source = "AWS", collection = "SENTINEL-2-L2A", tiles = c("20LKP", "20LLP"), bands = c("B02", "B8A", "B11"), picture_bands = c("B03", "B04"), cloud_band = "CLOUD", start_date = "2018-06-30", end_date = "2018-08-31", period = "P16D", res = 40L, region = list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), year = list(tile = "20LLP", start_date = "2018-01-01", end_date = "2018-12-31", window = 12L), multicores = 4L, rgb = list(red = "B04", green = "B03", blue = "B02"), stretch = c(0.02, 0.98), levels = 32L, quantiles = c(0.001, 0.999), map = list(around_km = 100L, overview = 3L)), 40L)`
15. Marks each pixel of each image as no data, clear or masked by sits. `purrr::map(cubes_record$tiles, cube_images_states, cubes_record$cloud_values)`
16. Picks the reflectance drawn black and white, one for red, green and blue. `cube_images_stretch(cubes_record, list(red = "B04", green = "B03", blue = "B02"), c(0.02, 0.98))`
17. Stores the year in one byte per value, each band between two percentiles. `cube_export_year_codes(cubes_record$year$values, c(0.001, 0.999))`
18. Reads each image of the book's periods alone, on the view sits builds for it with no mask, keeping the reads in cache/regular_cubes/. `cube_alone_book_reads(cube_listing, cubes_record, "B02", list(source = "AWS", collection = "SENTINEL-2-L2A", tiles = c("20LKP", "20LLP"), bands = c("B02", "B8A", "B11"), picture_bands = c("B03", "B04"), cloud_band = "CLOUD", start_date = "2018-06-30", end_date = "2018-08-31", period = "P16D", res = 40L, region = list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), year = list(tile = "20LLP", start_date = "2018-01-01", end_date = "2018-12-31", window = 12L), multicores = 4L, rgb = list(red = "B04", green = "B03", blue = "B02"), stretch = c(0.02, 0.98), levels = 32L, quantiles = c(0.001, 0.999), map = list(around_km = 100L, overview = 3L)), "cache/regular_cubes/alone_book_B02", "/Users/felipecarlos/Documents/sits/sits-embeddings/pipeline/cache/tmp", 0.5)`
19. Runs sits_regularize() on the book's call with the scene cloud cover removed, keeping the result in cache/regular_cubes/. `cube_checks_copied_run(cube_listing, "B02", list(source = "AWS", collection = "SENTINEL-2-L2A", tiles = c("20LKP", "20LLP" ), bands = c("B02", "B8A", "B11"), picture_bands = c("B03", "B04"), cloud_band = "CLOUD", start_date = "2018-06-30", end_date = "2018-08-31", period = "P16D", res = 40L, region = list( crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), year = list(tile = "20LLP", start_date = "2018-01-01", end_date = "2018-12-31", window = 12L), multicores = 4L, rgb = list(red = "B04", green = "B03", blue = "B02"), stretch = c(0.02, 0.98), levels = 32L, quantiles = c(0.001, 0.999), map = list( around_km = 100L, overview = 3L)), cubes_record, "cache/regular_cubes", "/Users/felipecarlos/Documents/sits/sits-embeddings/pipeline/cache/tmp")`
20. Reads the grid of the region on each tile at each resolution from the runs of the book's period. `cube_checks_region_grids(cube_listing, regularize_run, cubes_record, list(source = "AWS", collection = "SENTINEL-2-L2A", tiles = c("20LKP", "20LLP"), bands = c("B02", "B8A", "B11"), picture_bands = c("B03", "B04"), cloud_band = "CLOUD", start_date = "2018-06-30", end_date = "2018-08-31", period = "P16D", res = 40L, region = list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), year = list(tile = "20LLP", start_date = "2018-01-01", end_date = "2018-12-31", window = 12L), multicores = 4L, rgb = list(red = "B04", green = "B03", blue = "B02"), stretch = c(0.02, 0.98), levels = 32L, quantiles = c(0.001, 0.999), map = list(around_km = 100L, overview = 3L)), c(20L, 40L, 80L))`
21. Runs sits_regularize() on one image alone and gdalwarp at three overview choices, keeping the result in cache/regular_cubes/. `cube_checks_overview_check_run(cube_listing, cubes_record, list(tile = "20LLP", band = "B02"), list(source = "AWS", collection = "SENTINEL-2-L2A", tiles = c("20LKP", "20LLP" ), bands = c("B02", "B8A", "B11"), picture_bands = c("B03", "B04"), cloud_band = "CLOUD", start_date = "2018-06-30", end_date = "2018-08-31", period = "P16D", res = 40L, region = list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), year = list(tile = "20LLP", start_date = "2018-01-01", end_date = "2018-12-31", window = 12L), multicores = 4L, rgb = list(red = "B04", green = "B03", blue = "B02"), stretch = c(0.02, 0.98 ), levels = 32L, quantiles = c(0.001, 0.999), map = list( around_km = 100L, overview = 3L)), "cache/regular_cubes", "/Users/felipecarlos/Documents/sits/sits-embeddings/pipeline/cache/tmp")`
22. Reads the same image alone in natural colour and runs sits_regularize() on it with those bands. `cube_checks_pictures_check_run(cube_listing, cubes_record, list(tile = "20LLP", band = "B02"), regularize_bands, list( source = "AWS", collection = "SENTINEL-2-L2A", tiles = c("20LKP", "20LLP"), bands = c("B02", "B8A", "B11"), picture_bands = c("B03", "B04"), cloud_band = "CLOUD", start_date = "2018-06-30", end_date = "2018-08-31", period = "P16D", res = 40L, region = list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), year = list(tile = "20LLP", start_date = "2018-01-01", end_date = "2018-12-31", window = 12L), multicores = 4L, rgb = list(red = "B04", green = "B03", blue = "B02"), stretch = c(0.02, 0.98 ), levels = 32L, quantiles = c(0.001, 0.999), map = list( around_km = 100L, overview = 3L)), "cache/regular_cubes", "/Users/felipecarlos/Documents/sits/sits-embeddings/pipeline/cache/tmp")`
23. Pairs the cells of the strip both tiles cover. `cube_checks_book_strip(cubes_record, c("20LKP", "20LLP" ), 40L)`
24. Runs sits_regularize() on each image of the year alone, with the date of its period, keeping the files in cache/regularize/. `regularize_year_alone(cube_year_listing, cubes_record, "B02", "CLOUD", list(source = "AWS", collection = "SENTINEL-2-L2A", tiles = c("20LKP", "20LLP"), bands = c("B02", "B8A", "B11"), picture_bands = c("B03", "B04"), cloud_band = "CLOUD", start_date = "2018-06-30", end_date = "2018-08-31", period = "P16D", res = 40L, region = list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), year = list(tile = "20LLP", start_date = "2018-01-01", end_date = "2018-12-31", window = 12L), multicores = 4L, rgb = list(red = "B04", green = "B03", blue = "B02"), stretch = c(0.02, 0.98), levels = 32L, quantiles = c(0.001, 0.999), map = list(around_km = 100L, overview = 3L)), "cache/regularize/year_20llp_B02", 1L)`
25. Stacks the images of each period in sits' order and finds the one each pixel came from. `cube_alone_book_stacks(regular_cubes_book_reads, cubes_record, "B02")`
26. Compares the run with no scene cloud cover with the book's regular cube, after checking it against the images read alone. `cube_checks_copied_entries(regular_cubes_copied_run, cubes_record, regular_cubes_book_reads, cube_listing, "B02")`
27. Compares the two tiles' regular images where they overlap. `cube_checks_overlap_periods(cubes_record, regular_cubes_strip, "B02")`
28. Stops the run unless gdalwarp -ovr AUTO and the images read alone give sits' values on every pixel. `cube_checks_check_overview(regular_cubes_overview, regular_cubes_book_reads, regular_cubes_pictures)`
29. Draws one tile's images as they come and its regular images, small, for the journey's picture. `cube_checks_export_picture(cubes_record, cube_states, list(red = "B04", green = "B03", blue = "B02"), cube_stretch, 32L, list(tile = "20LLP", card_tile = 64L, sheet_tile = 192L))`
30. Finds which image gives each pixel of each period of the year, in the order sits takes them. `regularize_year_sources(regularize_alone, "20LLP", "B02")`
31. Writes one tile's part of the region over the year, one byte per value, for the website. `site_write_bin(cube_export_year_bytes(cube_year_codes$codes), "../web/public/data/cube-year.bin", "uint8")`
32. Writes the book's call, its images and regular cube, the map and the year, for the website. ``site_write_json(cube_export_record(cubes_record, cube_states, cube_stretch, cube_year_codes, list(source = "AWS", collection = "SENTINEL-2-L2A", tiles = c("20LKP", "20LLP"), bands = c("B02", "B8A", "B11"), picture_bands = c("B03", "B04"), cloud_band = "CLOUD", start_date = "2018-06-30", end_date = "2018-08-31", period = "P16D", res = 40L, region = list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), year = list(tile = "20LLP", start_date = "2018-01-01", end_date = "2018-12-31", window = 12L), multicores = 4L, rgb = list(red = "B04", green = "B03", blue = "B02"), stretch = c(0.02, 0.98), levels = 32L, quantiles = c(0.001, 0.999), map = list(around_km = 100L, overview = 3L)), list(tiles = list(`20LKP` = list(ard = "cube-ard-20lkp.png", mask = "cube-ard-20lkp-mask.png", regular = "cube-regular-20lkp.png", map = "cube-map-20lkp.png"), `20LLP` = list(ard = "cube-ard-20llp.png", mask = "cube-ard-20llp-mask.png", regular = "cube-regular-20llp.png", map = "cube-map-20llp.png")), year = "cube-year.bin")), "../web/src/data/cubes.json")``
33. Stops the run unless the images read alone, stacked in sits' order, give the book's regular cube on every pixel. `cube_alone_check_book(regular_cubes_book, cubes_record, "B02", "cache/regular_cubes/alone_book_B02")`
34. Reads each image of the year alone in natural colour, with its cloud band, keeping the reads in cache/regular_cubes/. `cube_alone_year_reads(cube_year_listing, cubes_record, regularize_sources, regularize_bands, list(source = "AWS", collection = "SENTINEL-2-L2A", tiles = c("20LKP", "20LLP" ), bands = c("B02", "B8A", "B11"), picture_bands = c("B03", "B04"), cloud_band = "CLOUD", start_date = "2018-06-30", end_date = "2018-08-31", period = "P16D", res = 40L, region = list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), year = list(tile = "20LLP", start_date = "2018-01-01", end_date = "2018-12-31", window = 12L), multicores = 4L, rgb = list(red = "B04", green = "B03", blue = "B02"), stretch = c(0.02, 0.98 ), levels = 32L, quantiles = c(0.001, 0.999), map = list( around_km = 100L, overview = 3L)), "cache/regular_cubes/alone_year_20llp", "/Users/felipecarlos/Documents/sits/sits-embeddings/pipeline/cache/tmp", 0.5)`
35. Stops the run unless the cloud band read here masks each image of the year as sits did. `cube_alone_check_year_cloud(regular_cubes_year_reads, regularize_alone, cubes_record$cloud_values, "cache/regular_cubes/alone_year_20llp")`
36. Gathers the grids, the sources of the book's pixels, the overview, the run with no cloud cover and the year, once the self-checks pass. `cube_checks_export_gather(regular_cubes_grids, regular_cubes_book, regular_cubes_strip, regular_cubes_overlap, regular_cubes_overview, regular_cubes_copied, regular_cubes_year_reads, list(regular_cubes_book_check, regular_cubes_overview_check, regular_cubes_cloud_check))`
37. Writes the grids, the sources of the book's pixels, the overview, the run with no cloud cover and the year, for the website. `site_write_json(cube_checks_export_record(regular_cubes_record, cubes_record, regularize_sources, regular_cubes_picture, list(source = "AWS", collection = "SENTINEL-2-L2A", tiles = c("20LKP", "20LLP"), bands = c("B02", "B8A", "B11"), picture_bands = c("B03", "B04"), cloud_band = "CLOUD", start_date = "2018-06-30", end_date = "2018-08-31", period = "P16D", res = 40L, region = list(crs = "EPSG:32720", xmin = 294880L, xmax = 310240L, ymin = 8840000L, ymax = 8848000L), year = list(tile = "20LLP", start_date = "2018-01-01", end_date = "2018-12-31", window = 12L), multicores = 4L, rgb = list(red = "B04", green = "B03", blue = "B02"), stretch = c(0.02, 0.98), levels = 32L, quantiles = c(0.001, 0.999), map = list(around_km = 100L, overview = 3L)), "B02", list(ranks = "regular-cubes-book-ranks.bin", cards = "regular-cubes-picture-cards-20llp.png", sheets = "regular-cubes-picture-sheets-20llp.png")), "../web/src/data/regular-cubes.json")`

The files:

- `cubes.json`: SHA-256 `f29bb2e9135a6f824c0d512868ea1c3d67bd2532c285b6662f87926e071d5874`. A rerun on aarch64-apple-darwin23 gave the same file, byte for byte.
- `regular-cubes.json`: SHA-256 `751974674c0e71d20c1054ce3e79129531244a10501768116130594eea997323`. A rerun on aarch64-apple-darwin23 gave the same file, byte for byte.
- `cube-year.bin`: SHA-256 `f8300b9b4fad10dbeb3582248d101b35e84c401e847c3cffba2fd167632e2dd2`. A rerun on aarch64-apple-darwin23 gave the same file, byte for byte.

To make them again: `cd pipeline && Rscript make.R`.

## What to try

1. **Walk through the year.** Drag along the strip or press the arrows: the stack opens at each date. The bars of the strip say how much of the region has a value then.
2. **Find a gap.** Pick a date with a short bar and click a cell of the open sheet that is crossed out: its series stops at that date. Open another date, and the cell's outline on the sheet of the gap is dashed.
3. **Read one band.** Press a band to draw the open sheet in grey and the series of that band alone; press *All* to see every band again.
