I'm working on a project and a small part of it consists of drawing a world map with 43 countries on my list. My dataset is as follows:
How do I put this on the world map with different colors for development status as follows?
Data is here :
https://wetransfer.com/downloads/0960ed96fba15e9591a2e9c14ac852fa20220301181615/dc25f41a87fc2ba165a72ab6712dd8d020220301181640/832b5e
A quick example using fake data:
library(dplyr)
library(ggplot2)
# simulate data for subset of countries
mydata <- map_data("world") %>%
distinct(region) %>%
mutate(fakedata = runif(n())) %>%
slice_sample(n = 200)
# add simulated values and remove Antarctica
worldmap <- map_data("world") %>%
filter(region != "Antarctica") %>%
left_join(mydata)
ggplot(worldmap) +
geom_polygon(aes(long, lat, group = group, fill = fakedata)) +
coord_quickmap() +
scale_fill_viridis_c(option = "plasma", na.value = NA) +
theme_void()
Also look into the {sf} package and geom_sf(), which among other things makes it easier to use different / less distorted / less biased map projections.
Similar to #zephryl answer, but using tmap. The first step is joining your data with the World data by country name. The next step is drawing the map.
library(dplyr)
library(tmap)
# Get World data
data("World")
# Dummy data frame similar to your data
df <- data.frame(location = World$name,
devStat = rnorm(length(World$name), 5, 2.5))
# Join by country name
# Just need to make sure that country names are written exactly the same
# in the two datasets
df2 <- World |>
left_join(df, by = c("name" = "location"))
# Create map
# Shape to add to the map
tm_shape(df2) +
# Draw the previous shape as polygons
# Set the attribute to which the polygons will be coloured
tm_polygons("devStat",
# select palette
palette = "-plasma",
# Set palette categories as order
style = "order",
# Horizontal legend
legend.is.portrait = FALSE) +
# Remove frame from layout
tm_layout(frame = FALSE,
# Put legend outsize the frame
legend.outside = T,
legend.outside.position = "top")
Related
In my specific requirement, I want to color just the neighbouring districts.
First, I have downloaded the data:
library(sp)
library(mapproj)
library(rgeos)
library(raster)
library(GADMTools)
library(precrec)
library(ggplot2)
library(tidyverse)
library(sf)
library(dplyr)
library(plotly)
library(plotrix)
India <- getData("GADM", country = "India", level = 2)
India_Level1 <- raster::getData("GADM", country = "India", level = 1)
# dist level map
dist_level_map_Gujarat <- India %>% st_as_sf() %>% filter(NAME_1 == "Gujarat")
And then I can plot all the districts within the state of Gujarat :
p_Gujarat <- ggplot(data = dist_level_map_Gujarat) +
geom_sf(mapping = aes(fill = NAME_2)) +
theme_void() + theme(legend.position = "none")
ggplotly(p_Gujarat)
Which is good.
But then if I only want to color (preferably gradient color) using 'Centroid' as the district, and all the neighbors to it. I am trying something like this:
# using sf, get the centriods
dist_level_map_Gujarat_Centroids_sf <- st_centroid(dist_level_map_Gujarat)
dist_level_map_Gujarat$Centroid <- dist_level_map_Gujarat_Centroids_sf$geometry
# Extract X and Y values of Centroids
dist_level_map_Gujarat$Centroid_X <- sapply(dist_level_map_Gujarat$Centroid,"[[",1)
dist_level_map_Gujarat$Centroid_Y <- sapply(dist_level_map_Gujarat$Centroid,"[[",2)
And I want to use something similar as below (the circle around the centroid to color the gradients, for a circle of, say 100 Kms, to color the neighboring districts)
plot(1:5,seq(1,10,length=5),type="n",xlab="",ylab="",main="Test draw.circle", axes=FALSE,ann=FALSE)
draw.circle(3,6,c(1,0.66,0.33),border="purple", col=c("#ff00ff","#ff77ff","#ffccff"),lty=1,lwd=1)
But not sure how can I do that.
Can someone please help?
Your question is not very clear to me:
What do you mean by using 'Centroid' as the district, and all the neighbors to it.? Centroid is the "center" of a polygon. If you need the neighbours of a polygon there are several methods, as getting the polygons that touches the polygon of reference.
And I want to use something similar as below (the circle around the centroid to color the gradients, for a circle of, say 100 Kms, to color the neighboring districts): Again, this is just for plotting or do you need to get the neighbours on a certain radius?
Still, I gave it a try. So the technique is:
You need to "project" the shapefile to a projection in meters. The data is downloaded in longitude/latitude coords, and distances on this system are not captured well.
I created sequentially buffers around the centroids. This effectively create circles of a certain radius. After that, the gradient part is almost trivial.
See if this can fit your needs, regards:
library(sf)
library(tidyverse)
India <- raster::getData("GADM", country = "India", level = 2)
# dist level map
dist_level_map_Gujarat <- India %>% st_as_sf() %>% filter(NAME_1 == "Gujarat")
st_crs(dist_level_map_Gujarat)$units
#> NULL
# Project, using: https://epsg.io/7761
dist_level_map_Gujarat <- dist_level_map_Gujarat %>%
st_transform(7761)
# Now units are meters
st_crs(dist_level_map_Gujarat)$units
#> [1] "m"
# Plot the map
ggplot(data = dist_level_map_Gujarat) +
geom_sf(mapping = aes(fill = NAME_2)) +
theme_void() + theme(legend.position = "none")
# Get the centroids of a couple of districts and buffer
centroid <- dist_level_map_Gujarat %>%
filter(NAME_2 %in% c("Botad", "Dahod")) %>%
st_centroid()
# Create buffers of 33, 66 and 100 km
buffers <- lapply(c(100,66,33), function(x){
# Create the buffer!
df <- st_buffer(centroid, x*1000)
df$label <- x
df
}) %>% bind_rows()
ggplot() +
geom_sf(data=dist_level_map_Gujarat) +
geom_sf(data=buffers, aes(fill=label), color="purple") +
scale_fill_gradientn(colors=
alpha(rev(c("#ff00ff","#ff77ff","#ffccff")),0.7 ),
label = scales::label_number(suffix = " km."),
guide = guide_colorsteps(title="My buffer")
)
Created on 2022-06-14 by the reprex package (v2.0.1)
Using the airline-safety dataset available here, I'm trying to create a heat map in R. I want to order the heat map so that the airlines with the highest number of fatal accidents are listed at the top.
I'm able to order the heat map by "value" -
but this orders the heatmap by value, regardless of what the group is i.e. incidents, fatal accidents or fatalities.
# load packages -----------
library(tidyverse)
library(ggplot2)
library(reshape2)
library(dplyr)
library(plyr)
library(scales)
library(forcats)
# read in the data
airlines <- read.csv("/Volumes/GoogleDrive/My Drive/Uni/DVN/AT2/Blog 2/airline_incidents.csv", header = TRUE)
# select relevant columns
airlines_00_14 <- airlines[,c(1,6,7,8)]
# create a long dataset
airlines_00_14.m <- melt(airlines_00_14)
# rescale values for heat map
airlines_00_14.m <- ddply(airlines_00_14.m, .(variable), transform, rescale = rescale(value))
# create heat map
(q <- airlines_00_14.m %>%
ggplot( aes(x = variable, y = reorder(airline, value))) +
geom_tile(aes(fill = rescale), colour = "white") +
scale_fill_gradient(low = "white", high = "steelblue"))
One way to do this is to create the order before you melt, like this:
# order by fatalities and generate air_order value
airlines_00_14 = airlines_00_14[order(airlines_00_14$fatal_accidents_00_14),]
airlines_00_14$air_order = seq_len(nrow(airlines_00_14))
Then, when you use reshape2::melt, set `id.vars = c("airline","air_order")
# create a long dataset
airlines_00_14.m <- reshape2::melt(airlines_00_14,id.vars = c("airline", "air_order"))
Then, in your plot, use y=reorder(airline, air_order) instead of the current y=reorder(airline, value)
Output:
I have a dataset of several thousand geographical points, each assigned a cluster (in my reproducible example below I simplify this to 8 points). I want to color an entire global map (only the landmass) using k nearest neighbours to these points. I want to visualise the map using leaflet in r (ultimately for a shiny app). I can plot the results ok using ggplot, but run into some problems when using leaflet. The problems are
The raster doesn't cover all of the antarctic in the leaflet map (I assume I need to use a different dataset than wrld_smpl to mask the raster, but have not succeeded in doing so).
Whereever two different clusters meet, there is a gap in the raster coverage
The coastlines are highly pixelated.
I greatly appreciate any guidance on how to tackle this!
Reproducible example:
# ggplot2 approach ###################################################################################
# Randomly generate 8 points with 8 clusterlabels
train = data.frame(lon=runif(8, -180, 180),lat=runif(8,-90,90), clustername=1:8)
train
library(ggplot2)
library(raster)
library(class)
library(maptools) # for wrld_smpl data
# Get data for land borders
data(wrld_simpl)
# Make grid of lat/lon points covering whole world
dfgrid = expand.grid(lon=seq(-180,180,by=.1), lat =seq(-90,90, by=.1))
# Convert to spatial points, specify correct coordinate reference system (CRS), same as wrld_smpl
pts <- SpatialPoints(dfgrid, proj4string=CRS(proj4string(wrld_simpl)))
# Only keep points over land
dfgrid = dfgrid[!is.na(over(pts, wrld_simpl)$FIPS),]
#dfgrid$clusterlabels = 1
# Asign cluster labels using knn
dfgrid$clusterlabels = knn(train=train[,c("lon","lat")],
test=dfgrid[,c("lon", "lat")],
cl=train$clustername,
k=1)
r = rasterFromXYZ(dfgrid, crs = "+proj=longlat")
r
#plotting
var_df <- as.data.frame(rasterToPoints(r))
p <- ggplot() +
geom_polygon(data = wrld_simpl[wrld_simpl#data$UN!="10",],
aes(x = long, y = lat, group = group),
colour = "black", fill = "grey") # does the fortification automatically
p <- p + geom_tile(data = var_df, aes(x = x, y = y, fill = as.factor(clusterlabels)))+
theme(legend.title = element_blank())
p = p + scale_fill_brewer(palette = "Paired")
p
# leaflet approach #############################################################################################
library(leaflet)
pal <- colorFactor("Paired", values(r),na.color = "transparent")
leaflet() %>% addTiles()
leaflet() %>% addTiles() %>%
addRasterImage(r, colors = pal, opacity = 0.8) %>%
addLegend(pal = pal, values = values(r),
title = "Cluster label")
I have county level data recording the year an invasive insect pest was first detected in that county between 2002 and 2018. I created a map using ggplot2 and the maps package that fills the county polygons with a color according to the year the pest was detected.
**Is there a way to use the gganimate package to animate this map with the first frame filling in only polygons with a detection date of 2002, the second frame filling polygons with a detection date of 2003 or earlier (so 2002 and 2003), a third frame for detection dates of 2004 or earlier (2002, 2003, 2004), etc.? **
Clarification: I'd like it so all the county polygons are always visible and filled in with white initially and each frame of the animation adds fills in counties based on the year of detection.
I've tried using the transition_reveal(data$detect_year) with the static plot but get an error that "along data must either be integer, numeric, POSIXct, Date, difftime, orhms".
Here's some code for a reproducible example:
library(dplyr)
library(purrr)
library(maps)
library(ggplot2)
library(gganimate)
# Reproducible example
set.seed(42)
map_df <- map_data("county") %>%
filter(region == "minnesota")
map_df$detection_year <- NA
# Add random detection year to each county
years <- 2002:2006
map_list <- split(map_df, f = map_df$subregion)
map_list <- map(map_list, function(.x) {
.x$detection_years <- mutate(.x, detection_years = sample(years, 1))
})
# collapse list back to data frame
map_df <- bind_rows(map_list)
map_df$detection_years <- as.factor(map_df$detection_years)
# Make plot
static_plot <- ggplot(map_df,
aes(x = long,
y = lat,
group = group)) +
geom_polygon(data = map_df, color = "black", aes(fill = detection_years)) +
scale_fill_manual(values = terrain.colors(n = length(unique(map_df$detection_years))),
name = "Year EAB First Detected") +
theme_void() +
coord_fixed(1.3)
animate_plot <- static_plot +
transition_reveal(detection_years)
If it's possible to do this with gganimate, I'd like to but I'm also open to other solutions if anyone has ideas.
After getting an answer from #RLave that almost did what I wanted and spending a little time with the documentation, I was able to figure out a way to do what I want. It doesn't seem very clean, but it works.
Essentially, I created a copy of my data frame for each year that needed a frame in the animation. Then for each year of detection I wanted to animate, I edited the detection_year variable in that copy of the data frame so that any county that had a detection in the year of interest or earlier retained their values and any county that had no detection yet was converted to the value I plotted as white. This made sure all the counties were always plotted. Then I needed to use transition_manual along with a unique ID I gave to each copy of the original data frame to determine the order of the animation.
library(dplyr)
library(purrr)
library(maps)
library(ggplot2)
library(gganimate)
# Reproducible example
set.seed(42)
years <- 2002:2006
map_df <- map_data("county") %>%
filter(region == "minnesota")
map_df <- map_df %>%
group_by(subregion) %>%
mutate(detection_year = sample(years,1))
animate_data <- data.frame()
for(i in 2002:2006){
temp_dat <- map_df %>%
mutate(detection_year = as.numeric(as.character(detection_year))) %>%
mutate(detection_year = case_when(
detection_year <= i ~ detection_year,
detection_year > i ~ 2001
),
animate_id = i - 2001
)
animate_data <- bind_rows(animate_data, temp_dat)
}
animate_data$detection_year <- as.factor(as.character(animate_data$detection_year))
# Make plot
static_plot <- ggplot(animate_data,
aes(x = long,
y = lat,
group = group)) +
geom_polygon(data = animate_data, color = "black", aes(fill = detection_year)) +
scale_fill_manual(values = c("white",
terrain.colors(n = 5)),
name = "Year First Detected") +
theme_void() +
coord_fixed(1.3) #+
facet_wrap(~animate_id)
animate_plot <- static_plot +
transition_manual(frames = animate_id)
animate_plot
Possibily this, but I'm not sure that this is the expected output.
I changed your code, probably you don't need to split. I used group_by to assign a year to each region.
set.seed(42)
years <- 2002:2006
map_df <- map_data("county") %>%
filter(region == "minnesota")
map_df <- map_df %>%
group_by(subregion) %>%
mutate(detection_year = sample(years,1))
For the transition you need to define the id, here the same as the grouping (subregion or group) and a correct date format for the transition (along) variable (I used lubridate::year())
# Make plot
static_plot <- ggplot(map_df,
aes(x = long,
y = lat,
group = group)) +
geom_polygon(color = "black", aes(fill = as.factor(detection_year))) +
scale_fill_manual(values = terrain.colors(n = length(unique(map_df$detection_year))),
name = "Year EAB First Detected") +
theme_void() +
coord_fixed(1.3)
animate_plot <- static_plot +
transition_reveal(subregion, # same as the group variable
lubridate::year(paste0(detection_year, "-01-01"))) # move along years
Does this do it for you?
Hi visualization lovers,
I am trying to create a color map plot,like this one:
(source: https://github.com/hrbrmstr/albersusa)
BUT i want this maps to be biased so that the areas of the states to be proportional to the value I provide (in particular,I use GPD value).
What i mean is that I want some states to look bigger, some smaller that they are in reality but reminding the real USA map as much as possible.
No problems with the states moving or shape destroying.
Any ideas? Any ready solutions?
Currently I use R and albersusa package because it is something I am familiar with. Open to change!
My current code for the plot is:
gmap<-
ggplot() +
geom_map(data = counties#data, map = cmap,
aes(fill =atan(y/x),alpha=x+y, map_id = name),
color = "gray50") +
geom_map(data = smap, map = smap,
aes(x = long, y = lat, map_id = id),
color = "black", size = .5, fill = NA) +
theme_map(base_size = 12) +
theme(plot.title=element_text(size = 16, face="bold",margin=margin(b=10))) +
theme(plot.subtitle=element_text(size = 14, margin=margin(b=-20))) +
theme(plot.caption=element_text(size = 9, margin=margin(t=-15),hjust=0)) +
scale_fill_viridis()+guides(alpha=F,fill=F)
Here's a very ugly first try to get you started, using the outlines from the maps package and some data manipulation from dplyr.
library(maps)
library(dplyr)
library(ggplot2)
# Generate the base outlines
mapbase <- map_data("state.vbm")
# Load the centroids
data(state.vbm.center)
# Coerce the list to a dataframe, then add in state names
# Then generate some random value (or your variable of interest, like population)
# Then rescale that value to the range 0.25 to 0.95
df <- state.vbm.center %>% as.data.frame() %>%
mutate(region = unique(mapbase$region),
somevalue = rnorm(50),
scaling = scales::rescale(somevalue, to = c(0.25, 0.95)))
df
# Join your centers and data to the full state outlines
df2 <- df %>%
full_join(mapbase)
df2
# Within each state, scale the long and lat points to be closer
# to the centroid by the scaling factor
df3 <- df2 %>%
group_by(region) %>%
mutate(longscale = scaling*(long - x) + x,
latscale = scaling*(lat - y) + y)
df3
# Plot both the outlines for reference and the rescaled polygons
ggplot(df3, aes(long, lat, group = region, fill = somevalue)) +
geom_path() +
geom_polygon(aes(longscale, latscale)) +
coord_fixed() +
theme_void() +
scale_fill_viridis()
These outlines aren't the best, and the centroid positions they shrink toward cause the polygons to sometimes overlap the original state outline. But it's a start; you can find better shapes for US states and various centroid algorithms.