Thursday, October 23, 2014

Google Maps API: How To Add Multiple Location Markers

Overview

If you ever had the opportunity to work with the Google Maps Javascript API v3.0 you would see that everything is very straightforward.  First, you initiate a map, specify the center point, specify a zoom level, or even drop a marker(pin), on a specific address.  All this is basic stuff and used by most websites that require mapping functionalities.  Google Maps likes to deal with latitude and longitude combinations because that is the underlying location standard.  It even provides a tool to geocode natural addresses and convert them into lat/lang combos.

Problem

Even though Google doesn't mind if you pass in lat/lang combos or natural street addresses, lat/lang combos would offer optimal performance because there is no data manipulation or conversion needed.  But that is not always the case in real life.  Sometimes we only have natural addresses that we can use.  No worries, because we can call the geocode function of the API to get back a lat/lang object.  The function to geocode an address and drop a marker on the map can be as simple as this:




But what if you have multiple addresses and need to have markers on the same map?  If you know the lat/lang combos, it is very easy.  Just pass in an array of lat/lang combos like this:



Then iterate though the array and create each marker one by one and associate it with the map.  Now, what if we were only given addresses to work with?  That means we would have to geocode every single one of the addressed to get the associated lat/lang combo.  This approach has two issues.

1) Geocoding is an asynchronous call and does not normally process address by address in the order you expect so going through a simple for loop to continuously call the geocoder will not yield expected results.  Unless you zoom out on the map all the way to the global level, maybe you will see desired results as you see the pins drop one at a time.  But that is not usually desired, you want the map to be centered over the dropped pins and zoomed in as much as possible to fit all the pins on the map so unless you know when the last address in the array has been geocoded, you cannot make the calculations required.

2) Geocoding takes a lot of resources from Google and they frown upon multiple calls happening too frequently so calls will start to fail after a few attempts.  There is no specific number of milliseconds to wait so its a matter of trial and error until the error message is not thrown anymore.  They also expect that service providers of mapping data to provide lat/lang data and not just natural addresses so geocoding addresses really is a last resort.


Solution

How do we get around the issue of the asynchronous calls?  It is quite simple.  Just make sure the next geocode request starts after the success of the current one.  To do this, we need to perform the geocodes recursively and checking for success.  First, define a method that takes in an array of string addresses and an object of type LatLngBounds.



...



You should also define a global array of Markers as a place to store all the markers that have been created for addresses that have been processed already.

Next, we perform the geocoding for an address as usual but upon success we need to store the marker in the array.




Then we extend the LatLngBounds object to include the current lat/lang position that was just calculated.




At the end of all this, you need to pop the current address out of the array and call the function again.





So far this appears to work but then we still run into the issue of performing too many geocoding requests consecutively.  A simple trick can be used to get around this, pause a little in between calls.  You can have this call at the beginning of the method.





This simple function is defined as:




At the end of all the geocode requests we can make use of all the lat/lang positions that have been added to the LatLngBounds object.



Fortunately, the map object has a fitBounds function built in that performs all the calculations need to determine the center point of all the lat/lang locations and the appropriate zoom level based on the canvas size of the map.  The calculated center point location and zoom level will also override anything you set manually when initializing the map.

Conclusion

Again, you should not be too reliant on the geocoding features of the API.  Even if you pay a monthly fee, you still cannot make consecutive calls too frequently and there is still a limit on the number of requests per IP address per day, just more than free users.  Try to use lat/lang combos whenever possible.  If the data provider does not provide this data, then maybe its time to find another provider.