I am working with temperature .nc files from NARCCAP. These data have a polar stereographic projection. From temperature minimums and maximums, I've created a matrix of days that qualify as maple syrup production days.
I want to turn this matrix into a raster, and project this raster to a lon/lat projection.
## This is the metadata for the projection from the .nc file:
# float lat[xc,yc]
# long_name: latitude
# standard_name: latitude
# units: degrees_north
# axis: Y
# float lon[xc,yc]
# long_name: longitude
# standard_name: longitude
# units: degrees_east
# axis: X
# float tasmax[xc,yc,time]
# coordinates: lon lat level
# _FillValue: 1.00000002004088e+20
# original_units: K
# long_name: Maximum Daily Surface Air Temperature
# missing_value: 1.00000002004088e+20
# original_name: T_MAX_GDS5_HTGL
# units: K
# standard_name: air_temperature
# cell_methods: time: maximum (interval: 24 hours)
# grid_mapping: polar_stereographic
# grid_mapping_name: polar_stereographic
# latitude_of_projection_origin: 90
# standard_parallel: 60
# false_easting: 4700000
# false_northing: 8400000
# longitude_of_central_meridian: 263
# straight_vertical_longitude_from_pole: 263
# The production days matrix I've created is called from a saved file:
path.ecp2 <- paste0("E:/all_files/production/narccap/GFDL/Production_Days_SkinnerECP2",
year, ".RData")
file.ecp2 <- get(load(path.ecp2))
dim(file.ecp2)
# 147 116
rast.ecp2 <- raster(file.ecp2)
rast.ecp2 <- flip(t(rast.ecp2), 2)
# class : RasterLayer
# dimensions : 116, 147, 17052 (nrow, ncol, ncell)
# resolution : 0.006802721, 0.00862069 (x, y)
# extent : 0, 1, 0, 1 (xmin, xmax, ymin, ymax)
# coord. ref. : NA
# data source : in memory
# names : layer
# values : 0, 671 (min, max)
# I assign the polar stereographic crs to this production days raster:
crs("+init=epsg:3031")
ecp2.proj <- "+proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +k=1 +x_0=4700000 +y_0=8400000 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0"
crs(rast.ecp2) <- crs(ecp2.proj)
rast.ecp2
# class : RasterLayer
# dimensions : 116, 147, 17052 (nrow, ncol, ncell)
# resolution : 0.006802721, 0.00862069 (x, y)
# extent : 0, 1, 0, 1 (xmin, xmax, ymin, ymax)
# coord. ref. : +proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +k=1 +x_0=4700000 +y_0=8400000 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
# data source : in memory
# names : layer
# values : 0, 671 (min, max)
When I use the steps that worked for me previously (see here), the values of rast.ecp2 all go to NA. Where am I going wrong?
# The projection I want to project TO:
source_rast <- raster(nrow=222, ncol=462, xmn=-124.75, xmx=-67, ymn=25.125, ymx=52.875,
crs="+proj=longlat +datum=WGS84")
rast.ecp2LL <- projectRaster(rast.ecp2, source_rast)
rast.ecp2LL
# class : RasterLayer
# dimensions : 222, 462, 102564 (nrow, ncol, ncell)
# resolution : 0.125, 0.125 (x, y)
# extent : -124.75, -67, 25.125, 52.875 (xmin, xmax, ymin, ymax)
# coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
# data source : in memory
# names : layer
# values : NA, NA (min, max)
I am posting the solution I found to work. It is based on this post and answer. I had to first convert the xc and yc coordinates of the .nc file to longitude and latitude points. Then I could properly reproject the raster. Below is the code that worked.
Note that mycrs is the CRS that "came with" the .nc file. It has to be assigned to the SpatialPoints since converting from xc/yc to SpatialPoints drops the associated CRS.
years <- seq(from=1971, to=2000, by=5)
model <- "CRCM"
convert.lonlat <- function(model, year)
{
max.stem <- "E:/all_files/www.earthsystemgrid.org/CCSM/tasmax_"
inputfile <- paste0(max.stem, model, "_ccsm_", year, "010106.nc")
lat <- raster(inputfile, varname="lat")
lon <- raster(inputfile, varname = "lon")
plat <- rasterToPoints(lat)
plon <- rasterToPoints(lon)
lonlat <- cbind(plon[,3], plat[,3])
lonlat <- SpatialPoints(lonlat, proj4string = crs(base.proj))
mycrs <- crs("+proj=stere +lon_0=263 +x_0=3475000 +y_0=7475000 +lat_0=90 +ellps=WGS84")
plonlat <- spTransform(lonlat, CRSobj = mycrs)
maxs <- brick(inputfile, varname="tasmax")
projection(maxs) <- mycrs
extent(maxs) <- extent(plonlat)
max.lonlat <- projectRaster(maxs, base.proj)
save(max.lonlat, file=paste0("E:/all_files/production/narccap/CCSM/", model, "max_lonlat_", year, ".RData"))
min.stem <- "E:/all_files/www.earthsystemgrid.org/CCSM/tasmin_"
inputfile <- paste0(min.stem, model, "_ccsm_", year, "010106.nc")
lat <- raster(inputfile, varname="lat")
lon <- raster(inputfile, varname = "lon")
plat <- rasterToPoints(lat)
plon <- rasterToPoints(lon)
lonlat <- cbind(plon[,3], plat[,3])
lonlat <- SpatialPoints(lonlat, proj4string = crs(maurer.proj))
mycrs <- crs("+proj=stere +lon_0=263 +x_0=3475000 +y_0=7475000 +lat_0=90 +ellps=WGS84")
plonlat <- spTransform(lonlat, CRSobj = mycrs)
mins <- brick(inputfile, varname="tasmin")
projection(mins) <- mycrs
extent(mins) <- extent(plonlat)
min.lonlat <- projectRaster(mins, maurer.proj)
save(min.lonlat, file=paste0("E:/all_files/production/narccap/CCSM/", model, "min_lonlat_", year, ".RData"))
}
lapply(years, convert.lonlat, model=model)
From here I go on to make the matrix of production days based on the saved files max.lonlat and min.lonlat.
Related
Hi am trying to extract values for xy point from a sample raster stack ras_dt. The ras_dt is EQUATES data with gridded coordinates within the domain of -115.00,38.00,-110.05,45.00. How can I change the projection and the lat lon coordinates of this raster stack so that I can extract data for point xy as in the code below.
library(raster)
dturl<- "https://www.dropbox.com/s/ztxqpszjfjhpavz/EQUATES_ACONC_O3_SAM.nc?dl=1"
download.file(dturl, "EQUATES_ACONC_O3_SAM.nc")
ras_dt <- raster::stack("EQUATES_ACONC_O3_SAM.nc",varname = "O3")
ras_dt
# the data domain is -115.00,38.00,-110.05,45.00
plot(ras_dt)
xy <- data.frame(lon=-113.0,lat=40.0)
coordinates(xy) <- ~lon + lat
extr_dt <- raster::extract(ras_dt, xy) # how to get O3 values for xy here?
extr_dt
The ras_dt is in "lambertConformalProjection" with following info:
char LambertConformalProjection;
:grid_mapping_name = "lambert_conformal_conic";
:latitude_of_projection_origin = 40.0; // double
:longitude_of_central_meridian = -97.0; // double
:standard_parallel = 33.0, 45.0; // double
:earth_radius = 6370000.0; // double
:_CoordinateTransformType = "Projection";
:_CoordinateAxes = "x y";
You need to set the extent and the coordinate reference system (CRS). Then transform your lon/lat points to that CRS and use extract. From your edited question we now have the CRS.
You specify the "domain", but you need to coordinates in the actual CRS. I do not know what these are, so I will guestimate it, but it will not be correct.
The data
library(terra)
dturl<- "https://www.dropbox.com/s/ztxqpszjfjhpavz/EQUATES_ACONC_O3_SAM.nc?dl=1"
download.file(dturl, "EQUATES_ACONC_O3_SAM.nc", mode="wb")
ras_dt <- rast("EQUATES_ACONC_O3_SAM.nc", "O3")
pcrs <- "+proj=lcc +lon_0=-97 +lat_0=40 +lat_1=33 +lat_2=45 +r =6370000.0"
Rough estimate of the extent:
v <- vect(rbind(c(-115, 38), c(-115, 45), c(-110.05, 38), c(-110.05, 45)), crs="+proj=longlat")
p <- project(v, pcrs)
e <- crds(p) |> apply(2, range) |> as.vector()
e
#[1] -1562368.5 -1025418.3 -139104.3 693662.9
Set the extent and the crs
ext(ras_dt) <- e
crs(ras_dt) <- pcrs
Transform the points to the crs of the raster
xy <- vect(cbind(lon=-113.0,lat=40.0), crs="+proj=longlat")
pxy <- project(xy, pcrs)
And extract
extract(ras_dt, pxy)
# ID O3_LAY=1_TSTEP=1 O3_LAY=1_TSTEP=2
#1 1 41.88482 40.99662
So our raster now looks like this. The spatial resolution should probably be a round number (12,000?)
ras_dt
#class : SpatRaster
#dimensions : 70, 45, 2 (nrow, ncol, nlyr)
#resolution : 11932.23, 11896.67 (x, y)
#extent : -1562369, -1025418, -139104.3, 693662.9 (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=lcc +lat_0=40 +lon_0=-97 +lat_1=33 +lat_2=45 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs
#source : EQUATES_ACONC_O3_SAM.nc:O3
#varname : O3 (O3 )
#names : O3_LAY=1_TSTEP=1, O3_LAY=1_TSTEP=2
#unit : ppbV , ppbV
Can you find and provide the correct extent somewhere in the documentation? And perhaps ask the data providers to follow the NetCDF conventions such that all the metadata required to use the data is stored in the files.
I'm working with environmental raster layers from WorldClim V2.1 (30 seconds resolution) in Latin America. I want the layers to be of a precise resolution (50km2 pixel size). However, I'm a bit unsure whether I'm following the correct steps to process the files. Any help on checking the process would be much appreciated.
And one last thing, I'm wondering what is the terra::resample doing with NAs.
Thanks !
Here's an example with average annual temperature
Libraries
library(rnaturalearth)
library(terra)
library(sf)
library(tidyverse)
Basemaps
# world and Latin America
world <- rnaturalearth::ne_countries(scale = 'medium', returnclass = 'sf')
bbox_Latam_unprojected <- c(xmin=-118.40137, ymin=-55.89170, xmax=-34.80547, ymax= 32.71533)
Latam_unprojected <- world %>% st_crop(bbox_Latam_unprojected)
# equal area projection (Equatorial Lambert azimuthal equal-area)
equalareaCRS <- '+proj=laea +lon_0=-73.125 +lat_0=0 +datum=WGS84 +units=m +no_defs'
Latam_projected <- sf::st_transform(Latam_unprojected, crs=equalareaCRS)
Latam <- st_union(Latam_projected)
# raster of Latin America to use as template - 50km^2 (50000m^2)
Latam.grid <- st_make_grid(Latam+1000, cellsize = 50000, crs = equalareaCRS) %>%
st_sf('geometry' = ., 'occ'= 0)
r <- rast(res=50000, ext(vect(Latam.grid)), crs=equalareaCRS)
Latam.raster <- terra::rasterize(x = vect(Latam.grid),
y = r, field = 'occ') %>% terra::mask(., vect(Latam))
Environmental layer
# annual temperature files
url <- 'https://biogeo.ucdavis.edu/data/worldclim/v2.1/base/wc2.1_30s_tavg.zip'
download.file(url, destfile='data/wc2.1_30s_tavg')
# average annual temperature
tavg_files <- list.files('data/wc2.1_30s_tavg', '.tif', full.names=TRUE)
world_tavg <- rast(tavg_files)
tavg_unprojected <- crop(world_tavg, ext(Latam_unprojected)+1)
tavg <- project(tavg_unprojected, equalareaCRS, method='near') # reproject to equal area
tavg <- mean(tavg)
names(tavg) <- 'tavg'
# resample to Latam's preferred resolution
tavg_Latam <- terra::resample(tavg, Latam.raster, method='bilinear')
tavg
class : SpatRaster
dimensions : 12512, 11213, 1 (nrow, ncol, nlyr)
resolution : 829.4488, 829.4488 (x, y)
extent : -5009004, 4291606, -6411068, 3966996 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=laea +lat_0=0 +lon_0=-73.125 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs
source : tavg.tif
name : tavg
min value : -15.775
max value : 29.56667
tavg_Latam
class : SpatRaster
dimensions : 199, 172, 1 (nrow, ncol, nlyr)
resolution : 50000, 50000 (x, y)
extent : -4409882, 4190118, -6080809, 3869191 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=laea +lat_0=0 +lon_0=-73.125 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs
source : memory
name : tavg
min value : -2.684936
max value : 29.17986
Cheers,
Flo
I wish to extract raster values based on a list of coordinates. I’ve found online some scripts that include coordinates(), SpatialPoints(), crs() and spTransform() and other that don’t. Could someone kindly explain if script 1 or script 2 is correct and why? Thank you very much!
SCRIPT 1
sites <- read.csv("df.csv")
coordinates(sites)= ~ Longitude+ Latitude
mypoints = SpatialPoints(sites,proj4string = CRS("+init=epsg:4326"))
myproj = CRS(myraster)
points.proj = spTransform(mypoints, myproj)
myvalues = extract(myraster, points.proj)
SCRIPT 2
sites <- read.csv("df.csv")
myvalues = extract(myraster, cbind(sites$Longitude, y=sites$Latitude), df=TRUE, method='simple', cellnumbers=T)
Either could be correct. With RasterLayer r and data.frame sites you can do
v <- extract(r, sites[, c("Longitude", "Latitude")])
Under the assumption that "Longitude" and "Latitude" are variables in sites.
However that only works when r also has a ("Longitude", "Latitude") coordinate reference system. That may not be the case. Consider this RasterLayer
f <- system.file("external/test.grd", package="raster")
r <- raster(f)
r
#class : RasterLayer
#dimensions : 115, 80, 9200 (nrow, ncol, ncell)
#resolution : 40, 40 (x, y)
#extent : 178400, 181600, 329400, 334000 (xmin, xmax, ymin, ymax)
#crs : +proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +units=m +towgs84=565.237,50.0087,465.658,-0.406857,0.350733,-1.87035,4.0812 +no_defs
#source : C:/soft/R/R-3.6.1/library/raster/external/test.grd
#names : test
#values : 128.434, 1805.78 (min, max)
The crs is "sterea ..." and the extent "178400, 181600, ...) shows that the coordinates are clearly not longitude and latitude (they are expressed in meters away from the origin of the crs.)
In this case, you might have a point in the area covered by r
site <- data.frame(Longitude=5.745039, Latitude=50.96254)
But extract returns NA because the crs do not match
extract(r, site)
# [,1]
#[1,] NA
So we do
pts <- SpatialPoints(site)
crs(pts) <- "+proj=longlat +datum=WGS84"
rcrs <- crs(r)
ptrans <- spTransform(pts, rcrs)
And now it works
extract(r, ptrans)
#1529.66
I have a RasterBrick consisting of daily snow cover data with the values 1, 2 and 3 (1= snow, 2= no snow, 3= cloud-obscured).
Example of snow cover of one day:
> snowcover
class : Large RasterBrick
dimensions : 26, 26, 2938 (nrow, ncol, nlayers)
resolution : 231, 232 (x, y)
extent : 718990, 724996, 5154964, 5160996 (xmin, xmax, ymin, ymax)
crs : +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs +ellps=WGS84
+towgs84=0,0,0
Now I wish to interpolate the cloud-obscured pixels (but only where there are less than 90 % cloud cover in a single RasterLayer, otherwise the original values should be retained for this Layers).
For spatial interpolation I want to use a digital elevation model (same study area and already in same resolution) to extract upper and lower snowline boundaries for each Layer of the RasterBrick respectively. The upper snow line represents the elevation where
all cloud-free pixels are classified as snow. The lower snowline identifies the
altitude below which all cloud-free pixels are also snow-free.
> dem
class : RasterLayer
resolution : 231, 232 (x, y)
extent : 718990.2, 724996.2, 5154964, 5160996 (xmin, xmax, ymin, ymax)
crs : +proj=utm +zone=32 +datum=WGS84 +units=m +no_defs +ellps=WGS84
+towgs84=0,0,0
values : 1503, 2135 (min, max)
For the upper snowlines I need the minimum elevation of the snow-covered pixels (value = 1). Now all pixels of value 3 in a RasterLayer of the RasterBrick above this minimum elevation, should be reclassified as value 1 (assumed to be snow-covered).
For the lower snowline on the other hand I need to identify the maximum elevation of the no-snow-pixels (value = 2). Now all pixels of value 3 in a RasterLayer of the RasterBrick above this maximum elevation should be reclassified as value 2 (assumed to be snow-free).
Is this possible using R?
I tried to make use of the overlay function, but I got stuck there.
# For the upper snowline:
overlay <- overlay(snowcover, dem, fun=function(x,y){ x[y>=minValue(y[x == 1])] <- 1; x})
Here is some example data
library(raster)
dem <- raster(ncol=8, nrow=7, xmn=720145, xmx=721993, ymn=5158211, ymx=5159835, crs='+proj=utm +zone=32 +datum=WGS84')
values(dem) <- ncell(dem):1
snow <- setValues(dem, c(1, 1, rep(1:3, each=18)))
snow[,c(2,5)] <- NA
snow[3] <- 3
plot(snow)
lines(as(dem, 'SpatialPolygons'))
text(dem)
The plot shows the snow classes (1, 2, 3) with the elevation values on top.
We can use mask, but need to deal with the missing values.
msnow <- reclassify(snow, cbind(NA, 0))
# mask to get only the snow elevations
x <- mask(dem, msnow, maskvalue=1, inverse=TRUE)
# minimum elevation of the snow-covered cells
minsnow <- minValue(x)
minsnow
#[1] 37
# snow elevation = 1
snowy <- reclassify(dem, rbind(c(-Inf, minsnow, NA), c(minsnow, Inf, 1)))
newsnow <- cover(snow, snowy)
s <- stack(dem, snow, newsnow)
names(s) <- c("elevation", "old_snow", "new_snow")
You were very close, as you can do
r <- overlay(dem, snow, fun=function(e, s){ s[e >= minsnow] <- 1; s})
But note that that also overwrites high cells with no snow.
Which could be fixed like this:
r <- overlay(dem, snow, fun=function(e, s){ s[e >= minsnow & is.na(s)] <- 1; s})
To select layers with more than x% cells with value 3 (here I use a threshold of 34%):
threshold = .34
s <- stack(snow, snow+1, snow+2)
f <- freq(snow)
f
# value count
#[1,] 1 14
#[2,] 2 13
#[3,] 3 15
#[4,] NA 14
nas <- f[is.na(f[,1]), 2]
ss <- subs(s, data.frame(from=3, to=1, subsWithNA=TRUE))
cs <- cellStats(ss, sum)
csf <- cs / (ncell(snow) - nas)
csf
# layer.1 layer.2 layer.3
#0.3571429 0.3095238 0.3333333
i <- which(csf < threshold)
use <- s[[i]]
#use
class : RasterStack
dimensions : 7, 8, 56, 2 (nrow, ncol, ncell, nlayers)
resolution : 231, 232 (x, y)
extent : 720145, 721993, 5158211, 5159835 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=utm +zone=32 +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
names : layer.2, layer.3
min values : 2, 3
max values : 4, 5
I would like to assign lonlatto a rasterbrick, r. rwas read from a netcdf file which had latlon in it. However, after reading the data, the metadata looks like:
class : RasterBrick
dimensions : 699, 639, 446661, 4779 (nrow, ncol, ncell, nlayers)
resolution : 1, 1 (x, y)
extent : 0.5, 639.5, 0.5, 699.5 (xmin, xmax, ymin, ymax)
coord. ref. : NA
CRS=CRS("+proj=lcc +lat_1=33 +lat_2=45 +lat_0=40 +lon_0=-97 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs") # WRF proj4")
I then read in the latlon separately into R:
library(ncdf4)
ncname <- "file.nc"
ncfname <- paste(ncname, ".nc4", sep = "")
dname <- "tmp" # note: tmp means temperature (not temporary)
# open a NetCDF file
ncin <- nc_open(ncfname, write = T)
print(ncin)
#---------
lon <- ncvar_get(ncin, "XLONG")
nlon <- dim(lon)
head(lon)
lat <- ncvar_get(ncin, "XLAT", verbose = F)
nlat <- dim(lat)
head(lat)
print(c(nlon, nlat))
[1] 639 699 639 699
You will notice that nlon and nlat has the same dimensions as r.
I don't want to projectRaster.
Question:
How can I assign the imported lon and latto r such that the extent of r is min(lon), max(lon), min(lat), max(lat)?
I tried coordinates(dat)=c(lon,lat) but it did not work.
Thanks.