My question is rather simple. I am currently researching and learning how to use PHP and MySQL along with Google API to allow users on my site to search specific cities, and then also get nearby results included. I will continue to learn this on my own, but I would like to future-proof my database so that it can be implemented later on.
So if I have users submit their zip code when registering, I know that I can use the API or a function to get the latitude and longitude, and then I can store that info in its own column, such as 'lat_lng' (also zip code will have its own column).
BIG QUESTION: How can I format the lat/long column so that later on I can implement the proximity searching ability. Example: One user's lat_lng column may read as "41.854301,-71.193893" with just a comma separating the latitude and longitude. Other users will have different numbers, but is this enough for me to setup the proximity searching at a later date?
You are looking for a topological, or distance sort on the points. You want to be able to calculate the distance between two points or locations. The formulae below yield distance with different accuracy. Pick one that yields your desired accuracy. You will find each need separate lat and lon, so keep them in separate columns to simplify SQL query statements.
Finding 'nearby' points given a geolocation (lat,lon) uses one of two well-know formulas, the haversine formula (popular because it yields accurate results with fewer significant digits) and the law of cosines formula ( see moveable-type.org fpr explanation ).
The haversine formula,
a = sin²(Δφ/2) + cos φ1 ⋅ cos φ2 ⋅ sin²(Δλ/2)
c = 2 ⋅ atan2( √a, √(1−a) )
d = R ⋅ c
where φ is latitude, λ is longitude, E is Earth’s radius (6371.010km); note that trignometric functions need angles converted to radians.
The Law of cosines:
d = acos( sin φ1 ⋅ sin φ2 + cos φ1 ⋅ cos φ2 ⋅ cos Δλ ) ⋅ E
An approximation can be found using Pythagoras' theorem (for small lat,lon differences), using fewer transcedentals (trignometric functions),
x = Δλ ⋅ cos φm
y = Δ
d = E ⋅ √x² + y²
Yet a coarser approximation uses Manhattan distance (N-S street distance),
d = E * ( abs(x) + abs(y) ) / √2
There are there numerous historical threads talking about converting screen x,y to Lat Long and hypothetically in my excel sheet I can convert x,y and lat longs back and forth all day, but once I export the x,y as lat/longs into a KML file important into Google Earth the link is broken. If I save the shape I just important, and run that back through my calculation process the x,y cords are so far off I can't relay on them for anything.
My process for making x,y is pretty basic: Lat or Long = (x or y * delta + base lat/long)
The inverse is not as basic but doable: x or y = (lat or long / delta) - (base lat or lont / delta).
This works all day long in excel without any issues, but once Google Earth gets involved the resulting x,y pairs are so bad its not funny any longer.
What do I need to do to get GE exported lat longs to be more 'accurate' to what I expect the x,y pairs to be (or what they are in excel)?
Thanks.
It sounds like you're not accounting for the cos(base_latitude) factor in your delta-x formula.
One degree of latitude is the same distance everywhere, but one degree of longitude, measured at the equator, is a longer distance than one degree of longitude measured at 45 deg north.
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 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
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.