simplify my us state shapefile with rmapshaper::ms_simplify gives error - r

I'm making chloropleth us state map on my shiny app using leaflet package. I found rendering the map is very slow. After googling , it seems like maybe the shapefile is too complex and simplifying that might make it a lot faster. According to this post, simplifying the shapefile might be the answer.
Reading shape file works OK. I was able to render my leaflet map.
states_shape <- tigris::states(cb = TRUE, resolution='500k')
leaflet(states_shape) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolygons(fillColor = "white",
color = "black",
weight = 0.5) %>%
setView(-98.5795, 39.8282, zoom=3)
I tried to simplify my shapefile with rmapshaper::ms_simplify
states_shape_simple <- rmapshaper::ms_simplify(states_shape, keep = 0.05, keep_shapes = TRUE)
I got error like below:
Error in FUN(X[[i]], ...) : isTRUE(gpclibPermitStatus()) is not TRUE
I have no idea what that mean and what to do. Does anyone know why that happened and how to make it work? Thanks a lot!

The following should work:
# packages
library(sf)
#> Linking to GEOS 3.8.0, GDAL 3.0.4, PROJ 6.3.1
library(leaflet)
# data
states_shape <- tigris::states(cb = TRUE, resolution='500k', class = "sf")
# simplify
states_shape_simple <- rmapshaper::ms_simplify(states_shape, keep = 0.05, keep_shapes = TRUE)
states_shape_simple <- st_transform(states_shape_simple, 4326)
# plot
leaflet(states_shape_simple) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolygons(fillColor = "white",
color = "black",
weight = 0.5) %>%
setView(-98.5795, 39.8282, zoom = 3)
Created on 2020-05-27 by the reprex package (v0.3.0)
I added states_shape_simple <- st_transform(states_shape_simple, 4326) since I received a warning message by leaflet saying that the object states_shape_simple had an invalid datum. I don't know if you face the same warning message.
Anyway if you want to read something about the differences between sf and sp check Chapter 1 of Geoocomputation with R (and maybe Chapter 6 about reprojections, such as st_transform). I don't know why it fails with sp, maybe you can ask to the package mantainer.

Related

leaflet map of the world - exclude Antarctica

The code below is reproducible - it builds the map of the world using leaflet.
I am really not interested in Antarctica and I am more interested in Scandinavia :)
Any way to cut Antarctica or at least force it to be always at the bottom of the map - so that the center of the map is farther north?
Thanks a lot for any pointers!
library(leaflet)
library(rnaturalearth)
countries <- rnaturalearth::countries110
goodnames <- countries$name
goodnames[goodnames %in% goodnames[32]] <- "Ivory Coast"
countries$name[32] <- goodnames[32]
mymap <- leaflet(countries, options = leafletOptions(minZoom = 2))
myvalues <- 1:177
mycolors <- colorNumeric(palette = c("#fee6ce","#e6550d"),
domain = myvalues)(myvalues)
mymap %>%
addPolygons(stroke = FALSE, smoothFactor = 0.2, fillOpacity = 1,
color = ~mycolors,
label = countries$name)
You can use setView to set the initial viewing point to any location of your choosing. If you want this map to focus on Scandinavia on opening, you can do...
mymap <- leaflet(countries, options = leafletOptions(minZoom = 2)) %>% setView(lng=18.6435,lat=60.1282,zoom=2)
The coordinates are simply from searching 'Sweden coordinates' on Google. You can use a site such as https://www.latlong.net/ to help you pick an appropriate center point.
Unfortunately 'rnaturalearth' is not (yet) available fpr R 3.4.2 and I have just updated one second ago so I can't prove my answer. But as you're asking for any pointer -
I use the 'rworldmap' package and take out Antarctica by excluding it after the map is defined by the package.
According to this my suggestion to your code would be:
mymap <- mymap[-which(row.names(mymap)=='Antarctica'),]

Crosstalk: filter Polylines in Leaflet

I can't get crosstalk to work with leaflet and Polylines - here is an MWE:
library(crosstalk)
library(leaflet)
theta <- seq(0, 2*pi, len = 100)
dat <- data.frame(
lon = cos(theta),
lat = sin(theta),
t = 1:100
)
sd <- SharedData$new(dat)
map <- leaflet() %>%
addTiles() %>%
addCircleMarkers(data = sd, lat = ~lat, lng = ~lon, color = "blue") %>%
addPolylines(data = sd, lat = ~lat, lng = ~lon, color = "blue")
bscols(
filter_slider("t", "Time", sd, column = ~t),
map
)
The time filter_slider applies to the circle markers but not the polylines.
Happy to having a go at fixing this in the R leaflet package if someone can point me in the right direction. I.e. what would be required to change / implement? I assume the support is missing on the javascript side as of now?
UPDATE: Good News!
#dmurdoch has submitted a pull request to add support for polylines and polygons.
Using his version of crosstalk, you can now filter leaflet lines/polygons if they're sp objects (note, it doesn't seem to work with sf yet).
First you will need to install this version of crosstalk:
devtools::install_github("dmurdoch/leaflet#crosstalk4")
Then you will need to make sure your features are Spatial objects, easy using rgdal or raster:
shapes_to_filter <- raster::shapefile("data/features.shp") # raster import to 'Spatial Object'
shapes_to_filter <- rgdal::readOGR("data/features.shp") # rgdal import to 'Spatial Object'
Or, if you use sf and dplyr for most spatial tasks (like me) convert an sf object to Spatial:
library(dplyr)
library(sf)
shapes_to_filter <- st_read("data/features.shp") %>% as('Spatial') # sf import to 'Spatial Object'
Then create an sd object for leaflet, and a data frame copy for the filters (IMPORTANT: note how the group for sd_df is set using the group names from the sd_map) :
library(crosstalk)
sd_map <- SharedData$new(shapes_to_filter)
sd_df <- SharedData$new(as.data.frame(shapes_to_filter#data), group = sd_map $groupName())
Create crosstalk filters using sd_df:
filter_select("filterid", "Select Filter Label", sd_df, ~SomeColumn)
Create the map using the sd_map object:
library(leaflet)
leaflet() %>%
addProviderTiles("OpenStreetMap") %>%
addPolygons(data = sd_map)
And any linked tables/charts need to also use the sd_df object:
library(DT)
datatable(sd_df)
Here's all of the sources for the solution:
GitHub Issue
Github pull request from dmurdoch to add support for polygons/lines
Original solution - with outdated method "sd$transform"
Updated example - with the new "group" method, but I couldnt get their RMD to work
As previously mentioned by Bhaskar Karambelkar:
"crosstalk for now works only with markers and not polylines/polygons"
I hope this changes soon.

R Shiny leaflet addPolygons (colors are not showing)

I hope you can help me. I have created a choropleth Map with Leaflet. I merged my (dataframe with countries and a random score) and a Shapefile with the Polygon data. So far it is working, however if I implement it in R-Shiny, the map is showing, but with no color. There is also no error showing. Anyone knows why?
My code:
ui <- fluidPage(
leafletOutput("map")
)
shinyServer(function(input, output) {
output$map <- renderLeaflet({
test_map
})
})
global.R
tmp <- tempdir()
url <- "http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/50m/cultural/ne_50m_admin_0_countries.zip"
file <- basename(url)
download.file(url, file)
unzip(file, exdir = tmp)
world <- readOGR(dsn = tmp, layer = "ne_50m_admin_0_countries", encoding = "UTF-8")
data <- data.frame(Code = c("AR", "AU", "BE", "BR"),
Score = c(0.01, -0.05, 0.15, -0.22))
world <- merge(world, data,
by.x = "iso_a2",
by.y = "Code",
sort = FALSE)
pal <- colorNumeric(
palette = "RdYlGn",
domain = world$Score
)
test_map <- leaflet(data = world) %>%
addTiles() %>%
addPolygons(fillColor = ~pal(Score),
fillOpacity = 0.9,
color = "#BDBDC3",
weight = 1)
I know this is an old question and I'm not sure whether this will help or not, but I believe I had a similar problem to you which was just solved.
In my case, I had no trouble displaying polygon colours within Rstudio on my own PC, but certain web browsers and older versions of Rstudio refused to fill polygons with colours, even though all other aspects of the map worked fine.
The problem was that my colour palette consisted of a vector of hex codes with an alpha channel (the last couple of digits, specifying transparency). Removing the alpha channel from the hex codes solved my problem. It may be worth checking whether your colour vectors include alpha and if so, removing it with something like gsub(".{2}$","",your_colour_vector) as per the answer to my own problem (link above).
It doesn't look like your colours include alpha in your sample code but maybe it's a problem in your full code. That would explain why the sample code works but the full code doesn't. Might be something to look into anyway? Sorry I can't help more, I know this is a bit of a shot in the dark and not a full solution.

TopoJSON choropleth in R/Leaflet?

Is it possible to style a TopoJSON file from its features for a choropleth using R/leaflet? Tried a few things, and I'm not sure if this is impossible with the leaflet package or if I just don't have the syntax right, especially accessing the properties to enter in the pal() function. Here's what I have:
pal<-colorNumeric(palette ="YlOrRd",domain = USAdata$GINI) #USAdata data frame I merged with the spdf before converting it to shp/topojson
map<-leaflet() %>%
addTiles(options=tileOptions(minZoom = 3)) %>%
setMaxBounds(-167.276413,5.499550,-52.233040, 83.162102) %>%
setView(93.85,37.45,zoom =3) %>%
#addGeoJSON(geojson = jso5)
addTopoJSON(topojson=jso, fillColor = ~pal("GINI"))
#addPolygons(data=poly)
this throws up an error:
"Error in UseMethod("doResolveFormula") :
no applicable method for 'doResolveFormula' applied to an object of class "NULL""
I also tried converting it to an R object the topojson with fromJSON() and adding style elements, but this won't load after I try send it back with toJSON().
Not sure if relevant, but the topojson was created from a shapefile made following the instructions here:
with cl:
topojson -o 'USApuma.json' --shapefile-encoding utf8 --id-property=+GEOID10 -p GINI,+STATEFP10,+GEOID10 -- 'usaetest.shp'
then read in with readLines().
Eventually trying to throw this into a shiny app. Here's some examples I've been following.
Do you need to use TopoJSON? If not consider using the tigris package (disclosure: I created and maintain the package). It'll get you access to just about any Census geographic dataset you need, and plays nicely with leaflet. Here's a brief example in line with what you are doing. For example, you can get all PUMAs in the continental US with the following code:
library(readr)
library(tigris)
library(leaflet)
us_states <- unique(fips_codes$state)[1:51]
continental_states <- us_states[!us_states %in% c("AK", "HI")]
pumas_list <- lapply(continental_states, function(x) {
pumas(state = x, cb = TRUE)
})
us_pumas <- rbind_tigris(pumas_list)
I've generated a sample dataset that measures PUMA median household income for this example; the geo_join function from the tigris package can merge the dataset to the spatial data frame us_pumas:
puma_income <- read_csv('http://personal.tcu.edu/kylewalker/data/puma_income.csv')
joined_pumas <- geo_join(us_pumas, puma_income, 'GEOID10', 'GEOID')
We can then plot with Leaflet:
pal <- colorQuantile(palette = 'YlOrRd', domain = joined_pumas$hhincome, n = 7)
leaflet(joined_pumas) %>%
addProviderTiles('CartoDB.Positron') %>%
addPolygons(weight = 0.5, fillColor = ~pal(hhincome),
color = 'lightgrey', fillOpacity = 0.75,
smoothFactor = 0.2) %>%
addLegend(pal = pal,
values = joined_pumas$hhincome)
If you are planning to build a Shiny app, I'd recommend saving out the PUMAs you obtain from tigris first as a .rda file and reading it in with your Shiny script so you don't have to rbind_tigris every time.

Remove unused GEOID in geo_join

I am attempting to plot profitability on top of counties in Minnesota, Iowa, and Nebraska. Using leaflet and tigris, I have been able to plot ALL counties, whether or not I have data for it. This leaves me with a few counties with colors and the rest labeled as NA. Is there a way for me to remove all NA's from my geo_join data so that it just isn't used ala unused Wisconsin areas? I have tried using fortify, but I can't figure out how to determine what county boundaries I'm looking at when I merge the TIGER boundary lines with my County FIPS file in order to remove them.
Here is what my leaflet currently looks like:
My code to get the map is this:
library(tigris)
library(leaflet)
pal <- colorNumeric(c("yellow","dark red"),county$Construction.Cost,na.color="white")
IA_counties <- counties(state="IA", cb=TRUE, resolution ="20m")
MN_counties <- counties(state="MN",cb=TRUE,resolution="20m")
NE_counties <- counties(state="NE",cb=TRUE,resolution="20m")
IA_merged <- geo_join(IA_counties,county,"GEOID", "GEOID")
MN_merged <- geo_join(MN_counties,county,"GEOID","GEOID")
NE_merged <- geo_join(NE_counties,county,"GEOID","GEOID")
popupIA <- paste0("County Projects: ", as.character(paste('$',formatC(format(round(IA_merged$Construction.Cost, 0), big.mark=',', format = 'f')))))
popupMN <- paste0("County Projects: ", as.character(paste('$',formatC(format(round(MN_merged$Construction.Cost, 0), big.mark=',', format = 'f')))))
popupNE <- paste0("County Projects: ", as.character(paste('$',formatC(format(round(NE_merged$Construction.Cost, 0), big.mark=',', format = 'f')))))
leaflet() %>%
addProviderTiles("MapQuestOpen.OSM") %>%
addLegend(pal = pal,
values = IA_merged$Construction.Cost,
position = "bottomright",
title = "County Projects",
labFormat=labelFormat(prefix="$")) %>%
addCircles(lng=yup2$lon, lat=yup2$lat,weight=.75,fillOpacity=0.01,color="red",
radius = 96560) %>%
addCircles(lng=yup2$lon, lat=yup2$lat,weight=.75,fillOpacity=0.01,color="blue",
radius = 193121) %>%
addPolygons(data = IA_counties,
fillColor = ~pal(IA_merged$Construction.Cost),
layerId=1,
fillOpacity = .25,
weight = 0.05,
popup = popupIA)%>%
addPolygons(data=MN_counties,
fillColor=~pal(MN_merged$Construction.Cost),
fillOpacity=0.25,
weight=0.05,
popup = popupMN) %>%
addPolygons(data=NE_counties,
fillColor=~pal(NE_merged$Construction.Cost),
fillOpacity=0.25,
weight=0.05,
popup = popupNE)
I apologize for not including reproducible data, but if needed, please ask. I'm hoping that this is more of a simple na.color= formula solution. The map looks "okay" as of now, but I'd like it if it's possible to not have to make the fillOpacity so light so the NA counties don't stand out.
Thanks for any and all help and please, let me know if you have any questions!
I'm the creator of the tigris package. Thanks so much for using it! In the development version of tigris on GitHub (https://github.com/walkerke/tigris), I've added an option to geo_join to accommodate inner joins, which would remove the unmatched data entirely from the resultant spatial data frame (if this is what you are looking for). You can also supply a common merge column name as a named argument to the new by parameter if you want. For example:
IA_merged <- geo_join(IA_counties, county, by = "GEOID", how = "inner")
should work. I'm still testing but I'll probably submit this update to CRAN in January.
So, embarrassingly, the answer to this question was as simple as I had hoped. I tweaked the following na.color code and it worked exactly as I wanted.
pal <- colorNumeric(c("yellow","dark red"),county$Construction.Cost,na.color="transparent")

Resources