Shifting the longitude of an sf object - r

I have a map of countries (sf object) and I would like to shift the longitude of the eastern portion of Russia so that it is not isolated from the rest of Russia.
See Image
I found the backend code for st_shift_longitude https://github.com/r-spatial/sf/blob/master/R/shift_longitude.R, which shifts all coordinates by 180 degrees, so that the resulting map looks as follows:
link
How can I modify this block of code to shift the eastern portion of Russia only?
shift_lon <- function(x){
xcrs = st_crs(x)
g = (x + c(360, 90)) %% c(360) - c(0, 90)
st_wrap_dateline(st_set_crs(g - c(180, 0), xcrs)) + c(180, 0)
st_set_crs(g, xcrs)
}
st_geometry(countries) <- shift_lon(st_geometry(countries))
Alternative solutions are also welcome.

You will need to break down your world object into two parts - one containing Russia, and other for the rest of world.
Then apply the pacific view / sf::st_shift_longitude() on the Russia part, and merge it back with the "rest of world" dataset.
In this example I am using the world dataset from giscoR package. It is my favorite, but it is not the only one available; and it has a feature (or a bug, depending on circumstances) of applying a thin interruption at the antimeridean; this results in an artefact around Chukotka in my map. Getting rid of it is a separate issue, and I am not certain you will be facing it with your version of the world dataset (so it may be, and may not be, relevant problem).
Anyway, here is a possible code implementation:
library(sf)
world <- giscoR::gisco_get_countries() # or your own world dataset
rossiya <- subset(world, ISO3_CODE == "RUS")
# shift Russia to a more Pacific centric worldview
pacified_rossiya <- st_shift_longitude(rossiya)
rest_of_world <- subset(world, ISO3_CODE != "RUS")
# this is the important section: bind together the shifted and unshifted parts
world2 <- rbind(pacified_rossiya,
rest_of_world)
plot(st_geometry(world2))

Related

changing extent of SpatVector for better visualisation

library(geodata)
ct_shp <- geodata::gadm('USA', level = 0, path=".")
plot(ct_shp)
When I plot the USA, I get this.
Any raster I plot for US using this shapefile as a basemap becomes really small. The only solution I have is to crop out polygons greater than 100 degrees longitude so that I get a bigger map of US and I can plot any US specifc raster for more appealing maps. I wondered if there's anyone who faces such issue and how they have dealt with it.
In this case I would first simplify the geometries a bit (that speeds things up, but is otherwise not related to your question).
library(geodata)
usa <- geodata::gadm('USA', level = 0, path=".")
usa <- simplifyGeom(usa, 0.01)
You can select the western hemisphere (or any other area) like this
e <- ext(-180, -60, 11, 78)
p <- crop(usa, e)
Alternatively, you can use rotate to eastern hemisphere bit so that it connects to the western hemisphere. For that you need "terra" >= 1.6-21 (currently the development version, you can install it with install.packages('terra', repos='https://rspatial.r-universe.dev'))
r <- rotate(usa, long=0, split=T)
plot(r)
This is still not a great basemap for the USA. There is a lot of whitespace and Alaska is way too big for most purposes. I would either use an appropriate (conical) map projection, or cut, resize, and move Alaska and Hawaii, but that is quite involved.

Why is tidycensus area provided different that calculated by sf::_st_area()

I am using the tidycensus R package to pull in census data and geometries. I want to be able to calculate population densities and have the results match what I see on censusreporter.org. I am noticing a difference between the geography variables returned from tidycenus compared to what I calculate myself using the sf package sf::st_area() function.
library(tidyverse)
library(tidycensus)
census_api_key("my_api_key")
library(sf)
options(tigris_use_cache = TRUE)
pop_texas <-
get_acs(geography = 'state',
variables = "B01003_001", # Total Population
year = 2020,
survey = 'acs5',
keep_geo_vars = TRUE,
geometry = TRUE) %>%
filter(GEOID == '48') # Filter to Texas
Since I included the keep_geo_vars argument as TRUE it returned an ALAND column which I believe is the correct area for the geography returned in square meters (m^2).
> pop_texas$ALAND %>% format(big.mark=",")
[1] "676,680,588,914"
# Conversion to square miles
> (pop_texas$ALAND / 1000000 / 2.5899881) %>% format(big.mark=",")
[1] "261,267.8"
When I convert the ALAND amount to square miles I get the same number as shown on censusreporter.org:
I have also tried to calculate the area using the sf::st_area() function, but I get a different result:
> sf::st_area(pop_texas) %>% format(big.mark=",", scientific=FALSE)
[1] "688,276,954,146 [m^2]"
# Conversion to square miles
> (sf::st_area(pop_texas) / 1000000 / 2.5899881) %>%
+ as.numeric() %>%
+ format(big.mark=",", scientific=FALSE)
[1] "265,745.2"
Please let me know if there is something I am missing to reconcile these numbers. I would expect to get the same results either directly through tidycensus or calculating the area using sf::st_area().
Right now I am off by a lot:
> (pop_texas$ALAND - as.numeric(st_area(pop_texas)) ) %>%
+ format(big.mark=",")
[1] "-11,596,365,232"
If you want the "official" area of a shape like Texas you should always use the ALAND or published area value. st_area() is using geometry to calculate the area of the polygon which is always going to be a simplified and imperfect representation of Texas (or any other area). For smaller shapes (like Census tracts) the calculations will probably be pretty close; for larger shapes like states (especially those with complex coastal geography, like Texas) you're going to be further off.
These differences are usually due to the CRS (the projection used on your sf objects). Some projections distort area, other projections distors the shape. See this to learn more http://wiki.gis.com/wiki/index.php/Distortion#:~:text=There%20are%20four%20main%20types,%2C%20direction%2C%20shape%20and%20area.

Removing outlying islands from US map in shapefile with R

I downloaded level-0 US map (National border) in R(sf) format from https://gadm.org/download_country_v3.html. I plotted US border (level 0) as follows:
library(tidyverse)
us0 <- readRDS("<file.path>\\gadm36_USA_0_sf.rds")
ggplot() +
geom_sf(data = us0, size = 1, color = "steelblue", fill = NA)
Resultant image shown below
I would like to remove outlying islands A and B, and move Alaska from C to C'.
I tried but failed to extract longitude and latitude data from us0. I searched online but did not find adequate answers on how to do this in R. I would like to know how longitude and latitude data could be extracted from us0 with R so that I can freely delete A and B and remove C. Thank you.
using the raster package and the geom() function you can extract the raw coordinates from the RDS object, as well as which island/state the coordinate is part of and whether it is solid or a hole.
In your case:
geom(us0)
Then it's just a matter of finding out which vertices belong to which islands. A quick way of deleting B would be to delete everything with a positive x coordinate. You'll need to find out which objects correspond to Alaska if you're to move and scale it. The top of Hawaii is (i.i.r.c) lower down than the bottom of the Florida Keys so you can also get away with removing everything with a latitude less than 23.
Also, I suspect many Hawaiians would object to Hawaii being referred to as an outlying island....

ggmap - merging two satellite ggmaps into 1 ggmap

I am trying to map data onto higher resolution Google satellite imagery. I could use a lower resolution image (e.g. zoom 13 and limit the scales - as suggested here - ggmap extended zoom or boundaries) however, the resultant image is not clear enough for my purpose. So basically I would like to be able to combine 2 14 zoom into 1 ggmap:
library(ggmap)
library(gridExtra)
g1 <- get_googlemap(center = c(-83.986927, 33.955656), maptype="satellite", zoom=14)
g2 <- get_googlemap(center = c(-83.938079, 33.955656), maptype="satellite", zoom=14)
gmap1 <- ggmap(g1)
gmap2 <- ggmap(g2)
grid.arrange(gmap1, gmap2, ncol =2)
but have 1 ggmap object that combined gmap1 and gmap2.
You can (and probably should) convert to raster objects. You should really use them independently from then on, like tiles, since their pixels don't seem to be on the same grid basis so mosaicing them might not be perfect. You can bodge this by adjusting the tolerance.
The objects from get_googlemap are matrices with colour values in hex ("#FF000" etc) and some attributes defining the extent. The following code converts that object to a three-band RGB raster, with the right extent and CRS:
library(raster)
ggmap2raster <- function(g){
rgb = col2rgb(g)
bands = apply(rgb, 1, function(band){
raster(t(matrix(band,ncol=ncol(g), nrow=nrow(g))))
})
s = stack(bands)
bb = attr(g, "bb")
extent(s) = extent(bb$ll.lon,bb$ur.lon, bb$ll.lat, bb$ur.lat)
crs(s) <- "+init=epsg:4326"
s
}
To merge a bunch of them, this code uses mosaic, but because the layers don't seem to line up quite right (possibly because the data are really in web mercator rather than WGS84) you need to up the tolerance and hope:
mergegg <- function(glist){
m = function(...){
mosaic(...,tolerance=0.5, fun=min)
}
do.call(m,
lapply(glist, function(g){
ggmap2raster(g)
})
)
}
> r = mergegg(list(g1, g2))
> plotRGB(r)
I suspect the tolerance problem may disappear if I convert the corner coords back to Web Mercator. But that's too much bother for a Friday morning. ggmap and its handling of coordinate systems is not something I want to get into right now. You could try binding the two g1 and g2 matrix objects together but you probably would have to do the reverse transform first and to be honest given the restrictions on using Google satellite images (you have read the license conditions?) I suspect its a bad thing.
To visualise raster objects, use the tmap package instead of ggmap.

How do I select the subset of my lat/lon data that is highway travel using R?

I have some data which consists of time-stamped lat/lon pairs, a subset of which I've mapped out below using ggmap. If I wanted to select only the data consisting of travel along the highways, which you can kinda see on the map - the 280 running NW-SE between the green mountains and the grey flat area & the 101 cutting through the middle of the grey flat area (where the red is dense) - how would I select only that data?
What I'd ultimately like to achieve is a dataframe which contains only the highway/interstate travel. I've seen this question, which is a brief sketch of a possible solution in javascript, & suggests to use the Directions API to return the nearest road for any given point. I could then filter those results, but I'm wondering if anyone has found a cleaner solution.
Here's some sample data (CSV)
Here's the code to plot the above:
require(ggmap)
map<-get_googlemap(center="Palo Alto", zoom = 10)
ggmap(map) + geom_point(data = sample, aes(x = lon, y = lat),size = 3, color = "red")
You don't need an API key to run the above.
I just found this post and thought this is an interesting question. I wanted to download your sample data file. Unfortunately, the link was not working any more. Therefore, I could not try the whole process I had in my mind. However, I believe the following will let you move forward if you still try to do this task.
I recently noticed that Natural Earth offers road data. That is, you can get long and lat for the roads in the US, for example. If you can compare lon/lat in your data set and lon/lat of the road data, and identify matches in data points, you can get the data you want. My concern is to what extent your data points are accurate. If lon/lat perfectly stays on the road you are interested in, you will be OK. But if there are some margins, you may have to think how you can filter your data.
What I would like to leave here is evidence that the road data and googlemap match pretty well. As long as I see the output, the road data is reliable. You can subset your data using the road data. Here is my code.
### Step 1: shapefile becomes SpatialLinesDataFrame.
foo <- readShapeLines("ne_10m_roads_north_america.shp")
### Step 2: If necessary, subset data before I use fortify().
### dplyr does not work with SpatialLinesDataFrame at this point.
CA <- foo %>%
subset(.,country == "United States" & state == "California")
### Step 3: I need to convert foo to data frame so that I can use the data
### with ggplot2.
ana <- fortify(CA)
### Step 4: Get a map using ggmap package
longitude <- c(-122.50, -121.85)
latitude <- c(37.15, 37.70)
map <- get_map(location = c(lon = mean(longitude), lat = mean(latitude)),
zoom = 12, source = "google",
maptype = "satellite")
ggmap(map) +
geom_path(aes(x = long, y = lat, group = group), data = ana)

Resources