Create a leaflet map based on postal codes - r

I have the dataframe below:
mapd<-structure(list(City = c("Henderson", "Henderson", "Los Angeles",
"Fort Lauderdale", "Fort Lauderdale", "Los Angeles", "Los Angeles",
"Los Angeles", "Los Angeles", "Los Angeles"), State = c("Kentucky",
"Kentucky", "California", "Florida", "Florida", "California",
"California", "California", "California", "California"), Zip = c(42420,
42420, 90036, 33311, 33311, 90032, 90032, 90032, 90032, 90032
), Sales = c(261.96, 731.94, 14.62, 957.5775, 22.368, 48.86,
7.28, 907.152, 18.504, 114.9)), row.names = c(NA, -10L), class = c("tbl_df",
"tbl", "data.frame"))
and I want to create a leaflet map that will display the Sales and City via markers. I guess that I need to use shapefiles data for us to do this and follow the logic like below but Im confused by the fact that I do not know where to find us shapefiles and also that I do not have latitude and longitude data.:
library(rgdal)
# Make sure the name of the shape file matches the name of the shape file
# from the ZIP archive
shp <- readOGR("geo_export_4e602fd1-be14-4590-8a68-fdbca198af8f.shp")
# Add count data
library(dplyr)
shp#data <- shp#data %>% left_join(mapd, by = c("zip" = "Zip"))
Example plot using leaflet.
library(leaflet)
leaflet(shp)
leaflet(data = shp) %>% addTiles() %>%
addMarkers(~long, ~lat, popup = ~as.numeric(Sales), label = ~as.character(City))

Here is an option using zipcodeR (if you don't need to show the city extents with a polygon). You can get the latitude and longitude for each zipcode using geocode_zip, then join the lat and long data to your original dataframe, then use leaflet.
library(zipcodeR)
library(leaflet)
library(tidyverse)
mapd %>%
left_join(.,
geocode_zip(mapd$Zip) %>% mutate(zipcode = as.numeric(zipcode)),
by = c("Zip" = "zipcode")) %>%
leaflet() %>%
addTiles() %>%
addMarkers(
~ lng,
~ lat,
popup = ~ as.character(Sales),
label = ~ as.character(City)
)
Output

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)

Converting Igraph to VisNetwork

I have this network graph that I made using the "igraph" library:
library(tidyverse)
library(igraph)
set.seed(123)
n=15
data = data.frame(tibble(d = paste(1:n)))
relations = data.frame(tibble(
from = sample(data$d),
to = lead(from, default=from[1]),
))
data$name = c("new york", "chicago", "los angeles", "orlando", "houston", "seattle", "washington", "baltimore", "atlanta", "las vegas", "oakland", "phoenix", "kansas", "miami", "newark" )
graph = graph_from_data_frame(relations, directed=T, vertices = data)
(edge_fac <- forcats::as_factor(get.edgelist(graph)[,1]))
n2 <- as.integer(factor(data$name,levels = levels(edge_fac)))
V(graph)$color <- ifelse(data$d == relations$from[1], "red", "orange")
V(graph)$label <- paste0(data$name,"\n\n\n",n2)
plot(graph, layout=layout.circle, edge.arrow.size = 0.2, main = "my_graph")
Is it somehow possible to convert the above graph into a "visnetwork" graph, so that it looks like this?
I know there is a function ( visIgraph() ) meant for converting "igraph" graps to "visnetwork" graphs: https://www.rdocumentation.org/packages/visNetwork/versions/2.1.0/topics/visNetwork-igraph
But I am not sure if I can transform the first "igraph" graph (with both "numeric" and "text" labels) into an interactive "visnetwork" graph.
I tried to do this with the following code :
visIgraph(graph)
But this creates an interactive graph without the "number" labels.
Is it possible to do this?
Thank you!
You have to do a bit of manipulation to make this work because this uses base R plotting.
Essentially, these are two different igraph objects lying on top of each other. This is the only way I could think of to have two different 'cex' sizes. It may require a bit of finesse, depending on where you go from here.
library(tidyverse)
library(igraph)
library(gridGraphics) # <--- I'm new!
library(grid) # <--- I'm new!
#----------- from question -----------
set.seed(123)
n=15
data = data.frame(tibble(d = paste(1:n)))
relations = data.frame(tibble(
from = sample(data$d),
to = lead(from, default=from[1]),
))
data$name = c("new york", "chicago", "los angeles", "orlando",
"houston", "seattle", "washington", "baltimore",
"atlanta", "las vegas", "oakland", "phoenix",
"kansas", "miami", "newark" )
graph = graph_from_data_frame(relations,
directed=T,
vertices = data)
(edge_fac <- forcats::as_factor(get.edgelist(graph)[,1]))
n2 <- as.integer(factor(data$name,levels = levels(edge_fac)))
V(graph)$color <- ifelse(data$d == relations$from[1],
"red", "orange")
This is where the changes begin.
#---------- prepare the first plot -----------
# make label text larger
V(graph)$label.cex = 1.5
# V(graph)$label <- paste0(data$name,"\n",n2)
V(graph)$label <- paste0(n2) # just the number instead
#---------- prepare to collect grob ----------
# collect base plot grob
grabber <- function(){
grid.echo()
grid.grab()
}
# create a copy for the top layer
graph2 <- graph
#-------------- plot and grab ----------------
# without arrow sizes
plot(graph, layout=layout.circle, main = "my_graph")
# grab the grob
g1 = grabber()
Now for the second graph; the top layer
#----------- create the top layer -------------
# with the copy, make the vertices transparent
V(graph2)$color <- "transparent"
# reset the font size
V(graph2)$label.cex = 1
# shift the labels below (while keeping the plot design the same)
V(graph2)$label <- paste0("\n\n\n\n", data$name)
# show me
plot(graph2, layout=layout.circle,
main = "my_graph",
edge.color = "transparent") # invisible arrows/ only 1 layer of arrows
# grab the grob
g2 = grabber()
Layer them!
#-------------- redraw the plots -------------
# make the plot background transparent on the top layer
g2[["children"]][["graphics-background"]][["gp"]][["fill"]] <- "transparent"
# draw it!
grid.draw(g1)
grid.draw(g2)
You might find it interesting that the graphs going into the grob look different than what comes out of them...grid essentially adjusts them. I thought that was kind of awesome.
What about creating the graph using visNetwork? You could then add both the number and name as a label inside the nodes.
library(tidyverse)
library(visNetwork)
set.seed(123)
n=15
data = data.frame(tibble(id = paste(1:n)))
relations = data.frame(tibble(
from = sample(data$id),
to = lead(from, default=from[1]),
))
data$name = c("new york", "chicago", "los angeles", "orlando", "houston", "seattle", "washington", "baltimore", "atlanta", "las vegas", "oakland", "phoenix", "kansas", "miami", "newark" )
data$shape ='circle'
data$label = paste0(data$id,'\n',data$name)
data$color = ifelse(data$id==1, 'red', 'orange')
visNetwork(data, relations, width = "100%") %>%
visEdges(arrows =list(to = list(enabled = TRUE))) %>%
visIgraphLayout(layout = "layout_in_circle")

Directly Adding Titles and Labels to Visnetwork

I have the following network graph:
library(tidyverse)
library(igraph)
set.seed(123)
n=15
data = data.frame(tibble(d = paste(1:n)))
relations = data.frame(tibble(
from = sample(data$d),
to = lead(from, default=from[1]),
))
data$name = c("new york", "chicago", "los angeles", "orlando", "houston", "seattle", "washington", "baltimore", "atlanta", "las vegas", "oakland", "phoenix", "kansas", "miami", "newark" )
graph = graph_from_data_frame(relations, directed=T, vertices = data)
V(graph)$color <- ifelse(data$d == relations$from[1], "red", "orange")
plot(graph, layout=layout.circle, edge.arrow.size = 0.2, main = "my_graph")
I was able to convert this graph into a "visnetwork" graph:
library(visNetwork)
visIgraph(graph)
Now, I am trying to put a title on this graph:
visIgraph(graph, main = "my title")
Although this doesn't work:
Error in layout_with_fr(graph, dim = dim, ...) :
unused argument (main = "my title")
I found this link https://datastorm-open.github.io/visNetwork/legend.html that shows how you can add titles to a "visnetwork" graph :
nodes <- data.frame(id = 1:3, group = c("B", "A", "B"))
edges <- data.frame(from = c(1,2), to = c(2,3))
# default, on group
visNetwork(nodes, edges,
main = "A really simple example",
submain = list(text = "Custom subtitle",
style = "font-family:Comic Sans MS;color:#ff0000;font-size:15px;text-align:center;"),
footer = "Fig.1 minimal example",
width = "100%")
This seems to be pretty straightforward, but it requires you to use the "visNetwork()" function instead of the "visIgraph()" function.
Is it possible to directly add titles using the "visIgraph()" function?
Thank you!
We can try this approach if you like
toVisNetworkData(graph) %>%
c(list(main = "my title")) %>%
do.call(visNetwork, .)
or
toVisNetworkData(graph) %>%
{
do.call(visNetwork, c(., list(main = "my title", submain = "subtitle")))
}
and you will see
I was not able to figure out how to do this with "visIgraph()" function - but I think I was able to figure out how to generate a random graph (meeting certain conditions: Generating Random Graphs According to Some Conditions) and using the regular "visNetwork()" function and then place a title on this graph:
n=15
data = data.frame(id = 1:n)
data$color = ifelse(data$id == 1, "Red", "Orange")
data$label = c("new york", "chicago", "los angeles", "orlando", "houston", "seattle", "washington", "baltimore", "atlanta", "las vegas", "oakland", "phoenix", "kansas", "miami", "newark" )
relations = data.frame(tibble(
from = sample(data$id),
to = lead(from, default=from[1]),
))
visNetwork(data, relations, main = "my graph") %>% visEdges(arrows = "to")
It feels great (somewhat) solving my own problem!
But is this possible directly with the "visNetwork()" function?

How to add the state names to the Australia map?

I have written the following code:
library(ozmaps)
oz(states = TRUE, coast = TRUE,
ylim = NULL, add = FALSE, ar = 1, eps = 0.25)
That generates:
I wonder how to add the names of the states on the map? If the whole process can be done by "map" package, it is fine as well.
Thanks.
I'm not familiar with package ozmaps. I could not find data for the state names and latitude and longitude values for state centroids within the package.
A quick internet search produced some representative state data from https://www.distancelatlong.com/distancecalculator/country/australia/
You can adjust this data or find a better source, so this should be a start.
library(tibble)
library(ggplot2)
library(ozmaps)
ggplot(ozmap_states)+
geom_sf()+
geom_text(data = oz_states, aes(long, lat, label = state))+
theme_void()
data
oz_states <- tribble(
~state, ~lat, ~long,
"Australian Capital Territory", -35.3546004, 149.2113468,
"New South Wales", -33.42004148, 151.3000048,
"Northern Territory", -13.81617348, 131.816698,
"Queensland", -26.67998777, 153.0500272,
"South Australia", -34.28293455, 140.6000378,
"Tasmania", -40.83292234, 145.1166613,
"Victoria", -37.73119953, 142.0234135,
"Western Australia", -33.58287392, 120.0333345
)
Created on 2021-03-29 by the reprex package (v1.0.0)

Adding Provinces to Canadian Map in R

I've been browsing a lot of the topics on mapping in R and would appreciate a little help.
I've made it to this code which builds an image of a purchase density then overlays a US State map on top and a Canadian national map as well.
It's an ok solution, but Ideally I'd like to show the provinces in Canada as well.
library(mapdata);
library(maps);
library(maptools);
library(spatstat);
png(filename=file_name, type="cairo-png", bg="transparent", width=10.*960, height=10.*960, pointsize=1);
spatstat.options(npixel=c(1000,1000));
densitymap<-density(points, sigma=0.15, weights=dedupedMergedZips[!is.na(dedupedMergedZips$longitude), zipCount]);
my.palette <- colorRampPalette(c("#3F3F3F","#e2ffcc","#b6ff7f","white"), bias=2, space="rgb")
image(densitymap, col=my.palette(200));
map("state", col="grey", fill=FALSE, bg="transparent", lwd=3.0, xlim=longitudeLimits, ylim=latitudeLimits, add = TRUE);
map("worldHires","Canada", xlim=longitudeLimits, ylim=latitudeLimits, col="grey", fill=FALSE, bg="transparent", lwd=3.0, add=TRUE)
dev.off()
Any tips on how I could add an additional Arguement to the second line to get the provinces to show?
Thanks
Here is a solution, based on leaflet:
library(rgdal)
if (!file.exists("./src/ref/ne_50m_admin_1_states_provinces_lakes/ne_50m_admin_1_states_provinces_lakes.dbf")){
download.file(file.path('http://www.naturalearthdata.com/http/',
'www.naturalearthdata.com/download/50m/cultural',
'ne_50m_admin_1_states_provinces_lakes.zip'),
f <- tempfile())
unzip(f, exdir = "./src/ref/ne_50m_admin_1_states_provinces_lakes")
rm(f)
}
region <- readOGR("./src/ref/ne_50m_admin_1_states_provinces_lakes", 'ne_50m_admin_1_states_provinces_lakes', encoding='UTF-8')
library(leaflet)
leaflet() %>%
addTiles() %>%
setView(-74.09, 45.7, zoom = 3) %>%
addPolygons(data = subset(region, name %in% c("British Columbia", "Alberta", "Saskatchewan", "Manitoba", "Ontario", "Quebec", "New Brunswick", "Prince Edward Island", "Nova Scotia", "Newfoundland and Labrador", "Yukon", "Northwest Territories", "Nunavut")),
fillColor = topo.colors(10, alpha = NULL),
weight = 1)
Here is another proposal leveraging ggplot2:
library(ggplot2)
regions <- subset(region, name %in% c("British Columbia", "Alberta", "Saskatchewan", "Manitoba", "Ontario", "Quebec", "New Brunswick", "Prince Edward Island", "Nova Scotia", "Newfoundland and Labrador", "Yukon", "Northwest Territories", "Nunavut")) # region is defined in the first part of the code (see above)
ggplot(regions) +
aes(long,lat, group = group, fill = group) +
geom_polygon() +
geom_path(color="white") +
coord_equal() +
guides(fill = FALSE)

Resources