More advanced stuff

Google Maps API Tutorial

Icon Sprites

This undocumented feature can be used to improve the page loading time in situations where you use several different custom icons.On the left is a single image file that contains ten icon images. GIcon.sprite allows the individual icon images to be cropped out from that image and used as marker icons.Here’s an exampleThe syntax looks like this:

   var myicon = new GIcon();
   myicon.sprite = {image:"sprites.png", width:20, height:34, top:68, left:0}
  • image: the URL of the sprite file. Countermands any existing .image setting.
  • width: the width of the icon. Ignored if the icon already has a .iconSize setting.
  • height: the height of the icon. Ignored if the icon already has a .iconSize setting.
  • top: the vertical pixel position within the sprite file where this icon image starts.
  • left: (optional) the horizontal pixel position within the sprite file where this icon image starts.

It is possible to have sprites of different sizes in the same sprite file.It is possible to use 2D arrays of icon images since v2.141, using “top” and “left” to specify the position.

Thanks to “Esa” for spotting the details of this feature.

Advertisements

More advanced stuff

Google Maps API Tutorial

GGeoXml with sidebar

GGeoXml doesn’t expose the content that it adds to your map, so you can’t directly obtain information on its markers from Javascript. However, in some circumstances, it is possible to intercept the addoverlay events which are generated when it adds the markers to the map.

Here’s an example

Warning: This trick relies on undocumented details of the GGeoXml behaviour. It may well fail to work in future releases.

Other, more stable, methods for creating a sidebar are EGeoXml and GeoXml

When GGeoXml adds overlays to a map, an “addoverlay” event is triggered. It’s possible to catch that event, extract information about that overlay, and build a sidebar.

This is possible because GGeoXml adds extra accessible properties to the overlays that it creates. These properties currently are:

  • name
  • description
  • snippet
  • hiddenInStream
  • id
  • parentFolderForCallbackOverlayAddTimeout
  • parentGeoXml

The property that we’re interested in for building the sidebar is “name”.

Problems

Overlay management

GGeoXml now performs some sort of crude overlay management on the overlays it creates. This causes the overlays to be removeOverlay()ed when they leave the viewport and addOverlay()ed again when they re-enter the viewport. We need to be careful not to add an extra side bar entry for the overlay each time it re-enters the viewport.

In my example, I add my own extra property to the overlay to remember that I already know about the overlay.

Sidebar order

GGeoXml adds the overlays in random order. If you need the overlays to appear in alphabetical order of name, then you have to sort them yourself.

Long Sidebar

There’s no indication of whether a particular overlay is the last one in the KML file. This means that the usual technique of building the sidebar HTML until the last entry has been processed won’t work. In my example, I add each sidebar entry one by one. If there are large numbers of overlays, this becomes inefficient, since the browser has to re-render the sidebar div after each entry has been added.

I can’t think of any nice way around this, unless you happen to know how many overlays your KML file contains.

Intercepting infowindow content

Another trick that you might want to perform with GGeoXml is to modify the content of the infowindows that it manages. For example you might want to add a “get directions” facility at the bottom, or you might want to have active content in the info window.

It turns out that modifying overlay.description doesn’t modify the contents of the infowindow. The data must be being stored somewhere else before the “addoverlay” event gets triggered.

What you can do is to listen for the undocumented map “infowindowprepareopen” event, and modify the content of the GInfoWindowTab that it passes as a parameter.

In my example I simply add an extra line of text at the bottom of the infowindow contents.

You could place deactivated content in a KML file, perhaps using [ and ] instead of < and >, so that GGeoXml will allow the text to pass through, and then replace those characters as the infowindow opens.

More advanced stuff

Google Maps API Tutorial

Using Cookies

If you want your map to remember its state when the user leaves the page and comes back, then you can write the information into a cookie.

A cookie is a small piece of text that you can ask the user’s browser to store for you so that you can read it back the next time the user visits your page.

You might think that you’d need to write the cookie information every time the map state changes, but in fact you only need to do it when the page exits. Instead of having the onunload=”GUnload()” you can write onunload=”setCookie()” and have your setCookie() function call GUnload() when it has finished.

A cookie can have an expiry time, after which the browser will delete the cookie. A browser with no expiry information is a “session cookie” and will disappear when the current browser session ends.

The cookie is stored by the user’s browser. It is not stored on a server. Each user can have their own stored settings. If the user uses a different browser or a different computer, they will not get the same cookie.

In this example the page will remember the lat, lng, zoom and maptype for seven days.

Although the cookie code doesn’t look too difficult, it seems to have a tendency to be awkward to debug. That’s why I’ve put it in the “Advanced” section of the tutorial.

More advanced stuff

Google Maps API Tutorial

Geocoding Low Quality Data

The technique for geocoding multiple addresses shown here is OK if your original address data is of reasonable quality.

I recently attempted to geocode a file of locations that had been collected in my family history system, and found that that mechanism wasn’t appropriate. Much of the data was collected from hand written documents, some of which are over 200 years old, so there are transcription errors. Some of the original information was obtained from illiterate individuals and uses strange spelling. Some of the streets no longer exist. Some of the towns have changed their names, e.g. “Layton with Warbreck” is now “Blackpool”. Some of the county boundaries have changed, e.g. the geocoder can’t find “Bowness, Westmorland” because it’s now in Cumbria.

I also seem to have hit several towns for which the geocoder has wildly inaccurate information. I have ancestors who came from Lytham, Charnock Richard, and North Meols.

My data was so bad that any sort of fully automated process would be useless. Every location needed to be looked at to see if the result is reasonable. Since I don’t actually know where the locations should be, I couldn’t drag a pointer to the correct locations. All I could do was to try modifying the address until the geocoder returned something sensible.

I ended up using two maps, one at street level and one zoomed out far enough so that I could see that the street was in the right town. I found that more convenient than zooming in and out with a single map.

Below the maps, I placed a control area in which the .getLocation() results could be displayed and selected, and the address text could be modified.

I display the results in XML format so that I can copy and paste the data into the final XML file. If your webhost supports server side scripting, you could post the data directly to your server to be stored in your online database.

I ended up with this

More advanced stuff

Google Maps API Tutorial

Context Menus for Overlays

Although there is an undocumented “singlerightclick” event for markers and some other overlays, it’s safer to use the documented map “singlerightclick” event because undocumented features could be changed or removed in future API releases without notice.

When using the map “singlerightclick” event to handle overlays we need to examine the third argument that gets returned. This argument indicates the overlay on which the click occurred.

You might want to display different options depending on the type of overlay that was clicked. You can test the type of overlay like if (overlay instanceof GMarker) {…}

At present, if a right click occurs on an infowindow, no “singlerightclick” event is generated.

Any variables that are going to be needed by the menu functions need to be held in global variables, because clicks on the menu are executed in global context. In particular, that means that we need to store a global reference to the overlay on which we opened the menu.

More advanced stuff

Google Maps API Tutorial

Context Menu

You may have noticed that maps.google.com now has a context menu that can be invoked with a right click.

It is possible to create context menus for your own API maps by using the “singlerightclick” event.

Here’s an Example

There’s nothing in the API to help you construct the context menu itself. You have to write your own code to do that.

What I basically do in that example, is to create a hidden div that contains the menus and has onclicks that each call a global function. When the user right-clicks the map, the “singlerightclick” event is triggered, and it returns a GPoint() indicating the pixel position of the click relative to the map container.

When the code detects such an event, I:

  • Store a copy of the pixel position in a global variable, in case any of my global functions need it later.
  • Check if the position is close to the right or bottom edge and adjust the context menu position accordingly.
  • Create a GControlPosition() for the position of the context menu.
  • Apply() the GControlPosition to the context menu div.
  • Make the context menu visible

Whenever there’s a click on one of the menu options, I perform the requested event, and then close the context menu.

Whenever there’s a click on the map or one of its clickable overlays, I close the context menu.

If one of the functions needs to know where on the map the right-click occurred, I read the pixel location from the global variable and convert it to a GLatLng with map.fromContainerPixelToLatLng().

The strange looking divs inside the links are there to make the whole width of the menu clickable.
Without them, only the actual text would be clickable.

You can obviously get your context menus to contain whatever options you want. You could even generate the .innerHTML of the context div dynamically when the right click occurs, with different options depending on the context.

Bits I’ve not figured out yet

  • The maps.google.com context menus close when the mouse exits the map container outwards, but not when it exits the map container by hovering over the context menu.

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

A Custom Slider

Writing a custom slider isn’t particularly easy. I’m not going to go through the code in detail. I’ll just give you two versions of some example code that have lots of comments, and point out some of the interesting points.

Here’s an example that doesn’t work correctly in MSIE6.

Here’s an example that works correctly, but is a little less readable.

The code is the same apart from the extra code to pass transparent PNG files to the AlphaImageLoader. If you’re using GIFs, JPEGs or PNGs without transparency then use the first version.

The Javascript that drives the slider is here and here.

Points of Interest

In the GEvent callback functions, the “this” isn’t a reference to our GControl, but we do need such a reference. We could set up a global variable to hold the reference, but then things would go wrong if we had two maps using the same slider code on the same web page. My solution is to store a reference to the “this” in a local variable on which we obtain Function Closure.

When the zoom level is changed by other code, we have to move the contents of the GDraggableObject by setting its style.top, and we also need to set the .top property of the GDraggableObject itself, so that the API knows about the new position.

When the user moves the slider, YSliderControl.setZoom() changes the zoom level of the map. This then triggers a zoomend event, which then triggers YSliderControl.setSlider(). I hadn’t really indended for that to happen, but it actually turns out nice, because if the user moves the slider to an inermediate position, the .setSlider() call causes the slider to snap to the exact position that represents the selected zoom level.

The slider movement performed by .setSlider() doesn’t trigger a dragend event on the GDraggableObject, so we don’t get into a loop.

More advanced stuff

Google Maps API Tutorial

Testing if a polygon contains a point

Version 2.88 introduces the polygon and polyline “click” events which make all the following redundant.

They also make it not work, since the the click event passed through the polygon to the map contains the polygon object reference ratgher than the point clicked.

Versions prior to v2.88

I’ve adapted some code that I found at alienryderflex.com into a Javascript method that tests whether a point lies inside a polygon or polyline.

The code is implemented as GPolygon.Contains(glatlng) and GPolyline.Contains(glatlng), which return true if the point is inside and false is the point is outside.

Here’s an example

[The XML file that it uses is here]

The method works for closed or open polygons and polylines. If the shape is open, it considers there to be an additional virtual line from the last point to the first.

If the point happens to lie exactly on the boundary of the polygon, then it may return either true or false.


The method gets pretty slow if you’ve got lots of complex polygons. But then again, plotting lots of complex polygons in the first place is pretty slow.

This example draws 50 polygons representing the states of the USA, using data loosely based on the SQL data posted by “Wabler”
there are 50 polygons with 3372 nodes between them occupying 139kb of XML data.

I reckon that that example is acceptable under Firefox on a fast computer, and that the speed under MSIE isn’t acceptable. You might think otherwise, depending on what you consider acceptable.

The initial loading time is affected by the size of the XML file and the speed at which the API draws polygons.

The zoom time is affected by the speed at which the API draws polygons.

The click response time is affected by the speed of my GPolygon.Contains() method, which I believe to be proportional to the total number of nodes being scanned. (I deliberately didn’t optimise the code by jumping out of the loop when a match is found, so that you can get a true feel for the natural speed of the algorithm.)

It would be possible to make further optimisations to the click response (e.g. by checking states whose centres are close to the click point first and jumping out of the loop when a match is found) but I don’t think that there’s anything that can be done about the slow zoom redraw when using lots of complex GPolygons.

More advanced stuff

Google Maps API Tutorial

Fixing the ‘inherited CSS’ problem

If you have cascading styles that apply to your map div, then those styles will be inherited by objects attached to the map. That can cause a problem with the info window contents.

The size of the info window is calculated before the info window is appended to the map, so it’s calculated with the text at the default size and with the default font. When the info window is appended to the map, the styles are inherited and the actual size of the contents may change.

If you don’t want to remove the inherited style, or can’t remove it because it’s part of something like a Blog template, then you need to countermand or duplicate the significant parts of the style in the html that you put in the info window.

E.g. if the map div is inside a container div that has a style #container {font-size:15pt;} then you could create a style to countermand that, like .iwstyle {font-size:20pt;} and wrap all your info window text inside <span class=’iwstyle’> … </span>.

Styles applied directly to the info window contents in this way are applied before the size is calculated, so the info window will then fit the contents. In this  example  the marker on the left inherits the container style, but the marker on the right has an info window that is sized correctly.

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

Restriction

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:

GTileLayerOverlays

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 "http://kml.lover.googlepages.com/white_map_tile.gif"; }; 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

GIcon.label

This feature allows you to build your icon from a background image plus a foreground label. Prior to the introduction of this feature, if you wanted to have markers with different letters and different background colours, you had to create a separate icon image for each combination.

There’s an example here.
Observe the way that the marker background and label are fetched separately when the images are not in cache (use shift-reload to see the effect if the images are already in your cache).

The syntax is a bit weird. The GIcon.label is an object of unnamed class that must have the following properties:

url string The URL of the image to be used as a label.
anchor GLatLng() or GPoint() The position offset of the label image relative to the main image.
size GSize() The pixel size of the label image

If you use a GLatLng() for the anchor the parameters are (down, right), but if you use a GPoint() the parameters are (right, down).

The easiest way to set things up is to have the label be the same size as the icon image and set the anchor to GPoint(0,0), but it’s more efficient to make the label smaller and use the size and anchor parameters to position it.

You can set the GIcon.label directly, like this

   var Icon = new GIcon();
   Icon.label = {"url":"overlay.png",
                 "anchor":new GPoint(4,4),
                 "size":new GSize(12,12)};

or you can use the new additional parameter of the GIcon() constructor, like this

   var mylabel = {"url":"overlay.png",
                  "anchor":new GPoint(4,4),
                  "size":new GSize(12,12)};
   var Icon = new GIcon(G_DEFAULT_ICON, "marker.png", mylabel);

Potential Pitfalls

  1. Using a GIcon.label interferes with the click processing of markers that don’t have a .transparent or .imageMap, because the label image would then be the top image in the stack. Therefore, if you use GIcon.label on a clickable label you must set up a .transparent and a .imageMap.

More advanced stuff

Google Maps API Tutorial

Making your own custom markers

To make custom markers, you’ll need a graphics program that supports partially transparent PNG images.

I’ve used Paint Shop Pro 8. In that program there’s just one special thing to watch out for: don’t use “save”, use the PNG optimiser and specify “Alpha Channel Transparency”.

To create a custom shadow, take a copy of your marker, change all the opaque points to black, reduce the opacity to about 50%, skew the image by about 45 degrees and add some blur.

I’ve also used POV-Ray to create custom 3d markers. Use the switches “+ua +fn” to create a partially transparent png file.

To create a shadow image in POV-Ray, I applied the keyword “noimage” to my marker object, and allowed the shadow to fall on a tilted plane

   plane {y,0 pigment {rgb 2} rotate -x*45}

In this case it’s not possible to make the plane transparent directly from POV-Ray, the white plane needs to be removed with a suitable image editor.

GIcon.transparent

To create a transparentImage for your custom marker, simply load the marker image into an image editor, change the opacity to 1% and export the result as a 24-bit partially transparent PNG. The transparentImage allows the marker to correctly respond to clicks and mouseover events in IE when the marker is under a shadow. You can omit the transparentImage, in which case clicks and mouseovers will not work correctly for icons that are in shadow.

GIcon.imageMap

To get shadowed markers to correctly respond to clicks in other browsers, you need to create an imageMap. That’s a lot trickier, and I’m not going to cover that in this tutorial.

GIcon.printImage and GIcon.mozPrintImage

Most browsers can’t print partially transparent PNG images. Google has provided the option to provide printable GIF images instead. If you want your markers to be printable you can create the required GIF images by converting your PNG images to GIF format. For the GIcon.printImage Google a GIF with transparency that matches that of the PNG image. For GIcon.mozPrintImage Google use a non-transparent GIF with the background set to a pale grey.

In my experiments, Firefox seems to print the transparent GIFs just as well as the non-transparent ones, it just ignores the transparency when printing.

If you omit the GIcon.printImage and GIcon.mozPrintImage settings, then IE6 will print the main PNG images of the markers (ignoring the transparency) but Firefox will omit the markers from the print.

GIcon.printShadow

If you want shadows to be printed you need to provide a GIF image for this parameter. GIF images don’t support partial transparency, but it is possible to dither the image, like this dithshadow.gif image used by Google

Third Party Markers

If you don’t want to draw your own markers, you can find some markers that other people have made.

There’s a list of available marker sets here: http://mapki.com/index.php?title=Icon_Image_Sets

Here’s an example that uses the finger icon.

    var Icon = new GIcon();
      Icon.image = "mymarker.png";
      Icon.iconSize = new GSize(20, 34);
      Icon.shadow = "myshadow.png";
      Icon.shadowSize = new GSize(36, 34);
      Icon.iconAnchor = new GPoint(5, 34);
      Icon.infoWindowAnchor = new GPoint(5, 2);
      Icon.transparent = "mytran.png";
      Icon.printImage = "mymarkerie.gif";
      Icon.mozPrintImage = "mymarkerff.gif";
      Icon.printShadow = "myshadow.gif";
      Icon.imageMap=[9,0,6,1,4,2,2,4,0,8,0,12,1,14,2,16,5,
       19,7,23,8,26,9,30,9,34,11,34,11,30,12,26,13,24,14,21,
       16,18,18,16,20,12,20,8,18,4,16,2,15,1,13,0];

This enlarged image shows the anchor points used for this marker.

  • Icon.image is simply the name of the main image for the marker.
  • Icon.iconSize is the pixel size of the Icon.image
  • Icon.shadow (optional) is the name of the shadow image.
  • Icon.shadowSize is the pixel size of the Icon.shadow
  • Icon.iconAnchor (indicated by the red pixel) is the pixel which will be placed on the specified geographical location. You can think of it as the point that the image points at.
  • Icon.infoWindowAnchor (indicated by the green pixel) is the pixel that the tip of the info Window stem will touch.
  • Icon.transparent (optional) is the name of a transparent image used for capturing clicks and mouseovers in MSIE.
  • Icon.printImage (optional) is the name of a GIF file to be used for printing.
  • Icon.mozPrintImage (optional) is the name of a GIF file to be used for printing from the Mozilla/Firefox browser.
  • Icon.printShadow (optional) is the name of a GIF file to be used for printing the shadow.
  • Icon.imageMap (optional) is an array of integers representing the x/y coordinates of the image map used fopr capturing image clicks in non-IE browsers.

Note that the Size parameters are specified as GSize objects and the Anchor parameters are specified as GPoint objects.

In both cases, the values count from the top left corner which is considered to be the point (1,1). This may well be different from what you see in an image editing program, where the top left pixel is usually considered to be (0,0).

Positioning the infoWindowAnchor is an art rather than a science. You can see when it looks wrong but there doesn’t seem to be any formula that will produce the result that looks right.

Required parameters

They’re not actually mandatory. There are certain strange circumstances in which you might want to omit almost any parameter, but you should think carefully before omitting any of these parameters:

  • iconSize : without it your marker will have zero size and be invisible
  • iconAnchor : without it your marker won’t appear in the correct place
  • infoWindowAnchor : without it marker.openInfoWindow() will fail
  • transparent : without it your marker won’t be clickable in MSIE if there’s an info window shadow nearby
  • imageMap : without it your marker won’t be clickable in Firefox if there’s an info window shadow nearby

Graham’s Custom Marker Maker

If you don’t want to go through all the bother of doing that yourself, then simply create the main image file and pass it to Graham’s Custom Marker Makerhttp://www.powerhut.co.uk/googlemaps/custom_markers.php. You get back a zip file that contains files for all the other parts of the GIcon and some sample code with all the parameter settings.

More advanced stuff

Google Maps API Tutorial

Distances

The API provides a function that calculates the straight line distance between two points.

To use it to calculate the distance in metres between two GLatLng objects, we writeP1.distanceFrom(P2).

That returns a number with the maximum number of decimal places that your Javascript interpreter supports, which is somewhat excessive so you might want to use the .toFixed(N) method to round the result to N decimal places.

Here’s an example of the code in use.

For the bearing, I’ve used an algorithm by Vincenty which is claimed to produce better results than using atan2() when the distances are large.

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.

The Basics

Google Maps API Tutorial

Using different languages

Normally, the text used by the API will be in US English.

The API will look through the preferred languages in your browser options and use the first language that Google supports.

If you wish to specify a particular language, then you can add a “hl” parameter to the line that loads the API code, e.g.
src=”http://maps.google.com/maps?file=api&amp;v=2&amp;hl=de&amp;key=abcdefg” 
The official list of supported languages is kept here: code.google.com/support/bin/answer.py?answer=60737&topic=12267

A slightly different, but equally official, list is available on this spreadsheet, in the “localized” column.gmaps-samples.googlecode.com/svn/trunk/mapcoverage_filtered.html

Directions details

When you use GDirections, by default, the route description text will be in the same language as that used in the API as described above.

If you want to use a different one of the supported languages, then you can add the locale option to your .load() or .loadFromWaypoints() call, e.g. gdir.load(“from: Detroit to: Dayton”, {locale:”fr”});.

Other languages

If you want to use a language that’s not yet supported by Google then you can do it like this example. All I’ve done there is replace the Google text with the same text in upper case. To use a real language, you’d need to translate all that text into your chosen language.

The way it works is that the API loader uses GAddMessages() to pass the language specific text strings to the main API code. We can overwrite those text strings with our own GAddMessages() call.

However, the GMapTypes are created before the loader returns control to our code. We can performGAddMessages{10050: ‘SATELLITE’} in our own code, but it’s too late to affect the map type. What we can do, however, is create our own map types which are clones of the Google map types except that they use different texts in the “name” parameter and in the “shortName”, “errorMessage” and “alt” options.

Copyright texts associated with a map type are generated in a server, so they’re not accessible by that method. What we can do is write our own custommap.getCopyrights() method which reads through the prefixes and replaces any occurrences of “Map Data” and “Imagery” with the equivalent in the desired language.

There’s no reasonable way to produce GDirections details in unsupported languages, because that’s performed in the direction server.

The Basics

Google Maps API Tutorial

GgeoXml

The GGeoXml() feature creates an overlay from a KML file.

You use it like this:

   var kml = new GGeoXml("http://mydomain.com/myfile.kml");
   map.addOverlay(kml)

Here’s an example

Potential Pitfalls

  1. The KML file must be accessible by the Google KML rendering server. It won’t work with KML files on your local computer. You don’t get any error messages if the KML file is inaccessible.
  2. GGeoXml overlays don’t support .hide() and .show() methods. To hide them, use map.removeOverlay().
  3. Calling map.removeOverlay() on a GGeoXml overlay recovers the memory used, and destroys the information. Once you use map.removeOverlay(xml), you have to re-create it before calling map.addOverlay(xml) again.

The Basics

Google Maps API Tutorial

Lots of sidebar entries

If you’ve got lots of sidebar entries, then a simple sidebar can end up being much longer than the height of the screen.

Here are four ways of handling the situation:

1. Sidebar with a scroll bar

Simply add “overflow:auto;” to the sidebar <div>, and specify a height for it.

example.

2. Multiple sidebars (a)

Create several sidebar divs in your HTML, and then use the modulus operator “%” to decide which sidebar to use for each entry.

example.

3. Multiple sidebars (b)

If your data is sorted into a useful order, you might prefer the data to be arranged vertically. You can distribute the entries into three vertical columns by dividing the entry number by a third of the total number of markers. You need to take a bit of care with Math.ceil() and Math.floor() to avoid the possibility of the final marker being placed in the next column.

example.

4. Select box

Instead of a sidebar, you can use a <select> box.

The action is then controlled by an “onChange” event on the <select> element, rather than an “onClick” event on each individual entry.

To obtain the marker number from the “onChange” event use the .selectedIndex property of the <select> box.

I like to have a dummy <option> in my <select> boxes that tells the user what to do, and I arrange for this to be treated differently in the onChange handler.

The Basics

Google Maps API Tutorial

The AJAX Philosophy

Note: It’s possible to use the basic philosophy behind AJAX without necessarily using ajaxslt.js or any of the specific technologies that happen to be mentioned in the AJAX acronym (Asynchronous Javascript And XML).

The fundamental philosophy of AJAX is to load the static parts of your webpage only once, and request a small amount of information about the bits that change from a server. This makes changing the displayed data much faster.

My new website does have PHP, so this example now uses it. Clicking the [A], [B] and [C] buttons causes the current markers to be cleared and a fresh set of information to be loaded from the corresponding XML file.

Here’s what the source of the PHP server script looks like PHP script, and here’s the server script actually running: map11.php?q=amap11.php?q=bmap11.php?q=c.

In a real application you could use a server side script to respond to a query based on user input or map position. The Javascript would construct a query string to be sent to the server containing all the relevant information and pass a URL something like ‘myserver.php?lat=-123.5&lng=44.2&zoom=11&selection=houses,schools,hotels’. The server would search the database for location information that matches the query and output XML code rather than HTML.

Here’s a really simple PHP script that reads this data file, finds the marker that’s “nearest” to the ?lat= &lng= parameters that are passed in the URL, and outputs the data as XML. [Somebody with more experience in PHP could probably write it a lot better than that].

Here’s that script in action map11a.php?lat=43.85&lng=-79.1

Non-AJAX Philosophy

There’s a temptation to write server side PHP or ASP script to generate the whole HTML page from scratch every time the data changes, generating a whole new set of HTML with the location specific information coded into Javascript commands. It works, but it means that a lot more data has to be sent from the server to the client, and the whole of the API code has to be reloaded, and, depending on the browser settings, it may mean that some or all of the map tiles, marker images and web page images get reloaded.

You can see the difference by reloading the example page, instead of clicking on the [A] icon.