I am creating GIS maps for DC using ggplot in R. I am trying to customize my legend bar and labels. I can move legend keys but not the label using gtable_filter. I would like to move the last label '1' near it's legend bar like other labels. Any help appreciated. Image of the map
I am using the below R code :
Data set looks like below
head(d1930)
R Output:
Simple feature collection with 6 features and 355 fields
geometry type: MULTIPOLYGON
dimension: XY
bbox: xmin: -77.0823 ymin: 38.89061 xmax: -77.0446 ymax: 38.94211
epsg (SRID): 4326
proj4string: +proj=longlat +datum=WGS84 +no_defs
fipsstate fipscounty tract NHGISST NHGISCTY GISJOIN GISJOIN2 SHAPE_AREA SHAPE_LEN X GISJOIN.x.1 year cenv1_1 cenv8_1
1 11 001 000001 110 0010 G11000100001 11000100001 1953567 8965.853 1 G11001000001 1930 7889 5885
2 11 001 000002 110 0010 G11000100002 11000100002 1345844 5668.739 10 G11001000002 1930 6250 5164
# # borrowed map theme and code from here
# # https://timogrossenbacher.ch/2016/12/beautiful-thematic-maps-with-ggplot2-only/
theme_map <- function(...) {
theme_minimal() +
theme(
text = element_text(family = "Ubuntu Regular", color = "#22211d"),
axis.line = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
# panel.grid.minor = element_line(color = "#ebebe5", size = 0.2),
panel.grid.major = element_line(color = "white", size = 0.2),
panel.grid.minor = element_blank(),
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "white", color = NA),
legend.background = element_rect(fill = "white", color = NA),
panel.border = element_blank(),
...
)
}
# create the color vector
my.cols <- brewer.pal(4, "Blues")
# compute labels
labels <- c()
# put manual breaks as desired
brks <- c(0,0.15,0.5,0.85,1)
# round the labels (actually, only the extremes)
for(idx in 1:length(brks)){
labels <- c(labels,round(brks[idx + 1], 2))
}
# put labels into label vector
labels <- labels[1:length(labels)-1]
# define a new variable on the data set just as above
d1930$brks <- cut(d1930$pAA,
breaks = brks,
include.lowest = TRUE,
labels = labels)
# define breaks scale and labels scales
brks_scale <- levels(d1930$brks)
labels_scale <- rev(brks_scale)
# draw the plot with legend at the bottom
p <- ggplot(d1930) +
geom_sf(aes(fill=brks),colour = "white")+
coord_sf() +
theme_map() +
theme(legend.position = "bottom",legend.background = element_rect(color = NA))
# provide manual scale and colors to the graph
tester <- p +
# now we have to use a manual scale,
# because only ever one number should be shown per label
scale_fill_manual(
# in manual scales, one has to define colors, well, we have done it earlier
values = my.cols,
breaks = rev(brks_scale),
name = "Share of Population African American",
drop = FALSE,
labels = labels_scale,
guide = guide_legend(
direction = "horizontal",
keyheight = unit(2.5, units = "mm"),
keywidth = unit(85 / length(labels), units = "mm"), title.position = 'top',
# shift the labels around, the should be placed
# exactly at the right end of each legend key
title.hjust = 0.5,
label.hjust = 1, ### change here
nrow = 1,
byrow = T,
# also the guide needs to be reversed
reverse = T,
label.position = "bottom"
)
)
tester
library(grid)
library(gtable)
extendLegendWithExtremes <- function(p){
p_grob <- ggplotGrob(p)
legend <- gtable_filter(p_grob, "guide-box")
legend_grobs <- legend$grobs[[1]]$grobs[[1]]
print(legend_grobs)
# grab the first key of legend
legend_first_key <- gtable_filter(legend_grobs, "key-3-1-1")
legend_first_key$widths <- unit(2, units = "cm")
# modify its width and x properties to make it longer
legend_first_key$grobs[[1]]$width <- unit(1, units = "cm")
legend_first_key$grobs[[1]]$x <- unit(1.6, units = "cm")
# last key of legend
legend_last_key <- gtable_filter(legend_grobs, "key-3-4-1")
legend_last_key$widths <- unit(2, units = "cm")
# analogous
legend_last_key$grobs[[1]]$width <- unit(1, units = "cm")
legend_last_key$grobs[[1]]$x <- unit(0.5, units = "cm")
# grab the last label so we can also shift its position
# below code is where i am stuck as this is not shifting the label
legend_last_label <- gtable_filter(legend_grobs, "label-5-4")
legend_last_label$widths <- unit(20, units = "cm")
legend_last_label$grobs[[1]]$x <- unit(-10.1, units = "cm")
legend_last_label$grobs[[1]]$width <- unit(10, units = "cm")
# Insert new color legend back into the combined legend
legend_grobs$grobs[legend_grobs$layout$name == "key-3-1-1"][[1]] <-
legend_first_key$grobs[[1]]
legend_grobs$grobs[legend_grobs$layout$name == "key-3-4-1"][[1]] <-
legend_last_key$grobs[[1]]
legend_grobs$grobs[legend_grobs$layout$name == "label-5-4"][[1]] <-
legend_last_label$grobs[[1]]
legend$grobs[[1]]$grobs[1][[1]] <- legend_grobs
p_grob$grobs[p_grob$layout$name == "guide-box"][[1]] <- legend
# the plot is now drawn using this grid function
grid.newpage()
grid.draw(p_grob)
print(legend_grobs)
# save the plot
ggsave(paste0("~/Desktop/RA/",dateo,"_dc_1930.jpg"),
plot = p_grob, dpi = 300, width = 11, height = 8.5, units = c("in"))
}
extendLegendWithExtremes(tester)
Might it be that you do not exactly catch the coordinate of the last label? For example
legend_last_label$grobs[[1]]$x
isNULL
and it should return1npc
. Here is a way of how you can get to it:Maybe is safer to use the
gPath
s of each grob to "catch" them and then edit them witheditGrob
function. Here is an example applied on your plottester
:The legend is somewhere at the bottom, identified as "guide-box.etc"
Your last label is mentioned as:
Now we need to build the
gPath
s for each grob. Maybe there are easier ways, but here is one way:The path to the text of your label is:
So, having the path, we can edit the grob, that is, move the label to the left:
Further, you can edit the width and position of a key. Here is how to edit the right-most key:
Also, I think you should replace
labels <- labels[1:length(labels)-1]
withlabels <- labels[-length(labels)]
; otherwise I get an error fromcut
function, when you buildd1930$brks