I need to plot some data over a map of Antarctica and I managed to do so following this thread
Polar/Stereographic map in R
library(rgdal)
library(raster)
library(ggplot2)
Long <- c(-75, -76, -77, -78, -79)
Lat <- c(123, 124, 125, 126, 127)
Sodio<- c(110, 209, 65.7, 47.6, 135)
data<-data.frame(Long, Lat, Sodio)
# Defines the x axes required
x_lines <- seq(-120,180, by = 60)
wm <- map_data("world")
ggplot() +
geom_polygon(data = wm, aes(x = long, y = lat, group = group), fill = "grey", colour = "black", alpha = 0.8) +
# Convert to polar coordinates
coord_map("ortho", orientation = c(-90, 0, 0)) +
scale_y_continuous(limits=c(-90,-60),breaks = seq(-45, -90, by = -5), labels = NULL) +
# Removes Axes and labels
scale_x_continuous(breaks = NULL) +
xlab("") +
ylab("") +
# Adds labels
geom_text(aes(x = -160, y = seq(-55, -85, by = -10), hjust = -0.2, label = paste0(seq(55, 85, by = 10), "°S"))) +
geom_text(aes(x = x_lines, y = -63, label = c("120°W", "60°W", "0°", "60°E", "120°E", "180°W"))) +
# Adds axes
geom_hline(aes(yintercept = -60), size = 1) +
geom_segment(aes(y = -60, yend = -90, x = x_lines, xend = x_lines), linetype = "dashed") +
# Change theme to remove axes and ticks
theme(panel.background = element_blank(),
panel.grid.major = element_line(size = 0.25, linetype = 'dashed',
colour = "black"),
axis.ticks=element_blank()) +
geom_point(data=data, aes(x=Long, y=Lat, color=Sodio))+
scale_color_gradient(low="blue", high="red")
And the code works. Like a charm and kudos to the original user to posting it because it helped me a ton.
The problem is that since all the data are in a small sector (mostly between 85° and 65 °N and between 180° W and 130° W, the one I posted are just a super short dummy set) I was tryig to plot just that "wedge" of the Antarctic continent in order to help me visualize the data more clearly. I could just expland the plot and cut it with photoshop but I need to plot a number of these.
I tried changing the projection to a conic one but it starts doing... extremely weird stuff like trying to peoject the entirety of the world even when I try to put limits in the scale_y_continuous.
Any idea on how to solve this? Also, any idea on how I can add "elevation" lines on the map? I'm working in ggplot because I need to plot some data on top of it afterwards and I'm more familiar with that package of R, but I'm ok with other packages as long as they allow me to plot data on top of it and they can save the plot at 300 dpi...
Related
I'm making a ggplot of a basemap with some points overlaid. I believe the default for plotting a basemap is to have the axes plot in latlong with degrees N/W (even though its projected in web mercator) but the degree symbol isn't showing up and instead is displayed as a little box?
library(sf)
library(basemaps)
library(ggplot2)
library(ggstar)
pt_df = data.frame(X = c(-10069438, -10827213),
Y = c(4014585, 4013104),
Type = c("MS", "OK"))
pt_df = st_as_sf(pt_df, coords = c("X", "Y"), crs = 3857)
bbox = st_box(pt_df)
bbox["xmin"]<- -11500000
bbox["xmax"]<- -9900000
bbox["ymin"] <- 3000000
bbox["ymax"] <- 4500000
basemap_ggplot(bbox, map_service="osm_stamen", map_type="terrain")+
geom_star(data = pt_df, aes(x=X, y=Y, fill="red"), size=5)+
xlab("")+
ylab("")
I wrote out the expression to recode the x- and y- axes yet they're still showing boxes.
basemap_ggplot(bbox, map_service="osm_stamen",
map_type="terrain")+
geom_star(data = pt_df, aes(x=X, y=Y, fill="red"), size=5)+
xlab("")+
ylab("")+
scale_y_continuous(breaks = c(28, 30, 32, 34, 36),
labels = c(expression(28~degree~N),
expression(30~degree~N),
expression(32~degree~N),
expression(34~degree~N),
expression(36~degree~N)))+
scale_x_continuous(breaks = c(-100, -95, -90, -85),
labels = c(expression(-100~degree~W),
expression(-95~degree~W),
expression(-90~degree~W),
expression(-85~degree~W)))
Any ideas?
Here's an example of my code but I almost think it might be some sort of setting issue and therefore isn't reproducible on someone else's computer? Any help/ideas appreciated.
We could insert unicode character:
scale_y_continuous(labels = function(x) paste0(x, '\u00B0', "N"))
ggplot() +
geom_sf(
data = border$osm_lines,
inherit.aes = FALSE,
color = "black",
size = .4,
alpha = .8
)+
scale_y_continuous(labels = ~ paste0(., '\u00B0', "N"))+
scale_x_continuous(labels = ~ paste0(., '\u00B0', "N"))
I would like to create a plot of wind directions displayed by arrows for several points.
This plot should have no legend and no background since I would like to export it as png and use it as overlay in further workflow.
I have an example data fame with 10 points (lon, lat) and wind directions in degrees.
Currently my plot looks like this:
I would like to:
extend the plot to the size of
3.604383, 14.60482, 47.07157, 54.73807 (xmin, xmax, ymin, ymax)
and
remove axis and background so I can export it as png or tiff or a raster with only arrows displayed.
Here is my current approach:
library(ggplot)
wind_data<-
structure(list(lon = c(8.87312091729266, 8.71871388830953, 10.5679127453358,
10.7216122406487, 10.4141464047873, 10.5679127453358, 10.1064188710028,
11.3357135330171, 11.4890576651767, 11.1822955254471), lat = c(54.6482360923283,
54.5584019137964, 54.2888913181823, 54.2888913181823, 54.1990517614177,
54.1990517614177, 54.0193686018903, 53.6599860593553, 53.6599860593553,
53.5701370357575), dd = c(238, 235, 238, 232, 232, 236, 239,
240, 240, 240)), class = "data.frame", row.names = c(NA, -10L
p<-ggplot(wind_data, aes(x = lon, y = lat)) +
geom_segment(aes(xend = lon + 0.08, yend = lat + 0.08),
arrow = arrow(length = unit(0.1, "cm")), size = 0.25) # Make the line segments 0.25 mm thick
p + theme_bw() + theme(panel.border = element_blank(), panel.grid.major = element_blank(),
panel.grid.minor = element_blank(), axis.line = element_line(colour = "black"),legend.position="none")
I tried to set the size of the plot by using
coord_cartesian(xlim = c(3.604383, 14.60482), ylim = ( 47.07157, 54.73807 ))
and removing legend
legend.position="none"
but it does not seem to work.
Besides the issue with not re-assignning to a variable after adjusting the limits and theme options a quick and easy approach to get rid of all non-data ink would be to use theme_void() instead of removing the theme elements one by one. However, one has to be aware that doing so will also remove e.g. the plot margins. But if needed adding them back in is still easily achieved by using e.g. + theme(plot.margin = margin(5.5, 5.5, 5.5, 5.5, unit = "pt")) for the default margins.
library(ggplot2)
p <- ggplot(wind_data, aes(x = lon, y = lat)) +
geom_segment(aes(xend = lon + 0.08, yend = lat + 0.08),
arrow = arrow(length = unit(0.1, "cm")), size = 0.25
) +
coord_cartesian(xlim = c(3.604383, 14.60482), ylim = c( 47.07157, 54.73807 )) +
theme_void()
p
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)
I am trying to produce a sterographic map similarly to this:
What I am trying to do is to add:
Coordinates
Graticule lines
This can be in both base R or with ggplot2. Any help is appreciated.
My attempts so far
library(rgdal)
library(raster)
proj <- "+proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 +k=1 +x_0=0 +y_0=0 +a=6378273 +b=6356889.449 +units=m +no_defs"
data("wrld_simpl", package = "maptools")
wm <- crop(wrld_simpl, extent(-180, 180, 45, 90))
plot(wm)
wm <- spTransform(wm, CRSobj = CRS(proj))
plot(wm)
This is quite a complex map to reproduce, and all the details required to make it work seem beyond the scope of a single question. However, this is most of the stuff you require.
Doing this in ggplot is easier to do that using the base graphics. However, it is quite a complex graph to make.
I have had to use a few hacks to get it to work. In particular, the axes produced from coord_map did not end at the edge of the plot, so I had to manually delete the axes and then recreate them using the geom_text and geom_segment lines below.
library(rgdal)
library(raster)
library(ggplot2)
# Defines the x axes required
x_lines <- seq(-120,180, by = 60)
ggplot() +
geom_polygon(data = wm_ggplot, aes(x = long, y = lat, group = group), fill = "grey", colour = "black", alpha = 0.8) +
# Convert to polar coordinates
coord_map("ortho", orientation = c(90, 0, 0)) +
scale_y_continuous(breaks = seq(45, 90, by = 5), labels = NULL) +
# Removes Axes and labels
scale_x_continuous(breaks = NULL) +
xlab("") +
ylab("") +
# Adds labels
geom_text(aes(x = 180, y = seq(55, 85, by = 10), hjust = -0.2, label = paste0(seq(55, 85, by = 10), "°N"))) +
geom_text(aes(x = x_lines, y = 39, label = c("120°W", "60°W", "0°", "60°E", "120°E", "180°W"))) +
# Adds axes
geom_hline(aes(yintercept = 45), size = 1) +
geom_segment(aes(y = 45, yend = 90, x = x_lines, xend = x_lines), linetype = "dashed") +
# Change theme to remove axes and ticks
theme(panel.background = element_blank(),
panel.grid.major = element_line(size = 0.25, linetype = 'dashed',
colour = "black"),
axis.ticks=element_blank()) +
labs(caption = "Designed by Mikey Harper")
An alternative solution using the PlotSvalbard package:
# devtools::install_github("MikkoVihtakari/PlotSvalbard") ## Run once
library(PlotSvalbard)
basemap("panarctic", limits = 60)
The function also plots bathymetry:
basemap("panarctic", limits = 60, bathymetry = TRUE)
Check the user manual for further features. Plotting data is possible using ggplot2 syntax. Use the add_land function to add land shapes on top of the raster you need to plot (needs to be in the same projection than the Pan-Arctic basemaps, see map_projection("panarctic")). The transform_coord function might be helpful for transforming projections.
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.