SitePoint Sponsor

User Tag List

Results 1 to 11 of 11
  1. #1
    SitePoint Wizard
    Join Date
    Oct 2005
    Posts
    1,832
    Mentioned
    5 Post(s)
    Tagged
    1 Thread(s)

    Adding Distance To GPS Coordinates To Get Bounding Box

    I am looking for a method to add distance to a starting GPS coordinate point to create a bounding box (to approximate a circle) which I will then use to find all matches in my MySQL database within that box.

    I found this to calculate distances between two GPS coordinates (PHP Haversine Formula):

    http://sgowtham.net/blog/2009/08/04/...s-coordinates/

    Let's start with these two coordinates (in this format):

    Lat = 36.171915
    Lon = -115.139974

    If I take those two coordinates, I would like to add a distance of 5 miles (or 10 miles or 25 miles or whatever) to them to get north, south, east, and west coordinates 5 miles from the source. I want a point 5 miles due north, a point 5 miles due south, and points 5 miles due east and west.

    Then I can take those bounding box coordinates and find anything in my DB within the box.

    Does anyone know of any PHP formula or script that can accomplish this with reasonable accuracy?

    Any help will be appreciated. I've been searching on this for a couple of hours now.

  2. #2
    SitePoint Wizard silver trophybronze trophy Cups's Avatar
    Join Date
    Oct 2006
    Location
    France, deep rural.
    Posts
    6,869
    Mentioned
    17 Post(s)
    Tagged
    1 Thread(s)
    http://www.sitepoint.com/forums/showthread.php?t=482470

    An sql query in which you feed it a lat/lng set a radius and it selects all lat/lngs from a database, is that what you are after?

  3. #3
    SitePoint Wizard
    Join Date
    Oct 2005
    Posts
    1,832
    Mentioned
    5 Post(s)
    Tagged
    1 Thread(s)
    Cups, that is interesting. Thanks for the link. I don't know how accurate that will be.

    Even if that worked (I will check it out later) and is accurate, I would still like to be able to know how to do it in PHP script. I would still like to get the coordinates 5 miles out and not just have the DB return matches.

    This is the first time I've ever tried to do something like this. Some examples I've seen of calculating the distance between to GPS points don't factor in the curvature of the earth, the distance on the surface. It looked like some of the examples drew a straight line between points going through the earth.

    If that SQL query factors in the curvature of the earth, maybe I could deconstruct it and find out how to make it work in PHP.

  4. #4
    SitePoint Wizard
    Join Date
    Oct 2005
    Posts
    1,832
    Mentioned
    5 Post(s)
    Tagged
    1 Thread(s)
    Nobody knows how to do this? Maybe it will be possible to do a little algebra on this MySQL query and get the coordinates.

    Code:
    SELECT * , 6371.04 * ACOS( COS( PI( ) /2 - RADIANS( 90 - Latitude) )
    * COS( PI( ) /2 - RADIANS( 90 - '$latitude' ) ) * COS( RADIANS(
    Longitude) - RADIANS( '$longitude' ) ) + SIN( PI( ) /2 - RADIANS( 90
    - Latitude) ) * SIN( PI( ) /2 - RADIANS( 90 - '$latitude' ) ) ) AS
    Distance
    FROM MyLocations
    WHERE ( 6371.04 * ACOS( COS( PI( ) /2 - RADIANS( 90 - Latitude) ) *
    COS( PI( ) /2 - RADIANS( 90 - '$latitude' ) ) * COS( RADIANS(
    Longitude) - RADIANS( '$longitude' ) ) + SIN( PI( ) /2 - RADIANS( 90
    - Latitude) ) * SIN( PI( ) /2 - RADIANS( 90 - '$latitude' ) ) ) <1 )
    GROUP BY one_id HAVING dist < '$radius'
    ORDER BY Distance
    LIMIT 0 , $numberOfResults

  5. #5
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2008
    Posts
    5,757
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

  6. #6
    SitePoint Wizard
    Join Date
    Oct 2005
    Posts
    1,832
    Mentioned
    5 Post(s)
    Tagged
    1 Thread(s)
    Thank you, crmalibu. I will test that out.

  7. #7
    SitePoint Wizard
    Join Date
    Oct 2005
    Posts
    1,832
    Mentioned
    5 Post(s)
    Tagged
    1 Thread(s)
    crmalibu,

    I tested this:

    http://xoxco.com/clickable/php-getboundingbox

    It is not usable. A box consists of 4 points. That script returns 2. And the 2 points it returns are not even on the same line with the original point. If I go a distance out of 1 mile, I would assume that I could draw a straight line between the three points--the original and the two new points. But it doesn't.

  8. #8
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2008
    Posts
    5,757
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    point1 = lat1 lon1
    point2 = lat1 lon2
    point3 = lat2 lon1
    point4 = lat2 lon2

  9. #9
    SitePoint Wizard silver trophybronze trophy Cups's Avatar
    Join Date
    Oct 2006
    Location
    France, deep rural.
    Posts
    6,869
    Mentioned
    17 Post(s)
    Tagged
    1 Thread(s)
    Do you want a bounding box or a radial collection?

  10. #10
    SitePoint Wizard
    Join Date
    Oct 2005
    Posts
    1,832
    Mentioned
    5 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by Cups View Post
    Do you want a bounding box or a radial collection?
    I am looking for a bounding box (sort of) but not with points in the corners. I want points due north, due south, due east, and due west. When you have points in the corners, your query result will be skewed because of the Pythagorean theorem. If you have a square with a side length of x, the diagonal will be of length x * square root of 2. That (half) will not match the distance I want to go north, south, east and west of the starting point.

    I looked at the bearings in the script. They are 0, 90, 180, and 270. Given those bearings, I do not see that this would produce coordinates on the diagonal despite what the author says. The bearings appear to be due north, south, east, and west. I could be wrong.

    Since latitude increments from north to south and longitude from east to west, I want points due north and south so I can query everything between the north and south points and do the same with longitude to find matches in my database.

    The script linked to by crmalibu is highly accurate for the North coordinate (using the new latitude and the original longitude), but all the other points, especially the south and west, are off. They are short. I'm not looking for points on the diagonal. But if I were, it would not matter because the distances are off.

    Here is my entire test script with the unaltered original script followed by an example I tested.

    Code:
    <?php
    
    $google_map_url = 'http://maps.google.com/maps?f=q&amp;q={lat},{lon}(Test)&amp;z=15&amp;iwloc=A';
    
    
    // McKnight & 17th.
    $lat0 = 45.02091417565888;
    $lon0 = -93.00496459007263;
    
    //Beam & McKnight from http://mapki.com/getLonLat.php
    //$lat0 = 45.02814095036411;
    //$lon0 = -93.00500750541687;
    
    // Beam & McKnight from script calculation.
    //$lat0 = 45.028142832457;
    //$lon0 = -93.004964590073;
    
    list($lat1,$lat2,$lon1,$lon2) = getBoundingBox($lat0, $lon0, .5);
    
    $url0 = str_replace('{lat}', $lat0, $google_map_url);
    $url0 = str_replace('{lon}', $lon0, $url0);
    
    $url1 = str_replace('{lat}', $lat1, $google_map_url);
    $url1 = str_replace('{lon}', $lon0, $url1);
    
    $url2 = str_replace('{lat}', $lat2, $google_map_url);
    $url2 = str_replace('{lon}', $lon0, $url2);
    
    $url3 = str_replace('{lat}', $lat0, $google_map_url);
    $url3 = str_replace('{lon}', $lon1, $url3);
    
    $url4 = str_replace('{lat}', $lat0, $google_map_url);
    $url4 = str_replace('{lon}', $lon2, $url4);
    
    echo '<a href="' . $url0 . '">url0 Original Coords</a>'  . '<br/>';
    echo '<a href="' . $url1 . '">url1 New lat1 (South) Original lon</a>'  . '<br/>';
    echo '<a href="' . $url2 . '">url2 New lat2 (North) Original lon</a>'  . '<br/>';
    echo '<a href="' . $url3 . '">url3 New lon1 (West) Original lat</a>'  . '<br/>';
    echo '<a href="' . $url4 . '">url4 New lon2 (East) Original lat</a>'  . '<br/>';
    
    // Original script.
    function getBoundingBox($lat_degrees,$lon_degrees,$distance_in_miles) {
    
    	$radius = 3963.1; // of earth in miles
    
    	// bearings
    	$due_north = 0;
    	$due_south = 180;
    	$due_east = 90;
    	$due_west = 270;
    
    	// convert latitude and longitude into radians
    	$lat_r = deg2rad($lat_degrees);
    	$lon_r = deg2rad($lon_degrees);
    
    	// find the northmost, southmost, eastmost and westmost corners $distance_in_miles away
    	// original formula from
    	// http://www.movable-type.co.uk/scripts/latlong.html
    
    	$northmost  = asin(sin($lat_r) * cos($distance_in_miles/$radius) + cos($lat_r) * sin ($distance_in_miles/$radius) * cos($due_north));
    	$southmost  = asin(sin($lat_r) * cos($distance_in_miles/$radius) + cos($lat_r) * sin ($distance_in_miles/$radius) * cos($due_south));
    
    	$eastmost = $lon_r + atan2(sin($due_east)*sin($distance_in_miles/$radius)*cos($lat_r),cos($distance_in_miles/$radius)-sin($lat_r)*sin($lat_r));
    	$westmost = $lon_r + atan2(sin($due_west)*sin($distance_in_miles/$radius)*cos($lat_r),cos($distance_in_miles/$radius)-sin($lat_r)*sin($lat_r));
    
    
    	$northmost = rad2deg($northmost);
    	$southmost = rad2deg($southmost);
    	$eastmost = rad2deg($eastmost);
    	$westmost = rad2deg($westmost);
    
    	// sort the lat and long so that we can use them for a between query
    	if ($northmost > $southmost) {
    		$lat1 = $southmost;
    		$lat2 = $northmost;
    
    	} else {
    		$lat1 = $northmost;
    		$lat2 = $southmost;
    	}
    
    
    	if ($eastmost > $westmost) {
    		$lon1 = $westmost;
    		$lon2 = $eastmost;
    
    	} else {
    		$lon1 = $eastmost;
    		$lon2 = $westmost;
    	}
    
    	return array($lat1,$lat2,$lon1,$lon2);
    	
    }
    ?>
    Our starting point is McKnight & 17th, 45.02091417565888, -93.00496459007263. I know with certainty that the distance from McKnight & 17th north to Beam & McKnight is 1/2 mile. I've measured it in two different vehicles in the past. I got the coordinates from http://mapki.com/getLonLat.php. I would love to know how to develop a tool like that.

    Running the script outputs the following Google Maps links:

    Original starting location:
    http://maps.google.com/maps?f=q&q=45...)&z=15&iwloc=A

    Point due south using original longitude and lat1 from the script:
    http://maps.google.com/maps?f=q&q=45...)&z=15&iwloc=A

    Point due north using original longitude and lat2 from the script:
    http://maps.google.com/maps?f=q&q=45...)&z=15&iwloc=A

    Point due west using original latitude and lon1:
    http://maps.google.com/maps?f=q&q=45...)&z=15&iwloc=A

    Point due east using original latitude and lon2:
    http://maps.google.com/maps?f=q&q=45...)&z=15&iwloc=A

    As I mentioned previously, the distance from our starting point at McKnight & 17th due north to McKnight & Beam is exactly 1/2 mile. The output from the script for the due north point is almost exactly accurate:

    http://maps.google.com/maps?f=q&q=45...)&z=15&iwloc=A

    Starting at the intersection of McKnight & 17th and going 1/2 mile north, we get a point at the intersection of McKnight & Beam as we should.

    As a test, let's move our starting point to Beam and McKnight and look at the south point produced by the PHP script. Starting coordinates are 45.02814095036411, -93.00500750541687.

    New original location at Beam & McKnight:
    http://maps.google.com/maps?f=q&q=45...9&z=15&iwloc=A

    The south point:
    http://maps.google.com/maps?f=q&q=45...9&z=15&iwloc=A

    We have established the distance from McKnight & 17th north to Beam & McKnight is 1/2 mile (.5). But if you look at the new south point, it is well short of the intersection of McKnight & 17th.

    Thus, the script has to be inaccurate.

    I have tested this on other distances of 1 mile and 5 miles and have found similar inaccuracies not just with the north-south points, but with the east-west points as well.

    Even if these points were supposed to be plotted on the diagonal (which I do not want), the north-south distances would have to be the same. But they are not. I tested those, too.

    If anyone has any idea if the original script can be salvaged or if you see any bugs in my code, please let me know.

    Or if there are any math and PHP gurus out there that can take the Haversine formula and modify it to return point coordinates instead of a distance, let me know.

    I am sure something like this could be beneficial to others. Of course, I did a search here at Sitepoint before I posted this thread. I didn't find any answers and have not found any despite hours of searching on the internet.

    Thank you.

  11. #11
    SitePoint Wizard
    Join Date
    Oct 2005
    Posts
    1,832
    Mentioned
    5 Post(s)
    Tagged
    1 Thread(s)
    I got something to work in PHP. You pass the function your starting latitude and longitude in decimal form, the distance you want the new point from your starting point, and the bearing (direction) in degrees and it will return the GPS coordinates of the new point in decimal form. A bearing of 0 is due north, 90 is due east, 180 due south, 270 due west, and anything in between.

    $unit can be 'm' for miles or 'km' for kilometers. Just be sure the distance you pass into the function is in the same units.

    Code:
    //	Radius of earth.  3959 miles or 6371 kilometers.  Must set radius to units you are using, in my case, miles.
    
    //  Pass in coordinates in Decimal form.  Example: -41.5786214
    
    function new_coords($latitude, $longitude, $bearing, $distance, $unit = 'm')
    {
    
    if ($unit == 'm')
    	{
    		$radius = 3959;
    	}
    elseif ($unit == 'km')
    	{
    		$radius = 6371;
    	}
    
    //	New latitude in degrees.
    $new_latitude = rad2deg(asin(sin(deg2rad($latitude)) * cos($distance / $radius) + cos(deg2rad($latitude)) * sin($distance / $radius) * cos(deg2rad($bearing))));
    		
    //	New longitude in degrees.
    $new_longitude = rad2deg(deg2rad($longitude) + atan2(sin(deg2rad($bearing)) * sin($distance / $radius) * cos(deg2rad($latitude)), cos($distance / $radius) - sin(deg2rad($latitude)) * sin(deg2rad($new_latitude))));
    
    //  Assign new latitude and longitude to an array to be returned to the caller.
    $coord['latitude'] = $new_latitude;
    $coord['longitude'] = $new_longitude;
    
    return $coord;
    
    }
    I tweaked my code to post the above code. The above is not tested. The important part is the functions to return the new lat and lon which work beautifully.


Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •