How to set the display bounds of a raster::plot? - r

summary
How to limit display of an R raster::plot to the bounds of a Raster* object? Why I ask:
I'm an R beginner who must
convert unprojected (lon-lat) global spatial data from ASCII to netCDF (solved)
"regrid" it: i.e., restricted the data to a region, project it, and downscale (decrease the size of the gridcells) it (mostly solved)
Unfortunately, in scientific work, one mostly QAs data transformations such as this by visual inspection. Step 1 above looks good. But though step 2 now looks much better, I really want to plot only the bounds (or extents) of the regridded RasterLayer. I have R code driven by bash scripts in a public git repository (here) that does the conversion step, and plots the converted output, apparently correctly. However my attempts to plot the output of the regridding with raster::plot are not quite right (see first 3 pages of output here).
How to fix?
details
background
I need to take some data (a global marine emissions inventory (EI)), combine it with other data (mostly other EIs), and input that to a model. The model wants to consume netCDF, and it wants that netCDF over a spatial domain slightly bigger than the contiguous US (CONUS). The borders of this image are the boundaries of the domain. (Note that part, but only part, of the domain is oceanic.) The model also wants that netCDF projected a certain way (LCC at 12-km resolution). I'm treating this as 2 independent problems:
convert the global EI from its native ASCII format to netCDF
"regrid" the netCDF from global/unprojected to the downscaled (finer-resolution), projected subdomain.
I'm attempting to solve these problems with code I have archived at this public git repository. If you clone that git repo, and then configure/run the bash driver GEIA_to_netCDF.sh as described for the "first example" in the README (and presuming your R is appropriately configured, notably with packages=raster, ncdf4) it will output netCDF and plot (using fields::image.plot) that output to a PDF, which should look like this: . The distribution of the output data appears correct, so problem 1 seems solved.
problem
Solving problem 2 (aka the "second example" in the README) seems to require R package=raster. My bash driver regrid_GEIA_netCDF.sh calls regrid.global.to.AQMEII.r (both in repository), which runs without error, and displays a PDF of its output. GEIA_N2O_oceanic_regrid.pdf comprises 4 pages, corresponding to 4 blocks of code at the end of regrid.global.to.AQMEII.r. Only the first 3 pages are relevant here (the 4th page tries to use fields::image.plot and has bigger problems).
Page=1/4 results from a simple raster::plot of the regridded output, plus a projected version of a CONUS map from wrld_simpl:
plot(out.raster)
plot(map.us.proj, add=TRUE)
Unfortunately the output appears global, or nearly so: But the desired domain, to which the output was regridded, is much smaller: . So I have 3 questions (1 main question, 2 followups):
Does the image displayed by raster::plot by default display only the extents of the RasterLayer given in its arguments?
If so, what is wrong with my regrid? (more below)
If not (i.e., if raster::plot by default displays more than the extents of its RasterLayer), how to make raster::plot display only the extents of that RasterLayer?
The code in regrid.global.to.AQMEII.r (here) that does the regridding seems to get the correct output:
out.crs <- '+proj=lcc +lat_1=33 +lat_2=45 +lat_0=40 +lon_0=-97 +x_0=-2556000 +y_0=-1728000'
Note that CRS appears to match the definition of the output domain given here. (Once at the linked page, scroll past the map.)
out.raster <-
projectRaster(
from=in.raster, to=template.raster, method='bilinear', crs=out.crs,
overwrite=TRUE, progress='window', format='CDF',
# args from writeRaster
NAflag=-999.0, # match emi_n2o:missing_value,_FillValue (TODO: copy)
varname=data.var.name,
varunit='ton N2O-N/yr',
longname='N2O emissions',
xname='COL',
yname='ROW',
filename=out.fp)
out.raster
# made with CRS
#> class : RasterLayer
#> dimensions : 299, 459, 137241 (nrow, ncol, ncell)
#> resolution : 53369.55, 56883.69 (x, y)
#> extent : -14802449, 9694173, -6258782, 10749443 (xmin, xmax, ymin, ymax)
# ??? why still proj=longlat ???
#> coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
#> data source : /home/rtd/code/R/GEIA_to_netCDF/GEIA_N2O_oceanic_regrid.nc
#> names : N2O.emissions
#> zvar : emi_n2o
But, as noted, the regrid output (out.raster) shows itself to be lon-lat in its CRS: I'm not sure why that is, or if it implies out.raster is global in extents.
I also tried to restrict the plot itself in two ways:
Firstly, I tried adding an extents to the plot, i.e.,
plot(out.raster, ext=template.raster)
plot(map.us.proj, add=TRUE)
which generates page=2/4 of the PDF. Unfortunately, that does not change the output at all: AFAICS, it is completely identical to page=1/4 (above).
Secondly, I tried using raster::crop to bound the regridded netCDF itself, before plotting that, using the same RasterLayer object (template.raster) that I used to bound the regrid:
template.raster.extent <- extent(template.raster)
out.raster.crop <-
# overwrite the uncropped file
crop(out.raster, template.raster.extent, filename=out.fp, overwrite=TRUE)
...
plot(out.raster.crop)
plot(map.us.proj, add=TRUE)
which generates page=3/4 of the PDF. Unfortunately, that it is also apparently completely identical to page=1/4 (above).
(In case you're wondering, page 4 of the PDF was generated using fields::image.plot, which has a different problem, described here, unless StackOverflow whacks that link.)
Your assistance to this R newbie is appreciated!

summary
My question
How to limit display of an R `raster::plot` to the bounds of a `Raster*` object?
was based on my incorrect diagnosis of the problem. The solution was to set the bounds (or extent) of the underlying Raster* data object (i.e., the source of the data for the plot) correctly, and particularly
to get the bounds of the template object (used to create the data object) by querying its underlying file (make no assumptions!)
to get the CRS of the template object by querying its underlying file (make no assumptions!)
to set the bounds and CRS for the template object directly
But
don't try to just set the resolution on the Extent; for me at least, that hangs
there is still one minor bounding problem with the raster::plot map; at least, relative to the map I'm using with fields::image.plot
details
The answer to this question is actually the answer to a different question: how to correctly set the extent of a Raster* object? because the raster::plot problem mostly went away once I properly set the extent of the object I wanted to plot. (With one minor exception--see below.) This may have been what Robert J. Hijmans, the main raster developer, was trying to convey in this post, but if so, I did not perceive it at the time.
I solved the problem after getting two suggestions which weren't themselves what I needed, but which set me on the proper path. In this case, that path led to the R package M3, which is useful for dealing with data input to and output by CMAQ and WRF. The suggestions were
Use project.lonlat.to.M3 for the regridding task.
The plot problem may be related to the assumption by the generating model (CMAQ) of a spherical projection.
The second suggestion seemed plausible, since I knew that I was getting and setting a CRS with +ellps=WGS84. (See profusely-commented R in this repository, specifically regrid.global.to.AQMEII.r.) So I resolved to look into that.
I knew the first suggestion would only work with difficulty (since it only projects points), but looked at the M3 doc just to be sure. The following functions in its ToC immediately caught my eye:
get.proj.info.M3, which not only returned a spherical PROJ.4 string from the template file, but one without the false eastings and northings which I believed I needed to supply:
# use package=M3 to get CRS from template file
out.crs <- get.proj.info.M3(template.in.fp)
cat(sprintf('out.crs=%s\n', out.crs)) # debugging
# out.crs=+proj=lcc +lat_1=33 +lat_2=45 +lat_0=40 +lon_0=-97 +a=6370000 +b=6370000
get.grid.info.M3, which can, with a bit of effort, return the bounds/extent of the template file:
extents.info <- get.grid.info.M3(template.in.fp)
extents.xmin <- extents.info$x.orig
extents.xmax <- max(
get.coord.for.dimension(
file=template.in.fp, dimension="col", position="upper", units="m")$coords)
extents.ymin <- extents.info$y.orig
extents.ymax <- max(
get.coord.for.dimension(
file=template.in.fp, dimension="row", position="upper", units="m")$coords)
template.extents <-
extent(extents.xmin, extents.xmax, extents.ymin, extents.ymax)
One can then set those bounds on the template Raster*
template.in.raster <- raster(template.in.fp, ...)
template.raster <- projectExtent(template.in.raster, crs=out.crs)
template.raster#extent <- template.extents
and use the template to regrid the input Raster*
out.raster <-
projectRaster(
# give a template with extents--fast, but gotta calculate extents
from=in.raster, to=template.raster, crs=out.crs,
# give a resolution instead of a template? no, that hangs
# from=in.raster, res=grid.res, crs=out.crs,
method='bilinear', overwrite=TRUE, format='CDF',
# args from writeRaster
NAflag=-999.0, # match emi_n2o:missing_value,_FillValue (TODO: copy)
varname=data.var.name,
varunit='ton N2O-N/yr',
longname='N2O emissions',
xname='COL',
yname='ROW',
filename=out.fp)
# above fails to set CRS, so
out.raster#crs <- CRS(out.crs)
(As suggested above, regridding by setting the template took only 7 sec, but regridding by setting a grid resolution failed to complete after 2 hr, when I killed the job.) After that, the raster::plot
map.us.unproj <- wrld_simpl[wrld_simpl$ISO3 %in% c('CAN', 'MEX', 'USA'),]
map.us.proj <-
spTransform(map.us.unproj, CRS(out.crs)) # projected
...
pdf(file=pdf.fp, width=5.5, height=4.25)
...
plot(out.raster, # remaining args from image.plot
main=title, sub=subtitle,
xlab='', ylab='', axes=F, col=colors(100),
axis.args=list(at=quantiles, labels=quantiles.formatted))
# add a projected CONUS map
plot(map.us.proj, add=TRUE)
was nearly as expected, with one exception: the map extends beyond the bounds of the data to the north and south (though not to the east and west), causing the image to insufficiently resemble published images of the domain, e.g. this. Interestingly, when I plot with fields::image.plot like
# see code in
# https://github.com/TomRoche/GEIA_to_netCDF/blob/master/plotLayersForTimestep.r
plot.raster(
raster=out.raster,
title=title,
subtitle=subtitle,
q.vec=probabilities.vec,
colors,
map.cmaq
)
I don't get that problem: . So I'll probably use fields::image.plot for display, at least for the moment.

Related

Raster increase in size when reprojected in R and QGIS

I'm using a Land cover raster of North America which is publicly available here: https://open.canada.ca/data/en/dataset/4e615eae-b90c-420b-adee-2ca35896caf6
I clipped it in R to cover Québec/Labrador:
veg <- raster("CanadaLandcover2015/CAN_LC_2015_CAL.tif")
e <- extent(c(1000000, 2700000, 500000, 2700000))#all qc
veg_qc <- crop(veg, e)
The raster is originally in projection ESPG:3978 NAD83/Canada Atlas Lambert. I wanted it to be in lat and long to be able to extract the values to datapoints.
veg_qc2 <- projectRaster(veg_qc,crs="+proj=longlat +ellps=WGS84 +no_defs")
That single line took ~12 hours to run and took over 200 GB of Temp data. Worst, there was a warning (sorry did not copy it) and only half the raster showed.
So I decided to try with the function Wrap in QGIS. Although it worked perfectly, the output raster was 16 Gb! The original clipped raster was only 693 MB.
To make things worse, I need the value layer to be included in R, so I used:
veg_qc <- getValues(veg_qc)
And I get the following error:
Error: cannot allocate vector of size 31.0 Gb
Why does the raster get bigger when reprojected?
Would there be a way to compress the raster or reproject without that giant increase of data?
How can I add the values to a big raster layer?
Ultimately, I could clip the raster further with the mcp of my data. I could also reproject my data and my other rasters in EPSG:3978 (although I am wondering if my other rasters my end up as > 10GB too).
I just had the same issue this week. After a reprojection, from 40Mb to 350Mb. I found the answer in this link Huge file size after averaging two rasters.
What I did in QGIS (not R by this time) is that I opened the following function in the Manu bar: raster > conversion > translate, and in Advanced parameters > Additional command-line parameters, I added -co COMPRESS=LZW. This will compress using the LZW method. Also, according to this link and considering your dataset, you can use PACKBITS. I hope this helps you.

How to get raster file from a nested raster list produced by landscapemetrics package in R?

Package landscapemetrics can calculate area of each patch for a given raster file, shape of that patch and so on. I want to have not only tibble-frame with patch metrics calculated, but a new raster where each pixel within specific patch will have a value of the area of that patch, shape indicator and so on. We can do it with function spatialize_lsm() (it produces a Large list nested object with probably RasterObject objects within):
library(landscapemetrics)
plot(podlasie_ccilc) # this raster data is provided with package
podlasie.metrics.area <- spatialize_lsm(podlasie_ccilc, what = 'lsm_p_area') # creates a list
plot(podlasie.metrics.area) # produces an error...
How to get a desirable raster file with patch metrics from that list? I guess it is a question of raster package or something else, since landscapemetrics documentation tells nothing about this step.
I not that this data and new raster do not have resolution of the pixel like in meters (30, 30 for Landsat satellite image, for example). So we cannot plot the new raster produced:
podlasie.metrics.area[[1]]
plot(podlasie.metrics.area[[1]])
So I guess landscapemetrics cannot deal with such rasters, we can even use its function to check a suitability of the prior raster for patch discovering:
check_landscape(podlasie_ccilc)
Upd. I did it for the Landsat dataset with resolution 30, 30 and it produced patch area raster, but again I cannot open/show/save as raster it, because of the same error.
Package maintainer helps to solve a problem (yes, it is just related to the structure of list):
plot(podlasie.metrics.area[[1]]$lsm_p_area)

writeRaster function in R is automatically setting (unwanted) maximum value, can I set the max value to null?

I am running into a problem with the "writeRaster" function in the raster package in R. I am importing a raster (TIF) that I made in ArcGIS (a distance to feature raster).
My goal was to resample the distance raster to the correct resolution and extent, then "mask" it with the appropriate raster to crop it to the shape I require. When I check the results of the mask with the basic plot function, everything looks great and I can see that each pixel in the new masked raster has a distance value.
However, when I write this raster to a file using the writeRaster function, the resulting raster looks like "swiss cheese" and has missing values for any distance over 35km. After much reading, I cannot find any documentation to suggest that there is a way to modify the maximum value set by writeRaster---or that it should even be setting a max value. I have included my code and the basic plots below. A big thank you to anyone who attempts to help me with this!
#Read in distance to fresh water raster
distFW <- raster("D:/Academia/Arc Data/Grackle/NicaCR_90mlayers/dist_FW.tif")
[plot(distFW)][1]
#Resample this layer to the desired resolution and template
NiCR_DistFW<-as.integer(resample(distFW,NiCRrast.tmpl,method="ngb"))
#essentially the same as the first plot
[plot(NiCR_DistFW)][2]
#Mask the resampled raster to the desired shape
NiCR.DistFW.mask.utm <- mask(NiCR_DistFW,NiCR_Mask) #with CA countries cut out.
[plot(NiCR.DistFW.mask.utm)][3]
#write raster to file (this is where things get weird)
writeRaster(x=NiCR.DistFW.mask.utm, filename='DistFWmask2.tif', format='GTiff', datatype='INT2S') #a way to ensure INT2S
#read the newly written raster file in to R so we can review it
dFW <-raster("DistFWMask2.tif")
[plot(dFW)_writeRaster_result][4]
[1]: https://i.stack.imgur.com/v9RkK.jpg
[2]: https://i.stack.imgur.com/v2DG3.jpg
[3]: https://i.stack.imgur.com/cCwJe.jpg
[4]: https://i.stack.imgur.com/MjWj7.jpg
As you can see from plot 4, an undesirable max value has been set. I was the raster I write to file to look like the one in plot 3, not plot 4.
Thanks in advance for any advice.
Well friends, after taking an hour to detail my question I managed to figure out the answer myself. It had to do with setting the datatype.
INT2S has a maximum value of 32,767
by switching it to INT4S, I capture the full range of values in my raster.
Problem solved!

Issue with coordinate projection for detecting spatial autocorrelation in R

We have a dataset that contains latitude and longitude coordinates, as well as attribute information, each in its own separate column, stored as numeric. These coordinates have been geocoded based on the geographic coordinate system WGS 1984.
We know that we have significant spatial autocorrelation in our data, which we are hoping to visualize in a bubble plot using the “sp” package. We are modeling our example off of others online, such as here: https://beckmw.wordpress.com/2013/01/07/breaking-the-rules-with-spatial-correlation/ . However, when we try to use the coordinates command within "sp", we keep getting an error message:
Code example:
coords <- data.frame(lead$X, lead$Y)
coordinates(coords) <- c("lead6.X","lead6.Y")
Error in if (nchar(projargs) == 0) projargs <- as.character(NA) missing value where TRUE/FALSE needed
We can't load our direct code because it's sensitive and hosted on a virtual environment without access to the internet. Does anyone have ideas for why this might be happening? We've looked into the proj4 package but can't figure out how to specify a projection system (or is that even the error that we are getting?). If anyone knows of any other packages in R or ways to visualize spatial autocorrelation, those would be much appreciated too.
Your code is a bit "strange": seems you are trying to build a dataset containing only coordinates. AFAIU, you may need something in this line :
data <- data.frame(lead$X, lead$Y, lead$Z)
,with lead$Z corresponding to a generic "variable" you want to inspect, then
coordinates(data) <- c('X','Y')`
proj4string(data) <- "+init=epsg:4326"
, which should give you a proper "SpatialPointsDataframe" with lat-lon WGS84 geographic coordinates (the first line could be also dropped, and you'll keep all variables in the data of the spatialpointsdataframe).
HTH

where could we get such a landscape GIS layer

Here, I found a landscape GIS layer is really attractive, especially for presenting species/samples distributions. I would like to know if it can be reached in R or any other resources?
The GIS layer were used in Fig 1. in this article (http://onlinelibrary.wiley.com/doi/10.1111/j.1469-8137.2010.03479.x/full).
This Fig 1 image is here:
http://onlinelibrary.wiley.com/store/10.1111/j.1469-8137.2010.03479.x/asset/image_t/NPH_3479_f1_thumb.gif?v=1&t=gsk5sbhs&s=e5e2e4bbb194f799f7ab9bec85a416e295405784
I have ever tried to submit this question in R-sig-geo. But, I failed. I expect to get some helps/directions here.
Thanks a lots for any directions.
Best wishes,
It is very possible to download this file and read it in with R, configure it to have the correct geo-coordinates so that overplotting works easily, and showing the image with the right colour scheme and so on. But, automating getting all of the data you need is not so easy.
You need the colour table from the GIF file so that you can plot the correct set of RGB values for each pixel (the information is in the file, but I'm not sure if this can be obtained directly with R, I will check - it certainly can be with GDAL, but extracting those values in an automated way depends on various tools being available).
UPDATE: It turns out that the raster package gets hold of the colour information correctly and plots it, see below.
You also need the geo-spatial information, i.e. the coordinates of a reference pixel (say, the top left pixel corner), and the scale (the geographic width and height of the pixels) and this information is not stored in the file. Also, the coordinate system of the file is not in the file, and very likely not provided explicitly with the image data.
If the colours and the coordinate system were stored with the file, then it would all be easy and something like the following would be enough.
(Note this worked for me once, but then I think subsequent requests are blocked by the server, so try to only download the file one time).
u <- "http://onlinelibrary.wiley.com/store/10.1111/j.1469-8137.2010.03479.x/asset/image_n/NPH_3479_f1.gif?v=1&t=gskxvi17&s=0f13fa9dae78bd6837aeee594065c6ca112864d2"
imfile <- paste(tempfile(), ".gif", sep = "")
download.file(u, imfile, mode = "wb")
library(raster) ## rgdal also required for this file format
library(rgdal)
im <- raster(imfile)
plot(im)
This looks fine but now see that there is no "real-world" coordinate system, this is just an axis from pixel 1 to the number in the X dimension (and same for Y).
axis(1, pos = 2)
So, still we need manually work to discover appropriate reference coordinates for the image - and guesses here can work fine, but still they are only guesses and you may end up creating a lot of pain for something seemingly simple.
If plot points interactively is enough for you, then you might use locator in conjunction with points and lines and text, and related plotting functions.
Feng,
if I read the Google docs correctly, you can modify the labels and displayed features with the extra parameters style and element.
I did not include custom parameters for these in the RgoogleMaps package, however, you can easily pass ANY addition parameters via the path argument !
If you read the help file for GetMap carefully, you will note the following example:
note that since the path string is just appended to the URL you can "abuse" the path argument to pass anything to the query, e.g. the style parameter:
#The following example displays a map of Brooklyn where local roads have been changed to bright green and the residential areas have been changed to black:
## Not run: GetMap(center='Brooklyn', zoom=12, maptype = "roadmap", path = "&style=feature:road.local|element:geometry|hue:0x00ff00|saturation:100&style=feature:landscape|element:geometry|lightness:-100", sensor='false', destfile = "MyTile4.png", RETURNIMAGE = FALSE);
Hope this helps,
Markus Loecher
If you just want data like this image, then there are packages to access imagery directly, again utilizing the tools in sp and rgdal. This example is close using gmap in the dismo package.
library(dismo)
e <- extent(-7, 5, 38, 44)
gm <- gmap(e, type = "terrain")
plot(gm)
Note that while we specify the extents in "longlat" the image comes back in its native (Google) Mercator.
print(gm)
See ?gmap for more options on transforming your own data to match the image's projection, or the broader function set in raster, rgdal and sp for other options. There are other imagery providers that might be preferable, and quite a few options in the R suite of contributed packages.

Resources