Quantcast
Channel: Search Results for “maps”– R-bloggers
Viewing all articles
Browse latest Browse all 589

Great circles lines on a world map with rworldmap and ggplot2 packages

$
0
0

(This article was first published on Robin Lovelace - R, and kindly contributed to R-bloggers)

Sometimes you may want to plot maps of the whole world,nthat little bluenspinning sphere thensurface of which provides a home for us all.nCreating maps of smaller areasnis covered in antutorialnI helped create called ‘Introduction to visualising spatial data in R’,nhosted with data and code on angithub repository.nThere are a range of options for plotting the world, including packages callednmaps,na function called map_data fromnggplot2 package and rworldmap.

nn

In this post we will use the latter two (newer) optionsnto show how maps of the entire worldncan easily be produced in R and overlaid with shortest-linenpaths called great circles. Amazingly, in each package, the geographicndata for the world and many of its subregionsnare included, saving thenneed to download and store files of unknown quality from the internet.

nn

plot of chunk ggplot2 projections

nnnn

Plotting continents and great circle lines in base graphics

nn

The first stage is to load the packages we’ll be using:

nn
x <- c("rworldmap", "geosphere", "ggmap")nlapply(x, require, character.only = T)n
nn

Let us proceed by loading an entire map of the world fromnthe rworldmap function getMap:

nn
s <- getMap() # load the map datanclass(s) # what type of are we dealing with?n
nn
## [1] "SpatialPolygonsDataFrame"n## attr(,"package")n## [1] "sp"n
nn
nrow(s) # n. polygonsn
nn
## [1] 244n
nn
plot(s) # the data plotted (not shown)nbbox(s) # the bounding box... of the entire worldn
nn
##    min    maxn## x -180 180.00n## y  -90  83.65n
nn

The above shows that in single line of code we have loadedns, which represents the entire world and all its countries.nThis impressive in itself,nand we can easily add further details like colour based onnthe countries’ attributes (incidentally, you can seenthe attribute data by typing s@data).

nn

Adding points randomly scattered over the face of the Earth

nn

But what if we want to add up points to the map ofnthe world and join them up? This can be done innthe same way as we’d add points to any R graphic.nUsing our knowledge of bbox we can define the limitsnof random numbers (from runif) to scatter points randomlynover the surface of the earth in terms of longitude. Note the use ofncos(abs(l)) to avoid oversampling at the poles,nwhich have a much lower surface area than the equator, pernline of longitude.

nn
set.seed(1984)nn = 20nx <- runif(n=n, min=bbox(s)[1,1], max = bbox(s)[1,2] )nl <- seq(from = -90, to = 90, by = 0.01)ny <- sample(l, size = n, prob = cos(abs(l) * pi / 180))np <- SpatialPoints(matrix(cbind(x,y), ncol=2), proj4string=CRS("+proj=longlat +datum=WGS84"))nplot(s)npoints(p, col = "red")n
nn

plot of chunk Plotting points

nn

Joining the dots

nn

So how to join these randomly scattered points on the planet?nA first approximation would be to join them with straight lines.nLet’s join point 1, for example, to all others to test this method:

nn
plot(s)nsegments(x0 = rep(coordinates(p[1,])[1], n), y0 = rep(coordinates(p[1,])[2], n),n         x1 = coordinates(p)[,1], y1 = coordinates(p)[,2])n
nn

plot of chunk Plotting segments

nn

(Incidentally, isn’t the use of segments here rather clunky - any suggestionsnof a more elegant way to do this welcome.)nThe lines certainly do join up, but something doesn’t seem right in the map, right?nWell the fact that you have perfectly straight lines in the image means bendynlines over the Earth’s surface: these are not the shortest,ngreat circle lines.nTo add these great circle lines, we must use the geosphere package:

nn
head(gcIntermediate(p[1,], p[2]), 2) # take a look at the output of the gcIntermediate functionn
nn
##        lon    latn## [1,] 55.16 -37.47n## [2,] 53.16 -37.25n
nn
plot(s)nlines(gcIntermediate(p[1,], p[2,]), col = "blue", lwd = 3)n# for loop to plot all lines going to zone 5nfor(i in 1:length(p)){n  lines(gcIntermediate(p[1,], p[i,]), col = "green")n}n
nn

plot of chunk Plotting great circles 1

nn

Fantastic. Now we have great circle lines represented on anmap with a geographic coordinate system (CRS)n(as opposed to a projected CRS, which approximates Euclidean distance).

nn

Beautifying the map

nn

The maps we created so far are not exactly beautiful.nLet’s try to make the map look a little nicer:

nn
names(s@data)n
nn
##  [1] "ScaleRank"    "LabelRank"    "FeatureCla"   "SOVEREIGNT"  n##  [5] "SOV_A3"       "ADM0_DIF"     "LEVEL"        "TYPE"        n##  [9] "ADMIN"        "ADM0_A3"      "GEOU_DIF"     "GEOUNIT"     n## ...n
nn
library(rgdal)n
nn
# s <- spTransform(s, CRSobj=CRS("+proj=robin +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"))nrcols <- terrain.colors(length(unique(s$REGION)))ns$col <- as.numeric(factor(s$REGION))npar(bg = 'lightblue')nplot(s, col = rcols[s$col], xlim = c(-180, 180))npoints(p, col = "red")nfor(i in 1:length(p)){n  lines(gcIntermediate(p[5,], p[i,]), col = "black")n}n
nn

plot of chunk Beautifying

nn
par(bg = 'white')n
nn

Doing it in ggplot2

nn

The ‘beautified’ map above certainly is more interesting visually, with addedncolours. But it’s difficult to call it truly beautiful. For that, as withnso many things in R plotting, we turn to ggplot2.

nn
s <- map_data("world")nm <- ggplot(s, aes(x=long, y=lat, group=group)) +n  geom_polygon(fill="green", colour="black")nmn
nn

plot of chunk ggplot world 1

nn

When we add the lines in projected maps (i.e. with a Euclidean coordinate system)nbased solely on origins and destinations, this works fine, butnas with the previous example, generates incorrectnshortest path lines:

nn
# adding linesn# for all combinations of lines, use this coden# p1 <- do.call(rbind, rep(list(coordinates(p)),n ))n# p2 <- cbind(rep(coordinates(p)[,1], each=n ), rep(coordinates(p)[,2], each=n ))n# for all lines goint to point 5:np1 <- coordinates(p[5,])[rep(1, n),]np2 <- coordinates(p)n# test plotting the linesn# ggplot() + geom_segment(aes(x = p1[,1], y = p1[,2], xend = p2[,1], yend = p2[,2]))nggplot() + geom_polygon(data = s,aes(x=long, y=lat, group=group), n  fill="green", colour="black") +  n  geom_segment(aes(x = p1[,1], y = p1[,2], xend = p2[,1], yend = p2[,2]))n
nn

plot of chunk Adding world lines ggplot2 style

nn

Adding great circle lines to ggplot2 maps

nn

Adding great circle lines in ggplot2 is similar, but we mustnsave all of the coordinates of the paths in advance before plotting,nbecause ggplot2 like to add all its layers in one function: youncannot iteratively add to the map using a for loop as we didnin the base graphics example above.

nn

To create the for loop, first create a data frame of a single line.nThe iterate for all zones and use rbind to place one data frame onntop of the next:

nn
paths <- gcIntermediate(p[5,], p[1,])npaths <- data.frame(paths)npaths$group <- 1nsel <- setdiff(2:length(p), 5)nfor(i in sel){n  paths.tmp <- gcIntermediate(p[5,], p[i,])n  paths.tmp <- data.frame(paths.tmp)n  paths.tmp$group <- in  paths <- rbind(paths, paths.tmp)n}n
nn

To plot multiple paths, we can use the geom_segment command.nBefore plotting the lines on the map, it’s sometimes best to firstnplot them on their own to ensure that everything is working.nNote the use of the command ggplot(), which initiates annempty ggplot2 instances, ready to be filled with layers.nThis is more flexible than stating the data at the outset.

nn
ggplot() + geom_polygon(data = s, aes(x=long, y=lat, group=group), n  fill = "green", colour="black") +n  geom_path(data = paths, aes(lon, lat , group = group)) +n  theme(panel.background = element_rect(fill = 'lightblue'))n
nn

plot of chunk polygon paths ggplo2

nn

Changing projection in ggplot

nn

ggplot2 has inbuilt map projection functionality with thenfunction coord_map. This distorts the Euclidean axis of thenmap and allows some truly extraodinary shapes (thesentransformations can also be done in base graphics, ne.g. by using spTransform). However,nas shown in the examples below, the library is currently buggynfor plotting polygons.

nn
# to see the range of projections available using this method, try ?mapprojectnm <- last_plot()nm + coord_map()n
nn

plot of chunk ggplot2 projections

nn
# remove fill as this clearly causes problems:nm <- ggplot() + geom_path(data = s, aes(x=long, y=lat, group=group), colour="black") +n  geom_path(data = paths, aes(lon, lat , group = group)) n# m + coord_map("bicentric", lon = 0)n# m + coord_map("bonne", lat= 0)nm + coord_map("ortho", orientation=c(41, -74, 0)) # for ortho mapsn
nn

plot of chunk ggplot2 projections

nn

Conclusion

nn

We’ve seen 2 ways of plotting maps of the world and overlayingn‘great circles’ lines on them. There are probably more, butnthese two options seem to work well, except withnthe bugs in ggplot2 for plotting polygons innmany map projections. The two methods are not incompatiblen(see fortify for plotting sp objects in ggplot2)nand can be combined in many other ways.

nn

For more information on plotting spatial data in R,nI recommend checking out R’s range ofnspatial packages.nFor an introductory tutorial on visualising spatial datanin R, you could do much worse than start withnVisualising Spatial Data in Rnby James Cheshire and myself.

n

To leave a comment for the author, please follow the link and comment on his blog: Robin Lovelace - R.

R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more...

Viewing all articles
Browse latest Browse all 589

Latest Images

Trending Articles



Latest Images