Estimate grid cell area from satelite data using a Stereographic polar projection - r

I am trying to work with satelite data from polar regions. They can be download as .nc (netcdf, see below).
They are in polar stereographic projection in a regular grid (https://nsidc.org/data/polar-stereo/ps_grids.html).
I would like to estimate each cell area to calculate the area of ice cover by multiplying the fraction of ice cover in each cell with the area of the cell.
I am able to extract cell area using "area" from {raster} only when the uniform grid is in geographical coordinates (lat-lon). However I could not find any R function to do this when the grid is uniform in stereographic coordinates.
I am very new to spatial data and projections and perhaps I am missing something important.
Below some relevant information and a piece of code with two trials.
RELEVANT DATA AND INFORMATION SOURCES
Polar Watch
https://polarwatch.noaa.gov/erddap/
Sea Ice concentration data
url to downoald data
https://polarwatch.noaa.gov/erddap/griddap/nsidcCDRiceSQnhmday.nc?seaice_conc_monthly_cdr[(2019-12-16T00:00:00Z):1:(2019-12-16T00:00:00Z)][(5837500.0):1:(-5337500.0)][(-3837500.0):1:(3737500.0)]
Can be download in R using
url1<- "https://polarwatch.noaa.gov/erddap/griddap/nsidcCDRiceSQnhmday.nc?seaice_conc_monthly_cdr[(2019-12-16T00:00:00Z):1:(2019-12-16T00:00:00Z)][(5837500.0):1:(-5337500.0)][(-3837500.0):1:(3737500.0)]"
download.file(url1, destfile='nsidcCDRiceSQnhmday_935c_47bd_a147.nc')
Data for the grid
Sea Ice Concentration Lat-Lon Grid, NOAA/NSIDC Climate Data Record V3,
Antarctic, 25km
Dataset ID: nsidcCDRice_sh_grid
url
https://polarwatch.noaa.gov/erddap/griddap/nsidcCDRice_sh_grid.nc?latitude[(4337500.0):1:(-3937500.0)][(-3937500.0):1:(3937500.0)],longitude[(4337500.0):1:(-3937500.0)][(-3937500.0):1:(3937500.0)]
url2<- "https://polarwatch.noaa.gov/erddap/griddap/nsidcCDRice_sh_grid.nc?latitude[(4337500.0):1:(-3937500.0)][(-3937500.0):1:(3937500.0)],longitude[(4337500.0):1:(-3937500.0)][(-3937500.0):1:(3937500.0)]"
download.file(url2, destfile="nsidcCDRice_sh_grid_c513_9d75_76c1.nc")
https://nsidc.org/data/polar-stereo/ps_grids.html
Table 4. Southern Hemisphere Projection Based on WGS 1984
Latitude of True Origin -70
SOME R TRIALS
require(raster)
require(ncdf4)
br<-brick("nsidcCDRiceSQnhmday_935c_47bd_a147.nc")
projection(br)<- CRS("+init=epsg:3976 +proj=stere +lat_0=-90 +lat_ts=-70 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs") #https://polarwatch.noaa.gov/tools-training/code-gallery/
br
res(br)
area(br)
which gives
Warning message:
In .local(x, ...) :
This function is only useful for Raster* objects with a longitude/latitude coordinates
Using the grid provided in the data sources
IceFgrid<-nc_open("nsidcCDRice_sh_grid_c513_9d75_76c1.nc")
ygridLatLon <- ncvar_get(IceFgrid, varid="ygrid")
xgridLatLon <- ncvar_get(IceFgrid, varid="xgrid")
longitude <- ncvar_get(IceFgrid, varid="longitude")
latitude <- ncvar_get(IceFgrid, varid="latitude")
nc_close(IceFgrid)
dim(longitude) # Matrix with Longitude of each grid point.
length(xgridLatLon)
length(ygridLatLon)
## Try to convert to data frame and the to raster.
dims <- dim(longitude)
icemap.df <- data.frame(Longitude=array(longitude,dims[1]*dims[2]),
Latitude=array(latitude,dims[1]*dims[2]))
icemap.df$Seaice <- array(1,dims[1]*dims[2])# Here I use 1 for ice cover just
rast<-rasterFromXYZ(icemap.df, crs="+proj=stere +lat_0=-90 +lat_ts=-70 +lon_0=0 +k=1 +x_0=0 +y_0=0 +a=6378273 +b=6356889.449 +units=m +no_defs")
But gives
Error in rasterFromXYZ(icemap.df, crs = "+proj=stere +lat_0=-90 +lat_ts=-70 +lon_0=0 +k=1 +x_0=0 +y_0=0 +a=6378273 +b=6356889.449 +units=m +no_defs") :
x cell sizes are not regular
Any help will be appreciated.
Thanks in advance
Angel

Here I use terra the replacement for raster
url1 <- "https://polarwatch.noaa.gov/erddap/griddap/nsidcCDRiceSQnhmday.nc?seaice_conc_monthly_cdr[(2019-12-16T00:00:00Z):1:(2019-12-16T00:00:00Z)][(5837500.0):1:(-5337500.0)][(-3837500.0):1:(3737500.0)]"
f <- 'nsidcCDRiceSQnhmday_935c_47bd_a147.nc'
download.file(url1, destfile='nsidcCDRiceSQnhmday_935c_47bd_a147.nc', mode="wb")
library(terra)
#terra version 1.3.4
r <- rast(f)
crs(r) <- "epsg:3976"
r
class : SpatRaster
dimensions : 448, 304, 1 (nrow, ncol, nlyr)
resolution : 25000, 25000 (x, y)
extent : -3850000, 3750000, -5350000, 5850000 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=stere +lat_0=-90 +lat_ts=-70 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs
source : nsidcCDRiceSQnhmday_935c_47bd_a147.nc
varname : seaice_conc_monthly_cdr (NOAA/NSIDC Climate Data Record of Passive Microwave Monthly Northern Hemisphere Sea Ice Concentration)
name : seaice_conc_monthly_cdr
unit : 1
time : 2019-12-16
As you can see, the nominal cell resolution is 25000 x 25000
res(r)
[1] 25000 25000
Because it is constant, raster does not want to compute it for you. But terra will give you either the naïve area (based on the constant the nominal resolution):
na <- cellSize(r, transform=FALSE)
minmax(na)
# area
#[1,] 6.25e+08
#[2,] 6.25e+08
Or the the true area. That is, accounting for distortion:
a <- cellSize(r, mask=F, unit="m", transform=TRUE)
a <- mask(a, r)
a
#class : SpatRaster
#dimensions : 448, 304, 1 (nrow, ncol, nlyr)
#resolution : 25000, 25000 (x, y)
#extent : -3850000, 3750000, -5350000, 5850000 (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=stere +lat_0=-90 +lat_ts=-70 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs
#source : memory
#name : area
#min value : 385538624
#max value : 664449196
#time : 2019-12-16
plot(a)
To get the sea-ice area in km2
x <- r * a / 1000000
global(x, "sum", na.rm=TRUE)
# sum
#_conc_monthly_cdr 11312232
The naïve estimate yields
n <- r * prod(res(r)) / 1000000
global(n, "sum", na.rm=TRUE)
# sum
#seaice_conc_monthly_cdr 11134025
That is about 1.5% lower
round(11134025 / 11312232, 3)
#[1] 0.984
This is not that different, because there is no ice at the edges, in the areas of severe distortion. And there is also some compensation in the central area (both smaller and larger areas) than 25*25 km2

Related

Matching points to raster in different projections

I'm mapping occurrence data for a species of interest to rasters of climate data from DAYMET, but I'm having trouble matching the points to the rasters.
Basically, I have a raster of daymet climate data (tmin) which has no projection when downloaded but is in Lambert conformal conic (https://daac.ornl.gov/DAYMET/guides/Daymet_mosaics.html), so I set the CRS:
#import daymet raster
tmin=raster("Tmin.tif")
#set projection for daymet data -> Lambert conformal
proj4string(tmin)<-CRS("+proj=lcc +lon_0=-100 +lat_0=42.5 +x_0=0 +y_0=0 +a=6378137 +rf=298.257223563 +lat_1=25 +lat_2=60")
And then I import the occurrence data which is in WGS84 lat/long(x,y), project, and transform to match tmin:
#import occurrences -> sp points
species<- read.csv("species_sites.csv", header=T)
coordinates(species)=~X+Y
#project occurences
projection(species) <- CRS("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0 ")
species <- spTransform(species, crs(tmin)) #transform to match daymet raster
The problem is they don't match when you plot together, and if you look at the extents they are off by a couple orders of magnitude:
> species
class : SpatialPointsDataFrame
features : 219
extent : 658883.2, 687373.3, 398524.3, 441106.2 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
variables : 3
names : Site, Species, Season
min values : 1, 0, -
max values : 219, 1, 2014
> tmin
class : RasterLayer
dimensions : 126, 138, 17388 (nrow, ncol, ncell)
resolution : 1, 1 (x, y)
extent : 591.25, 729.25, 355.5, 481.5 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=lcc +lon_0=-100 +lat_0=42.5 +x_0=0 +y_0=0 +a=6378137 +rf=298.257223563 +lat_1=25 +lat_2=60
names : Tmin
values : -9.410258, -7.011579 (min, max)
So I matched extents:
species#bbox<-as.matrix(extent(tmin))
but the points still don't match the raster. Not sure what else to do. Any suggestions or people with more detailed knowledge of the daymet data that might know whats going on?
Thanks #rar, figured that out last night...doh.
just needed to change CRS to:
CRS("+proj=lcc +lon_0=-100 +lat_0=42.5 +x_0=0 +y_0=0 +lat_1=25 +lat_2=60 +datum=WGS84 +units=km")

Worldclim database is not working for random samples

I have a raster with a forest cover of one area
class : RasterLayer
dimensions : 5436, 2633, 14312988 (nrow, ncol, ncell)
resolution : 100, 100 (x, y)
extent : -109346.5, 153953.5, -290837.1, 252762.9 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=tmerc +lat_0=39.66825833333333 +lon_0=-8.133108333333334 +k=1 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
data source : c://Total_Forest_cove
names : Total_Forest_cove
with values from 0 to 3 I want to create a dataset with random samples from the pixels with values higher than 0.
For that I do
library(dismo)
Forestcover[Forestcover < 1] <- NA
randompoints <- sampleRandom(Forestcover, size = 10, sp=TRUE, na.rm=TRUE)
plot(Forestcover, axes=FALSE, legend=FALSE)
randompoints <- coordinates(randompoints)
when I try to pull out the enviromental variables form worldclim
randpoints_wc <- extract(worldclim, randompoints)
randpoints_wc <- cbind(randompoints, randpoints_wc)
My dataset is always empty
Not sure if there is other method to extract random that can be apply here.
Regards
Your forest cover data set has a Mercator coordinate reference system
coord. ref. : +proj=tmerc
You do not show your WorldClim data, but presumably it has
coord. ref. : +proj=longlat
So, it is no surprise (and a good thing) that you get an empty result set. You can use rgadl::spTransform to make the coordinates match the raster.
For example:
fc <- reclassify(ForestCover, cbind(1, Inf, NA))
randompoints <- sampleRandom(fc, size = 10, sp=TRUE, na.rm=TRUE)
library(rgdal)
rp <- spTransform(randompoints, CRS("+proj=longlat +datum=WGS84"))
rp_wc <- extract(worldclim, rp)
rp_wc <- cbind(coordinates(randompoints), coordinates(rp), rp_wc)

Add SpatialPointsDataFrame and SpatialLinesDataFrame to raster plot

I need to convert a shape file into a raster and have absolutely no clue where to even start.
If anyone could help me out I'd really appreciate!
Update: I have found out about the 'readOGR'-function, but everytime I use it I get the following message:
Warning messages:
1: In readOGR(dsn = "C:/Users/Giaco/Dropbox/Random Walk/Waterbodies", :
Dropping null geometries: 308, 309
2: In readOGR(dsn = "C:/Users/Giaco/Dropbox/Random Walk/Waterbodies", :
Z-dimension discarded
Can somebody tell me what that means?
Edit:
altdata <- raster("altitude.tif")
plot(altdata)
Lotic <- readOGR(dsn="C:/Users/Giaco/Dropbox/Random Walk/Waterbodies",layer="Lotic")
Lentic <- readOGR(dsn="C:/Users/Giaco/Dropbox/Random Walk/Waterbodies",layer="Lentic")
How can I plot the raster "altdata", the SpatialPointsDataFrame "Lentic" and the SpatialLinesDataFrame "Lotic" all in one plot ?
Edit:
altdata
class : RasterLayer
dimensions : 1286, 963, 1238418 (nrow, ncol, ncell)
resolution : 15, 15 (x, y)
extent : 90938.29, 105383.3, 190000, 209290 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=tmerc +lat_0=39.66666666666666 +lon_0=1 +k=1 +x_0=200000 +y_0=300000 +ellps=intl +pm=-9.131906111111112 +units=m +no_defs
data source : C:\Users\Giaco\Dropbox\Random Walk\altitude.tif
names : altitude
values : -32768, 32767 (min, max)
> Lentic
class : SpatialPointsDataFrame
features : 182
extent : -108473.2, -95455.86, -107870.9, -91344.22 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=tmerc +lat_0=39.66825833333333 +lon_0=-8.133108333333334 +k=1 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs
variables : 3
names : Presence, Type, Accessible
min values : 0, Fountain, 0
max values : 1, Well, 2
> Lotic
class : SpatialLinesDataFrame
features : 317
extent : -108956.5, -93832.44, -108979.5, -90747.34 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=tmerc +lat_0=39.66825833333333 +lon_0=-8.133108333333334 +k=1 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs
variables : 1
names : Presence
min values : 0
max values : 1
Using base plot, there are two way to overlay vector data on a raster. First plot the raster, then you can either call plot for each feature class using the add=TRUE argument. Alternately, you can use the points and lines functions which will also add to the current plot.
Create some example data
library(raster)
library(sp)
r <- raster(nrows=180, ncols=360, xmn=571823.6, xmx=616763.6, ymn=4423540,
ymx=4453690, resolution=270, crs = CRS("+proj=utm +zone=12 +datum=NAD83
+units=m +no_defs +ellps=GRS80 +towgs84=0,0,0"))
r[] <- runif(ncell(r))
pts <- sampleRandom(r, 10, na.rm = TRUE, sp = TRUE)
Plot the raster then call plot again, with add=TRUE, to add points.
plot(r)
plot(pts, pch=20, cex=2, col="red", add=TRUE)
Or, plot the raster and use points to add the point feature class.
plot(r)
points(pts, pch=20, cex=2, col="red")
Edit: Your extents between the raster and vector feature classes do not overlap.
We can create SpatialPolygons using the extent of your objects and an example raster (with a uniform value of 1).
library(raster)
proj <- sp::CRS("+proj=tmerc +lat_0=39.66666666666666 +lon_0=1 +k=1 +x_0=200000 +y_0=300000
+ellps=intl +pm=-9.131906111111112 +units=m +no_defs")
r.ext <- as(extent(90938.29, 105383.3, 190000, 209290), "SpatialPolygons")
proj4string(r.ext) <- proj
pt.ext <- as(extent(-108473.2, -95455.86, -107870.9, -91344.22), "SpatialPolygons")
proj4string(pt.ext) <- proj
line.ext <- as(extent(-108956.5, -93832.44, -108979.5, -90747.34), "SpatialPolygons")
proj4string(line.ext) <- proj
r <- raster(r.ext, resolution = c(15,15), crs = proj)
r[] <- rep(1, ncell(r))
Here we see that if we plot the raster and then the point and line extent polygons, you cannot see them.
plot(r)
plot(pt.ext, add=TRUE)
plot(line.ext, add=TRUE)
However, if we plot the line and point extent polygons they overlay just fine.
plot(line.ext)
plot(pt.ext,add=TRUE)
If we limit the raster extent to the extent of the line object, you should see the raster sub-region, but do not. And, if you try to crop the raster you will receive an "Error in .local(x, y, ...) : extents do not overlap" error.
plot(line.ext)
plot(r, add=TRUE)

Crop rasterLayer with SpatialPolygonDataFrame leaves out part of the target region R

library(raster)
library(ncdf4)
library(rgdal)
I am trying to crop a raster layer based on polygon: sample data (~45MB) can be found here sample data for illustration:
dat<- raster('data.nc')# make a subset
dat
class : RasterLayer
dimensions : 824, 935, 770440 (nrow, ncol, ncell)
resolution : 10000, 10000 (x, y)
extent : -5000, 9345000, -5000, 8235000 (xmin, xmax, ymin, ymax)
coord. ref. : NA
data source : C:\Users\data.nc
names : Total.Precipitation
zvar : Total.Precipitation
As coord. ref is = NA, I assign a polar stereographic projection based on information from the website of the data provider.
projj=CRS("+proj=stere +lat_0=90 +lat_ts=90 +lon_0=-100 +k=0.994 +x_0=2000000 +y_0=2000000 +datum=WGS84 +units=m +no_defs")
proj4string(dat) <- projj
#rx <- projectRaster(from=dat, crs=wgs84.p4s)
Then transform the bbox shapefile to projj
shgrid <- spTransform(Prairie.Boundaries, projj)# transform to dat spat reference
plot(dat)
plot(shgrid,add=T)# this gives
CROPPING
cr <- crop(dat, extent(shgrid))
plot(cr)
fr <- rasterize(shgrid, cr)
dat1<- mask(x=cr, mask=fr)
levelplot(dat1)
OUTPUT:
The cropped image shows only part of canada but I want all of Canada.
What did I miss within my code? Could it be the projection?
Besides I would prefer the axes labels in latlon coordinates.
Any suggestions?

reproject raster (WGS84 to BNG) with large resolution

i'm doing a simple enough operation, reprojecting a raster from WGS84 to British National Grid, but i am wondering about some of the results post reprojection. The sum of the resulting rasters are quite different; is this due to the resolution and the way the bilinear interpolation in 'projectRaster' works?
I started with a csv that has global data covering the range of -180 to 180 degrees in both latitude and longitude (integer values only), with some z-values. this is subsetted, made into a raster of projection WGS84 and converted to BNG (subset below):
x <- rep(c(-10:3), times = 10)
n <- 14
y <- rep(48:57, each=n)
z <- rnorm(n=140, mean=20, sd=5)
ind <- which(z %in% sample(z, 45))
z[ind]<-NA
df <- data.frame("x"=x,"y"=y,"value"=z)
bng <- '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs'
wgs84 <- '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'
coordinates(df) = ~x + y
ras = rasterFromXYZ(df, crs=wgs84)
cellStats(ras,sum)
plot(ras)
ras_bng <- projectRaster(ras,crs=bng)
plot(ras_bng)
cellStats(ras_bng,sum)
the sum goes from 1919 to 2625 (in my case), a fair chunk.
Is it purely from the reprojection creating so many extra cells around the 'edges'? if i were to reproject to a raster of much smaller resolution (5km), would this reduce the differing sums considerably?
thanks, S
> ras
class : RasterLayer
dimensions : 10, 14, 140 (nrow, ncol, ncell)
resolution : 1, 1 (x, y)
extent : -10.5, 3.5, 47.5, 57.5 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +towgs84=0,0,0
data source : in memory
names : layer
values : 3.243918, 32.21532 (min, max)
> ras_bng
class : RasterLayer
dimensions : 12, 19, 228 (nrow, ncol, ncell)
resolution : 67900, 111000 (x, y)
extent : -375677.8, 914422.2, -343553.2, 988446.8 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs +towgs84=446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894
data source : in memory
names : layer
values : 8.174968, 28.31331 (min, max)
This is because the NA values in 'ras'.
The difference is small when you take out z[ind]<-NA. This is because projectRaster implicitly uses "na.rm=TRUE"; perhaps an argument is needed to change that. This is not as simple as in other cases, as, for example, only one of the cells interpolated from may be NA, in which case it should probably be computed.
In practice it is rare to find this many NA values, typically they are confined to the edges (of land) only.
By the way WGS84 is a datum,it is not a projection. The projection you are using would be 'longitude/latitude' (also known by other names) except that this is also not a projection since the whole point of a projection is to go from such angular coordinates to planar coordinates. So, you are transforming one coordinate reference system ('long/lat, WGS84') to another (BNG).

Resources