I am trying to figure out how to display a map including the legend with ggmap/ggplot.
I have gotten so far:
library(ggmap)
library(RColorBrewer)
bbox <- c(8.437526,47.328268,8.605915,47.462160)
map.base <- get_map(maptype='toner',source = 'stamen',location = bbox)
ggmap(map.base) +
geom_blank() +
ggtitle("2015-09-21 06:00:00 CEST") +
scale_colour_manual(values = rev(brewer.pal(7,"Spectral")), drop = FALSE)+
scale_size_manual(values=c(1:7), drop = FALSE) +
guides(color=guide_legend(title='Mean Delay [s]'), size = guide_legend(title='Mean Delay [s]'))+
ggsave(file=paste("map_","2015-09-21 060000",".png",sep=""),dpi = 100)
dev.off()
This generates the correct map in the correct bounding box. But even thought I have specified: "scale_colour_manual" and "scale_size_manual" with "drop = FALSE", no legend is appearing. How can I have the legend shown when no data is to be displayed?
The overall intention is to create a single map of a given interval in a time series. Now the problem is that some intervals have no data and so the map is displayed without a scale. If the map does not have a scale the dimensions of the map are different making it impossible to create a movie out of the different maps. That is why I need to be able to create a map WITHOUT data but WITH the legend showing.
Thank you.
Taking Jaap's comment into account, that I have to call a legend in aes I have been able to achieve what I want with following code:
library(ggmap)
library(RColorBrewer)
bbox <- c(8.437526,47.328268,8.605915,47.462160)
map.base <- get_map(maptype='toner',source = 'stamen',location = bbox)
ggmap(map.base) +
geom_point(aes(x=0,y=0, color=cut(0,breaks = c(-Inf,0,60,120,240,300,360,Inf),right = FALSE), size=cut(0,breaks = c(-Inf,0,60,120,240,300,360,Inf),right = FALSE))) +
ggtitle("2015-09-21 06:00:00 CEST") +
scale_colour_manual(values = rev(brewer.pal(7,"Spectral")), drop = FALSE)+
scale_size_manual(values=c(1:7), drop = FALSE) +
guides(color=guide_legend(title='Mean Delay [s]'), size = guide_legend(title='Mean Delay [s]')) +
ggsave(file=paste("map_","2015-09-21 060000",".png",sep=""),dpi = 100)
dev.off()
I know this is not the most elegant way, but it works for now.
I basically make a dummy point outside of the bounding box to be displayed. I then give the point a value, which is cut according to the breaks I want and then colored and sized accordingly. Just remember to put the values of x and y in aes outside of the bounding box.
Better solutions are welcome.
Related
I hope you can help me. I have the idea of visualizing segments within a plot with a rectangle that can be placed next to the y or x-axis which means that it would be outside of the plot area. It should look similar as in the image below:
I tried to reach the mentioned output by trying two different approaches:
I created two viewports with the grid package and put the plot in one viewport that I placed at the bottom and one viewport on top of that. The big problem here is that I need the coordinates from where the grey background panel of the ggplot starts so I can place the top viewport exactly there, so that the segments conincide with the x-axis length. My code looked like following:
container_viewport <- viewport(x=0,y=0,height=1,width=1,just = c("left","bottom"))
pushViewport(container_viewport)
grid.draw(rectGrob())
popViewport()
section_viewport <- viewport(x=0.055,y=0.99,height=0.085,width=0.935,just=c("left","top"))
pushViewport(section_viewport)
plot_obj <- ggplot_build(testplot)
plot_data <- plot_obj$data[[1]]
grid.draw(rectGrob(gp = gpar(col = "red")))
popViewport()
plot_viewport <- viewport(x=0,y=0,height=0.9,width=1,just=c("left","bottom"))
pushViewport(plot_viewport)
grid.draw(ggplotGrob(testplot))
popViewport()
This looks fine but I had to hardcode the coordinates of the viewport at the top.
I used grid.arrange() to arrange to stack the plots vertically (instead of a grob for the rectangle like in the other approach I create a ggplot instead for that). Here, basically the same problem exists, since I somehow need to put the plot representing the rectangle at the top in the right position on the x-axis. My code looked like following:
p1 <- plot_data %>%
ggplot()+
geom_rect(aes(xmin=-Inf,xmax=Inf,ymin=-Inf,ymax=Inf))
p2 <- testplot
test_plot <- grid.arrange(p1,p2,heights=c(1,10))
This approach does not work that good.
Since I would like to create a solution that can be applied generally, trial and error with the coordinates of the viewport is no option since the length of the y-axis label or tick labels can vary and therefore the length and coordinates of the background panel. When this step is done the segmentation of the rectangle should be no problem anymore.
Maybe this is just not possible but if then I would appreciate any help.
Thank you!
I would probably use patchwork here. Let's start by replicating your plot:
library(ggplot2)
library(patchwork)
p <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
geom_point(color = "red") +
labs(x = "test", y = "test")
p
That looks very similar. Now we define (in our own co-ordinates) where we want the section split to occur on the x axis.
section_split <- 5.25
Using just this number, we add rectangles and text annotations that cover a copy of our original plot, and remove its axis annotations using theme_void:
p2 <- p +
annotate("rect", xmin = c(-Inf, section_split), ymin = c(-Inf, -Inf),
xmax = c(section_split, Inf), ymax = c(Inf, Inf),
fill = c("#00a2e8", "#ff7f27")) +
annotate("text", label = c("Section A", "Section B"), size = 6,
y = rep(mean(layer_scales(p)$y$range$range), 2),
x = c((min(layer_scales(p)$x$range$range) + section_split)/2,
(max(layer_scales(p)$x$range$range) + section_split)/2)) +
theme_void()
Now we just draw this second plot above our first, adjusting the relative heights to about 1:10
p2/p + plot_layout(heights = c(1, 10))
The benefit of doing it this way is that, since we copied the original plot, the positional mapping of the x axis is identical between the two plots, and patchwork will automatically line up the panels.
Created on 2023-02-04 with reprex v2.0.2
Following from this question on boxplots and my own question on making a map of India, what is a good way to avoid code repetition in ggplot when dealing with various layers of a map?
Below is a reprex . I thought the easiest way would be to:
1. save a basic map with state and national borders
2. add district layer (displaying the variables).
Imagine repeating step 2 for dozens of variables.
library(ggplot2)
library(sf)
library(raster)
# Download district and state data (should be less than 10 Mb in total)
distSF <- st_as_sf(getData("GADM",country="IND",level=2))
stateSF <- st_as_sf(getData("GADM",country="IND",level=1))
# Add country border
countryborder <- st_union(stateSF)
# STEP 1: Basic plot
basicIndia <- ggplot() +
geom_sf(data = stateSF, color = "white", fill = NA) +
geom_sf(data = countryborder, color = "blue", fill = NA) +
theme_dark()
# STEP 2: Adding the data layer underneath so it doesn't cover the other borders
indiaMap$layers <- c(geom_sf(data = distSF, fill = "red")[[1]], indiaMap$layers[[2:3]])
indiaMap$layers <- c(geom_sf(data = distSF, fill = "gold")[[1]], indiaMap$layers[[2:3]])
indiaMap
However, in this way, one cannot make even minor modifications to that additional layer, like adding a different title. The following obviously does not work but makes my point.
basicIndia$layers <- c(
geom_sf(data = distSF, aes(fill = GINI), color = "white", size = 0.2)[[1]] +
labs(title = "Gini coefficient"),
basicIndia$layers)
Am I approaching the problem in the wrong way? Is this something that cannot be done?
Another way to approach the problem would be to use ggplot_build().
Make a ggplot_build object using:
indiaBuild <- ggplot_build(basicIndia)
Instead of your step 2 we could now use:
indiaBuild$plot$layers <- c(indiaBuild$plot$layers,
geom_sf(data=distSF, fill='gold')[[1]])
You can change various parts of the ggplot_build object then including the title:
indiaBuild$plot$labels$title <- 'Gini coefficient'
When finished you can extract just the plot using p <- indiaBuild$plot
I would like to be able to adjust the positions of the loading labels, so that they do not fall atop the the arrows. However, I do not know where the adjustments need to be made. The geom_text can be used to adjust the position of the site positions, but I cannot find where the vectors are stored in str(g).
library(ggplot2)
library(ggfortify)
df <- data.frame(replicate(10,sample(-10:10,10,rep=TRUE)))
names(df) <- c('up','down','left','right','circle','square','triangle','x','r1','l1')
rownames(df) <- paste('Dummy Site', seq(0,9,1))
g <- autoplot(prcomp(df[,-11], scale=TRUE), data=df,
loadings.label=TRUE, loadings=TRUE,
loadings.label.size=8, loadings.colour='blue',
label.size=5) +
geom_text(vjust=-1, label=rownames(df)) +
theme(plot.background=element_blank(),
panel.background=element_rect(fill='transparent',color='black',size=1),
legend.text=element_text(hjust=1),
legend.key=element_blank())
g
I've looked in ggplot2::theme and I've examined the help docs for autoplot, but can't find any mention of the adjusting label position. Bonus points if it can adjust based on the vector of the arrow, but a static adjustment would be acceptable.
Currently, here is what the plot looks like:
You can get the coordinates by layer_data(g, 2). But autoplot(prcomp.obj) passes other arguments to ggbiplot(), so you can change label and loadings.label position using arguments of ggbiplot(), such as loadings.label.hjust (see ?ggbiplot).
example code:
arrow_ends <- layer_data(g, 2)[,c(2,4)]
autoplot(prcomp(df[,-11], scale=TRUE), data=df,
loadings.label=TRUE, loadings=TRUE,
loadings.label.size=8, loadings.colour='blue',
label.size=5, loadings.label.vjust = 1.2) + # change loadings.label position
geom_point(data = arrow_ends, aes(xend, yend), size = 3) + # the coordinates from layer_data(...)
geom_text(vjust=-1, label=rownames(df)) +
theme(plot.background=element_blank(),
panel.background=element_rect(fill='transparent',color='black',size=1),
legend.text=element_text(hjust=1),
legend.key=element_blank())
I am attempting to plot several shapefiles on top of a map generated through ggmap. This is working well, however I want to constrain the view area to the shapefile (and not rely on the zoom argument in ggmaps). I've done this by getting the bounding box and passing it as an argument in ggplot's coord_cartesian While this works, I am getting some tearing issues on the edges of the map - most specifically on the western portion. I've tried adjusting the x-y coordinates manually but it seems to only severely distort the picture.
My thoughts are to zoom out slightly to allow the entire shapefile to be plotted in the area, but I can't seem to figure it out. It's also possible I am going about this entirely in the wrong way.
Here's the code I used to generate the map. The shapefile can be downloaded here
library(dplyr)
library(ggmap)
library(rgdal)
library(broom)
# Read in shapefile, project into long-lat
# Create 'tbox' which is a minimum bounding box around the shapefile
tracts <- readOGR(dsn = ".", layer = "CensusTracts2010") %>%
spTransform("+proj=longlat +ellps=WGS84")
tbox <- bbox(tracts)
# Plot data
tract_plot <- tidy(tracts)
DetroitMap <- qmap("Detroit", zoom = 11)
DetroitMap + geom_polygon(data = tract_plot, aes(x = long, y = lat, group = id), color = "black", fill = NA) +
coord_cartesian(xlim = c(tbox[1,1], tbox[1,2]),
ylim = c(tbox[2,1], tbox[2,2]))
I followed your workflow, which resulted in the same problem as you mentioned above. Then I changed the zoom on the qmap option from 11 to 10 and it resulted in a much better picture, although you do lose some of the place names but you can add those in manually yourself with annotate:
DetroitMap <- qmap("Detroit", zoom = 10)
DetroitMap + geom_polygon(data = tract_plot, aes(x = long, y = lat, group = id), color = "black", fill = NA) +
coord_cartesian(xlim = c(tbox[1,1], tbox[1,2]),
ylim = c(tbox[2,1], tbox[2,2]))
Im making a treemap of some data using a pretty cool library called treemapifyof which the details can be found here and github repository here
Based on my reading of the documentation it seems to be based on ggplot2 so it should be possible to modify the graph using the grammar of graphics
My code is below with some made up data. The end result is pretty nice but i want to change the color scheme to a more subtle using the line scale_colour_brewer. The graph runs fine but the colour scheme seems to be ignored. Has anyone had any experience with this?
# Create Random Data
country <- c("Ireland","England","France","Germany","USA","Spain")
job <- c("IT","SOCIAL","Project Manager","Director","Vice-President")
mydf <- data.frame(countries = sample(country,100,replace = TRUE),
career = sample(job,100,replace=TRUE),
participent = sample(1:100, replace = TRUE)
)
# Set Up the coords
treemap_coords <- treemapify(mydf,
area="participent",
fill="countries",
label="career",
group="countries")
# Plot the results using the Green Pallete
ggplotify(treemap_coords,
group.label.size.factor = 2,
group.label.colour = "white",
label.colour = "black",
label.size.factor = 1) +
labs(title="Work Breakdown") +
scale_colour_brewer(palette = "Greens")
If you want to change the fill color of the rectangles, try the scale for fill instead the one for colour:
scale_fill_brewer(palette = "Greens")