Daniel Lee
Daniel Lee

Reputation: 2070

ggplot2: Legend won't show

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:

Plot with no legend

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:

Base map Filled map

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!

Upvotes: 1

Views: 2321

Answers (2)

Paul Lemmens
Paul Lemmens

Reputation: 635

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.

Upvotes: 0

hrbrmstr
hrbrmstr

Reputation: 78842

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

enter image description here

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))

enter image description here

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))

enter image description here

The PDF also suggests Azimuth Equal Area is an preferred option:

gg <- gg + coord_map("azequalarea", xlim=c(-13, 35), ylim=c(32, 71))

enter image description here

Upvotes: 4

Related Questions