How to map raster correct projection in ggplot? - r

I want to plot USA raster using an Albers coordination,codes as follows:
#both shp_f and ras_f are WGS84 coordination
shp_f <- 'USA.shp'
ras_f <- 'USA.tif'
Albers <- crs('+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=23 +lon_0=-96
+x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs' )
shp <- readOGR(shp_f)
#convert raster cells as dataframe
ras_df <- ras_f %>% raster %>% rasterToPoints %>% as.data.frame
colnames(ras_df) <- c('x','y','val')
bm <- ggplot()+
geom_tile(data=ras_df,aes(x=x,y=y,fill=val))+
geom_polygon(data=shp,aes(x=long,y=lat,group=group),colour='grey',
fill=NA,linetype='solid',size=0.1)+
# convert plot coordination
ggalt::coord_proj(Albers)
This is a common Albers projection map with arc-shaped latitude lines, and
the xy labels are lon/lat degree. I want plot maps like it ,but it takes too much time to plot because convert each cell coordiantion in ggplot is very slow especially when the high raster resolution. So I consider that change the raster projection first ,then plot it. hence the second section codes as follows:
#convert shape polygon coordination from WGS84 to Albers
shp_albers <- spTransform(shp, Albers)
ras_df <- ras_f %>% raster
#convert the raster coordination from WGS84 to Albers
%>% projectRaster(., res=50000,crs=Albers)
%>% rasterToPoints %>% as.data.frame
colnames(ras_df) <- c('x','y','val')
bm <- ggplot()+
geom_tile(data=ras_df,aes(x=x,y=y,fill=val))+
geom_polygon(data=shp_albers,aes(x=long,y=lat,group=group),colour='grey',
fill=NA,linetype='solid',size=0.1)
But this is a Cartesian graphic that xy is orthogonal (not a albers shape map).
So the question is that:
In general ,how to plot a proper shape map with it's projeciton (not a simple orthogonal graphic)? Meanwhile how to change the xy labels to lon/lat degree and plot arc-shape lon/lat lines (not use orthogonal lines)? The first figure is expected,but it plot too slow.
The raster and shp file is here

Related

Create a spatial polygons data frame that preserves overlapping features with another spatial polygons data frame but does not clip polygon extent

I have been using the intersect() function from the raster package in R to clip a spatial polygons data frame (HUC-4 watersheds) to the extent of another spatial polygons data frame (a region consisting of Colorado, Idaho, Montana, Utah, and Wyoming).
I want to preserve the entire extent of the spatial polygons that overlap with the spatial data frame I am clipping to. Using intersect() clips the HUC-4 watersheds so that they do not extend past the extent of the states being clipped to.
The watershed data that I am using can be downloaded from: ftp://rockyftp.cr.usgs.gov/vdelivery/Datasets/Staged/Hydrography/WBD/National/GDB/ (WBD_National_GDB.zip).
The data for the region encompassing Colorado, Utah, Idaho, Wyoming, and Montana was extracted from county data available here: https://catalog.data.gov/dataset/tiger-line-shapefile-2017-nation-u-s-current-county-and-equivalent-national-shapefile.
The code I am using to do the clip with the intersect() function is as follows:
library(raster)
library(dplyr)
library(spdplyr)
library(rgdal)
library(rgeos)
# albers equal area projection
proj <- CRS("+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=37.5 +lon_0=-96 +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ")
counties <- readOGR(dsn = "./data/tl_2017_us_county/tl_2017_us_county.shp")
# filtering out only counties in our 5 states of interest
counties <- counties %>%
filter(STATEFP %in% c("08", "16", "30", "49", "56"))
# transforming to albers projection
counties <- spTransform(counties, proj)
# create a region shapefile (to clip watersheds with)
region <- gUnaryUnion(counties)
# Make Region into a SpatialPolygonsDataFrame
row.names(region) <- as.character(1:length(region))
region_data <- c("West")
region_data <- as.data.frame(region_data)
colnames(region_data) <- "Region"
region <- SpatialPolygonsDataFrame(region, region_data)
file <- "./data/WBD_National_GDB/WBD_National_GDB.gdb"
# huc4 watersheds
huc4 <- readOGR(dsn = file, layer = "WBDHU4")
# transforming to albers projection
huc4 <- spTransform(huc4, proj)
# selecting only huc4 watersheds that intersect with our states of interest
huc4_clip <- raster::intersect(huc4, region)
# plot the result
plot(huc4_clip)
I want an output file that does not clip the extent of the spatial polygons that are on the edge of the region of interest, but does not include any spatial polygons that do not directly overlap with the region of interest. Are there any other functions I can use that are similar to intersect() but that do not clip the extent of the spatial polygons on the region border?
If I understand the question properly, you could use function gIntersects to find out which watersheds intersect your region, and then extract only those from the huc4 dataset. In practice, something like this could work:
intersects <- which(gIntersects(huc4, region, byid = TRUE))
huc4_clip <- huc4[intersects, ]

Visual bug when changing Robinson projection's central meridian with ggplot2

I am attempting to project a world map in a Robinson projection where the central meridian is different from 0. According to this StackOverFlow thread, it should be an easy thing (albeit the example uses sp).
Here is my reproducible code:
library(sf)
library(ggplot2)
library(rnaturalearth)
world <- ne_countries(scale = 'small', returnclass = 'sf')
# Notice +lon_0=180 instead of 0
world_robinson <- st_transform(world, crs = '+proj=robin +lon_0=180 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs')
ggplot() +
geom_sf(data = world_robinson)
This is the result. Polygons are closing themselves from one side to the other of the projection.
Trying with sp gives the same effect. I also tried with a shapefile including only polygons from coastlines (no political borders) from http://www.naturalearthdata.com/ and the effect is similar.
I tried to run my snippet on two independent R installations on Mac OS X and Ubuntu 18.04.
Polygons that straddle the meridian line get stretched all the way across the map, after the transformation. One way to get around this is to split these polygons down the middle, so that all polygons are either completely to the west or east of the line.
# define a long & slim polygon that overlaps the meridian line & set its CRS to match
# that of world
polygon <- st_polygon(x = list(rbind(c(-0.0001, 90),
c(0, 90),
c(0, -90),
c(-0.0001, -90),
c(-0.0001, 90)))) %>%
st_sfc() %>%
st_set_crs(4326)
# modify world dataset to remove overlapping portions with world's polygons
world2 <- world %>% st_difference(polygon)
# perform transformation on modified version of world dataset
world_robinson <- st_transform(world2,
crs = '+proj=robin +lon_0=180 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs')
# plot
ggplot() +
geom_sf(data = world_robinson)
This is an extension to Z.lin's answer (i.e. use that answer first to calculate world_robinson). However, there is another useful step that can be added. After projecting, regions that were comprised of more than one polygon because they cross from one side of the map to the other in the original projection (see Antarctica, Fiji and Russia) still have this split after reprojection. For example, here is a close up of Antarctica where we can see that it has a boundary on the prime meridian where none should be:
To stitch these regions back togther, we can first find out which polygons are the problems by finding those that cross a the prime meridian:
bbox = st_bbox(world_robinson)
bbox[c(1,3)] = c(-1e-5,1e-5)
polygon2 <- st_as_sfc(bbox)
crosses = world_robinson %>%
st_intersects(polygon2) %>%
sapply(length) %>%
as.logical %>%
which
Now we can select those polygons and set their buffer size to zero:
library(magrittr)
world_robinson[crosses,] %<>%
st_buffer(0)
ggplot(world_robinson) + geom_sf()
As we can see, the map no longer has splits down the prime meridian:

Spatial polygons with messed up shape

I am new to spatial data. My goal is to get the gpx files from openstreetmap and plot polygons of Hungary's boundaries. I could successfully plot the boundaries under Járások but there are some boundaries under Kistérségek which are messed up, containing loops between points.
Image
Reproducable example for a specific boundary
library(XML)
library(magrittr)
library(sp)
parsed <- xmlParse("http://osmrm.openstreetmap.de/gpx.jsp?relation=1368104") %>% xmlToList()
coord <- do.call(rbind, parsed$rte)
name <- coord[1, 1]
coord <- coord[-(1:2), ]
coord <- apply(coord, 2, as.numeric)
poly <- Polygons(list(Polygon(coord)), name)
sp <- SpatialPolygons(list(poly), proj4string = CRS("+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0"))
plot(sp)
Result
Is the error because I missed something, or is it simply an issue with openstreetmap data? Any help is appreciated.

Plotting points on a map in the rgdal package with R

Borrowing code from Rob Berry (http://rob-barry.com/2015/06/14/Mapping-in-R/), I make a map of NY city. I have many lat long points I wish to plot on the map. The problem is that plotting a map like this results in plot area way outside of reasonable lat long ranges, so I assume there must either be a way to convert my points to the map scale, or rescale the map so the plot space can lay down lat lon points with the points() function.
Here is the code from Rob Berry:
download.file(destfile = "nypp_15b.zip")
unzip(zipfile = "nypp_15b.zip")
library("rgdal")
nypp <- readOGR("nypp_15b", "nypp")
plot(nypp)
Nice map! But now notice the plot extents:
par(“usr”)
The plots space numbers look like 888196.7, 1092361.0, 114013.0, 278953.2, so clearly lat lon points like the ones below won't show up on the map. So how do I get my points to plot correctly on the map?
lat <- c(40.75002, 40.74317)
lon <- c(-73.96905 -74.00366)
The following doesn't work because the scale is so different:
points(lat,lon, col = “red”)
Thank you very much.
nypp is in projected coordinate system, so you need to change to coordinate system of your points or of nypp. You can do something like this :-
nypp <- readOGR("nypp_15b", "nypp")
## Check the CRS of nypp
crs(nypp)
## CRS arguments:
+proj=lcc +lat_1=40.66666666666666 +lat_2=41.03333333333333 +lat_0=40.16666666666666 +lon_0=-74 +x_0=300000
+y_0=0 +datum=NAD83 +units=us-ft +no_defs +ellps=GRS80 +towgs84=0,0,0
plot(nypp)
lat <- c(40.75002, 40.74317)
lon <- c(-73.96905, -74.00366)
df <- data.frame(lat, lon)
## Convert to spatial dataframe and change the coordinates of the points
coordinates(df) <- ~lon + lat
crs(df) <- CRS("+proj=longlat +datum=WGS84")
df <- spTransform(df, crs(nypp))
## Add points to the plot
points(df$lon, df$lat, col = "red", pch =19)
Result:

Map raw data and mean data based on the shapefile

sI have the dataset (pts) like this:
x <- seq(-124.25,length=115,by=0.5)
y <- seq(26.25,length=46,by=0.5)
z = 1:5290
longlat <- expand.grid(x = x, y = y) # Create an X,Y grid
pts=data.frame(longlat,z)
names(pts) <- c( "x","y","data")
I knew that I can map the dataframe (pts) into a map by doing:
library(sp)
library(rgdal)
library(raster)
library(maps)
coordinates(pts)=~x+y
proj4string(pts)=CRS("+init=epsg:4326") # set it to long, lat
pts = spTransform(pts,CRS(" +init=epsg:4326 +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +towgs84=0,0,0"))
pts <- as(pts, "SpatialPixelsDataFrame")
r = raster(pts)
projection(r) = CRS(" +init=epsg:4326 +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +towgs84=0,0,0")
plot(r)
map("usa",add=T)
Now I would like to create a separate map which shows the means of pts across different regions. The shapefile I want to use is from ftp://ftp.epa.gov/wed/ecoregions/cec_na/NA_CEC_Eco_Level2.zip , however, this is a north america map. How can I create the map showing only US based on this north america map? Or is there another better way to do this? thanks so much.
I think that cutting out the non-US data based on the data in the shapefile alone would be hard, since the regions do not correspond to political boundaries - that could be done with rgeos though.
Assuming that "eco" is a SpatialPolygonsDataFrame read in by rgdal::readOGR or maptools::readShapeSpatial, see the available key data for indexing:
sapply(as.data.frame(eco), function(x) if(!is.numeric(x)) unique(x) else NULL)
If you just want to plot it, set up a map with only the US region to start with and then overplot.
library(maps)
map("usa", col = "transparent")
We see that the data is in Lambert Azimuthal Equal Area:
proj4string(eco)
[1] " +proj=laea +lat_0=45 +lon_0=-100 +x_0=0 +y_0=0 +a=6370997 +b=6370997 +units=m +no_defs"
So
require(rgdal)
eco.laea <- spTransform(eco, CRS("+proj=longlat +ellpse=WGS84"))
plot(eco.laea, add = TRUE)
If you want to plot in the original Lambert Azimuthal Equal Area you'll need to get the bounding box in that projection and start the plot based on that, I just used existing data to make an easy example. I'm pretty sure the data could also be cropped with rgeos against another boundary too, but depends what you actually want.

Resources