Even though several examples of great circle visualizations exist by now, I had not seen the code of one made with ggplot2. Both solutions offered, here using plot and here using lattice, basically loop through the great circle lines ordered from low to high number of flights and overplot the lines with fewer counts, which are plotted in a light color with those with higher counts, which are plotted in a dark color.
In ggplot we can simply use the alpha parameter for transparency in combination with scale_colour_gradient to obtain a similar effect.
Example data are all flights out of Beijing, China, downloaded from openflights.org.
[Update 5/5/2012: Note that I wrote this code with the earlier version of ggplot (0.89). Apparently there is an issue with ggplot 0.9 and the fortify.SpatialLinesDataFrame function - please see the comments below and suggestion for a workaround. Thank you Einar!]
10 thoughts on “Great circles on a recentered worldmap, in ggplot”
Thanks so much for posting this--that split polygon thing was driving me nuts, and this is a great patch. That 300 degree thing works brilliantly.
I pulled your code into a function with options to apply to either a polygon or string of segments for my own use (I may have introduced a couple mistakes along the way--it seems to have trouble with the Caspian sea and Antarctica in the shapefile I'm using, not from the maps package). Here it is, in case anyone else comes looking for something similar:
Recenter = function(
data=world,
center=260,
# positive values only - US centered view is 260
shapeType=c("polygon","segment"),
idfield=NULL
# shift coordinates to recenter great circles
) {
#use inherited id column, or create a new one from breaks in the data
if(is.null(idfield)) {
data$id=factor(cumsum(is.na(data$long)))
}else{
data$id = get(idfield,pos=data)
}
# shift coordinates to recenter worldmap
data$long <- ifelse(data$long < center - 180 , data$long + 360, data$long)
### Function to regroup split lines and polygons
# takes dataframe, column with long and unique group variable,
#returns df with added column named group.regroup
RegroupElements <- function(df, longcol, idcol){
g 300) {
# check if longitude within group differs more than 300 deg, ie if element was split
d mean(range(df[,longcol]))
# we use the mean to help us separate the extreme values
g[!d] <- 1
# some marker for parts that stay in place (we cheat here a little, as we do not take into account concave polygons)
g[d] <- 2
# parts that are moved
}
g <- paste(df[, idcol], g, sep=".") # attach to id to create unique group variable for the dataset
df$group.regroup <- g
df
}
### Function to close regrouped polygons
# takes dataframe, checks if 1st and last longitude value are the same, if not, inserts first as last and reassigns order variable
ClosePolygons <- function(df, longcol, ordercol){
if (df[1,longcol] != df[nrow(df),longcol]) {
tmp <- df[1,]
df <- rbind(df,tmp)
}
df
}
# close polys
if(shapeType[1]=="polygon") {
returnframe <- ddply(returnframe, .(group.regroup), ClosePolygons, "long", "order") # use the new grouping var
}
ggplot(returnframe,aes(x=long,y=lat))+geom_polygon(aes(group=group.regroup))
returnframe
}
Thanks a lot for this. I am replicating the steps.
I get this error Error: could not find function "fortify.SpatialLinesDataFrame"
while executing step
rts.ff <- fortify.SpatialLinesDataFrame(rts)
Would you know whats causing this.
Doyou have the ggplot package loaded?
library(ggplot2)
hi
got same error as Gaurav. and ggplot2 is installed (version 0.9.0).
not sure what is going on here, my quick fix was to obtain and running the function from: https://github.com/hadley/ggplot2/blob/master/R/fortify-spatial.r
prior to the running fortify.SpatialLinesDataFrame line.
einar
ps: thanks for sharing this example claudia
I'm a bit of novice. Would someone be so kind as to post complete working code for ggplot2 0.9? I'm not quite sure how to integrate fortify-spatial.r. Thank you, thank you. I was almost about to give up on great circles over the Pacific.
KP,
Download the entire code for fortify-spatial.r to your working directory.
In R:
source ("fortify-spatial.r") # if necessary adjust path
Then run the fortify.SpatialLinesDataFrame command from the code above.
if you have a problem like :
Error: could not find function "fortify.SpatialLinesDataFrame"
change this step.
rts.ff <- fortify.SpatialLinesDataFrame(rts)
to.
rts <- as(rts, "SpatialLinesDataFrame")
rts.ff <- fortify(rts)
it will be okay.
Thanks Andrew, this worked perfectly.
Thanks for sharing!
I was getting the error message "Error: Use 'theme' instead. (Defunct; last used in version 0.9.1)" and replaced the last few lines of code with the following and it workded:
Thanks so much for posting this--that split polygon thing was driving me nuts, and this is a great patch. That 300 degree thing works brilliantly.
I pulled your code into a function with options to apply to either a polygon or string of segments for my own use (I may have introduced a couple mistakes along the way--it seems to have trouble with the Caspian sea and Antarctica in the shapefile I'm using, not from the maps package). Here it is, in case anyone else comes looking for something similar:
Recenter = function(
data=world,
center=260,
# positive values only - US centered view is 260
shapeType=c("polygon","segment"),
idfield=NULL
# shift coordinates to recenter great circles
) {
#use inherited id column, or create a new one from breaks in the data
if(is.null(idfield)) {
data$id=factor(cumsum(is.na(data$long)))
}else{
data$id = get(idfield,pos=data)
}
# shift coordinates to recenter worldmap
data$long <- ifelse(data$long < center - 180 , data$long + 360, data$long)
### Function to regroup split lines and polygons
# takes dataframe, column with long and unique group variable,
#returns df with added column named group.regroup
RegroupElements <- function(df, longcol, idcol){
g 300) {
# check if longitude within group differs more than 300 deg, ie if element was split
d mean(range(df[,longcol]))
# we use the mean to help us separate the extreme values
g[!d] <- 1
# some marker for parts that stay in place (we cheat here a little, as we do not take into account concave polygons)
g[d] <- 2
# parts that are moved
}
g <- paste(df[, idcol], g, sep=".") # attach to id to create unique group variable for the dataset
df$group.regroup <- g
df
}
### Function to close regrouped polygons
# takes dataframe, checks if 1st and last longitude value are the same, if not, inserts first as last and reassigns order variable
ClosePolygons <- function(df, longcol, ordercol){
if (df[1,longcol] != df[nrow(df),longcol]) {
tmp <- df[1,]
df <- rbind(df,tmp)
}
df
}
# now regroup
returnframe <- ddply(data, .(id), RegroupElements, "long", "id")
# close polys
if(shapeType[1]=="polygon") {
returnframe <- ddply(returnframe, .(group.regroup), ClosePolygons, "long", "order") # use the new grouping var
}
ggplot(returnframe,aes(x=long,y=lat))+geom_polygon(aes(group=group.regroup))
returnframe
}
Thanks a lot for this. I am replicating the steps.
I get this error Error: could not find function "fortify.SpatialLinesDataFrame"
while executing step
rts.ff <- fortify.SpatialLinesDataFrame(rts)
Would you know whats causing this.
Doyou have the ggplot package loaded?
library(ggplot2)
hi
got same error as Gaurav. and ggplot2 is installed (version 0.9.0).
not sure what is going on here, my quick fix was to obtain and running the function from:
https://github.com/hadley/ggplot2/blob/master/R/fortify-spatial.r
prior to the running fortify.SpatialLinesDataFrame line.
einar
ps: thanks for sharing this example claudia
I'm a bit of novice. Would someone be so kind as to post complete working code for ggplot2 0.9? I'm not quite sure how to integrate fortify-spatial.r. Thank you, thank you. I was almost about to give up on great circles over the Pacific.
KP,
Download the entire code for fortify-spatial.r to your working directory.
In R:
source ("fortify-spatial.r") # if necessary adjust path
Then run the fortify.SpatialLinesDataFrame command from the code above.
if you have a problem like :
Error: could not find function "fortify.SpatialLinesDataFrame"
change this step.
rts.ff <- fortify.SpatialLinesDataFrame(rts)
to.
rts <- as(rts, "SpatialLinesDataFrame")
rts.ff <- fortify(rts)
it will be okay.
Thanks Andrew, this worked perfectly.
Thanks for sharing!
I was getting the error message "Error: Use 'theme' instead. (Defunct; last used in version 0.9.1)" and replaced the last few lines of code with the following and it workded:
ggplot() +
geom_polygon(aes(long.recenter,lat,group=group.regroup), size = 0.2, fill="#f9f9f9", colour = "grey65", data=worldmap.cp) +
geom_line(aes(long.recenter,lat,group=group.regroup, color=freq, alpha=freq), size=0.4, data= gcircles.rg) + # set transparency here
scale_colour_gradient(low="#fafafa", high="#EE0000") + # set color gradient here
theme(panel.background = element_blank(), panel.grid.minor = element_blank(), panel.grid.major = element_blank(), axis.ticks = element_blank(), axis.title.x = element_blank(), axis.title.y = element_blank(), axis.text.x = element_blank(), axis.text.y = element_blank(), legend.position = "none") +
ylim(-60, 90) +
coord_equal()