by Pavel Simakov 2007-05-07

The Google Maps API has been available for free, public-facing sites since February 2005. A remarkable variety of sites (over 30,000 in number) have already integrated Google’s mapping technology using this API. New sites are being built every day.

I have been doing Google Maps on and off from the very beginning. Over time it became clear to me that from the intergration perspective the Google Maps applications should be cost-effective to build, quick to deploy and easy to maintain for both simple websites and complex web applications. While pursuing these goals, I became aware several of important aspects of dealing with Google Maps:

  • There are many good Google Maps tutorials out there; my favorite tutorial is one by Mike Williams

  • Geocoding is great, but some manual work will always be needed; we had to correct spelling errors in addresses and massage addresses a bit before they get properly coded

  • Adding markers to Google Maps is slow (specifically on IE) if you want to place over 100 markers on the map at the same time; this is easily resolved by using elabel.js JavaScript library that is much more lightweight and can handle many more markers; even when using elabel.js avoid adding GEvent to each marker at all costs

  • When using AJAX to bring the map data from the server don’t use XML as data format – use JSON objects as described previously; the JSON objects are hundred times faster and less CPU intensive

  • Make sure that you know JavaScript very well; all over the place Google Maps API usesclosures and lambda functions; the structured error/exception handling is not there – if you do something wrong all you will ever get is "Object blah is null"; all Google's own code is obfuscated so you can't understand anything in there; you also have to know how prototype-based inheritance works in JavaScript

  • Obfuscate your JavaScript; the obfuscation is a snap using modified version of Rhino JavaScript engine called Dojo's Compressor, which is a part of Dojo Toolkit; it handles most of JavaScript correctly except for some forms of eval(); this code below will not work after it is obfuscated:




function foo(msg){

	alert(msg);

}



function do(){

	var msg = "Here we go!";

	eval("fo" + "o(msg);");

}



do();



While learning in depth about the Google Maps, I have created several rather complex working map applications. You can view them here:

The examples show the basics of working with Google Maps, but this is not the most intersting thing about them. These examples are built using custom JavaScript application framework. In this framework, the metadata is used to define the filters on the right-hand-side and a table at the bottom of the page. The main metadata file is called manifest.js and it defines all the information about the map. Here is an example of the JavaScript metadata required to configure the Timothy’s Coffee page:




//

//  These are the generic framework objects 

//





// zone represents one page with map, filters, and table

function oygZone(width, name, desc, url, columns, 

	rowBuilder, popupBuilder, iconChooser, filtersCaption, filters){ ...}



// column is a column in the data table

function oygColumn(propName, caption, isNum, isSelector, align){ ... }



// all kinds of filters are here

function oygDropDownActionList(caption, lambdas, captions, selIndex){ ... }

function oygOneOfOrAllDropDownFilter(caption, lambdas, captions, selIndex){ ... }

function oygCheckboxFilter(caption, propLambda, checked){ ... }

function oygSubStringFilter(caption, propLambda){ ... }



// spacer without text

function oygStaticSpacer(){ ...}



// old static text

function oygStaticText(caption, align, clazz){ ... }






//

// These are the resources used by the Timothy’s Coffee

//



var columns = [

	new oygColumn("wf", "Wireless Internet", false, false, "center"),

	new oygColumn("sadd", "Address", false, true, "left")

]; 





function iconChooser(item){

	return "<img border='0' src='/bin/fwk/img/pal1/gmarker.gif'>";

}





function rowBuilder(item){

	var check = "<img src='/bin/fwk/img/pal1/check.gif'>";



	var addNoCou = item.add;

	var idx = addNoCou.indexOf(", Canada"); 

	if (idx != -1){

		addNoCou = addNoCou.substr(0, idx);

	}



	return [

		item.wf ? check : "",

		addNoCou

	];

}

 

function popupBuilder(item){ 

	var bull = "• ";



	var buf = 

		"<div class='oyg-info'>" +

		"<b>Timothy's World Coffee</b><br />" +

		item.add + "<br />" +

		"Phone: " + item.pho + "<br /><br />" +

		(item.wf ? bull + "<b>Wireless Internet / Hot Spot</b>" : "") + 

		"</div>";  

  

	return buf;

}



var filters = [

	new oygStaticSpacer(), 

	new oygSubStringFilter("Address Contains", function(row){ return row.add; }),

	new oygStaticText("for example: 'M2M' or 'Yonge'", "center"), 	



 	new oygStaticSpacer(),

	new oygCheckboxFilter(

		"Wireless Internet", function(row, checked){ return !checked || row.wf; }, 

		false

	),

	 

	new oygStaticSpacer() 

];



var oygZones = [

	new oygZone(

		748, "Timothy's World Coffee", "<b>Timothy's World Coffee</b> (Fall 2006)", "json/20060826/web.js", 

		columns, rowBuilder, popupBuilder, iconChooser,

		"Find Timothy's Near You", filters

	)

];



The raw map data is loaded from JSON objects. The JSON objects can have any number of attributes. These attributes in the implementation of the filters and the rowBuilder() and the popupBuilder() callback functions. Here is a part of the complete data file for Timothy’s Coffee page:


"oygMarkers": [

{ 

  "lat":50.994854, "lon":-114.071649, 

  "add":"6455 Macleod Trail SW, Calgary, AB, T2H 0K3, Canada", 

  "sadd":"6455 Macleod Trail SW, Calgary", 

  "pho":"(403) 259-2274", "wf":false

},{

  "lat":51.064404, "lon":-114.096480, 

  "add":"1632 14th Avenue NW, Calgary, AB, T2N 1M7, Canada", 

  "sadd":"1632 14th Avenue NW, Calgary", "pho":"(403) 210-1266", "wf":true

}, {

  "lat":51.046615, "lon":-114.067489, 

  "add":"225 7 Avenue SW, Calgary, AB, T2P 2W3, Canada", 

  "sadd":"225 7 Avenue SW, Calgary", "pho":"(403) 266-5457", "wf":true

},

  ...

  ...

  ...

]

}

Now, when I have the framework for building maps, it takes me minutes to create a new working Google Maps application. No manual work – the map, the filters and the table are all built on the fly as defined by the metadata. I am able to reuse the most of the framework's code without actually changing it. Thus, less skill is required from a JavaScript developer to build a new map using the framework. The developer doesn't even need to know how Google Maps actually work...