[fusion-commits] r1736 - in trunk: . lib lib/OpenLayers widgets

svn_fusion at osgeo.org svn_fusion at osgeo.org
Wed Jan 14 10:42:24 EST 2009


Author: madair
Date: 2009-01-14 10:42:24 -0500 (Wed, 14 Jan 2009)
New Revision: 1736

Modified:
   trunk/build.xml
   trunk/fusion.cfg
   trunk/lib/OpenLayers/OpenLayers.js
   trunk/lib/fusion.js
   trunk/widgets/Measure.js
   trunk/widgets/Select.js
   trunk/widgets/SelectPolygon.js
   trunk/widgets/SelectRadius.js
Log:
re #200: conversion of Select and Measure tools; remove CanvasTools and RectTools from Fusion core load 

Modified: trunk/build.xml
===================================================================
--- trunk/build.xml	2009-01-14 15:25:12 UTC (rev 1735)
+++ trunk/build.xml	2009-01-14 15:42:24 UTC (rev 1736)
@@ -207,14 +207,11 @@
                           fusion.js
                           OpenLayers/OpenLayers.js
                           jxlib.uncompressed.js
-                          excanvas/excanvas.js
                           Error.js 
                           EventMgr.js
                           ApplicationDefinition.js
                           MGBroker.js 
                           Widget.js 
-                          CanvasTool.js 
-                          RectTool.js 
                           Search.js
                           Map.js"
         />

Modified: trunk/fusion.cfg
===================================================================
--- trunk/fusion.cfg	2009-01-14 15:25:12 UTC (rev 1735)
+++ trunk/fusion.cfg	2009-01-14 15:42:24 UTC (rev 1736)
@@ -27,6 +27,7 @@
 OpenLayers/Layer/WMS.js
 OpenLayers/Layer/MapGuide.js
 OpenLayers/Layer/MapServer.js
+OpenLayers/Layer/Google.js
 OpenLayers/Layer/Vector.js
 OpenLayers/Layer/Markers.js
 OpenLayers/Tile.js
@@ -36,10 +37,17 @@
 OpenLayers/Control/PanZoom.js
 OpenLayers/Control/ArgParser.js
 OpenLayers/Control/DrawFeature.js
+OpenLayers/Control/Measure.js
+OpenLayers/Handler/Box.js
 OpenLayers/Handler/Click.js
+OpenLayers/Handler/Drag.js
+OpenLayers/Handler/Feature.js
+OpenLayers/Handler/Hover.js
+OpenLayers/Handler/MouseWheel.js
 OpenLayers/Handler/Point.js
 OpenLayers/Handler/Path.js
 OpenLayers/Handler/Polygon.js
+OpenLayers/Handler/RegularPolygon.js
 OpenLayers/Projection.js
 OpenLayers/Feature.js
 OpenLayers/Marker.js
@@ -49,6 +57,14 @@
 OpenLayers/Lang.js
 OpenLayers/Popup.js
 OpenLayers/PopupAnchoredBubble.js
+OpenLayers/Renderer.js
+OpenLayers/Renderer/Canvas.js
+OpenLayers/Renderer/Elements.js
+OpenLayers/Renderer/SVG.js
+OpenLayers/Renderer/VML.js
+OpenLayers/Rule.js
+OpenLayers/Style.js
+OpenLayers/StyleMap.js
 
 [exclude]
 Firebug/firebug.js

Modified: trunk/lib/OpenLayers/OpenLayers.js
===================================================================
--- trunk/lib/OpenLayers/OpenLayers.js	2009-01-14 15:25:12 UTC (rev 1735)
+++ trunk/lib/OpenLayers/OpenLayers.js	2009-01-14 15:42:24 UTC (rev 1736)
@@ -1,95 +1,95 @@
-/*
-
-  OpenLayers.js -- OpenLayers Map Viewer Library
-
-  Copyright 2005-2008 MetaCarta, Inc., released under the Clear BSD license.
-  Please see http://svn.openlayers.org/trunk/openlayers/license.txt
-  for the full text of the license.
-
-  Includes compressed code under the following licenses:
-
-  (For uncompressed versions of the code used please see the
-  OpenLayers SVN repository: <http://openlayers.org/>)
-
-*/
-
-/* Contains portions of Prototype.js:
- *
- * Prototype JavaScript framework, version 1.4.0
- *  (c) 2005 Sam Stephenson <sam at conio.net>
- *
- *  Prototype is freely distributable under the terms of an MIT-style license.
- *  For details, see the Prototype web site: http://prototype.conio.net/
- *
- *--------------------------------------------------------------------------*/
-
-/**  
-*  
-*  Contains portions of Rico <http://openrico.org/>
-* 
-*  Copyright 2005 Sabre Airline Solutions  
-*  
-*  Licensed under the Apache License, Version 2.0 (the "License"); you
-*  may not use this file except in compliance with the License. You
-*  may obtain a copy of the License at
-*  
-*         http://www.apache.org/licenses/LICENSE-2.0  
-*  
-*  Unless required by applicable law or agreed to in writing, software
-*  distributed under the License is distributed on an "AS IS" BASIS,
-*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-*  implied. See the License for the specific language governing
-*  permissions and limitations under the License. 
-*
-**/
-
-/**
- * Contains XMLHttpRequest.js <http://code.google.com/p/xmlhttprequest/>
- * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- */
-
-/**
- * Contains portions of Gears <http://code.google.com/apis/gears/>
- *
- * Copyright 2007, Google Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *  3. Neither the name of Google Inc. nor the names of its contributors may be
- *     used to endorse or promote products derived from this software without
- *     specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * Sets up google.gears.*, which is *the only* supported way to access Gears.
- *
- * Circumvent this file at your own risk!
- *
- * In the future, Gears may automatically define google.gears.* without this
- * file. Gears may use these objects to transparently fix bugs and compatibility
- * issues. Applications that use the code below will continue to work seamlessly
- * when that happens.
- */
+/*
+
+  OpenLayers.js -- OpenLayers Map Viewer Library
+
+  Copyright 2005-2008 MetaCarta, Inc., released under the Clear BSD license.
+  Please see http://svn.openlayers.org/trunk/openlayers/license.txt
+  for the full text of the license.
+
+  Includes compressed code under the following licenses:
+
+  (For uncompressed versions of the code used please see the
+  OpenLayers SVN repository: <http://openlayers.org/>)
+
+*/
+
+/* Contains portions of Prototype.js:
+ *
+ * Prototype JavaScript framework, version 1.4.0
+ *  (c) 2005 Sam Stephenson <sam at conio.net>
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://prototype.conio.net/
+ *
+ *--------------------------------------------------------------------------*/
+
+/**  
+*  
+*  Contains portions of Rico <http://openrico.org/>
+* 
+*  Copyright 2005 Sabre Airline Solutions  
+*  
+*  Licensed under the Apache License, Version 2.0 (the "License"); you
+*  may not use this file except in compliance with the License. You
+*  may obtain a copy of the License at
+*  
+*         http://www.apache.org/licenses/LICENSE-2.0  
+*  
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+*  implied. See the License for the specific language governing
+*  permissions and limitations under the License. 
+*
+**/
+
+/**
+ * Contains XMLHttpRequest.js <http://code.google.com/p/xmlhttprequest/>
+ * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+
+/**
+ * Contains portions of Gears <http://code.google.com/apis/gears/>
+ *
+ * Copyright 2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. Neither the name of Google Inc. nor the names of its contributors may be
+ *     used to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Sets up google.gears.*, which is *the only* supported way to access Gears.
+ *
+ * Circumvent this file at your own risk!
+ *
+ * In the future, Gears may automatically define google.gears.* without this
+ * file. Gears may use these objects to transparently fix bugs and compatibility
+ * issues. Applications that use the code below will continue to work seamlessly
+ * when that happens.
+ */
 /* ======================================================================
     OpenLayers/SingleFile.js
    ====================================================================== */
@@ -192,7 +192,6 @@
             "OpenLayers/Tween.js",
             "Rico/Corner.js",
             "Rico/Color.js",
-            "Gears/gears_init.js",
             "OpenLayers/Ajax.js",
             "OpenLayers/Request.js",
             "OpenLayers/Request/XMLHttpRequest.js",
@@ -639,9 +638,9 @@
  * imgURL - {String} The url to use as the image source.
  * position - {String} The style.position value.
  * border - {String} The border to place around the image.
+ * opacity - {Float} Fractional value (0.0 - 1.0)
  * delayDisplay - {Boolean} If true waits until the image has been
  *                          loaded.
- * opacity - {Float} Fractional value (0.0 - 1.0)
  * 
  * Returns:
  * {DOMElement} A DOM Image created with the specified attributes.
@@ -777,31 +776,39 @@
 };
 
 /**
+ * Property: alphaHackNeeded
+ * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
+ */
+OpenLayers.Util.alphaHackNeeded = null;
+
+/**
  * Function: alphaHack
  * Checks whether it's necessary (and possible) to use the png alpha
  * hack which allows alpha transparency for png images under Internet
  * Explorer.
  * 
  * Returns:
- * {Boolean} true if alpha has is necessary and possible, false otherwise.
+ * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
  */
 OpenLayers.Util.alphaHack = function() {
-    var arVersion = navigator.appVersion.split("MSIE");
-    var version = parseFloat(arVersion[1]);
-    var filter = false;
+    if (OpenLayers.Util.alphaHackNeeded == null) {
+        var arVersion = navigator.appVersion.split("MSIE");
+        var version = parseFloat(arVersion[1]);
+        var filter = false;
     
-    // IEs4Lin dies when trying to access document.body.filters, because 
-    // the property is there, but requires a DLL that can't be provided. This
-    // means that we need to wrap this in a try/catch so that this can
-    // continue.
+        // IEs4Lin dies when trying to access document.body.filters, because 
+        // the property is there, but requires a DLL that can't be provided. This
+        // means that we need to wrap this in a try/catch so that this can
+        // continue.
     
-    try { 
-        filter = !!(document.body.filters);
-    } catch (e) {
-    }    
+        try { 
+            filter = !!(document.body.filters);
+        } catch (e) {}    
     
-    return ( filter &&
-                      (version >= 5.5) && (version < 7) );
+        OpenLayers.Util.alphaHackNeeded = (filter && 
+                                           (version >= 5.5) && (version < 7));
+    }
+    return OpenLayers.Util.alphaHackNeeded;
 };
 
 /** 
@@ -861,8 +868,10 @@
  * imgURL - {String}
  * position - {String}
  * border - {String}
- * sizing {String} 'crop', 'scale', or 'image'. Default is "scale"
- * delayDisplay{Boolean}
+ * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
+ * opacity - {Float} Fractional value (0.0 - 1.0)
+ * delayDisplay - {Boolean} If true waits until the image has been
+ *                          loaded.
  * 
  * Returns:
  * {DOMElement} A DOM Div created with a DOM Image inside it. If the hack is 
@@ -1736,7 +1745,7 @@
                 var postChunk = relStr.substr(index + 3);                
                 relStr = prevChunk + postChunk;
             }
-        } while(index != -1)
+        } while(index != -1);
 
         var windowAnchor = document.createElement("a");
         var windowUrl = window.location.href;
@@ -2945,7 +2954,14 @@
      * Property: top
      * {Number} Maximum vertical coordinate.
      */
-    top: null,    
+    top: null,
+    
+    /**
+     * Property: centerLonLat
+     * {<OpenLayers.LonLat>} A cached center location.  This should not be
+     *     accessed directly.  Use <getCenterLonLat> instead.
+     */
+    centerLonLat: null,
 
     /**
      * Constructor: OpenLayers.Bounds
@@ -3122,8 +3138,12 @@
      * {<OpenLayers.LonLat>} The center of the bounds in map space.
      */
     getCenterLonLat:function() {
-        return new OpenLayers.LonLat( (this.left + this.right) / 2,
-                                      (this.bottom + this.top) / 2);
+        if(!this.centerLonLat) {
+            this.centerLonLat = new OpenLayers.LonLat(
+                (this.left + this.right) / 2, (this.bottom + this.top) / 2
+            );
+        }
+        return this.centerLonLat;
     },
 
     /**
@@ -3164,7 +3184,7 @@
         var bottom = (this.bottom - origy) * ratio + origy;
         var right = (this.right - origx) * ratio + origx;
         var top = (this.top - origy) * ratio + origy;
-
+        
         return new OpenLayers.Bounds(left, bottom, right, top);
     },
 
@@ -3200,6 +3220,7 @@
     extend:function(object) {
         var bounds = null;
         if (object) {
+            // clear cached center location
             switch(object.CLASS_NAME) {
                 case "OpenLayers.LonLat":    
                     bounds = new OpenLayers.Bounds(object.lon, object.lat,
@@ -3216,6 +3237,7 @@
             }
     
             if (bounds) {
+                this.centerLonLat = null;
                 if ( (this.left == null) || (bounds.left < this.left)) {
                     this.left = bounds.left;
                 }
@@ -3406,6 +3428,8 @@
      * {<OpenLayers.Bounds>} Itself, for use in chaining operations.
      */
     transform: function(source, dest) {
+        // clear cached center location
+        this.centerLonLat = null;
         var ll = OpenLayers.Projection.transform(
             {'x': this.left, 'y': this.bottom}, source, dest);
         var lr = OpenLayers.Projection.transform(
@@ -4645,7 +4669,23 @@
     display: function(display) {
         this.imageDiv.style.display = (display) ? "" : "none"; 
     },
+    
 
+    /**
+     * APIMethod: isDrawn
+     * 
+     * Returns:
+     * {Boolean} Whether or not the icon is drawn.
+     */
+    isDrawn: function() {
+        // nodeType 11 for ie, whose nodes *always* have a parentNode
+        // (of type document fragment)
+        var isDrawn = (this.imageDiv && this.imageDiv.parentNode && 
+                       (this.imageDiv.parentNode.nodeType != 11));    
+
+        return isDrawn;   
+    },
+
     CLASS_NAME: "OpenLayers.Icon"
 });
 /* ======================================================================
@@ -4960,6 +5000,13 @@
      *     Default is false.
      */
     panMapIfOutOfView: false,
+
+    /**
+     * APIProperty: closeOnMove
+     * {Boolean} When map pans, close the popup.
+     *     Default is false.
+     */
+    closeOnMove: false,
     
     /** 
      * Property: map 
@@ -5040,6 +5087,10 @@
         this.opacity = null;
         this.border = null;
         
+        if (this.closeOnMove && this.map) {
+            this.map.events.unregister("movestart", this, this.hide);
+        }
+
         this.events.destroy();
         this.events = null;
         
@@ -5081,6 +5132,12 @@
                 px = this.map.getLayerPxFromLonLat(this.lonlat);
             }
         }
+
+        // this assumes that this.map already exists, which is okay because 
+        // this.draw is only called once the popup has been added to the map.
+        if (this.closeOnMove) {
+            this.map.events.register("movestart", this, this.hide);
+        }
         
         //listen to movestart, moveend to disable overflow (FF bug)
         if (OpenLayers.Util.getBrowserName() == 'firefox') {
@@ -6046,7 +6103,10 @@
     /**
      * APIMethod: issue
      * Create a new XMLHttpRequest object, open it, set any headers, bind
-     *     a callback to done state, and send any data.
+     *     a callback to done state, and send any data.  It is recommended that
+     *     you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.
+     *     This method is only documented to provide detail on the configuration
+     *     options available to all request methods.
      *
      * Parameters:
      * config - {Object} Object containing properties for configuring the
@@ -6066,12 +6126,20 @@
      *     <OpenLayers.ProxyHost>.
      * params - {Object} Any key:value pairs to be appended to the
      *     url as a query string.  Assumes url doesn't already include a query
-     *     string or hash.  Parameter values that are arrays will be
+     *     string or hash.  Typically, this is only appropriate for <GET>
+     *     requests where the query string will be appended to the url.
+     *     Parameter values that are arrays will be
      *     concatenated with a comma (note that this goes against form-encoding)
      *     as is done with <OpenLayers.Util.getParameterString>.
      * headers - {Object} Object with header:value pairs to be set on
      *     the request.
-     * data - {Object} Any data to send with the request.
+     * data - {String | Document} Optional data to send with the request.
+     *     Typically, this is only used with <POST> and <PUT> requests.
+     *     Make sure to provide the appropriate "Content-Type" header for your
+     *     data.  For <POST> and <PUT> requests, the content type defaults to
+     *     "application-xml".  If your data is a different content type, or
+     *     if you are using a different HTTP method, set the "Content-Type"
+     *     header to match your data type.
      * callback - {Function} Function to call when request is done.
      *     To determine if the request failed, check request.status (200
      *     indicates success).
@@ -8381,6 +8449,1354 @@
     return point;
 };
 /* ======================================================================
+    OpenLayers/Renderer/Canvas.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Renderer.js
+ */
+
+/**
+ * Class: OpenLayers.Renderer.Canvas 
+ * A renderer based on the 2D 'canvas' drawing element.element
+ * 
+ * Inherits:
+ *  - <OpenLayers.Renderer>
+ */
+OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
+
+    /**
+     * Property: root
+     * {DOMElement} root element of canvas.
+     */
+    root: null,
+
+    /**
+     * Property: canvas
+     * {Canvas} The canvas context object.
+     */
+    canvas: null, 
+    
+    /**
+     * Property: features
+     * {Object} Internal object of feature/style pairs for use in redrawing the layer.
+     */
+    features: null, 
+   
+    /**
+     * Property: geometryMap
+     * {Object} Geometry -> Feature lookup table. Used by eraseGeometry to
+     *     lookup features to remove from our internal table (this.features)
+     *     when erasing geoms.
+     */
+    geometryMap: null,
+ 
+    /**
+     * Constructor: OpenLayers.Renderer.Canvas
+     *
+     * Parameters:
+     * containerID - {<String>} 
+     */
+    initialize: function(containerID) {
+        OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
+        this.root = document.createElement("canvas");
+        this.container.appendChild(this.root);
+        this.canvas = this.root.getContext("2d");
+        this.features = {};
+        this.geometryMap = {};
+    },
+    
+    /** 
+     * Method: eraseGeometry
+     * Erase a geometry from the renderer. Because the Canvas renderer has
+     *     'memory' of the features that it has drawn, we have to remove the
+     *     feature so it doesn't redraw.   
+     * 
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    eraseGeometry: function(geometry) {
+        this.eraseFeatures(this.features[this.geometryMap[geometry.id]][0]);
+    },
+
+    /**
+     * APIMethod: supported
+     * 
+     * Returns:
+     * {Boolean} Whether or not the browser supports the renderer class
+     */
+    supported: function() {
+        var canvas = document.createElement("canvas");
+        return !!canvas.getContext;
+    },    
+    
+    /**
+     * Method: setExtent
+     * Set the visible part of the layer.
+     *
+     * Resolution has probably changed, so we nullify the resolution 
+     * cache (this.resolution), then redraw. 
+     *
+     * Parameters:
+     * extent - {<OpenLayers.Bounds>} 
+     */
+    setExtent: function(extent) {
+        this.extent = extent.clone();
+        this.resolution = null;
+        this.redraw();
+    },
+    
+    /**
+     * Method: setSize
+     * Sets the size of the drawing surface.
+     *
+     * Once the size is updated, redraw the canvas.
+     *
+     * Parameters:
+     * size - {<OpenLayers.Size>} 
+     */
+    setSize: function(size) {
+        this.size = size.clone();
+        this.root.style.width = size.w + "px";
+        this.root.style.height = size.h + "px";
+        this.root.width = size.w;
+        this.root.height = size.h;
+        this.resolution = null;
+    },
+    
+    /**
+     * Method: drawFeature
+     * Draw the feature. Stores the feature in the features list,
+     * then redraws the layer. 
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Feature.Vector>} 
+     * style - {<Object>} 
+     */
+    drawFeature: function(feature, style) {
+        if(style == null) {
+            style = feature.style;
+        }
+        style = OpenLayers.Util.extend({
+          'fillColor': '#000000',
+          'strokeColor': '#000000',
+          'strokeWidth': 2,
+          'fillOpacity': 1,
+          'strokeOpacity': 1
+        }, style);  
+        this.features[feature.id] = [feature, style]; 
+        this.geometryMap[feature.geometry.id] = feature.id; 
+        this.redraw();
+    },
+
+
+    /** 
+     * Method: drawGeometry
+     * Used when looping (in redraw) over the features; draws
+     * the canvas. 
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>} 
+     * style - {Object} 
+     * featureId - {<String>} 
+     */
+    drawGeometry: function(geometry, style) {
+        var className = geometry.CLASS_NAME;
+        if ((className == "OpenLayers.Geometry.Collection") ||
+            (className == "OpenLayers.Geometry.MultiPoint") ||
+            (className == "OpenLayers.Geometry.MultiLineString") ||
+            (className == "OpenLayers.Geometry.MultiPolygon")) {
+            for (var i = 0; i < geometry.components.length; i++) {
+                this.drawGeometry(geometry.components[i], style);
+            }
+            return;
+        };
+        switch (geometry.CLASS_NAME) {
+            case "OpenLayers.Geometry.Point":
+                this.drawPoint(geometry, style);
+                break;
+            case "OpenLayers.Geometry.LineString":
+                this.drawLineString(geometry, style);
+                break;
+            case "OpenLayers.Geometry.LinearRing":
+                this.drawLinearRing(geometry, style);
+                break;
+            case "OpenLayers.Geometry.Polygon":
+                this.drawPolygon(geometry, style);
+                break;
+            default:
+                break;
+        }
+    },
+
+    /**
+     * Method: drawExternalGraphic
+     * Called to draw External graphics. 
+     * 
+     * Parameters: 
+     * geometry - {<OpenLayers.Geometry>}
+     * style    - {Object}
+     */ 
+    drawExternalGraphic: function(pt, style) {
+       var img = new Image();
+       img.src = style.externalGraphic;
+
+       var width = style.graphicWidth || style.graphicHeight;
+       var height = style.graphicHeight || style.graphicWidth;
+       width = width ? width : style.pointRadius*2;
+       height = height ? height : style.pointRadius*2;
+       var xOffset = (style.graphicXOffset != undefined) ?
+           style.graphicXOffset : -(0.5 * width);
+       var yOffset = (style.graphicYOffset != undefined) ?
+           style.graphicYOffset : -(0.5 * height);
+       var opacity = style.graphicOpacity || style.fillOpacity;
+       
+       var context = { img: img, 
+                       x: (pt[0]+xOffset), 
+                       y: (pt[1]+yOffset), 
+                       width: width, 
+                       height: height, 
+                       canvas: this.canvas };
+
+       img.onload = OpenLayers.Function.bind( function() {
+           this.canvas.drawImage(this.img, this.x, 
+                                 this.y, this.width, this.height);
+       }, context);   
+    },
+
+    /**
+     * Method: setCanvasStyle
+     * Prepare the canvas for drawing by setting various global settings.
+     *
+     * Parameters:
+     * type - {String} one of 'stroke', 'fill', or 'reset'
+     * style - {Object} Symbolizer hash
+     */
+    setCanvasStyle: function(type, style) {
+        if (type == "fill") {     
+            this.canvas.globalAlpha = style['fillOpacity'];
+            this.canvas.fillStyle = style['fillColor'];
+        } else if (type == "stroke") {  
+            this.canvas.globalAlpha = style['strokeOpacity'];
+            this.canvas.strokeStyle = style['strokeColor'];
+            this.canvas.lineWidth = style['strokeWidth'];
+        } else {
+            this.canvas.globalAlpha = 0;
+            this.canvas.lineWidth = 1;
+        }
+    },
+
+    /**
+     * Method: drawPoint
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * geometry - {<OpenLayers.Geometry>}
+     * style    - {Object}
+     */ 
+    drawPoint: function(geometry, style) {
+        var pt = this.getLocalXY(geometry);
+        
+        if (style.externalGraphic) {
+            this.drawExternalGraphic(pt, style);
+        } else {
+            this.setCanvasStyle("fill", style);
+            this.canvas.beginPath();
+            this.canvas.arc(pt[0], pt[1], 6, 0, Math.PI*2, true);
+            this.canvas.fill();
+            
+            this.setCanvasStyle("stroke", style);
+            this.canvas.beginPath();
+            this.canvas.arc(pt[0], pt[1], 6, 0, Math.PI*2, true);
+            this.canvas.stroke();
+            this.setCanvasStyle("reset");
+        }
+    },
+
+    /**
+     * Method: drawLineString
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * geometry - {<OpenLayers.Geometry>}
+     * style    - {Object}
+     */ 
+    drawLineString: function(geometry, style) {
+        this.setCanvasStyle("stroke", style);
+        this.canvas.beginPath();
+        var start = this.getLocalXY(geometry.components[0]);
+        this.canvas.moveTo(start[0], start[1]);
+        for(var i = 1; i < geometry.components.length; i++) {
+            var pt = this.getLocalXY(geometry.components[i]);
+            this.canvas.lineTo(pt[0], pt[1]);
+        }
+        this.canvas.stroke();
+        this.setCanvasStyle("reset");
+    },    
+    
+    /**
+     * Method: drawLinearRing
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * geometry - {<OpenLayers.Geometry>}
+     * style    - {Object}
+     */ 
+    drawLinearRing: function(geometry, style) {
+        this.setCanvasStyle("fill", style);
+        this.canvas.beginPath();
+        var start = this.getLocalXY(geometry.components[0]);
+        this.canvas.moveTo(start[0], start[1]);
+        for(var i = 1; i < geometry.components.length - 1 ; i++) {
+            var pt = this.getLocalXY(geometry.components[i]);
+            this.canvas.lineTo(pt[0], pt[1]);
+        }
+        this.canvas.fill();
+        
+        var oldWidth = this.canvas.lineWidth; 
+        this.setCanvasStyle("stroke", style);
+        this.canvas.beginPath();
+        var start = this.getLocalXY(geometry.components[0]);
+        this.canvas.moveTo(start[0], start[1]);
+        for(var i = 1; i < geometry.components.length; i++) {
+            var pt = this.getLocalXY(geometry.components[i]);
+            this.canvas.lineTo(pt[0], pt[1]);
+        }
+        this.canvas.stroke();
+        this.setCanvasStyle("reset");
+    },    
+    
+    /**
+     * Method: drawPolygon
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * geometry - {<OpenLayers.Geometry>}
+     * style    - {Object}
+     */ 
+    drawPolygon: function(geometry, style) {
+        this.drawLinearRing(geometry.components[0], style);
+        for (var i = 1; i < geometry.components.length; i++) {
+            this.drawLinearRing(geometry.components[i], {
+                fillOpacity: 0, 
+                strokeWidth: 0, 
+                strokeOpacity: 0, 
+                strokeColor: '#000000', 
+                fillColor: '#000000'}
+            ); // inner rings are 'empty'  
+        }
+    },
+
+    /**
+     * Method: getLocalXY
+     * transform geographic xy into pixel xy
+     *
+     * Parameters: 
+     * point - {<OpenLayers.Geometry.Point>}
+     */
+    getLocalXY: function(point) {
+        var resolution = this.getResolution();
+        var extent = this.extent;
+        var x = (point.x / resolution + (-extent.left / resolution));
+        var y = ((extent.top / resolution) - point.y / resolution);
+        return [x, y];
+    },
+        
+    /**
+     * Method: clear
+     * Clear all vectors from the renderer.
+     * virtual function.
+     */    
+    clear: function() {
+        this.canvas.clearRect(0, 0, this.root.width, this.root.height);
+    },
+
+    /**
+     * Method: getFeatureIdFromEvent
+     * Returns a feature id from an event on the renderer.  
+     * 
+     * Parameters:
+     * evt - {<OpenLayers.Event>} 
+     *
+     * Returns:
+     * {String} A feature id or null.
+     */
+    getFeatureIdFromEvent: function(evt) {
+        var loc = this.map.getLonLatFromPixel(evt.xy);
+        var resolution = this.getResolution();
+        var bounds = new OpenLayers.Bounds(loc.lon - resolution * 5, 
+                                           loc.lat - resolution * 5, 
+                                           loc.lon + resolution * 5, 
+                                           loc.lat + resolution * 5);
+        var geom = bounds.toGeometry();
+        for (var feat in this.features) {
+            if (!this.features.hasOwnProperty(feat)) { continue; }
+            if (this.features[feat][0].geometry.intersects(geom)) {
+                return feat;
+            }
+        }   
+        return null;
+    },
+    
+    /**
+     * Method: eraseFeatures 
+     * This is called by the layer to erase features; removes the feature from
+     *     the list, then redraws the layer.
+     * 
+     * Parameters:
+     * features - {Array(<OpenLayers.Feature.Vector>)} 
+     */
+    eraseFeatures: function(features) {
+        if(!(features instanceof Array)) {
+            features = [features];
+        }
+        for(var i=0; i<features.length; ++i) {
+            delete this.features[features[i].id];
+        }
+        this.redraw();
+    },
+
+    /**
+     * Method: redraw
+     * The real 'meat' of the function: any time things have changed,
+     *     redraw() can be called to loop over all the data and (you guessed
+     *     it) redraw it.  Unlike Elements-based Renderers, we can't interact
+     *     with things once they're drawn, to remove them, for example, so
+     *     instead we have to just clear everything and draw from scratch.
+     */
+    redraw: function() {
+        if (!this.locked) {
+            this.clear();
+            for (var id in this.features) {
+                if (!this.features.hasOwnProperty(id)) { continue; }
+                if (!this.features[id][0].geometry) { continue; }
+                this.drawGeometry(this.features[id][0].geometry, this.features[id][1]);
+            }
+        }    
+    },
+
+    CLASS_NAME: "OpenLayers.Renderer.Canvas"
+});
+/* ======================================================================
+    OpenLayers/Renderer/Elements.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Renderer.js
+ */
+
+/**
+ * Class: OpenLayers.ElementsIndexer
+ * This class takes care of figuring out which order elements should be
+ *     placed in the DOM based on given indexing methods. 
+ */
+OpenLayers.ElementsIndexer = OpenLayers.Class({
+   
+    /**
+     * Property: maxZIndex
+     * {Integer} This is the largest-most z-index value for a node
+     *     contained within the indexer.
+     */
+    maxZIndex: null,
+    
+    /**
+     * Property: order
+     * {Array<String>} This is an array of node id's stored in the
+     *     order that they should show up on screen. Id's higher up in the
+     *     array (higher array index) represent nodes with higher z-indeces.
+     */
+    order: null, 
+    
+    /**
+     * Property: indices
+     * {Object} This is a hash that maps node ids to their z-index value
+     *     stored in the indexer. This is done to make finding a nodes z-index 
+     *     value O(1).
+     */
+    indices: null,
+    
+    /**
+     * Property: compare
+     * {Function} This is the function used to determine placement of
+     *     of a new node within the indexer. If null, this defaults to to
+     *     the Z_ORDER_DRAWING_ORDER comparison method.
+     */
+    compare: null,
+   
+    /**
+     * APIMethod: initialize
+     * Create a new indexer with 
+     * 
+     * Parameters:
+     * yOrdering - {Boolean} Whether to use y-ordering.
+     */
+    initialize: function(yOrdering) {
+
+        this.compare = yOrdering ? 
+            OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :
+            OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;
+            
+        this.order = [];
+        this.indices = {};
+        this.maxZIndex = 0;
+    },
+    
+    /**
+     * APIMethod: insert
+     * Insert a new node into the indexer. In order to find the correct 
+     *     positioning for the node to be inserted, this method uses a binary 
+     *     search. This makes inserting O(log(n)). 
+     * 
+     * Parameters:
+     * newNode - {DOMElement} The new node to be inserted.
+     * 
+     * Returns
+     * {DOMElement} the node before which we should insert our newNode, or
+     *     null if newNode can just be appended.
+     */
+    insert: function(newNode) {
+        // If the node is known to the indexer, remove it so we can
+        // recalculate where it should go.
+        if (this.exists(newNode)) {
+            this.remove(newNode);
+        }
+        
+        var nodeId = newNode.id;
+        
+        this.determineZIndex(newNode);       
+
+        var leftIndex = -1;
+        var rightIndex = this.order.length;
+        var middle;
+
+        while (rightIndex - leftIndex > 1) {
+            middle = parseInt((leftIndex + rightIndex) / 2);
+            
+            var placement = this.compare(this, newNode,
+                OpenLayers.Util.getElement(this.order[middle]));
+            
+            if (placement > 0) {
+                leftIndex = middle;
+            } else {
+                rightIndex = middle;
+            } 
+        }
+        
+        this.order.splice(rightIndex, 0, nodeId);
+        this.indices[nodeId] = this.getZIndex(newNode);
+        
+        // If the new node should be before another in the index
+        // order, return the node before which we have to insert the new one;
+        // else, return null to indicate that the new node can be appended.
+        var nextIndex = rightIndex + 1;
+        return nextIndex < this.order.length ?
+                OpenLayers.Util.getElement(this.order[nextIndex]) : null;
+    },
+    
+    /**
+     * APIMethod: remove
+     * 
+     * Parameters:
+     * node - {DOMElement} The node to be removed.
+     */
+    remove: function(node) {
+        var nodeId = node.id;
+        var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);
+        if (arrayIndex >= 0) {
+            // Remove it from the order array, as well as deleting the node
+            // from the indeces hash.
+            this.order.splice(arrayIndex, 1);
+            delete this.indices[nodeId];
+            
+            // Reset the maxium z-index based on the last item in the 
+            // order array.
+            if (this.order.length > 0) {
+                var lastId = this.order[this.order.length - 1];
+                this.maxZIndex = this.indices[lastId];
+            } else {
+                this.maxZIndex = 0;
+            }
+        }
+    },
+    
+    /**
+     * APIMethod: clear
+     */
+    clear: function() {
+        this.order = [];
+        this.indices = {};
+        this.maxZIndex = 0;
+    },
+    
+    /**
+     * APIMethod: exists
+     *
+     * Parameters:
+     * node- {DOMElement} The node to test for existence.
+     *
+     * Returns:
+     * {Boolean} Whether or not the node exists in the indexer?
+     */
+    exists: function(node) {
+        return (this.indices[node.id] != null);
+    },
+
+    /**
+     * APIMethod: getZIndex
+     * Get the z-index value for the current node from the node data itself.
+     * 
+     * Parameters:
+     * node - {DOMElement} The node whose z-index to get.
+     * 
+     * Returns:
+     * {Integer} The z-index value for the specified node (from the node 
+     *     data itself).
+     */
+    getZIndex: function(node) {
+        return node._style.graphicZIndex;  
+    },
+    
+    /**
+     * Method: determineZIndex
+     * Determine the z-index for the current node if there isn't one, 
+     *     and set the maximum value if we've found a new maximum.
+     * 
+     * Parameters:
+     * node - {DOMElement} 
+     */
+    determineZIndex: function(node) {
+        var zIndex = node._style.graphicZIndex;
+        
+        // Everything must have a zIndex. If none is specified,
+        // this means the user *must* (hint: assumption) want this
+        // node to succomb to drawing order. To enforce drawing order
+        // over all indexing methods, we'll create a new z-index that's
+        // greater than any currently in the indexer.
+        if (zIndex == null) {
+            zIndex = this.maxZIndex;
+            node._style.graphicZIndex = zIndex; 
+        } else if (zIndex > this.maxZIndex) {
+            this.maxZIndex = zIndex;
+        }
+    },
+    
+    CLASS_NAME: "OpenLayers.ElementsIndexer"
+});
+
+/**
+ * Namespace: OpenLayers.ElementsIndexer.IndexingMethods
+ * These are the compare methods for figuring out where a new node should be 
+ *     placed within the indexer. These methods are very similar to general 
+ *     sorting methods in that they return -1, 0, and 1 to specify the 
+ *     direction in which new nodes fall in the ordering.
+ */
+OpenLayers.ElementsIndexer.IndexingMethods = {
+    
+    /**
+     * Method: Z_ORDER
+     * This compare method is used by other comparison methods.
+     *     It can be used individually for ordering, but is not recommended,
+     *     because it doesn't subscribe to drawing order.
+     * 
+     * Parameters:
+     * indexer - {<OpenLayers.ElementsIndexer>}
+     * newNode - {DOMElement}
+     * nextNode - {DOMElement}
+     * 
+     * Returns:
+     * {Integer}
+     */
+    Z_ORDER: function(indexer, newNode, nextNode) {
+        var newZIndex = indexer.getZIndex(newNode);
+
+        var returnVal = 0;
+        if (nextNode) {
+            var nextZIndex = indexer.getZIndex(nextNode);
+            returnVal = newZIndex - nextZIndex; 
+        }
+        
+        return returnVal;
+    },
+
+    /**
+     * APIMethod: Z_ORDER_DRAWING_ORDER
+     * This method orders nodes by their z-index, but does so in a way
+     *     that, if there are other nodes with the same z-index, the newest 
+     *     drawn will be the front most within that z-index. This is the 
+     *     default indexing method.
+     * 
+     * Parameters:
+     * indexer - {<OpenLayers.ElementsIndexer>}
+     * newNode - {DOMElement}
+     * nextNode - {DOMElement}
+     * 
+     * Returns:
+     * {Integer}
+     */
+    Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {
+        var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
+            indexer, 
+            newNode, 
+            nextNode
+        );
+        
+        // Make Z_ORDER subscribe to drawing order by pushing it above
+        // all of the other nodes with the same z-index.
+        if (nextNode && returnVal == 0) {
+            returnVal = 1;
+        }
+        
+        return returnVal;
+    },
+
+    /**
+     * APIMethod: Z_ORDER_Y_ORDER
+     * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it
+     *     best describes which ordering methods have precedence (though, the 
+     *     name would be too long). This method orders nodes by their z-index, 
+     *     but does so in a way that, if there are other nodes with the same 
+     *     z-index, the nodes with the lower y position will be "closer" than 
+     *     those with a higher y position. If two nodes have the exact same y 
+     *     position, however, then this method will revert to using drawing  
+     *     order to decide placement.
+     * 
+     * Parameters:
+     * indexer - {<OpenLayers.ElementsIndexer>}
+     * newNode - {DOMElement}
+     * nextNode - {DOMElement}
+     * 
+     * Returns:
+     * {Integer}
+     */
+    Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {
+        var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
+            indexer, 
+            newNode, 
+            nextNode
+        );
+        
+        if (nextNode && returnVal == 0) {
+            var newLat = newNode._geometry.getBounds().bottom;
+            var nextLat = nextNode._geometry.getBounds().bottom;
+            
+            var result = nextLat - newLat;
+            returnVal = (result ==0) ? 1 : result;
+        }
+        
+        return returnVal;       
+    }
+};
+
+/**
+ * Class: OpenLayers.Renderer.Elements
+ * This is another virtual class in that it should never be instantiated by 
+ *  itself as a Renderer. It exists because there is *tons* of shared 
+ *  functionality between different vector libraries which use nodes/elements
+ *  as a base for rendering vectors. 
+ * 
+ * The highlevel bits of code that are implemented here are the adding and 
+ *  removing of geometries, which is essentially the same for any 
+ *  element-based renderer. The details of creating each node and drawing the
+ *  paths are of course different, but the machinery is the same. 
+ * 
+ * Inherits:
+ *  - <OpenLayers.Renderer>
+ */
+OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
+
+    /**
+     * Property: rendererRoot
+     * {DOMElement}
+     */
+    rendererRoot: null,
+    
+    /**
+     * Property: root
+     * {DOMElement}
+     */
+    root: null,
+
+    /**
+     * Property: xmlns
+     * {String}
+     */    
+    xmlns: null,
+    
+    /**
+     * Property: Indexer
+     * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer 
+     *     created upon initialization if the zIndexing or yOrdering options
+     *     passed to this renderer's constructor are set to true.
+     */
+    indexer: null, 
+    
+    /**
+     * Constant: BACKGROUND_ID_SUFFIX
+     * {String}
+     */
+    BACKGROUND_ID_SUFFIX: "_background",
+    
+    /**
+     * Property: minimumSymbolizer
+     * {Object}
+     */
+    minimumSymbolizer: {
+        strokeLinecap: "round",
+        strokeOpacity: 1,
+        strokeDashstyle: "solid",
+        fillOpacity: 1,
+        pointRadius: 0
+    },
+    
+    /**
+     * Constructor: OpenLayers.Renderer.Elements
+     * 
+     * Parameters:
+     * containerID - {String}
+     * options - {Object} options for this renderer. Supported options are:
+     *     * yOrdering - {Boolean} Whether to use y-ordering
+     *     * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
+     *         if yOrdering is set to true.
+     */
+    initialize: function(containerID, options) {
+        OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
+
+        this.rendererRoot = this.createRenderRoot();
+        this.root = this.createRoot();
+        
+        this.rendererRoot.appendChild(this.root);
+        this.container.appendChild(this.rendererRoot);
+        
+        if(options && (options.zIndexing || options.yOrdering)) {
+            this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);
+        }
+    },
+    
+    /**
+     * Method: destroy
+     */
+    destroy: function() {
+
+        this.clear(); 
+
+        this.rendererRoot = null;
+        this.root = null;
+        this.xmlns = null;
+
+        OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
+    },
+    
+    /**
+     * Method: clear
+     * Remove all the elements from the root
+     */    
+    clear: function() {
+        if (this.root) {
+            while (this.root.childNodes.length > 0) {
+                this.root.removeChild(this.root.firstChild);
+            }
+        }
+        if (this.indexer) {
+            this.indexer.clear();
+        }
+    },
+
+    /** 
+     * Method: getNodeType
+     * This function is in charge of asking the specific renderer which type
+     *     of node to create for the given geometry and style. All geometries
+     *     in an Elements-based renderer consist of one node and some
+     *     attributes. We have the nodeFactory() function which creates a node
+     *     for us, but it takes a 'type' as input, and that is precisely what
+     *     this function tells us.  
+     *  
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * 
+     * Returns:
+     * {String} The corresponding node type for the specified geometry
+     */
+    getNodeType: function(geometry, style) { },
+
+    /** 
+     * Method: drawGeometry 
+     * Draw the geometry, creating new nodes, setting paths, setting style,
+     *     setting featureId on the node.  This method should only be called
+     *     by the renderer itself.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * featureId - {String}
+     * 
+     * Returns:
+     * {Boolean} true if the geometry has been drawn completely; null if
+     *     incomplete; false otherwise
+     */
+    drawGeometry: function(geometry, style, featureId) {
+        var className = geometry.CLASS_NAME;
+        var rendered = true;
+        if ((className == "OpenLayers.Geometry.Collection") ||
+            (className == "OpenLayers.Geometry.MultiPoint") ||
+            (className == "OpenLayers.Geometry.MultiLineString") ||
+            (className == "OpenLayers.Geometry.MultiPolygon")) {
+            for (var i = 0, len=geometry.components.length; i<len; i++) {
+                rendered = this.drawGeometry(
+                    geometry.components[i], style, featureId) && rendered;
+            }
+            return rendered;
+        };
+
+        rendered = false;
+        if (style.display != "none") {
+            if (style.backgroundGraphic) {
+                this.redrawBackgroundNode(geometry.id, geometry, style,
+                    featureId);
+            }
+            rendered = this.redrawNode(geometry.id, geometry, style,
+                featureId);
+        }
+        if (rendered == false) {
+            var node = document.getElementById(geometry.id);
+            if (node) {
+                if (node._style.backgroundGraphic) {
+                    node.parentNode.removeChild(document.getElementById(
+                        geometry.id + this.BACKGROUND_ID_SUFFIX));
+                }
+                node.parentNode.removeChild(node);
+            }
+        }
+        return rendered;
+    },
+    
+    /**
+     * Method: redrawNode
+     * 
+     * Parameters:
+     * id - {String}
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * featureId - {String}
+     * 
+     * Returns:
+     * {Boolean} true if the complete geometry could be drawn, null if parts of
+     *     the geometry could not be drawn, false otherwise
+     */
+    redrawNode: function(id, geometry, style, featureId) {
+        // Get the node if it's already on the map.
+        var node = this.nodeFactory(id, this.getNodeType(geometry, style));
+        
+        // Set the data for the node, then draw it.
+        node._featureId = featureId;
+        node._geometry = geometry;
+        node._geometryClass = geometry.CLASS_NAME;
+        node._style = style;
+
+        var drawResult = this.drawGeometryNode(node, geometry, style);
+        if(drawResult === false) {
+            return false;
+        }
+         
+        node = drawResult.node;
+        
+        // Insert the node into the indexer so it can show us where to
+        // place it. Note that this operation is O(log(n)). If there's a
+        // performance problem (when dragging, for instance) this is
+        // likely where it would be.
+        var insert = this.indexer ? this.indexer.insert(node) : null;
+
+        if(insert) {
+            this.root.insertBefore(node, insert);
+        } else {
+            this.root.appendChild(node);
+        }
+        
+        this.postDraw(node);
+        
+        return drawResult.complete;
+    },
+    
+    /**
+     * Method: redrawBackgroundNode
+     * Redraws the node using special 'background' style properties. Basically
+     *     just calls redrawNode(), but instead of directly using the 
+     *     'externalGraphic', 'graphicXOffset', 'graphicYOffset', and 
+     *     'graphicZIndex' properties directly from the specified 'style' 
+     *     parameter, we create a new style object and set those properties 
+     *     from the corresponding 'background'-prefixed properties from 
+     *     specified 'style' parameter.
+     * 
+     * Parameters:
+     * id - {String}
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * featureId - {String}
+     * 
+     * Returns:
+     * {Boolean} true if the complete geometry could be drawn, null if parts of
+     *     the geometry could not be drawn, false otherwise
+     */
+    redrawBackgroundNode: function(id, geometry, style, featureId) {
+        var backgroundStyle = OpenLayers.Util.extend({}, style);
+        
+        // Set regular style attributes to apply to the background styles.
+        backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;
+        backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;
+        backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;
+        backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;
+        
+        // Erase background styles.
+        backgroundStyle.backgroundGraphic = null;
+        backgroundStyle.backgroundXOffset = null;
+        backgroundStyle.backgroundYOffset = null;
+        backgroundStyle.backgroundGraphicZIndex = null;
+        
+        return this.redrawNode(
+            id + this.BACKGROUND_ID_SUFFIX, 
+            geometry, 
+            backgroundStyle, 
+            null
+        );
+    },
+
+    /**
+     * Method: drawGeometryNode
+     * Given a node, draw a geometry on the specified layer.
+     *     node and geometry are required arguments, style is optional.
+     *     This method is only called by the render itself.
+     *
+     * Parameters:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * 
+     * Returns:
+     * {Object} a hash with properties "node" (the drawn node) and "complete"
+     *     (null if parts of the geometry could not be drawn, false if nothing
+     *     could be drawn)
+     */
+    drawGeometryNode: function(node, geometry, style) {
+        style = style || node._style;
+        OpenLayers.Util.applyDefaults(style, this.minimumSymbolizer);
+
+        var options = {
+            'isFilled': true,
+            'isStroked': !!style.strokeWidth
+        };
+        var drawn;
+        switch (geometry.CLASS_NAME) {
+            case "OpenLayers.Geometry.Point":
+                drawn = this.drawPoint(node, geometry);
+                break;
+            case "OpenLayers.Geometry.LineString":
+                options.isFilled = false;
+                drawn = this.drawLineString(node, geometry);
+                break;
+            case "OpenLayers.Geometry.LinearRing":
+                drawn = this.drawLinearRing(node, geometry);
+                break;
+            case "OpenLayers.Geometry.Polygon":
+                drawn = this.drawPolygon(node, geometry);
+                break;
+            case "OpenLayers.Geometry.Surface":
+                drawn = this.drawSurface(node, geometry);
+                break;
+            case "OpenLayers.Geometry.Rectangle":
+                drawn = this.drawRectangle(node, geometry);
+                break;
+            default:
+                break;
+        }
+
+        node._style = style; 
+        node._options = options; 
+
+        //set style
+        //TBD simplify this
+        if (drawn != false) {
+            return {
+                node: this.setStyle(node, style, options, geometry),
+                complete: drawn
+            };
+        } else {
+            return false;
+        }
+    },
+    
+    /**
+     * Method: postDraw
+     * Things that have do be done after the geometry node is appended
+     *     to its parent node. To be overridden by subclasses.
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     */
+    postDraw: function(node) {},
+    
+    /**
+     * Method: drawPoint
+     * Virtual function for drawing Point Geometry. 
+     *     Should be implemented by subclasses.
+     *     This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the point
+     */ 
+    drawPoint: function(node, geometry) {},
+
+    /**
+     * Method: drawLineString
+     * Virtual function for drawing LineString Geometry. 
+     *     Should be implemented by subclasses.
+     *     This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components of
+     *     the linestring, or false if nothing could be drawn
+     */ 
+    drawLineString: function(node, geometry) {},
+
+    /**
+     * Method: drawLinearRing
+     * Virtual function for drawing LinearRing Geometry. 
+     *     Should be implemented by subclasses.
+     *     This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components
+     *     of the linear ring, or false if nothing could be drawn
+     */ 
+    drawLinearRing: function(node, geometry) {},
+
+    /**
+     * Method: drawPolygon
+     * Virtual function for drawing Polygon Geometry. 
+     *    Should be implemented by subclasses.
+     *    This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components
+     *     of the polygon, or false if nothing could be drawn
+     */ 
+    drawPolygon: function(node, geometry) {},
+
+    /**
+     * Method: drawRectangle
+     * Virtual function for drawing Rectangle Geometry. 
+     *     Should be implemented by subclasses.
+     *     This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the rectangle
+     */ 
+    drawRectangle: function(node, geometry) {},
+
+    /**
+     * Method: drawCircle
+     * Virtual function for drawing Circle Geometry. 
+     *     Should be implemented by subclasses.
+     *     This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the circle
+     */ 
+    drawCircle: function(node, geometry) {},
+
+    /**
+     * Method: drawSurface
+     * Virtual function for drawing Surface Geometry. 
+     *     Should be implemented by subclasses.
+     *     This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the surface
+     */ 
+    drawSurface: function(node, geometry) {},
+
+    /**
+     * Method: getFeatureIdFromEvent
+     * 
+     * Parameters:
+     * evt - {Object} An <OpenLayers.Event> object
+     *
+     * Returns:
+     * {<OpenLayers.Geometry>} A geometry from an event that 
+     *     happened on a layer.
+     */
+    getFeatureIdFromEvent: function(evt) {
+        var target = evt.target;
+        var useElement = target && target.correspondingUseElement;
+        var node = useElement ? useElement : (target || evt.srcElement);
+        var featureId = node._featureId;
+        return featureId;
+    },
+
+    /** 
+     * Method: eraseGeometry
+     * Erase a geometry from the renderer. In the case of a multi-geometry, 
+     *     we cycle through and recurse on ourselves. Otherwise, we look for a 
+     *     node with the geometry.id, destroy its geometry, and remove it from
+     *     the DOM.
+     * 
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    eraseGeometry: function(geometry) {
+        if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") ||
+            (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") ||
+            (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") ||
+            (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) {
+            for (var i=0, len=geometry.components.length; i<len; i++) {
+                this.eraseGeometry(geometry.components[i]);
+            }
+        } else {    
+            var element = OpenLayers.Util.getElement(geometry.id);
+            if (element && element.parentNode) {
+                if (element.geometry) {
+                    element.geometry.destroy();
+                    element.geometry = null;
+                }
+                element.parentNode.removeChild(element);
+
+                if (this.indexer) {
+                    this.indexer.remove(element);
+                }
+                
+                if (element._style.backgroundGraphic) {
+                    var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;
+                    var bElem = OpenLayers.Util.getElement(backgroundId);
+                    if (bElem && bElem.parentNode) {
+                        // No need to destroy the geometry since the element and the background
+                        // node share the same geometry.
+                        bElem.parentNode.removeChild(bElem);
+                    }
+                }
+            }
+        }
+    },
+
+    /** 
+     * Method: nodeFactory
+     * Create new node of the specified type, with the (optional) specified id.
+     * 
+     * If node already exists with same ID and a different type, we remove it
+     *     and then call ourselves again to recreate it.
+     * 
+     * Parameters:
+     * id - {String}
+     * type - {String} type Kind of node to draw.
+     * 
+     * Returns:
+     * {DOMElement} A new node of the given type and id.
+     */
+    nodeFactory: function(id, type) {
+        var node = OpenLayers.Util.getElement(id);
+        if (node) {
+            if (!this.nodeTypeCompare(node, type)) {
+                node.parentNode.removeChild(node);
+                node = this.nodeFactory(id, type);
+            }
+        } else {
+            node = this.createNode(type, id);
+        }
+        return node;
+    },
+    
+    /** 
+     * Method: nodeTypeCompare
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * type - {String} Kind of node
+     * 
+     * Returns:
+     * {Boolean} Whether or not the specified node is of the specified type
+     *     This function must be overridden by subclasses.
+     */
+    nodeTypeCompare: function(node, type) {},
+    
+    /** 
+     * Method: createNode
+     * 
+     * Parameters:
+     * type - {String} Kind of node to draw.
+     * id - {String} Id for node.
+     * 
+     * Returns:
+     * {DOMElement} A new node of the given type and id.
+     *     This function must be overridden by subclasses.
+     */
+    createNode: function(type, id) {},
+
+    /**
+     * Method: isComplexSymbol
+     * Determines if a symbol cannot be rendered using drawCircle
+     * 
+     * Parameters:
+     * graphicName - {String}
+     * 
+     * Returns
+     * {Boolean} true if the symbol is complex, false if not
+     */
+    isComplexSymbol: function(graphicName) {
+        return (graphicName != "circle") && !!graphicName;
+    },
+
+    CLASS_NAME: "OpenLayers.Renderer.Elements"
+});
+
+
+/**
+ * Constant: OpenLayers.Renderer.symbol
+ * Coordinate arrays for well known (named) symbols.
+ */
+OpenLayers.Renderer.symbol = {
+    "star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301,
+            303,215, 231,161, 321,161, 350,75],
+    "cross": [4,0, 6,0, 6,4, 10,4, 10,6, 6,6, 6,10, 4,10, 4,6, 0,6, 0,4, 4,4,
+            4,0],
+    "x": [0,0, 25,0, 50,35, 75,0, 100,0, 65,50, 100,100, 75,100, 50,65, 25,100, 0,100, 35,50, 0,0],
+    "square": [0,0, 0,1, 1,1, 1,0, 0,0],
+    "triangle": [0,10, 10,10, 5,0, 0,10]
+};
+/* ======================================================================
     OpenLayers/Request/XMLHttpRequest.js
    ====================================================================== */
 
@@ -9028,7 +10444,7 @@
 };
 
 /** 
- * Function: loadURL
+ * APIFunction: loadURL
  * Background load a document.  For more flexibility in using XMLHttpRequest,
  *     see the <OpenLayers.Request> methods.
  *
@@ -11285,7 +12701,11 @@
      * {<OpenLayers.LonLat>}
      */
     getCenter: function () {
-        return this.center;
+        var center = null;
+        if (this.center) {
+            center = this.center.clone();
+        }
+        return center;
     },
 
 
@@ -11513,6 +12933,13 @@
             
             //send the move call to the baselayer and all the overlays    
             this.baseLayer.moveTo(bounds, zoomChanged, dragging);
+            if(dragging) {
+                this.baseLayer.events.triggerEvent("move");
+            } else {
+                this.baseLayer.events.triggerEvent("moveend",
+                    {"zoomChanged": zoomChanged}
+                );
+            }
             
             bounds = this.baseLayer.getExtent();
             
@@ -11535,9 +12962,13 @@
                     }
                     if (inRange && layer.visibility) {
                         layer.moveTo(bounds, zoomChanged, dragging);
-                        layer.events.triggerEvent("moveend",
-                            {"zoomChanged": zoomChanged}
-                        );
+                        if(dragging) {
+                            layer.events.triggerEvent("move");
+                        } else {
+                            layer.events.triggerEvent("moveend",
+                                {"zoomChanged": zoomChanged}
+                            );
+                        }
                     }
                 }                
             }
@@ -12309,6 +13740,17 @@
     },
 
     /**
+     * APIMethod: isDrawn
+     * 
+     * Returns:
+     * {Boolean} Whether or not the marker is drawn.
+     */
+    isDrawn: function() {
+        var isDrawn = (this.icon && this.icon.isDrawn());
+        return isDrawn;   
+    },
+
+    /**
      * Method: onScreen
      *
      * Returns:
@@ -12588,6 +14030,1702 @@
 OpenLayers.Popup.AnchoredBubble.CORNER_SIZE = 5;
 
 /* ======================================================================
+    OpenLayers/Renderer/SVG.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Renderer/Elements.js
+ */
+
+/**
+ * Class: OpenLayers.Renderer.SVG
+ * 
+ * Inherits:
+ *  - <OpenLayers.Renderer.Elements>
+ */
+OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
+
+    /** 
+     * Property: xmlns
+     * {String}
+     */
+    xmlns: "http://www.w3.org/2000/svg",
+    
+    /**
+     * Property: xlinkns
+     * {String}
+     */
+    xlinkns: "http://www.w3.org/1999/xlink",
+
+    /**
+     * Constant: MAX_PIXEL
+     * {Integer} Firefox has a limitation where values larger or smaller than  
+     *           about 15000 in an SVG document lock the browser up. This 
+     *           works around it.
+     */
+    MAX_PIXEL: 15000,
+
+    /**
+     * Property: translationParameters
+     * {Object} Hash with "x" and "y" properties
+     */
+    translationParameters: null,
+    
+    /**
+     * Property: symbolSize
+     * {Object} Cache for symbol sizes according to their svg coordinate space
+     */
+    symbolSize: {},
+
+    /**
+     * Constructor: OpenLayers.Renderer.SVG
+     * 
+     * Parameters:
+     * containerID - {String}
+     */
+    initialize: function(containerID) {
+        if (!this.supported()) { 
+            return; 
+        }
+        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
+                                                                arguments);
+        this.translationParameters = {x: 0, y: 0};
+    },
+
+    /**
+     * APIMethod: destroy
+     */
+    destroy: function() {
+        OpenLayers.Renderer.Elements.prototype.destroy.apply(this, arguments);
+    },
+    
+    /**
+     * APIMethod: supported
+     * 
+     * Returns:
+     * {Boolean} Whether or not the browser supports the SVG renderer
+     */
+    supported: function() {
+        var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
+        return (document.implementation && 
+           (document.implementation.hasFeature("org.w3c.svg", "1.0") || 
+            document.implementation.hasFeature(svgFeature + "SVG", "1.1") || 
+            document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
+    },    
+
+    /**
+     * Method: inValidRange
+     * See #669 for more information
+     *
+     * Parameters:
+     * x      - {Integer}
+     * y      - {Integer}
+     * xyOnly - {Boolean} whether or not to just check for x and y, which means
+     *     to not take the current translation parameters into account if true.
+     * 
+     * Returns:
+     * {Boolean} Whether or not the 'x' and 'y' coordinates are in the  
+     *           valid range.
+     */ 
+    inValidRange: function(x, y, xyOnly) {
+        var left = x + (xyOnly ? 0 : this.translationParameters.x);
+        var top = y + (xyOnly ? 0 : this.translationParameters.y);
+        return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&
+                top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);
+    },
+
+    /**
+     * Method: setExtent
+     * 
+     * Parameters:
+     * extent - {<OpenLayers.Bounds>}
+     * resolutionChanged - {Boolean}
+     * 
+     * Returns:
+     * {Boolean} true to notify the layer that the new extent does not exceed
+     *     the coordinate range, and the features will not need to be redrawn.
+     *     False otherwise.
+     */
+    setExtent: function(extent, resolutionChanged) {
+        OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, 
+                                                               arguments);
+        
+        var resolution = this.getResolution();
+        var left = -extent.left / resolution;
+        var top = extent.top / resolution;
+
+        // If the resolution has changed, start over changing the corner, because
+        // the features will redraw.
+        if (resolutionChanged) {
+            this.left = left;
+            this.top = top;
+            // Set the viewbox
+            var extentString = "0 0 " + this.size.w + " " + this.size.h;
+
+            this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
+            this.translate(0, 0);
+            return true;
+        } else {
+            var inRange = this.translate(left - this.left, top - this.top);
+            if (!inRange) {
+                // recenter the coordinate system
+                this.setExtent(extent, true);
+            }
+            return inRange;
+        }
+    },
+    
+    /**
+     * Method: translate
+     * Transforms the SVG coordinate system
+     * 
+     * Parameters:
+     * x - {Float}
+     * y - {Float}
+     * 
+     * Returns:
+     * {Boolean} true if the translation parameters are in the valid coordinates
+     *     range, false otherwise.
+     */
+    translate: function(x, y) {
+        if (!this.inValidRange(x, y, true)) {
+            return false;
+        } else {
+            var transformString = "";
+            if (x || y) {
+                transformString = "translate(" + x + "," + y + ")";
+            }
+            this.root.setAttributeNS(null, "transform", transformString);
+            this.translationParameters = {x: x, y: y};
+            return true;
+        }
+    },
+
+    /**
+     * Method: setSize
+     * Sets the size of the drawing surface.
+     * 
+     * Parameters:
+     * size - {<OpenLayers.Size>} The size of the drawing surface
+     */
+    setSize: function(size) {
+        OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
+        
+        this.rendererRoot.setAttributeNS(null, "width", this.size.w);
+        this.rendererRoot.setAttributeNS(null, "height", this.size.h);
+    },
+
+    /** 
+     * Method: getNodeType 
+     * 
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * 
+     * Returns:
+     * {String} The corresponding node type for the specified geometry
+     */
+    getNodeType: function(geometry, style) {
+        var nodeType = null;
+        switch (geometry.CLASS_NAME) {
+            case "OpenLayers.Geometry.Point":
+                if (style.externalGraphic) {
+                    nodeType = "image";
+                } else if (this.isComplexSymbol(style.graphicName)) {
+                    nodeType = "use";
+                } else {
+                    nodeType = "circle";
+                }
+                break;
+            case "OpenLayers.Geometry.Rectangle":
+                nodeType = "rect";
+                break;
+            case "OpenLayers.Geometry.LineString":
+                nodeType = "polyline";
+                break;
+            case "OpenLayers.Geometry.LinearRing":
+                nodeType = "polygon";
+                break;
+            case "OpenLayers.Geometry.Polygon":
+            case "OpenLayers.Geometry.Curve":
+            case "OpenLayers.Geometry.Surface":
+                nodeType = "path";
+                break;
+            default:
+                break;
+        }
+        return nodeType;
+    },
+
+    /** 
+     * Method: setStyle
+     * Use to set all the style attributes to a SVG node.
+     * 
+     * Takes care to adjust stroke width and point radius to be
+     * resolution-relative
+     *
+     * Parameters:
+     * node - {SVGDomElement} An SVG element to decorate
+     * style - {Object}
+     * options - {Object} Currently supported options include 
+     *                              'isFilled' {Boolean} and
+     *                              'isStroked' {Boolean}
+     */
+    setStyle: function(node, style, options) {
+        style = style  || node._style;
+        options = options || node._options;
+        var r = parseFloat(node.getAttributeNS(null, "r"));
+        var widthFactor = 1;
+        var pos;
+        if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
+            if (style.externalGraphic) {
+                pos = this.getPosition(node);
+                
+                if (style.graphicWidth && style.graphicHeight) {
+                  node.setAttributeNS(null, "preserveAspectRatio", "none");
+                }
+                var width = style.graphicWidth || style.graphicHeight;
+                var height = style.graphicHeight || style.graphicWidth;
+                width = width ? width : style.pointRadius*2;
+                height = height ? height : style.pointRadius*2;
+                var xOffset = (style.graphicXOffset != undefined) ?
+                    style.graphicXOffset : -(0.5 * width);
+                var yOffset = (style.graphicYOffset != undefined) ?
+                    style.graphicYOffset : -(0.5 * height);
+
+                var opacity = style.graphicOpacity || style.fillOpacity;
+                
+                node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed());
+                node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed());
+                node.setAttributeNS(null, "width", width);
+                node.setAttributeNS(null, "height", height);
+                node.setAttributeNS(this.xlinkns, "href", style.externalGraphic);
+                node.setAttributeNS(null, "style", "opacity: "+opacity);
+            } else if (this.isComplexSymbol(style.graphicName)) {
+                // the symbol viewBox is three times as large as the symbol
+                var offset = style.pointRadius * 3;
+                var size = offset * 2;
+                var id = this.importSymbol(style.graphicName);
+                var href = "#" + id;
+                pos = this.getPosition(node);
+                widthFactor = this.symbolSize[id] / size;
+                // Only set the href if it is different from the current one.
+                // This is a workaround for strange rendering behavior in FF3.
+                if (node.getAttributeNS(this.xlinkns, "href") != href) {
+                    node.setAttributeNS(this.xlinkns, "href", href);
+                } else if (size != parseFloat(node.getAttributeNS(null, "width"))) {
+                    // hide the element (and force a reflow so it really gets
+                    // hidden. This workaround is needed for Safari.
+                    node.style.visibility = "hidden";
+                    this.container.scrollLeft = this.container.scrollLeft;
+                }
+                node.setAttributeNS(null, "width", size);
+                node.setAttributeNS(null, "height", size);
+                node.setAttributeNS(null, "x", pos.x - offset);
+                node.setAttributeNS(null, "y", pos.y - offset);
+                // set the visibility back to normal (after the Safari
+                // workaround above)
+                node.style.visibility = "";
+            } else {
+                node.setAttributeNS(null, "r", style.pointRadius);
+            }
+
+            if (typeof style.rotation != "undefined" && pos) {
+                var rotation = OpenLayers.String.format(
+                    "rotate(${0} ${1} ${2})", [style.rotation, pos.x, pos.y]);
+                node.setAttributeNS(null, "transform", rotation);
+            }
+        }
+        
+        if (options.isFilled) {
+            node.setAttributeNS(null, "fill", style.fillColor);
+            node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
+        } else {
+            node.setAttributeNS(null, "fill", "none");
+        }
+
+        if (options.isStroked) {
+            node.setAttributeNS(null, "stroke", style.strokeColor);
+            node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
+            node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
+            node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap);
+            // Hard-coded linejoin for now, to make it look the same as in VML.
+            // There is no strokeLinejoin property yet for symbolizers.
+            node.setAttributeNS(null, "stroke-linejoin", "round");
+            node.setAttributeNS(null, "stroke-dasharray", this.dashStyle(style,
+                widthFactor));
+        } else {
+            node.setAttributeNS(null, "stroke", "none");
+        }
+        
+        if (style.pointerEvents) {
+            node.setAttributeNS(null, "pointer-events", style.pointerEvents);
+        }
+        
+        if (style.cursor != null) {
+            node.setAttributeNS(null, "cursor", style.cursor);
+        }
+        return node;
+    },
+
+    /** 
+     * Method: dashStyle
+     * 
+     * Parameters:
+     * style - {Object}
+     * widthFactor - {Number}
+     * 
+     * Returns:
+     * {String} A SVG compliant 'stroke-dasharray' value
+     */
+    dashStyle: function(style, widthFactor) {
+        var w = style.strokeWidth * widthFactor;
+
+        switch (style.strokeDashstyle) {
+            case 'solid':
+                return 'none';
+            case 'dot':
+                return [1, 4 * w].join();
+            case 'dash':
+                return [4 * w, 4 * w].join();
+            case 'dashdot':
+                return [4 * w, 4 * w, 1, 4 * w].join();
+            case 'longdash':
+                return [8 * w, 4 * w].join();
+            case 'longdashdot':
+                return [8 * w, 4 * w, 1, 4 * w].join();
+            default:
+                return style.strokeDashstyle.replace(/ /g, ",");
+        }
+    },
+    
+    /** 
+     * Method: createNode
+     * 
+     * Parameters:
+     * type - {String} Kind of node to draw
+     * id - {String} Id for node
+     * 
+     * Returns:
+     * {DOMElement} A new node of the given type and id
+     */
+    createNode: function(type, id) {
+        var node = document.createElementNS(this.xmlns, type);
+        if (id) {
+            node.setAttributeNS(null, "id", id);
+        }
+        return node;    
+    },
+    
+    /** 
+     * Method: nodeTypeCompare
+     * 
+     * Parameters:
+     * node - {SVGDomElement} An SVG element
+     * type - {String} Kind of node
+     * 
+     * Returns:
+     * {Boolean} Whether or not the specified node is of the specified type
+     */
+    nodeTypeCompare: function(node, type) {
+        return (type == node.nodeName);
+    },
+   
+    /**
+     * Method: createRenderRoot
+     * 
+     * Returns:
+     * {DOMElement} The specific render engine's root element
+     */
+    createRenderRoot: function() {
+        return this.nodeFactory(this.container.id + "_svgRoot", "svg");
+    },
+
+    /**
+     * Method: createRoot
+     * 
+     * Returns:
+     * {DOMElement} The main root element to which we'll add vectors
+     */
+    createRoot: function() {
+        return this.nodeFactory(this.container.id + "_root", "g");
+    },
+
+    /**
+     * Method: createDefs
+     *
+     * Returns:
+     * {DOMElement} The element to which we'll add the symbol definitions
+     */
+    createDefs: function() {
+        var defs = this.nodeFactory("ol-renderer-defs", "defs");
+        this.rendererRoot.appendChild(defs);
+        return defs;
+    },
+
+    /**************************************
+     *                                    *
+     *     GEOMETRY DRAWING FUNCTIONS     *
+     *                                    *
+     **************************************/
+
+    /**
+     * Method: drawPoint
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the point
+     */ 
+    drawPoint: function(node, geometry) {
+        return this.drawCircle(node, geometry, 1);
+    },
+
+    /**
+     * Method: drawCircle
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * radius - {Float}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the circle
+     */
+    drawCircle: function(node, geometry, radius) {
+        var resolution = this.getResolution();
+        var x = (geometry.x / resolution + this.left);
+        var y = (this.top - geometry.y / resolution);
+
+        if (this.inValidRange(x, y)) { 
+            node.setAttributeNS(null, "cx", x);
+            node.setAttributeNS(null, "cy", y);
+            node.setAttributeNS(null, "r", radius);
+            return node;
+        } else {
+            return false;
+        }    
+            
+    },
+    
+    /**
+     * Method: drawLineString
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components of
+     *     the linestring, or false if nothing could be drawn
+     */ 
+    drawLineString: function(node, geometry) {
+        var componentsResult = this.getComponentsString(geometry.components);
+        if (componentsResult.path) {
+            node.setAttributeNS(null, "points", componentsResult.path);
+            return (componentsResult.complete ? node : null);  
+        } else {
+            return false;
+        }
+    },
+    
+    /**
+     * Method: drawLinearRing
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components
+     *     of the linear ring, or false if nothing could be drawn
+     */ 
+    drawLinearRing: function(node, geometry) {
+        var componentsResult = this.getComponentsString(geometry.components);
+        if (componentsResult.path) {
+            node.setAttributeNS(null, "points", componentsResult.path);
+            return (componentsResult.complete ? node : null);  
+        } else {
+            return false;
+        }
+    },
+    
+    /**
+     * Method: drawPolygon
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components
+     *     of the polygon, or false if nothing could be drawn
+     */ 
+    drawPolygon: function(node, geometry) {
+        var d = "";
+        var draw = true;
+        var complete = true;
+        var linearRingResult, path;
+        for (var j=0, len=geometry.components.length; j<len; j++) {
+            d += " M";
+            linearRingResult = this.getComponentsString(
+                geometry.components[j].components, " ");
+            path = linearRingResult.path;
+            if (path) {
+                d += " " + path;
+                complete = linearRingResult.complete && complete;
+            } else {
+                draw = false;
+            }
+        }
+        d += " z";
+        if (draw) {
+            node.setAttributeNS(null, "d", d);
+            node.setAttributeNS(null, "fill-rule", "evenodd");
+            return complete ? node : null;
+        } else {
+            return false;
+        }    
+    },
+    
+    /**
+     * Method: drawRectangle
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the rectangle
+     */ 
+    drawRectangle: function(node, geometry) {
+        var resolution = this.getResolution();
+        var x = (geometry.x / resolution + this.left);
+        var y = (this.top - geometry.y / resolution);
+
+        if (this.inValidRange(x, y)) { 
+            node.setAttributeNS(null, "x", x);
+            node.setAttributeNS(null, "y", y);
+            node.setAttributeNS(null, "width", geometry.width / resolution);
+            node.setAttributeNS(null, "height", geometry.height / resolution);
+            return node;
+        } else {
+            return false;
+        }
+    },
+    
+    /**
+     * Method: drawSurface
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the surface
+     */ 
+    drawSurface: function(node, geometry) {
+
+        // create the svg path string representation
+        var d = null;
+        var draw = true;
+        for (var i=0, len=geometry.components.length; i<len; i++) {
+            if ((i%3) == 0 && (i/3) == 0) {
+                var component = this.getShortString(geometry.components[i]);
+                if (!component) { draw = false; }
+                d = "M " + component;
+            } else if ((i%3) == 1) {
+                var component = this.getShortString(geometry.components[i]);
+                if (!component) { draw = false; }
+                d += " C " + component;
+            } else {
+                var component = this.getShortString(geometry.components[i]);
+                if (!component) { draw = false; }
+                d += " " + component;
+            }
+        }
+        d += " Z";
+        if (draw) {
+            node.setAttributeNS(null, "d", d);
+            return node;
+        } else {
+            return false;
+        }    
+    },
+
+    /** 
+     * Method: getComponentString
+     * 
+     * Parameters:
+     * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
+     * separator - {String} character between coordinate pairs. Defaults to ","
+     * 
+     * Returns:
+     * {Object} hash with properties "path" (the string created from the
+     *     components and "complete" (false if the renderer was unable to
+     *     draw all components)
+     */
+    getComponentsString: function(components, separator) {
+        var renderCmp = [];
+        var complete = true;
+        var len = components.length;
+        var strings = [];
+        var str, component, j;
+        for(var i=0; i<len; i++) {
+            component = components[i];
+            renderCmp.push(component);
+            str = this.getShortString(component);
+            if (str) {
+                strings.push(str);
+            } else {
+                // The current component is outside the valid range. Let's
+                // see if the previous or next component is inside the range.
+                // If so, add the coordinate of the intersection with the
+                // valid range bounds.
+                if (i > 0) {
+                    if (this.getShortString(components[i - 1])) {
+                        strings.push(this.clipLine(components[i],
+                            components[i-1]));
+                    }
+                }
+                if (i < len - 1) {
+                    if (this.getShortString(components[i + 1])) {
+                        strings.push(this.clipLine(components[i],
+                            components[i+1]));
+                    }
+                }
+                complete = false;
+            }
+        }
+
+        return {
+            path: strings.join(separator || ","),
+            complete: complete
+        };
+    },
+    
+    /**
+     * Method: clipLine
+     * Given two points (one inside the valid range, and one outside),
+     * clips the line betweeen the two points so that the new points are both
+     * inside the valid range.
+     * 
+     * Parameters:
+     * badComponent - {<OpenLayers.Geometry.Point>)} original geometry of the
+     *     invalid point
+     * goodComponent - {<OpenLayers.Geometry.Point>)} original geometry of the
+     *     valid point
+     * Returns
+     * {String} the SVG coordinate pair of the clipped point (like
+     *     getShortString), or an empty string if both passed componets are at
+     *     the same point.
+     */
+    clipLine: function(badComponent, goodComponent) {
+        if (goodComponent.equals(badComponent)) {
+            return "";
+        }
+        var resolution = this.getResolution();
+        var maxX = this.MAX_PIXEL - this.translationParameters.x;
+        var maxY = this.MAX_PIXEL - this.translationParameters.y;
+        var x1 = goodComponent.x / resolution + this.left;
+        var y1 = this.top - goodComponent.y / resolution;
+        var x2 = badComponent.x / resolution + this.left;
+        var y2 = this.top - badComponent.y / resolution;
+        var k;
+        if (x2 < -maxX || x2 > maxX) {
+            k = (y2 - y1) / (x2 - x1);
+            x2 = x2 < 0 ? -maxX : maxX;
+            y2 = y1 + (x2 - x1) * k;
+        }
+        if (y2 < -maxY || y2 > maxY) {
+            k = (x2 - x1) / (y2 - y1);
+            y2 = y2 < 0 ? -maxY : maxY;
+            x2 = x1 + (y2 - y1) * k;
+        }
+        return x2 + "," + y2;
+    },
+
+    /** 
+     * Method: getShortString
+     * 
+     * Parameters:
+     * point - {<OpenLayers.Geometry.Point>}
+     * 
+     * Returns:
+     * {String} or false if point is outside the valid range
+     */
+    getShortString: function(point) {
+        var resolution = this.getResolution();
+        var x = (point.x / resolution + this.left);
+        var y = (this.top - point.y / resolution);
+
+        if (this.inValidRange(x, y)) { 
+            return x + "," + y;
+        } else {
+            return false;
+        }
+    },
+    
+    /**
+     * Method: getPosition
+     * Finds the position of an svg node.
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * 
+     * Returns:
+     * {Object} hash with x and y properties, representing the coordinates
+     *     within the svg coordinate system
+     */
+    getPosition: function(node) {
+        return({
+            x: parseFloat(node.getAttributeNS(null, "cx")),
+            y: parseFloat(node.getAttributeNS(null, "cy"))
+        });
+    },
+
+    /**
+     * Method: importSymbol
+     * add a new symbol definition from the rendererer's symbol hash
+     * 
+     * Parameters:
+     * graphicName - {String} name of the symbol to import
+     * 
+     * Returns:
+     * {String} - id of the imported symbol
+     */      
+    importSymbol: function (graphicName)  {
+        if (!this.defs) {
+            // create svg defs tag
+            this.defs = this.createDefs();
+        }
+        var id = this.container.id + "-" + graphicName;
+        
+        // check if symbol already exists in the defs
+        if (document.getElementById(id) != null) {
+            return id;
+        }
+        
+        var symbol = OpenLayers.Renderer.symbol[graphicName];
+        if (!symbol) {
+            throw new Error(graphicName + ' is not a valid symbol name');
+            return;
+        }
+
+        var symbolNode = this.nodeFactory(id, "symbol");
+        var node = this.nodeFactory(null, "polygon");
+        symbolNode.appendChild(node);
+        var symbolExtent = new OpenLayers.Bounds(
+                                    Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
+
+        var points = "";
+        var x,y;
+        for (var i=0; i<symbol.length; i=i+2) {
+            x = symbol[i];
+            y = symbol[i+1];
+            symbolExtent.left = Math.min(symbolExtent.left, x);
+            symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
+            symbolExtent.right = Math.max(symbolExtent.right, x);
+            symbolExtent.top = Math.max(symbolExtent.top, y);
+            points += " " + x + "," + y;
+        }
+        
+        node.setAttributeNS(null, "points", points);
+        
+        var width = symbolExtent.getWidth();
+        var height = symbolExtent.getHeight();
+        // create a viewBox three times as large as the symbol itself,
+        // to allow for strokeWidth being displayed correctly at the corners.
+        var viewBox = [symbolExtent.left - width,
+                        symbolExtent.bottom - height, width * 3, height * 3];
+        symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
+        this.symbolSize[id] = Math.max(width, height) * 3;
+        
+        this.defs.appendChild(symbolNode);
+        return symbolNode.id;
+    },
+
+    CLASS_NAME: "OpenLayers.Renderer.SVG"
+});
+/* ======================================================================
+    OpenLayers/Renderer/VML.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Renderer/Elements.js
+ */
+
+/**
+ * Class: OpenLayers.Renderer.VML
+ * Render vector features in browsers with VML capability.  Construct a new
+ * VML renderer with the <OpenLayers.Renderer.VML> constructor.
+ * 
+ * Note that for all calculations in this class, we use toFixed() to round a 
+ * float value to an integer. This is done because it seems that VML doesn't 
+ * support float values.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Renderer.Elements>
+ */
+OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
+
+    /**
+     * Property: xmlns
+     * {String} XML Namespace URN
+     */
+    xmlns: "urn:schemas-microsoft-com:vml",
+    
+    /**
+     * Property: symbolCache
+     * {DOMElement} node holding symbols. This hash is keyed by symbol name,
+     *     and each value is a hash with a "path" and an "extent" property.
+     */
+    symbolCache: {},
+
+    /**
+     * Property: offset
+     * {Object} Hash with "x" and "y" properties
+     */
+    offset: null,
+    
+    /**
+     * Constructor: OpenLayers.Renderer.VML
+     * Create a new VML renderer.
+     *
+     * Parameters:
+     * containerID - {String} The id for the element that contains the renderer
+     */
+    initialize: function(containerID) {
+        if (!this.supported()) { 
+            return; 
+        }
+        if (!document.namespaces.olv) {
+            document.namespaces.add("olv", this.xmlns);
+            var style = document.createStyleSheet();
+            style.addRule('olv\\:*', "behavior: url(#default#VML); " +
+                                   "position: absolute; display: inline-block;");
+        }
+        
+        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
+                                                                arguments);
+        this.offset = {x: 0, y: 0};
+    },
+
+    /**
+     * APIMethod: destroy
+     * Deconstruct the renderer.
+     */
+    destroy: function() {
+        OpenLayers.Renderer.Elements.prototype.destroy.apply(this, arguments);
+    },
+
+    /**
+     * APIMethod: supported
+     * Determine whether a browser supports this renderer.
+     *
+     * Returns:
+     * {Boolean} The browser supports the VML renderer
+     */
+    supported: function() {
+        return !!(document.namespaces);
+    },    
+
+    /**
+     * Method: setExtent
+     * Set the renderer's extent
+     *
+     * Parameters:
+     * extent - {<OpenLayers.Bounds>}
+     * resolutionChanged - {Boolean}
+     * 
+     * Returns:
+     * {Boolean} true to notify the layer that the new extent does not exceed
+     *     the coordinate range, and the features will not need to be redrawn.
+     */
+    setExtent: function(extent, resolutionChanged) {
+        OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, 
+                                                               arguments);
+        var resolution = this.getResolution();
+    
+        var left = extent.left/resolution;
+        var top = extent.top/resolution - this.size.h;
+        if (resolutionChanged) {
+            this.offset = {x: left, y: top};
+            left = 0;
+            top = 0;
+        } else {
+            left = left - this.offset.x;
+            top = top - this.offset.y;
+        }
+        
+        var org = left + " " + top;
+        this.root.setAttribute("coordorigin", org);
+
+        var size = this.size.w + " " + this.size.h;
+        this.root.setAttribute("coordsize", size);
+        
+        // flip the VML display Y axis upside down so it 
+        // matches the display Y axis of the map
+        this.root.style.flip = "y";
+        
+        return true;
+    },
+
+
+    /**
+     * Method: setSize
+     * Set the size of the drawing surface
+     *
+     * Parameters:
+     * size - {<OpenLayers.Size>} the size of the drawing surface
+     */
+    setSize: function(size) {
+        OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
+
+        this.rendererRoot.style.width = this.size.w + "px";
+        this.rendererRoot.style.height = this.size.h + "px";
+
+        this.root.style.width = this.size.w + "px";
+        this.root.style.height = this.size.h + "px";
+    },
+
+    /**
+     * Method: getNodeType
+     * Get the node type for a geometry and style
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     *
+     * Returns:
+     * {String} The corresponding node type for the specified geometry
+     */
+    getNodeType: function(geometry, style) {
+        var nodeType = null;
+        switch (geometry.CLASS_NAME) {
+            case "OpenLayers.Geometry.Point":
+                if (style.externalGraphic) {
+                    nodeType = "olv:rect";
+                } else if (this.isComplexSymbol(style.graphicName)) {
+                    nodeType = "olv:shape";
+                } else {
+                    nodeType = "olv:oval";
+                }
+                break;
+            case "OpenLayers.Geometry.Rectangle":
+                nodeType = "olv:rect";
+                break;
+            case "OpenLayers.Geometry.LineString":
+            case "OpenLayers.Geometry.LinearRing":
+            case "OpenLayers.Geometry.Polygon":
+            case "OpenLayers.Geometry.Curve":
+            case "OpenLayers.Geometry.Surface":
+                nodeType = "olv:shape";
+                break;
+            default:
+                break;
+        }
+        return nodeType;
+    },
+
+    /**
+     * Method: setStyle
+     * Use to set all the style attributes to a VML node.
+     *
+     * Parameters:
+     * node - {DOMElement} An VML element to decorate
+     * style - {Object}
+     * options - {Object} Currently supported options include 
+     *                              'isFilled' {Boolean} and
+     *                              'isStroked' {Boolean}
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    setStyle: function(node, style, options, geometry) {
+        style = style  || node._style;
+        options = options || node._options;
+        var widthFactor = 1;
+        
+        if (node._geometryClass == "OpenLayers.Geometry.Point") {
+            if (style.externalGraphic) {
+                var width = style.graphicWidth || style.graphicHeight;
+                var height = style.graphicHeight || style.graphicWidth;
+                width = width ? width : style.pointRadius*2;
+                height = height ? height : style.pointRadius*2;
+
+                var resolution = this.getResolution();
+                var xOffset = (style.graphicXOffset != undefined) ?
+                    style.graphicXOffset : -(0.5 * width);
+                var yOffset = (style.graphicYOffset != undefined) ?
+                    style.graphicYOffset : -(0.5 * height);
+                
+                node.style.left = ((geometry.x/resolution - this.offset.x)+xOffset).toFixed();
+                node.style.top = ((geometry.y/resolution - this.offset.y)-(yOffset+height)).toFixed();
+                node.style.width = width + "px";
+                node.style.height = height + "px";
+                node.style.flip = "y";
+                
+                // modify style/options for fill and stroke styling below
+                style.fillColor = "none";
+                options.isStroked = false;
+            } else if (this.isComplexSymbol(style.graphicName)) {
+                var cache = this.importSymbol(style.graphicName);
+                var symbolExtent = cache.extent;
+                var width = symbolExtent.getWidth();
+                var height = symbolExtent.getHeight();
+                node.setAttribute("path", cache.path);
+                node.setAttribute("coordorigin", symbolExtent.left + "," +
+                                                                symbolExtent.bottom);
+                node.setAttribute("coordsize", width + "," + height);
+                node.style.left = symbolExtent.left + "px";
+                node.style.top = symbolExtent.bottom + "px";
+                node.style.width = width + "px";
+                node.style.height = height + "px";
+        
+                this.drawCircle(node, geometry, style.pointRadius);
+                node.style.flip = "y";
+            } else {
+                this.drawCircle(node, geometry, style.pointRadius);
+            }
+        }
+
+        // fill 
+        if (options.isFilled) { 
+            node.setAttribute("fillcolor", style.fillColor); 
+        } else { 
+            node.setAttribute("filled", "false"); 
+        }
+        var fills = node.getElementsByTagName("fill");
+        var fill = (fills.length == 0) ? null : fills[0];
+        if (!options.isFilled) {
+            if (fill) {
+                node.removeChild(fill);
+            }
+        } else {
+            if (!fill) {
+                fill = this.createNode('olv:fill', node.id + "_fill");
+            }
+            fill.setAttribute("opacity", style.fillOpacity);
+
+            if (node._geometryClass == "OpenLayers.Geometry.Point" &&
+                    style.externalGraphic) {
+
+                // override fillOpacity
+                if (style.graphicOpacity) {
+                    fill.setAttribute("opacity", style.graphicOpacity);
+                }
+                
+                fill.setAttribute("src", style.externalGraphic);
+                fill.setAttribute("type", "frame");
+                
+                if (!(style.graphicWidth && style.graphicHeight)) {
+                  fill.aspect = "atmost";
+                }                
+            }
+            if (fill.parentNode != node) {
+                node.appendChild(fill);
+            }
+        }
+
+        // additional rendering for rotated graphics or symbols
+        if (typeof style.rotation != "undefined") {
+            if (style.externalGraphic) {
+                this.graphicRotate(node, xOffset, yOffset);
+                // make the fill fully transparent, because we now have
+                // the graphic as imagedata element. We cannot just remove
+                // the fill, because this is part of the hack described
+                // in graphicRotate
+                fill.setAttribute("opacity", 0);
+            } else {
+                node.style.rotation = style.rotation;
+            }
+        }
+
+        // stroke 
+        if (options.isStroked) { 
+            node.setAttribute("strokecolor", style.strokeColor); 
+            node.setAttribute("strokeweight", style.strokeWidth + "px"); 
+        } else { 
+            node.setAttribute("stroked", "false"); 
+        }
+        var strokes = node.getElementsByTagName("stroke");
+        var stroke = (strokes.length == 0) ? null : strokes[0];
+        if (!options.isStroked) {
+            if (stroke) {
+                node.removeChild(stroke);
+            }
+        } else {
+            if (!stroke) {
+                stroke = this.createNode('olv:stroke', node.id + "_stroke");
+                node.appendChild(stroke);
+            }
+            stroke.setAttribute("opacity", style.strokeOpacity);
+            stroke.setAttribute("endcap", !style.strokeLinecap || style.strokeLinecap == 'butt' ? 'flat' : style.strokeLinecap);
+            stroke.setAttribute("dashstyle", this.dashStyle(style));
+        }
+        
+        if (style.cursor != "inherit" && style.cursor != null) {
+            node.style.cursor = style.cursor;
+        }
+        return node;
+    },
+
+    /**
+     * Method: graphicRotate
+     * If a point is to be styled with externalGraphic and rotation, VML fills
+     * cannot be used to display the graphic, because rotation of graphic
+     * fills is not supported by the VML implementation of Internet Explorer.
+     * This method creates a olv:imagedata element inside the VML node,
+     * DXImageTransform.Matrix and BasicImage filters for rotation and
+     * opacity, and a 3-step hack to remove rendering artefacts from the
+     * graphic and preserve the ability of graphics to trigger events.
+     * Finally, OpenLayers methods are used to determine the correct
+     * insertion point of the rotated image, because DXImageTransform.Matrix
+     * does the rotation without the ability to specify a rotation center
+     * point.
+     * 
+     * Parameters:
+     * node    - {DOMElement}
+     * xOffset - {Number} rotation center relative to image, x coordinate
+     * yOffset - {Number} rotation center relative to image, y coordinate
+     */
+    graphicRotate: function(node, xOffset, yOffset) {
+        var style = style || node._style;
+        var options = node._options;
+        
+        var aspectRatio, size;
+        if (!(style.graphicWidth && style.graphicHeight)) {
+            // load the image to determine its size
+            var img = new Image();
+            img.onreadystatechange = OpenLayers.Function.bind(function() {
+                if(img.readyState == "complete" ||
+                        img.readyState == "interactive") {
+                    aspectRatio = img.width / img.height;
+                    size = Math.max(style.pointRadius * 2, 
+                        style.graphicWidth || 0,
+                        style.graphicHeight || 0);
+                    xOffset = xOffset * aspectRatio;
+                    style.graphicWidth = size * aspectRatio;
+                    style.graphicHeight = size;
+                    this.graphicRotate(node, xOffset, yOffset);
+                }
+            }, this);
+            img.src = style.externalGraphic;
+            
+            // will be called again by the onreadystate handler
+            return;
+        } else {
+            size = Math.max(style.graphicWidth, style.graphicHeight);
+            aspectRatio = style.graphicWidth / style.graphicHeight;
+        }
+        
+        var width = Math.round(style.graphicWidth || size * aspectRatio);
+        var height = Math.round(style.graphicHeight || size);
+        node.style.width = width + "px";
+        node.style.height = height + "px";
+        
+        // Three steps are required to remove artefacts for images with
+        // transparent backgrounds (resulting from using DXImageTransform
+        // filters on svg objects), while preserving awareness for browser
+        // events on images:
+        // - Use the fill as usual (like for unrotated images) to handle
+        //   events
+        // - specify an imagedata element with the same src as the fill
+        // - style the imagedata element with an AlphaImageLoader filter
+        //   with empty src
+        var image = document.getElementById(node.id + "_image");
+        if (!image) {
+            image = this.createNode("olv:imagedata", node.id + "_image");
+            node.appendChild(image);
+        }
+        image.style.width = width + "px";
+        image.style.height = height + "px";
+        image.src = style.externalGraphic;
+        image.style.filter =
+            "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + 
+            "src='', sizingMethod='scale')";
+
+        var rotation = style.rotation * Math.PI / 180;
+        var sintheta = Math.sin(rotation);
+        var costheta = Math.cos(rotation);
+
+        // do the rotation on the image
+        var filter =
+            "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta +
+            ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta +
+            ",SizingMethod='auto expand')\n";
+
+        // set the opacity (needed for the imagedata)
+        var opacity = style.graphicOpacity || style.fillOpacity;
+        if (opacity && opacity != 1) {
+            filter += 
+                "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + 
+                opacity+")\n";
+        }
+        node.style.filter = filter;
+
+        // do the rotation again on a box, so we know the insertion point
+        var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);
+        var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();
+        imgBox.rotate(style.rotation, centerPoint);
+        var imgBounds = imgBox.getBounds();
+
+        node.style.left = Math.round(
+            parseInt(node.style.left) + imgBounds.left) + "px";
+        node.style.top = Math.round(
+            parseInt(node.style.top) - imgBounds.bottom) + "px";
+    },
+
+    /**
+     * Method: postDraw
+     * Some versions of Internet Explorer seem to be unable to set fillcolor
+     * and strokecolor to "none" correctly before the fill node is appended to
+     * a visible vml node. This method takes care of that and sets fillcolor
+     * and strokecolor again if needed.
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     */
+    postDraw: function(node) {
+        var fillColor = node._style.fillColor;
+        var strokeColor = node._style.strokeColor;
+        if (fillColor == "none" &&
+                node.getAttribute("fillcolor") != fillColor) {
+            node.setAttribute("fillcolor", fillColor);
+        }
+        if (strokeColor == "none" &&
+                node.getAttribute("strokecolor") != strokeColor) {
+            node.setAttribute("strokecolor", strokeColor);
+        }
+    },
+
+
+    /**
+     * Method: setNodeDimension
+     * Get the geometry's bounds, convert it to our vml coordinate system, 
+     * then set the node's position, size, and local coordinate system.
+     *   
+     * Parameters:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    setNodeDimension: function(node, geometry) {
+
+        var bbox = geometry.getBounds();
+        if(bbox) {
+            var resolution = this.getResolution();
+        
+            var scaledBox = 
+                new OpenLayers.Bounds((bbox.left/resolution - this.offset.x).toFixed(),
+                                      (bbox.bottom/resolution - this.offset.y).toFixed(),
+                                      (bbox.right/resolution - this.offset.x).toFixed(),
+                                      (bbox.top/resolution - this.offset.y).toFixed());
+            
+            // Set the internal coordinate system to draw the path
+            node.style.left = scaledBox.left + "px";
+            node.style.top = scaledBox.top + "px";
+            node.style.width = scaledBox.getWidth() + "px";
+            node.style.height = scaledBox.getHeight() + "px";
+    
+            node.coordorigin = scaledBox.left + " " + scaledBox.top;
+            node.coordsize = scaledBox.getWidth()+ " " + scaledBox.getHeight();
+        }
+    },
+    
+    /** 
+     * Method: dashStyle
+     * 
+     * Parameters:
+     * style - {Object}
+     * 
+     * Returns:
+     * {String} A VML compliant 'stroke-dasharray' value
+     */
+    dashStyle: function(style) {
+        var dash = style.strokeDashstyle;
+        switch (dash) {
+            case 'solid':
+            case 'dot':
+            case 'dash':
+            case 'dashdot':
+            case 'longdash':
+            case 'longdashdot':
+                return dash;
+            default:
+                // very basic guessing of dash style patterns
+                var parts = dash.split(/[ ,]/);
+                if (parts.length == 2) {
+                    if (1*parts[0] >= 2*parts[1]) {
+                        return "longdash";
+                    }
+                    return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash";
+                } else if (parts.length == 4) {
+                    return (1*parts[0] >= 2*parts[1]) ? "longdashdot" :
+                        "dashdot";
+                }
+                return "solid";
+        }
+    },
+
+    /**
+     * Method: createNode
+     * Create a new node
+     *
+     * Parameters:
+     * type - {String} Kind of node to draw
+     * id - {String} Id for node
+     *
+     * Returns:
+     * {DOMElement} A new node of the given type and id
+     */
+    createNode: function(type, id) {
+        var node = document.createElement(type);
+        if (id) {
+            node.setAttribute('id', id);
+        }
+        
+        // IE hack to make elements unselectable, to prevent 'blue flash'
+        // while dragging vectors; #1410
+        node.setAttribute('unselectable', 'on', 0);
+        node.onselectstart = function() { return(false); };
+        
+        return node;    
+    },
+    
+    /**
+     * Method: nodeTypeCompare
+     * Determine whether a node is of a given type
+     *
+     * Parameters:
+     * node - {DOMElement} An VML element
+     * type - {String} Kind of node
+     *
+     * Returns:
+     * {Boolean} Whether or not the specified node is of the specified type
+     */
+    nodeTypeCompare: function(node, type) {
+
+        //split type
+        var subType = type;
+        var splitIndex = subType.indexOf(":");
+        if (splitIndex != -1) {
+            subType = subType.substr(splitIndex+1);
+        }
+
+        //split nodeName
+        var nodeName = node.nodeName;
+        splitIndex = nodeName.indexOf(":");
+        if (splitIndex != -1) {
+            nodeName = nodeName.substr(splitIndex+1);
+        }
+
+        return (subType == nodeName);
+    },
+
+    /**
+     * Method: createRenderRoot
+     * Create the renderer root
+     *
+     * Returns:
+     * {DOMElement} The specific render engine's root element
+     */
+    createRenderRoot: function() {
+        return this.nodeFactory(this.container.id + "_vmlRoot", "div");
+    },
+
+    /**
+     * Method: createRoot
+     * Create the main root element
+     *
+     * Returns:
+     * {DOMElement} The main root element to which we'll add vectors
+     */
+    createRoot: function() {
+        return this.nodeFactory(this.container.id + "_root", "olv:group");
+    },
+    
+    /**************************************
+     *                                    *
+     *     GEOMETRY DRAWING FUNCTIONS     *
+     *                                    *
+     **************************************/
+    
+    /**
+     * Method: drawPoint
+     * Render a point
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the point could not be drawn
+     */
+    drawPoint: function(node, geometry) {
+        return this.drawCircle(node, geometry, 1);
+    },
+
+    /**
+     * Method: drawCircle
+     * Render a circle.
+     * Size and Center a circle given geometry (x,y center) and radius
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * radius - {float}
+     * 
+     * Returns:
+     * {DOMElement} or false if the circle could not ne drawn
+     */
+    drawCircle: function(node, geometry, radius) {
+        if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
+            var resolution = this.getResolution();
+
+            node.style.left = ((geometry.x /resolution - this.offset.x).toFixed() - radius) + "px";
+            node.style.top = ((geometry.y /resolution - this.offset.y).toFixed() - radius) + "px";
+    
+            var diameter = radius * 2;
+            
+            node.style.width = diameter + "px";
+            node.style.height = diameter + "px";
+            return node;
+        }
+        return false;
+    },
+
+
+    /**
+     * Method: drawLineString
+     * Render a linestring.
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement}
+     */
+    drawLineString: function(node, geometry) {
+        return this.drawLine(node, geometry, false);
+    },
+
+    /**
+     * Method: drawLinearRing
+     * Render a linearring
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement}
+     */
+    drawLinearRing: function(node, geometry) {
+        return this.drawLine(node, geometry, true);
+    },
+
+    /**
+     * Method: DrawLine
+     * Render a line.
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * closeLine - {Boolean} Close the line? (make it a ring?)
+     * 
+     * Returns:
+     * {DOMElement}
+     */
+    drawLine: function(node, geometry, closeLine) {
+
+        this.setNodeDimension(node, geometry);
+
+        var resolution = this.getResolution();
+        var numComponents = geometry.components.length;
+        var parts = new Array(numComponents);
+
+        var comp, x, y;
+        for (var i = 0; i < numComponents; i++) {
+            comp = geometry.components[i];
+            x = (comp.x/resolution - this.offset.x);
+            y = (comp.y/resolution - this.offset.y);
+            parts[i] = " " + x.toFixed() + "," + y.toFixed() + " l ";
+        }
+        var end = (closeLine) ? " x e" : " e";
+        node.path = "m" + parts.join("") + end;
+        return node;
+    },
+
+    /**
+     * Method: drawPolygon
+     * Render a polygon
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement}
+     */
+    drawPolygon: function(node, geometry) {
+        this.setNodeDimension(node, geometry);
+
+        var resolution = this.getResolution();
+    
+        var path = [];
+        var linearRing, i, j, len, ilen, comp, x, y;
+        for (j = 0, len=geometry.components.length; j<len; j++) {
+            linearRing = geometry.components[j];
+
+            path.push("m");
+            for (i=0, ilen=linearRing.components.length; i<ilen; i++) {
+                comp = linearRing.components[i];
+                x = comp.x / resolution - this.offset.x;
+                y = comp.y / resolution - this.offset.y;
+                path.push(" " + x.toFixed() + "," + y.toFixed());
+                if (i==0) {
+                    path.push(" l");
+                }
+            }
+            path.push(" x ");
+        }
+        path.push("e");
+        node.path = path.join("");
+        return node;
+    },
+
+    /**
+     * Method: drawRectangle
+     * Render a rectangle
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement}
+     */
+    drawRectangle: function(node, geometry) {
+        var resolution = this.getResolution();
+    
+        node.style.left = (geometry.x/resolution - this.offset.x) + "px";
+        node.style.top = (geometry.y/resolution - this.offset.y) + "px";
+        node.style.width = geometry.width/resolution + "px";
+        node.style.height = geometry.height/resolution + "px";
+        
+        return node;
+    },
+
+    /**
+     * Method: drawSurface
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement}
+     */
+    drawSurface: function(node, geometry) {
+
+        this.setNodeDimension(node, geometry);
+
+        var resolution = this.getResolution();
+    
+        var path = [];
+        var comp, x, y;
+        for (var i=0, len=geometry.components.length; i<len; i++) {
+            comp = geometry.components[i];
+            x = comp.x / resolution - this.offset.x;
+            y = comp.y / resolution - this.offset.y;
+            if ((i%3)==0 && (i/3)==0) {
+                path.push("m");
+            } else if ((i%3)==1) {
+                path.push(" c");
+            }
+            path.push(" " + x + "," + y);
+        }
+        path.push(" x e");
+
+        node.path = path.join("");
+        return node;
+    },
+    
+    /**
+     * Method: importSymbol
+     * add a new symbol definition from the rendererer's symbol hash
+     * 
+     * Parameters:
+     * graphicName - {String} name of the symbol to import
+     * 
+     * Returns:
+     * {Object} - hash of {DOMElement} "symbol" and {Number} "size"
+     */      
+    importSymbol: function (graphicName)  {
+        var id = this.container.id + "-" + graphicName;
+        
+        // check if symbol already exists in the cache
+        var cache = this.symbolCache[id];
+        if (cache) {
+            return cache;
+        }
+        
+        var symbol = OpenLayers.Renderer.symbol[graphicName];
+        if (!symbol) {
+            throw new Error(graphicName + ' is not a valid symbol name');
+            return;
+        }
+
+        var symbolExtent = new OpenLayers.Bounds(
+                                    Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
+        
+        var pathitems = ["m"];
+        for (var i=0; i<symbol.length; i=i+2) {
+            x = symbol[i];
+            y = symbol[i+1];
+            symbolExtent.left = Math.min(symbolExtent.left, x);
+            symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
+            symbolExtent.right = Math.max(symbolExtent.right, x);
+            symbolExtent.top = Math.max(symbolExtent.top, y);
+
+            pathitems.push(x);
+            pathitems.push(y);
+            if (i == 0) {
+                pathitems.push("l");
+            }
+        }
+        pathitems.push("x e");
+        var path = pathitems.join(" ");
+        
+        cache = {
+            path: path,
+            extent: symbolExtent
+        };
+        this.symbolCache[id] = cache;
+        
+        return cache;
+    },
+    
+    CLASS_NAME: "OpenLayers.Renderer.VML"
+});
+/* ======================================================================
     OpenLayers/Tile/Image.js
    ====================================================================== */
 
@@ -12702,10 +15840,14 @@
     destroy: function() {
         if (this.imgDiv != null)  {
             if (this.layerAlphaHack) {
+                // unregister the "load" handler
                 OpenLayers.Event.stopObservingElement(this.imgDiv.childNodes[0].id);                
-            } else {
-                OpenLayers.Event.stopObservingElement(this.imgDiv.id);
             }
+
+            // unregister the "load" and "error" handlers. Only the "error" handler if
+            // this.layerAlphaHack is true.
+            OpenLayers.Event.stopObservingElement(this.imgDiv.id);
+            
             if (this.imgDiv.parentNode == this.frame) {
                 this.frame.removeChild(this.imgDiv);
                 this.imgDiv.map = null;
@@ -14205,7 +17347,7 @@
         if (this.checkModifiers(evt) && 
             this.control.handleRightClicks && 
             OpenLayers.Event.isRightClick(evt)) {
-          propogate = this.rightclick(evt);
+          propagate = this.rightclick(evt);
         }
 
         return propagate;
@@ -14723,6 +17865,569 @@
     CLASS_NAME: "OpenLayers.Handler.Drag"
 });
 /* ======================================================================
+    OpenLayers/Handler/Feature.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Handler.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Feature 
+ * Handler to respond to mouse events related to a drawn feature.  Callbacks
+ *     with the following keys will be notified of the following events
+ *     associated with features: click, clickout, over, out, and dblclick.
+ *
+ * This handler stops event propagation for mousedown and mouseup if those
+ *     browser events target features that can be selected.
+ */
+OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
+
+    /**
+     * Property: EVENTMAP
+     * {Object} A object mapping the browser events to objects with callback
+     *     keys for in and out.
+     */
+    EVENTMAP: {
+        'click': {'in': 'click', 'out': 'clickout'},
+        'mousemove': {'in': 'over', 'out': 'out'},
+        'dblclick': {'in': 'dblclick', 'out': null},
+        'mousedown': {'in': null, 'out': null},
+        'mouseup': {'in': null, 'out': null}
+    },
+
+    /**
+     * Property: feature
+     * {<OpenLayers.Feature.Vector>} The last feature that was hovered.
+     */
+    feature: null,
+
+    /**
+     * Property: lastFeature
+     * {<OpenLayers.Feature.Vector>} The last feature that was handled.
+     */
+    lastFeature: null,
+
+    /**
+     * Property: down
+     * {<OpenLayers.Pixel>} The location of the last mousedown.
+     */
+    down: null,
+
+    /**
+     * Property: up
+     * {<OpenLayers.Pixel>} The location of the last mouseup.
+     */
+    up: null,
+    
+    /**
+     * Property: clickoutTolerance
+     * {Number} The number of pixels the mouse can move during a click that
+     *     still constitutes a click out.  When dragging the map, clicks should
+     *     not trigger the clickout property unless this tolerance is reached.
+     *     Default is 4.
+     */
+    clickoutTolerance: 4,
+
+    /**
+     * Property: geometryTypes
+     * To restrict dragging to a limited set of geometry types, send a list
+     * of strings corresponding to the geometry class names.
+     * 
+     * @type Array(String)
+     */
+    geometryTypes: null,
+
+    /**
+     * Property: stopClick
+     * {Boolean} If stopClick is set to true, handled clicks do not
+     *      propagate to other click listeners. Otherwise, handled clicks
+     *      do propagate. Unhandled clicks always propagate, whatever the
+     *      value of stopClick. Defaults to true.
+     */
+    stopClick: true,
+
+    /**
+     * Property: stopDown
+     * {Boolean} If stopDown is set to true, handled mousedowns do not
+     *      propagate to other mousedown listeners. Otherwise, handled
+     *      mousedowns do propagate. Unhandled mousedowns always propagate,
+     *      whatever the value of stopDown. Defaults to true.
+     */
+    stopDown: true,
+
+    /**
+     * Property: stopUp
+     * {Boolean} If stopUp is set to true, handled mouseups do not
+     *      propagate to other mouseup listeners. Otherwise, handled mouseups
+     *      do propagate. Unhandled mouseups always propagate, whatever the
+     *      value of stopUp. Defaults to false.
+     */
+    stopUp: false,
+    
+    /**
+     * Constructor: OpenLayers.Handler.Feature
+     *
+     * Parameters:
+     * control - {<OpenLayers.Control>} 
+     * layers - {Array(<OpenLayers.Layer.Vector>)}
+     * callbacks - {Object} An object with a 'over' property whos value is
+     *     a function to be called when the mouse is over a feature. The 
+     *     callback should expect to recieve a single argument, the feature.
+     * options - {Object} 
+     */
+    initialize: function(control, layer, callbacks, options) {
+        OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
+        this.layer = layer;
+    },
+
+
+    /**
+     * Method: mousedown
+     * Handle mouse down.  Stop propagation if a feature is targeted by this
+     *     event (stops map dragging during feature selection).
+     * 
+     * Parameters:
+     * evt - {Event} 
+     */
+    mousedown: function(evt) {
+        this.down = evt.xy;
+        return this.handle(evt) ? !this.stopDown : true;
+    },
+    
+    /**
+     * Method: mouseup
+     * Handle mouse up.  Stop propagation if a feature is targeted by this
+     *     event.
+     * 
+     * Parameters:
+     * evt - {Event} 
+     */
+    mouseup: function(evt) {
+        this.up = evt.xy;
+        return this.handle(evt) ? !this.stopUp : true;
+    },
+
+    /**
+     * Method: click
+     * Handle click.  Call the "click" callback if click on a feature,
+     *     or the "clickout" callback if click outside any feature.
+     * 
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean}
+     */
+    click: function(evt) {
+        return this.handle(evt) ? !this.stopClick : true;
+    },
+        
+    /**
+     * Method: mousemove
+     * Handle mouse moves.  Call the "over" callback if moving in to a feature,
+     *     or the "out" callback if moving out of a feature.
+     * 
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean}
+     */
+    mousemove: function(evt) {
+        if (!this.callbacks['over'] && !this.callbacks['out']) {
+            return true;
+        }     
+        this.handle(evt);
+        return true;
+    },
+    
+    /**
+     * Method: dblclick
+     * Handle dblclick.  Call the "dblclick" callback if dblclick on a feature.
+     *
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean}
+     */
+    dblclick: function(evt) {
+        return !this.handle(evt);
+    },
+
+    /**
+     * Method: geometryTypeMatches
+     * Return true if the geometry type of the passed feature matches
+     *     one of the geometry types in the geometryTypes array.
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Vector.Feature>}
+     *
+     * Returns:
+     * {Boolean}
+     */
+    geometryTypeMatches: function(feature) {
+        return this.geometryTypes == null ||
+            OpenLayers.Util.indexOf(this.geometryTypes,
+                                    feature.geometry.CLASS_NAME) > -1;
+    },
+
+    /**
+     * Method: handle
+     *
+     * Parameters:
+     * evt - {Event}
+     *
+     * Returns:
+     * {Boolean} The event occurred over a relevant feature.
+     */
+    handle: function(evt) {
+        var type = evt.type;
+        var handled = false;
+        var previouslyIn = !!(this.feature); // previously in a feature
+        var click = (type == "click" || type == "dblclick");
+        this.feature = this.layer.getFeatureFromEvent(evt);
+        if(this.feature && this.feature.layer) {
+            var inNew = (this.feature != this.lastFeature);
+            if(this.geometryTypeMatches(this.feature)) {
+                // in to a feature
+                if(previouslyIn && inNew) {
+                    // out of last feature and in to another
+                    this.triggerCallback(type, 'out', [this.lastFeature]);
+                    this.triggerCallback(type, 'in', [this.feature]);
+                } else if(!previouslyIn || click) {
+                    // in feature for the first time
+                    this.triggerCallback(type, 'in', [this.feature]);
+                }
+                this.lastFeature = this.feature;
+                handled = true;
+            } else {
+                // not in to a feature
+                if(previouslyIn && inNew || (click && this.lastFeature)) {
+                    // out of last feature for the first time
+                    this.triggerCallback(type, 'out', [this.lastFeature]);
+                }
+                // next time the mouse goes in a feature whose geometry type
+                // doesn't match we don't want to call the 'out' callback
+                // again, so let's set this.feature to null so that
+                // previouslyIn will evaluate to false the next time
+                // we enter handle. Yes, a bit hackish...
+                this.feature = null;
+            }
+        } else {
+            if(this.lastFeature && this.lastFeature.layer &&
+               (previouslyIn || click)) {
+                this.triggerCallback(type, 'out', [this.lastFeature]);
+            }
+        }
+        return handled;
+    },
+    
+    /**
+     * Method: triggerCallback
+     * Call the callback keyed in the event map with the supplied arguments.
+     *     For click out, the <clickoutTolerance> is checked first.
+     *
+     * Parameters:
+     * type - {String}
+     */
+    triggerCallback: function(type, mode, args) {
+        var key = this.EVENTMAP[type][mode];
+        if(key) {
+            if(type == 'click' && mode == 'out' && this.up && this.down) {
+                // for clickout, only trigger callback if tolerance is met
+                var dpx = Math.sqrt(
+                    Math.pow(this.up.x - this.down.x, 2) +
+                    Math.pow(this.up.y - this.down.y, 2)
+                );
+                if(dpx <= this.clickoutTolerance) {
+                    this.callback(key, args);
+                }
+            } else {
+                this.callback(key, args);
+            }
+        }
+    },
+
+    /**
+     * Method: activate 
+     * Turn on the handler.  Returns false if the handler was already active.
+     *
+     * Returns:
+     * {Boolean}
+     */
+    activate: function() {
+        var activated = false;
+        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+            this.moveLayerToTop();
+            this.map.events.on({
+                "removelayer": this.handleMapEvents,
+                "changelayer": this.handleMapEvents,
+                scope: this
+            });
+            activated = true;
+        }
+        return activated;
+    },
+    
+    /**
+     * Method: deactivate 
+     * Turn off the handler.  Returns false if the handler was already active.
+     *
+     * Returns: 
+     * {Boolean}
+     */
+    deactivate: function() {
+        var deactivated = false;
+        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+            this.moveLayerBack();
+            this.feature = null;
+            this.lastFeature = null;
+            this.down = null;
+            this.up = null;
+            this.map.events.un({
+                "removelayer": this.handleMapEvents,
+                "changelayer": this.handleMapEvents,
+                scope: this
+            });
+            deactivated = true;
+        }
+        return deactivated;
+    },
+    
+    /**
+     * Method handleMapEvents
+     * 
+     * Parameters:
+     * evt - {Object}
+     */
+    handleMapEvents: function(evt) {
+        if (!evt.property || evt.property == "order") {
+            this.moveLayerToTop();
+        }
+    },
+    
+    /**
+     * Method: moveLayerToTop
+     * Moves the layer for this handler to the top, so mouse events can reach
+     * it.
+     */
+    moveLayerToTop: function() {
+        var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
+            this.layer.getZIndex()) + 1;
+        this.layer.setZIndex(index);
+        
+    },
+    
+    /**
+     * Method: moveLayerBack
+     * Moves the layer back to the position determined by the map's layers
+     * array.
+     */
+    moveLayerBack: function() {
+        var index = this.layer.getZIndex() - 1;
+        if (index >= this.map.Z_INDEX_BASE['Feature']) {
+            this.layer.setZIndex(index);
+        } else {
+            this.map.setLayerZIndex(this.layer,
+                this.map.getLayerIndex(this.layer));
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.Feature"
+});
+/* ======================================================================
+    OpenLayers/Handler/Hover.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the clear BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt 
+ * for the full text of the license. */
+
+/**
+ * @requires OpenLayers/Handler.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Hover
+ * The hover handler is to be used to emulate mouseovers on objects
+ *      on the map that aren't DOM elements. For example one can use
+ *      this handler to send WMS/GetFeatureInfo requests as the user
+ *      moves the mouve over the map.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Handler> 
+ */
+OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, {
+
+    /**
+     * APIProperty: delay
+     * {Integer} - Number of milliseconds between mousemoves before
+     *      the event is considered a hover. Default is 500.
+     */
+    delay: 500,
+    
+    /**
+     * APIProperty: pixelTolerance
+     * {Integer} - Maximum number of pixels between mousemoves for
+     *      an event to be considered a hover. Default is null.
+     */
+    pixelTolerance: null,
+
+    /**
+     * APIProperty: stopMove
+     * {Boolean} Stop other listeners from being notified on mousemoves.
+     *      Default is false.
+     */
+    stopMove: false,
+
+    /**
+     * Property: px
+     * {<OpenLayers.Pixel>} The location of the last mousemove, expressed
+     *      in pixels.
+     */
+    px: null,
+
+    /**
+     * Property: timerId
+     * {Number} The id of the timer.
+     */
+    timerId: null,
+ 
+    /**
+     * Constructor: OpenLayers.Handler.Hover
+     * Construct a hover handler.
+     *
+     * Parameters:
+     * control - {<OpenLayers.Control>} The control that initialized this
+     *     handler.  The control is assumed to have a valid map property; that
+     *     map is used in the handler's own setMap method.
+     * callbacks - {Object} An object whose properties correspond to abstracted
+     *     events or sequences of browser events.  The values for these
+     *     properties are functions defined by the control that get called by
+     *     the handler.
+     * options - {Object} An optional object whose properties will be set on
+     *     the handler.
+     */
+    initialize: function(control, callbacks, options) {
+        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * Method: mousemove
+     * Called when the mouse moves on the map.
+     *
+     * Parameters:
+     * evt - {<OpenLayers.Event>}
+     *
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    mousemove: function(evt) {
+        if(this.passesTolerance(evt.xy)) {
+            this.clearTimer();
+            this.callback('move', [evt]);
+            this.px = evt.xy;
+            // clone the evt so original properties can be accessed even
+            // if the browser deletes them during the delay
+            evt = OpenLayers.Util.extend({}, evt);
+            this.timerId = window.setTimeout(
+                OpenLayers.Function.bind(this.delayedCall, this, evt),
+                this.delay
+            );
+        }
+        return !this.stopMove;
+    },
+
+    /**
+     * Method: mouseout
+     * Called when the mouse goes out of the map.
+     *
+     * Parameters:
+     * evt - {<OpenLayers.Event>}
+     *
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    mouseout: function(evt) {
+        if (OpenLayers.Util.mouseLeft(evt, this.map.div)) {
+            this.clearTimer();
+            this.callback('move', [evt]);
+        }
+        return true;
+    },
+
+    /**
+     * Method: passesTolerance
+     * Determine whether the mouse move is within the optional pixel tolerance.
+     *
+     * Parameters:
+     * px - {<OpenLayers.Pixel>}
+     *
+     * Returns:
+     * {Boolean} The mouse move is within the pixel tolerance.
+     */
+    passesTolerance: function(px) {
+        var passes = true;
+        if(this.pixelTolerance && this.px) {
+            var dpx = Math.sqrt(
+                Math.pow(this.px.x - px.x, 2) +
+                Math.pow(this.px.y - px.y, 2)
+            );
+            if(dpx < this.pixelTolerance) {
+                passes = false;
+            }
+        }
+        return passes;
+    },
+
+    /**
+     * Method: clearTimer
+     * Clear the timer and set <timerId> to null.
+     */
+    clearTimer: function() {
+        if(this.timerId != null) {
+            window.clearTimeout(this.timerId);
+            this.timerId = null;
+        }
+    },
+
+    /**
+     * Method: delayedCall
+     * Triggers pause callback.
+     *
+     * Parameters:
+     * evt - {<OpenLayers.Event>}
+     */
+    delayedCall: function(evt) {
+        this.callback('pause', [evt]);
+    },
+
+    /**
+     * APIMethod: deactivate
+     * Deactivate the handler.
+     *
+     * Returns:
+     * {Boolean} The handler was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = false;
+        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+            this.clearTimer();
+            deactivated = true;
+        }
+        return deactivated;
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.Hover"
+});
+/* ======================================================================
     OpenLayers/Handler/MouseWheel.js
    ====================================================================== */
 
@@ -15048,12 +18753,14 @@
      *  - *loadend* Triggered when layer loading ends.
      *  - *loadcancel* Triggered when layer loading is canceled.
      *  - *visibilitychanged* Triggered when layer visibility is changed.
-     *  - *moveend* Triggered when layer is moved, object passed as
+     *  - *move* Triggered when layer moves (triggered with every mousemove
+     *      during a drag).
+     *  - *moveend* Triggered when layer is done moving, object passed as
      *      argument has a zoomChanged boolean property which tells that the
      *      zoom has changed.
      */
     EVENT_TYPES: ["loadstart", "loadend", "loadcancel", "visibilitychanged",
-                  "moveend"],
+                  "move", "moveend"],
         
     /**
      * APIProperty: events
@@ -16937,6 +20644,410 @@
     CLASS_NAME: "OpenLayers.Handler.Box"
 });
 /* ======================================================================
+    OpenLayers/Handler/RegularPolygon.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Handler/Drag.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.RegularPolygon
+ * Handler to draw a regular polygon on the map.  Polygon is displayed on mouse
+ *     down, moves or is modified on mouse move, and is finished on mouse up.
+ *     The handler triggers callbacks for 'done' and 'cancel'.  Create a new
+ *     instance with the <OpenLayers.Handler.RegularPolygon> constructor.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.RegularPolygon = OpenLayers.Class(OpenLayers.Handler.Drag, {
+    
+    /**
+     * APIProperty: sides
+     * {Integer} Number of sides for the regular polygon.  Needs to be greater
+     *     than 2.  Defaults to 4.
+     */
+    sides: 4,
+
+    /**
+     * APIProperty: radius
+     * {Float} Optional radius in map units of the regular polygon.  If this is
+     *     set to some non-zero value, a polygon with a fixed radius will be
+     *     drawn and dragged with mose movements.  If this property is not
+     *     set, dragging changes the radius of the polygon.  Set to null by
+     *     default.
+     */
+    radius: null,
+    
+    /**
+     * APIProperty: snapAngle
+     * {Float} If set to a non-zero value, the handler will snap the polygon
+     *     rotation to multiples of the snapAngle.  Value is an angle measured
+     *     in degrees counterclockwise from the positive x-axis.  
+     */
+    snapAngle: null,
+    
+    /**
+     * APIProperty: snapToggle
+     * {String} If set, snapToggle is checked on mouse events and will set
+     *     the snap mode to the opposite of what it currently is.  To disallow
+     *     toggling between snap and non-snap mode, set freehandToggle to
+     *     null.  Acceptable toggle values are 'shiftKey', 'ctrlKey', and
+     *     'altKey'. Snap mode is only possible if this.snapAngle is set to a
+     *     non-zero value.
+     */
+    snapToggle: 'shiftKey',
+    
+    /**
+     * APIProperty: persist
+     * {Boolean} Leave the feature rendered until clear is called.  Default
+     *     is false.  If set to true, the feature remains rendered until
+     *     clear is called, typically by deactivating the handler or starting
+     *     another drawing.
+     */
+    persist: false,
+
+    /**
+     * APIProperty: irregular
+     * {Boolean} Draw an irregular polygon instead of a regular polygon.
+     *     Default is false.  If true, the initial mouse down will represent
+     *     one corner of the polygon bounds and with each mouse movement, the
+     *     polygon will be stretched so the opposite corner of its bounds
+     *     follows the mouse position.  This property takes precedence over
+     *     the radius property.  If set to true, the radius property will
+     *     be ignored.
+     */
+    irregular: false,
+
+    /**
+     * Property: angle
+     * {Float} The angle from the origin (mouse down) to the current mouse
+     *     position, in radians.  This is measured counterclockwise from the
+     *     positive x-axis.
+     */
+    angle: null,
+
+    /**
+     * Property: fixedRadius
+     * {Boolean} The polygon has a fixed radius.  True if a radius is set before
+     *     drawing begins.  False otherwise.
+     */
+    fixedRadius: false,
+
+    /**
+     * Property: feature
+     * {<OpenLayers.Feature.Vector>} The currently drawn polygon feature
+     */
+    feature: null,
+
+    /**
+     * Property: layer
+     * {<OpenLayers.Layer.Vector>} The temporary drawing layer
+     */
+    layer: null,
+
+    /**
+     * Property: origin
+     * {<OpenLayers.Geometry.Point>} Location of the first mouse down
+     */
+    origin: null,
+
+    /**
+     * Constructor: OpenLayers.Handler.RegularPolygon
+     * Create a new regular polygon handler.
+     *
+     * Parameters:
+     * control - {<OpenLayers.Control>} The control that owns this handler
+     * callbacks - {Array} An object with a 'done' property whos value is a
+     *     function to be called when the polygon drawing is finished.
+     *     The callback should expect to recieve a single argument,
+     *     the polygon geometry.  If the callbacks object contains a
+     *     'cancel' property, this function will be called when the
+     *     handler is deactivated while drawing.  The cancel should
+     *     expect to receive a geometry.
+     * options - {Object} An object with properties to be set on the handler.
+     *     If the options.sides property is not specified, the number of sides
+     *     will default to 4.
+     */
+    initialize: function(control, callbacks, options) {
+        this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
+
+        OpenLayers.Handler.prototype.initialize.apply(this,
+                                                [control, callbacks, options]);
+        this.options = (options) ? options : new Object();
+    },
+    
+    /**
+     * APIMethod: setOptions
+     * 
+     * Parameters:
+     * newOptions - {Object} 
+     */
+    setOptions: function (newOptions) {
+        OpenLayers.Util.extend(this.options, newOptions);
+        OpenLayers.Util.extend(this, newOptions);
+    },
+    
+    /**
+     * APIMethod: activate
+     * Turn on the handler.
+     *
+     * Return:
+     * {Boolean} The handler was successfully activated
+     */
+    activate: function() {
+        var activated = false;
+        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+            // create temporary vector layer for rendering geometry sketch
+            var options = {
+                displayInLayerSwitcher: false,
+                // indicate that the temp vector layer will never be out of range
+                // without this, resolution properties must be specified at the
+                // map-level for this temporary layer to init its resolutions
+                // correctly
+                calculateInRange: function() { return true; }
+            };
+            this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);
+            this.map.addLayer(this.layer);
+            activated = true;
+        }
+        return activated;
+    },
+
+    /**
+     * APIMethod: deactivate
+     * Turn off the handler.
+     *
+     * Return:
+     * {Boolean} The handler was successfully deactivated
+     */
+    deactivate: function() {
+        var deactivated = false;
+        if(OpenLayers.Handler.Drag.prototype.deactivate.apply(this, arguments)) {
+            // call the cancel callback if mid-drawing
+            if(this.dragging) {
+                this.cancel();
+            }
+            // If a layer's map property is set to null, it means that that
+            // layer isn't added to the map. Since we ourself added the layer
+            // to the map in activate(), we can assume that if this.layer.map
+            // is null it means that the layer has been destroyed (as a result
+            // of map.destroy() for example.
+            if (this.layer.map != null) {
+                this.layer.destroy(false);
+                if (this.feature) {
+                    this.feature.destroy();
+                }
+            }
+            this.layer = null;
+            this.feature = null;
+            deactivated = true;
+        }
+        return deactivated;
+    },
+    
+    /**
+     * Method: down
+     * Start drawing a new feature
+     *
+     * Parameters:
+     * evt - {Event} The drag start event
+     */
+    down: function(evt) {
+        this.fixedRadius = !!(this.radius);
+        var maploc = this.map.getLonLatFromPixel(evt.xy);
+        this.origin = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);
+        // create the new polygon
+        if(!this.fixedRadius || this.irregular) {
+            // smallest radius should not be less one pixel in map units
+            // VML doesn't behave well with smaller
+            this.radius = this.map.getResolution();
+        }
+        if(this.persist) {
+            this.clear();
+        }
+        this.feature = new OpenLayers.Feature.Vector();
+        this.createGeometry();
+        this.layer.addFeatures([this.feature], {silent: true});
+        this.layer.drawFeature(this.feature, this.style);
+    },
+    
+    /**
+     * Method: move
+     * Respond to drag move events
+     *
+     * Parameters:
+     * evt - {Evt} The move event
+     */
+    move: function(evt) {
+        var maploc = this.map.getLonLatFromPixel(evt.xy);
+        var point = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);
+        if(this.irregular) {
+            var ry = Math.sqrt(2) * Math.abs(point.y - this.origin.y) / 2;
+            this.radius = Math.max(this.map.getResolution() / 2, ry);
+        } else if(this.fixedRadius) {
+            this.origin = point;
+        } else {
+            this.calculateAngle(point, evt);
+            this.radius = Math.max(this.map.getResolution() / 2,
+                                   point.distanceTo(this.origin));
+        }
+        this.modifyGeometry();
+        if(this.irregular) {
+            var dx = point.x - this.origin.x;
+            var dy = point.y - this.origin.y;
+            var ratio;
+            if(dy == 0) {
+                ratio = dx / (this.radius * Math.sqrt(2));
+            } else {
+                ratio = dx / dy;
+            }
+            this.feature.geometry.resize(1, this.origin, ratio);
+            this.feature.geometry.move(dx / 2, dy / 2);
+        }
+        this.layer.drawFeature(this.feature, this.style);
+    },
+
+    /**
+     * Method: up
+     * Finish drawing the feature
+     *
+     * Parameters:
+     * evt - {Event} The mouse up event
+     */
+    up: function(evt) {
+        this.finalize();
+    },
+
+    /**
+     * Method: out
+     * Finish drawing the feature.
+     *
+     * Parameters:
+     * evt - {Event} The mouse out event
+     */
+    out: function(evt) {
+        this.finalize();
+    },
+
+    /**
+     * Method: createGeometry
+     * Create the new polygon geometry.  This is called at the start of the
+     *     drag and at any point during the drag if the number of sides
+     *     changes.
+     */
+    createGeometry: function() {
+        this.angle = Math.PI * ((1/this.sides) - (1/2));
+        if(this.snapAngle) {
+            this.angle += this.snapAngle * (Math.PI / 180);
+        }
+        this.feature.geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(
+            this.origin, this.radius, this.sides, this.snapAngle
+        );
+    },
+    
+    /**
+     * Method: modifyGeometry
+     * Modify the polygon geometry in place.
+     */
+    modifyGeometry: function() {
+        var angle, dx, dy, point;
+        var ring = this.feature.geometry.components[0];
+        // if the number of sides ever changes, create a new geometry
+        if(ring.components.length != (this.sides + 1)) {
+            this.createGeometry();
+             ring = this.feature.geometry.components[0];
+        }
+        for(var i=0; i<this.sides; ++i) {
+            point = ring.components[i];
+            angle = this.angle + (i * 2 * Math.PI / this.sides);
+            point.x = this.origin.x + (this.radius * Math.cos(angle));
+            point.y = this.origin.y + (this.radius * Math.sin(angle));
+            point.clearBounds();
+        }
+    },
+    
+    /**
+     * Method: calculateAngle
+     * Calculate the angle based on settings.
+     *
+     * Parameters:
+     * point - {<OpenLayers.Geometry.Point>}
+     * evt - {Event}
+     */
+    calculateAngle: function(point, evt) {
+        var alpha = Math.atan2(point.y - this.origin.y,
+                               point.x - this.origin.x);
+        if(this.snapAngle && (this.snapToggle && !evt[this.snapToggle])) {
+            var snapAngleRad = (Math.PI / 180) * this.snapAngle;
+            this.angle = Math.round(alpha / snapAngleRad) * snapAngleRad;
+        } else {
+            this.angle = alpha;
+        }
+    },
+
+    /**
+     * APIMethod: cancel
+     * Finish the geometry and call the "cancel" callback.
+     */
+    cancel: function() {
+        // the polygon geometry gets cloned in the callback method
+        this.callback("cancel", null);
+        this.finalize();
+    },
+
+    /**
+     * Method: finalize
+     * Finish the geometry and call the "done" callback.
+     */
+    finalize: function() {
+        this.origin = null;
+        this.radius = this.options.radius;
+    },
+
+    /**
+     * APIMethod: clear
+     * Clear any rendered features on the temporary layer.  This is called
+     *     when the handler is deactivated, canceled, or done (unless persist
+     *     is true).
+     */
+    clear: function() {
+        this.layer.renderer.clear();
+        this.layer.destroyFeatures();
+    },
+    
+    /**
+     * Method: callback
+     * Trigger the control's named callback with the given arguments
+     *
+     * Parameters:
+     * name - {String} The key for the callback that is one of the properties
+     *     of the handler's callbacks object.
+     * args - {Array} An array of arguments with which to call the callback
+     *     (defined by the control).
+     */
+    callback: function (name, args) {
+        // override the callback method to always send the polygon geometry
+        if (this.callbacks[name]) {
+            this.callbacks[name].apply(this.control,
+                                       [this.feature.geometry.clone()]);
+        }
+        // since sketch features are added to the temporary layer
+        // they must be cleared here if done or cancel
+        if(!this.persist && (name == "done" || name == "cancel")) {
+            this.clear();
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.RegularPolygon"
+});
+/* ======================================================================
     OpenLayers/Layer/EventPane.js
    ====================================================================== */
 
@@ -17672,7 +21783,7 @@
         return zoom;
     },
 
-    CLASS_NAME: "FixedZoomLevels.js"
+    CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels"
 });
 
 /* ======================================================================
@@ -18050,7 +22161,6 @@
             if ((marker.icon != null) && (marker.icon.imageDiv != null) &&
                 (marker.icon.imageDiv.parentNode == this.div) ) {
                 this.div.removeChild(marker.icon.imageDiv);    
-                marker.drawn = false;
             }
         }
     },
@@ -18081,10 +22191,11 @@
         if (px == null) {
             marker.display(false);
         } else {
-            var markerImg = marker.draw(px);
-            if (!marker.drawn) {
+            if (!marker.isDrawn()) {
+                var markerImg = marker.draw(px);
                 this.div.appendChild(markerImg);
-                marker.drawn = true;
+            } else if(marker.icon) {
+                marker.icon.moveTo(px);
             }
         }
     },
@@ -18358,6 +22469,7 @@
      */
     drawFeature: function(geometry) {
         var feature = new OpenLayers.Feature.Vector(geometry);
+        feature.state = OpenLayers.State.INSERT;
         this.layer.addFeatures([feature]);
         this.featureAdded(feature);
         this.events.triggerEvent("featureadded",{feature : feature});
@@ -18366,6 +22478,256 @@
     CLASS_NAME: "OpenLayers.Control.DrawFeature"
 });
 /* ======================================================================
+    OpenLayers/Control/Measure.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Control.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Control.Measure
+ * Allows for drawing of features for measurements.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Control>
+ */
+OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, {
+
+    /**
+     * Constant: EVENT_TYPES
+     * {Array(String)} Supported application event types.  Register a listener
+     *     for a particular event with the following syntax:
+     * (code)
+     * control.events.register(type, obj, listener);
+     * (end)
+     *
+     * Listeners will be called with a reference to an event object.  The
+     *     properties of this event depends on exactly what happened.
+     *
+     * Supported control event types (in addition to those from <OpenLayers.Control>):
+     *  - *measure* Triggered when a measurement sketch is complete.  Listeners
+     *      will receive an event with measure, units, order, and geometry
+     *      properties.
+     *  - *measurepartial* Triggered when a new point is added to the
+     *      measurement sketch.  Listeners receive an event with measure,
+     *      units, order, and geometry.
+     */
+    EVENT_TYPES: ['measure', 'measurepartial'],
+
+    /**
+     * APIProperty: handlerOptions
+     * {Object} Used to set non-default properties on the control's handler
+     */
+    handlerOptions: null,
+    
+    /**
+     * Property: callbacks
+     * {Object} The functions that are sent to the handler for callback
+     */
+    callbacks: null,
+    
+    /**
+     * Property: displaySystem
+     * {String} Display system for output measurements.  Supported values
+     *     are 'english', 'metric', and 'geographic'.  Default is 'metric'.
+     */
+    displaySystem: 'metric',
+    
+    /**
+     * Property: displaySystemUnits
+     * {Object} Units for various measurement systems.  Values are arrays
+     *     of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing
+     *     order of length.
+     */
+    displaySystemUnits: {
+        geographic: ['dd'],
+        english: ['mi', 'ft', 'in'],
+        metric: ['km', 'm']
+    },
+
+    /**
+     * Constructor: OpenLayers.Control.Measure
+     * 
+     * Parameters:
+     * handler - {<OpenLayers.Handler>} 
+     * options - {Object} 
+     */
+    initialize: function(handler, options) {
+        // concatenate events specific to measure with those from the base
+        this.EVENT_TYPES =
+            OpenLayers.Control.Measure.prototype.EVENT_TYPES.concat(
+            OpenLayers.Control.prototype.EVENT_TYPES
+        );
+        OpenLayers.Control.prototype.initialize.apply(this, [options]);
+        this.callbacks = OpenLayers.Util.extend(
+            {done: this.measureComplete, point: this.measurePartial},
+            this.callbacks
+        );
+        this.handler = new handler(this, this.callbacks, this.handlerOptions);
+    },
+    
+    /**
+     * Method: updateHandler
+     *
+     * Parameters:
+     * handler - {Function} One of the sketch handler constructors.
+     * options - {Object} Options for the handler.
+     */
+    updateHandler: function(handler, options) {
+        var active = this.active;
+        if(active) {
+            this.deactivate();
+        }
+        this.handler = new handler(this, this.callbacks, options);
+        if(active) {
+            this.activate();
+        }
+    },
+
+    /**
+     * Method: measureComplete
+     * Called when the measurement sketch is done.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    measureComplete: function(geometry) {
+        this.measure(geometry, "measure");
+    },
+    
+    /**
+     * Method: measurePartial
+     * Called each time a new point is added to the measurement sketch.
+     *
+     * Parameters:
+     * point - {<OpenLayers.Geometry.Point>} The last point added.
+     * geometry - {<OpenLayers.Geometry>} The sketch geometry.
+     */
+    measurePartial: function(point, geometry) {
+        this.measure(geometry, "measurepartial");
+    },
+
+    /**
+     * Method: measure
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * eventType - {String}
+     */
+    measure: function(geometry, eventType) {
+        var stat, order;
+        if(geometry.CLASS_NAME.indexOf('LineString') > -1) {
+            stat = this.getBestLength(geometry);
+            order = 1;
+        } else {
+            stat = this.getBestArea(geometry);
+            order = 2;
+        }
+        this.events.triggerEvent(eventType, {
+            measure: stat[0],
+            units: stat[1],
+            order: order,
+            geometry: geometry
+        });
+    },
+    
+    /**
+     * Method: getBestArea
+     * Based on the <displaySystem> returns the area of a geometry.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     *
+     * Returns:
+     * {Array([Float, String])}  Returns a two item array containing the
+     *     area and the units abbreviation.
+     */
+    getBestArea: function(geometry) {
+        var units = this.displaySystemUnits[this.displaySystem];
+        var unit, area;
+        for(var i=0, len=units.length; i<len; ++i) {
+            unit = units[i];
+            area = this.getArea(geometry, unit);
+            if(area > 1) {
+                break;
+            }
+        }
+        return [area, unit];
+    },
+    
+    /**
+     * Method: getArea
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * units - {String} Unit abbreviation
+     *
+     * Returns:
+     * {Float} The geometry area in the given units.
+     */
+    getArea: function(geometry, units) {
+        var area = geometry.getArea();
+        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
+        if(inPerDisplayUnit) {
+            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[this.map.getUnits()];
+            area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);
+        }
+        return area;
+    },
+    
+    /**
+     * Method: getBestLength
+     * Based on the <displaySystem> returns the length of a geometry.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     *
+     * Returns:
+     * {Array([Float, String])}  Returns a two item array containing the
+     *     length and the units abbreviation.
+     */
+    getBestLength: function(geometry) {
+        var units = this.displaySystemUnits[this.displaySystem];
+        var unit, length;
+        for(var i=0, len=units.length; i<len; ++i) {
+            unit = units[i];
+            length = this.getLength(geometry, unit);
+            if(length > 1) {
+                break;
+            }
+        }
+        return [length, unit];
+    },
+
+    /**
+     * Method: getLength
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * units - {String} Unit abbreviation
+     *
+     * Returns:
+     * {Float} The geometry length in the given units.
+     */
+    getLength: function(geometry, units) {
+        var length = geometry.getLength();
+        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
+        if(inPerDisplayUnit) {
+            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[this.map.getUnits()];
+            length *= (inPerMapUnit / inPerDisplayUnit);
+        }
+        return length;
+    },
+
+    CLASS_NAME: "OpenLayers.Control.Measure"
+});
+/* ======================================================================
     OpenLayers/Control/ZoomBox.js
    ====================================================================== */
 
@@ -19775,12 +24137,12 @@
                 tileoffsetlon += tilelon;       
                 tileoffsetx += this.tileSize.w;
             } while ((tileoffsetlon <= bounds.right + tilelon * this.buffer)
-                     || colidx < minCols)  
+                     || colidx < minCols);
              
             tileoffsetlat -= tilelat;
             tileoffsety += this.tileSize.h;
         } while((tileoffsetlat >= bounds.bottom - tilelat * this.buffer)
-                || rowidx < minRows)
+                || rowidx < minRows);
         
         //shave off exceess rows and colums
         this.removeExcessTiles(rowidx, colidx);
@@ -20518,6 +24880,17 @@
      * {Boolean} Whether or not to handle right clicks. Default is false.
      */
     handleRightClicks: false,
+
+    /**
+     * APIProperty: zoomBoxKeyMask
+     * {Integer} <OpenLayers.Handler> key code of the key, which has to be
+     *    pressed, while drawing the zoom box with the mouse on the screen. 
+     *    You should probably set handleRightClicks to true if you use this
+     *    with MOD_CTRL, to disable the context menu for machines which use
+     *    CTRL-Click as a right click.
+     * Default: <OpenLayers.Handler.MOD_SHIFT
+     */
+    zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,
     
     /**
      * Constructor: OpenLayers.Control.Navigation
@@ -20601,7 +24974,7 @@
             OpenLayers.Util.extend({map: this.map}, this.dragPanOptions)
         );
         this.zoomBox = new OpenLayers.Control.ZoomBox(
-                    {map: this.map, keyMask: OpenLayers.Handler.MOD_SHIFT});
+                    {map: this.map, keyMask: this.zoomBoxKeyMask});
         this.dragPan.draw();
         this.zoomBox.draw();
         this.handlers.wheel = new OpenLayers.Handler.MouseWheel(
@@ -21764,6 +26137,193 @@
     CLASS_NAME: "OpenLayers.Layer.WMS"
 });
 /* ======================================================================
+    OpenLayers/Rule.js
+   ====================================================================== */
+
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Util.js
+ * @requires OpenLayers/Style.js
+ */
+
+/**
+ * Class: OpenLayers.Rule
+ * This class represents an SLD Rule, as being used for rule-based SLD styling.
+ */
+OpenLayers.Rule = OpenLayers.Class({
+    
+    /**
+     * Property: id
+     * {String} A unique id for this session.
+     */
+    id: null,
+    
+    /**
+     * APIProperty: name
+     * {String} name of this rule
+     */
+    name: 'default',
+    
+    /**
+     * Property: title
+     * {String} Title of this rule (set if included in SLD)
+     */
+    title: null,
+    
+    /**
+     * Property: description
+     * {String} Description of this rule (set if abstract is included in SLD)
+     */
+    description: null,
+
+    /**
+     * Property: context
+     * {Object} An optional object with properties that the rule should be
+     * evaluated against. If no context is specified, feature.attributes will
+     * be used.
+     */
+    context: null,
+    
+    /**
+     * Property: filter
+     * {<OpenLayers.Filter>} Optional filter for the rule.
+     */
+    filter: null,
+
+    /**
+     * Property: elseFilter
+     * {Boolean} Determines whether this rule is only to be applied only if
+     * no other rules match (ElseFilter according to the SLD specification). 
+     * Default is false.  For instances of OpenLayers.Rule, if elseFilter is
+     * false, the rule will always apply.  For subclasses, the else property is 
+     * ignored.
+     */
+    elseFilter: false,
+    
+    /**
+     * Property: symbolizer
+     * {Object} Symbolizer or hash of symbolizers for this rule. If hash of
+     * symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The
+     * latter if useful if it is required to style e.g. vertices of a line
+     * with a point symbolizer. Note, however, that this is not implemented
+     * yet in OpenLayers, but it is the way how symbolizers are defined in
+     * SLD.
+     */
+    symbolizer: null,
+    
+    /**
+     * APIProperty: minScaleDenominator
+     * {Number} or {String} minimum scale at which to draw the feature.
+     * In the case of a String, this can be a combination of text and
+     * propertyNames in the form "literal ${propertyName}"
+     */
+    minScaleDenominator: null,
+
+    /**
+     * APIProperty: maxScaleDenominator
+     * {Number} or {String} maximum scale at which to draw the feature.
+     * In the case of a String, this can be a combination of text and
+     * propertyNames in the form "literal ${propertyName}"
+     */
+    maxScaleDenominator: null,
+    
+    /** 
+     * Constructor: OpenLayers.Rule
+     * Creates a Rule.
+     *
+     * Parameters:
+     * options - {Object} An optional object with properties to set on the
+     *           rule
+     * 
+     * Returns:
+     * {<OpenLayers.Rule>}
+     */
+    initialize: function(options) {
+        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+        this.symbolizer = {};
+
+        OpenLayers.Util.extend(this, options);
+    },
+
+    /** 
+     * APIMethod: destroy
+     * nullify references to prevent circular references and memory leaks
+     */
+    destroy: function() {
+        for (var i in this.symbolizer) {
+            this.symbolizer[i] = null;
+        }
+        this.symbolizer = null;
+    },
+    
+    /**
+     * APIMethod: evaluate
+     * evaluates this rule for a specific feature
+     * 
+     * Parameters:
+     * feature - {<OpenLayers.Feature>} feature to apply the rule to.
+     * 
+     * Returns:
+     * {Boolean} true if the rule applies, false if it does not.
+     * This rule is the default rule and always returns true.
+     */
+    evaluate: function(feature) {
+        var context = this.getContext(feature);
+        var applies = true;
+
+        if (this.minScaleDenominator || this.maxScaleDenominator) {
+            var scale = feature.layer.map.getScale();
+        }
+        
+        // check if within minScale/maxScale bounds
+        if (this.minScaleDenominator) {
+            applies = scale >= OpenLayers.Style.createLiteral(
+                    this.minScaleDenominator, context);
+        }
+        if (applies && this.maxScaleDenominator) {
+            applies = scale < OpenLayers.Style.createLiteral(
+                    this.maxScaleDenominator, context);
+        }
+        
+        // check if optional filter applies
+        if(applies && this.filter) {
+            // feature id filters get the feature, others get the context
+            if(this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") {
+                applies = this.filter.evaluate(feature);
+            } else {
+                applies = this.filter.evaluate(context);
+            }
+        }
+
+        return applies;
+    },
+    
+    /**
+     * Method: getContext
+     * Gets the context for evaluating this rule
+     * 
+     * Paramters:
+     * feature - {<OpenLayers.Feature>} feature to take the context from if
+     *           none is specified.
+     */
+    getContext: function(feature) {
+        var context = this.context;
+        if (!context) {
+            context = feature.attributes || feature.data;
+        }
+        if (typeof this.context == "function") {
+            context = this.context(feature);
+        }
+        return context;
+    },
+        
+    CLASS_NAME: "OpenLayers.Rule"
+});
+/* ======================================================================
     OpenLayers/StyleMap.js
    ====================================================================== */
 
@@ -22930,11 +27490,7 @@
             this.drawn = true;
             var feature;
             for(var i=0, len=this.features.length; i<len; i++) {
-                if (i != (this.features.length - 1)) {
-                    this.renderer.locked = true;
-                } else {
-                    this.renderer.locked = false;
-                }    
+                this.renderer.locked = (i !== (len - 1));
                 feature = this.features[i];
                 this.drawFeature(feature);
             }
@@ -22997,9 +27553,7 @@
                 this.preFeatureInsert(feature);
             }
 
-            if (this.drawn) {
-                this.drawFeature(feature);
-            }
+            this.drawFeature(feature);
             
             if (notify) {
                 this.events.triggerEvent("featureadded", {
@@ -23118,6 +27672,12 @@
      * style - {Object} Symbolizer hash or {String} renderIntent
      */
     drawFeature: function(feature, style) {
+        // don't try to draw the feature with the renderer if the layer is not 
+        // drawn itself
+        if (!this.drawn) {
+            return
+        }
+        
         if (typeof style != "object") {
             var renderIntent = typeof style == "string" ?
                 style : feature.renderIntent;
@@ -23238,9 +27798,10 @@
      */
     getDataExtent: function () {
         var maxExtent = null;
-        if( this.features && (this.features.length > 0)){
-            var maxExtent = this.features[0].geometry.getBounds();
-            for(var i=0, len=this.features.length; i<len; i++){
+
+        if(this.features && (this.features.length > 0)) {
+            maxExtent = new OpenLayers.Bounds();
+            for(var i=0, len=this.features.length; i<len; i++) {
                 maxExtent.extend(this.features[i].geometry.getBounds());
             }
         }
@@ -23260,6 +27821,7 @@
 
 /**
  * @requires OpenLayers/Geometry/Collection.js
+ * @requires OpenLayers/Geometry/Point.js
  */
 
 /**
@@ -23323,197 +27885,6 @@
     CLASS_NAME: "OpenLayers.Geometry.MultiPoint"
 });
 /* ======================================================================
-    OpenLayers/Geometry/Polygon.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
- * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Geometry/Collection.js
- */
-
-/**
- * Class: OpenLayers.Geometry.Polygon 
- * Polygon is a collection of Geometry.LinearRings. 
- * 
- * Inherits from:
- *  - <OpenLayers.Geometry.Collection> 
- *  - <OpenLayers.Geometry> 
- */
-OpenLayers.Geometry.Polygon = OpenLayers.Class(
-  OpenLayers.Geometry.Collection, {
-
-    /**
-     * Property: componentTypes
-     * {Array(String)} An array of class names representing the types of
-     * components that the collection can include.  A null value means the
-     * component types are not restricted.
-     */
-    componentTypes: ["OpenLayers.Geometry.LinearRing"],
-
-    /**
-     * Constructor: OpenLayers.Geometry.Polygon
-     * Constructor for a Polygon geometry. 
-     * The first ring (this.component[0])is the outer bounds of the polygon and 
-     * all subsequent rings (this.component[1-n]) are internal holes.
-     *
-     *
-     * Parameters:
-     * components - {Array(<OpenLayers.Geometry.LinearRing>)} 
-     */
-    initialize: function(components) {
-        OpenLayers.Geometry.Collection.prototype.initialize.apply(this, 
-                                                                  arguments);
-    },
-    
-    /** 
-     * APIMethod: getArea
-     * Calculated by subtracting the areas of the internal holes from the 
-     *   area of the outer hole.
-     * 
-     * Returns:
-     * {float} The area of the geometry
-     */
-    getArea: function() {
-        var area = 0.0;
-        if ( this.components && (this.components.length > 0)) {
-            area += Math.abs(this.components[0].getArea());
-            for (var i=1, len=this.components.length; i<len; i++) {
-                area -= Math.abs(this.components[i].getArea());
-            }
-        }
-        return area;
-    },
-
-    /**
-     * Method: containsPoint
-     * Test if a point is inside a polygon.  Points on a polygon edge are
-     *     considered inside.
-     *
-     * Parameters:
-     * point - {<OpenLayers.Geometry.Point>}
-     *
-     * Returns:
-     * {Boolean | Number} The point is inside the polygon.  Returns 1 if the
-     *     point is on an edge.  Returns boolean otherwise.
-     */
-    containsPoint: function(point) {
-        var numRings = this.components.length;
-        var contained = false;
-        if(numRings > 0) {
-            // check exterior ring - 1 means on edge, boolean otherwise
-            contained = this.components[0].containsPoint(point);
-            if(contained !== 1) {
-                if(contained && numRings > 1) {
-                    // check interior rings
-                    var hole;
-                    for(var i=1; i<numRings; ++i) {
-                        hole = this.components[i].containsPoint(point);
-                        if(hole) {
-                            if(hole === 1) {
-                                // on edge
-                                contained = 1;
-                            } else {
-                                // in hole
-                                contained = false;
-                            }                            
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-        return contained;
-    },
-
-    /**
-     * APIMethod: intersects
-     * Determine if the input geometry intersects this one.
-     *
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>} Any type of geometry.
-     *
-     * Returns:
-     * {Boolean} The input geometry intersects this one.
-     */
-    intersects: function(geometry) {
-        var intersect = false;
-        var i, len;
-        if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
-            intersect = this.containsPoint(geometry);
-        } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" ||
-                  geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
-            // check if rings/linestrings intersect
-            for(i=0, len=this.components.length; i<len; ++i) {
-                intersect = geometry.intersects(this.components[i]);
-                if(intersect) {
-                    break;
-                }
-            }
-            if(!intersect) {
-                // check if this poly contains points of the ring/linestring
-                for(i=0, len=geometry.components.length; i<len; ++i) {
-                    intersect = this.containsPoint(geometry.components[i]);
-                    if(intersect) {
-                        break;
-                    }
-                }
-            }
-        } else {
-            for(i=0, len=geometry.components.length; i<len; ++ i) {
-                intersect = this.intersects(geometry.components[i]);
-                if(intersect) {
-                    break;
-                }
-            }
-        }
-        // check case where this poly is wholly contained by another
-        if(!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
-            // exterior ring points will be contained in the other geometry
-            var ring = this.components[0];
-            for(i=0, len=ring.components.length; i<len; ++i) {
-                intersect = geometry.containsPoint(ring.components[i]);
-                if(intersect) {
-                    break;
-                }
-            }
-        }
-        return intersect;
-    },
-
-    CLASS_NAME: "OpenLayers.Geometry.Polygon"
-});
-
-/**
- * APIMethod: createRegularPolygon
- * Create a regular polygon around a radius. Useful for creating circles 
- * and the like.
- *
- * Parameters:
- * origin - {<OpenLayers.Geometry.Point>} center of polygon.
- * radius - {Float} distance to vertex, in map units.
- * sides - {Integer} Number of sides. 20 approximates a circle.
- * rotation - {Float} original angle of rotation, in degrees.
- */
-OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {  
-    var angle = Math.PI * ((1/sides) - (1/2));
-    if(rotation) {
-        angle += (rotation / 180) * Math.PI;
-    }
-    var rotatedAngle, x, y;
-    var points = [];
-    for(var i=0; i<sides; ++i) {
-        rotatedAngle = angle + (i * 2 * Math.PI / sides);
-        x = origin.x + (radius * Math.cos(rotatedAngle));
-        y = origin.y + (radius * Math.sin(rotatedAngle));
-        points.push(new OpenLayers.Geometry.Point(x, y));
-    }
-    var ring = new OpenLayers.Geometry.LinearRing(points);
-    return new OpenLayers.Geometry.Polygon([ring]);
-};
-/* ======================================================================
     OpenLayers/Handler/Point.js
    ====================================================================== */
 
@@ -24088,6 +28459,334 @@
     CLASS_NAME: "OpenLayers.Geometry.LineString"
 });
 /* ======================================================================
+    OpenLayers/Geometry/LinearRing.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Geometry/LineString.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.LinearRing
+ * 
+ * A Linear Ring is a special LineString which is closed. It closes itself 
+ * automatically on every addPoint/removePoint by adding a copy of the first
+ * point as the last point. 
+ * 
+ * Also, as it is the first in the line family to close itself, a getArea()
+ * function is defined to calculate the enclosed area of the linearRing
+ * 
+ * Inherits:
+ *  - <OpenLayers.Geometry.LineString>
+ */
+OpenLayers.Geometry.LinearRing = OpenLayers.Class(
+  OpenLayers.Geometry.LineString, {
+
+    /**
+     * Property: componentTypes
+     * {Array(String)} An array of class names representing the types of 
+     *                 components that the collection can include.  A null 
+     *                 value means the component types are not restricted.
+     */
+    componentTypes: ["OpenLayers.Geometry.Point"],
+
+    /**
+     * Constructor: OpenLayers.Geometry.LinearRing
+     * Linear rings are constructed with an array of points.  This array
+     *     can represent a closed or open ring.  If the ring is open (the last
+     *     point does not equal the first point), the constructor will close
+     *     the ring.  If the ring is already closed (the last point does equal
+     *     the first point), it will be left closed.
+     * 
+     * Parameters:
+     * points - {Array(<OpenLayers.Geometry.Point>)} points
+     */
+    initialize: function(points) {
+        OpenLayers.Geometry.LineString.prototype.initialize.apply(this, 
+                                                                  arguments);
+    },
+
+    /**
+     * APIMethod: addComponent
+     * Adds a point to geometry components.  If the point is to be added to
+     *     the end of the components array and it is the same as the last point
+     *     already in that array, the duplicate point is not added.  This has 
+     *     the effect of closing the ring if it is not already closed, and 
+     *     doing the right thing if it is already closed.  This behavior can 
+     *     be overridden by calling the method with a non-null index as the 
+     *     second argument.
+     *
+     * Parameter:
+     * point - {<OpenLayers.Geometry.Point>}
+     * index - {Integer} Index into the array to insert the component
+     * 
+     * Returns:
+     * {Boolean} Was the Point successfully added?
+     */
+    addComponent: function(point, index) {
+        var added = false;
+
+        //remove last point
+        var lastPoint = this.components.pop();
+
+        // given an index, add the point
+        // without an index only add non-duplicate points
+        if(index != null || !point.equals(lastPoint)) {
+            added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, 
+                                                                    arguments);
+        }
+
+        //append copy of first point
+        var firstPoint = this.components[0];
+        OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, 
+                                                                [firstPoint]);
+        
+        return added;
+    },
+    
+    /**
+     * APIMethod: removeComponent
+     * Removes a point from geometry components.
+     *
+     * Parameters:
+     * point - {<OpenLayers.Geometry.Point>}
+     */
+    removeComponent: function(point) {
+        if (this.components.length > 4) {
+
+            //remove last point
+            this.components.pop();
+            
+            //remove our point
+            OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, 
+                                                                    arguments);
+            //append copy of first point
+            var firstPoint = this.components[0];
+            OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, 
+                                                                [firstPoint]);
+        }
+    },
+    
+    /**
+     * APIMethod: move
+     * Moves a collection in place
+     *
+     * Parameters:
+     * x - {Float} The x-displacement (in map units)
+     * y - {Float} The y-displacement (in map units)
+     */
+    move: function(x, y) {
+        for(var i = 0, len=this.components.length; i<len - 1; i++) {
+            this.components[i].move(x, y);
+        }
+    },
+
+    /**
+     * APIMethod: rotate
+     * Rotate a geometry around some origin
+     *
+     * Parameters:
+     * angle - {Float} Rotation angle in degrees (measured counterclockwise
+     *                 from the positive x-axis)
+     * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
+     */
+    rotate: function(angle, origin) {
+        for(var i=0, len=this.components.length; i<len - 1; ++i) {
+            this.components[i].rotate(angle, origin);
+        }
+    },
+
+    /**
+     * APIMethod: resize
+     * Resize a geometry relative to some origin.  Use this method to apply
+     *     a uniform scaling to a geometry.
+     *
+     * Parameters:
+     * scale - {Float} Factor by which to scale the geometry.  A scale of 2
+     *                 doubles the size of the geometry in each dimension
+     *                 (lines, for example, will be twice as long, and polygons
+     *                 will have four times the area).
+     * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
+     * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
+     */
+    resize: function(scale, origin, ratio) {
+        for(var i=0, len=this.components.length; i<len - 1; ++i) {
+            this.components[i].resize(scale, origin, ratio);
+        }
+    },
+    
+    /**
+     * APIMethod: transform
+     * Reproject the components geometry from source to dest.
+     *
+     * Parameters:
+     * source - {<OpenLayers.Projection>}
+     * dest - {<OpenLayers.Projection>}
+     * 
+     * Returns:
+     * {<OpenLayers.Geometry>} 
+     */
+    transform: function(source, dest) {
+        if (source && dest) {
+            for (var i=0, len=this.components.length; i<len - 1; i++) {
+                var component = this.components[i];
+                component.transform(source, dest);
+            }
+            this.bounds = null;
+        }
+        return this;
+    },
+
+    /**
+     * APIMethod: getArea
+     * Note - The area is positive if the ring is oriented CW, otherwise
+     *         it will be negative.
+     * 
+     * Returns:
+     * {Float} The signed area for a ring.
+     */
+    getArea: function() {
+        var area = 0.0;
+        if ( this.components && (this.components.length > 2)) {
+            var sum = 0.0;
+            for (var i=0, len=this.components.length; i<len - 1; i++) {
+                var b = this.components[i];
+                var c = this.components[i+1];
+                sum += (b.x + c.x) * (c.y - b.y);
+            }
+            area = - sum / 2.0;
+        }
+        return area;
+    },
+    
+    /**
+     * Method: containsPoint
+     * Test if a point is inside a linear ring.  For the case where a point
+     *     is coincident with a linear ring edge, returns 1.  Otherwise,
+     *     returns boolean.
+     *
+     * Parameters:
+     * point - {<OpenLayers.Geometry.Point>}
+     *
+     * Returns:
+     * {Boolean | Number} The point is inside the linear ring.  Returns 1 if
+     *     the point is coincident with an edge.  Returns boolean otherwise.
+     */
+    containsPoint: function(point) {
+        var approx = OpenLayers.Number.limitSigDigs;
+        var digs = 14;
+        var px = approx(point.x, digs);
+        var py = approx(point.y, digs);
+        function getX(y, x1, y1, x2, y2) {
+            return (((x1 - x2) * y) + ((x2 * y1) - (x1 * y2))) / (y1 - y2);
+        }
+        var numSeg = this.components.length - 1;
+        var start, end, x1, y1, x2, y2, cx, cy;
+        var crosses = 0;
+        for(var i=0; i<numSeg; ++i) {
+            start = this.components[i];
+            x1 = approx(start.x, digs);
+            y1 = approx(start.y, digs);
+            end = this.components[i + 1];
+            x2 = approx(end.x, digs);
+            y2 = approx(end.y, digs);
+            
+            /**
+             * The following conditions enforce five edge-crossing rules:
+             *    1. points coincident with edges are considered contained;
+             *    2. an upward edge includes its starting endpoint, and
+             *    excludes its final endpoint;
+             *    3. a downward edge excludes its starting endpoint, and
+             *    includes its final endpoint;
+             *    4. horizontal edges are excluded; and
+             *    5. the edge-ray intersection point must be strictly right
+             *    of the point P.
+             */
+            if(y1 == y2) {
+                // horizontal edge
+                if(py == y1) {
+                    // point on horizontal line
+                    if(x1 <= x2 && (px >= x1 && px <= x2) || // right or vert
+                       x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert
+                        // point on edge
+                        crosses = -1;
+                        break;
+                    }
+                }
+                // ignore other horizontal edges
+                continue;
+            }
+            cx = approx(getX(py, x1, y1, x2, y2), digs);
+            if(cx == px) {
+                // point on line
+                if(y1 < y2 && (py >= y1 && py <= y2) || // upward
+                   y1 > y2 && (py <= y1 && py >= y2)) { // downward
+                    // point on edge
+                    crosses = -1;
+                    break;
+                }
+            }
+            if(cx <= px) {
+                // no crossing to the right
+                continue;
+            }
+            if(x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {
+                // no crossing
+                continue;
+            }
+            if(y1 < y2 && (py >= y1 && py < y2) || // upward
+               y1 > y2 && (py < y1 && py >= y2)) { // downward
+                ++crosses;
+            }
+        }
+        var contained = (crosses == -1) ?
+            // on edge
+            1 :
+            // even (out) or odd (in)
+            !!(crosses & 1);
+
+        return contained;
+    },
+
+    /**
+     * APIMethod: intersects
+     * Determine if the input geometry intersects this one.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>} Any type of geometry.
+     *
+     * Returns:
+     * {Boolean} The input geometry intersects this one.
+     */
+    intersects: function(geometry) {
+        var intersect = false;
+        if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
+            intersect = this.containsPoint(geometry);
+        } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") {
+            intersect = geometry.intersects(this);
+        } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
+            intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply(
+                this, [geometry]
+            );
+        } else {
+            // check for component intersections
+            for(var i=0, len=geometry.components.length; i<len; ++ i) {
+                intersect = geometry.components[i].intersects(this);
+                if(intersect) {
+                    break;
+                }
+            }
+        }
+        return intersect;
+    },
+
+    CLASS_NAME: "OpenLayers.Geometry.LinearRing"
+});
+/* ======================================================================
     OpenLayers/Handler/Path.js
    ====================================================================== */
 
@@ -24306,6 +29005,7 @@
                 this.modifyFeature();
             }
             this.drawFeature();
+            //this.callback("move", [evt.xy]);
         }
         return true;
     },
@@ -24366,6 +29066,198 @@
     CLASS_NAME: "OpenLayers.Handler.Path"
 });
 /* ======================================================================
+    OpenLayers/Geometry/Polygon.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Geometry/Collection.js
+ * @requires OpenLayers/Geometry/LinearRing.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.Polygon 
+ * Polygon is a collection of Geometry.LinearRings. 
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Geometry.Collection> 
+ *  - <OpenLayers.Geometry> 
+ */
+OpenLayers.Geometry.Polygon = OpenLayers.Class(
+  OpenLayers.Geometry.Collection, {
+
+    /**
+     * Property: componentTypes
+     * {Array(String)} An array of class names representing the types of
+     * components that the collection can include.  A null value means the
+     * component types are not restricted.
+     */
+    componentTypes: ["OpenLayers.Geometry.LinearRing"],
+
+    /**
+     * Constructor: OpenLayers.Geometry.Polygon
+     * Constructor for a Polygon geometry. 
+     * The first ring (this.component[0])is the outer bounds of the polygon and 
+     * all subsequent rings (this.component[1-n]) are internal holes.
+     *
+     *
+     * Parameters:
+     * components - {Array(<OpenLayers.Geometry.LinearRing>)} 
+     */
+    initialize: function(components) {
+        OpenLayers.Geometry.Collection.prototype.initialize.apply(this, 
+                                                                  arguments);
+    },
+    
+    /** 
+     * APIMethod: getArea
+     * Calculated by subtracting the areas of the internal holes from the 
+     *   area of the outer hole.
+     * 
+     * Returns:
+     * {float} The area of the geometry
+     */
+    getArea: function() {
+        var area = 0.0;
+        if ( this.components && (this.components.length > 0)) {
+            area += Math.abs(this.components[0].getArea());
+            for (var i=1, len=this.components.length; i<len; i++) {
+                area -= Math.abs(this.components[i].getArea());
+            }
+        }
+        return area;
+    },
+
+    /**
+     * Method: containsPoint
+     * Test if a point is inside a polygon.  Points on a polygon edge are
+     *     considered inside.
+     *
+     * Parameters:
+     * point - {<OpenLayers.Geometry.Point>}
+     *
+     * Returns:
+     * {Boolean | Number} The point is inside the polygon.  Returns 1 if the
+     *     point is on an edge.  Returns boolean otherwise.
+     */
+    containsPoint: function(point) {
+        var numRings = this.components.length;
+        var contained = false;
+        if(numRings > 0) {
+            // check exterior ring - 1 means on edge, boolean otherwise
+            contained = this.components[0].containsPoint(point);
+            if(contained !== 1) {
+                if(contained && numRings > 1) {
+                    // check interior rings
+                    var hole;
+                    for(var i=1; i<numRings; ++i) {
+                        hole = this.components[i].containsPoint(point);
+                        if(hole) {
+                            if(hole === 1) {
+                                // on edge
+                                contained = 1;
+                            } else {
+                                // in hole
+                                contained = false;
+                            }                            
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return contained;
+    },
+
+    /**
+     * APIMethod: intersects
+     * Determine if the input geometry intersects this one.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>} Any type of geometry.
+     *
+     * Returns:
+     * {Boolean} The input geometry intersects this one.
+     */
+    intersects: function(geometry) {
+        var intersect = false;
+        var i, len;
+        if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
+            intersect = this.containsPoint(geometry);
+        } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" ||
+                  geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
+            // check if rings/linestrings intersect
+            for(i=0, len=this.components.length; i<len; ++i) {
+                intersect = geometry.intersects(this.components[i]);
+                if(intersect) {
+                    break;
+                }
+            }
+            if(!intersect) {
+                // check if this poly contains points of the ring/linestring
+                for(i=0, len=geometry.components.length; i<len; ++i) {
+                    intersect = this.containsPoint(geometry.components[i]);
+                    if(intersect) {
+                        break;
+                    }
+                }
+            }
+        } else {
+            for(i=0, len=geometry.components.length; i<len; ++ i) {
+                intersect = this.intersects(geometry.components[i]);
+                if(intersect) {
+                    break;
+                }
+            }
+        }
+        // check case where this poly is wholly contained by another
+        if(!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
+            // exterior ring points will be contained in the other geometry
+            var ring = this.components[0];
+            for(i=0, len=ring.components.length; i<len; ++i) {
+                intersect = geometry.containsPoint(ring.components[i]);
+                if(intersect) {
+                    break;
+                }
+            }
+        }
+        return intersect;
+    },
+
+    CLASS_NAME: "OpenLayers.Geometry.Polygon"
+});
+
+/**
+ * APIMethod: createRegularPolygon
+ * Create a regular polygon around a radius. Useful for creating circles 
+ * and the like.
+ *
+ * Parameters:
+ * origin - {<OpenLayers.Geometry.Point>} center of polygon.
+ * radius - {Float} distance to vertex, in map units.
+ * sides - {Integer} Number of sides. 20 approximates a circle.
+ * rotation - {Float} original angle of rotation, in degrees.
+ */
+OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {  
+    var angle = Math.PI * ((1/sides) - (1/2));
+    if(rotation) {
+        angle += (rotation / 180) * Math.PI;
+    }
+    var rotatedAngle, x, y;
+    var points = [];
+    for(var i=0; i<sides; ++i) {
+        rotatedAngle = angle + (i * 2 * Math.PI / sides);
+        x = origin.x + (radius * Math.cos(rotatedAngle));
+        y = origin.y + (radius * Math.sin(rotatedAngle));
+        points.push(new OpenLayers.Geometry.Point(x, y));
+    }
+    var ring = new OpenLayers.Geometry.LinearRing(points);
+    return new OpenLayers.Geometry.Polygon([ring]);
+};
+/* ======================================================================
     OpenLayers/Handler/Polygon.js
    ====================================================================== */
 

Modified: trunk/lib/fusion.js
===================================================================
--- trunk/lib/fusion.js	2009-01-14 15:25:12 UTC (rev 1735)
+++ trunk/lib/fusion.js	2009-01-14 15:42:24 UTC (rev 1736)
@@ -590,7 +590,7 @@
                   options.parameters = '?' + options.parameters;
               }
             }
-            new OpenLayers.Ajax.Request( url, options);
+            var temp = new OpenLayers.Ajax.Request( url, options);
         },
     
         /**
@@ -635,7 +635,7 @@
                 onSuccess: OpenLayers.Function.bind(this.xml2json, this, callback),
                 onFailure: OpenLayers.Function.bind(this.ajaxException, this)
             };
-            new OpenLayers.Ajax.Request(url, options);
+            var temp = new OpenLayers.Ajax.Request(url, options);
         },
     
         xml2json: function(callback, r, json) {
@@ -855,7 +855,6 @@
                 case 'kilometer':
                 case 'km':
                     return Fusion.KILOMETERS;
-                    break;
                 case 'degrees':
                 case 'degree':
                 case 'deg':
@@ -871,7 +870,7 @@
                 case 'px':
                     return Fusion.PIXELS;
                 default:
-                    Fusion.UNKNOWN;
+                    return Fusion.UNKNOWN;
             }
         },
         
@@ -1044,7 +1043,7 @@
                         var img = document.createElement('img');
                         img.src = src.slice(0, index)+'lib/a_pixel.png';
                         var s = img.src;
-                        var n = s.lastIndexOf('lib/a_pixel.png')
+                        var n = s.lastIndexOf('lib/a_pixel.png');
                         Fusion.fusionURL = s.slice(0, n);
                         Fusion.aPixel = img;
                         break;
@@ -1080,14 +1079,11 @@
     if (!Fusion._singleFile) {
         var coreScripts = ['lib/OpenLayers/OpenLayers.js',
                             'lib/jxlib.uncompressed.js',
-                            'lib/excanvas/excanvas-compressed.js',
                             'lib/EventMgr.js',
                             'lib/Error.js',
                             'lib/ApplicationDefinition.js',
                             'lib/MGBroker.js',
                             'lib/Widget.js',
-                            'lib/CanvasTool.js',
-                            'lib/RectTool.js',
                             'lib/Map.js',
                             'layers/Layers.js',
                             'lib/Search.js',

Modified: trunk/widgets/Measure.js
===================================================================
--- trunk/widgets/Measure.js	2009-01-14 15:25:12 UTC (rev 1735)
+++ trunk/widgets/Measure.js	2009-01-14 15:42:24 UTC (rev 1736)
@@ -39,7 +39,7 @@
 Fusion.Event.MEASURE_CLEAR = Fusion.Event.lastEventId++;
 Fusion.Event.MEASURE_COMPLETE = Fusion.Event.lastEventId++;
 
-Fusion.Widget.Measure = OpenLayers.Class(Fusion.Widget, Fusion.Tool.Canvas, {
+Fusion.Widget.Measure = OpenLayers.Class(Fusion.Widget, {
     isExclusive: true,
     uiClass: Jx.Button,
     
@@ -80,8 +80,6 @@
     areaStyle: null,
     
     initializeWidget: function(widgetTag) {
-        this.initializeCanvas();
-        
         this.asCursor = ['crosshair'];
         var json = widgetTag.extension;
         this.units = (json.Units && (json.Units[0] != '')) ?  Fusion.unitFromName(json.Units[0]): this.units;
@@ -91,7 +89,6 @@
         this.sTarget = json.Target ? json.Target[0] : "";
         this.sBaseUrl = Fusion.getFusionURL() + 'widgets/Measure/Measure.php';
         
-              
         //init measure type
         this.measureType = Fusion.Constant.MEASURE_TYPE_BOTH;
         if (json.Type) {
@@ -111,8 +108,8 @@
         var fillStyle = json.FillStyle ? json.FillStyle[0] : 'rgba(0,0,255, 0.3)';
         var lineStyleWidth = json.LineStyleWidth ? json.LineStyleWidth[0] : 2;
         var lineStyleColor = json.LineStyleColor ? json.LineStyleColor[0] : 'rgba(0,0,255,0.3)';     
-        this.fillStyle = new Fusion.Tool.Canvas.Style({fillStyle:fillStyle});
-        this.lineStyle = new Fusion.Tool.Canvas.Style({lineWidth:lineStyleWidth,strokeStyle:lineStyleColor});  	
+        //this.fillStyle = new Fusion.Tool.Canvas.Style({fillStyle:fillStyle});
+        //this.lineStyle = new Fusion.Tool.Canvas.Style({lineWidth:lineStyleWidth,strokeStyle:lineStyleColor});  	
         this.distanceMarkers = [];
         this.distances = [];
         
@@ -120,12 +117,58 @@
         this.registerEventID(Fusion.Event.MEASURE_CLEAR);
         this.registerEventID(Fusion.Event.MEASURE_COMPLETE);
         
-        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, OpenLayers.Function.bind(this.resetCanvas, this));
+        var mapWidget = this.getMap();
+        mapWidget.registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, OpenLayers.Function.bind(this.resetMeasure, this));
         this.keyHandler = OpenLayers.Function.bind(this.onKeyPress, this);
         Fusion.addWidgetStyleSheet(widgetTag.location + 'Measure/Measure.css');
 
-        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.setUnits, this, this.units));
+        mapWidget.registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.setUnits, this, this.units));
         this.registerParameter('Units');
+        
+            // style the sketch fancy
+        this.sketchSymbolizers = {
+                "Point": {
+                    pointRadius: 4,
+                    graphicName: "square",
+                    fillColor: "white",
+                    fillOpacity: 1,
+                    strokeWidth: 1,
+                    strokeOpacity: 1,
+                    strokeColor: "#333333"
+                },
+                "Line": {
+                    strokeWidth: 3,
+                    strokeOpacity: 1,
+                    strokeColor: "#666666",
+                    strokeDashstyle: "dash"
+                },
+                "Polygon": {
+                    strokeWidth: 2,
+                    strokeOpacity: 1,
+                    strokeColor: "#666666",
+                    fillColor: "white",
+                    fillOpacity: 0.3
+                }
+        };
+        var style = new OpenLayers.Style();
+        style.addRules([
+            new OpenLayers.Rule({symbolizer: this.sketchSymbolizers})
+        ]);
+        var styleMap = new OpenLayers.StyleMap({"default": style});
+            
+        //add in the OL Polygon handler
+        this.map = mapWidget.oMapOL;
+        this.handlerOptions = {                    
+            style: "default", // this forces default render intent
+            layerOptions: {styleMap: styleMap},
+            persist: true
+        };
+        this.handler = new OpenLayers.Handler.Path(this, {
+                done: this.dblClick,
+                point: this.mouseDown
+            }, this.handlerOptions);
+        this.handler.mousemove = OpenLayers.Function.bind(this.mouseMove, this);
+        mapWidget.handlers.push(this.handler);
     },
     
     onKeyPress: function(e) {
@@ -133,8 +176,8 @@
         var charCode = (e.charCode ) ? e.charCode : ((e.keyCode) ? e.keyCode : e.which);
         //console.log(charCode);
         if (charCode == OpenLayers.Event.KEY_ESC) {
-            this.resetCanvas();
-        } 
+            this.handler.clear();
+        }
     },
     
     /**
@@ -151,14 +194,13 @@
     },
     
     activate: function() {
-        this.activateCanvas();
-        this.initVars();
-        this.triggerEvent(Fusion.Event.MEASURE_CLEAR, this);
-         OpenLayers.Event.observe(document,"keypress",this.keyHandler);
+        this.handler.activate();
+        this.resetMeasure();
+        OpenLayers.Event.observe(document,"keypress",this.keyHandler);
         this.loadDisplayPanel();
     },
     
-    loadDisplayPanel : function() {
+    loadDisplayPanel: function() {
         if (this.sTarget) {
             var url = this.sBaseUrl;
             var queryStr = 'locale='+Fusion.locale;
@@ -187,13 +229,13 @@
                 this.totalDistanceMarker.domObj.parentNode != oDomElem) {
                 oDomElem.appendChild(this.totalDistanceMarker.domObj);
             }
-            this.totalDistanceMarker.domObj.className = 'divMeasureTotal';
+            this.totalDistanceMarker.domObj.addClass = 'divMeasureTotal';
             this.totalDistanceMarker.domObj.style.display = 'none';
             this.registerForEvent(Fusion.Event.MEASURE_CLEAR, OpenLayers.Function.bind(this.clearTotalDistance, this));  
             this.registerForEvent(Fusion.Event.MEASURE_SEGMENT_UPDATE, OpenLayers.Function.bind(this.updateTotalDistance, this));
             this.registerForEvent(Fusion.Event.MEASURE_COMPLETE, OpenLayers.Function.bind(this.updateTotalDistance, this));
         }
-    },    
+    },
     
     /**
      * (public) deactivate()
@@ -203,15 +245,14 @@
     deactivate: function() {
         console.log('Ruler.deactivate');
         OpenLayers.Event.stopObserving(document, 'keypress', this.keyHandler);           
-        this.deactivateCanvas();
-        this.resetCanvas();
+        this.handler.activate();
+        this.resetMeasure();
     },
     
-    resetCanvas: function() {
+    resetMeasure: function() {
         if (this.isDigitizing) {
             this.isDigitizing = false;
         }
-        this.clearContext();
         this.initVars();
         for (var i=0; i<this.distanceMarkers.length; i++)  {
             this.distanceMarkers[i].destroy();
@@ -227,45 +268,38 @@
      *
      * @param e Event the event that happened on the mapObj
      */
-    mouseDown: function(e) {
-        if (OpenLayers.Event.isLeftClick(e)) {
+    mouseDown: function(geom2) {
+        var evt = this.handler.evt;
+        //OL appears to be calling this for mouseup too so filter on mousedown
+        if (OpenLayers.Event.isLeftClick(evt) && evt.type=='mousedown') {
             var map = this.getMap();
-            var p = map.getEventPosition(e);
-            var gp = map.pixToGeo(p.x, p.y);
+            var p = map.getEventPosition(evt);
             
             if (!this.isDigitizing) {
-                this.resetCanvas();
-                var from = new Fusion.Tool.Canvas.Node(gp.x,gp.y, map);
-                var to = new Fusion.Tool.Canvas.Node(gp.x,gp.y, map);
-                var lastSegment = new Fusion.Tool.Canvas.Segment(from,to);
-                if (this.measureType == Fusion.Constant.MEASURE_TYPE_DISTANCE) {
-                    this.feature = new Fusion.Tool.Canvas.Line(map);
-                    this.feature.lineStyle = this.lineStyle;
-                } else {
-                    this.feature = new Fusion.Tool.Canvas.Polygon(map);
-                    this.feature.fillStyle = this.fillStyle;
-                    this.feature.lineStyle = this.lineStyle;
-                }
-                this.feature.addSegment(lastSegment);
-                this.aAreaFirstPoint = new Fusion.Tool.Canvas.Node(gp.x,gp.y, map);
-                this.isDigitizing = true;                                  
+                this.resetMeasure();
+                this.isDigitizing = true;
+                this.segStart = p;
+                
             } else {
                 //if digitizing
-                var lastSegment = this.feature.lastSegment();
-                lastSegment.to.set(gp.x,gp.y);
-                if (lastSegment.from.x == lastSegment.to.x && 
-                    lastSegment.from.y == lastSegment.to.y) {
-                    this.dblClick(e);
+                if (p == this.segStart) {
+                    this.dblClick(geom2);
                     return;
                 }
-                this.feature.extendLine();
-                this.updateMarker(this.lastMarker, lastSegment, e);
+                //create a new geometry to measure the last line segment drawn
+                //new point already added on mousedown so use -3,-2
+                var geom = this.handler.getGeometry();
+                var lastSeg =  new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString());
+                var p1 = geom.components[geom.components.length-3];
+                var p2 = geom.components[geom.components.length-2];
+                lastSeg.geometry.addPoint(p1.clone());
+                lastSeg.geometry.addPoint(p2.clone());
+                this.updateMarker(this.lastMarker, this.segStart, p, lastSeg);
+                this.segStart = p;
             }
             //create a new marker
             this.lastMarker = new Fusion.Widget.Measure.DistanceMarker(this.units, this.distPrecision);
             this.distanceMarkers.push(this.lastMarker);
-            this.clearContext();
-            this.feature.draw(this.context);
         }
     },
 
@@ -276,10 +310,14 @@
      *
      * @param e Event the event that happened on the mapObj
      */
-    mouseMove: function(e) {
+    mouseMove: function(evt) {
+        //var evt = this.handler.evt;
         if (!this.isDigitizing) {
             return;
         }
+        OpenLayers.Handler.Path.prototype.mousemove.apply(this.handler, [evt]);
+        var geom = this.handler.getGeometry();
+        
         var offset = {x:0,y:0};
         var oElement = this.getMap().getDomObj();
         //var target = e.target || e.srcElement;
@@ -287,17 +325,20 @@
             window.clearTimeout(this.delayUpdateTimer);
         }
         var map = this.getMap();
-        var p = map.getEventPosition(e);
-        var gp = map.pixToGeo(p.x, p.y);
+        var p = map.getEventPosition(evt);
         
-        var lastSegment = this.feature.lastSegment();
-        lastSegment.to.set(gp.x,gp.y);
-        this.clearContext();
-        this.feature.draw(this.context);
         this.lastMarker.setCalculating();
-        this.delayUpdateTimer = window.setTimeout(OpenLayers.Function.bind(this.delayUpdate, this, lastSegment, e), 100);
         
-        this.positionMarker(this.lastMarker, lastSegment);
+        //create a new geometry to measure the last line segment drawn
+        //from the last 2 points
+        var lastSeg =  new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString());
+        var p1 = geom.components[geom.components.length-2];
+        var p2 = geom.components[geom.components.length-1];
+        lastSeg.geometry.addPoint(p1.clone());
+        lastSeg.geometry.addPoint(p2.clone());
+        this.delayUpdateTimer = window.setTimeout(OpenLayers.Function.bind(this.delayUpdate, this, this.segStart, p, lastSeg), 100);
+        
+        this.positionMarker(this.lastMarker, this.segStart, p);
         if (this.totalDistanceMarker) {
           var size = this.totalDistanceMarker.getSize();
           this.totalDistanceMarker.domObj.style.top = (p.y - size.height) + 'px';
@@ -305,9 +346,9 @@
         }
     },
     
-    delayUpdate: function(lastSegment, e) {
+    delayUpdate: function(from, to, geom) {
         this.delayUpdateTimer = null;
-        this.updateMarker(this.lastMarker, lastSegment, e);
+        this.updateMarker(this.lastMarker, from, to, geom);
     },
    
     /**
@@ -317,17 +358,13 @@
      *
      * @param e Event the event that happened on the mapObj
      */
-    dblClick: function(e) {
+    dblClick: function(geom) {
         //console.log('Digitizer.dblClick');
         if (!this.isDigitizing) {
             return;
         }
-        var p = this.getMap().getEventPosition(e);
-        var gp = this.getMap().pixToGeo(p.x, p.y);   
-        var seg = this.feature.lastSegment();
-        seg.to.set(gp.x,gp.y);
-        this.clearContext();
-        this.feature.draw(this.context);
+        var evt = this.handler.evt;
+        var p = this.getMap().getEventPosition(evt);
         
         if (this.measureType == Fusion.Constant.MEASURE_TYPE_AREA || this.measureType == Fusion.Constant.MEASURE_TYPE_BOTH) {
             
@@ -340,49 +377,181 @@
         //mousedown creates a new segment before dblClick so remove the last one
         var lastMarker = this.distanceMarkers.pop();
         lastMarker.destroy();
-        this.triggerEvent(Fusion.Event.MEASURE_COMPLETE);                    
+        this.triggerEvent(Fusion.Event.MEASURE_COMPLETE);
     },
     
-    positionMarker: function(marker, segment) {
+    positionMarker: function(marker, from, to) {
         var oDomElem =  this.getMap().getDomObj();
         if (!marker.domObj.parentNode || 
             marker.domObj.parentNode != oDomElem) {
             oDomElem.appendChild(marker.domObj);
         }
         var size = marker.getSize();
-        var t = (segment.from.py + segment.to.py - size.height)/2 ;
-        var l = (segment.from.px + segment.to.px - size.width)/2;
+        var t = (from.y + to.y - size.height)/2 ;
+        var l = (from.x + to.x - size.width)/2;
         marker.domObj.style.top = t + 'px';
         marker.domObj.style.left = l + 'px';
     },
     
-    updateMarker: function(marker, segment, e) {
+    updateMarker: function(marker, from, to, geom) {
         if (!marker) {
             return;
         }
-        this.measureSegment(segment, marker);
-        this.positionMarker(marker, segment);
+        this.measureSegment2(marker, geom);
+        this.positionMarker(marker, from, to);
+        this.triggerEvent(Fusion.Event.MEASURE_SEGMENT_UPDATE);                    
     },
     
-    measureSegment: function(segment, marker) {
-        var aMaps = this.getMap().getAllMaps();
-        var s = 'layers/' + aMaps[0].arch + '/' + Fusion.getScriptLanguage() + "/Measure." + Fusion.getScriptLanguage() ;
+    measureSegment2: function(marker, geom) {
+        var dist = this.measure(geom.geometry);
+        marker.setDistance(dist);
+        geom.destroy();
+    },
+    
+    /**
+     * Method: measure
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * eventType - {String}
+     */
+    measure: function(geometry, eventType) {
+        var stat, order;
+        if(geometry.CLASS_NAME.indexOf('LineString') > -1) {
+            stat = this.getLength(geometry, this.units);
+            order = 1;
+        } else {
+            stat = this.getArea(geometry, this.units);
+            order = 2;
+        }
+        return stat;
+        /*
+        this.events.triggerEvent(eventType, {
+            measure: stat[0],
+            units: stat[1],
+            order: order,
+            geometry: geometry
+        });
+        */
+    },
+    
+    /**
+     * Method: getBestArea
+     * Based on the <displaySystem> returns the area of a geometry.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     *
+     * Returns:
+     * {Array([Float, String])}  Returns a two item array containing the
+     *     area and the units abbreviation.
+     */
+    displaySystemUnits: {
+        geographic: ['dd'],
+        english: ['mi', 'ft', 'in'],
+        metric: ['km', 'm']
+    },
+    getBestArea: function(geometry) {
+        var units = this.displaySystemUnits['metric'];
+        var unit, area;
+        for(var i=0, len=units.length; i<len; ++i) {
+            unit = units[i];
+            area = this.getArea(geometry, unit);
+            if(area > 1) {
+                break;
+            }
+        }
+        return [area, unit];
+    },
+    
+    /**
+     * Method: getArea
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * units - {String} Unit abbreviation
+     *
+     * Returns:
+     * {Float} The geometry area in the given units.
+     */
+    getArea: function(geometry, units) {
+        var area = geometry.getArea();
+        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
+        if(inPerDisplayUnit) {
+            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[this.map.getUnits()];
+            area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);
+        }
+        return area;
+    },
+    
+    /**
+     * Method: getBestLength
+     * Based on the <displaySystem> returns the length of a geometry.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     *
+     * Returns:
+     * {Array([Float, String])}  Returns a two item array containing the
+     *     length and the units abbreviation.
+     */
+    getBestLength: function(geometry) {
+        var units = this.displaySystemUnits['metric'];
+        var unit, length;
+        for(var i=0, len=units.length; i<len; ++i) {
+            unit = units[i];
+            length = this.getLength(geometry, unit);
+            if(length > 1) {
+                break;
+            }
+        }
+        return [length, unit];
+    },
+
+    /**
+     * Method: getLength
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * units - {String} Unit abbreviation
+     *
+     * Returns:
+     * {Float} The geometry length in the given units.
+     */
+    getLength: function(geometry, fusionUnits) {
+        var units = Fusion.aUnitNames[fusionUnits];
+        var units = "m";
+        var length = geometry.getLength();
+        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
+        if(inPerDisplayUnit) {
+            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[this.map.getUnits()];
+            length *= (inPerMapUnit / inPerDisplayUnit);
+        }
+        return length;
+    },
+    
+    measureSegment: function(marker, from, to, geom) {
+        var mapWidget = this.getMap();
+        var aMaps = mapWidget.getAllMaps();
+        var s = 'layers/' + aMaps[0].arch + '/' + Fusion.getScriptLanguage() + "/Measure." + Fusion.getScriptLanguage();
+        var fromGeo = mapWidget.pixToGeo(from.x, from.y);
+        var toGeo = mapWidget.pixToGeo(to.x, to.y);
         var options = {
             parameters: {
                 'session': aMaps[0].getSessionID(),
                 'locale': Fusion.locale,
-                'mapname': this.getMap().getMapName(),
-                'x1': segment.from.x,
-                'y1': segment.from.y,
-                'x2': segment.to.x,
-                'y2': segment.to.y
+                'mapname': mapWidget.getMapName(),
+                'x1': fromGeo.x,
+                'y1': fromGeo.y,
+                'x2': toGeo.x,
+                'y2': toGeo.y
             },
-            'onComplete': OpenLayers.Function.bind(this.measureCompleted, this, segment, marker)
+            'onComplete': OpenLayers.Function.bind(this.measureCompleted, this, from, to, marker)
         };
         Fusion.ajaxRequest(s, options);
     },
     
-    measureCompleted: function(segment, marker, r) {
+    measureCompleted: function(from, to, marker, r) {
         if (r.status == 200) {
             var o;
             eval('o='+r.responseText);
@@ -397,7 +566,7 @@
               }
               
               marker.setDistance(o.distance);
-              this.positionMarker(marker, segment);
+              this.positionMarker(marker, from, to);
               this.triggerEvent(Fusion.Event.MEASURE_SEGMENT_UPDATE);                    
             }
         }
@@ -511,8 +680,6 @@
 * A class for handling the 'tooltip' for the distance measurement.  Markers also hold the distance
 values and all markers are held in an array in the Measure widget for access.
 */
-//Fusion.Widget.Measure.DistanceMarker = Class.create();
-//Fusion.Widget.Measure.DistanceMarker.prototype = {
 Fusion.Widget.Measure.DistanceMarker = OpenLayers.Class(
 {
     calculatingImg: null,

Modified: trunk/widgets/Select.js
===================================================================
--- trunk/widgets/Select.js	2009-01-14 15:25:12 UTC (rev 1735)
+++ trunk/widgets/Select.js	2009-01-14 15:42:24 UTC (rev 1736)
@@ -35,7 +35,7 @@
     isExclusive: true,
     uiClass: Jx.Button,
     selectionType: 'INTERSECTS',
-    nTolerance : 3,     //default pixel tolernace for a point click
+    nTolerance: 3,     //default pixel tolernace for a point click
     bActiveOnly: false, //only select feature(s) on the active layer?
     maxFeatures: 0,     //deafult of 0 selects all features (i.e. no maximum)
     
@@ -70,11 +70,11 @@
         
         var mapWidget = this.getMap();
         this.map = mapWidget.oMapOL;
-        this.handler = new OpenLayers.Handler.Box(this,{done: this.execute},{keyMask:0});
-        this.shiftHandler = new OpenLayers.Handler.Box(this,{done: this.extend},
-                                        {keyMask:OpenLayers.Handler.MOD_SHIFT});
+        this.handler = new OpenLayers.Handler.Box(this,{done: this.execute});
+        //this.shiftHandler = new OpenLayers.Handler.Box(this,{done: this.extend},
+        //                                {keyMask:OpenLayers.Handler.MOD_SHIFT});
         mapWidget.handlers.push(this.handler);
-        mapWidget.handlers.push(this.shiftHandler);
+        //mapWidget.handlers.push(this.shiftHandler);
         
     },
     
@@ -96,9 +96,9 @@
        * This function should be defined for all functions that register
        * as a widget in the map
        */
-    activate : function() {
+    activate: function() {
         this.handler.activate();
-        this.shiftHandler.activate();
+        //this.shiftHandler.activate();
         this.getMap().setCursor(this.asCursor);
     },
 
@@ -107,9 +107,9 @@
        * This function should be defined for all functions that register
        * as a widget in the map
        **/
-    deactivate : function() {
+    deactivate: function() {
         this.handler.deactivate();
-        this.shiftHandler.deactivate();
+        //this.shiftHandler.deactivate();
         this.getMap().setCursor('auto');
     },
 
@@ -121,7 +121,7 @@
         *   position will be either an instance of OpenLayers.Bounds when the mouse has
         *   moved, or an OpenLayers.Pixel for click without dragging on the map
         **/
-    execute : function(position, extend) {
+    execute: function(position, extend) {
         //ctrl click is used to launch a URL defined on the feature. See ClickCTRL widget
         if (this.keyModifiers & OpenLayers.Handler.MOD_CTRL) {
           //return;
@@ -166,7 +166,7 @@
             }
         }
         
-        if (extend) {
+        if (this.handler.dragHandler.evt.shiftKey) {
             options.extendSelection = true;
         }
         
@@ -214,7 +214,7 @@
         *               'Tolerance' and 'SelectionType'
         * value - the value to sue for the parameter
         */
-    setParameter : function(param, value) {
+    setParameter: function(param, value) {
         if (param == "Tolerance" && value > 0) {
             this.nTolerance = value;
         }

Modified: trunk/widgets/SelectPolygon.js
===================================================================
--- trunk/widgets/SelectPolygon.js	2009-01-14 15:25:12 UTC (rev 1735)
+++ trunk/widgets/SelectPolygon.js	2009-01-14 15:42:24 UTC (rev 1736)
@@ -30,14 +30,13 @@
  * 
  * **********************************************************************/
 
-Fusion.Widget.SelectPolygon = OpenLayers.Class(Fusion.Widget, Fusion.Tool.Canvas, {
+Fusion.Widget.SelectPolygon = OpenLayers.Class(Fusion.Widget, {
     isExclusive: true,
     uiClass: Jx.Button,
     selectionType: 'INTERSECTS',
-    nTolerance : 3, //default pixel tolernace for a point click
+    nTolerance: 3, //default pixel tolernace for a point click
+    
     initializeWidget: function(widgetTag) {
-        this.initializeCanvas();
-        
         this.asCursor = ['auto'];
 
         var json = widgetTag.extension;
@@ -50,7 +49,12 @@
                            (json.ComputeMetadata[0] == 'true' ||
                             json.ComputeMetadata[0] == '1')) ? true : false;
         
-        this.polygon = new Fusion.Tool.Canvas.Polygon();
+        //add in the OL Polygon handler
+        var mapWidget = this.getMap();
+        this.map = mapWidget.oMapOL;
+        this.handlerOptions = {};
+        this.handler = new OpenLayers.Handler.Polygon(this, {done: this.execute}, this.handlerOptions);
+        mapWidget.handlers.push(this.handler);
     },
     
     /**
@@ -58,11 +62,9 @@
      * This function should be defined for all functions that register
      * as a widget in the map
      */
-    activate : function() {
-        this.activateCanvas();
+    activate: function() {
+        this.handler.activate();
         this.getMap().setCursor(this.asCursor);
-        /*icon button*/
-        this.polygon = new Fusion.Tool.Canvas.Polygon(this.getMap());
     },
 
     /**
@@ -70,109 +72,25 @@
      * This function should be defined for all functions that register
      * as a widget in the map
      **/
-    deactivate : function()
+    deactivate: function()
     {
-         this.deactivateCanvas();
-         this.getMap().setCursor('auto');
-         /*icon button*/
+        this.handler.deactivate();
+        this.getMap().setCursor('auto');
     },
     
     /**
-     * (public) mouseDown(e)
-     *
-     * handle the mouse down event
-     *
-     * @param e Event the event that happened on the mapObj
-     */
-    mouseDown: function(e) {
-        //console.log('SelectRadius.mouseDown');
-        if (OpenLayers.Event.isLeftClick(e)) {
-            var p = this.getMap().getEventPosition(e);
-
-            if (!this.isDigitizing) {
-                this.polygon = new Fusion.Tool.Canvas.Polygon(this.getMap());
-                var point = this.getMap().pixToGeo(p.x, p.y);
-                var from = new Fusion.Tool.Canvas.Node(point.x,point.y, this.getMap());
-                var to = new Fusion.Tool.Canvas.Node(point.x,point.y, this.getMap());
-                var seg = new Fusion.Tool.Canvas.Segment(from,to);
-                seg.setEditing(true);
-                this.polygon.addSegment(seg);
-                this.clearContext();
-                this.polygon.draw(this.context);     
-
-                this.isDigitizing = true;
-            } else {
-                var seg = this.polygon.lastSegment();
-                seg.setEditing(false);
-                seg = this.polygon.extendLine();
-                seg.setEditing(true);
-                this.clearContext();
-                this.polygon.draw(this.context);
-            }
-        }
-    },
-
-    /**
-     * (public) mouseMove(e)
-     *
-     * handle the mouse move event
-     *
-     * @param e Event the event that happened on the mapObj
-     */
-    mouseMove: function(e) {
-        //console.log('SelectRadius.mouseMove');
-        if (!this.isDigitizing) {
-            return;
-        }
-    
-        var p = this.getMap().getEventPosition(e);
-        var seg = this.polygon.lastSegment();
-        seg.to.setPx(p.x,p.y);
-        seg.to.updateGeo();
-        this.clearContext();
-        this.polygon.draw(this.context);
-    },
-    
-    /**
-     * (public) dblClick(e)
-     *
-     * handle the mouse dblclick event
-     *
-     * @param e Event the event that happened on the mapObj
-     */
-    dblClick: function(e) {
-        //console.log('Digitizer.dblClick');
-        if (!this.isDigitizing) {
-            return;
-        }
-        this.event = e;
-        var p = this.getMap().getEventPosition(e);
-        var point = this.getMap().pixToGeo(p.x, p.y);
-        var seg = this.polygon.lastSegment();
-        seg.setEditing(false);
-        seg.to.set(point.x,point.y);
-        this.clearContext();
-        this.isDigitizing = false;
-        this.execute();
-    },
-
-    /**
      *  
      **/
-    execute : function() {
-        var wkt = 'POLYGON((';
-        var nodes = this.polygon.getNodes();
-        var sep = '';
-        for (var i=0; i<nodes.length; i++) {
-            wkt = wkt + sep + nodes[i].x + ' ' + nodes[i].y;
-            sep = ',';
-        }
-        wkt = wkt + sep + nodes[0].x + ' ' + nodes[0].y + '))';
+    execute: function(geom) {
         
         var options = {};
-        options.geometry = wkt;
+        options.geometry = geom.toString();
         options.selectionType = this.selectionType;
         options.computed = this.bComputeMetadata;
+        
+        if (this.handler.evt.ctrlKey) {
+          options.extendSelection = true;
+        }
 
         if (this.bActiveOnly) {
             var layer = this.getMap().getActiveLayer();
@@ -183,14 +101,10 @@
             }
         }
         
-        if (this.event.shiftKey) {
-            options.extendSelection = true;
-        }
-        
         this.getMap().query(options);
     },
     
-    setParameter : function(param, value) {
+    setParameter: function(param, value) {
         if (param == "Tolerance" && value > 0) {
             this.nTolerance = value;
         }

Modified: trunk/widgets/SelectRadius.js
===================================================================
--- trunk/widgets/SelectRadius.js	2009-01-14 15:25:12 UTC (rev 1735)
+++ trunk/widgets/SelectRadius.js	2009-01-14 15:42:24 UTC (rev 1736)
@@ -35,13 +35,11 @@
     isExclusive: true,
     uiClass: Jx.Button,
     selectionType: 'INTERSECTS',
-    nTolerance : 3, //default pixel tolernace for a point click
+    nTolerance: 3, //default pixel tolernace for a point click
     defaultRadius: 20, //this is now in pixels
+    
     initializeWidget: function(widgetTag) {
-        this.initializeCanvas();
-
         this.asCursor = ['auto'];
-        this.isDigitizing = false;
 
         var json = widgetTag.extension;
         this.selectionType = json.SelectionType ? json.SelectionType[0] : 'INTERSECTS';
@@ -76,6 +74,19 @@
         }
         
         this.registerEventID(Fusion.Event.RADIUS_WIDGET_ACTIVATED);
+        
+        //add in the OL Polygon handler
+        var mapWidget = this.getMap();
+        this.map = mapWidget.oMapOL;
+        this.handlerOptions = {sides: 40};
+        this.handler = new OpenLayers.Handler.RegularPolygon(this, {
+            interval: 100,
+            done: this.execute,
+            down: this.mouseDown,
+            move: this.mouseMove,
+            up: this.mouseUp
+            }, this.handlerOptions);
+        mapWidget.handlers.push(this.handler);
     },
     
     setRadius: function(r) {
@@ -83,8 +94,8 @@
     },
     
     getRadius: function() {
-        if (this.circle) {
-            return this.circle.radius;
+        if (this.handler.active) {
+            return this.handler.radius;
         } else {
             return this.defaultRadius;
         }
@@ -96,12 +107,10 @@
      * as a widget in the map
      */
     activate: function() {
-        this.activateCanvas();
+        var radius = this.getMap().pixToGeoMeasure(this.defaultRadius);
+        this.handler.setOptions({radius: radius});
+        this.handler.activate();
         this.getMap().setCursor(this.asCursor);
-        /*icon button*/
-        if (!this.circle) {
-            this.circle = new Fusion.Tool.Canvas.Circle(this.getMap());
-        }
         /*map units for tool tip*/
         this.units = this.getMap().getAllMaps()[0].units;
         this.triggerEvent(Fusion.Event.RADIUS_WIDGET_ACTIVATED, true);
@@ -113,7 +122,7 @@
      * as a widget in the map
      **/
     deactivate: function() {
-        this.deactivateCanvas();
+        this.handler.deactivate();
         this.getMap().setCursor('auto');
         /*icon button*/
         this.triggerEvent(Fusion.Event.RADIUS_WIDGET_ACTIVATED, false);
@@ -126,31 +135,26 @@
      *
      * @param e Event the event that happened on the mapObj
      */
-    mouseDown: function(e) {
-        if (OpenLayers.Event.isLeftClick(e)) {
-            var p = this.getMap().getEventPosition(e);
-            var point = this.getMap().pixToGeo(p.x, p.y);
-            var radius = this.getMap().pixToGeoMeasure(this.defaultRadius);
-            
-            if (!this.isDigitizing) {
-                this.circle.setCenter(point.x, point.y);
-                this.circle.setRadius(radius);
-                this.clearContext();
-                this.circle.draw(this.context);     
-                this.isDigitizing = true;
-            }
-        }
-        if (this.radiusTip && this.radiusTipType == 'dynamic') {
-            this.radiusTip.style.display = 'block';
-            var size = $(this.radiusTip).getBorderBoxSize();
-            this.radiusTip.style.top = (p.y - size.height*2) + 'px';
-            this.radiusTip.style.left = p.x + 'px';
-            var r = this.circle.radius;
-            if (this.units == 'm' || this.units == 'ft') {
-                r = Math.round(r * 100)/100;
-            }
-            this.radiusTip.innerHTML = r + this.units;
-        }
+    mouseDown: function(geom) {
+      var evt = this.handler.evt;
+      if (OpenLayers.Event.isLeftClick(evt)) {
+          this.handler.fixedRadius = false;
+          
+          var p = this.getMap().getEventPosition(evt);
+          var point = this.getMap().pixToGeo(p.x, p.y);
+          var radius = this.getMap().pixToGeoMeasure(this.handler.radius);
+          
+          if (this.radiusTip && this.radiusTipType == 'dynamic') {
+              this.radiusTip.style.display = 'block';
+              var size = $(this.radiusTip).getBorderBoxSize();
+              this.radiusTip.style.top = (p.y - size.height*2) + 'px';
+              this.radiusTip.style.left = p.x + 'px';
+              if (this.units == 'm' || this.units == 'ft') {
+                  radius = Math.round(radius * 100)/100;
+              }
+              this.radiusTip.innerHTML = radius + this.units;
+          }
+      }
     },
 
     /**
@@ -160,52 +164,39 @@
      *
      * @param e Event the event that happened on the mapObj
      */
-    mouseMove: function(e) {
-        if (!this.isDigitizing) {
-            return;
-        }
+    mouseMove: function(geom) {
         
+      var evt = this.handler.evt;
+      if (OpenLayers.Event.isLeftClick(evt)) {
         var map = this.getMap();
-        var p = map.getEventPosition(e);
+        var p = map.getEventPosition(evt);
         var point = map.pixToGeo(p.x, p.y);
-        var center = this.circle.center;
+        //var center = this.circle.center;
         
-        var radius = Math.sqrt(Math.pow(center.x-point.x,2) + Math.pow(center.y-point.y,2));
+        var radius = this.getMap().pixToGeoMeasure(this.handler.radius);//Math.sqrt(Math.pow(center.x-point.x,2) + Math.pow(center.y-point.y,2));
 
-        if (radius > this.getMap().pixToGeoMeasure(this.nTolerance)) {
-            this.circle.setRadius(radius);
-        }
-        this.clearContext();
-        this.circle.draw(this.context);
-        
         if (this.radiusTip && this.radiusTipType == 'dynamic') {
             this.radiusTip.style.display = 'block';
             var size = $(this.radiusTip).getBorderBoxSize();
             this.radiusTip.style.top = (p.y - size.height*2) + 'px';
             this.radiusTip.style.left = p.x + 'px';
-            var r = this.circle.radius;
             if (this.units == 'm' || this.units == 'ft') {
-                r = Math.round(r * 100)/100;
+                radius = Math.round(radius * 100)/100;
             }
-            this.radiusTip.innerHTML = r + this.units;
+            this.radiusTip.innerHTML = radius + this.units;
         }
+      }
     },
     
-    mouseUp: function(e) {
-        if (this.isDigitizing) {
-            this.event = e;
-            this.clearContext();
-            this.isDigitizing = false;
-            var center = this.circle.center;
-            var radius = this.circle.radius;
-            this.execute(center, radius);
-        }
-        
+    mouseUp: function(geom) {
         if (this.radiusTip && this.radiusTipType == 'dynamic') {
             this.radiusTip.style.display = 'none';
             this.radiusTip.innerHTML = '';
         }
-        
+        if (this.handler.start == this.handler.last) {
+          this.handler.clear();
+          this.execute(geom);
+        }
     },
 
     /**
@@ -215,24 +206,9 @@
      * @param center
      * @param radius
      **/
-    execute : function(center, radius) {
-        var wkt = 'POLYGON((';
-        var nPoints = 16;
-        var angle = 2 * Math.PI / nPoints;
-        var sep = '';
-        var first;
-        for (var i=0; i<nPoints; i++) {
-            var x = center.x + radius * Math.cos(i*angle);
-            var y = center.y + radius * Math.sin(i*angle);
-            if (i==0) {
-                first = x + ' ' + y;
-            }
-            wkt = wkt + sep + x + ' ' + y;
-            sep = ',';
-        }
-        wkt = wkt + sep + first + '))';
+    execute: function(geom) {
         var options = {};
-        options.geometry = wkt;
+        options.geometry = geom.toString();
         options.selectionType = this.selectionType;
         options.computed = this.bComputeMetadata;
 
@@ -245,7 +221,7 @@
             }
         }
         
-        if (this.event.shiftKey) {
+        if (this.handler.evt.shiftKey) {
             options.extendSelection = true;
         }
         



More information about the fusion-commits mailing list