I have a dataframe with 2 pairs of UTM (32N) coordinates and I need to compute the differences in km between each of them, from origin to destination.
I'm trying with sf library, using "by_element" but I obtain an error message "Error in st_distance(data, by_element = TRUE) : !missing_y non รจ TRUE".
What's wrong?
If I use it without the "by_element" option, it works and the distance matrix between all coordinates is created, but this is not what I need.
library(sf)
df <- data.frame(id = c(1,2,3), x_origin = c(642683.2, 373775,383881 ), y_origin = c(5082920, 4997274,4994504), x_dest =c(642683.3, 1126050,942763.9 ), y_dest=c(5082920, 4374481,4534235 ))
data <- st_as_sf(df, coords = c("x_origin", "y_origin"), crs="4326" )
distances <- st_distance(data, by_element = TRUE )
You have provided x (origin) to st_distance()but no y (destination).
And that CRS can't be right, sf doesn't recognise EPSG code if it's a string and 4326 would suggest coordinates are in WGS84 lat/long. Assuming 32632, WGS 84 / UTM zone 32N.
library(sf)
df <- data.frame(id = c(1,2,3), x_origin = c(642683.2, 373775,383881 ), y_origin = c(5082920, 4997274,4994504), x_dest =c(642683.3, 1126050,942763.9 ), y_dest=c(5082920, 4374481,4534235 ))
origin <- st_as_sf(df, coords = c("x_origin", "y_origin"), crs=32632 )
dest <- st_as_sf(df, coords = c("x_dest", "y_dest"), crs=32632 )
(distances <- st_distance(origin, dest, by_element = TRUE ))
#> Units: [m]
#> 1 2 3
#> 0.1 976621.1 724015.0
Created on 2023-01-25 with reprex v2.0.2
First, sf can't know that you are calculating distances between origin and destination by the way you have input the data.
Second, the EPSG code for UTM Zone 32N is not 4326.
Third, you should have used crs = st_crs(4326) instead of crs = "4326".
Use the following piece of code to create the objects needed to calculate the distances you are interested in
library(sf)
df <- data.frame(id = c(1, 2, 3),
x_origin = c(642683.2, 373775, 383881),
y_origin = c(5082920, 4997274, 4994504),
x_dest = c(642683.3, 1126050, 942763.9),
y_dest = c(5082920, 4374481, 4534235))
origin <- st_as_sf(df, coords = c("x_origin", "y_origin"),
crs = st_crs(32632))
dest <- st_as_sf(df, coords = c("x_dest", "y_dest"),
crs = st_crs(32632))
Note the different EPSG code for the CRS.
Next, we calculate the distances (the default of this projection is in meters)
distances <- st_distance(origin, dest, by_element = TRUE)
If you use by_element = FALSE, you get all the pairwise distances.
Lastly, we can use the package units to convert the distances to km (or we can simply divide them by 1000).
units::set_units(distances, "km")
> Units: [km]
> [1] 0.0001 976.6211 724.0150
Related
I'm trying to create and visualize buffers around point locations with the sf package in R. An initial attempt looked like this:
library(sf)
library(dplyr)
library(mapview)
sf_use_s2(TRUE)
coord <- c(178.4, -80.1)
point <- st_sfc(st_point(coord), crs = 4326)
buffer <- st_buffer(point, 2000000, max_cells = 10000)
buffer %>%
st_wrap_dateline(options = c("WRAPDATELINE=YES", "DATELINEOFFSET=180")) %>%
mapview() + mapview(point)
I was able to fix this using st_shift_longitude() (sort of, latitude doesn't stretch to -90):
buffer %>%
st_shift_longitude() %>%
st_wrap_dateline(options = c("WRAPDATELINE=YES", "DATELINEOFFSET=180")) %>%
mapview() + mapview(point)
However, this approach fails for other points:
coord <- c(78.4, -80.1)
point <- st_sfc(st_point(coord), crs = 4326)
buffer <- st_buffer(point, 2000000, max_cells = 10000)
buffer %>%
st_shift_longitude() %>%
st_wrap_dateline(options = c("WRAPDATELINE=YES", "DATELINEOFFSET=180")) %>%
mapview() + mapview(point)
Is there a surefire way to produce buffers like this?
If your coordinates and polygons are near the poles as above, mapview's default projections (4326 I think 3857 web mercator) probably won't work well. You can use other projections (with the native.crs = T argument), but you'll have to supply polygon data for the landmass as well. The default 'background' of Earth's landmasses won't automatically appear.
Below I've used the crs and antarctic polygon from a github issue thread found here: https://github.com/r-spatial/mapview/issues/298. You might be able to find some other tips in the thread as well.
library(mapview)
library(sf)
library(leaflet)
library(dplyr)
#Loading data
# steal the crs & antarctic polygon (SFPoly2) for the points
load(url("https://github.com/elgabbas/Misc/blob/master/Data.RData?raw=true"))
# Your data
coord <- c(178.4, -80.1)
point <- st_sfc(st_point(coord), crs = 4326)
# Transform to use the polar crs
point <- st_transform(point, st_crs(SFPoint))
buffer <- st_buffer(point, 2000000, max_cells = 10000)
# Use mapview with the 'native.crs = T' argument
# There will be many warnings about old-style crs & not using long-lat data
mapview(SFPoly2, native.crs = T) + # Antarctic landmass
mapview(point, fill = 'red', native.crs = T) +
mapview(buffer, native.crs = T)
There are some other posts out there related to this one, such as these: Post 1, Post 2, Post 3. However, none of them deliver what I am hoping for. What I want is to be able to draw a line segment from a specific point (a sampling location) to the edge of a polygon fully surrounding that point (a lake border) in a specific direction ("due south" aka downward). I then want to measure the length of that line segment in between the sampling point and the polygon edge (really, it's only the distance I want, so if we can get the distance without drawing the line segment, so much the better!). Unfortunately, it doesn't seem like functionality to do this already exists within the sf package: See closed issue here.
I suspect, though, that this is possible through a modification of the solution offered here: See copy-pasted code below, modified by me. However, I am pretty lousy with the tools in sf--I got as far as making line segments that just go from the points themselves to the southern extent of the polygon, intersecting the polygon at some point:
library(sf)
library(dplyr)
df = data.frame(
lon = c(119.4, 119.4, 119.4, 119.5, 119.5),
lat = c(-5.192,-5.192,-5.167,-5.167,-5.191)
)
polygon <- df %>%
st_as_sf(coords = c("lon", "lat"), crs = 4326) %>%
summarise(geometry = st_combine(geometry)) %>%
st_cast("POLYGON")
plot(polygon)
df2 <- data.frame(lon = c(119.45, 119.49, 119.47),
lat = c(-5.172,-5.190,-5.183))
points <- df2 %>%
st_as_sf(coords = c("lon", "lat"), crs = 4326) %>%
summarise(geometry = st_combine(geometry)) %>%
st_cast("MULTIPOINT")
plot(points, add = TRUE, col = "red")
# Solution via a loop
xmin <- min(df$lat)
m = list()
# Iterate and create lines
for (i in 1:3) {
m[[i]] = st_linestring(matrix(
c(df2[i, "lon"],
df2[i, "lat"],
df2[i, "lon"],
xmin),
nrow = 2,
byrow = TRUE
))
}
test = st_multilinestring(m)
# Result is line MULTILINESTRING object
plot(test, col = "green", add = TRUE)
But now I can't figure out how to use st_intersection or any such function to figure out where the intersection points are. Most of the trouble lies, I think, in the fact that what I'm creating is not an sf object, and I can't figure out how to get it to be one. I assume that, if I could figure out where the segments intersect the polygon (or the most-northern time they do so, ideally), I could somehow measure from the intersection points to the sampling points using a function like st_distance. Since lake polygons are often really complex, though, it's possible a segment will intersect the polygon multiple times (such as if there is a peninsula south of a given point), in which case I figure I can find the "furthest north" intersection point for each sampling point and use that or else take the minimum such distance for each sampling point.
Anyhow, if someone can show me the couple of steps I'm missing, that'd be great! I feel like I'm so close and yet so far...
Consider this approach, loosely inspired by my earlier post about lines from points
To make it more reproducible I am using the well known & much loved North Carolina shapefile that ships with {sf} and a data frame of three semi-random NC cities.
What the code does is:
iterates via for cycle over the dataframe of cities
creates a line starting in each city ("observation") and ending on South Pole
intersects the line with dissolved North Carolina
blasts the intersection to individual linestrings
selects the linestring that passes within 1 meter of origin
calculates the lenght via sf::st_lenghth()
saves the the result as a {sf} data frame called res (short for result :)
I have included the actual line in the final object to make the result more clear, but you can choose to omit it.
library(sf)
library(dplyr)
library(ggplot2)
shape <- st_read(system.file("shape/nc.shp", package="sf")) %>% # included with sf package
summarise() %>%
st_transform(4326) # to align CRS with cities
cities <- data.frame(name = c("Raleigh", "Greensboro", "Plymouth"),
x = c(-78.633333, -79.819444, -76.747778),
y = c(35.766667, 36.08, 35.859722)) %>%
st_as_sf(coords = c("x", "y"), crs = 4326)
# a quick overview
ggplot() +
geom_sf(data = shape) + # polygon of North Carolina
geom_sf(data = cities, color = "red") # 3 cities
# now here's the action!!!
for (i in seq_along(cities$name)) {
# create a working linestring object
wrk_line <- st_coordinates(cities[i, ]) %>%
rbind(c(0, -90)) %>%
st_linestring() %>%
st_sfc(crs = 4326) %>%
st_intersection(shape) %>%
st_cast("LINESTRING") # separate individual segments of multilines
first_segment <- unlist(st_is_within_distance(cities[i, ], wrk_line, dist = 1))
# a single observation
line_data <- data.frame(
name = cities$name[i],
length = st_length(wrk_line[first_segment]),
geometry = wrk_line[first_segment]
)
# bind results rows to a single object
if (i == 1) {
res <- line_data
} else {
res <- dplyr::bind_rows(res, line_data)
} # /if - saving results
} # /for
# finalize results
res <- sf::st_as_sf(res, crs = 4326)
# result object
res
# Simple feature collection with 3 features and 2 fields
# Geometry type: LINESTRING
# Dimension: XY
# Bounding box: xmin: -79.81944 ymin: 33.92945 xmax: -76.74778 ymax: 36.08
# Geodetic CRS: WGS 84
# name length geometry
# 1 Raleigh 204289.21 [m] LINESTRING (-78.63333 35.76...
# 2 Greensboro 141552.67 [m] LINESTRING (-79.81944 36.08...
# 3 Plymouth 48114.32 [m] LINESTRING (-76.74778 35.85...
# a quick overview of the lines
ggplot() +
geom_sf(data = shape) + # polygon of North Carolina
geom_sf(data = res, color = "red") # 3 lines
I try to create a new coordinates point away from 250NM from a known point. I want to keep the trajectory from my starting point and a known point. How could I use this information in order to create a new point, with a known distance :
# starting point
lat_0 = 4.842816
lon_0 = 7.017196
#known point
lat_1 = 4.108957
lon_1 = 8.099835
# this point is 78NM away from the starting point
I'm using R but I could translate a mathematical formula without any problems :).
Thus, I want to create a new point 250NM away, keeping this trajectory
library(sf)
library(mapview)
library(dplyr)
library(geosphere)
# test: what are we working with here?
test_df <- data.frame(point = 0:1, lon = c(lon_0, lon_1), lat = c(lat_0, lat_1))
test_df %>% sf::st_as_sf(coords = c("lon", "lat"), crs = 4326) %>% mapview::mapview()
# initialise points
point0 <- c(lon_0, lat_0)
point1 <- c(lon_1, lat_1)
#calculate bearing 0 >> 1
bearing0_1 <- geosphere::bearing(point0, point1)
#[1] 123.9916
# Calculate new point with calulated bearing ans distance
# 250 MN = 463000.2 metres
point2 <- as.vector(geosphere::destPoint(p = point0, b = bearing0_1, d = 463000.2))
# test output
rbind(point0, point1, point2) %>% as.data.frame(col.names = c("lon", "lat")) %>%
dplyr::mutate(point = 0:2) %>%
sf::st_as_sf(coords = c(1, 2), crs = 4326) %>% mapview::mapview()
I defined a polygon:
library(rgee)
ee_Initialize()
polygon <- ee$Geometry$Polygon(
list(
c(91.17, -13.42),
c(154.10, -13.42),
c(154.10, 21.27),
c(91.17, 21.27),
c(91.17, -13.42)
))
Map$addLayer(polygon)
The polygon covers countries around south-east Asia
For each pixel in the polygon, I want to calculate monthly sum of a given band for a given year as follows:
month_vec <- 1:12
pr_ls <- list()
for(m in seq_along(month_vec)){
month_ref <- month_vec[m]
pr_ls[[m]] <-
ee$ImageCollection("NASA/NEX-GDDP")$
filterBounds(polygon)$ # filter it by polygon
select('pr')$ # select rainfall
filter(ee$Filter$calendarRange(2000, 2000, "year"))$ # filter the year
filter(ee$Filter$calendarRange(month_ref, month_ref, "month"))$ # filter the month
filter(ee$Filter$eq("model","ACCESS1-0"))$ # filter the model
sum() # sum the rainfall
}
Imagecollection_pr <- ee$ImageCollection(pr_ls)
ee_imagecollection_to_local(
ic = Imagecollection_pr,
region = polygon,
dsn = paste0('pr_')
)
Reading a single month's file
my_rast <- raster(list.files(pattern = '.tif', full.names = TRUE)[1])
Since this raster covers southeast asian countries, I downloaded the shapefile
sea_shp <- getData('GADM', country = c('IDN','MYS','SGP','BRN','PHL'), level = 0)
Plotting them on top of each other:
plot(my_rast)
plot(sea_shp, add = T)
There is a misalignment and I am not sure if it is the right raster that has been
processed for the given polygon. I also checked if their projection is same
crs(my_rast)
CRS arguments: +proj=longlat +datum=WGS84 +no_defs
crs(sea_shp)
CRS arguments: +proj=longlat +datum=WGS84 +no_defs
Both of them have the same projection as well. I cannot figure out what went wrong?
EDIT
As suggested in comments, I defined a new polygon covering Australia as follows:
polygon <- ee$Geometry$Polygon(
list(
c(88.75,-45.26),
c(162.58,-45.26),
c(162.58,8.67),
c(88.75,8.67),
c(88.75,-45.26)
)
)
Map$addLayer(polygon)
and repeated the above code. Plotting the raster again for the month of March on polygon gives me this:
Does anyone know if I can check if my raster is reversed w.r.t to polygon boundaries?
This seems to be related to rgdal rather than to the raster package. Some raster downloaded from GEE have data flipped with respect to y. I solved this problem, as follow:
library(rgee)
library(raster)
ee_Initialize()
polygon <- ee$Geometry$Polygon(
list(
c(91.17, -13.42),
c(154.10, -13.42),
c(154.10, 21.27),
c(91.17, 21.27),
c(91.17, -13.42)
))
month_vec <- 1:12
pr_ls <- list()
for(m in seq_along(month_vec)){
month_ref <- month_vec[m]
pr_ls[[m]] <-
ee$ImageCollection("NASA/NEX-GDDP")$
filterBounds(polygon)$ # filter it by polygon
select('pr')$ # select rainfall
filter(ee$Filter$calendarRange(2000, 2000, "year"))$ # filter the year
filter(ee$Filter$calendarRange(month_ref, month_ref, "month"))$ # filter the month
filter(ee$Filter$eq("model","ACCESS1-0"))$ # filter the model
sum() # sum the rainfall
}
Imagecollection_pr <- ee$ImageCollection(pr_ls) %>% ee_get(0)
exp1 <- ee_imagecollection_to_local(
ic = Imagecollection_pr,
region = polygon,
dsn = "pp_via_drive",
via = "drive" # please always use "drive" or "gcs" until rgee 1.0.6 release
)
# One option
gdalinfo <- try (rgdal::GDALinfo(exp1))
if (isTRUE(attr(gdalinfo, "ysign") == 1)) {
exp1_r <- flip(raster(exp1), direction='y')
}
Recent versions of the earthengine Python API causes some inconsistencies when via = "getInfo" is used, please always use via = "drive" until the release of rgee 1.0.6.
There does not seem to be a misalignment. To plot all these countries in one step, you could do
x <- lapply(c('IDN','MYS','SGP','BRN','PHL'), function(i) getData('GADM', country = i, level = 0))
sea_shp <- bind(x)
I have WRF output netCDF files with 149974991 dimensions produced with "Mercator" projection over the Horn Of Africa. I would like to convert netCDF files into raster stack to undertake further analysis. I have been trying different options but it didn't work for me. I am getting values on wrong locations. I require help in this regards and any help is much appreciated.
Here is the code :
ro_rast <- nc_open("wrf_CAM0_daily_pre.nc")
pre <- ncvar_get(ro_rast, "pre") ro_rast$dim$lon$vals -> lon ro_rast$dim$lat$vals -> lat ro_rast$dim$ncl2$vals -> time rm(ro_rast)
r1_brick <- brick(pre, xmn=min(lat), xmx=max(lat), ymn=min(lon), ymx=max(lon), crs=CRS("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs+ towgs84=0,0,0"))
names(r1_brick)<- seq(as.Date('2018-06-01'), as.Date('2018-08-31'), 'days')
# convert names of layer into date par(mar = c(2, 2, 2, 2))
cam1_mean <- t(calc(r1_brick, sum))
# seasonal sum precipitation
cam1 <- flip(cam1_mean, direction = 2)
library(akima)# intepolation
lonlat_reg <- expand.grid(lon = seq(min(lon), max(lon), length.out = 1499),
lat = seq(min(lat), max(lat), length.out = 749))
test <- interp(x = as.vector(lon), y = as.vector(lat), z = as.vector(pre),
xo = unique(lonlat_reg[,"lon"]), yo = unique(lonlat_reg[,"lat"]),
duplicate = "error", linear = FALSE, extrap = FALSE)
test <- interp(x = as.vector(lon), y = as.vector(lat), z = as.vector(pre),
nx = 1499, ny = 749, linear = FALSE, extrap = FALSE)
# turn into a raster
test_ras <- raster(test)
The standard approach would be
library(raster)
b <- brick("wrf_CAM0_daily_pre.nc")
It that does not work, can you point us to the file you are using?
I get this error message (you should have added that to your question).
Error in .rasterObjectFromCDF(x, type = objecttype, band = band, ...) :
cells are not equally spaced; you should extract values as points
I checked the file, and in this case, the raster is not a regular grid. The size of the cells changes with latitude. The file does not provide the x and y values of the coordinate reference system used. So the best you can do is extract these values as points, as you were doing, using the interface of the ncdf4 or another package. You can then not directly make a RasterBrick. But you do so using rasterize or interpolate.