Playing with Google maps API – part three: Geocoding and custom overlay

The main feature provided by the Google maps API, is the ability to convert an human readable location, such “Milan, Italy” to a geographic location represented by latitude and longitude, this process is called geocoding. The GClientGeocoder is the class which provide this service, through the method getLatLng() which accepts two arguments: a string representing an address and a callback function that will be called once the coordinates are retrieved. If the address will be successful located, the callback function will be invoked by passing a GLatLng object as argument, otherwise the function will receive a null. GLatLng is an object which represents a geographic location (latitude and longitude).

To request a geolocation for a given address we use a similar code:

var address = "Milan, Italy";
var locator = new GClientGeocoder();

locator.getLatLng(address, function(point) {
    if (point) {
        alert(address + " is located at: " + + " - " + point.lng());
    } else {
        alert("Unable to locate " + address);

In the example above we simply alert the located address, after testing for a defined point and by using the two simple getters methods (that due to their type of method, Google should naming them getLat() and getLng()): lat() and lng(). Usually we use the callback function to add overlay to the map and to place them in the right place… and is what we are going to do!

There are several types of overlay (markers, polylines, tiles…), the most common is the GMarker (the red balloon that Google places over the searched address). However the most interesting part is to realize custom overlay, which will have our desired look, will display data related to our application/web site and will provide as many features as we need.

Create a custom overlay is like to create a custom control (as showed in the “part two“). Differently from a custom control however, we have to extend the GOverlay class and implement its 4 methods: initialize(), remove(), copy() and redraw().

The first method has the duty to place an HTML node inside the map, the second as the opposite must remove that node, the third has simply to return an instance of our custom overlay with the same settings (in a few words it must return a copy of its self) and finally the forth method is used to adjust the position of the overlay whatever required. So the code will be something like:

// our "CustomOverlay" constructor
function CustomOverlay(settings) {
    // configures the instance with the required settings

// extends GOverlay class
CustomOverlay.prototype = new GOverlay();

// implements initialize()
CustomOverlay.prototype.initialize = function() {
    // creates an HTML element and appends it to the map'c container

// implements remove()
CustomOverlay.prototype.remove = function() {
    // removes the node from the DOM

// implements copy()
CustomOverlay.prototype.copy = function() {
    // returns a new instance of "CustomOverlay" with the same attributes

CustomOverlay.prototype.redraw = function(force) {
    if (force) {
        // adjust the position of the HTML element created by initialize()

The redraw() deserves special attention, because it MUST handle the “force” argument, which is a boolean that indicates if we have to recalculate the position of the element or not. Without checking “force”, the code inside the method will be executed several times needless, causing possible memory leaks and browser instability!.

Let’s see how to use a CustomOverlay, the GClientGeocoder and a bit of jQuery, in order to realize a small Javascript application that:

  • Read a file containing a list of addresses to locate and a value for each address which indicates an hypothetical numbers of participants to an event
  • For each address requires the geocoding service
  • For each address place a custom overlay on the map (according to its location) which will shows the numbers of participants and will looks different due to that number
  • Display a progress bar indicating the status of the “geocoded” address and disable the drag of the map while loading

For convenience the file containing the address will be a text file with a json formatted array of objects:

	{name: "Agrigento", quantity: 6},
	{name: "Alessandria", quantity: 8},
	{name: "Ancona", quantity: 5},

In my example each object has the property “name” which indicates the name of an Italian province and “quantity” which represents the number of participants to an hypothetical event from that city.

This is instead the CustomOverlay‘s code (comments inside the code describe all the logic behind):

// custom overlay constructor
function CustomOverlay(settings) {

	this.parentMap = settings.parentMap; // a reference to the map
	this.position = settings.position; // the geolocation of the address
	this.width = settings.width || 43; // elements's with
	this.height = settings.height || 15; // elements's height
	this.quantity = settings.quantity; // the number of partecipants to show


// custom overlay class extends the GOverlay class
CustomOverlay.prototype = new GOverlay();

// initialize the custom overlay object
CustomOverlay.prototype.initialize = function() {

	// the overlay element will have a shape of an arrow
	// this variable will be used in order to associate a
	// custom CSS class
	var arrowStyle;

	// determining the style of the label based on quantity value
	if (this.quantity  20 && this.quantity < 50) {
		arrowStyle = "yellow";
	} else {
		arrowStyle = "red";

	// div that rapresents the overlay object
	this.overlayBox = document.createElement("div");

	// set overlay's size = this.width + "px"; = this.height + "px";

	// set overlay's content
	this.overlayBox.innerHTML = this.quantity;

	// set the className to apply the stylesheet
	this.overlayBox.className = "custom_overlay_object arrow_" + arrowStyle;

	// add the html element to the dom


// removes the object from the dom
CustomOverlay.prototype.remove = function() {



// returns an object equal to the current one
CustomOverlay.prototype.copy = function() {

	return new CustomOverlay({
		parentMap: this.parentMap,
		position: this.position,
		width: this.width,
		height: this.height,
		quantity: this.quantity


// place the object in the right position
CustomOverlay.prototype.redraw = function(force) {

	// we use redraw() only if we really need it!
	if (force) {

		// get the pixels position of the overlay based on the geographic latidute and longitude
		var elementPosition = this.parentMap.fromLatLngToDivPixel(this.position);

		// positioning the overlay = (elementPosition.x - parseInt(this.width+4)) + "px"; = (elementPosition.y - parseInt(this.height/2)) + "px";



To request several geocoding successfully, we have to set an interval for the use of GClientGeocoder‘s method getLatLng(), that after some tests I realized that can’t never be less then 150 milliseconds, otherwise we will get errors from Google (we can’t simply use getLatLng() into a loop!). I’ve realized a js class which read the previously cited text file and manage this callings to getLatLng() at a given interval… I’ve called it DataManager, the code is the following (once again the comments inside describe the logic behind):

function DataManager(settings) {

	// Customizable parameters
	this.dataUrl = settings.dataUrl; // the url of the txt file to read
	this.gMap = settings.gMap; // a reference to the map

	// Internal parameters
	this._geoLocator = new GClientGeocoder(); // a reference to a GClientGeocoder instance
	this._processed = 0; // a counter for the processed requests
	var instance = this; // a reference to the current instance

	this._renderProgressBar = function() {

		// create a new HTML node for the progress bar
		var pb = document.createElement("div");

		// sets its ID = 'dm_progress_bar';

		// sets its content
		pb.innerHTML = '<div id="dm_bar_container"><div id="dm_bar"></div>;<div id="dm_info">Loading 0 of ? location(s).</div></div>';

		// adds the node to the map's container
		// (returned from getPane(G_MAP_MAP_PANE))


	this._removeProgressBar = function() {

		// get a reference to the html element representing the progress bar
		var pb = document.getElementById("dm_progress_bar");

		// removes the node from the map's container
		// (returned from getPane(G_MAP_MAP_PANE))

		// activates the drag feature


	this._processData = function(data) {

		// get the progress bar node
		var bar = document.getElementById("dm_bar");

		// get the node containing the "load x of y" message
		var info = document.getElementById("dm_info");

		// reference to the current width of the progress bar
		var barWidth = parseInt( || 0;

		// calculates the new with based on the loaded data
		var newBarWidth = Math.round(barWidth + 200/data.length);

		// requires the geocoding for each address

			// access to the address

			// callback funtion used by getLatLng()
			function(point) {

				// add the overlay to the dom
				instance.gMap.addOverlay(new CustomOverlay({
					parentMap: instance.gMap,
					position: point,
					quantity: data[instance._processed].quantity

				// increase the processed locations

				// sets the new with for the progress bar = newBarWidth > 200 ? 200 : newBarWidth + "px";

				if (instance._processed < data.length) {

					// if the data process is not finished, display the current status
					info.innerHTML = ["Loading: ", instance._processed, " of ", data.length, " location(s)."].join("");

				} else {

					// otherwise communicates that we have finished..
					info.innerHTML = "Loaded! ;-)";

					// ...and after 1/2 second removes the progress bar
					setTimeout(instance._removeProgressBar, 500);


				// if there are more locations to process call this method again every
				// 150 milliseconds. (this._processData() is a recursive function)
				if (instance._processed < data.length) {
					setTimeout(function() {
					}, 150);




	// display the ajax error
	this._showError = function() {



	// get the data
	this.getData = function() {

		// display the progress bar

		// uses jQuery to load the txt file via Ajax
			type: "GET",
			url: instance.dataUrl,
			dataType: "json",
			cache: false,
			success: instance._processData,
			error: instance._showError



In the page containing the map the necessary code to run the sample application is:

function load() {

    if (GBrowserIsCompatible()) {

        var map = new GMap2(document.getElementById("map"));


        map.setCenter(new GLatLng(41.65, 12.5), 6);

        var manager = new DataManager({
            dataUrl: "/gmaps/assets/js/data.json",
            gMap: map




The working example is here: (unfortunately due to the yourfreehosting service, the load of the page is slow and difficult :P).

A brief note: although the scheduled geolation requests and the progress bar are cool to see, in a real application would be a best practice to realize a static file which already contains the various geolocations, in order to realize a faster application and avoid the google’s defined limits for geocoding requests.