Plot RGB satellite imagery in true-color with ggplot2 in R

A condensed solution based on published blogs and Q&As

Tobias Stalder
4 min readJun 2, 2022

Recently, I found myself challenged with the task of plotting a basemap out of a satellite image in true colors with ggplot2 in R. Since ggplot2 does not natively support the plotting of raster data, another solution was needed. Upon some research on Stackoverflow and other programming platforms, I found various blogs and Q&As of fellow data analysts dealing with the same issue. In this article, my custom and preferred solution to plot RGB satellite imagery with ggplot2, which was carved out of this research, will be presented. The sources on which the approach is based on are mentioned at the end of the article.

Google Satellite Image of the Swiss Alps and Major Lakes
Satellite Image of the Swiss Alps and Major Lakes | © Google

Preparing the Environment in R

In R, the following libraries need to be installed and loaded.

library(tidyverse) #Loads ggplot2 as well
library(raster) #Raster operations
library(here) #Path management
library(prismatic) #Approximation to hexcode colors in console

Data

The satellite image used in this tutorial is shown above with a resolution of 100x100m tiles in .tif format. The image was derived from the Google satellite imagery service via Tile+ plugin in QGIS for the purpose of this tutorial. The file does not have a designated CRS since the scope of this article is not on how to manage geospatial reference systems regarding raster data.

As a first step, the .tif satellite image is loaded into the R session and transformed to a dataframe. The stack()function from the raster package is used to read the .tif file as a rasterstack. This allows to read all available bands/layers of a the spectral image file at once.

img <- stack(paste0(here(), r"(\images\alps.tif)"))df <- as.data.frame(basemap, xy= TRUE)

The console output of running dfshows that the coordinates and colorbands are now available in a dataframe.

head(df)
x y alps2.1 alps2.2 alps2.3 alps2.4
1 680264.7 5961113 42 67 41 255
2 680364.7 5961113 44 71 46 255
3 680464.7 5961113 45 74 50 255
4 680564.7 5961113 45 74 52 255
5 680664.7 5961113 26 58 37 255
6 680764.7 5961113 7 40 20 255

For the purpose of an easier understanding, the columns can be renamed to their specific meaning. In case that a satellite image is not rectangular, the “empty” data will have the value 0 in all bands after the transformation to a dataframe. If needed, these can be additionally filtered out as outlined below.

df <- df %>% rename(Red = alps2.1,   #Rename bands
Green = alps2.2,
Blue = alps2.3)
df <- df %>% filter(Red != 0) #drop data w/o rgb information

Next up, the color needs to be extracted from the dataframe and checked for its validity. The rgb() function allows for that by specifying the spectral bands (i.e by now, columns). For a graphic outline, the color() function of the prismatic package can be used to show a hexcolor approximation in the console output. This allows to control if the RGB information was correctly passed to the rgb() function.

prismatic::color(rgb(r = Red,              #Specify Bands
g = Green
b = Blue,
maxColorValue = 255)[1:200]) #subsample
Resulting Console Output of Colored Hexcodes Derived from RGB Values

Since the console output shows a color constellation that was expected from the image, the data is ready for plotting in ggplot2.

Plotting the Satellite Image

Before the first plotting attempt, it is advised to create a subset of the data since the dataframecan be huge (depending on the .tif resolution).

df_subset <- df[1:100000,]

The plot structure itself is fairly easy to create. For the visual transformation, geom_rasteris used with x and y inputs and a fill argument inside aes()where rgb() is passed as values.

Further, the fill is scaled with scale_fill_identity()for ggplot to use the provided hexcodes in the fill argument.

ggplot(data = df, aes(x = x, y =y))+                   #plot map
geom_raster(fill = rgb(r = Red,
g = Green,
b = Blue,
maxColorValue = 255)),
show.legend = FALSE) +
scale_fill_identity() +
ggtitle("Plot .tif rgb") +
theme_minimal() -> Map
ggsave(Map, #save map
filename = paste0(here(), "/satellite_img.jpg"),
dpi = 200)
Resulting RGB True Color Image made with ggplot2

Advantage

  • Using satellite images for basemaps directly in ggplot2 w/o additional ggplot2 extension packages or having to switch to another tool.
  • Easy to add additional geospatial vector data on top of the basemap (e.g. by using geom_sf()) for sophisticated maps.

Caveat

  • Depending on the image resolution, the transformation from rasterstack to dataframe is not feasible and plotting times are way higher than with other tools in the R ecosystem.

Acknowledgements

Thank you Christoph von Matt (twitter handle) for our discussions regarding raster data handling in R.

Sources

Florian Klein, Stackoverflow (2014): R — original colours of georeferenced raster image using ggplot2- and raster-packages. Link.

John Shekeine, spatialBit (2017): Colour compositing satellite imagery in R. Link.

The National Science Foundation’s National Ecological Observatory Network (NEON) (2019): Work With Multi-Band Rasters — Image Data in R. Link.

Ratnanil, Stackoverflow (2014): R plot background map from Geotiff with ggplot2. Link.

Thomas, Stackoverflow (2021): Extracting colors from .tif in R. Link.

--

--

Tobias Stalder

Data Visualisation Designer from Bern, Switzerland | behance.net/tobiasstalder | twitter: @toeb18