R: creating a map of selected Canadian provinces and U.S. states - r

I am attempting to create a map of selected Canadian provinces/territories and selected U.S. states. So far the nicest maps appear to be those generated with GADM data: http://www.gadm.org/
However, I have not been able to plot the U.S. and Canada on the same map or plot only selected provinces/territories and states. For example, I am interested in Alaska, Yukon, NWT, British Columbia, Alberta, and Montana among others.
Also, the U.S. map appears to be split along the international dateline.
Can someone please help me to:
plot the aforementioned provinces/territories and states on a single map
avoid having the U.S. split along the International dateline
overlay a latitude-longitude grid
select a specific projection, maybe the polyconic.
Maybe spplot does not allow users to specify projections. I did not see an option to select a projection on the spplot help page. I know how to select projections with the map function in the maps package but those maps did not appear to look as nice and I could not plot the desired subset of provinces/territories and states with that function either.
I do not know how to begin adding a latitude-longitude grid. However, Section 3.2 of the file 'sp.pdf' seems to address the topic.
Below is the code I have come up with so far. I have loaded every map-related package I have stumbled upon and commented out GADM data except for provincial/territorial or state boundaries.
Unfortunately, so far I have only managed to plot maps of Canada or the U.S.
library(maps)
library(mapproj)
library(mapdata)
library(rgeos)
library(maptools)
library(sp)
library(raster)
library(rgdal)
# can0<-getData('GADM', country="CAN", level=0) # Canada
can1<-getData('GADM', country="CAN", level=1) # provinces
# can2<-getData('GADM', country="CAN", level=2) # counties
plot(can1)
spplot(can1, "NAME_1") # colors the provinces and provides
# a color-coded legend for them
can1$NAME_1 # returns names of provinces/territories
# us0 <- getData('GADM', country="USA", level=0)
us1 <- getData('GADM', country="USA", level=1)
# us2 <- getData('GADM', country="USA", level=2)
plot(us1) # state boundaries split at
# the dateline
us1$NAME_1 # returns names of the states + DC
spplot(us1, "ID_1")
spplot(us1, "NAME_1") # color codes states and
# provides their names
#
# Here attempting unsuccessfully to combine U.S. and Canada on one map.
# Attempts at selecting given states or provinces have been unsuccessful.
#
plot(us1,can1)
us.can1 <- rbind(us1,can1)
Thanks for any help. So far I have made no progress with Steps 2 - 4 above. Perhaps I am asking for too much. Perhaps I should simply switch to ArcGIS and try that software.
I have read this StackOverflow post:
Can R be used for GIS?
EDIT
I have now borrowed an electronic copy of 'Applied Spatial Data Analysis with R' Bevand et al. (2008) and downloaded (or located) associated R code and data from the book's website:
http://www.asdar-book.org/
I also found some nice-looking GIS-related R code here:
https://sites.google.com/site/rodriguezsanchezf/news/usingrasagis
If and when I learn how to accomplish the desired objectives I will post solutions here. Although I may eventually move to ArcGIS if I cannot accomplish the objectives in R.

To plot multiple SpatialPolygons objects on the same device, one approach is to specify the geographic extent you wish to plot first, and then using plot(..., add=TRUE). This will add to the map only those points that are of interest.
Plotting using a projection, (e.g. a polyconic projection) requires first using the spTransform() function in the rgdal package to make sure all the layers are in the same projection.
## Specify a geographic extent for the map
## by defining the top-left and bottom-right geographic coordinates
mapExtent <- rbind(c(-156, 80), c(-68, 19))
## Specify the required projection using a proj4 string
## Use http://www.spatialreference.org/ to find the required string
## Polyconic for North America
newProj <- CRS("+proj=poly +lat_0=0 +lon_0=-100 +x_0=0
+y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs")
## Project the map extent (first need to specify that it is longlat)
mapExtentPr <- spTransform(SpatialPoints(mapExtent,
proj4string=CRS("+proj=longlat")),
newProj)
## Project other layers
can1Pr <- spTransform(can1, newProj)
us1Pr <- spTransform(us1, newProj)
## Plot each projected layer, beginning with the projected extent
plot(mapExtentPr, pch=NA)
plot(can1Pr, border="white", col="lightgrey", add=TRUE)
plot(us1Pr, border="white", col="lightgrey", add=TRUE)
Adding other features to the map, such as highlighting jurisdictions of interest, can easily be done using the same approach:
## Highlight provinces and states of interest
theseJurisdictions <- c("British Columbia",
"Yukon",
"Northwest Territories",
"Alberta",
"Montana",
"Alaska")
plot(can1Pr[can1Pr$NAME_1 %in% theseJurisdictions, ], border="white",
col="pink", add=TRUE)
plot(us1Pr[us1Pr$NAME_1 %in% theseJurisdictions, ], border="white",
col="pink", add=TRUE)
Here is the result:
Add grid-lines when a projection is used is sufficiently complex that it requires another post, I think. Looks as if #Mark Miller as added it below!

Below I have modified PaulG's outstanding answer to display a latitude-longitude grid. The grid is coarser than I would like, but might be adequate. I use the United Kingdom with the code below. I do not know how to include the result in this post.
library(rgdal)
library(raster)
# define extent of map area
mapExtent <- rbind(c(0, 62), c(5, 45))
# BNG is British National Grid
newProj <- CRS("+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.999601271625
+x_0=400000 +y_0=-100000 +ellps=airy +units=m +no_defs")
mapExtentPr <- spTransform(SpatialPoints(mapExtent,
proj4string=CRS("+proj=longlat")),
newProj)
# provide a valid 3 letter ISO country code
# obtain a list with: getData("ISO3")
uk0 <- getData('GADM', country="GBR", level=0) # UK
uk1 <- getData('GADM', country="GBR", level=1) # UK countries
uk2 <- getData('GADM', country="GBR", level=2) # UK counties
# United Kingdom projection
uk1Pr <- spTransform(uk1, newProj)
# latitude-longitude grid projection
grd.LL <- gridlines(uk1, ndiscr=100)
lat.longPR <- spTransform(grd.LL, newProj)
# latitude-longitude text projection
grdtxt_LL <- gridat(uk1)
grdtxtPR <- spTransform(grdtxt_LL, newProj)
# plot the map, lat-long grid and grid labels
plot(mapExtentPr, pch=NA)
plot(uk1Pr, border="white", col="lightgrey", add=TRUE)
plot(lat.longPR, col="black", add=TRUE)
text(coordinates(grdtxtPR),
labels=parse(text=as.character(grdtxtPR$labels)))
Result looks like:

Related

The points of occurrence (gbif) and the maps don't coincide when i use worldclim data

i'm new in the R world and i'm trying to do a species distribution model, but when i plot my result, the points stay out from my map, i tried to change CRS but i didn't solve my problem, now i'll go to show you my code
library(dismo)
library(raster)
library(dplyr)
library(rnaturalearth)
Here i downloaded my species from gbif
gbif("Miniopterus", "schreibersii" , download=F)
minio<- gbif("Miniopterus", "schreibersii" , download=T) #you need 2 min approximately
i saw the basis of record and then i selected 2 different types
table(minio$basisOfRecord)
#Filter data minio----
minio<- minio%>%
filter(!is.na(lat))%>%
filter(!is.na(lon))%>%
filter(year>1980)%>%
filter(basisOfRecord %in% c("HUMAN_OBSERVATION", "OBSERVATION"))
class(minio)
nrow(minio)
i selected only longitude and latitude
miniogeo<-minio%>%
select(lon,lat)
head(miniogeo)
miniogeo$species<-1
head(miniogeo)
nrow(miniogeo)
And i created the coordinates and set the crs
coordinates(miniogeo) <-c("lon","lat")
crs(miniogeo) <- "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"
proj4string(miniogeo) <- CRS("+init=epsg:4326")
Here start problems for me, i tried a lot of type of function for create a map but this is the most efficient (the most efficient of those functions I have found). I need to have a zoom of spain and portugal, and i need to exclude "Africa".
Europe <- ne_countries(scale="medium", type="map_units", returnclass="sf", continent="Europe")
Worldclim<-raster::getData('worldclim', var='bio', res=2.5)
Europe <- Europe %>%
dplyr::select(geometry,name_long) %>%
filter(name_long!='Russian Federation')
plot(Worldclim[[1]]) #or plot(worldclim$bio1)
plot(st_geometry(Europe))
points(miniogeo, cex=0.1)
envData<-crop(Worldclim, Europe)
EuropePred <- mask(envData, Europe) #we create a new raster without NA value
And here i plotted my points but, as you can see, my points went out of my map
plot(EuropePred[[1]]) #example
points(miniogeo, cex=0.2)
then i tried to do a zoom to Spain and Portugal.
extSpnPrt<-extent(c(-11,10,35,56))
miniogeo<-crop(miniogeo,extSpnPrt)
SpainPort<-crop(EuropePred,extSpnPrt)
plot(SpainPort$bio2)
points(miniogeo, cex=0.1)
There is someone that can understand my problem? i'm really really sorry, i tried a lot of time for undestand better but my level in R is so basics for now.
I say thank you to all that dedicate the time for read this. I hope you have a good day
This is the result of my map with only geometry and with worldclim data
enter image description here
One way to create a SpatialPointsDataFrame from your data.frame minio is:
coordinates(minio) <- ~ lon + lat
crs(minio) <- "+proj=longlat"
This is NOT correct:
coordinates(minio) <- c("lon", "lat")
Result:
plot(minio, cex=.5, col="red")
lines(as(Europe, "Spatial"))

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, ]

OSM, rgeos, osmar, area calculation, does not add up

I am trying to get the size of a polygon from OSM, using osmar to download the data. However, sanity check tells me the are is not right.
Below is an example of what I mean.
(1) Geographical area around Hyde Park in London. Extracting all ways and relations tagged as 'park'.
devtools::install_github('osmdatar/osmdata')
library(osmdata)
library(osmar)
library(sp)
library(sf)
library(rgeos)
osmO <- get_osm(center_bbox(-0.167919, 51.5072682, 2000, 2000))
ids_relations <- osmO$relations$tags[osmO$relations$tags$v=="park","id"]
ids_ways <- osmO$ways$tags[osmO$ways$tags$v=="park","id"]
ids_sub <- find_down(osmO, way(c(ids_relations, ids_ways)))
sp_sub_park <- as_sp(subset(osmO, ids = ids_sub), "polygons")
Now, I want to know the area of each of these 'parks' [Fig1] (the big one in the middle being Hyde Park).
spplot(sp_sub_park, c("version"), colorkey = FALSE, col.regions=c('green'))
There are two ways:
1) Use the slot 'area' in the polygon itself.
a1 <- sapply(sp_sub_park#polygons, function(x) x#area)
2) Calculate the area with the specified projection.
bg_poly_t <- spTransform(sp_sub_park, CRS("+proj=longlat +datum=WGS84"))
a2 <- rgeos::gArea(bg_poly_t, byid=TRUE)
These two give me the same result [Fig2] (note the two biggest areas are Hyde Park, split in two by a road)
plot(a1*1000000, a2*1000000)
However, the size is not what I would expect. The area is returned in square-km (plotted square-meters). According to that the two parts of Hyde Park add up to about 300 square-meters, the size of a big flat but not a park (Hyde Park ~1.420.000 square-meters).
Any ideas?
Since your "map" is in lat/lon coordinates, to compute the areas you have to either convert it to a "metric" projection (as suggested by #Phil), or use spherical geometry (e.g., as implemented in package geosphere).
Luckily, function st_area of the sf package computes areas of polygons using geosphere in case the object is in geographical coordinates. Therefore, you can do simply:
sf_sub_park <- st_as_sf(sp_sub_park)
areas <- st_area(sf_sub_park)
sum(areas)
, giving:
2551269 m^2
, which is pretty close to #Phil results.
HTH
I've transformed the data into British National Grid which uses metres as its unit of length (WGS84 maybe uses degrees, IDK?). The area is then 2.5million sq m, which seems more plausible?
sp_sub_park <- spTransform(sp_sub_park, CRS("+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 "))
gArea(sp_sub_park)
# [1] 2550387
This is higher than your estimated ~1.4million sq m, but is Hyde Park both large areas of your map (i.e. is Kensington Gardens separate?).

Create bubble plot in R using satellite map

I already created a bubble plot using the following code:
library(ggplot2)
library(sp)
library(raster)
library(maps)
library(mapdata)
library(maptools)
library(gstat)
library(ggmap)
xy <- se_pp[,c("longitude_s", "latitude_s")]
nl <- getData('GADM', country="Netherlands", level=2) #raster data, format SpatialPolygonsDataFrame
# coercing the polygon outlines to a SpatialLines object
spl <- list("sp.lines", as(nl, "SpatialLines"))
SPDF <- SpatialPointsDataFrame(coords=xy, data=se_pp)
projection(SPDF)<- "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +towgs84=0,0,0"
coordinates(SPDF)[1:5,] #retrieves spatial coordinates form the dataframe
bubble(SPDF, "Quantity", sp.layout=spl, main="Plaice Quantity (#/km2), end summer (Sand Engine)")
Now I get a map of the Dutch coast containing the bubble plot (via getData).
Unfortunately this is a very simple map.
I would like to have a satellite map of the same area, which shows more detail, to show my bubble plot.
Does anyone know if this is possible?
Thanks.
I am not sure if you still check SO. But, I leave one approach for you. You can play with the values in zoom to identify the best zoom for your purpose. The CRAN manual of ggmap offers you more examples. Please have a look.
map <- get_map(location = 'netherlands', zoom = 7,
source = 'google',
maptype = 'satellite')
ggmap(map)

Check if point is in spatial object which consists of multiple polygons/holes

I have a SpatialPolygonsDataFrame with 11589 objects of class "polygons". 10699 of those objects consists of exactly 1 polygon, however the rest of those objects consists of multiple polygons (2 to 22).
If an object of consists of multiple polygons, three scenarios are possible:
Sometimes, those additional polygons describe a "hole" in the geographic ara describe by the first polygon in the object of class "polygons".
Sometimes, those additional polygons describe additional geographic areas, i.e. the shape of the region is quite complex and described by putting together multiple parts.
Sometimes, it might be a mix of both, 1) and 2).
Stackoverflow helped me to plot such an spatial object properly (Plot spatial area defined by multiple polygons).
However, I am still not able to answer how to determine whether a point (defined by longitude/latitude) is in a polygon.
Below is my code. I tried to apply the function point.in.polygon in the sp package, but found no way how it could handle such an object which consists of multiple polygons/holes.
# Load packages
# ---------------------------------------------------------------------------
library(maptools)
library(rgdal)
library(rgeos)
library(ggplot2)
library(sp)
# 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
map <- readShapeSpatial(shp, proj4string = CRS("+init=epsg:25832"))
# Transform the geocoding from UTM to Longitude/Latitude
map <- spTransform(map, CRS("+proj=longlat +datum=WGS84"))
# Pick an geographic area which consists of multiple polygons
# ---------------------------------------------------------------------------
# Output a frequency table of areas with N polygons
nPolys <- sapply(map#polygons, function(x)length(x#Polygons))
# Get geographic area with the most polygons
polygon.with.max.polygons <- which(nPolys==max(nPolys))
# Get shape for the geographic area with the most polygons
Poly.coords <- map[which(nPolys==max(nPolys)),]
# Plot
# ---------------------------------------------------------------------------
# Plot region without Google maps (ggplot2)
plot(Poly.coords, col="lightgreen")
# Find if a point is in a polygon
# ---------------------------------------------------------------------------
# Define points
points_of_interest <- data.frame(long=c(10.5,10.51,10.15,10.4),
lat =c(51.85,51.72,51.81,51.7),
id =c("A","B","C","D"), stringsAsFactors=F)
# Plot points
points(points_of_interest$long, points_of_interest$lat, pch=19)
You can do this simply with gContains(...) in the rgeos package.
gContains(sp1,sp2)
returns a logical depending on whether sp2 is contained within sp1. The only nuance is that sp2 has to be a SpatialPoints object, and it has to have the same projection as sp1. To do that, you would do something like this:
point <- data.frame(lon=10.2, lat=51.7)
sp2 <- SpatialPoints(point,proj4string=CRS(proj4string(sp1)))
gContains(sp1,sp2)
Here is a working example based on the answer to your previous question.
library(rgdal) # for readOGR(...)
library(rgeos) # for gContains(...)
library(ggplot2)
setwd("< directory with all your files >")
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)),]
region.df <- fortify(region)
points <- data.frame(long=c(10.5,10.51,10.15,10.4),
lat =c(51.85,51.72,51.81,51.7),
id =c("A","B","C","D"), stringsAsFactors=F)
ggplot(region.df, aes(x=long,y=lat,group=group))+
geom_polygon(fill="lightgreen")+
geom_path(colour="grey50")+
geom_point(data=points,aes(x=long,y=lat,group=NULL, color=id), size=4)+
coord_fixed()
Here, point A is in the main polygon, point B is in a lake (hole), point C is on an island, and point D is completely outside the region. So this code checks all of the points using gContains(...)
sapply(1:4,function(i)
list(id=points[i,]$id,
gContains(region,SpatialPoints(points[i,1:2],proj4string=CRS(proj4string(region))))))
# [,1] [,2] [,3] [,4]
# id "A" "B" "C" "D"
# TRUE FALSE TRUE FALSE
Since you can use the "point in polygon" routine, and this apparently isn't already suitably designed to handle the multi-polygon case in R (which I find a bit odd actually), you are left with having to cycle through each of the multiple polygons. Now the trick is, if you are inside an odd number of polygons, you are inside the multi-polygon. If you are inside an even number of polygons, then you are actually outside of the shape.
Point in polygon testing that uses ray-crossings should ALREADY be able to handle this, just by making sure you pass in all the vertices to the original point.in.polygon test, but I am not sure which mechanism R is using, so I can only give you the even/odd advice above.
I also found this code, not sure if it will help:
require(sp)
require(rgdal)
require(maps)
# read in bear data, and turn it into a SpatialPointsDataFrame
bears <- read.csv("bear-sightings.csv")
coordinates(bears) <- c("longitude", "latitude")
# read in National Parks polygons
parks <- readOGR(".", "10m_us_parks_area")
# tell R that bear coordinates are in the same lat/lon reference system
# as the parks data -- BUT ONLY BECAUSE WE KNOW THIS IS THE CASE!
proj4string(bears) <- proj4string(parks)
# combine is.na() with over() to do the containment test; note that we
# need to "demote" parks to a SpatialPolygons object first
inside.park <- !is.na(over(bears, as(parks, "SpatialPolygons")))
# what fraction of sightings were inside a park?
mean(inside.park)
## [1] 0.1720648
# use 'over' again, this time with parks as a SpatialPolygonsDataFrame
# object, to determine which park (if any) contains each sighting, and
# store the park name as an attribute of the bears data
bears$park <- over(bears, parks)$Unit_Name
# draw a map big enough to encompass all points (but don't actually plot
# the points yet), then add in park boundaries superimposed upon a map
# of the United States
plot(coordinates(bears), type="n")
map("world", region="usa", add=TRUE)
plot(parks, border="green", add=TRUE)
legend("topright", cex=0.85,
c("Bear in park", "Bear not in park", "Park boundary"),
pch=c(16, 1, NA), lty=c(NA, NA, 1),
col=c("red", "grey", "green"), bty="n")
title(expression(paste(italic("Ursus arctos"),
" sightings with respect to national parks")))
# now plot bear points with separate colors inside and outside of parks
points(bears[!inside.park, ], pch=1, col="gray")
points(bears[inside.park, ], pch=16, col="red")
# write the augmented bears dataset to CSV
write.csv(bears, "bears-by-park.csv", row.names=FALSE)
# ...or create a shapefile from the points
writeOGR(bears, ".", "bears-by-park", driver="ESRI Shapefile")

Resources