Applying a mask to a spatial raster in R? - r

I would like to mask my background map using a shapefile representing a regional boundary. To do this I have read a spatial raster into Rstudio using read_osm
library(sp)
library(tmaptools)
HB_map <- spData::nz %>%
filter(Name=="Hawke's Bay") %>%
tmaptools::read_osm(type = "stamen-terrain")
I have then imported my shapefile
libary(sf)
Regional_boundary <- sf::st_read("regional_boundary.shp")
sf::st_crs(Regional_boundary)= 2193
Regional_boundary_sf_poly <- sf::st_transform(Regional_boundary, 27200) %>%
sf::st_cast(to="POLYGON")
I have a number of GIS data sets so I re-project my raster so it is in the same projection as my GIS data (i'm not sure if this bit is right)
test_map <- projectRaster(HB_map, crs ="+init=epsg:27200")
I then check the data projections are consistent
crs(Regional_boundary_sf_poly)
[1] "+proj=nzmg +lat_0=-41 +lon_0=173 +x_0=2510000 +y_0=6023150 +ellps=intl +towgs84=59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993 +units=m +no_defs"
crs(test_map)
+init=epsg:27200 +proj=nzmg +lat_0=-41 +lon_0=173 +x_0=2510000 +y_0=6023150 +datum=nzgd49
+units=m +no_defs +ellps=intl +towgs84=59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993
Now I apply my mask:
Library(raster)
test_map_mask <- raster::mask(test,Regional_boundary_sf_poly,inverse=FALSE,updatevalue=NA, updadateNA=FALSE)
And check the results
tmap::qtm(test_map_mask)
It all seems to work except the map colours no longer look like the original "stamen-terrain" but are instead different shades of orange". How do adjust the settings to make the map look like the original but with the mask?
Thanks for you help.
Kind regards,
Simon

NEW ANSWER
Try to get the tiles with cartography::getTiles, it seems to work. Note that you should have sf v0.9-0 and cartography v2.4-0 installed, both recently updated on CRAN. A complete list of tiles serves available on getTileshere.
library(sf)
library(cartography)
library(dplyr)
nz <- spData::nz %>% st_transform(27200)
#Get tile
HB_map <- cartography::getTiles(nz, type = "Stamen.Terrain")
class(HB_map)
#> [1] "RasterBrick"
#> attr(,"package")
#> [1] "raster"
st_crs(HB_map)$proj4string
#> [1] "+proj=nzmg +lat_0=-41 +lon_0=173 +x_0=2510000 +y_0=6023150 +ellps=intl +towgs84=59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993 +units=m +no_defs "
st_crs(nz)$proj4string
#> [1] "+proj=nzmg +lat_0=-41 +lon_0=173 +x_0=2510000 +y_0=6023150 +ellps=intl +towgs84=59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993 +units=m +no_defs "
library(raster)
test_map_mask <-
raster::mask(
HB_map,
nz,
)
#tmap
tmap::qtm(test_map_mask)
#> Warning: replacing previous import 'sf::st_make_valid' by
#> 'lwgeom::st_make_valid' when loading 'tmap'
#> Numeric values of test_map_mask interpreted as RGB values with max.value = 255. Run tm_shape(test_map_mask) + tm_raster() to visualize the data.
Created on 2020-04-04 by the reprex package (v0.3.0)
OLD ANSWER
You may want to check getPngLayer and pngLayer from cartography, basically with this you create a png file as a raster masked with a sf and plot it.
If your problem is the plotting, pngLayer Is basically a wrapper of raster::plotRGB.
Extra ball is that you can get also Stamen Terrain using getTiles , also on cartography, but make sure you install the latest version (released today on CRAN).
Reprex:
library(sf)
library(cartography)
library(dplyr)
HB_map <- spData::nz %>% getTiles(type = "Stamen.Terrain")
class(HB_map)
#> [1] "RasterBrick"
#> attr(,"package")
#> [1] "raster"
Regional_boundary <- spData::nz
st_crs(HB_map)$proj4string
#> [1] "+proj=tmerc +lat_0=0 +lon_0=173 +k=0.9996 +x_0=1600000 +y_0=10000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs "
st_crs(Regional_boundary)$proj4string
#> [1] "+proj=tmerc +lat_0=0 +lon_0=173 +k=0.9996 +x_0=1600000 +y_0=10000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs "
library(raster)
test_map_mask <-
raster::mask(
HB_map,
Regional_boundary,
)
#> Warning: st_crs<- : replacing crs does not reproject data; use st_transform for
#> that
par(mar=c(0,0,0,0))
tilesLayer(test_map_mask)
plot(st_geometry(Regional_boundary), border = "red", lwd=2, add = TRUE)
Created on 2020-04-04 by the reprex package (v0.3.0)
Vignette: https://dieghernan.github.io/cartographyvignette/
Source code: https://github.com/riatelab/cartography/blob/master/R/tilesLayer.R

Thanks to the answer by dieghrnan I can now get some example data, and the below now runs fine for me
library(sf)
library(cartography)
library(raster)
library(spData)
nz <- spData::nz
HB <- cartography::getTiles(nz, type = "Stamen.Terrain")
HB_mask <- raster::mask(HB, nz)
plotRGB(HB_mask)
If you want a projection:
test <- projectRaster(HB_mask, crs ="+init=epsg:27200")
# or perhaps
# test <- projectRaster(HB_mask, method="ngb", crs ="+init=epsg:27200")
plotRGB(test)
old answer:
At what step do you loose the colors? my guess is at projectRaster? I cannot get rJava to work so I cannot run this, but I would try
colortable(test_map_mask) <- colortable(HB_map)
tmap::qtm(test_map_mask)

Related

How to apply a grid transformation in sf with PROJ7?

I try to apply a gridded transformation of coordinates in sf, but it does not work. Is there something else to do apart from calling sf_proj_network(TRUE)? Here is the code I used:
pt_27572 <- data.frame(X=55824.4970,Y=2394454.2120) # set coordinates of one point in EPSG:27572
pt_2154 <- data.frame(X=107242.8310,Y=6832277.1820) # known accurate coordinates of the same point in EPSG:2154
ptSf_27572 <- st_as_sf(pt_27572, coords = c("X", "Y"), crs = 27572) # build sf object
ptSf_2154 <- st_as_sf(pt_2154, coords = c("X", "Y"), crs = 2154) # build sf object
sf_proj_network(TRUE) # allow search for online datum grids in the PROJ CDN
[1] "https://cdn.proj.org"
sf_proj_pipelines("EPSG:27572", "EPSG:2154") # grids (fr_ign_gr3df97a.tif) seem to be found for accurate transformation from EPSG:27572 to EPSG:2154
Candidate coordinate operations found: 3
Strict containment: FALSE
Axis order auth compl: FALSE
Source: EPSG:27572
Target: EPSG:2154
Best instantiable operation has accuracy: 1 m
Description: Inverse of Lambert zone II + NTF (Paris) to NTF (1) + NTF to
RGF93 (1) + Lambert-93
Definition: +proj=pipeline +step +inv +proj=lcc +lat_1=46.8 +lat_0=46.8
+lon_0=0 +k_0=0.99987742 +x_0=600000 +y_0=2200000
+ellps=clrk80ign +pm=paris +step +proj=push +v_3
+step +proj=cart +ellps=clrk80ign +step
+proj=xyzgridshift +grids=fr_ign_gr3df97a.tif
+grid_ref=output_crs +ellps=GRS80 +step +inv
+proj=cart +ellps=GRS80 +step +proj=pop +v_3 +step
+proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44
+x_0=700000 +y_0=6600000 +ellps=GRS80
ptSf_2154_grid <- st_transform(ptSf_27572,crs=2154) # apply transformation
st_distance(ptSf_2154_grid,ptSf_2154) # incorrect (ungridded) transformation, the distance should be zero. 3.777 m is the known error for the ungridded transformation.
Units: [m]
[,1]
[1,] 3.777346
Thanks for your help.
See https://github.com/r-spatial/sf/issues/1815. The underlying reason is a PROJ regression for versions 7.2.1-8.1.1 fixed by https://github.com/OSGeo/PROJ/pull/2884.

Mapview in R and problem with projections/crs in Pacific area

I am trying to plot data in R using mapView for the grid in the Pacific that crosses the longitude 180deg. The native crs is "+proj=merc +lon_0=150 +lat_ts=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0".
The example of equivalent data is:
test.coords<-as.data.frame(cbind(c(runif(15,-180,-130),runif(5,160,180)),runif(20,40,60)))
test.sp <- SpatialPointsDataFrame(coords = cbind(test.coords$V1,test.coords$V2), data = test.coords,
proj4string = CRS("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0"))
test.sp <- spTransform(test.sp,
"+proj=merc +lon_0=150 +lat_ts=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0")
plot(test.sp)
mapview(test.sp)
When using plot function, the points are centered, while using mapView they get split between two sides of the map (linked image). Can the view using mapView be centered on a different longitude?
Using native crs for this map does not help. I get an error.
mapview(plot, native.crs=TRUE)
Warning message:
sf layer is not long-lat data
Thank you
mapView image Pacific
Ok, so this is a semi-optimal solution to your question for several reasons:
this will only work for point data
it may not scale well for large point collections
projecting to another CRS may not work anymore (though that is not really related, as the issue at hand is about visualising)
For sets of lines or polygons, we would need another approach, but I am currently not aware of a sp/sf native solution to round-tripping coordinates of an object.
library(sp)
library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.2.3, PROJ 4.9.3
library(mapview)
test.coords<-as.data.frame(cbind(c(runif(15,-180,-130),runif(5,160,180)),runif(20,40,60)))
test.sp <- SpatialPointsDataFrame(coords = cbind(test.coords$V1,test.coords$V2), data = test.coords,
proj4string = CRS("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0"))
test_sf = st_as_sf(test.sp)
shift = function(x) {
geom = st_geometry(x)
st_geometry(x) = st_sfc(
lapply(seq_along(geom), function(i) {
geom[[i]][1] = ifelse(geom[[i]][1] < 0, geom[[i]][1] + 360, geom[[i]][1])
return(geom[[i]])
})
, crs = st_crs(geom)
)
return(x)
}
mapview(shift(test_sf)) +
mapview(test_sf, col.regions = "orange")
Created on 2019-12-11 by the reprex package (v0.3.0)
I chose to use sf rather than sp for this because mapview uses sf internally and was hoping to find a general approach to this problem which could potentially be integrated.
Here is a workaround for polygons - it produces some warnings, but overall does the job.
# transform grid to standard latitude and longitude
# original grid in crs "+proj=merc +lon_0=150 +lat_ts=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0"
grid<-spTransform(grid,"+init=epsg:4326")
summary(coordinates(grid))
# V1 V2
# Min. :-179.8 Min. :37.17
# 1st Qu.:-168.8 1st Qu.:51.70
# Median :-154.3 Median :55.02
# Mean :-118.0 Mean :54.65
# 3rd Qu.:-131.4 3rd Qu.:58.31
# Max. : 179.8 Max. :65.56
out <- lapply(1:length(grid), function(i) grid[i, ])
for (i in 1:length(grid)) {
cds <- slot(slot(slot(out[[i]], "polygons")[[1]], "Polygons")[[1]], "coords")
cds_polygon <- Polygons(list(Polygon(cds)), ID="out.x")
out.x <- SpatialPolygons(list(cds_polygon))
proj4string(out.x) <- CRS("+init=epsg:4326")
if (cds[1,1]>0) { # depending to which side one want to move polygons that are on both sides of International Date Line
out.y <- elide(out.x, shift=c(-360,0))
} else {
out.y<-out.x}
if(i==1){grid.shifted<-out.y} else {
grid.shifted<-raster::union(grid.shifted,out.y)
}
}
# add data in the original grid
grid.shifted <- SpatialPolygonsDataFrame(grid.shifted, grid#data, match.ID = F)
mapview(grid.shifted)

Why is sf::st_transform() returning an object with a different projection than used in the call?

I want to re-project my sf object using sf::st_transform(), but the projection of the transformed object is not the same as the projection I specified in the transformation call. Why?
library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.2.3, PROJ 4.9.3
# target proj4string
my_crs <- "+proj=lcc +lat_1=40.66666666666666 +lat_2=41.03333333333333 +lat_0=40.16666666666666 +lon_0=-74 +x_0=300000.0000000001 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=us-ft +no_defs"
# create test sfc
p1 <- st_sfc(st_point(c(1,2)), crs = "+init=epsg:3857")
# re-project p1 to `my_crs`
p2 <- st_transform(p1, crs = my_crs)
all.equal(my_crs, st_crs(p2)$proj4string)
#> [1] "1 string mismatch"
st_crs(p2)
#> Coordinate Reference System:
#> No EPSG code
#> proj4string: "+proj=lcc +lat_1=40.66666666666666 +lat_2=41.03333333333333 +lat_0=40.16666666666666 +lon_0=-74 +x_0=300000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=us-ft +no_defs"
The only difference between the projections is the +x_0 element in the proj4string. The trailing 1e-10 has been removed in p2's projection.
sf::st_transform() uses GDAL for projections. The help page states:
Some PROJ.4 projections are not supported by GDAL, e.g. "+proj=wintri" because it does
not have an inverse projection. Projecting to unsupported projections can be done by
st_transform_proj, part of package lwgeom. Note that the unsupported proj4string cannot
be passed as argument to st_crs, but has to be given as character string.
If you use PROJ.4 for the projection in your example it works correctly:
library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.2.3, PROJ 4.9.3
library(lwgeom)
#> Linking to liblwgeom 2.5.0dev r16016, GEOS 3.6.1, PROJ 4.9.3
my_crs <- "+proj=lcc +lat_1=40.66666666666666 +lat_2=41.03333333333333 +lat_0=40.16666666666666 +lon_0=-74 +x_0=300000.0000000001 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=us-ft +no_defs"
p1 <- st_sfc(st_point(c(1,2)), crs = "+init=epsg:3857")
p2 <- lwgeom::st_transform_proj(p1, crs = my_crs)
all.equal(my_crs, st_crs(p2)$proj4string)
#> [1] TRUE
I don't know enough about cartography to say if the difference is due to the inverse projection issue mentioned in the help page or something else.
Related:
https://stackoverflow.com/a/51663647/1707525
https://github.com/r-spatial/sf/issues/810
https://github.com/r-spatial/sf/issues/1019

Converting from Behrmann CRS map to +proj=longlat WGS84 in R

I have a shapefile of grid cells of 200km x 200km covering land areas of the world in Behrmann Equal Area Cylindrical projection. My goal is to convert the shapefile to +proj=longlat WGS84 format so I can match it to maps in commonly used projections such as wrld_simpl in maptools. However, I have not been successful and would appreciate some help with this.
rm(list = ls())
library(RCurl)
library(raster)
library(maptools)
library(rgdal)
data("wrld_simpl")
tmp <- tempfile() download.file("https://github.com/darunabas/extras/blob/master/temp_shapefile.zip?raw=true", destfile = tmp)
unzip(tmp, exdir = ".")
s <- rgdal::readOGR("temp_shapefile")
proj4string(s) = CRS("+proj=cea +lon_0=0 +lat_ts=30 +x_0=0 +y_0=0 +datum=WGS84 +ellps=WGS84 +units=m +no_defs")
p <- spTransform(s, CRS("+proj=longlat +datum=WGS84"))
I got the following error:
non finite transformation detected:
[,1] [,2] [,3] [,4]
Error in .spTransform_Polygon(input[[i]], to_args = to_args, from_args = from_args, :
failure in Polygons 1106 Polygon 1 points
In addition: Warning message:
In .spTransform_Polygon(input[[i]], to_args = to_args, from_args = from_args, :
2 projected point(s) not finite
I'm not very experienced in the geo-worls, but this might help:
library( sf )
sf <- read_sf( "./temp_shapefile.shp")
st_crs( sf ) <- "+proj=cea +lon_0=0 +lat_ts=30 +x_0=0 +y_0=0 +datum=WGS84 +ellps=WGS84 +units=m +no_defs"
sf2 <- st_transform( sf, crs = "+proj=longlat +datum=WGS84" )

Converting UTM's to Lat/Long in R

I have a .csv file of 9,000+ UTM coordinates that I would like to convert into decimal degrees and I am having a bit of trouble. I have searched through several of the posts that have been posted here and elsewhere and I can't seem find a solution that transforms my set of UTM's into usable and accurate lat/long's.
I essentially have two questions: 1) does anyone see any issues with my code; and 2) is anyone familiar with forgoing transformation of UTM's into lat/long's and just using UTM's in the Rgooglemaps package?
Here are some examples of my code and data:
Data:
>head(utm)
-Northing Easting
1 4236576 615805
2 4236576 615805
3 4236576 615805
4 4236576 615805
5 4236576 615805
6 4236576 615805
Code thus far:
utm <- read.csv(file="utm.csv", header=TRUE, sep=",")
library(rgdal)
utm <- utm[complete.cases(utm),]
utm1 <- data.frame(x=utm$Northing,y=utm$Easting)
coordinates(utm1) <- ~x+y
class(utm1)
proj4string(utm1) <- CRS("+proj=utm +zone=10 +datum=WGS84 +units=m +ellps=WGS84")
utm2 <- spTransform(utm1,CRS("+proj=longlat +datum=WGS84"))
Results
> head(utm2)
SpatialPoints:
x y
[1,] -91.08516 4.727323
[2,] -91.08516 4.727323
[3,] -91.08516 4.727323
[4,] -91.08516 4.727323
[5,] -91.08516 4.727323
[6,] -91.08516 4.727323
Coordinate Reference System (CRS) arguments: +proj=longlat +datum=WGS84 +ellps=WGS84
+towgs84=0,0,0
So, I am getting some output, but I am not getting sensible output. Is there something I am missing here? Also, for what its worth, I was planning on using the "Rgooglemaps" package for creating some heat maps and kernel density plots.
I'm using the following code to convert from UTM to Lat/Long. It's working for the London area
wgs84 = "+init=epsg:4326"
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'
ConvertCoordinates <- function(easting,northing) {
out = cbind(easting,northing)
mask = !is.na(easting)
sp <- sp::spTransform(sp::SpatialPoints(list(easting[mask],northing[mask]),proj4string=sp::CRS(bng)),sp::CRS(wgs84))
out[mask,]=sp#coords
out
}
I think you just need to swap X and Y--Northing as Y, Easting as X. The original code worked for me with this change.

Resources