I have a data set of latitude/longitude points that seek to convert to a simple feature (sf) in R.
My goal is to plot these locations on a US map with polygons retrieved from the urbnmapr library.
Plotting with our a geographic reference, as shown in the code, results in all points being displayed.
When the points are plotted using geom_sf() they end up in South Dakota. It seems the latitude/longitude points are not being converted into the correct coordinate reference system, despite what I think is the correct use of the st_as_sf() function.
What correction needs to be made to this code to show the distribution of wind turbine locations properly on the US map?
# Map the locations of US Wind Turbines
library(urbnmapr)
library(ggplot2)
library(readr)
library(dplyr)
library(sf)
# This file is available from https://eerscmap.usgs.gov/uswtdb/assets/data/uswtdbCSV.zip
turbine <- read_csv("C:\\mydir\\uswtdb_v3_1_20200717.csv")
# Convert lat/long to a sf
turbine_sf <- turbine %>%
st_as_sf(coords = c("xlong", "ylat"), crs=2163)
# obtain state geometries
states_sf <- get_urbn_map(map = "states", sf = TRUE)
# Remove AK, HI from state and PR and GU from turbines as well
states_sf <- states_sf[!(states_sf$state_abbv %in% c("HI","AK")),]
turbine <- turbine [!(turbine$t_state %in% c('HI','AK','PR','GU')),]
# simple plot shows all locations
ggplot(turbine, aes(x=xlong, y=ylat)) + geom_point()
#plot locations over map
ggplot() +
geom_sf(data = turbine_sf) +
geom_sf(data = states_sf, fill = NA, color = "black", size = 0.15, alpha = 0) +
coord_sf(datum = st_crs(2163)) +
labs(fill = "",
title = "",
caption='') +
theme_bw()
Your turbine dataset contains "xlong" and "ylat" in degrees i.e. geographic coordinate system with WGS84 datum (EPSG code: 4326). So, first, make it as crs = 4326 and then use st_transform(turbine_sf, crs=2163) to make same coordinate system with states_sf. You can use the following code
# Map the locations of US Wind Turbines
library(urbnmapr)
library(ggplot2)
library(readr)
library(dplyr)
library(sf)
# This file is available from https://eerscmap.usgs.gov/uswtdb/assets/data/uswtdbCSV.zip
turbine <- read_csv("uswtdb_v3_1_20200717.csv")
# Convert lat/long to a sf
turbine_sf <- turbine %>%
st_as_sf(coords = c("xlong", "ylat"), crs=4326)
turbine_sf_t <- st_transform(turbine_sf, crs=2163)
# obtain state geometries
states_sf <- get_urbn_map(map = "states", sf = TRUE)
st_crs(states_sf)
# Remove AK, HI from state and PR and GU from turbines as well
states_sf <- states_sf[!(states_sf$state_abbv %in% c("HI","AK")),]
turbine <- turbine [!(turbine$t_state %in% c('HI','AK','PR','GU')),]
# simple plot shows all locations
ggplot(turbine, aes(x=xlong, y=ylat)) + geom_point()
#plot locations over map
ggplot() +
geom_sf(data = turbine_sf_t) +
geom_sf(data = states_sf, fill = NA, color = "black", size = 0.15, alpha = 0) +
coord_sf(datum = st_crs(2163)) +
labs(fill = "",
title = "",
caption='') +
theme_bw()
By doing st_as_sf(coords = c("xlong", "ylat"), crs=2163) you're saying that the original long, lat from your turbine table are based on CRS of 2163. I think you want to set them as 4326 which is the long lat under WGS84.
After setting the initial CRS, use st_transform() to transform the CRS of your shape to new CRS, e.g. turbine_sf <- st_transform(turbine_sf, crs=2163)
Related
I have the below code which is intended to
a) Draw a base outline layer of all Middle Super Output Areas in the East of England
b) Generate 2,500 buffer boundaries around each plotted point from an imported dataset
c) Plot the buffer boundary layer over the base outline layer.
#Call necessary packages
library(tidyverse)
library (readxl)
library (openxlsx)
library(maptools)
library(classInt)
library(RColorBrewer)
library(sf)
library(tmap)
library(tmaptools)
library(geodata)
#Read in shape file for mapping
shp_name <- C:/Users/JWP/East of England/MSOA/Middle_Layer_Super_Output_Areas_December_2011_Generalised_Clipped_Boundaries_in_England_and_Wales.shp"
EofEMSOAsFinalList <- st_read(shp_name)%>%
st_as_sf
# Create union shape of polygons
union <- st_union(EofEMSOAsFinalList)
#Read in point location data
LocationData <- read_excel("C:/Users/JWP/LocationData.xlsx",
sheet = "Location Data")
#Geocode the address list with 2,500m boundaries around each point
LocationDataPlotted <- st_as_sf(LocationData, coords = c('Latitude', 'Longitude'), crs = 4326)
#Remove geometry
LocationDataPlotted2 <- LocationDataPlotted %>%
as.data.frame() %>%
# calculate around each point a buffer zone of 2,500m
mutate(buffer = st_buffer(geometry, dist = 2500)) %>%
select(-geometry) %>%
st_as_sf()
#Generate bounding box
mask_union <- union %>% as_tibble() %>%
mutate(bbox = st_as_sfc(st_bbox(c(xmin = -5.5, xmax = 9, ymax = 51.5, ymin = 42), crs = st_crs(4326)))) %>%
st_as_sf()
# compute difference between bounding box and union polygon to
# use as mask in the final layer
diff <- st_difference(mask_union$bbox, mask_union$geometry)
# Build map
OutputMap <-
# plot only shapes filled red
tm_shape(EofEMSOAsFinalList) +
tm_fill(col = "red") +
# plot only buffer zones of each point in green
tm_shape(LocationDataPlotted2)+
tm_fill(col = "forestgreen") +
# add mask
tm_shape(diff) +
tm_fill(col = "white") +
# plot borders of shape
tm_shape(EofEMSOAsFinalList) +
tm_borders(col = "white",
lwd = 1,
lty = "solid") +
# add custom legend
tm_add_legend(type = "symbol",
labels = c("Not Within 2500m", "Within 2500m"),
col = c("red", "forestgreen"),
title = "Access type",
size = 1.5,
shape = 21)
The correct output should therefore look similar to the above:
However I am now getting output like the below:
Can anyone please amend the above so it works correctly?
Many thanks
I'm making a fairly detailed map of the northern high latitudes that I want to show with an Arctic projection. I'd like to have a map that has a transparent circle from 50N to 90N as a mask for the final image. Seems easy. But I'm stuck. What I'd like is for the map in p1 below to be reprojected into North Pole Azimuthal Equidistant projection with the data below 50N to be greyed out and the data above 50N to show through. Any tips?
library(sf)
library(tidyverse)
#get coastlines as a demo
coastlines <- rnaturalearth::ne_coastline(scale = 50, returnclass = "sf")
st_crs(coastlines) <- "EPSG:4326"
# create arctic(ish) bounding box
arcticBox <- data.frame(id="A",
lon = c(-180,180,180,-180,-180),
lat = c(90,90,50,50,90))
otherBox <- data.frame(id="B",
lon = c(-180,180,180,-180,-180),
lat = c(50,50,-90,-90,50))
boxes <- bind_rows(arcticBox,otherBox)
boxesSF <- st_as_sf(boxes,coords=c("lon","lat"),crs="EPSG:4326") %>%
group_by(id) %>% summarise(geometry = st_combine(geometry)) %>%
st_cast("POLYGON")
p1 <- ggplot() +
geom_sf(data=coastlines,color="blue",size=0.25) +
geom_sf(data=boxesSF,mapping = aes(fill=id),color=NA) +
scale_fill_manual(values=alpha(colour=c("blue", "grey"),
alpha = c(0,1))) +
coord_sf(ylim = c(0,90)) +
theme(legend.position = "none")
p1
p1 + coord_sf(crs = sf::st_crs("ESRI:102016"))
Where did the masking polygons go? Something weird with having -180 to 180 meeting?
I want to produce a 2d-density plot based on spatial point data. In the background I want to show an open map (e.g. stamen terrain). Besides I want to plot the borders of Austria. Both datasets (data points and border) are shapefiles in EPSG 4326.
I managed to produce such a plot (see screenshots and code V1 below), but the problem is that there is a shift between the map in the background on the one side and the plotted points and the borders of Austria on the other side, as you can see below.
2D-Density Plot V1 - full
2D-Density Plot V1 - detail
Here is the code (V1):
library(sf)
library(rgdal)
library(ggplot2)
library(ggmap)
# Read point data (EPSG: 4326)
sk <- st_read("points.shp")
# Read country border polygon (EPSG: 4326)
blogr4326 <- readOGR(<path>, <layer name>)
bl4326_df <- fortify(blogr4326)
# Austria box (extent)
# Longitude: 9.6 to 16.94504
# Latitude: 46.52694 to 48.81667
map <- get_map(c(left = +9.6, bottom = 46.52694, right = +16.90, top = 48.99), color = "color", crop = FALSE)
hm_sk <- ggmap(map, extent = "panel", maprange=FALSE, darken=0.0) +
geom_point(data = sk, aes(x=X_WGS84, y=Y_WGS84)) +
stat_density2d(data = sk, aes(x=X_WGS84, y=Y_WGS84, fill = ..density.., alpha=cut(..density..,breaks=c(-Inf,0.08,Inf))), contour = FALSE, bins=16, geom = 'raster', n=500) +
ggtitle("Schwarzkiefer 2016/2020") + xlab("X_WGS84") + ylab("X_WGS84") +
scale_fill_distiller(palette= "Spectral", direction=-1, limits = c(0.08, 8.50)) +
scale_alpha_manual(values=c(0,0.7), guide="none") +
geom_polygon(data=bl4326_df, aes(long, lat, group=group), color='black', fill='NA', inherit.aes = TRUE) +
coord_fixed(1.5)
hm_sk
I found out that the shift is caused by the fact that the map in the background is in the projection EPSG:3857 and my shapefiles are in the projection EPSG:4326, as explained in this post. So I projected my shapefiles to EPSG 3857 and inserted the provided code into my code, as you can see here (V2):
library(sf)
library(rgdal)
library(ggplot2)
library(ggmap)
# Read point data (EPSG: 3857)
sk <- st_read("points.shp")
# Read country border polygon (EPSG: 3857)
blogr3857 <- readOGR(<path>, <layer name>)
bl3857_df <- fortify(blogr3857)
# Austria box (extent)
# Longitude: 9.6 to 16.94504
# Latitude: 46.52694 to 48.81667
map <- get_map(c(left = +9.6, bottom = 46.52694, right = +16.90, top = 48.99), color = "color", crop = FALSE)
#-------------------------------------------------------------------------------------------------
# Following code according to this link to avoid the shift between map and country border polygon:
# https://stackoverflow.com/questions/47749078/how-to-put-a-geom-sf-produced-map-on-top-of-a-ggmap-produced-raster
# Define a function to fix the bbox to be in EPSG:3857
ggmap_bbox <- function(map) {
if (!inherits(map, "ggmap")) stop("map must be a ggmap object")
# Extract the bounding box (in lat/lon) from the ggmap to a numeric vector,
# and set the names to what sf::st_bbox expects:
map_bbox <- setNames(unlist(attr(map, "bb")),
c("ymin", "xmin", "ymax", "xmax"))
# Convert the bbox to an sf polygon, transform it to 3857,
# and convert back to a bbox (convoluted, but it works)
bbox_3857 <- st_bbox(st_transform(st_as_sfc(st_bbox(map_bbox, crs = 4326)), 3857))
# Overwrite the bbox of the ggmap object with the transformed coordinates
attr(map, "bb")$ll.lat <- bbox_3857["ymin"]
attr(map, "bb")$ll.lon <- bbox_3857["xmin"]
attr(map, "bb")$ur.lat <- bbox_3857["ymax"]
attr(map, "bb")$ur.lon <- bbox_3857["xmax"]
map
}
# Use the function:
map <- ggmap_bbox(map)
#-------------------------------------------------------------------------------------------------
hm_sk <- ggmap(map,extent = "device", maprange=FALSE) +#, extent = "panel", maprange=FALSE, darken=0.0) +
coord_sf(crs = st_crs(3857)) + # f867orce the ggplot2 map to be in 3857
geom_point(data = sk, aes(x=X_PM, y=Y_PM)) +
stat_density2d(data = sk, aes(x=X_PM, y=X_PM, fill = ..density.., alpha=cut(..density..,breaks=c(-Inf,0.08,Inf))), contour = FALSE, bins=16, geom = 'raster', n=500) +
scale_fill_distiller(palette= "Spectral", direction=-1, limits = c(0.08, 8.50)) +
scale_alpha_manual(values=c(0,0.7), guide="none") +
geom_polygon(data=bl3857_df, aes(long, lat, group=group), color='black', fill='NA', inherit.aes = FALSE) +
ggtitle("Schwarzkiefer 2016/2020") + xlab("X_3857") + ylab("X_3857")
hm_sk
Now, the problem with the shift is solved, but the density plot is not visible anymore (only map, points and borders are plotted), as you can see here:
2D-Density Plot V2 - full
2D-Density Plot V2 - detail
Any suggestions, how I can produce a plot that is proper aligned AND includes the density plot? Thanks a lot in advance!
What I have:
points in the arctic and antarctic
raster data from various geophysical entities in arctic and antarctic
What I want:
A map in stereographic or any other polar projection with background map or coastlines, cropped to the extent of the points. In other words: A map like above with base map of my own choice.
What I did so far:
I loaded all the data (including land surface data from naturalearthdata; see MWE), projected them into stereographic and plotted that. The result including the polygon data looks then like this:
My MWE:
library(raster)
library(sf)
library(ggplot2)
library(rgdal)
# file load ---------------------------------------------------------------
# sea ice raster data
if (!file.exists("seaiceraster.tif")) {
url = "https://seaice.uni-bremen.de/data/smos/tif/20100514_hvnorth_rfi_l1c.tif"
download.file(url, destfile = 'seaiceraster.tif')
}
si.raster = raster::raster('seaiceraster.tif')
# land surface shapefile
if (!file.exists("110m-admin-0-countries")) {
url_land = "https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/physical/ne_10m_land.zip"
download.file(url_land, destfile = "110m-admin-0-countries")
unzip("110m-admin-0-countries")
}
world_shp = rgdal::readOGR("ne_10m_land.shp")
# points
p.data = structure(
list(
Lat = c(
73.0114126168676,70.325555278764,77.467797903163,
58.6423827457304,66.3616310851294,59.2097857474643,
75.3135274436283,60.1983078512275,72.6614399747201,
61.1566678672946,73.0822309615673,55.7759666826898,
75.1651656433833,69.0130753414173,62.3288262448589
),
Lon = c(
-59.9175490701543,-80.1900239630732,-40.4609968914928,
-61.0914448815381,-60.0703668488408,-21.027205418284,
-100.200463810276,-74.861777073788,-55.1093773178206,
-29.4108649230234,-64.5878251008461,-36.5343322019187,
-31.647365623387,-67.466355105829,-64.1162329769077
)
),
row.names = c(
1911L, 592L,2110L,3552L,3426L,1524L,635L,4668L,
3945L,2848L,3609L,36L,4262L,3967L,2725L
),
class = "data.frame"
)
p = sf::st_as_sf(p.data, coords = c("Lon", "Lat"),
crs = "+init=epsg:4326")
# project -----------------------------------------------------------------
polar.crs = CRS("+init=epsg:3995")
si.raster.proj = projectRaster(si.raster, crs = polar.crs)
world_shp.proj = sp::spTransform(world_shp, polar.crs)
p.proj = sf::st_transform(p, polar.crs)
# preparation -------------------------------------------------------------
AG = ggplot2::fortify(world_shp.proj)
# make raster to data.frame
si.raster.df = si.raster.proj %>%
raster::crop(., p.proj) %>%
raster::rasterToPoints(., spatial = TRUE) %>%
as.data.frame(.)
colnames(si.raster.df) = c("val", "x", "y")
# plot --------------------------------------------------------------------
ggplot() +
# geom_polygon(data = AG, aes(long, lat, group = group)) + # un-comment to see
geom_raster(data = si.raster.df, aes(x = x, y = y, fill = val)) +
geom_sf(data = p.proj, color = "green", size = 3)
I've changed the workflow in your example a bit to add the stars package for the sea ice data, but I think it should get you what you're looking for. You'll need to adjust the crop size to expand it a little, as the points p are right on the edge of the plotted area. st_buffer might help with that.
I used the crs from the seaicebuffer.tif file for all of the objects.
The .tif file has a crs that I'm not able to easily transform on my computer. It seems to be able to use meters as a lengthunit and might be a polar stereographic (variant B) projection. The points & world data don't seem to have a problem transforming to it though, which is why I've used it throughout.
library(raster)
library(sf)
library(ggplot2)
library(rgdal)
library(stars)
si <- stars::read_stars('seaiceraster.tif')
world_sf = rgdal::readOGR("ne_10m_land.shp") %>%
st_as_sf() %>%
st_transform(st_crs(si))
# p <- ... same as example and then:
p <- st_transform(p, st_crs(si))
# get a bounding box for the points to crop si & world.
p_bbox <- st_bbox(p) %>%
st_as_sfc() %>%
st_as_sf() %>%
st_buffer(100000)
# crop si & world_sf to an area around the points (p)
world_cropped <- st_crop(world_sf, p_bbox)
si_cropped <- st_crop(si, p_bbox)
#Plot
ggplot() +
geom_sf(data = world_cropped,
color = 'black',
fill = 'NA',
size = .2) +
geom_stars(data = si_cropped) +
geom_sf(data = p, color = 'red') +
scale_fill_continuous(na.value = 0)
Ugly hack for the southern .tif that stars reads as factors:
si <- stars::read_stars('20150324_hvsouth_rfi_l1c.tif', NA_value = 0 )
si$"20150324_hvsouth_rfi_l1c.tif" <- as.numeric(si$"20150324_hvsouth_rfi_l1c.tif")
ggplot() + geom_stars(data = si)
I'm looking to create some proximity maps using R, which show how far areas are from certain points. I can't find any examples in R code, but I've found an output which is the sort of thing I want:
It doesn't necessarily have to have all the labelling/internal boundaries wizardry, but I'd like it to stop at the sea border (thinking of using the rgeos function gintersection - see here).
I've tried doing a density plot as 'heatmaps' (this would be a pretty good solution/alternative) and putting a shapefile over the top (following this suggestion, but they're not lining up and I can't do a gintersection, probably because there's not a coordinate system attached to the density plot.
I used your question to play a little with new libraries...
Get a UK map and define random points
library(raster)
library(sf)
library(ggplot2)
library(dplyr)
library(tidyr)
library(forcats)
library(purrr)
# Get UK map
GBR <- getData(name = "GADM", country = "GBR", level = 1)
GBR_sf <- st_as_sf(GBR)
# Define 3 points on the UK map
pts <- matrix(c(-0.4966766, -2.0772529, -3.8437793,
51.91829, 52.86147, 56.73899), ncol = 2)
# Project in mercator to allow buffer with distances
pts_sf <- st_sfc(st_multipoint(pts), crs = 4326) %>%
st_sf() %>%
st_transform(27700)
ggplot() +
geom_sf(data = GBR_sf) +
geom_sf(data = pts_sf, colour = "red")
Calculate buffer areas
We create a list of multipolygons for each buffer distance. The point dataset must be in projected coordinates (here mercator) as buffer distance is in the scale of the coordinates system.
# Define distances to buffer
dists <- seq(5000, 150000, length.out = 5)
# Create buffer areas with each distances
pts_buf <- purrr::map(dists, ~st_buffer(pts_sf, .)) %>%
do.call("rbind", .) %>%
st_cast() %>%
mutate(
distmax = dists,
dist = glue::glue("<{dists/1000} km"))
# Plot: alpha allows to see overlapping polygons
ggplot() +
geom_sf(data = GBR_sf) +
geom_sf(data = pts_buf, fill = "red",
colour = NA, alpha = 0.1)
Remove overlapping
Buffer areas are overlapping. On the figure above, the more intense red color is due to multiple overlapping layers of transparent red. Let's remove the overlapping. We need to remove from larger areas, the buffer with the lower size. I then need to add again the smallest area to the list.
# Remove part of polygons overlapping smaller buffer
pts_holes <- purrr::map2(tail(1:nrow(pts_buf),-1),
head(1:nrow(pts_buf),-1),
~st_difference(pts_buf[.x,], pts_buf[.y,])) %>%
do.call("rbind", .) %>%
st_cast() %>%
select(-distmax.1, -dist.1)
# Add smallest polygon
pts_holes_tot <- pts_holes %>%
rbind(filter(pts_buf, distmax == min(dists))) %>%
arrange(distmax) %>%
mutate(dist = forcats::fct_reorder(dist, distmax))
# Plot and define color according to dist
ggplot() +
geom_sf(data = GBR_sf) +
geom_sf(data = pts_holes_tot,
aes(fill = dist),
colour = NA) +
scale_fill_brewer(direction = 2)
Remove areas in the sea
If you want to find proximity area on terrestrial parts only, we need to remove buffer areas that are in the sea. Intersection is computed between multipolygons with the same projection. I previously realize an union of the UK map.
# Remove part of polygons in the sea
# Union and projection of UK map
GBR_sf_merc <- st_transform(st_union(GBR_sf), 27700)
pts_holes_uk <- st_intersection(pts_holes_tot,
GBR_sf_merc)
ggplot() +
geom_sf(data = GBR_sf) +
geom_sf(data = pts_holes_uk,
aes(fill = dist),
colour = NA) +
scale_fill_brewer(direction = 2)
And here is the final proximity map using sf, ggplot2 and a few other libraries...
Based on Sébastien's example, a more old-fashioned approach:
library(raster)
GBR <- getData(name = "GADM", country = "GBR", level = 1)
pts <- matrix(c(-0.4966766, -2.0772529, -3.8437793, 51.91829, 52.86147, 56.73899), ncol = 2)
r <- raster(GBR, res=1/12)
d <- distanceFromPoints(r, pts)
m <- mask(d, GBR)
plot(m)