Limit Simple Features Plot based on geojson Polygon - r

I'd like to limit a plot based on a polygon defined in geojson, so that it only shows the area shaded blue here.
i.e. just plot the features inside and including the ring road.
The geojson is available here.
It would also be great to add a buffer around the edge to include the ring road.
My code to draw all the features (unlimited by the geojson is below).
library(tidyverse)
library(osmdata)
bounding_box <- getbb("Birmingham", featuretype = "city")
streets <- bounding_box %>%
opq()%>%
add_osm_feature(key = "highway",
value = c("motorway", "trunk", "primary", "secondary", "tertiary")) %>%
osmdata_sf()
ggplot() +
geom_sf(data = streets$osm_lines,
inherit.aes = FALSE,
color = "grey",
size = 1) +
theme_void() +
theme(
plot.background = element_rect(fill = "white"),
legend.position = "none"
) +
coord_sf(xlim = c(-1.933, -1.869),
ylim = c(52.46, 52.496),
expand = FALSE)

I assume in the following that the object streets has already been defined by running the first few lines of the code in the question. The next step is then to read the polygon using read_sf() from the sf package. The next line converts to a more suitable coordinate system (OSGB 1936 / British National Grid) because adding a buffer in meters is not possible in lon/lat-coordinates. A buffer of 40 meters is added using st_buffer() and finally the coordinates are transformed back to WGS84:
library(sf)
area <- read_sf("~/Birmingham CAZ 2020.GeoJSON") %>%
st_transform(27700) %>%
st_buffer(units::set_units(40, m)) %>%
st_transform(4326)
Of course, you need to adapt the path to where you have actually stored the file. Then I use st_intersection() to extract the part of streets$osm_lines that lies inside the polygon:
streets_area <- st_intersection(poly, streets$osm_lines)
And finally I produce the plot using the code from your question. Note that I have added a layer with the polygon in the second line in order to demonstrate that the streets indeed lie inside the polygon:
ggplot() +
geom_sf(data = area) +
geom_sf(data = streets_area,
inherit.aes = FALSE,
color = "grey",
size = 1) +
theme_void() +
theme(
plot.background = element_rect(fill = "white"),
legend.position = "none"
) +
coord_sf(xlim = c(-1.933, -1.869),
ylim = c(52.46, 52.496),
expand = FALSE)

Related

How to relocate the position on the map (ggplot/sf/geometry)

I am using R to draw a map of China and the USA. I would like to map the "holistic thinking" across these two countries. Could you help me with the followings?
relocate these two countries? There is too much space between the two countries. I would like to narrow the space between the two countries and zoom the size of the two countries.
smaller the size of "Alaska"? I think the size of Alaska is too big relative to the size of the USA.
This is my code.
holistic_10_plot1 <- st_read(" two_triad_current ") %>%
ggplot() +
geom_sf(aes(fill = holistic_10_province)) +
scale_fill_gradient(low = "lightblue", high = "darkblue") +
geom_sf_text(aes(label = province_current), color = "grey", family = "sans", angle = 0) +
theme_void() +
labs(title = "The number of holistic groups(10 focal groups) \n current states/provinces") +
theme(legend.position = "bottom", plot.title = element_text(hjust = 0.5), title = element_text(size = 14, face = "bold", color = "black")) +
guides(fill = guide_colorbar(title = "The number of the holistic groups", title.position = "top"))
This is the plot.
enter image description here
to relocate the positon of China or the USA to make them closer.
You can shift an sf object simply by recalculating its geometry column like so:
library(sf)
## create example sf object "nc":
nc <- st_read(system.file("shape/nc.shp", package="sf"))
add another geometry column (or change it in place):
library(sf)
## shift geometry .1 degrees east and .1 degrees north:
nc$shifted_geometry <- nc |> st_geometry() + c(.1, -.1)
plot original and shifted geometry:
nc |> st_geometry() |> plot()
nc$shifted_geometry |> plot(add = TRUE, border = 'red')
Concerning the area, you could set an equal-area projection with st_transform().

Plot lat/lon points on OpenStreetMap Using R

I am trouble wrapping my head around projection. My points for a place in Northern Europe ends up in Mid Africa.
My code goes as follow.
#Loading packages
library(OpenStreetMap)
library(rgdal)
library(ggplot2)
#defining world map
map <- openmap(c(70,-179), c(-70,179))
plot(map)
#Finding my work place in Northern Europe (Ørbækvej 100, Odense, Denmark from here: https://www.latlong.net/convert-address-to-lat-long.html)
subscr<-data.frame(lat=c(55.381640),
lon=c(10.433600))
#I am not sure what this does, but found on the web for a map in Germany: (https://gis.stackexchange.com/questions/209166/plotting-bubbles-on-top-of-openstreetmap-in-r)
coordinates(subscr)<-~lat+lon
proj4string(subscr)<-CRS("+init=epsg:4326")
points(spTransform(subscr,osm()))
#as can be seen using this method the dot turns up in Eastern Africa
symbols(y = subscr$lon, x = subscr$lat, circles = 1, add = TRUE,
inches = 0.0001, bg = "darkgreen")
#as can be seen using the method the dot turns up in Western/Mid Africa
Can anyone explain or even help me to get the dot placed in Denmark, Northern Europe?
I do not know what kind of map you want, but for plotting lat-lon points, leaflet is my default weapon of choice..
library( leaflet )
library( magrittr )
subscr<-data.frame(lat=c(55.381640),
lon=c(10.433600))
leaflet() %>% addTiles() %>%
addCircleMarkers(data = subscr,
lat = ~lat, lng = ~lon,
color = "blue")
Are you bound to using open street maps? You might consider using the ggmap package which interacts pretty well with ggplot2. However, I sometimes have troubles with downloading an open street map with ggmap, but google-maps should work.
Consider the following example. Note that I removed unnecessary text in the map in the download command.
# download
map <- get_googlemap(center = "Europe", zoom = 3,
style = paste0("feature:administrative.country|",
"element:labels|visibility:off"),
filename = "Map",
language = "en-EN") # you might want to adjust the language settings
# see what you've got
ggmap(map)
# edit map
ggmap(map)+
# do some scaling (make it smaller)
scale_x_continuous(limits = c(-12, 42), expand = c(0, 0)) +
scale_y_continuous(limits = c(35, 70), expand = c(0, 0))+
# remove unwanted information
theme(axis.title.x = element_blank(),
axis.title.y = element_blank(),
axis.line = element_blank(),
axis.ticks = element_blank(),
axis.text = element_blank(),
plot.title = element_blank(),
plot.background = element_blank())+
# add whatever you like based on your coordinates using annotate()
annotate("text", x = 10.433600, y = 55.381640,
label = "You are here",
size = 2.4, color = "black", fontface = "bold",
na.rm = TRUE, hjust = 0.5)
Does this solve your problem?

fit two sf polygons seamlessly

The problem
Suppose we have two shapefiles that should border seamlessly. Only, they don't. Is there a way to force them to stick to one another without gaps?
The specific case
I have two shapefiles: one for European regions -- REG, the other for the neighbouring countries -- NEI. Both shapefiles are taken from Eurostat repository and should fit together nicely; but there are small gaps. Also, I need to simplify the polygons, and then the gaps become really notable.
The best I can think of
I've tried several approaches but with no success. The only way to achieve the desired result that I see requires following steps:
create a line sf with just the border between my shapefiles;
from this line create a buffer polygon just big enough to cover all gaps;
join and dissolve this buffer to the shapefile for neighbours -- NEI;
clip off the expanded NEI with the REG shapefile.
Obviously, this is a rather clumsy workaround.
Is there a better way to go?
Reproducible example in this gist
A minimal example
# install dev version of ggplot2
devtools::dev_mode()
devtools::install_github("tidyverse/ggplot2")
library(tidyverse)
library(sf)
library(rmapshaper)
library(ggthemes)
# load data
source(file = url("https://gist.githubusercontent.com/ikashnitsky/4b92f6b9f4bcbd8b2190fb0796fd1ec0/raw/1e281b7bb8ec74c9c9989fe50a87b6021ddbad03/minimal-data.R"))
# test how good they fit together
ggplot() +
geom_sf(data = REG, color = "black", size = .2, fill = NA) +
geom_sf(data = NEI, color = "red", size = .2, fill = NA)+
coord_sf(datum = NA)+
theme_map()
ggsave("test-1.pdf", width = 12, height = 10)
# simplify
REGs <- REG %>% ms_simplify(keep = .5, keep_shapes = TRUE)
NEIs <- NEI %>% ms_simplify(keep = .5, keep_shapes = TRUE)
ggplot() +
geom_sf(data = REGs, color = "black", size = .2, fill = NA) +
geom_sf(data = NEIs, color = "red", size = .2, fill = NA)+
coord_sf(datum = NA)+
theme_map()
ggsave("test-2.pdf", width = 12, height = 10)
ms_simplify seems to work on your minimal example but you need first to group your 2 "shapefiles" into one "shapefile". If needed it would be easy to split them after the simplification of the boundaries.
(note : my version of rmapshaper returns an error when ms_simplify is used with an sf object. This is why I have transformed my tmp object in a sp object with as(tmp, "Spatial"))
NEI <- st_transform(NEI, st_crs(REG)$epsg)
tmp <- rbind(REG , NEI)
tmp <- ms_simplify(as(tmp, "Spatial"), keep = .1, keep_shapes = T)
ggplot() + geom_sf(data = st_as_sf(tmp)) + theme_bw()

How to add lines of longitude and latitude on a map using ggplot2?

I am now plotting the map of Canada using ggplot2. Because the default projection method is "aea"(Albers Equal Area), so the longitude and latitude are straight lines on the map. I wonder how I can display the longitude and latitude in the form of "110W, 100W, 90W" and "50N, 60N, 70N" on the map. They should be curves. Thanks a lot.
The arcgis shapfile is downloaded from https://www.arcgis.com/home/item.html?id=dcbcdf86939548af81efbd2d732336db
library(ggplot2)
library(rgdal)
countries<-readOGR("Canada.shp", layer="Canada")
ggplot()+geom_polygon(data=countries,aes(x=long,y=lat,group=group),fill='white',color = "black")
The final result should be like this.
You can do this with the coord_map argument of ggplot documented here
This uses projections to alter the coordinate grid. Curved lines would include equal-distance projections, but you should look here for a list of all projections allowed. Which one you choose is a manner of preference.
Using azequidistant (I think this is the Azimuthal equidistant projection), and adding labels manually:
axis_labels <- rbind(
data.frame(long = rep(-140,5),lat = seq(40,80,10), labels = seq(40,80,10)), # x axis labels
data.frame(long = seq(-140,-60,40),lat = rep(85,3), labels = seq(140,60,-40)) # y axis labels
)
ggplot() +
geom_polygon(data=countries,aes(x=long,y=lat,group=group),fill='white',color = "black") +
coord_map("azequidistant") +
scale_x_continuous(breaks = seq(-140,60, by = 20))+
scale_y_continuous(breaks = seq(40,80, by = 10)) +
geom_text(data = axis_labels, aes(x = long, y = lat, label = labels)) +
theme_bw() +
theme(panel.grid.major = element_line(colour = "grey"),
panel.border = element_blank(),
axis.text = element_blank())
You can use a separate graticule layer of spatial data, which you then project based on your Canada layer.
You can find free graticule layers for download at NaturalEarthData.
countries<-readOGR("Canada.shp", layer="Canada")
grat <- readOGR("graticule.shp", layer="graticule")
grat_prj <- spTransform(grat, CRS(countries))
ggplot() +
geom_polygon(data=countries, aes(x=long,y=lat,group=group),fill='white',color = "black") +
geom_path(data=grat_prj, aes(long, lat, group=group, fill=NULL), linetype="solid", color="grey50")

plotting shape file in ggplot2

I'm trying to figure out how to display my complete map in gglot2 including the island Both r_base and tmap were able to display the islands but ggplot2 couldn't differentiate the island from the rest of the waterbody...
.
My question is how to make the Islands appear in ggplot2?
See the code i used below.
library(ggplot2)
library (rgdal)
library (rgeos)
library(maptools)
library(tmap)
Loading the Persian Gulf shape fill referred to as iho
PG <- readShapePoly("iho.shp")
the shape file is available here
http://geo.vliz.be:80/geoserver/wfs?request=getfeature&service=wfs&version=1.0.0&typename=MarineRegions:iho&outputformat=SHAPE-ZIP&filter=%3CPropertyIsEqualTo%3E%3CPropertyName%3Eid%3C%2FPropertyName%3E%3CLiteral%3E41%3C%2FLiteral%3E%3C%2FPropertyIsEqualTo%3E
plot with r_base
Q<-plot(PG)
Corresponds to figure A
Ploting with tmap
qtm(PG)
Corresponds to figure B
convert to dataframe
AG <- fortify(PG)
Plot with ggplot2
ggplot()+ geom_polygon(data=AG, aes(long, lat, group = group),
colour = alpha("darkred", 1/2), size = 0.7, fill = 'skyblue', alpha = .3)
Corresponds to figure C
You need to tell ggplot you want the holes filled in with a different color..for example:
ggplot()+ geom_polygon(data=AG, aes(long, lat, group = group, fill = hole), colour = alpha("darkred", 1/2), size = 0.7) + scale_fill_manual(values = c("skyblue", "white")) + theme(legend.position="none")
Also try readOGR() function from the rgdal package instead of readShapePoly() it keeps all the projection and datum information when you read the shape file.
Further to #AdamMccurdy's answer:, there are some possibilities to get the same colour for islands and adjacent background.
The first sets the fill colour of the islands and the colour of the background to be the same. But the grid lines are under the polygon, and thus disappear.
The second is an attempt to get the grid lines back. It plots the background (which includes the grid lines) on top of the panel (using panel.ontop = TRUE). But it's a bit of a fiddle adjusting alpha values to get the same background and island colour.
The third sets the background and island colours to be the same (as in the first), then plots the grid lines on top of the panel. There's a couple of ways to do this; here, I grab the grid lines grob from the original plot, then draw them on top of the panel. Thus the colours remain the same, and no need for alpha transparencies.
library(ggplot2)
library (rgdal)
library (rgeos)
library(maptools)
PG <- readOGR("iho.shp", layer = "iho")
AG <- fortify(PG)
Method 1
bg = "grey92"
ggplot() +
geom_polygon(data = AG, aes(long, lat, group = group, fill = hole),
colour = alpha("darkred", 1/2), size = 0.7) +
scale_fill_manual(values = c("skyblue", bg)) +
theme(panel.background = element_rect(fill = bg),
legend.position = "none")
Method 2
ggplot() +
geom_polygon(data = AG, aes(long, lat, group = group, fill = hole),
colour = alpha("darkred", 1/2), size = 0.7) +
scale_fill_manual(values = c("skyblue", "grey97")) +
theme(panel.background = element_rect(fill = alpha("grey85", .5)),
panel.ontop = TRUE,
legend.position = "none")
Method 3
Minor edit updating to ggplot version 3.0.0
library(grid)
bg <- "grey92"
p <- ggplot() +
geom_polygon(data = AG, aes(long, lat, group = group, fill = hole),
colour = alpha("darkred", 1/2), size = 0.7) +
scale_fill_manual(values = c("skyblue", bg)) +
theme(panel.background = element_rect(fill = bg),
legend.position = "none")
# Get the ggplot grob
g <- ggplotGrob(p)
# Get the Grid lines
grill <- g[7,5]$grobs[[1]]$children[[1]]
# grill includes the grey background. Remove it.
grill$children[[1]] <- nullGrob()
# Draw the plot, and move to the panel viewport
p
downViewport("panel.7-5-7-5")
# Draw the edited grill on top of the panel
grid.draw(grill)
upViewport(0)
But this version might be a little more robust to changes to ggplot
library(grid)
bg <- "grey92"
p <- ggplot() +
geom_polygon(data = AG, aes(long, lat, group = group, fill = hole),
colour = alpha("darkred", 1/2), size = 0.7) +
scale_fill_manual(values = c("skyblue", bg)) +
theme(panel.background = element_rect(fill = bg),
legend.position = "none")
# Get the ggplot grob
g <- ggplotGrob(p)
# Get the Grid lines
grill <- getGrob(grid.force(g), gPath("grill"), grep = TRUE)
# grill includes the grey background. Remove it.
grill = removeGrob(grill, gPath("background"), grep = TRUE)
# Get the name of the viewport containing the panel grob.
# The names of the viewports are the same as the names of the grobs.
# It is easier to select panel's name from the grobs' names
names = grid.ls(grid.force(g))$name
match = grep("panel.\\d", names, value = TRUE)
# Draw the plot, and move to the panel viewport
grid.newpage(); grid.draw(g)
downViewport(match)
# Draw the edited grill on top of the panel
grid.draw(grill)
upViewport(0)

Resources