I've done quite a few blog entries on cfmap, ColdFusion's wrapper to the Google Map API. While cfmap makes using the API as easy as possible, there may be cases where you want to use the map API natively. For example, ColdFusion's use of the API is tied to version 2 of Google's API. The current version is 3. I decided to take a quick look at what's involved with working with Google's Map API by itself. Here are some examples and notes. Please note that there are multiple wrappers out there to make this process easier - including support for doing maps via a jQuery plugin. I'm intentionally avoiding these as I want a 100% pure solution.

To begin, you may want to take a look at what Google calls the Map API Family. This is a high level directory of all the Map APIs. This blog entry is concerned with the latest version of the JavaScript API. One thing I'll point out right away is that the latest version of the API does not require a developer key. That doesn't mean we get to use the service without limit (you'll see a good example of this later), but it does mean we skip signing up for a developer key. Google makes this easy - but also ties the key to a particular domain. In my cfmap examples I can't tell you how many times I got hit by a dev versus production move.

I began my research by reading the tutorial. It's a good entry, but a bit overly complex. I took their code and stripped it down a bit to make it even simpler. Here is my version of their first code block.


<!DOCTYPE html>
<html>

<head>
<style type="text/css">
  #map_canvas { width: 450px; height: 450px; }
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
function initialize() {
	var latlng = new google.maps.LatLng(31, -92);
	var myOptions = {
		zoom: 8,
		center: latlng,
		mapTypeId: google.maps.MapTypeId.ROADMAP
	};
	var map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
}
</script>
</head>

<body onload="initialize()">

  <div id="map_canvas"></div>

</body>
</html>

The critical parts of this template are:

  • Loading the Google Maps JavaScript API. What's with the sensor=false? That tells Google to not try to load your location. This is only useful on mobile devices or geolocation enabled browsers. And let's be honest. If your intent is to show your users the location of objects, you probably don't care where they are. (At least not yet.)
  • Notice the div called map_canvas. Google Maps works by writing the map into a div. It is critical that the div have a precise size. In this case the size is set in a style block.
  • The body tag runs a function, initialize, on startup. Creating a map comes down to choosing your settings and then passing it to a Map initializer. This example just uses 3 settings - a zoom level, a center, and a map type. According to the docs these three settings are your minimum.

All in all - not a lot of code, right? You can test this here. For a slightly more advanced example, this template simply adds a bunch more options. (You can find the full list of options here.)


<!DOCTYPE html>
<html>

<head>
<style type="text/css">
  #map_canvas { width: 450px; height: 450px; }
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
function initialize() {
	var latlng = new google.maps.LatLng(31, -92);
	var myOptions = {
		zoom: 8,
		center: latlng,
		mapTypeId: google.maps.MapTypeId.HYBRID,
		panControl:false,
		zoomControl:false,
		streetViewControl:false,
		mapTypeControl:false
	};
	var map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
}
</script>
</head>

<body onload="initialize()">

  <div id="map_canvas"></div>

</body>
</html>

You can run this demo here<. Ok - so far, I've all done is create a map at a certain longitude and latitude. (Keep watch - I'm going to typo the heck out of those words probably.) It's possible your data may contain such information already. It probably doesn't. Google does have a web service you can run to get geolocation information. (You can find a ColdFusion wrapper here.) I use this service at Adobe Groups to geolocate group data on a scheduled basis. But what if you want to geolocate on the fly? Luckily this is possible via JavaScript as well, but you are limited to 2500 requests per day. (See details here.) I'd probably suggest geocoding server side and doing it one time only as opposed to on the fly with every request. But if you do want to do it in JavaScript, here is an example.


<!DOCTYPE html>
<html>

<head>
<style type="text/css">
  #map_canvas { width: 450px; height: 450px; }
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
function initialize() {
	var address = "Lafayette, LA";
	geocoder = new google.maps.Geocoder();

	geocoder.geocode( { 'address': address}, function(results, status) {
		if (status == google.maps.GeocoderStatus.OK) {

			var myOptions = {
				zoom: 8,
				center: results[0].geometry.location,
				mapTypeId: google.maps.MapTypeId.ROADMAP
			};
			var map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);

			var marker = new google.maps.Marker({
				map: map, 
				position: results[0].geometry.location
			});
		} else {
			alert("Geocode was not successful for the following reason: " + status);
		}
	});
	
}
</script>
</head>

<body onload="initialize()">

  <div id="map_canvas"></div>

</body>
</html>

In this template, I begin with free form address string for Lafayette, LA. I create a new geocoder object and fire off the geocode request. This is asynchronous so a callback function is used to handle the result. If the result was good, we have a location object that contains our longitude and latitude. This can then be passed to the Map initializer. That by itself is it - but I went ahead and took more code from Google's doc to create a simple marker object. You can see this demo here.

Ok - can we use some ColdFusion now? I wanted to create a simple demo based on database data. I began by writing the query and a simple table output.


<cfquery name="getorders" datasource="cfartgallery" maxrows="10">
select	orderid, total, orderdate, address, city, state, postalcode
from	orders
</cfquery>

<table border="1" width="100%">
	<tr>
		<th>Order ID</th>
		<th>Total</th>
		<th>Date</th>
		<th>Shipped To</th>
	</tr>
	<cfoutput query="getorders">
	<tr>
		<td>#orderid#</td>
		<td>#dollarformat(total)#</td>
		<td>#dateFormat(orderdate)#</td>
		<td>
		#address#<br/>
		#city#, #state# #postalcode#
		</td>
	</tr>
	</cfoutput>
</table>

I assume nothing here needs explanation but if so, leave a comment. Now let's look at a modified version - one that integrates with Google maps.


<cfquery name="getorders" datasource="cfartgallery" maxrows="10">
select	orderid, total, orderdate, address, city, state, postalcode
from	orders
</cfquery>

<!DOCTYPE html>
<html>

<head>
<style type="text/css">
  #map_canvas { width: 450px; height: 450px; }
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
function showMap(address) {
	geocoder = new google.maps.Geocoder();

	geocoder.geocode( { 'address': address}, function(results, status) {
		if (status == google.maps.GeocoderStatus.OK) {

			var myOptions = {
				zoom: 8,
				center: results[0].geometry.location,
				mapTypeId: google.maps.MapTypeId.ROADMAP
			};
			var map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);

			var marker = new google.maps.Marker({
				map: map, 
				position: results[0].geometry.location
			});
		} else {
			alert("Geocode was not successful for the following reason: " + status);
		}
	});
}
</script>
</head>

<body>
<table border="1" width="500">
	<tr>
		<th width="60">Order #</th>
		<th width="100">Total</th>
		<th width="100">Date</th>
		<th>Shipped To</th>
	</tr>
	<cfoutput query="getorders">
	<tr>
		<td>#orderid#</td>
		<td>#dollarformat(total)#</td>
		<td>#dateFormat(orderdate)#</td>
		<td>
		#address#<br/>
		#city#, #state# #postalcode#<br/>
		<cfset fulladdress = address & " " & city & ", " & state & " " & postalcode>
		<a href="##" onclick="showMap('#jsStringFormat(fulladdress)#');return false">Show Map</a>
		</td>
	</tr>
	</cfoutput>
</table>

<div id="map_canvas"></div>

</body>
</html>

So what did I do? I began by adding a link to a JavaScript function called showMap. I passed in the address of the order data I am displaying. showMap is basically the same code as before except now my address value is passed in as an argument. You can see this demo here.

So - this is just scratching the surface of what's available, but as you can see it isn't too hard to use. I hope this helps - and if anyone has follow up questions, just leave a comment.