> Updated on February 2020 to reflect changes to the elevation
function and how it retrieves data from the web. The Google Elevation API was replaced by the GeoNames service.
Update 12/10/2015: I found and fixed an error in the formula for converting latlong coordinates to decimal degrees. My bad.
Update 14/9/2015: Scott Chamberlain of rOpenSci has added checks to the elevation function to warn when the input coordinates have impossible values, incomplete cases, and values at 0,0.
As part of a project on bat macroecology, I was given a spreadsheet of point occurrences for Stenodermatines. All the records had georeferenced location data in Degrees/Minutes/Seconds, but some did not include original elevation data. I wanted to fetch the elevation for the points with missing data – a simple enough task. At first, I considered doing it the way I remembered from my undergraduate projects, by plugging the coordinates for individual localities into third-party websites that locate them in an embedded Google Map and show the elevation (for example: mygeoposition.com).
Then I remembered that it’s not 2004 anymore. A quick search led me to the rgbif
package by the helpful folks from the rOpenSci project. rgbif
includes the elevation
function, which queries the relevant web resources (DEMs) to get the elevations for a data frame or list of points.
In this post, I go through some reproducible example code for getting elevation data using rgbif
.
Example code and data
When I received the point data, I was warned that records with no altitude used “9999” as the NA value. This was pretty obvious to spot and easy to put into the na.strings
argument when importing the data.
# load packages
library(rgbif) # Interface to the Global 'Biodiversity' Information Facility API
library(dplyr) # A Grammar of Data Manipulation
# read coordinate data
Localities <- read.csv("https://raw.githubusercontent.com/luisDVA/codeluis/master/rawCoords.csv", na.strings = 9999,stringsAsFactors = FALSE)
After loading the data, I used dplyr
to rename some columns, convert the coordinates into decimal degrees, and discard records that already have elevation data. Then I tried out the elevation()
function. Because I already had columns named ‘decimalLatitude’ and ‘decimalLongitude’, the only arguments needed were the name of the data frame and my GeoNames username (which is easy to obtain, see the help file for elevation()
).
After signing up for GeoNames account and going through the account validation process, make sure to enable Free Web Services at the account management page, otherwise you will get a 401 error and the elevation
function will not run.
LocalitiesNoElevation <- Localities %>%
filter(is.na(Elevation)) %>%
filter(State!="UNKNOWN") %>%
mutate(
decimalLatitude = LatitudeDegrees + LatitudeMinutes / 60 + LatitudeSeconds / 3600,
decimalLongitude = (abs(LongitudeDegrees) + LongitudeMinutes / 60 + LongitudeSeconds / 3600) * -1
)
# fetch elevations
# use your GeoNames username after enabling webservices for it
missingElevations<- elevation(LocalitiesNoElevation,username = "YOURGEONAMESUSERNAMEHERE")
The distinct localities and their elevations (with the default setting for the Elevation Model) look like this:
missingElevations %>% select(longitude,latitude,elevation_geonames) %>% distinct()
longitude latitude elevation_geonames
1 -99.43389 18.73972 1005
2 -88.56861 18.34250 9
3 -89.02167 18.20167 121
4 -88.55444 18.55333 62
5 -89.00000 18.20167 128
6 -88.58694 18.52556 30
7 -88.91306 18.19972 95
8 -88.60139 18.53694 33
9 -88.85778 17.92500 5
10 -88.61917 18.34833 31
11 -98.22333 20.39111 1121
12 -93.77139 16.28167 511
13 -97.00167 17.59028 1268
14 -99.60833 17.63083 1695
15 -98.78028 16.78222 285
16 -98.72000 16.78028 431
17 -98.74778 16.81278 267
For now, I hope this post helps others to fetch elevations programatically. Let me know if there are any errors.