I need to create a European map to show the distribution of a variable across countries. I need the map in black and white. I rely on ggplot and followed this approach as an example. I changed the legend based on this blogpost. All this works fine with this result:
My question is how to change the map in a way that the countries where I am missing the information for fill and are shown as pure white have a texture over-them (I am thinking diagonal lines)?
Since my script is a bit messy, I just show the ggplot here, without the data preparation part:
require(ggplot2)
plotCoords <- read.csv("http://eborbath.github.io/stackoverflow/PlotCoords.csv")
showCoords <- read.csv("http://eborbath.github.io/stackoverflow/showCoords.csv")
ggplot() +
geom_polygon(
data = plotCoords,
aes(x = long, y = lat, group = group),
fill = "white", colour = "darkgrey", size = 0.6) +
geom_polygon(
data = showCoords,
aes(x = long, y = lat, group = group),
fill = "grey", colour = "black", size = 0.6) +
geom_polygon(
data = showCoords,
aes(x = long, y = lat, group = group, fill = sh_left),
colour = "black", size = 0.1) +
scale_fill_gradient(
low = "gray90", high = "gray0",
name = "Share of left-wing protesters",
guide = guide_colorbar(
direction = "horizontal",
barheight = unit(2, units = "mm"),
barwidth = unit(50, units = "mm"),
draw.ulim = F,
title.position = 'top',
title.hjust = 0.5,
label.hjust = 0.5
)) +
scale_x_continuous(element_blank(), breaks = NULL) +
scale_y_continuous(element_blank(), breaks = NULL) +
coord_map(xlim = c(-26, 47), ylim = c(32.5, 73)) +
theme_bw() +
theme(legend.justification = c(-0.4, 1.2), legend.position = c(0, 1))
The first geom_polygon is for the background, I assume I have to edit the fill there. Obviously, this is important to differentiate no information from low values of the variable I plot. Given I have to rely on black and white I came up with the idea of using textures, but I am open to alternative suggestions.
Thanks!
it's technically possible with gridSVG, but not sure it's worth the effort.
I created a new geom based on GeomPolygon, and modified the draw_panel method to return,
gl <- by(munched, munched$group,
function(m){
g <- polygonGrob(m$x, m$y, default.units = "native")
patternFillGrob(g,
pattern = pattern(linesGrob(gp=gpar(col="red",lwd=3)),
width = unit(2, "mm"), height = unit(2, "mm"),
dev.width = 1, dev.height = 1))
}, simplify = FALSE)
gTree(children = do.call(gList, gl))
Related
I am trying to create an animation or GIF that shows the evolution of an environmental condition over time. Basically, I have a dataset (example below) with year, value of the environmental condition, unit, and coordinates.
year
condition
unit
Lat
Long
1945
-0.120148
TSS
41.36531
41.67889
1948
0.274646
TSS
30.45368
-87.99042
1948
0.074794
TSS
30.45368
-87.99042
1975
-0.102050
TSS
38.10541
-122.06782
1979
-0.169886
NTU
29.77048
-84.91630
Complete dataset: https://drive.google.com/file/d/1XQ95KP_x-kbq_wdmpfpCiOonF-RoFsU1/view?usp=sharing
I am using ggplot2 to create the plots comprising year gaps. Here is the code I am using to plot the variation from 1945 to 1980:
`ggplot() +
geom_map(data = world, map = world,aes(long, lat, map_id = region),color = "seashell2", fill = "seashell", size = 0.3, alpha=0.9)+
geom_point(data = mapa_variacao_anual_45_80,aes(Long, Lat, color = med_turb),size=2, shape=16, position = position_jitter(width = 8)) +
labs(title = "1945 to 1980")+
theme(plot.title = element_text(hjust = 0.5))+
scale_colour_gradient( low = "darkgreen", high = "red")+
xlab("Longitude") + ylab("Latitude")+
theme(legend.title= element_blank())+
theme(panel.background = element_rect(fill = 'aliceblue', colour = 'gray'))`
My plan is to have several plots with determined year ranges and in the end combine all of them in sequence to show temporal variation.
Is there an easy way to combine the plots? I have been looking for solutions online but they seem not to suit my goal or are just too complicated.
Thanks in advance for any help.
You could get gganimate to handle the animation for you:
library(ggplot2)
library(gganimate)
world <- map_data("world")
mapa_variacao_anual_45_80$frames <- as.numeric(
factor(mapa_variacao_anual_45_80$year))
p <- ggplot() +
geom_map(data = world, map = world,
aes(long, lat, map_id = region),
color = "seashell2", fill = "seashell", size = 0.3, alpha = 0.9)+
geom_point(data = mapa_variacao_anual_45_80,
aes(Long, Lat, color = med_turb),
size = 2, shape = 16, position = position_jitter(width = 8)) +
labs(title = "1945 to 1980")+
theme(plot.title = element_text(hjust = 0.5))+
scale_colour_gradient( low = "darkgreen", high = "red") +
geom_text(data = mapa_variacao_anual_45_80,
aes(x = -180, y = 65, label = year), hjust = 0, size = 8,
check_overlap = TRUE) +
xlab("Longitude") +
ylab("Latitude")+
theme(legend.title= element_blank())+
theme(panel.background = element_rect(fill = 'aliceblue', colour = 'gray')) +
transition_events(mapa_variacao_anual_45_80$frames,
enter_length = 1, exit_length = 1)
anim_save("map.gif", p, device = "ragg_png", duration = 20, fps = 30,
width = 900, height = 450)
You can create a series of png files and assemble them into an animation with the gifski package:
library(ggplot2)
library(gifski)
for(i in 1:30){
gg <- ggplot(......)
ggsave(sprintf("myplot%03d.png", i), gg)
}
png_files <- Sys.glob("myplot*.png")
gifski(
png_files,
"myanimation.gif",
width = 400, height = 400,
delay = 1/5 # 5 images per second
)
file.remove(png_files)
I'm having a problem with gganimate where it does not fill the geom_sf points I am using all of the time. A static version of the plot I'm using works fine:
precincts$margingroup <- cut(precincts$margin,
breaks = breaks, labels = c(1:37))
pointfig <- ggmap(myMap) +
geom_sf(data=centroids, aes(fill=precincts$margin,group=precincts$margingroup), size=precincts$dotsize, pch=21, alpha=1, inherit.aes = FALSE) +
scale_fill_gradient2(midpoint = 0, low='darkmagenta',
min = 'white',
high='orange',
limits = c(-50,50),
oob = scales::squish) +
geom_shadowtext(mapping = aes(x = longitude, y = latitude, label = name, vjust=vjust),
data = places, size = 5, fontface = "bold") +
labs(fill='Fidesz Margin, %',caption = "Data from valasztas.hu") +
ggtitle("Borsod-Abaúj-Zemplén 6th District By-Election by Precinct") +
theme_void() +
theme(legend.box.just = "center") +
theme(plot.title = element_text(size = 12, face = "bold", vjust=4)) +
theme(plot.margin = unit(c(1,1,1,1), "cm")) +
coord_sf()
But when I try to animate it I get an issue with fill. I want the points to appear in order of margin (but I had to group them so that there were fewer than 50 states).
anim <- pointfig + transition_states(precincts$margingroup,
transition_length = 1,
state_length = 0.45, wrap = FALSE) +
shadow_wake(wake_length = 0.1, alpha = TRUE)
This results in (small version for SO but you get the idea!):
The colours which appear towards the end are whole the whole thing should look. Any help would be hugely appreciated, thanks!
I am plotting a smooth to my data using geom_smooth and using geom_ribbon to plot shaded confidence intervals for this smooth. No matter what I try I cannot get a single legend that represents both the smooth and the ribbon correctly, i.e I am wanting a single legend that has the correct colours and labels for both the smooth and the ribbon. I have tried using + guides(fill = FALSE), guides(colour = FALSE), I also read that giving both colour and fill the same label inside labs() should produce a single unified legend.
Any help would be much appreciated.
Note that I have also tried to reset the legend labels and colours using scale_colour_manual()
The below code produces the below figure. Note that there are two curves here that are essentially overlapping. The relabelling and setting couours has worked for the geom_smooth legend but not the geom_ribbon legend and I still have two legends showing which is not what I want.
ggplot(pred.dat, aes(x = age.x, y = fit, colour = tagged)) +
geom_smooth(size = 1.2) +
geom_ribbon(aes(ymin = lci, ymax = uci, fill = tagged), alpha = 0.2, colour = NA) +
theme_classic() +
labs(x = "Age (days since hatch)", y = "Body mass (g)", colour = "", fill = "") +
scale_colour_manual(labels = c("Untagged", "Tagged"), values = c("#3399FF", "#FF0033")) +
theme(axis.title.x = element_text(face = "bold", size = 14),
axis.title.y = element_text(face = "bold", size = 14),
axis.text.x = element_text(size = 12),
axis.text.y = element_text(size = 12),
legend.text = element_text(size = 12))
The problem is that you provide new labels for the color-aesthetic but not for the fill-aesthetic. Consequently ggplot shows two legends because the labels are different.
You can either also provide the same labels for the fill-aesthetic (code option #1 below) or you can set the labels for the levels of your grouping variable ("tagged") before calling ggplot (code option #2).
library(ggplot2)
#make some data
x = seq(0,2*pi, by = 0.01)
pred.dat <- data.frame(x = c(x,x),
y = c(sin(x), cos(x)) + rnorm(length(x) * 2, 0, 1),
tag = rep(0:1, each = length(x)))
pred.dat$lci <- c(sin(x), cos(x)) - 0.4
pred.dat$uci <- c(sin(x), cos(x)) + 0.4
#option 1: set labels within ggplot call
pred.dat$tagged <- as.factor(pred.dat$tag)
ggplot(pred.dat, aes(x = x, y = y, color = tagged, fill = tagged)) +
geom_smooth(size = 1.2) +
geom_ribbon(aes(ymin = lci, ymax = uci), alpha = 0.2, color = NA) +
scale_color_manual(labels = c("untagged", "tagged"), values = c("#F8766D", "#00BFC4")) +
scale_fill_manual(labels = c("untagged", "tagged"), values = c("#F8766D", "#00BFC4")) +
theme_classic() + theme(legend.title = element_blank())
#option 2: set labels before ggplot call
pred.dat$tagged <- factor(pred.dat$tag, levels = 0:1, labels = c("untagged", "tagged"))
ggplot(pred.dat, aes(x = x, y = y, color = tagged, fill = tagged)) +
geom_smooth(size = 1.2) +
geom_ribbon(aes(ymin = lci, ymax = uci), alpha = 0.2, color = NA) +
theme_classic() + theme(legend.title = element_blank())
I've been struggling to change my fill legend shape from squares to circles. I just tried to replicate this issue in a vanilla rstudio.cloud environment, and it still isn't working:
library(tidyverse)
library(tigris)
options(tigris_class = "sf")
options(tigris_use_cache = TRUE)
df_ohio <- counties(state = "OH", cb = T, resolution = "20m") %>%
mutate(defiance = if_else(NAME == "Defiance", 1, 0))
ggplot() +
geom_sf(data = df_ohio, color = "black",
aes(fill = as.factor(defiance)), size = .1, alpha = .75) +
scale_fill_manual(values = c('#BB0000','navy'),
labels = c('No','Yes'),
name = "Defiance County?") +
theme_void() +
guides(fill = guide_legend(override.aes = list(shape = 20)))
When I try this I still get the result below, which has the fill legend shape as squares (shape 15, I guess) instead of circles (shape 20) like I specified. Any ideas?
You could add a layer of geom_point without data just to create a legend, and then set show.legend = F in the geom_sf layer to hide the squares, as suggested here:
ggplot() +
geom_sf(data = df_ohio, color = "black", aes(fill = as.factor(defiance)), size = .1, alpha = .75, show.legend = F) +
geom_point(data = df_ohio, aes(x = -83, y = 40, color = as.factor(defiance)), size=8, alpha = 0) +
scale_color_manual(values = c('#BB0000','navy'),
labels = c('No','Yes'),
name = "Defiance County?") +
scale_fill_manual(values = c('#BB0000','navy')) +
guides(color = guide_legend(override.aes = list(alpha = 1))) +
theme_void()
Within the geom_point layer you can change the shape with override.aes, but the default is a circle as you want.
You can specify the legend shape if you are working with points rather than polygons. Fortunately, geom_sf allows you to override the default and use point for legend, even if the underlying geom is a polygon, by specifying show.legend = "point".
Demonstration with a built-in dataset:
nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)
ggplot(nc) +
geom_sf(aes(fill = AREA >= 0.15), show.legend = "point") +
scale_fill_manual(values = c('#BB0000','navy'),
labels = c('No','Yes'),
name = "Defiance County?") +
theme_void() +
guides(fill = guide_legend(override.aes = list(shape = 21, size = 5)))
(Since it's used in fill, shape 21 seems more appropriate than shape 20; I've also increased the size for ease of view.)
I’m trying to use geom_curve() and scale_alpha() together, but the resulting legend guide is not cool =D.
In short, I want to plot a legend for scale_alpha() similar to the legend of scale_color_gradient().
Here is my reprex:
library(ggplot2)
library(dplyr)
theme_set(theme_void())
data_joined <- read.csv("https://gist.githubusercontent.com/kguidonimartins/6f49bf6ae13410799fb2eede56345fa5/raw/43486c44aee2aedf951e661ac576e46a5576a0da/country_data.csv", header = TRUE)
base_map <- borders(database = "world")
So, my first try was something like this. Not ideal due to the overlap. But the legend guide is ok!
data_joined %>%
ggplot() +
base_map +
geom_curve(
data = data_joined,
aes(
x = origin_x,
y = origin_y,
xend = destination_x,
yend = destination_y,
color = flow
),
arrow = arrow(length = unit(0.2, "cm")),
size = 1.5
)
So, I tried to simulate a scale_alpha() to maintain the legend guide.
data_joined %>%
ggplot() +
base_map +
geom_curve(
data = data_joined,
aes(
x = origin_x,
y = origin_y,
xend = destination_x,
yend = destination_y,
color = flow
),
arrow = arrow(length = unit(0.2, "cm")),
size = 1.5,
alpha = 0.5
) +
scale_color_gradient(low = "grey95", high = "blue", n.breaks=10)
Not good yet, maybe using a fixed color and applying alpha (in aes()) as a gradient:
data_joined %>%
ggplot() +
base_map +
geom_curve(
data = data_joined,
aes(
x = origin_x,
y = origin_y,
xend = destination_x,
yend = destination_y,
alpha = flow
),
arrow = arrow(length = unit(0.2, "cm")),
size = 1,
color = "blue"
) +
scale_alpha(n.breaks = 10)
I liked that solution but I was unable to change the legend guide. I want to maintain the legend guide similar when using scale_color_gradient(). I’m trying this from scratch changing the legend key, but the guides() don’t change the shape of the arrows in the legend.
data_joined %>%
ggplot() +
base_map +
geom_curve(
data = data_joined,
aes(
x = origin_x,
y = origin_y,
xend = destination_x,
yend = destination_y,
alpha = flow
),
arrow = arrow(length = unit(0.2, "cm")),
size = 1,
color = "blue"
) +
scale_alpha(n.breaks = 10) +
guides(alpha = guide_legend(reverse = TRUE, override.aes = list(shape = 22)))
Can someone help me with this?
Created on 2020-03-26 by the reprex package (v0.3.0)