It is well known that the Haversine formula is a useful mechanism in which to calculate distances between geographic coordinates. Obviously, there are certain packages in R which provider further refinements; accounting for the ellipsoidal nature of the earth.
What I cannot find, however, is any method for finding the straight-line distance between NED coordinates: that is, latitude, longitude and depth.
This type of distance calculation is essential for research on events which occur deep under oceans, or under the earth's crust in general.
Is anyone aware of a method to assist with this type of distance calculation on a sphere? On the earth? If not, does anyone have any ideas what the best trigonometric approach to this problem would be?
Linear distances are much easier to compute in a Cartesian coordinate system, so the first step is to convert your NED coordinates (i.e. long-lat-height) coordinates to some such system. In the open source world, PROJ4's "geocent" projection, which gives locations in meters in an orthogonal x-y-z coordinate system, is a good choice.
Simon Urbanek's proj4 package provides a good lightweight means of accomplishing that conversion. To demonstrate it's use, I'll write a little wrapper function that takes two points in NED coordinates and computes their separation in meters. (The one complication is that the ptransform() expects its latitude and longitude coordinate in radians; hence the divisions by 180/pi in the function's first and second lines.)
library(proj4)
findDist <- function(x=c(0,0,0), y=c(0,0,0)) {
x <- matrix(x/c(180/pi, 180/pi, 1), ncol=3)
y <- matrix(y/c(180/pi, 180/pi, 1), ncol=3)
xx <- ptransform(x, src.proj="+proj=longlat +ellps=WGS84",
dst.proj="+proj=geocent +ellps=WGS84")
yy <- ptransform(y, src.proj="+proj=longlat +ellps=WGS84",
dst.proj="+proj=geocent +ellps=WGS84")
sqrt(sum((yy-xx)^2))
}
## A sanity check. (Find distance between two points at 0°N 0°E, one on the
## surface and one 100m below the surface (i.e. at a height of -100m).)
findDist(c(0,0,0), c(0,0,-100))
## [1] 100
## Distance from North to South Pole
findDist(c(0,90,0), c(0,-90,0))
## [1] 12713505
## Diameter of the earth at its equator
findDist(c(0,0,0), c(180,0,0))
## [1] 12756274
You can use Matlab function ecefOffset to compute distance between two geographical locations in 3D.
https://www.mathworks.com/help/map/ref/ecefoffset.html
Related
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.
This question is more about math than programming. I am programming a function which takes a square of geographical distance between 2 points with known latitude and longitude as an argument. There's a straightforward way to compute it: calculate dot-product, then take arccos, and multiply by Earth radius. Then square the result and you get the square of geographical distance assuming Earth is a sphere (which is acceptable approximation in my case).
However I would like, if possible, to avoid an expensive arccos() call, especially given that I can easily obtain the square of the tunnel distance (by either Pythagorean theorem or the dot product).
I also read here http://en.wikipedia.org/wiki/Geographical_distance#Tunnel_distance about underestimation formula which I can use to get tunnel distance from geographical distance. In my case however, I need the opposite (tunnel to geographical), and for the square. I played with Taylor series and got a rough approximation:
G square = T2 / (1 - (T2/R2)/12.0) // here G2 is square of geographical distance, T2-square of tunnel, R2-square of Earth radius. I also was able to get a more accurate formula:
G square = T2 / (1 - (T2/R2)/12.0 - ((T2/R2)^2)/240.0).
This last formula gives error of only 3.8mm for G=1000 km, and less than 50cm for G=2000 km.
However, I still cannot mathematically prove this formula, at least when using Taylor series. Wonder if it's possible to get the mathematical proof and also expansion of this formula for larger values of G/T. Thanks!
Why tunnel distance from geo distance?. There is no geo distance. There are many possibilities to calculate a distance between two points on earth.
Just take the two lat/lon cooridnates, and then calculate the distance between them using a simmple cyclindrical projection.
This needs only a cos(centerLatitude), and a multiplication with a factor. (meters_per_degree)
See also Cyclindrical equi distant projection. Up to some kilomters (abou 10 to 100) this gives sufficient accuracy.
I'm wanting to calculate the location of point D, based on the location of point A B and C where I know the angle of point A relative to D and D relative to B and c relative to D.
In real terms, points A B and C are 3 locations i have marked with my GPS and point D is the location of a radiocollared animal I'm attempting to get a GPS location on. The angles I gain by knowing in which direction the radio collared animal is relative to north.
I've written the algorithm, but I know I can't put GPS co-ordinates straight into it and will have to convert them in and then out again. I've been googling, and I'm a bit confused, is the usage of cartesian or UTM more appropriate for this?
How do I go about converting GPS to UTM? I've searched and I'm a bit confused. Some conversions talk of degrees minutes adn seconds, my GPS appears to give me an additional number to this, so its N 68.21.446 and `w 12.14.284
Incase its relevant, I've assumed that the area is 2d in my calculations to make things a bit simpler.
Here is the code though I'm not sure it's needed:
#10/09/2013
#Enter your points for locations A B and C
#AN and AW is your first GPS points AA is the angle
AN<-10
AW<-0
AA<-45
#BN and BW are your second
BN<-10
BW<-0
BA<-0
#CN and CW are your third
CN<-0
CW<-10
CA<-90
#Convert these to ?
#work out distance
#For each co ordinate and angle, you need to calculate y=mx+c to make a line
#From these 3 lines, you can work out where they intersect
#If the angle is 0 it wont work, so make it very close to 0.
if(AA==0) {AA<-0.00001}
if(BA==0) {BA<-0.00001}
if(CA==0) {CA<-0.00001}
#Convert all angles to radians
AAr<-(AA*pi)/180
BAr<-(BA*pi)/180
CAr<-(CA*pi)/180
#Calculate M which is 1/tan(b)
AM<-1/tan(AAr)
BM<-1/tan(BAr)
CM<-1/tan(CAr)
#Calculate C the equation constant
#c=y-m*x
AC<-AW-AM*AN
BC<-BW-BM*BN
CC<-CW-CM*CN
#Caclulate intersections
#A and B
XAB<-(AC-BC)/(BM-AM)
YAB<-(AM*XAB+AC)
#B and C
XBC<-(BC-CC)/(CM-BM)
YBC<-(BM*XBC+BC)
#C and A
XAC<-(CC-AC)/(AM-CM)
YAC<-(CM*XAC+CC)
#Work out average of these 3 points
(XofABC<-(XAB+XBC+XAC)/(3))
(YofABC<-(YAB+YBC+YAC)/(3))
#Convert this back into GPS coordinate
`
UTMs are handy for this sort of operation as they're based on a square mapping datum and are flat 2D x-y cartesian system.
But beware of their limitations especially towards higher latitudes. And be careful that the system you choose is relevant to your location – some datum systems will be very warped if you use the wrong one.
Not sure why this is tagged in R?
Code looks like it should be fine.
Coordinate system transformations are done using the spTransform function in the rgdal package. You'll need to convert your coordinates to decimal degrees before you can convert them to UTM coords.
So, what is your "N 68.21.446" in decimal degrees? Well I'm not sure. Its 68 + (21/60) but you need to find out what the last number is. It might be a) thousandths of a minute (and if the first digit of it is ever 6 or more then that would seem likely) or b) two digits for seconds and then tenths of seconds.
For a) N 68.21.446 is then 68 + (21/60) + (446/1000)/60 decimal degrees.
For b) N 68.21.446 is then 68 + (21/60) + (44/3600) + (6/36000) decimal degrees.
You'll have to use some string matching functions to split it up.
Once you've got decimal degrees, create a spatial points dataframe with those numbers, set its CRS to your GPS coordinate system (probably EPSG code 4326) and then use spTransform to convert to your UTM code - use the one appropriate for your longitude.
Unless its polar bears or emperor penguins and the distances are not tens of km then the UTM coordinates should be a good approximation to a regular square grid. The bigger source of error is going to be your angular measurements!
On that subject, I did start writing an R package for doing location finding from radio direction finding equipment, implementing some of the statistical methods in the literature. You'll find that here: https://github.com/barryrowlingson/telemetr
If you have any comments on that package, address them to me via that github site, and not here on StackOverflow.
I have a set of latitudes and longitudes , so this is the data for an animal as it moves in time. what i want to do is to calculate turning angle, that is by what angle it turns between every movement. so say i have point 1, point 2 and point 3 with latitude and longitude value corresponding to each point(animal moves from point 1 to point 2 to point 3 and so on) and i want to calculate the angle between these 3 points, point 2 being the middle point. what should i do? my OS is windows and i am using R for analysis.
so here is my sample data:
longitude latitude
36.89379547 0.290166977
36.89384037 0.290194109
36.88999724 0.286821044
36.88708721 0.288339411
36.88650313 0.29010232
36.88563203 0.289939416
36.88545224 0.290924863
they are in decimal degrees
Using the function trackAzimuth in maptools:
library(maptools)
trackAngle <- function(xy) {
angles <- abs(c(trackAzimuth(xy), 0) -
c(0, rev(trackAzimuth(xy[nrow(xy):1, ]))))
angles <- ifelse(angles > 180, 360 - angles, angles)
angles[is.na(angles)] <- 180
angles[-c(1, length(angles))]
}
The trackAzimuth function is a simple loop wrapper around gzAzimuth. See ?gzAzimuth for references on calculating directions on the sphere.
Using your data:
x <- read.table(text = "longitude latitude
36.89379547 0.290166977
36.89384037 0.290194109
36.88999724 0.286821044
36.88708721 0.288339411
36.88650313 0.29010232
36.88563203 0.289939416
36.88545224 0.290924863", header = TRUE)
trackAngle(as.matrix(x))
[1] 10.12946 111.17211 135.88514 97.73801 89.74684
EDIT: I had to remove first/last angles from the function, something I was doing after the fact with this function elsewhere. Should be right now. :)
Also, the packages adehabitatLT and argosfilter contain functions to calculate track directions and angles.
Your data points vary over only a small range. We can look at one small patch of Earth's surface and pretend it's flat, two dimensional. You have to figure out the scale of how many km, meters, miles, whatever your favorite unit is, corresponds to one degree of latitude, and for one degree of longitude. The latter depends on latitude - it'll be the same as the scale for latitude when near the equator, but if you are standing within arm's length of the north pole, one step will take you through fifty degrees. Set up x,y coordinates where x=0 is at longitude 36.88000, and y=0 is latitude 0.29000.
So, now you have a series of (x,y) points. Take the differences from each point to the next: P2-P1, P3-P2, etc. These could be called "displacement vectors" but other terms may be used in other fields than where i'm from. Call them V1, V2, etc. Use dot products and norms: dot(V1,V2) = magnitude(V1)*magnitude(V2)*cos(a) where a is the angle by which V2 deviates from the direction of V1. Repeat for V3 and V2, and so on.
R has all the tools to do this, but I don't know enough syntax of R to give examples.