Plot sea surface temperature in R

Create a map with sst information from Copernicus in R.

Miriam Lerma true
2025-04-07

Intro

The Copernicus Marine Service (CMS), also known as the Copernicus Marine Environment Monitoring Service, is the marine component of the European Union’s Copernicus Programme.
It delivers free, regular, and systematic information on the state of the ocean, encompassing the Blue (physical), White (sea ice), and Green (biogeochemical) components, both globally and regionally.
Funded by the European Commission (EC) and implemented by Mercator Ocean International, the service is designed to support EU policies and international legal commitments related to Ocean Governance. It also addresses the global need for ocean knowledge and fosters the Blue Economy across all maritime sectors by providing cutting-edge ocean data and information at no cost.

This post includes:
- Download raster data
- Read and subset raster data
- Plot raster data

Data

Raster data is available at: Copernicus Marine Data Store

To access the products. You need to register to be able to download data

Sea surface temperature (SST) refers to the temperature of the uppermost layer of the ocean, typically measured at the surface of the sea.

For downloading the data: follow this instructions.

To download test data in nc format click here.

Read data

To read the data give the direction of your file.

this_file<-paste0(this_folder,"/cmems_mod_glo_phy_my_0.083deg-climatology_P1M-m_1729785319560.nc")

Load the terra package for reading raster data.

The function rast helps to read raster data - replacing package raster.

SST_file<-rast(this_file)

Subset raster data

Change to data frame for data wrangling.

SST_dataframe <- as.data.frame(SST_file, xy = TRUE)

Load the package tidyverse.

Use filter to subset the data to a specific geographical area.

SST_sub <-SST_dataframe  %>%
  filter(x > 2 & x < 10)%>%
  filter(y > 52 & y < 57)

Obtain mean values per latitude and longitude

There are many columns with data per depth, as data was collected almost every meter.

ncol(SST_sub)
[1] 230

To keep columns with the depth area use the functions select and starts_with.

SST_depth<-SST_sub %>%
  select(starts_with("thetao"))

Check the depths were the data was collected.

Depths<-colnames(SST_depth)
head(Depths)
[1] "thetao_depth=0.49402499_1" "thetao_depth=1.541375_1"  
[3] "thetao_depth=2.645669_1"   "thetao_depth=3.819495_1"  
[5] "thetao_depth=5.0782242_1"  "thetao_depth=6.4406142_1" 

Using functions from tidyverse such as rowwise, summarise the information per depth.

SST_depth_perloc<-SST_depth %>%
  rowwise()%>%
  mutate(mean_SST = mean(c_across(where(is.numeric)),na.rm=TRUE),
         min_SST = min(c_across(where(is.numeric)),na.rm=TRUE),
         max_SST = max(c_across(where(is.numeric))),na.rm=TRUE)%>%
  relocate(mean_SST,min_SST,max_SST)

The function arrange from the package tidyverse allows to see the columns of interest first.

first_column<-SST_depth[1,]
long_values<-first_column %>% 
  pivot_longer(
    cols = 1:228, 
    names_to = "type",
    values_to = "value"
)
arrange_values<-arrange(long_values, desc(value))
type value
thetao_depth=0.49402499_8 15.87216
thetao_depth=1.541375_8 15.84872
thetao_depth=2.645669_8 15.82235
thetao_depth=3.819495_8 15.80038
thetao_depth=5.0782242_8 15.78133
thetao_depth=6.4406142_8 15.76009

Now there is a value per latitude and longitude of the sea surface temperature, summarizing the first 60 m of the water column.

SST_sub$SST<-SST_depth_perloc$mean_SST
mean(SST_sub$SST)
[1] 10.57249

Plot

Load the package ggplot2

Plot your data using geom_raster

ggplot() +
  geom_raster(data = SST_sub , aes(x = x, y = y, fill = SST)) +
  theme_void()+
  theme(legend.position='bottom')+
  coord_sf(xlim = c(3,9), ylim = c(53,56))

To select different palettes explore ggplot2 book. Here are some of the palette options.

To change the color palette use scale_fill_distiller.

ggplot() +
  geom_raster(data = SST_sub , aes(x = x, y = y, fill = SST)) +
  scale_fill_distiller(palette = "YlOrBr")+
  theme_void()+
  theme(legend.position='bottom')+
  xlab('Longitude')+ylab('Latitude')+
  coord_sf(xlim = c(3,9), ylim = c(53,56),
                    label_axes = list(top = "E", left = "N", bottom = 'E', right='N'))

Add land shapefiles to complete the map. To add shapefiles load the package sf.

Use the function st_transform to convert to the same CRS.
For the exercise, use the shapefiles from the package GermanNorthSea

German_land<-st_transform(GermanNorthSea::German_land, 4326)

Add the land to the plot using geom_sf.

Plot_sst<-ggplot() +
  geom_tile(data = SST_sub , aes(x = x, y = y, fill = SST)) +
  scale_fill_distiller(name="Sea surface temperature (°C) \nFrom 0 to 60 m depth\n(April and May montly composites)",
                       palette = "Spectral")+
  geom_sf(data = German_land, colour = 'black', fill = '#d5bdaf')+
  coord_sf(xlim = c(3,9), ylim = c(53,56),
                    label_axes = list(top = "E", left = "N", bottom = 'E', right='N'))+
  theme_void()
Plot_sst

Change a little bit the design using arguments in the theme.

Plot_sst +
  theme_void()+
  xlab('Longitude')+ylab('Latitude')+
  theme(legend.background = element_rect(colour = "transparent", fill = "transparent"),
        legend.position = c(0.40,0.30),
        panel.background = element_rect(fill = '#edede9'),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_rect(colour = "black", fill=NA, size=1.5))+
  NULL

Further reading