Using ggplot to plot a map from a matrix - r

I wanted to create some maps by using ggplot2, but I am struggling to find the best way. Since I have a list ("models") with matrix, I wanted to use ggplot because then, it should be easier to create a multiplot (with facet_wrap..but just a thought).
I will create an reproducible example for doing this:
#European corrdinates
lon <- rep(loni,38) #longitude values -13W-34E
lat <- rep(lati, each = 48) #latitude values 34N-70N
#Matrix of values
mod <- matrix( rnorm(48*38,mean=0,sd=1), 48, 38)
#Create a list of matrix (as my real data)
dat.mod <- rep(list(mod), 4)
names(dat.mod) <- c("DJF","MAM","JJA","SON")
#Order data
md <- melt(dat.mod)
md$lon <- lon
md$lat <- lat
md$group <- "Model1"
names(md) <- c("X1","X2","SI","Season","lon","lat","model")
#
#First try:
#Problem here: I don't know how to add the map
brks <- seq(0, 2.2, by=0.2)
cols <- colorRampPalette(c("skyblue1", "lightcyan2", "lightyellow", "yellow", "orange", "red3"))(length(brks)-1)
m <- ggplot(data = md, aes(x = lon, y = lat, fill = SI)) +
geom_raster() +
scale_fill_gradientn(colours = cols, na.value = NA)+
facet_wrap(~ model~ Season, nrow =11,ncol=4)+
theme(strip.background = element_blank(),
strip.text.x = element_blank())
#I would get this image:
#Now, as a second try to add a map of Europe, I used:
map <- ggplot()
map <- map + coord_fixed()
map <- map + geom_polygon(data=map_data(map="world"), aes(x=long, y=lat, group=group), fill=NA, color="black", size=0.01)
map <- map + coord_cartesian(xlim=range(md$lon), ylim=range(md$lat))
map <- map + geom_tile(data=md, aes(x=lon, y=lat, fill=SI), alpha=I(0.7))
map <- map + scale_fill_gradientn(colours=cols)
map <- map + facet_grid(model~Season)
map <- map + theme(legend.position="none")
Now, I would have:
The problem with the second one is that I don't know how to change to background with white, and make the map more visible..is that possible?? The thing is, I wanted to plot 11 rows by 4 col. so when plotting all together it doesn't look very well...any suggestion?
Really appreciate any help.
Many thanks

Related

Overlay worldmap in R with rgeolocate plot

In my previous question, I presented that I have a table of "posts" with IPs, and that I wanted to geolocate them.
Geolocating a large number of posts based on IP Addresses. (880,000 rows)
The answer demonstrated how to use rgeolocate to achieve this, and after some effort in learning R I have managed to accomplish the same result:
library(iptools)
library(rgeolocate)
library(tidyverse)
library(readxl)
library(rworldmap)
library(ggmap)
library(rworldxtra)
post <- read_excel("filepath/post.xlsx")
view(post)
## grab my ips and, format them
ips <- unlist(post[,3], use.names=FALSE)
#geolocte them
system.time(
rgeolocate::maxmind(
ips, "~/R/GeoLite2-City.mmdb", c("longitude", "latitude")
) -> xdf
)
#user system elapsed
#6.04 0.02 6.05
xdf %>%
count(longitude, latitude) -> pts
#And, plot them:
ggplot(pts) +
geom_point(
aes(longitude, latitude, size = n),
shape=21, fill = "steelblue", color = "white", stroke=0.25
) +
ggalt::coord_proj("+proj=wintri") +
ggthemes::theme_map() +
theme(legend.justification = "center") +
theme(legend.position = "bottom")
The result is shown here:
This plot shows exactly the sort of grouping that I would expect, based on the data. So one step of success!
Of course, the next logical step is to increase the resolution and add an overlay of a world map. I have been unable to achieve either of these goals.
Using this code, I can make a high resolution world map:
newmap <- getMap(resolution = "high")
plot(newmap)
And the result is shown here:
Somehow I just can't achieve a combination of the map AND the data being plotted. It seems like any attempt to create the map needs me to plot the map itself, and any attempt to add points to that fail. For example:
newmap <- getMap(resolution = "high")
plot(newmap)
ggmap(newmap) +
geom_point(data = pts, aes(x = longitude, y = latitude, size=n),
shape=21, fill = "steelblue", color = "white", stroke=0.25)
Error: ggmap plots objects of class ggmap, see ?get_map
I have been trying to work based on the advice of http://www.milanor.net/blog/maps-in-r-plotting-data-points-on-a-map/ but this website focuses on a map of Europe, and I want to show my data on a map of the world.
Thank you for your help.
library(iptools)
library(rgeolocate)
library(tidyverse)
ips <- ip_random(1000000)
rgeolocate::maxmind(
ips, "~/Data/GeoLite2-City.mmdb", c("longitude", "latitude")
) -> xdf
xdf %>%
mutate(
longitude = (longitude %/% 5) * 5,
latitude = (latitude %/% 5) * 5
) %>%
count(longitude, latitude) -> pts
wrld <- tbl_df(map_data("world"))
wrld <- filter(wrld, region != "Antarctica")
ggplot() +
geom_map(
map = wrld, data = wrld, aes(long, lat, map_id=region),
color = "black", fill ="white", size=0.125
) +
geom_point(
data = pts, aes(longitude, latitude, size = n),
shape=21, fill = "steelblue", color = "white", stroke=0.25
) +
scale_size(name = "# IPs", label=scales::comma) +
ggalt::coord_proj("+proj=wintri") +
ggthemes::theme_map() +
theme(legend.justification = "center") +
theme(legend.position = "bottom")

display data on a map

I need to plot my variable Omega as a heat map on the Pacific Ocean.
I have the coordinates and the Omega values but ggplot would only plot a world map while my omega appears as a separate figure (See image attached)
This is my data where y and x are Lat and Longs for my sites
Country Omega y x
1 Guam 3.28 144.7822 13.66660
2 Guam 3.28 144.7605 13.62500
This is the code I am using
map.world <- map_data(map = "world")
gg <- ggplot()
gg <- gg + theme(legend.position = "none")
gg <- gg +
geom_map(data = map.world, map = map.world,
aes(map_id = region, x = long, y = lat),
fill = "white", colour = "black", size = 0.25) +
theme_bw()
omega <- read.csv("map.csv", header = T)
head(omega)
g <- gg +
geom_polygon() +
geom_point(data = omega, aes(y = y, x = x, color = Omega)) +
theme_minimal()
I need to plot my Omega in my study sites and generate a heat map. Please any help would be very appreciated. Thank you very much
You might want to have a look at the leaflet package, it’s easier to use and better
library(leaflet)
df <- data.frame(Country=c("Guam","Guam"),
Omega=c(3.28,3.28),
y=c(144.7822, 144.7605),
x=c(13.6660, 13.6250))
m <- leaflet(data=df)
content <- paste("Omega Value:",df$Omega)
m %>% addCircles(lng=~y, lat=~x, radius=df$Omega, popup=content) %>%
setView(lng=144.793732, lat=13.444304, zoom=10)

Fill voronoi polygons with ggplot

i want to fill voronoi polygons with colours based on a variable (z in my case). Here's the code:
library(rgdal)
library(deldir)
library(dplyr)
library(ggplot2)
library(UScensus2010)
x<-c(0,55,105,165,270,65,130,155,155,225,250,295,
30,100,110,135,160,190,230,300,30,70,105,170,
210,245,300,0,85,175,300,15,60,90,90,140,210,
260,270,295,5,55,55,90,100,140,190,255,285,270)
y<-c(305,310,305,310,310,260,255,265,285,280,250,
260,210,240,225,225,225,230,210,215,160,190,
190,175,160,160,170,120,135,115,110,85,90,90,
55,55,90,85,50,50,25,30,5,35,15,0,40,20,5,150)
z<-c(870,793,755,690,800,800,730,728,710,780,804,
855,813,762,765,740,765,760,790,820,855,812,
773,812,827,805,840,890,820,873,875,873,865,
841,862,908,855,850,882,910,940,915,890,880,
870,880,960,890,860,830)
dati<-data.frame(x,y,z)
vor_pts <- SpatialPointsDataFrame(cbind(dati$x,dati$y),dati, match.ID=TRUE)
SPointsDF_to_voronoi_SPolysDF <- function(sp) {
# tile.list extracts the polygon data from the deldir computation
vor_desc <- tile.list(deldir(sp#coords[,1], sp#coords[,2]))
lapply(1:(length(vor_desc)), function(i) {
# tile.list gets us the points for the polygons but we
# still have to close them, hence the need for the rbind
tmp <- cbind(vor_desc[[i]]$x, vor_desc[[i]]$y)
tmp <- rbind(tmp, tmp[1,])
# now we can make the Polygon(s)
Polygons(list(Polygon(tmp)), ID=i)
}) -> vor_polygons
# hopefully the caller passed in good metadata!
sp_dat <- sp#data
# this way the IDs _should_ match up w/the data & voronoi polys
rownames(sp_dat) <- sapply(slot(SpatialPolygons(vor_polygons),
'polygons'),
slot, 'ID')
SpatialPolygonsDataFrame(SpatialPolygons(vor_polygons),
data=sp_dat)
}
vor <- SPointsDF_to_voronoi_SPolysDF(vor_pts)
vor_df <- fortify(vor)
ggplot() +
geom_point(data = dati, aes(x, y), shape=21,
color = "white", fill = "steel blue") +
geom_map(data = vor_df, map = vor_df, aes(x = long, y = lat, map_id = id),
color = "#a5a5a5", fill = "#FFFFFF00", size = 0.25)
Now I have this image:
I want that each polygon should be mapped to a color scale based on the value of variable z... how can i do that?? Thanks!!!
As far as I know, you can't use a vector that isn't the same length as the data problem itself (in this case the number of rows in vor_df) to specify fill colors. However, if I make the assumption that each of the elements from z is to be applied to the id of a shape from vor_df, you could do this;
ggplot() +
geom_map(data=vor_df, map=vor_df,aes(x=long, y=lat, map_id=id, fill = id), size=0.25) +
geom_path(data=vor_df,aes(x=long, y=lat, map=id)) +
geom_point(data = dati, aes(x, y),shape=21, color="white", fill="steelblue")
This maps the id column to each polygon as a color. It should be easy enough to add a seperate column to add a value to each id based on vector z if you need it in the legend or any number of other things.
Though, this is all on the assumption that each id directly correlates to a number from vector z.
Simply use tileplot in latticeExtra package:
library(deldir)
library(latticeExtra)
tileplot(z~x*y, dati)
here is the output

Stacked bubble chart, "bottom aligned"

New to programming and first time post.
I'm trying to create a stacked bubble chart to display how a population breaks down into it's proportions. My aim is to write this as a function so that I can use it repeatedly easily, but I need to get the meat of the code sorted before turning it to a function.
This is the type of plot I would like:
This is the code I've tried so far:
library(ggplot2)
# some data
observations = c(850, 500, 200, 50)
plot_data = data.frame(
"x" = rep.int(1,length(observations))
,"y" = rep.int(1,length(observations))
, "size" = rep.int(1,length(observations))
,"colour" = c(1:length(observations))
)
# convert to percentage for relative sizes
for (i in 1:length(observations))
{
plot_data$size[i] = (observations[i]/max(observations))*100
}
ggplot(plot_data,aes(x = x, y = y)) +
geom_point(aes(size = size, color = colour)) +
scale_size_identity() +
scale_y_continuous (limits = c(0.5, 1.5)) +
theme(legend.position = "none")
This produces a bullseye type image.
My approach has been to try and work out how the circle radii are calculated, and then update the y value in the for loop for each entry such that all the circles touch at the base - this is where I have been failing.
So my question:
How can I work out what the y coordinates for each circle needs to be?
Thank you for any help and hints.
I think this simplifies the answer that Henrick found:
circle <- function(center, radius, group) {
th <- seq(0, 2*pi, len=200)
data.frame(group=group,
x=center[1] + radius*cos(th),
y=center[2] + radius*sin(th))
}
# Create a named vector for your values
obs <- c(Org1=500, Org2=850, Org3=50, Org4=200)
# this reverse sorts them (so the stacked layered circles work)
# and makes it a list
obs <- as.list(rev(sort(obs)))
# need the radii
rads <- lapply(obs, "/", 2)
# need the max
x <- max(sapply(rads, "["))
# build a data frame of created circles
do.call(rbind.data.frame, lapply(1:length(rads), function(i) {
circle(c(x, rads[[i]]), rads[[i]], names(rads[i]))
})) -> dat
# make the plot
gg <- ggplot(dat)
gg <- gg + geom_polygon(aes(x=x, y=y, group=group, fill=group),
color="black")
gg <- gg + coord_equal()
gg <- gg + ggthemes::theme_map()
gg <- gg + theme(legend.position="right")
gg
You can tweak the guides/colors with standard ggplot functions.

How to control width of multiple plots in ggplot2?

Map Data: InputSpatialData
Yield Data: InputYieldData
Results_using viewport():
EDIT: Results using "multiplot" function as suggested by #rawr (see comment below). I do love the new results, especially that the map is bigger. Nonetheless, the boxplot seems misaligned with the map plot still. Is there a more systematic way to control for centering and placement?
My Question: Is there a way to control for the size of the boxplot plot to make it close in size and centered with the map plot above it?
FullCode:
## Loading packages
library(rgdal)
library(plyr)
library(maps)
library(maptools)
library(mapdata)
library(ggplot2)
library(RColorBrewer)
library(foreign)
library(sp)
library(ggsubplot)
library(reshape)
library(gridExtra)
## get.centroids: function to extract polygon ID and centroid from shapefile
get.centroids = function(x){
poly = wmap#polygons[[x]]
ID = poly#ID
centroid = as.numeric(poly#labpt)
return(c(id=ID, long=centroid[1], lat=centroid[2]))
}
## read input files (shapefile and .csv file)
wmap <- readOGR(dsn=".", layer="ne_110m_admin_0_countries")
wyield <- read.csv(file = "F:/Purdue University/RA_Position/PhD_ResearchandDissert/PhD_Draft/GTAP-CGE/GTAP_Sims&Rests/NewFiles/RMaps_GTAP/AllWorldCountries_CCShocksGTAP.csv", header=TRUE, sep=",", na.string="NA", dec=".", strip.white=TRUE)
wyield$ID_1 <- substr(wyield$ID_1,3,10) # Eliminate the ID_1 column
## re-order the shapefile
wyield <- cbind(id=rownames(wmap#data),wyield)
## Build table of labels for annotation (legend).
labs <- do.call(rbind,lapply(1:17,get.centroids)) # Call the polygon ID and centroid from shapefile
labs <- merge(labs,wyield[,c("id","ID_1","name_long")],by="id") # merging the "labs" data with the spatial data
labs[,2:3] <- sapply(labs[,2:3],function(x){as.numeric(as.character(x))})
labs$sort <- as.numeric(as.character(labs$ID_1))
labs <- cbind(labs, name_code = paste(as.character(labs[,4]), as.character(labs[,5])))
labs <- labs[order(labs$sort),]
## Dataframe for boxplot plot
boxplot.df <- wyield[c("ID_1","name_long","A1B","A1BLow","A1F","A1T","A2","B1","B1Low","B2")]
boxplot.df[1] <- sapply(boxplot.df[1], as.numeric)
boxplot.df <- boxplot.df[order(boxplot.df$ID_1),]
boxplot.df <- cbind(boxplot.df, name_code = paste(as.character(boxplot.df[,1]), as.character(boxplot.df[,2])))
boxplot.df <- melt(boxplot.df, id=c("ID_1","name_long","name_code"))
boxplot.df <- transform(boxplot.df,name_code=factor(name_code,levels=unique(name_code)))
## Define new theme for map
## I have found this function on the website
theme_map <- function (base_size = 14, base_family = "serif") {
# Select a predefined theme for tweaking features
theme_bw(base_size = base_size, base_family = base_family) %+replace%
theme(
axis.line=element_blank(),
axis.text.x=element_text(size=rel(1.2), color="grey"),
axis.text.y=element_text(size=rel(1.2), color="grey"),
axis.ticks=element_blank(),
axis.ticks.length=unit(0.3, "lines"),
axis.ticks.margin=unit(0.5, "lines"),
axis.title.x=element_text(size=rel(1.2), color="grey"),
axis.title.y=element_text(size=rel(1.2), color="grey"),
legend.background=element_rect(fill="white", colour=NA),
legend.key=element_rect(colour="white"),
legend.key.size=unit(1.3, "lines"),
legend.position="right",
legend.text=element_text(size=rel(1.3)),
legend.title=element_text(size=rel(1.4), face="bold", hjust=0),
panel.border=element_blank(),
panel.grid.minor=element_blank(),
plot.title=element_text(size=rel(1.8), face="bold", hjust=0.5, vjust=2),
plot.margin=unit(c(0.5,0.5,0.5,0.5), "lines")
)}
## Transform shapefile to dataframe and merge with yield data
wmap_df <- fortify(wmap)
wmap_df <- merge(wmap_df,wyield, by="id") # merge the spatial data and the yield data
## Plot map
mapy <- ggplot(wmap_df, aes(long,lat, group=group))
mapy <- mapy + geom_polygon(aes(fill=AVG))
mapy <- mapy + geom_path(data=wmap_df, aes(long,lat, group=group, fill=A1BLow), color="white", size=0.4)
mapy <- mapy + labs(title="Average yield impacts (in %) across SRES scenarios ") + scale_fill_gradient2(name = "%Change in yield",low = "red3",mid = "snow2",high = "darkgreen")
mapy <- mapy + coord_equal() + theme_map()
mapy <- mapy + geom_text(data=labs, aes(x=long, y=lat, label=ID_1, group=ID_1), size=6, family="serif")
mapy
## Plot boxplot
boxploty <- ggplot(data=boxplot.df, aes(factor(name_code),value)) +
geom_boxplot(stat="boxplot",
position="dodge",
fill="grey",
outlier.colour = "blue",
outlier.shape = 16,
outlier.size = 4) +
labs(title="Distribution of yield impacts (in %) by GTAP region", y="Yield (% Change)") + theme_bw() + coord_flip() +
stat_summary(fun.y = "mean", geom = "point", shape=21, size= 4, color= "red") +
theme(plot.title = element_text(size = 26,
hjust = 0.5,
vjust = 1,
face = 'bold',
family="serif")) +
theme(axis.text.x = element_text(colour = 'black',
size = 18,
hjust = 0.5,
vjust = 1,
family="serif"),
axis.title.x = element_text(size = 14,
hjust = 0.5,
vjust = 0,
face = 'bold',
family="serif")) +
theme(axis.text.y = element_text(colour = 'black',
size = 18,
hjust = 0,
vjust = 0.5,
family="serif"),
axis.title.y = element_blank())
boxploty
## I found this code on the website, and tried to tweak it to achieve my desired
result, but failed
# Plot objects using widths and height and respect to fix aspect ratios
grid.newpage()
pushViewport( viewport( layout = grid.layout( 2 , 1 , widths = unit( c( 1 ) , "npc" ) ,
heights = unit( c( 0.45 ) , "npc" ) ,
respect = matrix(rep(2,1),2) ) ) )
print( mapy, vp = viewport( layout.pos.row = 1, layout.pos.col = 1 ) )
print( boxploty, vp = viewport( layout.pos.row = 2, layout.pos.col = 1 ) )
upViewport(0)
vp3 <- viewport( width = unit(0.5,"npc") , x = 0.9 , y = 0.5)
pushViewport(vp3)
#grid.draw( legend )
popViewport()
Is this close to what you had in mind?
Code:
library(rgdal)
library(ggplot2)
library(RColorBrewer)
library(reshape)
library(gridExtra)
setwd("<directory with all your files...>")
get.centroids = function(x){ # extract centroids from polygon with given ID
poly = wmap#polygons[[x]]
ID = poly#ID
centroid = as.numeric(poly#labpt)
return(c(id=ID, c.long=centroid[1], c.lat=centroid[2]))
}
wmap <- readOGR(dsn=".", layer="ne_110m_admin_0_countries")
wyield <- read.csv(file = "AllWorldCountries_CCShocksGTAP.csv", header=TRUE)
wyield <- transform(wyield, ID_1 = substr(ID_1,3,10)) #strip leading "TR"
# wmap#data and wyield have common, unique field: name
wdata <- data.frame(id=rownames(wmap#data),name=wmap#data$name)
wdata <- merge(wdata,wyield, by="name")
labs <- do.call(rbind,lapply(1:17,get.centroids)) # extract polygon IDs and centroids from shapefile
wdata <- merge(wdata,labs,by="id")
wdata[c("c.lat","c.long")] <- sapply(wdata[c("c.lat","c.long")],function(x) as.numeric(as.character(x)))
wmap.df <- fortify(wmap) # data frame for world map
wmap.df <- merge(wmap.df,wdata,by="id") # merge data to fill polygons
palette <- brewer.pal(11,"Spectral") # ColorBrewewr.org spectral palette, 11 colors
ggmap <- ggplot(wmap.df, aes(x=long, y=lat, group=group))
ggmap <- ggmap + geom_polygon(aes(fill=AVG))
ggmap <- ggmap + geom_path(colour="grey50", size=.1)
ggmap <- ggmap + geom_text(aes(x=c.long, y=c.lat, label=ID_1),size=3)
ggmap <- ggmap + scale_fill_gradientn(name="% Change",colours=rev(palette))
ggmap <- ggmap + theme(plot.title=element_text(face="bold"),legend.position="left")
ggmap <- ggmap + coord_fixed()
ggmap <- ggmap + labs(x="",y="",title="Average Yield Impacts across SRES Scenarios (% Change)")
ggmap <- ggmap + theme(plot.margin=unit(c(0,0.03,0,0.05),units="npc"))
ggmap
box.df <- wdata[order(as.numeric(wdata$ID_1)),] # order by ID_1
box.df$label <- with(box.df, paste0(name_long," [",ID_1,"]")) # create labels for boxplot
box.df <- melt(box.df,id.vars="label",measure.vars=c("A1B","A1BLow","A1F","A1T","A2","B1","B1Low","B2"))
box.df$label <- factor(box.df$label,levels=unique(box.df$label)) # need this so orderin is maintained in ggplot
ggbox <- ggplot(box.df,aes(x=label, y=value))
ggbox <- ggbox + geom_boxplot(fill="grey", outlier.colour = "blue", outlier.shape = 16, outlier.size = 4)
ggbox <- ggbox + stat_summary(fun.y=mean, geom="point", shape=21, size= 4, color= "red")
ggbox <- ggbox + coord_flip()
ggbox <- ggbox + labs(x="", y="% Change", title="Distribution of Yield Impacts by GTAP region")
ggbox <- ggbox + theme(plot.title=element_text(face="bold"), axis.text=element_text(color="black"))
ggbox <- ggbox + theme(plot.margin=unit(c(0,0.03,0,0.0),units="npc"))
ggbox
grid.newpage()
pushViewport(viewport(layout=grid.layout(2,1,heights=c(0.40,0.60))))
print(ggmap, vp=viewport(layout.pos.row=1,layout.pos.col=1))
print(ggbox, vp=viewport(layout.pos.row=2,layout.pos.col=1))
Explanation:
The last 4 lines of code do most of the work in arranging the layout. I create a viewport layout with 2 viewports arranged as 2 rows in 1 column. The upper viewport is 40% of the height of the grid, the lower viewport is 60% of the height. Then, in the ggplot calls I create a right margin of 3% of the plot width for both the map and he boxplot, and a left margin for the map so that the map and the boxplot are aligned on the left. There's a fair amount of tweaking to get everything lined up, but these are the parameters to play with. You should also know that, since we use coord_fixed() in the map, if you change the overall size of the plot (by resizing the plot window, for example), the map's width will change..
Finally, your code to create the choropleth map is a little dicey...
## re-order the shapefile
wyield <- cbind(id=rownames(wmap#data),wyield)
This does not reorder the shapefile. All you are doing here is prepending the wmap#data rownames to your wyield data. This works if the rows in wyield are in the same order as the polygons in wmap - a very dangerous assumption. If they are not, then you will get a map, but the coloring will be incorrect and unless you study the output very carefully, it is likely to be missed. So the code above creates an association between polygon ID and region name, merges the wyield data based on name, and then merges that into wmp.df based on polygon id.
wdata <- data.frame(id=rownames(wmap#data),name=wmap#data$name)
wdata <- merge(wdata,wyield, by="name")
...
wmap.df <- fortify(wmap) # data frame for world map
wmap.df <- merge(wmap.df,wdata,by="id") # merge data to fill polygons

Resources