I'm plotting points using geom_sf and ggplot, and would like to change the shape of the points. I can change them on the map, but the legend never reflects the shape of the points, even when using override.aes.
I can't tell if I'm missing something or if this is a bug. I've traced similar issues in the Tidyverse issue tracker, and this one is quite similar. But none of the "resolved" issues seem to address my problem.
Here is an example showing how ggplot fails to propagate the shape to the legend.
library(sf)
library(ggplot2)
cities <- tibble::tribble(
~ lon, ~ lat, ~ name, ~ pop,
5.121420, 52.09074, "Utrecht", 311367,
6.566502, 53.21938, "Groningen", 189991,
4.895168, 52.37022, "Amsterdam", 779808
) %>% sf::st_as_sf(coords = c("lon", "lat"), crs = 4326)
lines_sfc <- sf::st_sfc(list(
sf::st_linestring(rbind(cities$geometry[[1]], cities$geometry[[2]])),
sf::st_linestring(rbind(cities$geometry[[2]], cities$geometry[[3]]))
))
lines <- sf::st_sf(
id = 1:2,
size = c(10,50),
geometry = lines_sfc,
crs = 4326
)
ggplot(cities) +
geom_sf(aes(shape = name))
ggplot(cities) +
geom_sf(aes(shape = name)) +
scale_shape_manual(values = c(1:3),
guide = guide_legend(
override.aes = list(shape = c(1:3))))
I would expect the legend entries to have the same shapes as the map, but instead I get empty squares.
ggplot() +
geom_sf(data = cities, aes(shape = name), show.legend = "point") +
scale_shape_manual(values = c(1, 2, 3))
Related
I am attempting to map some geom-points/cordinates to a map of the country Sri Lanka. I am able to map the district borders, and the population as expected, but I am having trouble plotting the geom points onto the map.
Install package
devtools::install_github("thiyangt/ceylon")
Load package
library("ceylon")
library(tidyverse)
library(sp)
library(viridis)
data(sf_sl_0)
Mapping only Sri Lanka
ggplot(sf_sl_0) + geom_sf()
Mapping the districts of Sri Lanka + population
ggplot(district) + geom_sf(aes(fill = population), show.legend = TRUE) + scale_fill_viridis()
Mappping specific geom-cordinates onto the map of Sri Lanka districts
These are the cordinates I want to map (yes, they are definitely within SL)
df_cord <- data.frame (lat = c("6.2441521", "6.2234515"),
lon = c("80.0590804", "80.2126109"))
I tried:
ggplot(district) +
geom_sf(df_cord) + scale_fill_viridis() +
geom_point(
data = df_cord,
aes(x = lon, y = lat),
size = 4,
shape = 23,
fill = "darkred"
)
But I get an error: Error in validate_mapping():
! mapping must be created by aes()
It looks like I might need to find the x,y cordinates of every geom point, and then map it with cord_sf? But I am not having an luck figuring out how to do this. I found a cool function called usmap::usmap_transform, which converts US geom points to x,y cordinates... but I can't figure out how to do the same for this map of Sri Lanka.
I am very new to mapping -- could someone please advise? Many thanks! I am open to other approaches/solutions!
One way would be to convert the coordinates to an sf object using st_as_sf and plot them using geom_sf. Don't forget to reproject the data to the same coordinate sistem:
library(ceylon)
library(tidyverse)
library(sp)
library(viridis)
library(sf)
data(district)
df_cord <- data.frame (lat = c(6.2441521, 6.2234515),
lon = c(80.0590804, 80.2126109))
df_cord <- df_cord %>%
st_as_sf(coords = c("lon", "lat"), crs = 4326) %>%
st_transform(crs = st_crs(district)) #reproject coords using the coordinate system of the polygons
#plot
ggplot(district) +
geom_sf(aes(fill = population), show.legend = TRUE) +
geom_sf(data = df_cord ,
size = 4,
shape = 23,
fill = "darkred") +
scale_fill_viridis()
I think you can't assign two data frames in ggplot.
Put the latitude and longitude values inside the geom_point's aes(). Remember that longitude is the x-axis and latitude is the y-axis.
Try this:
ggplot() +
geom_sf(district) +
scale_fill_viridis() +
geom_point(
aes(x = c("80.0590804", "80.2126109"),
y =c("6.2441521", "6.2234515")),
size = 4,
shape = 23,
fill = "darkred"
)
You can add annotations (annotate) which will display your two coordinates. Also, set the right coordinate system like this:
ggplot(district) +
geom_sf(aes(fill = population), show.legend = TRUE) +
annotate("point", x = 80.0590804, y = 6.2441521, colour = "red", size = 2) +
annotate("point", x = 80.2126109, y = 6.2234515, colour = "red", size = 2) +
coord_sf(default_crs = sf::st_crs(4326)) +
scale_fill_viridis()
Output:
I’m currently trying to display my data on a map as points. In fact I got two different data sets of two different studies which I’d like to combine on the map. But my first problem was where to find a map to use for my purpose.
#In the R graphics Cookbook I read about this code to create a map using the ggplot2 package
library(ggplot2)
library(maps)
states_map <- mapd_data(“state”)
ggplot(states_map, aes(x=long, y=lat, group=group)) +
geom_polygon(fill=”white”, colour=”black”) +
#Then I added the data of my two data sets and it worked perfectly fine.
geom_point(data = dat1, aes(x = longitude, y = latitude, group= NA), shape=21, col = "black", fill=”red”, size = dat1$var1) +
geom_point(data = dat2, aes(x = longitude, y = latitude, group= NA), shape=21, col = "black", fill=”blue”, size = dat2$var2)
Now I want to add two legends, one to explain the difference between the point colors and the other to explain the point size. Both data sets can share the same legend for the point size because Var1 and Var2 are on the same scale. But I don’t know how to add custom legends to a ggplot (I even don’t achieve to add any legend to the ggplot). It would be nice to have anything like legend()-function which adds a legend to a normal plot, but I think ggplot wasn’t programmed to solve problems like this on your own. Another approach to solve the problem could be to plot the map without using ggplot.
If you want a legend then you have to map on an aesthetic, i.e. move fill=... and size=... inside aes(). After doing so you could set your desired fill colors via scale_fill_manual:
library(ggplot2)
library(maps)
states_map <- map_data("state")
dat1 <- data.frame(longitude = -110, latitude = 35, var1 = 100)
dat2 <- data.frame(longitude = -90, latitude = 40, var2 = 200)
ggplot(states_map, aes(x=long, y=lat)) +
geom_polygon(aes(group=group), fill="white", colour="black") +
geom_point(data = dat1, aes(x = longitude, y = latitude, fill = "dat1", size = var1), shape=21, col = "black") +
geom_point(data = dat2, aes(x = longitude, y = latitude, fill = "dat2", size = var2), shape=21, col = "black") +
scale_fill_manual(values = c(dat1 = "red", dat2 ="blue"), labels = c(dat1 = "Data 1", dat2 ="Data 2"))
[US state with geom_path][1][US state with geom_point][2]I am using R to overlay US states shape file above ogallala region shape file. I would ideally like to have shape boundaries as line but I get poorly formed map when I try that (in pictures) but when I try geom_point it works alright. Can someone please explain what I am doing wrong. [US state with geom_line][3]
OG_HUC = read.csv("input/Ogallala_huc.csv")
OG_table =right_join(HUC8_map.df,OG_HUC,by = c("HUC_CODE"="HUC8"))
#OG_table = merge(HUC8_map.df,OG_HUC,by = "HUC8", sort = FALSE)
OG_table[is.na(OG_table)] = 0
#write.csv(OG_table,'OG_table.csv')
State <- readOGR(
dsn= paste0(getwd(),"/input/State") ,
layer="states"
)
State_map <- spTransform(State, CRS("+proj=longlat +datum=WGS84"))
State_map#data$id = rownames(State_map#data)
State_map.points = fortify(State_map, region="id")
centroids.df <- as.data.frame(coordinates(State_map))
names(centroids.df) <- c("Longitude", "Latitude") #more sensible column names
State_map.df1 = merge(State_map.points, State_map#data, by="id")
State_map.df2 = data.frame(id = State_map#data$id, State_map#data, centroids.df)
ggplot()+geom_polygon(data=OG_table,aes(x = long, y = lat, group=group),fill="lightskyblue",col="black", alpha = 0.3) +
geom_text(data = State_map.df2, aes(Longitude, Latitude, label=STATE_ABBR),col="black")+
#geom_path(data = OG_table, aes(long, lat, group=group),color="black") +
geom_point(data = State_map.df1, aes(long, lat, label=STATE_ABBR),color="black")+
coord_map(xlim = c(-108,-95),ylim = c(31,45))+
scale_fill_identity()
```enter image description here
[1]: https://i.stack.imgur.com/7XzeB.png
[2]: https://i.stack.imgur.com/9tAB8.png
[3]: https://i.stack.imgur.com/MtgXs.png
Try:
Library (maps)
Your_data %>% ggplot (aes(lat, lon)) +
Borders ("states") +
geom_polygon()
I have the following:
set.seed(100)
df <- data.frame(
lng = runif(n=20, min=5, max=10),
lat = runif(n=20, min=40, max=50),
year = rep(c("2001","2002","2003","2004"), each=5),
season = sample(c("spring", "autumn"), 10, replace = T),
info = sample(c("yes","no"), 10, replace = T)
)
Which can be plotted by:
ggplot() +
geom_point(data=df,
aes(x = lng,
y = lat,
color = year,
shape = season),
size=3)
To produce:
Great. But I want a red outline on the shapes were info == "yes".
The desired output would be:
Not made using actual data, just for demonstrative purpose. Made in powerpoint.
Admittedly it is similar to this question here, but not quite.
I am happy to split the df using a filter if easier then two + geom_points()
Many thanks
Jim
Below is a quick solution (not the best), which is to use another scale, and below I use size as the scale, then use guides() to manually specify the shape to appear in the legend. you need to plot the bigger red shapes first and then plot over so that it looks like an outline:
ggplot() +
geom_point(data=subset(df,info=="yes"),
aes(x=lng,y=lat,shape = season,size=info),col="red") +
scale_size_manual(values=3.6)+
geom_point(data=df,
aes(x = lng,
y = lat,
color = year,
shape = season),
size=3)+
guides(size = guide_legend(override.aes = list(shape = 1)))
You can change the legend for the shape by playing around with options in the guide()
As I know gganimate has been in version 1.0.3,we can use transition_* function to plot a dynamic graph. But when I run following code ,there is an error :
Error in `$<-.data.frame`(`*tmp*`, "group", value = "") :
replacement has 1 row, data has 0
code:
library(ggmap)
library(gganimate)
world <- map_data("world")
world <- world[world$region!="Antarctica",]
data <- data.frame(state = c("Alabama","Alaska","Alberta","Alberta","Arizona"),
lon = c(-86.55,-149.52,-114.05,-113.25,-112.05),
lat = c(33.30,61.13,51.05,53.34,33.30)
)
ggplot()+
geom_map(data = world,
map = world,
aes(long,lat,map_id = region),
color = '#333300',
fill = '#663300') +
geom_point(data = data,
aes(x = lon, y = lat),
size = 2.5) +
geom_jitter(width = 0.1) +
transition_states(states = state)
You have no data defined in the top level ggplot() line, so state in transition_* comes out of nowhere.
It's also unclear to me why you have a geom_jitter level in your code. Like transition_*, it has no top level data / aesthetic mappings to inherit, so it would have thrown an error too, had transition_* not triggered an error first. In addition, even if we add the mappings, given the range of lat/lon coordinates in your data, jittering by 0.1 is hardly going to matter visually.
You can try the following:
# put data in top level ggplot()
ggplot(data,
aes(x = lon, y = lat))+
geom_map(data = world,
map = world,
aes(long,lat,map_id = region),
color = '#333300', fill = '#663300',
# lighter background for better visibility
alpha = 0.5) +
geom_point(size = 2.5) +
# limit coordinates to relevant range
coord_quickmap(x = c(-180, -50), y = c(25, 85)) +
transition_states(states = state)