[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