Currently I'm working with spatial data and applied a Delaunay triangulation on my data points. I additionally calculated the geodesic distances on the WGS84 ellipsoid for every edge (connection between 2 points) in the triangulation. Now I'm going to search the shortest path between every 2 points in the generated graph and calculate the path distance. The shortest distance should thus be calculated as the sum over all edge distances.
Below is a minimal working example:
library(deldir)
set.seed(31)
x <- runif(100)
y <- runif(100)
d <- deldir(x, y) #preforms tesselation & Delaunay triangulation
#Calculate edge distances (for reasons of simplicity I calculate here Euclidean distances)
geodists <- NULL
for (i in 1:nrow(d$delsgs)) {
geodists[i] <- sqrt((x[d$delsgs[i,5]] - x[d$delsgs[i,6]])^2 + (y[d$delsgs[i,5]] - y[d$delsgs[i,6]])^2)
}
#Plot data
plot(d, wlines="triang")
However, I have no idea how I can perform the shortest path search on the deldir object I created. Thus, I'd be very happy if you could provide some solutions for my problem:
How can I identify which edges are involved in the shortest path between point A and B?
How can I then efficiently calculate the path distance matrix?
Thanks a lot in advance for your help!
There are some path finding algorithms. One of them is A* (Wikipedia Link)
Maybe this helps you.
You can replace the regularly ordered points in an Euclidean Metric by the delaunay points of your collection of points.
Then always go to the next neighbor, which is closest to the finish point.
Related
library(geosphere)
distm(c(lon1, lat1), c(lon2, lat2), fun = distHaversine)
Just wondering that the code above will be calculated based on the shortest distance between these two points instead of the actual distance.
If possible is there any way to actually calculate the distance accurately let's say in the scenario of bike rides from point A to Point B which never the case of a straight line.
Thanks in advance
I was trying to generate Delaunay triangulation in R using the spatstat function 'delaunay'. However, I checked the documentation and seems there is no argument to set the maximum length.
I noticed this post:
How to set maximum length of triangle side in Delaunay triangulation in R?
This seems done the same thing as I want, but as my point pattern is large so that I would prefer a simple and quick solution. Thank you!
Here is my code:
pts <- data.frame(readMat(paste('./TMA - Coordinates/HE_Rescaled_Coords/', core,
'/HE.mat', sep = '')))
colnames(pts) <- c('x', 'y')
pts_ppp <- ppp(pts$x, pts$y, owin(poly = Region_HE))
delaunay_ppp <- delaunay(pts_ppp)
I am also open to solutions using other functions from other packages. As long as it's fast.
Here is the region data: https://livejohnshopkins-my.sharepoint.com/:u:/g/personal/hmi1_jh_edu/EU5YeWiKzXlIohj7WIbfE_kB52Nbh2soXSNdHwQVukYnLA?e=t9tCf9
Here is the points data: https://livejohnshopkins-my.sharepoint.com/:u:/g/personal/hmi1_jh_edu/EaIuRF913rtBpg3VHvlp6TkB1FUomrgUc3eeUeHbVPJ50g?e=cwg1os
The Delaunay triangulation is a mathematically defined triangulation that does not involve the concept of a maximum segment length. If you want to constrain the maximum length of the segments in the triangulation, then it's not a Delaunay triangulation any more, and the algorithm for computing the Delaunay triangulation is not applicable.
You will have to specify what you want to happen when you impose a limit on the segment length. Should the algorithm just delete the edges which are too long? Delete the triangles that have an edge which is too long? If you delete stuff then the result is no longer a triangulation of the original points. Do you want to produce a different triangulation?
If X is your point pattern, then
Dtess <- delaunay(X)
Dnet <- delaunayNetwork(X)
give the Delaunay triangulation as a tessellation Dtess and as a network Dnet.
To remove edges from Dnet that are longer than lmax:
len <- lengths_psp(as.psp(Dnet))
Net <- thinNetwork(Dnet, retainedges = (len <= lmax))
To remove triangles from Dtess that have at least one edge longer than lmax:
hypotenuse <- function(p) { max(lengths_psp(edges(p))) }
h <- sapply(tiles(Dtess), hypotenuse)
Tess <- Dtess[h <= lmax]
I want to know the fastest algorithms for obtaining the cartesian distances between each point in a SpatialPointsDataFrame (X) and either (a) the closest point in a second SpatialPointsDataFrame (Y), or (b) the closest line segment in a SpatialLinesDataFrame (Y). So this is basically 2 questions, with perhaps the same answer.
For the lines, I know I can use dist2Line(X,Y, distfun=distGeo) but this is insanely slow. I also tried using nncross, after converting both X and Y to ppp objects, as below. This is did NOT work; heat mapping the new distance measure showed that it does not radiate from Y.
X_ppp <- as(X, "ppp")
Y_psp <- as(Y, "psp")
distR <- nncross(X_ppp,Y_ppp,what="dist",k=1)
X$dist2road <- distR
For lines, I also tried using gDistance(X,Y) but was met with the error, for i=1,2: Spatial object i is not projected; GEOS expects planar coordinates. I think this is because I'm using lat-lon, and it needs a true projection. But all the files i'm working with are lat-lon, and I'm not sure how to choose and specify a projection (for tanzania) w/out coping it from another file.
For points, again using the nncross approach resulted in definitely wrong distances. (In each the point and line case, is this because the output vector is not ordered in the same way that the points within X are? If so, I see now way of outputting an ID for the point within X.)
Also for points, this knn code below did work. But it's clearly not in cartesian distance, and so I'd like to convert it or find some other algorithm that provides cartesian distance.
knn.results = knn(data=coordinates(market.shp),
query=coordinates(tzprice.shp), k=1)
knn.results <- data.frame(knn.results)
tzprice.shp$dist2market <- knn.results[,2]
Basically, my hope is to find the fastest algorithm for each purpose (distance to nearest point, distance to nearest line), with output either in cartesian distance or convertible to cartesian distance. Thanks!
Somebody pointed me towards one possible answer for finding the cartesian distance between each point in a SpatialPointsDataFrame (X) and the closest point in a second SpatialPointsDataFrame (let's call it Y). So that's the first half of my question... perhaps there's a faster method out there, but this way is quite fast, and it DOES return answers in Km, at least if proj=longlat.
tree <- createTree(coordinates(Y))
inds <- knnLookup(tree, newdat=coordinates(X), k=1)
distkm <- sapply(seq_len(nrow(inds)), function(i) spDists(X[i, ], Y[inds[i, ],]))
Still looking for an algorithm that (quickly) finds meters/km from each point in X to the nearest line in a SpatialLinesDataFrame.
I’m ashamed bothering you with a stupid (but very necessary to me) question. I’ve a bunch of lat/lon points distributed almost randomly within a rectangle of ca. two x three degrees (latitude x longitude).
I need to calculate the maximum distance to the second nearest neighbor as well as the maximum distance to the farthest neighbor. I calculated these using package spatstat,
d2 <- max(nndist(data[,2:3], k = 2)
dn <- max(nndist(data[,2:3], k=(nrow(data))-1))
, respectively, and the distances obtained were 0.3 to 4.2.
I need these distances in kilometers.
So, I supposed that distances provided by nndist where expressed in radians.
So, if θ = a /r, where θ is the subtended angle in radians, a is arc length, and r is Earth radius), then, to calculate a the equations becomes: a = θr.
However, the distances transformed in such a way ranged from:
a = 6371 * 0.3 = 1911.3, and
a= 6371 * 4.2 = 2650.2
This is evidently wrong; since the maximum distance measured using – for example – Qgis between the farthest points is just 480 km…
Can anybody indicate me where am I mistaken?
Thanks a lot in advance!!!
nndist is simply calculating the euclidean distance. It does no unit conversion. As such you have given it values in "degrees", and thus it will return a value whose units are degrees. (not radians).
Thus
6371*0.3*pi/180 = 33.36
will give an approximation of the distance between these points.
A better approach would be to use great circle distances (eg in geosphere or gstat packages or to project the lat/long coordinates onto an appropriate map projection. (rgdal::spTransform will do this) and then nndist will calculate your distances in metres.
How do you draw the curve representing the shortest distance between 2 points on a flat map of the Earth?
Of course, the line would not be a straight line because the Earth is curved. (For example, the shortest distance between 2 airports is curved.)
EDIT: THanks for all the answers guys - sorry I was slow to choose solution :/
I get this sort of information from the Aviation Formulary.
In this case:
Distance between points
The great circle distance d between
two points with coordinates
{lat1,lon1} and {lat2,lon2} is given
by:
d=acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon1-lon2))
A mathematically equivalent formula,
which is less subject to rounding
error for short distances is:
d=2*asin(sqrt((sin((lat1-lat2)/2))^2 +
cos(lat1)*cos(lat2)*(sin((lon1-lon2)/2))^2))
And
Intermediate points on a great circle
In previous sections we have found
intermediate points on a great circle
given either the crossing latitude or
longitude. Here we find points
(lat,lon) a given fraction of the
distance (d) between them. Suppose the
starting point is (lat1,lon1) and the
final point (lat2,lon2) and we want
the point a fraction f along the great
circle route. f=0 is point 1. f=1 is
point 2. The two points cannot be
antipodal ( i.e. lat1+lat2=0 and
abs(lon1-lon2)=pi) because then the
route is undefined. The intermediate
latitude and longitude is then given
by:
A=sin((1-f)*d)/sin(d)
B=sin(f*d)/sin(d)
x = A*cos(lat1)*cos(lon1) + B*cos(lat2)*cos(lon2)
y = A*cos(lat1)*sin(lon1) + B*cos(lat2)*sin(lon2)
z = A*sin(lat1) + B*sin(lat2)
lat=atan2(z,sqrt(x^2+y^2))
lon=atan2(y,x)
To draw the 3D shortest path between two points on Earth's surface onto a 2D map of Earth's surface, you have to know how the 3D surface of Earth was projected onto the 2D map in question. If you know the projection used, you just need to apply it to the 3D shortest path to project it onto the 2D map. If you don't know the exact projection used, but have access to it through some sort of interface (ie. input 3D surface coords -> output 2D map coords), you could sample points along the 3D surface path, generate their corresponding map points through said interface, and then approximate the projected path with line segments/bezier curves/etc. through the projected sample points.