We are happy to introduce satellite, an R package
designed to facilitate satellite remote sensing analysis in a structured
and user-friendly manner.
The main purpose of satellite is to provide standard
classes and methods to stream-line remote sensing analysis workflow in
R. It provides its own satellite-class along with standard
methods for basic image transformations such as atmospheric and
topgraphic corrections, among others.
The package is desinged with both flexibility and user-friendliness
in mind. Think of it as the sp-package for remote sensing
analysis. It provides core functionality and can be easily extended via
packages to suit your own analysis needs. Furthermore, the fact that
image bands are stored as Raster* objects means, that all
functionality currently available for these classes will also work
nicely with satellite.
In the following, we would like to highlight some of the
functionality provided by satellite.
To start a remote sensing alaysis workflow with
satellite you simply use its workhorse function
satellite() and point it to a folder where your satellite
data is stored.
library(satellite)
path <- system.file("extdata", package = "satellite") 
files <- list.files(path, pattern = glob2rx("LC08*.TIF"), full.names = TRUE) # Landsat 8 example data files
sat <- satellite(files)This will create an object of class satellite with three
slots:
RasterLayers of all available bandsFor supported satellite platforms all of this is done automatically.
At the moment of this writing, supported platforms are Landsat
generations 4 to 8. It is, however, very easy to expand this support to
other platforms by providing suitable look-up-tables (LUT). Even if no
suitable LUT is available, satellite will still import
slots @layers and @log.
As mentioned above, @layers contains a list of
RasterLayers of all available bands. The reason for storing
the individual bands as RasterLayers rather than a
RasterStack is that most satellite platforms provide at
least one layer of different spatial resolution that the rest.
## List of 12
##  $ :Formal class 'RasterLayer' [package "raster"] with 13 slots
##  $ :Formal class 'RasterLayer' [package "raster"] with 13 slots
##  $ :Formal class 'RasterLayer' [package "raster"] with 13 slots
##  $ :Formal class 'RasterLayer' [package "raster"] with 13 slots
##  $ :Formal class 'RasterLayer' [package "raster"] with 13 slots
##  $ :Formal class 'RasterLayer' [package "raster"] with 13 slots
##  $ :Formal class 'RasterLayer' [package "raster"] with 13 slots
##  $ :Formal class 'RasterLayer' [package "raster"] with 13 slots
##  $ :Formal class 'RasterLayer' [package "raster"] with 13 slots
##  $ :Formal class 'RasterLayer' [package "raster"] with 13 slots
##  $ :Formal class 'RasterLayer' [package "raster"] with 13 slots
##  $ :Formal class 'RasterLayer' [package "raster"] with 13 slotsIt is, however, easy to create a RasterStack from
selected layers as stack-method is defined for class
satellite. By default this will take all layers with the
same resolution as the first and stack them. A suitable warning is
provided so that the user is informed which layers were not included in
the RasterStack. Furthermore, one can simply provide a
vector of layer IDs (either layer names or numbers) to be stacked.
## default (all that are similar to layer 1; panchromatic 15-m band 8 is skipped here)
sat_stack <- stack(sat)## Warning in .local(x, ...): 
## layer B008n has different resolution
## not stacking this layer## class      : RasterStack 
## dimensions : 41, 41, 1681, 11  (nrow, ncol, ncell, nlayers)
## resolution : 30, 30  (x, y)
## extent     : 483285, 484515, 5627295, 5628525  (xmin, xmax, ymin, ymax)
## crs        : +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs 
## names      : B001n, B002n, B003n, B004n, B005n, B006n, B007n, B009n, B010n, B011n, B0QAn 
## min values :  9827,  8709,  7647,  6600,  8337,  6697,  6013,  5033, 27494, 24874,  2720 
## max values : 15466, 15069, 14143, 15257, 25759, 18589, 14713,  5113, 31926, 27882,  2720## class      : RasterStack 
## dimensions : 41, 41, 1681, 3  (nrow, ncol, ncell, nlayers)
## resolution : 30, 30  (x, y)
## extent     : 483285, 484515, 5627295, 5628525  (xmin, xmax, ymin, ymax)
## crs        : +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs 
## names      : B001n, B002n, B003n 
## min values :  9827,  8709,  7647 
## max values : 15466, 15069, 14143## class      : RasterStack 
## dimensions : 41, 41, 1681, 5  (nrow, ncol, ncell, nlayers)
## resolution : 30, 30  (x, y)
## extent     : 483285, 484515, 5627295, 5628525  (xmin, xmax, ymin, ymax)
## crs        : +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs 
## names      : B002n, B003n, B004n, B005n, B006n 
## min values :  8709,  7647,  6600,  8337,  6697 
## max values : 15069, 14143, 15257, 25759, 18589The @meta slot provides meta information for each of the
layers of the satellite object. Here’s a non-exhaustive
list of the most important entries:
In addition to these, several calibration coefficients (such as the sun zenith and azimuth angles , sun elevation, earth-sun distance etc.), information on spatial resolution and projection as well as information about file names and paths is also stored.
For the Landsat 8 example data shipped with the package the meta data looks like this:
## 'data.frame':    12 obs. of  42 variables:
##  $ SCENE   : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ BCDE    : Factor w/ 12 levels "B001n","B002n",..: 1 2 3 4 5 6 7 8 9 10 ...
##  $ DATE    : POSIXct, format: "2013-07-07" "2013-07-07" ...
##  $ SID     : Factor w/ 1 level "LC8": 1 1 1 1 1 1 1 1 1 1 ...
##  $ SENSOR  : chr  "Landsat 8" "Landsat 8" "Landsat 8" "Landsat 8" ...
##  $ SGRP    : Factor w/ 1 level "Landsat": 1 1 1 1 1 1 1 1 1 1 ...
##  $ BID     : Factor w/ 12 levels "1","10","11",..: 1 4 5 6 7 8 9 10 11 2 ...
##  $ TYPE    : Factor w/ 6 levels "NIR","PCM","QA",..: 6 6 6 6 1 4 4 2 4 5 ...
##  $ SPECTRUM: Factor w/ 2 levels "solar","thermal": 1 1 1 1 1 1 1 1 1 2 ...
##  $ CALIB   : chr  "SC" "SC" "SC" "SC" ...
##  $ RID.x   : chr  "R00001" "R00001" "R00001" "R00001" ...
##  $ RADA    : num  -60.7 -62.2 -57.3 -48.3 -29.6 ...
##  $ RADM    : num  0.01215 0.01244 0.01146 0.00967 0.00591 ...
##  $ REFA    : num  -0.1 -0.1 -0.1 -0.1 -0.1 -0.1 -0.1 -0.1 -0.1 NA ...
##  $ REFM    : num  2e-05 2e-05 2e-05 2e-05 2e-05 2e-05 2e-05 2e-05 2e-05 NA ...
##  $ BTK1    : num  NA NA NA NA NA ...
##  $ BTK2    : num  NA NA NA NA NA ...
##  $ SZEN    : num  31 31 31 31 31 ...
##  $ SAZM    : num  147 147 147 147 147 ...
##  $ SELV    : num  59 59 59 59 59 ...
##  $ ESD     : num  1.02 1.02 1.02 1.02 1.02 ...
##  $ LMIN    : num  0.43 0.45 0.53 0.64 0.85 1.57 2.11 0.5 1.36 10.6 ...
##  $ LMAX    : num  0.45 0.51 0.59 0.67 0.88 ...
##  $ RADMAX  : num  735 753 694 585 358 ...
##  $ RADMIN  : num  -60.7 -62.2 -57.3 -48.3 -29.6 ...
##  $ REFMAX  : num  1.21 1.21 1.21 1.21 1.21 ...
##  $ REFMIN  : num  -0.1 -0.1 -0.1 -0.1 -0.1 ...
##  $ LNBR    : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ LAYER   : chr  "LC08_L1TP_195025_20130707_20170503_01_T1_B1" "LC08_L1TP_195025_20130707_20170503_01_T1_B2" "LC08_L1TP_195025_20130707_20170503_01_T1_B3" "LC08_L1TP_195025_20130707_20170503_01_T1_B4" ...
##  $ FILE    : chr  "C:/Users/flowd/AppData/Local/Temp/RtmpInj31S/Rinst3ec42da91034/satellite/extdata/LC08_L1TP_195025_20130707_2017"| __truncated__ "C:/Users/flowd/AppData/Local/Temp/RtmpInj31S/Rinst3ec42da91034/satellite/extdata/LC08_L1TP_195025_20130707_2017"| __truncated__ "C:/Users/flowd/AppData/Local/Temp/RtmpInj31S/Rinst3ec42da91034/satellite/extdata/LC08_L1TP_195025_20130707_2017"| __truncated__ "C:/Users/flowd/AppData/Local/Temp/RtmpInj31S/Rinst3ec42da91034/satellite/extdata/LC08_L1TP_195025_20130707_2017"| __truncated__ ...
##  $ METAFILE: chr  "C:/Users/flowd/AppData/Local/Temp/RtmpInj31S/Rinst3ec42da91034/satellite/extdata/LC08_L1TP_195025_20130707_2017"| __truncated__ "C:/Users/flowd/AppData/Local/Temp/RtmpInj31S/Rinst3ec42da91034/satellite/extdata/LC08_L1TP_195025_20130707_2017"| __truncated__ "C:/Users/flowd/AppData/Local/Temp/RtmpInj31S/Rinst3ec42da91034/satellite/extdata/LC08_L1TP_195025_20130707_2017"| __truncated__ "C:/Users/flowd/AppData/Local/Temp/RtmpInj31S/Rinst3ec42da91034/satellite/extdata/LC08_L1TP_195025_20130707_2017"| __truncated__ ...
##  $ RID.y   : chr  "R00001" "R00001" "R00001" "R00001" ...
##  $ XRES    : num  30 30 30 30 30 30 30 15 30 30 ...
##  $ YRES    : num  30 30 30 30 30 30 30 15 30 30 ...
##  $ NROW    : int  41 41 41 41 41 41 41 82 41 41 ...
##  $ NCOL    : int  41 41 41 41 41 41 41 82 41 41 ...
##  $ NCELL   : num  1681 1681 1681 1681 1681 ...
##  $ XMIN    : num  483285 483285 483285 483285 483285 ...
##  $ XMAX    : num  484515 484515 484515 484515 484515 ...
##  $ YMIN    : num  5627295 5627295 5627295 5627295 5627295 ...
##  $ YMAX    : num  5628525 5628525 5628525 5628525 5628525 ...
##  $ PROJ    : chr  "+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs" "+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs" "+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs" "+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs" ...Everytime the user performs some calculation on some or all of the layers, this meta information will be updated accordingly. Here’s an example:
## add digital elevation model to existing 'Satellite' object
dem <- raster(system.file("extdata/DEM.TIF", package = "satellite"))
sat <- addSatDataLayer(sat, data = dem, info = NULL, bcde = "DEM", in_bcde = "DEM")
## perform topographic correction
sat_tc <- calcTopoCorr(sat)
tail(sat_tc@meta[, 1:6])##                  BCDE SCENE       DATE SID    SENSOR    SGRP
## 38 B004n_REF_TopoCorr    NA 2025-08-21 LC8 Landsat 8 Landsat
## 39 B005n_REF_TopoCorr    NA 2025-08-21 LC8 Landsat 8 Landsat
## 40 B006n_REF_TopoCorr    NA 2025-08-21 LC8 Landsat 8 Landsat
## 41 B007n_REF_TopoCorr    NA 2025-08-21 LC8 Landsat 8 Landsat
## 42 B008n_REF_TopoCorr    NA 2025-08-21 LC8 Landsat 8 Landsat
## 43 B009n_REF_TopoCorr    NA 2025-08-21 LC8 Landsat 8 LandsatAs you can see, all bands have been topographically corrected and the
meta data for the resulting layers is automatically appended to the
original data frame. Note for example how $DATE is set to
the date that layers were calculated.
Note, that in order to avoid too long console output, we only show the first and last six columns and rows, respectively, of the meta data here.
Similar to the meta data, log data is also updated every time an analyis is carried out on the object. The default entries (i.e. the ones created on intial import) are as follows:
## $ps0001
## $ps0001$time
## [1] "2025-08-21 07:21:06 CEST"
## 
## $ps0001$info
## [1] "Initial import"
## 
## $ps0001$layers
## [1] "all"
## 
## $ps0001$output
## [1] "all"
## 
## 
## $ps0002
## $ps0002$time
## [1] "2025-08-21 07:21:06 CEST"
## 
## $ps0002$info
## NULL
## 
## $ps0002$in_bcde
## [1] "DEM"
## 
## $ps0002$out_bcde
## [1] "DEM"And here’s how this is modified after the topographic correction:
## List of 2
##  $ ps0001:List of 4
##   ..$ time  : POSIXct[1:1], format: "2025-08-21 07:21:06"
##   ..$ info  : chr "Initial import"
##   ..$ layers: chr "all"
##   ..$ output: chr "all"
##  $ ps0002:List of 4
##   ..$ time    : POSIXct[1:1], format: "2025-08-21 07:21:06"
##   ..$ info    : NULL
##   ..$ in_bcde : chr "DEM"
##   ..$ out_bcde: chr "DEM"Note how, in addition to the info about the initial import, we now have additional logs entries for each band that was topographically corrected clearly showing which call was dispatched, when and on which layer. Even though we only show the first additional log entry here, entries are created for all processed layers. This ensures that we can easily trace what has been done so far and serves as a reference for the current state of processing.