Introduction

In a previous post I looked at how far Uluru is from the sea. In that analysis it was found Uluru is 679.35 kilometres from the sea and that the closest point on the coast is in South Australia at the head of the Great Australian Bight. That original calculation was based on the “as the crow flies” distance which is great, if you’re a crow, but this doesn’t help us land based mammals to determine the distance we’d have to travel to get to the coast from Uluru. This post will look at calculating how far a kangaroo would have to hop from Uluru to the sea, using the programming language R.

\label{fig:figs}A crow flies above the undulations of the earth's surface but a kangaroo would have to navigate these mountains and valleys.

Figure 1: A crow flies above the undulations of the earth’s surface but a kangaroo would have to navigate these mountains and valleys.

The Earth isn’t round

Bear with me, this isn’t a flat earth conspiracy. The Earth is often referred to as an oblate spheroid; a sphere that is a little fatter around the middle. The Earth’s equator rotates faster than the poles and bulges outward by about 21 kilometres. The term oblate spheroid still conjures up an image of a uniform surface throughout the entirety of the planet, but this isn’t true. The earth is a rock and like most rocks it comes with an uneven surface; it rises and falls.

\label{fig:figs}The Earth is often imagined as an oblate spheroid but in reality it's a lumpy rock with an uneven surface.

Figure 2: The Earth is often imagined as an oblate spheroid but in reality it’s a lumpy rock with an uneven surface.

The uneven surface of our planet creates obstacles that a land-based animal needs to navigate in order to reach it’s destination; they can’t fly above it all. How can the distance to be travelled, accounting for the obstacles, be calculated?

Calculating distance

Before calculating the distance, we’ll set the starting location - Uluru - and the projection. A projection is the method by which a three-dimensional coordinate system is displayed on a flat surface; in this case the widely used WGS84 projection is used. The WGS84 projection is set using the get_proj4 function from the tmaptools package.

location <- "Uluru"

# We'll use the WGS84 projection for this project
wgs.84 <- get_proj4("WGS84", output = "character")

Locating Uluru

The next step is to get the coordinates of Uluru based on the WGS84 projection. For this the geocode_OSM function from the tmaptools package is used. The geocode_OSM function uses the Open Street Maps (OSM) Nominatim to retrieve the latitude and longitude for Uluru.

loc <- geocode_OSM(location, projection = wgs.84)
loc_df <- as.data.frame(loc$coords)

# Get latitude and longitude of place of interest
longitude <- loc_df["x",]
latitude <- loc_df["y",]

The call to the OSM has placed Uluru at; longitude: 131.0369615 and latitude: -25.3455545. These coordinates can be converted to a geometry object using the readWKT function from the rgeos package. The readWKT function reads the Well Known Text (WKT), created by concatenating the latitude and longitude into the format POINT(longitude latitude), and outputs a spatial object in the specified WGS84.

# Get latitude and longitude of Uluru, create Well Known Text, and then geometry object
loc_txt <- paste("POINT(",longitude," ",latitude,")")
loc_wkt <- readWKT(loc_txt, p4s = CRS(wgs.84))

Elevation

With the location of Uluru on the Earth’s surface known how far above sea level Uluru is can be calculating using the get_elev_point function from the elevatr package. The elevatr package provides access to several web services that provide elevation data. The web service used for this project is the Amazon Web Service Terrain Tiles which provides a global dataset for bare-earth terrain heights. For Australia the elevation data is in a grid of either 5 metres for coastal regions or 90 metres for inland regions.

uluru_alt <- elevatr::get_elev_point(loc_wkt, src = "aws")

The get_elev_point function accepts a point and returns the elevation. Uluru is 545.37 metres above sea level.

The coast

As in the previous post coastline data can be obtained from the http://www.naturalearthdata.com website. The downloaded zip file is unzipped and read using the readOGR function from the rgdal package.

download.file("http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/physical/ne_10m_coastline.zip", 
              destfile = 'coastlines.zip')

# Unzip coastline data and load into a SpatialLinesDataFrame object
unzip(zipfile = 'coastlines.zip', 
      exdir = 'ne-coastlines-10m')
coast <- readOGR("ne-coastlines-10m/ne_10m_coastline.shp", p4s = wgs.84)

Crow flies distance

As in the previous post the distance from Uluru to the coast can be calculated using the dist2line function in the geosphere package.

# Find the minimum distance to the coast in metres, as well as the point on the coast closest to the location of interest
dist_line <- dist2Line(loc_wkt, coast)
closest_longitude <- dist_line[2]
closest_latitude <- dist_line[3]

Unsurprisingly the distance from Uluru to the sea is 679.35 kilometres; this was found previously. The other important output is the closest point on the coast to Uluru. Using the coordinates of Uluru, and the coordinates of the closest point on the coast a line can be drawn between the two; a SpatialLines object is created to connect the two. This SpatialLines object will be important for calculating changes in elevation.

#Create a line between Uluru and the closest point on the coast and make a SpatialLines object
x <- c(longitude, closest_longitude)
y <- c(latitude, closest_latitude)
xy <- cbind(x,y)

sp_line <- SpatialLines(list(Lines(Line(xy), ID = "a")))

Kangaroo hops

The elevatr package can calculate the elevation of a point. The line between Uluru and the coast can be broken up into many points. Using the spsample function from the sp package the line between Uluru and the coast can be split into many points along the route. In the below code snippet 10,000 points along the line are taken. As the code sets the type to “regular” the points are evenly spaced. This means we have 10,000 points all ~67.94 metres apart along the line between Uluru and the coast.

pts_2_sample <- 10000

# Break the line into n regular intervals and create a dataframe of these, add location
# of the point of interest along with the point on the coast
spdf <- spsample(x = sp_line, n = pts_2_sample, type = "regular") 
points <- as.data.frame(spdf)
points <- as.data.frame(rbind(xy[1,], points, xy[2,]))

The 10,000 points are stored in a data frame. This data frame can be passed to the get_elev_points function, used previously, to determine the elevation of each of the 10,000 points. The final step is to calculate the distance of each point from Uluru so as to be able to create an elevation profile.

# Get distance between points
dist_bw_pts <- dist_line[1] / (pts_2_sample + 1)

# Now get elevation of points, a SpatialPointsDataFrame is returned, turn that to a dataframe
# and add distance, in kilometres, from location of interest.
elevations <- elevatr::get_elev_point(points, prj = wgs.84 ,src = "aws")
elevations_df <- as.data.frame(elevations)
elevations_df <- cbind(elevations_df, "distance" = (as.numeric(row.names(elevations_df)) - 1) * dist_bw_pts)

Elevation profile

With the points and elevations in a data frame an elevation profile can be produced to look at the changes that occur along the path from Uluru to the coast. Although in the profile (Figure 3) it looks like there are significant peaks along the journey the initial rise of 300 metres in elevation occurs over almost 100 kilometres - hardly steep. The drop from almost 900 metres to the sea occurs over 500 kilometres.

# Now plot the elevation profile from location of interest to coast
ggplot(elevations_df) +
  geom_line(aes(x = (distance/1000), y = elevation)) +
  scale_x_continuous(name = "Distance (km)") +
  scale_y_continuous(name = "Elevation (m)") +
  ggtitle("Elevation Profile") +
  theme_bw()
\label{fig:figs}The profile elevation from Uluru to the coast

Figure 3: The profile elevation from Uluru to the coast

Distance travelled

To calculate the distance travelled over the terrain we call on Pythagoras’ theorem. The Pythagorean equation is:

\(a^2 + b^2 = c^2\)

The horizontal distance travelled and the change in elevation are both known which means we can calculate the hypotenuse (or \(c\)). The sum of the hypotenuse values for each of the 10,000 points will provide a reasonably good estimate of the distance a kangaroo at Uluru would have to travel to reach the sea.

\label{fig:figs}Using the Pythagorean equation the distance travelled by a kangaroo can be determined by using the horizontal distance travelled and the change of elevation

Figure 4: Using the Pythagorean equation the distance travelled by a kangaroo can be determined by using the horizontal distance travelled and the change of elevation

Note that because a change in elevation can be negative (i.e. a descent) the absolute elevation change is used in the calculation.

# For each point calculate the absolute elevation distance from point a to point b
first_elevation <- elevations_df[1,1]
elevations_df <- cbind(elevations_df, "next_elevation" = c(first_elevation, head(elevations_df$elevation, -1)))
elevations_df <- cbind(elevations_df, "elevation_diff" = abs(elevations_df$elevation - elevations_df$next_elevation))

# a2 + b2 = c2
distance <- sum(sqrt(dist_bw_pts^2 + elevations_df$elevation_diff^2))

The distance a kangaroo at Uluru would have to hop to reach the sea is 679.69 kilometres. This is 336.33 metres more than the crow would have to fly.

Conclusion

The relatively flat interior of Australia is probably not the best example to use when calculating distance travelled over land rather than through the more commonly used as the crow flies calculation. None-the-less the method described here provides a template to calculate the distance travelled, accounting for undulations in the Earth’s surface.