Split a map into two separate maps by latitude or longitude - r

Is there a way to slice a ggplot2 map into two separate maps? I have one large map with id labels that are illegible. I want to split the map vertically into two distinct maps, preferably with an overlapping area so that each polygon would show up whole in at least one map.
Here's a reproducible example. I would want to split the map into a northern one at 35 degrees north and then into a southern one at 35.5 degrees north (giving an overlap between 35 and 35.5 in both). (While I realize it might make more sense with this example to split the other way, my actual map is long vertically.)
library(sf)
library(ggplot2)
sf_nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)
plot <- ggplot2::ggplot(sf_nc) +
geom_sf(aes(color = NAME)) +
geom_sf_text(aes(label = NAME))

Maybe this is what you are looking for.
Following this post I first make use of st_crop to split the sf df by latitude and extract the FIPS codes for south and north regions.
The FIPS codes are then used to split the sf dataframe into two which ensures that regions on the dividing line are shown in total in both maps.
Finally, I add an ID and bind both dfs back together for easy plotting with facet_wrap
library(sf)
library(ggplot2)
library(dplyr)
sf_nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)
# Get FIPS/regiona codes for north regions
south <- st_crop(sf_nc, xmin=-180, xmax=180, ymin=-90, ymax=35.5) %>%
pull(FIPS)
north <- st_crop(sf_nc, xmin=-180, xmax=180, ymin=35.5, ymax=90) %>%
pull(FIPS)
# Make sf df for north and south
sf_nc_1 <- filter(sf_nc, FIPS %in% south) %>%
mutate(id = "South")
sf_nc_2 <- filter(sf_nc, FIPS %in% north) %>%
mutate(id = "North")
# Bind together for using facet_wrap
sf_nc_split <- rbind(sf_nc_1, sf_nc_2)
ggplot2::ggplot(sf_nc_split) +
geom_sf(aes(color = NAME)) +
geom_sf_text(aes(label = NAME), size = 2) +
guides(color = FALSE) +
facet_wrap(~id, ncol = 1) +
theme_void()

Related

How to create State & district level map in using GADM and ggplot?

I am using Covid data & looking to plot State & district level Indian data on map.
I have State, District Name of India along with Cases but do not have needed lat, long for them.
I came across this so post How to map an Indian state with districts in r?
and tried raster::getData("GADM", country = "India", level = 2) %>% as_tibble() but this doesn't work as it doesnt have lat,lon, shapefile etc.
library(raster)
library(rgdal)
library(rgeos)
state_level_map <- raster::getData("GADM", country = "India", level = 1) %>%
as_tibble() %>%
filter(NAME_1 == "Rajasthan") %>%
fortify()
ggplot() +
geom_map(data= state_level_map, map = state_level_map,
aes(x = long, y = lat, map_id = id, group = group))
I am new to spatial data / maps and not sure how exactly I can proceed in this situation. Is it possible to get lat, lon, shapefile etc. for State/districts name's info from any r packages or the only way is to manually google them for lat,lon ?
Appreciate any help.
You were almost there. Use sf for that.
library(raster)
library(sf)
library(rgeos)
library(dplyr)
state_level_map <- raster::getData("GADM", country = "India", level = 1) %>%
st_as_sf() %>%
filter(NAME_1 == "Rajasthan")
ggplot() +
geom_sf(data = state_level_map)
you can then easily use aes() to change your aesthetics of the ggplot as you normally would using variables.
sf uses a dataframe-like notation that incorporates both attribute data as well as geometries into a single and easy to use dataframe. just have a look at print(state_level_map). That is, you could join data using district names to augment you attributes and visualize them through aes(color = yourjoinedvar).

R:How to display city level data on map and how to ZOOM it on one specific region of the map

I am using usmap and ggplot to plot population on a map. My data has two columns - population and zipcodes.
Question: How can I display data on city level using the same libraries or if you know of other libraries that can do the job.
Question: I am plotting California map and I want to zoom on LA county and nearby counties.
Below code gives me a nice California map and population as a color.
library(usmap)
library(ggplot2)
usmap::plot_usmap("counties",
include = ("CA") )
plot_usmap(data = data, values = "pop_2015", include = c("CA"), color = "grey") +
theme(legend.position = "right")+scale_fill_gradient(trans = "log10")
The tigris package makes downloading zip code tabulation areas fairly simple. You can download as a simple features dataframe so joining your data by zip code using dplyr functions is fairly easy. Here is a quick example:
library(tigris)
library(dplyr)
library(ggplot2)
df <- zctas(cb = TRUE,
starts_with = c("778"),
class = "sf")
## generate some sample data that
## can be joined to the downloaded data
sample_data <- tibble(zips = df$ZCTA5CE10,
values = rnorm(n = df$ZCTA5CE10))
## left join the sample data to the downloaded data
df <- df %>%
left_join(sample_data,
by = c("ZCTA5CE10" = "zips"))
## plot something
ggplot(df) +
geom_sf(aes(fill = values))

Keep getting NAs when I run Over() function on Points(Lat,Lon) on shapefile polygons

I am not sure why i am keep getting NA whenever I run the Over function with Latitude and Longitude point on the polygon from shapefile. Please note that this is first time for me doing the spatial analysis, but I have done my research and replicated things, but didn't succeed. I need some points which are outside of the polygon to be NA, so I can focus on the real data.
I read these sources since these pertain to my cause but I can't work my problem out:
sp::over() for point in polygon analysis
https://gis.stackexchange.com/questions/133625/checking-if-points-fall-within-polygon-shapefile
https://gis.stackexchange.com/questions/278723/r-error-in-checking-point-inside-polygon
Here is my code chunk
library(sp)
library(rgdal)
library(readr)
gainsville_df <- read_csv("311_Service_Requests__myGNV_.csv")
gnv <- readOGR("~\\Downloads\\GIS_cgbound", layer = "cgbound")
gnv_latlon <- spTransform(gnv, CRS("+proj=longlat +ellps=WGS84 +datum=WGS84"))
gnv_raw <- data.frame(Longitude= gainsville_df$Longitude, Latitude= gainsville_df$Latitude)
coordinates(gnv_raw) <- ~Longitude + Latitude
proj4string(gnv_raw) <- proj4string(gnv)
over(gnv_raw, as(gnv,"SpatialLinesDataFrame"))
#Yeilds:
# FID_cgboun Id Perimeter Area Acres Hectares Shape_Leng
#1 NA NA NA NA NA NA NA
# Desired Output:
# Whereas I should have seen which gainesville Latitudes and Longitude are within the shpaefile
# polygon so I can drop the outliers, that have the NA. According to this, none of my LatLon points
# are inside the polygon.
The datafiles are here:
Shapefile: https://github.com/THsTestingGround/SO_readOGR_quest/tree/master/GIS_cgbound
reading csv file: https://github.com/THsTestingGround/SO_readOGR_quest/blob/master/311_Service_Requests__myGNV_.csv
I would appreciate if someone can help me out.
I realized that your point data is an sf object since you have POINT (-82.34323174 29.67058748) as character. Hence, I reconstructed your data first. I assigned a projection here as well.
library(tidyverse)
library(sf)
library(RCurl)
url <- getURL("https://raw.githubusercontent.com/THsTestingGround/SO_readOGR_quest/master/311_Service_Requests__myGNV_.csv")
mydf <- read_csv(url) %>%
mutate(Location = gsub(x = Location, pattern = "POINT \\(|\\)", replacement = "")) %>%
separate(col = "Location", into = c("lon", "lat"), sep = " ") %>%
st_as_sf(coords = c(3,4)) %>%
st_set_crs(4326)
I imported your shapefile using sf package since your data (mydf in this demonstration) is an sf object. When I imported the data, I realized that I had LINESTRING, not polygons. I believe this is the reason why over() did not work. Here I created polygons. Specifically, I joined all seven polygons all together.
mypoly <- st_read("cgbound.shp") %>%
st_transform(crs = 4326) %>%
st_polygonize() %>%
st_union()
Let's check how your data points and polygon are like. You surely have data points staying outside of the polygon.
ggplot() +
geom_sf(data = mypoly) +
geom_point(data = mydf, aes(x = Longitude, y = Latitude))
You said, "I need some points which are outside of the polygon to be NA." So I decided to create a new column in mydf using st_intersects(). If a data point stays in the polygon, you see TRUE in the new column, check. Otherwise, you see FALSE.
mutate(mydf,
check = as.vector(st_intersects(x = mydf, y = mypoly, sparse = FALSE))) -> result
Finally, check how data points are checked.
ggplot() +
geom_sf(data = mypoly) +
geom_point(data = result, aes(x = Longitude, y = Latitude, color = check))
If you wanna use over() mixing with this sf way, you can do the following.
mutate(mydf,
check = over(as(mydf, "Spatial"), as(mypoly, "Spatial")))
The last thing you wanna do is to subset the data
filter(result, check == TRUE)
THE SIMPLEST WAY
I demonstrated you how things are working with this sf approach. But the following is actually all you need. st_filter() extracts data points staying in mypoly. In this case, data points staying outside are removed. If you do not have to create NAs for these points, this is much easier.
st_filter(x = mydf, y = mypoly, predicate = st_intersects) -> result2
ggplot() +
geom_sf(data = mypoly) +
geom_sf(data = result2)

Find centre of polygons using dplyr

I'm making a map with arc lines connecting between counties for the US state of Missouri. I've calculated the 'good enough' centres of each county by taking the mean of each polygon's long/lat. This works good for the more or less square-shaped counties, but less so for the more intricately shaped counties. I think that this must be a common occurrence, but I can't find the answer online or with any function I've created. I'd like to use a tidyverse work flow (i.e. not transform to spatial objects if I can help it). Are there any tidyverse solutions to the problem at hand.
You can see the problem in the examples below.
library(tidyverse)
# import all state/county fortified
all_states <- as_tibble(map_data('county'))
# filter for missouri
mo_fortify <- all_states %>%
filter(region == 'missouri')
## Pull Iron county, which is relatively oddly shaped
mo_iron_fortify <- mo_fortify %>%
group_by(subregion) %>%
mutate(long_c = mean(long),
lat_c = mean(lat),
iron = ifelse(subregion == 'iron','Iron','Others')) %>%
ungroup()
# map a ggplot2 map
mo_iron_fortify %>%
ggplot(aes(long, lat, group = group))+
geom_polygon(aes(fill = iron),
color = 'white')+
geom_point(aes(long_c, lat_c))+
scale_fill_grey('Iron county is\na good example')+
coord_map()+
theme_bw()

Create choropleth map from coordinate points

I have a data frame consisting of multiple data points with specific geocoordinates (latitude and longitude). I'm looking to create a choropleth-style world map where geographical regions are shaded according to how many data points fall within the boundaries of the region.
Is there a simple way to accomplish what I'm trying to do in R, preferably using the "maps" package's world map and the "ggplot2" map-plotting functions?
Here is a minimally reproducible result of what I have:
library(ggplot2)
library(maps)
data <- data.frame(lat = 40.730610, lon = -73.935242)
ggplot() +
geom_polygon(data = map_data("world"), aes(x = long, y = lat, group = group, fill = group)) +
coord_fixed(1.3)
I've noticed that the fill parameter on plot item functions can be used to create a choropleth effect. Here, the fill parameter on the aes() function of the geom_polygon() function is used to create a choropleth where each group is color coded differently.
There are many ways to achieve this task. The general idea is to convert both the point data and polygon data to spatial objects. After that, count how many points fall within that polygon. I know we can do this using the sp package, which is widespread and well-known in the R community, but I decided to use the sf package because sf would be the next generation standard of spatial objects in R (https://cran.r-project.org/web/packages/sf/index.html). Knowing the usage and functionality of sf will probably be beneficial.
First, the OP provided an example point, but I decided to add more points so that we can see how to count the points and aggregate the data. To do so, I used the ggmap pakcage to geocode some cities that I selected as an example.
# Load package
library(tidyverse)
library(ggmap)
library(maps)
library(maptools)
library(sf)
# Create point data as a data frame
point_data <- data.frame(lat = 40.730610, lon = -73.935242)
# Geocode a series of cities
city <- c("Detroit", "Seattle", "Toranto", "Denver", "Mexico City", "Paris", "New Orleans",
"Tokyo", "Osaka", "Beijing", "Canberra", "New York", "Istanbul", "New Delhi",
"London", "Taipei", "Seoul", "Manila", "Bangkok", "Lagos", "Chicago", "Shanghai")
point_data2 <- geocode(city)
# Combine OP's example and the geocoding result
point_data3 <- bind_rows(point_data, point_data2)
Next, I converted the point_data3 data frame to the sf object. I will also get the polygon data of the world using the maps package and convert it to an sf object.
# Convert to simple feature object
point_sf <- st_as_sf(point_data3, coords = c("lon", "lat"), crs = 4326)
# Get world map data
worldmap <- maps::map("world", fill = TRUE, plot = FALSE)
# Convert world to sp class
IDs <- sapply(strsplit(worldmap$names, ":"), "[", 1L)
world_sp <- map2SpatialPolygons(worldmap, IDs = IDs,
proj4string = CRS("+proj=longlat +datum=WGS84"))
# Convert world_sp to simple feature object
world_sf <- st_as_sf(world_sp)
# Add country ID
world_sf <- world_sf %>%
mutate(region = map_chr(1:length(world_sp#polygons), function(i){
world_sp#polygons[[i]]#ID
}))
Now both point_sf and world_sf are sf objects. We can use the st_within function to examine which points are within which polygons.
# Use st_within
result <- st_within(point_sf, world_sf, sparse = FALSE)
# Calculate the total count of each polygon
# Store the result as a new column "Count" in world_sf
world_sf <- world_sf %>%
mutate(Count = apply(result, 2, sum))
The total count information is in the Count column of world_sf. We can get the world data frame as the OP did using the map_data function. We can then merge world_data and world_df.
# Convert world_sf to a data frame world_df
world_df <- world_sf
st_geometry(world_df) <- NULL
# Get world data frame
world_data <- map_data("world")
# Merge world_data and world_df
world_data2 <- world_data %>%
left_join(world_df, by = c("region"))
Now we are ready to plot the data. The following code is the same as the OP's ggplot code except that the input data is now world_data2 and fill = Count.
ggplot() +
geom_polygon(data = world_data2, aes(x = long, y = lat, group = group, fill = Count)) +
coord_fixed(1.3)

Resources