Creating a mask for natural earth R giving unexpected results - r

I am wanting to create a mask to color all parts of the image that are NOT within the specified land areas. I can get this to work successfully for two countries (e.g Burkina Faso and Nigeria). However, the same code does not work when I try a longer list of 9 countries in the same area. The error I am receiving is :
Error in [[<-.data.frame(tmp, attr(x, "sf_column"), value = list( : replacement has 2 rows, data has 1
Being relatively new to GIS, I am not quite sure what the issue is or how to resolve it. I would really appreciate some help if people could offer some insight. The method I used was based on the accepted answer from this question
Relevant code is below. Thankyou in advance for anyone who is able to help.
This code works perfectly fine
library(sf)
library(dplyr)
library(tidyverse)
library(rnaturalearth)
library(rnaturalearthdata)
# https://stackoverflow.com/questions/49266736/clip-spatial-polygon-by-world-map-in-r
x_coord = c(-20,-20,27,27)
y_coord = c(-1,28,28,-1)
# Create rectalgular polygon for a mask (slightly bigger than the image)
polygon <- cbind(x_coord, y_coord) %>%
st_linestring() %>%
st_cast("POLYGON") %>%
st_sfc(crs = 4326, check_ring_dir = TRUE) %>%
st_sf() %>%
st_wrap_dateline()
land = ne_countries(scale = "medium",
returnclass = "sf") %>%
filter(admin %in% c("Nigeria", "Burkina Faso"))
#filter(admin %in% c("Niger", "Burkina Faso", "Nigeria", "Mali", "Chad", "Mauritania",
# "Gambia", "Senegal", "Guinea", "Cameroon"))
# Get the difference between the bounding box and the area I want to plot
# This works fine when using only two countries
land = st_union(land)
polygon_land_diff = st_difference(polygon, land)
# Plot the data
plot(st_geometry(land))
plot(st_geometry(polygon), add=TRUE)
plot(st_geometry(polygon_land_diff),add=TRUE, col="green")
This code produces an error and I am not sure how to resolve it. The only difference is the number of countries selected
x_coord = c(-20,-20,27,27)
y_coord = c(-1,28,28,-1)
# Create rectalgular polygon for a mask (slightly bigger than the image)
polygon <- cbind(x_coord, y_coord) %>%
st_linestring() %>%
st_cast("POLYGON") %>%
st_sfc(crs = 4326, check_ring_dir = TRUE) %>%
st_sf() %>%
st_wrap_dateline()
land = ne_countries(scale = "medium",
returnclass = "sf") %>%
filter(admin %in% c("Niger", "Burkina Faso", "Nigeria", "Mali", "Chad", "Mauritania",
"Gambia", "Senegal", "Guinea", "Cameroon"))
# get the difference between the bounding box and the area I want to plot
# not quite working. The rror occurs when running `polygon_land_diff = st_difference(polygon, land)`
land = st_union(land)
polygon_land_diff = st_difference(polygon, land)
# Plot the data
plot(st_geometry(land))
plot(st_geometry(polygon), add=TRUE)
plot(st_geometry(polygon_land_diff),add=TRUE, col="green")

st_difference() is generic function and I guess it faild to choice appropriate method at your latter case. (I didn't read source code, this is an estimate.)
Maybe it choice not sfc but sf method. (because Polygon is class sf)
you can get desired output by making polygon sfc class or using sf:::st_difference.sfc directly.
Below is an example;
x_coord = c(-20,-20,27,27)
y_coord = c(-1,28,28,-1)
polygon <- cbind(x_coord, y_coord) %>%
st_linestring() %>%
st_cast("POLYGON") %>%
st_sfc(crs = 4326, check_ring_dir = TRUE)# %>% # keep sfc class
# st_sf() %>%
# st_wrap_dateline()
land = ne_countries(scale = "medium",
returnclass = "sf") %>%
filter(admin %in% c("Niger", "Burkina Faso", "Nigeria", "Mali", "Chad", "Mauritania",
"Gambia", "Senegal", "Guinea", "Cameroon"))
land = st_union(land)
polygon_land_diff = st_difference(polygon, land)
# Plot the data
plot(st_geometry(land))
plot(st_geometry(polygon), add=TRUE)
plot(st_geometry(polygon_land_diff),add=TRUE, col="green")

Related

Converting latitude and longidue into multipolygon

I have been trying to convert the latitude and longitude into multipolygon object so that that I can use tmap library to plot it, but I am not able to that. Converting useing st_as_sf is not wroking, can some help me? I am attaching sample data set.
coor<-structure(list(Type = c("Registry", "Registry", "Registry", "Registry", "Platform", "Registry"),`Location of coordinating center` = c("USA","USA", "USA", "USA", "United Kingdom", "United Kingdom"),`3ISO code` = c("USA", "USA", "USA", "USA", "GBR", "GBR"), `WHO region code` = c("AMR","AMR", "AMR", "AMR", "EUR", "EUR"), city = c("Philadelphia","Chicago", "Washington", "Alexandria", "London", "Manchester"), lat = c(32.7761, 41.8373, 38.9047, 38.8185, 51.50853, 53.4794), lng = c(-89.1221, -87.6862, -77.0163, -77.0861, -0.12574, -2.2453)), row.names = c(NA, -6L), class = c("tbl_df", "tbl", "data.frame"))
Two comments to this:
you are unlikely to convert your data to multiopolygon object, since you have only a single coordinate pair for each city / that spells points to me, not polygons
you were on a right track with sf::st_as_sf()
What you need to do in your st_as_sf call to make it work properly is
specify columns in your data frame that have coordinate information and
give a meaning to the figures (are they degrees? meters? or has an American been feeling patriotic and encoded the data in feet?) you use a coordinate reference system (crs) for that.
For long lat data EPSG:4326 is usually a good default.
library(sf)
library(tmap)
sf_coords <- coor %>%
st_as_sf(coords = c("lng", "lat"), crs = 4326)
tmap_mode("view")
tm_shape(sf_coords) +
tm_bubbles(size = 5, col = "red", id = "city") +
tm_basemap(leaflet::providers$Stamen.Watercolor)

How can I make an interactive map?

I am working on a project where I need to make a map for the top global streaming music. And I have got the map with song's streamings in different location.
Here is my map
Here is my code, Below are the background info and some data cleaning:
library("dplyr")
library("stringr")
library("tidyverse")
library("scales")
library("ggplot2")
library("maps")
library("leaflet")
# load the .csv into R studio, you can do this 1 of 2 ways
#read.csv("the name of the .csv you downloaded from kaggle")
#spotiify_origional <- read.csv("charts.csv")
spotiify_origional <- read.csv("https://raw.githubusercontent.com/info201a-au2022/project-group-1-section-aa/main/data/charts.csv")
# filters down the data
# removes the track id, explicit, and duration columns
spotify_modify <- spotiify_origional %>%
select(name, country, date, position, streams, artists, genres = artist_genres)
#returns all the data just from 2022
#this is the data set you should you on the project
spotify_2022 <- spotify_modify %>%
filter(date >= "2022-01-01") %>%
arrange(date) %>%
group_by(date)
spotify_2022$streams <- as.numeric(spotify_2022$streams)
View(spotify_2022)
spotify_2022_global <- spotify_modify %>%
filter(date >= "2022-01-01") %>%
filter(country == "global") %>%
arrange(date) %>%
group_by(date)
View(spotify_2022_global)
# use write.csv() to turn the new dataset into a .csv file
#write.csv(Your DataFrame,"Path to export the DataFrame\\File Name.csv", row.names = FALSE)
#write.csv(spotify_2022_global, "/Users/oliviasapp/Documents/info201/project-group-1-section-aa/data/spotify_2022.csv" , row.names = FALSE)
# top 5 most popular songs globally
top_5 <- spotify_2022_global[order(spotify_2022_global$streams, decreasing = TRUE), ]
top_5 <- top_5[1:5, ]
top_5$streams <- as.numeric(top_5$streams)
View(top_5)
# Pepas, Blank Space, I'm Tired, Yonaguni, and Heather
# were the most streamed song of the year according to top_5
Here is the map part:
# makes the map template
world_map <- map_data("world")
ggplot(world_map, aes(x = long, y = lat, group = group)) +
geom_polygon(fill="lightgray", colour = "white")
# a new data frame that has all the abrevated country codes and the country names
abrevations <- read.csv("https://pkgstore.datahub.io/core/country-list/data_csv/data/d7c9d7cfb42cb69f4422dec222dbbaa8/data_csv.csv")
#abrevations <- read.csv("wikipedia-iso-country-codes.csv")
# shortens abreviations to only include names and 2 char codes
abrevations <- abrevations %>%
select(region = Name, Code)
abrevations$region <- str_replace(abrevations$region, "United States", "USA")
abrevations$region <- str_replace(abrevations$region, "Libyan Arab Jamahiriya", "Libya")
abrevations$region <- str_replace(abrevations$region, "Côte d'Ivoire", "Ivory Coast")
abrevations$region <- str_replace(abrevations$region, "Tanzania, United Republic of", "Tanzania")
abrevations$region <- str_replace(abrevations$region, "Republic of Democratic Republic of the Congo", "Democratic Republic of the Congo")
abrevations$region <- str_replace(abrevations$region, "Congo", "Republic of Congo")
abrevations$region <- str_replace(abrevations$region, "Republic of Republic of Republic of Congo", "Republic of Congo")
abrevations$region <- str_replace(abrevations$region, "South Sudan", "Sudan")
abrevations$region <- str_replace(abrevations$region, "Syrian Arab Republic", "Syria")
abrevations$region <- str_replace(abrevations$region, "Korea, Democratic People's Republic of", "North Korea")
abrevations$region <- str_replace(abrevations$region, "Korea, Republic of (South Korea)", "South Korea")
abrevations$region <- str_replace(abrevations$region, "Lao People's Democratic Republic", "Laos")
abrevations$region <- str_replace(abrevations$region, "United Kingdom", "UK")
abrevations$region <- str_replace(abrevations$region, "Moldova, Republic of", "Moldova")
abrevations$region <- str_replace(abrevations$region, "Macedonia, the former Yugoslav Republic of", "North Macedonia")
# makes a list off all the countries where the song was popular this year
get_song_streams <- function(song_name) {
song_streams <- spotify_2022 %>%
select(name, date, country, streams) %>%
filter(name == song_name) %>%
filter(country != "global") %>%
group_by(country) %>%
summarize(streams = sum(streams)) %>%
rename(Code = country)
song_streams$Code <- toupper(song_streams$Code) #capatalizes country codes
# data frame that join's abrevations with the modified countries that listened to a song
# if a country listened to Peopas they get a 1, if not they get a 0
abrevs <- left_join(abrevations, song_streams, by = "Code") %>%
replace(is.na(.), 0)
# fixes some of the names of the countries in abrevs so they match the countries in world_map
# dataframe that will go into the map
top_country.map <- left_join(world_map, abrevs, by = "region")
return(top_country.map)
}
# gets rid of grid lines
blank_theme <- theme_bw() +
theme(
axis.line = element_blank(), # remove axis lines
axis.text = element_blank(), # remove axis labels
axis.ticks = element_blank(), # remove axis ticks
axis.title = element_blank(), # remove axis titles
plot.background = element_blank(), # remove gray background
panel.grid.major = element_blank(), # remove major grid lines
panel.grid.minor = element_blank(), # remove minor grid lines
panel.border = element_blank() # remove border around plot
)
plot_song_map <- function(song_name){
# map of the world. Yellow countries listened to Blenk Space, blue countries did not
# grey countries means we have no data
plot<- ggplot(get_song_streams(song_name), aes(map_id = region, fill = streams))+
geom_map(map = get_song_streams(song_name), color = "white")+
expand_limits(x = get_song_streams(song_name)$long,
y = get_song_streams(song_name)$lat)+
ggtitle(paste("How popular was the song", song_name, "in each country?")) +
scale_fill_continuous(type = "viridis", labels = comma) +
labs(fill = "Streams") +
blank_theme
return(plot)
}
leaflet(plot_song_map("Blank Space"))
plot_song_map("Blank Space")
plot_song_map("I’m Tired (with Zendaya) - Bonus Track")
plot_song_map("Yonaguni")
I wonder how can I make this map interactive? So I can upload it to Shiny later on. I tried to use leaflet but it returns a blank.
Thank you so much in advance! Any comments or suggestions will help!

How do I get plotly choropleth map locations to focus on Europe or certain European counties?

I am following the code from this website and trying to plot some data on an interactive map using ploty. Difference is I only have data for 6 euro pean countries. Currently the map is plotting for the entire world. How do I adjust the code to zoom in on either all of Europe or just the 6 European countries in the data. See sample code below. The country codes are from this link.
# Libraries
library(tidyverse)
library(plotly)
# Data
countries <- c("Germany", "Belgium", "Framce", "Italy", "Spain", "Poland")
codes <- c("DEU", "BEL", "FRA", "ITA", "ESP", "POL")
values <- c(100, 200, 300, 400, 500, 600)
df <- tibble(countries, codes, values)
# Maps
plot_geo(locations = df$codes) %>%
add_trace(
z = ~values,
locations = ~codes,
color = ~values
)

Unable to find the distance between the centroid of a census tract and school location coordinates

library(tidyverse)
library(tidycensus)
library(sf)
library(sp)
#install.packages('geosphere')
library('geosphere')
library(rgeos)
library(sfheaders)
#install.packages('reshape')
library('reshape')
#> Linking to GEOS 3.6.1, GDAL 2.1.3, PROJ 4.9.3
census_tract <- get_acs(geography = "tract",
variables = "B19013_001",
state = "CA",
county = c("San Joaquin","Merced","stanislaus"),
geometry = TRUE,
year = 2020)
plot(st_geometry(census_tract), axes = T)
plot(st_centroid(st_geometry(census_tract)), pch = "+", col = "red", add = T)
library(ggplot2)
ggplot(census_tract) + geom_sf() +
geom_sf(aes(geometry = st_centroid(st_geometry(census_tract))), colour = "red")
census_tract$centroid <- st_centroid(st_geometry(census_tract))
schoolloc <- read.csv("C:/Users/rlnu/Desktop/EXAMPLE/pubschls.csv")
schoolloc <- schoolloc%>% filter(County == c("San Joaquin","Merced","Stanislaus"))
census_tract <- census_tract %>%
mutate(long = unlist(map(census_tract$centroid,1)),
lat = unlist(map(census_tract$centroid,2)))
shortest_distance$min_distance <- expand.grid.df(census_tract,schoolloc) %>%
mutate(distance = distHaversine(p1 = cbind(long,lat),
p2 = cbind(Longitude,Latitude))
`
I am trying to find distance between the each census tract's centroid to three nearest schools. please help me out with it. I have written some code . The logic is wrong and the code is not working
Can achieve this using the sf package.
I could not access you schools data so made a dummy set of 4 schools.
library(sf)
schools <- data.frame(School_Name=c("School_1", "School_2", "School_3", "School_4"), Lat=c(37.83405, 38.10867, 37.97743, 37.51615), Long=c(-121.2810, -121.2312, -121.2575, -120.8772)) %>% st_as_sf(coords=c("Long", "Lat"), crs=4326)
Convert tracts to centroids and make the crs the same as the school set then calculate the distance matrix
census_centroid <- st_centroid(census_tract) %>% st_transform(4326)
DISTS<- st_distance(census_centroid, schools)
Rename the columns to be the school IDs
colnames(DISTS) <- schools$School_Name
link it back to centoids
cent_dists <- cbind(census_centroid, DISTS) %>% #bind ditances to centroids
pivot_longer(cols = -names(census_centroid), names_to = "School Name", values_to = "Distance") %>% #make long for ordering
group_by(NAME) %>% #group by centroid
slice_min(Distance,n= 3) %>% # take three closest
mutate(Near_No=paste0("Near_School_",rep(1:3))) #School distance ranking
Make wide if one row per census centroid desired, might want to play with column order though
cent_dists_wide <- cent_dists %>%
pivot_wider(names_from = c("Near_No"), values_from = c("Distance", "School Name"), names_sort = FALSE) #make wid if wyou want one row per centoid

How to create a world map in R with specific countries filled in?

I would like to use R to generate a very basic world map with a specific set of countries filled with a red colour to indicate that they are malaria endemic countries.
I have a list of these countries in a data frame but am struggling to overlay them on a world map.
I have tried using the wrld_simpl object and also the joinCountryData2Map method in the rworldmap package.
I would comment on this answer to prevent addition of a possibly redundant question but I do not have enough reputation at the moment, apologies for this.
https://stackoverflow.com/a/9102797/1470099
I am having difficulty understanding the arguments given to the plot() command - I wondered if there was just an easy way to tell R to plot all of the country NAMEs in my list on the wrld_simpl map instead of using grepl() etc. etc.
plot(wrld_simpl,
col = c(gray(.80), "red")[grepl("^U", wrld_simpl#data$NAME) + 1])
Using the rworldmap package, you could use the following:
library(rworldmap)
theCountries <- c("DEU", "COD", "BFA")
# These are the ISO3 names of the countries you'd like to plot in red
malDF <- data.frame(country = c("DEU", "COD", "BFA"),
malaria = c(1, 1, 1))
# malDF is a data.frame with the ISO3 country names plus a variable to
# merge to the map data
malMap <- joinCountryData2Map(malDF, joinCode = "ISO3",
nameJoinColumn = "country")
# This will join your malDF data.frame to the country map data
mapCountryData(malMap, nameColumnToPlot="malaria", catMethod = "categorical",
missingCountryCol = gray(.8))
# And this will plot it, with the trick that the color palette's first
# color is red
EDIT: Add other colors and include picture
## Create multiple color codes, with Burkina Faso in its own group
malDF <- data.frame(country = c("DEU", "COD", "BFA"),
malaria = c(1, 1, 2))
## Re-merge
malMap <- joinCountryData2Map(malDF, joinCode = "ISO3",
nameJoinColumn = "country")
## Specify the colourPalette argument
mapCountryData(malMap, nameColumnToPlot="malaria", catMethod = "categorical",
missingCountryCol = gray(.8), colourPalette = c("red", "blue"))
Try using googleVis package and use gvisGeoMap Functions
e.g.
G1 <- gvisGeoMap(Exports,locationvar='Country',numvar='Profit',options=list(dataMode='regions'))
plot(G1)
library(maptools)
data(wrld_simpl)
myCountries = wrld_simpl#data$NAME %in% c("Australia", "United Kingdom", "Germany", "United States", "Sweden", "Netherlands", "New Zealand")
plot(wrld_simpl, col = c(gray(.80), "red")[myCountries+1])

Resources