FrankHerrman.nl

Find Latitude and Longitude between map bounds

        By Frank on May 5, 2013 in API, JavaScript, PHP        

Working with Google Maps gives you endless possibilities. Though the API can do almost anything, it does have a steep learning curve and requires you to really get all the principles involved and use them wisely. Then again, this does give you all the flexibility you want from a public API.

One of the things people often want to accomplish is showing markers on a map that point to specific locations from a database. For example, a retailer loves to have its stores or point of sales shown on the map, while a Groupon-like site may show the restaurants that have a coupon-action on another map. There are many reasons to use Google Maps on your website.

Lets say you have a store locator on your website. It shows a map with markers on it. Now you want to grab those markers when the map changes position or zoomlevel through an Ajax call. You send along the boundaries of your map to your server which returns all locations that fit within those boundaries.

The first step would involve detecting changes on the map. Google offers a system of Events to make this happen. Using the following code you can schedule any action:

google.maps.event.addListener(targetObject, event, callback);

A targetObject is often the map object itself, a marker object or a infowindow object. The event can be anything from ‘zoom_changed’ to ‘mouse_over’. A full list of map-related events can be found on the Google reference page (scroll down a couple of lines to ‘Events’).

There is also a google.maps.event.addListenerOnce function which, as the name implies, will only execute the callback the first time the event is happening.

Now back to what we would like to achieve: loading new markers when the map changes. We could add an event for ‘zoom_changed’ and ‘drag_end’. This would require us to implement two events. We also have a ‘bounds_changed’ event. This would fire in case the user changes the zoom or drags the map. Sounds perfect, there is only one downside: it fires even if the user is not done dragging or zooming. So it will continuously be executed as long as the user is dragging the map which will definitely make the browser response slow down and giving your server way too many requests.

Of course, Google thought of that as well. The event ‘idle’ waits for any actions to be finished before it gets executed. The perfect event for what we try to accomplish. So our code would become something like:

google.maps.event.addListener(map, 'idle', function() {
    $.ajax('/load_locations.php', {data: {bounds: map.getBounds().toString()}, type: 'post', dataType: 'json', success: function(data, xhre, status) {
        if(status == 'SUCCESS') {
           // we got the locations!
        } else {
           // o noes!
        }
    }});
});

The above piece of code is fired after any user action. It uses jQuery to make an Ajax call to our server. It sends along the map boundaries (map.getBounds().toString()) and after executing the function it should add the markers to the map. Doing that is outside the scope of this article, but not difficult to find online.

The more interesting part is on the server side. We need to grab all locations within the boundaries of the map. We start with getting the necessary coordinates from the boundaries string. The boundaries string looks something like ‘((53.428376642, 5.88575553), (51.22322256, 4.223485867024))’. Just push this string through the following function:

function translate_bounds_string($bounds) {
	$bounds = explode('), (', $bounds);

	$top = $left = $bottom = $right = 0;

	if (count($bounds) == 2) {
	    $sw = explode(', ', $bounds[0]);
	    $ne = explode(', ', $bounds[1]);

	    if (count($ne) == 2 && count($sw) == 2) {
		$bottom = substr($sw[0], 2);
		$left = $sw[1];
		$top = $ne[0];
		$right = substr($ne[1], 0, -2);
	    }
	}

	return array($top, $right, $bottom, $left);
    }

list($ne_lat, $ne_lng, $sw_lat, $sw_lng) = translate_bounds_string($_POST['bounds']);

There we have the north-east and south-west latitude and longitude values, needed to determine the boundaries. Now we can determine for each location if the coordinates of that location are within the boundaries of the map. It seems like a simple problem, unfortunately it is not that simple. Latitude and longitude values can be negative. Latitude values are negative when locations are on the southern hemisphere. Longitude values are negative on the western hemisphere.

We cannot simply use a ‘between’ function in our database query to select the right locations. The following PHP code does filter out the locations though:

function check_in_bounds($sw_lat, $sw_lng, $ne_lat, $ne_lng, $lat, $lng) {
	$inLng = false;
	$inLat = false;
	
	if($sw_lat > $ne_lat) {
	    $inLat = $lat > $ne_lat && $lat < $sw_lat;
	} else {
	    $inLat = $lat < $ne_lat && $lat > $sw_lat;
	}
	
	$inLng = $lng < $ne_lng && $lng > $sw_lng;
	
	return $inLat && $inLng;
    }

This function expects the boundaries we got from the previous function, followed by the latitude and longitude values of the database location. It then returns either true or false which shows if the location is within that bounds. Now we can json_encode the locations that fit within the bounds and put them on the map in as markers.

Working with the world map coordinates can be a bit mathematically overwhelming, but in the end it is just a matter of taking your time. Or simply copy my functions.