How to convert given latitude and longitude to distance travel in KM - r

My project has given latitudes and longitudes for each travel route and I would like to know the function to covert it to distance travel in KM.
column name are: start_lat, start_lng, end_lat, end_lng
start_lat: 42.08000
start_lng: -87.68000
end_lat: 42.07000
end_lng: -87.69000
Is distGeo the right choice?
If you could explain, it'd be much appreciated
TIA

use the geosphere, of the sf-package
# put points in a named vector
start <- c(lon = -87.68000, lat = 42.08000)
end <- c(lon = -87.69000, lat = 42.07000)
#geosphere solution
library(geosphere)
geosphere::distGeo(start, end)
# [1] 1385.125
# sf solution
library(sf)
rbind(start, end) %>%
as.data.frame() %>%
st_as_sf(coords = c("lon", "lat"), crs = 4326) %>%
st_distance()
# Units: [m]
# [,1] [,2]
# [1,] 0.000 1384.797
# [2,] 1384.797 0.000

Related

Finding the region (district, state, etc.) of a given coordinate in GADM data (in R)

We are using the GADM data for France, and I'm curious if we can find the region of a point based on its latitude and longitude.
So I can plot a map of France using
library(raster)
fr = getData("GADM", country="FRA", level=1)
plot(fr)
The coordinates of a point in Paris is
latitude = 48.86122650866868
longitude = 2.341541835915652
Now when I look up different regions at level 1, I get
> fr$NAME_1
[1] "Auvergne-Rhône-Alpes" "Bourgogne-Franche-Comté" "Bretagne" "Centre-Val de Loire"
[5] "Corse" "Grand Est" "Hauts-de-France" "Île-de-France"
[9] "Normandie" "Nouvelle-Aquitaine" "Occitanie" "Pays de la Loire"
[13] "Provence-Alpes-Côte d'Azur"
Is it possible to extract the region of that coordinate, which should be Île-de-France?
with package {sf}:
library(sf)
my_point <- st_point(c(2.3415, 48.86122))
## convert fr to class sf
fr <- st_as_sf(fr)
i <- st_within(my_point, fr)
fr[as.integer(i),]$NAME_1
output:
[1] "Île-de-France"
edit to check, map point and provinces with labels:
library(ggplot2)
## set coordinate reference system for my_point
## to EPSG 4326 (WGS84):
my_point <- st_sfc(my_point, crs = 4326)
fr %>%
ggplot(mapping = aes(geometry = geometry)) +
geom_sf() +
geom_sf_label(aes(label = NAME_1)) +
geom_sf(my_point, mapping = aes(geometry = geometry))
It is better to use geodata::gadm. Then you would use "terra" instead of "raster" and do:
library(terra)
library(geodata)
fr = gadm("France", level=1)
latitude = 48.86122650866868
longitude = 2.341541835915652
v <- vect(cbind(longitude, latitude))
extract( fr[,"NAME_1"], v)
extract( fr[,"NAME_1"], v)
# id.y NAME_1
#1 1 Île-de-France

Why can I not get worldclim data (avg temp and prec) for the state which I live at all using R?

I'd like to get the average temperature and precipitation for the state of Ceara in Brazil; I made a cropped area on the map as:
and I used the center lat/lon as lat=-5.49839 and lon=-39.32062 I got the caps of latitude and longitude as latinicial=-7.24614 (minimum latitude), latfinal=-3.76140 (maximum latitude), longinicial=-40.38084 (minimum longitude) and longfinal=-38.77385 (maximum longitude) then I've simulated a uniformly distributed temperature for both latitude and longitude which lies in their maxima and minima.
My code is given as follows:
library(raster)
library(sp)
library(rgeos)
library(rgdal)
library(dismo)
library(rgdal)
library(sf)
d=getData('worldclim',lat=-5.49839,lon=-39.32062,res=0.5,var='bio')
latinicial=-7.24614
latfinal=-3.76140
longinicial=-40.38084
longfinal=-38.77385
latitude=runif(100,latinicial,latfinal)
longitude=runif(100,longinicial,longfinal)
coord=data.frame(latitude,longitude)
points = SpatialPoints(coord, proj4string = d#crs)
d <- d[[c(1,12)]]
names(d)=c("Temp","Prec")
extract(d,points)
But when I run it, I got NA values for all rows even though I'm showing you only 4 rows:
So, I'd like to know what happened to it. Why do I get NA values?
The problem is with the order of longitude and latitude in coords. When you put coords into SpatialPoints, it expects the order to be longitude then latitude, but you have it reversed. Once you fix that, then it will extract the data correctly. All the code above coord works fine. Also, if you are going to run this code multiple times, then I would recommend using set.seed. This will allow you to get the same values every time when you run the runif statements.
library(raster)
library(sp)
set.seed(243)
d = getData(
'worldclim',
lat = -5.49839,
lon = -39.32062,
res = 0.5,
var = 'bio'
)
latinicial = -7.24614
latfinal = -3.76140
longinicial = -40.38084
longfinal = -38.77385
latitude = runif(100, latinicial, latfinal)
longitude = runif(100, longinicial, longfinal)
coord = data.frame(longitude, latitude)
points = SpatialPoints(coord, proj4string = d#crs)
d <- d[[c(1, 12)]]
names(d) = c("Temp", "Prec")
extract(d, coord)
Output
head()
Temp Prec
[1,] 239 655
[2,] 267 832
[3,] 256 541
[4,] 269 740
[5,] 242 784
[6,] 233 981

sf: Generate random points with maximal distance condition

I'd like to generate 100 random points but imposed a maximal distance around points using st_buffer() of size 1000 meters around each point, and eliminating any offending points. But, in my example:
library(sf)
# Data set creation
set.seed(1)
df <- data.frame(
gr = c(rep("a",5),rep("b",5)),
x = rnorm(10),
y = rnorm(10)
)
df <- st_as_sf(df,coords = c("x","y"),remove = F, crs = 4326)
df.laea = st_transform(df,
crs = "+proj=laea +x_0=4600000 +y_0=4600000 +lon_0=0.13 +lat_0=0.24 +datum=WGS84 +units=m")
st_bbox(df.laea)
#
# Random simulation of 100 point inside df.laea extent
sim_study_area <- st_sample(st_as_sfc(st_bbox(df.laea)), 100) %>% # random points, as a list ...
st_sf()
border_area <- st_as_sfc(st_bbox(df.laea))%>% # random points, as a list ...
st_sf()
# I'd like to imposed a maximal distance of 1000 meters around points and for this:
i <- 1 # iterator start
buffer_size <- 1000 # minimal distance to be enforced (in meters)
repeat( {
# create buffer around i-th point
buffer <- st_buffer(sim_study_area[i,], buffer_size)
offending <- sim_study_area %>% # start with the intersection of master points...
st_intersects(buffer, sparse = F) # ... and the buffer, as a vector
# i-th point is not really offending
offending[i] <- TRUE
# if there are any offending points left - re-assign the master points
sim_study_area <- sim_study_area[offending,]
if ( i >= nrow(sim_study_area)) {
# the end was reached; no more points to process
break
} else {
# rinse & repeat
i <- i + 1
}
} )
# Visualizantion of points create with the offending condition:
simulation_area <- ggplot() +
geom_sf(data = border_area, col = 'gray40', fill = NA, lwd = 1) +
geom_sf(data = sim_study_area, pch = 3, col = 'red', alpha = 0.67) +
theme_bw()
plot(simulation_area)
It's not OK result because a don't have 100 points and I don't know how I can fix it.
Please any ideas?
Thanks in advance,
Alexandre
I think that the easiest solution is to adopt one of the sampling functions defined in the R package spatstat. For example:
# packages
library(sf)
#> Linking to GEOS 3.9.0, GDAL 3.2.1, PROJ 7.2.1
# create data
set.seed(1)
df <- data.frame(
gr = c(rep("a",5),rep("b",5)),
x = rnorm(10),
y = rnorm(10)
)
df <- st_as_sf(df,coords = c("x","y"),remove = F, crs = 4326)
df.laea = st_transform(
df,
crs = "+proj=laea +x_0=4600000 +y_0=4600000 +lon_0=0.13 +lat_0=0.24 +datum=WGS84 +units=m"
)
Now we sample with a Simple Sequential Inhibition Process. Check ?spatstat.core::rSSI for more details.
sampled_points <- st_sample(
x = st_as_sfc(st_bbox(df.laea)),
type = "SSI",
r = 1000, # threshold distance (in metres)
n = 100 # number of points
)
# Check result
par(mar = rep(0, 4))
plot(st_as_sfc(st_bbox(df.laea)), reset = FALSE)
plot(sampled_points, add = TRUE, pch = 16)
# Estimate all distances
all_distances <- st_distance(sampled_points)
all_distances[1:5, 1:5]
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.00 57735.67 183205.74 189381.50 81079.79
#> [2,] 57735.67 0.00 153892.93 143755.73 61475.85
#> [3,] 183205.74 153892.93 0.00 62696.68 213379.39
#> [4,] 189381.50 143755.73 62696.68 0.00 194237.12
#> [5,] 81079.79 61475.85 213379.39 194237.12 0.00
# Check they are all greater than 1000
sum(all_distances < 1000)
#> [1] 100 # since the diagonal is full of 100 zeros
Created on 2021-08-12 by the reprex package (v2.0.0)
Check here (in particular the answer from Prof. Baddeley), the references therein, and the help page of st_sample for more details.

Find nearest distance from spatial point with direction specified

I would like to calculate the nearest distance from a spatial point to spatial lines (or polygons) for predetermined bearings (0,45,90,135,180,225,270,315).
The idea is to calculate an exposure index for a number of bays along a coastline. A simple example is provided below:
Create lines
library(sp)
coords<-structure(list(lon = c(-6.1468506, -3.7628174, -3.24646,
-3.9605713, -4.4549561, -4.7955322, -4.553833, -5.9710693, -6.1468506),
lat = c(53.884916, 54.807017, 53.46189, 53.363665, 53.507651, 53.363665, 53.126998, 53.298056,53.884916)), class = "data.frame", row.names = c(NA,-9L))
l<-Line(coords)
sl<-SpatialLines(list(Lines(list(l),ID="a")),proj4string=CRS("+init=epsg:4326"))
Create point
pt<-SpatialPoints(coords[5,]+0.02,proj4string=CRS("+init=epsg:4326"))
Plot
plot(sl)
plot(pt,add=T)
I'm having trouble finding examples of what the next step might be and need help.
Example of what distance I would like to calculate
You can use geosphere library to accomplish it. You'll need to add a CRS to your points though:
library(geosphere)
pt <- SpatialPoints(c[5,],
proj4string=CRS("+init=epsg:4326"))
And then use dist2Line function:
st_distance(st_cast(sl, "POINT"), pt)
# distance lon lat ID
#[1,] 2580.843 -4.451901 53.50677 1
Alternatively you can convert your polylines to points using sf package and then get a matrix of distances (you'll need to convert you objects to sfclass):
library(sf)
sl <- SpatialLines(list(Lines(list(l),ID="a")),
proj4string=CRS("+init=epsg:4326")) %>%
st_as_sf()
pt <- SpatialPoints(coords[5,]+0.02,
proj4string=CRS("+init=epsg:4326")) %>%
st_as_sf()
st_distance(st_cast(sl, "POINT"), pt)
#Units: [m]
# [,1]
# [1,] 119833.165
# [2,] 149014.814
# [3,] 79215.071
# [4,] 36422.390
# [5,] 2591.267
# [6,] 30117.701
# [7,] 45287.637
# [8,] 105289.230
# [9,] 119833.165
As a heads-up: I'm no hero when it comes to geo-data in R.
Also: I have not automated the calculation for all bearings, but manually performed operations to get the distance to intersect on de 45-bearing.
You will have to figure out the looping by yourself, as I do not have the time. Feel free to provide/post your final findings/code here when you are done.
Here is my crack at this problem, step-by-step.
#load libraries used
library(geosphere)
library(tidyverse)
library(sf)
#get bearings of lines of the polygon
df.poly <- coords %>%
mutate( lon_next = lead(lon), lat_next = lead(lat) ) %>%
mutate( bearing_to_next = ifelse( !is.na( lon_next ),
unlist( pmap( list( a = lon, b = lat, x = lon_next, y = lat_next ),
~ round( geosphere::bearing( c(..1, ..2), c(..3, ..4) ) )
)
),
NA )
) %>%
filter( !is.na( lon_next ) )
# lon lat bearing_to_next
# 1 -6.146851 53.88492 56
# 2 -3.762817 54.80702 167
# 3 -3.246460 53.46189 -103
# 4 -3.960571 53.36366 -64
# 5 -4.454956 53.50765 -125
# 6 -4.795532 53.36366 148
# 7 -4.553833 53.12700 -78
# 8 -5.971069 53.29806 -10
#find intersection point based on the intersection of two 'great circles'
#from two points with a bearing
gcIntersectBearing(
#coordinates 2nd point of polyline, with bearing to third point
c( -3.7628174, 54.807017 ), 167,
#coordinates point, with bearing of 45
c( -4.454956, 53.50765 ), 45 )
# lon lat lon lat
# [1,] -3.476074 54.07798 176.5239 -54.07798
let's see what we have got so far
p_intersect <- data.frame( lon = -3.476074, lat = 54.07798 ) %>%
st_as_sf( coords = c( "lon", "lat" ), crs = 4326 )
startpoint <- coords %>% slice(5) %>% mutate( lon = lon + 0.02, lat = lat + 0.02 ) %>%
st_as_sf( coords = c("lon","lat"), crs = 4326 )
poly <- coords %>%
as.matrix() %>%
list() %>%
st_polygon() %>%
st_sfc() %>%
st_set_crs( 4326 )
mapview::mapview( list(poly, startpoint, p_intersect) )
The location of the intersection point p_intersect on the polygon poly from the startpoint with a 45-degrees bearing looks correct.
Now you can calculate the distance as follows:
#calculate distance
st_distance( startpoint, p_intersect )
# Units: [m]
# [,1]
# [1,] 87993.3
Google Maps seems to agree on the distance (bit of a margin due to mouseclicking aroung the points, but looks ok to me)
Now you will have to figure out some clever looping/vectorisation and you are done :)
I have to get back to my real job.
Thankyou to #patL and #Wimpel, I've used your suggestions to come up with a solution to this problem.
First I create spatial lines of set distance and bearings from an origin point using destPoint::geosphere. I then use gIntersection::rgeos to obtain the spatial points where each transect intersects the coastline. Finally I calculate the distance from the origin point to all intersect points for each transect line respectively using gDistance::rgeos and subset the minimum value i.e. the nearest intersect.
load packages
pkgs=c("sp","rgeos","geosphere","rgdal") # list packages
lapply(pkgs,require,character.only=T) # load packages
create data
coastline
coords<-structure(list(lon =c(-6.1468506,-3.7628174,-3.24646,
-3.9605713,-4.4549561,-4.7955322,-4.553833,-5.9710693,-6.1468506),
lat=c(53.884916,54.807017,53.46189,53.363665,53.507651,53.363665,53.126998,53.298056,53.884916)), class = "data.frame", row.names = c(NA,-9L))
l=Line(coords)
sl=SpatialLines(list(Lines(list(l),ID="a")),proj4string=CRS("+init=epsg:4326"))
point
sp=SpatialPoints(coords[5,]+0.02,proj4string=CRS("+init=epsg:4326"))
p=coordinates(sp) # needed for destPoint::geosphere
create transect lines
b=seq(0,315,45) # list bearings
tr=list() # container for transect lines
for(i in 1:length(b)){
tr[[i]]<-SpatialLines(list(Lines(list(Line(list(rbind(p,destPoint(p=p,b=b[i],d=200000))))),ID="a")),proj4string=CRS("+init=epsg:4326")) # create spatial lines 200km to bearing i from origin
}
calculate distances
minDistance=list() # container for distances
for(j in 1:length(tr)){ # for transect i
intersects=gIntersection(sl,tr[[j]]) # intersect with coastline
minDistance[[j]]=min(distGeo(sp,intersects)) # calculate distances and use minimum
}
do.call(rbind,minDistance)
In reality the origin point is a spatial point data frame and this process is looped multiple times for a number of sites. There are also a number of NULL results when carry out the intersect so the loop includes an if statement.

circle around a geographic point with st_buffer

I would like to plot a circle 110 NM (nautical miles) around Dublin airport using sf package.
(Later on I will intersect via st_intersect that with flight position reports from ADS-B.)
I have defined a new unit for NM as follows:
library(units)
library(tidyverse)
library(sf)
NM <- make_unit("NM")
install_conversion_constant("NM", "km", 1.852)
Then defined Dublin airport coordinates:
# DUB/EIDW location, see
# https://skyvector.com/airport/EIDW/Dublin-Airport
# Coordinates:
# N53°25.28' / W6°16.20' (Degrees Decimal Minutes (DDM) format)
# (-6.27, 53.421333) (lon/lat Decimal Degrees (DD))
# Elevation: 242.0 feet (MSL)
dub_lon <- -6.27
dub_lat <- 53.421333
dub_elv <- set_units(242.0, ft)
dub <- st_point( x = c(dub_lon, dub_lat, dub_elv), dim = "XYZ")
dub <- dub %>% st_sfc(crs = 4326)
Hence defined the radius of the circle around the airport (in meters):
r110 <- set_units(110, NM) %>% set_units(km)
Now when I try st_buffer things are not working:
> r110 <- set_units(110, NM) %>% set_units(km)
Error: cannot convert km into °
In addition: Warning message:
In st_buffer.sfc(dub, dist = r110) :
st_buffer does not correctly buffer longitude/latitude data, dist needs to be in decimal degrees.
If I try to pass a numeric value (203.72, these are km) as distance at least I get only a warning:
> dub110 <- st_buffer(dub, dist = 203.72)
Warning message:
In st_buffer.sfc(dub, dist = 203.72) :
st_buffer does not correctly buffer longitude/latitude data, dist needs to be in decimal degrees.
But plotting it shows quite a too big circle
library(mapview)
mapview(dub110)
What are the units for dist I should enter in st_buffer?
I read the documentation but didn't really find out what to do...
Any hints/helps really appreciated!
Thanks to Phil and Jul the complete solution to the initial question is as follows:
library(units)
library(tidyverse)
library(sf)
library(mapview)
library(units)
# define nautical miles (as per ICAO notation)
NM <- make_unit("NM")
install_conversion_constant("NM", "km", 1.852)
# DUB/EIDW location, see
# https://skyvector.com/airport/EIDW/Dublin-Airport
# Coordinates:
# N53°25.28' / W6°16.20' (Degrees Decimal Minutes (DDM) format)
# (-6.27, 53.421333) (lon/lat Decimal Degrees (DD))
# Elevation: 242.0 feet (MSL)
dub_lon <- -6.27
dub_lat <- 53.421333
dub_elv <- set_units(242.0, ft)
dub <- st_point(x = c(dub_lon, dub_lat, dub_elv), dim = "XYZ")
dub <- dub %>% st_sfc(crs = 4326)
# define radious of interest, i.e. 110 NM
r110 <- set_units(110, NM) %>% set_units(km) %>% set_units(m)
# change to Irish grid, which uses meters
dub <- st_transform(dub, 29902)
dub_buffer <- st_buffer(dub, r110)
# eventually convert back to WSG84 if needed for other purposes
dub <- st_transform(dub, 4326)
dub_buffer <- st_transform(dub_buffer, 4326)
mapview(dub_buffer)
Here's a pure sf answer if you prefer, but #Jul 's is perfectly serviceable.
Set up as your example:
library(units)
library(tidyverse)
library(sf)
NM <- make_unit("NM")
install_conversion_constant("NM", "km", 1.852)
# DUB/EIDW location, see
# https://skyvector.com/airport/EIDW/Dublin-Airport
# Coordinates:
# N53°25.28' / W6°16.20' (Degrees Decimal Minutes (DDM) format)
# (-6.27, 53.421333) (lon/lat Decimal Degrees (DD))
# Elevation: 242.0 feet (MSL)
dub_lon <- -6.27
dub_lat <- 53.421333
dub_elv <- set_units(242.0, ft)
dub <- st_point(x = c(dub_lon, dub_lat, dub_elv), dim = "XYZ")
dub <- dub %>% st_sfc(crs = 4326)
Then transform your coordinate to Irish Grid:
dub = st_transform(dub, 29902)
Create your buffer in metres around this point:
dub_buffer = st_buffer(dub, 110000)
Plot the result:
plot(dub_buffer)
plot(dub, add = TRUE)
As mentioned in Phil's, you need to transform your coordinates to a metres/'distance' projection rather than a degree-based projection.
I'm not familiar with sf, but with sp...
library(sp)
dub_transformed <- spTransform(dub,CRS("+init=epsg:29902"))
...before you run the buffer command should suffice.
You may then want to convert the buffered object back to epsg:4326 for plotting/additional processing. e.g.
dub110 <- spTransform(dub110,CRS("+init=epsg:4326"))

Resources