I'm trying to highlight zones on a map based on an associated factor, like so:
My initial problem was that, because the boundaries are overlapping, the zones would display improperly, like this:
I fixed that by hackily subsetting the data, which resulted in the first picture.
#First Map With No Legend
p <- ggplot() +
geom_polygon(data = OpportunityAreas.df[OpportunityAreas.df$Opp_Area == 'Yes',],
aes(long,lat,group=group), fill = "#d2eef7", color = 'black') +
geom_polygon(data = OpportunityAreas.df[OpportunityAreas.df$Opp_Area == 'No',],
aes(long,lat,group=group), fill = "#d2eef7", color = 'orange') +
coord_map()
But now I have no legend and also it just seems like a poor solution. Is there a proper way to color polygon boundaries by an associated factor?
#Second Map With Improper Boundaries
p.improperly.drawn <- ggplot() +
geom_polygon(data = OpportunityAreas.df,
aes(long,lat,group=group, color = Opp_Area), fill = "#d2eef7") +
coord_map()
Your first solution needs only a small tweak to get what you are looking for: move the color arguments within the geom_polygon calls to a mapping = aes() call instead. For the first call, mapping = aes(color = 'black') and for the second geom_polygon call use mapping = aes(color = 'orange').
I do not have access to the OpportunityAreas.df in your example so I will use some generic mapping data for my example below.
library(ggplot2)
library(mapdata)
counties <- map_data("county")
ca_county <- subset(counties, region == "california")
ca_county$color <- cut(ca_county$group, 2, labels = c("A", "B"))
ggplot() +
aes(x = long, y = lat, group = group) +
geom_polygon(data = subset(ca_county, color == "A"),
mapping = aes(color = "A"),
fill = NA) +
geom_polygon(data = subset(ca_county, color == "B"),
mapping = aes(color = "B"),
fill = NA)
Related
I am trying to make a map of the U.S. which shows two categorical variables, for example the income group of the state and the region the state belongs in. The idea is to use the "fill" aesthetic to show the income level of each state, and then the "color" aesthetic to show the outlines of each region. The information that I am trying to communicate is that lower-income and higher-income states are clustered in certain regions.
An alternative would be to somehow show the regional boundaries with a bolder or thicker boundary than the state boundaries, but I am also unsure how to do this. Other ideas which communicate the same information would also be welcome.
Ideally, it would be some combination of the following two plots:
## Create map data
state_map_data <- map_data("state")
state_regions <- tibble(state_name = tolower(state.name), state.region,
as_tibble(state.x77)) %>%
mutate(income_cat = cut(Income, breaks = 3,
labels = c("low", "medium", "high")))
state_map_data <- state_map_data %>%
left_join(state_regions,
by = c("region" = "state_name"))
## Map with just income
p1 <- ggplot() +
geom_polygon(data = state_map_data,
aes(x = long, y = lat, group = group,
fill = income_cat))
print(p1)
This generates the following map with income
## Map with just regions
p2 <- ggplot() +
geom_polygon(data = state_map_data,
aes(x = long, y = lat, group = group,
color = state.region))
print(p2)
This generates the following map with regions
## Map with both
p <- ggplot() +
geom_polygon(data = state_map_data,
aes(x = long, y = lat, group = group,
fill = income_cat)) +
geom_polygon(data = state_map_data,
aes(x = long, y = lat, group = group,
color = state.region))
print(p)
This does not produce the expected results of a map with both a color outline by region and filled states by income as seen here
The way you have your code you are drawing two sets of polygons, with state.region polygons on top of the income_cat polygons. Instead, you want to draw one set of polygons with the correct outline color and fill color:
ggplot() +
geom_polygon(data = state_map_data,
aes(x = long, y = lat, group = group,
fill = income_cat, color = state.region)
)
I am kind of stuck on how to plot world maps that do not adjust to the map_data provide by the ggplot2 package, which is "world" (from -180 to 180 degrees) and "world2" (from 0 to 360 degrees). For example, if I want to plot from 100°E to 20°E, no polygon provide by ggplot2 is useful. Here is the code of the example:
library(ggplot2)
map1 <- map_data('world')
map2 <- map_data('world2')
ggplot() +
theme_bw() +
geom_polygon(data = map1,
mapping = aes(x = long,
y = lat,
group = group),
col = 'gray') +
coord_fixed(1.4, xlim = c(100,380))
ggplot() +
theme_bw() +
geom_polygon(data = map2,
mapping = aes(x = long,
y = lat,
group = group),
col = 'gray') +
coord_fixed(1.4, xlim = c(100,380))
So, are there world maps polygons like those of the example but with a greater range?
This is scrappy, but why not? You could show a duplicated world so that every region can be shown without going over the edge.
library(dplyr)
map1 <- map_data('world')
map2 <- map_data('world') %>% mutate(long = long+360)
map3 <- map_data('world') %>% mutate(long = long+720)
ggplot() +
theme_bw() +
geom_polygon(data = map1,
mapping = aes(x = long,
y = lat,
group = group),
col = 'gray') +
geom_polygon(data = map2,
mapping = aes(x = long,
y = lat,
group = group),
col = 'gray') +
geom_polygon(data = map3,
mapping = aes(x = long,
y = lat,
group = group),
col = 'gray') +
coord_fixed(1.4, xlim = c(100,480))
Rather than finding those polygons, you might just clip the longitude boundaries with a coord_quickmap() or coord_sf(). Note that coord_map() will incorrectly group polygon's points unless clipped before the ggplot() call.
library(tidyverse)
ggplot() +
theme_bw() +
geom_polygon(data = map2,
mapping = aes(x = long,
y = lat,
group = group),
col = 'gray') +
coord_sf(xlim = c(20,100))
#coord_quickmap(xlim = c(100,20)) is also an alternative to coord_sf()
"world" and "world2" are two versions of the world map that are implemented in the "maps" package. But that package actually allows you to define a world map with any boundaries, e.g. c(20,380) or c(100,460).
The simplest way is just to use the "wrap" option (which is described in the man page of maps::map). This option is passed to maps::map() (so look in the maps package for full help page)
map3 <- map_data('world', wrap=c(20,380))
This should give you a nice map for any meridian that you may choose. Note that in this case "wrap" must be be a vector and that the left and right boundaries must be exactly 360 apart.
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"))
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)
I have the following code:
p <- ggplot() + coord_fixed() + xlab("") + ylab("")
base_world <- p + geom_polygon(data=world_map, aes(x=long, y=lat, group=group),
colour="green", fill="whitesmoke") +
geom_point(data = as.data.frame(coordinates(busxy)), size = 1,
mapping = aes(x = busxy#coords[,1], y = busxy#coords[,2],
color = busxy$color)) +
labs(title = "Cities\n", color = "States\n") +
scale_color_manual(labels = col2state$s, values = col2state$c)
It prints this:
The problem is the colors on map doesn't correspond with those in legend.
When I delete scale_color_manual(labels = col2state$s, values = col2state$c) from the plot it's all right but 'States' have names of colors from the data.
My question is: How to leave labels names like in the image but also assign proper colors to those labels as in the map?
In col2state$c are 29 color names (like #29A934)
In col2state$s are 29 state labels like in legend.
Data frame busxy contains 144k records with 29 unique values of states.
Data is from:
library(maps)
world_map <- map_data("world")
busxy <- data.frame(x=bus[[1]]$latitude, y=bus[[1]]$longitude, city=bus[[1]]$city, state=bus[[1]]$state)
bus <- llply(as.list(jfile5), function(x) jsonlite::stream_in(file(x), pagesize = 10000))
and jfile5 is the path to json file contains all data.
scale_color_manual can do without the labels parameter. A way to solve this is scale_color_manual(values = my_colors) where my_colors is the mapping of countries to their color, organized in a named character vector, e.g. c(AZ = "blue", NV = "red", ...)
An example:
df <- data.frame(x=1:3, y = 2:4, f = as.factor(1:3))
my_colors <- c('1'= "blue", '2' = "red", '3' = "yellow")
ggplot(df) + geom_point(aes(x = x, y = y, color = f)) + scale_color_manual(values = my_colors)
Instead of calling the columns, save vectors of the unique values for color and for state labels. Ensure the vectors are ordered to match.
vec_c <- unique(col2state$c)
vec_s <- unique(col2state$s) #may need to re-order, or opt to manually create vector
scale_color_manual(labels = vec_c, values = vec_s)