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

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?).

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

How can I project a raster in lat-long coordinates to UTM, for plotting in tmap?

I have a map with an elevation raster that is in lat/long coordinates but I now need to display the map in easting/northing. How can I accomplish this?
I have tried reprojecting the raster from lat/long to UTM but this warps the map (I assume for reasons discussed in this SO post).
A minimal working example follows a description of the code that I am using to produce the map in lat/long coordinates. I used a manually defined bounding box, extent, to download the elevation raster of interest from FedData. I then plotted the raster using tmap.
# - Libraries ----
library(proj4)
library(FedData)
library(rgdal)
library(tmap)
# extent defined manually
extent <- rgeos::readWKT("POLYGON((-118.25 36.45, -118.25 36.6, -118.25 36.9, -118.8 36.9, -118.8 36.45, -118.25 36.45))")
# define shape polygon on lat/long coordinates
proj4string(extent) <- "+proj=longlat +ellps=GRS80 +datum=NAD83 +no_defs"
# - Get national elevation raster ----
# download National Elevation Database elevation data in extent
# default resolution is 1 arcsecond (res="1);
# to get 1/3 arcsecond (res="13)
ned_raster<-FedData::get_ned(template=extent, label="ned_raster", res="1", force.redo = T)
# - Plot with tmap ----
tm_shape(ned_raster,unit.size=1)+
tm_graticules() +
tm_raster(legend.show=FALSE)
Update I'm updating the question with some additional clarification after Robert Hijmans' answer. I am including the code that I used for the reprojection here:
ned_raster2 <- raster::projectRaster(ned_raster, crs=CRS("+proj=utm +init=epsg:26711 +zone=11 +north +no_defs"))
The output is comparable to the one in the map you included in your answer. By modifying the bounding box with tmaptools::bb I can clip the map with tmap so that the projection is correct but it does not "appear" warped:
# - Project from angular to planar ----
ned_raster2 <- raster::projectRaster(ned_raster, crs=CRS("+proj=utm +init=epsg:26711 +zone=11 +north +no_defs"))
# redefine extent/bounding box
e2 = tmaptools::bb(ned_raster2)
e2 = e2*c(1.01,1.001,.99,.999)
# - Plot with tmap ----
tm_shape(ned_raster2,unit.size=1,bbox=e2)+
tm_graticules(n.x=5) +
tm_raster(legend.show=FALSE)
From this, how can I include the appropriate axes (in easting/northing) on this map?
I now need to display the map in easting/northing.
I understand that in stead of angular (lon/lat) coordinates you want to use planar (Cartesian) coordinates. That means that you need to choose an appropriate coordinate reference system (CRS). You say you used UTM, and that could be reasonable, but you found that the results are "warped". You should show the code you used because you probably made a mistake. There always is some distortion, but for a small area like this is would not be an issue if you specify the CRS correctly. (otherwise, explain "warped" and why it matters.)
For example
library(raster)
library(FedData)
# extent defined manually
e <- as(extent(-118.8, -118.25, 36.45, 36.9), "SpatialPolygons")
crs(e) <- "+proj=longlat"
ned <- FedData::get_ned(template=e, label="ned_raster", res="1", force.redo = T)
ned2 <- projectRaster(ned, crs="+proj=utm +zone=11")
plot(ned2)
For larger areas you cannot use UTM and you would need to use another CRS.
Alternatively, you could create grid-lines (graticule) from the projected raster, store their coordinates, and transform these back to longlat. That would be a bit odd. Normally, one might want to show longlat coordinates on a otherwise projected (planar) map.

Unit of calculated area for polygons using raster package area function

I am trying to calculate the area (square km or miles) of the intersection of counties and watersheds in R using the raster package and the area function.
My code looks like this so far:
counties <- readOGR('C:\\Shapefiles\\tl_2017_us_county\\tl_2017_us_county.shp')
counties <- spTransform(counties, CRS("+init=epsg:3455"))
huc2_10 <- readOGR('C:\\Shapefiles\\WBD_10_HU2_Shape\\Shape\\WBDHU6.shp')
huc2_10 <- spTransform(huc2_10, CRS("+init=epsg:3455"))
I then intersect the two shapefiles:
pi <- raster::intersect(huc2_10, counties)
The units of this projection are normally in meters (I believe), as it is a NAD83 projection for southern South Dakota, so the area function should calculate area in square meters. I am attempting to calculate the area (in square miles) of each polygon that is formed as a result of this intersection using the area function.
pi$area <- area(pi)/2.589988e6
However, the proj4string looks like this:
+init=epsg:3455 +proj=lcc +lat_1=44.4 +lat_2=42.83333333333334 +lat_0=42.33333333333334 +lon_0=-100.3333333333333 +x_0=600000 +y_0=0 +datum=NAD83 +units=us-ft +no_defs +ellps=GRS80 +towgs84=0,0,0
According to this, the units are "us-ft". So, does the area function output areas for each polygon in square feet because of this? This seems to make sense, but I would like to confirm this, changing my code to:
pi$area <- area(pi)/5280**2
Thank you.
The manual confirms what you expect:
?raster::area
If x is a SpatialPolygons* object: area if each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
If your map units are feet, the area will be in square feet.

Assigning spatial coordinates to an array in R

I have downloaded a text file of data from the following link: http://radon.unibas.ch/files/us_rn_50km.zip
After unzipping I use the following lines of code to plot up the data:
# load libraries
library(fields)
# function to rotate a matrix (and transpose)
rotate <- function(x) t(apply(x, 2, rev))
# read data
data <- as.matrix(read.table("~/Downloads/us_rn_50km.txt", skip=6))
data[data<=0] <- NA
# rotate data
data <- rotate(data)
# plot data
mean.rn <- mean(data, na.rm=T)
image.plot(data, main=paste("Mean Rn emissions =", sprintf("%.3f", mean.rn)) )
This all looks OK, but I want to be able to plot the data on a lat-long grid. I think I need to convert this array into an sp class object but I don't know how. I know the following (from the web site): "The projection used to project the latitude and longitude coordinates is that used for the Decade of North American Geology (DNAG) maps. The projection type is Spherical Transverse Mercator with a base latitude of zero degrees and a reference longitude of 100 degrees W. The scale factor used is 0.926 with no false easting or northing. The longitude-latitude datum is NAD27 and the units of the xy-coordinates are in meters. The ellipsoid used is Clarke 1866. The resolution of the map is 50x50km". But don't know what to do with this data. I tried:
proj4string(data)=CRS("+init=epsg:4267")
data.sp <- SpatialPoints(data, CRS("+proj=longlat+ellps=clrk66+datum=NAD27") )
But had various problems (with NA's) and fundamentally I think that the data isn't in the right format.. I think that the SpatialPoints function wants a data on location (in 2-D) and a third array of values associated with those locations (x,y,z data - I guess my problem is working out the x and the y's from my data!)
Any help greatly appreciated!
Thanks,
Alex
The file in question is an ASCII raster grid. Coordinates are implicit in this format; a header describes the position of the (usually) lower left corner, as well as the grid dimensions and resolution. After this header section, values separated by white space describe how the variable varies across the grid, with values given in row-major order. Open it in a text editor if you're interested.
You can import such files to R with the fantastic raster package, as follows:
download.file('http://radon.unibas.ch/files/us_rn_50km.zip',
destfile={f <- tempfile()})
unzip(f, exdir=tempdir())
r <- raster(file.path(tempdir(), 'us_rn_50km.txt'))
You can plot it immediately, without assigning the projection:
If you didn't want to transform it to another CRS, you wouldn't necessarily need to assign the current coordinate system. But since you do want to transform it to WGS84 (geographic), you need to first assign the CRS:
proj4string(r) <- '+proj=tmerc +lon_0=-100 +lat_0=0 +k_0=0.926 +datum=NAD27 +ellps=clrk66 +a=6378137 +b=6378137 +units=m +no_defs'
Unfortunately I'm not entirely sure whether this proj4string correctly reflects the info given at the website that provided the data (it would be great if they actually provided the definition in a standard format).
After assigning the CRS, you can project the raster with projectRaster:
r.wgs84 <- projectRaster(r, crs='+init=epsg:4326')
And if you want, write it out to a raster format of your choice, e.g.:
writeRaster(r.wgs84, filename='whatever.tif')

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

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:

Resources