Related
Suppose we want to plot this data:
library(ggplot2)
library(sf)
library(raster)
library(colorRamps)
min_lon <- 10
max_lon <- 17
min_lat <- 8
max_lat <- 17
grid_size <- 0.5
lon_grids <- 1 + ((max_lon - min_lon)/grid_size)
lat_grids <- 1 + ((max_lat - min_lat)/grid_size)
points <- data.frame(lon = rep(seq(min_lon, max_lon, grid_size), lat_grids), lat = rep(seq(min_lat, max_lat, grid_size), each = lon_grids))
points$Var <- runif(min= 10, max = 48, 285)
points$value <-cut(points$Var, breaks= seq(10.08, 47.80, length.out = 13), dig.lab = 1)
ggplot() +
coord_sf(xlim = c(min_lon, max_lon), ylim = c(min_lat, max_lat)) +
theme_bw()+
geom_raster(data = points, aes(x = lon, y = lat, fill = value), interpolate = FALSE) +
labs(x="Longitude", y="Latitude")+
scale_fill_manual(values = matlab.like(n = 13), name = "[m]",
labels = sprintf("%.2f", seq(10.08, 47.80, length.out = 13)),
guide = guide_legend(reverse = TRUE))+theme(legend.position = "bottom")
This code produces the following graph:
Two problems I am facing here:
To make it discrete, I used the cut function. I chose the breaks= seq(10.08, 47.80, length.out = 13) arbitrary based on the minimum and maximum values with a random length of 13. Is there any criteria to decide the correct range?
Is there any way to make the legend look like this?
One option would be to use e.g. scale_fill_stepsn with guide_binswhich does not require to manually discretize the variable mapped on fill. Additionally I use a custom function to set the breaks of the legend instead of the default mechanism to set the number of breaks.
set.seed(123)
library(ggplot2)
library(colorRamps)
base <- ggplot() +
coord_sf(xlim = c(min_lon, max_lon), ylim = c(min_lat, max_lat)) +
theme_bw() +
geom_raster(data = points, aes(x = lon, y = lat), interpolate = FALSE) +
labs(x = "Longitude", y = "Latitude") +
theme(legend.position = "bottom")
base +
aes(fill = Var) +
scale_fill_stepsn(colors = matlab.like(n = 13), name = "[m]",
breaks = function(x) seq(x[[1]], x[[2]], length.out = 13),
labels = ~ sprintf("%.0f", .x),
guide = guide_bins(axis = FALSE,
show.limits = TRUE))
I have two dataframes both recording the top 10 stations riders went. One is for casual rider, the other one is for member rider. Both dataframes contain column 'station','freq','latitude','longitude'. I'm able to use ggmap to plot the graph showing the locations of the stations from both dataframes, but not able to show the legend.
R scripe is showing below:
library(ggplot2)
library(rstudioapi)
library(ggmap)
map_location <- c (lon = -87.623177, lat = 41.881832)
chicago_map_zoom <- get_map (location = map_location,
maptype = 'roadmap',
color='bw',
source='google',
zoom=13,
)
chicago_plot <- ggmap(chicago_map_zoom) +
geom_point (data = casual_top_station,
aes (x = longitude,
y = latitude),
color = "red",
shape = 15,
alpha = 0.5,
size = 3) +
geom_point (data = member_top_station,
aes (x = longitude,
y = latitude),
color = "blue",
shape = 16,
alpha = 0.5,
size = 2) +
scale_color_identity (name = "Subscription type",
breaks = c("red","blue"),
labels = c("Casual","Member"),
guide = "legend") +
theme (axis.ticks = element_blank(),
axis.text = element_blank(),
axis.title = element_blank()) +
labs (title = "Top 10 casual and member rider stations",
subtitle = "Both start and end stations")
Result graph: Chicago_map
Instead of using scale_color_identity ... to set the values for color, shape and size I would suggest to first an id column to your data.frames
which could then be mapped on aesthetics inside aes. Afterwards set your desired colors, shapes and sizes via the scale_xxx_manual family of functions.
Using some fake data for the points:
library(ggplot2)
library(ggmap)
casual_top_station <- data.frame(
longitude = -87.65,
latitude = 41.9
)
member_top_station <- data.frame(
longitude = -87.65,
latitude = 41.86
)
casual_top_station$id <- "Casual"
member_top_station$id <- "Member"
legend_title <- "Subscription type"
base <- ggmap(chicago_map_zoom) +
scale_color_manual(values = c(Casual = "red", Member = "blue")) +
scale_shape_manual(values = c(Casual = 15, Member = 16)) +
scale_size_manual(values = c(Casual = 3, Member = 2)) +
theme(
axis.ticks = element_blank(),
axis.text = element_blank(),
axis.title = element_blank()
) +
labs(
title = "Top 10 casual and member rider stations",
subtitle = "Both start and end stations",
color = legend_title, shape = legend_title, size = legend_title
)
base +
geom_point(
data = casual_top_station,
aes(
x = longitude,
y = latitude,
color = id, shape = id, size = id
),
alpha = 0.5
) +
geom_point(
data = member_top_station,
aes(
x = longitude,
y = latitude,
color = id, shape = id, size = id
),
alpha = 0.5
)
Also, to simplify your code further I would suggest to bind both data frames by row using e.g. dplyr::bind_rows which would allow to add your points via just one geom_point.
top_station <- dplyr::bind_rows(casual_top_station, member_top_station)
base +
geom_point(
data = top_station,
aes(
x = longitude,
y = latitude,
color = id, shape = id, size = id
), alpha = .5)
I created a multi-layer map using ggmap() in R. I would like to add an arc between two points on the map. I tried doing so with geom_curve(). This doesn't work. The two maps in the overlay are then no longer properly superimposed. I suspect this is due to the different projections within the map overlay, but I don't fully understand this.
Is there a way to specify a coordinate reference system (CRS) for an arc that matches the CRS in use in the map already? Or a totally different way I could add one arc between two points in my exisiting map?
A similar question with an accepted solution was already asked on stack overflow ("Draw curved lines in ggmap, geom_curve not working"). However, as noted there, the solution doesn't work for larger map areas.
Here is a minimal working example with some comments:
My system
I'm using R 4.2.0 with RStudio version 2022.02.3 Build 492.
Required Packages
install.packages("rnaturalearth")
install.packages("devtools") # Needed for rnaturalearthhires
devtools::install_github("ropensci/rnaturalearthhires") # Ditto
install.packages("ggmap")
install.packages("ggplot2")
install.packages("dplyr")
install.packages("PBSmapping") #For clipping polygons
install.packages("ggsn") #For the scale bar
library(rnaturalearth)
library(rnaturalearthhires) # Needed for scale = large in ne_countries()
library(ggmap)
library(ggplot2)
library(dplyr)
library(PBSmapping)
library(ggsn)
Plot a map that includes Germany and Saudi Arabia
Define the map for the region including Germany and Saudi Arabia (KSA) with
latitude and longitude (x min, y min, x max, y max)
GermanyKSA_map_coordinates <- c(5, 16, 56, 56)
Set unique values for the two countries for coloring them on the larger map
GermanyKSA_values <- data.frame(region = factor(c("Germany", "Saudi Arabia")), data = c(15, 15))
Get an existing map of the region including both countries
GermanyKSA_map <- get_map(location = GermanyKSA_map_coordinates, source = "stamen", zoom = 6)
Get country polygon data and join these to the unique values
world <- ne_countries(scale = "large", returnclass = "sf")
mapdata <- map_data("world")
GermanyKSA_mapdata <- left_join(mapdata, GermanyKSA_values, by = "region")`
Create a bounding box for the region map
GermanyKSA_map_bb <- attr(GermanyKSA_map, "bb")
GermanyKSA_ylim <- c(GermanyKSA_map_bb$ll.lat, GermanyKSA_map_bb$ur.lat)
GermanyKSA_xlim <- c(GermanyKSA_map_bb$ll.lon, GermanyKSA_map_bb$ur.lon)`
Clip polygons to match the map
colnames(GermanyKSA_mapdata)[1:6] <- c("X","Y","PID","POS","region","subregion")
GermanyKSA_mapdata <- clipPolys(GermanyKSA_mapdata, xlim = GermanyKSA_xlim, ylim = GermanyKSA_ylim, keepExtra = TRUE)`
Add dots for the cities Regensburg, Germany, and Al-Hofuf, Saudi Arabia
Regensburg_Dot <- c(12.1015461, 49.0204469)
AlHofuf_Dot <- c(49.401569, 25.363091)`
Create a data frame with the dot coordinates
Dots <- rbind(Regensburg_Dot, AlHofuf_Dot) %>% as.data.frame()
Rename the columns
colnames(Dots) <- c("long", "lat")
Plot map overlay. Provides exactly what I need, only an arc between the two cities is missing
ggmap(GermanyKSA_map) +
coord_map(xlim = GermanyKSA_xlim, ylim = GermanyKSA_ylim) +
geom_polygon(data = GermanyKSA_mapdata, aes(x = X, y = Y, group = PID, fill = data),
color = "black", alpha=0.5) +
geom_point(data = Dots, aes(x = long, y = lat), col = "#990022", size = 4) +
scalebar(x.min = 5, x.max = 56, y.min = 16, y.max = 56, dist = 500,
dist_unit = "km", transform = TRUE, anchor = c(x = 52, y = 54),
box.fill = c("#990022", 'grey'), box.color = c("#990022", 'grey'),
st.dist = .025, st.size = 3) +
theme(legend.position = "none") +
theme(axis.title.x = element_blank(), axis.title.y = element_blank()) +
ggtitle("Regensburg–Al-Hofuf: 4,093 km")`
Unsuccessful attempt to add an arc between both points (Regensburg and Al-Hofuf)
Create data frame for drawing an arc
RegensburgToAlHofuf <- data.frame(Regensburg_x = 12.1015461,
Alhofuf_x = 49.401569, Regensburg_y = 49.0204469, Alhofuf_y = 25.363091)`
Plot map overlay, now with an attempt to draw an arc with geom_curve(). Resulting warning: "geom_curve is not implemented for non-linear coordinates"
ggmap(GermanyKSA_map) +
coord_map(xlim = GermanyKSA_xlim, ylim = GermanyKSA_ylim) +
geom_polygon(data = GermanyKSA_mapdata, aes(x = X, y = Y, group = PID,
fill = data), color = "black", alpha=0.5) +
geom_point(data = Dots, aes(x = long, y = lat), col = "#990022", size = 4) +
geom_curve(data = RegensburgToAlHofuf,
aes(x = Regensburg_x, y = Regensburg_y, xend = Alhofuf_x, yend = Alhofuf_y),
size = 1.5, color = "#990022", curvature = 0.15, inherit.aes = TRUE) +
scalebar(x.min = 5, x.max = 56, y.min = 16, y.max = 56, dist = 500,
dist_unit = "km", transform = TRUE, anchor = c(x = 52, y = 54),
box.fill = c("#990022", 'grey'), box.color = c("#990022", 'grey'),
st.dist = .025, st.size = 3) +
theme(legend.position = "none") +
theme(axis.title.x = element_blank(), axis.title.y = element_blank()) +
ggtitle("Regensburg–Al-Hofuf: 4,093 km")`
I added a call to a map projection, coord_cartesian(), following suggestion in "Draw curved lines in ggmap, geom_curve not working". However, plots are not correctly superimposed. I also tried coord_quickmap() instead of coord_cartesian(), which also didn't work.
ggmap(GermanyKSA_map) +
coord_map(xlim = GermanyKSA_xlim, ylim = GermanyKSA_ylim) +
geom_polygon(data = GermanyKSA_mapdata, aes(x = X, y = Y, group = PID,
fill = data), color = "black", alpha=0.5) +
geom_point(data = Dots, aes(x = long, y = lat), col = "#990022", size = 4) +
geom_curve(data = RegensburgToAlHofuf,
aes(x = Regensburg_x, y = Regensburg_y, xend = Alhofuf_x, yend = Alhofuf_y),
size = 1.5, color = "#990022", curvature = 0.15, inherit.aes = TRUE) +
scalebar(x.min = 5, x.max = 56, y.min = 16, y.max = 56, dist = 500,
dist_unit = "km", transform = TRUE, anchor = c(x = 52, y = 54),
box.fill = c("#990022", 'grey'), box.color = c("#990022", 'grey'),
st.dist = .025, st.size = 3) +
theme(legend.position = "none") +
theme(axis.title.x = element_blank(), axis.title.y = element_blank()) +
ggtitle("Regensburg–Al-Hofuf: 4,093 km") +
coord_cartesian()`
I am trying to draw a blue circle on the 66th parallel north. on a map I am making.
I usually fortify() a SpatialPolygonsDataFrame and then use the combination geom_polygon(), coord_map("ortho" , ylim = c(50, 90)) and geom_hline() to achieve this.
However, for a new project I will have to use ggspatial::layer_spatial() on an object that already has a CRS date, so this straight line drawing and automatic re-projection does not work.
This is a demonstration of my usual approach, which works:
library(tidyverse)
map_data <- rnaturalearth::ne_countries()
map_data_df <- fortify(map_data)
ggplot() +
geom_polygon(data = map_data_df,
aes(x = long,
y = lat,
group = group),
col = "black",
fill = "lightgrey") +
coord_map("ortho" , ylim = c(50, 90)) +
geom_hline(yintercept = 66, col = "blue")
and I am trying to replicate it in this code, but that obviously does not work because the geom_hline() does not have the right projection:
map_data_transformed <- sp::spTransform(map_data, sp::CRS("+init=epsg:3995"))
# needs some fixing to close all the polygons
map_data_transformed <- rgeos::gBuffer(map_data_transformed, byid=TRUE, width=0)
ggplot() +
ggspatial::layer_spatial(data = map_data_transformed,
fill = "lightgrey", col = "black", size = .2)+
coord_sf( xlim = c(-4500000 , 4500000),
ylim = c(-4500000 , 4500000)) +
geom_hline(yintercept = 66, col = "blue")
See if this works for you?
# define parallel north line as a spatial polygon with latitude / longitude projection
new_line <- sp::Polygon(coords = matrix(c(seq(-180, 180),
rep(66, times = 361)), # change this for other
# intercept values
ncol = 2))
new_line <- sp::SpatialPolygons(Srl = list(sp::Polygons(srl = list(new_line),
ID = "new.line")),
proj4string = sp::CRS("+proj=longlat +datum=WGS84"))
# change projection to match that of data
new_line <- sp::spTransform(new_line, sp::CRS("+init=epsg:3995"))
# plot
ggplot() +
ggspatial::layer_spatial(data = map_data_transformed,
fill = "lightgrey", col = "black", size = .2) +
ggspatial::layer_spatial(data = new_line,
fill = NA, col = "blue") +
coord_sf( xlim = c(-4500000 , 4500000),
ylim = c(-4500000 , 4500000))
I want to plot data points for various cities over a greyed-out map from google. As these cities are some distance from each other, I thought I would use a faceted plot.
Creating the map is easy enough; see image and code below. However, each facet shows the same area - in this case Greater London - with the result that the points for other cities are not shown.
Ideally I would like each facet to show each city with the relevant points overlaid. So the facet 'Cardiff' would show a zoomed map of Cardiff and its data points, 'Birmingham' would show Birmingham and its points and so on. I've tried changing various parameters such as zoom and center but I haven't been successful.
How can I show a different city and the relevant points in each facet?
require(ggmap)
require(reshape)
# create fake data
sites <- data.frame(site = 1:6,
name = c(
"Royal Albert Hall",
"Tower of London",
"Wales Millenium Centre",
"Cardiff Bay Barrage",
"Birmingham Bullring",
"Birmingham New Street Station"
),
coords = c(
"51.501076,-0.177265",
"51.508075,-0.07605",
"51.465211,-3.163208",
"51.44609,-3.166652",
"52.477644,-1.894158",
"52.477487,-1.898836"),
subzone = rep(c('London','Cardiff','Birmingham'), each = 2)
)
# use function from reshape to split/add column
sites = transform(sites,
new = colsplit(coords, split = ",", names = c('lat', 'lon')))
names(sites) <- c(names(sites)[1:4], 'lat','lon')
ggmap(get_googlemap(center = "London", # omitting this doesn't help
scale = 2,
zoom = 11, # fiddling with zoom doesn't work
color = 'bw',
maptype = 'roadmap',
extent = 'panel',
format = "png8",
filename = "facet_map_test",
)) +
facet_wrap(~ subzone, ncol = 1) +
geom_point(data = sites,
aes(x = lon, y = lat),
fill = "red",
size = 3,
colour = "black",
shape = 21,
alpha = 1) +
theme(legend.position = "none") +
theme()
Using the gridExtra package is probably the best way to go. The code:
# getting the maps
londonmap <- get_map(location = c(lon = -0.1266575, lat = 51.504575), zoom = 12)
cardiffmap <- get_map(location = c(lon = -3.16493, lat = 51.45565), zoom = 13)
birminghammap <- get_map(location = c(lon = -1.896497, lat = 52.477565), zoom = 14)
# plotting the maps
p1 <- ggmap(londonmap) +
geom_point(data = sites[sites$subzone == "London",],
aes(x = lon, y = lat, fill = "red", alpha = 0.8, size = 3),
shape = 21) +
ggtitle("London") +
theme(axis.title = element_blank(), legend.position = "none", plot.margin = unit(c(0,0,0,0), "lines"))
p2 <- ggmap(cardiffmap) +
geom_point(data = sites[sites$subzone == "Cardiff",],
aes(x = lon, y = lat, fill = "red", alpha = 0.8, size = 3),
shape = 21) +
ggtitle("Cardiff") +
theme(axis.title = element_blank(), legend.position = "none", plot.margin = unit(c(0,0,0,0), "lines"))
p3 <- ggmap(birminghammap) +
geom_point(data = sites[sites$subzone == "Birmingham",],
aes(x = lon, y = lat, fill = "red", alpha = 0.8, size = 3),
shape = 21) +
ggtitle("Birmingham") +
theme(axis.title = element_blank(), legend.position = "none", plot.margin = unit(c(0,0,0,0), "lines"))
# grouping the plots together in one plot
grid.arrange(p1, p2, p3, ncol = 1)
The result: