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:
Related
I am quite new to working with spatial dataframes, and have what I thought was a relatively simple task: take a dataframe of 6 points, with x and y columns representing the lat/long positions of those points, and project them so that they can be used in a spatial data frame that I have made.
Here is the way I coded in the 6 points:
d1 <- structure(list(latitude = c(37.427733, 37.565759, 37.580956, 37.429285, 37.424270, 37.502496), longitude = c(-108.011061, -107.814039, -107.676662, -107.677166, -108.898826, -108.586042)))
d2 <- as.data.frame(d1)
d3 <- SpatialPointsDataFrame(c(d2[,c('longitude','latitude')]), data = d2)
And I tried changing/assigning a projection for these (these lat/long data were taken from Google Maps), but I can't seem to make it work. The projection for the data I want to overlay these points on is the following:
+proj=aea +lat_0=23 +lon_0=-96 +lat_1=29.5 +lat_2=45.5 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs
So basically my question is, how can I convert these lat/long into the format for x/y that this projection uses? Here is the extent of the dataset I want to overlay it on for reference, showing that it is clearly not in simple lat/long:
class : Extent
xmin : -1145835
xmax : -1011345
ymin : 1613205
ymax : 1704855
Thank you all so much in advance!
You need to (re)project your spatial points into the same projection as your other data source. I'm more familiar with the sf package for working with spatial vector information in R, but it looks like you are using the sp packcage.
The first step is to assign your data frame to the correct projection. Latitude and longitude are generally in WGS84, or epsg:4326 so:
library(sp)
d1 <- structure(list(latitude = c(37.427733, 37.565759, 37.580956, 37.429285, 37.424270, 37.502496), longitude = c(-108.011061, -107.814039, -107.676662, -107.677166, -108.898826, -108.586042)))
d2 <- as.data.frame(d1)
d3 <- SpatialPointsDataFrame(c(d2[,c('longitude','latitude')]), data = d2)
proj4string(d3) <- CRS("+init=epsg:4326")
sf::st_bbox(d3)
# xmin ymin xmax ymax
# -108.89883 37.42427 -107.67666 37.58096
Looking at the summary, or this extent, you can see that the lat and long are as the original. Now we can reproject using the proj4string you supplied
target_crs = CRS("+proj=aea +lat_0=23 +lon_0=-96 +lat_1=29.5 +lat_2=45.5 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs") # This is your string assigned to an object
d4 = spTransform(d3, target_crs) ## object used to transform your data frame
sf::st_bbox(d4)
# xmin ymin xmax ymax
# -1127246 1661667 -1018854 1679942
Your coordinates are now represented in the target space, and should be able to be plotted on top of your background data.
If you are willing to use an alternative package, sf::st_transform() is a little easier to use, and the sf package is generally more user friendly.
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:
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
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.
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.