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

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)

Related

Applying a mask to a spatial raster in 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)

Reprojecting & CRS of Shapefile using sf Package in R

I am a new R user. I have a shapfile in R which I want to use to calculate the K nearest neighbor. Each polygon's centroid (Inside_X & Inside_Y) was calculated in arcmap (Example: X: 32570914 Y: 5791442). I used the sf package to get a CRS for my coordinates. Ideally, I would like to use the proj of my shapefile but it seems invalid in R. Below is my code. My problem is that the knearneighbor considers knearneigh: coordinates are not geographical and st_is_longlat gives[FALSE] even after setting a CRS. Please help.
>data<- readOGR (dsn = ".", layer ="My_Data")
>summary(data)
Object of class SpatialPolygonsDataFrame
Coordinates:
min max
x 32568756 32641426
y 5723944 5853056
Is projected: TRUE
proj4string :
[+proj=tmerc +lat_0=0 +lon_0=9 +k=0.9996 +x_0=32500000 +y_0=0 +ellps=GRS80 +units=m +no_defs]
Data attributes:
>data.sf <- st_as_sf(data)
## Set CRS
>data.crs <- st_transform(data.sf, crs = 4839)
>st_crs(data.crs)
data.coord <- cbind(data.crs$INSIDE_X, data.crs$INSIDE_Y)
## K Nearest neighbor
>library(spdep)
>data.knn <- knearneigh(data.coord, k = 10, longlat = TRUE)
Warning message:
In knearneigh(data.coord, k = 10, longlat = TRUE) :
knearneigh: coordinates are not geographical: longlat argument wrong
Use EPSG 4326 instead of 4839, 4839 is project https://epsg.io/4839

Error with Plotting Cartogram - 'This function does not give correct centroids and distances for longitude/latitude data.'

I'm trying to use the wrld_simple data from the maptools package to plot a Cartogram that involves some European countries based on their Population in 2005:
countries = c('Austria', 'Belgium', 'Bulgaria', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Germany', 'Greece', 'Hungary', 'Iceland', 'Ireland', 'Italy', 'Latvia', 'Lithuania', 'Luxembourg', 'Malta', 'Netherlands', 'Norway', 'Poland', 'Portugal', 'Slovenia', 'Spain', 'Sweden', 'United Kingdom')
Using the maptools package, I got the wrld_simpl data, and retrieved the data for those particular countries:
data(wrld_simpl)
mapsimple = wrld_simpl[wrld_simpl$NAME %in% countries]
plot(mapsimple)
So far so good, but when I plot it out:
cartogram= cartogram(mapping1, "POP2005", itermax = 7)
Please use cartogram_cont() instead of cartogram().
Error: Using an unprojected map. This function does not give correct centroids and distances for longitude/latitude data:
Use "st_transform()" to transform coordinates to another projection.
I try using cartogram_cont:
> cartogram= cartogram_cont(mapping1, "POP2005", itermax = 7)
Error: Using an unprojected map. This function does not give correct centroids and distances for longitude/latitude data:
Use "st_transform()" to transform coordinates to another projection.
I tried transforming it to sf and then doing the st_transform(), but to no avail:
sfno = st_as_sf(mapping1)
sfnoproj = st_transform(sfno, coords = c("lon", "lat"), crs = "+proj=longlat +datum=WGS84", agr = "constant")
cartogram= cartogram_cont(sfnoproj, "POP2005", itermax = 7)
Error: Using an unprojected map. This function does not give correct centroids and distances for longitude/latitude data:
Use "st_transform()" to transform coordinates to another projection.
I'm assuming I don't have the correct values for the projection right. How would I know to set it better? If I were to use other values besides 'POP2005', how do I go about it? What does the 'itermax' in cartogram mean (I don't quite understand the given definition)
Any help would be greatly appreciated as I'm quite new to this. Thank you!!!!
Some inconsistencies in your sample data (mapsimple v. mapping1 and a missing comma), so updated below:
library(maptools)
library(sf)
library(ggplot2)
countries = c('Austria', 'Belgium', 'Bulgaria', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Germany', 'Greece', 'Hungary', 'Iceland', 'Ireland', 'Italy', 'Latvia', 'Lithuania', 'Luxembourg', 'Malta', 'Netherlands', 'Norway', 'Poland', 'Portugal', 'Slovenia', 'Spain', 'Sweden', 'United Kingdom')
data(wrld_simpl)
mapsimple = wrld_simpl[wrld_simpl$NAME %in% countries,]
As the error indicated, your data was unprojected. It uses latitude/longitude, which doesn't work for cartogram_cont().
sfno <- st_as_sf(mapsimple)
st_crs(sfno)
Coordinate Reference System:
EPSG: 4326
proj4string: "+proj=longlat +datum=WGS84 +no_defs"
Your st_transform() was reprojecting sfno as the same thing: EPSG 4326:
sfnoproj <- st_transform(sfno, coords = c("lon", "lat"),
crs = "+proj=longlat +datum=WGS84",
agr = "constant")
st_crs(sfnoproj)
Coordinate Reference System:
EPSG: 4326
proj4string: "+proj=longlat +datum=WGS84 +no_defs"
Instead, choose a projected coordinate system for Europe, like 23038.
sfproj <- st_transform(sfno, crs = 23038)
st_crs(sfproj)
Coordinate Reference System:
EPSG: 23038
proj4string: "+proj=utm +zone=38 +ellps=intl +towgs84=-87,-98,-121,0,0,0,0 +units=m +no_defs"
Then, we can make and plot the cartogram:
cartogram <- cartogram_cont(sfproj, "POP2005", itermax = 7)
ggplot() +
geom_sf(data = cartogram, aes(fill = POP2005))

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" )

Resources