Create animation or GIF with point plots in R - r

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)

Related

Issue with colour fill with gganimate and geom_sf

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!

How to fix axis and add polygon to inset map in ggplot2

I'm trying to add a couple details to my map in ggplot2 but I'm new to the package and I'm not sure how to proceed. Code and map are below (ignore the weird spacing on the map, I took a screen shot in Rstudio). Thanks for taking a look!
I'm trying to do the following:
Add latitude and longitude tick marks on the x and y axis. I coded it in the script below but nothing is happening, not sure why - no errors pop up.
If possible, I'd like to add a box in the inset map showing the extent/coverage of the larger map, since the points in the smaller map (the red ones) are barely visible. I'm not sure how to add and place a polygon in the inset map.
Here is what the map currently looks like for reference:
library(raster)
library(ggplot2)
library(ggthemes)
library(ggsn)
library(ggmap)
library(maps)
library(mapdata)
mapdata <- getData("GADM", country = "panama", level = 1)
mymap <- fortify(mapdata)
mypoint <- data.frame(long=c(-79.743, -79.696, -79.645, -79.595),
lat=c(9.160, 9.117, 9.058, 9.015),
group=c("L", "GW", "OGR", "LC"))
mypoint2 <- data.frame(long=c(-79.846, -79.707, -79.665, -79.610),
lat=c(9.181, 9.112, 9.057, 9.014),
group=c("BCI", "G", "EH", "MF"))
g1 <- ggplot() +
geom_blank(data = mymap, aes(x=long, y=lat)) +
geom_map(data = mymap, map = mymap,
aes(group = group, map_id = id),
fill = "#b2b2b2", color = "black", size = 0.3) +
coord_sf(xlim=c(-80,-79.5), ylim=c(8.9, 9.25), expand = FALSE) +
geom_point(data = mypoint, aes(x = long, y = lat),
color = "black", size = 3) +
geom_label(data = mypoint, aes(label = group, x = long, y = lat),
size = 3, fontface = "bold", nudge_x = c(0.015, 0.02, 0.022, 0.018)) +
geom_point(data = mypoint2, aes(x = long, y = lat),
color = "blue", size = 3) +
geom_label(data = mypoint2, aes(label = group, x = long, y = lat),
size = 3, fontface = "bold", nudge_x = c(-0.02, -0.018, -0.02, -0.02)) +
scale_x_continuous(limits = c(-80,-79.5), expand = c(0, 0)) +
scale_y_continuous(limits = c(8.9, 9.25), expand = c(0, 0)) +
theme_map() +
ggsn::scalebar(location = "bottomleft", dist = 5,
transform = TRUE, dist_unit = "km", model = 'WGS84',
x.min = -79.97, x.max = -79.8,
y.min = 8.93, y.max = 9.25) +
north(x.min = -79.6, x.max = -79.5,
y.min = 9.2, y.max = 9.24,
location = "toprgiht", scale = 0.1)
g2 <- ggplotGrob(
ggplot() +
geom_polygon(data = mymap,
aes(x = long, y = lat, group = group),
fill = "#b2b2b2", color = "black", size = 0.3) +
geom_point(data = mypoint, aes(x = long, y = lat),
color = "red", size = 0.5) +
coord_map("polyconic") +
theme_map() +
theme(panel.background = element_rect(fill = NULL))
)
g3 <- g1 +
annotation_custom(grob = g2, xmin = -79.75, xmax = -79.51,
ymin = 8.9, ymax = 9.0)
g3
Latitude and longitude tick marks are disappearing because of theme_map() - it sets axis_ticks and axis_text (among other things) to element_blank(). One way to get them back is to override theme_map() with
g1_with_lbls <- g1 +
theme(
axis.text = element_text(),
axis.ticks = element_line(),
axis.title = element_text()
) +
xlab("Longitude") +
ylab("Lattitude")
You would place the polygon on the smaller map as a part of the ggplotGrob object you are creating
g2 <- ggplotGrob(
ggplot() +
geom_polygon(data = mymap,
aes(x = long, y = lat, group = group),
fill = "#b2b2b2", color = "black", size = 0.3) +
geom_point(data = mypoint, aes(x = long, y = lat),
color = "red", size = 0.5) +
coord_map("polyconic") +
theme_map() +
theme(
panel.background = element_rect(fill = NULL)
) +
geom_rect(
aes(xmin = -80, xmax = -79, ymin = 8.5, ymax = 9.5), fill = NA,
col = "red", size = 1
)
)
Then
g3 <- g1_with_lbls +
annotation_custom(grob = g2, xmin = -79.75, xmax = -79.51,
ymin = 8.9, ymax = 9.0)
g3
Has both the ticks and the rectangle.

R map - poor representation of land borders

I map the southern part of the South hemisphere. My issue is Australia which has poorly drawn borders.
My data :
library("maptools")
library("ggplot2")
library("tidyverse")
ylim_map <- c(-90, -30)
xlim_map <- c(-180, 180)
world <- maps::map("world", fill=TRUE, plot=FALSE, ylim = ylim_map)
Convert data in correct format for ggplot :
IDs <- sapply(strsplit(world$names, ":"), function(x) x[1])
world <- map2SpatialPolygons(world, IDs = IDs,
proj4string = CRS("+proj=longlat +datum=WGS84"))
world_map <- fortify(world)
world_map <- world_map[which(between(world_map$lat, ylim_map[1], ylim_map[2]) &
between(world_map$lon, xlim_map[1], xlim_map[2])),]
And my plot :
ggplot() +
coord_map("orthographic", orientation = c(-90, 0, 0),
xlim = xlim_map, ylim = c(ylim_map[1], ylim_map[2] + 10)) +
geom_map(data = world_map, map = world_map,
aes(x = long, y = lat, map_id = id), fill = "black") +
geom_text(aes(x = 180, y = ylim_map[2]+5, label = "180°E"), color = "black") +
geom_text(aes(x = 90, y = ylim_map[2]+5, label = "90°E"), angle = -90, color = "black") +
geom_text(aes(x = 0, y = ylim_map[2]+5, label = "0°"), color = "black") +
geom_text(aes(x = -90, y = ylim_map[2]+5, label = "90°W"), angle = 90, color = "black") +
labs(y = "", x = "") +
# Theme
theme(text = element_text(size = 20),
panel.background = element_blank(),
axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
axis.line = element_blank(),
aspect.ratio = 1)
TLDR:
You need to close your polygons.
Explanation:
Let's trim away extraneous code & zoom in onto Australia. (Though actually the problem exists for Africa & South America as well; it's just not as obvious there...)
We can see that the top line is misbehaving. It's intersecting with the coastline further down south, rather than sticking to its correct latitude level:
ggplot() +
coord_map("orthographic", orientation = c(-40, 130, 0)) +
geom_map(data = world_map, map = world_map,
aes(x = long, y = lat, map_id=id),
fill = "darkgrey") +
theme_bw()
Now a geom_map layer is essentially plotting polygons, and ?geom_polygon states:
Polygons are very similar to paths (as drawn by geom_path()) except
that the start and end points are connected and the inside is coloured
by fill. The group aesthetic determines which cases are connected
together into a polygon.
If we replace the geom_map layer with its geom_polygon / geom_path equivalents, the situation becomes much more obvious: the polygon corresponding to Australia has no top line. Instead, the path starts at the one corner and ends at the opposite corner. geom_polygon connects them with a straight line, which may intersect other lines when the coordinate system isn't linear (and coord_map isn't):
ggplot() +
coord_map("orthographic",
orientation = c(-40, 130, 0)) +
geom_polygon(data = world_map,
aes(x = long, y = lat, group = group),
fill = "lightgrey") +
geom_path(data = world_map,
aes(x = long, y = lat, group = group)) +
theme_bw()
Solution:
We can manually close each polygon by repeating its first point at the end. (For polygons that are already closed, this has no additional effect.)
library(dplyr)
world_map2 <- world_map %>%
group_by(group) %>% # each group corresponds to a unique polygon
arrange(order) %>% # sort points in the appropriate sequence
slice(c(1:n(), 1)) %>% # repeat first row after last row
mutate(order = seq(1, n())) %>% # define new order for n+1 rows
ungroup()
Check that the polygons are now closed, & the top line for Australia now traces its latitude level nicely:
ggplot() +
coord_map("orthographic",
orientation = c(-40, 130, 0)) +
geom_polygon(data = world_map2,
aes(x = long, y = lat, group = group),
fill = "lightgrey") +
geom_path(data = world_map2,
aes(x = long, y = lat, group = group)) +
theme_bw()
Applying this to the original use case:
ggplot() +
coord_map("orthographic", orientation = c(-90, 0, 0),
xlim = xlim_map, ylim = c(ylim_map[1], ylim_map[2] + 10)) +
geom_map(data = world_map2, map = world_map2,
aes(x = long, y = lat, map_id = id), fill = "black") +
geom_text(aes(x = 180, y = ylim_map[2]+5, label = "180°E"), color = "black") +
geom_text(aes(x = 90, y = ylim_map[2]+5, label = "90°E"), angle = -90, color = "black") +
geom_text(aes(x = 0, y = ylim_map[2]+5, label = "0°"), color = "black") +
geom_text(aes(x = -90, y = ylim_map[2]+5, label = "90°W"), angle = 90, color = "black") +
labs(y = "", x = "") +
# Theme
theme(text = element_text(size = 20),
panel.background = element_blank(),
axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
axis.line = element_blank(),
aspect.ratio = 1)

texture in geom_polygon fill

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

geom_density2d plot population data in r

I really need some help. I would like create an plot in R, but i do not know how should weight the point. I used this code below, and the point is equivalent. I would like is one of much bigger than another.
library(ggplot2)
library(ggmap)
Cau_map <- get_map(location='Caucasus', maptype="satellite", zoom = 5)
Cau <- data.frame(
lat = c(41.1057, 38.2968),
lon = c(44.2105, 47.1744),
data = c(52, 7)
)
ggmap(Cau_map, extent='device') +
geom_density2d(data=Cau, aes(x=lon, y=lat), color = "red", size=.3) +
stat_density2d(data=Cau, aes(x=lon, y=lat, fill = ..level.., alpha = ..level..), size = 0.08, bins = 6, geom = 'polygon')+
scale_fill_gradient(low = "red", high = "red4", guide=FALSE) + scale_alpha(range = c(0, 1), guide = FALSE)
This is what I got:

Resources