ggplot2: Legend won't show - r

Edited as a response to #hrbrmstr's great answer:
I'm mapping 2 groups of countries and would like a legend to go with it. Since I'm using my own data, it's hard to provide a MRE. In my previous code I was using geom_polygon to plot the shapes (read from a shapefile) because fortify throws away the additional data the associated dataframe:
ggplot() + aes(long, lat, group = group) + theme_bw() +
scale_x_continuous(limits = c(-15, 35)) +
scale_y_continuous(limits = c(25, 75)) +
scale_fill_manual("Affected?", labels = c("Yes", "No"),
values = c("gray10", "gray80")) +
geom_polygon(data = affected.countries, fill = "gray10", color = "black") +
geom_polygon(data = unaffected.countries, fill = "gray80", color = "black")
The result:
Now I've tried taking a page from #hrbrmstr's playbook. Because fortify throws away my other columns, I made 2 subsets of my original data, which is of class SpatialPolygonsDataFrame. I fortify them and given them a dummy variable that shows what I need and then try to plot them using the boolean column to control the fill:
affected.countries <- fortify(affected.countries)
affected.countries$affected <- T
unaffected.countries <- fortify(unaffected.countries)
unaffected.countries$affected <- F
# all.countries now contains column affected
all.countries <- rbind(affected.countries, unaffected.countries)
gg <- ggplot() + coord_map(xlim = c(-13, 35), ylim = c(32, 71)) + theme_bw()
# Base map
gg <- gg + geom_map(data = all.countries, map = all.countries,
aes(x = long, y = lat, map_id = id),
fill = NA, color="gray10")
# Base map looks OK
gg
# Add filled data
gg <- gg + geom_map(data = all.countries, map = all.countries,
aes(fill = affected, map_id = id),
fill="gray10")
# For some reason, everything is filled!
gg <- gg + scale_fill_manual("Affected?", labels = c("No", "Yes"),
values = c("gray80", "gray10"))
# And the legend isn't shown
gg
For these results:
I thought the problem was that my fill argument wasn't in aes, but here it is. Sadly, I don't see a way of using a second dataframe as in #hrbrmstr's answer, since I don't have the appropriate columns in my data, but I thought the boolean column would solve it. And although I could do it by hacking the code from the answer, I'd prefer my own country boundaries.
Notably, if I include the fill argument outside the aes but inside the geom_polygon call, the fill works correctly, but the legend isn't shown. If I specify the color in aes, a seemingly random color is shown.
What principle am I missing? Thanks again!

Here's a more complete example, esp since there was no projection used nor data provided in the original question:
library(ggplot2)
europe <- map_data('world',
region=c('Switzerland', 'Liechtenstein', 'Luxembourg',
'UK', 'Belgium', 'Norway', 'Germany', 'Ireland',
'Austria', 'France', 'Netherlands', 'Sweden',
'Denmark','Italy' ))
condition <- data.frame(region=c("Ireland", "France", "Netherlands",
"Sweden", "Finland", "UK", "Germany"),
affected=c(TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE),
stringsAsFactors=FALSE)
gg <- ggplot()
# base map
gg <- gg + geom_map(data=europe, map=europe,
aes(x=long, y=lat, map_id=region),
color="#7f7f7f", fill="white", size=0.25)
# fills according to affected
gg <- gg + geom_map(data=condition, map=europe,
aes(fill=affected, map_id=region),
color="#7f7f7f", size=0.01)
# a minimally decent projection tho one of the conical ones is prbly better
gg <- gg + coord_map(xlim=c(-13, 35), ylim=c(32, 71))
# fills for the aes mapping
gg <- gg + scale_fill_manual("Affected?", labels = c("No", "Yes"),
values = c("gray80", "gray10"))
# no need for axis labels since it's a map
gg <- gg + labs(x=NULL, y=NULL)
gg <- gg + theme_bw()
gg
As Gregor pointed out, this is a sub-optimal projection (but since there was no projection in the original post I didn't want to just toss one in besides the default Mercator). In case you wonder why a few of us obsess over projections on SO, take this one report as an example (a whole document from one of many workshops on just European projections).
There are a couple other choices. First, Lambert Conic Conformal, which you can use by changing the coord_map to:
gg <- gg + coord_map("lambert", lat0=32, lat1=71, xlim=c(-13, 35), ylim=c(32, 71))
Even though the linked-to document doesn't suggest using it, Gilbert is also (IMO) a decent one to use:
gg <- gg + coord_map("gilbert", xlim=c(-13, 35), ylim=c(32, 71))
The PDF also suggests Azimuth Equal Area is an preferred option:
gg <- gg + coord_map("azequalarea", xlim=c(-13, 35), ylim=c(32, 71))

I am not following your entire questions with the updates involved. But perhaps it could be a simple as setting guide = TRUE in scale_fill_manual(). See Turning off some legends in a ggplot.

Related

Mapping sampling sites on a map in R + creating a zoom in of a region in the same map

for my PhD project, I'd like to show my sampling sites (coordinates) on a map showing them first on a map of NZ and then building a zoom in of the region (coordinates that I pick myself) to show the sampling sites in that specific region. I am very new to R and I am finding a bit frustrating.
I managed to build a map of NZ (code follows) but how can I add the data points on it and how can I create a zoom in of a certain region and adding data points on it as well??
NZ <- map_data("nz",xlim = c(166, 179), ylim = c(-48, -34))
ggplot() +
geom_path(aes(long, lat, group=group), data=NZ, color="black") +
coord_equal() +
scalebar(NZ, dist = 100, dist_unit = "km", st.size=3, height=0.01, model = 'WGS84', transform = TRUE)
Thanks to whoever will help me!!
For example:
library(tidyverse)
dunedin <- tibble(X=170.5, Y=-45 - 52/60, Text="Dunedin")
NZ <- map_data("nz",xlim = c(166, 179), ylim = c(-48, -34))
ggplot() +
geom_path(aes(long, lat, group=group), data=NZ, color="black") +
geom_point(data=dunedin, aes(x=X, y=Y), colour="blue") +
geom_label(data=dunedin, aes(x=X, y=Y, label=Text), colour="blue", nudge_x=1) +
coord_equal()
Incidentally, scalebar isn't part of ggplot2, so your example isn't self-contained. That's not a major issue here, but could be in another situation.

R ggplot2 whitespace in legend key when using override.aes for size

I've recently gotten frustrated by the same issue as reported here regarding the hacks required to make up for ggplot not adding NA fills to the legend (please if anyone knows an update to this, do tell).
I therefore created a similar hack, but in a fairly complex plot where I was trying to demonstrate several things at once. The plot required setting the size of points to a small value so therefore I needed override.aes to have points large enough in the legend. Since I was using alpha as well as colour, I introduced both into the same legend key.
However, bizarrely (at least to me), the aes override creates ugly whitespace between the legend key box margin and the fill, as shown in the map with and without the size aes override:
How does this happen, and how can I make it go away? Any help would be much appreciated.
Here's the code to reproduce this. As said, my true plot is more complex than this but this shows the general idea.
## create an example using the world data
require(rworldmap)
require(rgeos)
require(ggplot2)
require(viridis)
## Load world map and subset
world <- getMap()
world <- world[!world#data$ADMIN %in% c("French Southern and Antarctic Lands",
"Heard Island and McDonald Islands",
"French Guiana"),] #NA entries for these in REGION col
world <- world[world#data$REGION == "South America",]
world#data$id = rownames(world#data)
## create example where we have NA for the fill variable
world#data$fillvar <- world#data$LAT
world#data$fillvar[round(world#data$LAT) == -9] <- NA #Peru
## create spatial data frame for plotting
polydf = ggplot2::fortify(world, region="id")
polydf <- merge(polydf, world#data, by="id")
## create arbitrary grouping variable
polydf$groupvar <- ifelse(polydf$NAME == "Bolivia", "Bolivia", "NotBolivia") # the two main classes
# simulate hack where we name the NA fill case as a new grouping level (which we will use alpha for)
polydf$groupvar[is.na(polydf$fillvar)] <- 'Removed'
polydf$groupvar <- factor(polydf$groupvar)
## create centroid points to show the grouping with points, again manual hack for NA
pointsdf <- gCentroid(world[!is.na(world#data$fillvar),],byid=TRUE)
pointsdf <- as.data.frame(pointsdf#coords)
pointsdf$groupvar <- ifelse(rownames(pointsdf)=="Bolivia","Bolivia","NotBolivia")
pointsNA <- gCentroid(world[is.na(world#data$fillvar),], byid=TRUE)
pointsNA <- as.data.frame(pointsNA#coords)
pointsNA$groupvar <- "Removed"
pointsdf <- rbind(pointsdf, pointsNA)
pointsdf$groupvar <- factor(pointsdf$groupvar)
## plot with override.aes for size
sizeover <- ggplot(polydf) + theme_bw() +
aes(long,lat,group=group) +
geom_polygon(aes(fill=fillvar, alpha=groupvar)) + # shading also for groupvar
geom_path(color="black") +
coord_equal() +
scale_fill_viridis("Fill", na.value="black") +
geom_point(inherit.aes=FALSE, data=pointsdf, aes(x=x, y=y, group=groupvar, col=groupvar), size=1) +
scale_color_manual('Alpha + colour', values=c("black","transparent","white")) +
scale_alpha_manual('Alpha + colour', values=c(0.8,0.8, 1)) +
theme(legend.key = element_rect(colour = "black"), legend.box = "vertical", legend.position = "top") +
guides(color=guide_legend(override.aes = list(size=2)),
alpha=guide_legend(override.aes = list(alpha=c(1,1,1), fill=c('white','white','black'))))
## plot without override.aes for size
nosizeover <- ggplot(polydf) + theme_bw() +
aes(long,lat,group=group) +
geom_polygon(aes(fill=fillvar, alpha=groupvar)) + # shading also for groupvar
geom_path(color="black") +
coord_equal() +
scale_fill_viridis("Fill", na.value="black") +
geom_point(inherit.aes=FALSE, data=pointsdf, aes(x=x, y=y, group=groupvar, col=groupvar), size=1) +
scale_color_manual('Alpha + colour', values=c("black","transparent","white")) +
scale_alpha_manual('Alpha + colour', values=c(0.8,0.8, 1)) +
theme(legend.key = element_rect(colour = "black"), legend.box = "vertical", legend.position = "top") +
guides(alpha=guide_legend(override.aes = list(alpha=c(1,1,1), fill=c('white','white','black'))))
grid.arrange(sizeover, nosizeover, ncol=2)
The problem exists in both figures (sizeover and nosizeover), it's just more pronounced in the former. The guilty party is the function draw_key_polygon() in ggplot2. It creates some space around the rectangle it draws, in proportion to the size setting (see here). I'm not quite sure why it's written like that, but presumably there's a reason.
The simplest solution is similar to this answer to a different question. Replace the legend-drawing function for geom_polygon(). In the case here, we don't even have to write a new one ourselves, we can just use an existing one:
GeomPolygon$draw_key <- ggplot2::draw_key_rect
sizeover
The only problem that this has created is that now the legend outline appears a bit thin, because the filled rectangle is drawn on top of the outline. We can fix this by making the line a bit thicker, by adding this to the plot:
+ theme(legend.key = element_rect(colour = "black", size = 1))
This seems like a reasonable solution to me.
Note that geom_polygon() will continue using the modified legend drawing function for the remainder of your session. To undo, run:
GeomPolygon$draw_key <- ggplot2::draw_key_polygon

ggplot alter map fill opacity by second variable

I'm creating a cloropleth map of the U.S where each county is colored by the proportion of the 2012 votes for Obama. I'd like to vary the opacity of county overlays by the population (similar to what was done here). I've tried adding alpha = populationVariable to my geom_map aes, but without luck.
Here is my current code. Can anyone point me in the right direction?
gg <- ggplot()
gg <- gg + geom_map(data=mapC, map=mapC, aes(x=long, y=lat, map_id=id, group=group, fill=mapC$proportionObama))
gg = gg+ scale_fill_gradient2(limits= c(0,100), name="Proportion of Votes for Obama", low="#E5000A", high="#0012BF",mid="#720964", midpoint=50)
gg = gg + theme_map() +coord_equal()
gg <- gg + geom_path(data = mapS, aes(long,lat, group=group), colour="gray50", size=.25)
gg = gg + theme(legend.position="right")
gg
I think that alpha needs to be a variable that's mapped to be between 0 and 1. The ggplot documentation always shows a fractional value.
Hue Scale - http://docs.ggplot2.org/0.9.3.1/scale_hue.html
Color Fill Alpha - http://docs.ggplot2.org/current/aes_colour_fill_alpha.html
You don't have reproducible code, so here was a quick test that seemed to work.
library(data.table)
library(ggplot2)
tmp = data.table(
'x'=c(1,2,3),
'y'=c(1,2,3),
'z'=c(.1,.5,.8)
)
p = ggplot()
p = p + geom_point( data=tmp , aes(x=x,y=y,alpha=z))
print(p)

How can I only plot a CONUS map in ggplot2?

So, I have a dataset that has data for not only CONUS but also the island areas and Alaska. Now, I want to plot only the data for CONUS. I know I can subset it easily. But is there any easier way to that? Maybe an option in ggplot I don't know?
Here is the code:
ggplot() +
geom_polygon( data=usamap, aes(x=long, y=lat,group=group),colour="black", fill="white" )+
geom_point(data=df,aes(x=Longitude,y=Latitude))+
scale_colour_gradientn(name = "DL",colours = myPalette(10))+
xlab('Longitude')+
ylab('Latitude')+
coord_map(projection = "mercator")+
theme_bw()+
theme(legend.position = c(.93,.20),panel.grid.major = element_line(colour = "#808080"))+
ggsave("test.png",width=10, height=8,dpi=300)
Here is the dataset:
https://www.dropbox.com/s/k1z5uquhtc2b9nd/exported.csv?dl=0
You can do it and also use a decent projection at the same time:
library(ggplot2)
library(readr)
library(dplyr)
us <- map_data("state")
us <- fortify(us, region="region")
# for theme_map
devtools::source_gist("33baa3a79c5cfef0f6df")
# read your data and filter out points not in CONUS
read_csv("exported.csv") %>%
filter(Longitude>=-124.848974 & Longitude<=-66.885444,
Latitude>=24.396308 & Latitude<=49.384358) -> data
gg <- ggplot()
gg <- gg + geom_map(data=us, map=us,
aes(x=long, y=lat, map_id=region, group=group),
fill="#ffffff", color="#7f7f7f", size=0.25)
gg <- gg + geom_point(data=data,
aes(x=Longitude, y=Latitude),
color="#cb181d", size=1, alpha=1/10)
gg <- gg + coord_map("albers", lat0=39, lat1=45)
gg <- gg + theme_map()
gg
You have no aesthetic color mapping I can see, so your color scaling will have no impact. I used an alpha for overlapping/close points instead.
Looking at your reposted question, I think by far the best way is a simple subset. From this link, you can see the box around the continental USA is:
(-124.848974, 24.396308) - (-66.885444, 49.384358)
if you do a simple subset:
usamap<-usamap[usamap$Longitude > -124.848 &
usamap$Longitude < -66.886 &
usamap$Latitude > 24.3964 &
usamap$Latitude < 49.3844, ]
You will get your required points.
Since you said you'd be interested in a ggplot2 solution, you might consider modifying the arguments in coord_map(). For example:
coord_map(project = "globular",
xlim = c(-125, -66),
ylim = c(24, 50))
Of course, the "mercator" argument works, too!

Why does coord_map produce a weird output?

I'm trying to draw a world map using ggplot. My code is in my gist file. The output is correct when I don't use coord_map but very strange when I use coord_map :
ggplot(data = test, aes(fill = x)) +
geom_map(aes(map_id = id), map =world.ggmap, color = "white") +
expand_limits(x = world.ggmap$long, y = world.ggmap$lat) +
theme_tufte() +
coord_map()
ggplot(data = test, aes(fill = x)) +
geom_map(aes(map_id = id), map =world.ggmap, color = "white") +
expand_limits(x = world.ggmap$long, y = world.ggmap$lat) +
theme_tufte()
I've got the same error when I use data from the maps package :
library(ggplot2)
library(maps)
world <- map_data("world")
ggplot() +
geom_map( data=world, aes(x=long, y=lat, group = group, map_id = region),colour="white", fill="grey10", map = world ) +
coord_map()
Does anyone has an answer ?
I had a similar problem before, due to longitude values outside the range [-180,180]. In your example the data do not have this problem but my trick seems to work also here. In my case I just used 'xlim' to exclude the problematic data.
This solution seems to work in your case also (I used the code from your gist):
map+coord_map(xlim=c(-180,180))
It produces the following map:
There is still a problem with Antarctica, you can also consider clipping it too if you don't need this area:
map+coord_map(xlim=c(-180,180), ylim=c(-60, 90))
Another solution is to use wrld_simpl from maptools instead, but it retains issues with Antarctica.
require(maptools)
require(ggmap)
md <- map_data("world2")
data(wrld_simpl)
ggplot(wrld_simpl, aes(group = group, x = long, y = lat)) +
geom_map() +
coord_map()
I'm sure that is quite late but the same problem is still happening in ggplot.
If you're trying to zoom-in use the following approach.
ggplot()+...+xlim(c(-100, -25))+ ylim(c(-60, 20))
Good luck!

Resources