1

I'm trying to use geom_path and geom_text to create a labeled map, but it is not going well. I can use each geom_path and geom_text separately, but I can't seem to get them to work together. I think it has something to do with geom_polygon, but I'm not sure what.

Here is how I prepared my shapefile for mapping:

meck.hiv <- as(meck.hiv, "Spatial")
meck.hiv@data$seq_id <- seq(1:nrow(meck.hiv@data)) #create unique id for each polygon
meck.hiv@data$id <- rownames(meck.hiv@data)
meck.hivdf <- fortify(meck.hiv) #fortify the data
zipcodedf <- merge(meck.hivdf, meck.hiv@data,
               by = "id")# merge the "fortified" data with the data from our spatial object
cnames <- aggregate(cbind(long, lat) ~ zip, data=zipcodedf, FUN=function(x)mean(range(x))) #get the names of our zipcode (and center the coordinates for labels)

With this, I was able to get the following maps:

With labels, no paths:

p2 <- ggplot(data = zipcodedf, aes(x = long, y = lat)) +
  geom_polygon(aes(group=group, fill=hispanic.dis)) +
  geom_text(data=cnames, aes(long, lat, label = zip), size=3, fontface='bold', color="black")+ #This put in zip code names
  scale_fill_brewer(breaks=c(1, 2, 3, 4, 5, 6, 7), labels=c("<5", "5-10", "11-15", "16-20", "21-25", "26-30", "31+"), palette="Reds",
                    na.value="darkgrey") +  
  coord_equal() +
  theme(panel.background= element_rect(color="black")) +
  theme(axis.title = element_blank(), axis.text = element_blank()) +
  labs(title = "Hispanic Population by Zip Code", fill="Hispanic Population (% of Total)")

Resulting map with labels but no path

Paths, but no labels:

p3 <- ggplot(data = zipcodedf, aes(x = long, y = lat, group = group, fill = hispanic.dis)) +
  geom_polygon() +
  geom_path(color = "black", size = 0.2)+ #Oddly, use of geom_path with the above leads to weird stuff, but we can customize map lines without labels here
  scale_fill_brewer(breaks=c(1, 2, 3, 4, 5, 6, 7), labels=c("<5", "5-10", "11-15", "16-20", "21-25", "26-30", "31+"), palette="Reds",
                    na.value="darkgrey") + 
  coord_equal() +
  theme(panel.background=element_blank())+
  theme(panel.background= element_rect(color="black")) +
  theme(axis.title = element_blank(), axis.text = element_blank()) +
  labs(title = "Hispanic Population by Zip Code", fill="Hispanic Population(% of Total)")

Resulting path with paths but no labels

Trying to combine

p4 <- ggplot(data = zipcodedf, aes(x = long, y = lat)) +
  geom_polygon(aes(group=group, fill=hispanic.dis)) +
  geom_text(data=cnames, aes(long, lat, label = zip), size=3, fontface='bold', color="black")+
  geom_path(color = "black", size = 0.2)+ #Oddly, use of geom_path with the above leads to weird stuff, but we can customize map lines without labels here
  scale_fill_brewer(breaks=c(1, 2, 3, 4, 5, 6, 7), labels=c("<5", "5-10", "11-15", "16-20", "21-25", "26-30", "31+"), palette="Reds",
                    na.value="darkgrey") + 
  coord_equal() +
  theme(panel.background=element_blank())+
  theme(panel.background= element_rect(color="black")) +
  theme(axis.title = element_blank(), axis.text = element_blank()) +
  labs(title = "Hispanic Population by Zip Code", fill="Hispanic Population(% of Total)")

Resulting screwed up plot with paths and labels

My trouble-shooting suggests that there's something about how geom_path is interacting with geom_polygon combined with ggplot(), but if I move all the instructions into geom_polygon, path doesn't show up at all (although a plot will generate), and if I move all the instructions into ggplot(), I get an error: "Error in FUN(X[[i]], ...): object 'group' not found" (below).

p5 <- ggplot(data = zipcodedf, aes(x = long, y = lat, group = group, fill = hispanic.dis)) +
  geom_polygon() +
  geom_path(color = "black", size = 0.2)+ 
  geom_text(data=cnames, mapping=aes(x=long, y=lat))+#Oddly, use of geom_path with the above leads to weird stuff, but we can customize map lines without labels here
  scale_fill_brewer(breaks=c(1, 2, 3, 4, 5, 6, 7), labels=c("<5", "5-10", "11-15", "16-20", "21-25", "26-30", "31+"), palette="Reds",
                    na.value="darkgrey") + 
  coord_equal() +
  theme(panel.background=element_blank())+
  theme(panel.background= element_rect(color="black")) +
  theme(axis.title = element_blank(), axis.text = element_blank()) +
  labs(title = "Hispanic Population by Zip Code", fill="Hispanic Population(% of Total)")

Any help is appreciated. Please let me know if I need to provide more information on anything.

1 Answer 1

2

{ggplot2}'s layering and inheritance can be confusing at times. I hope the following helps. Also check out whether you really want an "additional" geom_path() layer. It feels like you want to depict/highlight the county boundaries. Please also note the use of color for setting the boundary(line) color for a layer in the example.

I have to emulate your problem, as you did not provide a reproducible example. The initial code is to get me the map data for North Carolina. I construct a cnames dataframe for the county names. You should be able to apply this to your zip-code problem at the county level ... :)

library(dplyr)
library(ggplot)
library(maps)    # helper package to get some map data
library(mapdata) # helper for some map data

counties <- map_data("county") %>% filter(region == "north carolina")
head(counties)

yields a data frame

       long      lat group order         region subregion
1 -79.53800 35.84424  1857 54915 north carolina  alamance
2 -79.54372 35.89008  1857 54916 north carolina  alamance
3 -79.53800 35.98175  1857 54917 north carolina  alamance
4 -79.52081 36.23385  1857 54918 north carolina  alamance
5 -79.26298 36.23385  1857 54919 north carolina  alamance
6 -79.27444 35.90726  1857 54920 north carolina  alamance

Plotting the counties only (note: this would be your zip-code level). To explain the principle of layer inheritance, I "set" all parameters in the layer call (i.e. geom_polygon()):

ggplot() + 
  geom_polygon(data = counties
              , aes(x = long, y = lat, group = group)  # aesthetic mapping
              , color = "white"                        # fixed value for "line"
              , fill = "lightblue")                    # fixed value for "fill"

enter image description here

If you now would add the geom_path() layer without giving it aesthetics, the plot will not change. Check that I set the color to blue and the linesize to 2 for demo purposes.

ggplot() + 
  geom_polygon(data = counties, aes(x = long, y = lat, group = group), colour = "white", fill = "lightblue") + 
# ---------------- adding a layer with no aesthetics -----------
  geom_path(color = "blue", size = 2)  # ... no joy!

If you now move the data and aesthetics to ggplot() "base"-layer, also path will inherit the aesthetics. In this case path will draw the "outlines" of the of the grouped lat/lon positions. The layer order and color/size of geom_path() will "overwrite" the white coloured polygon lines.

ggplot(data = counties, aes(x = long, y = lat, group = group)) + 
  geom_polygon( colour = "white", fill = "lightblue") + 
#------------ path layer with inherited "polygon" grouping
  geom_path(color = "blue", size = 2) 

enter image description here

Next let's create the (zipcodes :) ) aka text labels by averaging the lat/lon values for the different polygon segment points.

cnames <- counties %>% 
   group_by(subregion) %>% 
   summarise(long = mean(long), lat = mean(lat) # averaging for "mid"-point
)
> cnames
# A tibble: 100 x 3
   subregion  long   lat
   <chr>     <dbl> <dbl>
 1 alamance  -79.4  36.0
 2 alexander -81.2  35.9
 3 alleghany -81.1  36.5

Now add a geom_text() layer to show the (zip codes) aka subregion names.

ggplot(data = counties, aes(x = long, y = lat, group = group)) + 
   geom_polygon( colour = "white", fill = "lightblue") + 
   geom_path(color = "blue", size = 2) +
# --------------- adding a geom_text() layer 
   geom_text(data = cnames, aes(x = long, y = lat), color = "green")
## ------- uuummmppfff throws an error
Error in FUN(X[[i]], ...) : object 'group' not found

This throws an error. So why is that? Implicitly the group aesthetic in the ggplot() call is understood by geom_polygon() and geom_path() ... however geom_text() has issues with.

Moving the group aesthetics to the polygon layer ...

ggplot(data = counties, aes(x = long, y = lat)) + 
    geom_polygon( aes(group=group), colour = "white", fill = "lightblue") + 
    geom_path(color = "blue", size = 2) + 
    geom_text(data = cnames, aes(x = long, y = lat, label = "subregion"), color = "green")

does the trick but corrupts the geom_path() layer.
What happens here is that the data points (i.e. lat/lon) are no longer grouped, and ggplot connects the end points in the order they appear in the counties data frame. These are the jig-jag lines you see across your plot.

Accordingly, you would need to put another aes(group=group) for the geom_path layer! ... assuming you really want the path for the outlines.

enter image description here

ggplot(data = counties, aes(x = long, y = lat)) + 
    geom_polygon( aes(group=group), colour = "white", fill = "lightblue") + 
    geom_path(aes(group=group), color = "blue", size = 2) + 
    geom_text(data = cnames, aes(x = long, y = lat, label = "subregion"), color = "green")

enter image description here

Obviously the color = "white" is overwritten by the geom_path() call. You may skip one or the other.

As a rule of thumb, ggplot works well with "long" data tables. The moment you add a 2nd (or more other data objects) make sure to track which aesthetics are required and/or inherited from one layer to the other. In your original example, you could move the geom_path() upwards to have the group = group aesthetics from the geom_polygon() call.
In case of doubt, always populate the data = ... and aes() for each layer before combining (and inheriting) parameters across layers.

Good luck!

Sign up to request clarification or add additional context in comments.

2 Comments

This was great, thanks! It turns out I didn't need path after all. :) Thank you for your in-depth explanation!
No prob @Kiran. I am happy, if it was helpful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.