GoogleMappers.com

  Huh?

  Other Articles

GxMagnifier is a free add-in control for Google Maps that creates a moveable, magnified window on top of your map. It was developed by Richard Kagerer, and is not supported by Google or part of their official API. Please note that GxMagnifier is beta software, so there may be some bugs. If you run into anything, report it in the Google-Maps-API forum. I try to keep an eye on messages there, but chances are one of the other members will be able to respond with a fix faster than I can.

In case you're curious, I got the idea of trying to make a magnifier control for Google Maps after watching a concept video that shows a similar feature in mapping technology demonstrated under Windows Longhorn (a.k.a. Vista).

Update: GxMagnifier has not yet been upgraded to version 2 of the Maps API, and unfortunately Google has dropped support for version 1. You may also want to take a look at http://boxme.net/wdch/test.php, where two other members of the Google Maps community have happily startled me by independantly adopting the original codebase and worked toward upgrading it. The performance is slow but much of the basics are functional.

Contents

Download

Introduction

    The "Hello World" of GxMagnifier
    Google Maps API Compatibility
    Browser Compatibility
    Legal
    10 Second Peek Behind the Scenes
    Updates
    Known Bugs

Framework for Examples

Examples

    Zooming
    Resizing
    Capturing the Click
    Moving the Button
    Automatically Panning
    Docking
    Consolidating Waypoints
    Skinning
    Starting with a Blank Map
    Multiple Magnifiers and Negative Zoom

API Overview

Class Reference

    GxMagnifier
    GxMagnifierControl
    GxControlPositionHack

Download

Before you get started you'll need to download these files and place them in the same directory as your web page.

GxMagnifier.1.js (09-Oct-2005, tested on maps.23.js)

magnify.png, spacer.png and optionally magnify2.png

If you want to poke around in the source code, you'll need GxMagnifier.1.source.js as well, and may find this generated overview of functions handy.

Note: The obfuscated GxMagnifier.1.js was compiled using a very basic find-replace program that I wrote. The output appears correct and works with the examples, but if you run into troubles try using GxMagnifier.1.source.js instead, and let me know.

Introduction

The "Hello World" of GxMagnifier

The simplest implementation of GxMagnifier requires only two lines of code. Here's an example based on the "Hello World" page of the Google Maps API documentation.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>GxMagnifier Example - simple</title>
    <script src="http://maps.google.com/maps?file=api&v=1&key=abcdefg" type="text/javascript"></script>
    <script src="GxMagnifier.1.js" type="text/javascript"></script>
  </head>
  <body>
    <div id="map" style="width: 300px; height: 300px"></div>
    <script type="text/javascript">
    //<![CDATA[

    if (GBrowserIsCompatible()) {
      var map = new GMap(document.getElementById("map"));
      map.centerAndZoom(new GPoint(-122.4618, 37.7902), 9);
      map.addControl(new GxMagnifierControl());
    }

    //]]>
    </script>
  </body>

</html>

View Example (simple.htm)

If you download the example, don't forget to grab the files listed above and to substitute your key for abcdefg.

Google Maps API Compatibility

GxMagnifier was developed under version 1 of the API, specifically under maps.12.js up to maps.14.js, and was originally released July 26, 2005.  It uses a handful of undocumented functions in the API, which means it could break with future map updates (even if the API version number doesn't change). I've tried to stick to functions that seem like they're here to stay (e.g. I definitely don't call ones that have been obfuscated down to jumbled letters).

Updates are briefly tested using the latest version of the API, and the example pages herein always use the latest version.

I'm hoping that the hoards of great people in the Google Maps programming community will help keep these classes working when the maps.x.js source files get updated. So if things break, you might want hop over to Google-Maps-API and do a quick search on "GxMagnifier".

Browser Compatibility

The control has been loosely tested in Microsoft Internet Explorer 6.0 and Mozilla Firefox 1.0.4. Since this was done entirely in my free time, and I don't have any free time, I anticipate issues will be raised (and hopefully addressed) in the forum.

Legal

Use of this software is entirely at your own risk. There are no warrantees of any kind implied, and the author shall not be held liable for any damages whatsoever resulting from its use.

10 Second Peek Behind the Scenes

When you add a GxMagnifierControl, two things happen. It adds the "magnifying glass" button to your map and then instantiates a new GxMagnifier, which in turn is responsible for creating and managing the magnifier window.  The window is created by making a copy of your main map at the time you add the control.  This means you have two maps on your page.  By default, the magnified map is kept up to date when new overlays are added or removed.

Updates

If you want to make modifications, go ahead. But if you want them integrated into the source code listed here, I have just one completely archaic request to make. In your code editor please set your tab size to two spaces, and make sure the code still looks right. I don't want to have to spend a lot of time scrolling the screen left and right just to get around whitespace, or deleting spaces to make things line up. The source code has some more tips on coding conventions.  Thanks, and sorry for the hassle.

Known Bugs

There's only one significant one I know of so far (as of July 26, 2005):

  • During extreme testing, I noticed my browser start to chew up memory.  I'm not sure if the memory leak is caused by this component or the Google Map in general, or if it's caused by one and then exaggerated by the other.  From what I can tell so far, it's only a problem if you interact with the page all day without closing the browser window.  It also does seem to be getting slightly better with newer releases of the maps.x.js files.

Framework for Examples

The surrounding HTML and code for initializing the main map has been omitted below to to make the examples easier to read. As well, I use a special script called keywriter.js in the examples below, which lets me move pages between my development box and the GoogleMappers.com website without having to cut and paste keys each time. When you see it in header section of the downloaded examples, you can replace it with the usual Google Maps API include line and your own key.

Finally, since the GxMagnifierControl is just a wrapper, most of the time we'll be accessing its related GxMagnifier by reading the GxMagnifierControl.GxMagnifier property. In order to further simplify the examples, it is assumed you already set a variable called  mag to this property.

In a nutshell, all the examples are encapsulated in a framework that looks like this (except for a few noted exceptions):

if (GBrowserIsCompatible()) {
  var map = new GMap(document.getElementById("map"));
  map.centerAndZoom(new GPoint(-122.4618, 37.7902), 4); 

  // Create the magnifier
  var magControl = new GxMagnifierControl();
  map.addControl(magControl);

  // Get reference to the associated GxMagnifier for use in the examples
  var mag = magControl.GxMagnifier;
 
  // ...rest of the example goes here...

}

View Example (almost_as_simple.htm)

This is the one you want to download to start you off on customizing the GxMagnifier.

Examples

Zooming

GxMagnifier automatically adjusts its zoom level as your main map is zoomed.  Here we make it magnify by 3 zoom levels, instead of the default 2.

mag.setMagnification(3);

View example (zoom.htm)

Resizing

By default, the magnified viewport is one third the size of your main map. You can change its dimensions.

mag.setSize(new GSize(150, 80));

View example (resize.htm)

Capturing the Click

You may not want your magnifier to zoom in on the map when the user clicks it.  Here we override the click event to tell us where we clicked and then close the magnifier window.  You can of course use GEvent.bind() for more sophisticated handling.

mag.disableDefaultClickHandler();
GEvent.addListener(mag, "click",
  function(point){
    alert("You clicked " + point.toString());
    this.hide(); // note, "this" is the GxMagnifier instance
  });

View example (click.htm)

Moving the Button

Here we place the "magnifying glass" button 10 pixels away from the top right corner. Note that GxMagnifierControl.setDefaultPosition() has to be called before adding the control to the map.

var magControl = new GxMagnifierControl();
magControl.setDefaultPosition(new GxControlPositionHack(1, 10, 10));
map.addControl(magControl);

View example (reposition.htm)

Automatically Panning

The magnifier can scroll the map when it gets moved near to an edge. Note the panning region extends inward from the edges of the main map by half the size of the magnifier window.

 mag.enableAutoPan();

View example (autopan.htm)

Docking

You can free the magnifier from the map's container, and put it anywhere on the page.  We do this by creating a GxMagnifier instance directly, and telling it to use our own div.  Here's a full screen map, with the magnifier docked to the bottom right corner.  The example deviates a bit from our typical ones.

In the head section:

<!-- Set up the containers, and make a "fullscreen" window -->
<style type="text/css">
  html, body {width: 100%; height: 100%}
  body {margin-top: 0px; margin-right: 0px; margin-left: 0px; margin-bottom: 0px}
  #map {position:absolute; width:100%; height:100%}
  #magnifier {position:absolute; right:0px; bottom:0px; width:200px; height:200px;
  border:2px solid; border-bottom-style:outset}
</style>

In the body:

<div id="map"></div>
<div id="magnifier"></div>

And here's the Javascript:

// Create the magnifier
var div = document.getElementById("magnifier");
div.style.zIndex = 10; // make it stay on top
var mag = new GxMagnifier(map, null, div);
mag.map.setMapType(G_SATELLITE_TYPE)

// Monitor the window resize event and let the map know when it occurs
// This isn't required for GxMagnifier, but it is required to accomplish
// a correct full-screen Google Map.
if (window.attachEvent) { 
  window.attachEvent("onresize", function() {this.map.onResize()} ); 
} else { 
  window.addEventListener("resize", function() {this.map.onResize()} , false);
} 

View example (docking.htm)

Consolidating Waypoints

You can take advantage of having two separate maps to come up with some novel approaches to waypoint consolidation.  The magnified map is accessed through the GxMagnifier.map property.  Here we create a "bubble" marker on the main map that you can inspect with the magnifier.  The GxMagnifier.parkAtMarker() function is also demonstrated, to open the magnifier on top of the marker.  Note that it doesn't stay attached to the marker if the maps moves (although you could recode it to do so).  The example is a little longer than the other ones.

Show Code     View example (consolidate.htm)

Skinning

You can change the border and other style properties of the magnifier's container, and even put your own images inside of it.

// Turn off the normal GxMagnifier "loading" message, and the crosshair
mag.hideLoadingMessage();

// Display a smaller border, and none on the top (since the image
// already has a black line across the top)
mag.container.style.border = "1px solid black"
mag.container.style.borderTop = "none"

// Add an image inside the magnified map container
var img = mag.createImage("skin.png");
with (img.style) {
  position = "absolute"; // you need this for it to stay on top
  left = "0px";
  top = "0px";
}

View example (skinning.html)

Starting with a Blank Map

Instead of making a copy of your main map, you can start the GxMagnifier with a blank map by specifying true in the constructor.  This could be used if for some reason you're adding the control after you've started to populate your main map.  Overlay synchronization is turned off, so that you can manage the magnified map markers yourself (or leave it blank, for better performance).

// Add 10 random markers
var bounds = map.getBoundsLatLng();
var width = bounds.maxX - bounds.minX;
var height = bounds.maxY - bounds.minY;
for (var i = 0; i < 10; i++) {
var point = new GPoint(bounds.minX + width * Math.random(),
bounds.minY + height * Math.random());
var marker = new GMarker(point);
map.addOverlay(marker);
}

// Create blank magnified map
var mag = new GxMagnifierControl(true);
map.addControl(mag);

View example (blank.htm)

Multiple Magnifiers and Negative Zoom

You can include multiple GxMagnifiers on your map.  In addition to the normal magnifier, this example expands on the Docking one and uses a negative magnification factor to create a "birds eye view" of where you are on the map.  Scroll around to see it in action.  (The code below shows the complete page)

Show Code     View example (multiple.htm)

API Overview

Most of the functionality is in the GxMagnifier class, which is responsible for managing the overlayed (magnified) map and its container. You can get at the magnified map (for adding markers, etc.) by accessing the GxMagnifier.map property.

The GxMagnifierControl is just a wrapper that creates a new GxMagnifier and exposes it to the API as a control (or at least, as close to a real control as I could interpret - see below). It is also in charge of the little magnifying glass icon.

There are a couple of other helper classes which I won't delve into.

The GxMagnifierControl class

As mentioned, this is basically just an interface to wrap the GxMagnifier class (which is where the guts are) and expose it as something resembling a normal map control, in order to be used with the GMap.addControl() method.  It's also in charge of the little magnify icon that gets put on the map..

Since Google hasn't yet published an official standard for custom map controls (as of 21-Jul-2005) I've had to do a fair bit of interpretation here as to how a map control should behave.  This class is probably more likely to break in future maps.x.js updates than the rest of the code, so you might consider using the GxMagnifier class directly and creating your own page elements to let the user control the magnifier (as in some of the examples above).  On the bright side, we've been through three maps.x.js updates in the last couple weeks alone, and they haven't caused any problems.

The GxMagnifier class

A GxMagnifier works by making a new copy of your map and mov around on top of your existing map.

In the GxMagnifier(map) constructor you provide the map to copy, and it appends a new div element onto your page, right after the container for your existing map.  It puts the copied map inside the new div, and you can later access the copy just like a normal GMap by using the GxMagnifier.map property.

GxMagnifier listens to events from the main map, and automatically updates its overlays and settings to remain synchronized. If you prefer, you can tell it to use a blank map instead, and then decide which overlays you want to add yourself.  If using this method, remember not to reuse the same marker/polyline object for both maps (i.e. make sure to pass a new object when you call GxMagnifier.map.addOverlay()).  Otherwise, changing zoom levels on one map will corrupt the marker on the other.

The GxControlPositionHack class

Until Google releases an official spec for add-in controls, I'm using this class to to tell the API where to position the control.

Class Reference

GxMagnifier

Creates and manages a magnifier "window".

Constructor

Constructor Description
GxMagnifier(map, useBlank?, container?) Creates a new magnifier on the page, by copying map.  If useBlank is true, a blank new map is created instead.  You can specify a custom div to use in container, which also puts the GxMagnifier into parked mode.

Methods

Configuration

Method Description
setMagnification(levels) Changes the magnification factor. levels determines how many levels to subtract from the zoom level of your main map. (default is 2)
setSize(size) Sets the size of the magnifier window to the given GSize. (default is is a third the size of your main map)
setBorderWidth(pixels) Sets the border width, in pixels, for the magnified map.  Use this instead of trying to modify  GxMagnifier.container.style. (default is 2)
setCursor(name) Sets cursor for the magnifier container. (default is "crosshair")
enableMapSync() Update the magnified map as required when the main one's center/zoom/type is changed (enabled by default)
disableMapSync() Lets you take over managing the magnified map's zoom/position/type.
enableOverlaySync() Keeps the overlays on the magnified map in sync with your main map. (enabled by default, unless useBlank was true in the constructor)
disableOverlaySync() Stops synchronizing overlays, allowing you to manage overlays on the magnified map yourself.
enableAutoPan() Pans the main map when the magnifier is brought to an edge. (disabled by default, ignored in park mode)
disableAutoPan() Turns off the autopan feature.
enableMouseTracking() Normal mode: The magnified map window follows the mouse around above the main map.
Parked mode: The magnified map recenters to the cursor location on the main map.
(enabled by default)
disableMouseTracking() Turns off all mouse tracking features (and disables mouseover  and mouseout events from firing)
enablePrefetch() Recenters the magnified map, even when it's hidden.  This causes the map to download tiles, theoretically making them appear faster when you turn on the magnifier. (enabled by default)
disablePrefetch() Doesn't update the magnified map if its not visible. Use to save bandwidth.  (Note: If the user is constantly moving the mouse around over the main map, prefetching causes the magnified one to constantly recenter - which in fact never gives ANY of the tiles a chance to download.  I'm curious to hear feedback on if the feature speeds things up, or slows things down, in practice)
enableAutoMagnify() Like enableMapSync(), but only applies to the zoom feature.  (enabled by default, ignored if you called disableMapSync).
disableAutoMagnify() Uncouples the zoom level of the magnified map, allowing you to set a specific one with GxMagnifier.map.zoomTo(). This is more convenient than using disableMapSync() if you simply want to have a perma-zoomed, parked map.
enableDefaultClickHandler() Zooms your main map when the magnifier is clicked on it. (enabled by default)
disableDefaultClickHandler() Disables all GxMagnifier click handling and lets you implement your own click handler. (note that click events are still fired)
enablePanBeforeAutoZoom() Pans the main map before zooming in after a click. (disabled by default)
disablePanBeforeAutoZoom() Disables the pan-before-auto-zoom feature.
enableAutoHide() Hide the magnified map window when the user clicks. (enabled by default, ignored in parked mode)
disableAutoHide() Keeps the magnified map open until the user closes it by pressing the magnifier icon again.
showLoadingMessage(msg?) Causes the "Loading Tiles" message to be displayed behind map tiles before they are loaded. The optional msg parameter can be set to your own HTML string (see note below).
hideLoadingMessage() Hides the "Loading Tiles" message.  Note: I'd appreciate it if you leave the unobtrusive reference to GoogleMappers.com behind your magnifier.  I put a lot of work into the component, and its the least you can do.
State

Method Description
show() Shows the magnified map.
hide() Hides the magnified map
park(point?) Causes the magnifier to enter parked mode, centered on the given GPoint (or wherever it happens to be). It will stay where it is, no longer following the mouse around the page, until you call unpark()
parkAtMarker(marker, offsetX?, offsetY?) Centers the magnifier on the given GMarker and enters parked mode.  Note that it is not explicitly attached to the marker, and will not move around with it as the map scrolls. OffsetX and OffsetY can be used to fine tune the magnifier's distance from the marker's anchor point.
unpark(marker) Allows the magnifier to follow the mouse (ignored if custom container was specified in the constructor).
syncOverlays() Clears all overlays on the magnified map and recopies them from the main map.
isVisible() Returns true when the magnifier window is visible.

Extras

Method Description
createImage(imageSrc) Appends a new <img> element to the document, in the magnified map's container, and adds alpha filtering for transparent images if running under Internet Explorer (although it sometimes doesn't seem to work - maybe someone could improve upon my implementation here).

Events

Event Arguments Description
click point Triggered when the user clicks anywhere in the magnified map.  As of GxMagnifier.1.js, point is always the Lng/Lat center of the magnified map.  This is probably only useful if not in parked mode, and the behavior of the argument may be changed in the future.
mouseover none Triggered when the mouse enters the magnified window (only use this in parked mode).
mouseout none Triggered when the mouse exits the magnified window (only use this in parked mode).  Note that both this and the above event behave differently from normal mouseover and mouseout events, in that they occur only when the mouse is truly leaving the element bounds, and not if the mouse is simple rolling over another element contained within the window.


GxMagnifierControl

Provides a wrapper for a GxMagnifier, allowing it to be added to a GMap using GMap.addControl().

Constructor

Constructor Description
GxMagnifierControl(useBlank?) Creates the control, passing the useBlank argument to the GxMagnifier constructor if supplied.  By default, the control is positioned in the top-left corner at an offset that makes it fit nicely between the north and west panning controls of a default GLargeMapControl or GSmallMapControl.

Properties

Method Description
GxMagnifier A reference to the GxMagnifier associated with this control.

Methods

Method Description
map() Returns a reference to the magnified map.  (Identical to using GxMagnifier.map).
show() Displays the magnifying glass icon if its been hidden.
hide() Hides the magnifying glass icon.
setMagnifyImage(src) Sets the image file to use for the magnifying glass icon.  If none is provided, "magnify.png" is used.  Note: There is also some code to create a simple text "z" button but its been depricated.
setDefaultPosition(position) Sets where to display the control. position must be a GxControlPositionHack object, or an instance of whatever equivalent class Google releases once an official API for map add-in controls is published. Must be called before you add the control to the map.


GxControlPositionHack

This is class is meant to temporarily tide things over until Google comes out with an official spec on add-in controls, or until someone points out a definition for them that I've missed.

Constructor

Constructor Description
GxControlPositionHack(anchor, offsetX?, offsetY?) Creates an object containing positioning information for a GxMagnifierControl. anchor is 0, 1, 2, or 3 to indicate top-left, top-right, bottom-left and bottom-right, respectively. The next two arguments are a horizontal and vertical offset indicating how far from the corner the control should be positioned.


One last quick note.  The stylesheet for this documentation was ruthlessly borrowed from the Google Maps API documentation, and even further ruthlessly edited in a really dumb HTML editor.  If anyone wants to clean up this page for me, please get in touch!  You can reach me in the Google-Maps-API forum or by using this contact form to send an email to me at my company.  Please, no phone calls related to this product.