Data Visualization & Geospatial Analysis with R
PART 2/2: Large-scale Geospatial Analysis
Taking examples from global satellite data in gridded/ raster format, we will demonstrate several common geospatial operations like projections, resampling, spatial extraction, cropping, masking etc. using rasters, shapefiles, and spatial data frames. For a seamless analysis across different datatypes and platforms, conversion from/to different data formats like data frames, matrices, rasters, and structured data like NetCDF/HDF will be discussed. Advanced topics will include working with data cubes (RasterStack/ RasterBrick), layer-wise operations on data cubes, cell-wise operations on raster time series by implementing user-defined functions with stackApply.
HPRC/TAMIDS Workshop: Data Visualization & Geospatial Analysis with (Part 2 of 2)
Course level: Advanced. Prior experience in R required.
Materials: For this excercise, we will use global dataset from two NASA satellites- MODIS for NDVI and SMAP for surface (~5cm) soil moisture. We will use global aridity estimates (from [Global Aridity and PET Database] (https://cgiarcsi.community/data/global-aridity-and-pet-database/) as a general climate classification (Hyper-arid, arid, semi-arid, sub-humid and humid).
The codes are tested on R version 4.0.3 - Bunny-Wunnies Freak Out. To update, see section S2 of the supplementary material.
Import libraries and sample dataset
We will begin by loading necessary libraries. The sample dataset for this excercise can be downloaded manually from GitHub by accessing https://github.com/Vinit-Sehgal/SampleData
Alternatively, use the following code to download and extract the sample data from GitHub repository.
###############################################################
#~~~ Load required libraries
lib_names=c("raster","ggplot2","unikn",
"gridExtra","rgdal","fields",
"RColorBrewer","ncdf4","rasterVis","rgeos","reshape2","maps")Install all necessary packages (Run once). If you see a prompt: Do you want to restart R prior to installing: Select No.
invisible(suppressMessages
(suppressWarnings
(lapply
(lib_names,install.packages,
repos="http://cran.r-project.org",character.only = T))))
# Load necessary packages
invisible(suppressMessages
(suppressWarnings
(lapply
(lib_names,library, character.only = T))))
###############################################################
#~~~ Import sample data from GitHub repository
download.file(url = "https://github.com/Vinit-Sehgal/SampleData/archive/master.zip",
destfile = "SampleData-master.zip") # Download ".Zip"
# Unzip the downloaded .zip file
unzip(zipfile = "SampleData-master.zip")
getwd() # Working directory for this workshop## [1] "G:/My Drive/TAMU/Teaching/DataVisWorkshop/DataVisWorkshop2020/revision2"
## [1] "CMIP_land" "functions"
## [3] "images" "location_points.xlsx"
## [5] "raster_files" "README.md"
## [7] "sample_pdfs" "SMAPL4_H5"
## [9] "SMOS_nc" "TreeRingData.csv"
## [11] "Workbook_DVGAR-Part1.html" "Workbook_DVGAR-Part2.html"
CHAPTER 1. Raster data visualization
1.1. Plotting and reprojection of rasters
Let’s import global raster of surface (~5 cm) soil moisture from SMAP. We will then reproject the rasters to multiple projections.
Lets us start by importing the raster. We will also generate a custom colormap for better visualization.
library(unikn) #For fancy colormaps
library(raster) # For raster operations
library(RColorBrewer) #Another library for fancy color palettes
# Import data from the downloaded folder
sm=raster("./SampleData-master/raster_files/SMAP_SM.tif") # SMAP soil moisture data
# Make custom color palette
mypal=unikn::seecol(pal = usecol(c("brown","orange","yellow","darkgreen","cyan","darkblue","black"), n = 20)) #User defined color palette using unikn library#Alternatively, use "brewer.pal" for color palettes
mypal=brewer.pal(11,"Spectral") #We can use color palettes from RColorBrewerLets reproject the raster to various projections. In R, the coordinate reference systems or CRS are commonly specified in EPSG (European Petroleum Survey Group) or PROJ4 format (See: https://epsg.io/). We will combine the output plots in a predefined user layout.
# Arrange the plots in user defined grid layout
layout(matrix(c(1,2,3,4,5,6), 2,3, byrow = TRUE)) #Use layout to arrange the plots
#~~ Projection 1: NAD83 (EPSG: 4269)
sm_proj1 = projectRaster(sm, crs = CRS("+init=epsg:4267"));
plot(sm_proj1,
main="NAD83", # Title of the plot
col=mypal, # Colormap for the plot
axes=FALSE, # Disable axes
box=FALSE, # Disable box around the plots
asp = NA, # No fixed aspect ratio
legend=FALSE, # Disable legend
interpolate=TRUE) # Interpolate output raster
#~~ Projection 2: Mercator (EPSG: 3857): Used in Google Maps, Open Street Maps, etc.
sm_proj2 = projectRaster(sm, crs = CRS("+init=epsg:3857"));
plot(sm_proj2, main="Mercator", col=mypal, axes=FALSE,
box=FALSE,asp = NA,legend=FALSE,interpolate=TRUE)
#~~ Projection 3: World Robinson Projection system
test_proj="+proj=robin +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
sm_proj3 = projectRaster(sm, crs = test_proj);
plot(sm_proj3, main="Robinson", col=mypal, axes=FALSE,
box=FALSE,asp = NA,legend=FALSE,interpolate=TRUE)
#~~ Projection 4: Azimuthal equidistant projection centered on Lat=0 and Lon=0
test_proj="+proj=aeqd +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
sm_proj4 = projectRaster(sm, crs = test_proj);
plot(sm_proj4, main="Azimuthal", col=mypal, axes=FALSE,
box=FALSE,asp = NA,legend=FALSE,interpolate=TRUE)
#~~ Projection 5: World Mercator
test_proj="+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
sm_proj5 = projectRaster(sm, crs = test_proj);
plot(sm_proj5, main="World Mercator", col=mypal, axes=FALSE,
box=FALSE,asp = NA,legend=FALSE,interpolate=TRUE)1.2. Plotting raster data using GGPLOT2
GGPLOT2 has several options to make fancier plots in R. We will use geom_tile to display soil moisture raster. As geom_tile requires data in the long format (Lat, Long, Z), we will first convert soil moisture raster to a spatial pixel dataframe. We will use the option scale_fill_gradientn for colormap as it gives the convenience of defining labels, breaks and limits for the plotted data.
library(ggplot2)
#~~~ 1.2.1. Transformation of raster to x-y-z format.
sm_df_smap = as.data.frame(as(sm, "SpatialPixelsDataFrame")) # Spatial dataframe of SM data
colnames(sm_df_smap) = c("SM", "Long", "Lat") # Rename the columns of the dataframe
head(sm_df_smap) # Checkout the spatial dataframe## SM Long Lat
## 1 0.06211190 -92.05394 78.88268
## 2 0.05005269 -89.06639 78.88268
## 3 0.06211190 -92.05394 78.50923
## 4 0.05005269 -89.06639 78.50923
## 5 0.06211190 -92.05394 78.13579
## 6 0.05005269 -89.06639 78.13579
sm_world=ggplot() + # Initialize the plot
geom_tile(data=sm_df_smap, # Add soil moisture data
aes(x=Long, y=Lat, fill=SM))+ # Provide X, Y and Z data
scale_fill_gradientn(colors=mypal, # Use user-defined colormap
name = "SM", # Name of the colorbar
na.value = "transparent", # transparent NA cells
labels=(c("0", "0.2", "0.4", "0.6", "0.8")), # Labels of colorbar
breaks=seq(0,0.8,by=0.2), # Set breaks of colorbar
limits=c(0,0.8))+ # Set limits of colorbar
coord_equal()+ # Aspect ratio of plot
xlab("Longitude")+ # X-axis label
ylab("Latitude")+ # Y-axis label
borders("world", # Add global landmass boundaries
colour="gray43", # Fill light-gray color to the landmass
fill="transparent")+ # Transparent background
borders("state", # Add US state borders
colour="gray43", # Use light-gray color
fill="transparent") # Use transparent background
print(sm_world)We will use a custom function provided with the material to plot the raster in Robinson projection.
#~~~ 1.2.2. Plot a fancier re-projected maps in Robinson projection
source("./SampleData-master/functions/robin_ras.R") # User-defined function for plotting
robin_plot=robin_ras(sm, # Raster to plot
map_col=rev(mypal), # Colormap
lim=c(0,0.8), # Set min and max z-limits
brk=seq(0,0.8,0.2), # Set breaks in colormap
name="Global Surface Soil Moisture from SMAP") # Name of the plot
robin_plot # print the reprojected plotWe will now plot the map for a specific extent (Australia, in this case) by using coord_fixed option. We will also use a different theme: theme_bw.
sm_aus=ggplot() + # Initialize the plot
geom_tile(data=sm_df_smap, # Add soil moisture data
aes(x=Long, y=Lat, fill=SM))+ # Provide X, Y and Z data
scale_fill_gradientn(colors=mypal, # Use user-defined colormap
name = "SM", # Name of the colorbar
na.value = "transparent", # transparent NA cells
labels=(c("0", "0.2", "0.4", "0.6", "0.8")), # Labels of colorbar
breaks=seq(0,0.8,by=0.2), # Set breaks of colorbar
limits=c(0,0.8))+ # Set limits of colorbar
coord_equal()+ # Aspect ratio of plot
xlab("Longitude")+ # X-axis label
ylab("Latitude")+ # Y-axis label
borders("world", # Add global landmass boundaries
colour="gray43", # Fill light-gray color to the landmass
fill="transparent")+ # Transparent background
borders("state", # Add US state borders
colour="gray43", # Use light-gray color
fill="transparent")+ # Use transparent background
coord_fixed(xlim = c(114,153), # Add extent for Australia
ylim = c(-43,-11))+
theme_bw() # Try black-and-white theme.
print(sm_aus)CHAPTER 2. Spatial polygons/shapefiles
2.1. Plotting shapefiles
We will use import the shapefile of the global updated IPCC climate reference regions (https://essd.copernicus.org/preprints/essd-2019-258/) as OpenGIS Simple Features Reference (OGR). We will also download global coastline shapefile from the web for plotting. We will demonstrate subsetting of spatial polygons, conversion to raster and resampling.
Let us first import the shapefile in R and overlay with SMAP SM raster.
###############################################################
#~~~ PART 2.1.1: Importing and visualizing shapefiles
library(sf)
library(raster)
library(rgdal)
library(fields)
# Import the shapefile of global IPCC climate reference regions (only for land)
IPCC_shp = readOGR("./SampleData-master/CMIP_land/CMIP_land.shp",verbose = FALSE)
# View attribute table of the shapefile
head(IPCC_shp@data)## V1 V2 V3 V4
## 0 ARCTIC Greenland/Iceland GIC 1
## 1 NORTH-AMERICA N.E.Canada NEC 2
## 2 NORTH-AMERICA C.North-America CNA 3
## 3 NORTH-AMERICA E.North-America ENA 4
## 4 NORTH-AMERICA N.W.North-America NWN 5
## 5 NORTH-AMERICA W.North-America WNA 6
# Download global coastlines
download.file("http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/physical/ne_10m_coastline.zip",destfile = 'coastlines.zip')
# Unzip the downloaded file
unzip(zipfile = "coastlines.zip",exdir = 'ne-coastlines-10m')
# Load global coastline shapefile
coastlines= readOGR("ne-coastlines-10m/ne_10m_coastline.shp",verbose = FALSE)
# Plot a raster with overlaying shapefiles
plot(sm, col=brewer.pal(10,"Spectral")) # Global SMAP soil moisture
plot(IPCC_shp,add=TRUE) # Add IPCC land regions in blue color
plot(coastlines, add=TRUE) # Add global coastline#Subset shapefile for Eastern North-America
plot(IPCC_shp[4,], main="Polygon for Eastern North-America")2.2. Summarizing rasters using shapefles
Let’s explore using a spatial polygon/shapefile for summarizing a raster (in this case, global SMAP soil moisture) by using extract function from the raster library. We will also transform global aridity raster to a polygon using rasterToPolygons function to find the mean soil moisture values for each aridity class.
First, we will use the IPCC shapefile to summarize the soil moisture raster.
###############################################################
#~~~ PART 2.2.1: Using shapefile to summarize a raster
sm_IPCC_df=raster::extract(sm, # Raster to be summarized
IPCC_shp, # Shapefile/ polygon to summarize the raster
df=TRUE, # Gives the summary statistics as a dataframe
fun=mean, # Desired statistic
na.rm=TRUE)# Ignore NA values? yes!
head(sm_IPCC_df)## ID SMAP_SM
## 1 1 0.2545766
## 2 2 0.3839087
## 3 3 0.2345457
## 4 4 0.3788003
## 5 5 0.2383580
## 6 6 0.1475145
In the next example, we will convert global aridity raster into a polygon and use that to summarize soil moisture for each aridity class.
###############################################################
#~~~ PART 2.2.2: Convert a raster to a shapefile
aridity=raster("./SampleData-master/raster_files/aridity_36km.tif") #Global aridity
plot(aridity,
col=mypal,
interpolate=TRUE,
lab.breaks=c(0:5),
breaks=c(0:5),
main="Global aridity map [1=driest, 5= wettest]") # Convert raster to shapefile
arid_poly=raster::rasterToPolygons(aridity, # Input raster
na.rm=TRUE, # Ignore NA values
digits=12, # No. of digits to round the coordinates
dissolve=TRUE) # FALSE will give boundaries for each cell
plot(arid_poly, col=arid_poly$aridity_36km) # Plot aridity polygonSummarize values of SMAP soil moisture raster for aridity classes:
sm_arid_df=raster::extract(sm, # Raster to be summarized
arid_poly, # Shapefile/ polygon to summarize the raster
df=TRUE, # Gives the summary statistics as a dataframe
fun=mean, # Desired statistic
na.rm=TRUE)# Ignore NA values? yes!
# Lets plot the climate-wise mean of surface soil moisture
plot(sm_arid_df,xaxt = "n",
xlab="Aridity", ylab="Soil moisture",
type="b",
col="blue",
main="Climate-wise mean of surface soil moisture")
axis(1, at=1:5, labels=c("Hyper-arid", "Arid", "Semi-Arid","Sub-humid","Humid"))2.3. Shapefile to raster
Lets checkout how to convert a shapefile to a raster using rasterize function from raster package, and then, to a matrix.
###############################################################
#~~~ PART 2.3.1: Converting shapefile to raster
samp_ras = raster(ncol=1000,nrow=1000) # Create an empty sample raster
extent(samp_ras) = extent(IPCC_shp) # Set raster extent to the shapefile
# Convert polygon to raster
IPCC_ras=raster::rasterize(IPCC_shp, # Input shapefile
samp_ras, # Sample raster
as.numeric(IPCC_shp$V4)) # Rasterize using field ("V4")We will resample the IPCC raster to extent and resolution of the soil moisture raster from SMAP.
###############################################################
#~~~ PART 2.3.2: Resampling for common extent and resolution
IPCC_ras_resamp=resample(IPCC_ras,sm,method='bilinear') # Resample using bilinear or ngb (nearest neighbor) interpolation
print(IPCC_ras_resamp) #Notice resolution, extent and coordiate. ref.## class : RasterLayer
## dimensions : 456, 964, 439584 (nrow, ncol, ncell)
## resolution : 0.373444, 0.373444 (x, y)
## extent : -180, 180, -85.24595, 85.0445 (xmin, xmax, ymin, ymax)
## crs : +proj=longlat +datum=WGS84 +no_defs
## source : memory
## names : layer
## values : 1, 41 (min, max)
Conversion of a raster to a matrix is easy too. Just use: as.matrix function. We can plot a 2-D matrix as a map using fields::image.plot
# Convert resampled raster to matrix
sp_mat=as.matrix(IPCC_ras_resamp)
# Notice the transpose and inversion used in sp_mat
fields::image.plot(t(sp_mat[nrow(sp_mat):1,]),main="IPCC land regions") CHAPTER 3. Data-cubes: RasterStack/RasterBrick
Whats is a RasterStack/ RasterBrick?
raster) is a collection of RasterLayer objects with the same spatial extent and resolution.
Raster brick exist entirely in memory takes longer time to create a raster brick compared to Raster Stack. However, various raster operations are significantly faster for RasterBrick over RasterStacks.
3.1. Create, subset and visualize RasterStack/ RasterBrick
We will create multilayer raster stacks/ bricks for NDVI and SMAP SM from the sample dataset and demonstrate several applications and operations. Working with Raster stack/brick are very similar to working with regular arrays or lists.
###############################################################
#~~~ PART 3.1.1: Create and plot NDVI RasterBrick/ Stack
library(raster)
library(fields)
library(unikn)
# Location of the NDVI raster files
ndvi_path="./SampleData-master/raster_files/NDVI/" #Specify location of NDVI rasters
# List of all NDVI rasters
ras_list=list.files(ndvi_path,pattern='*.tif',full.names=TRUE)
head(ras_list)## [1] "./SampleData-master/raster_files/NDVI/NDVI_resamp_2016-01-01.tif"
## [2] "./SampleData-master/raster_files/NDVI/NDVI_resamp_2016-01-17.tif"
## [3] "./SampleData-master/raster_files/NDVI/NDVI_resamp_2016-02-02.tif"
## [4] "./SampleData-master/raster_files/NDVI/NDVI_resamp_2016-02-18.tif"
## [5] "./SampleData-master/raster_files/NDVI/NDVI_resamp_2016-03-05.tif"
## [6] "./SampleData-master/raster_files/NDVI/NDVI_resamp_2016-03-21.tif"
Let’s import NDVI rasters from the location and store as a RasterBrick.
# Use lapply to create raster layers from the raster location
ras_brick=brick(lapply(paste(ras_list, sep=""), raster))
dim(ras_brick) #23 raster layers with 456x964 cells## [1] 456 964 23
# Subsetting raster stack/brick (notice the double [[]] bracket and similarity to lists)
sub_ras_brick=ras_brick[[c(1,3,5,10,12)]] #Select 1st, 3rd, 5th, 10th and 12th layers
#Plot first 4 elements of NDVI raster brick
plot(sub_ras_brick[[1:4]], # Select raster layers to plot
col=brewer.pal(10,"Spectral"),# Specify colormap
interpolate=TRUE, # Interpolate the output map
asp=NA) # Aspect ratio= NAThis time, let’s use levelplot from rasterVis library to plot RasterBrick layers.
mapTheme = rasterTheme(region = brewer.pal(10,"Spectral")) # Set raster theme (color etc.)
my.at=seq(-1,1,0.1) # Set colorkey/ colorbar sequence
myColorkey = list(at=my.at, space="right") # Set colorkey/colorbar to the right
plt= rasterVis::levelplot(sub_ras_brick[[1:4]], # Select first 4 raster layers to plot
margin = F,
pretty=TRUE,
layout=c(2,2), # Layout of the plots
row.values=2, # Number of rows
par.settings = mapTheme, # Add user define theme
colorkey=myColorkey, # Add user defined
main="NDVI")
plt=plt + layer(sp.lines(coastlines, col="black", lwd=0.1)) # Add coastline to the plots
print(plt)3.2. Numerical operations on RasterStack/ RasterBrick
We will demonstrate calculation of cell-wise/ layer-wise summary statistics and data transformation on RasterStacks/RasterBricks. Notice the similarities to simple vector operations. We will use cellStats, calc and stackApply to apply several default and custom functions to summarize RasterStacks/RasterBricks.
Let’s try some layer-wise cell-statistics:
###############################################################
#~~~ PART 3.2.1: Layer-wise cell-statistics
# Layer-wise Mean
cellStats(sub_ras_brick, mean) # Mean of each raster layer. Try modal, median, min etc. ## NDVI_resamp_2016.01.01 NDVI_resamp_2016.02.02 NDVI_resamp_2016.03.05
## 0.2840846 0.2948298 0.3057182
## NDVI_resamp_2016.05.24 NDVI_resamp_2016.06.25
## 0.4618550 0.4997354
## 25% 75%
## NDVI_resamp_2016.01.01 0.07200430 0.5136818
## NDVI_resamp_2016.02.02 0.09046099 0.5258916
## NDVI_resamp_2016.03.05 0.10357170 0.5185570
## NDVI_resamp_2016.05.24 0.23308019 0.6722358
## NDVI_resamp_2016.06.25 0.25382688 0.7282458
# User-defined statistics by defining own function
quant_fun = function(x, na.rm=TRUE) # Remember to add "na.rm" option
{quantile(x, probs = c(0.25, 0.75), na.rm=TRUE)}
cellStats(sub_ras_brick, quant_fun) # 25th, and 75th percentile of each layer## NDVI_resamp_2016.01.01 NDVI_resamp_2016.02.02 NDVI_resamp_2016.03.05
## 25% 0.07188153 0.0844293 0.09917563
## 75% 0.51358944 0.5177374 0.51130731
## NDVI_resamp_2016.05.24 NDVI_resamp_2016.06.25
## 25% 0.2367169 0.2600064
## 75% 0.6695234 0.7303487
Global cell-statistics can be calculated by converting raster stack to a vector and then using regular statistics functions.
###############################################################
#~~~ PART 3.2.2: Global cell-statistics
min_val=min(raster::minValue(sub_ras_brick))
max_val=max(raster::maxValue(sub_ras_brick))
print(c(min_val,max_val))## [1] -0.20000 0.95065
# Or by vectorizing the RasterBrick and finding statistics
min_val=min(as.vector(sub_ras_brick), na.rm=T)
max_val=max(as.vector(sub_ras_brick), na.rm=T)
print(c(min_val,max_val))## [1] -0.20000 0.95065
# Another example of statistics of vectorized RasterBrick
quant=quantile(as.vector(sub_ras_brick), c(0.01,0.99), na.rm=TRUE) #1st and 99th percentiles
print(quant)## 1% 99%
## -0.05663428 0.84776928
We will try some layer-wise operations on RasterStack/RasterBrick.
###############################################################
#~~~ PART 3.2.3: Layer-wise operations on RasterStack/RasterBrick
# Arithmetic operations on RasterBrick/ RatserStacks are same as lists
add=sub_ras_brick+10 # Add a number to raster layers
mult=sub_ras_brick*5 # Multiply a number to raster layers
subset_mult=sub_ras_brick[[1:3]]*10 # Multiply a number to a subset of raster layers
# Data filtering based on cell-value
filter_brick=sub_ras_brick # Create a RasterBrick to operate on
filter_brick[filter_brick<0.5]=NA # Assign NA to any value less than 0.5
# Let's plot the filtered rasters
plot(filter_brick,
asp=NA, # Aspect ratio. NA= No white space
colNA="gray95", # Background color
nr=2) # Number of rows to arrange the plots# Layer-wise Log-transformation
log_ras_brick=log(sub_ras_brick)
# Normalize raster layers to [0,1] based on min and max of raster brick/stack
norm_brk=(sub_ras_brick-min_val)/(max_val-min_val) # Notice that the values are between [0,1]
# Plot in Robinson projection
source("./SampleData-master/functions/robin_ras.R") # User defined function
ndvi_robin_plot=robin_ras(norm_brk[[1]], # Select first raster
map_col=rev(mypal), # Colormap
lim=c(0,1), # Set min and max z-limits
brk=c(0, 0.2, 0.4, 0.6, 0.8, 1),# Set breaks in colormap
name="Normalized NDVI") # Name of the plot
ndvi_robin_plot # print the reprojected plotWe will now carry-out cell-wise operations on raster stack/brick. These functions carry operations on each cell of a RasterStack/RasterBrick for all layers.
###############################################################
#~~~ PART 3.2.4: Cell-wise operations on raster stack/brick
# Mean of all cells accross layers
mean_ras=mean(sub_ras_brick, na.rm=TRUE) #Try modal, median, min etc.
# Approximate NA values by interpolating between Layers
approx_brick=approxNA(sub_ras_brick,method="linear") #Try "constant"
# Using calc function for user defined operations
quant_fun = function(x, na.rm=TRUE) # Remember to add "na.rm" option
{quantile(x, probs = c(0.25), na.rm=TRUE)}
# Apply the function "quant_fun" on rasterBrick using "calc" and "stackApply"
quant_ras=calc(sub_ras_brick,
fun = quant_fun)
quant_ras=raster::stackApply(sub_ras_brick,
fun=quant_fun,
indices=c(1,1,1,1,1))
# Plot output raster
plot(quant_ras, main="25th percentile of time series for each cell")We can apply raster::stackApply function on subsets of original RasterStach/ RasterBrick by using the option indices. The output is saved as a list, with layers for each index class.
# "Indices" specifies the group/subset of layers on which to apply the "fun"
quant_ras = raster::stackApply(sub_ras_brick,
fun=quant_fun,
indices=c(1,1,2,2,2))
names(quant_ras) = c("Output of rasters 1 to 2", "Output of rasters 3 to 5")
# Two layers are formed, one for each group of indices
# Lets plot the two output rasters
plot(quant_ras[[1:2]], # Layers to plot
asp=NA, # Aspect ratio. NA= No white space
colNA="gray95", # Background color
nr=2) # Number of rows to arrange the plots3.3. Geospatial operations on RasterStack/ RasterBrick
In this section, we will demonstrate application of several operations on RasterStack/RasterBrick for e.g. extracting time series at user-defined locations, spatial data extraction using spatial polygons/raster, crop and masking of raster brick/stack.
3.3.1. Time-series extraction from raster brick/stack
We will demonstrate three ways of extracting time series information from raster stack/brick.
Method 1: By converting raster brick to a spatial data frame using as.data.frame or raster::rasterToPoints functions
###############################################################
#~~~~ Method 1.1: Convert raster brick to spatial data frame
spdf = as(ras_brick, "SpatialPixelsDataFrame")
df_spdf = as.data.frame(spdf) # "x" and "y" in the df are long and lat
df_spdf[1:3,1:6] #Notice values of all raster layers are extracted for each cell## NDVI_resamp_2016.01.01 NDVI_resamp_2016.01.17 NDVI_resamp_2016.02.02
## 1 NA -0.09864961 -0.06309947
## 2 NA -0.11487599 -0.07173216
## 3 NA -0.09752892 -0.07198682
## NDVI_resamp_2016.02.18 NDVI_resamp_2016.03.05 NDVI_resamp_2016.03.21
## 1 -0.03692058 -0.03954601 -0.03983472
## 2 -0.04443348 -0.04195234 -0.03798087
## 3 -0.04526810 -0.04376680 -0.03974375
###############################################################
#~~~~ Method 1.2: Use rasterToPoints function
r.pts = rasterToPoints(ras_brick, spatial=TRUE)
df_spdf = data.frame(long=coordinates(r.pts)[,1],
lat=coordinates(r.pts)[,2],
r.pts@data)
df_spdf[1:3,1:6] #Notice values of all raster layers are extracted for each cell## long lat NDVI_resamp_2016.01.01 NDVI_resamp_2016.01.17
## 1 -167.4896 66.18558 NA -0.09864961
## 2 -167.1162 66.18558 NA -0.11487599
## 3 -166.7427 66.18558 NA -0.09752892
## NDVI_resamp_2016.02.02 NDVI_resamp_2016.02.18
## 1 -0.06309947 -0.03692058
## 2 -0.07173216 -0.04443348
## 3 -0.07198682 -0.04526810
Method 2: Using raster::extract function to extract time series data for specific location(s)
###############################################################
#~~~~ Method 2.1: Extract values for a single location
# User defined lat and long
Long=-96.33; Lat=30.62
# Extract time series for the location
ndvi_val=raster::extract(ras_brick,
SpatialPoints(cbind(Long,Lat)), # lat-long as spatial locations
method='bilinear') # or "simple" for nearest neighbor
# Create a dataframe using the dates (derived from raster layer names) and extracted values
ndvi_ts=data.frame(Date=as.Date(substr(colnames(ndvi_val),13,22), format = "%Y.%m.%d"), NDVI=as.vector(ndvi_val))
# Plot NDVI time series extracted from raster brick/stack
plot(ndvi_ts, type="l", col="maroon", ylim=c(0.2,0.8))Let us use Method 2 (described above) for multiple locations.
#~~~~ Method 2.2: Extract values for multiple locations
# Import sample locations from contrasting hydroclimate
library(readxl)
loc= read_excel("./SampleData-master/location_points.xlsx")
print(loc)## # A tibble: 3 x 4
## Aridity State Longitude Latitude
## <chr> <chr> <dbl> <dbl>
## 1 Humid Louisiana -92.7 34.3
## 2 Arid Nevada -116. 38.7
## 3 Semi-arid Kansas -99.8 38.8
# Extract time series using "raster::exract"
loc_ndvi=raster::extract(ras_brick,
#Convert lat -long to spatial locations
SpatialPoints(cbind(loc$Longitude,loc$Latitude)),
# Use bilinear interpolation (or simple) option
method='bilinear')Let’s plot the extracted NDVI for three locations to look for patterns (influence on climate on vegetation).
# Create a new data frame with dates of retrieval and NDVI for different hydroclimates
ndvi_locs = data.frame(Date=as.Date(substr(colnames(ndvi_val),13,22),format = "%Y.%m.%d"),
Humid = as.vector(loc_ndvi[1,]), #Location 1
Arid = as.vector(loc_ndvi[2,]), #Location 2
SemiArid = as.vector(loc_ndvi[3,])) #Location 3
# Convert data frame in "long" format for plotting using ggplot
library(tidyr)
df_long=ndvi_locs %>% gather(Climate,NDVI,-Date)
#"-" sign indicates decreasing order
head(df_long,n=4)## Date Climate NDVI
## 1 2016-01-01 Humid 0.5919486
## 2 2016-01-17 Humid 0.5762772
## 3 2016-02-02 Humid 0.5570874
## 4 2016-02-18 Humid 0.5752835
# Plot NDVI for different locations (hydroclimates). Do we see any pattern??
library(ggplot2)
l = ggplot(df_long,
aes(x=Date,
y=NDVI,
group=Climate)) +
geom_line(aes(color=Climate))+
geom_point(aes(color=Climate))
print(l)Method 3: Use rowFromY or rowFromX to find row and column number of the raster for the location.
###############################################################
#~~~~ Method 3: Retrieve values using cell row and column number
row = rowFromY(ras_brick[[1]], Lat)
col = colFromX(ras_brick[[1]], Long)
ras_brick[row,col][1:5]## [1] 0.5614925 0.5193725 0.4958409 0.5731566 0.6259534
3.3.2. Spatial extraction/summary from raster brick/stack
Let’s try extracting values of RasterBrick (for all layers simultaneously) based on polygon features using extract function.
#~~~~ Method 1: Extract values based on spatial polygons
IPCC_shp = rgdal::readOGR("./SampleData-master/CMIP_land/CMIP_land.shp",verbose = FALSE)
ndvi_sp=raster::extract(ras_brick,IPCC_shp, fun=mean, na.rm=T, df=TRUE, method='bilinear')
head(ndvi_sp,n=3)[1:5]## ID NDVI_resamp_2016.01.01 NDVI_resamp_2016.01.17 NDVI_resamp_2016.02.02
## 1 1 -0.08325353 -0.05983509 -0.04461097
## 2 2 0.02765715 0.02737234 0.03172858
## 3 3 0.21922899 0.22114161 0.21826435
## NDVI_resamp_2016.02.18
## 1 -0.03795020
## 2 0.03084664
## 3 0.27331317
We can also use a classified raster like aridity data we previously used, to extract values corresponding to each class of values using lapply and selective filtering. The two rasters must have same extent and resolution.
#~~~~ Method 2: Extract values based on another raster
aridity=raster("./SampleData-master/raster_files/aridity_36km.tif")
climate_ndvi=list()
# Extracts the time series of NDVI for pixels for each climate region
climate_ndvi=lapply(list(1,2,3,4,5),function(x) (na.omit((ras_brick[aridity==x]))))
# Calculate and store mean NDVI for each climate region
climate_ndvi_mean=lapply(list(1,2,3,4,5),function(x) (mean(ras_brick[aridity==x], na.rm=TRUE)))
plot(unlist(climate_ndvi_mean),type="b")3.3.3. Crop and mask RasterBrick/RasterStack
We will now crop and mask raster brick using shapefile from the disk or from imported shapefile from raster::getData.
#~~~~ Crop and mask raster based on shapefile
out_crop = crop(ras_brick, extent(region))
# Extract values matching the spatial boundary
out_crop_mask = raster::mask(out_crop,region)
plot(out_crop_mask[[1:4]],col=mypal)# b) Download geographical boundaries online
# Download polygons for USA at level 1 i.e. state from GADM dataset
state_shapefile = raster::getData("GADM",country="USA",level=1)
# Exclude Alaska and Hawaii to get CONUS shapefile
conus=state_shapefile[!(state_shapefile$NAME_1 %in% c("Alaska","Hawaii")),]
plot(conus)# Crop raster using dowloaded polygon
usa_trim = crop(ras_brick, extent(conus)) # Crop
ndvi_mask_usa = raster::mask(usa_trim,conus) # Mask
plot(ndvi_mask_usa[[1:4]],col=mypal)Let’s crop raster for selected states.
# Mask raster for selected states
states = c('Colorado','Texas','New Mexico')
# Subset shapefile for selected attributes
state_plot = state_shapefile[(state_shapefile$NAME_1 %in% states),]
states_trim = crop(ras_brick, extent(state_plot)) # Crop raster
ndvi_mask_states = raster::mask(states_trim,state_plot) # Mask
# Plot raster and shapefile
plot(ndvi_mask_states[[1]],col=mypal);
plot(state_plot, add=TRUE)CHAPTER 4. Moving beyond rasters: Using NetCDF
In this chapter, we will work with NetCDF (network Common Data Form) files in R. We will use a sample .nc file from SMOS-IC platform which provides daily global soil moisture and related variables based on ESA’s Soil Moisture Ocean Salinity (SMOS) satellite and NASA's SMAP Level-4 GEOS-5 (hydrology) model output.
4.1. Working with NetCDF Data
We will start by importing a .nc file for SMOS-IC global soil moisture and viewing its attributes.
###############################################################
#~~~~ "NetCDF" file
library(ncdf4)
file_name="./SampleData-master/SMOS_nc/SM_RE06_MIR_CDF3SA_20181225.nc"
nc= nc_open(file_name)
print(nc) # Notice the structure and attributes of the file## File ./SampleData-master/SMOS_nc/SM_RE06_MIR_CDF3SA_20181225.nc (NC_FORMAT_64BIT):
##
## 10 variables (excluding dimension variables):
## int Days[lon,lat]
## _FillValue: -2147483647
## long_name: Number of Days since 1/1/2000
## units:
## byte Processing_Flags[lon,lat]
## _FillValue: -128
## long_name: Processing Flags
## units:
## byte Quality_Flag[lon,lat]
## _FillValue: -128
## long_name: 0: data OK, 1: data not recommended, 2: missing data
## float RMSE[lon,lat]
## _FillValue: -2147483648
## long_name: RMSE TBmeas. TB modeled
## units:
## byte Scene_Flags[lon,lat]
## _FillValue: -128
## long_name: Scene Flags
## units:
## float Soil_Moisture[lon,lat]
## _FillValue: -2147483648
## long_name: Soil Moisture
## units: m3/m3
## float Soil_Moisture_StdError[lon,lat]
## _FillValue: -2147483648
## long_name: Soil Moisture standard error
## units: m3/m3
## float Soil_Temperature_Level1[lon,lat]
## _FillValue: -2147483648
## long_name: ECMWF Soil Temperature at surface level 1
## units: Kelvin
## int UTC_Microseconds[lon,lat]
## _FillValue: -2147483647
## long_name: Microseconds
## units: micros.
## int UTC_Seconds[lon,lat]
## _FillValue: -2147483647
## long_name: Number of Seconds
## units: s
##
## 2 dimensions:
## lon Size:1388
## _FillValue: -2147483648
## long_name: Longitude
## units: Degree
## lat Size:584
## _FillValue: -2147483648
## long_name: Latitude
## units: degree
##
## 19 global attributes:
## creation_time: 24-Mar-2019 12:36:34
## institution: INRA, CESBIO
## references: Fernandez-Moran, R., Al-Yaari, A. , Mialon, A. , Mahmoodi, A , Al Bitar, A. , De Lannoy, G., Lopez-Baeza, E., Kerr, Y., Wigneron, J.-P., 2017.SMOS-IC: An alternative SMOS soil moisture and vegetation optical depth product submitted Remote Sensing, March 2017
## title: SMOS IC
## contact: jean-pierre.wigneron@inra.fr
## ease_origin_lat: -83.51714
## ease_origin_lon: -179.8703
## ease_resolution: 25
## ease_projection: cylindrical
## ease_global: yes
## Conventions: 1.4
## product_version: 1.0
## netcdf_version_id: 3.6.2
## grip_mapping: projection
## datum: +ellps = WGS84
## srid: EPSG:6933
## proj4text: +proj=cea +lon_0=0 +lat_ts=30 +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs
## history: Wed Mar 27 12:22:10 2019: ncks -C -O -x -v Optical_Thickness_Nad_StdError,Optical_Thickness_Nad SM_RE06_MIR_CDF3SA_20181225T000000_20181225T235959_105_001_8.DBL SM_RE06_MIR_CDF3SA_20181225T000000_20181225T235959_105_001_8.DBL
## NCO: 4.0.8
Based on the structure of the NetCDF file, let’s import four attributes i) Soil moisture ii) Quality flag iii) Cell Latitude iv) Cell longitude.
# Extract variables from NC file
sm=ncvar_get(nc,"Soil_Moisture") # Soil moisture variable
Latitude=ncvar_get(nc, 'lat') # Latitude
Longitude=ncvar_get(nc, 'lon') # Longitude
flag=ncvar_get(nc,"Quality_Flag") # 0: data OK, 1: data not recommended
nc_close(nc) #Close .nc file. Imp if multiple files are opened recursivelyWe will now filter the flagged data from the input and convert the imported soil moisture matrix to a raster using a user-defined function mat2ras provided with the workshop material.
# Filter dataset
sm[!(flag==0)]=NA # Filter-out flagged data.
sm[sm<0]=NA # Negative values are floored at 0
# Convert the matrix to raster using lat-long
source("./SampleData-master/functions/mat2ras.R")
library(reshape2)
sm_ras=mat2ras(sm, Longitude,Latitude, FALSE)
plot(sm_ras, main="Swath of global SM retrieval (2018-12-25)")
plot(coastlines,add=TRUE)4.2. Working with HDF/.H5 files
Hierarchical Data Format (HDF) is another popular format for gridded data/ model output. .H5 files can be accessed in R as .nc files using ncdf4::nc_open.
###############################################################
#~~~~ ".H5" files
#list all SMAP-L4 .H5 files
smapl4_files=list.files("./SampleData-master/SMAPL4_H5", full.names = TRUE)
# Open the first file as an example
nc= nc_open(smapl4_files[1])
print(nc) # Notice the variables and the structure of the file## File ./SampleData-master/SMAPL4_H5/SMAP_L4_SM_gph_20190222T133000_Vv4030_001_HEGOUT.h5 (NC_FORMAT_NETCDF4):
##
## 6 variables (excluding dimension variables):
## float cell_lat[x,y] (Chunking: [3854,1]) (Compression: level 3)
## DIMENSION_LABELS: y
## DIMENSION_LABELS: x
## fmissing_value: -9999
## missing_value: -9999
## valid_max: 90
## units: degrees
## grid_mapping: projection_information
## valid_min: -90
## long_name: The geodetic latitude of the center of each cell in the cylindrical 9 km Earth-fixed EASE-Grid 2.0. Zero latitude represents the Equator. Positive latitudes represent locations North of the Equator. Negative latitudes represent locations South of the Equator.
## _FillValue: -9999
## float cell_lon[x,y] (Chunking: [3854,1]) (Compression: level 3)
## DIMENSION_LABELS: y
## DIMENSION_LABELS: x
## fmissing_value: -9999
## missing_value: -9999
## valid_max: 179.998992919922
## units: degrees
## grid_mapping: projection_information
## valid_min: -180
## long_name: The longitude of the center of each cell in the cylindrical 9 km Earth-fixed EASE-Grid 2.0. Zero longitude represents the Prime Meridian. Positive longitudes represent locations to the East of the Prime Meridian. Negative longitudes represent locations to the West of the Prime Meridian.
## _FillValue: -9999
## char projection_information[] (Contiguous storage)
## earth_inverse_flattening: 298.257223563
## standard_parallel: 30
## Datum: WGS84
## false_easting: 0
## false_northing: 0
## earth_semi_major_axis: 6378.137
## earth_semi_minor_axis: 6356.752314245
## grid_mapping_name: lambert_cylindrical_equal_area
## longitude_of_central_meridian: 0
## double time[phony_dim_2] (Contiguous storage)
## long_name: Time
## units: seconds since 2000-01-01 11:58:55.816
## actual_range: 604114200
## actual_range: 604114200
## delta_t: 0000-00-00 03:00:00
## float Geophysical_Data/sm_rootzone[x,y] (Chunking: [3854,1]) (Compression: level 3)
## DIMENSION_LABELS: y
## DIMENSION_LABELS: x
## _FillValue: -9999
## fmissing_value: -9999
## missing_value: -9999
## valid_max: 0.899999976158142
## units: m3 m-3
## grid_mapping: projection_information
## coordinates: /cell_lat /cell_lon
## valid_min: 0
## long_name: Root zone soil moisture (0-100 cm)
## float Geophysical_Data/sm_surface[x,y] (Chunking: [3854,1]) (Compression: level 3)
## long_name: Top layer soil moisture (0-5 cm)
## valid_max: 0.899999976158142
## _FillValue: -9999
## fmissing_value: -9999
## pixel_size: 9.00805473327637
## missing_value: -9999
## DIMENSION_LABELS: y
## DIMENSION_LABELS: x
## units: m3 m-3
## grid_mapping: projection_information
## coordinates: /cell_lat /cell_lon
## valid_min: 0
##
## 3 dimensions:
## x Size:3856
## axis: X
## long_name: X coordinate of cell center of output grid
## standard_name: projection_x_coordinate
## units: m
## y Size:1624
## axis: Y
## units: m
## long_name: Y coordinate of cell center of output grid
## standard_name: projection_y_coordinate
## phony_dim_2 Size:1
## [1] "vobjtovarid4: **** WARNING **** I was asked to get a varid for dimension named phony_dim_2 BUT this dimension HAS NO DIMVAR! Code will probably fail at this point"
##
## 9 global attributes:
## Source: reichle-LDASsa_m3-16_6_p1
## Institution: NASA Global Modeling and Assimilation Office
## History: File written by ldas2daac.x
## Comment: HDF-5
## Filename: /discover/nobackup/projects/gmao/smap/SMAP_L4/L4_SM/Vv4030/gph/Y2019/M02/D22/SMAP_L4_SM_gph_20190222T133000_Vv4030_001.h5
## Title: SMAP L4_SM Geophysical (GPH) Data Granule
## Conventions: CF
## References: see SMAP L4_SM Product Specification Documentation
## Contact: http://gmao.gsfc.nasa.gov
# Extract fields/ variables
sm=ncvar_get(nc,"Geophysical_Data/sm_surface") # Surface soil moisture
Latitude=ncvar_get(nc, 'cell_lat') # Latitude
Longitude=ncvar_get(nc, 'cell_lon') # Longitude
nc_close(nc) # Close connection to .nc file
print(Longitude[1:5,1:5]) #Notice how the values are repeated in each row## [,1] [,2] [,3] [,4] [,5]
## [1,] -179.9533 -179.9533 -179.9533 -179.9533 -179.9533
## [2,] -179.8600 -179.8600 -179.8600 -179.8600 -179.8600
## [3,] -179.7666 -179.7666 -179.7666 -179.7666 -179.7666
## [4,] -179.6732 -179.6732 -179.6732 -179.6732 -179.6732
## [5,] -179.5799 -179.5799 -179.5799 -179.5799 -179.5799
## [,1] [,2] [,3] [,4] [,5]
## [1,] 84.65644 83.95423 83.32523 82.75036 82.21761
## [2,] 84.65644 83.95423 83.32523 82.75036 82.21761
## [3,] 84.65644 83.95423 83.32523 82.75036 82.21761
## [4,] 84.65644 83.95423 83.32523 82.75036 82.21761
## [5,] 84.65644 83.95423 83.32523 82.75036 82.21761
# Subset the Lat-long fields to retrieve unique values
Latitude=Latitude[1,]
Longitude=Longitude[,1]
# Conver data to raster using mat2ras.R function
source("./SampleData-master/functions/mat2ras.R")
library(raster)
library(reshape2)
library(RColorBrewer)
sm_ras=mat2ras(sm, Longitude,Latitude, FALSE)
plot(sm_ras, main="SMAP-L4 Surface SM retrieval", col=brewer.pal(11, "Spectral"))
plot(coastlines,add=TRUE)4.3. Working with multiple NetCDF/HDF files
The process we tried on the sample .H5 file can be looped over multiple files. As an example, we will work on three .H5 listed below using a for loop.
source("./SampleData-master/functions/mat2ras.R")
library(reshape2) # Required for mat2ras.R
#list all SMAP-L4 .H5 files in the folder
smapl4_files=list.files("./SampleData-master/SMAPL4_H5", full.names = TRUE)
# List of ".H5" files
print(smapl4_files) ## [1] "./SampleData-master/SMAPL4_H5/SMAP_L4_SM_gph_20190222T133000_Vv4030_001_HEGOUT.h5"
## [2] "./SampleData-master/SMAPL4_H5/SMAP_L4_SM_gph_20190222T163000_Vv4030_001_HEGOUT.h5"
## [3] "./SampleData-master/SMAPL4_H5/SMAP_L4_SM_gph_20190222T193000_Vv4030_001_HEGOUT.h5"
# Create and empty brick to save rasters
collate_ras=brick()
# Create an empty list to store SM matrix
collate_mat=list()
for (file_num in 1:length(smapl4_files)){
#print(file_num) #Print loop count
# Extract data fields
nc= nc_open(smapl4_files[file_num])
# Extract fields/ variables
sm=ncvar_get(nc,"Geophysical_Data/sm_surface") # Soil moisture
Latitude=ncvar_get(nc, 'cell_lat') # Latitude
Longitude=ncvar_get(nc, 'cell_lon') # Longitude
nc_close(nc) # Close connection to .nc file
# Subset the Lat-long fields to retrieve unique values
Latitude=Latitude[1,]
Longitude=Longitude[,1]
# Convert soil moisture data matrix to raster
sm_ras=mat2ras(sm, Longitude,Latitude, # Inputs
plot_ind=FALSE) # Disable output plot
# Store layer data to RaterBrick or list
collate_mat[[file_num]]=sm
collate_ras=addLayer(collate_ras,sm_ras)
}Now let’s plot a sample raster and data matrix from the newly created RasterBrick and data list.
# Plot using RasterBrick
library(RColorBrewer)
collate_ras[collate_ras<0]=NA #Flooring negative values to zero
plot(collate_ras,
col=brewer.pal(11, "Spectral"))# Plot first element of list (2D matrix)
library(fields)
collate_mat[[1]][collate_mat[[1]]<0]=NA #Flooring negative values to zero
image.plot(Longitude,
rev(Latitude),
collate_mat[[1]][,ncol(collate_mat[[1]]):1],
col=brewer.pal(11, "Spectral"), xlab="Longitude",ylab="Latitude")Supplementary material
S1. Working with H5 data using rhdf5
One useful package for working with .H5 files is rhdf5. It is not available on CRAN, but can be installed from BiocManager using the following instructions.
# ~~~ Un-comment and run the following 5 lines if the package is not available on CRAN
# if (!requireNamespace("BiocManager", quietly = TRUE))
# install.packages("BiocManager")
# library(BiocManager)
# BiocManager::install(version = "3.11")
# BiocManager::install(c("rhdf5"))
#~~~~ Using rhdf5 for ".h5" files in R
# Import necessary functions and load libraries
source("./SampleData-master/functions/mat2ras.R")
library(reshape2) # Required for mat2ras.R
library(rhdf5)
library(raster)
smapl4_files=list.files("./SampleData-master/SMAPL4_H5", full.names = TRUE)
#View attributes of ".H5" files using h5ls function
h5ls(smapl4_files[1])## group name otype
## 0 / Geophysical_Data H5I_GROUP
## 1 /Geophysical_Data sm_rootzone H5I_DATASET
## 2 /Geophysical_Data sm_surface H5I_DATASET
## 3 / Metadata H5I_GROUP
## 4 /Metadata AcquisitionInformation H5I_GROUP
## 5 /Metadata/AcquisitionInformation platform H5I_GROUP
## 6 /Metadata/AcquisitionInformation platformDocument H5I_GROUP
## 7 /Metadata/AcquisitionInformation radar H5I_GROUP
## 8 /Metadata/AcquisitionInformation radarDocument H5I_GROUP
## 9 /Metadata/AcquisitionInformation radiometer H5I_GROUP
## 10 /Metadata/AcquisitionInformation radiometerDocument H5I_GROUP
## 11 /Metadata CRID H5I_GROUP
## 12 /Metadata/CRID GPH H5I_GROUP
## 13 /Metadata/CRID Root H5I_GROUP
## 14 /Metadata Config H5I_GROUP
## 15 /Metadata DataQuality H5I_GROUP
## 16 /Metadata/DataQuality SM H5I_GROUP
## 17 /Metadata/DataQuality/SM CompletenessOmission H5I_GROUP
## 18 /Metadata/DataQuality/SM DomainConsistency H5I_GROUP
## 19 /Metadata DatasetIdentification H5I_GROUP
## 20 /Metadata Extent H5I_GROUP
## 21 /Metadata GridSpatialRepresentation H5I_GROUP
## 22 /Metadata/GridSpatialRepresentation Latitude H5I_GROUP
## 23 /Metadata/GridSpatialRepresentation Longitude H5I_GROUP
## 24 /Metadata ProcessStep H5I_GROUP
## 25 /Metadata SeriesIdentification H5I_GROUP
## 26 /Metadata Source H5I_GROUP
## 27 /Metadata/Source GEOS5_lfo H5I_GROUP
## 28 /Metadata/Source GEOS5_lfo_corr H5I_GROUP
## 29 / cell_lat H5I_DATASET
## 30 / cell_lon H5I_DATASET
## 31 / projection_information H5I_DATASET
## 32 / time H5I_DATASET
## 33 / x H5I_DATASET
## 34 / y H5I_DATASET
## dclass dim
## 0
## 1 FLOAT 3856 x 1624
## 2 FLOAT 3856 x 1624
## 3
## 4
## 5
## 6
## 7
## 8
## 9
## 10
## 11
## 12
## 13
## 14
## 15
## 16
## 17
## 18
## 19
## 20
## 21
## 22
## 23
## 24
## 25
## 26
## 27
## 28
## 29 FLOAT 3856 x 1624
## 30 FLOAT 3856 x 1624
## 31 STRING ( 0 )
## 32 FLOAT 1
## 33 FLOAT 3856
## 34 FLOAT 1624
# Extract data fields
sm=h5read(smapl4_files[1],"Geophysical_Data/sm_surface")
Longitude = h5read(smapl4_files[1],"/cell_lon")
Latitude = h5read(smapl4_files[1],"/cell_lat")
# Subset the Lat-long fields to retrieve unique values
Latitude=Latitude[1,]
Longitude=Longitude[,1]
# Conver data to raster
sm_ras=mat2ras(sm, Longitude,Latitude, FALSE)
plot(sm_ras, main="SMAP-L4 Surface SM retrieval")
plot(coastlines,add=TRUE)S2. Updating R using RStudio
The operations in this tutorial are based on R version 4.0.3 - Bunny-Wunnies Freak Out. If necessary, update R from Rstudio using the updateR function from the installr package.
install.packages("installr")
library(installr)
updateR(keep_install_file=TRUE)