I am working on a Windows Phone 8 app and I am doing a location based search. I am using this example to find my current location which is working fine. I also have a list of latitude and longitude co-ordinates. What I want to do is find out from this list of latitude and longitude co-ordinates, which is the closest to my current location. I may also possibly modify this to find out the nearest 5 or 10 or something like that.
It sounds like it should be simple to find out but I don't know how to do it.
How do I find out which co-ordinates are closest to another?
Any help would be appreciated.
Thanks!
The actual distance requires a geodesic function:
http://en.wikipedia.org/wiki/Great-circle_distance
It is quite costly, so you want to filter first with another function that helps you trimming down the candidates and ordering them later on in your code.
In this prior pass you can use the euclidean distance:
http://en.wikipedia.org/wiki/Euclidean_distance
This two-pass approach greatly reduces computation costs (by factors of 10,000 if need be) and is described in Programming Pearls (chapter 8):
http://www.cs.bell-labs.com/cm/cs/pearls/
Since your distances are probably very short (e.g., < 25km) you can use a distance approximation vs. the Haversine formula. I would suggest using Pythagoras Theorem on an Equirectangular projection, which will correct for the curvature along the longitude lines. Below is a C# implementation:
// Convert Degress to Radians
//
private static double Deg2Rad( double deg )
{
return deg * Math.PI / 180;
}
// Get Distance between two lat/lng points using the PythagorsTheorm (a*a = (b*b + c*c))
// on an equirectangular projection
//
private double PythagorasEquirectangular( Geoposition coord1, Geoposition coord2 )
{
double lat1 = Deg2Rad( coord1.Coordinate.Latitude );
double lat2 = Deg2Rad( coord2.Coordinate.Latitude );
double lon1 = Deg2Rad( coord1.Coordinate.Longitude );
double lon2 = Deg2Rad( coord2.Coordinate.Longitude );
double R = 6371; // km
double x = (lon2-lon1) * Math.Cos((lat1+lat2)/2);
double y = (lat2-lat1);
double d= Math.Sqrt(x*x + y*y) * R;
return d;
}
// Find the closest point to your position
//
private Geoposition NearestPoint( List<Geoposition> points, Geoposition position )
{
double min_dist = 999999;
Geoposition closest = null;
// Calculate distance of each point in the list from your position
foreach ( Geoposition point in points )
{
double dist = PythagorasEquirectangular( position, point );
// keep track of which point is the current closest.
if ( dist < min_dist )
{
min_dist = dist;
closest = point;
}
}
// return the closest point
return closest;
}
The radius of the earth at equator = 6,371 km. The equator is divided into 360 degrees of longitude, so each degree at the equator represents approximately 111.32 km. Moving away from the equator towards a pole this distance decreases to zero at the pole.To calculate the distance at different latitudes multiply it by the cosine of the latitude
3 decimal places,0.001 degrees aproximates to
111.32 meters at equator
96.41meters at 30 degrees N/S
78.71 meters at 45 degrees N/S
55.66 meters at 60 degrees N/S
28.82 meters at 75 degrees N/S
For for small distances (100 meters) Pythagoras’ theorem can be used on an equirectangular projection to calculate distance. This is less complicated than Haversine or Spherical Law of Cosines but still allows for the convergence towards poles.
var R = 6371; // km
lat/lng in radians
In pseudo code as I don't know C#
var x = (lng2-lng1) * cos((lat1+lat2)/2);
var y = (lat2-lat1);
var d = sqrt(x*x + y*y) * R;
Related
The problem I want to solve is:
Calculate the intersection of the ball and the circle with the following information.
Known information about the circle:
1. The coordinates of the center A are known, and the center A is on the spherical surface.
2. The radius ra of the circle A is known.
3. Know the coordinates of a point B on the circle
Known information about the ball:
1. The coordinates of the spherical center B of the known ball.
2. The radius of the known ball rb.
Picture is as follows
Icon
With the above information, I hope to calculate the coordinates of the intersection point.
Then put the algorithm into C# so that the associated points on the plane can be placed on the sphere and the distance between these points does not change.
If the above method works, I can place many points equidistant from A on the sphere, and after placement, the distance between these points and A is unchanged.
I tried before:
1. Solve the problem with simultaneous equations, but unfortunately the final result is not available.
2. Try to get the result by the intersection of two circles, but the circle and the circle are both in a plane relationship, only the coordinates of x, y can be obtained, and the coordinates of z cannot be obtained.
I successfully put some points on the sphere through the following code, but the distance between the points and the points has changed, and the more the number of points, the more obvious this change, I don't want their distance to change, so I came up with it. Determine the position of these points by intersecting the circle with the ball。
The parameter (List atoms ) stores some points on the plane, and (double r) is the radius of the ball. By this method, the point on the plane will be "sticked" to the sphere.
/// <summary>
/// Stick the atom to the sphere
/// </summary>
private void BendAtom(List <Atom> atoms,double r)
{
double L = 2d * Math.PI * r;
double w = L;// * 2d;
double h = L / 2d;
for (int i = 0; i < atoms.Count; i++)
{
atoms[i].Point.x = (float)((360 / w) * atoms[i].Point.x - 180);
atoms[i].Point.y = (float)((180 / h) * atoms[i].Point.y - 90);
}
for (int i = 0; i < atoms.Count; i++)
{
double lat = atoms[i].Point.x;
double lng = atoms[i].Point.y;
double phi = ((90d - lat) * Math.PI) / 180d;
double theta = ((180 - lng) * Math.PI) / 180d;
double x = r * Math.Sin(phi) * Math.Cos(theta);
double y = r * Math.Cos(phi);
double z = r * Math.Sin(phi) * Math.Sin(theta);
atoms[i].Point.x = (float)x;
atoms[i].Point.y = (float)y;
atoms[i].Point.z = (float)z;
}
}
I hope that after the point is "posted" on the ball, the distance of the points does not change, but as a result, they change, and the more the number of points, the greater the change.
My math level is limited. If you have a better way to solve this problem, thank you very much and thank you for your help.
The result of the above code
I am using a C# port of libnoise with XNA (I know it's dead) to generate planets.
There is a function in libnoise that receives the coordinates of a vertex in a sphere surface (latitude and longitude) and returns a random value (from -1 to 1).
So with that value, I can change the height of each vertex on the surface of the sphere (the altitude), creating some elevation, simulating the surface of a planet (I'm not simply wrapping a texture around the sphere, I'm actually creating each vertex from scratch).
An example of what I have:
Now I want to animate the sphere, like this
But the thing is, libnoise only works with 3D noise.
The "planet" function maps the latitude and longitude to XYZ coordinates of a cube.
And I believe that, to animate a sphere like I want to, I need an extra coordinate there, to be the "time" dimension. Am I right? Or is it possible to do this with what libnoise offers?
OBS: As I mentioned, I'm using an UV sphere, not an icosphere or a spherical cube.
EDIT: Here is the algorithm used by libnoise to map lat/long to XYZ:
public double GetValue(double latitude, double longitude) {
double x=0, y=0, z=0;
double PI = 3.1415926535897932385;
double DEG_TO_RAD = PI / 180.0;
double r = System.Math.Cos(DEG_TO_RAD * lat);
x = r * System.Math.Cos(DEG_TO_RAD * lon);
y = System.Math.Sin(DEG_TO_RAD * lat);
z = r * System.Math.Sin(DEG_TO_RAD * lon);
return GetNoiseValueAt(x, y, z);
}
An n dimensional noise function takes n independent inputs (i0, i1, ..., in-1, in) & returns a value v, thus 3D noise is sufficient to generate a height map that varies over time. In your case the inputs would be longitude, latitude & time and the output would be the height offset.
The simple general algorithm would be:
at each time step (t){
for each vertex (v) on a sphere centered on some point (c){
calculate the longitude & latitude
get the scalar noise value (n) for the longitude, latitude & time
calculate the new vertex position (p) as follows p = ((v-c)n)+c
}
}
Note: this assumes you are not replacing/modifiying the original vertex values. You could either save a copy of them (uses less computation, but more memory) or recalculate them them based on a distance from c (uses less memory, but more computation). Also, you might get a smoother animation by calculating 2 (or more) larger time steps & interpolating to get the intermediate frames.
To the best of my knowledge, this solution should work for a UV sphere, an icosphere or a spherical cube.
Ok I think I made it.
I just added the time parameter to the mapped XYZ coordinates.
Using the same latitude and longitude but incrementing time by 0.01d gave me a nice result.
Here is my code:
public double GetValue(double latitude, double longitude, double time) {
double x=0, y=0, z=0;
double PI = 3.1415926535897932385;
double DEG_TO_RAD = PI / 180.0;
double r = System.Math.Cos(DEG_TO_RAD * lat);
x = r * System.Math.Cos(DEG_TO_RAD * lon);
y = System.Math.Sin(DEG_TO_RAD * lat);
z = r * System.Math.Sin(DEG_TO_RAD * lon);
return GetNoiseValueAt(x + time, y + time, z + time);
}
If someone has a better solution please share it!
Sorry for the late answer, but I couldn't find a satisfactory answer elsewhere online, so I'm writing this up for anyone who has this problem in the future.
What worked for me was using multiple 3d perlin noise sources, and combining them into 1 single noise source. Adding time to the xyz coordinates just creates a very noticeable effect of terrain moving in the (-1,-1,-1) direction.
Averaging over 4 uncorrelated noise sources does change the noise characteristics a bit, so you might have to adapt some factors to your use case.
This solution still isn't perfect, but I haven't seen any visual artifacts.
Code is C++ libnoise, but it should translate equally well to other languages.
noise::module::Perlin perlin_noise[4];
float get_height(ofVec3f p, float time) {
p*=2;
time /= 10 ;
return (perlin_noise[0].GetValue(p.x, p.y, p.z) +
perlin_noise[1].GetValue(p.x, p.y, time) +
perlin_noise[2].GetValue(p.x, time, p.z) +
perlin_noise[3].GetValue(time, p.y, p.z))/2;
}
Ideally, for a single 3d noise source, you want to multiply you x,y,z coords with a monotonic function of t, such that it explores a constantly expanding sphere surface of the noise source, but I haven't figured out the math yet..
Edit: the framework I use (openframeworks) has a 4d perlin noise function built in ofSignedNoise(glm::vec4)
It's been 10 years since I did any math like this... I am programming a game in 2D and moving a player around. As I move the player around I am trying to calculate the point on a circle 200 pixels away from the player position given a positive OR negative angle(degree) between -360 to 360. The screen is 1280x720 with 0,0 being the center point of the screen. The player moves around this entire Cartesian coordinate system. The point I am trying trying to find can be off screen.
I tried the formulas on article Find the point with radius and angle but I don't believe I am understanding what "Angle" is because I am getting weird results when I pass Angle as -360 to 360 into a Cos(angle) or Sin(angle).
So for example I have...
1280x720 on a Cartesian plane
Center Point (the position of player):
let x = a number between minimum -640 to maximum 640
let y = a number between minimum -360 to maximum 360
Radius of Circle around the player: let r always = 200
Angle: let a = a number given between -360 to 360 (allow negative to point downward or positive to point upward so -10 and 350 would give same answer)
What is the formula to return X on the circle?
What is the formula to return Y on the circle?
The simple equations from your link give the X and Y coordinates of the point on the circle relative to the center of the circle.
X = r * cosine(angle)
Y = r * sine(angle)
This tells you how far the point is offset from the center of the circle. Since you have the coordinates of the center (Cx, Cy), simply add the calculated offset.
The coordinates of the point on the circle are:
X = Cx + (r * cosine(angle))
Y = Cy + (r * sine(angle))
You should post the code you are using. That would help identify the problem exactly.
However, since you mentioned measuring your angle in terms of -360 to 360, you are probably using the incorrect units for your math library. Most implementations of trigonometry functions use radians for their input. And if you use degrees instead...your answers will be weirdly wrong.
x_oncircle = x_origin + 200 * cos (degrees * pi / 180)
y_oncircle = y_origin + 200 * sin (degrees * pi / 180)
Note that you might also run into circumstance where the quadrant is not what you'd expect. This can fixed by carefully selecting where angle zero is, or by manually checking the quadrant you expect and applying your own signs to the result values.
I highly suggest using matrices for this type of manipulations. It is the most generic approach, see example below:
// The center point of rotation
var centerPoint = new Point(0, 0);
// Factory method creating the matrix
var matrix = new RotateTransform(angleInDegrees, centerPoint.X, centerPoint.Y).Value;
// The point to rotate
var point = new Point(100, 0);
// Applying the transform that results in a rotated point
Point rotated = Point.Multiply(point, matrix);
Side note, the convention is to measure the angle counter clockwise starting form (positive) X-axis
I am getting weird results when I pass Angle as -360 to 360 into a Cos(angle) or Sin(angle).
I think the reason your attempt did not work is that you were passing angles in degrees. The sin and cos trigonometric functions expect angles expressed in radians, so the numbers should be from 0 to 2*M_PI. For d degrees you pass M_PI*d/180.0. M_PI is a constant defined in math.h header.
I also needed this to form the movement of the hands of a clock in code. I tried several formulas but they didn't work, so this is what I came up with:
motion - clockwise
points - every 6 degrees (because 360 degrees divided by 60 minuites is 6 degrees)
hand length - 65 pixels
center - x=75,y=75
So the formula would be
x=Cx+(r*cos(d/(180/PI))
y=Cy+(r*sin(d/(180/PI))
where x and y are the points on the circumference of a circle, Cx and Cy are the x,y coordinates of the center, r is the radius, and d is the amount of degrees.
Here is the c# implementation. The method will return the circular points which takes radius, center and angle interval as parameter. Angle is passed as Radian.
public static List<PointF> getCircularPoints(double radius, PointF center, double angleInterval)
{
List<PointF> points = new List<PointF>();
for (double interval = angleInterval; interval < 2 * Math.PI; interval += angleInterval)
{
double X = center.X + (radius * Math.Cos(interval));
double Y = center.Y + (radius * Math.Sin(interval));
points.Add(new PointF((float)X, (float)Y));
}
return points;
}
and the calling example:
List<PointF> LEPoints = getCircularPoints(10.0f, new PointF(100.0f, 100.0f), Math.PI / 6.0f);
The answer should be exactly opposite.
X = Xc + rSin(angle)
Y = Yc + rCos(angle)
where Xc and Yc are circle's center coordinates and r is the radius.
Recommend:
public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Vector3 angles)
{
return Quaternion.Euler(angles) * (point - pivot) + pivot;
}
You can use this:
Equation of circle
where
(x-k)2+(y-v)2=R2
where k and v is constant and R is radius
I found Haversine Formula in C# is there any other method better than this.
public double HaversineDistance(LatLng pos1, LatLng pos2, DistanceUnit unit)
{
double R = (unit == DistanceUnit.Miles) ? 3960 : 6371;
var lat = (pos2.Latitude - pos1.Latitude).ToRadians();
var lng = (pos2.Longitude - pos1.Longitude).ToRadians();
var h1 = Math.Sin(lat / 2) * Math.Sin(lat / 2) +
Math.Cos(pos1.Latitude.ToRadians()) * Math.Cos(pos2.Latitude.ToRadians()) *
Math.Sin(lng / 2) * Math.Sin(lng / 2);
var h2 = 2 * Math.Asin(Math.Min(1, Math.Sqrt(h1)));
return R * h2;
}
I suppose it is a matter of what you want to do with it. My guess is that you are trying to calculate distance based on a ZIP (Post) code and you want to know if pos2 is within x distance of pos1.
What you first need to understand is that (unless you have some awesome Geo Spatial data to work with) all calculations do not generally take into account elevation or any other topographical attributes of the given area so your calculations won't be exact. Further these calculations are "as the crow flies" which means point x to point y is a straight line so while point y may lie within 25 miles of central point x it may actually be 30 miles to travel from central point x to point y.
That being said the Haversine Formula is your best bet unless you are calculating small distances (< ~12 miles) in which case you could use Pythagorean's theorem which is expressed as:
d = sqrt((X2 - X1)^2 + (Y2 - Y1)^2)
Where X and Y are your coordinates, obviously. This is much faster but is far less accurate especially as distance increases.
The Haversine Formula is slow especially if you are repeated calling it but I am unaware of any faster methods for calculating distance based on this formula.
I searched but I could not find a complete answer.
In C# if at all possible.
I need the shortest distance between a WGS point and a WGS point defined line segment on a sphere (Earth exactly).
float DistanceInKilometres(PointF LineStartA, PointF LineEndB, PointF ThePoint)
EDIT: Perhaps an illustration would help
Please note that this is an ideal example. 'The point' could be anywhere on the surface of the sphere, the segment start-end, too. Obviously, I'm not looking for the distance through the sphere. Math isn't my stronger side, so I don't understand normalize or to cartesian. Maybe I should also note that path AB, is the shortest possible, and Distance?, is the shortest possible too.
You can use the spherical law of cosines:
http://en.wikipedia.org/wiki/Spherical_law_of_cosines
http://mathworld.wolfram.com/SphericalSegment.html
http://mathworld.wolfram.com/SphericalTrigonometry.html
You will have to use the earth's radius for calculations:
EARTH_RADIUS_KM = 6371;
Here, from my contributions to OsmMercator.java, from openstreetmap.org:
/**
* Gets the distance using Spherical law of cosines.
*
* #param la1 the Latitude in degrees
* #param lo1 the Longitude in degrees
* #param la2 the Latitude from 2nd coordinate in degrees
* #param lo2 the Longitude from 2nd coordinate in degrees
* #return the distance
*/
public static double getDistance(double la1, double lo1, double la2, double lo2) {
double aStartLat = Math.toRadians(la1);
double aStartLong = Math.toRadians(lo1);
double aEndLat =Math.toRadians(la2);
double aEndLong = Math.toRadians(lo2);
double distance = Math.acos(Math.sin(aStartLat) * Math.sin(aEndLat)
+ Math.cos(aStartLat) * Math.cos(aEndLat)
* Math.cos(aEndLong - aStartLong));
return (EARTH_RADIUS_KM * distance);
}
All you need to do is find the closest point with dot product and use that with the distance equation.
Here's the closest point example:
double[] nearestPointSegment (double[] a, double[] b, double[] c)
{
double[] t= nearestPointGreatCircle(a,b,c);
if (onSegment(a,b,t))
return t;
return (distance(a,c) < distance(b,c)) ? a : c;
}
How to calculate distance from a point to a line segment, on a sphere?
http://en.wikipedia.org/wiki/Great-circle_distance
Keep in mind the units haven't been explicitly declared. When dealing with points in space there're are a variety of ways to determine position. The main thing is you have to nail down your units to a consistent type.
When working with position on the earth, I mainly use lat/long coordinates and vectors for magnitude/direction. There're are several known types to use for vectors and earth's position. Among them are the following:
Earth-centered earth-fixed (ECEF) coordinate system
North-East-Down (NED)
Geodetic coordinate system
For your example, I might consider sticking to Geodetic.
Now, bringing this together, you might have some pseudo code which looks like this:
Where a Vector is made up of Geodetic coordinates:
class Vector {
double x=0.0; //latitude
double y=0.0; //longitude
double h=0.0; //height
...
}
public Vector closestPoint(Vector lineStartA, Vector lineEndB, final Vector thePoint ) {
Vector w = thePoint.subtract(lineStartA);
double proj = w.dot(lineEndB);
// endpoint 0 is closest point
if ( proj <= 0.0f )
return lineStartA;
else
{
//Vector square
double vsq = lineEndB.dot(lineEndB);
// endpoint 1 is closest point
if ( proj >= vsq )
return lineStartA.add(lineEndB);
else
return lineStartA.add(lineEndB.multiply(proj/vsq));
}
}
double DistanceInKilometres(Vector lineStartA, Vector lineEndB, Vector thePoint) {
Vector cp=closestPoint(lineStartA, lineEndB, thePoint);
return getDistance(cp.x, cp.y, thePoint.x, thePoint.y);
}
If your point lies within a corridor that is defined by the end points of your line segment, and perpendicular to the line, then this answer should do.
If your point lies outside that corridor then compute the distance from your point to each end of the line segment and take the smaller.