Interactive choropleth map with leaflet inaccurately mapping data - r

First post, I'll try to do my best.
I'm trying to make an interactive choropleth map with leaflet in R, based off this
tutorial. It all goes great until I compare the data represented on the map with the data in my data frame.
I'm using the same data as the person who wrote the tutorial. It is a Large Spatial Polygons Dataframe called world_spdf with 246 rows of world_spdf#data and world_spdf#polygons as well as three other objects I have no idea about. The data is essentially countries with long and lat. I don't know anything about spatial data, but I'm assuming it's responsible for the shapes of the countries on a rendered map.
I also have a data frame called results that initially has 234 rows, also about countries and some additional data for each country. This is how head(results) looks like (it goes on like this, and there are no NAs):
ISO2 v1 v2 v3
<chr> <dbl> <dbl> <dbl>
1 AD 0.118 0.880 0.001
2 AE 0.226 0.772 0.0016
3 AF 0.197 0.803 0.0001
4 AG 0.0884 0.911 0.0009
5 AI 0.172 0.827 0.00120
6 AL 0.107 0.891 0.0022
I am merging the two dataframes by ISO2 column which contains country codes. Everything is fine so far, the data in the merged dataframe is correctly assigned to the country.
world_spdf#data = merge(world_spdf#data, results, by = "ISO2")
However, when I try to plot the data, the resulting interactive map presents the data in a "wrong order", for example, data for Poland for Nigeria etc.
What I tried was to find the missing countries in the smaller dataframe like this:
differences = c(setdiff(world_spdf#data$ISO2, results$ISO2)
And then add rows with NAs to the dataframe so that all the countries in the spatial dataframe are represented with NAs at least. But this didn't help.
I am clueless as to why this occurs. Please help!

I cannot see your side. But it seems that your join process did not go well. I have seen people like that here before. In that tutorial, the author is using sp approach. But now we can use sp approach. The sp package is better, in that you can handle data manipulation much easier. For example, you can use the tidyverse package. If you use the sp package, you cannot use filter(), mutate(), left_join() and so on. In the end, it is your choice. But I recommend the sf package.
Here, I used the data you provided. So you do not see colors much in the following map. But I want to show you that country names are matching with correct locations. On my side, I see Poland has 38.2 for POP2005, which is the right value in mysf.
library(dplyr)
library(sf)
library(leaflet)
library(viridis)
# Read the shapefile as an sf object rather than as sp object.
mysf <- st_read(dsn = ".", layer = "TM_WORLD_BORDERS_SIMPL-0.3")
# Clean up the data as the tutorial shows.
mutate(mysf,
POP2005 = if_else(POP2005 == 0, NA_real_, POP2005),
POP2005 = round(POP2005 / 1000000, 2)) -> mysf
# Now join the results to mysf
left_join(mysf, results, by = "ISO2") -> mysf
# Create a color palette. I am not sure which variable you use.
# But I chose POP2005.
mypal <- colorNumeric(palette = "viridis",
domain = mysf$POP2005, na.color = "transparent")
# Draw a leaflet map
leaflet() %>%
addProviderTiles("OpenStreetMap.Mapnik") %>%
addPolygons(data = mysf, group = "continuous",
stroke = FALSE, smoothFactor = 0.2, fillOpacity = 0.5,
fillColor = ~mypal(mysf$POP2005),
popup = paste("Country: ", mysf$NAME, "<br>",
"ISO2: ", mysf$ISO2, "<br>",
"Population:", mysf$POP2005, "<br>")) %>%
addLayersControl(overlayGroups = "continuous") %>%
addLegend(position = "bottomright", pal = mypal, values = mysf$POP2005,
title = "Population 2005",
opacity = 0.5)

Related

color with gradient shapefile in r considering specific value

I have state provinces, but I need to color them considering the value I have related to every province.
data_char is made by two columns: A (provinces) and B (values)
A B
"province_A" 0.6
"province_B" 0.9
"province_Z" 0.7
state is made by different columns. That one I want to use for the join is called "provinces".
state <- shapefile(file.shp)
state_n <- merge(state, data_char, key = "province")
so in the shp file I want to have
A B.data_char
"province_A" 0.6
"province_B" 0.9
"province_Z" 0.7
After that, I want to plot the value for every province, as a gradient using ggplot or other function. I'd like to have a fill = "B.data_char".
You can convert to an sf object, then join the data together, then plot the data with scale_fill_gradient. Here, I'll provide some sample data using US states, but I'll rename them like your province data.
Sample Data
library(tidyverse)
library(sf)
library(maps)
provinces <- sf::st_as_sf(
map(
"state",
fill = TRUE,
plot = FALSE,
regions = c("washington", "oregon", "idaho")
)
) %>%
rename(province = ID) %>%
mutate(province = c("province_A", "province_B", "province_Z"))
data.char <- read.table(text = "province values
province_A 0.6
province_B 0.9
province_Z 0.7", header = T)
Plotting
left_join(provinces, data.char, by = "province") %>%
ggplot(aes(fill = values)) +
geom_sf() +
theme_bw() +
scale_fill_gradient(low = "seagreen3", high = "darkgreen")
Output
Convert .shp to sf object
If you need to convert the shapefile to an sf object, then I usually do the following, but you might need to change the projection (i.e., crs) for your area. If so, then you can skip the steps above, and go straight to the plotting section (i.e., with the left_join).
state <- raster::shapefile("file.shp") %>%
sf::st_as_sf(crs = 4326)

Separate long/lat columns for multilinestrings and plotting in colours

I am new to plotting in R/leaflet. I am plotting a shapefile with multilinestrings into R with leaflet.
Is there a way I can extract from the geometry column separate columns for lat and long coordinates? I have looked at this answer and this one, but I can't get the right result and it's a mixture of long/lat co-ordinates in each column.
The end result I would like to get is a map where the lines are coloured based on a variable (like this), but in order to run the loop I need to have long/lat co-ordinates separated.
I have two comments: to export lat & long coordinates of a multilinestring you could use sf::st_coordinates(), provided your multilinestring is in an unprojected CRS (otherwise you would get eastings & northings instead).
But I believe that - provided that I understand your question correctly - you are making it unnecessarily complicated.
Color coding multilines based on a variable can be done by the means of leaflet::colorFactor(). You first declare a palette by the means of a named lookup table - linking values to colors - and then apply it in your leaflet::addPolylines() call.
To illustrate on an example consider this piece of code; as your shapefile is not reproducible I am using a dataset of 13 rivers from Natural Earth to get a multiliestring (any multilinestring).
library(rnaturalearth) # to get some multilinestrings / any multilinestrings :)
library(dplyr)
library(sf)
rivers <- ne_download(category = 'physical',
scale = 110,
type = 'rivers_lake_centerlines',
returnclass = 'sf') %>%
mutate(color_source = case_when(name %in% c('Mississippi',
'Peace',
'Amazonas',
'ParanĂ¡') ~ "Americas",
T ~ "Rest of World")) %>%
group_by(color_source) %>%
summarise()
library(leaflet)
# prepare a palette - manual colors according to color_source column
palPwr <- leaflet::colorFactor(palette = c("Americas" = "red",
"Rest of World" = "blue"),
domain = rivers$color_source)
# first prepare a leaflet plot ...
lplot <- leaflet(rivers) %>%
addProviderTiles("CartoDB.Positron") %>% # or any other basemap...
addPolylines(color = ~palPwr(color_source), # note the tilde notation!
opacity = 2/3) %>% # opacity = alpha
leaflet::addLegend(position = "bottomright",
values = ~color_source, # data frame column for legend
opacity = .7, # alpha of the legend
pal = palPwr, # palette declared earlier
title = "Rivers of the World")
lplot # ... then display it

How to plot data on map without coordinates?

I am trying to plot dataframe like:
code name description estimate
0 Australia Vegetables 854658
0 Australia Fruit 667541
1 New South Wales Vegetables 45751
1 New South Wakes Fruit 77852
2 Victoria Vegetables 66211
2 Victoria Fruit 66211
.
.
.
For each region in Australia there are multiple rows with different description. What packages may I use to plot a map with estimate without coordinates?
I try ggplot and ozmaps with sf which mentioned in ggplot2 tutorial, and I filter the dataframe for only fruit, but there is error message :
stat_sf requires the following missing aesthetics: geometry
the code I tried :
ggplot() +
geom_sf(oz_states,mapping=aes())+
geom_sf(df,mapping=aes()) +
coord_sf()
The methods I found are all required langitude and latitude to plot the data map, I tried ggmaps or geom_ploygon but didn't figure out the correct way to do so. Is there a possible way to plot map with only region labels?
this is what I plot by tableau, and this is expected plot by using r as well:
So essentially, your first problem is that you're calling the wrong object within the ozmaps package. it's ozmap_states, meanwhile you called yours oz_states
I came up with this solution that I think takes what you want and elevates it.
df <- data.frame(code = rep(c(0,1,2), 2), name = rep(c("Australia", "New South Wales", "Victoria"), 2), description = rep(c("Vegetables", "Fruit"), 3), count =
c(854658, 45751, 66211, 667541, 77852, 66211))
library(tidyverse)
library(sf)
library(ozmaps)
library(leaflet)
library(tmap)
states_full <- right_join(df, ozmap_states, by = c("name" = "NAME"))
data <- states_full %>%
filter(description == "Fruit") %>%
select(name, geometry, count)
ozmap1 = tm_shape(ozmap_states) +tm_polygons()
tmap_mode("view")
ozmap1 + tm_shape(st_as_sf(data)) + tm_fill(col = "count")
Basically, instead of using the sample dataframe that I created from your data, you would just use your data in the right join. You can also choose whether you want fruits or vegetables in your filter function.
The tmap package is a mapping package that can make interactive leaflet like maps.
You can look at some tutorials here: https://geocompr.robinlovelace.net/adv-map.html
End solution looks something like this.
Note: This solution uses lng/lat, but it pulls it directly from the shape file for oz state maps in the ozmaps package, therefore fulfilling the need of the question.
When you add in more data, more of Australia will be colored in depending on their count.

Putting Values on a County Map in R

I am using an excel sheet for data. One column has FIPS numbers for GA counties and the other is labeled Count with numbers 1 - 5. I have made a map with these values using the following code:
library(usmap)
library(ggplot2)
library(rio)
carrierdata <- import("GA Info.xlsx")
plot_usmap( data = carrierdata, values = "Count", "counties", include = c("GA"), color="black") +
labs(title="Georgia")+
scale_fill_continuous(low = "#56B1F7", high = "#132B43", name="Count", label=scales::comma)+
theme(plot.background=element_rect(), legend.position="right")
I've included the picture of the map I get and a sample of the data I am using. Can anyone help me put the actual Count numbers on each county?
Thanks!
Data
The usmap package is a good source for county maps, but the data it contains is in the format of data frames of x, y co-ordinates of county outlines, whereas you need the numbers plotted in the center of the counties. The package doesn't seem to contain the center co-ordinates for each county.
Although it's a bit of a pain, it is worth converting the map into a formal sf data frame format to give better plotting options, including the calculation of the centroid for each county. First, we'll load the necessary packages, get the Georgia data and convert it to sf format:
library(usmap)
library(sf)
library(ggplot2)
d <- us_map("counties")
d <- d[d$abbr == "GA",]
GAc <- lapply(split(d, d$county), function(x) st_polygon(list(cbind(x$x, x$y))))
GA <- st_sfc(GAc, crs = usmap_crs()#projargs)
GA <- st_sf(data.frame(fips = unique(d$fips), county = names(GAc), geometry = GA))
Now, obviously I don't have your numeric data, so I'll have to make some up, equivalent to the data you are importing from Excel. I'll assume your own carrierdata has a column named "fips" and another called "values":
set.seed(69)
carrierdata <- data.frame(fips = GA$fips, values = sample(5, nrow(GA), TRUE))
So now we left_join our imported data to the GA county data:
GA <- dplyr::left_join(GA, carrierdata, by = "fips")
And we can calculate the center point for each county:
GA$centroids <- st_centroid(GA$geometry)
All that's left now is to plot the result:
ggplot(GA) +
geom_sf(aes(fill = values)) +
geom_sf_text(aes(label = values, geometry = centroids), colour = "white")

filled map + location map in R

Rephrasing the question...I am preparing report and one part of it is spatial viz.
I have 2 datasets. First(Scores) is countries with their scores. Second one (Locations) is exact longitude and latitude that refers to an exact location inside those countries. Let that be examples:
Scores = data.frame( Country = c("Lebanon","UK","Chille"), Score =c(1,3.5,5))
Locations = data.frame(Location_Name = c("London Bridge", "US Embassy in Lebanon" , "Embassy of Peru in Santiago"),
LONG = c(-0.087749, 35.596614, -70.618236),
LAT = c(51.507911, 33.933586, -33.423285))
What i want to achieve is get filled map of the world (in my dataset i have every country) and color inside of its boundouries with the Score (Scores$Score) on continous scale.
On top of that I would like to add pins, bubbles or whatever marker of Locations from Locations dataframe.
So my desired outcome would be combination of this view:
and this view:
Ideally i would like also to be able to draw 2km radius around the Locations from Locations data.frame also.
I know to do them separately but cant seem to achieve it on one nice clean map.
I really appreciate any help or tips on this, got stuck for whole day on that one
As suggested by #agila you can use the tmap package.
First merge your Scores data with World so you can fill countries based on Scores data. Note that your Country column should match the name in World exactly when merging.
You will need to use st_as_sf from sf package to make your Locations an sf object to add to map.
tm_dots can show points. An alternative for bubbles is tm_bubbles.
library(tmap)
library(sf)
data(World)
Scores = data.frame(Country = factor(c("Mexico","Brazil","Chile"), levels = levels(World$name)),
Score =c(1,3.5,5))
Locations = data.frame(Location_Name = c("Rio de Janeiro", "US Embassy in Lebanon" , "Embassy of Peru in Santiago"),
LONG = c(-43.196388, 35.596614, -70.618236),
LAT = c(-22.908333, 33.933586, -33.423285))
map_data <- merge(World, Scores, by.x = "name", by.y = "Country", all = TRUE)
locations_sf <- st_as_sf(Locations, coords = c('LONG', 'LAT'))
tm_shape(map_data) +
tm_polygons("Score", palette = "-Blues") +
tm_shape(locations_sf) +
tm_dots(size = .1)
Map

Resources