Shapefile and Raster with same CRS but NO output when clipping - r

Problem: Plot is empty, no results when clipping raster with shapefile.
I am using a shapefile with original EPSG4326 projection and a MODIS product with original sinusoidal projection. I converted both to the same projection (DesiredCRS) as you can see in the script, however when making a clip of the raster I don't get any results.
library(terra)
library(sf)
library(sp)
library(raster)
# Inputs
HDFfile <- "ModisProductsOriginal/MCD18A1.A2001001.h15v05.061.2020097222704.hdf"
Shapefile <- "Shapefile/Outline_5/Portugal_Outline_5_CAOP2019.shp"
DesiredCRS <- "+proj=longlat +datum=WGS84"
# Feature
Shp <- st_read(Shapefile)
Terceira <- Shp[Shp$DI == '43',1]
Terceira <- st_transform(Terceira, DesiredCRS)
plot(Terceira, axes=TRUE)
Terceira's plot:
Plot Output
# Modis data
SDSs <- sds(HDFfile)
SDS8 <- SDSs[8]
SDS8_template <- rast(ncol=1200, nrow=1200, xmin=-34, xmax=-24, ymin=36, ymax=41, crs=DesiredCRS)
SDS8_reprojected <- project(SDS8, SDS8_template) # Reproject changes pixels?
SDS8_raster <- raster(SDS8_reprojected)
plot(SDS8_raster)
SDS8_raster's plot: Plot Output
# Clip
Clip.step1 <- crop(SDS8_raster, extent(Terceira))
Clip <- mask(Clip.step1, Terceira)
plot(Clip)
Clip.step1's plot: Plot Output
Clip's plot: Plot Output
All images show axes with the same projection. I don't understand what I'm doing wrong... Am I correctly defining the CRS and the extent?
Updated:
Panoply view of original shapefile and original HDF product:
Panoply Output
Plot of the two datasets together by Hijmans: Plot Output

This is but tricky to debug because you mix different packages, and you do not show(object). Anyway, here is a terra approach, showing where it would be useful to see the objects metadata; and a plot that shows the raster and vector data together.
library(terra)
HDFfile <- "MCD18A1.A2001001.h15v05.061.2020097222704.hdf"
Shapefile <- "Portugal_Outline_5_CAOP2019.shp"
DesiredCRS <- "+proj=longlat +datum=WGS84"
Shp <- vect(Shapefile)
Terceira <- Shp[Shp$DI == '43',1]
SDSs <- sds(HDFfile)
SDS8 <- SDSs[8]
Start by projecting the vector data to the original raster data.
TercSin <- project(Terceira, crs(SDS8))
plot(SDS8, 1)
lines(TercSin)
As you saw, that does not work. The reason is that GDAL/PROJ reads from file or assumes the wrong ellipsoid
cat(substr(crs(SDS8),1,230), "\n")
#PROJCRS["unnamed",
# BASEGEOGCRS["Unknown datum based upon the Clarke 1866 ellipsoid",
# DATUM["Not specified (based on Clarke 1866 spheroid)",
# ELLIPSOID["Clarke 1866",6378206.4,294.978698213898,
Or like this
describe("HDF4_EOS:EOS_GRID:\"MCD18A1.A2001001.h15v05.061.2020097222704.hdf\":MODISRAD:GMT_1200_DSR")[1:8]
#[1] "Driver: HDF4Image/HDF4 Dataset"
#[2] "Files: MCD18A1.A2001001.h15v05.061.2020097222704.hdf"
#[3] "Size is 1200, 1200"
#[4] "Coordinate System is:"
#[5] "PROJCRS[\"unnamed\","
#[6] " BASEGEOGCRS[\"Unknown datum based upon the Clarke 1866 ellipsoid\","
#[7] " DATUM[\"Not specified (based on Clarke 1866 spheroid)\","
#[8] " ELLIPSOID[\"Clarke 1866\",6378206.4,294.978698213898,"
As you point out (and see here), MODIS (or at least some products) uses
modcrs <- "+proj=sinu +lon_0=0 +x_0=0 +y_0=0 +a=6371007.181 +b=6371007.181 +units=m"
So let's try that
TercSin <- project(Terceira, modcrs)
plot(SDS8, 1)
lines(TercSin)
That looks OK. So we need to do
crs(SDS8) <- modcrs
and continue
Terceira <- project(Terceira, DesiredCRS)
Terceira
SDS8_template <- rast(ncol=1200, nrow=1200, xmin=-34, xmax=-24, ymin=36, ymax=41, crs=DesiredCRS)
SDS8_reprojected <- project(SDS8, SDS8_template)
SDS8_reprojected
# Again plot the two datasets together
plot(SDS8_reprojected)
lines(Terceira)
Clip.step1 <- crop(SDS8_reprojected, Terceira)
Clip <- mask(Clip.step1, Terceira)
Clip
plot(Clip)
lines(Terceira)

Related

select points over land area in R

I have a point vector for the entire globe at 0.25 degree resolution.
library(terra)
library(rnaturalearth)
# create point data
ref_grid <- terra::ext(-180, 180, -90, 90)
ref_grid <- terra::rast(ref_grid)
res(ref_grid)<- 0.25
values(ref_grid)<-1 #dummy values
projections <- "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0"
crs(ref_grid) <- projections
pts <- terra::as.points(ref_grid, values=TRUE, na.rm=TRUE)
# download world shapefile
world_shp <- rnaturalearth::ne_countries()
world_shp <- terra::vect(world_shp)
world_shp <- terra::project(world_shp, projections)
I want to extract those points that fall within a landmass i.e. remove all those points that are in the ocean. So I did this:
e <- terra::extract(world_shp, pts)
But it's been two hours now and this is still running. Is there any faster way to do this?
You should not transform raster data to vector data if you want efficiency. In this case, you can do the following in a second or so:
library(terra)
library(rnaturalearth)
ref_grid <- terra::rast(res=0.25)
world_shp <- rnaturalearth::ne_countries(returnclass="sf") |> vect()
ref_grid <- rasterize(world_shp, ref_grid)
p <- as.points(ref_grid)
(but perhaps, depending on what you do next, the last step should also be avoided)

Counting number of points on a raster layer in R

I've got a map with certain number of points on it. I want to (1) calculate the number of points that fall within the raster layer, and (2) extract these points to a data frame.
This is what I've done:
# Packages
library(raster)
library(ggplot2)
library(maptools)
library(tidyverse)
library(dplyr)
library(sp)
# Transform tree ring kml to dataframe
zz<-getKMLcoordinates('treering.kml', ignoreAltitude=TRUE)
l<-as.data.frame(zz)
l<-t(l)
tree <-SpatialPointsDataFrame(l, l,
proj4string = CRS(" +proj=longlat +ellps=WGS84 +datum=WGS84
+no_defs +towgs84=0,0,0"))
# Get world map
data(wrld_simpl)
# Transform World to raster
r <- raster(wrld_simpl, res = 1)
wrld_r <- rasterize(wrld_simpl, r)
# Import permafrost layer to raster
dist1<-raster("PZI.flt")
# Set CRS
dist1 <- projectRaster(from = dist1, crs = CRS("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
+towgs84=0,0,0"))
# Change colours
micolor <- rev(rainbow(12, alpha = 0.35))
transp <- rainbow(12, alpha = 0)
micolor[1:3] <- transp[1]
# Plot all
plot(wrld_r, col = "lightgrey")
plot(dist1, add=TRUE, legend = F, col = micolor)
plot(tree, add=T, pch = 20, col='black', cex=0.2)
I want to calculate and extract black points located on the colorful parts of this map
First raster::projectRaster does not "set" the projection but, rather reprojects the raster given a transformation and resampling. Given the computational requirements of this it is much faster to reproject the point data using sp::spTransform. Once your data is in the same projection space, you can use raster::extract to extract the raster values. Values out side the raster or in nodata (NA) areas will be assigned NA values. You can drop these observations using a simple NA index with which.
It looks like your data may have a constant value outside of the permafrost. Once you identify what this value is (eg., 0) you can remove these points as well. Here is a worked example. First we add packages and create some example data that is similar to yours.
library(sp)
library(raster)
dist1 <- raster(nrow=20, ncol=20)
dist1[] <- sample(1:10, ncell(dist1), replace=TRUE)
dist1[200:400] <- 0
trees <- sampleRandom(dist1, 100, sp=TRUE)
plot(dist1)
plot(trees,pch=20,col="red",add=TRUE)
Now, we extract the raster values and look at the dimensions of the point object (please note that I do not have to use the sp=TRUE argument in the raster::extract function).
trees#data <- data.frame(trees#data, dist1 = extract(dist1, trees))
dim(trees)
Now we create a row index indicating which rows contain zeros, make sure that we have identified rows (using an if statement) and then remove them. Looking at the object dimensions again, we can see how many points were removed from the original point data.
( idx <- which(trees$dist1 %in% 0) )
if(length(idx) > 0) trees <- trees[-idx,]
dim(trees)

Plotting a raster with recentered Mollweide projection in R

I would like to generate a map of the world in Mollweide projection, but centered on longitude 150. I would like to plot a raster and a vector map of the world on top.
library(maptools)
library(raster)
library(rgdal)
# generate some dummy data
data(wrld_simpl)
ras <- raster(ext=extent(wrld_simpl), res=c(2,2))
values(ras) <- runif(ncell(ras))
ras <- mask(ras, wrld_simpl, inverse=TRUE)
# Here is the map unprojected, without recentering
plot(ras)
plot(wrld_simpl, add=TRUE, col='black')
# now I transform to Mollweide
mollproj <- '+proj=moll +lon_0=150 +ellps=WGS84'
# project raster
ext <- projectExtent(ras, mollproj)
rasMoll <- projectRaster(ras, to=ext)
# project vector map
wrldMoll <- spTransform(wrld_simpl, CRS(mollproj))
# plot
plot(rasMoll)
plot(wrldMoll, add=TRUE)
There are several problems here. The map is not at full extent, the vector map has horizontal lines, and there are floating pieces of the raster beyond the bounds of the world.
Any suggestions on how to get this to work?
Thanks!

Clipping raster using shapefile in R, but keeping the geometry of the shapefile

I am using {raster} to clip (or crop) a raster based on an irregular shapefile (the Amazon biome) but the output always has a rectangular extent. However, I need the output in the exact same geometry of the shapefile. Any tips? Cheers.
library(raster)
library(rgdal)
myshp <- readOGR("Amazon.shp", layer="Amazon")
e <- extent(myshp)
myraster <- raster("Temperature.tif")
myraster.crop <- crop(myraster, e, snap="out", filename="myoutput.tif")
One option is to use raster::mask()
library(maptools) ## For wrld_simpl
library(raster)
## Example SpatialPolygonsDataFrame
data(wrld_simpl)
SPDF <- subset(wrld_simpl, NAME=="Brazil")
## Example RasterLayer
r <- raster(nrow=1e3, ncol=1e3, crs=proj4string(SPDF))
r[] <- 1:length(r)
## crop and mask
r2 <- crop(r, extent(SPDF))
r3 <- mask(r2, SPDF)
## Check that it worked
plot(r3)
plot(SPDF, add=TRUE, lwd=2)
Package terra:: made it simpler, you can ::crop and mask in the same step.
# Load packages
library(maptools) # For geometry
library(terra) # Perform the crop and mask
###--- Preparing polygon and raster ---###
# Example SpatialPolygonsDataFrame
data(wrld_simpl)
polygon <- subset(wrld_simpl, NAME=="Luxembourg")
plot(polygon) # have a look
# Convert from SpatialPolygonsDataFrame to SpatVector (terra package format)
# And create a smaller polygon with buffer (negative to be "inside")
polygon_bf <- buffer(vect(polygon), width= -100000)
plot(polygon_bf, add= T) # have a look on both
# Create a SpatRaster from a file
f <- system.file("ex/elev.tif", package="terra")
r_lux <- rast(f)
plot(r_lux) # have a look on SpatRaster (terra package format)
# See the steps with plot
plot(polygon, add= T)
plot(polygon_bf, add= T)
Click to see intermediate steps (all files)
########################################################
### Crop and mask by any polygon ###
raster_cp <- crop(r_lux, polygon_bf, mask= T)
# Note: if mask= F, the crop will be by extent (box) ###
########################################################
### Check the results
plot(raster_cp)
plot(polygon_bf, lwd=1, add=T)
Click to see the final output
In addition, (with raster package) in case you want to perform with a simple geometry (i.g. box), the coordinates of the extent can be place directly:
e <- as(extent(c(xmin= -16, xmax= -7.25, ymin= 4, ymax= 12.75)), 'SpatialPolygons')
crs(e) <- "+proj=longlat +datum=WGS84 +no_defs"
r <- crop(my_raster, e)

Plot spatial area defined by multiple polygons

I have a SpatialPolygonsDataFrame with 11589 spatial objects of class "polygons". 10699 of those objects consists of exactly 1 polygon. However, the rest of those spatial objects consist of multiple polygons (2 to 22).
If an object of consists of multiple polygons, three scenarios are possible:
1) The additional polygons could describe a "hole" in the spatial area described by the first polygon .
2) The additional polygons could also describe additional geographic areas, i.e. the shape of the region is quite complex and described by putting together multiple parts.
3) Often it is a mix of both, 1) and 2).
My question is: How to plot such a spatial object which is based on multiple polygons?
I have been able to extract and plot the information of the first polygon, but I have not figured out how plot all polygons of such a complex spatial object at once.
Below you find my code. The problem is the 15th last line.
# Load packages
# ---------------------------------------------------------------------------
library(maptools)
library(rgdal)
library(ggmap)
library(rgeos)
# Get data
# ---------------------------------------------------------------------------
# Download shape information from the internet
URL <- "http://www.geodatenzentrum.de/auftrag1/archiv/vektor/vg250_ebenen/2012/vg250_2012-01-01.utm32s.shape.ebenen.zip"
td <- tempdir()
setwd(td)
temp <- tempfile(fileext = ".zip")
download.file(URL, temp)
unzip(temp)
# Get shape file
shp <- file.path(tempdir(),"vg250_0101.utm32s.shape.ebenen/vg250_ebenen/vg250_gem.shp")
# Read in shape file
x <- readShapeSpatial(shp, proj4string = CRS("+init=epsg:25832"))
# Transform the geocoding from UTM to Longitude/Latitude
x <- spTransform(x, CRS("+proj=longlat +datum=WGS84"))
# Extract relevant information
att <- attributes(x)
Poly <- att$polygons
# Pick an geographic area which consists of multiple polygons
# ---------------------------------------------------------------------------
# Output a frequency table of areas with N polygons
order.of.polygons.in.shp <- sapply(x#polygons, function(x) x#plotOrder)
length.vector <- unlist(lapply(order.of.polygons.in.shp, length))
table(length.vector)
# Get geographic area with the most polygons
polygon.with.max.polygons <- which(length.vector==max(length.vector))
# Check polygon
# x#polygons[polygon.with.max.polygons]
# Get shape for the geographic area with the most polygons
### HERE IS THE PROBLEM ###
### ONLY information for the first polygon is extracted ###
Poly.coords <- data.frame(slot(Poly[[polygon.with.max.polygons ]]#Polygons[[1]], "coords"))
# Plot
# ---------------------------------------------------------------------------
## Calculate centroid for the first polygon of the specified area
coordinates(Poly.coords) <- ~X1+X2
proj4string(Poly.coords) <- CRS("+proj=longlat +datum=WGS84")
center <- gCentroid(Poly.coords)
# Download a map which is centered around this centroid
al1 = get_map(location = c(lon=center#coords[1], lat=center#coords[2]), zoom = 10, maptype = 'roadmap')
# Plot map
ggmap(al1) +
geom_path(data=as.data.frame(Poly.coords), aes(x=X1, y=X2))
I may be misinterpreting your question, but it's possible that you are making this much harder than necessary.
(Note: I had trouble dealing with the .zip file in R, so I just downloaded and unzipped it in the OS).
library(rgdal)
library(ggplot2)
setwd("< directory with shapefiles >")
map <- readOGR(dsn=".", layer="vg250_gem", p4s="+init=epsg:25832")
map <- spTransform(map, CRS("+proj=longlat +datum=WGS84"))
nPolys <- sapply(map#polygons, function(x)length(x#Polygons))
region <- map[which(nPolys==max(nPolys)),]
plot(region, col="lightgreen")
# using ggplot...
region.df <- fortify(region)
ggplot(region.df, aes(x=long,y=lat,group=group))+
geom_polygon(fill="lightgreen")+
geom_path(colour="grey50")+
coord_fixed()
Note that ggplot does not deal with the holes properly: geom_path(...) works fine, but geom_polygon(...) fills the holes. I've had this problem before (see this question), and based on the lack of response it may be a bug in ggplot. Since you are not using geom_polygon(...), this does not affect you...
In your code above, you would replace the line:
ggmap(al1) + geom_path(data=as.data.frame(Poly.coords), aes(x=X1, y=X2))
with:
ggmap(al1) + geom_path(data=region.df, aes(x=long,y=lat,group=group))

Resources