Adding Provinces to Canadian Map in R - 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)

Related

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?

Create a leaflet map based on postal codes

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

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)

Mapping by county level with R

I need to create a map like that on the picture in R, but by County level. I have CSV file with names of Florida Counties and the column with the population by county.
How can I create a map?
Thank you!
The built in maps data is kinda meh and out of date. I have no idea if the county boundaries have changed. I prefer the county maps from here: http://www.baruch.cuny.edu/geoportal/data/esri/esri_usa.htm
And, this http://www.baruch.cuny.edu/geoportal/data/esri/usa/census/counties.zip is the direct link to the US counties shapefile.
If you grab ogr you can extract Florida with:
ogr2ogr -f "ESRI Shapefile" -where "STATE_NAME = 'FLORIDA'" fl.shp counties.shp
Then read it into R and have some fun. I found some population data:
pop <- structure(list(County = c("Alachua", "Baker", "Bay", "Bradford",
"Brevard", "Broward", "Calhoun", "Charlotte", "Citrus", "Clay",
"Collier", "Columbia", "DeSoto", "Dixie", "Duval", "Escambia",
"Flagler", "Franklin", "Gadsden", "Gilchrist", "Glades", "Gulf",
"Hamilton", "Hardee", "Hendry", "Hernando", "Highlands", "Hillsborough",
"Holmes", "Indian River", "Jackson", "Jefferson", "Lafayette",
"Lake", "Lee", "Leon", "Levy", "Liberty", "Madison", "Manatee",
"Marion", "Martin", "Miami-Dade", "Monroe", "Nassau", "Okaloosa",
"Okeechobee", "Orange", "Osceola", "Palm Beach", "Pasco", "Pinellas",
"Polk", "Putnam", "St. Johns", "St. Lucie", "Santa Rosa", "Sarasota",
"Seminole", "Sumter", "Suwannee", "Taylor", "Union", "Volusia",
"Wakulla", "Walton", "Washington"),
pop = c(248002L, 26881L,
169866L, 27217L, 548424L, 1784715L, 14621L, 163679L, 140519L,
192843L, 333663L, 67489L, 34367L, 16263L, 876075L, 301120L, 97843L,
11562L, 47588L, 16880L, 12658L, 16106L, 14507L, 27682L, 37808L,
173808L, 99092L, 1276410L, 20022L, 139586L, 50166L, 14554L, 8618L,
303317L, 643367L, 278377L, 40304L, 8483L, 19395L, 333880L, 335008L,
148077L, 2582375L, 73560L, 74661L, 188349L, 39762L, 1202978L,
288361L, 1345652L, 473566L, 926610L, 613950L, 72605L, 201541L,
281151L, 157317L, 385292L, 431074L, 105104L, 43873L, 23018L,
15483L, 498978L, 30869L, 57779L, 24793L)),
.Names = c("County",
"pop"), class = "data.frame", row.names = c(NA, -67L))
and that is easily integrated into a choropleth:
library(sp)
library(maptools)
library(ggplot2)
library(plyr)
library(ggplot2)
# read in the florida county shapefile
fl <- readShapePoly("fl.shp", repair=TRUE, IDvar="NAME")
# make it work nicely with ggplot
fl.f <- fortify(fl, region="NAME")
# start the plot
gg <- ggplot(pop)
# plot the base mape
gg <- gg + geom_map(data=fl.f, map = fl.f, aes(map_id=id, x = long, y = lat),
fill="white", color="#7f7f7f", size=0.25)
# add the county population data
gg <- gg + geom_map(map = fl.f, aes(map_id = County, fill = pop), size=0.25)
# should prbly not do this - use `cut` for explicit groupings
# i was pressed for time
gg <- gg + scale_fill_gradient(low="#fff7bc", high="#cc4c02", name="Population")
gg <- gg + theme_bw()
gg <- gg + labs(x="", y="")
gg <- gg + theme(plot.background = element_rect(fill = "transparent",colour = NA),
panel.border = element_blank(),
panel.background =element_rect(fill = "transparent",colour = NA),
panel.grid = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
legend.position="right")
gg
NOTE that you'll have to ensure the county names match up with what's in the fl.shp structure:
unique(fl.f$id)
## [1] "Alachua" "Baker" "Bay" "Bradford" "Brevard" "Broward" "Calhoun"
## [8] "Charlotte" "Citrus" "Clay" "Collier" "Columbia" "DeSoto" "Dixie"
## [15] "Duval" "Escambia" "Flagler" "Franklin" "Gadsden" "Gilchrist" "Glades"
## [22] "Gulf" "Hamilton" "Hardee" "Hendry" "Hernando" "Highlands" "Hillsborough"
## [29] "Holmes" "Indian River" "Jackson" "Jefferson" "Lafayette" "Lake" "Lee"
## [36] "Leon" "Levy" "Liberty" "Madison" "Manatee" "Marion" "Martin"
## [43] "Miami-Dade" "Monroe" "Nassau" "Okaloosa" "Okeechobee" "Orange" "Osceola"
## [50] "Palm Beach" "Pasco" "Pinellas" "Polk" "Putnam" "Santa Rosa" "Sarasota"
## [57] "Seminole" "St. Johns" "St. Lucie" "Sumter" "Suwannee" "Taylor" "Union"
## [64] "Volusia" "Wakulla" "Walton" "Washington"
I didn't bother checking for my example (tho it looks like it's OK), but for real data you prbly should.
Here is how you get a map of counties for Florida:
library(maps)
m <- map("county", "Florida")
Adding the information to the map (such as a heat index) is a different question.
If you want to map the population of Florida at the county level check out this https://cran.r-project.org/web/packages/usmap/vignettes/mapping.html. They use usmaps.

Resources