How to combine sf elements (layers) in R - r

despite having some experience with R, I am much less experienced using R for GIS-like tasks.
I have a shapefile of all communities within Germany and created a new object that only shows the borders of the 16 states of Germany.
gem <- readOGR(path/to/shapefile.shp) # reading shapefile
gemsf <- st_read(path/to/shapefile.shp) # reading shapefile as sf object
f00 <- gUnaryUnion(gem, id = gem#data$SN_L) # SN_L is the column of the various states - this line creates a new sp object with only the states instead of all communities
f002 <- sf::st_as_sf(f00, coords = c("x","y")) # turning the object into an sf object, so graphing with ggplot is easier
To check my work so far I plotted the base data (communities) using
gemsf %>%
ggplot(data = .,) + geom_sf( aes(fill = SN_L)) # fill by state
as well as plot(f002) which creates a plot of the 16 states, while the ggplot-code provides a nice map of Germany by community, with each state filled in a different color.
Now I'd like to overlay this with a second layer that indicates the borders of the states (so if you e.g. plot population density you can still distinguish states easily).
My attempt to do so, I used "standard procedure" and added another layer
ggplot() +
geom_sf(data = gemsf, aes(fill = SN_L)) + # fill by state
geom_sf(data = f002) # since the f002 data frame/sf object ONLY has a geometry column, there is no aes()
results in the following output: https://i.ibb.co/qk9zWRY/ggplot-map-layer.png
So how do I get to add a second layer that only provides the borders and does not cover the actual layer of interest below? In QGIS or ArcGIS, this is common procedure and not a problem, and I'd like to be able to recreate this in R, too.
Thank you very much for your help!

I found a solution which I want to share with everyone.
ggplot() +
geom_sf(data = gemsf_data, aes(fill = log(je_km2))) + # fill by state
geom_sf(data = f002, alpha = 0, color = "black") + # since the f002 data frame/sf object ONLY has a geometry column, there is no aes()
theme_minimal()
The trick was adding "alpha" not in the aes() part, but rather just as shown above.

Related

How to only keep counties crossed by a motorway?

I am newish to R and very new to GIS plotting on R with sf and ggplot2 packages. I have a dataset "comuni" containing all communes in Italy (similar to counties) and one of all motorways in Italy called "only_motorway". I know that I can use certain regions as a cookie cutter and keep only the motorways that are contained within such regions using st_intersection() function. However, I would like to do the inverse where, given I have a shapefile of the A3 motorway, I would like to keep only those communes that are crossed by that specific motorway.
I've tried using st_intersection function in the following way:
only_motorway_A3 <- only_motorway %>%
filter(ref == "A3")
comuni_A3 <- st_intersection(only_motorway_A3,comuni)
ggplot() +
geom_sf(data = comuni_A3,
color = "black", size = 0.1, fill = "black") +
geom_sf(data = only_motorway_A3, size = 0.15, color = "green") +
coord_sf(crs = 32632) +
theme_void()
But the results is the picture below:
ggplot
ie both only_motorway_A3 and comuni_A3 have the same geometry column and they both plot the highway line. What I wanted to plot instead was the highway line (in green) from only_motoway_A3 and all around it the communes crossed by it (in black) from comuni_A3. I hope it is clear and thank you in advance for your help!
Consider a sf::st_join() call, using first your polygons and secondly your line string objects, with parameter left set to false.
It will perform an inner (filtering) spatial join of the two objects. Only those polygons (the first argument) that contain a motorway will be retained.

Map FAO fishing areas in R

I would like to make a map in R that colours in the FAO Fishing Areas according to a data set (in my case, length data of shark species).
I would prefer to do a choropleth map in ggplot but other types of maps are also fine. Worst case scenario a base map of FAO areas that I can add bubbles to. Even just an existing base map of FAO areas would be great. Any suggestions welcome!
I went to this page and clicked through to find this link to retrieve a GeoJSON file:
download.file("http://www.fao.org/fishery/geoserver/fifao/ows?service=WFS&request=GetFeature&version=1.0.0&typeName=fifao:FAO_AREAS_CWP&outputFormat=json", dest="FAO.json")
From here on, I was following this example from the R graph gallery, with a little help from this SO question and these notes:
library(geojsonio)
library(sp)
library(broom)
library(ggplot2)
library(dplyr) ## for joining values to map
spdf <- geojson_read("FAO.json", what = "sp")
At this point, plot(spdf) will bring up a plain (base-R) plot of the regions.
spdf_fortified <- tidy(spdf)
## make up some data to go with ...
fake_fish <- data.frame(id = as.character(1:324), value = rnorm(324))
spdf2 <- spdf_fortified %>% left_join(fake_fish, by = "id")
ggplot() +
geom_polygon(data = spdf2, aes( x = long, y = lat, group = group,
fill = value), color="grey") +
scale_fill_viridis_c() +
theme_void() +
theme(plot.background = element_rect(fill = 'lightgray', colour = NA)) +
coord_map() +
coord_sf(crs = "+proj=cea +lon_0=0 +lat_ts=45") ## Gall projection
ggsave("FAO.png")
notes
some of the steps are slow, it might be worth looking up how to coarsen/lower resolution of a spatial polygons object (if you just want to show the picture, the level of resolution might be overkill)
to be honest the default sequential colour scheme might be better but all the cool kids seem to like "viridis" these days so ...
There are probably better ways to do a lot of these pieces (e.g. set map projection, fill in background colour for land masses, ... ?)

Shading a map with underlying data in Julia

I want to create a map of Germany where each state is shaded according to its gross domestic product. I know how to do this in R (and put the code below). Is there a possibility to do this in Julia in an equally simple way?
library(tidyverse)
library(ggplot2)
library(sf)
shpData = st_read("./geofile.shp")
GDPData <- read.delim("./stateGDP.csv", header=FALSE)
GDPData <- rename(GDPData,StateName=V1,GDP=V2)
GDPData %>%
left_join(shpData) ->mergedData
ggplot(mergedData) + geom_sf(data = mergedData, aes(fill = BIP,geometry=geometry)) + coord_sf(crs = st_crs(mergedData))-> pBIP1
You'd load the Shapefile and use Plots to plot it.
The ideomatic code is something like
using Plots, Shapefile, CSV
shp = Shapefile.shapes(Shapefile.Table("geofile.shp"))
GDPData = CSV.read("stateGDP.csv")
plot(shp, fill_z = GDPData.V2')
Note the ' which transposes the values to a column vector - this will tell Plots to apply the colors to individual polygons.

Bizarre polygons from a shapefile

I'm trying to apply a shapefile to a ggmaps map, but it's giving me really weird results. The shapefile in question is the "Statistical Local Area" (groups similar to postcode) shapefile from the Australian Bureau of Statistics available here.
Normally I might think that it's a problem of cut off edge points, but I'm hitting it even at zoom level 1 (in fact it looks even worse):
Here's some code I used to produce the charts above:
library(tidyverse)
library(ggmap)
library(rgdal)
slas <- readOGR(dsn="SLA",layer="SLA11aAust")
aus4 <- get_map("Australia",zoom=4)
ggmap(aus4)
ggmap(aus4)+
geom_polygon(data=slas, aes(x=long,y=lat))
aus1 <- get_map("Australia",zoom=1)
ggmap(aus1)
ggmap(aus1)+
geom_polygon(data=slas, aes(x=long,y=lat))
Am I doing something wrong, or is the shapefile incorrectly configured somehow?
I think you just need to (optionally) fortify the variable slas, don't forget to group and make the boundaries visible with a color:
slas <- fortify(slas, region = "SLA_CODE11")
ggmap(aus4) +
geom_polygon(data = slas2, color = "white", aes(x = long, y = lat, group = group))

How to properly join data and geometry using ggmap

An image is worth a thousand words:
Observed behaviour: As can be seen from the image above, countries' names do not match with their actual geometries.
Expected behaviour: I would like to properly join a data frame with its geometry and display the result in ggmap.
I've previously joined different data frames, but things get wrong by the fact that apparently ggmap needs to "fortify" (actually I don't know what really means) data frame in order to display results.
This is what I've done so far:
library(rgdal)
library(dplyr)
library(broom)
library(ggmap)
# Load GeoJSON file with countries.
countries = readOGR(dsn = "https://gist.githubusercontent.com/ccamara/fc26d8bb7e777488b446fbaad1e6ea63/raw/a6f69b6c3b4a75b02858e966b9d36c85982cbd32/countries.geojson")
# Load dataframe.
df = read.csv("https://gist.githubusercontent.com/ccamara/fc26d8bb7e777488b446fbaad1e6ea63/raw/a6f69b6c3b4a75b02858e966b9d36c85982cbd32/sample-dataframe.csv")
# Join geometry with dataframe.
countries$iso_a2 = as.factor(countries$iso_a2)
countries#data = left_join(countries#data, df, by = c('iso_a2' = 'country_code'))
# Convert to dataframe so it can be used by ggmap.
countries.t = tidy(countries)
# Here's where the problem starts, as by doing so, data has been lost!
# Recover attributes' table that was destroyed after using broom::tidy.
countries#data$id = rownames(countries#data) # Adding a new id variable.
countries.t = left_join(countries.t, countries#data, by = "id")
ggplot(data = countries.t,
aes(long, lat, fill = country_name, group = group)) +
geom_polygon() +
geom_path(colour="black", lwd=0.05) + # polygon borders
coord_equal() +
ggtitle("Data and geometry have been messed!") +
theme(axis.text = element_blank(), # change the theme options
axis.title = element_blank(), # remove axis titles
axis.ticks = element_blank()) # remove axis ticks
While your work is a reasonable approach - I would like to rethink your design, mainly because of two simple reasons:
1) while GeoJSON is the future, R still heavily relies on the sp package and its correspondent sp* objects - very soon you wish you had switched early on. It`s just about the packages and most of them (if not all) rely on sp* objects.
2) ggplot has great plotting capabilities combined with ggmap - but its still quite limited compared to sp* in combination with leaflet for R etc.
probably the fastest way to go is simple as:
library(sp)
library(dplyr)
library(geojsonio)
library(dplyr)
library(tmap)
#get sp* object instead of geojson
countries <- geojsonio::geojson_read("foo.geojson",what = "sp")
#match sp* object with your data.frame
countries#data <- dplyr::left_join(countries#data, your_df, by =
c("identifier_1" = "identifier_2"))
#creates a fast and nice looking plot / lots of configuration available
p1 <- tm_shape(countries) +
tm_polygons()
p1
#optional interactive leaflet plot
tmap_leaflet(p1)
It is written out of my head / bear with me if there are minor issues.
It is a different approach but its at least in my eyes a faster and more concise approach in R right now (hopefully geojson will receive more support in the future).
There is a reason for the messed up behaviour.
countries starts out as a large SpatialPolygonsDataFrame with 177 elements (and correspondingly 177 rows in countries#data). When you perform left_join on countries#data and df, the number of elements in countries isn't affected, but the number of rows in countries#data grows to 210.
Fortifying countries using broom::tidy converts countries, with its 177 elements, into a data frame with id running from 0 to 176. (I'm not sure why it's zero-indexed, but I usually prefer to specify the regions explicitly anyway).
Adding id to countries#data based on rownames(countries#data), on the other hand, results in id values running from 1 to 210, since that's the number of rows in countries#data after the earlier join with df. Consequently, everything is out of sync.
Try the following instead:
# (we start out right after loading countries & df)
# no need to join geometry with df first
# convert countries to data frame, specifying the regions explicitly
# (note I'm using the name column rather than the iso_a2 column from countries#data;
# this is because there are some repeat -99 values in iso_a2, and we want
# one-to-one matching.)
countries.t = tidy(countries, region = "name")
# join with the original file's data
countries.t = left_join(countries.t, countries#data, by = c("id" = "name"))
# join with df
countries.t = left_join(countries.t, df, by = c("iso_a2" = "country_code"))
# no change to the plot's code, except for ggtitle
ggplot(data = countries.t,
aes(long, lat, fill = country_name, group = group)) +
geom_polygon() +
geom_path(colour="black", lwd = 0.05) +
coord_equal() +
ggtitle("Data and geometry are fine") +
theme(axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank())
p.s. You don't actually need the ggmap package for this. Just the ggplot2 package that it loads.

Resources