views:

154

answers:

1

I have been able to create php function to determine a list of zip codes within a certain range of a given zip code. However, my next task is a bit more complex.

Lets say the table has the following columns:

id,
username
info
latitude
longitude
range

Each record has a unique row id, some information, a latitude, a longitude, and a maximum range from those coordinates that the person wants this entry to be found for. So, if I create an entry with the coordinates -20, 50 with a range of 15, that means I only want people within 15 miles of the coordinates -20, 50 to be able to see my entry. Figuring out the latitude and longitude of the user running the search is a trivial matter.

When a user is searching through the database, all records should be retrieved for latitude and longitude coordinates within the value of range from the users lat/long.

So, a non-functional example of code using the distance formula to illustrate this would be

$userLat = The latitude of the user that is running the search

$userLong = The longitude of the user that is running the search

SELECT info FROM table 
WHERE sqrt(($userLat - lat)^2 - ($userLong - long)^2) <= range 
ORDER BY username ASC

That would be ideal, but it is not valid from a coding standpoint. Is there any way to run the above comparisons using PHP and MySQL in one query? This would involve being able to run operations using multiple column values from a given row.


UPDATE + SOLUTION

I created another question entry that addresses this issue and has a very compact function to do what this question wanted. The function given by coderpros served as the inspiration for it (his function has a lot better handling for certain situations). However, since I am using my function with a great degree of control over the input, I created a separate function. It can be found at the link below:

http://stackoverflow.com/questions/3640448/mysql-user-defined-function-for-latitude-longitude-syntax

+2  A: 

This nifty lil mySQL function that I wrote should definitely do the trick for you.

BEGIN
   DECLARE x decimal(18,15);
   DECLARE EarthRadius decimal(7,3);
   DECLARE distInSelectedUnit decimal(18, 10);

   IF originLatitude = relativeLatitude AND originLongitude = relativeLongitude THEN
          RETURN 0; -- same lat/lon points, 0 distance
   END IF;

   -- default unit of measurement will be miles
   IF measure != 'Miles' AND measure != 'Kilometers' THEN
          SET measure = 'Miles';
   END IF;

   -- lat and lon values must be within -180 and 180.
   IF originLatitude < -180 OR relativeLatitude < -180 OR originLongitude < -180 OR relativeLongitude < -180 THEN
          RETURN 0;
   END IF;

   IF originLatitude > 180 OR relativeLatitude > 180 OR originLongitude > 180 OR relativeLongitude > 180 THEN
         RETURN 0;
   END IF;

   SET x = 0.0;

   -- convert from degrees to radians
   SET originLatitude = originLatitude * PI() / 180.0,
       originLongitude = originLongitude * PI() / 180.0,
       relativeLatitude = relativeLatitude * PI() / 180.0,
       relativeLongitude = relativeLongitude * PI() / 180.0;

   -- distance formula, accurate to within 30 feet
   SET x = Sin(originLatitude) * Sin(relativeLatitude) + Cos(originLatitude) * Cos(relativeLatitude) * Cos(relativeLongitude - originLongitude);

   IF 1 = x THEN
          SET distInSelectedUnit = 0; -- same lat/long points
          -- not enough precision in MySQL to detect this earlier in the function
   END IF;

   SET EarthRadius = 3963.189;
   SET distInSelectedUnit = EarthRadius * (-1 * Atan(x / Sqrt(1 - x * x)) + PI() / 2);

   -- convert the result to kilometers if desired
   IF measure = 'Kilometers' THEN
          SET distInSelectedUnit = MilesToKilometers(distInSelectedUnit);
   END IF;

   RETURN distInSelectedUnit;
END

It takes originLatitude, originLongitude, relativeLatitude, relativeLongitude, and measure as parameters. Measure can simply be Miles or Kilometers

Hope that helps!

coderpros
Thank you very much for the quick and informative reply. Pardon my idiocy, but I am not that familiar with using custom mysql functions. How exactly would I use this with php?
Well, what you need to do is create the above function in your mySQL database, then you can call it in your php like so: $sql = "SELECT * FROM users WHERE CalculateDistance(userLat,userLong, relLat, relLong,'miles) < 15; Does that make sense?
coderpros
ah, gotcha. Just one last thing and I should be ready to use this. In the part below:CalculateDistance(userLat,userLong, relLat, relLong,'miles) < 15I would be passing the values of userLat and userLong as php variables and relLat and relLong would be the names of the columns in mysql. Also, 15 would be replaced by the value of the range column for that row. Would your code do that? Sorry, just want to make sure since setting something up and spending hours debegging the wrong thing is a nightmare. I'm sure you understand.
No problem. You got it exactly right. Lemme know if you get stuck anywhere.
coderpros
I don't have enough reputation points yet to vote up this reply, but thank you very much. It was very helpful.
That's okay. I'm glad that I could help.
coderpros
Curious, I am trying to figure out the performance implications of using this custom function. It serves my purposes exactly (once again, thanks). Would this function be quicker relative to determining if a user is within n range of a given latitude longitude, or would it be better to store all the zip codes within n range a given point (let's say 80 zip codes) and just check against all of those records? I will run benchmarks of course, but some outside information would be very useful.