I am writing a tool for a game that involves calculating the distance between two coordinates on a toroidal plane 500 units across. That is, [0,0] through [499,499] are valid coordinates, and [0,0] and [499,499] are also right next to each other.
Currently, in my application, I am comparing the distance between a city with an [X,Y] location respective to the user's own [X,Y] location, which they have configured in advance.
To do this, I found this algorithm, which kind of works:
Math.sqrt ( dx * dx + dy * dy );
Because sorting a paged list by distance is a useful thing to be able to do, I implemented this algorithm in a MySQL query and have made it available to my application using the following part of my SELECT statement:
SQRT( POW( ( ".strval($sourceX)." - cityX ) , 2 ) + POW( ( ".strval($sourceY)." - cityY ) , 2 ) ) AS distance
This works fine for many calculations, but does not take into account the fact that [0,0] and [499,499] are kitty-corner to one another.
Is there any way I can tweak this algorithm to generate an accurate distance, given that 0 and 499 are adjacent?
I assume you mean wrapping coordinates and nothing spherical shaped. Like a flat piece of paper where the ends are magically connected to each other.
That means that for a map sized 500x500, the distance in the x (or y) direction is at most 250. (If it would be more than 250 steps, we could better walk 500-x steps backward.)
A simple way to fix this, would be
dx = Math.abs(dx);
dy = Math.abs(dy);
if (dx > 250)
dx = 500 - dx;
if (dy > 250)
dy = 500 - dy;
distance = Math.sqrt ( dx * dx + dy * dy );
Update (torus):
OK, from your own comments, it seems that you do mean the torus -- the surface of a donut -- and not the sphere. (This is a big difference, and you should edit your question: calling it a sphere is wrong.)
For this, the answer is fairly simple -- the cartesian formula you give is more or less correct. However, you need to wrap distances around so that anything greater than or equal to 250=500/2 gets translated down to between 0 and 250.
So the answer is something like (I don't know PHP at all, so this may need to be modified for syntax)
dx1 = min(dx, 500-dx)
dy1 = min(dy, 500-dy);
distance = Math.sqrt(dx1*dx1 + dy1*dy1);
(This assumes that you have defined dx and dy to be the absolute value of the differences.)
Just found this routine which implements the same calculation in a nicely-packaged function.
Original Answer (sphere):
You haven't explained how your (x,y) coordinates map to points on the sphere!
There are (literally) an infinite number of choices, each corresponding to a different map projection, and the formula is different for each of them. Note that no matter what choice you make, the meaning of the two coordinates is very different.
If your (x,y) are really longitude and latitude, for example, there are plenty of canned formulae (i.e., haversine) but you'll have to first translate 0->499 to 0->360 degrees for longitude and -90->90 degrees for latitude. (Note that lon and lat behave very differently on the sphere!)
But I emphasize that any choice you make will distort from the flat geometry that you get if you plot in (x,y) versus the way it really looks on the sphere.
(Finally, if you really mean that the top edge is the same as the bottom and the right the same as the left, then you probably have a torus and not a sphere!)
If you know latitude and longitude of two points - you could use haversine formula to compute distance between two points on sphere.
But as I understood you want formula which is accurate for nearly antipodal points. Haversine formula fails here. In such case you need Vincenty's formula which is accurate even in antipodal cases.
http://en.wikipedia.org/wiki/Great-circle_distance#Formulae
It sounds like you are simply using a special finite Cartesian space that is "tiled". In this case each object does not have a unique position. Instead of (x, y) it is (x + i*w, y + j*h) for all possible integer values i and j and where w and h are the widths and heights of your "window" respectively.
Obviously then the distance is not unique but the minimum distance is which is just min(d(p1,p2))) over all i, j.
If your coordinates are wrapped then you just need to compute it for i=-1,0,1 and j=-1,0,1 then take the smallest one.
That general algorithm is fine for rectangular coordinate systems or very sort distances in spherical coordinates, but it's not appropriate for a spherical coordinate system.
I think a better approach would be based on latitude and longitude, like this:
http://jan.ucc.nau.edu/~cvm/latlongdist.html
MySQL has geo-coding built into it. Why not use that?
http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
Although some of the answers here were very close, the problem was finally solved with this SELECT segment:
SQRT( POW( LEAST( ABS($sourceXstr-cityX), ( 500 +LEAST($sourceXstr,cityX)-GREATEST($sourceXstr,cityX))) , 2 ) + POW( LEAST( ABS($sourceYstr-cityY), ( 500 +LEAST($sourceYstr,cityY)-GREATEST($sourceYstr,cityY))) , 2 ) ) AS distance
I am writing answer if two coordinates are in 2 dimensional plane where ends are not meeting each other which OP has not asked. But it might help someone in future.
If your points are in a 2 dimensional plane, the distance between points (x1, y1) and (x2, y2) is given by the Pythagorean theorem:
d = squareroot( square(x2 - x1) + square(y2 - y1) )
In PHP,
$x1 = $y1 = 2;
$x2 = $y2 = 5;
$distance = sqrt( pow(($x2-$x1),2) + pow(($y2-$y1),2) );
echo $distance; // 4.2426406871193
Related
Background /
Currently right now we have a 100 or so Lat/Lng wich get mapped onto google maps. The aim is to show the hot spot location of these by selecting from the database La/Lo values that are within a few meters of each other.
The group values will then be used to find the central point and draw a circle on the map of the hot spot.
Current setup /
Currently the datase we are requesting from has just id(int),La(float),Lo(float)
Concept /
What i ''think'' is needed is this
Select * from table
Group by (
( La + 0.30 && La - 0.30) && ( Lo + 0.30 && Lo - 0.30)
)
First off, the earth is not a square so to get accurate distances you will need to use a haversine function (look it up), however, for distances of a few metres , maybe accuracy doesn't matter.
Second, the distance between degrees of lattitude varies between 110.574 and 111.694 km and for degrees of longitude between 111.32 and 0.0 km (yes 0.0km at the poles). In any case 0.3 is somewhere between 0 and 35,000 m - not exactly "a few" unless your hotspots are at the north and/or south poles.
Third, ignoring the bad geography, the SQL statement you are groping for will require a cross join and sorting on calculated distances and will be very slow even for a small data set - you need to look at using whatever support mysql has for native geography types. I know SQL Server has them and I suspect MySQL does too.
Edit
It does, see this question.
How would i create the points of a Geo-Fence(bounding box) using lat and lon and a distance.
I have the lat and lon of the center point
I have the geofence distance.
What would the logic be to create the geo-fence around these multiple points?
thanks for any help.
1 Convert center lat / lon to cartesian (x,y) in units meter.
Then you do all geometry as you have learned in school:
2 Create one corner point of the square, using polar coordinates formula
phi = 45 * TO_RADIANS;
corner.x = tcenter.x + r * sin(phi);
corner.y = tcenter.y + r * cos(phi);
where r is the length in meter of the half diagonal of your bounding box square
do the same for the other points by using phi =(90*i + 45), i= 0..3
3 transform corners (x/y) back to lat/lon using the inverse transformation
If you have a lat/lon point and a distance and you want to find other lat/lon points, this is called a bearing-range problem. See the section Destination point given distance and bearing from start point at the website http://www.movable-type.co.uk/scripts/latlong.html
If you want the box (fence) parallel to the equator, then a bearing for the north-east corner of your box will be 45 degrees (don't forget to convert to radians before using the equations). Then add 90 degrees to get the next corner point until you have done all 4 points.
I'm working on a project that will track a vehicle as it drives around. The location of the vehicle is taken from the Geoloqi API and showed on a map that automatically is updated. The coordinates is passed to an AHAH function in Drupal that will load some more info about position via some other API:s. One of them is Geonames.org.
I do not want to load data from remote servers too often as this will increase load and use our credits too fast. However. I do want it to be as real time as possible as this is essential to the project. So I thought of doing it like this:
Geonames gives a string of GPS locations for the current street so that it can be drawn on a map. The "line" is given to the next intersection. We would eat our tickets too fast if we asked for current street name every time the location changed but how about at every intersection?
Would it be possible to, in PHP, check if a given gps coordinate is at the line between two other coordinates? It would also be good if this line could be extended a few meters in all directions in order to clear any offset.
Thank you so much for all suggestions.
Your original question comes down to a simple math problem: find an equation of a line given two points that it passes. This part is simple: say, you have two points with (lat1, lon1) and (lat2, lon2) coordinates, then the equation of the line will be
(lon2 - lon1) * X + (lat1 - lat2) * Y + (lat1 * lon2 + lat2 * lon1) = 0
Now, when you get any other point (lat, lon), just put those coordinates as (X, Y) respectively into the equation. If you get 0, then the point is on the line. Then check that your lat is between lat1 and lat2 and your lon is between lon1 and lon2. If yes, then the new point is on the line between the two existing points.
The more complicated part comes from two things:
GPS error. To account for that, you can do some approximation, for example, instead of comparing to 0, give it some leeway, e.g. compare from -err to +err where err is some value that you'd define.
The fact that most streets are not straight. The picture below shows the problem. If you look at the line between the two ends of the top street, the top of the vertical street will be on that line - but that's not what you want. .
Maybe a better approach would be to use a distance change. Do not re-query position if the GPS-reported distance changed by less than some predefined value.
This question have been asked many times but i want the answer which i can use in a php application.
The closest answer i found was this
int x = (int) ((MAP_WIDTH/360.0) * (180 + lon));
int y = (int) ((MAP_HEIGHT/180.0) * (90 - lat));
I don't need to show the map or something. I just need to calculate it.
What should be the MAP_WIDTH and MAP_HEIGTH if i use the above formula ?
There is no such thing as x and y coordinates for the earth. At most, you could compute x, y and z. Note that Euclidean distance then would travel through the earth surface.
It should also be pretty obvious that any x-y based system will produce completely inaccurate results close to the poles and close to the date line...
Keep the data as it is - in latitude, longitude (and height, if available) - and instead of computing Euclidean distance, use the great circle distance. Unless you have detailed information about the actual shape of the earth, the common great circle approximation with a sphere is the best you can do. And actually quite easy to use.
http://en.wikipedia.org/wiki/Great-circle_distance
I have just been developing a postcode distance calculator for my Dads company, where all our customers are kept on file and every time a new potential customer makes an enquiry the system will check against all other customers' postcodes. The problem is that it doesn't yet do the distance calculation. Typing in a DT1 postcode with the distance set to 5 miles says that DT1, 2 and 3 are close by. DT2 says DT1 and 2, DT3 says DT1, 3 and 4. This doesn't make any sense. Yet if I put in BH2 with a distance of 50 miles it will bring back Bournemouth, Dorchester, Portsmouth, Bath, Southampton and a couple of other towns. These are all correct - however I do not know what is going on with the DT postcodes. DT11 doesn't bring back any (under 5 miles), nor does DT10 (except for themselves).
I first of all used the root of (x * x + y * y) - I believe this is called Pythagoras' theorem.. not sure since it have been years since I used that term!!
I have also tried the following:
$sinstuff = sin(deg2rad($latitude))*sin(deg2rad($latitude2));
$cosstuff = cos(deg2rad($latitude))*cos(deg2rad($latitude2))*cos(deg2rad($longitude-$longitude2));
$sumstuff = $sinstuff + $cosstuff;
$acosstuff = rad2deg(acos($sumstuff));
$distance = $acosstuff*60*1.1515; //dunno what the 60 and 1.1515 is all about - I got this formula off a website.
This is the formula which seems to be bringing up the weird results. I also notice that the exact distance between DT11 and DT11 is something like +/- 0.00000989{and lots of other numbers}. This also seems a bit odd since, surely, the distance between DT11 and itself is 0...
Has anyone ever done anything like this successfully before?
I do have a massive batch of files - about 158MB in total - containing every UK postcode in full, with its corresponding latitude and longitude. My aim is, rather than comparing the specified full postcode to every other full postcode, to work out which area codes are less than a certain distance away then compare the specified postcode to all the full postcodes which are within these areas. Is this efficient? Is there a more efficient way?
Any help with this would be greatly appreciated.
Regards,
Richard
PS I know there are some sites out there which tell you how to work out distances but I dont seem to be able to get any of them working correctly (as you can tell from above).
PPS I know I can use the Google API for this - but this is not an option since this is going to be an offline application running on a local installation of wamp server. It is for use at trade shows where we cannot guarantee an internet connection.
You're use the haversine formula:
$l1 ==> latitude1
$o1 ==> longitude1
$l2 ==> latitude2
$o2 ==> longitude2
(taken from http://www.go4expert.com/forums/showthread.php?t=339)
function haversine ($l1, $o1, $l2, $o2)
{
$l1 = deg2rad ($l1);
$sinl1 = sin ($l1);
$l2 = deg2rad ($l2);
$o1 = deg2rad ($o1);
$o2 = deg2rad ($o2);
return (7926 - 26 * $sinl1) * asin (min (1, 0.707106781186548 * sqrt ((1 - (sin ($l2) * $sinl1) - cos ($l1) * cos ($l2) * cos ($o2 - $o1)))));
}
That should give you the distance between both points.
I think the Post Office maintains a Postcode-Address file which lists
every UK postcode and its associated address (not sure if it has
lattitude and longitude information). I've seen copies of this (maybe
not totally up to date) on cover discs on various PC magazines in the
past. I'm sure you have to pay for the latest version, but you might be
able to get a starting point by checking out the cover discs from back
issues.