Say you have n GPS coordinates how could you work out the central GPS point between them?
In case it helps anyone now or in the future, here's an algorithm that's valid even for points near the poles (if it's valid at all, i.e. if I haven't made a silly math mistake ;-):
Convert the latitude/longitude coordinates to 3D Cartesian coordinates:
x = cos(lat) * cos(lon)
y = cos(lat) * sin(lon)
z = sin(lat)
Compute the average of x, the average of y, and the average of z:
x_avg = sum(x) / count(x)
y_avg = sum(y) / count(y)
z_avg = sum(z) / count(z)
Convert that direction back to latitude and longitude:
lat_avg = arctan(z_avg / sqrt(x_avg ** 2 + y_avg ** 2))
lon_avg = arctan(y_avg / x_avg)
Depends on what you mean by the central GPS point. You could simply take the average of all the points, as suggested by Stephen - but keep in mind that GPS coordinates are not continuous - this will fail spectacularly around discontinuities such as the poles.
In most cases you'll need to convert to a coordinate system that doesn't have this issue.
You could also look at all the points bounded by it, calculated all the distances to each GPS point, and minimize the sum of the distances to all the GPS points. You'll need to look into great circle calculations for this.
Further, each GPS might have a higher or lower degree of uncertainty, you should take that into account and weight them accordingly.
What exactly are you trying to find out?
-Adam
Related
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.
I have a set of lng/lat coordinates. What would be an efficient method of calculating the greatest distance between any two points in the set (the "maximum diameter" if you will)?
A naive way is to use Haversine formula to calculate the distance between each 2 points and get the maximum, but this doesn't scale well obviously.
Edit: the points are located on a sufficiently small area, measuring the area in which a person carrying a mobile device was active in the course of a single day.
Theorem #1: The ordering of any two great circle distances along the surface of the earth is the same as the ordering as the straight line distance between the points where you tunnel through the earth.
Hence turn your lat-long into x,y,z based either on a spherical earth of arbitrary radius or an ellipsoid of given shape parameters. That's a couple of sines/cosines per point (not per pair of points).
Now you have a standard 3-d problem that doesn't rely on computing Haversine distances. The distance between points is just Euclidean (Pythagoras in 3d). Needs a square-root and some squares, and you can leave out the square root if you only care about comparisons.
There may be fancy spatial tree data structures to help with this. Or algorithms such as http://www.tcs.fudan.edu.cn/rudolf/Courses/Algorithms/Alg_ss_07w/Webprojects/Qinbo_diameter/2d_alg.htm (click 'Next' for 3d methods). Or C++ code here: http://valis.cs.uiuc.edu/~sariel/papers/00/diameter/diam_prog.html
Once you've found your maximum distance pair, you can use the Haversine formula to get the distance along the surface for that pair.
I think that the following could be a useful approximation, which scales linearly instead of quadratically with the number of points, and is quite easy to implement:
calculate the center of mass M of the points
find the point P0 that has the maximum distance to M
find the point P1 that has the maximum distance to P0
approximate the maximum diameter with the distance between P0 and P1
This can be generalized by repeating step 3 N times,
and taking the distance between PN-1 and PN
Step 1 can be carried out efficiently approximating M as the average of longitudes and latitudes, which is OK when distances are "small" and the poles are sufficiently far away. The other steps could be carried out using the exact distance formula, but they are much faster if the points' coordinates can be approximated as lying on a plane. Once the "distant pair" (hopefully the pair with the maximum distance) has been found, its distance can be re-calculated with the exact formula.
An example of approximation could be the following: if φ(M) and λ(M) are latitude and longitude of the center of mass calculated as Σφ(P)/n and Σλ(P)/n,
x(P) = (λ(P) - λ(M) + C) cos(φ(P))
y(P) = φ(P) - φ(M) [ this is only for clarity, it can also simply be y(P) = φ(P) ]
where C is usually 0, but can be ± 360° if the set of points crosses the λ=±180° line. To find the maximum distance you simply have to find
max((x(PN) - x(PN-1))2 + (y(PN) - y(PN-1))2)
(you don't need the square root because it is monotonic)
The same coordinate transformation could be used to repeat step 1 (in the new coordinate system) in order to have a better starting point. I suspect that if some conditions are met, the above steps (without repeating step 3) always lead to the "true distant pair" (my terminology). If I only knew which conditions...
EDIT:
I hate building on others' solutions, but someone will have to.
Still keeping the above 4 steps, with the optional (but probably beneficial, depending on the typical distribution of points) repetition of step 3,
and following the solution of Spacedman,
doing calculations in 3D overcomes the limitations of closeness and distance from poles:
x(P) = sin(φ(P))
y(P) = cos(φ(P)) sin(λ(P))
z(P) = cos(φ(P)) cos(λ(P))
(the only approximation is that this holds only for a perfect sphere)
The center of mass is given by x(M) = Σx(P)/n, etc.,
and the maximum one has to look for is
max((x(PN) - x(PN-1))2 + (y(PN) - y(PN-1))2 + (z(PN) - z(PN-1))2)
So: you first transform spherical to cartesian coordinates, then start from the center of mass, to find, in at least two steps (steps 2 and 3), the farthest point from the preceding point. You could repeat step 3 as long as the distance increases, perhaps with a maximum number of repetitions, but this won't take you away from a local maximum. Starting from the center of mass is not of much help, either, if the points are spread all over the Earth.
EDIT 2:
I learned enough R to write down the core of the algorithm (nice language for data analysis!)
For the plane approximation, ignoring the problem around the λ=±180° line:
# input: lng, lat (vectors)
rad = pi / 180;
x = (lng - mean(lng)) * cos(lat * rad)
y = (lat - mean(lat))
i = which.max((x - mean(x))^2 + (y )^2)
j = which.max((x - x[i] )^2 + (y - y[i])^2)
# output: i, j (indices)
On my PC it takes less than a second to find the indices i and j for 1000000 points. The following 3D version is a bit slower, but works for any distribution of points (and does not need to be amended when the λ=±180° line is crossed):
# input: lng, lat
rad = pi / 180
x = sin(lat * rad)
f = cos(lat * rad)
y = sin(lng * rad) * f
z = cos(lng * rad) * f
i = which.max((x - mean(x))^2 + (y - mean(y))^2 + (z - mean(z))^2)
j = which.max((x - x[i] )^2 + (y - y[i] )^2 + (z - z[i] )^2)
k = which.max((x - x[j] )^2 + (y - y[j] )^2 + (z - z[j] )^2) # optional
# output: j, k (or i, j)
The calculation of k can be left out (i.e., the result could be given by i and j), depending on the data and on the requirements. On the other hand, my experiments have shown that calculating a further index is useless.
It should be remembered that, in any case, the distance between the resulting points is an estimate which is a lower bound of the "diameter" of the set, although it very often will be the diameter itself (how often depends on the data.)
EDIT 3:
Unfortunately the relative error of the plane approximation can, in extreme cases, be as much as 1-1/√3 ≅ 42.3%, which may be unacceptable, even if very rare. The algorithm can be modified in order to have an upper bound of approximately 20%, which I have derived by compass and straight-edge (the analytic solution is cumbersome). The modified algorithm finds a pair of points whith a locally maximal distance, then repeats the same steps, but this time starting from the midpoint of the first pair, possibly finding a different pair:
# input: lng, lat
rad = pi / 180
x = (lng - mean(lng)) * cos(lat * rad)
y = (lat - mean(lat))
i.n_1 = 1 # n_1: n-1
x.n_1 = mean(x)
y.n_1 = 0 # = mean(y)
s.n_1 = 0 # s: square of distance
repeat {
s = (x - x.n_1)^2 + (y - y.n_1)^2
i.n = which.max(s)
x.n = x[i.n]
y.n = y[i.n]
s.n = s[i.n]
if (s.n <= s.n_1) break
i.n_1 = i.n
x.n_1 = x.n
y.n_1 = y.n
s.n_1 = s.n
}
i.m_1 = 1
x.m_1 = (x.n + x.n_1) / 2
y.m_1 = (y.n + y.n_1) / 2
s.m_1 = 0
m_ok = TRUE
repeat {
s = (x - x.m_1)^2 + (y - y.m_1)^2
i.m = which.max(s)
if (i.m == i.n || i.m == i.n_1) { m_ok = FALSE; break }
x.m = x[i.m]
y.m = y[i.m]
s.m = s[i.m]
if (s.m <= s.m_1) break
i.m_1 = i.m
x.m_1 = x.m
y.m_1 = y.m
s.m_1 = s.m
}
if (m_ok && s.m > s.n) {
i = i.m
j = i.m_1
} else {
i = i.n
j = i.n_1
}
# output: i, j
The 3D algorithm can be modified in a similar way. It is possible (both in the 2D and in the 3D case) to start over once again from the midpoint of the second pair of points (if found). The upper bound in this case is "left as an exercise for the reader" :-).
Comparison of the modified algorithm with the (too) simple algorithm has shown, for normal and for square uniform distributions, a near doubling of processing time, and a reduction of the average error from .6% to .03% (order of magnitude). A further restart from the midpoint results in an a just slightly better average error, but almost equal maximum error.
EDIT 4:
I have to study this article yet, but it looks like the 20% I found with compass and straight-edge is in fact 1-1/√(5-2√3) ≅ 19.3%
Here's a naive example that doesn't scale well (as you say), as you say but might help with building a solution in R.
## lonlat points
n <- 100
d <- cbind(runif(n, -180, 180), runif(n, -90, 90))
library(sp)
## distances on WGS84 ellipsoid
x <- spDists(d, longlat = TRUE)
## row, then column index of furthest points
ind <- c(row(x)[which.max(x)], col(x)[which.max(x)])
## maps
library(maptools)
data(wrld_simpl)
plot(as(wrld_simpl, "SpatialLines"), col = "grey")
points(d, pch = 16, cex = 0.5)
## draw the points and a line between on the page
points(d[ind, ], pch = 16)
lines(d[ind, ], lwd = 2)
## for extra credit, draw the great circle on which the furthest points lie
library(geosphere)
lines(greatCircle(d[ind[1], ], d[ind[2], ]), col = "firebrick")
The geosphere package provides more options for distance calculation if that's needed. See ?spDists in sp for the details used here.
You don't tell us whether these points will be located in a sufficiently small part of the globe. For truly global sets of points, my first guess would be running a naive O(n^2) algorithm, possibly getting performance boost with some spatial indexing (R*-trees, octal-trees etc.). The idea is to pre-generate an n*(n-1) list of the triangle in the distance matrix and feed it in chunks to a fast distance library to minimize I/O and process churn. Haversine is fine, you could also do it with Vincenty's method (the greatest contributor to running time is quadratic complexity, not the (fixed number of) iterations in Vincenty's formula). As a side note, in fact, you don't need R for this stuff.
EDIT #2: The Barequet-Har-Peled algorithm (as pointed at by Spacedman in his reply) has O((n+1/(e^3))log(1/e)) complexity for e>0, and is worth exploring.
For the quasi-planar problem, this is known as "diameter of convex hull" and has three parts:
Computing convex hull with Graham's scan which is O(n*log(n)) - in fact, one should try transforming points into a transverse Mercator projection (using the centroid of the points in data set).
Finding antipodal points by Rotating Calipers algorithm - linear O(n).
Finding the largest distance among all antipodal pairs - linear search, O(n).
The link with pseudo-code and discussion: http://fredfsh.com/2013/05/03/convex-hull-and-its-diameter/
See also the discussion on a related question here: https://gis.stackexchange.com/questions/17358/how-can-i-find-the-farthest-point-from-a-set-of-existing-points
EDIT: Spacedman's solution pointed me to the Malandain-Boissonnat algorithm (see the paper in pdf here). However, this is worse or the same as the bruteforce naive O(n^2) algorithm.
Calculate resultant acceleration from x and y.
Calculate angle of resultant acceleration.
You can use vector maths to calculate the vector's length and angle:
length = sqrt(x * x + y * y)
angle = atan2(y, x) //this might be changed depending on your angle definitions
You dont need the x and y acceleration, just calculate the acceleration as speed change per time.
For Gps i reccommend to use the speed attribute of the location, which is much more accurate then the positions.
If you dont have speed, then calculate speed as distance per time.
Distance: use any distance formula you find for calculate distance between two lat/lon coordinates.
If you want to use the values you got from the acceleration sensor, then you should use 3-axis acceleration vector:
See also how-do-i-get-the-total-acceleration-from-3-axes
One further tipp to check if implementation is plausible:
Sum up all your (one dimension) accelerations, they shoud be near 0.
If you want the direction of the acceleration with respect to the world coordinate system, then you have to register for both TYPE_ACCELEROMETER and TYPE_MAGNETIC_FIELD. Using the results you call getRotationMatrix and then multiply the accelerometer values by this matrix will give you the coordinates of the acceleration in term of the world coordinate system. The first 2 coordinates would be the x and y coordinates.
Actually you better also register for TYPE_GRAVITY and pass the results together with the magnetic field results to the getRotationMatrix, so that you do not have to filter accelerometer values yourself.
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.
I'm wondering if it's possible to find all points by longitude and latitude within X radius of one point?
So, if I provide a latitude/longitude of -76.0000, 38.0000, is it possible to simply find all the possible coordinates within (for example) a 10 mile radius of that?
I know that there's a way to calculate the distance between two points, which is why I'm not clear as to whether this is possible. Because, it seems like you need to know the center coordinates (-76 and 38 in this case) as well as the coordinates of every other point in order to determine whether it falls within the specified radius. Is that right?
#David's strategy is correct, his implementation is seriously flawed. I suggest that before you perform the calculations you transform your lat,long pair to UTM coordinates and work in distance, not angular, measurements. If you are not familiar with Universal Transverse Mercator, hit Google or Wikipedia.
I reckon that your point (-76,38) is at UTM 37C 472995 (Easting) 1564346 (Northing). So you want to do your calculations of distance from that point. You'll find it easier, working with UTM, to work in metres, so your distance is (if you are using statute miles of 5280 feet) 16040 metres.
Incidentally, (-76,38) is well outside the contintental US -- does the US Post Office define zip codes for Antarctica ?
If you accept that the Earth is a perfect sphere, you can obtain the spatial coordinates of a point by
x = R.cos(Lat).cos(Long)
y = R.cos(Lat).sin(Long)
z = R.sin(Lat)
Now, take two points and compute the angle they form with the center of the Earth (using a dot product):
cos(Phi) = (x'.x" + y'.y" + z'.z") / R²
(the value of R gets simplified).
In your case, the angular distance, Phi, equals 2Pi.D/R. (R=6 378.1 km).
A point P" is inside the ground distance (D) of P' when the dot product is larger than cos(Phi).
CAUTION: all angles must be in radians.
Depending on the precision, the data set of points within a certain distance may be extremely large or even infinite (impossible). In a given area of a circle with a positive radius you will have infinitely many points. Thus, it is trivial to determine if a point falls within a circle, however to enumerate over all the points is impossible.
If you do set a fixed precision (such as a single digit), you can loop over all possible latitude and longitude combinations and perform the distance test.
Kevin is correct. There is no reason to calculate every possible coordinate-pair in the radius.
If you start at the centerpoint pC = Point(-76.0000, 38.0000) and are testing to find out if arbitrary point pA = Point(Ax, Ay) is within a 10 mile radius... use the Pythagorean theorem:
xDist = abs( pCx - Ax )
yDist = abs ( pCy - Ay )
r^2 = (xDist)^2 + (yDist)^2
A reasonable approximation is to only query the points where
pAx >= (-76.0000 - 10.0000) && pAx <= (-76.0000 + 10.0000)
pAy >= ( 38.0000 - 10.0000) && pAy <= ( 38.0000 + 10.0000)
then perform the more intensive calculation above.