Twitter: raymondcamden


Address: Lafayette, LA, USA

Simple introduction to Google Maps

02-15-2011 17,138 views JavaScript, ColdFusion 45 Comments

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.

view plain print about
1<!DOCTYPE html>
2<html>
3
4<head>
5<style type="text/css">
6 #map_canvas { width: 450px; height: 450px; }
7</style>
8<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
9<script type="text/javascript">
10function initialize() {
11    var latlng = new google.maps.LatLng(31, -92);
12    var myOptions = {
13        zoom: 8,
14        center: latlng,
15        mapTypeId: google.maps.MapTypeId.ROADMAP
16    };
17    var map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
18}
19</script>
20</head>
21
22<body onload="initialize()">
23
24 <div id="map_canvas"></div>
25
26</body>
27</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.)

view plain print about
1<!DOCTYPE html>
2<html>
3
4<head>
5<style type="text/css">
6 #map_canvas { width: 450px; height: 450px; }
7</style>
8<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
9<script type="text/javascript">
10function initialize() {
11    var latlng = new google.maps.LatLng(31, -92);
12    var myOptions = {
13        zoom: 8,
14        center: latlng,
15        mapTypeId: google.maps.MapTypeId.HYBRID,
16        panControl:false,
17        zoomControl:false,
18        streetViewControl:false,
19        mapTypeControl:false
20    };
21    var map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
22}
23</script>
24</head>
25
26<body onload="initialize()">
27
28 <div id="map_canvas"></div>
29
30</body>
31</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.

view plain print about
1<!DOCTYPE html>
2<html>
3
4<head>
5<style type="text/css">
6 #map_canvas { width: 450px; height: 450px; }
7</style>
8<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
9<script type="text/javascript">
10function initialize() {
11    var address = "Lafayette, LA";
12    geocoder = new google.maps.Geocoder();
13
14    geocoder.geocode( { 'address': address}, function(results, status) {
15        if (status == google.maps.GeocoderStatus.OK) {
16
17            var myOptions = {
18                zoom: 8,
19                center: results[0].geometry.location,
20                mapTypeId: google.maps.MapTypeId.ROADMAP
21            };
22            var map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
23
24            var marker = new google.maps.Marker({
25                map: map,
26                position: results[0].geometry.location
27            });
28        } else {
29            alert("Geocode was not successful for the following reason: " + status);
30        }
31    });
32    
33}
34</script>
35</head>
36
37<body onload="initialize()">
38
39 <div id="map_canvas"></div>
40
41</body>
42</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.

view plain print about
1<cfquery name="getorders" datasource="cfartgallery" maxrows="10">
2select    orderid, total, orderdate, address, city, state, postalcode
3from    orders
4</cfquery>
5
6<table border="1" width="100%">
7    <tr>
8        <th>Order ID</th>
9        <th>Total</th>
10        <th>Date</th>
11        <th>Shipped To</th>
12    </tr>
13    <cfoutput query="getorders">
14    <tr>
15        <td>#orderid#</td>
16        <td>#dollarformat(total)#</td>
17        <td>#dateFormat(orderdate)#</td>
18        <td>
19        #address#<br/>
20        #city#, #state# #postalcode#
21        </td>
22    </tr>
23    </cfoutput>
24</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.

view plain print about
1<cfquery name="getorders" datasource="cfartgallery" maxrows="10">
2select    orderid, total, orderdate, address, city, state, postalcode
3from    orders
4</cfquery>
5
6<!DOCTYPE html>
7<html>
8
9<head>
10<style type="text/css">
11 #map_canvas { width: 450px; height: 450px; }
12</style>
13<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
14<script type="text/javascript">
15function showMap(address) {
16    geocoder = new google.maps.Geocoder();
17
18    geocoder.geocode( { 'address': address}, function(results, status) {
19        if (status == google.maps.GeocoderStatus.OK) {
20
21            var myOptions = {
22                zoom: 8,
23                center: results[0].geometry.location,
24                mapTypeId: google.maps.MapTypeId.ROADMAP
25            };
26            var map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
27
28            var marker = new google.maps.Marker({
29                map: map,
30                position: results[0].geometry.location
31            });
32        } else {
33            alert("Geocode was not successful for the following reason: " + status);
34        }
35    });
36}
37</script>
38</head>
39
40<body>
41<table border="1" width="500">
42    <tr>
43        <th width="60">Order #</th>
44        <th width="100">Total</th>
45        <th width="100">Date</th>
46        <th>Shipped To</th>
47    </tr>
48    <cfoutput query="getorders">
49    <tr>
50        <td>#orderid#</td>
51        <td>#dollarformat(total)#</td>
52        <td>#dateFormat(orderdate)#</td>
53        <td>
54        #address#<br/>
55        #city#, #state# #postalcode#<br/>
56        <cfset fulladdress = address & " " & city & ", " & state & " " & postalcode>
57        <a href="##" onclick="showMap('#jsStringFormat(fulladdress)#');return false">Show Map</a>
58        </td>
59    </tr>
60    </cfoutput>
61</table>
62
63<div id="map_canvas"></div>
64
65</body>
66</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.

Related Blog Entries

45 Comments

  • Commented on 02-16-2011 at 6:11 AM
    Great stuff Ray. I love the v3 API, and wrote a custom tag a few months back that's on RIAForge (http://cfgmap.riaforge.org). The whole thing is heavily documented, but basically it allows one to put some config options as tag attributes, and the tag will create the initialization of the map. It takes an array of object locations to plot on the map. Any locations that are geocoded can have their lat/lng's passed back to the server via a callback option in the tag to update those location's records in the db. There's even an example script that show's how to use the custom map object for getting directions from input and links, recentering the map on specific locations, and more.

    http://blog.cutterscrossing.com/index.cfm/2011/1/1...
  • Ed #
    Commented on 02-16-2011 at 1:30 PM
    Great writeup. Have you done any work with google maps where there were too many points in an area and you showed larger icons until you zoom in? I haven't figured out how to implement something like that. That would be a blog post I would be very interested in. =)
  • Commented on 02-16-2011 at 3:50 PM
    @Steve: Very cool. Thats a good example of where custom tags still make a lot of sense.

    @Ed: Um - not quite there yet. ;)
  • Ed #
    Commented on 02-16-2011 at 6:25 PM
    I had done some research on the too many markers issue some time ago but it looks like Google has recently documented some solutions for this:

    http://code.google.com/apis/maps/articles/toomanym...

    In particular MarkerClusterer looks like what I was envisioning. We'll see how it pans out...
  • ja #
    Commented on 03-03-2011 at 12:41 PM
    Very easy to follow..Ray. Just have one issue I cannot seem to resolve..
    - I'm using your code on my Spry "Map Customer" Tab to display a single cust/address.
    - I have everything working fine, except my query will not refresh & grab the current customer?
    - Here is my query: <cfquery name="rsCAImap" datasource="salesPipeline">
    SELECT wkRptID, wkRptAcqDate, wkRptCustName, wkRptBusAdd, wkRptCustCity, wkRptBusSt, wkRptBusZip
    FROM tblWeeklyRpt
    WHERE wkRptCd = 'CAI' AND tblWeeklyRpt.wkRptID = <cfqueryparam value="#URL.id_com#" cfsqltype="cf_sql_numeric">
    ORDER BY wkRptCustName ASC
    </cfquery>
    -----------------------------------------
    - Here is an example of my page URL: http://192.168.1.77/COE/salespipeline_wrd.cfm?wkRp...
    - Is there something I can change in your "Calling Code" below to make sure the Query "grabs" the URL parameter before running?
    - Here is your "calling code": <cfoutput query="rsCAImap">
    <tr>
    <td bgcolor="##FFFFFF"> #wkRptBusAdd#<br/>
    #wkRptCustCity#, #wkRptBusSt# #wkRptBusZip#<br/>
    <cfset fulladdress = wkRptBusAdd & " " & wkRptCustCity & ", " & wkRptBusSt & " " & wkRptBusZip>
    <a href="##" onclick="showMap('#jsStringFormat(fulladdress)#');return false">Show Map</a></td>
    </tr>
    </cfoutput>
    --------------------------------------------
    Thanks again for a cool, quick Google map implementation..
    jaa
  • Commented on 03-10-2011 at 5:18 PM
    So you have 2 tabs then - tab one a list with links and tab 2 the map?
  • ja #
    Commented on 03-10-2011 at 7:23 PM
    Ray, I figured it out.. just needed to reference my main form's query, didn't need the other query at all..
    - To answer the question, Yes I have multiple SpryTabs, but only one main query, see image below:
    - http://cerberus.clearwave.com/jerry/Google_Map_Tab...

    By the way, I also tested your orders2.cfm Demo where it lists all customers down the page..
    How does one alter that code to plot multiple addresses on a single map?

    No cfmap in my CF7MX.. Thanks again.. ja
  • Commented on 03-11-2011 at 1:44 PM
    You need to use markers. A bit more work, but not terribly so.
  • Adrian #
    Commented on 03-24-2011 at 5:29 PM
    Hi

    This is perfect for what I was thinking of doing. Quick question is, how can I modify this to accept more than 1 location, e.g. if I wanted to show all accommodation for town?
    Thanks
  • Commented on 03-27-2011 at 9:45 AM
    You see how my code adds markers one at a time? You would just... well more. :) Does that make sense?
  • Irv #
    Commented on 04-06-2011 at 3:43 PM
    Hi, Ray

    Is there a way to use the API v3 to make a map black and white based on your code (above)?
    I found some code I'm using and it works, but the code seems awfully 'verbose' (at least to me). I can provide the url if necessary.

    Thanks!
  • Commented on 04-06-2011 at 3:54 PM
    According to the API docs there is a style system you can use:

    http://code.google.com/apis/maps/documentation/jav...

    It doesn't look terribly simple - but I believe this would be the way.

    Of course - if you don't mind using the Static Map API (which I've discussed many times on the blog), you can use ColdFusion to download the map and make it BW pretty quickly.
  • Irv #
    Commented on 04-06-2011 at 6:01 PM
    Thank for the quick response!

    I 'm using code very similar to the one that page, but I was looking for something a little simpler. Once again, thank you for your generosity. You're one of the true gems of the Coldfusion world!
  • Commented on 04-07-2011 at 8:21 AM
    Going to do a blog entry today or tomorrow on simple/dumb cfimage tricks with Google Static Maps. I will probably forget to post back here - so check the RSS feed. :)
  • Dani #
    Commented on 04-19-2011 at 2:32 PM
    Hi Ray, great post. Do you have any examples using the Google Map API with cfwindow or cfdiv? I thought that re-writing my functions in this format: functionName = function(arguments) {function body} would solve the issue. However, I still haven't had luck getting a google map to render in a cfdiv or cfwindow.
  • Commented on 04-19-2011 at 2:39 PM
    I'd probably just use cfmap if using cfdiv as well. In general, I don't mix CF's front end ajaxy stuff with "native" JS stuff very often. That being said - how is it failing for you?
  • Dani #
    Commented on 04-19-2011 at 3:24 PM
    Thanks for responding so quickly. I borrowed some of your example code to illustrate the problem I'm having:

    http://pastebin.com/kzSQvxqW

    It seems like the body onload function is not working within the cfwindow. I tried calling initialize() using ColdFusion.Window.onShow(). I am not really sure where the problem is.
  • Commented on 04-19-2011 at 3:34 PM
    You need to move your script tag that loads google maps into your parent document.
  • Commented on 05-20-2011 at 6:52 AM
    I also tried with coldfusion.navigate and cfdiv to load cfmap. Tried to put load google maps in parent, in self. Nothing happens. Perhaps cfmaps or google map api doesn't work with iframe (tested that as well with no success), or cfdiv?
  • Commented on 05-20-2011 at 7:12 AM
    Sorry for the unnecessary post, found it...
  • Matt #
    Commented on 06-20-2011 at 11:54 AM
    Thanks for the post. Have you had any luck or have any knowledge how one would save the google map generated by javascript api v3 to someone's My Maps account AND how to use cf to save as pdf without resorting back to static maps.

    I am trying to plot map with lots of markers and google does not give a print button or save to my maps with the map that is returned so was hoping you had some sneaky CF workarounds!

    Thanks
    Matt
  • Commented on 06-20-2011 at 9:15 PM
    As far as I know there is no way to go to PDF from the JS based map. As to your first question, I'd check the Maps API. That sounds like something that should be possible.
  • Commented on 06-21-2011 at 5:08 AM
    @Matt -

    Client side, you can programmatically add a button or link that has the "window.print();" javascript attached to it, allowing the user to easily print the output. Since the js api is client side, there really isn't a way to use cfpdf, or something else, to generate a pdf at the server. A user with Adobe Acrobat can manually change their print driver to create a pdf, but that's about the extent of it.
  • Commented on 07-14-2011 at 9:01 AM
    I experience a CFMAP mystery:

    I have a page where there are 3 divs showing different parts of content. When you open the page div 1 is visible and the other 2 are hidden (style="display:none;").
    You can click to open (toggle visibility) div 2 or 3.

    In div 2 is a google map (cfmap script).

    Now here's the mystery:
    If Div 2 (with the cfmap) is NOT hidden initially, everything is fine

    See: http://www.cyprustripadvisor.com/cyprus-company-de...
    Here both div1 and 2 are initially visible.

    If Div2 IS hidden initially and you click to toggle visibily of the divs the map has shifted left and up.
    See: http://www.cyprustripadvisor.com/cyprus-company-de...
    Here only div1 is initially visible. Click "LOCATION" to make visible.

    Does anybody has a clue why this can happen? Or had a similar experience?
  • Commented on 07-24-2011 at 9:26 AM
    When I tried your site and clicked LOCATION, nothing was visible. I mean below your top header.
  • Commented on 07-24-2011 at 9:27 AM
    I did a view source and saw this: Google Map License key is not defined.

    Looks like your map key isn't rght.
  • Commented on 07-25-2011 at 1:10 AM
    Hi Ray,
    I've found the cause of the problem. It has nothing to do with cfdiv/cfmap but it is a problem of google map in general. Google describe what to do on http://code.google.com/apis/maps/documentation/jav....
    I've sent an email to you with the details on 21-7.
    Thanks a lot for your help,
    Grietje
  • Commented on 07-25-2011 at 6:28 AM
    Ah ok - I see it. Still a bit buried in email. :)
  • Commented on 02-07-2012 at 6:47 PM
    Ray,

    I am still stuck in CF8 so no CFMAP for me, but I did use your examples and have expanded on them via other Google things I've found online.

    One mystery though: how in the heck does one generate the tag that users can click to 'get directions'? Nothing I try seems to want to bring that little rectangle up with the get directions link.

    Thanks for any light you can shed on this.
    RLS
  • Commented on 02-08-2012 at 10:48 AM
    Hmm - not quite sure what you mean. Obviously there is a Directions API, and I've blogged on this, but you're asking about a particular UI element. Do you have an example I can look at?
  • Commented on 02-08-2012 at 12:39 PM
    When you go to maps.google.com and enter an address, they allow you to get the links. When you grab the HTML iFrame code and put it into your site and execute it, you get the white balloon box that has the "Directions" link in it (Example: http://home.tmrme.mobi/#/index.cfm?pageid=1747).

    This white rectangle box with the Directions, Search Nearby, and More links is what I was looking for.

    While waiting, I have employed a pretty robust (and cool!) solution, and, yes, it took a lot of digging to figure it out. It uses geolocation and works great in Chrome and IE9, less well on Firefox, and sucks on IE8. But that's beside the point.

    So, unless there is a "widget" to produce the white rectangle box, I think I have a workable solution in place now. Still, that would be so much easier and more compatible...
  • Commented on 02-09-2012 at 5:58 PM
    Google Maps has an API for creating those popups. So you would need to check their docs on how to add the popups. I think - stress think - I did a blog post demo showing it. If not though it's not that difficult.
  • Commented on 02-09-2012 at 6:17 PM
    Thanks - I looked all over for it at the time but to no avail. When I tested the early version of the site, I found a very limiting factor was that not all of the people who tested the site had geolocation capabilities or they chose to turn them off. That means the app is basically worthless.

    So, I switched to generating the iFrame code the way Google does so at least most phones would be able to see a map. When I did it this way, that neat little directions link showed up just fine.

    Not anywhere near as cool as what I had spent a good 12+ hours writing, but usability was primo as this is targeted for the lcd.

    Thanks for your reply!
    RLS
  • Commented on 02-09-2012 at 6:19 PM
    Oh - I take that back - I could generate the popups, but the "directions" link was not a widget you could easily insert. I had to write my own "Show Directions", which turned out quite awesome. Unfortunately, functionality beat awesome this time. RLS
  • Commented on 02-09-2012 at 6:30 PM
    You can test for Geolocation support. If you do that, you can then show/not show the Directions link.
  • Commented on 02-09-2012 at 7:08 PM
    Thanks - yes, I did know that, unfortunately my Java/JavaScript skills are so poor that I would have spent another 6 hours just figuring that out, and I needed to get this out the door. Fortunately, the solution I implemented is perfect for what is needed...this time.
  • Misty #
    Commented on 01-07-2013 at 11:07 PM
    Hi ray, My Situation is here bit different, I need the following items to be searched in the Google Map:

    Address,City,State,Zipcode,County,SubDivision, Lat, Lon

    I checked all CFC's of yours at riaforge and others but none is providing county and subdivision
  • Commented on 01-08-2013 at 2:25 PM
    All I can suggest is looking at Google's APIs and see if any support geocoding of county or subdivision. I'd be a bit surprised if subdivision was supported.
  • Jose Rico #
    Commented on 08-14-2013 at 1:38 PM
    What if I need to show in a MAP the available spaces in a new Township? (I'm working for a Real Estate Company in this moment).
  • Commented on 08-14-2013 at 1:58 PM
    Can you use markers? Or are you trying to do shapes? Google Maps has a shapes API that lets you draw ad hoc shapes for stuff like this.
  • Asad #
    Commented on 05-24-2014 at 9:16 AM
    Hello Raymond
    Thanks for nice explanation. I am wondering to know is it possible to specify two or more address here and find out the distance and travel time like we did with google maps ?
  • Commented on 05-24-2014 at 9:28 AM
    Yes, Google has a Directions API that will do this. I've blogged on it too.
  • Asad #
    Commented on 05-24-2014 at 9:57 AM
    Hello Raymond
    I am unable to find your blog on Direction API will you please provide the url here
    Thanks
  • Commented on 05-24-2014 at 3:51 PM
    Go to the search, type directions api. I saw them immediately.
  • Commented on 09-05-2014 at 5:00 PM
    Thanks Ray!
    Worked like a charm the first try!

Post Reply

Please refrain from posting large blocks of code as a comment. Use Pastebin or Gists instead. Text wrapped in asterisks (*) will be bold and text wrapped in underscores (_) will be italicized.

Leave this field empty