More advanced stuff

Google Maps API Tutorial

Snap point to street

It is now possible (in countries that support directions) to snap points to the nearest street that’s in the Google database.

This could be useful if you want to record a specific path by drawing a polyline that follows the streets.

The effect is achieved by requesting directions from the clicked point to itself. When the directions information returns, it will include a polyline with a single vertex. This polyline “follows” the street grid, and that vertex is the nearest point that lies on the street grid. So instead of plotting the point exactly where the user clicked, we can plot the point at the returned street vertex.

Here’s a simple example. Click on the map and the marker will be added to the nearest known street.

In this example, the markers are draggable and snap to a street when the drag ends.

In this example, polylines following the streets are added between the snapped points.

In this example, the two previous features are combined. Polylines are redrawn when a marker is dragged.

Potential Pitfalls

  1. Some of the very small streets shown on the map are not in the street garid database.
    E.g. the small streets to the East of the A583 roundabout in the first example are paths inside a caravan park, points will not snap to those paths because they’re not in the street database.
  2. Don’t try to snap all the points in your database dynamically, there are issues with running multiple GDirections calls at the same time. Snap your points, one by one, offline and store the snapped locations in your database.

More advanced stuff

Google Maps API Tutorial

Get Directions on your own map

It is now possible to obtain directions on your own map.

Here’s an example.

There are initially two links: “To here” and “From here”. Clicking these links opens a corresponding form.

The initial version of the info window, one with the “To here” form and one with the “From here” form. When the “To here” or “From here” link is clicked, the corresponding html string is loaded into the info window.

When the “Get Directions” button is clicked, the form invokes the “getDirections()” function which makes a GDirections() call.

The details of using GDirections() is well described in the Official Documentation, so I’ll not repeat the information here.

From v2.124 you can specify walking directions and avoid highways by using the options {travelMode:G_TRAVEL_MODE_WALKING} and {avoidHighways:true}.

Potential Pitfalls

  1. GDirections() doesn’t currently support queries in the form “43.82589,-79.1004(My House)”.
    It does support the format “My House@43.82589,-79.1004”.
  2. For legal reasons, walking directions don’t work if you omit the second parameter when you create the GDirections() object. The API needs somewhere to put the disclaimer.

More advanced stuff

Google Maps API Tutorial

Marker Categories

If you have a map that shows several categories of markers, you may want to hide and show all the markers of one category at once, and remove the corresponding sidebar entries.

Here’s an example that does that.

The code makes use fo several of the techniques that have been described previously, which I’m not going to describe again.

The distinctive features of this code are:

Hiding and showing markers

The “hide(category)” and “show(category)” functions read through the array of markers and hide() or show() all the markers which match the specified category.

Dynamic sidebar

Rather than adding the sidebar entries as each marker is created, the “makeSidebar()” function is executed after all the markers already exist, by reading the gmarkers[] array. It omits entries for which gmarkers[i].isHidden().

The “makeSidebar()” function is called whenever the marker display changes.

Marker properties

Extra Properties are added to the marker Objects, to store the category and sidebar name information. This allows the “makeSidebar()”, “hide(category)” and “show(category)” functions to obtain all the information that they need from the marker Objects, rather than having to use some external list.

Checkbox settings

I can’t seen to get <input type=”checkbox” checked > to do exactly what I want, so I’ve added a line to the “hide()” and “show()” functions to ensure that the checkbox is checked and cleared.

In order for this to work, I’ve set the “id” of the checkbox that manages the “theatre” category to “theatrebox”, etc., so that the Javascript can easily obtain the name of the checkbox if it knows the category.

Associative array of GIcons

I’ve used an associative array to hold the GIcons, (as explained in the Associative arrays and Custom Icons tutorial). I’ve used the same names for the icons and categories.

More advanced stuff

Google Maps API Tutorial

“Did You Mean?”

The page will ask a “Did you mean?” question in response to an address search under certain circumstances. You might possibly want to offer the same functionality within the API.

Note: The API geocoder is quite different from the geocoder. Situations which cause the conditions which provoke a “Did you mean?” in one geocoder will not usually cause those conditions in the other geocoder.

Multiple Hits

One condition which provokes the question is when the geocoder returns more than one Placemark in response to the query.

To detect this situation with the API geocoder, simply test whether result.Placemark.length is greater than 1.
If it is greater than 1, then display a list of clickable options, using the Placemark.address values that are returned. Store the corresponding Placemark.coordinates so that they can be used to plot the marker when the user selects one of the alternatives.

Here’s an example

The API geocoder doesn’t often return multiple results. Bispham Road, Bispham, UK is one example address which currently produces three results in the API geocoder (because there really are three Bispham Roads in Bispham).

Potential Pitfalls

  1. Don’t store the Placemark.address and try to geocode that when the user selects it.
    Even though the geocoder returns that address, it doesn’t mean that it can geocode it if you pass it back.

Significantly Different Address

The geocoder may occasionally return an address which is sufficiently different from the requested address to be worth asking the “Did you mean?” question.

For example, if you search for Bespham Road, Cleveleys, FY2, the API geocoder will return a single result for Bispham Rd, Blackpool, FY2 0, United Kingdom, which you might consider to be sufficiently different to be worth querying.

This circumstance is much harder to test for. The returned address will often look very different from the query just because the geocoder converts the address into a standard format. In this case, the fact that “Gilford” has been replaced by the zip code “03249”; and the fact that “,USA” has been added; should not be considered to be “significant” differences in the address.

I reckon that it’s impractical to compare anything other than the house number and street. To go any further would require some fairly sophisticated AI to handle all the possible valid variations that might be used, and you’d almost need to write your own geocoder to check if “Gilford” actually is zip code “03249”.

My strategy for matching the street is to

  • Write a little function that converts words like “street”, “lane”, “north” etc. into standard versions “st”, “la”, “n” etc.
  • Split the reply at the first comma to obtain the part that specifies the street.
  • Convert the query and the reply to the same case, e.g. .toLowerCase().
  • Remove any apostrophes, so that “St Fred’s Rd” matches “St Freds Rd”.
  • Replace any other punctuation with spaces, since the geocoder ignores punctuation except that it separates words.
  • Replace multiple spaces with a single space, so that we now have a string of words separated by single spaces.
  • Split the strings into arrays of words.
  • Convert each word to the “standard” version.
  • Compare corresponding words from the query and the reply

Here’s an example

I’ve not done an awful lot of testing with this.

More advanced stuff

Google Maps API Tutorial

Setting the z-index of markers

It is possible to set the z-index of markers.

You might want to do this if your map tends to be rather cluttered with overlapping markers, and you want to highlight a particular marker, but normally find it to be partially obscured behind less significant ones.

To use the facility, you need to create a little function which will return an integer to be used as the z-index. Markers with higher z-index values are drawn on top of those with lower z-index values.

You can calculate the value from scratch, or you might modify the usual value, which can be obtained fromGOverlay.getZIndex(marker.getPoint().lat()).

The Function is passed two Parameters. The first Parameter is a reference to the marker and the second one always seems to be undefined at present.

Attach your function to the marker by using the GMarkerOption {zIndexProcess:myFunction}

You can’t change the order of the markers afterwards. The Function only gets evaluated when the marker is addOverlay()ed.

I don’t think you can pass a Parameter directly to the zIndexProcess Function, but what you can so is to add a Property to the marker before you addOverlay() it. The function can read that Property and use it in the calculation. For example, if you want important markers to be placed on top, then you can add a marker.importance property to each marker, and return higher z-index values for markers with higher importance.

More advanced stuff

Google Maps API Tutorial

Geocoding multiple addresses

If you’ve got lots of addresses to geocode, then you can’t just write a loop that calls the getAddress() function in the previous example several times. There are several problems:

  • Geocoding takes quite a bit of computer power. Let’s try to avoid needlessly wasting Google server resources.
  • If you send geocode requests to quickly, some of the requests will fail with code 620 “G_GEO_TOO_MANY_QUERIES”.
  • Geocoding your locations each time will make your page take longer to open. A geocode request can sometimes take as long as a second to be processed.
  • There are awkward pitfalls for the unwary coder when there’s more than one geocode request in flight at the same time.
  • Geocoders aren’t too smart. If the Google geocoder can’t find a good match for your address it will sometimes make guesses that a human would consider to be really silly. The geocoding database changes fairly rapidly, and it’s possible that an address that it guesses right today might go wrong next week. If you geocode once and check the results then you know that you’ve got the right place.

If your webhost supports server-side scripting, then I recommend finding a non-Google batch geocoder and using that. If not, you can do something like this example, calling the Google geocoder as each reply comes back and displaying the results in a format that you can copy and paste into an XML file.

The goeocoder quota is now set at 15,000 geocode calls per IP address per day. The old 1.725 second speed limit per API key no longer applies, but there’s a mysterious “limit to the maximum rate” mentionedhere and here. At the time of writing, it seems that a 100ms delay between geocode requests works well for long runs. Shorter delays case more 620 errors and may take longer to complete the retries.

If you use .getLatLng() for geocoding multiple addresses, then you won’t be able to recognise the 620 errors. I recommend using .getLocations() so that you can monitor the 620 errors. If you decide to use .getlatLng(), then I suggest using a considerably longer delay.

In my example, I start with a 100ms delay and increase the length of the delay each time I retry an address due to receiving a 620 error.

In my example, the list of target addresses is hard coded, but you could read the list from one XML file, pass forward all the attributes to your getAddress() function and include all the details in your output.

If you can run server-side scripting on your webserver, then you could send each result to your server to be stored in your database, but in that case you’d probably find it easier to use a non-Google batch geocoder that you could call directly from your server.

More advanced stuff

Google Maps API Tutorial

Geocoding with error handling

The mechanics of geocoding without error handling are well explained in the official documentation but the examples shown there do not include error handling.

Geocoding requests can fail for a variety of reasons, so it’s a good idea to check for successful completion before attempting to plot markers using the returned information.

It’s also helpful to display the failure reason to your users, where possible, otherwise your users might waste their time doing things like trying to guess the correct spelling of “Sauchiehall Street, Glasgow, UK”, if the problem isn’t the spelling but the fact that you’ve used up youir geocoding quota already.

What you can do is to set up an array like this

 var reasons=[];

 reasons[G_GEO_SUCCESS]            = "Success";

 reasons[G_GEO_MISSING_ADDRESS]    = "Missing Address: The address was either missing or had
                                      no value.";

 reasons[G_GEO_UNKNOWN_ADDRESS]    = "Unknown Address:  No corresponding geographic location
                                      could be found for the specified address.";

 reasons[G_GEO_UNAVAILABLE_ADDRESS]= "Unavailable Address:  The geocode for the given address
                                      cannot be returned due to legal or contractual reasons.";

 reasons[G_GEO_BAD_KEY]            = "Bad Key: The API key is either invalid or does not match
                                      the domain for which it was given";

 reasons[G_GEO_TOO_MANY_QUERIES]   = "Too Many Queries: The daily geocoding quota for this site
                                      has been exceeded.";

 reasons[G_GEO_SERVER_ERROR]       = "Server error: The geocoding request could not be
                                      successfully processed.";

and display the text associated with the reply.Status.code.

When using geocoder.getLocations(), accessing the status code is straight forward, see this example

When using geocoder.getLatLng(), the status code is not returned. You could follow a failed getLatLng() with a getLocations() request and examine its status code. There is a possibility that the status code might be different, in particular, if there was a server timeout on the first attempt, the second attempt might even succeed.

I recommend avoiding geocoder.getLatLng(). It might seem to make the coding easier, but the lack of error handling makes things more difficult in the long run.

Fitting the zoom

The geocoder now returns an ExtendedData section which gives details of a Lat/Lon box that can be used to zoom the map appropriately. Here’s an example.

In that example, if you search for “France” you get zoom level 6. If you search for “123 Avenue des Champs-Elysees, Paris, France” you get zoom level 16.

Note that the Lat/Lng box isn’t a perfect fit for the target location. It’s just there to provide sensible zooming.

Potential Pitfalls

  1. Be aware that geocoder requests are asyncrhonous. The request is sent to the geocoder and processing continues immediately without waiting for the reply to come back.Make sure that all the code that operates on the results is placed inside the event handler function.
  2. Don’t send lots of requests at once. There’s a rate limit on the number of geocode requests that you can send per second.

More advanced stuff

Google Maps API Tutorial

Toned map – New method

You can now change the colour tone of a map by overlaying partially opaque GPolygons over the map.

Things go slightly askew if you try to use a single polygon for the whole 360 degrees of longitude. Four polygons that span 90 degrees each works.

Things can also go wrong when a polygon crosses the International Date Line, but you can have one polygon that ends at 180 degrees and the next polygon starts at 180.000001.

Here’s an example


Polygons don’t wrap round the Earth. If you zoom out so that more than one copy of the Earth is visible, then only one copy of the Earth will be toned.

Draggable Markers and GTileLayerOverlays

Draggable markers

We added draggable markers as a hidden feature in revision 2.46, but today they are making their official debut as part of the Maps API. To make a marker draggable, set the marker option draggable to true when you declare it: var marker = new GMarker(center, {draggable: true});.

When you drag the marker, a little X will appear to mark the spot where the marker will fall. After you let go of the marker, it bounces into place (to let you know how much fun it’s having). If you prefer non-bouncy markers, set the marker option bouncy to false.

Try dragging this marker:


The Maps API has GTileLayers (which you can use to build GMapTypes) and GOverlays (which you can show on top of a Google Map). Today we’re introducing GTileLayerOverlays, which lets you add tile layers without needing to introduce new map types. You can also add & remove them on the fly, unlike map types.

For example, to create a desaturated map, you can overlay it with a white tile overlay at 50% opacity:

 var tilelayer = new GTileLayer(new GCopyrightCollection(), 0, 17); tilelayer.getTileUrl = function(tile, zoom) { // A more interesting tile server would do something with the numbers // tile.x, tile.y and zoom. return ""; }; tilelayer.getOpacity = function() {return 0.5;} map.addOverlay(new GTileLayerOverlay(tilelayer)); 

If you’d like to start testing these new features right away, don’t forget to add the v=2.x parameter to your<script> src, so you get the latest revision instead of the default v2.60.

API v1 Current: 1.31
API v1 Default: 1.31
API v2 Current: 2.61
API v2 Default: 2.60

More advanced stuff

Google Maps API Tutorial

Limiting the range

If you want to limit the range of zoom levels that the user is allowed to use on your map, you can overwrite the .getMinimumResolution() and .getMaximumResolution() methods of all the map types.

If you want to limit the range of movement that the user is allowed to use on your map, you can add listeners for the “move” event, and countermand any operations that go outside the permitted region.

Things happen so quickly that there’s no time for anything out of bounds to get plotted, so it all looks reasonably smooth.

Here’s an example

What I’ve done for the position is to specify an allowable region in GLatLngBounds() format, and require that the centre point lie within that region.

Potential Pitfalls

  1. Make sure that your permitted region is larger than the visible region at the maximum permitted zoom level.
  2. Allow enough room around the edge so that the automatic movements caused by opening infowindows partially off screen are not restricted.
  3. Don’t call map.setCenter() unless you are actually changing the values. E.g. if you omit the “if” statement around “map.centerAtLatLng(new GPoint(X,Y));”, then that operation itself would trigger a “move” event, and the code would go into a loop.
  4. Don’t try to use the .lat.hi. .lat.lo, .lng.hi and .lng.lo properties of the GLatLngBounds() object.
    The values are in radians, not degrees, so they can’t be used for recentering the view.

More advanced stuff

Google Maps API Tutorial

Standard Tooltips

It’s possible to add tooltips to your markers.

Here’s an example

The preferred syntax for GMarker() allows a set of options to be specified. One of these options is “title”, which specifies the tooltip text.

E.g. new GMarker(point, {title:"My House"});

These types of tooltip are plain text. You can’t apply any formatting to them. If you want to change the style, position or duration you’ll need to use custom tooltips.

Potential Pitfalls

  1. Don’t try to mix this syntax with the old GMarker() syntaxThis won’t work: new GMarker(point, myIcon, {title:”My House”}). When you use the new syntax, the options must be the second parameter, like this new GMarker(point, {icon:myIcon, title:”My House”}).

More advanced stuff

Google Maps API Tutorial

Dual Maps

It’s possible to have a pair of maps that track each other’s movements by using “move” or “moveend” listeners one each other and updating the position of the other map to match the one that moved. For example, a small zoomed-out map can be used as a dynamic thumbnail to help you see what the main map is zoomed in on.

The only problem is that updating the position of the second map cases “move” and “moveend” events, and we don’t want to attempt to process those events, otherwise the code goes into a loop and gets stuck.

All we need to do is to add a pair of extra variables that remember whether the maps are being moved by our own code, and only update the other map when this is not the case, i.e. when the map is being moved by the user.

In this example there’s a crosshair on the small map. This is just a marker which gets recentred every time either map moves.

Credit Thanks to Esa Ojala for posing the original question which led me to develop this technique.

The Basics

Google Maps API Tutorial

Google Chrome Considerations



Currently unsupported
3D Map Type
Async behaviour
Maps in new tabs
Poor developer tools
Polygon winding rule
Polygon fill order

Chrome is an awful lot faster at client side processing. In particular, it compiles Javascript code rather than interpreting it.
[But it’s possible that you ain’t seen nothing yet. TraceMonkey is rumoured to run Javascript faster.]

This doesn’t mean that every map page will run faster. If the critical factor is the amount of time it takes to fetch resources from a server then there’s not a lot that Chrome can do to improve the speed.

What’s acceptable is very subjective, and depends on the hardware and Internet connection speed as well as the browser. On my computer, I reckon that the number of displayed markers and polylines that make the browser unacceptably sluggish are:
MSIE: 100 markers or 10 polylines.
Firefox: 300 markers or 100 polylines.
Chrome: 2000 markers or 1000 polylines
Currently Unsupported

Google Chrome is not included in the current list of supported browsers.
Most of the Google Maps API functionality works under Google Chrome, and GBrowserIsCompatible() accepts it, but if you happen to stumble upon something that doesn’t work under Chrome, then there may not be a fix for it.

3D Map Type

G_SATELLITE_3D_MAP currently doesn’t work under Chrome. The Google Earth plugin is not recognised.
Async Behaviour

Chrome has more asynchronous behaviour than other browsers. In other browsers, the display only gets refreshed when Javascript pauses, but Chrome is designed to run Javascript and refresh the display at the same time. This might possibly be a problem if you depend on things happening in a predictable order.
One particular problem that you might encounter is that it’s possible for the Javascript to start executing before CSS files have been read. If your map container has its size specified in an external CSS file, Chrome may well execute the “new GMAp2()” command before the map container size is known. A moment later, Chrome processes the CSS information and changes the size of the container, but the GMap2 object has been created with the wrong size.

Maps in new tabs

There’s an occasional problem that results in a page that’s launched into a new tab to be centred incorrectly.
It seems to occur intermittently with the API, but it occurs consistently with For example If you shift-click this link in Chrome and wait a few seconds for the page to be rendered in the closed tab, then the marker gets placed in the correct location but the map is centred on Moscow instead of Blackpool.

Poor developer tools

At present there are no useful extensions for Google Chrome equivalent to things like Firebug, Venkman, HTMLValidator and WebDeveloper in Firefox.
I recommend developing your code in Firefox and testing it in Chrome, MSIE, etc afterwards.

Polygon winding rule

When you create an encoded polygon with holes, the holes may not appear when your map is viewed with Chrome.
This is because the API uses the CANVAS graphics technology to render polylines and polygons, and CANVAS has a different winding rule.

In the other three graphics technologies, a region of a polygon is filled if its winding number is odd, and left empty if the winding number is even. You can consider the “winding number” to be the number of times that the path winds round the region in a clockwise direction. So, if you have a clockwise outer boundary and a clockwise inner boundary, the inner region has winding number 2, and is rendered as a hole.

In CANVAS, a region of a polygon is filled if its winding number is non-zero, and left empty only if the winding number is zero. So, if you have a clockwise outer boundary and a clockwise inner boundary, the inner region has winding number 2, and is not rendered as a hole. To create a hole you need to have one path be clockwise and the other path be anticlockwise for the centre to be renedered as a hole.

Here’s an example

Similarly, if you have a polygon with a path that crosses itself, the interior will appear different in Chrome.

Here’s an example

The different winding rule is a basic feature of the graphic technology, and there’s nothing that the API can do about it.

Polygon fill order

The fill of a polygon appears to be performed above the polyline that forms its boundary, whereas with the other graphics technologies the boundary is placed on top of the fill. This can make the result look quite different.
Consider this example and this one

From time to time, Chrome will decide to update itself. Without asking.
During the update process, the computer becomes so sluggish that it’s effectively useless for doing anything else until Chrome has finished its update.

Chrome also seems to occasionally perform extensive housekeeping tasks. At least I guess that’s what it’s doing. Every so often it will grab 100% of the CPU for about 10 minutes without any Internet I/O. The only way I’ve found to do any other work on the computer is to close Chrome, then, when I don’t want to use the computer for a while, restart Chrome and let it do its thing.

The Basics

Google Maps API Tutorial

Using a percentage height for the map div

If you try to use style=”width:100%;height:100%” on your map div, you get a map div that has zero height. That’s because the div tries to be a percentage of the size of the <body>, but by default the <body> has an indeterminate height.

There are ways to determine the height of the screen and use that number of pixels as the height of the map div, but a simple alternative is to change the <body> so that its height is 100% of the page. We can do this by applying style=”height:100%” to both the <body> and the <html>. (We have to do it to both, otherwise the <body> tries to be 100% of the height of the document, and the default for that is an indeterminate height.)

The <body> also has a margin by default. If you want to switch that off, add “margin:0” to the <body> style, to push the body right out to the edge of the screen.

In this example I’ve set the map div height to 96% to leave room for the back link.

The Basics

Google Maps API Tutorial

Fitting the map to the data

The map.getBoundsZoomLevel() method allows us to calculate a zoom level setting that fits a set of markers.

Here’s an example

It is necessary tp perform a map.setCenter() call before starting to add markers. At this point we don’t know the real parameters for the call, so just use any values.

   map.setCenter(new GLatLng(0,0),0);

Create an empty GLatLngBounds() object.

   var bounds = new GLatLngBounds();

Each time a point is read, extend the bounds to include that point.


When all the points have been processed, the zoom level can be set to fit the points.


The centre can be obtained by using the bounds.getCenter() method


Potential Pitfalls

You can’t mix v1 compatibility mode and v2 native syntax in this code. You do need to use a GLatLngBounds(), not a GBounds(), and you can only use a GLatLng in its .extend() method, not a GPoint.

The Basics

Google Maps API Tutorial

Tabbed Info Windows

The API makes it easy to use tabbed info windows.

Here’s a simple example

Here’s a more complicated example  in which each info window has a different number of tabs.

The map.openInfoWindowTabs(), map.openInfoWindowTabsHtml(), marker.openInfoWindowTabs() and marker.openInfoWindowTabsHtml() methods allow tabbed info windows to be created. All the tab switching is done for you by the API.

To use openInfoWindowTabsHtml() to create a tabbed window, you need to pass it an array of GInfoWindowTab() objects. One such object for each tab.

The GInfoWindowTab() constructor is: new GInfoWindowTab(label, contents, onClick)
The label is a string that will be displayed on the tab. It holds 10 characters.
The content is a HTML DOM element (openInfoWindowTabs) or a string containing HTML (openInfoWindowTabsHtml).
I don’t yet know what the onClick does. It looks like it’s supposed to be a function that gets called when the tab is clicked, but I can’t get it to work.

You can have as many tabs as you like, but if you have more than two the graphics fall apart if the info window isn’t wide enough. Make the info window wider by using a style “width:XXXpx” in the GInfoWindow() contents. The width needs to be at least 88 pixels per tab.

Potential Pitfalls

  1. The size of the info window is calculated beforehand to be large enough to hold the largest of the contents. If you’ve got one panel that needs a very large info window then another panel that has very little content will look rather strange.

The Basics

Google Maps API Tutorial

Maps in articles

If you’ve got a page that contains significant information other than the map, then you won’t want to deny access to people using browsers that are not compatible with the Google maps API or which have Javascript turned off. They might be interested to read the text in the rest of the page

What you can do is to remove the <noscript> section, and the alert that you’d normally throw for a non-compatible browser. Instead, initially create the map <div> with zero size, like this.

<div id=”map” style=”width: 0px; height: 0px”></div>
Then, in your Javascript, if the browser is compatible, change the width and height to the required size just before creating the GMap.

var m = document.getElementById(“map”); = “400px”; = “500px”;
var map = new GMap2(m);
If you switch off Javascript and reload this page, or view it in a non-compatible browser, you’ll still be able to read all the text and see everything else on the page, but there will be no map, and no empty space where the map would have been.

The Basics

Google Maps API Tutorial

Onload function and external controls

If you’ve been following this tutorial, adding external controls that affect the map is straight forward. If, however, you made an initial decision to put all the API Javascript code into an onload function you’ll need to make some of your variables and function declarations global.

Any variables and functions that are declared local to the onload function cannot be accessed from click or mouseover events that occur on html elements outside the map.

Here’s an example of using external controls to change the zoom level of the map.

All the functions which are called from clicks and mouseovers on html elements need to be global. That just means that you need to place the function declarations outside the onload function, like this

    // This function zooms in or out
    function myzoom(a) {
      map.setZoom(map.getZoom() + a);

    function onLoad() {

Similarly, any variables which are used by those global functions need to be global. That just means making the initial declaration of the variables before any function declarations, and then populating those global variables in your onload function. In this particular example, the myzoom() function needs to use the “map” variable, and the myclick() function which handles the sidebar needs to access the gmarkers[ ] and htmls[ ] arrays.

   <script type="text/javascript">
    var gmarkers = [];
    var htmls = [];
    var map;

    function myclick(i) {
    . . .

Potential Pitfalls

  1. Remember that “onload()” in all lower case behaves like a reserved word in Firefox. Use a different name or change the capitalisation.
  2. Clicks and mouseovers in the html inside an info window are “external” events. Any functions or variables they use also need to be global.
  3. Don’t re-declare your variables inside the onload function. Make the onload function use the global variables rather than creating it’s own local variables with the same name. I.e. change the
            var map = new GMap(document.getElementById("map"));


            map = new GMap(document.getElementById("map"));
  4. Beware free webhosting services that make their money by adding adverts to your pages. Some of them set their own onload function, using “window.onload”, which overwrites the onload setting in <body onload>.You may need to reset “window.onload” after their header code has executed, and then might be contractually obligated to call their olnload function as well as your own. The details vary depending on the webhosting service. Some of them go to considerable lengths to make this sort of thing very difficult because they don’t want people to be able to remove the adverts.

The Basics

Google Maps API Tutorial

Get Directions

One way to offer directions is to throw a request at, and have the results be displayed on a real Google map. You can arrange for the resulting Google map to be displayed in a new window.

It is now also possible to display directions on your own map, see this advanced tutorial

Here’s an example that uses XML.

Here’s another example that doesn’t use XML.

There are initially two links: “To here” and “From here”. Clicking these links opens a corresponding form. Submitting the form throws a request to in a new browser window.

When the data is read from the XML file, the HTML for each of the three different info window displays for each marker are generated and stored in two new arrays to_htmls[ ] and from_htmls[ ]. The initial version of the info window, one with the “To here” form and one with the “From here” form. When the “To here” or “From here” link is clicked, the corresponding html string is loaded into the info window.

It’s also possible to add text, in brackets, to the end of the &saddr or &daddr parameters and have that text be displayed in bold on the Google Local page, instead of having the latitude and longitude appear in bold.

If you want something simpler, you can just provide a link to Google Maps with an empty &saddr. When Google Maps loads, it will display the &daddr destination, and allow the user to enter the start address.


Google maps now supports multi-point routing. You can request multi-point routes by adding “+to:+” followed by subsequent destinations to the end of the &daddr string.