I'm just trying to make a simple study area map that also contains an inset of the state that I am working in (North Carolina). I would like to convert the inset map to a grob object to plot it within the main study area map, then use ggsave to save the map as an image, pdf, etc. I am using shapefiles for my actual map, but I'll show you what I am trying for using map_data:
library(ggplot2)
library(ggmap)
library(maps)
library(mapdata)
library(gridExtra)
library(grid)
# get the NC data:
states <- map_data("state")
nc_df <- subset(states, region == "north carolina")
# study area map:
nc_base <- ggplot() +
geom_polygon(data = nc_df, aes(x = long, y = lat, group = group), fill="grey", color="black") +
coord_fixed(xlim=c(-80, -77.5), ylim=c(33.5, 34.9), ratio = 1.3) +
theme_bw()
nc_base
# inset map:
insetmap<-ggplot() +
geom_polygon(data = nc_df, aes(x = long, y = lat, group = group), fill="grey", color="black") + # get the state border back on top
coord_fixed(ratio = 1.3) +
annotate(geom = "rect", ymax = 34.9, ymin = 33.5, xmax = -77.5, xmin = -80, colour = "red", fill = NA) +
ylab("") +
xlab("") +
theme_nothing()
insetmap
insetmap.grob <- ggplotGrob(insetmap)
final_map <- nc_base + annotation_custom(insetmap.grob, xmin=-79.5, xmax=-79, ymin=33.75, ymax=34)
final_map
When I run the script to produce the final map, only the study area map is produced. I'm wondering if I'm using ggplotGrob incorrectly, or it is something else? I might have read elsewhere that the annotation_custom function does not work unless you are using the coord_cartesian function in ggplot2 (and here I am using coord_fixed). If that is the case, can I zoom in similarly with that function, or is there another coord_ function to zoom in on my study area map?
Thanks,
Jay
I do this kind of thing fairly often and have found that the grid::viewport approach works well...although note that you cannot use ggsave when you are working with multiple viewports as ggsave will only save the last viewport.
Try:
nc_base <- ggplot() +
geom_polygon(data = nc_df, aes(x = long, y = lat, group = group), fill="grey", color="black") +
coord_fixed(xlim=c(-80, -77.5), ylim=c(33.5, 34.9), ratio = 1.3) +
theme_bw()
print(nc_base)
# inset map:
insetmap <- ggplot() +
geom_polygon(data = nc_df, aes(x = long, y = lat, group = group), fill="grey", color="black") + # get the state border back on top
coord_fixed(ratio = 1.3) +
annotate(geom = "rect", ymax = 34.9, ymin = 33.5, xmax = -77.5, xmin = -80, colour = "red", fill = NA) +
ylab("") +
xlab("") +
# used theme_inset instead of theme_nothing
theme_inset()
print(insetmap)
# save where you want to with filename arg in png(). Currently saves 'map.png' to your working directory
# set resolution, width, height
png(filename = "map.png", width = 1150, height = 800, res = 300)
# create a viewport for inset
# vp_inset width/height arguments set the size of the inset; x and y arguments set the position (from 0 to 1) of the left, top corner of the inset along each axis (i.e. not map coordinates as you have in your annotation custom). You can adjust these as you see fit.
vp_inset <- grid::viewport(width = 0.35, height = 0.35, x = 0.2, y = 0.5, just = c("left", "top"))
print(nc_base)
print(insetmap, vp = vp_inset)
dev.off()
Related
I want to build a world map from the map_data ggplot2 function, and then crop it to a specific extension.
I am trying to build a world map with this extension:
ext <- data.frame(xmin = -10.57514, xmax = 37.21653, ymin = 29.68319, ymax = 73.22486)
The code that I have used is this:
library(ggplot2)
ggplot(map_data("world"), aes(long, lat)) +
geom_polygon(aes(group = group), color = "white", fill = "lightgray", size = 0.2) +
theme_void() +
coord_fixed() +
scale_x_continuous(limits = c(ext$xmin, ext$xmax)) +
scale_y_continuous(limits = c(ext$ymin, ext$ymax))
This results in:
As you can see, the bottom of the plot (i.e, most of North Africa) is not accurate.
How would you fix that? Many thanks 😊
One option is to put the x and y limits into coord_fixed:
library(ggplot2)
ggplot(map_data("world"), aes(long, lat)) +
geom_polygon(aes(group = group),
color = "white",
fill = "lightgray",
size = 0.2) +
theme_void() +
coord_fixed(xlim = c(ext$xmin, ext$xmax),
ylim = c(ext$ymin, ext$ymax))
Output
I have created maps using the ggplot function on R. I then wanted to add bathymetric lines. I so downloaded them from the NOAA portal using the code lines:
library(marmap)
bat <- getNOAA.bathy(-11.99792 ,-5.002083 ,35.00208,43.99792,res=4, keep=TRUE)
plot(bat, land=TRUE, n=100, lwd=0.03)
plot(bat, deep=-200, shallow=-200, step=0, lwd=0.5, drawlabel=TRUE, add=TRUE)
plot(bat, deep=-1000, shallow=-1000, step=0, lwd=0.3, drawlabel=TRUE, add=TRUE)
Until this moment the lines look homogeneous and not discontinued.
I then run these two lines:
bathy_df <- inlmisc::Grid2Polygons(as.SpatialGridDataFrame(bat),
level=TRUE, pretty=TRUE)
bathy_map <- fortify(bathy_df)
But then, when I add bathy_map to my ggplot, the lines of the bathymetry appear as you can see in the attached picture: not homogeneous at all and discontinue.
This is the script of my ggplot:
D <- ggplot() +
geom_raster(data = Fig_D, aes(x = x, y = y, fill = value)) +
facet_wrap(~ variable) +
coord_equal() +
scale_fill_viridis_c(option = "turbo", direction = 1, limits=c(0, 160)) +
geom_polygon(data=bathy_map,
aes(x=long,y=lat,group=group), inherit.aes=F,
colour='black', fill=NA, lwd=0.5)+
geom_polygon(data = coast_map,
aes(x=long,y=lat,group=group), inherit.aes=F,
colour='black', fill='gray', lwd=0.5) +
theme_void(base_size = 12) +
theme(strip.text.x = element_text(size = 10, vjust = 1)) +
theme(legend.position = "right", legend.justification = "left") +
labs(fill='SPL')
(the first geom_polygon is the one referring to the bathymetric lines)
Do you know how I can make these lines look more homogeneous? And also, do you know how I can show the depth of some of the most important bathymetric lines?
Thank you.
Maps created with ggplot showing bathymetric lines
When you want to draw maps with ggplot2, always think library(sf)! ;-)
Here is an example:
# Load useful packages
library(sf)
library(marmap)
library(tidyverse)
library(rnaturalearth)
# Get bathymetric data
bat <- getNOAA.bathy(-12, -5, 35, 44, res = 4, keep = TRUE)
bat_xyz <- as.xyz(bat)
# Import country data
country <- ne_countries(scale = "medium", returnclass = "sf")
# Plot using ggplot and sf
ggplot() +
geom_sf(data = country) +
geom_tile(data = bat_xyz, aes(x = V1, y = V2, fill = V3)) +
geom_contour(data = bat_xyz,
aes(x = V1, y = V2, z = V3),
binwidth = 100, color = "grey85", size = 0.1) +
geom_contour(data = bat_xyz,
aes(x = V1, y = V2, z = V3),
breaks = -200, color = "grey85", size = 0.5) +
geom_sf(data = country) +
coord_sf(xlim = c(-12, -5),
ylim = c(35, 44)) +
labs(x = "Longitude", y = "Latitude", fill = "Depth (m)") +
theme_minimal()
And here is the result.
You don't need to use inlmisc::Grid2Polygons() nor ggplot2::fortify(). Simply use marmap::as.xyz() to transform your bathymetric data into a long data.frame, and use either (or both), of geom_tile() and geom_contour().
Of course, you can add several layers of geom_contour(). Here, I've added one with one isobath every 100 meters, and a darker one at -200m.
As for labelling some (or all) of the isobaths, have a look at metR::geom_label_contour()
I have a massive dataset that makes graph plotting tedious and complex.
Assume this simplified dataset:
library(data.table)
library(plotly)
library(ggplot2)
library(dplyr)
df <- data.table(continent = c(rep("America",3), rep("Europe",4)),
state = c("USA", "Brazil", "Chile", "Italy", "Swiss", "Spain", "Greece"),
X = rnorm(7, 5, 1),
Y = rnorm(7, -13, 1),
)
df$X_sd = sd(df$X)
df$Y_sd = sd(df$Y)
Consider having > 30 levels for "state", which makes it very difficult to show them with different colours or shapes.
I have decided to use plotly to show this dataset.
Here what I have done:
p <- df %>%
ggplot(aes(x=X,
y=Y,
fill = continent,
color = continent)) +
geom_errorbarh(aes(xmin = X - X_sd,
xmax = X + X_sd),
size = 0.5,
alpha = 0.3) +
geom_errorbar(aes(ymin = Y - Y_sd,
ymax = Y + Y_sd),
size = 0.5,
alpha = 0.3) +
geom_point(shape=21,
color="black",
size=3) +
theme_bw()
ggplotly(p)
However, the interactive window does not show information regarding the country, which is what I want to achieve.
In fact, every time I go over a point, I would like to have a window that shows: Continent, Country, X and Y (and in case I will have more factors or columns, I would like to be to include them too).
I have tried to add shape = country within the aesthetics, but 1) there are not enough shapes, 2) it fights against my decision of having shape = 21 for geom_point(), and 3) it adds a huge legend which I don't want.
How can I personalize the interaction window of plotly without adding extra and not-needed aesthetics?
Furthermore, I have tried to remove the legend by using:
guides(fill="none", color="none")+
or by
%>% hide_legend()
but either way, do not work. How can I remove the legend?
What you can do is add label in your aes to add factors like state. You can do that multiple times. You can use the following code:
p <- df %>%
ggplot(aes(label = state,
x=X,
y=Y,
fill = continent)) +
geom_errorbarh(aes(xmin = X - X_sd,
xmax = X + X_sd),
size = 0.5,
alpha = 0.3) +
geom_errorbar(aes(ymin = Y - Y_sd,
ymax = Y + Y_sd),
size = 0.5,
alpha = 0.3) +
geom_point(shape=21,
color="black",
size=3) +
theme_bw() +
theme(legend.position = "none")
ggplotly(p)
Output:
I want to animate some data using gganimate. Taking an example from their github page I changed it a bit to reflect my case. X-axis are dates and I want a logo in the same position for all frames.
Reproducible code:
library(magick)
library(gapminder)
library(ggplot2)
library(rsvg)
library(gganimate)
tiger <- image_read_svg('http://jeroen.github.io/images/tiger.svg', width = 400)
(p <- ggplot(gapminder, aes(year, lifeExp, size = pop, colour = country)) +
geom_point(alpha = 0.7, show.legend = FALSE) +
scale_colour_manual(values = country_colors) +
scale_size(range = c(2, 12)) +
scale_x_log10() +
annotation_raster(tiger, ymin = 75, ymax = 100, xmin = 1965, xmax = 2005) )
# here the animate part (not needed just for ilustrative purposes)
p + labs(title = 'Year: {frame_time}', x = 'Year', y = 'life expectancy') +
transition_time(year) +
ease_aes('linear')
The issue is I can plot the logo in any chart when x-axis are not dates.
I suspect this issue is related to date type but no success so far.
Your issue appears to relate to the log scale you are calling for your x-axis. It's taking your year vector — which is not a date, rather a 4-digit integer — and applying a log transformation to it... which as #camille has pointed out means that when you are calling animation_raster the coordinates (xmin / xmax) are off the plot grid.
Here's a solution that incorporates dates by changing the year in your data frame to a date format. It also layers the image behind the geoms and renders in its original scale ie. 1x1.
library(magick)
library(gapminder)
library(ggplot2)
library(rsvg)
library(gganimate)
library(lubridate)
tiger <- image_read_svg('http://jeroen.github.io/images/tiger.svg', width =
400)
xmin <- ymd("1965/01/01")
xmax <- ymd("2005/01/01")
ymin <- 30
height <- round(as.numeric(xmax-xmin)/356, 0)
ymax <- ymin + height
gapminder %>%
mutate(year = ymd(year, truncated = 2L)) %>%
ggplot() +
aes(year, lifeExp, size = pop, colour = country) +
annotation_raster(tiger, ymin = ymin, ymax = ymax, xmin = xmin, xmax =
xmax) +
geom_point(alpha = 0.7, show.legend = FALSE) +
scale_colour_manual(values = country_colors) +
scale_size(range = c(2, 12)) +
labs(title = 'Year: {frame_time}', x = 'Year', y = 'life expectancy') +
transition_time(year) +
ease_aes('linear') +
anim_save("animated_tiger_timeseries.gif")
Which produces this...
Is this what you are looking for?
I am plotting a map of Norway with an area of interest highlighted with a red rectangle using ggplot2. If I omit the geom_rect or coord_map, the map plots very quickly (< 1 seconds). If I use both - which I need to - it is extremely slow to print and render (about five minutes).
I presume this is something to do with the munching - projecting the rectangle onto the new coordinate system. Is there a way to control this?
library(ggplot2)
library(maps)
library(mapdata)
xlim <- c(5, 10)
ylim <- c(60, 62)
norwaymap <- map_data("worldHires", "Norway")
a <- ggplot(norwaymap, aes(x = long, y = lat, group = group)) +
geom_polygon(colour = NA, fill = "grey60") +
geom_rect(xmin = xlim[1], xmax = xlim[2], ymin = ylim[1], ymax = ylim[2],
colour = "red", fill = NA) +
coord_map(xlim = c(3, 33), ylim = c(57, 72))
print(a) # super slow
Using the low resolution map makes the map plotting much faster (about 10 seconds).
No need to resort to mercator approximations:
library(ggplot2)
library(maps)
library(mapdata)
norwaymap <- map_data("worldHires", "Norway")
xlim <- c(5, 10)
ylim <- c(60, 62)
ggplot() +
geom_map(data=norwaymap, map=norwaymap,
aes(long, lat, map_id=region),
color=NA, fill="grey60") +
geom_rect(data=data.frame(),
aes(xmin=xlim[1], xmax=xlim[2], ymin=ylim[1], ymax=ylim[2]),
color="red", fill=NA) +
coord_map(xlim=c(3, 33), ylim=c(57, 72)) +
ggthemes::theme_map()
Another option would be to use an Albers equal-area conic projection (a typical one for that region):
ggplot() +
geom_map(data=norwaymap, map=norwaymap,
aes(long, lat, map_id=region),
color=NA, fill="grey60") +
geom_rect(data=data.frame(),
aes(xmin=xlim[1], xmax=xlim[2], ymin=ylim[1], ymax=ylim[2]),
color="red", fill=NA) +
ggalt::coord_proj("+proj=aea +lat_1=60 +lat_2=70 +lon_0=18.37",
xlim=c(3, 33), ylim=c(57, 72)) +
ggthemes::theme_map()
That has a "disadvantage" of the rectangle being projected (it is with Mercator, too, there's just no distortion).
Either way, the magic for the rectangle is ensuring you're plotting only one, like Luke said.
Use coord_quickmap and especially annotate instead of geom_rect to speed things up:
ggplot(norwaymap, aes(x = long, y = lat, group = group)) +
geom_polygon(colour = NA, fill = "grey60") +
annotate(geom="rect", xmin = xlim[1], xmax = xlim[2], ymin = ylim[1],
ymax = ylim[2], colour = "red", fill = NA) +
coord_quickmap(xlim = c(3, 33), ylim = c(57, 72))
geom_rect overplots several rectangles on the same spot, annotate just plots one rectangle. You can read about the difference between coord_map and coord_quickmap in the help files: ?coord_quickmap.